@dynamic-framework/ui-react 2.7.0 → 2.9.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 (58) hide show
  1. package/dist/css/dynamic-ui.css +276 -22
  2. package/dist/css/dynamic-ui.min.css +2 -2
  3. package/dist/index.esm.js +677 -298
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +683 -297
  6. package/dist/index.js.map +1 -1
  7. package/dist/types/components/DDropdown/DDropdown.d.ts +10 -2
  8. package/dist/types/components/DInputCheck/DInputCheck.d.ts +1 -0
  9. package/dist/types/components/DInputSwitch/DInputSwitch.d.ts +2 -1
  10. package/dist/types/components/DModal/components/DModalHeader.d.ts +5 -1
  11. package/dist/types/components/DOffcanvas/components/DOffcanvasHeader.d.ts +5 -1
  12. package/dist/types/components/DOtp/DOtp.d.ts +1 -0
  13. package/dist/types/components/DOtp/components/OtpCountdown.d.ts +3 -2
  14. package/dist/types/components/DTooltip/DTooltip.d.ts +38 -3
  15. package/dist/types/components/DVoucher/DVoucher.d.ts +3 -1
  16. package/dist/types/components/DVoucher/hooks/useScreenshotDownload.d.ts +1 -1
  17. package/dist/types/components/DVoucher/hooks/useScreenshotDownload.spec.d.ts +1 -0
  18. package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.d.ts +1 -1
  19. package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.spec.d.ts +1 -0
  20. package/dist/types/components/DVoucher/index.d.ts +3 -0
  21. package/dist/types/components/index.d.ts +1 -1
  22. package/dist/types/hooks/index.d.ts +3 -0
  23. package/dist/types/hooks/tests/useCountdown.spec.d.ts +1 -0
  24. package/dist/types/hooks/useCountdown.d.ts +14 -0
  25. package/dist/types/hooks/useInputCurrency.d.ts +1 -6
  26. package/dist/types/hooks/useOtp.d.ts +98 -0
  27. package/dist/types/utils/buildUrl.d.ts +32 -0
  28. package/dist/types/utils/index.d.ts +2 -0
  29. package/dist/types/utils/sanitizeHref.d.ts +37 -0
  30. package/dist/types/utils/tests/buildUrl.spec.d.ts +1 -0
  31. package/dist/types/utils/tests/sanitizeHref.spec.d.ts +1 -0
  32. package/package.json +9 -6
  33. package/src/style/_shame.scss +1 -5
  34. package/src/style/abstracts/variables/_+import.scss +2 -0
  35. package/src/style/abstracts/variables/_box.scss +7 -0
  36. package/src/style/abstracts/variables/_forms.scss +1 -3
  37. package/src/style/abstracts/variables/_input-currency.scss +1 -0
  38. package/src/style/abstracts/variables/_shadow.scss +12 -1
  39. package/src/style/base/_+import.scss +0 -1
  40. package/src/style/base/_form-check.scss +4 -2
  41. package/src/style/components/_+import.scss +7 -0
  42. package/src/style/components/_d-box-file.scss +5 -0
  43. package/src/style/components/_d-box.scss +5 -5
  44. package/src/style/components/_d-close.scss +1 -0
  45. package/src/style/components/_d-confirm-modal.scss +17 -0
  46. package/src/style/components/_d-data-state-wrapper.scss +47 -0
  47. package/src/style/components/_d-datepicker.scss +11 -0
  48. package/src/style/components/_d-error-boundary.scss +7 -0
  49. package/src/style/components/_d-input-currency.scss +5 -0
  50. package/src/style/components/_d-otp.scss +54 -0
  51. package/src/style/components/_d-password-strength-meter.scss +76 -0
  52. package/src/style/components/_d-popover.scss +4 -0
  53. package/src/style/components/_d-stepper.scss +8 -0
  54. package/src/style/components/_d-tabs.scss +18 -0
  55. package/src/style/components/_d-voucher.scss +30 -0
  56. package/dist/types/components/DOtp/DOtpspec.d.ts +0 -0
  57. package/dist/types/components/DOtp/hooks/useCountdown.d.ts +0 -4
  58. package/src/style/base/_form-text.scss +0 -12
package/dist/index.js CHANGED
@@ -583,12 +583,12 @@ function DIcon(_a) {
583
583
  return (jsxRuntime.jsx(DIconBase, Object.assign({ icon: resolvedIcon, familyClass: propFamilyClass !== null && propFamilyClass !== void 0 ? propFamilyClass : familyClass, familyPrefix: propFamilyPrefix !== null && propFamilyPrefix !== void 0 ? propFamilyPrefix : familyPrefix, materialStyle: propMaterialStyle !== null && propMaterialStyle !== void 0 ? propMaterialStyle : materialStyle }, props)));
584
584
  }
585
585
 
586
- function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle = false, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle = false, showClose, onClose, children, id, className, style, dataAttributes, }) {
587
- const { iconMap: { alert, xLg, }, } = useDContext();
586
+ function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle, showClose, onClose, children, id, className, style, dataAttributes, }) {
587
+ const { icon: { materialStyle, familyClass, familyPrefix, }, iconMap: { alert, xLg, }, } = useDContext();
588
588
  const icon = React.useMemo(() => iconProp || alert[color], [alert, iconProp, color]);
589
589
  const iconClose = React.useMemo(() => (iconCloseProp || xLg), [iconCloseProp, xLg]);
590
590
  const generateClasses = React.useMemo(() => (Object.assign({ alert: true, [`alert-${color}`]: true, 'fade show': !!showClose }, className && { [className]: true })), [color, showClose, className]);
591
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames(generateClasses), style: style, role: "alert", id: id }, dataAttributes, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "alert-icon", icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })), jsxRuntime.jsx("div", { className: "alert-text", children: children }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass, familyPrefix: iconCloseFamilyPrefix, materialStyle: iconCloseMaterialStyle }) }))] })));
591
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames(generateClasses), style: style, role: "alert", id: id }, dataAttributes, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "alert-icon", icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })), jsxRuntime.jsx("div", { className: "alert-text", children: children }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass !== null && iconCloseFamilyClass !== void 0 ? iconCloseFamilyClass : familyClass, familyPrefix: iconCloseFamilyPrefix !== null && iconCloseFamilyPrefix !== void 0 ? iconCloseFamilyPrefix : familyPrefix, materialStyle: iconCloseMaterialStyle !== null && iconCloseMaterialStyle !== void 0 ? iconCloseMaterialStyle : materialStyle }) }))] })));
592
592
  }
593
593
 
594
594
  function DAvatar({ id, size, image, name: nameProp, useNameAsInitials = false, className, style, dataAttributes, }) {
@@ -610,6 +610,7 @@ function DAvatar({ id, size, image, name: nameProp, useNameAsInitials = false, c
610
610
 
611
611
  function DBadge(props) {
612
612
  const { text, soft = false, color = 'primary', id, rounded, className, size, style, iconStart, iconEnd, iconMaterialStyle, iconFamilyClass, iconFamilyPrefix, dataAttributes, } = props;
613
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
613
614
  // Responsive size resolution using useResponsiveProp
614
615
  const { responsivePropValue } = useResponsiveProp(true);
615
616
  const resolvedSize = React.useMemo(() => {
@@ -626,7 +627,7 @@ function DBadge(props) {
626
627
  'rounded-pill': !!rounded,
627
628
  [`badge-${resolvedSize}`]: !!resolvedSize,
628
629
  }), [rounded, soft, color, resolvedSize]);
629
- return (jsxRuntime.jsxs("span", Object.assign({ className: classNames(generateClasses, className), style: style }, id && { id }, dataAttributes, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })), jsxRuntime.jsx("span", { children: text }), iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle }))] })));
630
+ return (jsxRuntime.jsxs("span", Object.assign({ className: classNames(generateClasses, className), style: style }, id && { id }, dataAttributes, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })), jsxRuntime.jsx("span", { children: text }), iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle }))] })));
630
631
  }
631
632
 
632
633
  function DBox({ className, style, children, dataAttributes, }) {
@@ -658,6 +659,7 @@ function DInput(_a, ref) {
658
659
  const inputRef = useProvidedRefOrCreate(ref);
659
660
  const innerId = React.useId();
660
661
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
662
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
661
663
  const handleOnChange = React.useCallback((event) => {
662
664
  onChange === null || onChange === void 0 ? void 0 : onChange(event.currentTarget.value);
663
665
  }, [onChange]);
@@ -720,7 +722,7 @@ function DInput(_a, ref) {
720
722
  [`input-group-${size}`]: !!size,
721
723
  'input-group': true,
722
724
  'has-validation': invalid || valid,
723
- }), children: [!!inputStart && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputStart`, children: inputStart })), iconStart && (onIconStartClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading || iconStartDisabled, "aria-label": iconStartAriaLabel || (typeof iconStart === 'string' ? iconStart : 'start icon'), tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Start`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle }) }))), dynamicComponent, (iconEnd && !loading) && (onIconEndClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel || (typeof iconEnd === 'string' ? iconEnd : 'end icon'), tabIndex: iconEndTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}End`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }) }))), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", "data-testid": "loading-spinner", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
725
+ }), children: [!!inputStart && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputStart`, children: inputStart })), iconStart && (onIconStartClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: handleOnIconStartClick, disabled: disabled || loading || iconStartDisabled, "aria-label": iconStartAriaLabel || (typeof iconStart === 'string' ? iconStart : 'start icon'), tabIndex: iconStartTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Start`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle }) }))), dynamicComponent, (iconEnd && !loading) && (onIconEndClick ? (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel || (typeof iconEnd === 'string' ? iconEnd : 'end icon'), tabIndex: iconEndTabIndex, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) })) : (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}End`, "aria-hidden": "true", tabIndex: -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) }))), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", "data-testid": "loading-spinner", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
724
726
  }
725
727
  const ForwardedDInput = React.forwardRef(DInput);
726
728
  ForwardedDInput.displayName = 'DInput';
@@ -1116,7 +1118,7 @@ function useDBoxFile(props) {
1116
1118
 
1117
1119
  function DBoxFile(_a) {
1118
1120
  var { icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, children, className, style, dataAttributes } = _a, props = tslib.__rest(_a, ["icon", "iconFamilyClass", "iconFamilyPrefix", "iconMaterialStyle", "children", "className", "style", "dataAttributes"]);
1119
- const { iconMap: { upload } } = useDContext();
1121
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { upload }, } = useDContext();
1120
1122
  const icon = React.useMemo(() => iconProp || upload, [iconProp, upload]);
1121
1123
  const { inputRef, rootRef, isDragValid, isDragInvalid, acceptAttr, files, handleFileSelect, handleDrop, handleDragEnter, handleDragLeave, handleClick, handleKeyDown, handleRemoveFile, openFileDialog, } = useDBoxFile(props);
1122
1124
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("section", Object.assign({ className: classNames('d-box-file', {
@@ -1124,13 +1126,14 @@ function DBoxFile(_a) {
1124
1126
  'd-box-file-disabled': props.disabled,
1125
1127
  'd-box-file-valid': isDragValid,
1126
1128
  'd-box-file-invalid': isDragInvalid,
1127
- }, className), style: style }, dataAttributes, { children: jsxRuntime.jsxs("div", Object.assign({ className: "d-box-file-dropzone", ref: rootRef, onDragEnter: handleDragEnter, onDragOver: (e) => e.preventDefault(), onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: handleClick, onKeyDown: handleKeyDown }, (!props.disabled && !props.noKeyboard ? { tabIndex: 0 } : {}), { role: "presentation", children: [jsxRuntime.jsx("input", { type: "file", multiple: props.multiple, style: { display: 'none' }, ref: inputRef, disabled: props.disabled, onChange: handleFileSelect, onClick: (e) => e.stopPropagation(), tabIndex: -1, accept: acceptAttr }), icon && iconProp !== false && (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })), jsxRuntime.jsx("div", { className: "d-box-content", children: typeof children === 'function'
1129
+ }, className), style: style }, dataAttributes, { children: jsxRuntime.jsxs("div", Object.assign({ className: "d-box-file-dropzone", ref: rootRef, onDragEnter: handleDragEnter, onDragOver: (e) => e.preventDefault(), onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: handleClick, onKeyDown: handleKeyDown }, (!props.disabled && !props.noKeyboard ? { tabIndex: 0 } : {}), { role: "presentation", children: [jsxRuntime.jsx("input", { type: "file", multiple: props.multiple, style: { display: 'none' }, ref: inputRef, disabled: props.disabled, onChange: handleFileSelect, onClick: (e) => e.stopPropagation(), tabIndex: -1, accept: acceptAttr }), icon && iconProp !== false && (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })), jsxRuntime.jsx("div", { className: "d-box-content", children: typeof children === 'function'
1128
1130
  ? children(openFileDialog)
1129
- : children || (jsxRuntime.jsx("p", { className: "text-center m-0", children: "Drag and drop some files here, or click to select files" })) })] })) })), !!files.length && (jsxRuntime.jsx("ul", { className: "d-box-files", children: files.map((file, index) => (jsxRuntime.jsx(ForwardedDInput, { value: file.name, iconStart: "Paperclip", iconEnd: "Trash", readOnly: true, onIconEndClick: () => handleRemoveFile(index) }, `${file.name} ${index}`))) }))] }));
1131
+ : children || (jsxRuntime.jsx("p", { className: "d-box-file-empty-text", children: "Drag and drop some files here, or click to select files" })) })] })) })), !!files.length && (jsxRuntime.jsx("ul", { className: "d-box-files", children: files.map((file, index) => (jsxRuntime.jsx(ForwardedDInput, { value: file.name, iconStart: "Paperclip", iconEnd: "Trash", readOnly: true, onIconEndClick: () => handleRemoveFile(index) }, `${file.name} ${index}`))) }))] }));
1130
1132
  }
1131
1133
 
