@planningcenter/tapestry 4.12.0 → 4.13.0-rc.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.
Files changed (134) hide show
  1. package/dist/componentRegistrars.d.ts +1 -0
  2. package/dist/componentRegistrars.d.ts.map +1 -1
  3. package/dist/componentRegistrars.js +6 -2
  4. package/dist/componentRegistrars.js.map +1 -1
  5. package/dist/components/DataTable/ActiveFilters.d.ts +23 -1
  6. package/dist/components/DataTable/ActiveFilters.d.ts.map +1 -1
  7. package/dist/components/DataTable/ActiveFilters.js +49 -4
  8. package/dist/components/DataTable/ActiveFilters.js.map +1 -1
  9. package/dist/components/DataTable/CollectionEditorsLazy.d.ts.map +1 -1
  10. package/dist/components/DataTable/CollectionEditorsLazy.js +20 -18
  11. package/dist/components/DataTable/CollectionEditorsLazy.js.map +1 -1
  12. package/dist/components/DataTable/DataTable.d.ts +66 -50
  13. package/dist/components/DataTable/DataTable.d.ts.map +1 -1
  14. package/dist/components/DataTable/DataTable.js +78 -17
  15. package/dist/components/DataTable/DataTable.js.map +1 -1
  16. package/dist/components/DataTable/ManageSavedViewsModal.d.ts +18 -7
  17. package/dist/components/DataTable/ManageSavedViewsModal.d.ts.map +1 -1
  18. package/dist/components/DataTable/ManageSavedViewsModal.js +120 -104
  19. package/dist/components/DataTable/ManageSavedViewsModal.js.map +1 -1
  20. package/dist/components/DataTable/ManageSavedViewsModal.module.css.js +1 -1
  21. package/dist/components/DataTable/NewSavedViewButton.d.ts.map +1 -1
  22. package/dist/components/DataTable/NewSavedViewButton.js +1 -1
  23. package/dist/components/DataTable/NewSavedViewButton.js.map +1 -1
  24. package/dist/components/DataTable/SavedViewActions.d.ts +19 -0
  25. package/dist/components/DataTable/SavedViewActions.d.ts.map +1 -0
  26. package/dist/components/DataTable/SavedViewActions.js +34 -0
  27. package/dist/components/DataTable/SavedViewActions.js.map +1 -0
  28. package/dist/components/DataTable/SavedViewMenu.d.ts +18 -0
  29. package/dist/components/DataTable/SavedViewMenu.d.ts.map +1 -0
  30. package/dist/components/DataTable/SavedViewMenu.js +25 -0
  31. package/dist/components/DataTable/SavedViewMenu.js.map +1 -0
  32. package/dist/components/DataTable/SavedViewsBar.d.ts +25 -13
  33. package/dist/components/DataTable/SavedViewsBar.d.ts.map +1 -1
  34. package/dist/components/DataTable/SavedViewsBar.js +43 -83
  35. package/dist/components/DataTable/SavedViewsBar.js.map +1 -1
  36. package/dist/components/DataTable/TableActions.d.ts +4 -2
  37. package/dist/components/DataTable/TableActions.d.ts.map +1 -1
  38. package/dist/components/DataTable/TableActions.js +11 -14
  39. package/dist/components/DataTable/TableActions.js.map +1 -1
  40. package/dist/components/DataTable/index.d.ts +2 -0
  41. package/dist/components/DataTable/index.d.ts.map +1 -1
  42. package/dist/components/DataTable/saveErrorMessage.d.ts +3 -0
  43. package/dist/components/DataTable/saveErrorMessage.d.ts.map +1 -0
  44. package/dist/components/DataTable/saveErrorMessage.js +9 -0
  45. package/dist/components/DataTable/saveErrorMessage.js.map +1 -0
  46. package/dist/components/DataTable/savedViews.d.ts +15 -30
  47. package/dist/components/DataTable/savedViews.d.ts.map +1 -1
  48. package/dist/components/DataTable/savedViews.js +51 -98
  49. package/dist/components/DataTable/savedViews.js.map +1 -1
  50. package/dist/components/DataTable/useActiveSavedView.d.ts +1 -1
  51. package/dist/components/DataTable/useActiveSavedView.d.ts.map +1 -1
  52. package/dist/components/DataTable/useActiveSavedView.js +5 -4
  53. package/dist/components/DataTable/useActiveSavedView.js.map +1 -1
  54. package/dist/components/DataTable/useColumnVisibility.d.ts +7 -7
  55. package/dist/components/DataTable/useColumnVisibility.d.ts.map +1 -1
  56. package/dist/components/DataTable/useColumnVisibility.js +14 -14
  57. package/dist/components/DataTable/useColumnVisibility.js.map +1 -1
  58. package/dist/components/DataTable/useResultsAnnouncement.d.ts +26 -0
  59. package/dist/components/DataTable/useResultsAnnouncement.d.ts.map +1 -0
  60. package/dist/components/DataTable/{useFilterAnnouncement.js → useResultsAnnouncement.js} +16 -12
  61. package/dist/components/DataTable/useResultsAnnouncement.js.map +1 -0
  62. package/dist/components/DataTable/useSaveStatus.d.ts +32 -0
  63. package/dist/components/DataTable/useSaveStatus.d.ts.map +1 -0
  64. package/dist/components/DataTable/useSaveStatus.js +80 -0
  65. package/dist/components/DataTable/useSaveStatus.js.map +1 -0
  66. package/dist/components/DataTable/useSavedViewNameModal.d.ts +14 -0
  67. package/dist/components/DataTable/useSavedViewNameModal.d.ts.map +1 -0
  68. package/dist/components/DataTable/useSavedViewNameModal.js +43 -0
  69. package/dist/components/DataTable/useSavedViewNameModal.js.map +1 -0
  70. package/dist/components/DataTable/useSavedViewsClient.d.ts +58 -0
  71. package/dist/components/DataTable/useSavedViewsClient.d.ts.map +1 -0
  72. package/dist/components/DataTable/useSavedViewsClient.js +87 -0
  73. package/dist/components/DataTable/useSavedViewsClient.js.map +1 -0
  74. package/dist/components/DataTable/useSavedViewsState.d.ts +35 -0
  75. package/dist/components/DataTable/useSavedViewsState.d.ts.map +1 -0
  76. package/dist/components/DataTable/useSavedViewsState.js +192 -0
  77. package/dist/components/DataTable/useSavedViewsState.js.map +1 -0
  78. package/dist/components/DataTable/useUpdateSavedViewName.d.ts +2 -3
  79. package/dist/components/DataTable/useUpdateSavedViewName.d.ts.map +1 -1
  80. package/dist/components/DataTable/useUpdateSavedViewName.js +1 -2
  81. package/dist/components/DataTable/useUpdateSavedViewName.js.map +1 -1
  82. package/dist/components/button/BaseButton.d.ts +2 -0
  83. package/dist/components/button/BaseButton.d.ts.map +1 -1
  84. package/dist/components/button/BaseButton.js +8 -17
  85. package/dist/components/button/BaseButton.js.map +1 -1
  86. package/dist/components/internal/LoadingAnnouncement.d.ts +6 -0
  87. package/dist/components/internal/LoadingAnnouncement.d.ts.map +1 -1
  88. package/dist/components/internal/LoadingAnnouncement.js +12 -3
  89. package/dist/components/internal/LoadingAnnouncement.js.map +1 -1
  90. package/dist/components/internal/index.d.ts +1 -1
  91. package/dist/components/internal/index.d.ts.map +1 -1
  92. package/dist/components/internal/tooltip/useTooltipTriggerProps.d.ts +13 -0
  93. package/dist/components/internal/tooltip/useTooltipTriggerProps.d.ts.map +1 -0
  94. package/dist/components/internal/tooltip/useTooltipTriggerProps.js +24 -0
  95. package/dist/components/internal/tooltip/useTooltipTriggerProps.js.map +1 -0
  96. package/dist/components/link/BaseLink.d.ts.map +1 -1
  97. package/dist/components/link/BaseLink.js +3 -13
  98. package/dist/components/link/BaseLink.js.map +1 -1
  99. package/dist/components/segmented-control/IconSegment.d.ts +30 -8
  100. package/dist/components/segmented-control/IconSegment.d.ts.map +1 -1
  101. package/dist/components/segmented-control/IconSegment.js +10 -3
  102. package/dist/components/segmented-control/IconSegment.js.map +1 -1
  103. package/dist/components/segmented-control/SegmentBase.d.ts.map +1 -1
  104. package/dist/components/segmented-control/SegmentBase.js +4 -2
  105. package/dist/components/segmented-control/SegmentBase.js.map +1 -1
  106. package/dist/components/segmented-control/SegmentedControl.d.ts.map +1 -1
  107. package/dist/components/segmented-control/SegmentedControl.js +10 -3
  108. package/dist/components/segmented-control/SegmentedControl.js.map +1 -1
  109. package/dist/index.css +20 -5
  110. package/dist/index.css.map +1 -1
  111. package/dist/reactRender.css +1409 -1319
  112. package/dist/reactRender.css.map +1 -1
  113. package/dist/reactRender.js +1 -1
  114. package/dist/reactRenderLegacy.css +1409 -1319
  115. package/dist/reactRenderLegacy.css.map +1 -1
  116. package/dist/reactRenderLegacy.js +1 -1
  117. package/dist/tapestryMount.css +1409 -1319
  118. package/dist/tapestryMount.css.map +1 -1
  119. package/dist/tapestryMount.js +1 -1
  120. package/dist/tapestryMountLegacy.css +1409 -1319
  121. package/dist/tapestryMountLegacy.css.map +1 -1
  122. package/dist/tapestryMountLegacy.js +1 -1
  123. package/dist/tokens-dark.css +2 -2
  124. package/dist/tokens-dark.css.map +1 -1
  125. package/dist/tokens.css +3 -3
  126. package/dist/tokens.css.map +1 -1
  127. package/dist/unstable.css +385 -295
  128. package/dist/unstable.css.map +1 -1
  129. package/dist/unstable.js +1 -0
  130. package/dist/unstable.js.map +1 -1
  131. package/package.json +6 -7
  132. package/dist/components/DataTable/useFilterAnnouncement.d.ts +0 -24
  133. package/dist/components/DataTable/useFilterAnnouncement.d.ts.map +0 -1
  134. package/dist/components/DataTable/useFilterAnnouncement.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import type { Sort } from "./DataTable";
