@dynamic-framework/ui-react 2.5.2 → 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.
Files changed (104) hide show
  1. package/README.md +4 -0
  2. package/dist/css/dynamic-ui.css +38 -10
  3. package/dist/css/dynamic-ui.min.css +2 -2
  4. package/dist/index.esm.js +466 -83
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/index.js +471 -82
  7. package/dist/index.js.map +1 -1
  8. package/dist/types/components/DAlert/DAlert.d.ts +1 -1
  9. package/dist/types/components/DAvatar/DAvatar.d.ts +2 -1
  10. package/dist/types/components/DBadge/DBadge.d.ts +2 -1
  11. package/dist/types/components/DBox/DBox.d.ts +1 -1
  12. package/dist/types/components/DBoxFile/DBoxFile.d.ts +1 -1
  13. package/dist/types/components/DButtonIcon/DButtonIcon.d.ts +1 -1
  14. package/dist/types/components/DCard/DCard.d.ts +1 -1
  15. package/dist/types/components/DCard/components/DCardBody.d.ts +1 -1
  16. package/dist/types/components/DCard/components/DCardFooter.d.ts +1 -1
  17. package/dist/types/components/DCard/components/DCardHeader.d.ts +1 -1
  18. package/dist/types/components/DCarousel/components/DCarouselSlide.d.ts +1 -1
  19. package/dist/types/components/DChip/DChip.d.ts +2 -1
  20. package/dist/types/components/DCollapse/DCollapse.d.ts +1 -1
  21. package/dist/types/components/DConfirmModal/DConfirmModalContainer.d.ts +21 -0
  22. package/dist/types/components/DConfirmModal/DConfirmModalUI.d.ts +7 -0
  23. package/dist/types/components/DConfirmModal/confirmModalStore.d.ts +6 -0
  24. package/dist/types/components/DConfirmModal/types.d.ts +45 -0
  25. package/dist/types/components/DCreditCard/DCreditCard.d.ts +2 -1
  26. package/dist/types/components/DCurrencyText/DCurrencyText.d.ts +2 -1
  27. package/dist/types/components/DDataStateWrapper/DDataStateWrapper.d.ts +8 -1
  28. package/dist/types/components/DDataStateWrapper/components/EmptyState.d.ts +2 -1
  29. package/dist/types/components/DDataStateWrapper/components/ErrorState.d.ts +2 -1
  30. package/dist/types/components/DDataStateWrapper/components/LoadingState.d.ts +2 -1
  31. package/dist/types/components/DDataStateWrapper/index.d.ts +4 -0
  32. package/dist/types/components/DDatePicker/DDatePicker.d.ts +2 -1
  33. package/dist/types/components/DDatePicker/components/DDatePickerHeaderSelector.d.ts +1 -1
  34. package/dist/types/components/DDatePicker/components/DDatePickerTime.d.ts +1 -1
  35. package/dist/types/components/DDropdown/DDropdown.d.ts +4 -1
  36. package/dist/types/components/DErrorBoundary/DErrorBoundary.d.ts +1 -1
  37. package/dist/types/components/DErrorBoundary/components/DefaultErrorBoundary.d.ts +2 -1
  38. package/dist/types/components/DIcon/DIcon.d.ts +1 -1
  39. package/dist/types/components/DIconBase/DIconBase.d.ts +2 -1
  40. package/dist/types/components/DInputCheck/DInputCheck.d.ts +1 -1
  41. package/dist/types/components/DInputPin/DInputPin.d.ts +2 -1
  42. package/dist/types/components/DInputSearch/DInputSearch.d.ts +11 -0
  43. package/dist/types/components/DInputSearch/index.d.ts +3 -0
  44. package/dist/types/components/DInputSelect/DInputSelect.d.ts +1 -1
  45. package/dist/types/components/DInputSwitch/DInputSwitch.d.ts +2 -1
  46. package/dist/types/components/DLayout/DLayout.d.ts +1 -1
  47. package/dist/types/components/DLayout/components/DLayoutPane.d.ts +1 -1
  48. package/dist/types/components/DListGroup/DListGroup.d.ts +1 -1
  49. package/dist/types/components/DListGroup/components/DListGroupItem.d.ts +1 -1
  50. package/dist/types/components/DModal/DModal.d.ts +1 -1
  51. package/dist/types/components/DModal/components/DModalBody.d.ts +1 -1
  52. package/dist/types/components/DModal/components/DModalFooter.d.ts +1 -1
  53. package/dist/types/components/DModal/components/DModalHeader.d.ts +1 -1
  54. package/dist/types/components/DOffcanvas/DOffcanvas.d.ts +1 -1
  55. package/dist/types/components/DOffcanvas/components/DOffcanvasBody.d.ts +1 -1
  56. package/dist/types/components/DOffcanvas/components/DOffcanvasFooter.d.ts +1 -1
  57. package/dist/types/components/DOffcanvas/components/DOffcanvasHeader.d.ts +1 -1
  58. package/dist/types/components/DOtp/DOtp.d.ts +1 -1
  59. package/dist/types/components/DOtp/components/OtpCountdown.d.ts +2 -1
  60. package/dist/types/components/DPaginator/DPaginator.d.ts +2 -1
  61. package/dist/types/components/DPasswordStrengthMeter/DPasswordStrengthMeter.d.ts +3 -1
  62. package/dist/types/components/DPasswordStrengthMeter/PasswordCheckItem.d.ts +2 -1
  63. package/dist/types/components/DPasswordStrengthMeter/PasswordCheckList.d.ts +2 -1
  64. package/dist/types/components/DPasswordStrengthMeter/PasswordStrength.d.ts +2 -1
  65. package/dist/types/components/DPopover/DPopover.d.ts +1 -1
  66. package/dist/types/components/DProgress/DProgress.d.ts +2 -1
  67. package/dist/types/components/DSelect/DSelect.d.ts +2 -1
  68. package/dist/types/components/DSelect/components/DSelectClearIndicator.d.ts +2 -1
  69. package/dist/types/components/DSelect/components/DSelectDropdownIndicator.d.ts +2 -1
  70. package/dist/types/components/DSelect/components/DSelectLoadingIndicator.d.ts +2 -1
  71. package/dist/types/components/DSelect/components/DSelectMultiValueRemove.d.ts +2 -1
  72. package/dist/types/components/DSelect/components/DSelectOptionCheck.d.ts +2 -1
  73. package/dist/types/components/DSelect/components/DSelectOptionEmoji.d.ts +2 -1
  74. package/dist/types/components/DSelect/components/DSelectOptionIcon.d.ts +2 -1
  75. package/dist/types/components/DSelect/components/DSelectPlaceholder.d.ts +2 -1
  76. package/dist/types/components/DSelect/components/DSelectSingleValueEmoji.d.ts +2 -1
  77. package/dist/types/components/DSelect/components/DSelectSingleValueEmojiText.d.ts +2 -1
  78. package/dist/types/components/DSelect/components/DSelectSingleValueIconText.d.ts +2 -1
  79. package/dist/types/components/DStepper/DStepper.d.ts +2 -1
  80. package/dist/types/components/DStepperDesktop/DStepperDesktop.d.ts +2 -1
  81. package/dist/types/components/DStepperMobile/DStepperMobile.d.ts +2 -1
  82. package/dist/types/components/DTabs/DTabs.d.ts +1 -1
  83. package/dist/types/components/DTabs/components/DTabContent.d.ts +1 -1
  84. package/dist/types/components/DTimeline/DTimeline.d.ts +1 -1
  85. package/dist/types/components/DToast/DToast.d.ts +1 -1
  86. package/dist/types/components/DToast/components/DToastBody.d.ts +1 -1
  87. package/dist/types/components/DToast/components/DToastHeader.d.ts +1 -1
  88. package/dist/types/components/DToastContainer/DToastContainer.d.ts +1 -1
  89. package/dist/types/components/DTooltip/DTooltip.d.ts +1 -1
  90. package/dist/types/components/DVoucher/DVoucher.d.ts +1 -1
  91. package/dist/types/components/index.d.ts +6 -1
  92. package/dist/types/components/interface.d.ts +1 -1
  93. package/dist/types/contexts/DContext.d.ts +25 -3
  94. package/dist/types/contexts/DPortalContext.d.ts +1 -1
  95. package/dist/types/hooks/index.d.ts +2 -0
  96. package/dist/types/hooks/useConfirmModal.d.ts +34 -0
  97. package/package.json +4 -2
  98. package/src/style/_shame.scss +13 -0
  99. package/src/style/abstracts/variables/_forms.scss +1 -1
  100. package/src/style/abstracts/variables/_list-group.scss +1 -1
  101. package/src/style/base/_backdrop.scss +7 -0
  102. package/src/style/components/_d-carousel.scss +1 -1
  103. package/src/style/components/_d-modal.scss +8 -0
  104. 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