1132
1134
  const DButton = React.forwardRef((props, ref) => {
1133
1135
  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']);
1136
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
1134
1137
  // Responsive size resolution using useResponsiveProp
1135
1138
  const { responsivePropValue } = useResponsiveProp(true);
1136
1139
  const resolvedSize = React.useMemo(() => {
@@ -1184,7 +1187,7 @@ const DButton = React.forwardRef((props, ref) => {
1184
1187
  ref.current = node;
1185
1188
  }, className: classNames(classes, className), style: Object.assign(Object.assign({}, style), (loading && buttonWidth
1186
1189
  ? { minWidth: `${buttonWidth}px` }
1187
- : undefined)), "aria-label": ariaLabel, "aria-busy": loading, "aria-disabled": isDisabled, onClick: handleClick }, dataAttributes, { children: [loading && (jsxRuntime.jsxs("span", { className: "btn-loading", children: [jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", "aria-hidden": "true" }), loadingText && jsxRuntime.jsx("span", { role: "status", children: loadingText })] })), !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle })), content, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }))] }))] })));
1190
+ : undefined)), "aria-label": ariaLabel, "aria-busy": loading, "aria-disabled": isDisabled, onClick: handleClick }, dataAttributes, { children: [loading && (jsxRuntime.jsxs("span", { className: "btn-loading", children: [jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", "aria-hidden": "true" }), loadingText && jsxRuntime.jsx("span", { role: "status", children: loadingText })] })), !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle })), content, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }))] }))] })));
1188
1191
  }
1189
1192
  return (jsxRuntime.jsxs("button", Object.assign({ ref: (node) => {
1190
1193
  buttonRef.current = node;
@@ -1198,12 +1201,13 @@ const DButton = React.forwardRef((props, ref) => {
1198
1201
  // eslint-disable-next-line react/button-has-type
1199
1202
  type: type, className: classNames(classes, className), style: Object.assign(Object.assign({}, style), (loading && buttonWidth
1200
1203
  ? { minWidth: `${buttonWidth}px` }
1201
- : undefined)), disabled: isDisabled, "aria-label": ariaLabel, "aria-busy": loading, "aria-disabled": isDisabled, onClick: handleClick }, dataAttributes, rest, { children: [loading && (jsxRuntime.jsxs("span", { className: "btn-loading", children: [jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", "aria-hidden": "true" }), loadingText && jsxRuntime.jsx("span", { role: "status", children: loadingText })] })), !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle })), content, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }))] }))] })));
1204
+ : undefined)), disabled: isDisabled, "aria-label": ariaLabel, "aria-busy": loading, "aria-disabled": isDisabled, onClick: handleClick }, dataAttributes, rest, { children: [loading && (jsxRuntime.jsxs("span", { className: "btn-loading", children: [jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", "aria-hidden": "true" }), loadingText && jsxRuntime.jsx("span", { role: "status", children: loadingText })] })), !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle })), content, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }))] }))] })));
1202
1205
  });
1203
1206
  DButton.displayName = 'DButton';
1204
1207
 
1205
1208
  function DButtonIcon(_a) {
1206
1209
  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']);
1210
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
1207
1211
  const generateClasses = React.useMemo(() => {
1208
1212
  const variantClass = !variant || variant === 'solid'
1209
1213
  ? `btn-${color}`
@@ -1227,11 +1231,11 @@ function DButtonIcon(_a) {
1227
1231
  if (href) {
1228
1232
  return (jsxRuntime.jsx("a", Object.assign({ id: id, href: href, target: target, rel: rel, className: classNames(generateClasses, className), style: style, onClick: clickHandler, "aria-label": ariaLabel, "aria-disabled": isDisabled }, dataAttributes, { children: loading
1229
1233
  ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1230
- : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })) })));
1234
+ : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1231
1235
  }
1232
1236
  return (jsxRuntime.jsx("button", Object.assign({ className: classNames(generateClasses, className), style: style, disabled: state === 'disabled' || loading, onClick: clickHandler, "aria-label": ariaLabel }, dataAttributes, rest, { children: loading
1233
1237
  ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1234
- : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })) })));
1238
+ : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })) })));
1235
1239
  }
1236
1240
 
1237
1241
  function DCardHeader({ className, style, children, }) {
@@ -1294,12 +1298,12 @@ function DChip({ color = 'primary', text, icon, iconFamilyClass, iconFamilyPrefi
1294
1298
  'd-chip': true,
1295
1299
  [`d-chip-${color}`]: !!color,
1296
1300
  }), [color]);
1297
- const { iconMap: { xLg, }, } = useDContext();
1301
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { xLg, }, } = useDContext();
1298
1302
  const iconClose = React.useMemo(() => iconCloseProp || xLg, [iconCloseProp, xLg]);
1299
- return (jsxRuntime.jsxs("span", Object.assign({ className: classNames(generateClasses, className), style: style }, dataAttributes, { children: [icon && (jsxRuntime.jsx("div", { className: "d-chip-icon-container", children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle }) })), jsxRuntime.jsx("span", { children: text }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-chip-icon-container", onClick: onClose, "aria-label": closeAriaLabel, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass, familyPrefix: iconCloseFamilyPrefix, materialStyle: iconCloseMaterialStyle }) }))] })));
1303
+ return (jsxRuntime.jsxs("span", Object.assign({ className: classNames(generateClasses, className), style: style }, dataAttributes, { children: [icon && (jsxRuntime.jsx("div", { className: "d-chip-icon-container", children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle }) })), jsxRuntime.jsx("span", { children: text }), showClose && (jsxRuntime.jsx("button", { type: "button", className: "d-chip-icon-container", onClick: onClose, "aria-label": closeAriaLabel, children: jsxRuntime.jsx(DIcon, { icon: iconClose, familyClass: iconCloseFamilyClass !== null && iconCloseFamilyClass !== void 0 ? iconCloseFamilyClass : familyClass, familyPrefix: iconCloseFamilyPrefix !== null && iconCloseFamilyPrefix !== void 0 ? iconCloseFamilyPrefix : familyPrefix, materialStyle: iconCloseMaterialStyle !== null && iconCloseMaterialStyle !== void 0 ? iconCloseMaterialStyle : materialStyle }) }))] })));
1300
1304
  }
1301
1305
 
1302
- function DCollapse({ id, className, style, Component, defaultCollapsed = true, onChange, children, iconOpen: iconOpenProp, iconClose: iconCloseProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle = false, dataAttributes, }) {
1306
+ function DCollapse({ id, className, style, Component, defaultCollapsed = true, onChange, children, iconOpen: iconOpenProp, iconClose: iconCloseProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, dataAttributes, }) {
1303
1307
  const [collapsed, setCollapsed] = React.useState(defaultCollapsed);
1304
1308
  const onChangeCollapse = () => {
1305
1309
  setCollapsed((prev) => {
@@ -1313,10 +1317,10 @@ function DCollapse({ id, className, style, Component, defaultCollapsed = true, o
1313
1317
  React.useEffect(() => {
1314
1318
  setCollapsed(defaultCollapsed);
1315
1319
  }, [defaultCollapsed]);
1316
- const { iconMap: { chevronDown, chevronUp, }, } = useDContext();
1320
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { chevronDown, chevronUp, }, } = useDContext();
1317
1321
  const iconOpen = React.useMemo(() => iconOpenProp || chevronDown, [chevronDown, iconOpenProp]);
1318
1322
  const iconClose = React.useMemo(() => iconCloseProp || chevronUp, [chevronUp, iconCloseProp]);
1319
- return (jsxRuntime.jsxs("div", Object.assign({ id: id, className: classNames('collapse-container', className), style: style }, dataAttributes, { children: [jsxRuntime.jsxs("button", { className: "collapse-button", type: "button", onClick: onChangeCollapse, children: [jsxRuntime.jsx("div", { className: "flex-grow-1", children: Component }), jsxRuntime.jsx(DIcon, { color: "primary", size: "1.25rem", icon: collapsed ? iconOpen : iconClose, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })] }), jsxRuntime.jsx("div", { className: classNames('collapse-body-wrapper', {
1323
+ return (jsxRuntime.jsxs("div", Object.assign({ id: id, className: classNames('collapse-container', className), style: style }, dataAttributes, { children: [jsxRuntime.jsxs("button", { className: "collapse-button", type: "button", onClick: onChangeCollapse, children: [jsxRuntime.jsx("div", { className: "flex-grow-1", children: Component }), jsxRuntime.jsx(DIcon, { className: "collapse-icon", color: "primary", size: "1.25rem", icon: collapsed ? iconOpen : iconClose, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyle })] }), jsxRuntime.jsx("div", { className: classNames('collapse-body-wrapper', {
1320
1324
  show: !collapsed,
1321
1325
  }), children: jsxRuntime.jsx("div", { className: "collapse-body", children: children }) })] })));
1322
1326
  }
@@ -1502,9 +1506,9 @@ function DDatePickerHeaderSelector({ date, changeYear, changeMonth, decreaseMont
1502
1506
  return (jsxRuntime.jsxs("div", { className: classNames('react-datepicker__header-selector react-datepicker__header-year-selector', className), style: style, children: [jsxRuntime.jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseYear, disabled: prevYearButtonDisabled, "aria-label": prevYearAriaLabel, className: "header-button" }), jsxRuntime.jsx("p", { children: `${startYear} - ${endYear}` }), jsxRuntime.jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseYear, disabled: nextYearButtonDisabled, "aria-label": nextYearAriaLabel, className: "header-button" })] }));
1503
1507
  }
1504
1508
  if (pickerType === PickerType.Quarter || pickerType === PickerType.Month) {
1505
- return (jsxRuntime.jsxs("div", { className: classNames(`react-datepicker__header-selector react-datepicker__header-${pickerType}-selector`, className), style: style, children: [jsxRuntime.jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseYear, disabled: prevMonthButtonDisabled, "aria-label": prevMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === 0 ? 'visible' : 'hidden' } }), jsxRuntime.jsx("div", { className: "d-flex justify-content-center flex-grow-1", children: showHeaderSelectors ? (jsxRuntime.jsx(DSelect, { options: years, value: defaultYear, defaultValue: defaultYear, onChange: (year) => changeYear(Number(year === null || year === void 0 ? void 0 : year.value)), searchable: false })) : (jsxRuntime.jsx("p", { children: defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label })) }), jsxRuntime.jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseYear, disabled: nextMonthButtonDisabled, "aria-label": nextMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === monthsShown - 1 ? 'visible' : 'hidden' } })] }));
1509
+ return (jsxRuntime.jsxs("div", { className: classNames(`react-datepicker__header-selector react-datepicker__header-${pickerType}-selector`, className), style: style, children: [jsxRuntime.jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseYear, disabled: prevMonthButtonDisabled, "aria-label": prevMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === 0 ? 'visible' : 'hidden' } }), jsxRuntime.jsx("div", { className: "react-datepicker__header-year-selector", children: showHeaderSelectors ? (jsxRuntime.jsx(DSelect, { options: years, value: defaultYear, defaultValue: defaultYear, onChange: (year) => changeYear(Number(year === null || year === void 0 ? void 0 : year.value)), searchable: false })) : (jsxRuntime.jsx("p", { children: defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label })) }), jsxRuntime.jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseYear, disabled: nextMonthButtonDisabled, "aria-label": nextMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === monthsShown - 1 ? 'visible' : 'hidden' } })] }));
1506
1510
  }
1507
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "datepicker-top-header", children: [showHeaderSelectors && (jsxRuntime.jsx(DSelect, { options: years, value: defaultYear, defaultValue: defaultYear, onChange: (year) => changeYear(Number(year === null || year === void 0 ? void 0 : year.value)), searchable: false, className: "custom-year-selector" })), jsxRuntime.jsx("h4", { className: "mb-0 fw-normal", children: dateFns.format(monthDate, formatHeaderDate, { locale }) })] }), jsxRuntime.jsxs("div", { className: classNames('react-datepicker__header-selector react-datepicker__header-day-selector', className), style: style, children: [jsxRuntime.jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseMonth, disabled: prevMonthButtonDisabled, "aria-label": prevMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === 0 ? 'visible' : 'hidden' } }), showHeaderSelectors ? (jsxRuntime.jsx(DSelect, { options: months, value: defaultMonth, defaultValue: defaultMonth, onChange: (month) => changeMonth((month === null || month === void 0 ? void 0 : month.value) || 0), searchable: false, className: "custom-month-selector" })) : (jsxRuntime.jsx("p", { children: `${defaultMonth.label} ${defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label}` })), jsxRuntime.jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseMonth, disabled: nextMonthButtonDisabled, "aria-label": nextMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === monthsShown - 1 ? 'visible' : 'hidden' } })] })] }));
1511
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "datepicker-top-header", children: [showHeaderSelectors && (jsxRuntime.jsx(DSelect, { options: years, value: defaultYear, defaultValue: defaultYear, onChange: (year) => changeYear(Number(year === null || year === void 0 ? void 0 : year.value)), searchable: false, className: "custom-year-selector" })), jsxRuntime.jsx("h4", { className: "datepicker-top-header-title", children: dateFns.format(monthDate, formatHeaderDate, { locale }) })] }), jsxRuntime.jsxs("div", { className: classNames('react-datepicker__header-selector react-datepicker__header-day-selector', className), style: style, children: [jsxRuntime.jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseMonth, disabled: prevMonthButtonDisabled, "aria-label": prevMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === 0 ? 'visible' : 'hidden' } }), showHeaderSelectors ? (jsxRuntime.jsx(DSelect, { options: months, value: defaultMonth, defaultValue: defaultMonth, onChange: (month) => changeMonth((month === null || month === void 0 ? void 0 : month.value) || 0), searchable: false, className: "custom-month-selector" })) : (jsxRuntime.jsx("p", { children: `${defaultMonth.label} ${defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label}` })), jsxRuntime.jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseMonth, disabled: nextMonthButtonDisabled, "aria-label": nextMonthAriaLabel, className: "header-button", style: { visibility: customHeaderCount === monthsShown - 1 ? 'visible' : 'hidden' } })] })] }));
1508
1512
  }
1509
1513
 
1510
1514
  function DDatePicker(_a) {
@@ -1639,11 +1643,24 @@ function formatValue(value, currencyOptions) {
1639
1643
  }
1640
1644
  return currency(value, Object.assign(Object.assign({}, currencyOptions), { symbol: '' })).format();
1641
1645
  }
