@marwes-ui/svelte 1.3.0 → 1.4.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 (182) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +43 -11
  3. package/dist/components/README.md +31 -0
  4. package/dist/components/accordion/AccordionField.svelte +20 -6
  5. package/dist/components/accordion/index.d.ts +5 -2
  6. package/dist/components/accordion/index.js +7 -1
  7. package/dist/components/accordion/types.d.ts +3 -0
  8. package/dist/components/avatar/AvatarGroup.svelte +5 -2
  9. package/dist/components/avatar/AvatarGroup.svelte.d.ts +1 -0
  10. package/dist/components/avatar/TeamAvatarGroup.svelte +1 -0
  11. package/dist/components/avatar/TeamAvatarGroup.svelte.d.ts +1 -0
  12. package/dist/components/avatar/index.d.ts +7 -1
  13. package/dist/components/avatar/index.js +6 -0
  14. package/dist/components/avatar/types.d.ts +28 -0
  15. package/dist/components/badge/BadgeGroup.svelte +2 -1
  16. package/dist/components/badge/index.d.ts +5 -1
  17. package/dist/components/badge/index.js +4 -0
  18. package/dist/components/badge/types.d.ts +3 -0
  19. package/dist/components/banner/Banner.svelte +67 -0
  20. package/dist/components/banner/Banner.svelte.d.ts +4 -0
  21. package/dist/components/banner/ErrorBanner.svelte +9 -0
  22. package/dist/components/banner/ErrorBanner.svelte.d.ts +4 -0
  23. package/dist/components/banner/InfoBanner.svelte +9 -0
  24. package/dist/components/banner/InfoBanner.svelte.d.ts +4 -0
  25. package/dist/components/banner/SuccessBanner.svelte +9 -0
  26. package/dist/components/banner/SuccessBanner.svelte.d.ts +4 -0
  27. package/dist/components/banner/WarningBanner.svelte +9 -0
  28. package/dist/components/banner/WarningBanner.svelte.d.ts +4 -0
  29. package/dist/components/banner/index.d.ts +7 -0
  30. package/dist/components/banner/index.js +6 -0
  31. package/dist/components/banner/types.d.ts +14 -0
  32. package/dist/components/banner/types.js +1 -0
  33. package/dist/components/breadcrumb/Breadcrumb.svelte +115 -0
  34. package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +4 -0
  35. package/dist/components/breadcrumb/index.d.ts +2 -0
  36. package/dist/components/breadcrumb/index.js +1 -0
  37. package/dist/components/breadcrumb/types.d.ts +8 -0
  38. package/dist/components/breadcrumb/types.js +1 -0
  39. package/dist/components/button/Button.svelte +8 -4
  40. package/dist/components/button/DestructiveButton.svelte +1 -1
  41. package/dist/components/button/IconButton.svelte +9 -0
  42. package/dist/components/button/IconButton.svelte.d.ts +4 -0
  43. package/dist/components/button/index.d.ts +24 -1
  44. package/dist/components/button/index.js +23 -0
  45. package/dist/components/button/types.d.ts +30 -1
  46. package/dist/components/card/Card.svelte +2 -1
  47. package/dist/components/card/index.d.ts +4 -1
  48. package/dist/components/card/index.js +3 -0
  49. package/dist/components/card/types.d.ts +4 -0
  50. package/dist/components/checkbox/CheckboxField.svelte +4 -3
  51. package/dist/components/checkbox/CheckboxGroupField.svelte +21 -9
  52. package/dist/components/checkbox/index.d.ts +2 -2
  53. package/dist/components/checkbox/index.js +4 -1
  54. package/dist/components/checkbox/types.d.ts +7 -0
  55. package/dist/components/context-menu/ContextMenu.svelte +56 -0
  56. package/dist/components/context-menu/ContextMenu.svelte.d.ts +4 -0
  57. package/dist/components/context-menu/index.d.ts +2 -0
  58. package/dist/components/context-menu/index.js +1 -0
  59. package/dist/components/context-menu/types.d.ts +7 -0
  60. package/dist/components/context-menu/types.js +1 -0
  61. package/dist/components/date-picker/DatePicker.svelte +14 -2
  62. package/dist/components/date-picker/DatePickerField.svelte +72 -0
  63. package/dist/components/date-picker/DatePickerField.svelte.d.ts +4 -0
  64. package/dist/components/date-picker/index.d.ts +2 -2
  65. package/dist/components/date-picker/index.js +3 -1
  66. package/dist/components/date-picker/types.d.ts +15 -0
  67. package/dist/components/dialog/Dialog.svelte +1 -0
  68. package/dist/components/dialog/DialogModal.svelte +12 -0
  69. package/dist/components/dialog/index.d.ts +1 -1
  70. package/dist/components/dialog/types.d.ts +7 -3
  71. package/dist/components/drawer/Drawer.svelte +132 -0
  72. package/dist/components/drawer/Drawer.svelte.d.ts +4 -0
  73. package/dist/components/drawer/index.d.ts +2 -0
  74. package/dist/components/drawer/index.js +1 -0
  75. package/dist/components/drawer/types.d.ts +16 -0
  76. package/dist/components/drawer/types.js +1 -0
  77. package/dist/components/heading/H1.svelte +10 -0
  78. package/dist/components/heading/H2.svelte +6 -0
  79. package/dist/components/heading/H3.svelte +7 -0
  80. package/dist/components/input/CurrencyField.svelte +1 -1
  81. package/dist/components/input/Input.svelte +1 -0
  82. package/dist/components/input/InputField.svelte +4 -3
  83. package/dist/components/input/InputOtp.svelte +21 -44
  84. package/dist/components/input/InputOtpField.svelte +81 -0
  85. package/dist/components/input/InputOtpField.svelte.d.ts +4 -0
  86. package/dist/components/input/RichText.svelte +7 -6
  87. package/dist/components/input/RichTextField.svelte +4 -3
  88. package/dist/components/input/Select.svelte +7 -31
  89. package/dist/components/input/SelectField.svelte +6 -3
  90. package/dist/components/input/Textarea.svelte +1 -0
  91. package/dist/components/input/TextareaField.svelte +20 -5
  92. package/dist/components/input/index.d.ts +12 -4
  93. package/dist/components/input/index.js +19 -3
  94. package/dist/components/input/types.d.ts +59 -5
  95. package/dist/components/pagination/Pagination.svelte +399 -0
  96. package/dist/components/pagination/Pagination.svelte.d.ts +4 -0
  97. package/dist/components/pagination/PaginationField.svelte +72 -0
  98. package/dist/components/pagination/PaginationField.svelte.d.ts +4 -0
  99. package/dist/components/pagination/index.d.ts +2 -0
  100. package/dist/components/pagination/index.js +2 -0
  101. package/dist/components/pagination/types.d.ts +40 -0
  102. package/dist/components/pagination/types.js +1 -0
  103. package/dist/components/progress-bar/ProgressBar.svelte +50 -0
  104. package/dist/components/progress-bar/ProgressBar.svelte.d.ts +4 -0
  105. package/dist/components/progress-bar/index.d.ts +2 -0
  106. package/dist/components/progress-bar/index.js +1 -0
  107. package/dist/components/progress-bar/types.d.ts +6 -0
  108. package/dist/components/progress-bar/types.js +1 -0
  109. package/dist/components/radio/OptionRadioGroup.svelte +39 -5
  110. package/dist/components/radio/OptionRadioGroup.svelte.d.ts +2 -5
  111. package/dist/components/radio/Radio.svelte +1 -0
  112. package/dist/components/radio/RadioGroupField.svelte +4 -3
  113. package/dist/components/radio/RatingRadioGroup.svelte +18 -3
  114. package/dist/components/radio/YesNoRadioGroup.svelte +18 -3
  115. package/dist/components/radio/index.d.ts +4 -2
  116. package/dist/components/radio/index.js +6 -1
  117. package/dist/components/radio/types.d.ts +47 -0
  118. package/dist/components/segmented-control/SegmentedControl.svelte +125 -0
  119. package/dist/components/segmented-control/SegmentedControl.svelte.d.ts +25 -0
  120. package/dist/components/segmented-control/SegmentedControlField.svelte +76 -0
  121. package/dist/components/segmented-control/SegmentedControlField.svelte.d.ts +25 -0
  122. package/dist/components/segmented-control/index.d.ts +2 -0
  123. package/dist/components/segmented-control/index.js +2 -0
  124. package/dist/components/segmented-control/types.d.ts +41 -0
  125. package/dist/components/segmented-control/types.js +1 -0
  126. package/dist/components/skip-link/SkipLink.svelte +12 -0
  127. package/dist/components/skip-link/SkipLink.svelte.d.ts +4 -0
  128. package/dist/components/skip-link/index.d.ts +2 -0
  129. package/dist/components/skip-link/index.js +1 -0
  130. package/dist/components/skip-link/types.d.ts +7 -0
  131. package/dist/components/skip-link/types.js +1 -0
  132. package/dist/components/slider/SliderField.svelte +9 -8
  133. package/dist/components/slider/index.d.ts +5 -2
  134. package/dist/components/slider/index.js +7 -1
  135. package/dist/components/slider/types.d.ts +21 -0
  136. package/dist/components/spinner/EmptyStateSpinner.svelte +2 -3
  137. package/dist/components/spinner/EmptyStateSpinner.svelte.d.ts +1 -2
  138. package/dist/components/spinner/Spinner.svelte +2 -0
  139. package/dist/components/spinner/index.d.ts +2 -1
  140. package/dist/components/spinner/index.js +1 -0
  141. package/dist/components/spinner/types.d.ts +1 -0
  142. package/dist/components/switch/Switch.svelte +1 -1
  143. package/dist/components/switch/SwitchField.svelte +6 -4
  144. package/dist/components/switch/index.d.ts +4 -2
  145. package/dist/components/switch/index.js +6 -1
  146. package/dist/components/switch/types.d.ts +3 -0
  147. package/dist/components/tab/TabGroup.svelte +7 -4
  148. package/dist/components/text/Text.svelte +23 -0
  149. package/dist/components/text/Text.svelte.d.ts +4 -0
  150. package/dist/components/text/index.d.ts +4 -0
  151. package/dist/components/text/index.js +2 -0
  152. package/dist/components/text/types.d.ts +15 -0
  153. package/dist/components/text/types.js +1 -0
  154. package/dist/components/tooltip/TooltipGroup.svelte +10 -2
  155. package/dist/index.d.ts +47 -29
  156. package/dist/index.js +51 -13
  157. package/dist/provider/MarwesProvider.svelte +26 -14
  158. package/dist/provider/types.d.ts +5 -2
  159. package/package.json +6 -5
  160. package/dist/components/accordion/README.md +0 -19
  161. package/dist/components/avatar/README.md +0 -19
  162. package/dist/components/badge/README.md +0 -16
  163. package/dist/components/button/README.md +0 -21
  164. package/dist/components/card/README.md +0 -15
  165. package/dist/components/checkbox/README.md +0 -17
  166. package/dist/components/date-picker/README.md +0 -17
  167. package/dist/components/dialog/README.md +0 -23
  168. package/dist/components/divider/README.md +0 -15
  169. package/dist/components/heading/README.md +0 -13
  170. package/dist/components/icon/README.md +0 -18
  171. package/dist/components/input/README.md +0 -24
  172. package/dist/components/paragraph/README.md +0 -13
  173. package/dist/components/radio/README.md +0 -16
  174. package/dist/components/skeleton/README.md +0 -16
  175. package/dist/components/slider/README.md +0 -20
  176. package/dist/components/spacing/README.md +0 -15
  177. package/dist/components/spinner/README.md +0 -19
  178. package/dist/components/stat-tile/README.md +0 -15
  179. package/dist/components/switch/README.md +0 -17
  180. package/dist/components/tab/README.md +0 -26
  181. package/dist/components/toast/README.md +0 -35
  182. package/dist/components/tooltip/README.md +0 -22
