@dynamic-framework/ui-react 2.7.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.
Files changed (40) hide show
  1. package/dist/css/dynamic-ui.css +272 -1
  2. package/dist/css/dynamic-ui.min.css +2 -2
  3. package/dist/index.esm.js +503 -267
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +507 -266
  6. package/dist/index.js.map +1 -1
  7. package/dist/types/components/DDropdown/DDropdown.d.ts +10 -2
  8. package/dist/types/components/DOtp/DOtp.d.ts +1 -0
  9. package/dist/types/components/DOtp/components/OtpCountdown.d.ts +3 -2
  10. package/dist/types/components/DVoucher/DVoucher.d.ts +3 -1
  11. package/dist/types/components/DVoucher/hooks/useScreenshotDownload.d.ts +1 -1
  12. package/dist/types/components/DVoucher/hooks/useScreenshotDownload.spec.d.ts +1 -0
  13. package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.d.ts +1 -1
  14. package/dist/types/components/DVoucher/hooks/useScreenshotWebShare.spec.d.ts +1 -0
  15. package/dist/types/components/DVoucher/index.d.ts +3 -0
  16. package/dist/types/components/index.d.ts +1 -1
  17. package/dist/types/hooks/index.d.ts +3 -0
  18. package/dist/types/hooks/tests/useCountdown.spec.d.ts +1 -0
  19. package/dist/types/hooks/useCountdown.d.ts +14 -0
  20. package/dist/types/hooks/useInputCurrency.d.ts +1 -6
  21. package/dist/types/hooks/useOtp.d.ts +98 -0
  22. package/package.json +1 -1
  23. package/src/style/abstracts/variables/_+import.scss +1 -0
  24. package/src/style/abstracts/variables/_input-currency.scss +1 -0
  25. package/src/style/components/_+import.scss +7 -0
  26. package/src/style/components/_d-box-file.scss +5 -0
  27. package/src/style/components/_d-close.scss +1 -0
  28. package/src/style/components/_d-confirm-modal.scss +17 -0
  29. package/src/style/components/_d-data-state-wrapper.scss +47 -0
  30. package/src/style/components/_d-datepicker.scss +11 -0
  31. package/src/style/components/_d-error-boundary.scss +7 -0
  32. package/src/style/components/_d-input-currency.scss +5 -0
  33. package/src/style/components/_d-otp.scss +54 -0
  34. package/src/style/components/_d-password-strength-meter.scss +76 -0
  35. package/src/style/components/_d-popover.scss +4 -0
  36. package/src/style/components/_d-stepper.scss +8 -0
  37. package/src/style/components/_d-tabs.scss +18 -0
  38. package/src/style/components/_d-voucher.scss +30 -0
  39. package/dist/types/components/DOtp/DOtpspec.d.ts +0 -0
  40. package/dist/types/components/DOtp/hooks/useCountdown.d.ts +0 -4
@@ -8,7 +8,14 @@ export type DropdownAction = {
8
8
  isDivider?: boolean;
9
9
  label: string;
10
10
  };