1642
- function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref) {
1646
+ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue) {
1643
1647
  const inputRef = useProvidedRefOrCreate(ref);
1648
+ const clampValue = React.useCallback((newValue) => {
1649
+ if (newValue === undefined) {
1650
+ return newValue;
1651
+ }
1652
+ let clampedValue = newValue;
1653
+ if (minValue !== undefined) {
1654
+ clampedValue = Math.max(clampedValue, minValue);
1655
+ }
1656
+ if (maxValue !== undefined) {
1657
+ clampedValue = Math.min(clampedValue, maxValue);
1658
+ }
1659
+ return clampedValue;
1660
+ }, [minValue, maxValue]);
1644
1661
  const [innerType, setInnerType] = React.useState('text');
1645
- const [innerNumber, setInnerNumber] = React.useState(value);
1646
- const [innerString, setInnerString] = React.useState(formatValue(value, currencyOptions));
1662
+ const [innerNumber, setInnerNumber] = React.useState(clampValue(value));
1663
+ const [innerString, setInnerString] = React.useState(formatValue(clampValue(value), currencyOptions));
1647
1664
  const handleOnFocus = React.useCallback((event) => {
1648
1665
  event.stopPropagation();
1649
1666
  setInnerType('number');
@@ -1652,15 +1669,14 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
1652
1669
  const handleOnBlur = React.useCallback((event) => {
1653
1670
  event.stopPropagation();
1654
1671
  setInnerType('text');
1672
+ const clampedNumber = clampValue(innerNumber);
1673
+ if (clampedNumber !== innerNumber) {
1674
+ setInnerNumber(clampedNumber);
1675
+ setInnerString(formatValue(clampedNumber, currencyOptions));
1676
+ onChange === null || onChange === void 0 ? void 0 : onChange(clampedNumber);
1677
+ }
1655
1678
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1656
- }, [onBlur]);
1657
- const generateStyleVariables = React.useMemo(() => ({
1658
- [`--${PREFIX_BS}input-currency-component-symbol-color`]: `var(--${PREFIX_BS}secondary)`,
1659
- [`--${PREFIX_BS}input-currency-symbol-color`]: `var(--${PREFIX_BS}input-currency-component-symbol-color)`,
1660
- }), []);
1661
- const generateSymbolStyleVariables = React.useMemo(() => ({
1662
- color: `var(--${PREFIX_BS}input-currency-symbol-color)`,
1663
- }), []);
1679
+ }, [onBlur, innerNumber, clampValue, currencyOptions, onChange]);
1664
1680
  const handleOnChange = React.useCallback((newValue) => {
1665
1681
  const newNumber = (newValue === undefined || newValue === '') ? undefined : Number(newValue);
1666
1682
  if (newNumber !== innerNumber) {
@@ -1669,7 +1685,12 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
1669
1685
  onChange === null || onChange === void 0 ? void 0 : onChange(newNumber);
1670
1686
  }
1671
1687
  }, [currencyOptions, onChange, innerNumber]);
1688
+ const isMountedRef = React.useRef(false);
1672
1689
  React.useEffect(() => {
1690
+ if (!isMountedRef.current) {
1691
+ isMountedRef.current = true;
1692
+ return;
1693
+ }
1673
1694
  if (value !== innerNumber) {
1674
1695
  setInnerNumber(value);
1675
1696
  setInnerString(formatValue(value, currencyOptions));
@@ -1683,8 +1704,6 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
1683
1704
  handleOnFocus,
1684
1705
  handleOnChange,
1685
1706
  handleOnBlur,
1686
- generateStyleVariables,
1687
- generateSymbolStyleVariables,
1688
1707
  };
1689
1708
  }
1690
1709
 
@@ -1789,6 +1808,149 @@ function useConfirmModal(config) {
1789
1808
  return { open };
1790
1809
  }
1791
1810
 
1811
+ /**
1812
+ * Generic seconds-based countdown hook.
1813
+ *
1814
+ * Starts counting down from `seconds` to `0` and exposes `restartCountdown`
1815
+ * to reset and restart the timer (e.g. for "resend code in Xs" patterns,
1816
+ * session-expiry banners, or any other cooldown/throttle UI).
1817
+ *
1818
+ * @example
1819
+ * const { secondsLeft, restartCountdown } = useCountdown(30);
1820
+ */
1821
+ function useCountdown(seconds) {
1822
+ const [secondsLeft, setSecondsLeft] = React.useState(seconds);
1823
+ const [isActive, setIsActive] = React.useState(true);
1824
+ const resetCountdown = React.useCallback((newSeconds = seconds) => {
1825
+ setIsActive(false);
1826
+ setSecondsLeft(newSeconds);
1827
+ }, [seconds]);
1828
+ const restartCountdown = React.useCallback(() => {
1829
+ resetCountdown(seconds);
1830
+ setIsActive(true);
1831
+ }, [resetCountdown, seconds]);
1832
+ React.useEffect(() => {
1833
+ if (!isActive) {
1834
+ return () => { };
1835
+ }
1836
+ const interval = setInterval(() => {
1837
+ setSecondsLeft((prevSeconds) => {
1838
+ const newSeconds = prevSeconds - 1;
1839
+ if (newSeconds <= 0) {
1840
+ clearInterval(interval);
1841
+ setIsActive(false);
1842
+ return 0;
1843
+ }
1844
+ return newSeconds;
1845
+ });
1846
+ }, 1000);
1847
+ return () => clearInterval(interval);
1848
+ }, [isActive]);
1849
+ return { secondsLeft, restartCountdown };
1850
+ }
1851
+
1852
+ /**
1853
+ * Headless hook exposing the same OTP logic used internally by `DOtp`
1854
+ * (code state, length + correctness validation, submit action and resend
1855
+ * countdown), without any bundled markup/styles.
1856
+ *
1857
+ * Use this when the default `DOtp` component doesn't match your design and
1858
+ * you need to build a fully custom UI while reusing the same behavior.
1859
+ *
1860
+ * Internally composes `useCountdown` for the resend timer and re-exposes
1861
+ * `secondsLeft`/`restartCountdown`, so there is no need to call
1862
+ * `useCountdown` separately.
1863
+ *
1864
+ * `invalid` becomes `true` both when the OTP is shorter than `otpSize` and
1865
+ * when `action` throws/rejects (e.g. the backend rejected the entered code
1866
+ * as incorrect). In the latter case, the thrown value is exposed as `error`
1867
+ * so custom UIs can render an appropriate message (e.g. "Invalid code" vs.
1868
+ * "Enter all digits"). `invalid`/`error` are cleared automatically as soon
1869
+ * as the user edits the code via `setOtp`.
1870
+ *
1871
+ * @example
1872
+ * function CustomOtp() {
1873
+ * const {
1874
+ * otp,
1875
+ * setOtp,
1876
+ * invalid,
1877
+ * error,
1878
+ * submit,
1879
+ * isLoading,
1880
+ * secondsLeft,
1881
+ * restartCountdown,
1882
+ * } = useOtp({ action: async () => verifyOtp(otp), otpSize: 6, seconds: 15 });
1883
+ *
1884
+ * return (
1885
+ * <>
1886
+ * <MyCustomPinInput value={otp} onChange={setOtp} invalid={invalid} />
1887
+ * {invalid && <span>{error ? 'Invalid code' : 'Enter all digits'}</span>}
1888
+ * <button disabled={isLoading} onClick={() => { submit().catch(() => {}); }}>
1889
+ * {isLoading ? 'Submitting...' : 'Submit'}
1890
+ * </button>
1891
+ * <button disabled={secondsLeft > 0} onClick={restartCountdown}>
1892
+ * Resend {secondsLeft > 0 ? `(${secondsLeft}s)` : ''}
1893
+ * </button>
1894
+ * </>
1895
+ * );
1896
+ * }
1897
+ */
1898
+ function useOtp({ action, otpSize = 6, seconds = 15, }) {
1899
+ const [otp, setOtpValue] = React.useState('');
1900
+ const [invalid, setInvalid] = React.useState(false);
1901
+ const [error, setError] = React.useState(null);
1902
+ const [isLoading, setIsLoading] = React.useState(false);
1903
+ const { secondsLeft, restartCountdown } = useCountdown(seconds);
1904
+ const setOtp = React.useCallback((value) => {
1905
+ setOtpValue((prevOtp) => {
1906
+ // Guard against no-op calls (e.g. DInputPin re-notifying the same
1907
+ // value when its onChange reference changes) so we don't wipe out
1908
+ // an `invalid`/`error` state that was just set by submit().
1909
+ if (prevOtp === value) {
1910
+ return prevOtp;
1911
+ }
1912
+ setInvalid(false);
1913
+ setError(null);
1914
+ return value;
1915
+ });
1916
+ }, []);
1917
+ const submit = React.useCallback(async () => {
1918
+ if (otp.length < otpSize) {
1919
+ setInvalid(true);
1920
+ setError(null);
1921
+ return;
1922
+ }
1923
+ setIsLoading(true);
1924
+ try {
1925
+ await action();
1926
+ setInvalid(false);
1927
+ setError(null);
1928
+ }
1929
+ catch (err) {
1930
+ setInvalid(true);
1931
+ setError(err);
1932
+ throw err;
1933
+ }
1934
+ finally {
1935
+ setIsLoading(false);
1936
+ }
1937
+ }, [
1938
+ otp.length,
1939
+ action,
1940
+ otpSize,
1941
+ ]);
1942
+ return {
1943
+ otp,
1944
+ setOtp,
1945
+ invalid,
1946
+ error,
1947
+ submit,
1948
+ isLoading,
1949
+ secondsLeft,
1950
+ restartCountdown,
1951
+ };
1952
+ }
1953
+
1792
1954
  function DInputCounter(_a, ref) {
1793
1955
  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"]);
1794
1956
  const { handleOnWheel, } = useDisableInputWheel(ref);
@@ -1831,8 +1993,8 @@ function DInputCurrency(_a, ref) {
1831
1993
  var { value, minValue, maxValue, currencyCode, onFocus, onBlur, onChange } = _a, props = tslib.__rest(_a, ["value", "minValue", "maxValue", "currencyCode", "onFocus", "onBlur", "onChange"]);
1832
1994
  const { currency: currencyOptions } = useDContext();
1833
1995
  const { handleOnWheel, } = useDisableInputWheel(ref);
1834
- const { inputRef, innerValue, innerType, handleOnFocus, handleOnChange, handleOnBlur, generateStyleVariables, generateSymbolStyleVariables, } = useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref);
1835
- return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange, style: generateStyleVariables, inputMode: "decimal", type: innerType, onFocus: handleOnFocus, onBlur: handleOnBlur, onWheel: handleOnWheel, inputStart: (jsxRuntime.jsx("span", { slot: "input-start", style: generateSymbolStyleVariables, children: currencyCode || currencyOptions.symbol })) }, props)));
1996
+ const { inputRef, innerValue, innerType, handleOnFocus, handleOnChange, handleOnBlur, } = useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue);
1997
+ return (jsxRuntime.jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange, inputMode: "decimal", type: innerType, onFocus: handleOnFocus, onBlur: handleOnBlur, onWheel: handleOnWheel, inputStart: (jsxRuntime.jsx("span", { slot: "input-start", className: "d-input-currency-symbol", children: currencyCode || currencyOptions.symbol })) }, props)));
1836
1998
  }
1837
1999
  const ForwardedDInputCurrency = React.forwardRef(DInputCurrency);
1838
2000
  ForwardedDInputCurrency.displayName = 'DInputCurrency';
@@ -1854,26 +2016,25 @@ ForwardedDInputPassword.displayName = 'DInputPassword';
1854
2016
 
1855
2017
  function PasswordCheckItem({ password, regex, text, }) {
1856
2018
  const isValid = regex.test(password);
1857
- return (jsxRuntime.jsxs("li", { className: "d-flex gap-2 align-items-start small text-gray-600", children: [jsxRuntime.jsx(DIcon, { className: classNames('flex-shrink-0', isValid ? 'text-success' : 'text-gray-300'), icon: isValid ? 'CircleCheck' : 'Circle', size: "16px" }), jsxRuntime.jsx("span", { className: classNames({ 'text-success': isValid }), children: text })] }));
2019
+ return (jsxRuntime.jsxs("li", { className: `d-password-check-item${isValid ? ' is-valid' : ''}`, children: [jsxRuntime.jsx(DIcon, { className: "d-password-check-item-icon", icon: isValid ? 'CircleCheck' : 'Circle', size: "16px" }), jsxRuntime.jsx("span", { children: text })] }));
1858
2020
  }
1859
2021
 
