@worknice/whiteboard 0.36.0 → 0.37.1

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.
@@ -8,7 +8,7 @@ declare const meta: {
8
8
  displaySearch?: boolean;
9
9
  iconRight?: import("react").ReactNode;
10
10
  fullWidth?: boolean;
11
- } & ({
11
+ } & (({
12
12
  children: import("react").ReactNode;
13
13
  icon?: import("react").ReactNode;
14
14
  } | {
@@ -23,7 +23,7 @@ declare const meta: {
23
23
  header: string;
24
24
  options: Array<import("./MenuButton").MenuButtonOption | null>;
25
25
  }>;
26
- })) => import("react/jsx-runtime").JSX.Element;
26
+ }))) => import("react/jsx-runtime").JSX.Element;
27
27
  argTypes: {
28
28
  id: {
29
29
  control: false;
@@ -29,34 +29,37 @@ const ListBoxInput = ({ onChange, options, optionToId, optionToLabel, optionToDi
29
29
  value: searchTerm
30
30
  })
31
31
  }),
32
- 0 !== filteredOptions.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
33
- className: __WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].optionsContainer,
34
- children: filteredOptions.map((option)=>{
35
- const optionId = optionToId(option);
36
- const optionLabel = optionToLabel(option);
37
- const optionDisabled = optionToDisabled ? optionToDisabled(option) : false;
38
- const selected = value.some((value)=>optionToId(value) === optionToId(option));
39
- return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("label", {
40
- className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].option, {
41
- [__WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].disabled]: optionDisabled
42
- }),
43
- children: [
44
- /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__CheckboxInput_js_1a7df59a__["default"], {
45
- disabled: optionDisabled,
46
- value: selected,
47
- onChange: ()=>{
48
- onChange(option);
49
- if (trackingContext && id) trackingContext.trackInput(id, includeValueInTracking ? optionToId(option) : void 0);
50
- }
32
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
33
+ className: __WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].listContainer,
34
+ children: 0 !== filteredOptions.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
35
+ className: __WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].optionsContainer,
36
+ children: filteredOptions.map((option)=>{
37
+ const optionId = optionToId(option);
38
+ const optionLabel = optionToLabel(option);
39
+ const optionDisabled = optionToDisabled ? optionToDisabled(option) : false;
40
+ const selected = value.some((value)=>optionToId(value) === optionToId(option));
41
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("label", {
42
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].option, {
43
+ [__WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].disabled]: optionDisabled
51
44
  }),
52
- optionLabel
53
- ]
54
- }, optionId);
55
- })
56
- }) : /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
57
- className: __WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].noResultsContainer,
58
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("p", {
59
- children: "No options found."
45
+ children: [
46
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__CheckboxInput_js_1a7df59a__["default"], {
47
+ disabled: optionDisabled,
48
+ value: selected,
49
+ onChange: ()=>{
50
+ onChange(option);
51
+ if (trackingContext && id) trackingContext.trackInput(id, includeValueInTracking ? optionToId(option) : void 0);
52
+ }
53
+ }),
54
+ optionLabel
55
+ ]
56
+ }, optionId);
57
+ })
58
+ }) : /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
59
+ className: __WEBPACK_EXTERNAL_MODULE__ListBoxInput_module_js_52d8d802__["default"].noResultsContainer,
60
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("p", {
61
+ children: "No options found."
62
+ })
60
63
  })
61
64
  })
62
65
  ]