11
- type DropdownPlacement = 'auto' | 'down' | 'up' | 'start' | 'end';
11
+ type ResolvedSide = 'down' | 'up' | 'start' | 'end';
12
+ type DropdownPlacement = 'auto' | ResolvedSide;
13
+ /**
14
+ * Horizontal alignment of the menu relative to the toggle, applied only when
15
+ * the resolved placement is vertical (`down`/`up`) — mirrors Bootstrap's
16
+ * `dropdown-menu-end`/`dropdown-center` menu alignment options.
17
+ */
18
+ export type DropdownAlignment = 'start' | 'end' | 'center';
12
19
  type Props = {
13
20
  actions: DropdownAction[];
14
21
  dropdownToggle?: ((props: {
@@ -19,6 +26,7 @@ type Props = {
19
26
  classNameMenu?: string;
20
27
  asPortal?: boolean;
21
28
  placement?: DropdownPlacement;
29
+ alignment?: DropdownAlignment;
22
30
  };
23
- export default function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal, placement, }: Props): import("react").JSX.Element;
31
+ export default function DDropdown({ actions, dropdownToggle, className, classNameMenu, asPortal, placement, alignment, }: Props): import("react").JSX.Element;
24
32
  export {};
@@ -11,6 +11,7 @@ export type PropsOtp = PropsWithChildren<{
11
11
  title?: string;
12
12
  contact?: string | React.ReactNode;
13
13
  resendText?: string;
14
+ invalidCode?: string;
14
15
  };
15
16
  }>;
16
17
  export default function DOtp({ className, action, isLoading, otpSize, texts, seconds, }: PropsOtp): import("react").JSX.Element;
@@ -1,8 +1,9 @@
1
1
  /// <reference types="react" />
2
2
  type Props = {
3
- seconds: number;
3
+ secondsLeft: number;
4
+ restartCountdown: () => void;
4
5
  resendText?: string;
5
6
  message?: (secondsLeft: number) => string;
6
7
  };
7
- export default function OtpCountdown({ seconds, resendText, message, }: Props): import("react").JSX.Element;
8
+ export default function OtpCountdown({ secondsLeft, restartCountdown, resendText, message, }: Props): import("react").JSX.Element;
8
9
  export {};
@@ -9,7 +9,9 @@ type Props = PropsWithChildren<{
9
9
  title: string;
10
10
  downloadText?: string;
11
11
  shareText?: string;
12
+ fileName?: string;
13
+ hideActions?: boolean;
12
14
  onError?: (err: Error) => Promise<void> | void;
13
15
  }>;
14
- export default function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText, shareText, className, children, }: Props): import("react").JSX.Element;
16
+ export default function DVoucher({ amount, amountDetails, icon, title, onError, message, downloadText, shareText, fileName, hideActions, className, children, }: Props): import("react").JSX.Element;
15
17
  export {};
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
2
  export default function useScreenshotDownload(): {
3
- download: () => Promise<void>;
3
+ download: (fileName?: string) => Promise<void>;
4
4
  downloadRef: import("react").RefObject<HTMLDivElement | null>;
5
5
  };
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
2
  export default function useScreenshotWebShare(): {
3
- share: () => Promise<void>;
3
+ share: (fileName?: string) => Promise<void>;
4
4
  shareRef: import("react").RefObject<HTMLDivElement | null>;
5
5
  };
@@ -1,2 +1,5 @@
1
1
  import DVoucher from './DVoucher';
2
+ export { default as useScreenshot } from './hooks/useScreenshot';
3
+ export { default as useScreenshotDownload } from './hooks/useScreenshotDownload';
4
+ export { default as useScreenshotWebShare } from './hooks/useScreenshotWebShare';
2
5
  export default DVoucher;
@@ -50,7 +50,7 @@ export type { DToastContainerProps, DToastOptions, ToastData } from './DToastCon
50
50
  export { default as DInputPhone } from './DInputPhone';
51
51
  export { default as DCreditCard } from './DCreditCard';
52
52
  export { default as DDropdown } from './DDropdown';
53
- export { default as DVoucher } from './DVoucher';
53
+ export { default as DVoucher, useScreenshot, useScreenshotDownload, useScreenshotWebShare, } from './DVoucher';
54
54
  export { default as DOtp } from './DOtp';
55
55
  export { default as DErrorBoundary, useErrorBoundary, type FallbackProps, getErrorMessage, } from './DErrorBoundary';
56
56
  export { default as DDataStateWrapper, EmptyState, ErrorState, LoadingState, } from './DDataStateWrapper';
@@ -9,3 +9,6 @@ export { default as useMediaQuery } from './useMediaQuery';
9
9
  export { default as useConfirmModal } from './useConfirmModal';
10
10
  export type { UseConfirmModalConfig, UseConfirmModalReturn, ConfirmModalColor, CriticalConfirmConfig, } from './useConfirmModal';
11
11
  export { useMediaBreakpointUpXs, useMediaBreakpointUpSm, useMediaBreakpointUpMd, useMediaBreakpointUpLg, useMediaBreakpointUpXl, useMediaBreakpointUpXxl, } from './useMediaBreakpointUp';
