Skip to content

Commit

Permalink
Fix primefaces#3693: Datatable allow responsive stack and scrollable
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Dec 28, 2022
1 parent 6f7ea66 commit fdbc5dd
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 27 deletions.
48 changes: 28 additions & 20 deletions components/lib/datatable/DataTable.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import PrimeReact, { FilterMatchMode, FilterOperator, FilterService } from '../api/Api';
import { useEventListener, useMountEffect, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks';
import { useMediaQuery } from '../hooks/useMediaQuery';
import { Paginator } from '../paginator/Paginator';
import { DomHandler, ObjectUtils, UniqueComponentId, classNames } from '../utils/Utils';
import { VirtualScroller } from '../virtualscroller/VirtualScroller';
Expand All @@ -15,12 +16,14 @@ export const DataTable = React.forwardRef((props, ref) => {
const [sortOrderState, setSortOrderState] = React.useState(props.sortOrder);
const [multiSortMetaState, setMultiSortMetaState] = React.useState(props.multiSortMeta);
const [filtersState, setFiltersState] = React.useState(props.filters);
const [scrollableState, setScrollableState] = React.useState(props.scrollable);
const [columnOrderState, setColumnOrderState] = React.useState([]);
const [groupRowsSortMetaState, setGroupRowsSortMetaState] = React.useState(null);
const [editingMetaState, setEditingMetaState] = React.useState({});
const [attributeSelectorState, setAttributeSelectorState] = React.useState(null);
const [d_rowsState, setD_rowsState] = React.useState(props.rows);
const [d_filtersState, setD_filtersState] = React.useState({});
const matchesResponsiveBreakpoint = useMediaQuery(`(max-width: ${props.breakpoint})`);
const elementRef = React.useRef(null);
const tableRef = React.useRef(null);
const wrapperRef = React.useRef(null);
Expand Down Expand Up @@ -81,7 +84,7 @@ export const DataTable = React.forwardRef((props, ref) => {
};

const isVirtualScrollerDisabled = () => {
return ObjectUtils.isEmpty(props.virtualScrollerOptions) || !props.scrollable;
return ObjectUtils.isEmpty(props.virtualScrollerOptions) || !scrollableState;
};

const isEquals = (data1, data2) => {
Expand Down Expand Up @@ -367,7 +370,7 @@ export const DataTable = React.forwardRef((props, ref) => {
let innerHTML = '';

widths.forEach((width, index) => {
let style = props.scrollable ? `flex: 1 1 ${width}px !important` : `width: ${width}px !important`;
let style = scrollableState ? `flex: 1 1 ${width}px !important` : `width: ${width}px !important`;

innerHTML += `
.p-datatable[${attributeSelectorState}] .p-datatable-thead > tr > th:nth-child(${index + 1}),
Expand Down Expand Up @@ -563,7 +566,7 @@ export const DataTable = React.forwardRef((props, ref) => {

widths.forEach((width, index) => {
let colWidth = index === colIndex ? newColumnWidth : nextColumnWidth && index === colIndex + 1 ? nextColumnWidth : width;
let style = props.scrollable ? `flex: 1 1 ${colWidth}px !important` : `width: ${colWidth}px !important`;
let style = scrollableState ? `flex: 1 1 ${colWidth}px !important` : `width: ${colWidth}px !important`;

innerHTML += `
.p-datatable[${attributeSelectorState}] .p-datatable-thead > tr > th:nth-child(${index + 1}),
Expand Down Expand Up @@ -1308,14 +1311,6 @@ export const DataTable = React.forwardRef((props, ref) => {
}
});

useUpdateEffect(() => {
elementRef.current.setAttribute(attributeSelectorState, '');

if (props.responsiveLayout === 'stack' && !props.scrollable) {
createResponsiveStyle();
}
}, [attributeSelectorState]);

useUpdateEffect(() => {
const filters = cloneFilters(props.filters);

Expand All @@ -1334,12 +1329,25 @@ export const DataTable = React.forwardRef((props, ref) => {
});

useUpdateEffect(() => {
elementRef.current.setAttribute(attributeSelectorState, '');

destroyResponsiveStyle();

if (props.responsiveLayout === 'stack' && !props.scrollable) {
if (props.responsiveLayout === 'stack') {
createResponsiveStyle();
}
}, [props.responsiveLayout, props.scrollable]);
}, [props.responsiveLayout, attributeSelectorState]);

useUpdateEffect(() => {
let isScrollable = props.scrollable;

// #3693 disable scrolling if responsive stack breakpoint is hit
if (props.responsiveLayout === 'stack' && isScrollable) {
isScrollable = !matchesResponsiveBreakpoint;
}

setScrollableState(isScrollable);
}, [props.responsiveLayout, props.scrollable, matchesResponsiveBreakpoint]);

useUpdateEffect(() => {
if (props.globalFilter) {
Expand Down Expand Up @@ -1492,7 +1500,7 @@ export const DataTable = React.forwardRef((props, ref) => {
editingRows={props.editingRows}
onRowReorder={props.onRowReorder}
reorderableRows={props.reorderableRows}
scrollable={props.scrollable}
scrollable={scrollableState}
rowGroupMode={props.rowGroupMode}
groupRowsBy={props.groupRowsBy}
expandableRowGroups={props.expandableRowGroups}
Expand Down Expand Up @@ -1566,7 +1574,7 @@ export const DataTable = React.forwardRef((props, ref) => {
editingRows={props.editingRows}
onRowReorder={props.onRowReorder}
reorderableRows={props.reorderableRows}
scrollable={props.scrollable}
scrollable={scrollableState}
rowGroupMode={props.rowGroupMode}
groupRowsBy={props.groupRowsBy}
expandableRowGroups={props.expandableRowGroups}
Expand Down Expand Up @@ -1741,11 +1749,11 @@ export const DataTable = React.forwardRef((props, ref) => {
'p-datatable-auto-layout': props.autoLayout,
'p-datatable-resizable': props.resizableColumns,
'p-datatable-resizable-fit': props.resizableColumns && props.columnResizeMode === 'fit',
'p-datatable-scrollable': props.scrollable,
'p-datatable-scrollable-vertical': props.scrollable && props.scrollDirection === 'vertical',
'p-datatable-scrollable-horizontal': props.scrollable && props.scrollDirection === 'horizontal',
'p-datatable-scrollable-both': props.scrollable && props.scrollDirection === 'both',
'p-datatable-flex-scrollable': props.scrollable && props.scrollHeight === 'flex',
'p-datatable-scrollable': scrollableState,
'p-datatable-scrollable-vertical': scrollableState && props.scrollDirection === 'vertical',
'p-datatable-scrollable-horizontal': scrollableState && props.scrollDirection === 'horizontal',
'p-datatable-scrollable-both': scrollableState && props.scrollDirection === 'both',
'p-datatable-flex-scrollable': scrollableState && props.scrollHeight === 'flex',
'p-datatable-responsive-stack': props.responsiveLayout === 'stack',
'p-datatable-responsive-scroll': props.responsiveLayout === 'scroll',
'p-datatable-striped': props.stripedRows,
Expand Down
15 changes: 8 additions & 7 deletions components/lib/hooks/Hooks.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { usePrevious } from './usePrevious';
import { useMountEffect } from './useMountEffect';
import { useUpdateEffect } from './useUpdateEffect';
import { useUnmountEffect } from './useUnmountEffect';
import { useEventListener } from './useEventListener';
import { useInterval } from './useInterval';
import { useMediaQuery } from './useMediaQuery';
import { useMountEffect } from './useMountEffect';
import { useOverlayListener } from './useOverlayListener';
import { useOverlayScrollListener } from './useOverlayScrollListener';
import { usePrevious } from './usePrevious';
import { useResizeListener } from './useResizeListener';
import { useInterval } from './useInterval';
import { useStorage, useLocalStorage, useSessionStorage } from './useStorage';
import { useLocalStorage, useSessionStorage, useStorage } from './useStorage';
import { useTimeout } from './useTimeout';
import { useUnmountEffect } from './useUnmountEffect';
import { useUpdateEffect } from './useUpdateEffect';

export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useLocalStorage, useSessionStorage, useTimeout };
export { usePrevious, useMediaQuery, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useLocalStorage, useSessionStorage, useTimeout };
1 change: 1 addition & 0 deletions components/lib/hooks/hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export declare function useMountEffect(effect: React.EffectCallback): void;
export declare function useUpdateEffect(effect: React.EffectCallback, deps?: React.DependencyList): void;
export declare function useUnmountEffect(effect: React.EffectCallback): void;
export declare function useEventListener(options: EventOptions): any[];
export declare function useMediaQuery(query: string, initialValue?: boolean): boolean;
export declare function useOverlayListener(options: OverlayEventOptions): any[];
export declare function useOverlayScrollListener(options: EventOptions): any[];
export declare function useResizeListener(options: ResizeEventOptions): any[];
Expand Down
44 changes: 44 additions & 0 deletions components/lib/hooks/useMediaQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useEffect, useRef, useState } from 'react';

/**
* Hook to notify when media queries are satisfied.
*
* @param {*} query the media query like '(min-width: 900px)'
* @param {*} initialValue override the initial value if you don't want it detected
* @returns boolean if the media query matches
*/
export function useMediaQuery(query, initialValue) {
const [matches, setMatches] = useState(getInitialValue(query, initialValue));
const queryRef = useRef();

useEffect(() => {
if ('matchMedia' in window) {
queryRef.current = window.matchMedia(query);
setMatches(queryRef.current.matches);

return attachMediaListener(queryRef.current, (event) => setMatches(event.matches));
}

return undefined;
}, [query]);

return matches;
}

function attachMediaListener(query, callback) {
query.addEventListener('change', callback);

return () => query.removeEventListener('change', callback);
}

function getInitialValue(query, initialValue) {
if (typeof initialValue === 'boolean') {
return initialValue;
}

if (typeof window !== 'undefined' && 'matchMedia' in window) {
return window.matchMedia(query).matches;
}

return false;
}

0 comments on commit fdbc5dd

Please sign in to comment.