Skip to content

Commit

Permalink
Merge branch 'meetings-table' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
TPReal committed Dec 17, 2023
2 parents cb15daa + e6a5739 commit 7b5ff42
Show file tree
Hide file tree
Showing 27 changed files with 781 additions and 190 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@
],
"no-unused-expressions": "warn",
"no-unused-vars": "off",
"no-warning-comments": [
"warn",
{
"terms": ["DO NOT SUBMIT"],
"location": "anywhere"
}
],
"sort-imports": "off",
"sort-keys": "off"
},
Expand Down
2 changes: 2 additions & 0 deletions resources/js/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {MemoTitle} from "./features/root/MemoTitle";
const AdminFacilitiesListPage = lazy(() => import("features/root/pages/AdminFacilitiesList.page"));
const AdminUsersListPage = lazy(() => import("features/root/pages/AdminUsersList.page"));
const CalendarPage = lazy(() => import("features/root/pages/Calendar.page"));
const CalendarTablePage = lazy(() => import("features/root/pages/CalendarTable.page"));
const LoginPage = lazy(() => import("features/authentication/pages/Login.page"));
const RootPage = lazy(() => import("features/root/pages/Root.page"));

Expand Down Expand Up @@ -51,6 +52,7 @@ const App: VoidComponent = () => {
<Route path="/admin" component={FacilityAdminPages}>
<UnknownNotFound />
<LeafRoute routeKey="facility.admin.calendar" path="/calendar" component={CalendarPage} />
<LeafRoute routeKey="facility.admin.calendar_table" path="/calendar-table" component={CalendarTablePage} />
<LeafRoute routeKey="facility.admin.clients" path="/clients" component={NotYetImplemented} />
<LeafRoute routeKey="facility.admin.staff" path="/staff" component={NotYetImplemented} />
<LeafRoute routeKey="facility.admin.reports" path="/reports" component={NotYetImplemented} />
Expand Down
64 changes: 38 additions & 26 deletions resources/js/components/ui/Table/TQueryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from "@tanstack/solid-table";
import {createLocalStoragePersistence} from "components/persistence/persistence";
import {richJSONSerialiser} from "components/persistence/serialiser";
import {NON_NULLABLE} from "components/utils";
import {toastMessages} from "components/utils/toast";
import {FilterH} from "data-access/memo-api/tquery/filter_utils";
import {
Expand All @@ -18,7 +17,14 @@ import {
tableHelper,
} from "data-access/memo-api/tquery/table";
import {createTQuery} from "data-access/memo-api/tquery/tquery";
import {ColumnName, ColumnType, DataColumnSchema, DataItem, isDataColumn} from "data-access/memo-api/tquery/types";
import {
ColumnName,
ColumnType,
DataColumnSchema,
DataItem,
Sort,
isDataColumn,
} from "data-access/memo-api/tquery/types";
import {DEV, JSX, VoidComponent, createComputed, createEffect, createMemo, createSignal} from "solid-js";
import toast from "solid-toast";
import {
Expand Down Expand Up @@ -70,18 +76,16 @@ export interface TQueryTableProps {
/** The entity URL, must not change. */
readonly staticEntityURL: string;
readonly staticTranslations?: TableTranslations;
/**
* The key part used to persist the table settings.
* If not provided, the settings are persisted with the entity URL as the key. Specifying this
* key allows persisting the settings separately for different tables showing the same entity.
*/
/** The key to use for persisting the parameters of the displayed page. If not present, nothing is persisted. */
readonly staticPersistenceKey?: string;
/**
* The filter that is always applied to the data, regardless of other filtering.
* This is used to create e.g. a table of entities A on the details page of a particular
* entity B, so only entities A related direclty to that particular entity B should be shown.
*/
readonly intrinsicFilter?: FilterH;
/** The sort that is always applied to the data at the end of the filter specified by the user. */
readonly intrinsicSort?: Sort;
/** The definition of the columns in the table, in their correct order. */
readonly columns: readonly PartialColumnConfig[];
readonly initialSort?: SortingState;
Expand Down Expand Up @@ -173,16 +177,22 @@ export const TQueryTable: VoidComponent<TQueryTableProps> = (props) => {
["bool", {cell: tableCells.bool, size: 100}],
["date", {cell: tableCells.date}],
["datetime", {cell: tableCells.datetime}],
["int", {cell: tableCells.int}],
["int", {cell: tableCells.int, size: 150}],
["list", {enableSorting: false}],
["object", {enableSorting: false}],
["string", {}],
["text", {enableSorting: false}],
["uuid", {cell: tableCells.uuid, enableSorting: false, size: 80}],
["uuid_list", {cell: tableCells.uuidList, enableSorting: false, size: 80}],
["dict", {cell: tableCells.dict}],
["dict_list", {cell: tableCells.dictList, enableSorting: false}],
]);

const [allInitialised, setAllInitialised] = createSignal(false);
const requestCreator = createTableRequestCreator({
columnsConfig,
intrinsicFilter: () => props.intrinsicFilter,
intrinsicSort: () => props.intrinsicSort,
initialSort: props.initialSort,
initialPageSize:
props.initialPageSize ||
Expand Down Expand Up @@ -213,25 +223,27 @@ export const TQueryTable: VoidComponent<TQueryTableProps> = (props) => {
sorting,
pagination,
} = requestController;
createLocalStoragePersistence<PersistentState>({
key: ["TQueryTable", entityURL, props.staticPersistenceKey].filter(NON_NULLABLE).join(":"),
value: () => ({
colVis: columnVisibility[0](),
}),
onLoad: (value) => {
// Ensure a bad (e.g. outdated) entry won't affect visibility of a columnn that cannot have
// the visibility controlled by the user.
const colVis = {...value.colVis};
for (const col of columnsConfig()) {
if (col.columnDef.enableHiding === false) {
delete colVis[col.name];
if (props.staticPersistenceKey) {
createLocalStoragePersistence<PersistentState>({
key: `TQueryTable:${props.staticPersistenceKey}`,
value: () => ({
colVis: columnVisibility[0](),
}),
onLoad: (value) => {
// Ensure a bad (e.g. outdated) entry won't affect visibility of a columnn that cannot have
// the visibility controlled by the user.
const colVis = {...value.colVis};
for (const col of columnsConfig()) {
if (col.columnDef.enableHiding === false) {
delete colVis[col.name];
}
}
}
columnVisibility[1](colVis);
},
serialiser: richJSONSerialiser<PersistentState>(),
version: [PERSISTENCE_VERSION],
});
columnVisibility[1](colVis);
},
serialiser: richJSONSerialiser<PersistentState>(),
version: [PERSISTENCE_VERSION],
});
}
// Allow querying data now that the DEV columns are added and columns visibility is loaded.
setAllInitialised(true);
const {rowsCount, pageCount, scrollToTopSignal, filterErrors} = tableHelper({
Expand Down
68 changes: 36 additions & 32 deletions resources/js/components/ui/Table/Table.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,52 @@
> .scrollingWrapper {
@apply overflow-x-auto;

> .tableBg {
@apply w-max bg-gray-300 mr-16;
> .scrollToTopElement {
@apply w-max;

> .table {
@apply grid p-px gap-px;
> .tableBg {
@apply w-max bg-gray-300 mr-16;

> .headerRow {
@apply contents;
> .table {
@apply grid p-px gap-px;

> .cell {
@apply outline outline-1 outline-gray-400 bg-gray-100;
min-height: 30px;
> .headerRow {
@apply contents;

> .cell {
@apply outline outline-1 outline-gray-400 bg-gray-100;
min-height: 30px;
}
}
}

> .dataRow {
@apply contents;
> .dataRow {
@apply contents;

> .cell {
@apply bg-white text-black rounded-sm px-1.5 py-1 flex items-center overflow-hidden;
min-height: 30px;
}
> .cell {
@apply bg-white text-black rounded-sm px-1.5 py-1 flex items-center overflow-hidden;
min-height: 30px;
}

&:hover > .cell {
@apply bg-hover;
&:hover > .cell {
@apply bg-hover;
}
}
}

> .wideRow {
grid-column: 1 / -1;
@apply bg-white p-2 text-gray-500 text-center;
}
> .wideRow {
grid-column: 1 / -1;
@apply bg-white p-2 text-gray-500 text-center;
}

> .bottomBorder {
grid-column: 1 / -1;
@apply border-b border-b-gray-300;
margin-top: -5px;
}
> .bottomBorder {
grid-column: 1 / -1;
@apply border-b border-b-gray-300;
margin-top: -5px;
}

&.dimmed {
> .dataRow > .cell {
@apply bg-opacity-70 text-opacity-30;
&.dimmed {
> .dataRow > .cell {
@apply bg-opacity-70 text-opacity-30;
}
}
}
}
Expand All @@ -65,7 +69,7 @@
@apply pr-2;
}

> .scrollingWrapper > .tableBg > .table {
> .scrollingWrapper > .scrollToTopElement > .tableBg > .table {
> .headerRow > .cell {
@apply sticky top-px;
}
Expand Down
119 changes: 62 additions & 57 deletions resources/js/components/ui/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ const DEFAULT_PROPS = {
*/
export const Table = <T,>(allProps: VoidProps<Props<T>>): JSX.Element => {
const props = mergeProps(DEFAULT_PROPS, allProps);
let scrollToTopPoint: HTMLDivElement | undefined;
let scrollToTopElement: HTMLDivElement | undefined;
createEffect(
on(
() => props.scrollToTopSignal?.(),
(_input, prevInput) => {
if (prevInput !== undefined) {
scrollToTopPoint?.scrollIntoView({behavior: "smooth"});
scrollToTopElement?.scrollIntoView({behavior: "smooth"});
}
},
),
Expand Down Expand Up @@ -196,64 +196,69 @@ export const Table = <T,>(allProps: VoidProps<Props<T>>): JSX.Element => {
onScroll={[setLastScrollTimestamp, Date.now()]}
onScrollEnd={[setLastScrollTimestamp, 0]}
>
<div ref={scrollToTopPoint} class={s.tableBg}>
<div
class={cx(s.table, {[s.dimmed!]: props.isDimmed})}
style={{"grid-template-columns": gridTemplateColumns()}}
>
<div class={s.headerRow}>
<For each={getHeaders(props.table)}>
{({header, column}) => (
<Show when={header()}>
{(header) => (
<div
class={s.cell}
onWheel={(e) => {
const scrWrapper = scrollingWrapper();
if (scrWrapper && e.deltaY) {
setDesiredScrollX((l = scrWrapper.scrollLeft) =>
Math.min(Math.max(l + e.deltaY, 0), scrWrapper.scrollWidth - scrWrapper.clientWidth),
);
e.preventDefault();
}
}}
>
<Show when={!header().isPlaceholder}>
<CellRenderer component={column.columnDef.header} props={header().getContext()} />
</Show>
</div>
)}
</Show>
)}
</For>
</div>
<Dynamic
component={{For, Index}[props.rowsIteration]}
each={props.table.getRowModel().rows}
fallback={
<div class={s.wideRow}>
<Show when={props.isDimmed} fallback={EMPTY_VALUE_SYMBOL}>
<BigSpinner />
</Show>
</div>
}
<div ref={scrollToTopElement} class={s.scrollToTopElement}>
<div class={s.tableBg}>
<div
class={cx(s.table, {[s.dimmed!]: props.isDimmed})}
style={{"grid-template-columns": gridTemplateColumns()}}
>
{(rowMaybeAccessor: Row<T> | Accessor<Row<T>>) => {
const row = typeof rowMaybeAccessor === "function" ? rowMaybeAccessor : () => rowMaybeAccessor;
return (
<div class={s.dataRow} inert={props.isDimmed || undefined}>
<Index each={row().getVisibleCells()}>
{(cell) => (
<span class={s.cell}>
<CellRenderer component={cell().column.columnDef.cell} props={cell().getContext()} />
</span>
<div class={s.headerRow}>
<For each={getHeaders(props.table)}>
{({header, column}) => (
<Show when={header()}>
{(header) => (
<div
class={s.cell}
onWheel={(e) => {
const scrWrapper = scrollingWrapper();
if (scrWrapper && e.deltaY) {
setDesiredScrollX((l = scrWrapper.scrollLeft) =>
Math.min(
Math.max(l + e.deltaY, 0),
scrWrapper.scrollWidth - scrWrapper.clientWidth,
),
);
e.preventDefault();
}
}}
>
<Show when={!header().isPlaceholder}>
<CellRenderer component={column.columnDef.header} props={header().getContext()} />
</Show>
</div>
)}
</Index>
</Show>
)}
</For>
</div>
<Dynamic
component={{For, Index}[props.rowsIteration]}
each={props.table.getRowModel().rows}
fallback={
<div class={s.wideRow}>
<Show when={props.isDimmed} fallback={EMPTY_VALUE_SYMBOL}>
<BigSpinner />
</Show>
</div>
);
}}
</Dynamic>
<div class={s.bottomBorder} />
}
>
{(rowMaybeAccessor: Row<T> | Accessor<Row<T>>) => {
const row = typeof rowMaybeAccessor === "function" ? rowMaybeAccessor : () => rowMaybeAccessor;
return (
<div class={s.dataRow} inert={props.isDimmed || undefined}>
<Index each={row().getVisibleCells()}>
{(cell) => (
<span class={s.cell}>
<CellRenderer component={cell().column.columnDef.cell} props={cell().getContext()} />
</span>
)}
</Index>
</div>
);
}}
</Dynamic>
<div class={s.bottomBorder} />
</div>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 7b5ff42

Please sign in to comment.