@dynamic-framework/ui-react 2.0.0-dev.20 → 2.0.0-dev.22

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/index.js CHANGED
@@ -44,16 +44,51 @@ var LucideIcons__namespace = /*#__PURE__*/_interopNamespaceDefault(LucideIcons);
44
44
 
45
45
  const PREFIX_BS = 'bs-';
46
46
 
47
+ /* eslint-disable no-lonely-if */
47
48
  function useDisableBodyScrollEffect(disable) {
48
49
  React.useEffect(() => {
49
- if (disable) {
50
+ let observer;
51
+ let timer;
52
+ const lock = () => {
53
+ const { clientWidth } = document.documentElement;
54
+ const { innerWidth } = window;
55
+ const scrollbarWidth = clientWidth ? innerWidth - clientWidth : 0;
50
56
  document.body.style.overflow = 'hidden';
51
- document.body.style.paddingRight = '0';
57
+ document.body.style.paddingRight = `${Math.max(0, scrollbarWidth)}px`;
58
+ };
59
+ const unlock = () => {
60
+ document.body.style.overflow = 'unset';
61
+ document.body.style.paddingRight = '0px';
62
+ };
63
+ if (disable) {
64
+ lock();
52
65
  }
53
66
  else {
54
- document.body.style.overflow = 'unset';
55
- document.body.style.paddingRight = 'unset';
67
+ // Wait until all portal elements are removed (exit animations done)
68
+ if (document.querySelector('.portal')) {
69
+ observer = new MutationObserver(() => {
70
+ if (!document.querySelector('.portal')) {
71
+ unlock();
72
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
73
+ }
74
+ });
75
+ observer.observe(document.body, { childList: true, subtree: true });
76
+ // Fallback in case observer misses changes
77
+ timer = window.setTimeout(() => {
78
+ unlock();
79
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
80
+ }, 3000);
81
+ }
82
+ else {
83
+ unlock();
84
+ }
56
85
  }
86
+ return () => {
87
+ if (observer)
88
+ observer.disconnect();
89
+ if (timer)
90
+ window.clearTimeout(timer);
91
+ };
57
92
  }, [disable]);
58
93
  }
59
94
 
@@ -987,7 +1022,7 @@ function DBoxFile(_a) {
987
1022
  }
988
1023
 
989
1024
  const DButton = React.forwardRef((props, ref) => {
990
- 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' } = props, rest = tslib.__rest(props, ["color", "size", "variant", "text", "children", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartMaterialStyle", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndMaterialStyle", "loading", "loadingText", "loadingAriaLabel", "disabled", "className", "style", "dataAttributes", "onClick", "type"]);
1025
+ 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 } = props, rest = tslib.__rest(props, ["color", "size", "variant", "text", "children", "iconStart", "iconStartFamilyClass", "iconStartFamilyPrefix", "iconStartMaterialStyle", "iconEnd", "iconEndFamilyClass", "iconEndFamilyPrefix", "iconEndMaterialStyle", "loading", "loadingText", "loadingAriaLabel", "disabled", "className", "style", "dataAttributes", "onClick", "type", "target", "rel"]);
991
1026
  const [buttonWidth, setButtonWidth] = React.useState();
992
1027
  const buttonRef = React.useRef(null);
993
1028
  const isDisabled = React.useMemo(() => disabled || loading, [disabled, loading]);
@@ -1024,6 +1059,19 @@ const DButton = React.forwardRef((props, ref) => {
1024
1059
  }
1025
1060
  // eslint-disable-next-line react-hooks/exhaustive-deps
1026
1061
  }, [content, iconEnd, iconStart]);
1062
+ if (props.href) {
1063
+ return (jsxRuntime.jsxs("a", Object.assign({ href: props.href, target: target, rel: rel, ref: (node) => {
1064
+ buttonRef.current = node;
1065
+ if (typeof ref === 'function')
1066
+ ref(node);
1067
+ // eslint-disable-next-line max-len
1068
+ // eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
1069
+ else if (ref)
1070
+ ref.current = node;
1071
+ }, className: classNames(classes, className), style: Object.assign(Object.assign({}, style), (loading && buttonWidth
1072
+ ? { minWidth: `${buttonWidth}px` }
1073
+ : undefined)), "aria-label": ariaLabel, "aria-busy": loading, "aria-disabled": isDisabled, onClick: handleClick }, dataAttributes, { children: [loading && (jsxRuntime.jsxs("span", { className: "btn-loading", children: [jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", "aria-hidden": "true" }), loadingText && jsxRuntime.jsx("span", { role: "status", children: loadingText })] })), !loading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconStart && (jsxRuntime.jsx(DIcon, { icon: iconStart, familyClass: iconStartFamilyClass, familyPrefix: iconStartFamilyPrefix, materialStyle: iconStartMaterialStyle })), content, iconEnd && (jsxRuntime.jsx(DIcon, { icon: iconEnd, familyClass: iconEndFamilyClass, familyPrefix: iconEndFamilyPrefix, materialStyle: iconEndMaterialStyle }))] }))] })));
1074
+ }
1027
1075
  return (jsxRuntime.jsxs("button", Object.assign({ ref: (node) => {
1028
1076
  buttonRef.current = node;
1029
1077
  if (typeof ref === 'function')
@@ -1040,23 +1088,32 @@ const DButton = React.forwardRef((props, ref) => {
1040
1088
  });
1041
1089
  DButton.displayName = 'DButton';
1042
1090
 
1043
- function DButtonIcon({ id, icon, size, className, variant, state, loadingAriaLabel, iconMaterialStyle, ariaLabel, color = 'primary', type = 'button', loading = false, disabled = false, stopPropagationEnabled = true, style, iconFamilyClass, iconFamilyPrefix, dataAttributes, onClick, }) {
1091
+ function DButtonIcon({ id, icon, size, className, variant, state, loadingAriaLabel, iconMaterialStyle, ariaLabel, color = 'primary', type = 'button', loading = false, disabled = false, href, target, rel, stopPropagationEnabled = true, style, iconFamilyClass, iconFamilyPrefix, dataAttributes, onClick, }) {
1044
1092
  const generateClasses = React.useMemo(() => {
1045
1093
  const variantClass = variant
1046
1094
  ? `btn-${variant}-${color}`
1047
1095
  : `btn-${color}`;
1048
1096
  return Object.assign(Object.assign(Object.assign({ 'btn d-button-icon': true, [variantClass]: true }, size && { [`btn-${size}`]: true }), (state && state !== 'disabled') && { [state]: true }), { loading });
1049
1097
  }, [variant, color, size, state, loading]);
1098
+ const isDisabled = React.useMemo(() => (state === 'disabled' || loading || disabled), [state, loading, disabled]);
1050
1099
  const clickHandler = React.useCallback((event) => {
1051
1100
  if (stopPropagationEnabled) {
1052
1101
  event.stopPropagation();
1053
1102
  }
1103
+ if (isDisabled) {
1104
+ event.preventDefault();
1105
+ return;
1106
+ }
1054
1107
  onClick === null || onClick === void 0 ? void 0 : onClick(event);
1055
- }, [stopPropagationEnabled, onClick]);
1056
- const isDisabled = React.useMemo(() => (state === 'disabled' || loading || disabled), [state, loading, disabled]);
1108
+ }, [stopPropagationEnabled, onClick, isDisabled]);
1057
1109
  const newAriaLabel = React.useMemo(() => (loading
1058
1110
  ? (loadingAriaLabel || ariaLabel)
1059
1111
  : (ariaLabel)), [ariaLabel, loading, loadingAriaLabel]);
1112
+ if (href) {
1113
+ return (jsxRuntime.jsx("a", Object.assign({ href: href, target: target, rel: rel, className: classNames(generateClasses, className), style: style, onClick: clickHandler, "aria-label": newAriaLabel, "aria-disabled": isDisabled, id: id }, dataAttributes, { children: loading
1114
+ ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1115
+ : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })) })));
1116
+ }
1060
1117
  return (jsxRuntime.jsx("button", Object.assign({ className: classNames(generateClasses, className), style: style, type: type, disabled: isDisabled, onClick: clickHandler, "aria-label": newAriaLabel, id: id }, dataAttributes, { children: loading
1061
1118
  ? (jsxRuntime.jsx("span", { className: "spinner-border spinner-border-sm", role: "status", "aria-hidden": "true", children: jsxRuntime.jsx("span", { className: "visually-hidden", children: "Loading..." }) }))
1062
1119
  : (jsxRuntime.jsx(DIcon, { icon: icon, familyClass: iconFamilyClass, familyPrefix: iconFamilyPrefix, materialStyle: iconMaterialStyle })) })));
@@ -2439,7 +2496,7 @@ ForwardedDInputPhone.displayName = 'DInputPhone';
2439
2496
  const DEFAULT_IMAGE = 'https://cdn.modyo.cloud/uploads/06b434f7-b943-4f54-9543-84a904e189aa/original/Visa_Logo_1_.png';
2440
2497
  const CHIP_IMAGE = 'https://cdn.modyo.cloud/uploads/4660ad00-e5d8-477e-8919-52b53d0a26fb/original/chip-debit-svgrepo-com_1_.png';
2441
2498
  function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder', logoImage, isChipVisible = true, className, isVertical = false, }) {
2442
- return (jsxRuntime.jsxs("div", { className: classNames('d-credit-card overflow-hidden text-white', 'position-relative rounded-3', 'd-none d-lg-flex', isVertical && 'is-vertical', className), children: [jsxRuntime.jsxs("div", { className: "d-credit-card-header", children: [jsxRuntime.jsx("img", { src: logoImage || DEFAULT_IMAGE, alt: brand, className: "d-credit-card-logo", width: 100 }), isChipVisible && (jsxRuntime.jsx("div", { className: "d-credit-card-chip p-2 rounded-2", children: jsxRuntime.jsx("img", { src: CHIP_IMAGE, alt: "chip", width: 30, className: "d-credit-card-chip-image" }) }))] }), jsxRuntime.jsxs("div", { className: "d-credit-card-details mt-auto d-none d-sm-block", children: [jsxRuntime.jsx("div", { className: "d-credit-card-number d-none d-sm-block mb-4", children: number }), jsxRuntime.jsx("small", { className: "d-block opacity-50", children: holderText }), jsxRuntime.jsx("span", { className: "name", children: name })] })] }));
2499
+ return (jsxRuntime.jsxs("div", { className: classNames('d-credit-card overflow-hidden text-white', 'position-relative rounded-3', 'd-flex', isVertical && 'is-vertical', className), children: [jsxRuntime.jsxs("div", { className: "d-credit-card-header", children: [jsxRuntime.jsx("img", { src: logoImage || DEFAULT_IMAGE, alt: brand, className: "d-credit-card-logo", width: 100 }), isChipVisible && (jsxRuntime.jsx("div", { className: "d-credit-card-chip p-2 rounded-2", children: jsxRuntime.jsx("img", { src: CHIP_IMAGE, alt: "chip", width: 30, className: "d-credit-card-chip-image" }) }))] }), jsxRuntime.jsxs("div", { className: "d-credit-card-details mt-auto d-none d-sm-block", children: [jsxRuntime.jsx("div", { className: "d-credit-card-number d-none d-sm-block mb-4", children: number }), jsxRuntime.jsx("small", { className: "d-block opacity-50", children: holderText }), jsxRuntime.jsx("span", { className: "name", children: name })] })] }));
2443
2500
  }
2444
2501
 
2445
2502
  const getItemClass = (action) => {
@@ -2581,7 +2638,7 @@ function useScreenshotWebShare() {
2581
2638
  };
2582
2639
  }
2583
2640
 
2584
- function DVoucher({ amount, amountDetails, icon = 'CircleCheckBig', color = 'success', title, onError, message, downloadText = 'Download', shareText = 'Share', children, }) {
2641
+ function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText = 'Download', shareText = 'Share', className, children, }) {
2585
2642
  const { shareRef, share } = useScreenshotWebShare();
2586
2643
  const { downloadRef, download } = useScreenshotDownload();
2587
2644
  const handleShare = () => {
@@ -2606,10 +2663,94 @@ function DVoucher({ amount, amountDetails, icon = 'CircleCheckBig', color = 'suc
2606
2663
  // Error already handled by onError
2607
2664
  });
2608
2665
  };
2609
- return (jsxRuntime.jsx("div", { className: "d-voucher", ref: (el) => {
2666
+ const defaultIconProps = {
2667
+ icon: 'CircleCheckBig',
2668
+ color: 'success',
2669
+ size: '2rem',
2670
+ hasCircle: true,
2671
+ };
2672
+ const resolvedIconProps = (() => {
2673
+ if (icon === false || icon == null)
2674
+ return null;
2675
+ if (typeof icon === 'string')
2676
+ return Object.assign(Object.assign({}, defaultIconProps), { icon });
2677
+ if (typeof icon === 'object')
2678
+ return Object.assign(Object.assign({}, defaultIconProps), icon);
2679
+ return defaultIconProps;
2680
+ })();
2681
+ return (jsxRuntime.jsx("div", { className: classNames('d-voucher', className), ref: (el) => {
2610
2682
  shareRef.current = el;
2611
2683
  downloadRef.current = el;
2612
- }, children: jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "d-voucher-header", children: [jsxRuntime.jsx(DIcon, { icon: icon, color: color }), jsxRuntime.jsxs("div", { className: "text-center", children: [jsxRuntime.jsx("h3", { className: "mb-2", children: title }), jsxRuntime.jsx("p", { className: "m-0", children: message })] })] }), amount && (jsxRuntime.jsxs("div", { className: "d-voucher-amount", children: [jsxRuntime.jsx("div", { className: classNames('text-center fw-bold fs-3', amountDetails ? 'mb-1' : 'm-0'), children: amount }), amountDetails] })), jsxRuntime.jsx("hr", { className: "my-4" }), children, jsxRuntime.jsx("hr", { className: "my-4" }), jsxRuntime.jsxs("div", { className: "d-voucher-footer", children: [jsxRuntime.jsx(DButton, { onClick: handleShare, iconStart: "Share2", text: shareText, variant: "outline", size: "sm" }), jsxRuntime.jsx(DButton, { onClick: handleDownload, iconStart: "Download", text: downloadText, variant: "outline", size: "sm" })] })] }) }));
2684
+ }, children: jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "d-voucher-header", children: [resolvedIconProps && (jsxRuntime.jsx(DIcon, Object.assign({}, resolvedIconProps))), jsxRuntime.jsxs("div", { className: "text-center", children: [jsxRuntime.jsx("h3", { className: "mb-2", children: title }), jsxRuntime.jsx("p", { className: "m-0", children: message })] })] }), amount && (jsxRuntime.jsxs("div", { className: "d-voucher-amount", children: [jsxRuntime.jsx("div", { className: classNames('text-center fw-bold fs-3', amountDetails ? 'mb-1' : 'm-0'), children: amount }), amountDetails] })), jsxRuntime.jsx("hr", { className: "my-4" }), children, jsxRuntime.jsx("hr", { className: "my-4" }), jsxRuntime.jsxs("div", { className: "d-voucher-footer", children: [jsxRuntime.jsx(DButton, { onClick: handleShare, iconStart: "Share2", text: shareText, variant: "outline", size: "sm" }), jsxRuntime.jsx(DButton, { onClick: handleDownload, iconStart: "Download", text: downloadText, variant: "outline", size: "sm" })] })] }) }));
2685
+ }
2686
+
2687
+ function useCountdown(seconds) {
2688
+ const [secondsLeft, setSecondsLeft] = React.useState(seconds);
2689
+ const [isActive, setIsActive] = React.useState(true);
2690
+ const resetCountdown = React.useCallback((newSeconds = seconds) => {
2691
+ setIsActive(false);
2692
+ setSecondsLeft(newSeconds);
2693
+ }, [seconds]);
2694
+ const restartCountdown = React.useCallback(() => {
2695
+ resetCountdown(seconds);
2696
+ setIsActive(true);
2697
+ }, [resetCountdown, seconds]);
2698
+ React.useEffect(() => {
2699
+ if (!isActive) {
2700
+ return () => { };
2701
+ }
2702
+ const interval = setInterval(() => {
2703
+ setSecondsLeft((prevSeconds) => {
2704
+ const newSeconds = prevSeconds - 1;
2705
+ if (newSeconds <= 0) {
2706
+ clearInterval(interval);
2707
+ setIsActive(false);
2708
+ return 0;
2709
+ }
2710
+ return newSeconds;
2711
+ });
2712
+ }, 1000);
2713
+ return () => clearInterval(interval);
2714
+ }, [isActive]);
2715
+ return { secondsLeft, restartCountdown };
2716
+ }
2717
+
2718
+ const defaultMessage = (secs) => (secs > 0
2719
+ ? `Didn't get any code? Resend in: ${secs}s`
2720
+ : "Didn't get any code?");
2721
+ function OtpCountdown({ seconds, resendText, message, }) {
2722
+ const { secondsLeft, restartCountdown } = useCountdown(seconds);
2723
+ return (jsxRuntime.jsxs("div", { className: "d-flex gap-2 align-items-center", children: [jsxRuntime.jsx("p", { className: "mb-0", children: message ? message(secondsLeft) : defaultMessage(secondsLeft) }), jsxRuntime.jsx(DButton, { text: resendText, variant: "link", disabled: secondsLeft > 0, onClick: restartCountdown })] }));
2724
+ }
2725
+
2726
+ const TEXT_PROPS = {
2727
+ resend: 'Resend',
2728
+ resendText: 'Resend code',
2729
+ submit: 'Authorize and continue',
2730
+ title: 'We will send you a 6-digit code to your associated phone number so you can continue with your request.',
2731
+ contact: (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { children: "Problems with your digital token? Contact us" }), ' ', jsxRuntime.jsx("a", { href: "https://www.modyo.com", className: "link-primary text-nowrap", target: "_blank", rel: "noreferrer", children: "Contact us" })] })),
2732
+ };
2733
+ function DOtp({ className, action, isLoading, otpSize = 6, texts = TEXT_PROPS, seconds = 15, }) {
2734
+ const [otp, setOtp] = React.useState('');
2735
+ const [invalid, setInvalid] = React.useState(false);
2736
+ const handler = React.useCallback(async () => {
2737
+ if (otp.length < otpSize) {
2738
+ setInvalid(true);
2739
+ return;
2740
+ }
2741
+ setInvalid(false);
2742
+ await action();
2743
+ }, [
2744
+ otp.length,
2745
+ action,
2746
+ otpSize,
2747
+ ]);
2748
+ return (jsxRuntime.jsxs("div", { className: className, children: [jsxRuntime.jsx("p", { children: texts.title }), jsxRuntime.jsxs("div", { className: "d-flex flex-column gap-6 pb-4 px-3", children: [jsxRuntime.jsxs("div", { className: "d-flex flex-column gap-6", children: [jsxRuntime.jsx(DInputPin, { className: "modal-otp-pin", characters: otpSize, onChange: (e) => setOtp(e), invalid: invalid && otp.length < otpSize, placeholder: "0" }), jsxRuntime.jsx(OtpCountdown, { seconds: seconds, resendText: texts.resend })] }), jsxRuntime.jsx("hr", { className: "m-0" }), jsxRuntime.jsxs("div", { className: "d-flex flex-column flex-lg-row gap-4 align-items-center", children: [jsxRuntime.jsx(DButton, { text: texts.submit, onClick: () => {
2749
+ handler().catch((err) => {
2750
+ // eslint-disable-next-line no-console
2751
+ console.error('Error in DOtp action:', err);
2752
+ });
2753
+ }, loading: isLoading }), jsxRuntime.jsx("p", { className: "small ms-lg-auto mb-0", children: texts.contact })] })] })] }));
2613
2754
  }
2614
2755
 
2615
2756
  exports.DAlert = DAlert;
@@ -2658,6 +2799,7 @@ exports.DOffcanvas = DOffcanvas;
2658
2799
  exports.DOffcanvasBody = DOffcanvasBody;
2659
2800
  exports.DOffcanvasFooter = DOffcanvasFooter;
2660
2801
  exports.DOffcanvasHeader = DOffcanvasHeader;
2802
+ exports.DOtp = DOtp;
2661
2803
  exports.DPaginator = DPaginator;
2662
2804
  exports.DPasswordStrengthMeter = DPasswordStrengthMeter;
2663
2805
  exports.DPopover = DPopover;