@@ -1,2 +1,2 @@
1
- export { default as DatePicker } from "./DatePicker.svelte";
2
- export type { DatePickerProps } from "./types.js";
1
+ export { default as DatePickerField } from "./DatePickerField.svelte";
2
+ export type { DatePickerFieldProps } from "./types.js";
@@ -1 +1,3 @@
1
- export { default as DatePicker } from "./DatePicker.svelte";
1
+ // `DatePicker` atom intentionally NOT re-exported — use `DatePickerField`.
2
+ // Internal consumers can deep-import "./DatePicker.svelte" directly.
3
+ export { default as DatePickerField } from "./DatePickerField.svelte";
@@ -11,3 +11,18 @@ export interface DatePickerProps extends DatePickerOptions {
11
11
  onapply?: () => void;
12
12
  class?: string;
13
13
  }
14
+ export interface DatePickerFieldProps {
15
+ /** Optional: if omitted, a stable id is generated. */
16
+ id?: string;
17
+ /** Field label (required for accessibility). */
18
+ label: string;
19
+ /** Optional helper text shown below the calendar. */
20
+ helperText?: string;
21
+ /** Optional error message. */
22
+ error?: string;
23
+ /** Props forwarded to the DatePicker atom. */
24
+ datePicker?: Omit<DatePickerProps, "ariaLabel" | "ariaLabelledBy" | "calendarLabel">;
25
+ /** Additional aria-describedby IDs to merge with internal helper/error IDs. */
26
+ ariaDescribedBy?: string;
27
+ class?: string;
28
+ }
@@ -63,6 +63,7 @@
63
63
  }