1860
- const getColorClass = (strength, total) => {
2022
+ const getStrengthModifier = (strength, total) => {
1861
2023
  const percentage = total > 0 ? strength / total : 0;
1862
2024
  if (percentage === 0)
1863
- return 'bg-gray-200';
2025
+ return 'is-empty';
1864
2026
  if (percentage <= 0.25)
1865
- return 'bg-danger';
2027
+ return 'is-weak';
1866
2028
  if (percentage <= 0.5)
1867
- return 'bg-warning';
2029
+ return 'is-fair';
1868
2030
  if (percentage <= 0.75)
1869
- return 'bg-info';
1870
- return 'bg-success';
2031
+ return 'is-good';
2032
+ return 'is-strong';
1871
2033
  };
1872
2034
  function PasswordStrengthBar({ strength, total }) {
1873
2035
  const percentage = total > 0 ? (strength / total) * 100 : 0;
1874
- return (jsxRuntime.jsx("div", { className: "w-100 rounded-3 overflow-hidden bg-gray-100 mb-2", style: { height: '8px' }, children: jsxRuntime.jsx("div", { className: `h-100 ${getColorClass(strength, total)}`, style: {
2036
+ return (jsxRuntime.jsx("div", { className: "d-password-strength-bar", children: jsxRuntime.jsx("div", { className: `d-password-strength-bar-fill ${getStrengthModifier(strength, total)}`, style: {
1875
2037
  width: `${percentage}%`,
1876
- transition: 'width 0.3s ease-in-out',
1877
2038
  } }) }));
1878
2039
  }
1879
2040
 
@@ -1909,7 +2070,7 @@ function PasswordChecksList({ password, validationMessages, enabledChecks, }) {
1909
2070
  const passwordChecks = allChecks.filter((check) => enabledChecks.includes(check.key));
1910
2071
  const passed = passwordChecks.filter((r) => r.regex.test(password)).length;
1911
2072
  const total = passwordChecks.length;
1912
- return (jsxRuntime.jsxs("div", { className: "mt-2", children: [jsxRuntime.jsx(PasswordStrengthBar, { strength: passed, total: total }), jsxRuntime.jsx("ul", { className: "list-unstyled m-0 d-flex flex-column gap-2", children: passwordChecks.map(({ key, regex, text }) => (jsxRuntime.jsx(PasswordCheckItem, { password: password, regex: regex, text: text }, key))) })] }));
2073
+ return (jsxRuntime.jsxs("div", { className: "d-password-strength-meter", children: [jsxRuntime.jsx(PasswordStrengthBar, { strength: passed, total: total }), jsxRuntime.jsx("ul", { className: "d-password-check-list", children: passwordChecks.map(({ key, regex, text }) => (jsxRuntime.jsx(PasswordCheckItem, { password: password, regex: regex, text: text }, key))) })] }));
1913
2074
  }
1914
2075
 
1915
2076
  const DEFAULT_VALIDATION_MESSAGES = {
@@ -1950,9 +2111,9 @@ function DInputCheck(_a) {
1950
2111
  ]);
1951
2112
  React.useEffect(() => {
1952
2113
  if (innerRef.current) {
1953
- innerRef.current.indeterminate = Boolean(indeterminate);
2114
+ innerRef.current.indeterminate = type === 'checkbox' && Boolean(indeterminate);
1954
2115
  }
1955
- }, [indeterminate]);
2116
+ }, [indeterminate, type]);
1956
2117
  React.useEffect(() => {
1957
2118
  if (innerRef.current) {
1958
2119
  innerRef.current.checked = checked;
@@ -2145,10 +2306,18 @@ function DInputSelect({ id: idProp, name, label = '', className, style, options
2145
2306
  }), children: [iconStart && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}Start`, onClick: iconStartClickHandler, disabled: disabled || loading, "aria-label": iconStartAriaLabel, children: iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix })) })), dynamicComponent, iconEnd && !loading && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: iconEndClickHandler, disabled: disabled || loading, "aria-label": iconEndAriaLabel, children: iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix })) })), loading && (jsxRuntime.jsx("div", { className: "input-group-text form-control-icon loading", children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2146
2307
  }
2147
2308
 
2148
- function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, disabled, invalid = false, valid = false, readonly, className, style, dataAttributes, inputClassName, onChange, }) {
2309
+ function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, disabled, invalid = false, valid = false, hint, readonly, className, style, dataAttributes, inputClassName, onChange, }) {
2149
2310
  const innerId = React.useId();
2150
2311
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
2151
2312
  const [internalIsChecked, setInternalIsChecked] = React.useState(checked);
2313
+ const ariaDescribedby = React.useMemo(() => ([
2314
+ !!hint && `${id}Hint`,
2315
+ ]
2316
+ .filter(Boolean)
2317
+ .join(' ')), [
2318
+ id,
2319
+ hint,
2320
+ ]);
2152
2321
  React.useEffect(() => {
2153
2322
  setInternalIsChecked(checked);
2154
2323
  }, [checked]);
@@ -2157,10 +2326,10 @@ function DInputSwitch({ id: idProp, label, ariaLabel, name, checked, disabled, i
2157
2326
  setInternalIsChecked(value);
2158
2327
  onChange === null || onChange === void 0 ? void 0 : onChange(value);
2159
2328
  }, [onChange]);
2160
- return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('form-check form-switch', className) }, dataAttributes, { children: [jsxRuntime.jsx("input", { id: id, name: name, onChange: readonly ? () => false : changeHandler, className: classNames('form-check-input', {
2329
+ return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('form-check form-switch', className) }, dataAttributes, { children: [jsxRuntime.jsx("input", Object.assign({ id: id, name: name, onChange: readonly ? () => false : changeHandler, className: classNames('form-check-input', {
2161
2330
  'is-invalid': invalid,
2162
2331
  'is-valid': valid,
2163
- }, inputClassName), style: style, type: "checkbox", role: "switch", checked: internalIsChecked, disabled: disabled, "aria-label": ariaLabel }), label && (jsxRuntime.jsx("label", { className: "form-check-label", htmlFor: id, children: label }))] })));
2332
+ }, inputClassName), style: style, type: "checkbox", role: "switch", checked: internalIsChecked, disabled: disabled, "aria-label": ariaLabel }, ariaDescribedby && { 'aria-describedby': ariaDescribedby })), label && (jsxRuntime.jsx("label", { className: "form-check-label", htmlFor: id, children: label })), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2164
2333
  }
2165
2334
 
2166
2335
  function DInputRange(_a, ref) {
@@ -2201,6 +2370,7 @@ const ForwardedDInputRange = React.forwardRef(DInputRange);
2201
2370
  ForwardedDInputRange.displayName = 'DInputRange';
2202
2371
 
2203
2372
  function DListGroupItem({ as = 'li', action: actionProp, active, disabled, href, onClick, color, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, children, className, style, dataAttributes, }) {
2373
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
2204
2374
  const Tag = React.useMemo(() => {
2205
2375
  if (href) {
2206
2376
  return 'a';
@@ -2229,7 +2399,7 @@ function DListGroupItem({ as = 'li', action: actionProp, active, disabled, href,
2229
2399
  }
2230
2400
  return Object.assign(Object.assign({}, active && { 'aria-current': true }), disabled && { 'aria-disabled': true });
2231
2401
  }, [Tag, active, disabled]);
2232
- return (jsxRuntime.jsxs(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, Tag === 'a' && href && { href }, onClick && { onClick }, ariaAttributes, dataAttributes, Tag === 'button' && { type: 'button' }, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle })), children, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle, className: "ms-auto" }))] })));
2402
+ return (jsxRuntime.jsxs(Tag, Object.assign({ className: classNames(generateClasses, className), style: style }, Tag === 'a' && href && { href }, onClick && { onClick }, ariaAttributes, dataAttributes, Tag === 'button' && { type: 'button' }, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass !== null && iconStartFamilyClass !== void 0 ? iconStartFamilyClass : familyClass, familyPrefix: iconStartFamilyPrefix !== null && iconStartFamilyPrefix !== void 0 ? iconStartFamilyPrefix : familyPrefix, materialStyle: iconStartMaterialStyle !== null && iconStartMaterialStyle !== void 0 ? iconStartMaterialStyle : materialStyle })), children, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle, className: "ms-auto" }))] })));
2233
2403
  }
2234
2404
 
2235
2405
  function DListGroup$1({ as = 'ul', numbered, flush, horizontal, children, className, style, dataAttributes, }) {
@@ -2256,10 +2426,11 @@ var DListGroup = Object.assign(DListGroup$1, {
2256
2426
  Item: DListGroupItem,
2257
2427
  });
2258
2428
 
2259
- function DModalHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, materialStyle = false, }) {
2260
- const { iconMap: { xLg, }, } = useDContext();
2429
+ function DModalHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, iconMaterialStyle, materialStyle: materialStyleProp, }) {
2430
+ var _a;
2431
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { xLg, }, } = useDContext();
2261
2432
  const icon = React.useMemo(() => iconProp || xLg, [iconProp, xLg]);
2262
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: classNames('modal-header', className), style: style, children: [jsxRuntime.jsx("div", { children: children }), showCloseButton && (jsxRuntime.jsx("button", { type: "button", className: "d-close align-self-center", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: materialStyle }) }))] }), jsxRuntime.jsx("div", { className: "d-modal-separator" })] }));
2433
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: classNames('modal-header', className), style: style, children: [jsxRuntime.jsx("div", { children: children }), showCloseButton && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: (_a = iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyleProp) !== null && _a !== void 0 ? _a : materialStyle }) }))] }), jsxRuntime.jsx("div", { className: "d-modal-separator" })] }));
2263
2434
  }
2264
2435
 
2265
2436
  function DModalBody({ children, className, style, }) {
@@ -2300,10 +2471,11 @@ var DModal = Object.assign(DModal$1, {
2300
2471
  Footer: DModalFooter,
2301
2472
  });
2302
2473
 
2303
- function DOffcanvasHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, materialStyle = false, }) {
2304
- const { iconMap: { xLg, }, } = useDContext();
2474
+ function DOffcanvasHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, iconMaterialStyle, materialStyle: materialStyleProp, }) {
2475
+ var _a;
2476
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { xLg, }, } = useDContext();
2305
2477
  const icon = React.useMemo(() => iconProp || xLg, [iconProp, xLg]);
2306
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: classNames('offcanvas-header', className), style: style, children: [jsxRuntime.jsx("div", { children: children }), showCloseButton && (jsxRuntime.jsx("button", { type: "button", className: "d-close align-self-center", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: materialStyle }) }))] }), jsxRuntime.jsx("div", { className: "d-offcanvas-separator" })] }));
2478
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: classNames('offcanvas-header', className), style: style, children: [jsxRuntime.jsx("div", { children: children }), showCloseButton && (jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass !== null && iconFamilyClass !== void 0 ? iconFamilyClass : familyClass, familyPrefix: iconFamilyPrefix !== null && iconFamilyPrefix !== void 0 ? iconFamilyPrefix : familyPrefix, materialStyle: (_a = iconMaterialStyle !== null && iconMaterialStyle !== void 0 ? iconMaterialStyle : materialStyleProp) !== null && _a !== void 0 ? _a : materialStyle }) }))] }), jsxRuntime.jsx("div", { className: "d-offcanvas-separator" })] }));
2307
2479
  }
2308
2480
 
2309
2481
  function DOffcanvasBody({ children, className, style, }) {
@@ -2430,7 +2602,7 @@ function DPopover({ children, renderComponent, open, setOpen, adjustContentToRen
2430
2602
  }
2431
2603
  const reference = React.cloneElement(triggerElement, Object.assign({ ref: refs.setReference }, getReferenceProps(triggerElement.props)));
2432
2604
  return (jsxRuntime.jsxs("div", Object.assign({ className: classNames('d-popover', className), style: generateStyleVariables }, dataAttributes, { children: [reference, isOpen && (jsxRuntime.jsx(react.FloatingFocusManager, { context: context, modal: false, children: jsxRuntime.jsx("div", Object.assign({ className: classNames('d-popover-content', {
2433
- 'w-100': adjustContentToRender,
2605
+ 'd-popover-content-fill': adjustContentToRender,
2434
2606
  }), ref: refs.setFloating, style: floatingStyles, "aria-labelledby": headingId }, getFloatingProps(), { children: children })) }))] })));
2435
2607
  }
2436
2608
 
@@ -2444,8 +2616,8 @@ function DProgress({ className, style, currentValue, minValue = 0, maxValue = 10
2444
2616
  return (jsxRuntime.jsx("div", Object.assign({ className: classNames('progress', className), style: Object.assign({ height }, style) }, dataAttributes, { children: jsxRuntime.jsx("div", { className: classNames(generateClasses), role: "progressbar", "aria-label": "Progress bar", style: { width: formatProgress }, "aria-valuenow": currentValue, "aria-valuemin": minValue, "aria-valuemax": maxValue, children: !hideCurrentValue && formatProgress }) })));
2445
2617
  }
2446
2618
 
2447
- function DStepper$2({ options, currentStep, iconSuccess: iconSuccessProp, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle = false, vertical = false, completed, alignStart = false, className, style, }) {
2448
- const { iconMap: { check, }, } = useDContext();
2619
+ function DStepper$2({ options, currentStep, iconSuccess: iconSuccessProp, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle, vertical = false, completed, alignStart = false, className, style, }) {
2620
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconMap: { check, }, } = useDContext();
2449
2621
  const icon = React.useMemo(() => iconSuccessProp || check, [check, iconSuccessProp]);
2450
2622
  if (currentStep < 1 || currentStep > options.length) {
2451
2623
  throw new Error('Current step should be in the range from 1 to options length');
@@ -2460,7 +2632,7 @@ function DStepper$2({ options, currentStep, iconSuccess: iconSuccessProp, iconSu
2460
2632
  }), children: [jsxRuntime.jsx("div", { className: "d-step-value", children: jsxRuntime.jsxs("div", { className: classNames({
2461
2633
  'd-step-icon-container': true,
2462
2634
  'd-step-check': value < currentStep || completed,
2463
- }), children: [((value < currentStep) || completed) && (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconSuccessFamilyClass, familyPrefix: iconSuccessFamilyPrefix, materialStyle: iconSuccessMaterialStyle, className: "d-step-icon" })), value] }) }), jsxRuntime.jsxs("div", { className: "d-step-text-container", children: [jsxRuntime.jsx("div", { className: "d-step-label", children: label }), description && (jsxRuntime.jsx("div", { className: "d-step-description", children: description }))] })] }, value))) }));
2635
+ }), children: [((value < currentStep) || completed) && (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconSuccessFamilyClass !== null && iconSuccessFamilyClass !== void 0 ? iconSuccessFamilyClass : familyClass, familyPrefix: iconSuccessFamilyPrefix !== null && iconSuccessFamilyPrefix !== void 0 ? iconSuccessFamilyPrefix : familyPrefix, materialStyle: iconSuccessMaterialStyle !== null && iconSuccessMaterialStyle !== void 0 ? iconSuccessMaterialStyle : materialStyle, className: "d-step-icon" })), value] }) }), jsxRuntime.jsxs("div", { className: "d-step-text-container", children: [jsxRuntime.jsx("div", { className: "d-step-label", children: label }), description && (jsxRuntime.jsx("div", { className: "d-step-description", children: description }))] })] }, value))) }));
2464
2636
  }
2465
2637
 
2466
2638
  function DStepper$1({ options, currentStep, className, style, }) {
@@ -2493,14 +2665,21 @@ function DStepper$1({ options, currentStep, className, style, }) {
2493
2665
  return (jsxRuntime.jsxs("div", { className: classNames('d-stepper', className), style: style, children: [jsxRuntime.jsx("div", { className: "d-step-bar", style: { background: progressStyle }, children: jsxRuntime.jsx("p", { className: "d-step-number", children: `${currentStep}/${options.length}` }) }), jsxRuntime.jsx("div", { className: "d-step-info", children: Object.keys(currentOption).length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "d-step-label", children: currentOption.label }), jsxRuntime.jsx("div", { className: "d-step-description", children: currentOption.description || '' })] })) })] }));
2494
2666
  }
2495
2667
 
2496
- function DStepper({ options, currentStep, iconSuccess, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle = false, vertical = false, breakpoint = 'lg', className, completed = false, style, dataAttributes, }) {
2497
- return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx("div", { className: `d-block d-${breakpoint}-none`, children: jsxRuntime.jsx(DStepper$1, { options: options, currentStep: currentStep }) }), jsxRuntime.jsx("div", { className: `d-none d-${breakpoint}-block`, children: jsxRuntime.jsx(DStepper$2, { options: options, currentStep: currentStep, vertical: vertical, iconSuccess: iconSuccess, iconSuccessFamilyClass: iconSuccessFamilyClass, iconSuccessFamilyPrefix: iconSuccessFamilyPrefix, iconSuccessMaterialStyle: iconSuccessMaterialStyle, completed: completed }) })] })));
2668
+ function DStepper({ options, currentStep, iconSuccess, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle, vertical = false, breakpoint = 'lg', className, completed = false, style, dataAttributes, }) {
2669
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
2670
+ return (jsxRuntime.jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsxRuntime.jsx("div", { className: classNames('d-stepper-mobile', `d-block d-${breakpoint}-none`), children: jsxRuntime.jsx(DStepper$1, { options: options, currentStep: currentStep }) }), jsxRuntime.jsx("div", { className: classNames('d-stepper-desktop', `d-none d-${breakpoint}-block`), children: jsxRuntime.jsx(DStepper$2, { options: options, currentStep: currentStep, vertical: vertical, iconSuccess: iconSuccess, iconSuccessFamilyClass: iconSuccessFamilyClass !== null && iconSuccessFamilyClass !== void 0 ? iconSuccessFamilyClass : familyClass, iconSuccessFamilyPrefix: iconSuccessFamilyPrefix !== null && iconSuccessFamilyPrefix !== void 0 ? iconSuccessFamilyPrefix : familyPrefix, iconSuccessMaterialStyle: iconSuccessMaterialStyle !== null && iconSuccessMaterialStyle !== void 0 ? iconSuccessMaterialStyle : materialStyle, completed: completed }) })] })));
2498
2671
  }
2499
2672
 
2500
2673
  const ARROW_WIDTH = 8;
2501
2674
  const ARROW_HEIGHT = 4;
2502
- const GAP = 2;
2503
- function DTooltip({ className, childrenClassName, style, offSet = ARROW_HEIGHT + GAP, padding, withFocus = false, withClick = false, withHover = true, open = false, placement = 'top', size, Component, children, }) {
2675
+ const GAP$1 = 2;
2676
+ /**
2677
+ * Tooltip is an abstraction component that wraps any element (`Component`) and displays
2678
+ * floating contextual content (`children`) positioned relative to it using Floating UI.
2679
+ * Use it to add short, non-critical hints or descriptions triggered by hover, focus, or
2680
+ * click, without altering the surrounding layout.
2681
+ */
2682
+ function DTooltip({ className, childrenClassName, style, offSet = ARROW_HEIGHT + GAP$1, padding, withFocus = false, withClick = false, withHover = true, open = false, placement = 'top', size, Component, children, }) {
2504
2683
  const [isOpen, setIsOpen] = React.useState(open);
2505
2684
  const arrowRef = React.useRef(null);
2506
2685
  const { refs, context, floatingStyles, } = react.useFloating({
@@ -2569,8 +2748,16 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2569
2748
  React.useEffect(() => {
2570
2749
  setSelected(defaultSelected);
2571
2750
  }, [defaultSelected]);
2572
- const generateClasses = React.useMemo(() => (Object.assign({ nav: true, 'flex-column align-items-center': vertical && variant !== 'tabs', [`nav-${variant}`]: true }, className && { [className]: true })), [vertical, variant, className]);
2751
+ const generateClasses = React.useMemo(() => (Object.assign({ nav: true, 'd-tabs-nav-vertical': vertical && variant !== 'tabs', [`nav-${variant}`]: true }, className && { [className]: true })), [vertical, variant, className]);
2573
2752
  const tabRefs = React.useRef([]);
2753
+ // Always holds the latest `options` without needing to be a dependency:
2754
+ // `options` is commonly passed as an inline array literal (e.g.
2755
+ // `options={[{ label: 'SMS', tab: 'sms' }, ...]}`), so it's a new array
2756
+ // reference on every parent render even when its content hasn't changed.
2757
+ // Reading it from this ref (updated synchronously on every render) lets
2758
+ // the focus effect below react only to `selected` changing.
2759
+ const optionsRef = React.useRef(options);
2760
+ optionsRef.current = options;
2574
2761
  React.useEffect(() => {
2575
2762
  tabRefs.current = options.map((_, i) => tabRefs.current[i] || React.createRef());
2576
2763
  }, [options]);
@@ -2585,20 +2772,28 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2585
2772
  setSelected(firstEnabled.tab);
2586
2773
  }
2587
2774
  }, [options, selected]);
2588
- // Declarative focus management
2589
- const focusTab = (idx) => {
2775
+ // Declarative focus management. Wrapped in `useCallback` with an empty
2776
+ // dependency array since it only reads from the `tabRefs` ref, so its
2777
+ // identity stays stable across renders and it can safely be used as an
2778
+ // effect dependency below.
2779
+ const focusTab = React.useCallback((idx) => {
2590
2780
  var _a;
2591
2781
  if ((_a = tabRefs.current[idx]) === null || _a === void 0 ? void 0 : _a.current) {
2592
2782
  tabRefs.current[idx].current.focus();
2593
2783
  }
2594
- };
2595
- // Focus selected tab when selected changes
2784
+ }, []);
2785
+ // Focus selected tab when selected changes.
2786
+ // Reads `options` from `optionsRef` (see comment above) instead of
2787
+ // depending on `options` directly, so a parent re-render that merely
2788
+ // creates a new `options` reference with identical content doesn't call
2789
+ // `focusTab` again and steal focus away from unrelated elements on the
2790
+ // page (e.g. an OTP input).
2596
2791
  React.useEffect(() => {
2597
- const idx = options.findIndex((opt) => opt.tab === selected && !opt.disabled);
2792
+ const idx = optionsRef.current.findIndex((opt) => opt.tab === selected && !opt.disabled);
2598
2793
  if (idx !== -1) {
2599
2794
  focusTab(idx);
2600
2795
  }
2601
- }, [selected, options]);
2796
+ }, [selected, focusTab]);
2602
2797
  const handleKeyDown = React.useCallback((idx, e) => {
2603
2798
  const count = options.length;
2604
2799
  if (count === 0)
@@ -2627,7 +2822,7 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2627
2822
  }
2628
2823
  }
2629
2824
  }
2630
- }, [options, vertical]);
2825
+ }, [options, vertical, focusTab]);
2631
2826
  let tablistProps = {};
2632
2827
  if (ariaLabelledBy) {
2633
2828
  tablistProps = { 'aria-labelledby': ariaLabelledBy };
@@ -2640,12 +2835,12 @@ function DTabs$1({ children, defaultSelected, onChange, options, className, clas
2640
2835
  isSelected,
2641
2836
  }), [isSelected]);
2642
2837
  return (jsxRuntime.jsx(TabContext.Provider, { value: value, children: jsxRuntime.jsxs("div", Object.assign({ className: classNames({
2643
- 'd-flex w-100': true,
2644
- 'flex-column': !vertical || variant === 'tabs',
2838
+ 'd-tabs': true,
2839
+ 'd-tabs-column': !vertical || variant === 'tabs',
2645
2840
  }), style: style }, dataAttributes, { children: [jsxRuntime.jsx("ul", Object.assign({ className: classNames(generateClasses), role: "tablist", "aria-orientation": vertical ? 'vertical' : undefined }, tablistProps, { children: options.map((option, idx) => {
2646
2841
  const isTabSelected = !!option.tab && option.tab === selected;
2647
2842
  return (jsxRuntime.jsx("li", { role: "presentation", className: "nav-item", children: jsxRuntime.jsx("button", { ref: tabRefs.current[idx], id: `${option.tab}Tab`, className: classNames('nav-link', { active: isTabSelected }, classNameTab), type: "button", role: "tab", "aria-controls": `${option.tab}Pane`, "aria-selected": isTabSelected, tabIndex: isTabSelected ? 0 : -1, disabled: option.disabled, onClick: () => onSelect(option), onKeyDown: (e) => handleKeyDown(idx, e), children: option.label }) }, option.tab));
2648
- }) })), jsxRuntime.jsx("div", { className: "tab-content w-100", children: children })] })) }));
2843
+ }) })), jsxRuntime.jsx("div", { className: "d-tabs-content tab-content", children: children })] })) }));
2649
2844
  }
2650
2845
  var DTabs = Object.assign(DTabs$1, {
2651
2846
  Tab: DTabContent,
@@ -2707,11 +2902,11 @@ function useDToast() {
2707
2902
  if (!description) {
2708
2903
  return (jsxRuntime.jsx(DToast, { className: classNames({
2709
2904
  [`toast-${color}`]: !!color,
2710
- }, 'show'), children: jsxRuntime.jsxs(DToast.Body, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), jsxRuntime.jsx("button", { type: "button", className: "d-close align-self-center", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }) }));
2905
+ }, 'show'), children: jsxRuntime.jsxs(DToast.Body, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }) }));
2711
2906
  }
2712
2907
  return (jsxRuntime.jsxs(DToast, { className: classNames({
2713
2908
  [`toast-${color}`]: !!color,
2714
- }, 'show'), children: [jsxRuntime.jsxs(DToast.Header, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), timestamp && (jsxRuntime.jsx("small", { className: "toast-timestamp", children: timestamp })), jsxRuntime.jsx("button", { type: "button", className: "d-close align-self-center", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }), jsxRuntime.jsx(DToast.Body, { children: jsxRuntime.jsx("span", { children: description }) })] }));
2909
+ }, 'show'), children: [jsxRuntime.jsxs(DToast.Header, { children: [icon && (jsxRuntime.jsx(DIcon, { className: "toast-icon", icon: icon })), jsxRuntime.jsx("p", { className: "toast-title", children: title }), timestamp && (jsxRuntime.jsx("small", { className: "toast-timestamp", children: timestamp })), jsxRuntime.jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => reactHotToast.toast.dismiss(id), children: jsxRuntime.jsx(DIcon, { icon: closeIcon || xLg }) })] }), jsxRuntime.jsx(DToast.Body, { children: jsxRuntime.jsx("span", { children: description }) })] }));
2715
2910
  }, toastProps);
2716
2911
  }, [xLg]);
2717
2912
  return {
@@ -2733,11 +2928,10 @@ async function configureI8n(resources, _a = {}) {
2733
2928
  .then((t) => t);
2734
2929
  }
2735
2930
 
2736
- function getQueryString(value, config = {
2737
- useSearch: true,
2738
- }) {
2739
- const urlParams = new URLSearchParams(config.useSearch ? window.location.search : '');
2740
- return urlParams.get(value) || config.default;
2931
+ function getQueryString(value, config = {}) {
2932
+ const { useSearch = true, default: defaultValue } = config;
2933
+ const urlParams = new URLSearchParams(useSearch ? window.location.search : '');
2934
+ return urlParams.get(value) || defaultValue;
2741
2935
  }
2742
2936
 
2743
2937
  function changeQueryString(values, { useSearch = true, pushState = false, } = {}) {
@@ -2767,11 +2961,132 @@ function validatePhoneNumber(phone) {
2767
2961
  }
2768
2962
  }
2769
2963
 
2964
+ const SAFE_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);
2965
+ /**
2966
+ * Characters the URL parser drops before resolving a URL, so they cannot be used to hide a
2967
+ * scheme-relative prefix or a dangerous scheme (e.g. `/<TAB>/evil.com`, `java<TAB>script:`).
2968
+ */
2969
+ const IGNORED_CHARS$1 = /[\t\n\r]/g;
2970
+ /**
2971
+ * Two leading separators in any combination. Browsers treat a leading `\` exactly like a
2972
+ * leading `/`, so `//`, `/\`, `\/` and `\\` all resolve to a scheme-relative URL.
2973
+ */
2974
+ const SCHEME_RELATIVE = /^[/\\]{2}/;
2975
+ const LEADING_SEPARATOR = /^[/\\]/;
2976
+ /**
2977
+ * Validates a `href` value and returns it only if it is safe to render as a link,
2978
+ * preventing XSS through unsafe URL schemes and scheme-relative URLs.
2979
+ *
2980
+ * Before validating, the value is trimmed and every tab, line feed and carriage return is
2981
+ * removed, because the URL parser ignores those characters too and they would otherwise
2982
+ * hide an unsafe value from the checks below.
2983
+ *
2984
+ * A `href` is considered safe when it is either:
2985
+ * - An absolute path starting with a single separator (e.g. `/accounts/42`). A leading `\`
2986
+ * is normalized to `/` in the returned value, since that is how the browser resolves it.
2987
+ * - A URL using one of the allowed schemes: `http:`, `https:`, `mailto:`, `tel:`.
2988
+ *
2989
+ * A `href` is rejected (returns `undefined`) when it is:
2990
+ * - Empty or made only of whitespace/ignored characters (`''`, `' '`).
2991
+ * - Scheme-relative (starts with two separators, e.g. `//evil.com`, `/\evil.com`,
2992
+ * `/<TAB>/evil.com`), since the browser would resolve it using the current protocol and it
2993
+ * can be used to redirect to an attacker-controlled host.
2994
+ * - Using a disallowed/dangerous scheme, such as `javascript:` or `data:`
2995
+ * (e.g. `javascript:alert(1)`, `data:text/html,<script>...</script>`).
2996
+ * - Not parseable as an absolute URL and not an absolute path, such as bare fragments
2997
+ * (`#anchor`) or scheme-less relative paths (`relative/path`). These cannot be validated
2998
+ * against `SAFE_SCHEMES`, so they are rejected rather than assumed safe.
2999
+ *
3000
+ * @param href - The raw URL or path to validate, typically coming from user input,
3001
+ * a CMS, or an external API.
3002
+ * @returns The sanitized `href` when safe, `undefined` otherwise.
3003
+ *
3004
+ * @example
3005
+ * sanitizeHref('/accounts/42'); // '/accounts/42'
3006
+ * sanitizeHref('https://example.com'); // 'https://example.com'
3007
+ * sanitizeHref('mailto:test@example.com'); // 'mailto:test@example.com'
3008
+ * sanitizeHref('//evil.com'); // undefined
3009
+ * sanitizeHref('/\\evil.com'); // undefined
3010
+ * sanitizeHref('javascript:alert(1)'); // undefined
3011
+ */
3012
+ function sanitizeHref(href) {
3013
+ const normalized = href.trim().replace(IGNORED_CHARS$1, '');
3014
+ if (!normalized)
3015
+ return undefined;
3016
+ if (SCHEME_RELATIVE.test(normalized))
3017
+ return undefined;
3018
+ if (LEADING_SEPARATOR.test(normalized))
3019
+ return `/${normalized.slice(1)}`;
3020
+ try {
3021
+ const { protocol } = new URL(normalized);
3022
+ return SAFE_SCHEMES.has(protocol) ? normalized : undefined;
3023
+ }
3024
+ catch (_a) {
3025
+ return undefined;
3026
+ }
3027
+ }
3028
+
3029
+ /**
3030
+ * Characters the URL parser drops before resolving a URL, so they cannot be used to hide a
3031
+ * scheme-relative prefix (e.g. `/<TAB>/evil.com`).
3032
+ */
3033
+ const IGNORED_CHARS = /[\t\n\r]/g;
3034
+ /**
3035
+ * Leading separators. Browsers treat a leading `\` exactly like a leading `/`, so both have
3036
+ * to be stripped to prevent scheme-relative URLs (`//evil.com`, `/\evil.com`, `\\evil.com`).
3037
+ */
3038
+ const LEADING_SEPARATORS = /^[/\\]+/;
3039
+ /**
3040
+ * Builds an internal URL from a `path` and a set of query `params`, normalizing leading
3041
+ * separators to prevent scheme-relative URLs (e.g. `//accounts` and `/\accounts` both become
3042
+ * `/accounts`).
3043
+ *
3044
+ * The `path` is trimmed and every tab, line feed and carriage return is removed before
3045
+ * building the result, because the URL parser ignores those characters too and they would
3046
+ * otherwise hide a scheme-relative prefix (`/<TAB>/evil.com` resolves to `//evil.com`).
3047
+ *
3048
+ * Handles the special case where `path` is the literal string `'#'`, which happens when
3049
+ * a Liquid template variable used as a link target is left unconfigured; in that case the
3050
+ * params are ignored and `'#'` is returned as-is instead of building a broken URL.
3051
+ *
3052
+ * @param path - The path to build the URL from. Leading separators are stripped before
3053
+ * building the result (`/accounts`, `//accounts` and `\accounts` are all treated as `accounts`).
3054
+ * @param params - Key/value pairs serialized as the query string.
3055
+ * @returns The resulting URL:
3056
+ * - `'#'` when `path` normalizes to `'#'` (`'#'`, `'/#'`), ignoring the params.
3057
+ * - `'/'` when `path` is empty and there are no params.
3058
+ * - `'/?<query>'` when `path` is empty and there are params.
3059
+ * - `'<path>'` or `'<path>?<query>'` when `path` starts with `#`, since a fragment must not be
3060
+ * prefixed with `/` (it would navigate away from the current document).
3061
+ * - `'/<path>'` or `'/<path>?<query>'` otherwise.
3062
+ *
3063
+ * @example
3064
+ * buildUrl('accounts', { account_id: '42' }); // '/accounts?account_id=42'
3065
+ * buildUrl('//accounts', {}); // '/accounts'
3066
+ * buildUrl('#', { account_id: '42' }); // '#'
3067
+ * buildUrl('#accounts', { account_id: '42' }); // '#accounts?account_id=42'
3068
+ * buildUrl('', { tab: 'movimientos' }); // '/?tab=movimientos'
3069
+ */
3070
+ function buildUrl(path, params) {
3071
+ const normalizedPath = path
3072
+ .trim()
3073
+ .replace(IGNORED_CHARS, '')
3074
+ .replace(LEADING_SEPARATORS, '');
3075
+ if (normalizedPath === '#')
3076
+ return '#';
3077
+ const query = new URLSearchParams(Object.entries(params).map(([key, value]) => [key, String(value)])).toString();
3078
+ if (normalizedPath === '')
3079
+ return query ? `/?${query}` : '/';
3080
+ const prefix = normalizedPath.startsWith('#') ? '' : '/';
3081
+ return `${prefix}${normalizedPath}${query ? `?${query}` : ''}`;
3082
+ }
3083
+
2770
3084
  function DInputPhone(_a, ref) {
2771
3085
  var { id: idProp, style, className, label = '', disabled = false, loading = false, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle, iconEnd, iconEndDisabled, iconEndFamilyClass, iconEndFamilyPrefix, iconEndAriaLabel, iconEndTabIndex, iconEndMaterialStyle, hint, size, invalid = false, valid = false, floatingLabel = false, inputEnd, value, placeholder = '', dataAttributes, onChange, onIconEndClick, countrySelectorProps, filteredCountries, defaultCountry = 'cl' } = _a, inputProps = tslib.__rest(_a, ["id", "style", "className", "label", "disabled", "loading", "iconFamilyClass", "iconFamilyPrefix", "iconMaterialStyle", "iconEnd", "iconEndDisabled", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndAriaLabel", "iconEndTabIndex", "iconEndMaterialStyle", "hint", "size", "invalid", "valid", "floatingLabel", "inputEnd", "value", "placeholder", "dataAttributes", "onChange", "onIconEndClick", "countrySelectorProps", "filteredCountries", "defaultCountry"]);
2772
3086
  const innerRef = useProvidedRefOrCreate(ref);
2773
3087
  const innerId = React.useId();
2774
3088
  const id = React.useMemo(() => idProp || innerId, [idProp, innerId]);
3089
+ const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
2775
3090
  const handleOnIconEndClick = React.useCallback(() => {
2776
3091
  onIconEndClick === null || onIconEndClick === void 0 ? void 0 : onIconEndClick(value);
2777
3092
  }, [onIconEndClick, value]);
@@ -2845,7 +3160,7 @@ function DInputPhone(_a, ref) {
2845
3160
  [`input-group-${size}`]: !!size,
2846
3161
  'input-group': true,
2847
3162
  'has-validation': invalid || valid,
2848
- }), children: [jsxRuntime.jsx(reactInternationalPhone.CountrySelector, Object.assign({}, countrySelectorProps, { selectedCountry: country.iso2, onSelect: ({ iso2 }) => setCountry(iso2), countries: countries, disabled: disabled || loading, className: classNames('input-group-text', countrySelectorProps === null || countrySelectorProps === void 0 ? void 0 : countrySelectorProps.className) })), dynamicComponent, (iconEnd && !loading) && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel, tabIndex: onIconEndClick ? iconEndTabIndex : -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }) })), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
3163
+ }), children: [jsxRuntime.jsx(reactInternationalPhone.CountrySelector, Object.assign({}, countrySelectorProps, { selectedCountry: country.iso2, onSelect: ({ iso2 }) => setCountry(iso2), countries: countries, disabled: disabled || loading, className: classNames('input-group-text', countrySelectorProps === null || countrySelectorProps === void 0 ? void 0 : countrySelectorProps.className) })), dynamicComponent, (iconEnd && !loading) && (jsxRuntime.jsx("button", { type: "button", className: "input-group-text", id: `${id}End`, onClick: handleOnIconEndClick, disabled: disabled || loading || iconEndDisabled, "aria-label": iconEndAriaLabel, tabIndex: onIconEndClick ? iconEndTabIndex : -1, children: jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass !== null && iconEndFamilyClass !== void 0 ? iconEndFamilyClass : familyClass, familyPrefix: iconEndFamilyPrefix !== null && iconEndFamilyPrefix !== void 0 ? iconEndFamilyPrefix : familyPrefix, materialStyle: iconEndMaterialStyle !== null && iconEndMaterialStyle !== void 0 ? iconEndMaterialStyle : materialStyle }) })), loading && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}Loading`, children: jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }) })), !!inputEnd && (jsxRuntime.jsx("div", { className: "input-group-text", id: `${id}InputEnd`, children: inputEnd }))] }), hint && (jsxRuntime.jsx("div", { className: "form-text", id: `${id}Hint`, children: hint }))] })));
2849
3164
  }
2850
3165
  const ForwardedDInputPhone = React.forwardRef(DInputPhone);
2851
3166
  ForwardedDInputPhone.displayName = 'DInputPhone';
@@ -2860,89 +3175,228 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
2860
3175
  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 })] })] }));
2861
3176
  }
2862
3177
 
3178
+ // Gap between the toggle and the menu.
3179
+ const GAP = 4;
3180
+ // Minimum distance the menu should keep from the viewport edges.
3181
+ const VIEWPORT_PADDING = 8;
3182
+ // Minimum usable height left for the menu before we consider a side unusable.
3183
+ const MIN_USABLE_SPACE = 120;
2863
3184
  const getItemClass = (action) => classNames({
2864
3185
  'dropdown-item d-flex align-items-center': true,
2865
3186
  [`dropdown-item-${action.color}`]: !!action.color,
2866
3187
  disabled: action.disabled,
2867
3188
  });
2868
- function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal = false, placement = 'auto', }) {
3189
+ const clamp = (value, min, max) => {
3190
+ if (max < min)
3191
+ return min;
3192
+ return Math.min(Math.max(value, min), max);
3193
+ };
3194
+ /**
3195
+ * `position: fixed` is normally relative to the viewport, but any ancestor
3196
+ * with a `transform`, `perspective`, `filter`, `will-change` (of those) or a
3197
+ * non-trivial `contain` establishes a new containing block, making `fixed`
3198
+ * behave like `absolute` relative to that ancestor instead (e.g. Storybook's
3199
+ * docs canvas wrapper, animated cards, sticky containers, etc.). We detect
3200
+ * that ancestor so the computed coordinates can be adjusted accordingly,
3201
+ * keeping the menu correctly anchored to the toggle in every context.
3202
+ */
3203
+ const getFixedPositioningOffset = (fromEl) => {
3204
+ var _a;
3205
+ let node = (_a = fromEl === null || fromEl === void 0 ? void 0 : fromEl.parentElement) !== null && _a !== void 0 ? _a : null;
3206
+ while (node && node !== document.body) {
3207
+ const cs = window.getComputedStyle(node);
3208
+ // Treat empty/undefined values as "unset" (jsdom returns "" rather than
3209
+ // the CSS initial value like "none" for some of these properties).
3210
+ const isSet = (value) => !!value && value !== 'none';
3211
+ const establishesContainingBlock = isSet(cs.transform)
3212
+ || isSet(cs.perspective)
3213
+ || isSet(cs.filter)
3214
+ || isSet(cs.backdropFilter)
3215
+ || ['transform', 'perspective', 'filter'].some((prop) => { var _a; return (_a = cs.willChange) === null || _a === void 0 ? void 0 : _a.includes(prop); })
3216
+ || cs.contain.split(' ').some((value) => ['layout', 'paint', 'strict', 'content'].includes(value));
3217
+ if (establishesContainingBlock) {
3218
+ const rect = node.getBoundingClientRect();
3219
+ return { top: rect.top, left: rect.left };
3220
+ }
3221
+ node = node.parentElement;
3222
+ }
3223
+ return { top: 0, left: 0 };
3224
+ };
3225
+ /**
3226
+ * Resolves where the menu should render (flip) and where it should sit along
3227
+ * the cross axis (shift), so it always stays fully inside the viewport
3228
+ * regardless of where the toggle is placed or how the viewport is resized.
3229
+ * This mimics floating-ui's `flip` + `shift` middlewares without adding the
3230
+ * dependency, keeping the existing Bootstrap markup/classes.
3231
+ */
3232
+ const computeCoords = (toggleRect, menuSize, placement, alignment) => {
3233
+ const viewportWidth = window.innerWidth;
3234
+ const viewportHeight = window.innerHeight;
3235
+ const spaceBelow = viewportHeight - toggleRect.bottom;
3236
+ const spaceAbove = toggleRect.top;
3237
+ const spaceLeft = toggleRect.left;
3238
+ const spaceRight = viewportWidth - toggleRect.right;
3239
+ let side = placement === 'auto'
3240
+ ? 'down'
3241
+ : placement;
3242
+ if (placement === 'auto') {
3243
+ side = spaceBelow >= menuSize.height + GAP || spaceBelow >= spaceAbove ? 'down' : 'up';
3244
+ }
3245
+ // Flip within the current axis if the chosen side can't fit the menu but
3246
+ // the opposite side (same axis) can.
3247
+ if (side === 'down' && spaceBelow < menuSize.height + GAP && spaceAbove > spaceBelow) {
3248
+ side = 'up';
3249
+ }
3250
+ else if (side === 'up' && spaceAbove < menuSize.height + GAP && spaceBelow > spaceAbove) {
3251
+ side = 'down';
3252
+ }
3253
+ else if (side === 'start' && spaceLeft < menuSize.width + GAP && spaceRight > spaceLeft) {
3254
+ side = 'end';
3255
+ }
3256
+ else if (side === 'end' && spaceRight < menuSize.width + GAP && spaceLeft > spaceRight) {
3257
+ side = 'start';
3258
+ }
3259
+ // Cross-axis fallback: e.g. a horizontally-centered toggle on a narrow
3260
+ // viewport can't open sideways (neither `start` nor `end` fit), but there
3261
+ // is plenty of room above/below — switch the whole axis in that case
3262
+ // (and vice versa for `down`/`up` squeezed against a short viewport).
3263
+ const bestVerticalSpace = Math.max(spaceBelow, spaceAbove);
3264
+ const bestHorizontalSpace = Math.max(spaceLeft, spaceRight);
3265
+ if (side === 'down' || side === 'up') {
3266
+ if (bestVerticalSpace < MIN_USABLE_SPACE && bestHorizontalSpace > bestVerticalSpace) {
3267
+ side = spaceRight >= spaceLeft ? 'end' : 'start';
3268
+ }
3269
+ }
3270
+ else if (bestHorizontalSpace < menuSize.width + GAP
3271
+ && bestVerticalSpace > bestHorizontalSpace) {
3272
+ side = spaceBelow >= spaceAbove ? 'down' : 'up';
3273
+ }
3274
+ if (side === 'down' || side === 'up') {
3275
+ const top = side === 'down'
3276
+ ? toggleRect.bottom + GAP
3277
+ : toggleRect.top - menuSize.height - GAP;
3278
+ // Align horizontally per the requested `alignment` (start/end/center), then
3279
+ // fall back to the opposite edge if the preferred alignment would overflow
3280
+ // the viewport, and clamp as a final safety net so it never overflows.
3281
+ let left;
3282
+ if (alignment === 'end') {
3283
+ left = toggleRect.right - menuSize.width;
3284
+ }
3285
+ else if (alignment === 'center') {
3286
+ left = toggleRect.left + (toggleRect.width / 2) - (menuSize.width / 2);
3287
+ }
3288
+ else {
3289
+ left = toggleRect.left;
3290
+ }
3291
+ if (left + menuSize.width > viewportWidth - VIEWPORT_PADDING && alignment !== 'end') {
3292
+ const altLeft = toggleRect.right - menuSize.width;
3293
+ if (altLeft >= VIEWPORT_PADDING)
3294
+ left = altLeft;
3295
+ }
3296
+ else if (left < VIEWPORT_PADDING && alignment !== 'start') {
3297
+ const altLeft = toggleRect.left;
3298
+ if (altLeft + menuSize.width <= viewportWidth - VIEWPORT_PADDING)
3299
+ left = altLeft;
3300
+ }
3301
+ left = clamp(left, VIEWPORT_PADDING, Math.max(viewportWidth - menuSize.width - VIEWPORT_PADDING, VIEWPORT_PADDING));
3302
+ const availableHeight = side === 'down' ? spaceBelow : spaceAbove;
3303
+ // Cap strictly to the space actually available on the resolved side —
3304
+ // MIN_USABLE_SPACE is only meant to influence the flip/shift decision
3305
+ // above, not to force a taller box than what physically fits and
3306
+ // overflow the viewport.
3307
+ const maxUsableHeight = Math.max(availableHeight - GAP - VIEWPORT_PADDING, 0);
3308
+ return {
3309
+ top,
3310
+ left,
3311
+ minWidth: Math.max(toggleRect.width, 160),
3312
+ maxHeight: `${maxUsableHeight}px`,
3313
+ resolvedSide: side,
3314
+ };
3315
+ }
3316
+ // side is 'start' or 'end'
3317
+ let left = side === 'start'
3318
+ ? toggleRect.left - menuSize.width - GAP
3319
+ : toggleRect.right + GAP;
3320
+ // Safety-net clamp: in rare cases where the toggle is squeezed so tightly
3321
+ // that no side of any axis has enough room, keep the menu inside the
3322
+ // viewport rather than letting it render off-screen.
3323
+ left = clamp(left, VIEWPORT_PADDING, Math.max(viewportWidth - menuSize.width - VIEWPORT_PADDING, VIEWPORT_PADDING));
3324
+ // Shift vertically: align with the toggle's top edge by default, but switch
3325
+ // to the bottom edge (or clamp) if that would overflow the viewport.
3326
+ let { top } = toggleRect;
3327
+ if (top + menuSize.height > viewportHeight - VIEWPORT_PADDING) {
3328
+ top = toggleRect.bottom - menuSize.height;
3329
+ }
3330
+ top = clamp(top, VIEWPORT_PADDING, Math.max(viewportHeight - menuSize.height - VIEWPORT_PADDING, VIEWPORT_PADDING));
3331
+ return {
3332
+ top,
3333
+ left,
3334
+ minWidth: Math.max(toggleRect.width, 160),
3335
+ maxHeight: `${Math.max(viewportHeight - top - VIEWPORT_PADDING, 0)}px`,
3336
+ resolvedSide: side,
3337
+ };
3338
+ };
3339
+ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal = false, placement = 'auto', alignment = 'start', }) {
2869
3340
  const [open, setOpen] = React.useState(false);
2870
- const [position, setPosition] = React.useState('down');
2871
- const [menuCoords, setMenuCoords] = React.useState(null);
2872
- // Used in default (non-portal) mode: wraps the entire dropdown
2873
- const dropdownRef = React.useRef(null);
2874
- // Used in portal mode: ref on the toggle wrapper to read its viewport coords
3341
+ const [coords, setCoords] = React.useState(null);
3342
+ // Initialize from `placement` (falling back to 'down' for 'auto') so the
3343
+ // wrapper's `drop-*` class reflects the requested side even before the
3344
+ // first open/measurement, matching the previous `drop-${placement}` class
3345
+ // behavior (relevant for Bootstrap caret/orientation styling).
3346
+ const [resolvedSide, setResolvedSide] = React.useState(placement === 'auto' ? 'down' : placement);
3347
+ // Wraps the toggle in both modes — used to read its viewport-relative rect.
2875
3348
  const toggleRef = React.useRef(null);
2876
- // Ref on the rendered <ul> — used for outside-click detection in portal mode
3349
+ // Ref on the rendered <ul> — used to measure its size and for outside-click detection.
2877
3350
  const menuRef = React.useRef(null);
2878
3351
  const toggle = () => {
2879
- setMenuCoords(null);
3352
+ setCoords(null);
2880
3353
  setOpen((prev) => !prev);
2881
3354
  };
2882
- const resolvedInlinePosition = placement === 'auto' ? position : placement;
2883
- // Compute fixed coords from the toggle's bounding rect (portal mode only)
2884
- const computePortalCoords = React.useCallback(() => {
2885
- if (!toggleRef.current)
3355
+ // Measures the toggle + menu and (re)computes fixed, pixel-perfect coords
3356
+ // with collision detection, so the menu never overflows the viewport.
3357
+ const updatePosition = React.useCallback(() => {
3358
+ if (!toggleRef.current || !menuRef.current)
3359
+ return;
3360
+ const toggleRect = toggleRef.current.getBoundingClientRect();
3361
+ // If the toggle itself has scrolled completely out of the viewport
3362
+ // (regardless of `placement`), close the menu instead of leaving it
3363
+ // rendered detached from the toggle that opened it.
3364
+ const toggleIsMeasured = toggleRect.width > 0 || toggleRect.height > 0;
3365
+ const toggleIsOutOfView = toggleRect.bottom <= 0
3366
+ || toggleRect.top >= window.innerHeight
3367
+ || toggleRect.right <= 0
3368
+ || toggleRect.left >= window.innerWidth;
3369
+ if (toggleIsMeasured && toggleIsOutOfView) {
3370
+ setOpen(false);
2886
3371
  return;
2887
- const rect = toggleRef.current.getBoundingClientRect();
2888
- const spaceRight = window.innerWidth - rect.right;
2889
- const spaceBottom = window.innerHeight - rect.bottom;
2890
- const spaceTop = rect.top;
2891
- let resolvedPlacement;
2892
- if (placement === 'auto') {
2893
- if (spaceBottom < 150) {
2894
- resolvedPlacement = 'up';
2895
- }
2896
- else if (spaceRight < 150) {
2897
- resolvedPlacement = 'start';
2898
- }
2899
- else {
2900
- resolvedPlacement = 'down';
2901
- }
2902
- }
2903
- else {
2904
- resolvedPlacement = placement;
2905
- }
2906
- const openStart = resolvedPlacement === 'start';
2907
- const openEnd = resolvedPlacement === 'end';
2908
- const openUp = resolvedPlacement === 'up';
2909
- let availableHeight = spaceBottom;
2910
- if (openUp) {
2911
- availableHeight = spaceTop;
2912
- }
2913
- else if (openStart || openEnd) {
2914
- availableHeight = window.innerHeight - rect.top;
2915
- }
2916
- const verticalCoords = openUp
2917
- ? { bottom: window.innerHeight - rect.top + 4 }
2918
- : { top: openStart || openEnd ? rect.top : rect.bottom + 4 };
2919
- let horizontalCoords;
2920
- if (openStart) {
2921
- horizontalCoords = { right: window.innerWidth - rect.left + 4 };
2922
- }
2923
- else if (openEnd) {
2924
- horizontalCoords = { left: rect.right + 4 };
2925
- }
2926
- else {
2927
- horizontalCoords = { left: rect.left };
2928
3372
  }
2929
- setMenuCoords(Object.assign(Object.assign(Object.assign({}, verticalCoords), horizontalCoords), { minWidth: Math.max(rect.width, 160), maxHeight: `${Math.max(availableHeight - 12, 120)}px` }));
2930
- }, [placement]);
2931
- // Outside-click: default mode
2932
- React.useEffect(() => {
2933
- if (asPortal)
2934
- return () => { };
2935
- const handler = (event) => {
2936
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
2937
- setOpen(false);
2938
- }
2939
- };
2940
- document.addEventListener('mousedown', handler);
2941
- return () => document.removeEventListener('mousedown', handler);
2942
- }, [asPortal]);
2943
- // Outside-click: portal mode (must check both toggle and floating menu)
2944
- React.useEffect(() => {
2945
- if (!asPortal || !open)
3373
+ const menuRect = menuRef.current.getBoundingClientRect();
3374
+ const _a = computeCoords(toggleRect, { width: menuRect.width, height: menuRect.height }, placement, alignment), { resolvedSide: side } = _a, menuCoords = tslib.__rest(_a, ["resolvedSide"]);
3375
+ // In portal mode the menu is appended to document.body, whose containing
3376
+ // block for `fixed` is always the viewport (no adjustment needed). In
3377
+ // inline mode the menu stays next to the toggle, so we must look for a
3378
+ // containing-block-establishing ancestor starting from the toggle.
3379
+ const offset = asPortal
3380
+ ? { top: 0, left: 0 }
3381
+ : getFixedPositioningOffset(toggleRef.current);
3382
+ setResolvedSide(side);
3383
+ setCoords(Object.assign(Object.assign({}, menuCoords), { top: menuCoords.top - offset.top, left: menuCoords.left - offset.left }));
3384
+ }, [placement, alignment, asPortal]);
3385
+ // Throttles scroll/resize-triggered updates to at most once per frame,
3386
+ // avoiding repeated forced reflows on busy/high-frequency scroll events.
3387
+ const rafIdRef = React.useRef(null);
3388
+ const scheduleUpdatePosition = React.useCallback(() => {
3389
+ if (rafIdRef.current !== null)
3390
+ return;
3391
+ rafIdRef.current = window.requestAnimationFrame(() => {
3392
+ rafIdRef.current = null;
3393
+ updatePosition();
3394
+ });
3395
+ }, [updatePosition]);
3396
+ // Outside-click detection (checks both the toggle and the floating menu,
3397
+ // since in portal mode the menu isn't a DOM descendant of the toggle).
3398
+ React.useLayoutEffect(() => {
3399
+ if (!open)
2946
3400
  return () => { };
2947
3401
  const handler = (event) => {
2948
3402
  var _a, _b;
@@ -2953,35 +3407,25 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
2953
3407
  };
2954
3408
  document.addEventListener('mousedown', handler);
2955
3409
  return () => document.removeEventListener('mousedown', handler);
2956
- }, [asPortal, open]);
2957
- // Auto-position: default mode
2958
- React.useEffect(() => {
2959
- if (asPortal || !open || !dropdownRef.current || placement !== 'auto')
2960
- return;
2961
- const rect = dropdownRef.current.getBoundingClientRect();
2962
- const spaceBottom = window.innerHeight - rect.bottom;
2963
- const spaceRight = window.innerWidth - rect.right;
2964
- if (spaceBottom < 150)
2965
- setPosition('up');
2966
- else if (spaceRight < 150)
2967
- setPosition('start');
2968
- else
2969
- setPosition('down');
2970
- }, [asPortal, open, placement]);
2971
- // Compute + track coords: portal mode
2972
- // Re-runs whenever the menu opens and keeps coords fresh on scroll/resize
2973
- React.useEffect(() => {
2974
- if (!asPortal || !open)
3410
+ }, [open]);
3411
+ // Computes the initial position synchronously (before paint, to avoid a
3412
+ // flicker) and keeps it fresh while the menu is open and the viewport
3413
+ // scrolls or resizes (throttled to one update per frame, see above).
3414
+ React.useLayoutEffect(() => {
3415
+ if (!open)
2975
3416
  return () => { };
2976
- computePortalCoords();
2977
- const update = () => computePortalCoords();
2978
- window.addEventListener('scroll', update, { passive: true, capture: true });
2979
- window.addEventListener('resize', update, { passive: true });
3417
+ updatePosition();
3418
+ window.addEventListener('scroll', scheduleUpdatePosition, { passive: true, capture: true });
3419
+ window.addEventListener('resize', scheduleUpdatePosition, { passive: true });
2980
3420
  return () => {
2981
- window.removeEventListener('scroll', update, { capture: true });
2982
- window.removeEventListener('resize', update);
3421
+ window.removeEventListener('scroll', scheduleUpdatePosition, { capture: true });
3422
+ window.removeEventListener('resize', scheduleUpdatePosition);
3423
+ if (rafIdRef.current !== null) {
3424
+ window.cancelAnimationFrame(rafIdRef.current);
3425
+ rafIdRef.current = null;
3426
+ }
2983
3427
  };
2984
- }, [asPortal, open, computePortalCoords]);
3428
+ }, [open, updatePosition, scheduleUpdatePosition]);
2985
3429
  let ToggleElement;
2986
3430
  if (dropdownToggle) {
2987
3431
  ToggleElement = typeof dropdownToggle === 'function'
@@ -2991,57 +3435,30 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
2991
3435
  else {
2992
3436
  ToggleElement = (jsxRuntime.jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle, icon: "MoreVertical" }));
2993
3437
  }
2994
- const portalMenuStyle = asPortal && menuCoords
3438
+ // Until the first measurement is available, keep the menu off-screen but
3439
+ // measurable (visibility: hidden preserves layout, unlike display: none).
3440
+ const menuStyle = coords
2995
3441
  ? {
2996
3442
  position: 'fixed',
2997
- top: menuCoords.top,
2998
- bottom: menuCoords.bottom,
2999
- left: menuCoords.left,
3000
- right: menuCoords.right,
3001
- minWidth: menuCoords.minWidth,
3002
- maxHeight: menuCoords.maxHeight,
3443
+ top: coords.top,
3444
+ left: coords.left,
3445
+ minWidth: coords.minWidth,
3446
+ maxHeight: coords.maxHeight,
3003
3447
  overflowY: 'auto',
3004
3448
  zIndex: 1050,
3005
3449
  transform: 'none',
3450
+ visibility: 'visible',
3006
3451
  }
3007
- : undefined;
3008
- let inlineTop = '100%';
3009
- if (resolvedInlinePosition === 'up') {
3010
- inlineTop = 'auto';
3011
- }
3012
- else if (resolvedInlinePosition === 'start' || resolvedInlinePosition === 'end') {
3013
- inlineTop = 0;
3014
- }
3015
- let inlineLeft = 0;
3016
- if (resolvedInlinePosition === 'start') {
3017
- inlineLeft = 'auto';
3018
- }
3019
- else if (resolvedInlinePosition === 'end') {
3020
- inlineLeft = '100%';
3021
- }
3022
- let inlineRight = 'auto';
3023
- if (resolvedInlinePosition === 'start') {
3024
- inlineRight = '100%';
3025
- }
3026
- let inlineTransform = 'translateY(4px)';
3027
- if (resolvedInlinePosition === 'up') {
3028
- inlineTransform = 'translateY(-4px)';
3029
- }
3030
- else if (resolvedInlinePosition === 'start') {
3031
- inlineTransform = 'translateX(-4px)';
3032
- }
3033
- else if (resolvedInlinePosition === 'end') {
3034
- inlineTransform = 'translateX(4px)';
3035
- }
3036
- const inlineMenuStyle = {
3037
- position: 'absolute',
3038
- top: inlineTop,
3039
- bottom: resolvedInlinePosition === 'up' ? '100%' : 'auto',
3040
- left: inlineLeft,
3041
- right: inlineRight,
3042
- transform: inlineTransform,
3043
- };
3044
- 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) => {
3452
+ : {
3453
+ position: 'fixed',
3454
+ top: 0,
3455
+ left: 0,
3456
+ visibility: 'hidden',
3457
+ pointerEvents: 'none',
3458
+ zIndex: 1050,
3459
+ transform: 'none',
3460
+ };
3461
+ const menuItems = (jsxRuntime.jsx("ul", { ref: menuRef, role: "menu", className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), style: menuStyle, children: actions.map((action, index) => {
3045
3462
  if (action.isDivider) {
3046
3463
  return jsxRuntime.jsx("hr", { className: "dropdown-divider" }, index);
3047
3464
  }
@@ -3060,10 +3477,7 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
3060
3477
  }
3061
3478
  }, disabled: action.disabled, children: [action.icon && jsxRuntime.jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) }, index));
3062
3479
  }) }));
3063
- if (asPortal) {
3064
- return (jsxRuntime.jsxs("div", { ref: toggleRef, className: classNames('dropdown', className), children: [ToggleElement, open && menuCoords && reactDom.createPortal(menuItems, document.body)] }));
3065
- }
3066
- return (jsxRuntime.jsxs("div", { className: classNames(`dropdown drop-${resolvedInlinePosition}`, className), ref: dropdownRef, children: [ToggleElement, menuItems] }));
3480
+ return (jsxRuntime.jsxs("div", { ref: toggleRef, className: classNames(`dropdown drop-${resolvedSide} d-inline-block`, className), children: [ToggleElement, open && (asPortal ? reactDom.createPortal(menuItems, document.body) : menuItems)] }));
3067
3481
  }
3068
3482
 
3069
3483
  function useScreenshot() {
@@ -3093,13 +3507,18 @@ function useScreenshot() {
3093
3507
 
3094
3508
  function useScreenshotDownload() {
3095
3509
  const { clipRef, takeBlob } = useScreenshot();
3096
- const download = React.useCallback(async () => {
3097
- const blob = await takeBlob();
3510
+ const download = React.useCallback(async (fileName = 'voucher') => {
3511
+ const blob = await takeBlob('image/jpeg');
3098
3512
  const url = window.URL.createObjectURL(blob);
3099
3513
  const link = window.document.createElement('a');
3100
3514
  link.style.display = 'none';
3101
3515
  link.href = url;
3102
- link.download = 'voucher.jpg';
3516
+ // Fall back to the default base name when the caller passes a blank/whitespace value,
3517
+ // so the file is never named just ".jpg".
3518
+ const safeFileName = fileName.trim() || 'voucher';
3519
+ // The extension is always appended by the hook to guarantee a valid image file,
3520
+ // regardless of what fileName the caller passes (e.g. "receipt.png" -> "receipt.png.jpg").
3521
+ link.download = `${safeFileName}.jpg`;
3103
3522
  document.body.appendChild(link);
3104
3523
  link.click();
3105
3524
  document.body.removeChild(link);
@@ -3113,11 +3532,15 @@ function useScreenshotDownload() {
3113
3532
 
3114
3533
  function useScreenshotWebShare() {
3115
3534
  const { takeBlob, clipRef } = useScreenshot();
3116
- const share = React.useCallback(async () => {
3117
- const blob = await takeBlob();
3118
- const image = new File([blob], 'voucher.jpeg', { type: 'image/jpeg' });
3119
- if (!navigator.canShare
3120
- && (navigator.canShare && !navigator.canShare({ files: [image] }))) {
3535
+ const share = React.useCallback(async (fileName = 'voucher') => {
3536
+ const blob = await takeBlob('image/jpeg');
3537
+ // Fall back to the default base name when the caller passes a blank/whitespace value,
3538
+ // so the file is never named just ".jpeg".
3539
+ const safeFileName = fileName.trim() || 'voucher';
3540
+ // The extension is always appended by the hook to guarantee a valid image file,
3541
+ // regardless of what fileName the caller passes (e.g. "receipt.png" -> "receipt.png.jpeg").
3542
+ const image = new File([blob], `${safeFileName}.jpeg`, { type: 'image/jpeg' });
3543
+ if (!navigator.canShare || !navigator.canShare({ files: [image] })) {
3121
3544
  window.print();
3122
3545
  return;
3123
3546
  }
@@ -3129,11 +3552,11 @@ function useScreenshotWebShare() {
3129
3552
  };
3130
3553
  }
3131
3554
 
3132
- function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText = 'Download', shareText = 'Share', className, children, }) {
3555
+ function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText = 'Download', shareText = 'Share', fileName = 'voucher', hideActions = false, className, children, }) {
3133
3556
  const { shareRef, share } = useScreenshotWebShare();
3134
3557
  const { downloadRef, download } = useScreenshotDownload();
3135
3558
  const handleShare = () => {
3136
- share()
3559
+ share(fileName)
3137
3560
  .catch(async (err) => {
3138
3561
  if (onError) {
3139
3562
  await onError(err);
@@ -3144,7 +3567,7 @@ function DVoucher({ amount, amountDetails, icon, title, onError, message, downlo
3144
3567
  });
3145
3568
  };
3146
3569
  const handleDownload = () => {
3147
- download()
3570
+ download(fileName)
3148
3571
  .catch(async (err) => {
3149
3572
  if (onError) {
3150
3573
  await onError(err);
@@ -3172,80 +3595,36 @@ function DVoucher({ amount, amountDetails, icon, title, onError, message, downlo
3172
3595
  return (jsxRuntime.jsx("div", { className: classNames('d-voucher', className), ref: (el) => {
3173
3596
  shareRef.current = el;
3174
3597
  downloadRef.current = el;
3175
- }, children: jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "d-voucher-header", children: [resolvedIconProps && (jsxRuntime.jsx(DIcon, Object.assign({}, resolvedIconProps))), jsxRuntime.jsxs("div", { className: "text-center", children: [jsxRuntime.jsx("h3", { className: "mb-2", children: title }), jsxRuntime.jsx("p", { className: "m-0", children: message })] })] }), amount && (jsxRuntime.jsxs("div", { className: "d-voucher-amount", children: [jsxRuntime.jsx("div", { className: classNames('text-center fw-bold fs-3', amountDetails ? 'mb-1' : 'm-0'), children: amount }), amountDetails] })), jsxRuntime.jsx("hr", { className: "my-4" }), children, jsxRuntime.jsx("hr", { className: "my-4" }), jsxRuntime.jsxs("div", { className: "d-voucher-footer", children: [jsxRuntime.jsx(DButton, { onClick: handleShare, iconStart: "Share2", text: shareText, variant: "outline", size: "sm" }), jsxRuntime.jsx(DButton, { onClick: handleDownload, iconStart: "Download", text: downloadText, variant: "outline", size: "sm" })] })] }) }));
3176
- }
3177
-
3178
- function useCountdown(seconds) {
3179
- const [secondsLeft, setSecondsLeft] = React.useState(seconds);
3180
- const [isActive, setIsActive] = React.useState(true);
3181
- const resetCountdown = React.useCallback((newSeconds = seconds) => {
3182
- setIsActive(false);
3183
- setSecondsLeft(newSeconds);
3184
- }, [seconds]);
3185
- const restartCountdown = React.useCallback(() => {
3186
- resetCountdown(seconds);
3187
- setIsActive(true);
3188
- }, [resetCountdown, seconds]);
3189
- React.useEffect(() => {
3190
- if (!isActive) {
3191
- return () => { };
3192
- }
3193
- const interval = setInterval(() => {
3194
- setSecondsLeft((prevSeconds) => {
3195
- const newSeconds = prevSeconds - 1;
3196
- if (newSeconds <= 0) {
3197
- clearInterval(interval);
3198
- setIsActive(false);
3199
- return 0;
3200
- }
3201
- return newSeconds;
3202
- });
3203
- }, 1000);
3204
- return () => clearInterval(interval);
3205
- }, [isActive]);
3206
- return { secondsLeft, restartCountdown };
3598
+ }, children: jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "d-voucher-header", children: [resolvedIconProps && (jsxRuntime.jsx(DIcon, Object.assign({}, resolvedIconProps))), jsxRuntime.jsxs("div", { className: "d-voucher-title-group", children: [jsxRuntime.jsx("h3", { className: "d-voucher-title", children: title }), jsxRuntime.jsx("p", { className: "d-voucher-message", children: message })] })] }), amount && (jsxRuntime.jsxs("div", { className: "d-voucher-amount", children: [jsxRuntime.jsx("div", { className: classNames('d-voucher-amount-value', { 'd-voucher-amount-value-with-details': !!amountDetails }), children: amount }), amountDetails] })), jsxRuntime.jsx("hr", { className: "d-voucher-divider" }), children, !hideActions && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "my-4" }), jsxRuntime.jsxs("div", { className: "d-voucher-footer", children: [jsxRuntime.jsx(DButton, { onClick: handleShare, iconStart: "Share2", text: shareText, variant: "outline", size: "sm" }), jsxRuntime.jsx(DButton, { onClick: handleDownload, iconStart: "Download", text: downloadText, variant: "outline", size: "sm" })] })] }))] }) }));
3207
3599
  }
3208
3600
 
3209
3601
  const defaultMessage = (secs) => (secs > 0
3210
3602
  ? `Didn't get any code? Resend in: ${secs}s`
3211
3603
  : "Didn't get any code?");
3212
- function OtpCountdown({ seconds, resendText, message, }) {
3213
- const { secondsLeft, restartCountdown } = useCountdown(seconds);
3214
- return (jsxRuntime.jsxs("div", { className: "d-flex gap-2 align-items-center", children: [jsxRuntime.jsx("p", { className: "mb-0 flex-1", children: message ? message(secondsLeft) : defaultMessage(secondsLeft) }), jsxRuntime.jsx(DButton, { text: resendText, variant: "link", className: "text-nowrap", disabled: secondsLeft > 0, onClick: restartCountdown })] }));
3604
+ function OtpCountdown({ secondsLeft, restartCountdown, resendText, message, }) {
3605
+ return (jsxRuntime.jsxs("div", { className: "d-otp-countdown", children: [jsxRuntime.jsx("p", { className: "d-otp-countdown-text", children: message ? message(secondsLeft) : defaultMessage(secondsLeft) }), jsxRuntime.jsx(DButton, { text: resendText, variant: "link", className: "text-nowrap", disabled: secondsLeft > 0, onClick: restartCountdown })] }));
3215
3606
  }
3216
3607
 
3217
3608
  const TEXT_PROPS = {
3218
3609
  resend: 'Resend',
3219
- resendText: 'Resend code',
3610
+ resendText: 'Resend',
3220
3611
  submit: 'Authorize and continue',
3221
3612
  title: 'We will send you a 6-digit code to your associated phone number so you can continue with your request.',
3613
+ invalidCode: 'Invalid code, please try again.',
3222
3614
  contact: (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { children: "Problems with your digital token? Contact us" }), ' ', jsxRuntime.jsx("a", { href: "https://www.modyo.com", className: "link-primary text-nowrap", target: "_blank", rel: "noreferrer", children: "Contact us" })] })),