@@ -2,6 +2,7 @@ import "./ListBoxInput_module.css";
2
2
  const ListBoxInput_module_rslib_entry_ = {
3
3
  container: "container-RqDms0",
4
4
  searchContainer: "searchContainer-V8D3A2",
5
+ listContainer: "listContainer-a9BpgI",
5
6
  optionsContainer: "optionsContainer-q0if3P",
6
7
  noResultsContainer: "noResultsContainer-FEqb9D",
7
8
  option: "option-uViftw",
@@ -1,12 +1,22 @@
1
1
  .container-RqDms0 {
2
2
  border: 1px solid var(--color-grey-t08);
3
3
  border-radius: var(--size-n3);
4
- overflow-y: auto;
4
+ flex-direction: column;
5
+ max-height: 320px;
6
+ display: flex;
7
+ overflow: hidden;
5
8
  }
6
9
 
7
10
  .searchContainer-V8D3A2 {
8
11
  padding: var(--size-n1);
9
12
  border-bottom: 1px solid var(--color-grey-t08);
13
+ flex-shrink: 0;
14
+ }
15
+
16
+ .listContainer-a9BpgI {
17
+ flex: 1;
18
+ min-height: 0;
19
+ overflow-y: auto;
10
20
  }
11
21
 
12
22
  .optionsContainer-q0if3P {
@@ -0,0 +1,19 @@
1
+ declare const sizes: {
2
+ xsmall: number;
3
+ small: number;
4
+ default: number;
5
+ large: number;
6
+ xlarge: number;
7
+ };
8
+ type Props = {
9
+ /** Progress from 0 to 1 (e.g. 0.4 for 40%) */
10
+ percentage: number;
11
+ size?: keyof typeof sizes;
12
+ tone?: "default" | "primary" | "muted" | "danger" | "warning" | "success" | "info" | "light";
13
+ };
14
+ /**
15
+ * Renders a circular progress indicator with exact percentage.
16
+ * Uses light grey for the incomplete portion and darker grey for the completed portion.
17
+ */
18
+ declare const ProgressCircle: ({ percentage, size, tone }: Props) => import("react/jsx-runtime").JSX.Element;
19
+ export default ProgressCircle;
@@ -0,0 +1,45 @@
1
+ import * as __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__ from "react/jsx-runtime";
2
+ const sizes = {
3
+ xsmall: 12,
4
+ small: 16,
5
+ default: 20,
6
+ large: 24,
7
+ xlarge: 48
8
+ };
9
+ const ProgressCircle = ({ percentage, size = "default", tone = "muted" })=>{
10
+ const r = 7;
11
+ const cx = 8;
12
+ const cy = 10;
13
+ const p = Math.max(0, Math.min(1, percentage));
14
+ const angle = 2 * p * Math.PI;
15
+ const endX = cx + r * Math.sin(angle);
16
+ const endY = cy - r * Math.cos(angle);
17
+ const largeArc = p > 0.5 ? 1 : 0;
18
+ const pathD = p <= 0 ? "" : p >= 1 ? `M ${cx} ${cy - r} A ${r} ${r} 0 0 1 ${cx} ${cy + r} A ${r} ${r} 0 0 1 ${cx} ${cy - r} Z` : `M ${cx} ${cy - r} A ${r} ${r} 0 ${largeArc} 1 ${endX} ${endY} L ${cx} ${cy} Z`;
19
+ const sizePx = sizes[size];
20
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("svg", {
21
+ width: sizePx,
22
+ height: sizePx,
23
+ viewBox: "0 0 16 20",
24
+ fill: "none",
25
+ xmlns: "http://www.w3.org/2000/svg",
26
+ style: {
27
+ color: `var(--tone-${tone})`
28
+ },
29
+ children: [
30
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("circle", {
31
+ cx: cx,
32
+ cy: cy,
33
+ r: r,
34
+ fill: "currentColor",
35
+ fillOpacity: "0.25"
36
+ }),
37
+ pathD && /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("path", {
38
+ d: pathD,
39
+ fill: "currentColor"
40
+ })
41
+ ]
42
+ });
43
+ };
44
+ const ProgressCircle_rslib_entry_ = ProgressCircle;
45
+ export { ProgressCircle_rslib_entry_ as default };
@@ -3,16 +3,36 @@ import { type ReactNode } from "react";
3
3
  import { type ZodTypeAny } from "zod";
4
4
  import { type MenuButtonOption } from "../controls/MenuButton";
5
5
  type PrimitiveValue = boolean | string | undefined | number;
6
+ type SingleFilterOption<Type> = {
7
+ id: string;
8
+ label: string;
9
+ /**
10
+ * If omitted true will always be returned and no filtering will be applied (e.g. "Any" option).
11
+ */
12
+ predicate?: (row: Type) => boolean;
13
+ };
14
+ type MultiFilterOption<Type> = {
15
+ id: string;
16
+ label: string;
17
+ predicate: (row: Type) => boolean;
18
+ };
19
+ /**
20
+ * When using single select filters (isMulti: false), the first option is the default value.
21
+ * Multi select filters (isMulti: true) have no default value.
22
+ */
6
23
  type Filter<Type> = {
24
+ isMulti?: false;
7
25
  label: string;
8
- options: Array<{
9
- id: string;
10
- label: string;
11
- /**
12
- * If omitted no filtering will be applied.
13
- */
14
- predicate?: (row: Type) => boolean;
15
- }>;
26
+ options: Array<SingleFilterOption<Type>>;
27
+ } | {
28
+ isMulti: true;
29
+ label: string;
30
+ options: Array<MultiFilterOption<Type>>;
31
+ /**
32
+ * Use ListBox instead of checkboxes.
33
+ * For when the list of options is large
34
+ * */
35
+ useListBox?: boolean;
16
36
  };
17
37
  type Grouping<Type> = {
18
38
  fn: (row: Type) => string;
@@ -9,7 +9,11 @@ import * as __WEBPACK_EXTERNAL_MODULE_utf8__ from "utf8";
9
9
  import * as __WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__ from "../controls/Button.js";
10
10
  import * as __WEBPACK_EXTERNAL_MODULE__controls_Disclosure_js_6e4cc59c__ from "../controls/Disclosure.js";
11
11
  import * as __WEBPACK_EXTERNAL_MODULE__controls_MenuButton_js_b23cdd05__ from "../controls/MenuButton.js";
12
+ import * as __WEBPACK_EXTERNAL_MODULE__fields_CheckboxSetField_js_bce910a4__ from "../fields/CheckboxSetField.js";
13
+ import * as __WEBPACK_EXTERNAL_MODULE__fields_ListBoxField_js_b7d4cb12__ from "../fields/ListBoxField.js";
14
+ import * as __WEBPACK_EXTERNAL_MODULE__fields_SimpleRadioSetField_js_118e9421__ from "../fields/SimpleRadioSetField.js";
12
15
  import * as __WEBPACK_EXTERNAL_MODULE__forms_FormButtonSet_js_c160eb1a__ from "../forms/FormButtonSet.js";
16
+ import * as __WEBPACK_EXTERNAL_MODULE__forms_useForm_js_e756b4e7__ from "../forms/useForm.js";
13
17
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_CheckboxInput_js_3a83f64b__ from "../inputs/CheckboxInput.js";
14
18
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_SelectInput_js_d1a6f32e__ from "../inputs/SelectInput.js";
15
19
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_StringInput_js_091f1e06__ from "../inputs/StringInput.js";
@@ -52,8 +56,18 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
52
56
  enableGlobalFilter: false !== globalFiltering,
53
57
  enableSorting,
54
58
  filterFn: filter ? (row, _, filterValue)=>{
55
- const option = filter.options.find((option)=>option.id === filterValue);
56
- return option?.predicate?.(row.original) ?? true;
59
+ if (filter.isMulti && Array.isArray(filterValue)) {
60
+ if (0 === filterValue.length) return true;
61
+ return filterValue.some((optionId)=>{
62
+ const option = filter.options.find((opt)=>opt.id === optionId);
63
+ return option ? option.predicate(row.original) : false;
64
+ });
65
+ }
66
+ if (!filter.isMulti && "string" == typeof filterValue) {
67
+ const option = filter.options.find((opt)=>opt.id === filterValue);
68
+ return option?.predicate?.(row.original) ?? true;
69
+ }
70
+ return true;
57
71
  } : void 0,
58
72
  getGroupingValue: grouping ? (row)=>grouping.fn(row) : void 0,
59
73
  header,
@@ -135,11 +149,24 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
135
149
  const filterableColumns = columns.filter((column)=>void 0 !== column.filter);
136
150
  const groupableColumns = columns.filter((column)=>void 0 !== column.grouping);
137
151
  const csvExportableColumns = columns.filter((column)=>true === column.csvExport);
138
- const columnFiltersInitialState = filterableColumns.map((col)=>({
139
- id: col.id,
140
- value: col.filter.options[0].id
141
- }));
142
- const localStorageData = "undefined" != typeof window && pathName && id ? window.localStorage.getItem(`${pathName}#${id}`) : null;
152
+ const columnFiltersInitialState = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>filterableColumns.map((col)=>{
153
+ const filter = col.filter;
154
+ if (filter.isMulti) return {
155
+ id: col.id,
156
+ value: []
157
+ };
158
+ return {
159
+ id: col.id,
160
+ value: filter.options[0].id
161
+ };
162
+ }), [
163
+ filterableColumns
164
+ ]);
165
+ const storageKey = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>pathName && id ? `${pathName}#${id}_table` : null, [
166
+ pathName,
167
+ id
168
+ ]);
169
+ const localStorageData = "undefined" != typeof window && storageKey ? window.localStorage.getItem(storageKey) : null;
143
170
  let parsedLocalStorage = {};
144
171
  if (localStorageData && localStorageSchema) try {
145
172
  parsedLocalStorage = JSON.parse(localStorageData);
@@ -228,12 +255,25 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
228
255
  a.click();
229
256
  };
230
257
  const throttledSetGlobalFilter = (0, __WEBPACK_EXTERNAL_MODULE__react_hook_throttle_d66151d4__.useThrottleCallback)((value)=>table.setGlobalFilter(value), 4);
231
- const showSearchReset = columnFiltersInitialState.some((filter, index)=>filter.value !== table.getState().columnFilters[index].value) || table.initialState.globalFilter !== table.getState().globalFilter;
258
+ const activeFilterCount = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>{
259
+ const currentFilters = tableState.columnFilters;
260
+ return columnFiltersInitialState.filter((initialFilter)=>{
261
+ const currentFilter = currentFilters.find((f)=>f.id === initialFilter.id);
262
+ const currentValue = currentFilter?.value;
263
+ const initialValue = initialFilter.value;
264
+ if (Array.isArray(initialValue) && Array.isArray(currentValue)) return currentValue.length !== initialValue.length || initialValue.some((v, i)=>v !== currentValue[i]);
265
+ return currentValue !== initialValue;
266
+ }).length;
267
+ }, [
268
+ tableState.columnFilters,
269
+ columnFiltersInitialState
270
+ ]);
271
+ const showSearchReset = activeFilterCount > 0 || "" !== tableState.globalFilter;
232
272
  const filteredSelectedRows = table.getFilteredSelectedRowModel();
