@dynamic-framework/ui-react 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/dynamic-ui.css +278 -3
- package/dist/css/dynamic-ui.min.css +2 -2
- package/dist/index.esm.js +575 -284
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +577 -281
- package/dist/index.js.map +1 -1
- package/dist/types/components/DCarousel/DCarousel.d.ts +9 -2
- package/dist/types/components/DDropdown/DDropdown.d.ts +10 -2
- package/dist/types/components/DIcon/DIcon.d.ts +4 -4
- package/dist/types/components/DIconBase/DIconBase.d.ts +3 -2
- package/dist/types/components/DIconBase/index.d.ts +1 -0
- package/dist/types/components/DOffcanvas/DOffcanvas.d.ts +15 -3
- package/dist/types/components/DOtp/DOtp.d.ts +1 -0
- package/dist/types/components/DOtp/components/OtpCountdown.d.ts +3 -2
- package/dist/types/components/DVoucher/DVoucher.d.ts +3 -1
- package/dist/types/components/DVoucher/hooks/useScreenshotDownload.d.ts +1 -1
- package/dist/types/components/DVoucher/hooks/useScreenshotDownload.spec.d.ts +1 -0
- package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.d.ts +1 -1
- package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.spec.d.ts +1 -0
- package/dist/types/components/DVoucher/index.d.ts +3 -0
- package/dist/types/components/index.d.ts +1 -1
- package/dist/types/components/interface.d.ts +5 -3
- package/dist/types/contexts/DContext.d.ts +3 -2
- package/dist/types/hooks/index.d.ts +3 -0
- package/dist/types/hooks/tests/useCountdown.spec.d.ts +1 -0
- package/dist/types/hooks/useCountdown.d.ts +14 -0
- package/dist/types/hooks/useInputCurrency.d.ts +1 -6
- package/dist/types/hooks/useOtp.d.ts +98 -0
- package/package.json +3 -1
- package/src/style/abstracts/variables/_+import.scss +1 -0
- package/src/style/abstracts/variables/_input-currency.scss +1 -0
- package/src/style/abstracts/variables/_offcanvas.scss +8 -0
- package/src/style/components/_+import.scss +7 -0
- package/src/style/components/_d-box-file.scss +5 -0
- package/src/style/components/_d-carousel.scss +19 -1
- package/src/style/components/_d-close.scss +1 -0
- package/src/style/components/_d-confirm-modal.scss +17 -0
- package/src/style/components/_d-data-state-wrapper.scss +47 -0
- package/src/style/components/_d-datepicker.scss +11 -0
- package/src/style/components/_d-error-boundary.scss +7 -0
- package/src/style/components/_d-input-currency.scss +5 -0
- package/src/style/components/_d-otp.scss +54 -0
- package/src/style/components/_d-password-strength-meter.scss +76 -0
- package/src/style/components/_d-popover.scss +4 -0
- package/src/style/components/_d-stepper.scss +8 -0
- package/src/style/components/_d-tabs.scss +18 -0
- package/src/style/components/_d-voucher.scss +30 -0
- package/dist/types/components/DOtp/DOtpspec.d.ts +0 -0
- package/dist/types/components/DOtp/hooks/useCountdown.d.ts +0 -4
package/dist/index.esm.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import React, { useEffect, useState, useCallback, useMemo, useContext, createContext, useLayoutEffect, useSyncExternalStore, forwardRef, useId, useRef, isValidElement, cloneElement, createRef } from 'react';
|
|
3
|
+
import React, { useEffect, useState, useCallback, useMemo, useContext, createContext, useLayoutEffect, useSyncExternalStore, createElement, forwardRef, useId, useRef, isValidElement, cloneElement, createRef } from 'react';
|
|
4
4
|
import { __rest } from 'tslib';
|
|
5
5
|
import * as LucideIcons from 'lucide-react';
|
|
6
|
+
import { isValidElementType } from 'react-is';
|
|
6
7
|
import { createPortal } from 'react-dom';
|
|
7
8
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
8
9
|
import { fromEvent } from 'file-selector';
|
|
9
|
-
import { SplideSlide, Splide } from '@splidejs/react-splide';
|
|
10
|
+
import { SplideSlide, Splide, SplideTrack } from '@splidejs/react-splide';
|
|
10
11
|
import currency from 'currency.js';
|
|
11
12
|
import DatePicker from 'react-datepicker';
|
|
12
13
|
import { getYear, format, getMonth } from 'date-fns';
|
|
@@ -309,6 +310,7 @@ const DEFAULT_STATE = {
|
|
|
309
310
|
familyPrefix: 'bi-',
|
|
310
311
|
materialStyle: false,
|
|
311
312
|
},
|
|
313
|
+
iconRegistry: undefined,
|
|
312
314
|
iconMap: {
|
|
313
315
|
x: 'X',
|
|
314
316
|
xLg: 'X',
|
|
@@ -374,11 +376,12 @@ const DContext = createContext(DEFAULT_STATE);
|
|
|
374
376
|
* @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
|
|
375
377
|
* Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
|
|
376
378
|
*/
|
|
377
|
-
function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
|
|
379
|
+
function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconRegistry = DEFAULT_STATE.iconRegistry, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
|
|
378
380
|
const [internalContext, setInternalContext,] = useState({
|
|
379
381
|
language,
|
|
380
382
|
currency,
|
|
381
383
|
icon,
|
|
384
|
+
iconRegistry,
|
|
382
385
|
iconMap,
|
|
383
386
|
breakpoints: DEFAULT_STATE.breakpoints,
|
|
384
387
|
});
|
|
@@ -495,17 +498,21 @@ function useResponsiveProp(useListener = false) {
|
|
|
495
498
|
return { responsivePropValue };
|
|
496
499
|
}
|
|
497
500
|
|
|
501
|
+
function isIconComponent(value) {
|
|
502
|
+
return typeof value !== 'string' && isValidElementType(value);
|
|
503
|
+
}
|
|
498
504
|
function DIconBase({ icon, color, style, className, size, useListenerSize = false, hasCircle = false, materialStyle = false, familyClass, familyPrefix, strokeWidth = 2, dataAttributes, }) {
|
|
499
505
|
// If materialStyle is true, use Material Design icons (legacy)
|
|
500
|
-
const
|
|
506
|
+
const isStringIcon = typeof icon === 'string';
|
|
507
|
+
const useMaterialIcons = materialStyle && isStringIcon;
|
|
501
508
|
// Get Lucide icon component
|
|
502
509
|
const LucideIcon = useMemo(() => {
|
|
503
|
-
if (useMaterialIcons)
|
|
510
|
+
if (!isStringIcon || useMaterialIcons)
|
|
504
511
|
return null;
|
|
505
512
|
// Try to find the icon in Lucide (expects PascalCase)
|
|
506
513
|
const icons = LucideIcons;
|
|
507
514
|
return icons[icon] || null;
|
|
508
|
-
}, [icon, useMaterialIcons]);
|
|
515
|
+
}, [icon, isStringIcon, useMaterialIcons]);
|
|
509
516
|
const { responsivePropValue } = useResponsiveProp(useListenerSize);
|
|
510
517
|
const resolvedSize = useMemo(() => {
|
|
511
518
|
if (!size)
|
|
@@ -525,24 +532,35 @@ function DIconBase({ icon, color, style, className, size, useListenerSize = fals
|
|
|
525
532
|
}, [resolvedSize]);
|
|
526
533
|
// Render Material Design icon (legacy support)
|
|
527
534
|
if (useMaterialIcons) {
|
|
528
|
-
return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: icon })));
|
|
535
|
+
return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: isStringIcon ? icon : null })));
|
|
536
|
+
}
|
|
537
|
+
if (isIconComponent(icon)) {
|
|
538
|
+
return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: createElement(icon, {
|
|
539
|
+
width: resolvedSize || 24,
|
|
540
|
+
height: resolvedSize || 24,
|
|
541
|
+
strokeWidth,
|
|
542
|
+
}) })));
|
|
529
543
|
}
|
|
530
544
|
// Render Lucide icon
|
|
531
545
|
if (!LucideIcon) {
|
|
532
|
-
if (familyClass && familyPrefix) {
|
|
546
|
+
if (isStringIcon && familyClass && familyPrefix) {
|
|
533
547
|
return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass, `${familyPrefix}${icon}`), style: generateStyleVariables }, dataAttributes)));
|
|
534
548
|
}
|
|
535
549
|
// eslint-disable-next-line no-console
|
|
536
|
-
console.warn(`Icon "${icon}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
|
|
550
|
+
console.warn(`Icon "${String(icon)}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
|
|
537
551
|
return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: "?" })));
|
|
538
552
|
}
|
|
539
553
|
return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: jsx(LucideIcon, { size: iconSize || 24, strokeWidth: strokeWidth }) })));
|
|
540
554
|
}
|
|
541
555
|
|
|
542
556
|
function DIcon(_a) {
|
|
543
|
-
var { familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = __rest(_a, ["familyClass", "familyPrefix", "materialStyle"]);
|
|
544
|
-
const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
|
|
545
|
-
|
|
557
|
+
var { icon, familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = __rest(_a, ["icon", "familyClass", "familyPrefix", "materialStyle"]);
|
|
558
|
+
const { icon: { familyClass, familyPrefix, materialStyle, }, iconRegistry, } = useDContext();
|
|
559
|
+
const registryIcon = typeof icon === 'string'
|
|
560
|
+
? iconRegistry === null || iconRegistry === void 0 ? void 0 : iconRegistry[icon]
|
|
561
|
+
: undefined;
|
|
562
|
+
const resolvedIcon = registryIcon || icon;
|
|
563
|
+
return (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)));
|
|
546
564
|
}
|
|
547
565
|
|
|
548
566
|
function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle = false, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle = false, showClose, onClose, children, id, className, style, dataAttributes, }) {
|
|
@@ -1088,7 +1106,7 @@ function DBoxFile(_a) {
|
|
|
1088
1106
|
'd-box-file-invalid': isDragInvalid,
|
|
1089
1107
|
}, className), style: style }, dataAttributes, { children: 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: [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 && (jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })), jsx("div", { className: "d-box-content", children: typeof children === 'function'
|
|
1090
1108
|
? children(openFileDialog)
|
|
1091
|
-
: children || (jsx("p", { className: "
|
|
1109
|
+
: children || (jsx("p", { className: "d-box-file-empty-text", children: "Drag and drop some files here, or click to select files" })) })] })) })), !!files.length && (jsx("ul", { className: "d-box-files", children: files.map((file, index) => (jsx(ForwardedDInput, { value: file.name, iconStart: "Paperclip", iconEnd: "Trash", readOnly: true, onIconEndClick: () => handleRemoveFile(index) }, `${file.name} ${index}`))) }))] }));
|
|
1092
1110
|
}
|
|
1093
1111
|
|
|
1094
1112
|
const DButton = forwardRef((props, ref) => {
|
|
@@ -1222,8 +1240,14 @@ function DCarouselSlide(_a) {
|
|
|
1222
1240
|
return (jsx(SplideSlide, Object.assign({ className: classNames('d-carousel-slide', className) }, props)));
|
|
1223
1241
|
}
|
|
1224
1242
|
|
|
1243
|
+
const DEFAULT_ARROW_PATH = 'm15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z';
|
|
1244
|
+
function DefaultArrowIcon() {
|
|
1245
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 40 40", width: 40, height: 40, focusable: "false", "aria-hidden": "true", children: jsx("path", { d: DEFAULT_ARROW_PATH }) }));
|
|
1246
|
+
}
|
|
1225
1247
|
function DCarousel(_a, ref) {
|
|
1226
|
-
var { children, className, style, options, dataAttributes } = _a, props = __rest(_a, ["children", "className", "style", "options", "dataAttributes"]);
|
|
1248
|
+
var { children, className, style, options, dataAttributes, iconArrowLeft, iconArrowRight, hasTrack: propsHasTrack } = _a, props = __rest(_a, ["children", "className", "style", "options", "dataAttributes", "iconArrowLeft", "iconArrowRight", "hasTrack"]);
|
|
1249
|
+
// Explicit `options.arrows === false` always wins, even when icon props are set.
|
|
1250
|
+
const hasCustomArrows = Boolean((iconArrowLeft || iconArrowRight) && (options === null || options === void 0 ? void 0 : options.arrows) !== false);
|
|
1227
1251
|
return (jsx(Splide, Object.assign({ className: classNames('d-carousel', className), style: style, ref: ref, options: Object.assign(Object.assign({}, options), { classes: {
|
|
1228
1252
|
// Arrows
|
|
1229
1253
|
arrows: 'splide__arrows d-carousel-arrows',
|
|
@@ -1233,7 +1257,11 @@ function DCarousel(_a, ref) {
|
|
|
1233
1257
|
// Paginator
|
|
1234
1258
|
pagination: 'splide__pagination d-carousel-pagination',
|
|
1235
1259
|
page: 'splide__pagination__page d-carousel-pagination-page',
|
|
1236
|
-
} }) }, dataAttributes, props, {
|
|
1260
|
+
} }) }, dataAttributes, props, {
|
|
1261
|
+
// Rendering our own arrows requires taking over the track wrapper, so this
|
|
1262
|
+
// must be applied after `...props` to prevent a consumer-provided `hasTrack`
|
|
1263
|
+
// from re-enabling Splide's automatic track when custom arrows are active.
|
|
1264
|
+
hasTrack: hasCustomArrows ? false : propsHasTrack, children: hasCustomArrows ? (jsxs(Fragment, { children: [jsx(SplideTrack, { children: children }), jsxs("div", { className: "splide__arrows d-carousel-arrows", children: [jsx("button", { type: "button", className: "splide__arrow splide__arrow--prev d-carousel-arrow d-carousel-arrow-prev", children: iconArrowLeft ? jsx(DIcon, Object.assign({}, iconArrowLeft)) : jsx(DefaultArrowIcon, {}) }), jsx("button", { type: "button", className: "splide__arrow splide__arrow--next d-carousel-arrow d-carousel-arrow-next", children: iconArrowRight ? jsx(DIcon, Object.assign({}, iconArrowRight)) : jsx(DefaultArrowIcon, {}) })] })] })) : children })));
|
|
1237
1265
|
}
|
|
1238
1266
|
const ForwardedDCarousel = forwardRef(DCarousel);
|
|
1239
1267
|
ForwardedDCarousel.displayName = 'DCarousel';
|
|
@@ -1454,9 +1482,9 @@ function DDatePickerHeaderSelector({ date, changeYear, changeMonth, decreaseMont
|
|
|
1454
1482
|
return (jsxs("div", { className: classNames('react-datepicker__header-selector react-datepicker__header-year-selector', className), style: style, children: [jsx(DButtonIcon, { icon: iconPrev || chevronLeft, size: iconSize, variant: "link", onClick: decreaseYear, disabled: prevYearButtonDisabled, "aria-label": prevYearAriaLabel, className: "header-button" }), jsx("p", { children: `${startYear} - ${endYear}` }), jsx(DButtonIcon, { icon: iconNext || chevronRight, size: iconSize, variant: "link", onClick: increaseYear, disabled: nextYearButtonDisabled, "aria-label": nextYearAriaLabel, className: "header-button" })] }));
|
|
1455
1483
|
}
|
|
1456
1484
|
if (pickerType === PickerType.Quarter || pickerType === PickerType.Month) {
|
|
1457
|
-
return (jsxs("div", { className: classNames(`react-datepicker__header-selector react-datepicker__header-${pickerType}-selector`, className), style: style, children: [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' } }), jsx("div", { className: "
|
|
1485
|
+
return (jsxs("div", { className: classNames(`react-datepicker__header-selector react-datepicker__header-${pickerType}-selector`, className), style: style, children: [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' } }), jsx("div", { className: "react-datepicker__header-year-selector", children: showHeaderSelectors ? (jsx(DSelect$1, { options: years, value: defaultYear, defaultValue: defaultYear, onChange: (year) => changeYear(Number(year === null || year === void 0 ? void 0 : year.value)), searchable: false })) : (jsx("p", { children: defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label })) }), 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' } })] }));
|
|
1458
1486
|
}
|
|
1459
|
-
return (jsxs(Fragment, { children: [jsxs("div", { className: "datepicker-top-header", children: [showHeaderSelectors && (jsx(DSelect$1, { 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" })), jsx("h4", { className: "
|
|
1487
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: "datepicker-top-header", children: [showHeaderSelectors && (jsx(DSelect$1, { 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" })), jsx("h4", { className: "datepicker-top-header-title", children: format(monthDate, formatHeaderDate, { locale }) })] }), jsxs("div", { className: classNames('react-datepicker__header-selector react-datepicker__header-day-selector', className), style: style, children: [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 ? (jsx(DSelect$1, { 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" })) : (jsx("p", { children: `${defaultMonth.label} ${defaultYear === null || defaultYear === void 0 ? void 0 : defaultYear.label}` })), 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' } })] })] }));
|
|
1460
1488
|
}
|
|
1461
1489
|
|
|
1462
1490
|
function DDatePicker(_a) {
|
|
@@ -1591,11 +1619,24 @@ function formatValue(value, currencyOptions) {
|
|
|
1591
1619
|
}
|
|
1592
1620
|
return currency(value, Object.assign(Object.assign({}, currencyOptions), { symbol: '' })).format();
|
|
1593
1621
|
}
|
|
1594
|
-
function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref) {
|
|
1622
|
+
function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue) {
|
|
1595
1623
|
const inputRef = useProvidedRefOrCreate(ref);
|
|
1624
|
+
const clampValue = useCallback((newValue) => {
|
|
1625
|
+
if (newValue === undefined) {
|
|
1626
|
+
return newValue;
|
|
1627
|
+
}
|
|
1628
|
+
let clampedValue = newValue;
|
|
1629
|
+
if (minValue !== undefined) {
|
|
1630
|
+
clampedValue = Math.max(clampedValue, minValue);
|
|
1631
|
+
}
|
|
1632
|
+
if (maxValue !== undefined) {
|
|
1633
|
+
clampedValue = Math.min(clampedValue, maxValue);
|
|
1634
|
+
}
|
|
1635
|
+
return clampedValue;
|
|
1636
|
+
}, [minValue, maxValue]);
|
|
1596
1637
|
const [innerType, setInnerType] = useState('text');
|
|
1597
|
-
const [innerNumber, setInnerNumber] = useState(value);
|
|
1598
|
-
const [innerString, setInnerString] = useState(formatValue(value, currencyOptions));
|
|
1638
|
+
const [innerNumber, setInnerNumber] = useState(clampValue(value));
|
|
1639
|
+
const [innerString, setInnerString] = useState(formatValue(clampValue(value), currencyOptions));
|
|
1599
1640
|
const handleOnFocus = useCallback((event) => {
|
|
1600
1641
|
event.stopPropagation();
|
|
1601
1642
|
setInnerType('number');
|
|
@@ -1604,15 +1645,14 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
|
|
|
1604
1645
|
const handleOnBlur = useCallback((event) => {
|
|
1605
1646
|
event.stopPropagation();
|
|
1606
1647
|
setInnerType('text');
|
|
1648
|
+
const clampedNumber = clampValue(innerNumber);
|
|
1649
|
+
if (clampedNumber !== innerNumber) {
|
|
1650
|
+
setInnerNumber(clampedNumber);
|
|
1651
|
+
setInnerString(formatValue(clampedNumber, currencyOptions));
|
|
1652
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(clampedNumber);
|
|
1653
|
+
}
|
|
1607
1654
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
1608
|
-
}, [onBlur]);
|
|
1609
|
-
const generateStyleVariables = useMemo(() => ({
|
|
1610
|
-
[`--${PREFIX_BS}input-currency-component-symbol-color`]: `var(--${PREFIX_BS}secondary)`,
|
|
1611
|
-
[`--${PREFIX_BS}input-currency-symbol-color`]: `var(--${PREFIX_BS}input-currency-component-symbol-color)`,
|
|
1612
|
-
}), []);
|
|
1613
|
-
const generateSymbolStyleVariables = useMemo(() => ({
|
|
1614
|
-
color: `var(--${PREFIX_BS}input-currency-symbol-color)`,
|
|
1615
|
-
}), []);
|
|
1655
|
+
}, [onBlur, innerNumber, clampValue, currencyOptions, onChange]);
|
|
1616
1656
|
const handleOnChange = useCallback((newValue) => {
|
|
1617
1657
|
const newNumber = (newValue === undefined || newValue === '') ? undefined : Number(newValue);
|
|
1618
1658
|
if (newNumber !== innerNumber) {
|
|
@@ -1621,7 +1661,12 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
|
|
|
1621
1661
|
onChange === null || onChange === void 0 ? void 0 : onChange(newNumber);
|
|
1622
1662
|
}
|
|
1623
1663
|
}, [currencyOptions, onChange, innerNumber]);
|
|
1664
|
+
const isMountedRef = useRef(false);
|
|
1624
1665
|
useEffect(() => {
|
|
1666
|
+
if (!isMountedRef.current) {
|
|
1667
|
+
isMountedRef.current = true;
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1625
1670
|
if (value !== innerNumber) {
|
|
1626
1671
|
setInnerNumber(value);
|
|
1627
1672
|
setInnerString(formatValue(value, currencyOptions));
|
|
@@ -1635,8 +1680,6 @@ function useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref
|
|
|
1635
1680
|
handleOnFocus,
|
|
1636
1681
|
handleOnChange,
|
|
1637
1682
|
handleOnBlur,
|
|
1638
|
-
generateStyleVariables,
|
|
1639
|
-
generateSymbolStyleVariables,
|
|
1640
1683
|
};
|
|
1641
1684
|
}
|
|
1642
1685
|
|
|
@@ -1741,6 +1784,149 @@ function useConfirmModal(config) {
|
|
|
1741
1784
|
return { open };
|
|
1742
1785
|
}
|
|
1743
1786
|
|
|
1787
|
+
/**
|
|
1788
|
+
* Generic seconds-based countdown hook.
|
|
1789
|
+
*
|
|
1790
|
+
* Starts counting down from `seconds` to `0` and exposes `restartCountdown`
|
|
1791
|
+
* to reset and restart the timer (e.g. for "resend code in Xs" patterns,
|
|
1792
|
+
* session-expiry banners, or any other cooldown/throttle UI).
|
|
1793
|
+
*
|
|
1794
|
+
* @example
|
|
1795
|
+
* const { secondsLeft, restartCountdown } = useCountdown(30);
|
|
1796
|
+
*/
|
|
1797
|
+
function useCountdown(seconds) {
|
|
1798
|
+
const [secondsLeft, setSecondsLeft] = useState(seconds);
|
|
1799
|
+
const [isActive, setIsActive] = useState(true);
|
|
1800
|
+
const resetCountdown = useCallback((newSeconds = seconds) => {
|
|
1801
|
+
setIsActive(false);
|
|
1802
|
+
setSecondsLeft(newSeconds);
|
|
1803
|
+
}, [seconds]);
|
|
1804
|
+
const restartCountdown = useCallback(() => {
|
|
1805
|
+
resetCountdown(seconds);
|
|
1806
|
+
setIsActive(true);
|
|
1807
|
+
}, [resetCountdown, seconds]);
|
|
1808
|
+
useEffect(() => {
|
|
1809
|
+
if (!isActive) {
|
|
1810
|
+
return () => { };
|
|
1811
|
+
}
|
|
1812
|
+
const interval = setInterval(() => {
|
|
1813
|
+
setSecondsLeft((prevSeconds) => {
|
|
1814
|
+
const newSeconds = prevSeconds - 1;
|
|
1815
|
+
if (newSeconds <= 0) {
|
|
1816
|
+
clearInterval(interval);
|
|
1817
|
+
setIsActive(false);
|
|
1818
|
+
return 0;
|
|
1819
|
+
}
|
|
1820
|
+
return newSeconds;
|
|
1821
|
+
});
|
|
1822
|
+
}, 1000);
|
|
1823
|
+
return () => clearInterval(interval);
|
|
1824
|
+
}, [isActive]);
|
|
1825
|
+
return { secondsLeft, restartCountdown };
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Headless hook exposing the same OTP logic used internally by `DOtp`
|
|
1830
|
+
* (code state, length + correctness validation, submit action and resend
|
|
1831
|
+
* countdown), without any bundled markup/styles.
|
|
1832
|
+
*
|
|
1833
|
+
* Use this when the default `DOtp` component doesn't match your design and
|
|
1834
|
+
* you need to build a fully custom UI while reusing the same behavior.
|
|
1835
|
+
*
|
|
1836
|
+
* Internally composes `useCountdown` for the resend timer and re-exposes
|
|
1837
|
+
* `secondsLeft`/`restartCountdown`, so there is no need to call
|
|
1838
|
+
* `useCountdown` separately.
|
|
1839
|
+
*
|
|
1840
|
+
* `invalid` becomes `true` both when the OTP is shorter than `otpSize` and
|
|
1841
|
+
* when `action` throws/rejects (e.g. the backend rejected the entered code
|
|
1842
|
+
* as incorrect). In the latter case, the thrown value is exposed as `error`
|
|
1843
|
+
* so custom UIs can render an appropriate message (e.g. "Invalid code" vs.
|
|
1844
|
+
* "Enter all digits"). `invalid`/`error` are cleared automatically as soon
|
|
1845
|
+
* as the user edits the code via `setOtp`.
|
|
1846
|
+
*
|
|
1847
|
+
* @example
|
|
1848
|
+
* function CustomOtp() {
|
|
1849
|
+
* const {
|
|
1850
|
+
* otp,
|
|
1851
|
+
* setOtp,
|
|
1852
|
+
* invalid,
|
|
1853
|
+
* error,
|
|
1854
|
+
* submit,
|
|
1855
|
+
* isLoading,
|
|
1856
|
+
* secondsLeft,
|
|
1857
|
+
* restartCountdown,
|
|
1858
|
+
* } = useOtp({ action: async () => verifyOtp(otp), otpSize: 6, seconds: 15 });
|
|
1859
|
+
*
|
|
1860
|
+
* return (
|
|
1861
|
+
* <>
|
|
1862
|
+
* <MyCustomPinInput value={otp} onChange={setOtp} invalid={invalid} />
|
|
1863
|
+
* {invalid && <span>{error ? 'Invalid code' : 'Enter all digits'}</span>}
|
|
1864
|
+
* <button disabled={isLoading} onClick={() => { submit().catch(() => {}); }}>
|
|
1865
|
+
* {isLoading ? 'Submitting...' : 'Submit'}
|
|
1866
|
+
* </button>
|
|
1867
|
+
* <button disabled={secondsLeft > 0} onClick={restartCountdown}>
|
|
1868
|
+
* Resend {secondsLeft > 0 ? `(${secondsLeft}s)` : ''}
|
|
1869
|
+
* </button>
|
|
1870
|
+
* </>
|
|
1871
|
+
* );
|
|
1872
|
+
* }
|
|
1873
|
+
*/
|
|
1874
|
+
function useOtp({ action, otpSize = 6, seconds = 15, }) {
|
|
1875
|
+
const [otp, setOtpValue] = useState('');
|
|
1876
|
+
const [invalid, setInvalid] = useState(false);
|
|
1877
|
+
const [error, setError] = useState(null);
|
|
1878
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1879
|
+
const { secondsLeft, restartCountdown } = useCountdown(seconds);
|
|
1880
|
+
const setOtp = useCallback((value) => {
|
|
1881
|
+
setOtpValue((prevOtp) => {
|
|
1882
|
+
// Guard against no-op calls (e.g. DInputPin re-notifying the same
|
|
1883
|
+
// value when its onChange reference changes) so we don't wipe out
|
|
1884
|
+
// an `invalid`/`error` state that was just set by submit().
|
|
1885
|
+
if (prevOtp === value) {
|
|
1886
|
+
return prevOtp;
|
|
1887
|
+
}
|
|
1888
|
+
setInvalid(false);
|
|
1889
|
+
setError(null);
|
|
1890
|
+
return value;
|
|
1891
|
+
});
|
|
1892
|
+
}, []);
|
|
1893
|
+
const submit = useCallback(async () => {
|
|
1894
|
+
if (otp.length < otpSize) {
|
|
1895
|
+
setInvalid(true);
|
|
1896
|
+
setError(null);
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
setIsLoading(true);
|
|
1900
|
+
try {
|
|
1901
|
+
await action();
|
|
1902
|
+
setInvalid(false);
|
|
1903
|
+
setError(null);
|
|
1904
|
+
}
|
|
1905
|
+
catch (err) {
|
|
1906
|
+
setInvalid(true);
|
|
1907
|
+
setError(err);
|
|
1908
|
+
throw err;
|
|
1909
|
+
}
|
|
1910
|
+
finally {
|
|
1911
|
+
setIsLoading(false);
|
|
1912
|
+
}
|
|
1913
|
+
}, [
|
|
1914
|
+
otp.length,
|
|
1915
|
+
action,
|
|
1916
|
+
otpSize,
|
|
1917
|
+
]);
|
|
1918
|
+
return {
|
|
1919
|
+
otp,
|
|
1920
|
+
setOtp,
|
|
1921
|
+
invalid,
|
|
1922
|
+
error,
|
|
1923
|
+
submit,
|
|
1924
|
+
isLoading,
|
|
1925
|
+
secondsLeft,
|
|
1926
|
+
restartCountdown,
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1744
1930
|
function DInputCounter(_a, ref) {
|
|
1745
1931
|
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"]);
|
|
1746
1932
|
const { handleOnWheel, } = useDisableInputWheel(ref);
|
|
@@ -1783,8 +1969,8 @@ function DInputCurrency(_a, ref) {
|
|
|
1783
1969
|
var { value, minValue, maxValue, currencyCode, onFocus, onBlur, onChange } = _a, props = __rest(_a, ["value", "minValue", "maxValue", "currencyCode", "onFocus", "onBlur", "onChange"]);
|
|
1784
1970
|
const { currency: currencyOptions } = useDContext();
|
|
1785
1971
|
const { handleOnWheel, } = useDisableInputWheel(ref);
|
|
1786
|
-
const { inputRef, innerValue, innerType, handleOnFocus, handleOnChange, handleOnBlur,
|
|
1787
|
-
return (jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange,
|
|
1972
|
+
const { inputRef, innerValue, innerType, handleOnFocus, handleOnChange, handleOnBlur, } = useInputCurrency(currencyOptions, value, onFocus, onChange, onBlur, ref, minValue, maxValue);
|
|
1973
|
+
return (jsx(ForwardedDInput, Object.assign({ ref: inputRef, value: innerValue, onChange: handleOnChange, inputMode: "decimal", type: innerType, onFocus: handleOnFocus, onBlur: handleOnBlur, onWheel: handleOnWheel, inputStart: (jsx("span", { slot: "input-start", className: "d-input-currency-symbol", children: currencyCode || currencyOptions.symbol })) }, props)));
|
|
1788
1974
|
}
|
|
1789
1975
|
const ForwardedDInputCurrency = forwardRef(DInputCurrency);
|
|
1790
1976
|
ForwardedDInputCurrency.displayName = 'DInputCurrency';
|
|
@@ -1806,26 +1992,25 @@ ForwardedDInputPassword.displayName = 'DInputPassword';
|
|
|
1806
1992
|
|
|
1807
1993
|
function PasswordCheckItem({ password, regex, text, }) {
|
|
1808
1994
|
const isValid = regex.test(password);
|
|
1809
|
-
return (jsxs("li", { className:
|
|
1995
|
+
return (jsxs("li", { className: `d-password-check-item${isValid ? ' is-valid' : ''}`, children: [jsx(DIcon, { className: "d-password-check-item-icon", icon: isValid ? 'CircleCheck' : 'Circle', size: "16px" }), jsx("span", { children: text })] }));
|
|
1810
1996
|
}
|
|
1811
1997
|
|
|
1812
|
-
const
|
|
1998
|
+
const getStrengthModifier = (strength, total) => {
|
|
1813
1999
|
const percentage = total > 0 ? strength / total : 0;
|
|
1814
2000
|
if (percentage === 0)
|
|
1815
|
-
return '
|
|
2001
|
+
return 'is-empty';
|
|
1816
2002
|
if (percentage <= 0.25)
|
|
1817
|
-
return '
|
|
2003
|
+
return 'is-weak';
|
|
1818
2004
|
if (percentage <= 0.5)
|
|
1819
|
-
return '
|
|
2005
|
+
return 'is-fair';
|
|
1820
2006
|
if (percentage <= 0.75)
|
|
1821
|
-
return '
|
|
1822
|
-
return '
|
|
2007
|
+
return 'is-good';
|
|
2008
|
+
return 'is-strong';
|
|
1823
2009
|
};
|
|
1824
2010
|
function PasswordStrengthBar({ strength, total }) {
|
|
1825
2011
|
const percentage = total > 0 ? (strength / total) * 100 : 0;
|
|
1826
|
-
return (jsx("div", { className: "
|
|
2012
|
+
return (jsx("div", { className: "d-password-strength-bar", children: jsx("div", { className: `d-password-strength-bar-fill ${getStrengthModifier(strength, total)}`, style: {
|
|
1827
2013
|
width: `${percentage}%`,
|
|
1828
|
-
transition: 'width 0.3s ease-in-out',
|
|
1829
2014
|
} }) }));
|
|
1830
2015
|
}
|
|
1831
2016
|
|
|
@@ -1861,7 +2046,7 @@ function PasswordChecksList({ password, validationMessages, enabledChecks, }) {
|
|
|
1861
2046
|
const passwordChecks = allChecks.filter((check) => enabledChecks.includes(check.key));
|
|
1862
2047
|
const passed = passwordChecks.filter((r) => r.regex.test(password)).length;
|
|
1863
2048
|
const total = passwordChecks.length;
|
|
1864
|
-
return (jsxs("div", { className: "
|
|
2049
|
+
return (jsxs("div", { className: "d-password-strength-meter", children: [jsx(PasswordStrengthBar, { strength: passed, total: total }), jsx("ul", { className: "d-password-check-list", children: passwordChecks.map(({ key, regex, text }) => (jsx(PasswordCheckItem, { password: password, regex: regex, text: text }, key))) })] }));
|
|
1865
2050
|
}
|
|
1866
2051
|
|
|
1867
2052
|
const DEFAULT_VALIDATION_MESSAGES = {
|
|
@@ -2211,7 +2396,7 @@ var DListGroup$1 = Object.assign(DListGroup, {
|
|
|
2211
2396
|
function DModalHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, materialStyle = false, }) {
|
|
2212
2397
|
const { iconMap: { xLg, }, } = useDContext();
|
|
2213
2398
|
const icon = useMemo(() => iconProp || xLg, [iconProp, xLg]);
|
|
2214
|
-
return (jsxs(Fragment, { children: [jsxs("div", { className: classNames('modal-header', className), style: style, children: [jsx("div", { children: children }), showCloseButton && (jsx("button", { type: "button", className: "d-close
|
|
2399
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: classNames('modal-header', className), style: style, children: [jsx("div", { children: children }), showCloseButton && (jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: materialStyle }) }))] }), jsx("div", { className: "d-modal-separator" })] }));
|
|
2215
2400
|
}
|
|
2216
2401
|
|
|
2217
2402
|
function DModalBody({ children, className, style, }) {
|
|
@@ -2255,7 +2440,7 @@ var DModal$1 = Object.assign(DModal, {
|
|
|
2255
2440
|
function DOffcanvasHeader({ showCloseButton, onClose, children, className, style, iconFamilyClass, iconFamilyPrefix, icon: iconProp, materialStyle = false, }) {
|
|
2256
2441
|
const { iconMap: { xLg, }, } = useDContext();
|
|
2257
2442
|
const icon = useMemo(() => iconProp || xLg, [iconProp, xLg]);
|
|
2258
|
-
return (jsxs(Fragment, { children: [jsxs("div", { className: classNames('offcanvas-header', className), style: style, children: [jsx("div", { children: children }), showCloseButton && (jsx("button", { type: "button", className: "d-close
|
|
2443
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: classNames('offcanvas-header', className), style: style, children: [jsx("div", { children: children }), showCloseButton && (jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: onClose, children: jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: materialStyle }) }))] }), jsx("div", { className: "d-offcanvas-separator" })] }));
|
|
2259
2444
|
}
|
|
2260
2445
|
|
|
2261
2446
|
function DOffcanvasBody({ children, className, style, }) {
|
|
@@ -2296,10 +2481,37 @@ const defaultTransition = {
|
|
|
2296
2481
|
ease: 'easeInOut',
|
|
2297
2482
|
duration: 0.3,
|
|
2298
2483
|
};
|
|
2299
|
-
function DOffcanvas({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', transition, children, dataAttributes, }) {
|
|
2484
|
+
function DOffcanvas({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', width, height, transition, children, dataAttributes, }) {
|
|
2485
|
+
// Only subscribe to breakpoint-change listeners when a responsive object is
|
|
2486
|
+
// actually provided, avoiding unnecessary matchMedia subscriptions for the
|
|
2487
|
+
// common case of plain string values.
|
|
2488
|
+
const hasResponsiveProp = typeof openFrom === 'object'
|
|
2489
|
+
|| typeof width === 'object'
|
|
2490
|
+
|| typeof height === 'object';
|
|
2491
|
+
const { responsivePropValue } = useResponsiveProp(hasResponsiveProp);
|
|
2492
|
+
const resolvedOpenFrom = useMemo(() => {
|
|
2493
|
+
var _a;
|
|
2494
|
+
if (typeof openFrom === 'string')
|
|
2495
|
+
return openFrom;
|
|
2496
|
+
return (_a = responsivePropValue(openFrom)) !== null && _a !== void 0 ? _a : 'end';
|
|
2497
|
+
}, [responsivePropValue, openFrom]);
|
|
2498
|
+
const resolvedWidth = useMemo(() => {
|
|
2499
|
+
if (!width)
|
|
2500
|
+
return undefined;
|
|
2501
|
+
if (typeof width === 'string')
|
|
2502
|
+
return width;
|
|
2503
|
+
return responsivePropValue(width);
|
|
2504
|
+
}, [responsivePropValue, width]);
|
|
2505
|
+
const resolvedHeight = useMemo(() => {
|
|
2506
|
+
if (!height)
|
|
2507
|
+
return undefined;
|
|
2508
|
+
if (typeof height === 'string')
|
|
2509
|
+
return height;
|
|
2510
|
+
return responsivePropValue(height);
|
|
2511
|
+
}, [responsivePropValue, height]);
|
|
2300
2512
|
return (jsx(motion.div, Object.assign({ className: classNames('offcanvas portal show', {
|
|
2301
|
-
[`offcanvas-${
|
|
2302
|
-
}, className), style: Object.assign(Object.assign({}, style), { transition: 'none' }), id: name, tabIndex: -1, "aria-labelledby": `${name}Label`, "aria-hidden": "false", custom:
|
|
2513
|
+
[`offcanvas-${resolvedOpenFrom}`]: resolvedOpenFrom,
|
|
2514
|
+
}, className), style: Object.assign(Object.assign(Object.assign(Object.assign({}, style), { transition: 'none' }), (resolvedWidth && { [`--${PREFIX_BS}offcanvas-width`]: resolvedWidth })), (resolvedHeight && { [`--${PREFIX_BS}offcanvas-height`]: resolvedHeight })), id: name, tabIndex: -1, "aria-labelledby": `${name}Label`, "aria-hidden": "false", custom: resolvedOpenFrom, variants: variants, initial: "hidden", animate: "visible", exit: "hidden", transition: Object.assign(Object.assign({}, (transition !== null && transition !== void 0 ? transition : defaultTransition)), { delay: 0.15 }) }, staticBackdrop && ({
|
|
2303
2515
|
[`data-${PREFIX_BS}backdrop`]: 'static',
|
|
2304
2516
|
[`data-${PREFIX_BS}keyboard`]: 'false',
|
|
2305
2517
|
}), scrollable && ({
|
|
@@ -2355,7 +2567,7 @@ function DPopover({ children, renderComponent, open, setOpen, adjustContentToRen
|
|
|
2355
2567
|
}
|
|
2356
2568
|
const reference = cloneElement(triggerElement, Object.assign({ ref: refs.setReference }, getReferenceProps(triggerElement.props)));
|
|
2357
2569
|
return (jsxs("div", Object.assign({ className: classNames('d-popover', className), style: generateStyleVariables }, dataAttributes, { children: [reference, isOpen && (jsx(FloatingFocusManager, { context: context, modal: false, children: jsx("div", Object.assign({ className: classNames('d-popover-content', {
|
|
2358
|
-
'
|
|
2570
|
+
'd-popover-content-fill': adjustContentToRender,
|
|
2359
2571
|
}), ref: refs.setFloating, style: floatingStyles, "aria-labelledby": headingId }, getFloatingProps(), { children: children })) }))] })));
|
|
2360
2572
|
}
|
|
2361
2573
|
|
|
@@ -2419,13 +2631,13 @@ function DStepper$1({ options, currentStep, className, style, }) {
|
|
|
2419
2631
|
}
|
|
2420
2632
|
|
|
2421
2633
|
function DStepper({ options, currentStep, iconSuccess, iconSuccessFamilyClass, iconSuccessFamilyPrefix, iconSuccessMaterialStyle = false, vertical = false, breakpoint = 'lg', className, completed = false, style, dataAttributes, }) {
|
|
2422
|
-
return (jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsx("div", { className: `d-block d-${breakpoint}-none
|
|
2634
|
+
return (jsxs("div", Object.assign({ className: className, style: style }, dataAttributes, { children: [jsx("div", { className: classNames('d-stepper-mobile', `d-block d-${breakpoint}-none`), children: jsx(DStepper$1, { options: options, currentStep: currentStep }) }), jsx("div", { className: classNames('d-stepper-desktop', `d-none d-${breakpoint}-block`), children: jsx(DStepper$2, { options: options, currentStep: currentStep, vertical: vertical, iconSuccess: iconSuccess, iconSuccessFamilyClass: iconSuccessFamilyClass, iconSuccessFamilyPrefix: iconSuccessFamilyPrefix, iconSuccessMaterialStyle: iconSuccessMaterialStyle, completed: completed }) })] })));
|
|
2423
2635
|
}
|
|
2424
2636
|
|
|
2425
2637
|
const ARROW_WIDTH = 8;
|
|
2426
2638
|
const ARROW_HEIGHT = 4;
|
|
2427
|
-
const GAP = 2;
|
|
2428
|
-
function DTooltip({ className, childrenClassName, style, offSet = ARROW_HEIGHT + GAP, padding, withFocus = false, withClick = false, withHover = true, open = false, placement = 'top', size, Component, children, }) {
|
|
2639
|
+
const GAP$1 = 2;
|
|
2640
|
+
function DTooltip({ className, childrenClassName, style, offSet = ARROW_HEIGHT + GAP$1, padding, withFocus = false, withClick = false, withHover = true, open = false, placement = 'top', size, Component, children, }) {
|
|
2429
2641
|
const [isOpen, setIsOpen] = useState(open);
|
|
2430
2642
|
const arrowRef = useRef(null);
|
|
2431
2643
|
const { refs, context, floatingStyles, } = useFloating({
|
|
@@ -2494,8 +2706,16 @@ function DTabs({ children, defaultSelected, onChange, options, className, classN
|
|
|
2494
2706
|
useEffect(() => {
|
|
2495
2707
|
setSelected(defaultSelected);
|
|
2496
2708
|
}, [defaultSelected]);
|
|
2497
|
-
const generateClasses = useMemo(() => (Object.assign({ nav: true, '
|
|
2709
|
+
const generateClasses = useMemo(() => (Object.assign({ nav: true, 'd-tabs-nav-vertical': vertical && variant !== 'tabs', [`nav-${variant}`]: true }, className && { [className]: true })), [vertical, variant, className]);
|
|
2498
2710
|
const tabRefs = useRef([]);
|
|
2711
|
+
// Always holds the latest `options` without needing to be a dependency:
|
|
2712
|
+
// `options` is commonly passed as an inline array literal (e.g.
|
|
2713
|
+
// `options={[{ label: 'SMS', tab: 'sms' }, ...]}`), so it's a new array
|
|
2714
|
+
// reference on every parent render even when its content hasn't changed.
|
|
2715
|
+
// Reading it from this ref (updated synchronously on every render) lets
|
|
2716
|
+
// the focus effect below react only to `selected` changing.
|
|
2717
|
+
const optionsRef = useRef(options);
|
|
2718
|
+
optionsRef.current = options;
|
|
2499
2719
|
useEffect(() => {
|
|
2500
2720
|
tabRefs.current = options.map((_, i) => tabRefs.current[i] || createRef());
|
|
2501
2721
|
}, [options]);
|
|
@@ -2510,20 +2730,28 @@ function DTabs({ children, defaultSelected, onChange, options, className, classN
|
|
|
2510
2730
|
setSelected(firstEnabled.tab);
|
|
2511
2731
|
}
|
|
2512
2732
|
}, [options, selected]);
|
|
2513
|
-
// Declarative focus management
|
|
2514
|
-
|
|
2733
|
+
// Declarative focus management. Wrapped in `useCallback` with an empty
|
|
2734
|
+
// dependency array since it only reads from the `tabRefs` ref, so its
|
|
2735
|
+
// identity stays stable across renders and it can safely be used as an
|
|
2736
|
+
// effect dependency below.
|
|
2737
|
+
const focusTab = useCallback((idx) => {
|
|
2515
2738
|
var _a;
|
|
2516
2739
|
if ((_a = tabRefs.current[idx]) === null || _a === void 0 ? void 0 : _a.current) {
|
|
2517
2740
|
tabRefs.current[idx].current.focus();
|
|
2518
2741
|
}
|
|
2519
|
-
};
|
|
2520
|
-
// Focus selected tab when selected changes
|
|
2742
|
+
}, []);
|
|
2743
|
+
// Focus selected tab when selected changes.
|
|
2744
|
+
// Reads `options` from `optionsRef` (see comment above) instead of
|
|
2745
|
+
// depending on `options` directly, so a parent re-render that merely
|
|
2746
|
+
// creates a new `options` reference with identical content doesn't call
|
|
2747
|
+
// `focusTab` again and steal focus away from unrelated elements on the
|
|
2748
|
+
// page (e.g. an OTP input).
|
|
2521
2749
|
useEffect(() => {
|
|
2522
|
-
const idx =
|
|
2750
|
+
const idx = optionsRef.current.findIndex((opt) => opt.tab === selected && !opt.disabled);
|
|
2523
2751
|
if (idx !== -1) {
|
|
2524
2752
|
focusTab(idx);
|
|
2525
2753
|
}
|
|
2526
|
-
}, [selected,
|
|
2754
|
+
}, [selected, focusTab]);
|
|
2527
2755
|
const handleKeyDown = useCallback((idx, e) => {
|
|
2528
2756
|
const count = options.length;
|
|
2529
2757
|
if (count === 0)
|
|
@@ -2552,7 +2780,7 @@ function DTabs({ children, defaultSelected, onChange, options, className, classN
|
|
|
2552
2780
|
}
|
|
2553
2781
|
}
|
|
2554
2782
|
}
|
|
2555
|
-
}, [options, vertical]);
|
|
2783
|
+
}, [options, vertical, focusTab]);
|
|
2556
2784
|
let tablistProps = {};
|
|
2557
2785
|
if (ariaLabelledBy) {
|
|
2558
2786
|
tablistProps = { 'aria-labelledby': ariaLabelledBy };
|
|
@@ -2565,12 +2793,12 @@ function DTabs({ children, defaultSelected, onChange, options, className, classN
|
|
|
2565
2793
|
isSelected,
|
|
2566
2794
|
}), [isSelected]);
|
|
2567
2795
|
return (jsx(TabContext.Provider, { value: value, children: jsxs("div", Object.assign({ className: classNames({
|
|
2568
|
-
'd-
|
|
2569
|
-
'
|
|
2796
|
+
'd-tabs': true,
|
|
2797
|
+
'd-tabs-column': !vertical || variant === 'tabs',
|
|
2570
2798
|
}), style: style }, dataAttributes, { children: [jsx("ul", Object.assign({ className: classNames(generateClasses), role: "tablist", "aria-orientation": vertical ? 'vertical' : undefined }, tablistProps, { children: options.map((option, idx) => {
|
|
2571
2799
|
const isTabSelected = !!option.tab && option.tab === selected;
|
|
2572
2800
|
return (jsx("li", { role: "presentation", className: "nav-item", children: 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));
|
|
2573
|
-
}) })), jsx("div", { className: "
|
|
2801
|
+
}) })), jsx("div", { className: "d-tabs-content tab-content", children: children })] })) }));
|
|
2574
2802
|
}
|
|
2575
2803
|
var DTabs$1 = Object.assign(DTabs, {
|
|
2576
2804
|
Tab: DTabContent,
|
|
@@ -2632,11 +2860,11 @@ function useDToast() {
|
|
|
2632
2860
|
if (!description) {
|
|
2633
2861
|
return (jsx(DToast$1, { className: classNames({
|
|
2634
2862
|
[`toast-${color}`]: !!color,
|
|
2635
|
-
}, 'show'), children: jsxs(DToast$1.Body, { children: [icon && (jsx(DIcon, { className: "toast-icon", icon: icon })), jsx("p", { className: "toast-title", children: title }), jsx("button", { type: "button", className: "d-close
|
|
2863
|
+
}, 'show'), children: jsxs(DToast$1.Body, { children: [icon && (jsx(DIcon, { className: "toast-icon", icon: icon })), jsx("p", { className: "toast-title", children: title }), jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => toast.dismiss(id), children: jsx(DIcon, { icon: closeIcon || xLg }) })] }) }));
|
|
2636
2864
|
}
|
|
2637
2865
|
return (jsxs(DToast$1, { className: classNames({
|
|
2638
2866
|
[`toast-${color}`]: !!color,
|
|
2639
|
-
}, 'show'), children: [jsxs(DToast$1.Header, { children: [icon && (jsx(DIcon, { className: "toast-icon", icon: icon })), jsx("p", { className: "toast-title", children: title }), timestamp && (jsx("small", { className: "toast-timestamp", children: timestamp })), jsx("button", { type: "button", className: "d-close
|
|
2867
|
+
}, 'show'), children: [jsxs(DToast$1.Header, { children: [icon && (jsx(DIcon, { className: "toast-icon", icon: icon })), jsx("p", { className: "toast-title", children: title }), timestamp && (jsx("small", { className: "toast-timestamp", children: timestamp })), jsx("button", { type: "button", className: "d-close", "aria-label": "Close", onClick: () => toast.dismiss(id), children: jsx(DIcon, { icon: closeIcon || xLg }) })] }), jsx(DToast$1.Body, { children: jsx("span", { children: description }) })] }));
|
|
2640
2868
|
}, toastProps);
|
|
2641
2869
|
}, [xLg]);
|
|
2642
2870
|
return {
|
|
@@ -2658,11 +2886,10 @@ async function configureI8n(resources, _a = {}) {
|
|
|
2658
2886
|
.then((t) => t);
|
|
2659
2887
|
}
|
|
2660
2888
|
|
|
2661
|
-
function getQueryString(value, config = {
|
|
2662
|
-
useSearch
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
return urlParams.get(value) || config.default;
|
|
2889
|
+
function getQueryString(value, config = {}) {
|
|
2890
|
+
const { useSearch = true, default: defaultValue } = config;
|
|
2891
|
+
const urlParams = new URLSearchParams(useSearch ? window.location.search : '');
|
|
2892
|
+
return urlParams.get(value) || defaultValue;
|
|
2666
2893
|
}
|
|
2667
2894
|
|
|
2668
2895
|
function changeQueryString(values, { useSearch = true, pushState = false, } = {}) {
|
|
@@ -2785,89 +3012,228 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
|
|
|
2785
3012
|
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 })] })] }));
|
|
2786
3013
|
}
|
|
2787
3014
|
|
|
3015
|
+
// Gap between the toggle and the menu.
|
|
3016
|
+
const GAP = 4;
|
|
3017
|
+
// Minimum distance the menu should keep from the viewport edges.
|
|
3018
|
+
const VIEWPORT_PADDING = 8;
|
|
3019
|
+
// Minimum usable height left for the menu before we consider a side unusable.
|
|
3020
|
+
const MIN_USABLE_SPACE = 120;
|
|
2788
3021
|
const getItemClass = (action) => classNames({
|
|
2789
3022
|
'dropdown-item d-flex align-items-center': true,
|
|
2790
3023
|
[`dropdown-item-${action.color}`]: !!action.color,
|
|
2791
3024
|
disabled: action.disabled,
|
|
2792
3025
|
});
|
|
2793
|
-
|
|
3026
|
+
const clamp = (value, min, max) => {
|
|
3027
|
+
if (max < min)
|
|
3028
|
+
return min;
|
|
3029
|
+
return Math.min(Math.max(value, min), max);
|
|
3030
|
+
};
|
|
3031
|
+
/**
|
|
3032
|
+
* `position: fixed` is normally relative to the viewport, but any ancestor
|
|
3033
|
+
* with a `transform`, `perspective`, `filter`, `will-change` (of those) or a
|
|
3034
|
+
* non-trivial `contain` establishes a new containing block, making `fixed`
|
|
3035
|
+
* behave like `absolute` relative to that ancestor instead (e.g. Storybook's
|
|
3036
|
+
* docs canvas wrapper, animated cards, sticky containers, etc.). We detect
|
|
3037
|
+
* that ancestor so the computed coordinates can be adjusted accordingly,
|
|
3038
|
+
* keeping the menu correctly anchored to the toggle in every context.
|
|
3039
|
+
*/
|
|
3040
|
+
const getFixedPositioningOffset = (fromEl) => {
|
|
3041
|
+
var _a;
|
|
3042
|
+
let node = (_a = fromEl === null || fromEl === void 0 ? void 0 : fromEl.parentElement) !== null && _a !== void 0 ? _a : null;
|
|
3043
|
+
while (node && node !== document.body) {
|
|
3044
|
+
const cs = window.getComputedStyle(node);
|
|
3045
|
+
// Treat empty/undefined values as "unset" (jsdom returns "" rather than
|
|
3046
|
+
// the CSS initial value like "none" for some of these properties).
|
|
3047
|
+
const isSet = (value) => !!value && value !== 'none';
|
|
3048
|
+
const establishesContainingBlock = isSet(cs.transform)
|
|
3049
|
+
|| isSet(cs.perspective)
|
|
3050
|
+
|| isSet(cs.filter)
|
|
3051
|
+
|| isSet(cs.backdropFilter)
|
|
3052
|
+
|| ['transform', 'perspective', 'filter'].some((prop) => { var _a; return (_a = cs.willChange) === null || _a === void 0 ? void 0 : _a.includes(prop); })
|
|
3053
|
+
|| cs.contain.split(' ').some((value) => ['layout', 'paint', 'strict', 'content'].includes(value));
|
|
3054
|
+
if (establishesContainingBlock) {
|
|
3055
|
+
const rect = node.getBoundingClientRect();
|
|
3056
|
+
return { top: rect.top, left: rect.left };
|
|
3057
|
+
}
|
|
3058
|
+
node = node.parentElement;
|
|
3059
|
+
}
|
|
3060
|
+
return { top: 0, left: 0 };
|
|
3061
|
+
};
|
|
3062
|
+
/**
|
|
3063
|
+
* Resolves where the menu should render (flip) and where it should sit along
|
|
3064
|
+
* the cross axis (shift), so it always stays fully inside the viewport
|
|
3065
|
+
* regardless of where the toggle is placed or how the viewport is resized.
|
|
3066
|
+
* This mimics floating-ui's `flip` + `shift` middlewares without adding the
|
|
3067
|
+
* dependency, keeping the existing Bootstrap markup/classes.
|
|
3068
|
+
*/
|
|
3069
|
+
const computeCoords = (toggleRect, menuSize, placement, alignment) => {
|
|
3070
|
+
const viewportWidth = window.innerWidth;
|
|
3071
|
+
const viewportHeight = window.innerHeight;
|
|
3072
|
+
const spaceBelow = viewportHeight - toggleRect.bottom;
|
|
3073
|
+
const spaceAbove = toggleRect.top;
|
|
3074
|
+
const spaceLeft = toggleRect.left;
|
|
3075
|
+
const spaceRight = viewportWidth - toggleRect.right;
|
|
3076
|
+
let side = placement === 'auto'
|
|
3077
|
+
? 'down'
|
|
3078
|
+
: placement;
|
|
3079
|
+
if (placement === 'auto') {
|
|
3080
|
+
side = spaceBelow >= menuSize.height + GAP || spaceBelow >= spaceAbove ? 'down' : 'up';
|
|
3081
|
+
}
|
|
3082
|
+
// Flip within the current axis if the chosen side can't fit the menu but
|
|
3083
|
+
// the opposite side (same axis) can.
|
|
3084
|
+
if (side === 'down' && spaceBelow < menuSize.height + GAP && spaceAbove > spaceBelow) {
|
|
3085
|
+
side = 'up';
|
|
3086
|
+
}
|
|
3087
|
+
else if (side === 'up' && spaceAbove < menuSize.height + GAP && spaceBelow > spaceAbove) {
|
|
3088
|
+
side = 'down';
|
|
3089
|
+
}
|
|
3090
|
+
else if (side === 'start' && spaceLeft < menuSize.width + GAP && spaceRight > spaceLeft) {
|
|
3091
|
+
side = 'end';
|
|
3092
|
+
}
|
|
3093
|
+
else if (side === 'end' && spaceRight < menuSize.width + GAP && spaceLeft > spaceRight) {
|
|
3094
|
+
side = 'start';
|
|
3095
|
+
}
|
|
3096
|
+
// Cross-axis fallback: e.g. a horizontally-centered toggle on a narrow
|
|
3097
|
+
// viewport can't open sideways (neither `start` nor `end` fit), but there
|
|
3098
|
+
// is plenty of room above/below — switch the whole axis in that case
|
|
3099
|
+
// (and vice versa for `down`/`up` squeezed against a short viewport).
|
|
3100
|
+
const bestVerticalSpace = Math.max(spaceBelow, spaceAbove);
|
|
3101
|
+
const bestHorizontalSpace = Math.max(spaceLeft, spaceRight);
|
|
3102
|
+
if (side === 'down' || side === 'up') {
|
|
3103
|
+
if (bestVerticalSpace < MIN_USABLE_SPACE && bestHorizontalSpace > bestVerticalSpace) {
|
|
3104
|
+
side = spaceRight >= spaceLeft ? 'end' : 'start';
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
else if (bestHorizontalSpace < menuSize.width + GAP
|
|
3108
|
+
&& bestVerticalSpace > bestHorizontalSpace) {
|
|
3109
|
+
side = spaceBelow >= spaceAbove ? 'down' : 'up';
|
|
3110
|
+
}
|
|
3111
|
+
if (side === 'down' || side === 'up') {
|
|
3112
|
+
const top = side === 'down'
|
|
3113
|
+
? toggleRect.bottom + GAP
|
|
3114
|
+
: toggleRect.top - menuSize.height - GAP;
|
|
3115
|
+
// Align horizontally per the requested `alignment` (start/end/center), then
|
|
3116
|
+
// fall back to the opposite edge if the preferred alignment would overflow
|
|
3117
|
+
// the viewport, and clamp as a final safety net so it never overflows.
|
|
3118
|
+
let left;
|
|
3119
|
+
if (alignment === 'end') {
|
|
3120
|
+
left = toggleRect.right - menuSize.width;
|
|
3121
|
+
}
|
|
3122
|
+
else if (alignment === 'center') {
|
|
3123
|
+
left = toggleRect.left + (toggleRect.width / 2) - (menuSize.width / 2);
|
|
3124
|
+
}
|
|
3125
|
+
else {
|
|
3126
|
+
left = toggleRect.left;
|
|
3127
|
+
}
|
|
3128
|
+
if (left + menuSize.width > viewportWidth - VIEWPORT_PADDING && alignment !== 'end') {
|
|
3129
|
+
const altLeft = toggleRect.right - menuSize.width;
|
|
3130
|
+
if (altLeft >= VIEWPORT_PADDING)
|
|
3131
|
+
left = altLeft;
|
|
3132
|
+
}
|
|
3133
|
+
else if (left < VIEWPORT_PADDING && alignment !== 'start') {
|
|
3134
|
+
const altLeft = toggleRect.left;
|
|
3135
|
+
if (altLeft + menuSize.width <= viewportWidth - VIEWPORT_PADDING)
|
|
3136
|
+
left = altLeft;
|
|
3137
|
+
}
|
|
3138
|
+
left = clamp(left, VIEWPORT_PADDING, Math.max(viewportWidth - menuSize.width - VIEWPORT_PADDING, VIEWPORT_PADDING));
|
|
3139
|
+
const availableHeight = side === 'down' ? spaceBelow : spaceAbove;
|
|
3140
|
+
// Cap strictly to the space actually available on the resolved side —
|
|
3141
|
+
// MIN_USABLE_SPACE is only meant to influence the flip/shift decision
|
|
3142
|
+
// above, not to force a taller box than what physically fits and
|
|
3143
|
+
// overflow the viewport.
|
|
3144
|
+
const maxUsableHeight = Math.max(availableHeight - GAP - VIEWPORT_PADDING, 0);
|
|
3145
|
+
return {
|
|
3146
|
+
top,
|
|
3147
|
+
left,
|
|
3148
|
+
minWidth: Math.max(toggleRect.width, 160),
|
|
3149
|
+
maxHeight: `${maxUsableHeight}px`,
|
|
3150
|
+
resolvedSide: side,
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
// side is 'start' or 'end'
|
|
3154
|
+
let left = side === 'start'
|
|
3155
|
+
? toggleRect.left - menuSize.width - GAP
|
|
3156
|
+
: toggleRect.right + GAP;
|
|
3157
|
+
// Safety-net clamp: in rare cases where the toggle is squeezed so tightly
|
|
3158
|
+
// that no side of any axis has enough room, keep the menu inside the
|
|
3159
|
+
// viewport rather than letting it render off-screen.
|
|
3160
|
+
left = clamp(left, VIEWPORT_PADDING, Math.max(viewportWidth - menuSize.width - VIEWPORT_PADDING, VIEWPORT_PADDING));
|
|
3161
|
+
// Shift vertically: align with the toggle's top edge by default, but switch
|
|
3162
|
+
// to the bottom edge (or clamp) if that would overflow the viewport.
|
|
3163
|
+
let { top } = toggleRect;
|
|
3164
|
+
if (top + menuSize.height > viewportHeight - VIEWPORT_PADDING) {
|
|
3165
|
+
top = toggleRect.bottom - menuSize.height;
|
|
3166
|
+
}
|
|
3167
|
+
top = clamp(top, VIEWPORT_PADDING, Math.max(viewportHeight - menuSize.height - VIEWPORT_PADDING, VIEWPORT_PADDING));
|
|
3168
|
+
return {
|
|
3169
|
+
top,
|
|
3170
|
+
left,
|
|
3171
|
+
minWidth: Math.max(toggleRect.width, 160),
|
|
3172
|
+
maxHeight: `${Math.max(viewportHeight - top - VIEWPORT_PADDING, 0)}px`,
|
|
3173
|
+
resolvedSide: side,
|
|
3174
|
+
};
|
|
3175
|
+
};
|
|
3176
|
+
function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal = false, placement = 'auto', alignment = 'start', }) {
|
|
2794
3177
|
const [open, setOpen] = useState(false);
|
|
2795
|
-
const [
|
|
2796
|
-
|
|
2797
|
-
//
|
|
2798
|
-
|
|
2799
|
-
//
|
|
3178
|
+
const [coords, setCoords] = useState(null);
|
|
3179
|
+
// Initialize from `placement` (falling back to 'down' for 'auto') so the
|
|
3180
|
+
// wrapper's `drop-*` class reflects the requested side even before the
|
|
3181
|
+
// first open/measurement, matching the previous `drop-${placement}` class
|
|
3182
|
+
// behavior (relevant for Bootstrap caret/orientation styling).
|
|
3183
|
+
const [resolvedSide, setResolvedSide] = useState(placement === 'auto' ? 'down' : placement);
|
|
3184
|
+
// Wraps the toggle in both modes — used to read its viewport-relative rect.
|
|
2800
3185
|
const toggleRef = useRef(null);
|
|
2801
|
-
// Ref on the rendered <ul> — used for outside-click detection
|
|
3186
|
+
// Ref on the rendered <ul> — used to measure its size and for outside-click detection.
|
|
2802
3187
|
const menuRef = useRef(null);
|
|
2803
3188
|
const toggle = () => {
|
|
2804
|
-
|
|
3189
|
+
setCoords(null);
|
|
2805
3190
|
setOpen((prev) => !prev);
|
|
2806
3191
|
};
|
|
2807
|
-
|
|
2808
|
-
//
|
|
2809
|
-
const
|
|
2810
|
-
if (!toggleRef.current)
|
|
3192
|
+
// Measures the toggle + menu and (re)computes fixed, pixel-perfect coords
|
|
3193
|
+
// with collision detection, so the menu never overflows the viewport.
|
|
3194
|
+
const updatePosition = useCallback(() => {
|
|
3195
|
+
if (!toggleRef.current || !menuRef.current)
|
|
3196
|
+
return;
|
|
3197
|
+
const toggleRect = toggleRef.current.getBoundingClientRect();
|
|
3198
|
+
// If the toggle itself has scrolled completely out of the viewport
|
|
3199
|
+
// (regardless of `placement`), close the menu instead of leaving it
|
|
3200
|
+
// rendered detached from the toggle that opened it.
|
|
3201
|
+
const toggleIsMeasured = toggleRect.width > 0 || toggleRect.height > 0;
|
|
3202
|
+
const toggleIsOutOfView = toggleRect.bottom <= 0
|
|
3203
|
+
|| toggleRect.top >= window.innerHeight
|
|
3204
|
+
|| toggleRect.right <= 0
|
|
3205
|
+
|| toggleRect.left >= window.innerWidth;
|
|
3206
|
+
if (toggleIsMeasured && toggleIsOutOfView) {
|
|
3207
|
+
setOpen(false);
|
|
2811
3208
|
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') {
|
|
2818
|
-
if (spaceBottom < 150) {
|
|
2819
|
-
resolvedPlacement = 'up';
|
|
2820
|
-
}
|
|
2821
|
-
else if (spaceRight < 150) {
|
|
2822
|
-
resolvedPlacement = 'start';
|
|
2823
|
-
}
|
|
2824
|
-
else {
|
|
2825
|
-
resolvedPlacement = 'down';
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
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 };
|
|
2850
|
-
}
|
|
2851
|
-
else {
|
|
2852
|
-
horizontalCoords = { left: rect.left };
|
|
2853
3209
|
}
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
const
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
};
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
if (
|
|
3210
|
+
const menuRect = menuRef.current.getBoundingClientRect();
|
|
3211
|
+
const _a = computeCoords(toggleRect, { width: menuRect.width, height: menuRect.height }, placement, alignment), { resolvedSide: side } = _a, menuCoords = __rest(_a, ["resolvedSide"]);
|
|
3212
|
+
// In portal mode the menu is appended to document.body, whose containing
|
|
3213
|
+
// block for `fixed` is always the viewport (no adjustment needed). In
|
|
3214
|
+
// inline mode the menu stays next to the toggle, so we must look for a
|
|
3215
|
+
// containing-block-establishing ancestor starting from the toggle.
|
|
3216
|
+
const offset = asPortal
|
|
3217
|
+
? { top: 0, left: 0 }
|
|
3218
|
+
: getFixedPositioningOffset(toggleRef.current);
|
|
3219
|
+
setResolvedSide(side);
|
|
3220
|
+
setCoords(Object.assign(Object.assign({}, menuCoords), { top: menuCoords.top - offset.top, left: menuCoords.left - offset.left }));
|
|
3221
|
+
}, [placement, alignment, asPortal]);
|
|
3222
|
+
// Throttles scroll/resize-triggered updates to at most once per frame,
|
|
3223
|
+
// avoiding repeated forced reflows on busy/high-frequency scroll events.
|
|
3224
|
+
const rafIdRef = useRef(null);
|
|
3225
|
+
const scheduleUpdatePosition = useCallback(() => {
|
|
3226
|
+
if (rafIdRef.current !== null)
|
|
3227
|
+
return;
|
|
3228
|
+
rafIdRef.current = window.requestAnimationFrame(() => {
|
|
3229
|
+
rafIdRef.current = null;
|
|
3230
|
+
updatePosition();
|
|
3231
|
+
});
|
|
3232
|
+
}, [updatePosition]);
|
|
3233
|
+
// Outside-click detection (checks both the toggle and the floating menu,
|
|
3234
|
+
// since in portal mode the menu isn't a DOM descendant of the toggle).
|
|
3235
|
+
useLayoutEffect(() => {
|
|
3236
|
+
if (!open)
|
|
2871
3237
|
return () => { };
|
|
2872
3238
|
const handler = (event) => {
|
|
2873
3239
|
var _a, _b;
|
|
@@ -2878,35 +3244,25 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
|
|
|
2878
3244
|
};
|
|
2879
3245
|
document.addEventListener('mousedown', handler);
|
|
2880
3246
|
return () => document.removeEventListener('mousedown', handler);
|
|
2881
|
-
}, [
|
|
2882
|
-
//
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
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)
|
|
3247
|
+
}, [open]);
|
|
3248
|
+
// Computes the initial position synchronously (before paint, to avoid a
|
|
3249
|
+
// flicker) and keeps it fresh while the menu is open and the viewport
|
|
3250
|
+
// scrolls or resizes (throttled to one update per frame, see above).
|
|
3251
|
+
useLayoutEffect(() => {
|
|
3252
|
+
if (!open)
|
|
2900
3253
|
return () => { };
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
window.addEventListener('
|
|
2904
|
-
window.addEventListener('resize', update, { passive: true });
|
|
3254
|
+
updatePosition();
|
|
3255
|
+
window.addEventListener('scroll', scheduleUpdatePosition, { passive: true, capture: true });
|
|
3256
|
+
window.addEventListener('resize', scheduleUpdatePosition, { passive: true });
|
|
2905
3257
|
return () => {
|
|
2906
|
-
window.removeEventListener('scroll',
|
|
2907
|
-
window.removeEventListener('resize',
|
|
3258
|
+
window.removeEventListener('scroll', scheduleUpdatePosition, { capture: true });
|
|
3259
|
+
window.removeEventListener('resize', scheduleUpdatePosition);
|
|
3260
|
+
if (rafIdRef.current !== null) {
|
|
3261
|
+
window.cancelAnimationFrame(rafIdRef.current);
|
|
3262
|
+
rafIdRef.current = null;
|
|
3263
|
+
}
|
|
2908
3264
|
};
|
|
2909
|
-
}, [
|
|
3265
|
+
}, [open, updatePosition, scheduleUpdatePosition]);
|
|
2910
3266
|
let ToggleElement;
|
|
2911
3267
|
if (dropdownToggle) {
|
|
2912
3268
|
ToggleElement = typeof dropdownToggle === 'function'
|
|
@@ -2916,57 +3272,30 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
|
|
|
2916
3272
|
else {
|
|
2917
3273
|
ToggleElement = (jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle, icon: "MoreVertical" }));
|
|
2918
3274
|
}
|
|
2919
|
-
|
|
3275
|
+
// Until the first measurement is available, keep the menu off-screen but
|
|
3276
|
+
// measurable (visibility: hidden preserves layout, unlike display: none).
|
|
3277
|
+
const menuStyle = coords
|
|
2920
3278
|
? {
|
|
2921
3279
|
position: 'fixed',
|
|
2922
|
-
top:
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
minWidth: menuCoords.minWidth,
|
|
2927
|
-
maxHeight: menuCoords.maxHeight,
|
|
3280
|
+
top: coords.top,
|
|
3281
|
+
left: coords.left,
|
|
3282
|
+
minWidth: coords.minWidth,
|
|
3283
|
+
maxHeight: coords.maxHeight,
|
|
2928
3284
|
overflowY: 'auto',
|
|
2929
3285
|
zIndex: 1050,
|
|
2930
3286
|
transform: 'none',
|
|
3287
|
+
visibility: 'visible',
|
|
2931
3288
|
}
|
|
2932
|
-
:
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
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) => {
|
|
3289
|
+
: {
|
|
3290
|
+
position: 'fixed',
|
|
3291
|
+
top: 0,
|
|
3292
|
+
left: 0,
|
|
3293
|
+
visibility: 'hidden',
|
|
3294
|
+
pointerEvents: 'none',
|
|
3295
|
+
zIndex: 1050,
|
|
3296
|
+
transform: 'none',
|
|
3297
|
+
};
|
|
3298
|
+
const menuItems = (jsx("ul", { ref: menuRef, role: "menu", className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), style: menuStyle, children: actions.map((action, index) => {
|
|
2970
3299
|
if (action.isDivider) {
|
|
2971
3300
|
return jsx("hr", { className: "dropdown-divider" }, index);
|
|
2972
3301
|
}
|
|
@@ -2985,10 +3314,7 @@ function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal
|
|
|
2985
3314
|
}
|
|
2986
3315
|
}, disabled: action.disabled, children: [action.icon && jsx(DIcon, { icon: action.icon, className: "me-2", size: "1rem" }), action.label] })) }, index));
|
|
2987
3316
|
}) }));
|
|
2988
|
-
|
|
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] }));
|
|
3317
|
+
return (jsxs("div", { ref: toggleRef, className: classNames(`dropdown drop-${resolvedSide} d-inline-block`, className), children: [ToggleElement, open && (asPortal ? createPortal(menuItems, document.body) : menuItems)] }));
|
|
2992
3318
|
}
|
|
2993
3319
|
|
|
2994
3320
|
function useScreenshot() {
|
|
@@ -3018,13 +3344,18 @@ function useScreenshot() {
|
|
|
3018
3344
|
|
|
3019
3345
|
function useScreenshotDownload() {
|
|
3020
3346
|
const { clipRef, takeBlob } = useScreenshot();
|
|
3021
|
-
const download = useCallback(async () => {
|
|
3022
|
-
const blob = await takeBlob();
|
|
3347
|
+
const download = useCallback(async (fileName = 'voucher') => {
|
|
3348
|
+
const blob = await takeBlob('image/jpeg');
|
|
3023
3349
|
const url = window.URL.createObjectURL(blob);
|
|
3024
3350
|
const link = window.document.createElement('a');
|
|
3025
3351
|
link.style.display = 'none';
|
|
3026
3352
|
link.href = url;
|
|
3027
|
-
|
|
3353
|
+
// Fall back to the default base name when the caller passes a blank/whitespace value,
|
|
3354
|
+
// so the file is never named just ".jpg".
|
|
3355
|
+
const safeFileName = fileName.trim() || 'voucher';
|
|
3356
|
+
// The extension is always appended by the hook to guarantee a valid image file,
|
|
3357
|
+
// regardless of what fileName the caller passes (e.g. "receipt.png" -> "receipt.png.jpg").
|
|
3358
|
+
link.download = `${safeFileName}.jpg`;
|
|
3028
3359
|
document.body.appendChild(link);
|
|
3029
3360
|
link.click();
|
|
3030
3361
|
document.body.removeChild(link);
|
|
@@ -3038,11 +3369,15 @@ function useScreenshotDownload() {
|
|
|
3038
3369
|
|
|
3039
3370
|
function useScreenshotWebShare() {
|
|
3040
3371
|
const { takeBlob, clipRef } = useScreenshot();
|
|
3041
|
-
const share = useCallback(async () => {
|
|
3042
|
-
const blob = await takeBlob();
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3372
|
+
const share = useCallback(async (fileName = 'voucher') => {
|
|
3373
|
+
const blob = await takeBlob('image/jpeg');
|
|
3374
|
+
// Fall back to the default base name when the caller passes a blank/whitespace value,
|
|
3375
|
+
// so the file is never named just ".jpeg".
|
|
3376
|
+
const safeFileName = fileName.trim() || 'voucher';
|
|
3377
|
+
// The extension is always appended by the hook to guarantee a valid image file,
|
|
3378
|
+
// regardless of what fileName the caller passes (e.g. "receipt.png" -> "receipt.png.jpeg").
|
|
3379
|
+
const image = new File([blob], `${safeFileName}.jpeg`, { type: 'image/jpeg' });
|
|
3380
|
+
if (!navigator.canShare || !navigator.canShare({ files: [image] })) {
|
|
3046
3381
|
window.print();
|
|
3047
3382
|
return;
|
|
3048
3383
|
}
|
|
@@ -3054,11 +3389,11 @@ function useScreenshotWebShare() {
|
|
|
3054
3389
|
};
|
|
3055
3390
|
}
|
|
3056
3391
|
|
|
3057
|
-
function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText = 'Download', shareText = 'Share', className, children, }) {
|
|
3392
|
+
function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText = 'Download', shareText = 'Share', fileName = 'voucher', hideActions = false, className, children, }) {
|
|
3058
3393
|
const { shareRef, share } = useScreenshotWebShare();
|
|
3059
3394
|
const { downloadRef, download } = useScreenshotDownload();
|
|
3060
3395
|
const handleShare = () => {
|
|
3061
|
-
share()
|
|
3396
|
+
share(fileName)
|
|
3062
3397
|
.catch(async (err) => {
|
|
3063
3398
|
if (onError) {
|
|
3064
3399
|
await onError(err);
|
|
@@ -3069,7 +3404,7 @@ function DVoucher({ amount, amountDetails, icon, title, onError, message, downlo
|
|
|
3069
3404
|
});
|
|
3070
3405
|
};
|
|
3071
3406
|
const handleDownload = () => {
|
|
3072
|
-
download()
|
|
3407
|
+
download(fileName)
|
|
3073
3408
|
.catch(async (err) => {
|
|
3074
3409
|
if (onError) {
|
|
3075
3410
|
await onError(err);
|
|
@@ -3097,80 +3432,36 @@ function DVoucher({ amount, amountDetails, icon, title, onError, message, downlo
|
|
|
3097
3432
|
return (jsx("div", { className: classNames('d-voucher', className), ref: (el) => {
|
|
3098
3433
|
shareRef.current = el;
|
|
3099
3434
|
downloadRef.current = el;
|
|
3100
|
-
}, children: jsxs("div", { children: [jsxs("div", { className: "d-voucher-header", children: [resolvedIconProps && (jsx(DIcon, Object.assign({}, resolvedIconProps))), jsxs("div", { className: "
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
function useCountdown(seconds) {
|
|
3104
|
-
const [secondsLeft, setSecondsLeft] = useState(seconds);
|
|
3105
|
-
const [isActive, setIsActive] = useState(true);
|
|
3106
|
-
const resetCountdown = useCallback((newSeconds = seconds) => {
|
|
3107
|
-
setIsActive(false);
|
|
3108
|
-
setSecondsLeft(newSeconds);
|
|
3109
|
-
}, [seconds]);
|
|
3110
|
-
const restartCountdown = useCallback(() => {
|
|
3111
|
-
resetCountdown(seconds);
|
|
3112
|
-
setIsActive(true);
|
|
3113
|
-
}, [resetCountdown, seconds]);
|
|
3114
|
-
useEffect(() => {
|
|
3115
|
-
if (!isActive) {
|
|
3116
|
-
return () => { };
|
|
3117
|
-
}
|
|
3118
|
-
const interval = setInterval(() => {
|
|
3119
|
-
setSecondsLeft((prevSeconds) => {
|
|
3120
|
-
const newSeconds = prevSeconds - 1;
|
|
3121
|
-
if (newSeconds <= 0) {
|
|
3122
|
-
clearInterval(interval);
|
|
3123
|
-
setIsActive(false);
|
|
3124
|
-
return 0;
|
|
3125
|
-
}
|
|
3126
|
-
return newSeconds;
|
|
3127
|
-
});
|
|
3128
|
-
}, 1000);
|
|
3129
|
-
return () => clearInterval(interval);
|
|
3130
|
-
}, [isActive]);
|
|
3131
|
-
return { secondsLeft, restartCountdown };
|
|
3435
|
+
}, children: jsxs("div", { children: [jsxs("div", { className: "d-voucher-header", children: [resolvedIconProps && (jsx(DIcon, Object.assign({}, resolvedIconProps))), jsxs("div", { className: "d-voucher-title-group", children: [jsx("h3", { className: "d-voucher-title", children: title }), jsx("p", { className: "d-voucher-message", children: message })] })] }), amount && (jsxs("div", { className: "d-voucher-amount", children: [jsx("div", { className: classNames('d-voucher-amount-value', { 'd-voucher-amount-value-with-details': !!amountDetails }), children: amount }), amountDetails] })), jsx("hr", { className: "d-voucher-divider" }), children, !hideActions && (jsxs(Fragment, { children: [jsx("hr", { className: "my-4" }), jsxs("div", { className: "d-voucher-footer", children: [jsx(DButton, { onClick: handleShare, iconStart: "Share2", text: shareText, variant: "outline", size: "sm" }), jsx(DButton, { onClick: handleDownload, iconStart: "Download", text: downloadText, variant: "outline", size: "sm" })] })] }))] }) }));
|
|
3132
3436
|
}
|
|
3133
3437
|
|
|
3134
3438
|
const defaultMessage = (secs) => (secs > 0
|
|
3135
3439
|
? `Didn't get any code? Resend in: ${secs}s`
|
|
3136
3440
|
: "Didn't get any code?");
|
|
3137
|
-
function OtpCountdown({
|
|
3138
|
-
|
|
3139
|
-
return (jsxs("div", { className: "d-flex gap-2 align-items-center", children: [jsx("p", { className: "mb-0 flex-1", children: message ? message(secondsLeft) : defaultMessage(secondsLeft) }), jsx(DButton, { text: resendText, variant: "link", className: "text-nowrap", disabled: secondsLeft > 0, onClick: restartCountdown })] }));
|
|
3441
|
+
function OtpCountdown({ secondsLeft, restartCountdown, resendText, message, }) {
|
|
3442
|
+
return (jsxs("div", { className: "d-otp-countdown", children: [jsx("p", { className: "d-otp-countdown-text", children: message ? message(secondsLeft) : defaultMessage(secondsLeft) }), jsx(DButton, { text: resendText, variant: "link", className: "text-nowrap", disabled: secondsLeft > 0, onClick: restartCountdown })] }));
|
|
3140
3443
|
}
|
|
3141
3444
|
|
|
3142
3445
|
const TEXT_PROPS = {
|
|
3143
3446
|
resend: 'Resend',
|
|
3144
|
-
resendText: 'Resend
|
|
3447
|
+
resendText: 'Resend',
|
|
3145
3448
|
submit: 'Authorize and continue',
|
|
3146
3449
|
title: 'We will send you a 6-digit code to your associated phone number so you can continue with your request.',
|
|
3450
|
+
invalidCode: 'Invalid code, please try again.',
|
|
3147
3451
|
contact: (jsxs(Fragment, { children: [jsx("span", { children: "Problems with your digital token? Contact us" }), ' ', jsx("a", { href: "https://www.modyo.com", className: "link-primary text-nowrap", target: "_blank", rel: "noreferrer", children: "Contact us" })] })),
|
|
3148
3452
|
};
|
|
3149
3453
|
function DOtp({ className, action, isLoading, otpSize = 6, texts = TEXT_PROPS, seconds = 15, }) {
|
|
3150
|
-
const
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
if (otp.length < otpSize) {
|
|
3154
|
-
setInvalid(true);
|
|
3155
|
-
return;
|
|
3156
|
-
}
|
|
3157
|
-
setInvalid(false);
|
|
3158
|
-
await action();
|
|
3159
|
-
}, [
|
|
3160
|
-
otp.length,
|
|
3161
|
-
action,
|
|
3162
|
-
otpSize,
|
|
3163
|
-
]);
|
|
3164
|
-
return (jsxs("div", { className: className, children: [jsx("p", { children: texts.title }), jsxs("div", { className: "d-flex flex-column gap-6 pb-4 px-3", children: [jsxs("div", { className: "d-flex flex-column gap-6", children: [jsx(DInputPin, { className: "modal-otp-pin", characters: otpSize, onChange: (e) => setOtp(e), invalid: invalid && otp.length < otpSize, placeholder: "0" }), jsx(OtpCountdown, { seconds: seconds, resendText: texts.resend })] }), jsx("hr", { className: "m-0" }), jsxs("div", { className: "d-flex flex-column flex-lg-row gap-4 align-items-center", children: [jsx(DButton, { text: texts.submit, onClick: () => {
|
|
3165
|
-
handler().catch((err) => {
|
|
3454
|
+
const { setOtp, invalid, submit, isLoading: isSubmitting, secondsLeft, restartCountdown, } = useOtp({ action, otpSize, seconds });
|
|
3455
|
+
return (jsxs("div", { className: className, children: [jsx("p", { children: texts.title }), jsxs("div", { className: "d-otp d-otp-content", children: [jsxs("div", { className: "d-otp-fields", children: [jsx(DInputPin, { className: "modal-otp-pin", characters: otpSize, onChange: (e) => setOtp(e), invalid: invalid, hint: invalid ? texts.invalidCode : undefined, placeholder: "0" }), jsx(OtpCountdown, { secondsLeft: secondsLeft, restartCountdown: restartCountdown, resendText: texts.resendText })] }), jsx("hr", { className: "d-otp-divider" }), jsxs("div", { className: "d-otp-footer", children: [jsx(DButton, { text: texts.submit, onClick: () => {
|
|
3456
|
+
submit().catch((err) => {
|
|
3166
3457
|
// eslint-disable-next-line no-console
|
|
3167
3458
|
console.error('Error in DOtp action:', err);
|
|
3168
3459
|
});
|
|
3169
|
-
}, loading: isLoading }), jsx("p", { className: "
|
|
3460
|
+
}, loading: isLoading || isSubmitting }), jsx("p", { className: "d-otp-contact", children: texts.contact })] })] })] }));
|
|
3170
3461
|
}
|
|
3171
3462
|
|
|
3172
3463
|
function DefaultErrorBoundary({ resetErrorBoundary }) {
|
|
3173
|
-
return (jsx(DAlert, { color: "danger", showClose: false, children: jsxs("div", { className: "d-
|
|
3464
|
+
return (jsx(DAlert, { color: "danger", showClose: false, children: jsxs("div", { className: "d-error-boundary-content", children: [jsx("span", { children: "An unexpected error occurred." }), jsx(DButton, { color: "secondary", variant: "outline", size: "sm", onClick: resetErrorBoundary, children: "Retry" })] }) }));
|
|
3174
3465
|
}
|
|
3175
3466
|
|
|
3176
3467
|
function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children, }) {
|
|
@@ -3188,15 +3479,15 @@ function DErrorBoundary({ name, fallback, resetKeys, onReset, onError, children,
|
|
|
3188
3479
|
}
|
|
3189
3480
|
|
|
3190
3481
|
function ErrorState({ message, onRetry, retryMessage = 'Retry', color = 'danger', }) {
|
|
3191
|
-
return (jsxs(DAlert, { color: color, className: "d-
|
|
3482
|
+
return (jsxs(DAlert, { color: color, className: "d-error-state", children: [jsx("div", { className: "d-error-state-content", children: jsx("p", { className: "d-error-state-message", children: message !== null && message !== void 0 ? message : 'An unexpected error occurred.' }) }), onRetry && (jsx(DButton, { onClick: onRetry, text: retryMessage, variant: "outline", iconStart: "RefreshCw" }))] }));
|
|
3192
3483
|
}
|
|
3193
3484
|
|
|
3194
3485
|
function EmptyState({ message, icon = 'FileText', actionText, onAction, }) {
|
|
3195
|
-
return (jsxs("div", { className: "d-
|
|
3486
|
+
return (jsxs("div", { className: "d-empty-state", children: [jsx(DIcon, { icon: icon, size: "3rem", className: "d-empty-state-icon" }), jsx("p", { className: "d-empty-state-message", children: message !== null && message !== void 0 ? message : 'No data available.' }), actionText && onAction && (jsx(DButton, { onClick: onAction, text: actionText, variant: "outline" }))] }));
|
|
3196
3487
|
}
|
|
3197
3488
|
|
|
3198
3489
|
function LoadingState({ ariaLabel = 'Loading...', className }) {
|
|
3199
|
-
return (jsx("div", { className: `d-
|
|
3490
|
+
return (jsx("div", { className: `d-loading-state ${className || ''}`.trim(), "aria-busy": "true", "aria-live": "polite", children: jsx("span", { className: "spinner-border", role: "status", "aria-label": ariaLabel }) }));
|
|
3200
3491
|
}
|
|
3201
3492
|
|
|
3202
3493
|
function render(renderable) {
|
|
@@ -3255,7 +3546,7 @@ function DConfirmModalUI({ entry }) {
|
|
|
3255
3546
|
}
|
|
3256
3547
|
});
|
|
3257
3548
|
}, [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: "
|
|
3549
|
+
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: "d-confirm-modal-body", children: [jsx("p", { className: "d-confirm-modal-message", children: message }), critical && (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 }))] }), 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
3550
|
}
|
|
3260
3551
|
|
|
3261
3552
|
/**
|
|
@@ -3308,5 +3599,5 @@ function DConfirmModalContainer({ nodeId }) {
|
|
|
3308
3599
|
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
3600
|
}
|
|
3310
3601
|
|
|
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 };
|
|
3602
|
+
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, useCountdown, useDContext, useDPortalContext, useDToast, useDisableBodyScrollEffect, useDisableInputWheel, useFormatCurrency, useInputCurrency, useItemSelection, useMediaBreakpointUpLg, useMediaBreakpointUpMd, useMediaBreakpointUpSm, useMediaBreakpointUpXl, useMediaBreakpointUpXs, useMediaBreakpointUpXxl, useMediaQuery, useOtp, useProvidedRefOrCreate, useScreenshot, useScreenshotDownload, useScreenshotWebShare, useStackState, useTabContext, validatePhoneNumber };
|
|
3312
3603
|
//# sourceMappingURL=index.esm.js.map
|