@dynamic-framework/ui-react 2.5.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/css/dynamic-ui.css +38 -10
- package/dist/css/dynamic-ui.min.css +2 -2
- package/dist/index.esm.js +466 -83
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +471 -82
- package/dist/index.js.map +1 -1
- package/dist/types/components/DAlert/DAlert.d.ts +1 -1
- package/dist/types/components/DAvatar/DAvatar.d.ts +2 -1
- package/dist/types/components/DBadge/DBadge.d.ts +2 -1
- package/dist/types/components/DBox/DBox.d.ts +1 -1
- package/dist/types/components/DBoxFile/DBoxFile.d.ts +1 -1
- package/dist/types/components/DButtonIcon/DButtonIcon.d.ts +1 -1
- package/dist/types/components/DCard/DCard.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardBody.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardFooter.d.ts +1 -1
- package/dist/types/components/DCard/components/DCardHeader.d.ts +1 -1
- package/dist/types/components/DCarousel/components/DCarouselSlide.d.ts +1 -1
- package/dist/types/components/DChip/DChip.d.ts +2 -1
- package/dist/types/components/DCollapse/DCollapse.d.ts +1 -1
- package/dist/types/components/DConfirmModal/DConfirmModalContainer.d.ts +21 -0
- package/dist/types/components/DConfirmModal/DConfirmModalUI.d.ts +7 -0
- package/dist/types/components/DConfirmModal/confirmModalStore.d.ts +6 -0
- package/dist/types/components/DConfirmModal/types.d.ts +45 -0
- package/dist/types/components/DCreditCard/DCreditCard.d.ts +2 -1
- package/dist/types/components/DCurrencyText/DCurrencyText.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/DDataStateWrapper.d.ts +8 -1
- package/dist/types/components/DDataStateWrapper/components/EmptyState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/components/ErrorState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/components/LoadingState.d.ts +2 -1
- package/dist/types/components/DDataStateWrapper/index.d.ts +4 -0
- package/dist/types/components/DDatePicker/DDatePicker.d.ts +2 -1
- package/dist/types/components/DDatePicker/components/DDatePickerHeaderSelector.d.ts +1 -1
- package/dist/types/components/DDatePicker/components/DDatePickerTime.d.ts +1 -1
- package/dist/types/components/DDropdown/DDropdown.d.ts +4 -1
- package/dist/types/components/DErrorBoundary/DErrorBoundary.d.ts +1 -1
- package/dist/types/components/DErrorBoundary/components/DefaultErrorBoundary.d.ts +2 -1
- package/dist/types/components/DIcon/DIcon.d.ts +1 -1
- package/dist/types/components/DIconBase/DIconBase.d.ts +2 -1
- package/dist/types/components/DInputCheck/DInputCheck.d.ts +1 -1
- package/dist/types/components/DInputPin/DInputPin.d.ts +2 -1
- package/dist/types/components/DInputSearch/DInputSearch.d.ts +11 -0
- package/dist/types/components/DInputSearch/index.d.ts +3 -0
- package/dist/types/components/DInputSelect/DInputSelect.d.ts +1 -1
- package/dist/types/components/DInputSwitch/DInputSwitch.d.ts +2 -1
- package/dist/types/components/DLayout/DLayout.d.ts +1 -1
- package/dist/types/components/DLayout/components/DLayoutPane.d.ts +1 -1
- package/dist/types/components/DListGroup/DListGroup.d.ts +1 -1
- package/dist/types/components/DListGroup/components/DListGroupItem.d.ts +1 -1
- package/dist/types/components/DModal/DModal.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalBody.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalFooter.d.ts +1 -1
- package/dist/types/components/DModal/components/DModalHeader.d.ts +1 -1
- package/dist/types/components/DOffcanvas/DOffcanvas.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasBody.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasFooter.d.ts +1 -1
- package/dist/types/components/DOffcanvas/components/DOffcanvasHeader.d.ts +1 -1
- package/dist/types/components/DOtp/DOtp.d.ts +1 -1
- package/dist/types/components/DOtp/components/OtpCountdown.d.ts +2 -1
- package/dist/types/components/DPaginator/DPaginator.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/DPasswordStrengthMeter.d.ts +3 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordCheckItem.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordCheckList.d.ts +2 -1
- package/dist/types/components/DPasswordStrengthMeter/PasswordStrength.d.ts +2 -1
- package/dist/types/components/DPopover/DPopover.d.ts +1 -1
- package/dist/types/components/DProgress/DProgress.d.ts +2 -1
- package/dist/types/components/DSelect/DSelect.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectClearIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectDropdownIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectLoadingIndicator.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectMultiValueRemove.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionCheck.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionEmoji.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectOptionIcon.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectPlaceholder.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueEmoji.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueEmojiText.d.ts +2 -1
- package/dist/types/components/DSelect/components/DSelectSingleValueIconText.d.ts +2 -1
- package/dist/types/components/DStepper/DStepper.d.ts +2 -1
- package/dist/types/components/DStepperDesktop/DStepperDesktop.d.ts +2 -1
- package/dist/types/components/DStepperMobile/DStepperMobile.d.ts +2 -1
- package/dist/types/components/DTabs/DTabs.d.ts +1 -1
- package/dist/types/components/DTabs/components/DTabContent.d.ts +1 -1
- package/dist/types/components/DTimeline/DTimeline.d.ts +1 -1
- package/dist/types/components/DToast/DToast.d.ts +1 -1
- package/dist/types/components/DToast/components/DToastBody.d.ts +1 -1
- package/dist/types/components/DToast/components/DToastHeader.d.ts +1 -1
- package/dist/types/components/DToastContainer/DToastContainer.d.ts +1 -1
- package/dist/types/components/DTooltip/DTooltip.d.ts +1 -1
- package/dist/types/components/DVoucher/DVoucher.d.ts +1 -1
- package/dist/types/components/index.d.ts +6 -1
- package/dist/types/components/interface.d.ts +1 -1
- package/dist/types/contexts/DContext.d.ts +25 -3
- package/dist/types/contexts/DPortalContext.d.ts +1 -1
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/useConfirmModal.d.ts +34 -0
- package/package.json +4 -2
- package/src/style/_shame.scss +13 -0
- package/src/style/abstracts/variables/_forms.scss +1 -1
- package/src/style/abstracts/variables/_list-group.scss +1 -1
- package/src/style/base/_backdrop.scss +7 -0
- package/src/style/components/_d-carousel.scss +1 -1
- package/src/style/components/_d-modal.scss +8 -0
- package/src/style/root/_root.scss +31 -0
package/dist/index.esm.js
CHANGED
|
@@ -58,7 +58,7 @@ function useDisableBodyScrollEffect(disable) {
|
|
|
58
58
|
timer = window.setTimeout(() => {
|
|
59
59
|
unlock();
|
|
60
60
|
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
61
|
-
},
|
|
61
|
+
}, 300);
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
64
|
unlock();
|
|
@@ -143,7 +143,7 @@ function getKeyboardFocusableElements(container) {
|
|
|
143
143
|
const DPortalContext = createContext(undefined);
|
|
144
144
|
function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
145
145
|
const { created } = usePortal(portalName);
|
|
146
|
-
const [stack, { push, pop
|
|
146
|
+
const [stack, { push, pop }] = useStackState([]);
|
|
147
147
|
useDisableBodyScrollEffect(Boolean(stack.length));
|
|
148
148
|
const openPortal = useCallback(
|
|
149
149
|
// eslint-disable-next-line prefer-arrow-callback
|
|
@@ -164,11 +164,9 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
|
164
164
|
(_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
|
|
165
165
|
}, [availablePortals, push]);
|
|
166
166
|
const closePortal = useCallback(() => {
|
|
167
|
-
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
167
|
+
// pop() is safe on empty stacks, so close remains idempotent.
|
|
170
168
|
pop();
|
|
171
|
-
}, [
|
|
169
|
+
}, [pop]);
|
|
172
170
|
const publicStack = useMemo(() => stack.map(({ name, payload }) => ({ name, payload })), [stack]);
|
|
173
171
|
const value = useMemo(() => ({
|
|
174
172
|
stack: publicStack,
|
|
@@ -259,6 +257,40 @@ function useDPortalContext() {
|
|
|
259
257
|
return context;
|
|
260
258
|
}
|
|
261
259
|
|
|
260
|
+
function createConfirmModalStore() {
|
|
261
|
+
const listeners = new Set();
|
|
262
|
+
let entries = [];
|
|
263
|
+
function notify() {
|
|
264
|
+
listeners.forEach((listener) => listener([...entries]));
|
|
265
|
+
}
|
|
266
|
+
const store = {
|
|
267
|
+
subscribe(listener) {
|
|
268
|
+
listeners.add(listener);
|
|
269
|
+
listener([...entries]);
|
|
270
|
+
return () => {
|
|
271
|
+
listeners.delete(listener);
|
|
272
|
+
};
|
|
273
|
+
},
|
|
274
|
+
push(entry) {
|
|
275
|
+
entries = [...entries, entry];
|
|
276
|
+
notify();
|
|
277
|
+
},
|
|
278
|
+
remove(id) {
|
|
279
|
+
entries = entries.filter((e) => e.id !== id);
|
|
280
|
+
notify();
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
return store;
|
|
284
|
+
}
|
|
285
|
+
const ConfirmModalStoreContext = createContext(null);
|
|
286
|
+
function useConfirmModalStore() {
|
|
287
|
+
const store = useContext(ConfirmModalStoreContext);
|
|
288
|
+
if (!store) {
|
|
289
|
+
throw new Error('useConfirmModal must be used within a <DContextProvider>.');
|
|
290
|
+
}
|
|
291
|
+
return store;
|
|
292
|
+
}
|
|
293
|
+
|
|
262
294
|
function getCssVariable(variable) {
|
|
263
295
|
const computedStyle = getComputedStyle(document.documentElement);
|
|
264
296
|
return computedStyle.getPropertyValue(variable).trim();
|
|
@@ -317,6 +349,28 @@ const DContext = createContext(DEFAULT_STATE);
|
|
|
317
349
|
* component to configure icons, currency, language, and portal settings
|
|
318
350
|
* for all descendant Dynamic UI components.
|
|
319
351
|
*
|
|
352
|
+
* To enable confirmation modals you must also mount `DConfirmModalContainer`
|
|
353
|
+
* somewhere inside this provider (typically right before the closing tag of
|
|
354
|
+
* your root layout), similar to how `DToastContainer` works:
|
|
355
|
+
*
|
|
356
|
+
* ```tsx
|
|
357
|
+
* // Default: portalName="d-portal"
|
|
358
|
+
* <DContextProvider>
|
|
359
|
+
* <App />
|
|
360
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
361
|
+
* </DContextProvider>
|
|
362
|
+
* ```
|
|
363
|
+
*
|
|
364
|
+
* If you customize `portalName`, match it in `DConfirmModalContainer.nodeId`:
|
|
365
|
+
*
|
|
366
|
+
* ```tsx
|
|
367
|
+
* // Custom portalName
|
|
368
|
+
* <DContextProvider portalName="my-custom-portal">
|
|
369
|
+
* <App />
|
|
370
|
+
* <DConfirmModalContainer nodeId="my-custom-portal" />
|
|
371
|
+
* </DContextProvider>
|
|
372
|
+
* ```
|
|
373
|
+
*
|
|
320
374
|
* @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
|
|
321
375
|
* Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
|
|
322
376
|
*/
|
|
@@ -342,7 +396,8 @@ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAUL
|
|
|
342
396
|
});
|
|
343
397
|
}, [setContext]);
|
|
344
398
|
const value = useMemo(() => (Object.assign(Object.assign({}, internalContext), { setContext })), [internalContext, setContext]);
|
|
345
|
-
|
|
399
|
+
const confirmStore = useMemo(() => createConfirmModalStore(), []);
|
|
400
|
+
return (jsx(ConfirmModalStoreContext.Provider, { value: confirmStore, children: jsx(DContext.Provider, { value: value, children: jsx(DPortalContextProvider, { portalName: portalName, availablePortals: availablePortals, children: children }) }) }));
|
|
346
401
|
}
|
|
347
402
|
/**
|
|
348
403
|
* Returns the Dynamic UI context value set by `DContextProvider`.
|
|
@@ -1037,7 +1092,7 @@ function DBoxFile(_a) {
|
|
|
1037
1092
|
}
|
|
1038
1093
|
|
|
1039
1094
|
const DButton = forwardRef((props, ref) => {
|
|
1040
|
-
const { color = 'primary', size, variant, text, children, iconStart, iconStartFamilyClass, iconStartFamilyPrefix, iconStartMaterialStyle, iconEnd, iconEndFamilyClass, iconEndFamilyPrefix, iconEndMaterialStyle, loading = false, loadingText, loadingAriaLabel, disabled = false, className, style, dataAttributes, onClick, type = 'button', target, rel, href, 'aria-label': ariaLabelProp } = props, rest = __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']);
|
|
1095
|
+
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 = __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']);
|
|
1041
1096
|
// Responsive size resolution using useResponsiveProp
|
|
1042
1097
|
const { responsivePropValue } = useResponsiveProp(true);
|
|
1043
1098
|
const resolvedSize = useMemo(() => {
|
|
@@ -1052,9 +1107,9 @@ const DButton = forwardRef((props, ref) => {
|
|
|
1052
1107
|
const isDisabled = useMemo(() => disabled || loading, [disabled, loading]);
|
|
1053
1108
|
const content = useMemo(() => children || text, [children, text]);
|
|
1054
1109
|
const classes = useMemo(() => {
|
|
1055
|
-
const variantClass = variant
|
|
1056
|
-
? `btn-${
|
|
1057
|
-
: `btn-${color}`;
|
|
1110
|
+
const variantClass = variant === 'solid'
|
|
1111
|
+
? `btn-${color}`
|
|
1112
|
+
: `btn-${variant}-${color}`;
|
|
1058
1113
|
return {
|
|
1059
1114
|
btn: true,
|
|
1060
1115
|
[variantClass]: true,
|
|
@@ -1112,9 +1167,9 @@ DButton.displayName = 'DButton';
|
|
|
1112
1167
|
function DButtonIcon(_a) {
|
|
1113
1168
|
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 = __rest(_a, ["id", "icon", "size", "className", "variant", "state", "loadingAriaLabel", "iconMaterialStyle", "disabled", "color", "loading", "href", "target", "rel", "stopPropagationEnabled", "style", "iconFamilyClass", "iconFamilyPrefix", "dataAttributes", "onClick", 'aria-label']);
|
|
1114
1169
|
const generateClasses = useMemo(() => {
|
|
1115
|
-
const variantClass = variant
|
|
1116
|
-
? `btn-${
|
|
1117
|
-
: `btn-${color}`;
|
|
1170
|
+
const variantClass = !variant || variant === 'solid'
|
|
1171
|
+
? `btn-${color}`
|
|
1172
|
+
: `btn-${variant}-${color}`;
|
|
1118
1173
|
return Object.assign(Object.assign(Object.assign({ 'btn d-button-icon': true, [variantClass]: true }, size && { [`btn-${size}`]: true }), (state && state !== 'disabled') && { [state]: true }), { loading });
|
|
1119
1174
|
}, [variant, color, size, state, loading]);
|
|
1120
1175
|
const isDisabled = useMemo(() => (state === 'disabled' || loading || disabled), [state, loading, disabled]);
|
|
@@ -1406,6 +1461,18 @@ function DDatePickerHeaderSelector({ date, changeYear, changeMonth, decreaseMont
|
|
|
1406
1461
|
|
|
1407
1462
|
function DDatePicker(_a) {
|
|
1408
1463
|
var { inputLabel, inputHint, inputAriaLabel, ariaLabelInputTime, inputActionAriaLabel = 'open calendar', inputId = 'input-calendar', timeId = 'input-time', timeInputLabel, minYearSelect, maxYearSelect, iconHeaderSize, iconMaterialStyle, iconInput, headerPrevMonthAriaLabel, headerNextMonthAriaLabel, invalid = false, valid = false, renderCustomHeader: renderCustomHeaderProp, className, dateFormatCalendar: dateFormatCalendarProp, style, dataAttributes, placeholder, showHeaderSelectors, formatHeaderDate } = _a, props = __rest(_a, ["inputLabel", "inputHint", "inputAriaLabel", "ariaLabelInputTime", "inputActionAriaLabel", "inputId", "timeId", "timeInputLabel", "minYearSelect", "maxYearSelect", "iconHeaderSize", "iconMaterialStyle", "iconInput", "headerPrevMonthAriaLabel", "headerNextMonthAriaLabel", "invalid", "valid", "renderCustomHeader", "className", "dateFormatCalendar", "style", "dataAttributes", "placeholder", "showHeaderSelectors", "formatHeaderDate"]);
|
|
1464
|
+
// Some test runtimes can resolve react-datepicker as a module object.
|
|
1465
|
+
// Normalize to a renderable component for both function and { default } shapes.
|
|
1466
|
+
const SafeDatePicker = useMemo(() => {
|
|
1467
|
+
const interopDefault = DatePicker.default;
|
|
1468
|
+
if (interopDefault !== undefined) {
|
|
1469
|
+
return interopDefault;
|
|
1470
|
+
}
|
|
1471
|
+
if (typeof DatePicker === 'function') {
|
|
1472
|
+
return DatePicker;
|
|
1473
|
+
}
|
|
1474
|
+
return DatePicker;
|
|
1475
|
+
}, []);
|
|
1409
1476
|
const pickerType = useMemo(() => {
|
|
1410
1477
|
if (props.showQuarterYearPicker)
|
|
1411
1478
|
return PickerType.Quarter;
|
|
@@ -1433,7 +1500,7 @@ function DDatePicker(_a) {
|
|
|
1433
1500
|
]);
|
|
1434
1501
|
const defaultRenderCustomHeader = useCallback((headerProps) => (jsx(DatePickerHeader, Object.assign({}, headerProps))), [DatePickerHeader]);
|
|
1435
1502
|
const renderCustomHeader = useMemo(() => (renderCustomHeaderProp || defaultRenderCustomHeader), [defaultRenderCustomHeader, renderCustomHeaderProp]);
|
|
1436
|
-
return (jsx(
|
|
1503
|
+
return (jsx(SafeDatePicker, Object.assign({}, dataAttributes, props, { calendarClassName: "d-date-picker", renderCustomHeader: renderCustomHeader, placeholderText: placeholder, customInput: (jsx(ForwardedDDatePickerInput, { id: inputId, "aria-label": inputAriaLabel, iconEndAriaLabel: inputActionAriaLabel, iconMaterialStyle: iconMaterialStyle, iconEnd: iconInput, inputLabel: inputLabel, className: className, style: style, invalid: invalid, valid: valid, hint: inputHint })), customTimeInput: (jsx(DDatePickerTime, { id: timeId, "aria-label": ariaLabelInputTime })) })));
|
|
1437
1504
|
}
|
|
1438
1505
|
|
|
1439
1506
|
function DLayoutPane({ className, style, children, cols, colsXs, colsSm, colsMd, colsLg, colsXl, colsXxl, dataAttributes, }) {
|
|
@@ -1463,6 +1530,55 @@ var DLayout$1 = Object.assign(DLayout, {
|
|
|
1463
1530
|
Pane: DLayoutPane,
|
|
1464
1531
|
});
|
|
1465
1532
|
|
|
1533
|
+
function normalizeValue(value) {
|
|
1534
|
+
return value !== null && value !== void 0 ? value : '';
|
|
1535
|
+
}
|
|
1536
|
+
function DInputSearch(_a, ref) {
|
|
1537
|
+
var { debounceMs = 300, onChange, onImmediateChange, value, defaultValue, placeholder = 'Search...' } = _a, props = __rest(_a, ["debounceMs", "onChange", "onImmediateChange", "value", "defaultValue", "placeholder"]);
|
|
1538
|
+
const inputRef = useProvidedRefOrCreate(ref);
|
|
1539
|
+
const isControlled = value !== undefined;
|
|
1540
|
+
const onChangeRef = useRef(onChange);
|
|
1541
|
+
onChangeRef.current = onChange;
|
|
1542
|
+
const [internalValue, setInternalValue] = useState(normalizeValue(isControlled ? value : defaultValue));
|
|
1543
|
+
// null = no keystroke has occurred yet (skip debounce on mount)
|
|
1544
|
+
const [pendingValue, setPendingValue] = useState(null);
|
|
1545
|
+
useEffect(() => {
|
|
1546
|
+
if (!isControlled)
|
|
1547
|
+
return;
|
|
1548
|
+
setInternalValue(normalizeValue(value));
|
|
1549
|
+
}, [isControlled, value]);
|
|
1550
|
+
const handleChange = useCallback((nextValue) => {
|
|
1551
|
+
if (!isControlled) {
|
|
1552
|
+
setInternalValue(nextValue);
|
|
1553
|
+
}
|
|
1554
|
+
setPendingValue(nextValue);
|
|
1555
|
+
onImmediateChange === null || onImmediateChange === void 0 ? void 0 : onImmediateChange(nextValue);
|
|
1556
|
+
}, [isControlled, onImmediateChange]);
|
|
1557
|
+
useEffect(() => {
|
|
1558
|
+
var _a;
|
|
1559
|
+
let timeoutId;
|
|
1560
|
+
if (pendingValue !== null) {
|
|
1561
|
+
if (debounceMs <= 0) {
|
|
1562
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, pendingValue);
|
|
1563
|
+
setPendingValue(null);
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
timeoutId = window.setTimeout(() => {
|
|
1567
|
+
var _a;
|
|
1568
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, pendingValue);
|
|
1569
|
+
setPendingValue(null);
|
|
1570
|
+
}, debounceMs);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
return () => {
|
|
1574
|
+
window.clearTimeout(timeoutId);
|
|
1575
|
+
};
|
|
1576
|
+
}, [debounceMs, pendingValue]);
|
|
1577
|
+
return (jsx(ForwardedDInput, Object.assign({ ref: inputRef }, props, { type: "search", value: internalValue, onChange: handleChange, placeholder: placeholder })));
|
|
1578
|
+
}
|
|
1579
|
+
const ForwardedDInputSearch = forwardRef(DInputSearch);
|
|
1580
|
+
ForwardedDInputSearch.displayName = 'DInputSearch';
|
|
1581
|
+
|
|
1466
1582
|
function DInputMask(props, ref) {
|
|
1467
1583
|
return (jsx(InputMask, Object.assign({ ref: ref, component: ForwardedDInput }, props)));
|
|
1468
1584
|
}
|
|
@@ -1567,6 +1683,64 @@ function useItemSelection({ getItemIdentifier: getItemIdentifierProp, previousSe
|
|
|
1567
1683
|
};
|
|
1568
1684
|
}
|
|
1569
1685
|
|
|
1686
|
+
/**
|
|
1687
|
+
* Hook to display a confirmation modal.
|
|
1688
|
+
*
|
|
1689
|
+
* Renders into the portal node specified by `DConfirmModalContainer` (typically `#d-portal`).
|
|
1690
|
+
* The confirm modal floats above the portal stack and handles its own Escape key,
|
|
1691
|
+
* preventing interference with underlying modals.
|
|
1692
|
+
*
|
|
1693
|
+
* Requires both `DContextProvider` wrapping your application AND an explicit
|
|
1694
|
+
* `<DConfirmModalContainer nodeId="d-portal" />` mounted in your app.
|
|
1695
|
+
*
|
|
1696
|
+
* @example
|
|
1697
|
+
* function App() {
|
|
1698
|
+
* return (
|
|
1699
|
+
* <DContextProvider>
|
|
1700
|
+
* <YourContent />
|
|
1701
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
1702
|
+
* </DContextProvider>
|
|
1703
|
+
* );
|
|
1704
|
+
* }
|
|
1705
|
+
*
|
|
1706
|
+
* function MyComponent() {
|
|
1707
|
+
* const confirm = useConfirmModal({
|
|
1708
|
+
* title: 'Delete Account',
|
|
1709
|
+
* message: 'This action cannot be undone.',
|
|
1710
|
+
* critical: { code: 'DELETE ACCOUNT' },
|
|
1711
|
+
* onConfirm: async () => { await deleteAccount(); },
|
|
1712
|
+
* });
|
|
1713
|
+
*
|
|
1714
|
+
* return <DButton onClick={confirm.open} text="Delete" color="danger" />;
|
|
1715
|
+
* }
|
|
1716
|
+
*/
|
|
1717
|
+
function useConfirmModal(config) {
|
|
1718
|
+
const store = useConfirmModalStore();
|
|
1719
|
+
const idRef = useRef(`confirm-modal-${Math.random().toString(36).slice(2)}`);
|
|
1720
|
+
const close = useCallback(() => {
|
|
1721
|
+
store.remove(idRef.current);
|
|
1722
|
+
queueMicrotask(() => {
|
|
1723
|
+
var _a;
|
|
1724
|
+
(_a = config.onClose) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
1725
|
+
});
|
|
1726
|
+
}, [store, config]);
|
|
1727
|
+
const open = useCallback(() => {
|
|
1728
|
+
const handleConfirmAction = async () => {
|
|
1729
|
+
try {
|
|
1730
|
+
await config.onConfirm();
|
|
1731
|
+
store.remove(idRef.current);
|
|
1732
|
+
}
|
|
1733
|
+
catch (_a) {
|
|
1734
|
+
// Keep modal open on error
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
// Remove any existing entry with this id to prevent duplicates on re-entrancy
|
|
1738
|
+
store.remove(idRef.current);
|
|
1739
|
+
store.push(Object.assign(Object.assign({}, config), { id: idRef.current, onConfirmAction: handleConfirmAction, onCloseAction: close }));
|
|
1740
|
+
}, [store, config, close]);
|
|
1741
|
+
return { open };
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1570
1744
|
function DInputCounter(_a, ref) {
|
|
1571
1745
|
var { minValue, maxValue, value = minValue, invalid, iconStart: iconStartProp, iconEnd: iconEndProp, iconStartAriaLabel = 'decrease action', iconEndAriaLabel = 'increase action', style, onChange } = _a, props = __rest(_a, ["minValue", "maxValue", "value", "invalid", "iconStart", "iconEnd", "iconStartAriaLabel", "iconEndAriaLabel", "style", "onChange"]);
|
|
1572
1746
|
const { handleOnWheel, } = useDisableInputWheel(ref);
|
|
@@ -1698,7 +1872,7 @@ const DEFAULT_VALIDATION_MESSAGES = {
|
|
|
1698
1872
|
notMatch: 'The password confirmation and the new password do not match.',
|
|
1699
1873
|
};
|
|
1700
1874
|
const DEFAULT_ENABLED_CHECKS = ['uppercase', 'lowercase', 'number', 'specialChar'];
|
|
1701
|
-
function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, }) {
|
|
1875
|
+
function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '', name, disabled = false, invalid = false, validationMessages = DEFAULT_VALIDATION_MESSAGES, enabledChecks = DEFAULT_ENABLED_CHECKS, className, style, dataAttributes, onChange, readonly = false, }) {
|
|
1702
1876
|
const [password, setPassword] = useState(value);
|
|
1703
1877
|
useEffect(() => {
|
|
1704
1878
|
setPassword(value);
|
|
@@ -1707,7 +1881,7 @@ function DPasswordStrengthMeter({ id, label = 'Password', placeholder, value = '
|
|
|
1707
1881
|
setPassword(newValue);
|
|
1708
1882
|
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
|
|
1709
1883
|
};
|
|
1710
|
-
return (jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsx(ForwardedDInputPassword, { id: id, label: label, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange }), jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
|
|
1884
|
+
return (jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsx(ForwardedDInputPassword, { id: id, label: label, placeholder: placeholder, value: password, name: name, disabled: disabled, invalid: invalid, onChange: handleChange, readonly: readonly }), jsx(PasswordChecksList, { password: password, validationMessages: validationMessages, enabledChecks: enabledChecks })] })));
|
|
1711
1885
|
}
|
|
1712
1886
|
|
|
1713
1887
|
function DInputCheck(_a) {
|
|
@@ -2611,82 +2785,210 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
|
|
|
2611
2785
|
return (jsxs("div", { className: classNames('d-credit-card', isVertical && 'is-vertical', className), children: [jsxs("div", { className: "d-credit-card-header", children: [jsx("img", { src: logoImage || BRAND_LOGOS[brand] || DEFAULT_IMAGE, alt: brand, className: "d-credit-card-logo", width: 100 }), isChipVisible && (jsx("div", { className: "d-credit-card-chip", children: jsx("img", { src: CHIP_IMAGE, alt: "chip", width: 30, className: "d-credit-card-chip-image" }) }))] }), jsxs("div", { className: "d-credit-card-details", children: [jsx("div", { className: "d-credit-card-number", children: number }), jsx("small", { className: "d-credit-card-holder-text", children: holderText }), jsx("span", { className: "d-credit-card-name", children: name })] })] }));
|
|
2612
2786
|
}
|
|
2613
2787
|
|
|
2614
|
-
const getItemClass = (action) => {
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
return base;
|
|
2621
|
-
};
|
|
2622
|
-
function DDropdown({ actions, dropdownToggle, className, classNameMenu, }) {
|
|
2788
|
+
const getItemClass = (action) => classNames({
|
|
2789
|
+
'dropdown-item d-flex align-items-center': true,
|
|
2790
|
+
[`dropdown-item-${action.color}`]: !!action.color,
|
|
2791
|
+
disabled: action.disabled,
|
|
2792
|
+
});
|
|
2793
|
+
function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal = false, placement = 'auto', }) {
|
|
2623
2794
|
const [open, setOpen] = useState(false);
|
|
2795
|
+
const [position, setPosition] = useState('down');
|
|
2796
|
+
const [menuCoords, setMenuCoords] = useState(null);
|
|
2797
|
+
// Used in default (non-portal) mode: wraps the entire dropdown
|
|
2624
2798
|
const dropdownRef = useRef(null);
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2799
|
+
// Used in portal mode: ref on the toggle wrapper to read its viewport coords
|
|
2800
|
+
const toggleRef = useRef(null);
|
|
2801
|
+
// Ref on the rendered <ul> — used for outside-click detection in portal mode
|
|
2802
|
+
const menuRef = useRef(null);
|
|
2803
|
+
const toggle = () => {
|
|
2804
|
+
setMenuCoords(null);
|
|
2805
|
+
setOpen((prev) => !prev);
|
|
2806
|
+
};
|
|
2807
|
+
const resolvedInlinePosition = placement === 'auto' ? position : placement;
|
|
2808
|
+
// Compute fixed coords from the toggle's bounding rect (portal mode only)
|
|
2809
|
+
const computePortalCoords = useCallback(() => {
|
|
2810
|
+
if (!toggleRef.current)
|
|
2811
|
+
return;
|
|
2812
|
+
const rect = toggleRef.current.getBoundingClientRect();
|
|
2813
|
+
const spaceRight = window.innerWidth - rect.right;
|
|
2814
|
+
const spaceBottom = window.innerHeight - rect.bottom;
|
|
2815
|
+
const spaceTop = rect.top;
|
|
2816
|
+
let resolvedPlacement;
|
|
2817
|
+
if (placement === 'auto') {
|
|
2641
2818
|
if (spaceBottom < 150) {
|
|
2642
|
-
|
|
2819
|
+
resolvedPlacement = 'up';
|
|
2643
2820
|
}
|
|
2644
2821
|
else if (spaceRight < 150) {
|
|
2645
|
-
|
|
2822
|
+
resolvedPlacement = 'start';
|
|
2646
2823
|
}
|
|
2647
2824
|
else {
|
|
2648
|
-
|
|
2825
|
+
resolvedPlacement = 'down';
|
|
2649
2826
|
}
|
|
2650
2827
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2828
|
+
else {
|
|
2829
|
+
resolvedPlacement = placement;
|
|
2830
|
+
}
|
|
2831
|
+
const openStart = resolvedPlacement === 'start';
|
|
2832
|
+
const openEnd = resolvedPlacement === 'end';
|
|
2833
|
+
const openUp = resolvedPlacement === 'up';
|
|
2834
|
+
let availableHeight = spaceBottom;
|
|
2835
|
+
if (openUp) {
|
|
2836
|
+
availableHeight = spaceTop;
|
|
2837
|
+
}
|
|
2838
|
+
else if (openStart || openEnd) {
|
|
2839
|
+
availableHeight = window.innerHeight - rect.top;
|
|
2840
|
+
}
|
|
2841
|
+
const verticalCoords = openUp
|
|
2842
|
+
? { bottom: window.innerHeight - rect.top + 4 }
|
|
2843
|
+
: { top: openStart || openEnd ? rect.top : rect.bottom + 4 };
|
|
2844
|
+
let horizontalCoords;
|
|
2845
|
+
if (openStart) {
|
|
2846
|
+
horizontalCoords = { right: window.innerWidth - rect.left + 4 };
|
|
2847
|
+
}
|
|
2848
|
+
else if (openEnd) {
|
|
2849
|
+
horizontalCoords = { left: rect.right + 4 };
|
|
2656
2850
|
}
|
|
2657
2851
|
else {
|
|
2658
|
-
|
|
2852
|
+
horizontalCoords = { left: rect.left };
|
|
2659
2853
|
}
|
|
2854
|
+
setMenuCoords(Object.assign(Object.assign(Object.assign({}, verticalCoords), horizontalCoords), { minWidth: Math.max(rect.width, 160), maxHeight: `${Math.max(availableHeight - 12, 120)}px` }));
|
|
2855
|
+
}, [placement]);
|
|
2856
|
+
// Outside-click: default mode
|
|
2857
|
+
useEffect(() => {
|
|
2858
|
+
if (asPortal)
|
|
2859
|
+
return () => { };
|
|
2860
|
+
const handler = (event) => {
|
|
2861
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
2862
|
+
setOpen(false);
|
|
2863
|
+
}
|
|
2864
|
+
};
|
|
2865
|
+
document.addEventListener('mousedown', handler);
|
|
2866
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
2867
|
+
}, [asPortal]);
|
|
2868
|
+
// Outside-click: portal mode (must check both toggle and floating menu)
|
|
2869
|
+
useEffect(() => {
|
|
2870
|
+
if (!asPortal || !open)
|
|
2871
|
+
return () => { };
|
|
2872
|
+
const handler = (event) => {
|
|
2873
|
+
var _a, _b;
|
|
2874
|
+
const target = event.target;
|
|
2875
|
+
if (!((_a = toggleRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)) && !((_b = menuRef.current) === null || _b === void 0 ? void 0 : _b.contains(target))) {
|
|
2876
|
+
setOpen(false);
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
document.addEventListener('mousedown', handler);
|
|
2880
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
2881
|
+
}, [asPortal, open]);
|
|
2882
|
+
// Auto-position: default mode
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
if (asPortal || !open || !dropdownRef.current || placement !== 'auto')
|
|
2885
|
+
return;
|
|
2886
|
+
const rect = dropdownRef.current.getBoundingClientRect();
|
|
2887
|
+
const spaceBottom = window.innerHeight - rect.bottom;
|
|
2888
|
+
const spaceRight = window.innerWidth - rect.right;
|
|
2889
|
+
if (spaceBottom < 150)
|
|
2890
|
+
setPosition('up');
|
|
2891
|
+
else if (spaceRight < 150)
|
|
2892
|
+
setPosition('start');
|
|
2893
|
+
else
|
|
2894
|
+
setPosition('down');
|
|
2895
|
+
}, [asPortal, open, placement]);
|
|
2896
|
+
// Compute + track coords: portal mode
|
|
2897
|
+
// Re-runs whenever the menu opens and keeps coords fresh on scroll/resize
|
|
2898
|
+
useEffect(() => {
|
|
2899
|
+
if (!asPortal || !open)
|
|
2900
|
+
return () => { };
|
|
2901
|
+
computePortalCoords();
|
|
2902
|
+
const update = () => computePortalCoords();
|
|
2903
|
+
window.addEventListener('scroll', update, { passive: true, capture: true });
|
|
2904
|
+
window.addEventListener('resize', update, { passive: true });
|
|
2905
|
+
return () => {
|
|
2906
|
+
window.removeEventListener('scroll', update, { capture: true });
|
|
2907
|
+
window.removeEventListener('resize', update);
|
|
2908
|
+
};
|
|
2909
|
+
}, [asPortal, open, computePortalCoords]);
|
|
2910
|
+
let ToggleElement;
|
|
2911
|
+
if (dropdownToggle) {
|
|
2912
|
+
ToggleElement = typeof dropdownToggle === 'function'
|
|
2913
|
+
? dropdownToggle({ open, toggle })
|
|
2914
|
+
: dropdownToggle;
|
|
2660
2915
|
}
|
|
2661
2916
|
else {
|
|
2662
|
-
ToggleElement = (jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick:
|
|
2917
|
+
ToggleElement = (jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle, icon: "MoreVertical" }));
|
|
2663
2918
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2919
|
+
const portalMenuStyle = asPortal && menuCoords
|
|
2920
|
+
? {
|
|
2921
|
+
position: 'fixed',
|
|
2922
|
+
top: menuCoords.top,
|
|
2923
|
+
bottom: menuCoords.bottom,
|
|
2924
|
+
left: menuCoords.left,
|
|
2925
|
+
right: menuCoords.right,
|
|
2926
|
+
minWidth: menuCoords.minWidth,
|
|
2927
|
+
maxHeight: menuCoords.maxHeight,
|
|
2928
|
+
overflowY: 'auto',
|
|
2929
|
+
zIndex: 1050,
|
|
2930
|
+
transform: 'none',
|
|
2931
|
+
}
|
|
2932
|
+
: undefined;
|
|
2933
|
+
let inlineTop = '100%';
|
|
2934
|
+
if (resolvedInlinePosition === 'up') {
|
|
2935
|
+
inlineTop = 'auto';
|
|
2936
|
+
}
|
|
2937
|
+
else if (resolvedInlinePosition === 'start' || resolvedInlinePosition === 'end') {
|
|
2938
|
+
inlineTop = 0;
|
|
2939
|
+
}
|
|
2940
|
+
let inlineLeft = 0;
|
|
2941
|
+
if (resolvedInlinePosition === 'start') {
|
|
2942
|
+
inlineLeft = 'auto';
|
|
2943
|
+
}
|
|
2944
|
+
else if (resolvedInlinePosition === 'end') {
|
|
2945
|
+
inlineLeft = '100%';
|
|
2946
|
+
}
|
|
2947
|
+
let inlineRight = 'auto';
|
|
2948
|
+
if (resolvedInlinePosition === 'start') {
|
|
2949
|
+
inlineRight = '100%';
|
|
2950
|
+
}
|
|
2951
|
+
let inlineTransform = 'translateY(4px)';
|
|
2952
|
+
if (resolvedInlinePosition === 'up') {
|
|
2953
|
+
inlineTransform = 'translateY(-4px)';
|
|
2954
|
+
}
|
|
2955
|
+
else if (resolvedInlinePosition === 'start') {
|
|
2956
|
+
inlineTransform = 'translateX(-4px)';
|
|
2957
|
+
}
|
|
2958
|
+
else if (resolvedInlinePosition === 'end') {
|
|
2959
|
+
inlineTransform = 'translateX(4px)';
|
|
2960
|
+
}
|
|
2961
|
+
const inlineMenuStyle = {
|
|
2962
|
+
position: 'absolute',
|
|
2963
|
+
top: inlineTop,
|
|
2964
|
+
bottom: resolvedInlinePosition === 'up' ? '100%' : 'auto',
|
|
2965
|
+
left: inlineLeft,
|
|
2966
|
+
right: inlineRight,
|
|
2967
|
+
transform: inlineTransform,
|
|
2968
|
+
};
|
|
2969
|
+
const menuItems = (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) => {
|
|
2970
|
+
if (action.isDivider) {
|
|
2971
|
+
return jsx("hr", { className: "dropdown-divider" }, index);
|
|
2972
|
+
}
|
|
2973
|
+
return (jsx("li", { children: action.href ? (jsxs("a", { className: getItemClass(action), href: action.href, onClick: (e) => {
|
|
2974
|
+
if (action.disabled) {
|
|
2975
|
+
e.preventDefault();
|
|
2976
|
+
}
|
|
2977
|
+
else {
|
|
2978
|
+
setOpen(false);
|
|
2979
|
+
}
|
|
2980
|
+
}, children: [action.icon && jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) : (jsxs("button", { className: getItemClass(action), type: "button", onClick: () => {
|
|
2981
|
+
var _a;
|
|
2982
|
+
if (!action.disabled) {
|
|
2983
|
+
(_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action);
|
|
2984
|
+
setOpen(false);
|
|
2985
|
+
}
|
|
2986
|
+
}, disabled: action.disabled, children: [action.icon && jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) }, index));
|
|
2987
|
+
}) }));
|
|
2988
|
+
if (asPortal) {
|
|
2989
|
+
return (jsxs("div", { ref: toggleRef, className: classNames('dropdown', className), children: [ToggleElement, open && menuCoords && createPortal(menuItems, document.body)] }));
|
|
2990
|
+
}
|
|
2991
|
+
return (jsxs("div", { className: classNames(`dropdown drop-${resolvedInlinePosition}`, className), ref: dropdownRef, children: [ToggleElement, menuItems] }));
|
|
2690
2992
|
}
|
|
2691
2993
|
|
|
2692
2994
|
function useScreenshot() {
|
|
@@ -2902,28 +3204,109 @@ function render(renderable) {
|
|
|
2902
3204
|
return null;
|
|
2903
3205
|
return typeof renderable === 'function' ? renderable() : renderable;
|
|
2904
3206
|
}
|
|
2905
|
-
function DDataStateWrapper({ isLoading, isError, data, onRetry, renderLoading, renderEmpty, renderError, children, }) {
|
|
3207
|
+
function DDataStateWrapper({ isLoading, isError, data, onRetry, messages, renderLoading, renderEmpty, renderError, children, }) {
|
|
2906
3208
|
// 1. Loading
|
|
2907
3209
|
if (isLoading) {
|
|
2908
3210
|
if (renderLoading)
|
|
2909
3211
|
return render(renderLoading);
|
|
2910
|
-
return jsx(LoadingState, {});
|
|
3212
|
+
return jsx(LoadingState, { ariaLabel: messages === null || messages === void 0 ? void 0 : messages.loading });
|
|
2911
3213
|
}
|
|
2912
3214
|
// 2. Error
|
|
2913
3215
|
if (isError) {
|
|
2914
3216
|
if (renderError)
|
|
2915
3217
|
return render(renderError);
|
|
2916
|
-
return (jsx(ErrorState, { onRetry: onRetry }));
|
|
3218
|
+
return (jsx(ErrorState, { onRetry: onRetry, message: messages === null || messages === void 0 ? void 0 : messages.error, retryMessage: messages === null || messages === void 0 ? void 0 : messages.retry }));
|
|
2917
3219
|
}
|
|
2918
3220
|
// 3. Empty
|
|
2919
3221
|
if (!(data === null || data === void 0 ? void 0 : data.length)) {
|
|
2920
3222
|
if (renderEmpty)
|
|
2921
3223
|
return render(renderEmpty);
|
|
2922
|
-
return (jsx(EmptyState, {}));
|
|
3224
|
+
return (jsx(EmptyState, { message: messages === null || messages === void 0 ? void 0 : messages.empty }));
|
|
2923
3225
|
}
|
|
2924
3226
|
// 4. Success
|
|
2925
3227
|
return jsx(Fragment, { children: children(data) });
|
|
2926
3228
|
}
|
|
2927
3229
|
|
|
2928
|
-
|
|
3230
|
+
function DConfirmModalUI({ entry }) {
|
|
3231
|
+
const { title = 'Are you sure you want to proceed?', message = 'Please confirm if you want to continue with this action.', confirmLabel = 'Confirm', cancelLabel = 'Cancel', confirmColor = 'primary', critical, onConfirmAction, onCloseAction, } = entry;
|
|
3232
|
+
const [confirmationCode, setConfirmationCode] = useState('');
|
|
3233
|
+
const [internalLoading, setInternalLoading] = useState(false);
|
|
3234
|
+
const isMountedRef = useRef(true);
|
|
3235
|
+
useEffect(() => {
|
|
3236
|
+
const cleanup = () => {
|
|
3237
|
+
isMountedRef.current = false;
|
|
3238
|
+
};
|
|
3239
|
+
return cleanup;
|
|
3240
|
+
}, []);
|
|
3241
|
+
const handleClose = useCallback(() => {
|
|
3242
|
+
onCloseAction();
|
|
3243
|
+
}, [onCloseAction]);
|
|
3244
|
+
const isConfirmDisabled = Boolean(internalLoading
|
|
3245
|
+
|| (critical && confirmationCode !== critical.code));
|
|
3246
|
+
const handleConfirmClick = useCallback(() => {
|
|
3247
|
+
setInternalLoading(true);
|
|
3248
|
+
onConfirmAction()
|
|
3249
|
+
.catch(() => undefined)
|
|
3250
|
+
.finally(() => {
|
|
3251
|
+
// Only update state if the component is still mounted.
|
|
3252
|
+
// onConfirmAction() removes the entry on success, which unmounts this component.
|
|
3253
|
+
if (isMountedRef.current) {
|
|
3254
|
+
setInternalLoading(false);
|
|
3255
|
+
}
|
|
3256
|
+
});
|
|
3257
|
+
}, [onConfirmAction]);
|
|
3258
|
+
return (jsxs(DModal$1, { name: entry.id, size: "lg", className: `confirm-modal ${critical ? 'critical-modal' : ''}`, children: [jsx(DModal$1.Header, { onClose: handleClose, showCloseButton: true, children: jsx("h5", { className: "fw-bold", children: title }) }), jsxs(DModal$1.Body, { className: "py-3 px-5", children: [jsx("p", { className: "mb-4", children: message }), critical && (jsx(ForwardedDInput, { type: "text", label: critical.codeLabel || 'Confirmation code', placeholder: critical.inputPlaceholder, value: confirmationCode, onChange: (value) => setConfirmationCode(value), className: "mb-4", autoFocus: true }))] }), jsxs(DModal$1.Footer, { children: [jsx(DButton, { text: cancelLabel, variant: "outline", color: "secondary", onClick: handleClose, disabled: internalLoading }), jsx(DButton, { text: confirmLabel, color: confirmColor, onClick: handleConfirmClick, disabled: isConfirmDisabled, loading: internalLoading })] })] }));
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
/**
|
|
3262
|
+
* Container that renders confirm modals into the specified portal node.
|
|
3263
|
+
*
|
|
3264
|
+
* Must be explicitly mounted by the user (typically as a sibling of app content
|
|
3265
|
+
* within `DContextProvider`). The container floats above the portal stack and
|
|
3266
|
+
* intercepts Escape key events to close the top confirm modal without affecting
|
|
3267
|
+
* underlying modals or overlays.
|
|
3268
|
+
*
|
|
3269
|
+
* @example
|
|
3270
|
+
* <DContextProvider>
|
|
3271
|
+
* <App />
|
|
3272
|
+
* <DConfirmModalContainer nodeId="d-portal" />
|
|
3273
|
+
* </DContextProvider>
|
|
3274
|
+
*/
|
|
3275
|
+
function DConfirmModalContainer({ nodeId }) {
|
|
3276
|
+
const store = useConfirmModalStore();
|
|
3277
|
+
const [entries, setEntries] = useState([]);
|
|
3278
|
+
useEffect(() => {
|
|
3279
|
+
const unsubscribe = store.subscribe((next) => {
|
|
3280
|
+
setEntries(next);
|
|
3281
|
+
});
|
|
3282
|
+
return unsubscribe;
|
|
3283
|
+
}, [store]);
|
|
3284
|
+
// Capture Escape keydown to close the top confirm modal without affecting
|
|
3285
|
+
// the underlying portal stack (which also handles Escape).
|
|
3286
|
+
useEffect(() => {
|
|
3287
|
+
if (entries.length === 0) {
|
|
3288
|
+
return () => { };
|
|
3289
|
+
}
|
|
3290
|
+
const handleEscapeCapture = (event) => {
|
|
3291
|
+
if (event.key === 'Escape') {
|
|
3292
|
+
// Close the top (last) confirm modal
|
|
3293
|
+
entries[entries.length - 1].onCloseAction();
|
|
3294
|
+
// Prevent the event from reaching other handlers (e.g., DPortalContextProvider)
|
|
3295
|
+
event.stopPropagation();
|
|
3296
|
+
event.preventDefault();
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
document.addEventListener('keydown', handleEscapeCapture, true);
|
|
3300
|
+
return () => {
|
|
3301
|
+
document.removeEventListener('keydown', handleEscapeCapture, true);
|
|
3302
|
+
};
|
|
3303
|
+
}, [entries]);
|
|
3304
|
+
const portalNode = document.getElementById(nodeId);
|
|
3305
|
+
if (!portalNode || entries.length === 0) {
|
|
3306
|
+
return null;
|
|
3307
|
+
}
|
|
3308
|
+
return createPortal(jsx(AnimatePresence, { children: entries.map((entry) => (jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0, transition: { delay: 0.3 } }, transition: { duration: 0.15, ease: 'linear' }, children: [jsx("div", { className: "backdrop backdrop-confirm-modal", onClick: entry.onCloseAction, role: "presentation" }), jsx(DConfirmModalUI, { entry: entry })] }, entry.id))) }), portalNode);
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
export { DAlert, DAvatar, DBadge, DBox, DBoxFile, DButton, DButtonIcon, DCard$1 as DCard, DCardBody, DCardFooter, DCardHeader, DCarousel$1 as DCarousel, DCarouselSlide, DChip, DCollapse, DConfirmModalContainer, DContext, DContextProvider, DCreditCard, DCurrencyText, DDataStateWrapper, DDatePicker, DDropdown, DErrorBoundary, DIcon, DIconBase, ForwardedDInput as DInput, DInputCheck, ForwardedDInputCounter as DInputCounter, ForwardedDInputCurrency as DInputCurrency, ForwardedDInputMask as DInputMask, ForwardedDInputPassword as DInputPassword, ForwardedDInputPhone as DInputPhone, DInputPin, ForwardedDInputRange as DInputRange, ForwardedDInputSearch as DInputSearch, DInputSelect, DInputSwitch, DLayout$1 as DLayout, DLayoutPane, DListGroup$1 as DListGroup, DListGroupItem, DModal$1 as DModal, DModalBody, DModalFooter, DModalHeader, DOffcanvas$1 as DOffcanvas, DOffcanvasBody, DOffcanvasFooter, DOffcanvasHeader, DOtp, DPaginator, DPasswordStrengthMeter, DPopover, DProgress, DSelect$1 as DSelect, DStepper, DStepper$2 as DStepperDesktop, DStepper$1 as DStepperMobile, DTabContent, DTabs$1 as DTabs, DTimeline, DToast$1 as DToast, DToastContainer, DTooltip, DVoucher, EmptyState, ErrorState, LoadingState, changeQueryString, checkMediaQuery, configureI8n as configureI18n, formatCurrency, getCssVariable, getQueryString, subscribeToMediaQuery, useConfirmModal, useDContext, useDPortalContext, useDToast, useDisableBodyScrollEffect, useDisableInputWheel, useFormatCurrency, useInputCurrency, useItemSelection, useMediaBreakpointUpLg, useMediaBreakpointUpMd, useMediaBreakpointUpSm, useMediaBreakpointUpXl, useMediaBreakpointUpXs, useMediaBreakpointUpXxl, useMediaQuery, useProvidedRefOrCreate, useStackState, useTabContext, validatePhoneNumber };
|
|
2929
3312
|
//# sourceMappingURL=index.esm.js.map
|