233
273
  const validPrimaryBulkActions = bulkActions.filter((bulkAction)=>filteredSelectedRows.rows.some((row)=>bulkAction.predicate ? bulkAction.predicate(row.original) : true));
234
274
  const validSecondaryBulkActions = secondaryBulkActions.filter((bulkAction)=>filteredSelectedRows.rows.some((row)=>bulkAction.predicate ? bulkAction.predicate(row.original) : true));
235
275
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
236
- if (pathName && id) window.localStorage.setItem(`${pathName}#${id}`, JSON.stringify({
276
+ if (storageKey) window.localStorage.setItem(storageKey, JSON.stringify({
237
277
  columnFilters: tableState.columnFilters,
238
278
  sorting: tableState.sorting,
239
279
  grouping: tableState.grouping
@@ -242,8 +282,7 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
242
282
  tableState.columnFilters,
243
283
  tableState.sorting,
244
284
  tableState.grouping,
245
- pathName,
246
- id
285
+ storageKey
247
286
  ]);
248
287
  const shouldShowCsvExport = csvExportableColumns.length > 0 && csvFilename;
249
288
  const shouldShowMoreActions = validSecondaryBulkActions.length > 0 || shouldShowCsvExport;
@@ -372,13 +411,16 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
372
411
  0 !== filterableColumns.length || 0 !== groupableColumns.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("div", {
373
412
  className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterSelectMenus,
374
413
  children: [
375
- /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__["default"], {
414
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)(__WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__["default"], {
376
415
  type: "secondary",
377
416
  onClick: ()=>setIsFilterModalOpen(true),
378
417
  icon: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Icon_js_0d271bb6__["default"], {
379
418
  symbol: "Filter"
380
419
  }),
381
- children: "Filters (1)"
420
+ children: [
421
+ "Filters",
422
+ activeFilterCount > 0 ? ` (${activeFilterCount})` : ""
423
+ ]
382
424
  }),
383
425
  0 !== groupableColumns.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__inputs_SelectInput_js_d1a6f32e__["default"], {
384
426
  id: "columnGroupings",
@@ -544,12 +586,95 @@ const Table = ({ data, columns, bulkActions = [], secondaryBulkActions = [], csv
544
586
  ]
545
587
  }),
546
588
  isFilterModalOpen && /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(FilterModal, {
547
- onClose: ()=>setIsFilterModalOpen(false)
589
+ onClose: ()=>setIsFilterModalOpen(false),
590
+ filterableColumns: filterableColumns,
591
+ currentFilters: tableState.columnFilters,
592
+ onFiltersChange: (filters)=>table.setColumnFilters(filters)
548
593
  })
549
594
  ]
550
595
  });
551
596
  };
552
- const FilterModal = ({ onClose })=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Modal_js_50f53bdf__["default"], {
597
+ const FilterExpander = ({ label, children, highlight = false })=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("details", {
598
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpander, {
599
+ [__WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpanderHighlight]: highlight
600
+ }),
601
+ children: [
602
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("summary", {
603
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpanderRow,
604
+ children: [
605
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
606
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpanderIcon,
607
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Icon_js_0d271bb6__["default"], {
608
+ symbol: "ChevronRight"
609
+ })
610
+ }),
611
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
612
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpanderLabel,
613
+ children: label
614
+ })
615
+ ]
616
+ }),
617
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
618
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpanderWrapper,
619
+ children: children
620
+ })
621
+ ]
622
+ });
623
+ const FilterModal = ({ onClose, filterableColumns, currentFilters, onFiltersChange })=>{
624
+ const initialFilterValues = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>{
625
+ const result = {};
626
+ for (const column of filterableColumns){
627
+ const current = currentFilters.find((f)=>f.id === column.id);
628
+ const filter = column.filter;
629
+ if (filter.isMulti) result[column.id] = Array.isArray(current?.value) ? current.value : [];
630
+ else result[column.id] = ("string" == typeof current?.value ? current.value : null) ?? filter.options[0].id;
631
+ }
632
+ return result;
633
+ }, [
634
+ filterableColumns,
635
+ currentFilters
636
+ ]);
637
+ const resetFilterValues = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>{
638
+ const result = {};
639
+ for (const col of filterableColumns){
640
+ const filter = col.filter;
641
+ result[col.id] = filter.isMulti ? [] : filter.options[0].id;
642
+ }
643
+ return result;
644
+ }, [
645
+ filterableColumns
646
+ ]);
647
+ const { data, setData, status, submit } = (0, __WEBPACK_EXTERNAL_MODULE__forms_useForm_js_e756b4e7__["default"])({
648
+ initialValues: {
649
+ filters: initialFilterValues
650
+ },
651
+ onSubmit: async (values)=>{
652
+ const filters = Object.entries(values.filters).map(([columnId, value])=>({
653
+ id: columnId,
654
+ value
655
+ }));
656
+ onFiltersChange(filters);
657
+ }
658
+ });
659
+ const handleMultiFilterChange = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((columnId, option)=>{
660
+ setData((prev)=>{
661
+ const prevValue = prev.filters[columnId];
662
+ const current = Array.isArray(prevValue) ? prevValue : [];
663
+ const next = current.includes(option.id) ? current.filter((id)=>id !== option.id) : [
664
+ ...current,
665
+ option.id
666
+ ];
667
+ return {
668
+ filters: {
669
+ ...prev.filters,
670
+ [columnId]: next
671
+ }
672
+ };
673
+ });
674
+ }, [
675
+ setData
676
+ ]);
677
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Modal_js_50f53bdf__["default"], {
553
678
  onClose: onClose,
554
679
  children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Card_js_d67c086a__["default"], {
555
680
  header: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__PlainText_js_cd0b6798__["default"], {
@@ -562,16 +687,23 @@ const FilterModal = ({ onClose })=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_r
562
687
  children: [
563
688
  /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__["default"], {
564
689
  type: "secondary",
565
- onClick: onClose,
690
+ onClick: ()=>{
691
+ setData(()=>({
692
+ filters: resetFilterValues
693
+ }));
694
+ },
566
695
  icon: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__Icon_js_0d271bb6__["default"], {
567
- symbol: "Close"
696
+ symbol: "Restore"
568
697
  }),
569
- children: "Clear filters"
698
+ children: "Reset all filters"
570
699
  }),
571
700
  /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__forms_FormButtonSet_js_c160eb1a__["default"], {
572
- formStatus: "fresh",
701
+ formStatus: status,
573
702
  primary: {
574
- action: onClose,
703
+ action: async ()=>{
704
+ submit();
705
+ onClose();
706
+ },
575
707
  label: "Apply filters"
576
708
  },
577
709
  secondary: {
@@ -581,10 +713,119 @@ const FilterModal = ({ onClose })=>/*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_r
581
713
  })
582
714
  ]
583
715
  }),
584
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__CardContent_js_20e3f6de__["default"], {
585
- children: "TODO: Add filters"
716
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
717
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].filterExpandersContainer,
718
+ children: filterableColumns.map((column)=>{
719
+ const filter = column.filter;
720
+ if (filter.isMulti) {
721
+ const filterValue = data.filters[column.id];
722
+ const selectedIds = Array.isArray(filterValue) ? filterValue : [];
723
+ const selectedOptions = filter.options.filter((opt)=>selectedIds.includes(opt.id));
724
+ const multiFilterLabel = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)(__WEBPACK_EXTERNAL_MODULE__HStack_js_cc26058d__["default"], {
725
+ justify: "space-between",
726
+ children: [
727
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
728
+ children: selectedIds.length > 0 ? `${filter.label} (${selectedIds.length} selected)` : filter.label
729
+ }),
730
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
731
+ style: {
732
+ visibility: selectedIds.length > 0 ? "visible" : "hidden"
733
+ },
734
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__["default"], {
735
+ type: "secondary",
736
+ size: "small",
737
+ onClick: (e)=>{
738
+ e.stopPropagation();
739
+ setData((prev)=>({
740
+ filters: {
741
+ ...prev.filters,
742
+ [column.id]: []
743
+ }
744
+ }));
745
+ },
746
+ children: "Reset"
747
+ })
748
+ })
749
+ ]
750
+ });
751
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(FilterExpander, {
752
+ label: multiFilterLabel,
753
+ highlight: selectedIds.length > 0,
754
+ children: filter.useListBox ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
755
+ className: __WEBPACK_EXTERNAL_MODULE__Table_v2_module_js_4f5732a4__["default"].listBoxFilterContainer,
756
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__fields_ListBoxField_js_b7d4cb12__["default"], {
757
+ id: `${column.id}-filter`,
758
+ label: "",
759
+ options: filter.options,
760
+ optionToId: (opt)=>opt.id,
761
+ optionToLabel: (opt)=>opt.label,
762
+ searchField: "label",
763
+ value: selectedOptions,
764
+ onChange: (option)=>handleMultiFilterChange(column.id, option)
765
+ })
766
+ }) : /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__fields_CheckboxSetField_js_bce910a4__["default"], {
767
+ id: `${column.id}-filter`,
768
+ label: "",
769
+ options: filter.options,
770
+ optionToId: (opt)=>opt.id,
771
+ optionToLabel: (opt)=>opt.label,
772
+ values: selectedOptions,
773
+ onChange: (option)=>handleMultiFilterChange(column.id, option)
774
+ })
775
+ }, column.id);
776
+ }
777
+ const rawValue = data.filters[column.id];
778
+ const value = "string" == typeof rawValue ? rawValue : filter.options[0].id;
779
+ const hasNonDefaultValue = "string" == typeof rawValue && rawValue !== filter.options[0].id;
780
+ const singleFilterLabel = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)(__WEBPACK_EXTERNAL_MODULE__HStack_js_cc26058d__["default"], {
781
+ justify: "space-between",
782
+ children: [
783
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
784
+ children: hasNonDefaultValue ? `${filter.label} (Selected)` : filter.label
785
+ }),
786
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("span", {
787
+ style: {
788
+ visibility: hasNonDefaultValue ? "visible" : "hidden"
789
+ },
790
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__controls_Button_js_f591ba2e__["default"], {
791
+ type: "secondary",
792
+ size: "small",
793
+ onClick: (e)=>{
794
+ e.stopPropagation();
795
+ setData((prev)=>({
796
+ filters: {
797
+ ...prev.filters,
798
+ [column.id]: filter.options[0].id
799
+ }
800
+ }));
801
+ },
802
+ children: "Reset"
803
+ })
804
+ })
805
+ ]
806
+ });
807
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(FilterExpander, {
808
+ label: singleFilterLabel,
809
+ highlight: hasNonDefaultValue,
810
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__fields_SimpleRadioSetField_js_118e9421__["default"], {
811
+ id: `${column.id}-filter`,
812
+ label: "",
813
+ options: filter.options,
814
+ value: value,
815
+ onChange: (optionId)=>{
816
+ setData((prev)=>({
817
+ filters: {
818
+ ...prev.filters,
819
+ [column.id]: optionId
820
+ }
821
+ }));
822
+ }
823
+ })
824
+ }, column.id);
825
+ })
586
826
  })
587
827
  })
588
828
  });
829
+ };
589
830
  const Table_v2_rslib_entry_ = Table;
590
831
  export { Table_v2_rslib_entry_ as default };
@@ -18,6 +18,15 @@ const Table_v2_module_rslib_entry_ = {
18
18
  bulkSelectionActions: "bulkSelectionActions-HWKS5Z",
19
19
  filterBar: "filterBar-BGMBrI",
20
20
  filterSelectMenus: "filterSelectMenus-uB_VUK",
21
+ filterExpandersContainer: "filterExpandersContainer-cQ5EFN",
22
+ filterExpanderRow: "filterExpanderRow-h7i68V",
23
+ filterExpanderHighlight: "filterExpanderHighlight-AqhkRv",
24
+ filterExpanderLabel: "filterExpanderLabel-JiJU2v",
25
+ filterExpanderIcon: "filterExpanderIcon-XaLt7D",
26
+ filterExpander: "filterExpander-v_b9Ry",
27
+ filterExpanderWrapper: "filterExpanderWrapper-Ea0hYg",
28
+ filterOptions: "filterOptions-nwLHze",
29
+ listBoxFilterContainer: "listBoxFilterContainer-SylnjB",
21
30
  filterButton: "filterButton-_xmjv4",
22
31
  downloadLink: "downloadLink-ikiKns"
23
32
  };
@@ -127,6 +127,66 @@
127
127
  }
128
128
  }
129
129
 
130
+ .filterExpandersContainer-cQ5EFN {
131
+ flex-direction: column;
132
+ width: 100%;
133
+ display: flex;
134
+ }
135
+
136
+ .filterExpandersContainer-cQ5EFN > :not(:first-child) {
137
+ border-top: solid var(--size-n5) var(--color-grey-t08);
138
+ }
139
+
140
+ .filterExpanderRow-h7i68V {
141
+ width: 100%;
142
+ padding: var(--size-00);
143
+ align-items: center;
144
+ gap: var(--size-n2);
145
+ cursor: pointer;
146
+ text-align: left;
147
+ background: none;
148
+ border: none;
149
+ outline: none;
150
+ display: flex;
151
+ }
152
+
153
+ .filterExpanderHighlight-AqhkRv .filterExpanderLabel-JiJU2v {
154
+ font: var(--font-regular-bold);
155
+ }
156
+
157
+ .filterExpanderLabel-JiJU2v {
158
+ min-width: 0;
159
+ font: var(--font-label);
160
+ flex: 1;
161
+ }
162
+
163
+ .filterExpanderIcon-XaLt7D {
164
+ transition: transform .15s;
165
+ }
166
+
167
+ .filterExpander-v_b9Ry[open] .filterExpanderRow-h7i68V {
168
+ padding-bottom: var(--size-n2);
169
+ }
170
+
171
+ .filterExpander-v_b9Ry[open] .filterExpanderIcon-XaLt7D {
172
+ transform: rotate(90deg);
173
+ }
174
+
175
+ .filterExpanderWrapper-Ea0hYg {
176
+ padding: 0 var(--size-00) var(--size-p1);
177
+ padding-inline-start: calc(var(--size-p1) + 15px + var(--size-n2));
178
+ }
179
+
180
+ .filterOptions-nwLHze {
181
+ gap: var(--size-n2);
182
+ flex-direction: column;
183
+ display: flex;
184
+ }
185
+
186
+ .listBoxFilterContainer-SylnjB {
187
+ height: 360px;
188
+ }
189
+
130
190
  .filterButton-_xmjv4, .downloadLink-ikiKns {
131
191
  justify-content: flex-end;
132
192
  gap: var(--size-00);
@@ -1,7 +1,7 @@
1
1
  declare const useNextContext: () => {
2
2
  Image: typeof import("next/image").default;
3
3
  Link: import("react").ForwardRefExoticComponent<Omit<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, keyof import("next/link").LinkProps<any>> & import("next/link").LinkProps<any> & {
4
- children?: import("react").ReactNode | undefined;
4
+ children?: React.ReactNode | undefined;
5
5
  } & import("react").RefAttributes<HTMLAnchorElement>>;
6
6
  pathname: string | null;
7
7
  router: import("next/router").NextRouter | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@worknice/whiteboard",
3
3
  "description": "",
4
- "version": "0.36.0",
4
+ "version": "0.37.1",
5
5
  "license": "MIT",
6
6
  "private": false,
7
7
  "files": [
@@ -39,7 +39,7 @@
39
39
  "react-markdown": "^10.1.0",
40
40
  "utf8": "^3.0.0",
41
41
  "zod": "^4.1.13",
42
- "@worknice/utils": "^0.19.0"
42
+ "@worknice/utils": "^0.20.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@anolilab/semantic-release-pnpm": "^3.2.2",
@@ -74,7 +74,7 @@
74
74
  "semantic-release": "^24.2.2",
75
75
  "storybook": "^10.1.2",
76
76
  "temporal-polyfill": "^0.2.5",
77
- "typescript": "~5.5.0",
77
+ "typescript": "^5.9.3",
78
78
  "uuid": "^11.0.5",
79
79
  "vitest": "^2.1.0"
80
80
  },