@homebound/beam 2.135.2 → 2.137.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/dist/components/AutoSaveIndicator.d.ts +6 -0
- package/dist/components/AutoSaveIndicator.js +42 -0
- package/dist/components/BeamContext.js +2 -1
- package/dist/components/Filters/Filters.js +1 -1
- package/dist/components/Modal/Modal.js +7 -6
- package/dist/components/SuperDrawer/SuperDrawer.js +20 -19
- package/dist/components/Table/GridTable.js +38 -33
- package/dist/components/Table/RowState.d.ts +3 -3
- package/dist/components/Table/RowState.js +16 -16
- package/dist/inputs/MultiSelectField.d.ts +1 -1
- package/dist/inputs/SelectField.d.ts +1 -1
- package/dist/inputs/SelectField.js +2 -1
- package/dist/inputs/internal/SelectFieldBase.d.ts +14 -8
- package/dist/inputs/internal/SelectFieldBase.js +27 -3
- package/dist/types.d.ts +1 -0
- package/dist/types.js +5 -0
- package/package.json +2 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoSaveIndicator = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
|
+
const form_state_1 = require("@homebound/form-state");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const Css_1 = require("../Css");
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const _1 = require(".");
|
|
10
|
+
const Icon_1 = require("./Icon");
|
|
11
|
+
function AutoSaveIndicator({ hideOnIdle, doNotReset }) {
|
|
12
|
+
const { status, resetStatus, errors } = (0, form_state_1.useAutoSaveStatus)();
|
|
13
|
+
(0, react_1.useEffect)(() => {
|
|
14
|
+
if (doNotReset)
|
|
15
|
+
return;
|
|
16
|
+
/**
|
|
17
|
+
* Any time AutoSaveIndicator dismounts, most likely on Page Navigation,
|
|
18
|
+
* state should clear before the next Indicator mounts
|
|
19
|
+
*/
|
|
20
|
+
return () => resetStatus();
|
|
21
|
+
}, []);
|
|
22
|
+
switch (status) {
|
|
23
|
+
case form_state_1.AutoSaveStatus.IDLE:
|
|
24
|
+
return hideOnIdle ? null : (0, jsx_runtime_1.jsx)(Icon_1.Icon, { icon: "cloudSave", color: Css_1.Palette.Gray700 }, void 0);
|
|
25
|
+
case form_state_1.AutoSaveStatus.SAVING:
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)(HelperText, Object.assign({ text: "Saving...", color: Css_1.Palette.Gray700 }, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { icon: "refresh", color: Css_1.Palette.Gray700 }, void 0) }), void 0));
|
|
27
|
+
case form_state_1.AutoSaveStatus.DONE:
|
|
28
|
+
return ((0, jsx_runtime_1.jsx)(HelperText, Object.assign({ text: "Saved", color: Css_1.Palette.LightBlue700 }, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { icon: "cloudSave", color: Css_1.Palette.LightBlue700 }, void 0) }), void 0));
|
|
29
|
+
case form_state_1.AutoSaveStatus.ERROR:
|
|
30
|
+
return (
|
|
31
|
+
/**
|
|
32
|
+
* Tooltip is expanding to fill as much available space as it can, possibly
|
|
33
|
+
* rendering it far away from the Icon/Text. Wrap it with a div to constrain
|
|
34
|
+
* it.
|
|
35
|
+
*/
|
|
36
|
+
(0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.dif.$ }, { children: (0, jsx_runtime_1.jsx)(_1.Tooltip, Object.assign({ title: String(errors), placement: "bottom" }, { children: (0, jsx_runtime_1.jsx)(HelperText, Object.assign({ text: "Error saving", color: Css_1.Palette.Gray700 }, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { icon: "error", color: Css_1.Palette.Red500 }, void 0) }), void 0) }), void 0) }), void 0));
|
|
37
|
+
default:
|
|
38
|
+
(0, types_1.assertNever)(status);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.AutoSaveIndicator = AutoSaveIndicator;
|
|
42
|
+
const HelperText = ({ text, color, children }) => ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.gap1.color(color).$ }, { children: [children, text] }), void 0));
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useBeamContext = exports.BeamProvider = exports.BeamContext = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
|
+
const form_state_1 = require("@homebound/form-state");
|
|
5
6
|
const react_1 = require("react");
|
|
6
7
|
const react_aria_1 = require("react-aria");
|
|
7
8
|
const Modal_1 = require("./Modal/Modal");
|
|
@@ -56,7 +57,7 @@ function BeamProvider({ children, ...presentationProps }) {
|
|
|
56
57
|
tabActionsDiv,
|
|
57
58
|
};
|
|
58
59
|
}, [modalBodyDiv, modalFooterDiv]);
|
|
59
|
-
return ((0, jsx_runtime_1.jsx)(exports.BeamContext.Provider, Object.assign({ value: { ...context } }, { children: (0, jsx_runtime_1.jsx)(PresentationContext_1.PresentationProvider, Object.assign({}, presentationProps, { children: (0, jsx_runtime_1.jsxs)(SnackbarContext_1.SnackbarProvider, { children: [(0, jsx_runtime_1.jsxs)(react_aria_1.OverlayProvider, { children: [children, modalRef.current && drawerContentStackRef.current.length === 0 && (0, jsx_runtime_1.jsx)(Modal_1.Modal, Object.assign({}, modalRef.current), void 0)] }, void 0), (0, jsx_runtime_1.jsx)(SuperDrawer_1.SuperDrawer, {}, void 0)] }, void 0) }), void 0) }), void 0));
|
|
60
|
+
return ((0, jsx_runtime_1.jsx)(exports.BeamContext.Provider, Object.assign({ value: { ...context } }, { children: (0, jsx_runtime_1.jsx)(PresentationContext_1.PresentationProvider, Object.assign({}, presentationProps, { children: (0, jsx_runtime_1.jsx)(form_state_1.AutoSaveStatusProvider, { children: (0, jsx_runtime_1.jsxs)(SnackbarContext_1.SnackbarProvider, { children: [(0, jsx_runtime_1.jsxs)(react_aria_1.OverlayProvider, { children: [children, modalRef.current && drawerContentStackRef.current.length === 0 && (0, jsx_runtime_1.jsx)(Modal_1.Modal, Object.assign({}, modalRef.current), void 0)] }, void 0), (0, jsx_runtime_1.jsx)(SuperDrawer_1.SuperDrawer, {}, void 0)] }, void 0) }, void 0) }), void 0) }), void 0));
|
|
60
61
|
}
|
|
61
62
|
exports.BeamProvider = BeamProvider;
|
|
62
63
|
/** Looks like a ref, but invokes a re-render on set (w/o changing the setter identity). */
|
|
@@ -29,7 +29,7 @@ function Filters(props) {
|
|
|
29
29
|
return [Object.fromEntries(impls), {}];
|
|
30
30
|
}, [numberOfPageFilters, filterDefs]);
|
|
31
31
|
const numModalFilters = (0, utils_1.safeKeys)(modalFilters).filter((fk) => filter[fk] !== undefined).length;
|
|
32
|
-
const maybeGroupByField = groupBy ? ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(SelectField_1.SelectField, { label: "Group by", compact: !vertical, inlineLabel: !vertical, sizeToContent: !vertical, options: groupBy.options, getOptionValue: (o) => o.id, getOptionLabel: (o) => o.name, value: groupBy.value, onSelect: (g) => groupBy.setValue(g) }, void 0) }, void 0)) : null;
|
|
32
|
+
const maybeGroupByField = groupBy ? ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(SelectField_1.SelectField, { label: "Group by", compact: !vertical, inlineLabel: !vertical, sizeToContent: !vertical, options: groupBy.options, getOptionValue: (o) => o.id, getOptionLabel: (o) => o.name, value: groupBy.value, onSelect: (g) => g && groupBy.setValue(g) }, void 0) }, void 0)) : null;
|
|
33
33
|
// Return list of filter components. `onSelect` should update the `filter`
|
|
34
34
|
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
|
|
35
35
|
...(vertical ? Css_1.Css.df.fdc.childGap2.$ : Css_1.Css.df.aic.childGap1.$),
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ModalFooter = exports.ModalBody = exports.ModalHeader = exports.Modal = void 0;
|
|
7
7
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
8
|
+
const form_state_1 = require("@homebound/form-state");
|
|
8
9
|
const resize_observer_1 = __importDefault(require("@react-hook/resize-observer"));
|
|
9
10
|
const react_1 = require("react");
|
|
10
11
|
const react_aria_1 = require("react-aria");
|
|
@@ -64,12 +65,12 @@ function Modal(props) {
|
|
|
64
65
|
modalBodyRef.current.appendChild(modalBodyDiv);
|
|
65
66
|
modalFooterRef.current.appendChild(modalFooterDiv);
|
|
66
67
|
}, [modalBodyRef, modalFooterRef, modalHeaderRef]);
|
|
67
|
-
return ((0, jsx_runtime_1.jsx)(react_aria_1.OverlayContainer, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.underlay.z4.$ }, underlayProps, testId.underlay, { children: (0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, Object.assign({ contain: true, restoreFocus: true, autoFocus: true }, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.br24.bgWhite.bshModal.overflowHidden
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
return ((0, jsx_runtime_1.jsx)(react_aria_1.OverlayContainer, { children: (0, jsx_runtime_1.jsx)(form_state_1.AutoSaveStatusProvider, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.underlay.z4.$ }, underlayProps, testId.underlay, { children: (0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, Object.assign({ contain: true, restoreFocus: true, autoFocus: true }, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.br24.bgWhite.bshModal.overflowHidden
|
|
69
|
+
.maxh("90vh")
|
|
70
|
+
.df.fdc.wPx(width)
|
|
71
|
+
.mh((0, Css_1.px)(defaultMinHeight))
|
|
72
|
+
.if(isFixedHeight)
|
|
73
|
+
.hPx(height).$, ref: ref }, overlayProps, dialogProps, modalProps, testId, { children: [(0, jsx_runtime_1.jsxs)("header", Object.assign({ css: Css_1.Css.df.p3.fs0.if(drawHeaderBorder).bb.bGray200.$ }, { children: [(0, jsx_runtime_1.jsx)("h1", Object.assign({ css: Css_1.Css.fg1.xl2Em.gray900.$, ref: modalHeaderRef }, titleProps, testId.title), void 0), (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.fs0.pl1.$ }, { children: (0, jsx_runtime_1.jsx)(IconButton_1.IconButton, Object.assign({ icon: "x", onClick: closeModal }, testId.titleClose), void 0) }), void 0)] }), void 0), (0, jsx_runtime_1.jsxs)("main", Object.assign({ ref: contentRef, css: Css_1.Css.fg1.overflowYAuto.if(hasScroll).bb.bGray200.if(!!forceScrolling).overflowYScroll.$ }, { children: [content, (0, jsx_runtime_1.jsx)("div", { ref: modalBodyRef }, void 0)] }), void 0), (0, jsx_runtime_1.jsx)("footer", Object.assign({ css: Css_1.Css.fs0.$ }, { children: (0, jsx_runtime_1.jsx)("div", { ref: modalFooterRef }, void 0) }), void 0)] }), void 0) }), void 0) }), void 0) }, void 0) }, void 0));
|
|
73
74
|
}
|
|
74
75
|
exports.Modal = Modal;
|
|
75
76
|
function ModalHeader({ children }) {
|
|
@@ -4,6 +4,7 @@ exports.SuperDrawer = void 0;
|
|
|
4
4
|
const react_1 = require("@emotion/react");
|
|
5
5
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
6
6
|
const react_2 = require("@emotion/react");
|
|
7
|
+
const form_state_1 = require("@homebound/form-state");
|
|
7
8
|
const framer_motion_1 = require("framer-motion");
|
|
8
9
|
const react_3 = require("react");
|
|
9
10
|
const react_dom_1 = require("react-dom");
|
|
@@ -71,29 +72,29 @@ function SuperDrawer() {
|
|
|
71
72
|
animate: { opacity: 1 },
|
|
72
73
|
// Unmount styles
|
|
73
74
|
exit: { opacity: 0, transition: { delay: 0.2 } }, onClick: closeDrawer },
|
|
74
|
-
(0, jsx_runtime_1.
|
|
75
|
+
(0, jsx_runtime_1.jsx)(framer_motion_1.motion.aside, Object.assign({ css: Css_1.Css.bgWhite.h100.maxw((0, Css_1.px)(1040)).w100.df.fdc.relative.$,
|
|
75
76
|
// Keeping initial x to 1040 as this will still work if the container is smaller
|
|
76
77
|
initial: { x: 1040 }, animate: { x: 0 },
|
|
77
78
|
// Custom transitions settings for the translateX animation
|
|
78
79
|
transition: { ease: "linear", duration: 0.2, delay: 0.2 }, exit: { transition: { ease: "linear", duration: 0.2 }, x: 1040 },
|
|
79
80
|
// Preventing clicks from triggering parent onClick
|
|
80
|
-
onClick: (e) => e.stopPropagation() }, { children: [(0, jsx_runtime_1.jsxs)("header", Object.assign({ css: Css_1.Css.df.p3.bb.bGray200.df.aic.jcsb.gap2.$ }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.aic.$ }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.xl2Em.gray900.mr2.$ }, testId.title, { ref: drawerHeaderRef }, { children: !modalState.current && (title || null) }), void 0), !modalState.current && (titleLeftContent || null)] }), void 0), !modalState.current && (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
onClick: (e) => e.stopPropagation() }, { children: (0, jsx_runtime_1.jsxs)(form_state_1.AutoSaveStatusProvider, { children: [(0, jsx_runtime_1.jsxs)("header", Object.assign({ css: Css_1.Css.df.p3.bb.bGray200.df.aic.jcsb.gap2.$ }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.aic.$ }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.xl2Em.gray900.mr2.$ }, testId.title, { ref: drawerHeaderRef }, { children: !modalState.current && (title || null) }), void 0), !modalState.current && (titleLeftContent || null)] }), void 0), !modalState.current && (
|
|
82
|
+
// Forcing height to 32px to match title height
|
|
83
|
+
(0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.childGap3.aic.hPx(32).fs0.$ }, { children: [titleRightContent || null, (0, jsx_runtime_1.jsx)(components_1.ButtonGroup, Object.assign({ buttons: [
|
|
84
|
+
{
|
|
85
|
+
icon: "chevronLeft",
|
|
86
|
+
onClick: () => onPrevClick && onPrevClick(),
|
|
87
|
+
disabled: !onPrevClick || isDetail,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
icon: "chevronRight",
|
|
91
|
+
onClick: () => onNextClick && onNextClick(),
|
|
92
|
+
disabled: !onNextClick || isDetail,
|
|
93
|
+
},
|
|
94
|
+
] }, testId.headerActions), void 0), (0, jsx_runtime_1.jsx)(components_1.IconButton, Object.assign({ icon: "x", onClick: closeDrawer }, testId.close), void 0)] }), void 0))] }), void 0), content, modalState.current && (
|
|
95
|
+
// Forcing some design constraints on the modal component
|
|
96
|
+
(0, jsx_runtime_1.jsxs)("div", Object.assign({ css:
|
|
97
|
+
// topPX(81) is the offset from the header
|
|
98
|
+
Css_1.Css.fg1.topPx(81).left0.right0.bottom0.absolute.bgWhite.df.aic.jcc.fg1.fdc.z5.$ }, { children: [modalState.current.content, (0, jsx_runtime_1.jsx)("div", { ref: modalBodyRef }, void 0), (0, jsx_runtime_1.jsx)("div", { ref: modalFooterRef }, void 0)] }), void 0))] }, void 0) }), "superDrawerContainer"))] }, void 0)) }, void 0), document.body);
|
|
98
99
|
}
|
|
99
100
|
exports.SuperDrawer = SuperDrawer;
|
|
@@ -140,8 +140,8 @@ function GridTable(props) {
|
|
|
140
140
|
}
|
|
141
141
|
else {
|
|
142
142
|
// Otherwise, maybe one of the children match.
|
|
143
|
-
|
|
144
|
-
if (
|
|
143
|
+
// Always filter children rows whether or not they are collapsed in order to determine proper "selected" state of the group row.
|
|
144
|
+
if (!!((_c = row.children) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
145
145
|
const matchedChildren = row.children.reduce(filterRows, []);
|
|
146
146
|
// If some children did match, then add the parent row with its matched children.
|
|
147
147
|
if (matchedChildren.length > 0) {
|
|
@@ -152,7 +152,7 @@ function GridTable(props) {
|
|
|
152
152
|
return acc;
|
|
153
153
|
}, [filter, collapsedIds, columns]);
|
|
154
154
|
// Flatten + component-ize the sorted rows.
|
|
155
|
-
let [headerRows,
|
|
155
|
+
let [headerRows, visibleDataRows, totalsRows, filteredRowIds] = (0, react_1.useMemo)(() => {
|
|
156
156
|
function makeRowComponent(row, level) {
|
|
157
157
|
// We only pass sortState to header rows, b/c non-headers rows shouldn't have to re-render on sorting
|
|
158
158
|
// changes, and so by not passing the sortProps, it means the data rows' React.memo will still cache them.
|
|
@@ -178,20 +178,25 @@ function GridTable(props) {
|
|
|
178
178
|
// Split out the header rows from the data rows so that we can put an `infoMessage` in between them (if needed).
|
|
179
179
|
const headerRows = [];
|
|
180
180
|
const totalsRows = [];
|
|
181
|
-
const
|
|
181
|
+
const visibleDataRows = [];
|
|
182
|
+
const filteredRowIds = [];
|
|
182
183
|
// Misc state to track our nested card-ification, i.e. interleaved actual rows + chrome rows
|
|
183
|
-
const nestedCards = !!style.nestedCards && new nestedCards_1.NestedCards(columns,
|
|
184
|
-
function visit([row, children], level) {
|
|
185
|
-
let isCard = nestedCards && nestedCards.maybeOpenCard(row);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
const nestedCards = !!style.nestedCards && new nestedCards_1.NestedCards(columns, visibleDataRows, style.nestedCards);
|
|
185
|
+
function visit([row, children], level, visible) {
|
|
186
|
+
let isCard = visible && nestedCards && nestedCards.maybeOpenCard(row);
|
|
187
|
+
visible && visibleDataRows.push([row, makeRowComponent(row, level)]);
|
|
188
|
+
// This row may be invisible (because it's parent is collapsed), but we still want
|
|
189
|
+
// to consider it matched if it or it's parent matched a filter.
|
|
190
|
+
filteredRowIds.push(row.id);
|
|
191
|
+
if (children.length) {
|
|
192
|
+
// Consider "isCollapsed" as true if the parent wasn't visible.
|
|
193
|
+
const isCollapsed = !visible || collapsedIds.includes(row.id);
|
|
194
|
+
!isCollapsed && nestedCards && nestedCards.addSpacer();
|
|
195
|
+
visitRows(children, isCard, level + 1, !isCollapsed);
|
|
191
196
|
}
|
|
192
|
-
!(0, nestedCards_1.isLeafRow)(row) && isCard && nestedCards && nestedCards.closeCard();
|
|
197
|
+
visible && !(0, nestedCards_1.isLeafRow)(row) && isCard && nestedCards && nestedCards.closeCard();
|
|
193
198
|
}
|
|
194
|
-
function visitRows(rows, addSpacer, level) {
|
|
199
|
+
function visitRows(rows, addSpacer, level, visible) {
|
|
195
200
|
const length = rows.length;
|
|
196
201
|
rows.forEach((row, i) => {
|
|
197
202
|
if (row[0].kind === "header") {
|
|
@@ -202,15 +207,15 @@ function GridTable(props) {
|
|
|
202
207
|
totalsRows.push([row[0], makeRowComponent(row[0], level)]);
|
|
203
208
|
return;
|
|
204
209
|
}
|
|
205
|
-
visit(row, level);
|
|
206
|
-
addSpacer && nestedCards && i !== length - 1 && nestedCards.addSpacer();
|
|
210
|
+
visit(row, level, visible);
|
|
211
|
+
visible && addSpacer && nestedCards && i !== length - 1 && nestedCards.addSpacer();
|
|
207
212
|
});
|
|
208
213
|
}
|
|
209
214
|
// Call `visitRows` with our a pre-filtered set list
|
|
210
215
|
// If nestedCards is set, we assume the top-level kind is a card, and so should add spacers between them
|
|
211
|
-
visitRows(maybeSorted.reduce(filterRows, []), !!nestedCards, 0);
|
|
216
|
+
visitRows(maybeSorted.reduce(filterRows, []), !!nestedCards, 0, true);
|
|
212
217
|
nestedCards && nestedCards.done();
|
|
213
|
-
return [headerRows,
|
|
218
|
+
return [headerRows, visibleDataRows, totalsRows, filteredRowIds];
|
|
214
219
|
}, [
|
|
215
220
|
as,
|
|
216
221
|
maybeSorted,
|
|
@@ -230,21 +235,21 @@ function GridTable(props) {
|
|
|
230
235
|
hasTotalsRow,
|
|
231
236
|
]);
|
|
232
237
|
let tooManyClientSideRows = false;
|
|
233
|
-
if (filterMaxRows &&
|
|
238
|
+
if (filterMaxRows && visibleDataRows.length > filterMaxRows) {
|
|
234
239
|
tooManyClientSideRows = true;
|
|
235
|
-
|
|
240
|
+
visibleDataRows = visibleDataRows.slice(0, filterMaxRows);
|
|
236
241
|
}
|
|
237
|
-
rowState.
|
|
242
|
+
rowState.setMatchedRows(filteredRowIds);
|
|
238
243
|
// Push back to the caller a way to ask us where a row is.
|
|
239
244
|
const { rowLookup } = props;
|
|
240
245
|
if (rowLookup) {
|
|
241
246
|
// Refs are cheap to assign to, so we don't bother doing this in a useEffect
|
|
242
|
-
rowLookup.current = (0, GridRowLookup_1.createRowLookup)(columns,
|
|
247
|
+
rowLookup.current = (0, GridRowLookup_1.createRowLookup)(columns, visibleDataRows, virtuosoRef);
|
|
243
248
|
}
|
|
244
249
|
(0, react_1.useEffect)(() => {
|
|
245
|
-
setRowCount && (
|
|
246
|
-
}, [
|
|
247
|
-
const noData =
|
|
250
|
+
setRowCount && (visibleDataRows === null || visibleDataRows === void 0 ? void 0 : visibleDataRows.length) !== undefined && setRowCount(visibleDataRows.length);
|
|
251
|
+
}, [visibleDataRows === null || visibleDataRows === void 0 ? void 0 : visibleDataRows.length, setRowCount]);
|
|
252
|
+
const noData = visibleDataRows.length === 0;
|
|
248
253
|
const firstRowMessage = (noData && fallbackMessage) || (tooManyClientSideRows && "Hiding some rows, use filter...") || infoMessage;
|
|
249
254
|
const borderless = (_a = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _a === void 0 ? void 0 : _a.borderless;
|
|
250
255
|
const typeScale = (_b = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _b === void 0 ? void 0 : _b.typeScale;
|
|
@@ -263,7 +268,7 @@ function GridTable(props) {
|
|
|
263
268
|
// behave semantically the same as `as=div` did for its tests.
|
|
264
269
|
const _as = as === "virtual" && runningInJest ? "div" : as;
|
|
265
270
|
const rowStateContext = (0, react_1.useMemo)(() => ({ rowState }), [rowState]);
|
|
266
|
-
return ((0, jsx_runtime_1.jsx)(RowState_1.RowStateContext.Provider, Object.assign({ value: rowStateContext }, { children: (0, jsx_runtime_1.jsxs)(PresentationContext_1.PresentationProvider, Object.assign({ fieldProps: fieldProps, wrap: (_c = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _c === void 0 ? void 0 : _c.wrap }, { children: [(0, jsx_runtime_1.jsx)("div", { ref: resizeRef, css: Css_1.Css.w100.$ }, void 0), renders[_as](style, id, columns, headerRows, totalsRows,
|
|
271
|
+
return ((0, jsx_runtime_1.jsx)(RowState_1.RowStateContext.Provider, Object.assign({ value: rowStateContext }, { children: (0, jsx_runtime_1.jsxs)(PresentationContext_1.PresentationProvider, Object.assign({ fieldProps: fieldProps, wrap: (_c = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _c === void 0 ? void 0 : _c.wrap }, { children: [(0, jsx_runtime_1.jsx)("div", { ref: resizeRef, css: Css_1.Css.w100.$ }, void 0), renders[_as](style, id, columns, headerRows, totalsRows, visibleDataRows, firstRowMessage, stickyHeader, (_d = style.nestedCards) === null || _d === void 0 ? void 0 : _d.firstLastColumnWidth, xss, virtuosoRef)] }), void 0) }), void 0));
|
|
267
272
|
}
|
|
268
273
|
exports.GridTable = GridTable;
|
|
269
274
|
// Determine which HTML element to use to build the GridTable
|
|
@@ -273,7 +278,7 @@ const renders = {
|
|
|
273
278
|
virtual: renderVirtual,
|
|
274
279
|
};
|
|
275
280
|
/** Renders table using divs with flexbox rows, which is the default render */
|
|
276
|
-
function renderDiv(style, id, columns, headerRows, totalsRows,
|
|
281
|
+
function renderDiv(style, id, columns, headerRows, totalsRows, visibleDataRows, firstRowMessage, _stickyHeader, firstLastColumnWidth, xss, _virtuosoRef) {
|
|
277
282
|
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
|
|
278
283
|
// Use `fit-content` to ensure the width of the table takes up the full width of its content.
|
|
279
284
|
// Otherwise, the table's width would be that of its container, which may not be as wide as the table itself.
|
|
@@ -290,10 +295,10 @@ function renderDiv(style, id, columns, headerRows, totalsRows, filteredRows, fir
|
|
|
290
295
|
...style.rootCss,
|
|
291
296
|
...(style.minWidthPx ? Css_1.Css.mwPx(style.minWidthPx).$ : {}),
|
|
292
297
|
...xss,
|
|
293
|
-
}, "data-testid": id }, { children: [totalsRows.map(([, node]) => node), headerRows.map(([, node]) => node), firstRowMessage && ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: { ...style.firstRowMessageCss }, "data-gridrow": true }, { children: firstRowMessage }), void 0)),
|
|
298
|
+
}, "data-testid": id }, { children: [totalsRows.map(([, node]) => node), headerRows.map(([, node]) => node), firstRowMessage && ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: { ...style.firstRowMessageCss }, "data-gridrow": true }, { children: firstRowMessage }), void 0)), visibleDataRows.map(([, node]) => node)] }), void 0));
|
|
294
299
|
}
|
|
295
300
|
/** Renders as a table, primarily/solely for good print support. */
|
|
296
|
-
function renderTable(style, id, columns, headerRows, totalsRows,
|
|
301
|
+
function renderTable(style, id, columns, headerRows, totalsRows, visibleDataRows, firstRowMessage, _stickyHeader, _firstLastColumnWidth, xss, _virtuosoRef) {
|
|
297
302
|
return ((0, jsx_runtime_1.jsxs)("table", Object.assign({ css: {
|
|
298
303
|
...Css_1.Css.w100.add("borderCollapse", "collapse").$,
|
|
299
304
|
...Css_1.Css.addIn("& > tbody > tr ", style.betweenRowsCss || {})
|
|
@@ -302,7 +307,7 @@ function renderTable(style, id, columns, headerRows, totalsRows, filteredRows, f
|
|
|
302
307
|
...style.rootCss,
|
|
303
308
|
...(style.minWidthPx ? Css_1.Css.mwPx(style.minWidthPx).$ : {}),
|
|
304
309
|
...xss,
|
|
305
|
-
}, "data-testid": id }, { children: [(0, jsx_runtime_1.jsx)("thead", { children: [...totalsRows, ...headerRows].map(([, node]) => node) }, void 0), (0, jsx_runtime_1.jsxs)("tbody", { children: [firstRowMessage && ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", Object.assign({ colSpan: columns.length, css: { ...style.firstRowMessageCss } }, { children: firstRowMessage }), void 0) }, void 0)),
|
|
310
|
+
}, "data-testid": id }, { children: [(0, jsx_runtime_1.jsx)("thead", { children: [...totalsRows, ...headerRows].map(([, node]) => node) }, void 0), (0, jsx_runtime_1.jsxs)("tbody", { children: [firstRowMessage && ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", Object.assign({ colSpan: columns.length, css: { ...style.firstRowMessageCss } }, { children: firstRowMessage }), void 0) }, void 0)), visibleDataRows.map(([, node]) => node)] }, void 0)] }), void 0));
|
|
306
311
|
}
|
|
307
312
|
/**
|
|
308
313
|
* Uses react-virtuoso to render rows virtually.
|
|
@@ -324,7 +329,7 @@ function renderTable(style, id, columns, headerRows, totalsRows, filteredRows, f
|
|
|
324
329
|
* [2]: https://github.com/tannerlinsley/react-virtual/issues/85
|
|
325
330
|
* [3]: https://github.com/tannerlinsley/react-virtual/issues/108
|
|
326
331
|
*/
|
|
327
|
-
function renderVirtual(style, id, columns, headerRows, totalsRows,
|
|
332
|
+
function renderVirtual(style, id, columns, headerRows, totalsRows, visibleDataRows, firstRowMessage, stickyHeader, firstLastColumnWidth, xss, virtuosoRef) {
|
|
328
333
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
329
334
|
const { footerStyle, listStyle } = (0, react_1.useMemo)(() => {
|
|
330
335
|
var _a;
|
|
@@ -363,8 +368,8 @@ function renderVirtual(style, id, columns, headerRows, totalsRows, filteredRows,
|
|
|
363
368
|
index--;
|
|
364
369
|
}
|
|
365
370
|
// Lastly render `filteredRow`
|
|
366
|
-
return
|
|
367
|
-
}, totalCount: (headerRows.length || 0) + (totalsRows.length || 0) + (firstRowMessage ? 1 : 0) + (
|
|
371
|
+
return visibleDataRows[index][1];
|
|
372
|
+
}, totalCount: (headerRows.length || 0) + (totalsRows.length || 0) + (firstRowMessage ? 1 : 0) + (visibleDataRows.length || 0) }, void 0));
|
|
368
373
|
}
|
|
369
374
|
/**
|
|
370
375
|
* A table might render two of these components to represent two virtual lists.
|
|
@@ -20,7 +20,7 @@ export declare class RowState {
|
|
|
20
20
|
private readonly collapsedRows;
|
|
21
21
|
private persistCollapse;
|
|
22
22
|
private readonly selectedRows;
|
|
23
|
-
private
|
|
23
|
+
private matchedRows;
|
|
24
24
|
rows: GridDataRow<any>[];
|
|
25
25
|
activeRowId: string | undefined;
|
|
26
26
|
/**
|
|
@@ -29,14 +29,14 @@ export declare class RowState {
|
|
|
29
29
|
constructor();
|
|
30
30
|
loadCollapse(persistCollapse: string | undefined, rows: GridDataRow<any>[]): void;
|
|
31
31
|
setRows(rows: GridDataRow<any>[]): void;
|
|
32
|
-
|
|
32
|
+
setMatchedRows(rowIds: string[]): void;
|
|
33
33
|
get selectedIds(): string[];
|
|
34
34
|
getSelected(id: string): SelectedState;
|
|
35
35
|
selectRow(id: string, selected: boolean): void;
|
|
36
36
|
get collapsedIds(): string[];
|
|
37
37
|
isCollapsed(id: string): boolean;
|
|
38
38
|
toggleCollapsed(id: string): void;
|
|
39
|
-
private
|
|
39
|
+
private getMatchedChildrenStates;
|
|
40
40
|
private setNestedSelectedStates;
|
|
41
41
|
}
|
|
42
42
|
/** Provides a context for rows to access their table's `RowState`. */
|
|
@@ -30,8 +30,8 @@ class RowState {
|
|
|
30
30
|
// A set of just row ids, i.e. not row.kind+row.id
|
|
31
31
|
this.collapsedRows = new mobx_1.ObservableSet();
|
|
32
32
|
this.selectedRows = new mobx_1.ObservableMap();
|
|
33
|
-
// Set of just row ids. Keeps track of which rows
|
|
34
|
-
this.
|
|
33
|
+
// Set of just row ids. Keeps track of which rows match the filter. Used to filter rows from `selectedIds`
|
|
34
|
+
this.matchedRows = new mobx_1.ObservableSet();
|
|
35
35
|
// The current list of rows, basically a useRef.current. Not reactive.
|
|
36
36
|
this.rows = [];
|
|
37
37
|
// Keeps track of the 'active' row, formatted `${row.kind}_${row.id}`
|
|
@@ -42,11 +42,11 @@ class RowState {
|
|
|
42
42
|
// We only shallow observe rows so that:
|
|
43
43
|
// a) we don't deeply/needlessly proxy-ize a large Apollo fragment cache, but
|
|
44
44
|
// b) if rows changes, we re-run computeds like getSelectedRows that may need to see the
|
|
45
|
-
// updated _contents_ of a given row, even if our other selected/
|
|
45
|
+
// updated _contents_ of a given row, even if our other selected/matched row states don't change.
|
|
46
46
|
// (as any b/c rows is private, so the mapped type doesn't see it)
|
|
47
47
|
{ rows: mobx_1.observable.shallow });
|
|
48
|
-
// Whenever our `
|
|
49
|
-
(0, mobx_1.reaction)(() => [...this.
|
|
48
|
+
// Whenever our `matchedRows` change (i.e. via filtering) then we need to re-derive header and parent rows' selected state.
|
|
49
|
+
(0, mobx_1.reaction)(() => [...this.matchedRows.values()].sort(), () => {
|
|
50
50
|
const map = new Map();
|
|
51
51
|
map.set("header", deriveParentSelected(this.rows.flatMap((row) => this.setNestedSelectedStates(row, map))));
|
|
52
52
|
// Merge the changes back into the selected rows state
|
|
@@ -95,20 +95,20 @@ class RowState {
|
|
|
95
95
|
// Finally replace our existing list of rows
|
|
96
96
|
this.rows = rows;
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
setMatchedRows(rowIds) {
|
|
99
99
|
// ObservableSet doesn't seem to do a `diff` inside `replace` before firing
|
|
100
100
|
// observers/reactions that watch it, which can lead to render loops with the
|
|
101
101
|
// application page is observing `GridTableApi.getSelectedRows`, and merely
|
|
102
102
|
// the act of rendering GridTable (w/o row changes) causes it's `useComputed`
|
|
103
103
|
// to be triggered.
|
|
104
|
-
if (!mobx_1.comparer.shallow(rowIds, [...this.
|
|
105
|
-
this.
|
|
104
|
+
if (!mobx_1.comparer.shallow(rowIds, [...this.matchedRows.values()])) {
|
|
105
|
+
this.matchedRows.replace(rowIds);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
get selectedIds() {
|
|
109
109
|
// Return only ids that are fully checked, i.e. not partial
|
|
110
110
|
const ids = [...this.selectedRows.entries()]
|
|
111
|
-
.filter(([id, v]) => this.
|
|
111
|
+
.filter(([id, v]) => this.matchedRows.has(id) && v === "checked")
|
|
112
112
|
.map(([k]) => k);
|
|
113
113
|
// Hide our header marker
|
|
114
114
|
const headerIndex = ids.indexOf("header");
|
|
@@ -129,7 +129,7 @@ class RowState {
|
|
|
129
129
|
// Just mash the header + all rows + children as selected
|
|
130
130
|
const map = new Map();
|
|
131
131
|
map.set("header", "checked");
|
|
132
|
-
(0, visitor_1.visit)(this.rows, (row) => this.
|
|
132
|
+
(0, visitor_1.visit)(this.rows, (row) => this.matchedRows.has(row.id) && row.kind !== "totals" && map.set(row.id, "checked"));
|
|
133
133
|
this.selectedRows.replace(map);
|
|
134
134
|
}
|
|
135
135
|
else {
|
|
@@ -147,15 +147,15 @@ class RowState {
|
|
|
147
147
|
}
|
|
148
148
|
// Everything here & down is deterministically on/off
|
|
149
149
|
const map = new Map();
|
|
150
|
-
(0, visitor_1.visit)([curr.row], (row) => this.
|
|
150
|
+
(0, visitor_1.visit)([curr.row], (row) => this.matchedRows.has(row.id) && map.set(row.id, selected ? "checked" : "unchecked"));
|
|
151
151
|
// Now walk up the parents and see if they are now-all-checked/now-all-unchecked/some-of-each
|
|
152
152
|
for (const parent of [...curr.parents].reverse()) {
|
|
153
153
|
if (parent.children) {
|
|
154
|
-
map.set(parent.id, deriveParentSelected(this.
|
|
154
|
+
map.set(parent.id, deriveParentSelected(this.getMatchedChildrenStates(parent.children, map)));
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
// And do the header + top-level "children" as a final one-off
|
|
158
|
-
map.set("header", deriveParentSelected(this.
|
|
158
|
+
map.set("header", deriveParentSelected(this.getMatchedChildrenStates(this.rows, map)));
|
|
159
159
|
this.selectedRows.merge(map);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -218,14 +218,14 @@ class RowState {
|
|
|
218
218
|
sessionStorage.setItem(this.persistCollapse, JSON.stringify(collapsedIds));
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
|
|
221
|
+
getMatchedChildrenStates(children, map) {
|
|
222
222
|
return children
|
|
223
|
-
.filter((row) => row.id !== "header" && this.
|
|
223
|
+
.filter((row) => row.id !== "header" && this.matchedRows.has(row.id))
|
|
224
224
|
.map((row) => map.get(row.id) || this.getSelected(row.id));
|
|
225
225
|
}
|
|
226
226
|
// Recursively traverse through rows to determine selected state of parent rows based on children
|
|
227
227
|
setNestedSelectedStates(row, map) {
|
|
228
|
-
if (this.
|
|
228
|
+
if (this.matchedRows.has(row.id)) {
|
|
229
229
|
if (!row.children) {
|
|
230
230
|
return [this.getSelected(row.id)];
|
|
231
231
|
}
|
|
@@ -2,7 +2,7 @@ import { ReactNode } from "react";
|
|
|
2
2
|
import { Value } from "./";
|
|
3
3
|
import { BeamSelectFieldBaseProps } from "./internal/SelectFieldBase";
|
|
4
4
|
import { HasIdAndName, Optional } from "../types";
|
|
5
|
-
export interface MultiSelectFieldProps<O, V extends Value> extends BeamSelectFieldBaseProps<O, V> {
|
|
5
|
+
export interface MultiSelectFieldProps<O, V extends Value> extends Exclude<BeamSelectFieldBaseProps<O, V>, "unsetLabel"> {
|
|
6
6
|
/** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. */
|
|
7
7
|
getOptionMenuLabel?: (opt: O) => string | ReactNode;
|
|
8
8
|
getOptionValue: (opt: O) => V;
|
|
@@ -4,7 +4,7 @@ import { HasIdAndName, Optional } from "../types";
|
|
|
4
4
|
export interface SelectFieldProps<O, V extends Value> extends Omit<BeamSelectFieldBaseProps<O, V>, "values" | "onSelect"> {
|
|
5
5
|
/** The current value; it can be `undefined`, even if `V` cannot be. */
|
|
6
6
|
value: V | undefined;
|
|
7
|
-
onSelect: (value: V, opt: O) => void;
|
|
7
|
+
onSelect: (value: V | undefined, opt: O | undefined) => void;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Provides a non-native select/dropdown widget.
|
|
@@ -9,7 +9,8 @@ function SelectField(props) {
|
|
|
9
9
|
options, onSelect, value, ...otherProps } = props;
|
|
10
10
|
return ((0, jsx_runtime_1.jsx)(SelectFieldBase_1.SelectFieldBase, Object.assign({}, otherProps, { options: options, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, values: [value], onSelect: (values, options) => {
|
|
11
11
|
if (values.length > 0 && options.length > 0) {
|
|
12
|
-
|
|
12
|
+
const option = options[0];
|
|
13
|
+
onSelect(values[0], option === SelectFieldBase_1.unsetOption ? undefined : option);
|
|
13
14
|
}
|
|
14
15
|
} }), void 0));
|
|
15
16
|
}
|
|
@@ -3,8 +3,8 @@ import { PresentationFieldProps } from "../../components/PresentationContext";
|
|
|
3
3
|
import { Value } from "../Value";
|
|
4
4
|
import { BeamFocusableProps } from "../../interfaces";
|
|
5
5
|
export interface BeamSelectFieldBaseProps<O, V extends Value> extends BeamFocusableProps, PresentationFieldProps {
|
|
6
|
-
/** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. */
|
|
7
|
-
getOptionMenuLabel?: (opt: O) => string | ReactNode;
|
|
6
|
+
/** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. `isUnsetOpt` is only defined for single SelectField */
|
|
7
|
+
getOptionMenuLabel?: (opt: O, isUnsetOpt?: boolean) => string | ReactNode;
|
|
8
8
|
getOptionValue: (opt: O) => V;
|
|
9
9
|
getOptionLabel: (opt: O) => string;
|
|
10
10
|
/** The current value; it can be `undefined`, even if `V` cannot be. */
|
|
@@ -12,12 +12,7 @@ export interface BeamSelectFieldBaseProps<O, V extends Value> extends BeamFocusa
|
|
|
12
12
|
onSelect: (values: V[], opts: O[]) => void;
|
|
13
13
|
multiselect?: boolean;
|
|
14
14
|
disabledOptions?: V[];
|
|
15
|
-
options: O
|
|
16
|
-
initial: O[];
|
|
17
|
-
load: () => Promise<{
|
|
18
|
-
options: O[];
|
|
19
|
-
}>;
|
|
20
|
-
};
|
|
15
|
+
options: OptionsOrLoad<O>;
|
|
21
16
|
/** Whether the field is disabled. If a ReactNode, it's treated as a "disabled reason" that's shown in a tooltip. */
|
|
22
17
|
disabled?: boolean | ReactNode;
|
|
23
18
|
required?: boolean;
|
|
@@ -39,6 +34,8 @@ export interface BeamSelectFieldBaseProps<O, V extends Value> extends BeamFocusa
|
|
|
39
34
|
contrast?: boolean;
|
|
40
35
|
/** Placeholder content */
|
|
41
36
|
placeholder?: string;
|
|
37
|
+
/** Only used for Single Select Fields. If set, prepends an option with a `undefined` value at the top of the list */
|
|
38
|
+
unsetLabel?: string;
|
|
42
39
|
}
|
|
43
40
|
/**
|
|
44
41
|
* Provides a non-native select/dropdown widget.
|
|
@@ -50,3 +47,12 @@ export interface BeamSelectFieldBaseProps<O, V extends Value> extends BeamFocusa
|
|
|
50
47
|
* and so we cannot easily change them.
|
|
51
48
|
*/
|
|
52
49
|
export declare function SelectFieldBase<O, V extends Value>(props: BeamSelectFieldBaseProps<O, V>): JSX.Element;
|
|
50
|
+
declare type OptionsOrLoad<O> = O[] | {
|
|
51
|
+
initial: O[];
|
|
52
|
+
load: () => Promise<{
|
|
53
|
+
options: O[];
|
|
54
|
+
}>;
|
|
55
|
+
};
|
|
56
|
+
export declare function initializeOptions<O>(options: OptionsOrLoad<O>, unsetLabel: string | undefined): OptionsOrLoad<O>;
|
|
57
|
+
export declare const unsetOption: {};
|
|
58
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SelectFieldBase = void 0;
|
|
3
|
+
exports.unsetOption = exports.initializeOptions = exports.SelectFieldBase = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const react_aria_1 = require("react-aria");
|
|
@@ -23,7 +23,15 @@ const utils_1 = require("../../utils");
|
|
|
23
23
|
*/
|
|
24
24
|
function SelectFieldBase(props) {
|
|
25
25
|
var _a;
|
|
26
|
-
const { disabled, readOnly, onSelect, options
|
|
26
|
+
const { disabled, readOnly, onSelect, options, multiselect = false, values = [], nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, ...otherProps } = props;
|
|
27
|
+
// Call `initializeOptions` to prepend the `unset` option if the `unsetLabel` was provided.
|
|
28
|
+
const maybeOptions = (0, react_1.useMemo)(() => initializeOptions(options, unsetLabel), [options, unsetLabel]);
|
|
29
|
+
// Memoize the callback functions and handle the `unset` option if provided.
|
|
30
|
+
const getOptionLabel = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? unsetLabel : props.getOptionLabel(o)), [props.getOptionLabel, unsetLabel]);
|
|
31
|
+
const getOptionValue = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? undefined : props.getOptionValue(o)), [props.getOptionValue, unsetLabel]);
|
|
32
|
+
const getOptionMenuLabel = (0, react_1.useCallback)((o) => props.getOptionMenuLabel
|
|
33
|
+
? props.getOptionMenuLabel(o, Boolean(unsetLabel) && o === exports.unsetOption)
|
|
34
|
+
: getOptionLabel(o), [props.getOptionValue, unsetLabel, getOptionLabel]);
|
|
27
35
|
const { contains } = (0, react_aria_1.useFilter)({ sensitivity: "base" });
|
|
28
36
|
const isDisabled = !!disabled;
|
|
29
37
|
const isReadOnly = !!readOnly;
|
|
@@ -105,7 +113,9 @@ function SelectFieldBase(props) {
|
|
|
105
113
|
async function maybeInitLoad() {
|
|
106
114
|
if (!Array.isArray(maybeOptions)) {
|
|
107
115
|
setFieldState((prevState) => ({ ...prevState, optionsLoading: true }));
|
|
108
|
-
const
|
|
116
|
+
const loadedOptions = (await maybeOptions.load()).options;
|
|
117
|
+
// Ensure the `unset` option is prepended to the top of the list if `unsetLabel` was provided
|
|
118
|
+
const options = !unsetLabel ? loadedOptions : getOptionsWithUnset(unsetLabel, loadedOptions);
|
|
109
119
|
setFieldState((prevState) => ({
|
|
110
120
|
...prevState,
|
|
111
121
|
filteredOptions: options,
|
|
@@ -245,3 +255,17 @@ function getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSele
|
|
|
245
255
|
? nothingSelectedText
|
|
246
256
|
: "";
|
|
247
257
|
}
|
|
258
|
+
function initializeOptions(options, unsetLabel) {
|
|
259
|
+
if (!unsetLabel) {
|
|
260
|
+
return options;
|
|
261
|
+
}
|
|
262
|
+
if (Array.isArray(options)) {
|
|
263
|
+
return getOptionsWithUnset(unsetLabel, options);
|
|
264
|
+
}
|
|
265
|
+
return { ...options, initial: getOptionsWithUnset(unsetLabel, options.initial) };
|
|
266
|
+
}
|
|
267
|
+
exports.initializeOptions = initializeOptions;
|
|
268
|
+
function getOptionsWithUnset(unsetLabel, options) {
|
|
269
|
+
return [exports.unsetOption, ...options];
|
|
270
|
+
}
|
|
271
|
+
exports.unsetOption = {};
|
package/dist/types.d.ts
CHANGED
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebound/beam",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.137.0",
|
|
4
4
|
"author": "Homebound",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"format": "prettier --loglevel warn --write \"**/*.{ts,tsx,css,md}\""
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@homebound/form-state": "2.
|
|
35
|
+
"@homebound/form-state": "2.15.1",
|
|
36
36
|
"@internationalized/number": "^3.0.3",
|
|
37
37
|
"@react-aria/utils": "^3.11.3",
|
|
38
38
|
"@react-hook/resize-observer": "^1.2.2",
|