@homebound/beam 2.99.1 → 2.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ export interface IconProps extends AriaAttributes, DOMProps {
9
9
  /** The size of the icon in increments, i.e. 1 == 8px, default is 3 == 24px. */
10
10
  inc?: number;
11
11
  /** Styles overrides */
12
- xss?: Xss<Margin>;
12
+ xss?: Xss<Margin | "visibility">;
13
13
  }
14
14
  export declare const Icon: React.MemoExoticComponent<(props: IconProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
15
15
  /**
@@ -103,13 +103,13 @@ export declare type RowTuple<R extends Kinded> = [GridDataRow<R> | undefined, Re
103
103
  export declare type GridSortConfig<S> = {
104
104
  on: "client";
105
105
  /** The optional initial column (index in columns) and direction to sort. */
106
- initial?: [S | GridColumn<any>, Direction];
106
+ initial?: [S | GridColumn<any>, Direction] | undefined;
107
107
  } | {
108
108
  on: "server";
109
109
  /** The current sort by value + direction (if server-side sorting). */
110
110
  value?: [S, Direction];
111
- /** Callback for when the column is sorted (if server-side sorting). */
112
- onSort: (orderBy: S, direction: Direction) => void;
111
+ /** Callback for when the column is sorted (if server-side sorting). Parameters set to `undefined` is a signal to return to the initial sort state */
112
+ onSort: (orderBy: S | undefined, direction: Direction | undefined) => void;
113
113
  };
114
114
  export interface GridTableProps<R extends Kinded, S, X> {
115
115
  id?: string;
@@ -92,12 +92,10 @@ function GridTable(props) {
92
92
  };
93
93
  }
94
94
  const [sortState, setSortKey] = (0, useSortState_1.useSortState)(columns, sorting);
95
- // Disclaimer that technically even though this is a useMemo, sortRows is mutating `rows` directly
96
95
  const maybeSorted = (0, react_1.useMemo)(() => {
97
96
  if ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client" && sortState) {
98
97
  // If using client-side sort, the sortState use S = number
99
- (0, sortRows_1.sortRows)(columns, rows, sortState);
100
- return rows;
98
+ return (0, sortRows_1.sortRows)(columns, rows, sortState);
101
99
  }
102
100
  return rows;
103
101
  }, [columns, rows, sorting, sortState]);
@@ -6,6 +6,7 @@ const react_1 = require("react");
6
6
  const Icon_1 = require("../Icon");
7
7
  const GridSortContext_1 = require("./GridSortContext");
8
8
  const Css_1 = require("../../Css");
9
+ const hooks_1 = require("../../hooks");
9
10
  const useTestIds_1 = require("../../utils/useTestIds");
10
11
  /**
11
12
  * Wraps column header names with up/down sorting icons.
@@ -20,8 +21,12 @@ const useTestIds_1 = require("../../utils/useTestIds");
20
21
  */
21
22
  function SortHeader(props) {
22
23
  const { content, xss } = props;
24
+ const { isHovered, hoverProps } = (0, hooks_1.useHover)({});
23
25
  const { sorted, toggleSort } = (0, react_1.useContext)(GridSortContext_1.GridSortContext);
24
26
  const tid = (0, useTestIds_1.useTestIds)(props, "sortHeader");
25
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, tid, { css: { ...Css_1.Css.df.aic.cursorPointer.selectNone.$, ...xss }, onClick: toggleSort }, { children: [content, sorted === "ASC" && (0, jsx_runtime_1.jsx)(Icon_1.Icon, Object.assign({ icon: "sortUp", inc: 2 }, tid.icon, { xss: Css_1.Css.mlPx(4).$ }), void 0), sorted === "DESC" && (0, jsx_runtime_1.jsx)(Icon_1.Icon, Object.assign({ icon: "sortDown", inc: 2 }, tid.icon, { xss: Css_1.Css.mlPx(4).$ }), void 0)] }), void 0));
27
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, tid, { css: { ...Css_1.Css.df.aic.h100.cursorPointer.selectNone.$, ...xss } }, hoverProps, { onClick: toggleSort }, { children: [content, (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.fs0.$ }, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, Object.assign({ icon: sorted === "DESC" ? "sortDown" : "sortUp", color: sorted !== undefined ? Css_1.Palette.LightBlue700 : Css_1.Palette.Gray400, xss: Css_1.Css.ml1
28
+ .visibility("hidden")
29
+ .if(isHovered || sorted !== undefined)
30
+ .visibility("visible").$, inc: 2 }, tid.icon), void 0) }), void 0)] }), void 0));
26
31
  }