2
2
  import type { ActiveFilter } from "./FilterTypes";
3
3
  export interface SavedView {
4
+ /** A search (`q`) filter is ignored; applying a view keeps the search. */
4
5
  activeFilters: ActiveFilter[];
5
6
  /** Omit to leave the current column visibility unchanged when applied. */
6
7
  hiddenColumnNames?: string[];
@@ -9,11 +10,6 @@ export interface SavedView {
9
10
  name: string;
10
11
  /** Omit to leave the current sort unchanged when applied. */
11
12
  sort?: Sort;
12
- /**
13
- * The view's position, as a fractional index. Views without one are listed
14
- * after those with one.
15
- */
16
- sortKey?: string;
17
13
  }
18
14
  /** An app-provided view. Read-only. */
19
15
  export type StaticView = SavedView & {
@@ -25,36 +21,25 @@ export type StaticView = SavedView & {
25
21
  */
26
22
  export declare const initialView: StaticView;
27
23
  /**
28
- * Views by `sortKey`, then views without one, each in array order. Compares
29
- * by code unit rather than locale, since keys mix upper and lower case.
24
+ * A view from the app, without any search. Search isn't part of a view, so
25
+ * views enter the table without it. A view without one is returned as is.
30
26
  */
31
- export declare function sortSavedViews(savedViews: SavedView[]): SavedView[];
27
+ export declare function withoutSearch<View extends SavedView>(view: View): View;
32
28
  /**
33
- * A `sortKey` after every view's, for a view added at the end. Undefined
34
- * while any view has no key, since those are listed last, so a view added
35
- * without one stays at the end.
29
+ * A duplicate of a view under a new name. It's a new view, so it doesn't keep
30
+ * the original's id.
36
31
  */
37
- export declare function sortKeyAfter(savedViews: SavedView[]): string | undefined;
32
+ export declare function savedViewDuplicate({ id: _id, ...view }: SavedView, name: string): Omit<SavedView, "id">;
38
33
  /**
39
- * The views to update so the view at `from` is dropped before or after the
40
- * view at `target`, both positions in the order. Usually just the dropped
41
- * view, with a key between its new neighbors'. Dropping among views without
42
- * a key also gives a key to those before it, since they come after every
43
- * view with one.
34
+ * Whether a view can be renamed, deleted, or saved over. Static views can't,
35
+ * and neither can a view that isn't saved yet, since updates are matched by id.
44
36
  */
45
- export declare function savedViewUpdatesForReorder(savedViews: SavedView[], from: number, target: number, dropPosition: "after" | "before"): SavedView[];
46
- interface SavedViewState {
47
- activeFilters: ActiveFilter[];
48
- hiddenColumnNames: string[];
49
- sort: Sort | undefined;
50
- }
37
+ export declare function isSavedViewEditable(view: SavedView, savedViews: SavedView[]): boolean;
51
38
  /**
52
- * The most specific view matching the current state, or undefined. Only
53
- * picks the initially selected view. Filters match regardless of order,
54
- * ignoring display-only labels. A view's `sort` and `hiddenColumnNames` are
55
- * only compared when the view sets them, so a view that sets more of them is
56
- * more specific. Among equally specific matches, the first wins.
39
+ * Whether the table's filters, sort, or hidden columns differ from a view.
40
+ * Filters match by name, operator, scope, and value, in any order; labels are
41
+ * display-only. Sort and hidden columns count only when both the view and the
42
+ * table have them.
57
43
  */
58
- export declare function findActiveSavedView(savedViews: SavedView[], state: SavedViewState): SavedView | undefined;
59
- export {};
44
+ export declare function isSavedViewModified(view: SavedView, { activeFilters, hiddenColumnNames, sort }: Omit<SavedView, "id" | "name">): boolean;
60
45
  //# sourceMappingURL=savedViews.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"savedViews.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/savedViews.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,6DAA6D;IAC7D,IAAI,CAAC,EAAE,IAAI,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAA;AAE5D;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,UAIzB,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAOnE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS,CAOxE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,SAAS,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GAAG,QAAQ,GAC/B,SAAS,EAAE,CAmBb;AAED,UAAU,cAAc;IACtB,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,EAAE,EACvB,KAAK,EAAE,cAAc,GACpB,SAAS,GAAG,SAAS,CAYvB"}
1
+ {"version":3,"file":"savedViews.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/savedViews.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGjD,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,6DAA6D;IAC7D,IAAI,CAAC,EAAE,IAAI,CAAA;CACZ;AAED,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAA;AAE5D;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,UAIzB,CAAA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,SAAS,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAQtE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAEvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAE3E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,EAAE,aAAa,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC,WAoB3E"}
@@ -1,4 +1,4 @@
1
- import { generateNKeysBetween, generateKeyBetween } from 'fractional-indexing';
1
+ import { isSearchFilter } from './useSearchFilter.js';
2
2
 
3
3
  /**
4
4
  * Always listed, so there's a view to fall back to. Added before the static
@@ -10,121 +10,74 @@ const initialView = {
10
10
  name: "All",
11
11
  };
12
12
  /**
13
- * Views by `sortKey`, then views without one, each in array order. Compares
14
- * by code unit rather than locale, since keys mix upper and lower case.
13
+ * A view from the app, without any search. Search isn't part of a view, so
14
+ * views enter the table without it. A view without one is returned as is.
15
15
  */
16
- function sortSavedViews(savedViews) {
17
- return [...savedViews].sort((a, b) => {
18
- if (a.sortKey === b.sortKey)
19
- return 0;
20
- if (a.sortKey === undefined)
21
- return 1;
22
- if (b.sortKey === undefined)
23
- return -1;
24
- return a.sortKey < b.sortKey ? -1 : 1;
25
- });
16
+ function withoutSearch(view) {
17
+ if (!view.activeFilters.some(isSearchFilter))
18
+ return view;
19
+ return {
20
+ ...view,
21
+ activeFilters: view.activeFilters.filter((filter) => !isSearchFilter(filter)),
22
+ };
26
23
  }
27
24
  /**
28
- * A `sortKey` after every view's, for a view added at the end. Undefined
29
- * while any view has no key, since those are listed last, so a view added
30
- * without one stays at the end.
25
+ * A duplicate of a view under a new name. It's a new view, so it doesn't keep
26
+ * the original's id.
31
27
  */
32
- function sortKeyAfter(savedViews) {
33
- let lastSortKey = null;
34
- for (const { sortKey } of savedViews) {
35
- if (sortKey === undefined)
36
- return undefined;
37
- if (lastSortKey === null || sortKey > lastSortKey)
38
- lastSortKey = sortKey;
39
- }
40
- return generateKeyBetween(lastSortKey, null);
28
+ function savedViewDuplicate({ id: _id, ...view }, name) {
29
+ return { ...view, name };
41
30
  }
42
31
  /**
43
- * The views to update so the view at `from` is dropped before or after the
44
- * view at `target`, both positions in the order. Usually just the dropped
45
- * view, with a key between its new neighbors'. Dropping among views without
46
- * a key also gives a key to those before it, since they come after every
47
- * view with one.
32
+ * Whether a view can be renamed, deleted, or saved over. Static views can't,
33
+ * and neither can a view that isn't saved yet, since updates are matched by id.
48
34
  */
49
- function savedViewUpdatesForReorder(savedViews, from, target, dropPosition) {
50
- let to = dropPosition === "after" ? target + 1 : target;
51
- // Taking the view out shifts the views after it up one.
52
- if (from < to)
53
- to--;
54
- if (from === to)
55
- return [];
56
- const newOrder = [...savedViews];
57
- const [view] = newOrder.splice(from, 1);
58
- newOrder.splice(to, 0, view);
59
- let start = to;
60
- while (start > 0 && newOrder[start - 1].sortKey === undefined)
61
- start--;
62
- const keys = generateNKeysBetween(newOrder[start - 1]?.sortKey ?? null, newOrder[to + 1]?.sortKey ?? null, to - start + 1);
63
- return newOrder
64
- .slice(start, to + 1)
65
- .map((view, index) => ({ ...view, sortKey: keys[index] }));
35
+ function isSavedViewEditable(view, savedViews) {
36
+ return view.id !== undefined && savedViews.includes(view);
66
37
  }
67
38
  /**
68
- * The most specific view matching the current state, or undefined. Only
69
- * picks the initially selected view. Filters match regardless of order,
70
- * ignoring display-only labels. A view's `sort` and `hiddenColumnNames` are
71
- * only compared when the view sets them, so a view that sets more of them is
72
- * more specific. Among equally specific matches, the first wins.
39
+ * Whether the table's filters, sort, or hidden columns differ from a view.
40
+ * Filters match by name, operator, scope, and value, in any order; labels are
41
+ * display-only. Sort and hidden columns count only when both the view and the
42
+ * table have them.
73
43
  */
74
- function findActiveSavedView(savedViews, state) {
75
- let mostSpecificMatch;
76
- for (const view of savedViews) {
77
- if (!savedViewMatches(view, state))
78
- continue;
79
- if (!mostSpecificMatch ||
80
- specificity(view) > specificity(mostSpecificMatch)) {
81
- mostSpecificMatch = view;
82
- }
83
- }
84
- return mostSpecificMatch;
85
- }
86
- function specificity(view) {
87
- return Number(Boolean(view.sort)) + Number(Boolean(view.hiddenColumnNames));
88
- }
89
- function savedViewMatches(view, state) {
90
- if (!filtersMatch(view.activeFilters, state.activeFilters))
91
- return false;
92
- if (view.sort && !sortMatches(view.sort, state.sort))
93
- return false;
94
- return (!view.hiddenColumnNames ||
95
- namesMatch(view.hiddenColumnNames, state.hiddenColumnNames));
96
- }
97
- function filtersMatch(a, b) {
98
- if (a.length !== b.length)
99
- return false;
100
- const unmatched = [...b];
101
- return a.every((filter) => {
102
- const index = unmatched.findIndex((other) => filterMatches(filter, other));
103
- if (index === -1)
104
- return false;
105
- unmatched.splice(index, 1);
44
+ function isSavedViewModified(view, { activeFilters, hiddenColumnNames, sort }) {
45
+ if (view.sort && sort && !isSameSort(view.sort, sort))
46
+ return true;
47
+ if (view.hiddenColumnNames &&
48
+ hiddenColumnNames &&
49
+ !isSameNames(view.hiddenColumnNames, hiddenColumnNames))
106
50
  return true;
107
- });
51
+ // Each saved filter matches one current filter, so repeats count.
52
+ const unmatched = [...view.activeFilters];
53
+ return (activeFilters.length !== unmatched.length ||
54
+ !activeFilters.every((filter) => {
55
+ const index = unmatched.findIndex((saved) => isSameFilter(saved, filter));
56
+ if (index === -1)
57
+ return false;
58
+ unmatched.splice(index, 1);
59
+ return true;
60
+ }));
108
61
  }
109
- function filterMatches(a, b) {
62
+ function isSameFilter(a, b) {
110
63
  return (a.name === b.name &&
111
64
  a.operator === b.operator &&
112
65
  a.scope === b.scope &&
113
- valueMatches(a.value, b.value));
66
+ isSameValue(a.value, b.value));
114
67
  }
115
- function valueMatches(a, b) {
116
- if (Array.isArray(a) && Array.isArray(b)) {
117
- return a.length === b.length && a.every((item, i) => item === b[i]);
118
- }
119
- return a === b;
68
+ // Filter values are primitives or flat arrays, such as ranges. Arrays match
69
+ // only in the same order.
70
+ function isSameValue(a, b) {
71
+ if (!Array.isArray(a) || !Array.isArray(b))
72
+ return a === b;
73
+ return a.length === b.length && a.every((item, index) => item === b[index]);
120
74
  }
121
- function sortMatches(a, b) {
122
- return a.column === b?.column && a.direction === b?.direction;
75
+ function isSameSort(a, b) {
76
+ return a.column === b.column && a.direction === b.direction;
123
77
  }
124
- function namesMatch(a, b) {
125
- const names = new Set(b);
126
- return new Set(a).size === names.size && a.every((name) => names.has(name));
78
+ function isSameNames(a, b) {
79
+ return a.length === b.length && a.every((name) => b.includes(name));
127
80
  }
128
81
 
129
- export { findActiveSavedView, initialView, savedViewUpdatesForReorder, sortKeyAfter, sortSavedViews };
82
+ export { initialView, isSavedViewEditable, isSavedViewModified, savedViewDuplicate, withoutSearch };
130
83
  //# sourceMappingURL=savedViews.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"savedViews.js","sources":["../../../src/components/DataTable/savedViews.ts"],"sourcesContent":["import { generateKeyBetween, generateNKeysBetween } from \"fractional-indexing\"\n\nimport type { Sort } from \"./DataTable\"\nimport type { ActiveFilter } from \"./FilterTypes\"\n\nexport interface SavedView {\n activeFilters: ActiveFilter[]\n /** Omit to leave the current column visibility unchanged when applied. */\n hiddenColumnNames?: string[]\n /** Assigned by the consumer once the view is saved. */\n id?: number | string\n name: string\n /** Omit to leave the current sort unchanged when applied. */\n sort?: Sort\n /**\n * The view's position, as a fractional index. Views without one are listed\n * after those with one.\n */\n sortKey?: string\n}\n\n/** An app-provided view. Read-only. */\nexport type StaticView = SavedView & { id: number | string }\n\n/**\n * Always listed, so there's a view to fall back to. Added before the static\n * views unless one of them uses its id, which replaces it.\n */\nexport const initialView: StaticView = {\n activeFilters: [],\n id: \"initial\",\n name: \"All\",\n}\n\n/**\n * Views by `sortKey`, then views without one, each in array order. Compares\n * by code unit rather than locale, since keys mix upper and lower case.\n */\nexport function sortSavedViews(savedViews: SavedView[]): SavedView[] {\n return [...savedViews].sort((a, b) => {\n if (a.sortKey === b.sortKey) return 0\n if (a.sortKey === undefined) return 1\n if (b.sortKey === undefined) return -1\n return a.sortKey < b.sortKey ? -1 : 1\n })\n}\n\n/**\n * A `sortKey` after every view's, for a view added at the end. Undefined\n * while any view has no key, since those are listed last, so a view added\n * without one stays at the end.\n */\nexport function sortKeyAfter(savedViews: SavedView[]): string | undefined {\n let lastSortKey: string | null = null\n for (const { sortKey } of savedViews) {\n if (sortKey === undefined) return undefined\n if (lastSortKey === null || sortKey > lastSortKey) lastSortKey = sortKey\n }\n return generateKeyBetween(lastSortKey, null)\n}\n\n/**\n * The views to update so the view at `from` is dropped before or after the\n * view at `target`, both positions in the order. Usually just the dropped\n * view, with a key between its new neighbors'. Dropping among views without\n * a key also gives a key to those before it, since they come after every\n * view with one.\n */\nexport function savedViewUpdatesForReorder(\n savedViews: SavedView[],\n from: number,\n target: number,\n dropPosition: \"after\" | \"before\"\n): SavedView[] {\n let to = dropPosition === \"after\" ? target + 1 : target\n // Taking the view out shifts the views after it up one.\n if (from < to) to--\n if (from === to) return []\n const newOrder = [...savedViews]\n const [view] = newOrder.splice(from, 1)\n newOrder.splice(to, 0, view)\n\n let start = to\n while (start > 0 && newOrder[start - 1].sortKey === undefined) start--\n const keys = generateNKeysBetween(\n newOrder[start - 1]?.sortKey ?? null,\n newOrder[to + 1]?.sortKey ?? null,\n to - start + 1\n )\n return newOrder\n .slice(start, to + 1)\n .map((view, index) => ({ ...view, sortKey: keys[index] }))\n}\n\ninterface SavedViewState {\n activeFilters: ActiveFilter[]\n hiddenColumnNames: string[]\n sort: Sort | undefined\n}\n\n/**\n * The most specific view matching the current state, or undefined. Only\n * picks the initially selected view. Filters match regardless of order,\n * ignoring display-only labels. A view's `sort` and `hiddenColumnNames` are\n * only compared when the view sets them, so a view that sets more of them is\n * more specific. Among equally specific matches, the first wins.\n */\nexport function findActiveSavedView(\n savedViews: SavedView[],\n state: SavedViewState\n): SavedView | undefined {\n let mostSpecificMatch: SavedView | undefined\n for (const view of savedViews) {\n if (!savedViewMatches(view, state)) continue\n if (\n !mostSpecificMatch ||\n specificity(view) > specificity(mostSpecificMatch)\n ) {\n mostSpecificMatch = view\n }\n }\n return mostSpecificMatch\n}\n\nfunction specificity(view: SavedView): number {\n return Number(Boolean(view.sort)) + Number(Boolean(view.hiddenColumnNames))\n}\n\nfunction savedViewMatches(view: SavedView, state: SavedViewState): boolean {\n if (!filtersMatch(view.activeFilters, state.activeFilters)) return false\n if (view.sort && !sortMatches(view.sort, state.sort)) return false\n return (\n !view.hiddenColumnNames ||\n namesMatch(view.hiddenColumnNames, state.hiddenColumnNames)\n )\n}\n\nfunction filtersMatch(a: ActiveFilter[], b: ActiveFilter[]): boolean {\n if (a.length !== b.length) return false\n const unmatched = [...b]\n return a.every((filter) => {\n const index = unmatched.findIndex((other) => filterMatches(filter, other))\n if (index === -1) return false\n unmatched.splice(index, 1)\n return true\n })\n}\n\nfunction filterMatches(a: ActiveFilter, b: ActiveFilter): boolean {\n return (\n a.name === b.name &&\n a.operator === b.operator &&\n a.scope === b.scope &&\n valueMatches(a.value, b.value)\n )\n}\n\nfunction valueMatches(a: unknown, b: unknown): boolean {\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => item === b[i])\n }\n return a === b\n}\n\nfunction sortMatches(a: Sort, b: Sort | undefined): boolean {\n return a.column === b?.column && a.direction === b?.direction\n}\n\nfunction namesMatch(a: string[], b: string[]): boolean {\n const names = new Set(b)\n return new Set(a).size === names.size && a.every((name) => names.has(name))\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"savedViews.js","sources":["../../../src/components/DataTable/savedViews.ts"],"sourcesContent":["import type { Sort } from \"./DataTable\"\nimport type { ActiveFilter } from \"./FilterTypes\"\nimport { isSearchFilter } from \"./useSearchFilter\"\n\nexport interface SavedView {\n /** A search (`q`) filter is ignored; applying a view keeps the search. */\n activeFilters: ActiveFilter[]\n /** Omit to leave the current column visibility unchanged when applied. */\n hiddenColumnNames?: string[]\n /** Assigned by the consumer once the view is saved. */\n id?: number | string\n name: string\n /** Omit to leave the current sort unchanged when applied. */\n sort?: Sort\n}\n\n/** An app-provided view. Read-only. */\nexport type StaticView = SavedView & { id: number | string }\n\n/**\n * Always listed, so there's a view to fall back to. Added before the static\n * views unless one of them uses its id, which replaces it.\n */\nexport const initialView: StaticView = {\n activeFilters: [],\n id: \"initial\",\n name: \"All\",\n}\n\n/**\n * A view from the app, without any search. Search isn't part of a view, so\n * views enter the table without it. A view without one is returned as is.\n */\nexport function withoutSearch<View extends SavedView>(view: View): View {\n if (!view.activeFilters.some(isSearchFilter)) return view\n return {\n ...view,\n activeFilters: view.activeFilters.filter(\n (filter) => !isSearchFilter(filter)\n ),\n }\n}\n\n/**\n * A duplicate of a view under a new name. It's a new view, so it doesn't keep\n * the original's id.\n */\nexport function savedViewDuplicate(\n { id: _id, ...view }: SavedView,\n name: string\n): Omit<SavedView, \"id\"> {\n return { ...view, name }\n}\n\n/**\n * Whether a view can be renamed, deleted, or saved over. Static views can't,\n * and neither can a view that isn't saved yet, since updates are matched by id.\n */\nexport function isSavedViewEditable(view: SavedView, savedViews: SavedView[]) {\n return view.id !== undefined && savedViews.includes(view)\n}\n\n/**\n * Whether the table's filters, sort, or hidden columns differ from a view.\n * Filters match by name, operator, scope, and value, in any order; labels are\n * display-only. Sort and hidden columns count only when both the view and the\n * table have them.\n */\nexport function isSavedViewModified(\n view: SavedView,\n { activeFilters, hiddenColumnNames, sort }: Omit<SavedView, \"id\" | \"name\">\n) {\n if (view.sort && sort && !isSameSort(view.sort, sort)) return true\n if (\n view.hiddenColumnNames &&\n hiddenColumnNames &&\n !isSameNames(view.hiddenColumnNames, hiddenColumnNames)\n )\n return true\n // Each saved filter matches one current filter, so repeats count.\n const unmatched = [...view.activeFilters]\n return (\n activeFilters.length !== unmatched.length ||\n !activeFilters.every((filter) => {\n const index = unmatched.findIndex((saved) => isSameFilter(saved, filter))\n if (index === -1) return false\n unmatched.splice(index, 1)\n return true\n })\n )\n}\n\nfunction isSameFilter(a: ActiveFilter, b: ActiveFilter) {\n return (\n a.name === b.name &&\n a.operator === b.operator &&\n a.scope === b.scope &&\n isSameValue(a.value, b.value)\n )\n}\n\n// Filter values are primitives or flat arrays, such as ranges. Arrays match\n// only in the same order.\nfunction isSameValue(a: unknown, b: unknown) {\n if (!Array.isArray(a) || !Array.isArray(b)) return a === b\n return a.length === b.length && a.every((item, index) => item === b[index])\n}\n\nfunction isSameSort(a: Sort, b: Sort) {\n return a.column === b.column && a.direction === b.direction\n}\n\nfunction isSameNames(a: string[], b: string[]) {\n return a.length === b.length && a.every((name) => b.includes(name))\n}\n"],"names":[],"mappings":"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"}
@@ -2,6 +2,6 @@ import type { SavedView } from "./savedViews";
2
2
  export declare function useActiveSavedView(savedViews: SavedView[], getInitialView: () => SavedView | undefined): {
3
3
  activeSavedView: SavedView | undefined;
4
4
  select: (view: SavedView) => void;
5
- selectWhenSaved: (savedView: Promise<SavedView> | SavedView) => Promise<void>;
5
+ selectWhenSaved: (savedView: Promise<SavedView | undefined> | SavedView) => Promise<SavedView | undefined>;
6
6
  };
7
7
  //# sourceMappingURL=useActiveSavedView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useActiveSavedView.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/useActiveSavedView.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,EAAE,EACvB,cAAc,EAAE,MAAM,SAAS,GAAG,SAAS;;mBAY1B,SAAS;iCAIW,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS;EAQpE"}
1
+ {"version":3,"file":"useActiveSavedView.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/useActiveSavedView.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,EAAE,EACvB,cAAc,EAAE,MAAM,SAAS,GAAG,SAAS;;mBAY1B,SAAS;iCAMX,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS;EAW1D"}
@@ -13,14 +13,15 @@ function useActiveSavedView(savedViews, getInitialView) {
13
13
  selectionCount.current++;
14
14
  setActiveId(view.id);
15
15
  },
16
+ // Resolves to the view it selected, if any.
16
17
  selectWhenSaved: async (savedView) => {
17
18
  const selection = ++selectionCount.current;
18
19
  // Tolerates a consumer that returns nothing, leaving the selection as is.
19
- const id = (await savedView)?.id;
20
- if (id === undefined)
20
+ const saved = await savedView;
21
+ if (saved?.id === undefined || selection !== selectionCount.current)
21
22
  return;
22
- if (selection === selectionCount.current)
23
- setActiveId(id);
23
+ setActiveId(saved.id);
24
+ return saved;
24
25
  },
25
26
  };
26
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useActiveSavedView.js","sources":["../../../src/components/DataTable/useActiveSavedView.ts"],"sourcesContent":["import { useRef, useState } from \"react\"\n\nimport type { SavedView } from \"./savedViews\"\n\nexport function useActiveSavedView(\n savedViews: SavedView[],\n getInitialView: () => SavedView | undefined\n) {\n const [activeId, setActiveId] = useState(() => getInitialView()?.id)\n // Lets a slow save skip selecting its view if the user picked another.\n const selectionCount = useRef(0)\n\n return {\n // A view without an id isn't saved yet, so it can't be the active one.\n activeSavedView:\n activeId === undefined\n ? undefined\n : savedViews.find(({ id }) => id === activeId),\n select: (view: SavedView) => {\n selectionCount.current++\n setActiveId(view.id)\n },\n selectWhenSaved: async (savedView: Promise<SavedView> | SavedView) => {\n const selection = ++selectionCount.current\n // Tolerates a consumer that returns nothing, leaving the selection as is.\n const id = (await savedView)?.id\n if (id === undefined) return\n if (selection === selectionCount.current) setActiveId(id)\n },\n }\n}\n"],"names":[],"mappings":";;AAIM,SAAU,kBAAkB,CAChC,UAAuB,EACvB,cAA2C,EAAA;AAE3C,IAAA,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,MAAM,cAAc,EAAE,EAAE,EAAE,CAAC;;AAEpE,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IAEhC,OAAO;;QAEL,eAAe,EACb,QAAQ,KAAK;AACX,cAAE;AACF,cAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,QAAQ,CAAC;AAClD,QAAA,MAAM,EAAE,CAAC,IAAe,KAAI;YAC1B,cAAc,CAAC,OAAO,EAAE;AACxB,YAAA,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,CAAC;AACD,QAAA,eAAe,EAAE,OAAO,SAAyC,KAAI;AACnE,YAAA,MAAM,SAAS,GAAG,EAAE,cAAc,CAAC,OAAO;;YAE1C,MAAM,EAAE,GAAG,CAAC,MAAM,SAAS,GAAG,EAAE;YAChC,IAAI,EAAE,KAAK,SAAS;gBAAE;AACtB,YAAA,IAAI,SAAS,KAAK,cAAc,CAAC,OAAO;gBAAE,WAAW,CAAC,EAAE,CAAC;QAC3D,CAAC;KACF;AACH;;;;"}
1
+ {"version":3,"file":"useActiveSavedView.js","sources":["../../../src/components/DataTable/useActiveSavedView.ts"],"sourcesContent":["import { useRef, useState } from \"react\"\n\nimport type { SavedView } from \"./savedViews\"\n\nexport function useActiveSavedView(\n savedViews: SavedView[],\n getInitialView: () => SavedView | undefined\n) {\n const [activeId, setActiveId] = useState(() => getInitialView()?.id)\n // Lets a slow save skip selecting its view if the user picked another.\n const selectionCount = useRef(0)\n\n return {\n // A view without an id isn't saved yet, so it can't be the active one.\n activeSavedView:\n activeId === undefined\n ? undefined\n : savedViews.find(({ id }) => id === activeId),\n select: (view: SavedView) => {\n selectionCount.current++\n setActiveId(view.id)\n },\n // Resolves to the view it selected, if any.\n selectWhenSaved: async (\n savedView: Promise<SavedView | undefined> | SavedView\n ) => {\n const selection = ++selectionCount.current\n // Tolerates a consumer that returns nothing, leaving the selection as is.\n const saved = await savedView\n if (saved?.id === undefined || selection !== selectionCount.current)\n return\n setActiveId(saved.id)\n return saved\n },\n }\n}\n"],"names":[],"mappings":";;AAIM,SAAU,kBAAkB,CAChC,UAAuB,EACvB,cAA2C,EAAA;AAE3C,IAAA,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,MAAM,cAAc,EAAE,EAAE,EAAE,CAAC;;AAEpE,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IAEhC,OAAO;;QAEL,eAAe,EACb,QAAQ,KAAK;AACX,cAAE;AACF,cAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,QAAQ,CAAC;AAClD,QAAA,MAAM,EAAE,CAAC,IAAe,KAAI;YAC1B,cAAc,CAAC,OAAO,EAAE;AACxB,YAAA,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,CAAC;;AAED,QAAA,eAAe,EAAE,OACf,SAAqD,KACnD;AACF,YAAA,MAAM,SAAS,GAAG,EAAE,cAAc,CAAC,OAAO;;AAE1C,YAAA,MAAM,KAAK,GAAG,MAAM,SAAS;YAC7B,IAAI,KAAK,EAAE,EAAE,KAAK,SAAS,IAAI,SAAS,KAAK,cAAc,CAAC,OAAO;gBACjE;AACF,YAAA,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;AACrB,YAAA,OAAO,KAAK;QACd,CAAC;KACF;AACH;;;;"}
@@ -1,22 +1,22 @@
1
1
  import type { DataTableColumn } from "./DataTableColumn";
2
2
  /**
3
- * Resolves which columns render for a given `visibleColumnNames` value. Only
3
+ * Resolves which columns render for a given `hiddenColumnNames` value. Only
4
4
  * columns that opted in with `hideable` can be hidden; every other column is
5
- * always visible. Omitting `visibleColumnNames` leaves all columns visible,
5
+ * always visible. Omitting `hiddenColumnNames` leaves all columns visible,
6
6
  * and unknown names are ignored.
7
7
  *
8
8
  * Visibility is controlled: `onColumnVisibilityChange` reports the next value
9
9
  * but nothing is stored here, so the table renders whatever
10
- * `visibleColumnNames` says on the next render.
10
+ * `hiddenColumnNames` says on the next render.
11
11
  */
12
- export declare function useColumnVisibility({ columns, onVisibleColumnNamesChange, visibleColumnNames, }: {
12
+ export declare function useColumnVisibility({ columns, hiddenColumnNames, onHiddenColumnNamesChange, }: {
13
13
  columns: DataTableColumn[];
14
- onVisibleColumnNamesChange: ((visibleColumnNames: string[]) => void) | undefined;
15
- visibleColumnNames: string[] | undefined;
14
+ hiddenColumnNames: string[] | undefined;
15
+ onHiddenColumnNamesChange: ((hiddenColumnNames: string[]) => void) | undefined;
16
16
  }): {
17
17
  hideableColumns: DataTableColumn[];
18
18
  isColumnVisible: ({ hideable, name }: DataTableColumn) => boolean;
19
- onColumnVisibilityChange: (selectedColumns: string[]) => void;
19
+ onColumnVisibilityChange: (visibleNames: string[]) => void;
20
20
  visibleHideableColumns: string[];
21
21
  };
22
22
  //# sourceMappingURL=useColumnVisibility.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useColumnVisibility.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/useColumnVisibility.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAExD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,0BAA0B,EAC1B,kBAAkB,GACnB,EAAE;IACD,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,0BAA0B,EACtB,CAAC,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GACxC,SAAS,CAAA;IACb,kBAAkB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CACzC;;0CAiBwB,eAAe;gDAWlB,MAAM,EAAE;;EAuB7B"}
1
+ {"version":3,"file":"useColumnVisibility.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/useColumnVisibility.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAExD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,iBAAiB,EACjB,yBAAyB,GAC1B,EAAE;IACD,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IACvC,yBAAyB,EAAE,CAAC,CAAC,iBAAiB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CAC/E;;0CAiBwB,eAAe;6CAWrB,MAAM,EAAE;;EAuB1B"}
@@ -1,35 +1,35 @@
1
1
  import { useMemo, useCallback } from 'react';
2
2
 
3
3
  /**
4
- * Resolves which columns render for a given `visibleColumnNames` value. Only
4
+ * Resolves which columns render for a given `hiddenColumnNames` value. Only
5
5
  * columns that opted in with `hideable` can be hidden; every other column is
6
- * always visible. Omitting `visibleColumnNames` leaves all columns visible,
6
+ * always visible. Omitting `hiddenColumnNames` leaves all columns visible,
7
7
  * and unknown names are ignored.
8
8
  *
9
9
  * Visibility is controlled: `onColumnVisibilityChange` reports the next value
10
10
  * but nothing is stored here, so the table renders whatever
11
- * `visibleColumnNames` says on the next render.
11
+ * `hiddenColumnNames` says on the next render.
12
12
  */
13
- function useColumnVisibility({ columns, onVisibleColumnNamesChange, visibleColumnNames, }) {
13
+ function useColumnVisibility({ columns, hiddenColumnNames, onHiddenColumnNamesChange, }) {
14
14
  // Everything below keys off the props themselves. `DataTable` documents
15
15
  // `columns` as a stable reference, so a consumer that honors that contract
16
16
  // gets the memoization; one that passes a fresh literal each render only
17
17
  // repeats a filter and a `Set` build, which is cheaper than the indirection
18
18
  // needed to avoid it.
19
19
  const hideableColumns = useMemo(() => columns.filter(({ hideable }) => hideable), [columns]);
20
- const visibleNames = useMemo(() => visibleColumnNames && new Set(visibleColumnNames), [visibleColumnNames]);
21
- const isColumnVisible = useCallback(({ hideable, name }) => !hideable || !visibleNames || visibleNames.has(name), [visibleNames]);
20
+ const hiddenNames = useMemo(() => new Set(hiddenColumnNames), [hiddenColumnNames]);
21
+ const isColumnVisible = useCallback(({ hideable, name }) => !hideable || !hiddenNames.has(name), [hiddenNames]);
22
22
  /**
23
- * Maps the toggle's selection back to every visible column name — including
24
- * the always-visible ones — in `columns` order, so the reported value can be
25
- * passed straight back as `visibleColumnNames`.
23
+ * Reports the hideable columns left out of `visibleNames`, in `columns`
24
+ * order, so the reported value can be passed straight back as
25
+ * `hiddenColumnNames`.
26
26
  */
27
- const onColumnVisibilityChange = useCallback((selectedColumns) => {
28
- const selectedNames = new Set(selectedColumns);
29
- onVisibleColumnNamesChange?.(columns
30
- .filter(({ hideable, name }) => !hideable || selectedNames.has(name))
27
+ const onColumnVisibilityChange = useCallback((visibleNames) => {
28
+ const visibleNameSet = new Set(visibleNames);
29
+ onHiddenColumnNamesChange?.(hideableColumns
30
+ .filter(({ name }) => !visibleNameSet.has(name))
31
31
  .map(({ name }) => name));
32
- }, [columns, onVisibleColumnNamesChange]);
32
+ }, [hideableColumns, onHiddenColumnNamesChange]);
33
33
  const visibleHideableColumns = useMemo(() => hideableColumns.filter(isColumnVisible).map(({ name }) => name), [hideableColumns, isColumnVisible]);
34
34
  return {
35
35
  hideableColumns,
@@ -1 +1 @@
1
- {"version":3,"file":"useColumnVisibility.js","sources":["../../../src/components/DataTable/useColumnVisibility.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\"\n\nimport type { DataTableColumn } from \"./DataTableColumn\"\n\n/**\n * Resolves which columns render for a given `visibleColumnNames` value. Only\n * columns that opted in with `hideable` can be hidden; every other column is\n * always visible. Omitting `visibleColumnNames` leaves all columns visible,\n * and unknown names are ignored.\n *\n * Visibility is controlled: `onColumnVisibilityChange` reports the next value\n * but nothing is stored here, so the table renders whatever\n * `visibleColumnNames` says on the next render.\n */\nexport function useColumnVisibility({\n columns,\n onVisibleColumnNamesChange,\n visibleColumnNames,\n}: {\n columns: DataTableColumn[]\n onVisibleColumnNamesChange:\n | ((visibleColumnNames: string[]) => void)\n | undefined\n visibleColumnNames: string[] | undefined\n}) {\n // Everything below keys off the props themselves. `DataTable` documents\n // `columns` as a stable reference, so a consumer that honors that contract\n // gets the memoization; one that passes a fresh literal each render only\n // repeats a filter and a `Set` build, which is cheaper than the indirection\n // needed to avoid it.\n const hideableColumns = useMemo(\n () => columns.filter(({ hideable }) => hideable),\n [columns]\n )\n\n const visibleNames = useMemo(\n () => visibleColumnNames && new Set(visibleColumnNames),\n [visibleColumnNames]\n )\n\n const isColumnVisible = useCallback(\n ({ hideable, name }: DataTableColumn) =>\n !hideable || !visibleNames || visibleNames.has(name),\n [visibleNames]\n )\n\n /**\n * Maps the toggle's selection back to every visible column name — including\n * the always-visible ones — in `columns` order, so the reported value can be\n * passed straight back as `visibleColumnNames`.\n */\n const onColumnVisibilityChange = useCallback(\n (selectedColumns: string[]) => {\n const selectedNames = new Set(selectedColumns)\n\n onVisibleColumnNamesChange?.(\n columns\n .filter(({ hideable, name }) => !hideable || selectedNames.has(name))\n .map(({ name }) => name)\n )\n },\n [columns, onVisibleColumnNamesChange]\n )\n\n const visibleHideableColumns = useMemo(\n () => hideableColumns.filter(isColumnVisible).map(({ name }) => name),\n [hideableColumns, isColumnVisible]\n )\n\n return {\n hideableColumns,\n isColumnVisible,\n onColumnVisibilityChange,\n visibleHideableColumns,\n }\n}\n"],"names":[],"mappings":";;AAIA;;;;;;;;;AASG;AACG,SAAU,mBAAmB,CAAC,EAClC,OAAO,EACP,0BAA0B,EAC1B,kBAAkB,GAOnB,EAAA;;;;;;IAMC,MAAM,eAAe,GAAG,OAAO,CAC7B,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,QAAQ,CAAC,EAChD,CAAC,OAAO,CAAC,CACV;AAED,IAAA,MAAM,YAAY,GAAG,OAAO,CAC1B,MAAM,kBAAkB,IAAI,IAAI,GAAG,CAAC,kBAAkB,CAAC,EACvD,CAAC,kBAAkB,CAAC,CACrB;AAED,IAAA,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAmB,KAClC,CAAC,QAAQ,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EACtD,CAAC,YAAY,CAAC,CACf;AAED;;;;AAIG;AACH,IAAA,MAAM,wBAAwB,GAAG,WAAW,CAC1C,CAAC,eAAyB,KAAI;AAC5B,QAAA,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC;QAE9C,0BAA0B,GACxB;AACG,aAAA,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;aACnE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,CAC3B;AACH,IAAA,CAAC,EACD,CAAC,OAAO,EAAE,0BAA0B,CAAC,CACtC;AAED,IAAA,MAAM,sBAAsB,GAAG,OAAO,CACpC,MAAM,eAAe,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EACrE,CAAC,eAAe,EAAE,eAAe,CAAC,CACnC;IAED,OAAO;QACL,eAAe;QACf,eAAe;QACf,wBAAwB;QACxB,sBAAsB;KACvB;AACH;;;;"}
1
+ {"version":3,"file":"useColumnVisibility.js","sources":["../../../src/components/DataTable/useColumnVisibility.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\"\n\nimport type { DataTableColumn } from \"./DataTableColumn\"\n\n/**\n * Resolves which columns render for a given `hiddenColumnNames` value. Only\n * columns that opted in with `hideable` can be hidden; every other column is\n * always visible. Omitting `hiddenColumnNames` leaves all columns visible,\n * and unknown names are ignored.\n *\n * Visibility is controlled: `onColumnVisibilityChange` reports the next value\n * but nothing is stored here, so the table renders whatever\n * `hiddenColumnNames` says on the next render.\n */\nexport function useColumnVisibility({\n columns,\n hiddenColumnNames,\n onHiddenColumnNamesChange,\n}: {\n columns: DataTableColumn[]\n hiddenColumnNames: string[] | undefined\n onHiddenColumnNamesChange: ((hiddenColumnNames: string[]) => void) | undefined\n}) {\n // Everything below keys off the props themselves. `DataTable` documents\n // `columns` as a stable reference, so a consumer that honors that contract\n // gets the memoization; one that passes a fresh literal each render only\n // repeats a filter and a `Set` build, which is cheaper than the indirection\n // needed to avoid it.\n const hideableColumns = useMemo(\n () => columns.filter(({ hideable }) => hideable),\n [columns]\n )\n\n const hiddenNames = useMemo(\n () => new Set(hiddenColumnNames),\n [hiddenColumnNames]\n )\n\n const isColumnVisible = useCallback(\n ({ hideable, name }: DataTableColumn) =>\n !hideable || !hiddenNames.has(name),\n [hiddenNames]\n )\n\n /**\n * Reports the hideable columns left out of `visibleNames`, in `columns`\n * order, so the reported value can be passed straight back as\n * `hiddenColumnNames`.\n */\n const onColumnVisibilityChange = useCallback(\n (visibleNames: string[]) => {\n const visibleNameSet = new Set(visibleNames)\n\n onHiddenColumnNamesChange?.(\n hideableColumns\n .filter(({ name }) => !visibleNameSet.has(name))\n .map(({ name }) => name)\n )\n },\n [hideableColumns, onHiddenColumnNamesChange]\n )\n\n const visibleHideableColumns = useMemo(\n () => hideableColumns.filter(isColumnVisible).map(({ name }) => name),\n [hideableColumns, isColumnVisible]\n )\n\n return {\n hideableColumns,\n isColumnVisible,\n onColumnVisibilityChange,\n visibleHideableColumns,\n }\n}\n"],"names":[],"mappings":";;AAIA;;;;;;;;;AASG;AACG,SAAU,mBAAmB,CAAC,EAClC,OAAO,EACP,iBAAiB,EACjB,yBAAyB,GAK1B,EAAA;;;;;;IAMC,MAAM,eAAe,GAAG,OAAO,CAC7B,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,QAAQ,CAAC,EAChD,CAAC,OAAO,CAAC,CACV;AAED,IAAA,MAAM,WAAW,GAAG,OAAO,CACzB,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC,EAChC,CAAC,iBAAiB,CAAC,CACpB;AAED,IAAA,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAmB,KAClC,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EACrC,CAAC,WAAW,CAAC,CACd;AAED;;;;AAIG;AACH,IAAA,MAAM,wBAAwB,GAAG,WAAW,CAC1C,CAAC,YAAsB,KAAI;AACzB,QAAA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC;QAE5C,yBAAyB,GACvB;AACG,aAAA,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;aAC9C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,CAC3B;AACH,IAAA,CAAC,EACD,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAC7C;AAED,IAAA,MAAM,sBAAsB,GAAG,OAAO,CACpC,MAAM,eAAe,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EACrE,CAAC,eAAe,EAAE,eAAe,CAAC,CACnC;IAED,OAAO;QACL,eAAe;QACf,eAAe;QACf,wBAAwB;QACxB,sBAAsB;KACvB;AACH;;;;"}
@@ -0,0 +1,26 @@
1
+ import type { DataTableLoadingState } from "./DataTable";
2
+ import type { ActiveFilter } from "./FilterTypes";
3
+ import { type ResultNoun } from "./ResultsCount";
4
+ export declare const RESULTS_ANNOUNCEMENT_DELAY_MS = 500;
5
+ /**
6
+ * Announces the results count to screen reader users after each filter
7
+ * change and each load, once the results settle: `loadingState` is
8
+ * `"idle"`, and neither it nor the count has changed for
9
+ * {@link RESULTS_ANNOUNCEMENT_DELAY_MS}. A load covers the first load and
10
+ * page changes too. A change to `"error"` cancels the message, because the
11
+ * error is announced on its own. An `"error"` left over from an earlier
12
+ * request only delays it.
13
+ *
14
+ * Returns an `onActiveFiltersChange` to use in place of the caller's, so
15
+ * filter changes the user makes are announced without a load. Filter changes
16
+ * from outside are announced only when they load.
17
+ */
18
+ export declare function useResultsAnnouncement({ isPageCount, loadingState, onActiveFiltersChange, resultNoun, totalCount, }: {
19
+ /** Whether `totalCount` covers only the current page, not the whole set. */
20
+ isPageCount: boolean;
21
+ loadingState: DataTableLoadingState;
22
+ onActiveFiltersChange: (activeFilters: ActiveFilter[]) => void;
23
+ resultNoun: ResultNoun | undefined;
24
+ totalCount: number;
25
+ }): (activeFilters: ActiveFilter[]) => void;
26
+ //# sourceMappingURL=useResultsAnnouncement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResultsAnnouncement.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/useResultsAnnouncement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEtE,eAAO,MAAM,6BAA6B,MAAM,CAAA;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,UAAU,EACV,UAAU,GACX,EAAE;IACD,4EAA4E;IAC5E,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,qBAAqB,CAAA;IACnC,qBAAqB,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAC9D,UAAU,EAAE,UAAU,GAAG,SAAS,CAAA;IAClC,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,IAAI,CA+B1C"}
@@ -2,19 +2,21 @@ import { useAnnounce } from '../../utilities/useAnnounce.js';
2
2
  import { useRef, useState, useEffect } from 'react';
3
3
  import { getResultsCountLabel } from './ResultsCount.js';
4
4
 
5
- const FILTER_ANNOUNCEMENT_DELAY_MS = 500;
5
+ const RESULTS_ANNOUNCEMENT_DELAY_MS = 500;
6
6
  /**
7
7
  * Announces the results count to screen reader users after each filter
8
- * change, once the results settle: `loadingState` is not `"loading"`, and
9
- * neither it nor the count has changed for
10
- * {@link FILTER_ANNOUNCEMENT_DELAY_MS}. A change to `"error"` cancels the
11
- * message, because the error is announced on its own. An `"error"` left over
12
- * from an earlier request only delays it.
8
+ * change and each load, once the results settle: `loadingState` is
9
+ * `"idle"`, and neither it nor the count has changed for
10
+ * {@link RESULTS_ANNOUNCEMENT_DELAY_MS}. A load covers the first load and
11
+ * page changes too. A change to `"error"` cancels the message, because the
12
+ * error is announced on its own. An `"error"` left over from an earlier
13
+ * request only delays it.
13
14
  *
14
- * Returns an `onActiveFiltersChange` to use in place of the caller's, so only
15
- * changes the user makes are announced.
15
+ * Returns an `onActiveFiltersChange` to use in place of the caller's, so
16
+ * filter changes the user makes are announced without a load. Filter changes
17
+ * from outside are announced only when they load.
16
18
  */
17
- function useFilterAnnouncement({ isPageCount, loadingState, onActiveFiltersChange, resultNoun, totalCount, }) {
19
+ function useResultsAnnouncement({ isPageCount, loadingState, onActiveFiltersChange, resultNoun, totalCount, }) {
18
20
  const announce = useAnnounce();
19
21
  const isDue = useRef(false);
20
22
  const previousLoadingState = useRef(loadingState);
@@ -24,6 +26,8 @@ function useFilterAnnouncement({ isPageCount, loadingState, onActiveFiltersChang
24
26
  useEffect(() => {
25
27
  const failed = loadingState === "error" && previousLoadingState.current !== "error";
26
28
  previousLoadingState.current = loadingState;
29
+ if (loadingState === "loading")
30
+ isDue.current = true;
27
31
  if (!isDue.current)
28
32
  return;
29
33
  if (failed) {
@@ -35,7 +39,7 @@ function useFilterAnnouncement({ isPageCount, loadingState, onActiveFiltersChang
35
39
  const timeout = setTimeout(() => {
36
40
  isDue.current = false;
37
41
  announce(message);
38
- }, FILTER_ANNOUNCEMENT_DELAY_MS);
42
+ }, RESULTS_ANNOUNCEMENT_DELAY_MS);
39
43
  return () => clearTimeout(timeout);
40
44
  }, [announce, changeCount, loadingState, message]);
41
45
  return (activeFilters) => {
@@ -45,5 +49,5 @@ function useFilterAnnouncement({ isPageCount, loadingState, onActiveFiltersChang
45
49
  };
46
50
  }
47
51
 
48
- export { FILTER_ANNOUNCEMENT_DELAY_MS, useFilterAnnouncement };
49
- //# sourceMappingURL=useFilterAnnouncement.js.map
52
+ export { RESULTS_ANNOUNCEMENT_DELAY_MS, useResultsAnnouncement };
53
+ //# sourceMappingURL=useResultsAnnouncement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResultsAnnouncement.js","sources":["../../../src/components/DataTable/useResultsAnnouncement.ts"],"sourcesContent":["import { useAnnounce } from \"@utilities/useAnnounce\"\nimport { useEffect, useRef, useState } from \"react\"\n\nimport type { DataTableLoadingState } from \"./DataTable\"\nimport type { ActiveFilter } from \"./FilterTypes\"\nimport { getResultsCountLabel, type ResultNoun } from \"./ResultsCount\"\n\nexport const RESULTS_ANNOUNCEMENT_DELAY_MS = 500\n\n/**\n * Announces the results count to screen reader users after each filter\n * change and each load, once the results settle: `loadingState` is\n * `\"idle\"`, and neither it nor the count has changed for\n * {@link RESULTS_ANNOUNCEMENT_DELAY_MS}. A load covers the first load and\n * page changes too. A change to `\"error\"` cancels the message, because the\n * error is announced on its own. An `\"error\"` left over from an earlier\n * request only delays it.\n *\n * Returns an `onActiveFiltersChange` to use in place of the caller's, so\n * filter changes the user makes are announced without a load. Filter changes\n * from outside are announced only when they load.\n */\nexport function useResultsAnnouncement({\n isPageCount,\n loadingState,\n onActiveFiltersChange,\n resultNoun,\n totalCount,\n}: {\n /** Whether `totalCount` covers only the current page, not the whole set. */\n isPageCount: boolean\n loadingState: DataTableLoadingState\n onActiveFiltersChange: (activeFilters: ActiveFilter[]) => void\n resultNoun: ResultNoun | undefined\n totalCount: number\n}): (activeFilters: ActiveFilter[]) => void {\n const announce = useAnnounce()\n const isDue = useRef(false)\n const previousLoadingState = useRef(loadingState)\n const [changeCount, setChangeCount] = useState(0)\n const countLabel = getResultsCountLabel(totalCount, resultNoun || true)\n const message = isPageCount ? `${countLabel} on this page` : countLabel\n\n useEffect(() => {\n const failed =\n loadingState === \"error\" && previousLoadingState.current !== \"error\"\n previousLoadingState.current = loadingState\n if (loadingState === \"loading\") isDue.current = true\n if (!isDue.current) return\n if (failed) {\n isDue.current = false\n return\n }\n if (loadingState !== \"idle\") return\n const timeout = setTimeout(() => {\n isDue.current = false\n announce(message)\n }, RESULTS_ANNOUNCEMENT_DELAY_MS)\n return () => clearTimeout(timeout)\n }, [announce, changeCount, loadingState, message])\n\n return (activeFilters) => {\n isDue.current = true\n setChangeCount((count) => count + 1)\n onActiveFiltersChange(activeFilters)\n }\n}\n"],"names":[],"mappings":";;;;AAOO,MAAM,6BAA6B,GAAG;AAE7C;;;;;;;;;;;;AAYG;AACG,SAAU,sBAAsB,CAAC,EACrC,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,UAAU,EACV,UAAU,GAQX,EAAA;AACC,IAAA,MAAM,QAAQ,GAAG,WAAW,EAAE;AAC9B,IAAA,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;AAC3B,IAAA,MAAM,oBAAoB,GAAG,MAAM,CAAC,YAAY,CAAC;IACjD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,oBAAoB,CAAC,UAAU,EAAE,UAAU,IAAI,IAAI,CAAC;AACvE,IAAA,MAAM,OAAO,GAAG,WAAW,GAAG,CAAA,EAAG,UAAU,CAAA,aAAA,CAAe,GAAG,UAAU;IAEvE,SAAS,CAAC,MAAK;QACb,MAAM,MAAM,GACV,YAAY,KAAK,OAAO,IAAI,oBAAoB,CAAC,OAAO,KAAK,OAAO;AACtE,QAAA,oBAAoB,CAAC,OAAO,GAAG,YAAY;QAC3C,IAAI,YAAY,KAAK,SAAS;AAAE,YAAA,KAAK,CAAC,OAAO,GAAG,IAAI;QACpD,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE;QACpB,IAAI,MAAM,EAAE;AACV,YAAA,KAAK,CAAC,OAAO,GAAG,KAAK;YACrB;QACF;QACA,IAAI,YAAY,KAAK,MAAM;YAAE;AAC7B,QAAA,MAAM,OAAO,GAAG,UAAU,CAAC,MAAK;AAC9B,YAAA,KAAK,CAAC,OAAO,GAAG,KAAK;YACrB,QAAQ,CAAC,OAAO,CAAC;QACnB,CAAC,EAAE,6BAA6B,CAAC;AACjC,QAAA,OAAO,MAAM,YAAY,CAAC,OAAO,CAAC;IACpC,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAElD,OAAO,CAAC,aAAa,KAAI;AACvB,QAAA,KAAK,CAAC,OAAO,GAAG,IAAI;QACpB,cAAc,CAAC,CAAC,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC;QACpC,qBAAqB,CAAC,aAAa,CAAC;AACtC,IAAA,CAAC;AACH;;;;"}