@lutra-ui-system/react 0.1.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 (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/dist/components/button/button.d.ts +105 -0
  4. package/dist/components/button/button.d.ts.map +1 -0
  5. package/dist/components/button/button.js +91 -0
  6. package/dist/components/button/button.js.map +1 -0
  7. package/dist/components/button/index.d.ts +3 -0
  8. package/dist/components/button/index.d.ts.map +1 -0
  9. package/dist/components/button/index.js +2 -0
  10. package/dist/components/button/index.js.map +1 -0
  11. package/dist/components/card/card.d.ts +100 -0
  12. package/dist/components/card/card.d.ts.map +1 -0
  13. package/dist/components/card/card.js +51 -0
  14. package/dist/components/card/card.js.map +1 -0
  15. package/dist/components/card/index.d.ts +3 -0
  16. package/dist/components/card/index.d.ts.map +1 -0
  17. package/dist/components/card/index.js +2 -0
  18. package/dist/components/card/index.js.map +1 -0
  19. package/dist/components/checkbox/checkbox.d.ts +67 -0
  20. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  21. package/dist/components/checkbox/checkbox.js +63 -0
  22. package/dist/components/checkbox/checkbox.js.map +1 -0
  23. package/dist/components/checkbox/index.d.ts +3 -0
  24. package/dist/components/checkbox/index.d.ts.map +1 -0
  25. package/dist/components/checkbox/index.js +2 -0
  26. package/dist/components/checkbox/index.js.map +1 -0
  27. package/dist/components/dialog/dialog.d.ts +88 -0
  28. package/dist/components/dialog/dialog.d.ts.map +1 -0
  29. package/dist/components/dialog/dialog.js +156 -0
  30. package/dist/components/dialog/dialog.js.map +1 -0
  31. package/dist/components/dialog/index.d.ts +3 -0
  32. package/dist/components/dialog/index.d.ts.map +1 -0
  33. package/dist/components/dialog/index.js +2 -0
  34. package/dist/components/dialog/index.js.map +1 -0
  35. package/dist/components/dropdown/dropdown.d.ts +89 -0
  36. package/dist/components/dropdown/dropdown.d.ts.map +1 -0
  37. package/dist/components/dropdown/dropdown.js +173 -0
  38. package/dist/components/dropdown/dropdown.js.map +1 -0
  39. package/dist/components/dropdown/index.d.ts +3 -0
  40. package/dist/components/dropdown/index.d.ts.map +1 -0
  41. package/dist/components/dropdown/index.js +2 -0
  42. package/dist/components/dropdown/index.js.map +1 -0
  43. package/dist/components/error-summary/error-summary.d.ts +81 -0
  44. package/dist/components/error-summary/error-summary.d.ts.map +1 -0
  45. package/dist/components/error-summary/error-summary.js +85 -0
  46. package/dist/components/error-summary/error-summary.js.map +1 -0
  47. package/dist/components/error-summary/index.d.ts +3 -0
  48. package/dist/components/error-summary/index.d.ts.map +1 -0
  49. package/dist/components/error-summary/index.js +2 -0
  50. package/dist/components/error-summary/index.js.map +1 -0
  51. package/dist/components/field/field.d.ts +55 -0
  52. package/dist/components/field/field.d.ts.map +1 -0
  53. package/dist/components/field/field.js +26 -0
  54. package/dist/components/field/field.js.map +1 -0
  55. package/dist/components/field/index.d.ts +3 -0
  56. package/dist/components/field/index.d.ts.map +1 -0
  57. package/dist/components/field/index.js +2 -0
  58. package/dist/components/field/index.js.map +1 -0
  59. package/dist/components/field-group/field-group.d.ts +67 -0
  60. package/dist/components/field-group/field-group.d.ts.map +1 -0
  61. package/dist/components/field-group/field-group.js +38 -0
  62. package/dist/components/field-group/field-group.js.map +1 -0
  63. package/dist/components/field-group/index.d.ts +3 -0
  64. package/dist/components/field-group/index.d.ts.map +1 -0
  65. package/dist/components/field-group/index.js +2 -0
  66. package/dist/components/field-group/index.js.map +1 -0
  67. package/dist/components/input/index.d.ts +3 -0
  68. package/dist/components/input/index.d.ts.map +1 -0
  69. package/dist/components/input/index.js +2 -0
  70. package/dist/components/input/index.js.map +1 -0
  71. package/dist/components/input/input.d.ts +96 -0
  72. package/dist/components/input/input.d.ts.map +1 -0
  73. package/dist/components/input/input.js +26 -0
  74. package/dist/components/input/input.js.map +1 -0
  75. package/dist/components/link/index.d.ts +3 -0
  76. package/dist/components/link/index.d.ts.map +1 -0
  77. package/dist/components/link/index.js +2 -0
  78. package/dist/components/link/index.js.map +1 -0
  79. package/dist/components/link/link.d.ts +44 -0
  80. package/dist/components/link/link.d.ts.map +1 -0
  81. package/dist/components/link/link.js +26 -0
  82. package/dist/components/link/link.js.map +1 -0
  83. package/dist/components/link-card/index.d.ts +3 -0
  84. package/dist/components/link-card/index.d.ts.map +1 -0
  85. package/dist/components/link-card/index.js +2 -0
  86. package/dist/components/link-card/index.js.map +1 -0
  87. package/dist/components/link-card/link-card.d.ts +62 -0
  88. package/dist/components/link-card/link-card.d.ts.map +1 -0
  89. package/dist/components/link-card/link-card.js +43 -0
  90. package/dist/components/link-card/link-card.js.map +1 -0
  91. package/dist/components/mobile-navigation/index.d.ts +3 -0
  92. package/dist/components/mobile-navigation/index.d.ts.map +1 -0
  93. package/dist/components/mobile-navigation/index.js +2 -0
  94. package/dist/components/mobile-navigation/index.js.map +1 -0
  95. package/dist/components/mobile-navigation/mobile-navigation.d.ts +88 -0
  96. package/dist/components/mobile-navigation/mobile-navigation.d.ts.map +1 -0
  97. package/dist/components/mobile-navigation/mobile-navigation.js +153 -0
  98. package/dist/components/mobile-navigation/mobile-navigation.js.map +1 -0
  99. package/dist/components/navigation-item/index.d.ts +3 -0
  100. package/dist/components/navigation-item/index.d.ts.map +1 -0
  101. package/dist/components/navigation-item/index.js +2 -0
  102. package/dist/components/navigation-item/index.js.map +1 -0
  103. package/dist/components/navigation-item/navigation-item.d.ts +38 -0
  104. package/dist/components/navigation-item/navigation-item.d.ts.map +1 -0
  105. package/dist/components/navigation-item/navigation-item.js +22 -0
  106. package/dist/components/navigation-item/navigation-item.js.map +1 -0
  107. package/dist/components/radio/index.d.ts +3 -0
  108. package/dist/components/radio/index.d.ts.map +1 -0
  109. package/dist/components/radio/index.js +2 -0
  110. package/dist/components/radio/index.js.map +1 -0
  111. package/dist/components/radio/radio.d.ts +58 -0
  112. package/dist/components/radio/radio.d.ts.map +1 -0
  113. package/dist/components/radio/radio.js +37 -0
  114. package/dist/components/radio/radio.js.map +1 -0
  115. package/dist/components/select/index.d.ts +3 -0
  116. package/dist/components/select/index.d.ts.map +1 -0
  117. package/dist/components/select/index.js +2 -0
  118. package/dist/components/select/index.js.map +1 -0
  119. package/dist/components/select/select.d.ts +67 -0
  120. package/dist/components/select/select.d.ts.map +1 -0
  121. package/dist/components/select/select.js +25 -0
  122. package/dist/components/select/select.js.map +1 -0
  123. package/dist/components/select-field/index.d.ts +3 -0
  124. package/dist/components/select-field/index.d.ts.map +1 -0
  125. package/dist/components/select-field/index.js +2 -0
  126. package/dist/components/select-field/index.js.map +1 -0
  127. package/dist/components/select-field/select-field.d.ts +43 -0
  128. package/dist/components/select-field/select-field.d.ts.map +1 -0
  129. package/dist/components/select-field/select-field.js +24 -0
  130. package/dist/components/select-field/select-field.js.map +1 -0
  131. package/dist/components/table/index.d.ts +3 -0
  132. package/dist/components/table/index.d.ts.map +1 -0
  133. package/dist/components/table/index.js +2 -0
  134. package/dist/components/table/index.js.map +1 -0
  135. package/dist/components/table/table.d.ts +251 -0
  136. package/dist/components/table/table.d.ts.map +1 -0
  137. package/dist/components/table/table.js +209 -0
  138. package/dist/components/table/table.js.map +1 -0
  139. package/dist/components/toggle/index.d.ts +3 -0
  140. package/dist/components/toggle/index.d.ts.map +1 -0
  141. package/dist/components/toggle/index.js +2 -0
  142. package/dist/components/toggle/index.js.map +1 -0
  143. package/dist/components/toggle/toggle.d.ts +66 -0
  144. package/dist/components/toggle/toggle.d.ts.map +1 -0
  145. package/dist/components/toggle/toggle.js +49 -0
  146. package/dist/components/toggle/toggle.js.map +1 -0
  147. package/dist/components/tooltip/index.d.ts +3 -0
  148. package/dist/components/tooltip/index.d.ts.map +1 -0
  149. package/dist/components/tooltip/index.js +2 -0
  150. package/dist/components/tooltip/index.js.map +1 -0
  151. package/dist/components/tooltip/tooltip.d.ts +74 -0
  152. package/dist/components/tooltip/tooltip.d.ts.map +1 -0
  153. package/dist/components/tooltip/tooltip.js +180 -0
  154. package/dist/components/tooltip/tooltip.js.map +1 -0
  155. package/dist/index.d.ts +57 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +38 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/internal/development.d.ts +2 -0
  160. package/dist/internal/development.d.ts.map +1 -0
  161. package/dist/internal/development.js +2 -0
  162. package/dist/internal/development.js.map +1 -0
  163. package/dist/internal/field-support.d.ts +68 -0
  164. package/dist/internal/field-support.d.ts.map +1 -0
  165. package/dist/internal/field-support.js +42 -0
  166. package/dist/internal/field-support.js.map +1 -0
  167. package/dist/internal/isomorphic-layout-effect.d.ts +26 -0
  168. package/dist/internal/isomorphic-layout-effect.d.ts.map +1 -0
  169. package/dist/internal/isomorphic-layout-effect.js +26 -0
  170. package/dist/internal/isomorphic-layout-effect.js.map +1 -0
  171. package/dist/styles.css +3474 -0
  172. package/dist/tokens.json +995 -0
  173. package/package.json +76 -0
@@ -0,0 +1,173 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useEffect, useId, useRef, useState } from 'react';
3
+ import { Button } from '../button/index.js';
4
+ /** Focusable things inside the panel, in DOM order. Disabled buttons are skipped by the browser. */
5
+ const FOCUSABLE = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
6
+ /**
7
+ * A disclosure that reveals a panel of actions or links from a button.
8
+ *
9
+ * **It is a disclosure, not an ARIA menu, and that is deliberate.** The accessibility annotation is
10
+ * explicit: the trigger is a button, and the panel is a container of buttons or links depending on
11
+ * what each item does. No `role="menu"`, no `role="menuitem"`, and no arrow-key roving.
12
+ *
13
+ * The reason is that the two models cannot be mixed. A real ARIA menu promises arrow-key navigation
14
+ * *and* takes Tab away from the items inside it; a half-built one promises navigation that does not
15
+ * work, which is worse than no role at all. As a disclosure, everything inside keeps its native
16
+ * semantics and Tab keeps working, which is what the annotation asks for.
17
+ *
18
+ * Behaviour:
19
+ *
20
+ * - Enter or Space opens, because the trigger is a real button.
21
+ * - `aria-expanded` reflects the state and `aria-controls` points at the panel.
22
+ * - Focus moves to the first item on open.
23
+ * - Tab moves through the items in DOM order and out the other side.
24
+ * - Escape closes and returns focus to the trigger.
25
+ * - A pointer press outside closes it, without interfering with what was pressed.
26
+ * - Focus leaving the component closes it, so a Tab out does not strand an open panel.
27
+ *
28
+ * Open state is uncontrolled by default and controllable with `open` + `onOpenChange`, matching the
29
+ * pattern the form controls already use. There is no state machine beyond that.
30
+ */
31
+ export function Dropdown({ label, icon, children, open: controlledOpen, defaultOpen = false, onOpenChange, triggerVariant = 'secondary', triggerSize = 'md', className, }) {
32
+ const panelId = `lutra-dropdown-${useId()}`;
33
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
34
+ const isControlled = controlledOpen !== undefined;
35
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
36
+ const wrapperRef = useRef(null);
37
+ const triggerRef = useRef(null);
38
+ const panelRef = useRef(null);
39
+ // Set when the component itself closes, so focus only returns to the trigger on those paths and
40
+ // not when a consumer closes it for their own reasons.
41
+ const returnFocus = useRef(false);
42
+ const setOpen = useCallback((next) => {
43
+ if (!isControlled)
44
+ setUncontrolledOpen(next);
45
+ onOpenChange?.(next);
46
+ }, [isControlled, onOpenChange]);
47
+ const close = useCallback(({ focusTrigger }) => {
48
+ returnFocus.current = focusTrigger;
49
+ setOpen(false);
50
+ }, [setOpen]);
51
+ // The trigger toggles, and one press must produce exactly one notification. It sets the
52
+ // focus-return flag directly rather than going through `close` first: closing and then opening
53
+ // would report `false` before `true` on the way open, and `false` twice on the way closed, which
54
+ // is a state change a consumer never asked for.
55
+ const toggle = useCallback(() => {
56
+ returnFocus.current = false;
57
+ setOpen(!open);
58
+ }, [open, setOpen]);
59
+ // Focus the first item on open; return focus to the trigger when the component closed itself.
60
+ useEffect(() => {
61
+ if (open) {
62
+ panelRef.current?.querySelector(FOCUSABLE)?.focus();
63
+ return;
64
+ }
65
+ if (returnFocus.current) {
66
+ returnFocus.current = false;
67
+ triggerRef.current?.focus();
68
+ }
69
+ }, [open]);
70
+ // A pointer press outside closes the panel. `pointerdown` rather than `click`, and nothing is
71
+ // prevented or stopped, so whatever was pressed still receives its own event normally.
72
+ useEffect(() => {
73
+ if (!open)
74
+ return;
75
+ const onPointerDown = (event) => {
76
+ const target = event.target;
77
+ if (target instanceof Node && wrapperRef.current?.contains(target))
78
+ return;
79
+ close({ focusTrigger: false });
80
+ };
81
+ document.addEventListener('pointerdown', onPointerDown);
82
+ return () => {
83
+ document.removeEventListener('pointerdown', onPointerDown);
84
+ };
85
+ }, [open, close]);
86
+ // Escape and focus-loss are delegated with native listeners on the wrapper rather than JSX
87
+ // handlers. The wrapper is a positioning container, not something interactive, and hanging key
88
+ // handlers off a plain div is exactly what `jsx-a11y/no-static-element-interactions` objects to —
89
+ // rightly, because it usually means a div is pretending to be a control. Here the real handling
90
+ // belongs to the button and the items that bubble their events up, so delegation is the honest
91
+ // shape. `focusout` is used because, unlike `blur`, it bubbles.
92
+ useEffect(() => {
93
+ const wrapper = wrapperRef.current;
94
+ if (!open || !wrapper)
95
+ return;
96
+ const onKeyDown = (event) => {
97
+ if (event.key !== 'Escape')
98
+ return;
99
+ // One press, one layer, through two different mechanisms.
100
+ //
101
+ // `preventDefault()` is for a native modal `<dialog>` above this panel: Escape reaching one is
102
+ // a *close request*, and the close-request algorithm only runs the close watcher when the
103
+ // keydown was not cancelled. Cancelling it says this press has been spent.
104
+ //
105
+ // `stopPropagation()` is for JavaScript layers that listen for the key themselves, which would
106
+ // otherwise act on the same press.
107
+ event.preventDefault();
108
+ event.stopPropagation();
109
+ close({ focusTrigger: true });
110
+ };
111
+ // Checked against the element receiving focus rather than a timer, so it is not fooled by focus
112
+ // moving between the panel's own items.
113
+ const onFocusOut = (event) => {
114
+ const next = event.relatedTarget;
115
+ if (next instanceof Node && wrapper.contains(next))
116
+ return;
117
+ close({ focusTrigger: false });
118
+ };
119
+ wrapper.addEventListener('keydown', onKeyDown);
120
+ wrapper.addEventListener('focusout', onFocusOut);
121
+ return () => {
122
+ wrapper.removeEventListener('keydown', onKeyDown);
123
+ wrapper.removeEventListener('focusout', onFocusOut);
124
+ };
125
+ }, [open, close]);
126
+ return (_jsxs("div", { ref: wrapperRef, className: className === undefined ? 'lutra-dropdown' : `lutra-dropdown ${className}`, children: [_jsx(Button, { ref: triggerRef, variant: triggerVariant, size: triggerSize, "aria-expanded": open, "aria-controls": panelId, onClick: toggle, ...(icon
127
+ ? { contentType: 'icon-only', 'aria-label': label, children: icon }
128
+ : { children: label }) }), open && (_jsx("div", { ref: panelRef, id: panelId, className: "lutra-dropdown__panel", children: children }))] }));
129
+ }
130
+ /**
131
+ * One item in a Dropdown panel.
132
+ *
133
+ * Renders a `<button>`, or an `<a>` when `href` is given. That is the whole of the semantic
134
+ * decision, and it follows the annotation: items that navigate are anchors, items that act are
135
+ * buttons. Neither gets a menu role.
136
+ *
137
+ * `disabled` uses whichever real mechanism the element has — the `disabled` attribute on a button,
138
+ * `aria-disabled` plus a suppressed activation on a link, since an anchor has no disabled attribute
139
+ * and one that merely looks unavailable but still navigates is a trap.
140
+ *
141
+ * `tone="danger"` is a visual cue only. A destructive action still needs its own confirmation step;
142
+ * colour is not a safeguard.
143
+ */
144
+ export const DropdownItem = forwardRef(function DropdownItem({ href, tone = 'default', icon, children, disabled = false, className, ...rest }, ref) {
145
+ const shared = {
146
+ className: className === undefined ? 'lutra-dropdown__item' : `lutra-dropdown__item ${className}`,
147
+ 'data-tone': tone,
148
+ };
149
+ // Rendered as real JSX children rather than passed through the shared spread, so the element
150
+ // visibly has content — both to a reader and to `jsx-a11y/anchor-has-content`.
151
+ const body = (_jsxs(_Fragment, { children: [icon !== undefined && icon !== null && (_jsx("span", { className: "lutra-dropdown__item-icon", "aria-hidden": "true", children: icon })), _jsx("span", { className: "lutra-dropdown__item-label", children: children })] }));
152
+ if (href !== undefined) {
153
+ const anchorProps = rest;
154
+ return (_jsx("a", { ...anchorProps, ...shared, ref: ref, href: href, "aria-disabled": disabled || undefined, onClick: (event) => {
155
+ // An anchor has no disabled attribute, so activation is suppressed by hand. Without this
156
+ // an item that looks unavailable would still navigate.
157
+ if (disabled) {
158
+ event.preventDefault();
159
+ return;
160
+ }
161
+ anchorProps.onClick?.(event);
162
+ }, children: body }));
163
+ }
164
+ const buttonProps = rest;
165
+ return (_jsx("button", { ...buttonProps, ...shared, ref: ref, type: "button", disabled: disabled, children: body }));
166
+ });
167
+ /** A visual divider between groups of items. Presentational only. */
168
+ export function DropdownSeparator({ className }) {
169
+ return (_jsx("hr", { className: className === undefined
170
+ ? 'lutra-dropdown__separator'
171
+ : `lutra-dropdown__separator ${className}` }));
172
+ }
173
+ //# sourceMappingURL=dropdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropdown.js","sourceRoot":"","sources":["../../../src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { Dropdown, DropdownItem, DropdownSeparator } from './dropdown.js';
2
+ export type { DropdownProps, DropdownItemProps } from './dropdown.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/dropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Dropdown, DropdownItem, DropdownSeparator } from './dropdown.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/dropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,81 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export interface ErrorSummaryItem {
3
+ /**
4
+ * The `id` of the control this message belongs to. The entry links to it, and activating the entry
5
+ * moves focus there.
6
+ *
7
+ * The control needs an explicit `id` for this to work — pass the same one to `Field`, `Select
8
+ * Field` or `FieldGroup`.
9
+ */
10
+ fieldId: string;
11
+ /**
12
+ * The message, **word for word the same as the field's own inline error**. Two wordings for one
13
+ * problem reads as two problems.
14
+ */
15
+ message: string;
16
+ }
17
+ export interface ErrorSummaryProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
18
+ /** One entry per invalid field, in the order the fields appear in the form. */
19
+ errors: ErrorSummaryItem[];
20
+ /** @default 'There is a problem' */
21
+ title?: string;
22
+ /**
23
+ * Whether to take focus when the summary appears.
24
+ *
25
+ * On by default, because that is the whole point of the component: a summary nobody is sent to is
26
+ * a summary nobody reads. Turn it off only if you are managing focus yourself.
27
+ *
28
+ * Named `focusOnMount` rather than `autoFocus` on purpose. The native `autoFocus` attribute is a
29
+ * different mechanism with a deservedly bad reputation — it fires on page load, before anyone has
30
+ * asked for anything. This fires when a submit has already failed, which is the opposite
31
+ * situation, and the distinct name keeps the two from being read as the same thing.
32
+ *
33
+ * @default true
34
+ */
35
+ focusOnMount?: boolean;
36
+ /**
37
+ * Replaces `document.title` while the summary is mounted, restoring the previous title when it
38
+ * unmounts.
39
+ *
40
+ * **Opt-in**, and the contract asks for it: someone who navigated away and came back learns from
41
+ * the title alone that the submit failed. It is not automatic because a library silently rewriting
42
+ * the page title would be a surprise, and because the wording belongs to the application —
43
+ * `"Error: Apply for support"` reads better than anything this component could compose.
44
+ */
45
+ documentTitle?: string;
46
+ }
47
+ /**
48
+ * The list of everything that went wrong, shown above the form after a failed submit — and only
49
+ * then.
50
+ *
51
+ * ## What it is for
52
+ *
53
+ * A long form that fails validation leaves someone with no idea how many problems there are or where
54
+ * they live. The summary answers both at the top of the page, and every entry is a link to the field
55
+ * it names, so the answer comes with a way to act on it.
56
+ *
57
+ * **It never replaces inline errors.** The field keeps its own message, its `aria-invalid` and its
58
+ * `aria-describedby`; the summary is a second, earlier route to the same information. And the entry
59
+ * text must match the inline message word for word — two wordings for one problem reads as two
60
+ * problems.
61
+ *
62
+ * ## Focus
63
+ *
64
+ * The container is focusable **programmatically only** — `tabIndex={-1}`, so it never joins the tab
65
+ * order — and takes focus when it appears. That is what announces the failure immediately rather
66
+ * than leaving it to be discovered.
67
+ *
68
+ * It focuses on mount. A second failed submit while the summary is already on screen will not
69
+ * re-announce by itself: give it a `key` that changes per submission, or call `.focus()` on the ref.
70
+ * Both are shown in the stories.
71
+ *
72
+ * There is no `role="alert"` and no live region. Focus moving into the container is what announces
73
+ * it; a live region as well would say the same thing twice, and out of order.
74
+ *
75
+ * ## What it is not
76
+ *
77
+ * Not a form framework. It takes a list of errors you already have and renders it — validation, when
78
+ * to run it, and what the messages say are all yours.
79
+ */
80
+ export declare const ErrorSummary: import("react").ForwardRefExoticComponent<ErrorSummaryProps & import("react").RefAttributes<HTMLDivElement>>;
81
+ //# sourceMappingURL=error-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-summary.d.ts","sourceRoot":"","sources":["../../../src/components/error-summary/error-summary.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,OAAO,CAAC;AAIxD,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACzF,+EAA+E;IAC/E,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,YAAY,8GAoFvB,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useRef } from 'react';
3
+ import { useIsomorphicLayoutEffect } from '../../internal/isomorphic-layout-effect.js';
4
+ function AlertCircleIcon() {
5
+ return (_jsxs("svg", { className: "lutra-error-summary__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 7v6", strokeLinecap: "round" }), _jsx("path", { d: "M12 16.5h.01", strokeLinecap: "round" })] }));
6
+ }
7
+ /**
8
+ * The list of everything that went wrong, shown above the form after a failed submit — and only
9
+ * then.
10
+ *
11
+ * ## What it is for
12
+ *
13
+ * A long form that fails validation leaves someone with no idea how many problems there are or where
14
+ * they live. The summary answers both at the top of the page, and every entry is a link to the field
15
+ * it names, so the answer comes with a way to act on it.
16
+ *
17
+ * **It never replaces inline errors.** The field keeps its own message, its `aria-invalid` and its
18
+ * `aria-describedby`; the summary is a second, earlier route to the same information. And the entry
19
+ * text must match the inline message word for word — two wordings for one problem reads as two
20
+ * problems.
21
+ *
22
+ * ## Focus
23
+ *
24
+ * The container is focusable **programmatically only** — `tabIndex={-1}`, so it never joins the tab
25
+ * order — and takes focus when it appears. That is what announces the failure immediately rather
26
+ * than leaving it to be discovered.
27
+ *
28
+ * It focuses on mount. A second failed submit while the summary is already on screen will not
29
+ * re-announce by itself: give it a `key` that changes per submission, or call `.focus()` on the ref.
30
+ * Both are shown in the stories.
31
+ *
32
+ * There is no `role="alert"` and no live region. Focus moving into the container is what announces
33
+ * it; a live region as well would say the same thing twice, and out of order.
34
+ *
35
+ * ## What it is not
36
+ *
37
+ * Not a form framework. It takes a list of errors you already have and renders it — validation, when
38
+ * to run it, and what the messages say are all yours.
39
+ */
40
+ export const ErrorSummary = forwardRef(function ErrorSummary({ errors, title = 'There is a problem', focusOnMount = true, documentTitle, className, ...nativeProps }, forwardedRef) {
41
+ const containerRef = useRef(null);
42
+ const setRef = (element) => {
43
+ containerRef.current = element;
44
+ if (typeof forwardedRef === 'function')
45
+ forwardedRef(element);
46
+ else if (forwardedRef)
47
+ forwardedRef.current = element;
48
+ };
49
+ // Before paint, so the summary is focused in the same frame it appears rather than a frame later,
50
+ // which a screen reader can otherwise miss.
51
+ useIsomorphicLayoutEffect(() => {
52
+ if (focusOnMount)
53
+ containerRef.current?.focus();
54
+ // Deliberately on mount only, and the empty dependency list is the point: re-running whenever
55
+ // `errors` changed would steal focus from someone who is mid-correction, which is the opposite
56
+ // of helping. A second failed submit re-announces through a new `key` or the forwarded ref.
57
+ }, []);
58
+ useEffect(() => {
59
+ if (documentTitle === undefined)
60
+ return;
61
+ const previous = document.title;
62
+ document.title = documentTitle;
63
+ return () => {
64
+ document.title = previous;
65
+ };
66
+ }, [documentTitle]);
67
+ const onEntryClick = (fieldId) => (event) => {
68
+ const target = document.getElementById(fieldId);
69
+ if (!target)
70
+ return;
71
+ // The anchor is real and its href works on its own — this only makes the landing better. A
72
+ // fragment link moves focus natively when the target is focusable, and does nothing useful when
73
+ // it is a wrapper, so focus is placed explicitly.
74
+ //
75
+ // `focus()` scrolls the element into view by itself, so there is no `scrollIntoView()` here:
76
+ // calling it as well would be a second scroll doing the same job, and one more browser API to
77
+ // feature-detect for no gain.
78
+ event.preventDefault();
79
+ target.focus();
80
+ };
81
+ return (_jsxs("div", { ...nativeProps, ref: setRef, className: className === undefined ? 'lutra-error-summary' : `lutra-error-summary ${className}`,
82
+ // Focusable on purpose, in the tab order never.
83
+ tabIndex: -1, children: [_jsxs("div", { className: "lutra-error-summary__heading-row", children: [_jsx(AlertCircleIcon, {}), _jsx("h2", { className: "lutra-error-summary__title", children: title })] }), _jsx("ul", { className: "lutra-error-summary__list", children: errors.map((error) => (_jsx("li", { className: "lutra-error-summary__item", children: _jsx("a", { className: "lutra-error-summary__link", href: `#${error.fieldId}`, onClick: onEntryClick(error.fieldId), children: error.message }) }, error.fieldId))) })] }));
84
+ });
85
+ //# sourceMappingURL=error-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-summary.js","sourceRoot":"","sources":["../../../src/components/error-summary/error-summary.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { ErrorSummary } from './error-summary.js';
2
+ export type { ErrorSummaryProps, ErrorSummaryItem } from './error-summary.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/error-summary/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ErrorSummary } from './error-summary.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/error-summary/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { FieldIndicator } from '../../internal/field-support.js';
3
+ import type { InputProps } from '../input/index.js';
4
+ export type { FieldIndicator };
5
+ /**
6
+ * Input props that Field derives rather than forwards.
7
+ *
8
+ * `invalid` is derived from `error`, and `aria-invalid` was never on `InputProps` to begin with, so
9
+ * the field's look, its announced state and its visible message all come from the one value.
10
+ */
11
+ type ForwardedInputProps = Omit<InputProps, 'invalid'>;
12
+ export interface FieldProps extends ForwardedInputProps {
13
+ /**
14
+ * The visible label. Always required — a field without one is not usable, and a placeholder is
15
+ * not a substitute for it.
16
+ */
17
+ label: ReactNode;
18
+ /**
19
+ * Supporting text shown above the control and connected with `aria-describedby`. Use it for
20
+ * format guidance the person needs before they start typing.
21
+ */
22
+ hint?: ReactNode;
23
+ /**
24
+ * The error message. **Its presence is what puts the field in the error state** — it drives the
25
+ * red treatment, `aria-invalid` on the control, and the visible message together, so they cannot
26
+ * disagree. Omit it and the field is valid.
27
+ */
28
+ error?: ReactNode;
29
+ /**
30
+ * The word shown after the label text and included in the accessible name.
31
+ *
32
+ * Defaults to `'required'` when the native `required` attribute is set, and `'none'` otherwise,
33
+ * so the visible word and the announced state agree by default. Pass `'optional'` to mark
34
+ * optional fields in a form where most are required.
35
+ */
36
+ indicator?: FieldIndicator;
37
+ /** Extra class on the field wrapper, not on the control. */
38
+ className?: string;
39
+ }
40
+ /**
41
+ * A labelled text field: label, optional hint, an `Input`, and an optional error message.
42
+ *
43
+ * This is the ordinary way to render an Input. The primitive deliberately does not manufacture a
44
+ * label; Field is what supplies one, associates it with `for`/`id`, and wires the hint and error to
45
+ * the control with `aria-describedby`.
46
+ *
47
+ * Everything not listed above is forwarded to the underlying `Input`, so `type`, `name`, `value`,
48
+ * `placeholder`, `required`, `readOnly`, `autoComplete`, `size`, `fullWidth`, the event handlers and
49
+ * `data-*` all behave exactly as they do there. A `ref` reaches the native `<input>`.
50
+ *
51
+ * Order is label → hint → control → error. The hint is above the control so it is read before the
52
+ * control is operated; the error is below it, matching the Figma annotation and Field Group.
53
+ */
54
+ export declare const Field: import("react").ForwardRefExoticComponent<FieldProps & import("react").RefAttributes<HTMLInputElement>>;
55
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../src/components/field/field.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;;GAKG;AACH,KAAK,mBAAmB,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAEvD,MAAM,WAAW,UAAW,SAAQ,mBAAmB;IACrD;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,KAAK,yGA4ChB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { FieldShell, useFieldIds } from '../../internal/field-support.js';
4
+ import { Input } from '../input/index.js';
5
+ /**
6
+ * A labelled text field: label, optional hint, an `Input`, and an optional error message.
7
+ *
8
+ * This is the ordinary way to render an Input. The primitive deliberately does not manufacture a
9
+ * label; Field is what supplies one, associates it with `for`/`id`, and wires the hint and error to
10
+ * the control with `aria-describedby`.
11
+ *
12
+ * Everything not listed above is forwarded to the underlying `Input`, so `type`, `name`, `value`,
13
+ * `placeholder`, `required`, `readOnly`, `autoComplete`, `size`, `fullWidth`, the event handlers and
14
+ * `data-*` all behave exactly as they do there. A `ref` reaches the native `<input>`.
15
+ *
16
+ * Order is label → hint → control → error. The hint is above the control so it is read before the
17
+ * control is operated; the error is below it, matching the Figma annotation and Field Group.
18
+ */
19
+ export const Field = forwardRef(function Field({ label, hint, error, indicator, className, id, required = false, disabled = false, 'aria-describedby': describedBy, ...inputProps }, ref) {
20
+ const hasHint = hint !== undefined && hint !== null && hint !== false;
21
+ const hasError = error !== undefined && error !== null && error !== false;
22
+ const ids = useFieldIds({ id, hasHint, hasError, describedBy });
23
+ const resolvedIndicator = indicator ?? (required ? 'required' : 'none');
24
+ return (_jsx(FieldShell, { controlId: ids.controlId, label: label, indicator: resolvedIndicator, hint: hint, hintId: ids.hintId, error: error, errorId: ids.errorId, disabled: disabled, className: className, children: _jsx(Input, { ...inputProps, ref: ref, id: ids.controlId, required: required, disabled: disabled, invalid: hasError, "aria-describedby": ids.describedBy }) }));
25
+ });
26
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../../../src/components/field/field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAGnC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AA0C1C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA+B,SAAS,KAAK,CAC1E,EACE,KAAK,EACL,IAAI,EACJ,KAAK,EACL,SAAS,EACT,SAAS,EACT,EAAE,EACF,QAAQ,GAAG,KAAK,EAChB,QAAQ,GAAG,KAAK,EAChB,kBAAkB,EAAE,WAAW,EAC/B,GAAG,UAAU,EACd,EACD,GAAG;IAEH,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,CAAC;IAE1E,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;IAChE,MAAM,iBAAiB,GAAmB,SAAS,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAExF,OAAO,CACL,KAAC,UAAU,IACT,SAAS,EAAE,GAAG,CAAC,SAAS,EACxB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,iBAAiB,EAC5B,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,GAAG,CAAC,MAAM,EAClB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,GAAG,CAAC,OAAO,EACpB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,YAEpB,KAAC,KAAK,OACA,UAAU,EACd,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,GAAG,CAAC,SAAS,EACjB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,sBACC,GAAG,CAAC,WAAW,GACjC,GACS,CACd,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Field } from './field.js';
2
+ export type { FieldProps, FieldIndicator } from './field.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/field/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Field } from './field.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/field/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { FieldsetHTMLAttributes, ReactNode } from 'react';
2
+ /**
3
+ * Native `<fieldset>` attributes, minus what this component owns.
4
+ *
5
+ * `FieldsetHTMLAttributes` rather than the generic `HTMLAttributes`, so the element's own
6
+ * attributes survive the wrapper: `name` and `form` are specific to `<fieldset>` and would be
7
+ * dropped by the generic type. `disabled` belongs to it too, and is redeclared below only so it can
8
+ * carry documentation and a default.
9
+ */
10
+ type NativeFieldsetProps = Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'children' | 'disabled'>;
11
+ export interface FieldGroupProps extends NativeFieldsetProps {
12
+ /**
13
+ * The question the controls answer, rendered as a `<legend>`.
14
+ *
15
+ * The legend provides the accessible name for the group, and is the context an assistive
16
+ * technology has available while a user moves through the controls inside it. Keep it short:
17
+ * assistive technologies may expose that context repeatedly as the controls are navigated.
18
+ */
19
+ legend: ReactNode;
20
+ /**
21
+ * Instructions or supporting text for the group, connected with `aria-describedby`.
22
+ *
23
+ * **There is no default.** "Choose all that apply" is right for a checkbox group and wrong for a
24
+ * radio group, and a generic default would be wrong roughly half the time — which is why the
25
+ * design master removed it. Say what this particular question needs.
26
+ */
27
+ hint?: ReactNode;
28
+ /**
29
+ * The error message for the group. Its presence is what puts the group in the error state, so the
30
+ * red treatment and the visible message cannot disagree.
31
+ *
32
+ * A group-level failure is the normal case for radios and checkboxes: the question was not
33
+ * answered, rather than one option being wrong.
34
+ */
35
+ error?: ReactNode;
36
+ /** Whether the group is disabled. Native — it disables every control inside. */
37
+ disabled?: boolean;
38
+ /** The controls: `CheckboxItem`s, `RadioItem`s, or anything else the question needs. */
39
+ children: ReactNode;
40
+ }
41
+ /**
42
+ * A group of related controls that answer one question.
43
+ *
44
+ * Renders a native `<fieldset>` with a `<legend>`, which is the platform's own grouping semantic:
45
+ * the legend names the group, and `disabled` on the fieldset disables every control in it. Neither
46
+ * is reimplemented, and neither needs ARIA to replace it.
47
+ *
48
+ * Order is legend → hint → controls → error, matching `Field`.
49
+ *
50
+ * ## Why there is no `role="group"` or `aria-invalid` here
51
+ *
52
+ * `<fieldset>` already carries the group semantic, so adding `role="group"` would restate what the
53
+ * element says. `aria-invalid` is not placed on the fieldset either: it is defined for widgets that
54
+ * hold a value, and a fieldset holds none — assistive technology would have nothing to attach it to.
55
+ *
56
+ * The group's invalid state is therefore expressed as a relationship rather than a state flag: the
57
+ * error message is connected to the `<fieldset>` with `aria-describedby`, which is the established
58
+ * way to attach supporting text to a group, and the individual controls draw their own error
59
+ * treatment through their `invalid` prop. Exactly how and when a given screen reader surfaces a
60
+ * group-level description varies, and this component does not promise any particular behaviour —
61
+ * which is why the message is also visible on screen and the controls are drawn in their error
62
+ * state. That is the accessibility annotation's model, and it is why `Radio`'s `invalid` is visual
63
+ * only.
64
+ */
65
+ export declare function FieldGroup({ legend, hint, error, disabled, className, children, 'aria-describedby': describedBy, ...nativeProps }: FieldGroupProps): import("react").JSX.Element;
66
+ export {};
67
+ //# sourceMappingURL=field-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-group.d.ts","sourceRoot":"","sources":["../../../src/components/field-group/field-group.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE/D;;;;;;;GAOG;AACH,KAAK,mBAAmB,GAAG,IAAI,CAC7B,sBAAsB,CAAC,mBAAmB,CAAC,EAC3C,UAAU,GAAG,UAAU,CACxB,CAAC;AAEF,MAAM,WAAW,eAAgB,SAAQ,mBAAmB;IAC1D;;;;;;OAMG;IACH,MAAM,EAAE,SAAS,CAAC;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wFAAwF;IACxF,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,KAAK,EACL,QAAgB,EAChB,SAAS,EACT,QAAQ,EACR,kBAAkB,EAAE,WAAW,EAC/B,GAAG,WAAW,EACf,EAAE,eAAe,+BAmDjB"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from 'react';
3
+ /**
4
+ * A group of related controls that answer one question.
5
+ *
6
+ * Renders a native `<fieldset>` with a `<legend>`, which is the platform's own grouping semantic:
7
+ * the legend names the group, and `disabled` on the fieldset disables every control in it. Neither
8
+ * is reimplemented, and neither needs ARIA to replace it.
9
+ *
10
+ * Order is legend → hint → controls → error, matching `Field`.
11
+ *
12
+ * ## Why there is no `role="group"` or `aria-invalid` here
13
+ *
14
+ * `<fieldset>` already carries the group semantic, so adding `role="group"` would restate what the
15
+ * element says. `aria-invalid` is not placed on the fieldset either: it is defined for widgets that
16
+ * hold a value, and a fieldset holds none — assistive technology would have nothing to attach it to.
17
+ *
18
+ * The group's invalid state is therefore expressed as a relationship rather than a state flag: the
19
+ * error message is connected to the `<fieldset>` with `aria-describedby`, which is the established
20
+ * way to attach supporting text to a group, and the individual controls draw their own error
21
+ * treatment through their `invalid` prop. Exactly how and when a given screen reader surfaces a
22
+ * group-level description varies, and this component does not promise any particular behaviour —
23
+ * which is why the message is also visible on screen and the controls are drawn in their error
24
+ * state. That is the accessibility annotation's model, and it is why `Radio`'s `invalid` is visual
25
+ * only.
26
+ */
27
+ export function FieldGroup({ legend, hint, error, disabled = false, className, children, 'aria-describedby': describedBy, ...nativeProps }) {
28
+ const generated = useId();
29
+ const base = `lutra-field-group-${generated}`;
30
+ const hasHint = hint !== undefined && hint !== null && hint !== false;
31
+ const hasError = error !== undefined && error !== null && error !== false;
32
+ const hintId = hasHint ? `${base}-hint` : undefined;
33
+ const errorId = hasError ? `${base}-error` : undefined;
34
+ // The consumer's own description is preserved and listed first, as it is in Field.
35
+ const parts = [describedBy, hintId, errorId].filter((part) => Boolean(part));
36
+ return (_jsxs("fieldset", { ...nativeProps, className: className === undefined ? 'lutra-field-group' : `lutra-field-group ${className}`, disabled: disabled, "aria-describedby": parts.length > 0 ? parts.join(' ') : undefined, "data-invalid": hasError ? 'true' : undefined, children: [_jsx("legend", { className: "lutra-field-group__legend", children: legend }), hasHint && (_jsx("p", { className: "lutra-field-group__hint", id: hintId, children: hint })), _jsx("div", { className: "lutra-field-group__items", children: children }), hasError && (_jsxs("p", { className: "lutra-field-group__error", id: errorId, children: [_jsxs("svg", { className: "lutra-field-group__error-icon", viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "8", cy: "8", r: "7", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M8 4.5v4.25", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }), _jsx("circle", { cx: "8", cy: "11.5", r: "0.9", fill: "currentColor" })] }), error] }))] }));
37
+ }
38
+ //# sourceMappingURL=field-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-group.js","sourceRoot":"","sources":["../../../src/components/field-group/field-group.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AA+C9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,KAAK,EACL,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,QAAQ,EACR,kBAAkB,EAAE,WAAW,EAC/B,GAAG,WAAW,EACE;IAChB,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,qBAAqB,SAAS,EAAE,CAAC;IAE9C,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,CAAC;IAE1E,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAEvD,mFAAmF;IACnF,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAE7F,OAAO,CACL,uBACM,WAAW,EACf,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,qBAAqB,SAAS,EAAE,EAC3F,QAAQ,EAAE,QAAQ,sBACA,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,kBAClD,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,aAE3C,iBAAQ,SAAS,EAAC,2BAA2B,YAAE,MAAM,GAAU,EAE9D,OAAO,IAAI,CACV,YAAG,SAAS,EAAC,yBAAyB,EAAC,EAAE,EAAE,MAAM,YAC9C,IAAI,GACH,CACL,EAED,cAAK,SAAS,EAAC,0BAA0B,YAAE,QAAQ,GAAO,EAEzD,QAAQ,IAAI,CACX,aAAG,SAAS,EAAC,0BAA0B,EAAC,EAAE,EAAE,OAAO,aAIjD,eACE,SAAS,EAAC,+BAA+B,EACzC,OAAO,EAAC,WAAW,iBACP,MAAM,EAClB,SAAS,EAAC,OAAO,aAEjB,iBAAQ,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,GAAG,EAClF,eAAM,CAAC,EAAC,aAAa,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,GAAG,EACtF,iBAAQ,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,MAAM,EAAC,CAAC,EAAC,KAAK,EAAC,IAAI,EAAC,cAAc,GAAG,IACnD,EACL,KAAK,IACJ,CACL,IACQ,CACZ,CAAC;AACJ,CAAC"}