27
32
  exports.SortHeader = SortHeader;
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from "react";
2
2
  import { GridCellContent, GridColumn, GridDataRow, GridSortConfig, Kinded } from "./GridTable";
3
3
  import { SortState } from "./useSortState";
4
- export declare function sortRows<R extends Kinded>(columns: GridColumn<R>[], rows: GridDataRow<R>[], sortState: SortState<number>): void;
4
+ export declare function sortRows<R extends Kinded>(columns: GridColumn<R>[], rows: GridDataRow<R>[], sortState: SortState<number>): GridDataRow<R>[];
5
5
  export declare function ensureClientSideSortValueIsSortable(sorting: GridSortConfig<any> | undefined, isHeader: boolean, column: GridColumn<any>, idx: number, maybeContent: ReactNode | GridCellContent): void;
@@ -2,21 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureClientSideSortValueIsSortable = exports.sortRows = void 0;
4
4
  const GridTable_1 = require("./GridTable");
5
- // We currently mutate `rows` while sorting; this would be bad if rows was directly
6
- // read from an immutable store like the apollo cache, but we basically always make
7
- // a copy in the process of adding our `kind` tags.
8
- //
9
- // I suppose that is an interesting idea, would we ever want to render a GQL query/cache
10
- // result directly into the table without first doing a kind-mapping? Like maybe we could
11
- // use __typename as the kind.
5
+ // Returns a shallow copy of the `rows` parameter sorted based on `sortState`
12
6
  function sortRows(columns, rows, sortState) {
13
- sortBatch(columns, rows, sortState);
7
+ const sorted = sortBatch(columns, rows, sortState);
14
8
  // Recursively sort child rows
15
- for (const row of rows) {
9
+ sorted.forEach((row, i) => {
16
10
  if (row.children) {
17
- sortRows(columns, row.children, sortState);
11
+ sorted[i] = { ...sorted[i], children: sortRows(columns, row.children, sortState) };
18
12
  }
19
- }
13
+ });
14
+ return sorted;
20
15
  }
21
16
  exports.sortRows = sortRows;
