Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create paginator component #3195

Merged
merged 25 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c703170
Create paginator component
CarolineDenis Mar 15, 2023
44ac47a
Move Paginator component to molecule and delete uncessary libraries
CarolineDenis Mar 16, 2023
928be8b
Simplify usage of usePaginator()
maxpatiiuk Mar 17, 2023
08bb7ba
Sort Record Sets on the back-end
maxpatiiuk Mar 17, 2023
5cfa38e
Fix sort config causing infinite fetch loop
maxpatiiuk Mar 17, 2023
1ec1f25
Fix wrong sizing for the paginator <select>
maxpatiiuk Mar 17, 2023
bf23b48
Remember user's page size preference
maxpatiiuk Mar 17, 2023
88137e7
Implement pagination in Query overlay and refactor it
CarolineDenis Mar 20, 2023
05a0c4a
Delete duplicate file
maxpatiiuk Apr 19, 2023
93babd6
Fix wrong typing for fetchRows
maxpatiiuk Apr 19, 2023
0360179
Merge remote-tracking branch 'origin/xml-editor' into issue-1398
maxpatiiuk Apr 19, 2023
3f73d6c
Lint code with ESLint and Prettier
maxpatiiuk Apr 19, 2023
d0f8a15
Fix dialog size when navigating between pages in RecordSets overlay
CarolineDenis Apr 20, 2023
62147ef
Fix the size of dialog when changing page in Query Overlay
CarolineDenis Apr 20, 2023
0d08ab3
Remove unnecessary import
CarolineDenis Apr 20, 2023
edb9802
Remove full-height from ResourceView Dialog
CarolineDenis Apr 20, 2023
2032722
Simplify code
CarolineDenis Apr 21, 2023
e9aed7f
Fix failing test
maxpatiiuk Apr 21, 2023
17d5982
Provide simple mock for resize observer
maxpatiiuk Apr 21, 2023
0e47c2b
Work arround DevTools UX issue
maxpatiiuk Apr 27, 2023
73cc56a
Always apply padding right to numeric fields
maxpatiiuk Apr 27, 2023
b397f5a
Fix slider positioning in Interactions Dialog
maxpatiiuk Apr 27, 2023
4763ca4
Lint code with ESLint and Prettier
maxpatiiuk Apr 27, 2023
c428f14
Merge branch 'xml-editor' into issue-1398
CarolineDenis May 3, 2023
949a08a
Fix failing tests
CarolineDenis May 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,6 @@ export function ResourceView<SCHEMA extends AnySchema>({
* navigation buttons don't jump around a lot as you navigate between
* records
*/
const isFullHeight =
dialog === 'modal' && typeof headerButtons === 'function' && !isSubForm;

return (
<Dialog
Expand All @@ -300,9 +298,7 @@ export function ResourceView<SCHEMA extends AnySchema>({
)
}
className={{
container: `${dialogClassNames.normalContainer} ${
isFullHeight ? 'h-full' : ''
}`,
container: `${dialogClassNames.normalContainer}`,
CarolineDenis marked this conversation as resolved.
Show resolved Hide resolved
content: `${className.formStyles} ${dialogClassNames.flexContent}`,
}}
dimensionsKey={viewName ?? resource?.specifyTable.view}
Expand Down
43 changes: 43 additions & 0 deletions specifyweb/frontend/js_src/lib/components/Molecules/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ export function Dialog({
[positionKey]
);

useFreezeDialogSize(container, dimensionsKey);

const isFullScreen = containerClassName.includes(dialogClassNames.fullScreen);

const draggableContainer: Props['contentElement'] = React.useCallback(
Expand Down Expand Up @@ -569,3 +571,44 @@ function useTitleChangeNotice(dimensionKey: string | undefined): void {
);
}, [dimensionKey]);
}

export function useFreezeDialogSize(
CarolineDenis marked this conversation as resolved.
Show resolved Hide resolved
containerSizeRef: HTMLDivElement | null,
dimensionKey: string | false | undefined
CarolineDenis marked this conversation as resolved.
Show resolved Hide resolved
): void {
React.useEffect(() => {
if (containerSizeRef === null) return undefined;
let oldHeight = containerSizeRef.offsetHeight;
let oldWidth = containerSizeRef.offsetWidth;
const resizeObserver = new ResizeObserver(() => {
const newHeight = containerSizeRef.offsetHeight;
const newWidth = containerSizeRef.offsetWidth;

const width = f.parseInt(containerSizeRef.style.width);
const height = f.parseInt(containerSizeRef.style.height);
const hasBeenChanged =
typeof width === 'number' && typeof height === 'number';

if (oldHeight !== undefined && newHeight < oldHeight && !hasBeenChanged) {
containerSizeRef.style.minHeight = `${oldHeight}px`;
} else oldHeight = newHeight;

if (oldWidth !== undefined && newWidth < oldWidth && !hasBeenChanged) {
containerSizeRef.style.minWidth = `${oldWidth}px`;
} else oldWidth = newWidth;

if (hasBeenChanged) {
containerSizeRef.style.minHeight = '';
containerSizeRef.style.minWidth = '';
}
});

resizeObserver.observe(containerSizeRef);

return () => {
resizeObserver.disconnect();
containerSizeRef.style.minHeight = '';
containerSizeRef.style.minWidth = '';
};
}, [containerSizeRef, dimensionKey]);
}
177 changes: 91 additions & 86 deletions specifyweb/frontend/js_src/lib/components/Toolbar/Query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { getTableById, tables } from '../DataModel/tables';
import type { SpQuery } from '../DataModel/types';
import { userInformation } from '../InitialContext/userInformation';
import { DateElement } from '../Molecules/DateElement';
import { Dialog } from '../Molecules/Dialog';
import { Dialog, LoadingScreen } from '../Molecules/Dialog';
import { usePaginator } from '../Molecules/Paginator';
import { SortIndicator, useSortConfig } from '../Molecules/Sorting';
import { TableIcon } from '../Molecules/TableIcon';
Expand All @@ -31,6 +31,7 @@ import { QueryEditButton } from '../QueryBuilder/Edit';
import { OverlayContext } from '../Router/Router';
import { SafeOutlet } from '../Router/RouterUtils';
import { QueryTables } from './QueryTables';
import { loadingGif } from '../Molecules';

export function QueriesOverlay(): JSX.Element {
const handleClose = React.useContext(OverlayContext);
Expand All @@ -52,7 +53,7 @@ export type QueryListContextType = {
) => string | undefined;
readonly children?: (props: {
readonly totalCount: number;
readonly records: RA<SerializedResource<SpQuery>>;
readonly records: RA<SerializedResource<SpQuery>> | undefined;
CarolineDenis marked this conversation as resolved.
Show resolved Hide resolved
readonly children: JSX.Element;
readonly dialog: (children: JSX.Element) => JSX.Element;
}) => JSX.Element;
Expand Down Expand Up @@ -92,7 +93,7 @@ export function QueryListDialog({
}),
[limit, offset, orderBy]
),
true
false
);

React.useEffect(
Expand All @@ -113,92 +114,96 @@ export function QueryListDialog({
[data]
);

const totalCountRef = React.useRef<number | undefined>(undefined);
totalCountRef.current = data?.totalCount ?? totalCountRef.current;
const totalCount = totalCountRef.current;

const isReadOnly = React.useContext(ReadOnlyContext);

return typeof data === 'object'
? children({
...data,
children: (
<>
<table className="grid-table grid-cols-[repeat(3,auto)_min-content] gap-2">
<thead>
<tr>
<th
className="pl-[calc(theme(spacing.table-icon)_+_theme(spacing.2))]"
scope="col"
return totalCount === undefined ? (
<LoadingScreen />
) : (
children({
totalCount,
records: data?.records,
children: (
<>
<table className="grid-table grid-cols-[repeat(3,auto)_min-content] gap-2">
<thead>
<tr>
<th
className="pl-[calc(theme(spacing.table-icon)_+_theme(spacing.2))]"
scope="col"
>
<Button.LikeLink onClick={(): void => handleSort('name')}>
{getField(tables.SpQuery, 'name').label}
<SortIndicator fieldName="name" sortConfig={sortConfig} />
</Button.LikeLink>
</th>
<th scope="col">
<Button.LikeLink
onClick={(): void => handleSort('timestampCreated')}
>
{getField(tables.SpQuery, 'timestampCreated').label}
<SortIndicator
fieldName="timestampCreated"
sortConfig={sortConfig}
/>
</Button.LikeLink>
</th>
<th scope="col">
<Button.LikeLink
onClick={(): void => handleSort('timestampModified')}
>
<Button.LikeLink onClick={(): void => handleSort('name')}>
{getField(tables.SpQuery, 'name').label}
<SortIndicator fieldName="name" sortConfig={sortConfig} />
</Button.LikeLink>
</th>
<th scope="col">
<Button.LikeLink
onClick={(): void => handleSort('timestampCreated')}
>
{getField(tables.SpQuery, 'timestampCreated').label}
<SortIndicator
fieldName="timestampCreated"
sortConfig={sortConfig}
/>
</Button.LikeLink>
</th>
<th scope="col">
<Button.LikeLink
onClick={(): void => handleSort('timestampModified')}
>
{getField(tables.SpQuery, 'timestampModified').label}
<SortIndicator
fieldName="timestampModified"
sortConfig={sortConfig}
/>
</Button.LikeLink>
</th>
<td />
</tr>
</thead>
<tbody>
{data.records.map((query) => (
<QueryList
key={query.id}
query={query}
isReadOnly={isReadOnly}
getQuerySelectUrl={getQuerySelectUrl}
/>
))}
{data.totalCount !== data.records.length && (
<tr>
<td colSpan={3}>{commonText.listTruncated()}</td>
</tr>
)}
</tbody>
</table>
{paginator(data?.totalCount)}
</>
),
dialog: (children) => (
<Dialog
buttons={
<>
<Button.DialogClose>{commonText.cancel()}</Button.DialogClose>
{(hasToolPermission('queryBuilder', 'create') ||
hasPermission('/querybuilder/query', 'execute')) && (
<Link.Blue href={newQueryUrl}>{commonText.new()}</Link.Blue>
)}
</>
}
header={commonText.countLine({
resource: queryText.queries(),
count: data.totalCount,
})}
icon={<span className="text-blue-500">{icons.documentSearch}</span>}
onClose={handleClose}
>
{children}
</Dialog>
),
})
: null;
{getField(tables.SpQuery, 'timestampModified').label}
<SortIndicator
fieldName="timestampModified"
sortConfig={sortConfig}
/>
</Button.LikeLink>
</th>
<td />
</tr>
</thead>
<tbody>
{data?.records.map((query) => (
<QueryList
key={query.id}
query={query}
isReadOnly={isReadOnly}
getQuerySelectUrl={getQuerySelectUrl}
/>
))}
</tbody>
</table>
<span className="-ml-2 flex-1" />
{data === undefined && loadingGif}
{paginator(data?.totalCount)}
</>
),
dialog: (children) => (
<Dialog
buttons={
<>
<Button.DialogClose>{commonText.cancel()}</Button.DialogClose>
{(hasToolPermission('queryBuilder', 'create') ||
hasPermission('/querybuilder/query', 'execute')) && (
<Link.Blue href={newQueryUrl}>{commonText.new()}</Link.Blue>
)}
</>
}
header={commonText.countLine({
resource: queryText.queries(),
count: totalCount,
})}
icon={<span className="text-blue-500">{icons.documentSearch}</span>}
onClose={handleClose}
>
{children}
</Dialog>
),
})
);
}

function QueryList({
Expand Down
Loading