64
64
  </script>
65
65
 
66
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
66
67
  <section
67
68
  id={dialogId}
68
69
  class={mergedClass}
@@ -8,6 +8,9 @@
8
8
  closeOnEscape = true,
9
9
  closeOnScrimClick = true,
10
10
  restoreFocus = true,
11
+ surfaceWidth,
12
+ tone = "default",
13
+ divider = "visible",
11
14
  onopenchange,
12
15
  overlayClass,
13
16
  footer,
@@ -19,6 +22,11 @@
19
22
  let previouslyFocused: HTMLElement | null = $state(null);
20
23
 
21
24
  const mergedOverlayClass = $derived(mergeClass("mw-dialog-modal", overlayClass));
25
+ const surfaceWidthStyle = $derived(
26
+ surfaceWidth === undefined
27
+ ? undefined
28
+ : `--mw-dialog-surface-width: ${typeof surfaceWidth === "number" ? `${surfaceWidth}px` : surfaceWidth}`
29
+ );
22
30
 
23
31
  function close(): void {
24
32
  if (!open) return;
@@ -108,6 +116,10 @@
108
116
  <div
109
117
  bind:this={overlayElement}
110
118
  class={mergedOverlayClass}
119
+ style={surfaceWidthStyle}
120
+ data-surface-width={surfaceWidth === undefined ? undefined : "custom"}
121
+ data-tone={tone}
122
+ data-divider={divider}
111
123
  onclick={handleScrimClick}
112
124
  onkeydown={handleKeyDown}
113
125
  role="presentation"
@@ -3,4 +3,4 @@ export { default as DialogModal } from "./DialogModal.svelte";
3
3
  export { default as ConfirmDialog } from "./ConfirmDialog.svelte";
4
4
  export { default as DestructiveDialog } from "./DestructiveDialog.svelte";
5
5
  export { default as InfoDialog } from "./InfoDialog.svelte";
6
- export type { ConfirmDialogProps, DestructiveDialogProps, DialogModalProps, DialogProps, InfoDialogProps, } from "./types.js";
6
+ export type { ConfirmDialogProps, DestructiveDialogProps, DialogFooterControls, DialogModalProps, DialogProps, InfoDialogProps, } from "./types.js";
@@ -12,14 +12,18 @@ export interface DialogProps extends DialogOptions {
12
12
  class?: string;
13
13
  dataAttributes?: Record<string, string>;
14
14
  }
15
+ export interface DialogFooterControls {
16
+ close: () => void;
17
+ }
15
18
  export interface DialogModalProps extends Omit<DialogProps, "footer" | "onclose"> {
16
19
  open?: boolean;
17
20
  closeOnEscape?: boolean;
18
21
  closeOnScrimClick?: boolean;
19
22
  restoreFocus?: boolean;
20
- footer?: Snippet<[{
21
- close: () => void;
22
- }]>;
23
+ surfaceWidth?: string | number;
24
+ tone?: "default" | "calm";
25
+ divider?: "visible" | "hidden";
26
+ footer?: Snippet<[DialogFooterControls]>;
23
27
  onopenchange?: (open: boolean) => void;
24
28
  class?: string;
25
29
  overlayClass?: string;
@@ -0,0 +1,132 @@
1
+ <script lang="ts">
2
+ import { IconName, createDrawerRecipe } from "@marwes-ui/core";
3
+ import type { DrawerOptions } from "@marwes-ui/core";
4
+ import { mergeClass } from "../../internal/merge-class.js";
5
+ import Icon from "../icon/Icon.svelte";
6
+ import type { DrawerProps } from "./types.js";
7
+
8
+ let {
9
+ id: userProvidedId,
10
+ title,
11
+ description,
12
+ children,
13
+ footer,
14
+ onclose,
15
+ class: className,
16
+ panelClass,
17
+ dataAttributes,
18
+ size,
19
+ placement,
20
+ showFooter,
21
+ dismissible,
22
+ modal,
23
+ showScrim,
24
+ ariaLabel,
25
+ ariaLabelledBy,
26
+ ariaDescribedBy,
27
+ }: DrawerProps = $props();
28
+
29
+ const uniqueId = $props.id();
30
+ const drawerId = $derived(userProvidedId ?? `mw-drawer-${uniqueId}`);
31
+ const titleId = $derived(`${drawerId}-title`);
32
+ const descriptionId = $derived(`${drawerId}-description`);
33
+
34
+ const hasTitle = $derived(title !== undefined && title.trim().length > 0);
35
+ const hasDescription = $derived(description !== undefined && description.trim().length > 0);
36
+ const hasFooter = $derived((showFooter ?? true) && footer !== undefined);
37
+ const isDismissible = $derived((dismissible ?? true) && onclose !== undefined);
38
+
39
+ const drawerOptions = $derived.by((): DrawerOptions => {
40
+ const opts: DrawerOptions = {
41
+ showFooter: hasFooter,
42
+ dismissible: isDismissible,
43
+ };
44
+ if (modal !== undefined) opts.modal = modal;
45
+ if (showScrim !== undefined) opts.showScrim = showScrim;
46
+ if (dataAttributes !== undefined) opts.dataAttributes = dataAttributes;
47
+ if (size !== undefined) opts.size = size;
48
+ if (placement !== undefined) opts.placement = placement;
49
+ if (ariaLabel !== undefined) opts.ariaLabel = ariaLabel;
50
+ if (ariaLabelledBy !== undefined) {
51
+ opts.ariaLabelledBy = ariaLabelledBy;
52
+ } else if (ariaLabel === undefined && hasTitle) {
53
+ opts.ariaLabelledBy = titleId;
54
+ }
55
+ if (ariaDescribedBy !== undefined) {
56
+ opts.ariaDescribedBy = ariaDescribedBy;
57
+ } else if (hasDescription) {
58
+ opts.ariaDescribedBy = descriptionId;
59
+ }
60
+ return opts;
61
+ });
62
+
63
+ const kit = $derived(createDrawerRecipe(drawerOptions));
64
+ const mergedClass = $derived(mergeClass(kit.className, className));
65
+ const mergedPanelClass = $derived(mergeClass(kit.panel.className, panelClass));
66
+
67
+ function handleClose(): void {
68
+ onclose?.();
69
+ }
70
+ </script>
71
+
72
+ <div class={mergedClass} {...kit.dataAttributes}>
73
+ {#if kit.showScrim}
74
+ <div
75
+ class={kit.scrim.className}
76
+ aria-hidden="true"
77
+ onclick={isDismissible ? handleClose : undefined}
78
+ {...kit.scrim.dataAttributes}
79
+ ></div>
80
+ {/if}
81
+
82
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
83
+ <div
84
+ id={drawerId}
85
+ class={mergedPanelClass}
86
+ role={kit.panel.a11y.role}
87
+ aria-modal={kit.panel.a11y.ariaModal ? "true" : undefined}
88
+ aria-label={kit.panel.a11y.ariaLabel}
89
+ aria-labelledby={kit.panel.a11y.ariaLabelledBy}
90
+ aria-describedby={kit.panel.a11y.ariaDescribedBy}
91
+ tabindex={-1}
92
+ {...kit.panel.dataAttributes}
93
+ >
94
+ {#if hasTitle || kit.showCloseButton}
95
+ <div class="mw-drawer__header">
96
+ {#if hasTitle}
97
+ <div class="mw-drawer__heading-group">
98
+ <h2 class="mw-drawer__title" id={titleId}>{title}</h2>
99
+ </div>
100
+ {:else}
101
+ <span class="mw-drawer__spacer" aria-hidden="true"></span>
102
+ {/if}
103
+
104
+ {#if kit.showCloseButton && onclose}
105
+ <button
106
+ type="button"
107
+ class="mw-drawer__close"
108
+ aria-label="Close drawer"
109
+ onclick={handleClose}
110
+ >
111
+ <Icon name={IconName.X} decorative />
112
+ </button>
113
+ {/if}
114
+ </div>
115
+ <div class="mw-drawer__header-divider" aria-hidden="true"></div>
116
+ {/if}
117
+
118
+ <div class="mw-drawer__content">
119
+ {#if hasDescription}
120
+ <p class="mw-drawer__description" id={descriptionId}>{description}</p>
121
+ {/if}
122
+ {@render children?.()}
123
+ </div>
124
+
125
+ {#if hasFooter}
126
+ <div class="mw-drawer__footer-divider" aria-hidden="true"></div>
127
+ <div class="mw-drawer__footer">
128
+ {@render footer?.({ close: handleClose })}
129
+ </div>
130
+ {/if}
131
+ </div>
132
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { DrawerProps } from "./types.js";
2
+ declare const Drawer: import("svelte").Component<DrawerProps, {}, "">;
3
+ type Drawer = ReturnType<typeof Drawer>;
4
+ export default Drawer;
@@ -0,0 +1,2 @@
1
+ export { default as Drawer } from "./Drawer.svelte";
2
+ export type { DrawerPlacement, DrawerProps, DrawerSize } from "./types.js";
@@ -0,0 +1 @@
1
+ export { default as Drawer } from "./Drawer.svelte";
@@ -0,0 +1,16 @@
1
+ import type { DrawerOptions, DrawerPlacement, DrawerSize } from "@marwes-ui/core";
2
+ import type { Snippet } from "svelte";
3
+ export interface DrawerProps extends DrawerOptions {
4
+ id?: string;
5
+ title?: string;
6
+ description?: string;
7
+ children?: Snippet;
8
+ footer?: Snippet<[{
9
+ close: () => void;
10
+ }]>;
11
+ onclose?: () => void;
12
+ class?: string;
13
+ panelClass?: string;
14
+ dataAttributes?: Record<string, string>;
15
+ }
16
+ export type { DrawerPlacement, DrawerSize };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,13 @@
1
+ <!--
2
+ H1 (Heading Level 1) — semantic top-level heading rendered as `<h1>`.
3
+
4
+ @remarks
5
+ Marwes officially styles H1-H3 only. For semantic H4-H6 (deep section
6
+ nesting, accessibility outlines), render
7
+ `<Text headingLevel={4} variant="..." />` — it produces the correct
8
+ heading tag while letting the consumer pick the visual variant.
9
+ Type your wrappers against `SemanticHeadingLevel` from `@marwes-ui/core`.
10
+ -->
1
11
  <script lang="ts">
2
12
  import { headingRecipe } from "@marwes-ui/core";
3
13
  import { cssVarsToStyle, mergeStyle } from "../../internal/css-vars.js";
@@ -1,3 +1,9 @@
1
+ <!--
2
+ H2 (Heading Level 2) — semantic section heading rendered as `<h2>`.
3
+
4
+ @remarks
5
+ For semantic H4-H6, use `<Text headingLevel={4..6} variant="..." />`.
6
+ -->
1
7
  <script lang="ts">
2
8
  import { headingRecipe } from "@marwes-ui/core";
3
9
  import { cssVarsToStyle, mergeStyle } from "../../internal/css-vars.js";
@@ -1,3 +1,10 @@
1
+ <!--
2
+ H3 (Heading Level 3) — semantic subsection heading rendered as `<h3>`.
3
+
4
+ @remarks
5
+ H3 is the deepest blessed rung. For H4-H6, use
6
+ `<Text headingLevel={4..6} variant="..." />`.
7
+ -->
1
8
  <script lang="ts">
2
9
  import { headingRecipe } from "@marwes-ui/core";
3
10
  import { cssVarsToStyle, mergeStyle } from "../../internal/css-vars.js";
@@ -5,7 +5,7 @@
5
5
  let { currency, input = {}, ...props }: Props = $props();
6
6
 
7
7
  const symbols: Record<string, string> = { USD: "$", EUR: "€", GBP: "£", JPY: "¥", SEK: "kr", NOK: "kr", DKK: "kr", CHF: "CHF", BTC: "₿" };
8
- const leadingSymbol = currency ? symbols[currency] : undefined;
8
+ const leadingSymbol = $derived(currency ? symbols[currency] : undefined);
9
9
  </script>
10
10
  <div data-purpose="currency" data-currency={currency}>
11
11
  <InputField {...props} input={{ ...input, type: "text", inputMode: "decimal" }} {...(leadingSymbol ? { leadingSymbol } : {})} />
@@ -42,6 +42,7 @@
42
42
  readonly={kit.a11y.readOnly}
43
43
  required={kit.a11y.required}
44
44
  aria-label={kit.a11y.ariaLabel}
45
+ aria-labelledby={kit.a11y.ariaLabelledBy}
45
46
  aria-invalid={kit.a11y.ariaInvalid}
46
47
  aria-describedby={kit.a11y.ariaDescribedBy}
47
48
  {value}
@@ -2,6 +2,7 @@
2
2
  import { buildInputFieldA11yIds } from "@marwes-ui/core";
3
3
  import { mergeClass } from "../../internal/merge-class.js";
4
4
  import Icon from "../icon/Icon.svelte";
5
+ import Text from "../text/Text.svelte";
5
6
  import Input from "./Input.svelte";
6
7
  import type { InputFieldProps } from "./types.js";
7
8
 
@@ -76,7 +77,7 @@
76
77
 
77
78
  <div class={wrapperClass}>
78
79
  <label class="mw-input-field__label" for={fieldId}>
79
- <p class="mw-p mw-p--md">{label}</p>
80
+ <Text variant="label">{label}</Text>
80
81
  </label>
81
82
 
82
83
  <div class="mw-input-field__input-wrapper">
@@ -127,13 +128,13 @@
127
128
 
128
129
  {#if hasHelperText && !hasError}
129
130
  <div class="mw-input-field__helper" id={a11yIds.helperTextId}>
130
- <p class="mw-p mw-p--sm">{helperText}</p>
131
+ <Text variant="caption">{helperText}</Text>
131
132
  </div>
132
133
  {/if}
133
134
 
134
135
  {#if hasError}
135
136
  <div class="mw-input-field__error" id={a11yIds.errorId} aria-live="polite">
136
- <p class="mw-p mw-p--sm">{error}</p>
137
+ <Text variant="caption">{error}</Text>
137
138
  </div>
138
139
  {/if}
139
140
  </div>
@@ -1,21 +1,18 @@
1
1
  <script lang="ts">
2
- import {
3
- buildInputFieldA11yIds,
4
- createInputOtpRecipe,
5
- sanitizeInputOtpValue,
6
- } from "@marwes-ui/core";
2
+ import { createInputOtpRecipe, sanitizeInputOtpValue } from "@marwes-ui/core";
7
3
  import type { InputOtpOptions } from "@marwes-ui/core";
8
4
  import { cssVarsToStyle } from "../../internal/css-vars.js";
9
5
  import { mergeClass } from "../../internal/merge-class.js";
10
6
  import type { InputOtpProps } from "./types.js";
11
7
 
8
+ /**
9
+ * InputOtp (Atom) — bare OTP cells. Renders the visual cells and a hidden
10
+ * native `<input>` that captures keyboard entry. Pair with `InputOtpField`
11
+ * for label + helper/error wiring.
12
+ */
12
13
  let {
13
14
  id: userProvidedId,
14
15
  name,
15
- label,
16
- helperText,
17
- error,
18
- ariaDescribedBy,
19
16
  value: controlledValue,
20
17
  defaultValue,
21
18
  length: lengthProp,
@@ -23,8 +20,11 @@
23
20
  disabled,
24
21
  readOnly,
25
22
  required,
26
- invalid: invalidProp,
23
+ invalid,
27
24
  ariaLabel,
25
+ label,
26
+ ariaLabelledBy,
27
+ describedBy,
28
28
  onvaluechange,
29
29
  class: className,
30
30
  }: InputOtpProps = $props();
@@ -33,44 +33,34 @@
33
33
  const fieldId = $derived(userProvidedId ?? `mw-input-otp-${uniqueId}`);
34
34
  const otpLength = $derived(Math.max(1, lengthProp ?? 6));
35
35
 
36
- function hasTextContent(text: string | undefined): boolean {
37
- return text !== undefined && text.trim().length > 0;
38
- }
39
-
40
- const hasHelperText = $derived(hasTextContent(helperText));
41
- const hasError = $derived(hasTextContent(error));
42
- const isInvalid = $derived(hasError || invalidProp === true);
43
36
  const isControlled = $derived(controlledValue !== undefined);
44
37
 
45
- let uncontrolledValue = $state(sanitizeInputOtpValue(defaultValue, lengthProp ?? 6));
38
+ function getInitialUncontrolledValue(): string {
39
+ return sanitizeInputOtpValue(defaultValue, lengthProp ?? 6);
40
+ }
41
+
42
+ let uncontrolledValue = $state(getInitialUncontrolledValue());
46
43
 
47
44
  const currentValue = $derived(
48
45
  sanitizeInputOtpValue(isControlled ? controlledValue : uncontrolledValue, otpLength)
49
46
  );
50
47
 
51
- const a11yIds = $derived(
52
- buildInputFieldA11yIds({
53
- id: fieldId,
54
- hasHelperText,
55
- hasError,
56
- externalDescribedBy: ariaDescribedBy,
57
- })
58
- );
59
-
60
48
  const recipeOptions = $derived.by((): InputOtpOptions => {
61
49
  const opts: InputOtpOptions = {
62
50
  id: fieldId,
63
51
  value: currentValue,
64
52
  length: otpLength,
65
- invalid: isInvalid,
66
53
  };
67
54
  if (name) opts.name = name;
68
55
  if (placeholderCharacter) opts.placeholderCharacter = placeholderCharacter;
69
56
  if (disabled) opts.disabled = true;
70
57
  if (readOnly) opts.readOnly = true;
71
58
  if (required) opts.required = true;
72
- if (a11yIds.describedBy) opts.describedBy = a11yIds.describedBy;
59
+ if (invalid) opts.invalid = true;
60
+ if (describedBy) opts.describedBy = describedBy;
73
61
  if (ariaLabel) opts.ariaLabel = ariaLabel;
62
+ if (label) opts.label = label;
63
+ if (ariaLabelledBy) opts.ariaLabelledBy = ariaLabelledBy;
74
64
  return opts;
75
65
  });
76
66
 
@@ -91,13 +81,9 @@
91
81
  class={mergedClass}
92
82
  style={mergedStyle}
93
83
  data-component="input-otp"
94
- data-invalid={isInvalid ? "true" : undefined}
84
+ data-invalid={invalid ? "true" : undefined}
95
85
  data-disabled={disabled ? "true" : undefined}
96
86
  >
97
- <label class="mw-input-otp__label" for={fieldId}>
98
- <p class="mw-p mw-p--md">{label}</p>
99
- </label>
100
-
101
87
  <div class="mw-input-otp__cells">
102
88
  {#each kit.displayCells as cell (cell.key)}
103
89
  <span
@@ -121,20 +107,11 @@
121
107
  readonly={kit.a11y.readOnly}
122
108
  required={kit.a11y.required}
123
109
  aria-label={kit.a11y.ariaLabel}
110
+ aria-labelledby={kit.a11y.ariaLabelledBy}
124
111
  aria-invalid={kit.a11y.ariaInvalid}
125
112
  aria-describedby={kit.a11y.ariaDescribedBy}
126
113
  value={kit.displayValue}
127
114
  oninput={handleChange}
128
115
  />
129
116
  </div>
130
-
131
- {#if hasError}
132
- <div class="mw-input-otp__error" id={a11yIds.errorId} aria-live="polite">
133
- <p class="mw-p mw-p--sm">{error}</p>
134
- </div>
135
- {:else if hasHelperText}
136
- <div class="mw-input-otp__helper" id={a11yIds.helperTextId}>
137
- <p class="mw-p mw-p--sm">{helperText}</p>
138
- </div>
139
- {/if}
140
117
  </div>
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import { buildInputFieldA11yIds } from "@marwes-ui/core";
3
+ import { mergeClass } from "../../internal/merge-class.js";
4
+ import Text from "../text/Text.svelte";
5
+ import InputOtp from "./InputOtp.svelte";
6
+ import type { InputOtpFieldProps, InputOtpProps } from "./types.js";
7
+
8
+ /**
9
+ * InputOtpField (Molecule) — labeled wrapper around `InputOtp`.
10
+ */
11
+ let {
12
+ id: userProvidedId,
13
+ label,
14
+ helperText,
15
+ error,
16
+ inputOtp = {},
17
+ ariaDescribedBy,
18
+ class: className,
19
+ }: InputOtpFieldProps = $props();
20
+
21
+ const uniqueId = $props.id();
22
+ const fieldId = $derived(userProvidedId ?? `mw-input-otp-${uniqueId}`);
23
+ const labelId = $derived(`${fieldId}-label`);
24
+
25
+ function hasTextContent(text: string | undefined): boolean {
26
+ return text !== undefined && text.trim().length > 0;
27
+ }
28
+
29
+ const hasHelperText = $derived(hasTextContent(helperText));
30
+ const hasError = $derived(hasTextContent(error));
31
+ const isInvalid = $derived(hasError || inputOtp.invalid === true);
32
+
33
+ const a11yIds = $derived(
34
+ buildInputFieldA11yIds({
35
+ id: fieldId,
36
+ hasHelperText,
37
+ hasError,
38
+ externalDescribedBy: ariaDescribedBy,
39
+ })
40
+ );
41
+
42
+ const wrapperClass = $derived(
43
+ mergeClass(
44
+ "mw-input-otp-field",
45
+ hasError && "mw-input-otp-field--invalid",
46
+ inputOtp.disabled && "mw-input-otp-field--disabled",
47
+ className
48
+ )
49
+ );
50
+
51
+ const otpProps = $derived.by((): InputOtpProps => {
52
+ const props: InputOtpProps = {
53
+ ...inputOtp,
54
+ id: fieldId,
55
+ invalid: isInvalid,
56
+ ariaLabelledBy: labelId,
57
+ };
58
+ if (a11yIds.describedBy) {
59
+ props.describedBy = a11yIds.describedBy;
60
+ }
61
+ return props;
62
+ });
63
+ </script>
64
+
65
+ <div class={wrapperClass}>
66
+ <label class="mw-input-otp-field__label" for={fieldId} id={labelId}>
67
+ <Text variant="label">{label}</Text>
68
+ </label>
69
+
70
+ <InputOtp {...otpProps} />
71
+
72
+ {#if hasError}
73
+ <div class="mw-input-otp-field__error" id={a11yIds.errorId} aria-live="polite">
74
+ <Text variant="caption">{error}</Text>
75
+ </div>
76
+ {:else if hasHelperText}
77
+ <div class="mw-input-otp-field__helper" id={a11yIds.helperTextId}>
78
+ <Text variant="caption">{helperText}</Text>
79
+ </div>
80
+ {/if}
81
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { InputOtpFieldProps } from "./types.js";
2
+ declare const InputOtpField: import("svelte").Component<InputOtpFieldProps, {}, "">;
3
+ type InputOtpField = ReturnType<typeof InputOtpField>;
4
+ export default InputOtpField;
@@ -32,11 +32,15 @@
32
32
  const showToolbar = $derived(!options.readOnly);
33
33
 
34
34
  let editorElement: HTMLDivElement | undefined = $state(undefined);
35
- let uncontrolledHtml = $state(normalizeRichTextHtml(defaultValue));
36
- let isEditorEmpty = $state(isRichTextHtmlEmpty(isControlled ? normalizeRichTextHtml(controlledValue) : uncontrolledHtml));
35
+ function getInitialUncontrolledHtml(): string {
36
+ return normalizeRichTextHtml(defaultValue);
37
+ }
38
+
39
+ let uncontrolledHtml = $state(getInitialUncontrolledHtml());
37
40
  let activeFormats = $state<RichTextFormat[]>([]);
38
41
 
39
42
  const editorHtml = $derived(isControlled ? normalizeRichTextHtml(controlledValue) : uncontrolledHtml);
43
+ const isEditorEmpty = $derived(isRichTextHtmlEmpty(editorHtml));
40
44
 
41
45
  const formatLabels: Record<RichTextFormat, string> = {
42
46
  bold: "Bold",
@@ -46,9 +50,7 @@
46
50
 
47
51
  function syncEditorState(nextHtml: string): string {
48
52
  const normalized = normalizeRichTextHtml(nextHtml);
49
- const empty = isRichTextHtmlEmpty(normalized);
50
53
  if (!isControlled) uncontrolledHtml = normalized;
51
- isEditorEmpty = empty;
52
54
  onvaluechange?.(normalized);
53
55
  return normalized;
54
56
  }
@@ -113,7 +115,6 @@
113
115
  if (!editorElement) return;
114
116
  const normalizedDom = normalizeRichTextHtml(editorElement.innerHTML);
115
117
  if (normalizedDom !== editorHtml) editorElement.innerHTML = editorHtml;
116
- isEditorEmpty = isRichTextHtmlEmpty(editorHtml);
117
118
  });
118
119
 
119
120
  $effect(() => {
@@ -154,7 +155,7 @@
154
155
  data-empty={isEditorEmpty ? "true" : undefined}
155
156
  data-placeholder={options.placeholder}
156
157
  id={kit.a11y.id}
157
- role={kit.a11y.role}
158
+ role="textbox"
158
159
  tabindex={kit.a11y.tabIndex}
159
160
  aria-label={kit.a11y.ariaLabel}
160
161
  aria-labelledby={kit.a11y.ariaLabelledBy}
@@ -2,6 +2,7 @@
2
2
  import { buildRichTextFieldA11yIds } from "@marwes-ui/core";
3
3
  import type { RichTextOptions } from "@marwes-ui/core";
4
4
  import { mergeClass } from "../../internal/merge-class.js";
5
+ import Text from "../text/Text.svelte";
5
6
  import RichText from "./RichText.svelte";
6
7
  import type { RichTextFieldProps } from "./types.js";
7
8
 
@@ -57,7 +58,7 @@
57
58
  el?.focus();
58
59
  }}
59
60
  >
60
- <p class="mw-p mw-p--md">{label}</p>
61
+ <Text variant="label">{label}</Text>
61
62
  </button>
62
63
 
63
64
  <div class="mw-input-field__input-wrapper">
@@ -72,13 +73,13 @@
72
73
 
73
74
  {#if hasHelperText && !hasError}
74
75
  <div class="mw-input-field__helper" id={a11yIds.helperTextId}>
75
- <p class="mw-p mw-p--sm">{helperText}</p>
76
+ <Text variant="caption">{helperText}</Text>
76
77
  </div>
77
78
  {/if}
78
79
 
79
80
  {#if hasError}
80
81
  <div class="mw-input-field__error" id={a11yIds.errorId} aria-live="polite">
81
- <p class="mw-p mw-p--sm">{error}</p>
82
+ <Text variant="caption">{error}</Text>
82
83
  </div>
83
84
  {/if}
84
85
  </div>