@dynamic-framework/ui-react 2.5.1 → 2.6.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 +4 -0
- package/dist/css/dynamic-ui.css +38 -10
- package/dist/css/dynamic-ui.min.css +2 -2
- package/dist/index.esm.js +466 -83
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +471 -82
- package/dist/index.js.map +1 -1
- package/dist/types/components/DAlert/DAlert.d.ts +1 -1
- package/dist/types/components/DAvatar/DAvatar.d.ts +2 -1
- package/dist/types/components/DBadge/DBadge.d.ts +2 -1
- package/dist/types/components/DBox/DBox.d.ts +1 -1
- package/dist/types/components/DBoxFile/DBoxFile.d.ts +1 -1
- package/dist/types/components/DButtonIcon/DButtonIcon.d.ts +1 -1
- package/dist/types/components/DCard/DCard.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardBody.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardFooter.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardHeader.d.ts +1 -1
- package/dist/types/components/DCarousel/components/DCarouselSlide.d.ts +1 -1
- package/dist/types/components/DChip/DChip.d.ts +2 -1
- package/dist/types/components/DCollapse/DCollapse.d.ts +1 -1
- package/dist/types/components/DConfirmModal/DConfirmModalContainer.d.ts +21 -0
- package/dist/types/components/DConfirmModal/DConfirmModalUI.d.ts +7 -0
- package/dist/types/components/DConfirmModal/confirmModalStore.d.ts +6 -0
- package/dist/types/components/DConfirmModal/types.d.ts +45 -0
- package/dist/types/components/DCreditCard/DCreditCard.d.ts +2 -1
- package/dist/types/components/DCurrencyText/DCurrencyText.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/DDataStateWrapper.d.ts +8 -1
- package/dist/types/components/DDataStateWrapper/components/EmptyState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/components/ErrorState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/components/LoadingState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/index.d.ts +4 -0
- package/dist/types/components/DDatePicker/DDatePicker.d.ts +2 -1
- package/dist/types/components/DDatePicker/components/DDatePickerHeaderSelector.d.ts +1 -1
- package/dist/types/components/DDatePicker/components/DDatePickerTime.d.ts +1 -1
- package/dist/types/components/DDropdown/DDropdown.d.ts +4 -1
- package/dist/types/components/DErrorBoundary/DErrorBoundary.d.ts +1 -1
- package/dist/types/components/DErrorBoundary/components/DefaultErrorBoundary.d.ts +2 -1
- package/dist/types/components/DIcon/DIcon.d.ts +1 -1
- package/dist/types/components/DIconBase/DIconBase.d.ts +2 -1
- package/dist/types/components/DInputCheck/DInputCheck.d.ts +1 -1
- package/dist/types/components/DInputPin/DInputPin.d.ts +2 -1
- package/dist/types/components/DInputSearch/DInputSearch.d.ts +11 -0
- package/dist/types/components/DInputSearch/index.d.ts +3 -0
- package/dist/types/components/DInputSelect/DInputSelect.d.ts +1 -1
- package/dist/types/components/DInputSwitch/DInputSwitch.d.ts +2 -1
- package/dist/types/components/DLayout/DLayout.d.ts +1 -1
- package/dist/types/components/DLayout/components/DLayoutPane.d.ts +1 -1
- package/dist/types/components/DListGroup/DListGroup.d.ts +1 -1
- package/dist/types/components/DListGroup/components/DListGroupItem.d.ts +1 -1
- package/dist/types/components/DModal/DModal.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalBody.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalFooter.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalHeader.d.ts +1 -1
- package/dist/types/components/DOffcanvas/DOffcanvas.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasBody.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasFooter.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasHeader.d.ts +1 -1
- package/dist/types/components/DOtp/DOtp.d.ts +1 -1
- package/dist/types/components/DOtp/components/OtpCountdown.d.ts +2 -1
- package/dist/types/components/DPaginator/DPaginator.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/DPasswordStrengthMeter.d.ts +3 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordCheckItem.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordCheckList.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordStrength.d.ts +2 -1
- package/dist/types/components/DPopover/DPopover.d.ts +1 -1
- package/dist/types/components/DProgress/DProgress.d.ts +2 -1
- package/dist/types/components/DSelect/DSelect.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectClearIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectDropdownIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectLoadingIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectMultiValueRemove.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionCheck.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionEmoji.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionIcon.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectPlaceholder.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueEmoji.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueEmojiText.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueIconText.d.ts +2 -1
- package/dist/types/components/DStepper/DStepper.d.ts +2 -1
- package/dist/types/components/DStepperDesktop/DStepperDesktop.d.ts +2 -1
- package/dist/types/components/DStepperMobile/DStepperMobile.d.ts +2 -1
- package/dist/types/components/DTabs/DTabs.d.ts +1 -1
- package/dist/types/components/DTabs/components/DTabContent.d.ts +1 -1
- package/dist/types/components/DTimeline/DTimeline.d.ts +1 -1
- package/dist/types/components/DToast/DToast.d.ts +1 -1
- package/dist/types/components/DToast/components/DToastBody.d.ts +1 -1
- package/dist/types/components/DToast/components/DToastHeader.d.ts +1 -1
- package/dist/types/components/DToastContainer/DToastContainer.d.ts +1 -1
- package/dist/types/components/DTooltip/DTooltip.d.ts +1 -1
- package/dist/types/components/DVoucher/DVoucher.d.ts +1 -1
- package/dist/types/components/index.d.ts +6 -1
- package/dist/types/components/interface.d.ts +1 -1
- package/dist/types/contexts/DContext.d.ts +25 -3
- package/dist/types/contexts/DPortalContext.d.ts +1 -1
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/useConfirmModal.d.ts +34 -0
- package/package.json +7 -2
- package/src/style/_shame.scss +13 -0
- package/src/style/abstracts/variables/_forms.scss +1 -1
- package/src/style/abstracts/variables/_list-group.scss +1 -1
- package/src/style/base/_backdrop.scss +7 -0
- package/src/style/components/_d-carousel.scss +1 -1
- package/src/style/components/_d-modal.scss +8 -0
- package/src/style/root/_root.scss +31 -0
package/dist/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function useDisableBodyScrollEffect(disable) {
|
|
|
78
78
|
timer = window.setTimeout(() => {
|
|
79
79
|
unlock();
|
|
80
80
|
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
81
|
-
},
|
|
81
|
+
}, 300);
|
|
82
82
|
}
|
|
83
83
|
else {
|
|
84
84
|
unlock();
|
|
@@ -163,7 +163,7 @@ function getKeyboardFocusableElements(container) {
|
|
|
163
163
|
const DPortalContext = React.createContext(undefined);
|
|
164
164
|
function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
165
165
|
const { created } = usePortal(portalName);
|
|
166
|
-
const [stack, { push, pop
|
|
166
|
+
const [stack, { push, pop }] = useStackState([]);
|
|
167
167
|
useDisableBodyScrollEffect(Boolean(stack.length));
|
|
168
168
|
const openPortal = React.useCallback(
|
|
169
169
|
// eslint-disable-next-line prefer-arrow-callback
|
|
@@ -184,11 +184,9 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
|
184
184
|
(_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
|
|
185
185
|
}, [availablePortals, push]);
|
|
186
186
|
const closePortal = React.useCallback(() => {
|
|
187
|
-
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
187
|
+
// pop() is safe on empty stacks, so close remains idempotent.
|
|
190
188
|
pop();
|
|
191
|
-
}, [
|
|
189
|
+
}, [pop]);
|
|
192
190
|
const publicStack = React.useMemo(() => stack.map(({ name, payload }) => ({ name, payload })), [stack]);
|
|
193
191
|
const value = React.useMemo(() => ({
|
|
194
192
|
stack: publicStack,
|
|
@@ -279,6 +277,40 @@ function useDPortalContext() {
|
|
|
279
277
|
return context;
|
|
280
278
|
}
|
|
281
279
|
|
|
280
|
+
function createConfirmModalStore() {
|
|
281
|
+
const listeners = new Set();
|
|
282
|
+
let entries = [];
|
|
283
|
+
function notify() {
|
|
284
|
+
listeners.forEach((listener) => listener([...entries]));
|
|
285
|
+
}
|
|
286
|
+
const store = {
|
|
287
|
+
subscribe(listener) {
|
|
288
|
+
listeners.add(listener);
|
|
289
|
+
listener([...entries]);
|
|
290
|
+
return () => {
|
|
291
|
+
listeners.delete(listener);
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
push(entry) {
|
|
295
|
+
entries = [...entries, entry];
|
|
296
|
+
notify();
|
|
297
|
+
},
|
|
298
|
+
remove(id) {
|
|
299
|
+
entries = entries.filter((e) => e.id !== id);
|
|
300
|
+
notify();
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
return store;
|
|
304
|
+
}
|
|
305
|
+
const ConfirmModalStoreContext = React.createContext(null);
|
|
306
|
+
function useConfirmModalStore() {
|
|
307
|
+
const store = React.useContext(ConfirmModalStoreContext);
|
|
308
|
+
if (!store) {
|
|
309
|
+
throw new Error('useConfirmModal must be used within a <DContextProvider>.');
|
|
310
|
+
}
|
|
311
|
+
return store;
|
|
312
|
+
}
|
|
313
|
+
|
|
282
314
|
function getCssVariable(variable) {
|
|
283
315
|
const computedStyle = getComputedStyle(document.documentElement);
|
|
284
316
|
return computedStyle.getPropertyValue(variable).trim();
|
|
@@ -337,6 +369,28 @@ const DContext = React.createContext(DEFAULT_STATE);
|
|
|
337
369
|
* component to configure icons, currency, language, and portal settings
|
|
338
370
|
* for all descendant Dynamic UI components.
|
|
339
371
|
*
|
|
372
|
+
* To enable confirmation modals you must also mount `DConfirmModalContainer`
|
|
373
|
+
* somewhere inside this provider (typically right before the closing tag of
|
|
374
|
+
* your root layout), similar to how `DToastContainer` works:
|
|
375
|
+
*
|
|
376
|
+
* ```tsx
|
|
377
|
+
* // Default: portalName="d-portal"
|
|
378
|
+
* <DContextProvider>
|
|
379
|
+
* <App />
|
|
380
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
381
|
+
* </DContextProvider>
|
|
382
|
+
* ```
|
|
383
|
+
*
|
|
384
|
+
* If you customize `portalName`, match it in `DConfirmModalContainer.nodeId`:
|
|
385
|
+
*
|
|
386
|
+
* ```tsx
|
|
387
|
+
* // Custom portalName
|
|
388
|
+
* <DContextProvider portalName="my-custom-portal">
|
|
389
|
+
* <App />
|
|
390
|
+
* <DConfirmModalContainer nodeId="my-custom-portal" />
|
|
391
|
+
* </DContextProvider>
|
|
392
|
+
* ```
|
|
393
|
+
*
|
|
340
394
|
* @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
|
|
341
395
|
* Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
|
|
342
396
|
*/
|
|
@@ -362,7 +416,8 @@ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAUL
|
|
|
362
416
|
});
|
|
363
417
|
}, [setContext]);
|
|
364
418
|
const value = React.useMemo(() => (Object.assign(Object.assign({}, internalContext), { setContext })), [internalContext, setContext]);
|
|
365
|
-
|
|
419
|
+
const confirmStore = React.useMemo(() => createConfirmModalStore(), []);
|
|
420
|
+
return (jsxRuntime.jsx(ConfirmModalStoreContext.Provider, { value: confirmStore, children: jsxRuntime.jsx(DContext.Provider, { value: value, children: jsxRuntime.jsx(DPortalContextProvider, { portalName: portalName, availablePortals: availablePortals, children: children }) }) }));
|
|
366
421
|
}
|
|
367
422
|
/**
|
|
368
423
|
* Returns the Dynamic UI context value set by `DContextProvider`.
|
|
@@ -1057,7 +1112,7 @@ function DBoxFile(_a) {
|
|
|
1057
1112
|
}
|
|
1058
1113
|
|
|
1059
1114
|
const DButton = React.forwardRef((props, ref) => {
|
|
1060
|
-
const { color = 'primary', size, variant, text, children, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, loading = false, loadingText, loadingAriaLabel, disabled = false, className, style, dataAttributes, onClick, type = 'button', target, rel, href, 'aria-label': ariaLabelProp } = props, rest = tslib.__rest(props, ["color", "size", "variant", "text", "children", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartMaterialStyle", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndMaterialStyle", "loading", "loadingText", "loadingAriaLabel", "disabled", "className", "style", "dataAttributes", "onClick", "type", "target", "rel", "href", 'aria-label']);
|
|
1115
|
+
const { color = 'primary', size, variant = 'solid', text, children, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, loading = false, loadingText, loadingAriaLabel, disabled = false, className, style, dataAttributes, onClick, type = 'button', target, rel, href, 'aria-label': ariaLabelProp } = props, rest = tslib.__rest(props, ["color", "size", "variant", "text", "children", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartMaterialStyle", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndMaterialStyle", "loading", "loadingText", "loadingAriaLabel", "disabled", "className", "style", "dataAttributes", "onClick", "type", "target", "rel", "href", 'aria-label']);
|
|
1061
1116
|
// Responsive size resolution using useResponsiveProp
|
|
1062
1117
|
const { responsivePropValue } = useResponsiveProp(true);
|
|
1063
1118
|
const resolvedSize = React.useMemo(() => {
|
|
@@ -1072,9 +1127,9 @@ const DButton = React.forwardRef((props, ref) => {
|
|
|
1072
1127
|
const isDisabled = React.useMemo(() => disabled || loading, [disabled, loading]);
|
|
1073
1128
|
const content = React.useMemo(() => children || text, [children, text]);
|
|
1074
1129
|
const classes = React.useMemo(() => {
|
|
1075
|
-
const variantClass = variant
|
|
1076
|
-
? `btn-${
|
|
1077
|
-
: `btn-${color}`;
|
|
1130
|
+
const variantClass = variant === 'solid'
|
|
1131
|
+
? `btn-${color}`
|
|
1132
|
+
: `btn-${variant}-${color}`;
|
|
1078
1133
|
return {
|
|
1079
1134
|
btn: true,
|
|
1080
1135
|
[variantClass]: true,
|
|
@@ -1132,9 +1187,9 @@ DButton.displayName = 'DButton';
|
|
|
1132
1187
|
function DButtonIcon(_a) {
|
|
1133
1188
|
var { id, icon, size, className, variant, state, loadingAriaLabel, iconMaterialStyle, disabled = false, color = 'primary', loading = false, href, target, rel, stopPropagationEnabled = true, style, iconFamilyClass, iconFamilyPrefix, dataAttributes, onClick, 'aria-label': ariaLabelProp } = _a, rest = tslib.__rest(_a, ["id", "icon", "size", "className", "variant", "state", "loadingAriaLabel", "iconMaterialStyle", "disabled", "color", "loading", "href", "target", "rel", "stopPropagationEnabled", "style", "iconFamilyClass", "iconFamilyPrefix", "dataAttributes", "onClick", 'aria-label']);
|
|
1134
1189
|
const generateClasses = React.useMemo(() => {
|
|
1135
|
-
const variantClass = variant
|
|
1136
|
-
? `btn-${
|
|
1137
|
-
: `btn-${color}`;
|
|
1190
|
+
const variantClass = !variant || variant === 'solid'
|
|
1191
|
+
? `btn-${color}`
|
|
1192
|
+
: `btn-${variant}-${color}`;
|
|
1138
1193
|
return Object.assign(Object.assign(Object.assign({ 'btn d-button-icon': true, [variantClass]: true }, size && { [`btn-${size}`]: true }), (state && state !== 'disabled') && { [state]: true }), { loading });
|
|
1139
1194
|
}, [variant, color, size, state, loading]);
|
|
1140
1195
|
const isDisabled = React.useMemo(() => (state === 'disabled' || loading || disabled), [state, loading, disabled]);
|
|
@@ -1426,6 +1481,18 @@ function DDatePickerHeaderSelector({ date, changeYear, changeMonth, decreaseMont
|
|
|
1426
1481
|
|
|
1427
1482
|
function DDatePicker(_a) {
|
|
1428
1483
|
var { inputLabel, inputHint, inputAriaLabel, ariaLabelInputTime, inputActionAriaLabel = 'open calendar', inputId = 'input-calendar', timeId = 'input-time', timeInputLabel, minYearSelect, maxYearSelect, iconHeaderSize, iconMaterialStyle, iconInput, headerPrevMonthAriaLabel, headerNextMonthAriaLabel, invalid = false, valid = false, renderCustomHeader: renderCustomHeaderProp, className, dateFormatCalendar: dateFormatCalendarProp, style, dataAttributes, placeholder, showHeaderSelectors, formatHeaderDate } = _a, props = tslib.__rest(_a, ["inputLabel", "inputHint", "inputAriaLabel", "ariaLabelInputTime", "inputActionAriaLabel", "inputId", "timeId", "timeInputLabel", "minYearSelect", "maxYearSelect", "iconHeaderSize", "iconMaterialStyle", "iconInput", "headerPrevMonthAriaLabel", "headerNextMonthAriaLabel", "invalid", "valid", "renderCustomHeader", "className", "dateFormatCalendar", "style", "dataAttributes", "placeholder", "showHeaderSelectors", "formatHeaderDate"]);
|
|
1484
|
+
// Some test runtimes can resolve react-datepicker as a module object.
|
|
1485
|
+
// Normalize to a renderable component for both function and { default } shapes.
|
|
1486
|
+
const SafeDatePicker = React.useMemo(() => {
|
|
1487
|
+
const interopDefault = DatePicker.default;
|
|
1488
|
+
if (interopDefault !== undefined) {
|
|
1489
|
+
return interopDefault;
|
|
1490
|
+
}
|
|
1491
|
+
if (typeof DatePicker === 'function') {
|
|
1492
|
+
return DatePicker;
|
|
1493
|
+
}
|
|
1494
|
+
return DatePicker;
|
|
1495
|
+
}, []);
|
|
1429
1496
|
const pickerType = React.useMemo(() => {
|
|
1430
1497
|
if (props.showQuarterYearPicker)
|
|
1431
1498
|
return PickerType.Quarter;
|
|
@@ -1453,7 +1520,7 @@ function DDatePicker(_a) {
|
|
|
1453
1520
|
]);
|
|
1454
1521
|
const defaultRenderCustomHeader = React.useCallback((headerProps) => (jsxRuntime.jsx(DatePickerHeader, Object.assign({}, headerProps))), [DatePickerHeader]);
|
|
1455
1522
|
const renderCustomHeader = React.useMemo(() => (renderCustomHeaderProp || defaultRenderCustomHeader), [defaultRenderCustomHeader, renderCustomHeaderProp]);
|
|
1456
|
-
return (jsxRuntime.jsx(
|
|
1523
|
+
return (jsxRuntime.jsx(SafeDatePicker, Object.assign({}, dataAttributes, props, { calendarClassName: "d-date-picker", renderCustomHeader: renderCustomHeader, placeholderText: placeholder, customInput: (jsxRuntime.jsx(ForwardedDDatePickerInput, { id: inputId, "aria-label": inputAriaLabel, iconEndAriaLabel: inputActionAriaLabel, iconMaterialStyle: iconMaterialStyle, iconEnd: iconInput, inputLabel: inputLabel, className: className, style: style, invalid: invalid, valid: valid, hint: inputHint })), customTimeInput: (jsxRuntime.jsx(DDatePickerTime, { id: timeId, "aria-label": ariaLabelInputTime })) })));
|
|
1457
1524
|
}
|
|
1458
1525
|
|
|
1459
1526
|
function DLayoutPane({ className, style, children, cols, colsXs, colsSm, colsMd, colsLg, colsXl, colsXxl, dataAttributes, }) {
|
|
@@ -1483,6 +1550,55 @@ var DLayout = Object.assign(DLayout$1, {
|
|
|
1483
1550
|
Pane: DLayoutPane,
|
|
1484
1551
|
});
|
|
1485
1552
|
|
|
1553
|
+
function normalizeValue(value) {
|
|
1554
|
+
return value !== null && value !== void 0 ? value : '';
|
|
1555
|
+
}
|
|
1556
|
+
function DInputSearch(_a, ref) {
|
|
1557
|
+
var { debounceMs = 300, onChange, onImmediateChange, value, defaultValue, placeholder = 'Search...' } = _a, props = tslib.__rest(_a, ["debounceMs", "onChange", "onImmediateChange", "value", "defaultValue", "placeholder"]);
|
|
1558
|
+
const inputRef = useProvidedRefOrCreate(ref);
|
|
1559
|
+
const isControlled = value !== undefined;
|
|
1560
|
+
const onChangeRef = React.useRef(onChange);
|
|
1561
|
+
onChangeRef.current = onChange;
|
|
1562
|
+
const [internalValue, setInternalValue] = React.useState(normalizeValue(isControlled ? value : defaultValue));
|
|
1563
|
+
// null = no keystroke has occurred yet (skip debounce on mount)
|
|
1564
|
+
const [pendingValue, setPendingValue] = React.useState(null);
|
|
1565
|
+
React.useEffect(() => {
|
|
1566
|
+
if (!isControlled)
|
|
1567
|
+
return;
|
|
1568
|
+
setInternalValue(normalizeValue(value));
|
|
1569
|
+
}, [isControlled, value]);
|
|
1570
|
+
const handleChange = React.useCallback((nextValue) => {
|
|
1571
|
+
if (!isControlled) {
|
|
1572
|
+
setInternalValue(nextValue);
|
|
1573
|
+
}
|
|
1574
|
+
setPendingValue(nextValue);
|
|
1575
|
+
onImmediateChange === null || onImmediateChange === void 0 ? void 0 : onImmediateChange(nextValue);
|
|
1576
|
+
}, [isControlled, onImmediateChange]);
|
|
1577
|
+
React.useEffect(() => {
|
|
1578
|
+
var _a;
|
|
1579
|
+
let timeoutId;
|
|
1580
|
+
if (pendingValue !== null) {
|
|
1581
|
+
if (debounceMs <= 0) {
|
|
1582
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, pendingValue);
|
|
1583
|
+
setPendingValue(null);
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
timeoutId = window.setTimeout(() => {
|
|
1587
|
+
var _a;
|
|
1588
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, pendingValue);
|
|
1589
|
+
setPendingValue(null);
|
|
1590
|
+
}, debounceMs);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return () => {
|
|
1594
|
+
window.clearTimeout(timeoutId);
|
|
1595
|
+
};
|
|
1596
|
+
}, [debounceMs, pendingValue]);
|
|
1597
|
+
return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef }, props, { type: "search", value: internalValue, onChange: handleChange, placeholder: placeholder })));
|
|
1598
|
+
}
|
|
1599
|
+
const ForwardedDInputSearch = React.forwardRef(DInputSearch);
|
|
1600
|
+
ForwardedDInputSearch.displayName = 'DInputSearch';
|
|
1601
|
+
|
|
1486
1602
|
function DInputMask(props, ref) {
|
|
1487
1603
|
return (jsxRuntime.jsx(mask.InputMask, Object.assign({ ref: ref, component: ForwardedDInput }, props)));
|
|
1488
1604
|
}
|
|
@@ -1587,6 +1703,64 @@ function useItemSelection({ getItemIdentifier: getItemIdentifierProp, previousSe
|
|
|
1587
1703
|
};
|
|
1588
1704
|
}
|
|
1589
1705
|
|
|
1706
|
+
/**
|
|
1707
|
+
* Hook to display a confirmation modal.
|
|
1708
|
+
*
|
|
1709
|
+
* Renders into the portal node specified by `DConfirmModalContainer` (typically `#d-portal`).
|
|
1710
|
+
* The confirm modal floats above the portal stack and handles its own Escape key,
|
|
1711
|
+
* preventing interference with underlying modals.
|
|
1712
|
+
*
|
|
1713
|
+
* Requires both `DContextProvider` wrapping your application AND an explicit
|
|
1714
|
+
* `<DConfirmModalContainer nodeId="d-portal" />` mounted in your app.
|
|
1715
|
+
*
|
|
1716
|
+
* @example
|
|
1717
|
+
* function App() {
|
|
1718
|
+
* return (
|
|
1719
|
+
* <DContextProvider>
|
|
1720
|
+
* <YourContent />
|
|
1721
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
1722
|
+
* </DContextProvider>
|
|
1723
|
+
* );
|
|
1724
|
+
* }
|
|
1725
|
+
*
|
|
1726
|
+
* function MyComponent() {
|
|
1727
|
+
* const confirm = useConfirmModal({
|
|
1728
|
+
* title: 'Delete Account',
|
|
1729
|
+
* message: 'This action cannot be undone.',
|
|
1730
|
+
* critical: { code: 'DELETE ACCOUNT' },
|
|
1731
|
+
* onConfirm: async () => { await deleteAccount(); },
|
|
1732
|
+
* });
|
|
1733
|
+
*
|
|
1734
|
+
* return <DButton onClick={confirm.open} text="Delete" color="danger" />;
|
|
1735
|
+
* }
|
|
1736
|
+
*/
|
|
1737
|
+
function useConfirmModal(config) {
|
|
1738
|
+
const store = useConfirmModalStore();
|
|
1739
|
+
const idRef = React.useRef(`confirm-modal-${Math.random().toString(36).slice(2)}`);
|
|
1740
|
+
const close = React.useCallback(() => {
|
|
1741
|
+
store.remove(idRef.current);
|
|
1742
|
+
queueMicrotask(() => {
|
|
1743
|
+
var _a;
|
|
1744
|
+
(_a = config.onClose) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
1745
|
+
});
|
|
1746
|
+
}, [store, config]);
|
|
1747
|
+
const open = React.useCallback(() => {
|
|
1748
|
+
const handleConfirmAction = async () => {
|
|
1749
|
+
try {
|
|
1750
|
+
await config.onConfirm();
|
|
1751
|
+
store.remove(idRef.current);
|
|
1752
|
+
}
|
|
1753
|
+
catch (_a) {
|
|
1754
|
+
// Keep modal open on error
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
// Remove any existing entry with this id to prevent duplicates on re-entrancy
|
|
1758
|
+
store.remove(idRef.current);
|
|
1759
|
+
store.push(Object.assign(Object.assign({}, config), { id: idRef.current, onConfirmAction: handleConfirmAction, onCloseAction: close }));
|
|
1760
|
+
}, [store, config, close]);
|
|
1761
|
+
return { open };
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1590
1764
|
function DInputCounter(_a, ref) {
|
|
1591
1765
|
var { minValue, maxValue, value = minValue, invalid, iconStart: iconStartProp, iconEnd: iconEndProp, iconStartAriaLabel = 'decrease action', iconEndAriaLabel = 'increase action', style, onChange } = _a, props = tslib.__rest(_a, ["minValue", "maxValue", "value", "invalid", "iconStart", "iconEnd", "iconStartAriaLabel", "iconEndAriaLabel", "style", "onChange"]);
|
|
1592
1766
|
const { handleOnWheel, } = useDisableInputWheel(ref);
|
|
@@ -1718,7 +1892,7 @@ const DEFAULT_VALIDATION_MESSAGES = {
|
|
|
1718
1892
|
notMatch: 'The password confirmation and the new password do not match.',
|
|
1719
1893
|
};
|
|
1720
1894
|
const DEFAULT_ENABLED_CHECKS = ['uppercase', 'lowercase', 'number', 'specialChar'];
|
|
1721
|
-
function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, }) {
|
|
1895
|
+
function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, readonly = false, }) {
|
|
1722
1896
|
const [password, setPassword] = React.useState(value);
|
|
1723
1897
|
React.useEffect(() => {
|
|
1724
1898
|
setPassword(value);
|
|
@@ -1727,7 +1901,7 @@ function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '
|
|
|
1727
1901
|
setPassword(newValue);
|
|
1728
1902
|
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
|
|
1729
1903
|
};
|
|
1730
|
-
return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx(ForwardedDInputPassword, { id: id, label: label, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange }), jsxRuntime.jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
|
|
1904
|
+
return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx(ForwardedDInputPassword, { id: id, label: label, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange, readonly: readonly }), jsxRuntime.jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
|
|
1731
1905
|
}
|
|
1732
1906
|
|
|
1733
1907
|
function DInputCheck(_a) {
|
|
@@ -2631,82 +2805,210 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
|
|
|
2631
2805
|
return (jsxRuntime.jsxs("div", { className: classNames('d-credit-card', isVertical && 'is-vertical', className), children: [jsxRuntime.jsxs("div", { className: "d-credit-card-header", children: [jsxRuntime.jsx("img", { src: logoImage || BRAND_LOGOS[brand] || DEFAULT_IMAGE, alt: brand, className: "d-credit-card-logo", width: 100 }), isChipVisible && (jsxRuntime.jsx("div", { className: "d-credit-card-chip", children: jsxRuntime.jsx("img", { src: CHIP_IMAGE, alt: "chip", width: 30, className: "d-credit-card-chip-image" }) }))] }), jsxRuntime.jsxs("div", { className: "d-credit-card-details", children: [jsxRuntime.jsx("div", { className: "d-credit-card-number", children: number }), jsxRuntime.jsx("small", { className: "d-credit-card-holder-text", children: holderText }), jsxRuntime.jsx("span", { className: "d-credit-card-name", children: name })] })] }));
|
|
2632
2806
|
}
|
|
2633
2807
|
|
|
2634
|
-
const getItemClass = (action) => {
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
return base;
|
|
2641
|
-
};
|
|
2642
|
-
function DDropdown({ actions, dropdownToggle, className, classNameMenu, }) {
|
|
2808
|
+
const getItemClass = (action) => classNames({
|
|
2809
|
+
'dropdown-item d-flex align-items-center': true,
|
|
2810
|
+
[`dropdown-item-${action.color}`]: !!action.color,
|
|
2811
|
+
disabled: action.disabled,
|
|
2812
|
+
});
|
|
2813
|
+
function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal = false, placement = 'auto', }) {
|
|
2643
2814
|
const [open, setOpen] = React.useState(false);
|
|
2815
|
+
const [position, setPosition] = React.useState('down');
|
|
2816
|
+
const [menuCoords, setMenuCoords] = React.useState(null);
|
|
2817
|
+
// Used in default (non-portal) mode: wraps the entire dropdown
|
|
2644
2818
|
const dropdownRef = React.useRef(null);
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2819
|
+
// Used in portal mode: ref on the toggle wrapper to read its viewport coords
|
|
2820
|
+
const toggleRef = React.useRef(null);
|
|
2821
|
+
// Ref on the rendered <ul> — used for outside-click detection in portal mode
|
|
2822
|
+
const menuRef = React.useRef(null);
|
|
2823
|
+
const toggle = () => {
|
|
2824
|
+
setMenuCoords(null);
|
|
2825
|
+
setOpen((prev) => !prev);
|
|
2826
|
+
};
|
|
2827
|
+
const resolvedInlinePosition = placement === 'auto' ? position : placement;
|
|
2828
|
+
// Compute fixed coords from the toggle's bounding rect (portal mode only)
|
|
2829
|
+
const computePortalCoords = React.useCallback(() => {
|
|
2830
|
+
if (!toggleRef.current)
|
|
2831
|
+
return;
|
|
2832
|
+
const rect = toggleRef.current.getBoundingClientRect();
|
|
2833
|
+
const spaceRight = window.innerWidth - rect.right;
|
|
2834
|
+
const spaceBottom = window.innerHeight - rect.bottom;
|
|
2835
|
+
const spaceTop = rect.top;
|
|
2836
|
+
let resolvedPlacement;
|
|
2837
|
+
if (placement === 'auto') {
|
|
2661
2838
|
if (spaceBottom < 150) {
|
|
2662
|
-
|
|
2839
|
+
resolvedPlacement = 'up';
|
|
2663
2840
|
}
|
|
2664
2841
|
else if (spaceRight < 150) {
|
|
2665
|
-
|
|
2842
|
+
resolvedPlacement = 'start';
|
|
2666
2843
|
}
|
|
2667
2844
|
else {
|
|
2668
|
-
|
|
2845
|
+
resolvedPlacement = 'down';
|
|
2669
2846
|
}
|
|
2670
2847
|
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2848
|
+
else {
|
|
2849
|
+
resolvedPlacement = placement;
|
|
2850
|
+
}
|
|
2851
|
+
const openStart = resolvedPlacement === 'start';
|
|
2852
|
+
const openEnd = resolvedPlacement === 'end';
|
|
2853
|
+
const openUp = resolvedPlacement === 'up';
|
|
2854
|
+
let availableHeight = spaceBottom;
|
|
2855
|
+
if (openUp) {
|
|
2856
|
+
availableHeight = spaceTop;
|
|
2857
|
+
}
|
|
2858
|
+
else if (openStart || openEnd) {
|
|
2859
|
+
availableHeight = window.innerHeight - rect.top;
|
|
2860
|
+
}
|
|
2861
|
+
const verticalCoords = openUp
|
|
2862
|
+
? { bottom: window.innerHeight - rect.top + 4 }
|
|
2863
|
+
: { top: openStart || openEnd ? rect.top : rect.bottom + 4 };
|
|
2864
|
+
let horizontalCoords;
|
|
2865
|
+
if (openStart) {
|
|
2866
|
+
horizontalCoords = { right: window.innerWidth - rect.left + 4 };
|
|
2867
|
+
}
|
|
2868
|
+
else if (openEnd) {
|
|
2869
|
+
horizontalCoords = { left: rect.right + 4 };
|
|
2676
2870
|
}
|
|
2677
2871
|
else {
|
|
2678
|
-
|
|
2872
|
+
horizontalCoords = { left: rect.left };
|
|
2679
2873
|
}
|
|
2874
|
+
setMenuCoords(Object.assign(Object.assign(Object.assign({}, verticalCoords), horizontalCoords), { minWidth: Math.max(rect.width, 160), maxHeight: `${Math.max(availableHeight - 12, 120)}px` }));
|
|
2875
|
+
}, [placement]);
|
|
2876
|
+
// Outside-click: default mode
|
|
2877
|
+
React.useEffect(() => {
|
|
2878
|
+
if (asPortal)
|
|
2879
|
+
return () => { };
|
|
2880
|
+
const handler = (event) => {
|
|
2881
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
2882
|
+
setOpen(false);
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2885
|
+
document.addEventListener('mousedown', handler);
|
|
2886
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
2887
|
+
}, [asPortal]);
|
|
2888
|
+
// Outside-click: portal mode (must check both toggle and floating menu)
|
|
2889
|
+
React.useEffect(() => {
|
|
2890
|
+
if (!asPortal || !open)
|
|
2891
|
+
return () => { };
|
|
2892
|
+
const handler = (event) => {
|
|
2893
|
+
var _a, _b;
|
|
2894
|
+
const target = event.target;
|
|
2895
|
+
if (!((_a = toggleRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)) && !((_b = menuRef.current) === null || _b === void 0 ? void 0 : _b.contains(target))) {
|
|
2896
|
+
setOpen(false);
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
document.addEventListener('mousedown', handler);
|
|
2900
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
2901
|
+
}, [asPortal, open]);
|
|
2902
|
+
// Auto-position: default mode
|
|
2903
|
+
React.useEffect(() => {
|
|
2904
|
+
if (asPortal || !open || !dropdownRef.current || placement !== 'auto')
|
|
2905
|
+
return;
|
|
2906
|
+
const rect = dropdownRef.current.getBoundingClientRect();
|
|
2907
|
+
const spaceBottom = window.innerHeight - rect.bottom;
|
|
2908
|
+
const spaceRight = window.innerWidth - rect.right;
|
|
2909
|
+
if (spaceBottom < 150)
|
|
2910
|
+
setPosition('up');
|
|
2911
|
+
else if (spaceRight < 150)
|
|
2912
|
+
setPosition('start');
|
|
2913
|
+
else
|
|
2914
|
+
setPosition('down');
|
|
2915
|
+
}, [asPortal, open, placement]);
|
|
2916
|
+
// Compute + track coords: portal mode
|
|
2917
|
+
// Re-runs whenever the menu opens and keeps coords fresh on scroll/resize
|
|
2918
|
+
React.useEffect(() => {
|
|
2919
|
+
if (!asPortal || !open)
|
|
2920
|
+
return () => { };
|
|
2921
|
+
computePortalCoords();
|
|
2922
|
+
const update = () => computePortalCoords();
|
|
2923
|
+
window.addEventListener('scroll', update, { passive: true, capture: true });
|
|
2924
|
+
window.addEventListener('resize', update, { passive: true });
|
|
2925
|
+
return () => {
|
|
2926
|
+
window.removeEventListener('scroll', update, { capture: true });
|
|
2927
|
+
window.removeEventListener('resize', update);
|
|
2928
|
+
};
|
|
2929
|
+
}, [asPortal, open, computePortalCoords]);
|
|
2930
|
+
let ToggleElement;
|
|
2931
|
+
if (dropdownToggle) {
|
|
2932
|
+
ToggleElement = typeof dropdownToggle === 'function'
|
|
2933
|
+
? dropdownToggle({ open, toggle })
|
|
2934
|
+
: dropdownToggle;
|
|
2680
2935
|
}
|
|
2681
2936
|
else {
|
|
2682
|
-
ToggleElement = (jsxRuntime.jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick:
|
|
2937
|
+
ToggleElement = (jsxRuntime.jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle, icon: "MoreVertical" }));
|
|
2683
2938
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2939
|
+
const portalMenuStyle = asPortal && menuCoords
|
|
2940
|
+
? {
|
|
2941
|
+
position: 'fixed',
|
|
2942
|
+
top: menuCoords.top,
|
|
2943
|
+
bottom: menuCoords.bottom,
|
|
2944
|
+
left: menuCoords.left,
|
|
2945
|
+
right: menuCoords.right,
|
|
2946
|
+
minWidth: menuCoords.minWidth,
|
|
2947
|
+
maxHeight: menuCoords.maxHeight,
|
|
2948
|
+
overflowY: 'auto',
|
|
2949
|
+
zIndex: 1050,
|
|
2950
|
+
transform: 'none',
|
|
2951
|
+
}
|
|
2952
|
+
: undefined;
|
|
2953
|
+
let inlineTop = '100%';
|
|
2954
|
+
if (resolvedInlinePosition === 'up') {
|
|
2955
|
+
inlineTop = 'auto';
|
|
2956
|
+
}
|
|
2957
|
+
else if (resolvedInlinePosition === 'start' || resolvedInlinePosition === 'end') {
|
|
2958
|
+
inlineTop = 0;
|
|
2959
|
+
}
|
|
2960
|
+
let inlineLeft = 0;
|
|
2961
|
+
if (resolvedInlinePosition === 'start') {
|
|
2962
|
+
inlineLeft = 'auto';
|
|
2963
|
+
}
|
|
2964
|
+
else if (resolvedInlinePosition === 'end') {
|
|
2965
|
+
inlineLeft = '100%';
|
|
2966
|
+
}
|
|
2967
|
+
let inlineRight = 'auto';
|
|
2968
|
+
if (resolvedInlinePosition === 'start') {
|
|
2969
|
+
inlineRight = '100%';
|
|
2970
|
+
}
|
|
2971
|
+
let inlineTransform = 'translateY(4px)';
|
|
2972
|
+
if (resolvedInlinePosition === 'up') {
|
|
2973
|
+
inlineTransform = 'translateY(-4px)';
|
|
2974
|
+
}
|
|
2975
|
+
else if (resolvedInlinePosition === 'start') {
|
|
2976
|
+
inlineTransform = 'translateX(-4px)';
|
|
2977
|
+
}
|
|
2978
|
+
else if (resolvedInlinePosition === 'end') {
|
|
2979
|
+
inlineTransform = 'translateX(4px)';
|
|
2980
|
+
}
|
|
2981
|
+
const inlineMenuStyle = {
|
|
2982
|
+
position: 'absolute',
|
|
2983
|
+
top: inlineTop,
|
|
2984
|
+
bottom: resolvedInlinePosition === 'up' ? '100%' : 'auto',
|
|
2985
|
+
left: inlineLeft,
|
|
2986
|
+
right: inlineRight,
|
|
2987
|
+
transform: inlineTransform,
|
|
2988
|
+
};
|
|
2989
|
+
const menuItems = (jsxRuntime.jsx("ul", { ref: menuRef, role: "menu", className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), style: portalMenuStyle !== null && portalMenuStyle !== void 0 ? portalMenuStyle : inlineMenuStyle, children: actions.map((action, index) => {
|
|
2990
|
+
if (action.isDivider) {
|
|
2991
|
+
return jsxRuntime.jsx("hr", { className: "dropdown-divider" }, index);
|
|
2992
|
+
}
|
|
2993
|
+
return (jsxRuntime.jsx("li", { children: action.href ? (jsxRuntime.jsxs("a", { className: getItemClass(action), href: action.href, onClick: (e) => {
|
|
2994
|
+
if (action.disabled) {
|
|
2995
|
+
e.preventDefault();
|
|
2996
|
+
}
|
|
2997
|
+
else {
|
|
2998
|
+
setOpen(false);
|
|
2999
|
+
}
|
|
3000
|
+
}, children: [action.icon && jsxRuntime.jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) : (jsxRuntime.jsxs("button", { className: getItemClass(action), type: "button", onClick: () => {
|
|
3001
|
+
var _a;
|
|
3002
|
+
if (!action.disabled) {
|
|
3003
|
+
(_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action);
|
|
3004
|
+
setOpen(false);
|
|
3005
|
+
}
|
|
3006
|
+
}, disabled: action.disabled, children: [action.icon && jsxRuntime.jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) }, index));
|
|
3007
|
+
}) }));
|
|
3008
|
+
if (asPortal) {
|
|
3009
|
+
return (jsxRuntime.jsxs("div", { ref: toggleRef, className: classNames('dropdown', className), children: [ToggleElement, open && menuCoords && reactDom.createPortal(menuItems, document.body)] }));
|
|
3010
|
+
}
|
|
3011
|
+
return (jsxRuntime.jsxs("div", { className: classNames(`dropdown drop-${resolvedInlinePosition}`, className), ref: dropdownRef, children: [ToggleElement, menuItems] }));
|
|
2710
3012
|
}
|
|
2711
3013
|
|
|
2712
3014
|
function useScreenshot() {
|
|
@@ -2922,29 +3224,110 @@ function render(renderable) {
|
|
|
2922
3224
|
return null;
|
|
2923
3225
|
return typeof renderable === 'function' ? renderable() : renderable;
|
|
2924
3226
|
}
|
|
2925
|
-
function DDataStateWrapper({ isLoading, isError, data, onRetry, renderLoading, renderEmpty, renderError, children, }) {
|
|
3227
|
+
function DDataStateWrapper({ isLoading, isError, data, onRetry, messages, renderLoading, renderEmpty, renderError, children, }) {
|
|
2926
3228
|
// 1. Loading
|
|
2927
3229
|
if (isLoading) {
|
|
2928
3230
|
if (renderLoading)
|
|
2929
3231
|
return render(renderLoading);
|
|
2930
|
-
return jsxRuntime.jsx(LoadingState, {});
|
|
3232
|
+
return jsxRuntime.jsx(LoadingState, { ariaLabel: messages === null || messages === void 0 ? void 0 : messages.loading });
|
|
2931
3233
|
}
|
|
2932
3234
|
// 2. Error
|
|
2933
3235
|
if (isError) {
|
|
2934
3236
|
if (renderError)
|
|
2935
3237
|
return render(renderError);
|
|
2936
|
-
return (jsxRuntime.jsx(ErrorState, { onRetry: onRetry }));
|
|
3238
|
+
return (jsxRuntime.jsx(ErrorState, { onRetry: onRetry, message: messages === null || messages === void 0 ? void 0 : messages.error, retryMessage: messages === null || messages === void 0 ? void 0 : messages.retry }));
|
|
2937
3239
|
}
|
|
2938
3240
|
// 3. Empty
|
|
2939
3241
|
if (!(data === null || data === void 0 ? void 0 : data.length)) {
|
|
2940
3242
|
if (renderEmpty)
|
|
2941
3243
|
return render(renderEmpty);
|
|
2942
|
-
return (jsxRuntime.jsx(EmptyState, {}));
|
|
3244
|
+
return (jsxRuntime.jsx(EmptyState, { message: messages === null || messages === void 0 ? void 0 : messages.empty }));
|
|
2943
3245
|
}
|
|
2944
3246
|
// 4. Success
|
|
2945
3247
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(data) });
|
|
2946
3248
|
}
|
|
2947
3249
|
|
|
3250
|
+
function DConfirmModalUI({ entry }) {
|
|
3251
|
+
const { title = 'Are you sure you want to proceed?', message = 'Please confirm if you want to continue with this action.', confirmLabel = 'Confirm', cancelLabel = 'Cancel', confirmColor = 'primary', critical, onConfirmAction, onCloseAction, } = entry;
|
|
3252
|
+
const [confirmationCode, setConfirmationCode] = React.useState('');
|
|
3253
|
+
const [internalLoading, setInternalLoading] = React.useState(false);
|
|
3254
|
+
const isMountedRef = React.useRef(true);
|
|
3255
|
+
React.useEffect(() => {
|
|
3256
|
+
const cleanup = () => {
|
|
3257
|
+
isMountedRef.current = false;
|
|
3258
|
+
};
|
|
3259
|
+
return cleanup;
|
|
3260
|
+
}, []);
|
|
3261
|
+
const handleClose = React.useCallback(() => {
|
|
3262
|
+
onCloseAction();
|
|
3263
|
+
}, [onCloseAction]);
|
|
3264
|
+
const isConfirmDisabled = Boolean(internalLoading
|
|
3265
|
+
|| (critical && confirmationCode !== critical.code));
|
|
3266
|
+
const handleConfirmClick = React.useCallback(() => {
|
|
3267
|
+
setInternalLoading(true);
|
|
3268
|
+
onConfirmAction()
|
|
3269
|
+
.catch(() => undefined)
|
|
3270
|
+
.finally(() => {
|
|
3271
|
+
// Only update state if the component is still mounted.
|
|
3272
|
+
// onConfirmAction() removes the entry on success, which unmounts this component.
|
|
3273
|
+
if (isMountedRef.current) {
|
|
3274
|
+
setInternalLoading(false);
|
|
3275
|
+
}
|
|
3276
|
+
});
|
|
3277
|
+
}, [onConfirmAction]);
|
|
3278
|
+
return (jsxRuntime.jsxs(DModal, { name: entry.id, size: "lg", className: `confirm-modal ${critical ? 'critical-modal' : ''}`, children: [jsxRuntime.jsx(DModal.Header, { onClose: handleClose, showCloseButton: true, children: jsxRuntime.jsx("h5", { className: "fw-bold", children: title }) }), jsxRuntime.jsxs(DModal.Body, { className: "py-3 px-5", children: [jsxRuntime.jsx("p", { className: "mb-4", children: message }), critical && (jsxRuntime.jsx(ForwardedDInput, { type: "text", label: critical.codeLabel || 'Confirmation code', placeholder: critical.inputPlaceholder, value: confirmationCode, onChange: (value) => setConfirmationCode(value), className: "mb-4", autoFocus: true }))] }), jsxRuntime.jsxs(DModal.Footer, { children: [jsxRuntime.jsx(DButton, { text: cancelLabel, variant: "outline", color: "secondary", onClick: handleClose, disabled: internalLoading }), jsxRuntime.jsx(DButton, { text: confirmLabel, color: confirmColor, onClick: handleConfirmClick, disabled: isConfirmDisabled, loading: internalLoading })] })] }));
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
/**
|
|
3282
|
+
* Container that renders confirm modals into the specified portal node.
|
|
3283
|
+
*
|
|
3284
|
+
* Must be explicitly mounted by the user (typically as a sibling of app content
|
|
3285
|
+
* within `DContextProvider`). The container floats above the portal stack and
|
|
3286
|
+
* intercepts Escape key events to close the top confirm modal without affecting
|
|
3287
|
+
* underlying modals or overlays.
|
|
3288
|
+
*
|
|
3289
|
+
* @example
|
|
3290
|
+
* <DContextProvider>
|
|
3291
|
+
* <App />
|
|
3292
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
3293
|
+
* </DContextProvider>
|
|
3294
|
+
*/
|
|
3295
|
+
function DConfirmModalContainer({ nodeId }) {
|
|
3296
|
+
const store = useConfirmModalStore();
|
|
3297
|
+
const [entries, setEntries] = React.useState([]);
|
|
3298
|
+
React.useEffect(() => {
|
|
3299
|
+
const unsubscribe = store.subscribe((next) => {
|
|
3300
|
+
setEntries(next);
|
|
3301
|
+
});
|
|
3302
|
+
return unsubscribe;
|
|
3303
|
+
}, [store]);
|
|
3304
|
+
// Capture Escape keydown to close the top confirm modal without affecting
|
|
3305
|
+
// the underlying portal stack (which also handles Escape).
|
|
3306
|
+
React.useEffect(() => {
|
|
3307
|
+
if (entries.length === 0) {
|
|
3308
|
+
return () => { };
|
|
3309
|
+
}
|
|
3310
|
+
const handleEscapeCapture = (event) => {
|
|
3311
|
+
if (event.key === 'Escape') {
|
|
3312
|
+
// Close the top (last) confirm modal
|
|
3313
|
+
entries[entries.length - 1].onCloseAction();
|
|
3314
|
+
// Prevent the event from reaching other handlers (e.g., DPortalContextProvider)
|
|
3315
|
+
event.stopPropagation();
|
|
3316
|
+
event.preventDefault();
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
document.addEventListener('keydown', handleEscapeCapture, true);
|
|
3320
|
+
return () => {
|
|
3321
|
+
document.removeEventListener('keydown', handleEscapeCapture, true);
|
|
3322
|
+
};
|
|
3323
|
+
}, [entries]);
|
|
3324
|
+
const portalNode = document.getElementById(nodeId);
|
|
3325
|
+
if (!portalNode || entries.length === 0) {
|
|
3326
|
+
return null;
|
|
3327
|
+
}
|
|
3328
|
+
return reactDom.createPortal(jsxRuntime.jsx(framerMotion.AnimatePresence, { children: entries.map((entry) => (jsxRuntime.jsxs(framerMotion.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0, transition: { delay: 0.3 } }, transition: { duration: 0.15, ease: 'linear' }, children: [jsxRuntime.jsx("div", { className: "backdrop backdrop-confirm-modal", onClick: entry.onCloseAction, role: "presentation" }), jsxRuntime.jsx(DConfirmModalUI, { entry: entry })] }, entry.id))) }), portalNode);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
2948
3331
|
Object.defineProperty(exports, "getErrorMessage", {
|
|
2949
3332
|
enumerable: true,
|
|
2950
3333
|
get: function () { return reactErrorBoundary.getErrorMessage; }
|
|
@@ -2968,6 +3351,7 @@ exports.DCarousel = DCarousel;
|
|
|
2968
3351
|
exports.DCarouselSlide = DCarouselSlide;
|
|
2969
3352
|
exports.DChip = DChip;
|
|
2970
3353
|
exports.DCollapse = DCollapse;
|
|
3354
|
+
exports.DConfirmModalContainer = DConfirmModalContainer;
|
|
2971
3355
|
exports.DContext = DContext;
|
|
2972
3356
|
exports.DContextProvider = DContextProvider;
|
|
2973
3357
|
exports.DCreditCard = DCreditCard;
|
|
@@ -2987,6 +3371,7 @@ exports.DInputPassword = ForwardedDInputPassword;
|
|
|
2987
3371
|
exports.DInputPhone = ForwardedDInputPhone;
|
|
2988
3372
|
exports.DInputPin = DInputPin;
|
|
2989
3373
|
exports.DInputRange = ForwardedDInputRange;
|
|
3374
|
+
exports.DInputSearch = ForwardedDInputSearch;
|
|
2990
3375
|
exports.DInputSelect = DInputSelect;
|
|
2991
3376
|
exports.DInputSwitch = DInputSwitch;
|
|
2992
3377
|
exports.DLayout = DLayout;
|
|
@@ -3017,6 +3402,9 @@ exports.DToast = DToast;
|
|
|
3017
3402
|
exports.DToastContainer = DToastContainer;
|
|
3018
3403
|
exports.DTooltip = DTooltip;
|
|
3019
3404
|
exports.DVoucher = DVoucher;
|
|
3405
|
+
exports.EmptyState = EmptyState;
|
|
3406
|
+
exports.ErrorState = ErrorState;
|
|
3407
|
+
exports.LoadingState = LoadingState;
|
|
3020
3408
|
exports.changeQueryString = changeQueryString;
|
|
3021
3409
|
exports.checkMediaQuery = checkMediaQuery;
|
|
3022
3410
|
exports.configureI18n = configureI8n;
|
|
@@ -3024,6 +3412,7 @@ exports.formatCurrency = formatCurrency;
|
|
|
3024
3412
|
exports.getCssVariable = getCssVariable;
|
|
3025
3413
|
exports.getQueryString = getQueryString;
|
|
3026
3414
|
exports.subscribeToMediaQuery = subscribeToMediaQuery;
|
|
3415
|
+
exports.useConfirmModal = useConfirmModal;
|
|
3027
3416
|
exports.useDContext = useDContext;
|
|
3028
3417
|
exports.useDPortalContext = useDPortalContext;
|
|
3029
3418
|
exports.useDToast = useDToast;
|