3223
3615
  };
3224
3616
  function DOtp({ className, action, isLoading, otpSize = 6, texts = TEXT_PROPS, seconds = 15, }) {
3225
- const [otp, setOtp] = React.useState('');
3226
- const [invalid, setInvalid] = React.useState(false);
3227
- const handler = React.useCallback(async () => {
3228
- if (otp.length < otpSize) {
3229
- setInvalid(true);
3230
- return;
3231
- }
3232
- setInvalid(false);
3233
- await action();
3234
- }, [
3235
- otp.length,
3236
- action,
3237
- otpSize,
3238
- ]);
3239
- return (jsxRuntime.jsxs("div", { className: className, children: [jsxRuntime.jsx("p", { children: texts.title }), jsxRuntime.jsxs("div", { className: "d-flex flex-column gap-6 pb-4 px-3", children: [jsxRuntime.jsxs("div", { className: "d-flex flex-column gap-6", children: [jsxRuntime.jsx(DInputPin, { className: "modal-otp-pin", characters: otpSize, onChange: (e) => setOtp(e), invalid: invalid && otp.length < otpSize, placeholder: "0" }), jsxRuntime.jsx(OtpCountdown, { seconds: seconds, resendText: texts.resend })] }), jsxRuntime.jsx("hr", { className: "m-0" }), jsxRuntime.jsxs("div", { className: "d-flex flex-column flex-lg-row gap-4 align-items-center", children: [jsxRuntime.jsx(DButton, { text: texts.submit, onClick: () => {
3240
- handler().catch((err) => {
3617
+ const { setOtp, invalid, submit, isLoading: isSubmitting, secondsLeft, restartCountdown, } = useOtp({ action, otpSize, seconds });
3618
+ return (jsxRuntime.jsxs("div", { className: className, children: [jsxRuntime.jsx("p", { children: texts.title }), jsxRuntime.jsxs("div", { className: "d-otp d-otp-content", children: [jsxRuntime.jsxs("div", { className: "d-otp-fields", children: [jsxRuntime.jsx(DInputPin, { className: "modal-otp-pin", characters: otpSize, onChange: (e) => setOtp(e), invalid: invalid, hint: invalid ? texts.invalidCode : undefined, placeholder: "0" }), jsxRuntime.jsx(OtpCountdown, { secondsLeft: secondsLeft, restartCountdown: restartCountdown, resendText: texts.resendText })] }), jsxRuntime.jsx("hr", { className: "d-otp-divider" }), jsxRuntime.jsxs("div", { className: "d-otp-footer", children: [jsxRuntime.jsx(DButton, { text: texts.submit, onClick: () => {
3619
+ submit().catch((err) => {
3241
3620
  // eslint-disable-next-line no-console
3242
3621
  console.error('Error in DOtp action:', err);
3243
3622
  });
3244
- }, loading: isLoading }), jsxRuntime.jsx("p", { className: "small ms-lg-auto mb-0", children: texts.contact })] })] })] }));
3623
+ }, loading: isLoading || isSubmitting }), jsxRuntime.jsx("p", { className: "d-otp-contact", children: texts.contact })] })] })] }));
3245
3624
  }
3246
3625
 
3247
3626
  function DefaultErrorBoundary({ resetErrorBoundary }) {
3248
- return (jsxRuntime.jsx(DAlert, { color: "danger", showClose: false, children: jsxRuntime.jsxs("div", { className: "d-flex align-items-center gap-2", children: [jsxRuntime.jsx("span", { children: "An unexpected error occurred." }), jsxRuntime.jsx(DButton, { color: "secondary", variant: "outline", size: "sm", onClick: resetErrorBoundary, children: "Retry" })] }) }));
3627
+ return (jsxRuntime.jsx(DAlert, { color: "danger", showClose: false, children: jsxRuntime.jsxs("div", { className: "d-error-boundary-content", children: [jsxRuntime.jsx("span", { children: "An unexpected error occurred." }), jsxRuntime.jsx(DButton, { color: "secondary", variant: "outline", size: "sm", onClick: resetErrorBoundary, children: "Retry" })] }) }));
3249
3628
  }
3250
3629
 
3251
3630
  function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children, }) {
@@ -3263,15 +3642,15 @@ function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children,
3263
3642
  }