12
+ export { default as useCountdown } from './useCountdown';
13
+ export { default as useOtp } from './useOtp';
14
+ export type { UseOtpConfig, UseOtpReturn } from './useOtp';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Generic seconds-based countdown hook.
3
+ *
4
+ * Starts counting down from `seconds` to `0` and exposes `restartCountdown`
5
+ * to reset and restart the timer (e.g. for "resend code in Xs" patterns,
6
+ * session-expiry banners, or any other cooldown/throttle UI).
7
+ *
8
+ * @example
9
+ * const { secondsLeft, restartCountdown } = useCountdown(30);
10
+ */
11
+ export default function useCountdown(seconds: number): {
12
+ secondsLeft: number;
13
+ restartCountdown: () => void;
14
+ };
@@ -1,15 +1,10 @@
1
1
  import type { RefObject, ForwardedRef, FocusEvent } from 'react';
2
2
  import type { Options } from 'currency.js';
3
- import type { CustomStyles } from '../components/interface';
4
- export default function useInputCurrency(currencyOptions: Options, value?: number, onFocus?: (event: FocusEvent<HTMLInputElement>) => void, onChange?: (value?: number) => void, onBlur?: (event: FocusEvent<HTMLInputElement>) => void, ref?: ForwardedRef<HTMLInputElement>): {
3
+ export default function useInputCurrency(currencyOptions: Options, value?: number, onFocus?: (event: FocusEvent<HTMLInputElement>) => void, onChange?: (value?: number) => void, onBlur?: (event: FocusEvent<HTMLInputElement>) => void, ref?: ForwardedRef<HTMLInputElement>, minValue?: number, maxValue?: number): {
5
4
  inputRef: RefObject<HTMLInputElement | null>;
6
5
  innerValue: string;
7
6
  innerType: string;
8
7
  handleOnFocus: (event: FocusEvent<HTMLInputElement>) => void;
9
8
  handleOnChange: (newValue?: string) => void;
10
9
  handleOnBlur: (event: FocusEvent<HTMLInputElement>) => void;
11
- generateStyleVariables: CustomStyles;
12
- generateSymbolStyleVariables: {
13
- color: string;
14
- };
15
10
  };
@@ -0,0 +1,98 @@
1
+ export type UseOtpConfig = {
2
+ /** Called when the OTP reaches the expected length and `submit()` is invoked. */
3
+ action: () => Promise<void> | void;
4
+ /** Expected number of digits/characters for a valid OTP. Defaults to `6`. */
5
+ otpSize?: number;
6
+ /** Initial countdown (in seconds) before resend becomes available. Defaults to `15`. */
7
+ seconds?: number;
8
+ };
9
+ export type UseOtpReturn = {
10
+ /** Current OTP value. */
11
+ otp: string;
12
+ /**
13
+ * Updates the OTP value (wire this to your custom pin input's `onChange`).
14
+ * Also clears `invalid`/`error` as soon as the value actually changes, so
15
+ * the error state disappears once the user edits the code (a no-op call
16
+ * with the same value, e.g. from an uncontrolled input re-notifying its
17
+ * current value, does not reset `invalid`/`error`).
18
+ */
19
+ setOtp: (value: string) => void;
20
+ /**
21
+ * `true` when `submit()` is called with an OTP shorter than `otpSize`, or
22
+ * when `action` throws/rejects (e.g. the backend rejected the entered
23
+ * code as incorrect).
24
+ */
25
+ invalid: boolean;
26
+ /**
27
+ * The value thrown/rejected by `action`, or `null` otherwise. Use it to
28
+ * render a specific message for a wrong code, as opposed to a too-short
29
+ * one.
30
+ */
31
+ error: unknown;
32
+ /**
33
+ * Validates the OTP length; if valid, calls `action`. If `action`
34
+ * rejects, the returned promise rejects too (so callers can still
35
+ * `.catch()` it for logging), and `invalid`/`error` are set beforehand.
36
+ */
37
+ submit: () => Promise<void>;
38
+ /**
39
+ * `true` while `action` is being awaited inside `submit()` (i.e. the OTP
40
+ * passed the length check and the async action hasn't settled yet). Wire
41
+ * it to your submit button's loading/disabled state. It's `false` while
42
+ * idle and immediately after `action` settles (whether it resolves or
43
+ * rejects), and it's never set to `true` for the too-short/length
44
+ * validation failure since `action` isn't called in that case.
45
+ */
46
+ isLoading: boolean;
47
+ /** Seconds remaining until resend is available. */
48
+ secondsLeft: number;
49
+ /** Restarts the resend countdown (call it from your "Resend" action). */
50
+ restartCountdown: () => void;
51
+ };
52
+ /**
53
+ * Headless hook exposing the same OTP logic used internally by `DOtp`
54
+ * (code state, length + correctness validation, submit action and resend
55
+ * countdown), without any bundled markup/styles.
56
+ *
57
+ * Use this when the default `DOtp` component doesn't match your design and
58
+ * you need to build a fully custom UI while reusing the same behavior.
59
+ *
60
+ * Internally composes `useCountdown` for the resend timer and re-exposes
61
+ * `secondsLeft`/`restartCountdown`, so there is no need to call
62
+ * `useCountdown` separately.
63
+ *
64
+ * `invalid` becomes `true` both when the OTP is shorter than `otpSize` and
65
+ * when `action` throws/rejects (e.g. the backend rejected the entered code
66
+ * as incorrect). In the latter case, the thrown value is exposed as `error`
67
+ * so custom UIs can render an appropriate message (e.g. "Invalid code" vs.
68
+ * "Enter all digits"). `invalid`/`error` are cleared automatically as soon
69
+ * as the user edits the code via `setOtp`.
70
+ *
71
+ * @example
72
+ * function CustomOtp() {
73
+ * const {
74
+ * otp,
75
+ * setOtp,
76
+ * invalid,
77
+ * error,
78
+ * submit,
79
+ * isLoading,
80
+ * secondsLeft,
81
+ * restartCountdown,
82
+ * } = useOtp({ action: async () => verifyOtp(otp), otpSize: 6, seconds: 15 });
83
+ *
84
+ * return (
85
+ * <>
86
+ * <MyCustomPinInput value={otp} onChange={setOtp} invalid={invalid} />
87
+ * {invalid && <span>{error ? 'Invalid code' : 'Enter all digits'}</span>}
88
+ * <button disabled={isLoading} onClick={() => { submit().catch(() => {}); }}>
89
+ * {isLoading ? 'Submitting...' : 'Submit'}
90
+ * </button>
91
+ * <button disabled={secondsLeft > 0} onClick={restartCountdown}>
92
+ * Resend {secondsLeft > 0 ? `(${secondsLeft}s)` : ''}
93
+ * </button>
94
+ * </>
95
+ * );
96
+ * }
97
+ */
98
+ export default function useOtp({ action, otpSize, seconds, }: UseOtpConfig): UseOtpReturn;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "sideEffects": [
4
4
  "*.css"
5
5
  ],
6
- "version": "2.7.0",
6
+ "version": "2.8.0",
7
7
  "description": "React Dynamic Framework",
8
8
  "license": "https://github.com/dynamic-framework/dynamic-ui/blob/master/LICENSE.md",
9
9
  "repository": {
@@ -94,4 +94,5 @@ $input-btn-border-width: var(--#{$prefix}border-width) !default;
94
94
  @import "box-file";
95
95
  @import "datepicker";
96
96
  @import "input-phone";
97
+ @import "input-currency";
97
98
  // end custom
@@ -0,0 +1 @@
1
+ $input-currency-symbol-color: var(--#{$prefix}secondary) !default;
@@ -22,3 +22,10 @@
22
22
  @import "d-table-head";
23
23
  @import "d-input-phone";
24
24
  @import "d-credit-card";
25
+ @import "d-input-currency";
26
+ @import "d-confirm-modal";
27
+ @import "d-data-state-wrapper";
28
+ @import "d-error-boundary";
29
+ @import "d-otp";
30
+ @import "d-password-strength-meter";
31
+ @import "d-stepper";
@@ -69,6 +69,11 @@
69
69
  }
70
70
  }
71
71
 
72
+ .d-box-file-empty-text {
73
+ margin: 0;
74
+ text-align: center;
75
+ }
76
+
72
77
  .d-box-files {
73
78
  --#{$prefix}box-file-list-gap: #{$box-file-list-gap};
74
79
  --#{$prefix}box-file-list-padding: #{$box-file-list-padding};
@@ -1,5 +1,6 @@
1
1
  .d-close {
2
2
  display: flex;
3
+ align-self: center;
3
4
  padding: 0;
4
5
  background: transparent;
5
6
  border: 0;
@@ -0,0 +1,17 @@
1
+ .confirm-modal {
2
+ --#{$prefix}confirm-modal-body-padding-y: var(--#{$prefix}ref-spacer-3);
3
+ --#{$prefix}confirm-modal-body-padding-x: var(--#{$prefix}ref-spacer-5);
4
+ --#{$prefix}confirm-modal-field-gap: var(--#{$prefix}ref-spacer-4);
5
+ }
6
+
7
+ .d-confirm-modal-body {
8
+ padding: var(--#{$prefix}confirm-modal-body-padding-y) var(--#{$prefix}confirm-modal-body-padding-x);
9
+ }
10
+
11
+ .d-confirm-modal-message {
12
+ margin-bottom: var(--#{$prefix}confirm-modal-field-gap);
13
+ }
14
+
15
+ .d-confirm-modal-field {
16
+ margin-bottom: var(--#{$prefix}confirm-modal-field-gap);
17
+ }
@@ -0,0 +1,47 @@
1
+ .d-empty-state {
2
+ --#{$prefix}empty-state-padding: var(--#{$prefix}ref-spacer-5);
3
+ --#{$prefix}empty-state-spacing: var(--#{$prefix}ref-spacer-3);
4
+ --#{$prefix}empty-state-color: var(--#{$prefix}secondary);
5
+
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ justify-content: center;
10
+ padding: var(--#{$prefix}empty-state-padding);
11
+ text-align: center;
12
+ }
13
+
14
+ .d-empty-state-icon {
15
+ margin-bottom: var(--#{$prefix}empty-state-spacing);
16
+ color: var(--#{$prefix}empty-state-color);
17
+ }
18
+
19
+ .d-empty-state-message {
20
+ margin-bottom: var(--#{$prefix}empty-state-spacing);
21
+ color: var(--#{$prefix}empty-state-color);
22
+ }
23
+
24
+ .d-error-state {
25
+ --#{$prefix}error-state-gap: var(--#{$prefix}ref-spacer-3);
26
+
27
+ display: flex;
28
+ gap: var(--#{$prefix}error-state-gap);
29
+ align-items: center;
30
+ }
31
+
32
+ .d-error-state-content {
33
+ flex: 1 1 auto;
34
+ }
35
+
36
+ .d-error-state-message {
37
+ margin-bottom: 0;
38
+ }
39
+
40
+ .d-loading-state {
41
+ --#{$prefix}loading-state-padding: var(--#{$prefix}ref-spacer-4);
42
+
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ padding: var(--#{$prefix}loading-state-padding);
47
+ }
@@ -215,6 +215,12 @@
215
215
  }
216
216
  }
217
217
 
218
+ &-year-selector {
219
+ display: flex;
220
+ flex-grow: 1;
221
+ justify-content: center;
222
+ }
223
+
218
224
  &-day-selector {
219
225
  margin-bottom: var(--#{$prefix}ref-spacer-4);
220
226
 
@@ -287,6 +293,11 @@
287
293
  margin-top: -.5rem;
288
294
  }
289
295
 
296
+ .datepicker-top-header-title {
297
+ margin-bottom: 0;
298
+ font-weight: var(--#{$prefix}fw-normal);
299
+ }
300
+
290
301
  .react-datepicker__year,
291
302
  .react-datepicker__quarterPicker {
292
303
  --#{$prefix}datepicker-body-gap: 1rem;
@@ -0,0 +1,7 @@
1
+ .d-error-boundary-content {
2
+ --#{$prefix}error-boundary-gap: var(--#{$prefix}ref-spacer-2);
3
+
4
+ display: flex;
5
+ gap: var(--#{$prefix}error-boundary-gap);
6
+ align-items: center;
7
+ }
@@ -0,0 +1,5 @@
1
+ .d-input-currency-symbol {
2
+ --#{$prefix}input-currency-symbol-color: var(--#{$prefix}input-currency-component-symbol-color, #{$input-currency-symbol-color});
3
+
4
+ color: var(--#{$prefix}input-currency-symbol-color);
5
+ }
@@ -0,0 +1,54 @@
1
+ .d-otp {
2
+ --#{$prefix}otp-content-gap: var(--#{$prefix}ref-spacer-6);
3
+ --#{$prefix}otp-content-padding-x: var(--#{$prefix}ref-spacer-3);
4
+ --#{$prefix}otp-content-padding-bottom: var(--#{$prefix}ref-spacer-4);
5
+ --#{$prefix}otp-footer-gap: var(--#{$prefix}ref-spacer-4);
6
+ }
7
+
8
+ .d-otp-content {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: var(--#{$prefix}otp-content-gap);
12
+ padding: 0 var(--#{$prefix}otp-content-padding-x) var(--#{$prefix}otp-content-padding-bottom);
13
+ }
14
+
15
+ .d-otp-fields {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: var(--#{$prefix}otp-content-gap);
19
+ }
20
+
21
+ .d-otp-divider {
22
+ margin: 0;
23
+ }
24
+
25
+ .d-otp-footer {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: var(--#{$prefix}otp-footer-gap);
29
+ align-items: center;
30
+
31
+ @include media-breakpoint-up(lg) {
32
+ flex-direction: row;
33
+ }
34
+ }
35
+
36
+ .d-otp-contact {
37
+ margin-bottom: 0;
38
+ font-size: .875em;
39
+
40
+ @include media-breakpoint-up(lg) {
41
+ margin-left: auto;
42
+ }
43
+ }
44
+
45
+ .d-otp-countdown {
46
+ display: flex;
47
+ gap: var(--#{$prefix}ref-spacer-2);
48
+ align-items: center;
49
+ }
50
+
51
+ .d-otp-countdown-text {
52
+ flex: 1 1 auto;
53
+ margin-bottom: 0;
54
+ }
@@ -0,0 +1,76 @@
1
+ .d-password-strength-meter {
2
+ --#{$prefix}password-strength-meter-margin-top: var(--#{$prefix}ref-spacer-2);
3
+
4
+ margin-top: var(--#{$prefix}password-strength-meter-margin-top);
5
+ }
6
+
7
+ .d-password-strength-bar {
8
+ --#{$prefix}password-strength-bar-height: 8px;
9
+ --#{$prefix}password-strength-bar-radius: var(--#{$prefix}border-radius-lg);
10
+ --#{$prefix}password-strength-bar-bg: var(--#{$prefix}gray-100);
11
+ --#{$prefix}password-strength-bar-margin-bottom: var(--#{$prefix}ref-spacer-2);
12
+
13
+ width: 100%;
14
+ height: var(--#{$prefix}password-strength-bar-height);
15
+ margin-bottom: var(--#{$prefix}password-strength-bar-margin-bottom);
16
+ overflow: hidden;
17
+ background: var(--#{$prefix}password-strength-bar-bg);
18
+ border-radius: var(--#{$prefix}password-strength-bar-radius);
19
+ }
20
+
21
+ .d-password-strength-bar-fill {
22
+ height: 100%;
23
+ transition: width .3s ease-in-out;
24
+
25
+ &.is-empty {
26
+ background: var(--#{$prefix}gray-200);
27
+ }
28
+
29
+ &.is-weak {
30
+ background: var(--#{$prefix}danger);
31
+ }
32
+
33
+ &.is-fair {
34
+ background: var(--#{$prefix}warning);
35
+ }
36
+
37
+ &.is-good {
38
+ background: var(--#{$prefix}info);
39
+ }
40
+
41
+ &.is-strong {
42
+ background: var(--#{$prefix}success);
43
+ }
44
+ }
45
+
46
+ .d-password-check-list {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: var(--#{$prefix}ref-spacer-2);
50
+ padding-left: 0;
51
+ margin: 0;
52
+ list-style: none;
53
+ }
54
+
55
+ .d-password-check-item {
56
+ --#{$prefix}password-check-item-color: var(--#{$prefix}gray-600);
57
+
58
+ display: flex;
59
+ gap: var(--#{$prefix}ref-spacer-2);
60
+ align-items: flex-start;
61
+ font-size: .875em;
62
+ color: var(--#{$prefix}password-check-item-color);
63
+
64
+ &.is-valid {
65
+ color: var(--#{$prefix}success);
66
+ }
67
+ }
68
+
69
+ .d-password-check-item-icon {
70
+ flex-shrink: 0;
71
+ color: var(--#{$prefix}gray-300);
72
+ }
73
+
74
+ .d-password-check-item.is-valid .d-password-check-item-icon {
75
+ color: var(--#{$prefix}success);
76
+ }
@@ -11,5 +11,9 @@
11
11
  &:focus-visible {
12
12
  outline: 0;
13
13
  }
14
+
15
+ &.d-popover-content-fill {
16
+ width: 100%;
17
+ }
14
18
  }
15
19
  }
@@ -0,0 +1,8 @@
1
+ // Semantic hook classes for theming. The mobile/desktop toggle itself relies on
2
+ // Bootstrap's responsive display utilities (d-{breakpoint}-none / d-{breakpoint}-block)
3
+ // because the breakpoint is dynamic (component prop) and CSS custom properties
4
+ // cannot be read inside media queries.
5
+ .d-stepper-mobile,
6
+ .d-stepper-desktop {
7
+ width: 100%;
8
+ }
@@ -1,3 +1,21 @@
1
+ .d-tabs {
2
+ display: flex;
3
+ width: 100%;
4
+ }
5
+
6
+ .d-tabs-column {
7
+ flex-direction: column;
8
+ }
9
+
10
+ .d-tabs-nav-vertical {
11
+ flex-direction: column;
12
+ align-items: center;
13
+ }
14
+
15
+ .d-tabs-content {
16
+ width: 100%;
17
+ }
18
+
1
19
  .nav-toggle-button-group {
2
20
  --#{$prefix}toggle-button-group-border-color: var(--#{$prefix}gray-50);
3
21
  --#{$prefix}toggle-button-group-border-radius: 4rem;
@@ -5,6 +5,9 @@
5
5
  --#{$prefix}voucher-amount-rounded: var(--#{$prefix}ref-spacer-4);
6
6
  --#{$prefix}voucher-footer-gap: var(--#{$prefix}ref-spacer-4);
7
7
  --#{$prefix}voucher-margin: 0 0 var(--#{$prefix}ref-spacer-4) 0;
8
+ --#{$prefix}voucher-title-margin-bottom: var(--#{$prefix}ref-spacer-2);
9
+ --#{$prefix}voucher-amount-value-margin: var(--#{$prefix}ref-spacer-1);
10
+ --#{$prefix}voucher-divider-margin: var(--#{$prefix}ref-spacer-4);
8
11
  }
9
12
 
10
13
  .d-voucher-header {
@@ -28,3 +31,30 @@
28
31
  gap: var(--#{$prefix}voucher-footer-gap);
29
32
  justify-content: flex-start;
30
33
  }
34
+
35
+ .d-voucher-title-group {
36
+ text-align: center;
37
+ }
38
+
39
+ .d-voucher-title {
40
+ margin-bottom: var(--#{$prefix}voucher-title-margin-bottom);
41
+ }
42
+
43
+ .d-voucher-message {
44
+ margin: 0;
45
+ }
46
+
47
+ .d-voucher-amount-value {
48
+ margin: 0;
49
+ font-size: var(--#{$prefix}fs-3);
50
+ font-weight: var(--#{$prefix}fw-bold);
51
+ text-align: center;
52
+
53
+ &.d-voucher-amount-value-with-details {
54
+ margin-bottom: var(--#{$prefix}voucher-amount-value-margin);
55
+ }
56
+ }
57
+
58
+ .d-voucher-divider {
59
+ margin: var(--#{$prefix}voucher-divider-margin) 0;
60
+ }
File without changes
@@ -1,4 +0,0 @@
1
- export default function useCountdown(seconds: number): {
2
- secondsLeft: number;
3
- restartCountdown: () => void;
4
- };