22
17
  function sortBatch(columns, batch, sortState) {
@@ -24,7 +19,8 @@ function sortBatch(columns, batch, sortState) {
24
19
  const [value, direction] = sortState;
25
20
  const column = columns[value];
26
21
  const invert = direction === "DESC";
27
- batch.sort((a, b) => {
22
+ // Make a shallow copy for sorting to avoid mutating the original list
23
+ return [...batch].sort((a, b) => {
28
24
  const v1 = sortValue((0, GridTable_1.applyRowFn)(column, a));
29
25
  const v2 = sortValue((0, GridTable_1.applyRowFn)(column, b));
30
26
  const v1e = v1 === null || v1 === undefined;
@@ -10,3 +10,4 @@ import { Direction, GridColumn, GridSortConfig, Kinded } from "./GridTable";
10
10
  export declare type SortState<S> = readonly [S, Direction];
11
11
  /** Small custom hook that wraps the "setSortColumn inverts the current sort" logic. */
12
12
  export declare function useSortState<R extends Kinded, S>(columns: GridColumn<R, S>[], sorting?: GridSortConfig<S>): [SortState<S> | undefined, (value: S) => void];
13
+ export declare function deriveSortState<S>(currentSortState: SortState<S> | undefined, clickedKey: S, initialSortState: SortState<S> | undefined): SortState<S> | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useSortState = void 0;
3
+ exports.deriveSortState = exports.useSortState = void 0;
4
4
  const react_1 = require("react");
5
5
  const GridTable_1 = require("./GridTable");
6
6
  /** Small custom hook that wraps the "setSortColumn inverts the current sort" logic. */
@@ -8,34 +8,34 @@ function useSortState(columns, sorting) {
8
8
  // If we're server-side sorting, use the caller's `sorting.value` prop to initialize our internal
9
9
  // `useState`. After this, we ignore `sorting.value` because we assume it should match what our
10
10
  // `setSortState` just changed anyway (in response to the user sorting a column).
11
- const [sortState, setSortState] = (0, react_1.useState)(() => {
11
+ const initialSortState = (0, react_1.useMemo)(() => {
12
12
  if ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client") {
13
13
  const { initial } = sorting;
14
- if (initial) {
14
+ if (initial === undefined && "initial" in sorting) {
15
+ // if explicitly set to `undefined`, then do not sort
16
+ return undefined;
17
+ }
18
+ else if (initial) {
15
19
  const key = typeof initial[0] === "number" ? initial[0] : columns.indexOf(initial[0]);
16
20
  return [key, initial[1]];
17
21
  }
18
22
  else {
19
23
  // If no explicit sorting, assume 1st column ascending
20
24
  const firstSortableColumn = columns.findIndex((c) => c.clientSideSort !== false);
21
- return [firstSortableColumn, "ASC"];
25
+ return [firstSortableColumn, GridTable_1.ASC];
22
26
  }
23
27
  }
24
28
  else {
25
29
  return sorting === null || sorting === void 0 ? void 0 : sorting.value;
26
30
  }
27
- });
28
- // Make a custom setSortKey that is useState-like but contains the invert-if-same-column-clicked-twice logic.
31
+ }, [sorting, columns]);
32
+ const [sortState, setSortState] = (0, react_1.useState)(initialSortState);
33
+ // Make a custom setSortKey that is useState-like but contains the ASC->DESC->RESET logic.
29
34
  const setSortKey = (0, react_1.useCallback)((clickedKey) => {
30
- const [currentKey, currentDirection] = sortState || [];
31
- const [newKey, newDirection] =
32
- // If clickedKey === currentKey, then toggle direction
33
- clickedKey === currentKey
34
- ? [currentKey, currentDirection === GridTable_1.ASC ? GridTable_1.DESC : GridTable_1.ASC]
35
- : // Otherwise, use the new key, and default to ascending
36
- [clickedKey, GridTable_1.ASC];
37
- setSortState([newKey, newDirection]);
35
+ const newState = deriveSortState(sortState, clickedKey, initialSortState);
36
+ setSortState(newState);
38
37
  if ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "server") {
38
+ const [newKey, newDirection] = newState !== null && newState !== void 0 ? newState : [undefined, undefined];
39
39
  sorting.onSort(newKey, newDirection);
40
40
  }
41
41
  },
@@ -44,3 +44,19 @@ function useSortState(columns, sorting) {
44
44
  return [sortState, setSortKey];
45
45
  }
46
46
  exports.useSortState = useSortState;
47
+ // Exported for testing purposes
48
+ function deriveSortState(currentSortState, clickedKey, initialSortState) {
49
+ const [currentKey, currentDirection] = currentSortState || [];
50
+ // If the current sort state is not defined, or clicking a new column, then sort ASC on the clicked key
51
+ if (!currentSortState || clickedKey !== currentKey) {
52
+ return [clickedKey, GridTable_1.ASC];
53
+ }
54
+ // Otherwise when clicking the current column, toggle through sort states
55
+ if (currentDirection === GridTable_1.ASC) {
56
+ // if ASC -> go to desc
57
+ return [clickedKey, GridTable_1.DESC];
58
+ }
59
+ // Else, direction is already DESC, so revert to original sort value.
60
+ return initialSortState;
61
+ }
62
+ exports.deriveSortState = deriveSortState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.99.1",
3
+ "version": "2.100.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",