3264
3643
 
3265
3644
  function ErrorState({ message, onRetry, retryMessage = 'Retry', color = 'danger', }) {
3266
- return (jsxRuntime.jsxs(DAlert, { color: color, className: "d-flex align-items-center gap-3", children: [jsxRuntime.jsx("div", { className: "flex-grow-1", children: jsxRuntime.jsx("p", { className: "mb-0", children: message !== null && message !== void 0 ? message : 'An unexpected error occurred.' }) }), onRetry && (jsxRuntime.jsx(DButton, { onClick: onRetry, text: retryMessage, variant: "outline", iconStart: "RefreshCw" }))] }));
3645
+ return (jsxRuntime.jsxs(DAlert, { color: color, className: "d-error-state", children: [jsxRuntime.jsx("div", { className: "d-error-state-content", children: jsxRuntime.jsx("p", { className: "d-error-state-message", children: message !== null && message !== void 0 ? message : 'An unexpected error occurred.' }) }), onRetry && (jsxRuntime.jsx(DButton, { onClick: onRetry, text: retryMessage, variant: "outline", iconStart: "RefreshCw" }))] }));
3267
3646
  }
3268
3647
 
3269
3648
  function EmptyState({ message, icon = 'FileText', actionText, onAction, }) {
3270
- return (jsxRuntime.jsxs("div", { className: "d-flex flex-column align-items-center justify-content-center p-5 text-center", children: [jsxRuntime.jsx(DIcon, { icon: icon, size: "3rem", className: "text-secondary mb-3" }), jsxRuntime.jsx("p", { className: "text-secondary mb-3", children: message !== null && message !== void 0 ? message : 'No data available.' }), actionText && onAction && (jsxRuntime.jsx(DButton, { onClick: onAction, text: actionText, variant: "outline" }))] }));
3649
+ return (jsxRuntime.jsxs("div", { className: "d-empty-state", children: [jsxRuntime.jsx(DIcon, { icon: icon, size: "3rem", className: "d-empty-state-icon" }), jsxRuntime.jsx("p", { className: "d-empty-state-message", children: message !== null && message !== void 0 ? message : 'No data available.' }), actionText && onAction && (jsxRuntime.jsx(DButton, { onClick: onAction, text: actionText, variant: "outline" }))] }));
3271
3650
  }