- }, 3000);
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, isEmpty }] = useStackState([]);
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
- if (isEmpty()) {
188
- return;
189
- }
187
+ // pop() is safe on empty stacks, so close remains idempotent.
190
188
  pop();
191
- }, [isEmpty, pop]);
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
- return (jsxRuntime.jsx(DContext.Provider, { value: value, children: jsxRuntime.jsx(DPortalContextProvider, { portalName: portalName, availablePortals: availablePortals, children: children }) }));
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-${variant}-${color}`
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-${variant}-${color}`
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(DatePicker, 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 })) })));
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
- const base = classNames({
2636
- 'dropdown-item d-flex align-items-center': true,
2637
- [`dropdown-item-${action.color}`]: !!action.color,
2638
- disabled: action.disabled,
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
- const [position, setPosition] = React.useState('down'); // 🆕
2646
- // Cerrar al hacer click fuera
2647
- React.useEffect(() => {
2648
- const handleClickOutside = (event) => {
2649
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
2650
- setOpen(false);
2651
- }
2652
- };
2653
- document.addEventListener('mousedown', handleClickOutside);
2654
- return () => document.removeEventListener('mousedown', handleClickOutside);
2655
- }, []);
2656
- React.useEffect(() => {
2657
- if (open && dropdownRef.current) {
2658
- const rect = dropdownRef.current.getBoundingClientRect();
2659
- const spaceBottom = window.innerHeight - rect.bottom;
2660
- const spaceRight = window.innerWidth - rect.right;
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
- setPosition('up');
2839
+ resolvedPlacement = 'up';
2663
2840
  }
2664
2841
  else if (spaceRight < 150) {
2665
- setPosition('start');
2842
+ resolvedPlacement = 'start';
2666
2843
  }
2667
2844
  else {
2668
- setPosition('down');
2845
+ resolvedPlacement = 'down';
2669
2846
  }
2670
2847
  }
2671
- }, [open]);
2672
- let ToggleElement;
2673
- if (dropdownToggle) {
2674
- if (typeof dropdownToggle === 'function') {
2675
- ToggleElement = dropdownToggle({ open, toggle: () => setOpen(!open) });
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
- ToggleElement = dropdownToggle;
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: () => setOpen(!open), icon: "MoreVertical" }));
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
- return (jsxRuntime.jsxs("div", { className: classNames(`dropdown drop-${position}`, className), ref: dropdownRef, children: [ToggleElement, jsxRuntime.jsx("ul", { style: {
2685
- position: 'absolute',
2686
- top: position === 'up' ? 'auto' : '100%',
2687
- bottom: position === 'up' ? '100%' : 'auto',
2688
- left: position === 'start' ? 'auto' : 0,
2689
- right: position === 'start' ? '0' : 'auto',
2690
- transform: 'translateY(4px)',
2691
- }, className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), children: actions.map((action, index) => {
2692
- if (action.isDivider) {
2693
- return (jsxRuntime.jsx("hr", { className: "dropdown-divider" }, index));
2694
- }
2695
- return (jsxRuntime.jsx("li", { children: action.href ? (jsxRuntime.jsxs("a", { className: getItemClass(action), href: action.href, onClick: (e) => {
2696
- if (action.disabled) {
2697
- e.preventDefault();
2698
- }
2699
- else {
2700
- setOpen(false);
2701
- }
2702
- }, 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: () => {
2703
- var _a;
2704
- if (!action.disabled) {
2705
- (_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action);
2706
- setOpen(false);
2707
- }
2708
- }, disabled: action.disabled, children: [action.icon && (jsxRuntime.jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" })), action.label] })) }, index));
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;