hs-uix 1.0.3 → 1.1.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.
- package/README.md +7 -4
- package/dist/datatable.js +68 -14
- package/dist/datatable.mjs +68 -14
- package/dist/form.js +59 -20
- package/dist/form.mjs +59 -20
- package/dist/index.js +127 -34
- package/dist/index.mjs +127 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,13 @@ import { DataTable, FormBuilder } from "hs-uix";
|
|
|
21
21
|
|
|
22
22
|
Requires `react` >= 18.0.0 and `@hubspot/ui-extensions` >= 0.12.0 as peer dependencies (already present in any HubSpot UI Extensions project).
|
|
23
23
|
|
|
24
|
+
## Components
|
|
25
|
+
|
|
26
|
+
| Component | Description | Docs |
|
|
27
|
+
|-----------|-------------|------|
|
|
28
|
+
| **DataTable** | Filterable, sortable, paginated table with auto-sized columns, inline editing, row grouping, and more | [Full documentation](https://github.com/05bmckay/hs-uix/blob/main/packages/datatable/README.md) |
|
|
29
|
+
| **FormBuilder** | Declarative, config-driven form with validation, multi-step wizards, and 20+ field types | [Full documentation](https://github.com/05bmckay/hs-uix/blob/main/packages/form/README.md) |
|
|
30
|
+
|
|
24
31
|
---
|
|
25
32
|
|
|
26
33
|
# DataTable
|
|
@@ -90,8 +97,6 @@ Two edit modes: **discrete** (click-to-edit, default) and **inline** (always-vis
|
|
|
90
97
|
|
|
91
98
|
Connect live CRM data (contacts, deals, tickets, etc.) to a DataTable with `useAssociations` from `@hubspot/ui-extensions/crm`.
|
|
92
99
|
|
|
93
|
-
> **Full documentation:** [DataTable README](./packages/datatable/README.md) — includes full API reference, all examples, server-side mode, and more.
|
|
94
|
-
|
|
95
100
|
---
|
|
96
101
|
|
|
97
102
|
# FormBuilder
|
|
@@ -167,8 +172,6 @@ const fields = [
|
|
|
167
172
|
|
|
168
173
|

|
|
169
174
|
|
|
170
|
-
> **Full documentation:** [FormBuilder README](./packages/form/README.md) — includes full API reference, all field types, validation details, props tables, and more.
|
|
171
|
-
|
|
172
175
|
---
|
|
173
176
|
|
|
174
177
|
## Migrating from `@hs-uix/datatable` or `@hs-uix/form`
|
package/dist/datatable.js
CHANGED
|
@@ -160,12 +160,16 @@ var DataTable = ({
|
|
|
160
160
|
// enable fuzzy matching via Fuse.js
|
|
161
161
|
fuzzyOptions,
|
|
162
162
|
// custom Fuse.js options (threshold, distance, etc.)
|
|
163
|
+
showSearch = true,
|
|
164
|
+
// show the SearchInput in the toolbar
|
|
163
165
|
// Filters
|
|
164
166
|
filters = [],
|
|
165
167
|
showFilterBadges = true,
|
|
166
168
|
// show active filter chips/badges
|
|
167
169
|
showClearFiltersButton = true,
|
|
168
170
|
// show "Clear all" filters reset button
|
|
171
|
+
filterInlineLimit = 2,
|
|
172
|
+
// number of filters shown inline before overflow
|
|
169
173
|
// Pagination
|
|
170
174
|
pageSize = 10,
|
|
171
175
|
maxVisiblePageButtons,
|
|
@@ -247,6 +251,8 @@ var DataTable = ({
|
|
|
247
251
|
// optional key to force clear uncontrolled selection memory
|
|
248
252
|
resetSelectionOnQueryChange = true,
|
|
249
253
|
// clear uncontrolled selection on search/filter/sort changes
|
|
254
|
+
showSelectionBar = true,
|
|
255
|
+
// show the selection action bar when rows are selected
|
|
250
256
|
recordLabel,
|
|
251
257
|
// { singular: "Contact", plural: "Contacts" } — defaults to Record/Records
|
|
252
258
|
// -----------------------------------------------------------------------
|
|
@@ -267,11 +273,31 @@ var DataTable = ({
|
|
|
267
273
|
// (row, field, newValue) => void
|
|
268
274
|
onRowEditInput,
|
|
269
275
|
// optional live-input callback: (row, field, inputValue) => void
|
|
276
|
+
onEditStart,
|
|
277
|
+
// (row, field, currentValue) => void — fires when editing begins
|
|
278
|
+
onEditCancel,
|
|
279
|
+
// (row, field) => void — fires when editing is cancelled without commit
|
|
270
280
|
// -----------------------------------------------------------------------
|
|
271
281
|
// Auto-width
|
|
272
282
|
// -----------------------------------------------------------------------
|
|
273
|
-
autoWidth = true
|
|
283
|
+
autoWidth = true,
|
|
274
284
|
// auto-compute column widths from content analysis
|
|
285
|
+
// -----------------------------------------------------------------------
|
|
286
|
+
// Labels / i18n
|
|
287
|
+
// -----------------------------------------------------------------------
|
|
288
|
+
labels,
|
|
289
|
+
// override hardcoded UI strings for i18n
|
|
290
|
+
// -----------------------------------------------------------------------
|
|
291
|
+
// Render overrides (Phase 3 — full replacement escape hatches)
|
|
292
|
+
// -----------------------------------------------------------------------
|
|
293
|
+
renderSelectionBar,
|
|
294
|
+
// ({ selectedIds, selectedCount, displayCount, countLabel, onSelectAll, onDeselectAll, selectionActions }) => ReactNode
|
|
295
|
+
renderEmptyState,
|
|
296
|
+
// ({ title, message }) => ReactNode
|
|
297
|
+
renderLoadingState,
|
|
298
|
+
// ({ label }) => ReactNode
|
|
299
|
+
renderErrorState
|
|
300
|
+
// ({ error, title, message }) => ReactNode
|
|
275
301
|
}) => {
|
|
276
302
|
const initialSortState = (0, import_react.useMemo)(() => {
|
|
277
303
|
return normalizeSortState(columns, defaultSort);
|
|
@@ -505,11 +531,11 @@ var DataTable = ({
|
|
|
505
531
|
const type = filter.type || "select";
|
|
506
532
|
const prefix = filter.chipLabel || filter.placeholder || filter.name;
|
|
507
533
|
if (type === "multiselect") {
|
|
508
|
-
const
|
|
534
|
+
const labels2 = value.map((v) => {
|
|
509
535
|
var _a;
|
|
510
536
|
return ((_a = filter.options.find((o) => o.value === v)) == null ? void 0 : _a.label) || v;
|
|
511
537
|
}).join(", ");
|
|
512
|
-
chips.push({ key: filter.name, label: `${prefix}: ${
|
|
538
|
+
chips.push({ key: filter.name, label: `${prefix}: ${labels2}` });
|
|
513
539
|
} else if (type === "dateRange") {
|
|
514
540
|
const parts = [];
|
|
515
541
|
if (value.from) parts.push(`from ${formatDateChip(value.from)}`);
|
|
@@ -550,7 +576,17 @@ var DataTable = ({
|
|
|
550
576
|
const countLabel = (n) => n === 1 ? singularLabel : pluralLabel;
|
|
551
577
|
const resolvedEmptyTitle = emptyTitle || "No results found";
|
|
552
578
|
const resolvedEmptyMessage = emptyMessage || `No ${pluralLabel} match your search or filter criteria.`;
|
|
553
|
-
const
|
|
579
|
+
const resolvedSelectedLabel = (labels == null ? void 0 : labels.selected) || ((count, label) => `${count}\xA0${label}\xA0selected`);
|
|
580
|
+
const resolvedSelectAllLabel = (labels == null ? void 0 : labels.selectAll) || ((count, label) => `Select all ${count} ${label}`);
|
|
581
|
+
const resolvedDeselectAllLabel = (labels == null ? void 0 : labels.deselectAll) || "Deselect all";
|
|
582
|
+
const resolvedFiltersButtonLabel = (labels == null ? void 0 : labels.filtersButton) || "Filters";
|
|
583
|
+
const resolvedClearAllLabel = (labels == null ? void 0 : labels.clearAll) || "Clear all";
|
|
584
|
+
const resolvedDateFromLabel = (labels == null ? void 0 : labels.dateFrom) || "From";
|
|
585
|
+
const resolvedDateToLabel = (labels == null ? void 0 : labels.dateTo) || "To";
|
|
586
|
+
const resolvedLoadingLabel = (labels == null ? void 0 : labels.loading) || `Loading ${pluralLabel}...`;
|
|
587
|
+
const resolvedErrorTitle = (labels == null ? void 0 : labels.errorTitle) || "Something went wrong.";
|
|
588
|
+
const resolvedErrorMessage = (labels == null ? void 0 : labels.errorMessage) || "An error occurred while loading data.";
|
|
589
|
+
const resolvedRetryMessage = (labels == null ? void 0 : labels.retryMessage) || "Please try again.";
|
|
554
590
|
const recordCountLabel = rowCountText ? rowCountText(shownOnPageCount, displayCount) : displayCount === totalDataCount ? `${totalDataCount} ${countLabel(totalDataCount)}` : `${displayCount} of ${totalDataCount} ${countLabel(totalDataCount)}`;
|
|
555
591
|
const [internalSelectedIds, setInternalSelectedIds] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
556
592
|
const selectionResetRef = (0, import_react.useRef)("");
|
|
@@ -639,7 +675,11 @@ var DataTable = ({
|
|
|
639
675
|
setEditingCell({ rowId, field });
|
|
640
676
|
setEditValue(currentValue);
|
|
641
677
|
setEditError(null);
|
|
642
|
-
|
|
678
|
+
if (onEditStart) {
|
|
679
|
+
const row = data.find((r) => r[rowIdField] === rowId);
|
|
680
|
+
if (row) onEditStart(row, field, currentValue);
|
|
681
|
+
}
|
|
682
|
+
}, [onEditStart, data, rowIdField]);
|
|
643
683
|
const commitEdit = (0, import_react.useCallback)((row, field, value) => {
|
|
644
684
|
const col = columns.find((c) => c.field === field);
|
|
645
685
|
if (col == null ? void 0 : col.editValidate) {
|
|
@@ -661,6 +701,7 @@ var DataTable = ({
|
|
|
661
701
|
const commit = (val) => commitEdit(row, col.field, val);
|
|
662
702
|
const exitEdit = () => {
|
|
663
703
|
if (editError) return;
|
|
704
|
+
if (onEditCancel) onEditCancel(row, col.field);
|
|
664
705
|
setEditingCell(null);
|
|
665
706
|
setEditValue(null);
|
|
666
707
|
};
|
|
@@ -880,7 +921,7 @@ var DataTable = ({
|
|
|
880
921
|
{
|
|
881
922
|
name: `filter-${filter.name}-from`,
|
|
882
923
|
label: "",
|
|
883
|
-
placeholder:
|
|
924
|
+
placeholder: resolvedDateFromLabel,
|
|
884
925
|
format: "medium",
|
|
885
926
|
value: rangeVal.from,
|
|
886
927
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, from: val })
|
|
@@ -891,7 +932,7 @@ var DataTable = ({
|
|
|
891
932
|
size: "sm",
|
|
892
933
|
name: `filter-${filter.name}-to`,
|
|
893
934
|
label: "",
|
|
894
|
-
placeholder:
|
|
935
|
+
placeholder: resolvedDateToLabel,
|
|
895
936
|
format: "medium",
|
|
896
937
|
value: rangeVal.to,
|
|
897
938
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, to: val })
|
|
@@ -914,7 +955,7 @@ var DataTable = ({
|
|
|
914
955
|
}
|
|
915
956
|
);
|
|
916
957
|
};
|
|
917
|
-
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
|
|
958
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
|
|
918
959
|
import_ui_extensions.SearchInput,
|
|
919
960
|
{
|
|
920
961
|
name: "datatable-search",
|
|
@@ -922,7 +963,7 @@ var DataTable = ({
|
|
|
922
963
|
value: searchTerm,
|
|
923
964
|
onChange: handleSearchChange
|
|
924
965
|
}
|
|
925
|
-
), filters.slice(0,
|
|
966
|
+
), filters.slice(0, filterInlineLimit).map(renderFilterControl), filters.length > filterInlineLimit && /* @__PURE__ */ import_react.default.createElement(
|
|
926
967
|
import_ui_extensions.Button,
|
|
927
968
|
{
|
|
928
969
|
variant: "transparent",
|
|
@@ -930,16 +971,25 @@ var DataTable = ({
|
|
|
930
971
|
onClick: () => setShowMoreFilters((prev) => !prev)
|
|
931
972
|
},
|
|
932
973
|
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "filter", size: "sm" }),
|
|
933
|
-
"
|
|
934
|
-
|
|
974
|
+
" ",
|
|
975
|
+
resolvedFiltersButtonLabel
|
|
976
|
+
)), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "end", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ import_react.default.createElement(
|
|
935
977
|
import_ui_extensions.Button,
|
|
936
978
|
{
|
|
937
979
|
variant: "transparent",
|
|
938
980
|
size: "extra-small",
|
|
939
981
|
onClick: () => handleFilterRemove("all")
|
|
940
982
|
},
|
|
941
|
-
|
|
942
|
-
)))), showRowCount && displayCount > 0 && !(selectable && selectedIds.size > 0) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), selectable && selectedIds.size > 0 &&
|
|
983
|
+
resolvedClearAllLabel
|
|
984
|
+
)))), showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0) && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), showSelectionBar && selectable && selectedIds.size > 0 && (renderSelectionBar ? renderSelectionBar({
|
|
985
|
+
selectedIds,
|
|
986
|
+
selectedCount: selectedIds.size,
|
|
987
|
+
displayCount,
|
|
988
|
+
countLabel,
|
|
989
|
+
onSelectAll: handleSelectAllRows,
|
|
990
|
+
onDeselectAll: handleDeselectAll,
|
|
991
|
+
selectionActions
|
|
992
|
+
}) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { inline: true, format: { fontWeight: "demibold" } }, typeof resolvedSelectedLabel === "function" ? resolvedSelectedLabel(selectedIds.size, countLabel(selectedIds.size)) : resolvedSelectedLabel), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "transparent", size: "extra-small", onClick: handleSelectAllRows }, typeof resolvedSelectAllLabel === "function" ? resolvedSelectAllLabel(displayCount, countLabel(displayCount)) : resolvedSelectAllLabel), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "transparent", size: "extra-small", onClick: handleDeselectAll }, resolvedDeselectAllLabel), selectionActions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
|
|
943
993
|
import_ui_extensions.Button,
|
|
944
994
|
{
|
|
945
995
|
key: i,
|
|
@@ -950,7 +1000,11 @@ var DataTable = ({
|
|
|
950
1000
|
action.icon && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: action.icon, size: "sm" }),
|
|
951
1001
|
" ",
|
|
952
1002
|
action.label
|
|
953
|
-
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), loading ?
|
|
1003
|
+
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel))))), loading ? renderLoadingState ? renderLoadingState({ label: resolvedLoadingLabel }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.LoadingSpinner, { label: resolvedLoadingLabel, layout: "centered" }) : error ? renderErrorState ? renderErrorState({
|
|
1004
|
+
error,
|
|
1005
|
+
title: typeof error === "string" ? error : resolvedErrorTitle,
|
|
1006
|
+
message: typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage
|
|
1007
|
+
}) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.ErrorState, { title: typeof error === "string" ? error : resolvedErrorTitle }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, null, typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage)) : displayRows.length === 0 ? renderEmptyState ? renderEmptyState({ title: resolvedEmptyTitle, message: resolvedEmptyMessage }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.EmptyState, { title: resolvedEmptyTitle, layout: "vertical" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, null, resolvedEmptyMessage))) : /* @__PURE__ */ import_react.default.createElement(
|
|
954
1008
|
import_ui_extensions.Table,
|
|
955
1009
|
{
|
|
956
1010
|
bordered,
|
package/dist/datatable.mjs
CHANGED
|
@@ -155,12 +155,16 @@ var DataTable = ({
|
|
|
155
155
|
// enable fuzzy matching via Fuse.js
|
|
156
156
|
fuzzyOptions,
|
|
157
157
|
// custom Fuse.js options (threshold, distance, etc.)
|
|
158
|
+
showSearch = true,
|
|
159
|
+
// show the SearchInput in the toolbar
|
|
158
160
|
// Filters
|
|
159
161
|
filters = [],
|
|
160
162
|
showFilterBadges = true,
|
|
161
163
|
// show active filter chips/badges
|
|
162
164
|
showClearFiltersButton = true,
|
|
163
165
|
// show "Clear all" filters reset button
|
|
166
|
+
filterInlineLimit = 2,
|
|
167
|
+
// number of filters shown inline before overflow
|
|
164
168
|
// Pagination
|
|
165
169
|
pageSize = 10,
|
|
166
170
|
maxVisiblePageButtons,
|
|
@@ -242,6 +246,8 @@ var DataTable = ({
|
|
|
242
246
|
// optional key to force clear uncontrolled selection memory
|
|
243
247
|
resetSelectionOnQueryChange = true,
|
|
244
248
|
// clear uncontrolled selection on search/filter/sort changes
|
|
249
|
+
showSelectionBar = true,
|
|
250
|
+
// show the selection action bar when rows are selected
|
|
245
251
|
recordLabel,
|
|
246
252
|
// { singular: "Contact", plural: "Contacts" } — defaults to Record/Records
|
|
247
253
|
// -----------------------------------------------------------------------
|
|
@@ -262,11 +268,31 @@ var DataTable = ({
|
|
|
262
268
|
// (row, field, newValue) => void
|
|
263
269
|
onRowEditInput,
|
|
264
270
|
// optional live-input callback: (row, field, inputValue) => void
|
|
271
|
+
onEditStart,
|
|
272
|
+
// (row, field, currentValue) => void — fires when editing begins
|
|
273
|
+
onEditCancel,
|
|
274
|
+
// (row, field) => void — fires when editing is cancelled without commit
|
|
265
275
|
// -----------------------------------------------------------------------
|
|
266
276
|
// Auto-width
|
|
267
277
|
// -----------------------------------------------------------------------
|
|
268
|
-
autoWidth = true
|
|
278
|
+
autoWidth = true,
|
|
269
279
|
// auto-compute column widths from content analysis
|
|
280
|
+
// -----------------------------------------------------------------------
|
|
281
|
+
// Labels / i18n
|
|
282
|
+
// -----------------------------------------------------------------------
|
|
283
|
+
labels,
|
|
284
|
+
// override hardcoded UI strings for i18n
|
|
285
|
+
// -----------------------------------------------------------------------
|
|
286
|
+
// Render overrides (Phase 3 — full replacement escape hatches)
|
|
287
|
+
// -----------------------------------------------------------------------
|
|
288
|
+
renderSelectionBar,
|
|
289
|
+
// ({ selectedIds, selectedCount, displayCount, countLabel, onSelectAll, onDeselectAll, selectionActions }) => ReactNode
|
|
290
|
+
renderEmptyState,
|
|
291
|
+
// ({ title, message }) => ReactNode
|
|
292
|
+
renderLoadingState,
|
|
293
|
+
// ({ label }) => ReactNode
|
|
294
|
+
renderErrorState
|
|
295
|
+
// ({ error, title, message }) => ReactNode
|
|
270
296
|
}) => {
|
|
271
297
|
const initialSortState = useMemo(() => {
|
|
272
298
|
return normalizeSortState(columns, defaultSort);
|
|
@@ -500,11 +526,11 @@ var DataTable = ({
|
|
|
500
526
|
const type = filter.type || "select";
|
|
501
527
|
const prefix = filter.chipLabel || filter.placeholder || filter.name;
|
|
502
528
|
if (type === "multiselect") {
|
|
503
|
-
const
|
|
529
|
+
const labels2 = value.map((v) => {
|
|
504
530
|
var _a;
|
|
505
531
|
return ((_a = filter.options.find((o) => o.value === v)) == null ? void 0 : _a.label) || v;
|
|
506
532
|
}).join(", ");
|
|
507
|
-
chips.push({ key: filter.name, label: `${prefix}: ${
|
|
533
|
+
chips.push({ key: filter.name, label: `${prefix}: ${labels2}` });
|
|
508
534
|
} else if (type === "dateRange") {
|
|
509
535
|
const parts = [];
|
|
510
536
|
if (value.from) parts.push(`from ${formatDateChip(value.from)}`);
|
|
@@ -545,7 +571,17 @@ var DataTable = ({
|
|
|
545
571
|
const countLabel = (n) => n === 1 ? singularLabel : pluralLabel;
|
|
546
572
|
const resolvedEmptyTitle = emptyTitle || "No results found";
|
|
547
573
|
const resolvedEmptyMessage = emptyMessage || `No ${pluralLabel} match your search or filter criteria.`;
|
|
548
|
-
const
|
|
574
|
+
const resolvedSelectedLabel = (labels == null ? void 0 : labels.selected) || ((count, label) => `${count}\xA0${label}\xA0selected`);
|
|
575
|
+
const resolvedSelectAllLabel = (labels == null ? void 0 : labels.selectAll) || ((count, label) => `Select all ${count} ${label}`);
|
|
576
|
+
const resolvedDeselectAllLabel = (labels == null ? void 0 : labels.deselectAll) || "Deselect all";
|
|
577
|
+
const resolvedFiltersButtonLabel = (labels == null ? void 0 : labels.filtersButton) || "Filters";
|
|
578
|
+
const resolvedClearAllLabel = (labels == null ? void 0 : labels.clearAll) || "Clear all";
|
|
579
|
+
const resolvedDateFromLabel = (labels == null ? void 0 : labels.dateFrom) || "From";
|
|
580
|
+
const resolvedDateToLabel = (labels == null ? void 0 : labels.dateTo) || "To";
|
|
581
|
+
const resolvedLoadingLabel = (labels == null ? void 0 : labels.loading) || `Loading ${pluralLabel}...`;
|
|
582
|
+
const resolvedErrorTitle = (labels == null ? void 0 : labels.errorTitle) || "Something went wrong.";
|
|
583
|
+
const resolvedErrorMessage = (labels == null ? void 0 : labels.errorMessage) || "An error occurred while loading data.";
|
|
584
|
+
const resolvedRetryMessage = (labels == null ? void 0 : labels.retryMessage) || "Please try again.";
|
|
549
585
|
const recordCountLabel = rowCountText ? rowCountText(shownOnPageCount, displayCount) : displayCount === totalDataCount ? `${totalDataCount} ${countLabel(totalDataCount)}` : `${displayCount} of ${totalDataCount} ${countLabel(totalDataCount)}`;
|
|
550
586
|
const [internalSelectedIds, setInternalSelectedIds] = useState(/* @__PURE__ */ new Set());
|
|
551
587
|
const selectionResetRef = useRef("");
|
|
@@ -634,7 +670,11 @@ var DataTable = ({
|
|
|
634
670
|
setEditingCell({ rowId, field });
|
|
635
671
|
setEditValue(currentValue);
|
|
636
672
|
setEditError(null);
|
|
637
|
-
|
|
673
|
+
if (onEditStart) {
|
|
674
|
+
const row = data.find((r) => r[rowIdField] === rowId);
|
|
675
|
+
if (row) onEditStart(row, field, currentValue);
|
|
676
|
+
}
|
|
677
|
+
}, [onEditStart, data, rowIdField]);
|
|
638
678
|
const commitEdit = useCallback((row, field, value) => {
|
|
639
679
|
const col = columns.find((c) => c.field === field);
|
|
640
680
|
if (col == null ? void 0 : col.editValidate) {
|
|
@@ -656,6 +696,7 @@ var DataTable = ({
|
|
|
656
696
|
const commit = (val) => commitEdit(row, col.field, val);
|
|
657
697
|
const exitEdit = () => {
|
|
658
698
|
if (editError) return;
|
|
699
|
+
if (onEditCancel) onEditCancel(row, col.field);
|
|
659
700
|
setEditingCell(null);
|
|
660
701
|
setEditValue(null);
|
|
661
702
|
};
|
|
@@ -875,7 +916,7 @@ var DataTable = ({
|
|
|
875
916
|
{
|
|
876
917
|
name: `filter-${filter.name}-from`,
|
|
877
918
|
label: "",
|
|
878
|
-
placeholder:
|
|
919
|
+
placeholder: resolvedDateFromLabel,
|
|
879
920
|
format: "medium",
|
|
880
921
|
value: rangeVal.from,
|
|
881
922
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, from: val })
|
|
@@ -886,7 +927,7 @@ var DataTable = ({
|
|
|
886
927
|
size: "sm",
|
|
887
928
|
name: `filter-${filter.name}-to`,
|
|
888
929
|
label: "",
|
|
889
|
-
placeholder:
|
|
930
|
+
placeholder: resolvedDateToLabel,
|
|
890
931
|
format: "medium",
|
|
891
932
|
value: rangeVal.to,
|
|
892
933
|
onChange: (val) => handleFilterChange(filter.name, { ...rangeVal, to: val })
|
|
@@ -909,7 +950,7 @@ var DataTable = ({
|
|
|
909
950
|
}
|
|
910
951
|
);
|
|
911
952
|
};
|
|
912
|
-
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, searchFields.length > 0 && /* @__PURE__ */ React.createElement(
|
|
953
|
+
return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
|
|
913
954
|
SearchInput,
|
|
914
955
|
{
|
|
915
956
|
name: "datatable-search",
|
|
@@ -917,7 +958,7 @@ var DataTable = ({
|
|
|
917
958
|
value: searchTerm,
|
|
918
959
|
onChange: handleSearchChange
|
|
919
960
|
}
|
|
920
|
-
), filters.slice(0,
|
|
961
|
+
), filters.slice(0, filterInlineLimit).map(renderFilterControl), filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(
|
|
921
962
|
Button,
|
|
922
963
|
{
|
|
923
964
|
variant: "transparent",
|
|
@@ -925,16 +966,25 @@ var DataTable = ({
|
|
|
925
966
|
onClick: () => setShowMoreFilters((prev) => !prev)
|
|
926
967
|
},
|
|
927
968
|
/* @__PURE__ */ React.createElement(Icon, { name: "filter", size: "sm" }),
|
|
928
|
-
"
|
|
929
|
-
|
|
969
|
+
" ",
|
|
970
|
+
resolvedFiltersButtonLabel
|
|
971
|
+
)), showMoreFilters && filters.length > filterInlineLimit && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "end", gap: "sm", wrap: "wrap" }, filters.slice(filterInlineLimit).map(renderFilterControl)), activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showFilterBadges && activeChips.map((chip) => /* @__PURE__ */ React.createElement(Tag, { key: chip.key, variant: "default", onDelete: () => handleFilterRemove(chip.key) }, chip.label)), showClearFiltersButton && /* @__PURE__ */ React.createElement(
|
|
930
972
|
Button,
|
|
931
973
|
{
|
|
932
974
|
variant: "transparent",
|
|
933
975
|
size: "extra-small",
|
|
934
976
|
onClick: () => handleFilterRemove("all")
|
|
935
977
|
},
|
|
936
|
-
|
|
937
|
-
)))), showRowCount && displayCount > 0 && !(selectable && selectedIds.size > 0) && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), selectable && selectedIds.size > 0 &&
|
|
978
|
+
resolvedClearAllLabel
|
|
979
|
+
)))), showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0) && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "end" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), showSelectionBar && selectable && selectedIds.size > 0 && (renderSelectionBar ? renderSelectionBar({
|
|
980
|
+
selectedIds,
|
|
981
|
+
selectedCount: selectedIds.size,
|
|
982
|
+
displayCount,
|
|
983
|
+
countLabel,
|
|
984
|
+
onSelectAll: handleSelectAllRows,
|
|
985
|
+
onDeselectAll: handleDeselectAll,
|
|
986
|
+
selectionActions
|
|
987
|
+
}) : /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "nowrap" }, /* @__PURE__ */ React.createElement(Text, { inline: true, format: { fontWeight: "demibold" } }, typeof resolvedSelectedLabel === "function" ? resolvedSelectedLabel(selectedIds.size, countLabel(selectedIds.size)) : resolvedSelectedLabel), /* @__PURE__ */ React.createElement(Button, { variant: "transparent", size: "extra-small", onClick: handleSelectAllRows }, typeof resolvedSelectAllLabel === "function" ? resolvedSelectAllLabel(displayCount, countLabel(displayCount)) : resolvedSelectAllLabel), /* @__PURE__ */ React.createElement(Button, { variant: "transparent", size: "extra-small", onClick: handleDeselectAll }, resolvedDeselectAllLabel), selectionActions.map((action, i) => /* @__PURE__ */ React.createElement(
|
|
938
988
|
Button,
|
|
939
989
|
{
|
|
940
990
|
key: i,
|
|
@@ -945,7 +995,11 @@ var DataTable = ({
|
|
|
945
995
|
action.icon && /* @__PURE__ */ React.createElement(Icon, { name: action.icon, size: "sm" }),
|
|
946
996
|
" ",
|
|
947
997
|
action.label
|
|
948
|
-
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)))), loading ?
|
|
998
|
+
)))), showRowCount && displayCount > 0 && /* @__PURE__ */ React.createElement(Box, { flex: 1, alignSelf: "center" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", justify: "end" }, /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel))))), loading ? renderLoadingState ? renderLoadingState({ label: resolvedLoadingLabel }) : /* @__PURE__ */ React.createElement(LoadingSpinner, { label: resolvedLoadingLabel, layout: "centered" }) : error ? renderErrorState ? renderErrorState({
|
|
999
|
+
error,
|
|
1000
|
+
title: typeof error === "string" ? error : resolvedErrorTitle,
|
|
1001
|
+
message: typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage
|
|
1002
|
+
}) : /* @__PURE__ */ React.createElement(ErrorState, { title: typeof error === "string" ? error : resolvedErrorTitle }, /* @__PURE__ */ React.createElement(Text, null, typeof error === "string" ? resolvedRetryMessage : resolvedErrorMessage)) : displayRows.length === 0 ? renderEmptyState ? renderEmptyState({ title: resolvedEmptyTitle, message: resolvedEmptyMessage }) : /* @__PURE__ */ React.createElement(Flex, { direction: "column", align: "center", justify: "center" }, /* @__PURE__ */ React.createElement(EmptyState, { title: resolvedEmptyTitle, layout: "vertical" }, /* @__PURE__ */ React.createElement(Text, null, resolvedEmptyMessage))) : /* @__PURE__ */ React.createElement(
|
|
949
1003
|
Table,
|
|
950
1004
|
{
|
|
951
1005
|
bordered,
|