3272
3651
 
3273
3652
  function LoadingState({ ariaLabel = 'Loading...', className }) {
3274
- return (jsxRuntime.jsx("div", { className: `d-flex align-items-center justify-content-center p-4 ${className || ''}`.trim(), "aria-busy": "true", "aria-live": "polite", children: jsxRuntime.jsx("span", { className: "spinner-border", role: "status", "aria-label": ariaLabel }) }));
3653
+ return (jsxRuntime.jsx("div", { className: `d-loading-state ${className || ''}`.trim(), "aria-busy": "true", "aria-live": "polite", children: jsxRuntime.jsx("span", { className: "spinner-border", role: "status", "aria-label": ariaLabel }) }));
3275
3654
  }
3276
3655
 
3277
3656
  function render(renderable) {
@@ -3330,7 +3709,7 @@ function DConfirmModalUI({ entry }) {
3330
3709
  }
3331
3710
  });
3332
3711
  }, [onConfirmAction]);
3333
- 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 })] })] }));
3712
+ 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: "d-confirm-modal-body", children: [jsxRuntime.jsx("p", { className: "d-confirm-modal-message", children: message }), critical && (jsxRuntime.jsx(ForwardedDInput, { type: "text", label: critical.codeLabel || 'Confirmation code', placeholder: critical.inputPlaceholder, value: confirmationCode, onChange: (value) => setConfirmationCode(value), className: "d-confirm-modal-field", 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 })] })] }));
3334
3713
  }
3335
3714
 
3336
3715
  /**
@@ -3460,14 +3839,17 @@ exports.DVoucher = DVoucher;
3460
3839
  exports.EmptyState = EmptyState;
3461
3840
  exports.ErrorState = ErrorState;
3462
3841
  exports.LoadingState = LoadingState;
3842
+ exports.buildUrl = buildUrl;
3463
3843
  exports.changeQueryString = changeQueryString;
3464
3844
  exports.checkMediaQuery = checkMediaQuery;
3465
3845
  exports.configureI18n = configureI8n;
3466
3846
  exports.formatCurrency = formatCurrency;
3467
3847
  exports.getCssVariable = getCssVariable;
3468
3848
  exports.getQueryString = getQueryString;
3849
+ exports.sanitizeHref = sanitizeHref;
3469
3850
  exports.subscribeToMediaQuery = subscribeToMediaQuery;
3470
3851
  exports.useConfirmModal = useConfirmModal;
3852
+ exports.useCountdown = useCountdown;
3471
3853
  exports.useDContext = useDContext;
3472
3854
  exports.useDPortalContext = useDPortalContext;
3473
3855
  exports.useDToast = useDToast;
@@ -3483,7 +3865,11 @@ exports.useMediaBreakpointUpXl = useMediaBreakpointUpXl;
3483
3865
  exports.useMediaBreakpointUpXs = useMediaBreakpointUpXs;
3484
3866
  exports.useMediaBreakpointUpXxl = useMediaBreakpointUpXxl;
3485
3867
  exports.useMediaQuery = useMediaQuery;
3868
+ exports.useOtp = useOtp;
3486
3869
  exports.useProvidedRefOrCreate = useProvidedRefOrCreate;
3870
+ exports.useScreenshot = useScreenshot;
3871
+ exports.useScreenshotDownload = useScreenshotDownload;
3872
+ exports.useScreenshotWebShare = useScreenshotWebShare;
3487
3873
  exports.useStackState = useStackState;
3488
3874
  exports.useTabContext = useTabContext;
3489
3875
  exports.validatePhoneNumber = validatePhoneNumber;