@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,3 @@
1
+ export { Card, CardFooter } from './card.js';
2
+ export type { CardProps, CardElevation, CardFooterProps, CardFooterAlign, HeadingLevel, } from './card.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC7C,YAAY,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,eAAe,EACf,YAAY,GACb,MAAM,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Card, CardFooter } from './card.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { InputHTMLAttributes, ReactNode } from 'react';
2
+ /**
3
+ * Native `<input type="checkbox">` attributes, minus what this component owns.
4
+ *
5
+ * `type` is fixed, `aria-invalid` is derived from `invalid`, and `children` is dropped because
6
+ * `<input>` is a void element.
7
+ */
8
+ type NativeCheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'aria-invalid' | 'children' | 'size'>;
9
+ export interface CheckboxProps extends NativeCheckboxProps {
10
+ /**
11
+ * Puts the box in its mixed state.
12
+ *
13
+ * **This is the native DOM property, not a third checked value.** `HTMLInputElement.indeterminate`
14
+ * exists only in the DOM — there is no HTML attribute for it and it is unrelated to `checked`, so
15
+ * React cannot set it from JSX. It is assigned in a layout effect after every render, which is
16
+ * what makes it survive re-renders, prop changes and remounts.
17
+ *
18
+ * A mixed box is still either checked or unchecked underneath; the mixed state is a display of
19
+ * "some of the things below are selected", and clicking it resolves to a real value.
20
+ *
21
+ * @default false
22
+ */
23
+ indeterminate?: boolean;
24
+ /**
25
+ * Marks the control as failing validation. Sets the visual treatment and `aria-invalid="true"`
26
+ * from one value, so they cannot disagree.
27
+ *
28
+ * @default false
29
+ */
30
+ invalid?: boolean;
31
+ }
32
+ /**
33
+ * A checkbox. Renders a native `<input type="checkbox">`.
34
+ *
35
+ * Native on purpose: the space-bar toggle, the label-click target, form participation, the mixed
36
+ * state announcement and the disabled semantics all come from the platform. A `<div role="checkbox">`
37
+ * would have to reimplement every one of them and would still be worse.
38
+ *
39
+ * **Every rendered Checkbox must have an accessible name.** The primitive does not manufacture one —
40
+ * use `CheckboxItem` for the ordinary labelled composition, or supply `aria-label` /
41
+ * `aria-labelledby` for a checkbox whose meaning comes from elsewhere, such as a table row selector.
42
+ *
43
+ * `forwardRef` rather than a plain `ref` prop: this package supports React 18.3 as well as 19. The
44
+ * component keeps its own internal ref as well, so `indeterminate` works whether or not a consumer
45
+ * passes one.
46
+ */
47
+ export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
48
+ export interface CheckboxItemProps extends CheckboxProps {
49
+ /** The visible label. It is the checkbox's accessible name. */
50
+ label: ReactNode;
51
+ /** Supporting text below the label, connected with `aria-describedby`. */
52
+ description?: ReactNode;
53
+ /** Extra class on the item wrapper, not on the control. */
54
+ className?: string;
55
+ }
56
+ /**
57
+ * A checkbox with its visible label, and optionally a description.
58
+ *
59
+ * This is the ordinary way to render a Checkbox — the composition the Figma `Checkbox Item`
60
+ * component draws. The label is a real `<label>` associated by `for`/`id`, so clicking or tapping
61
+ * anywhere on it toggles the box, which also makes the target far larger than the 20px box.
62
+ *
63
+ * The label and description grow with their content. Nothing sets a fixed height.
64
+ */
65
+ export declare const CheckboxItem: import("react").ForwardRefExoticComponent<CheckboxItemProps & import("react").RefAttributes<HTMLInputElement>>;
66
+ export {};
67
+ //# sourceMappingURL=checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/checkbox.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAK5D;;;;;GAKG;AACH,KAAK,mBAAmB,GAAG,IAAI,CAC7B,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,CAC9C,CAAC;AAEF,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,4GA8CnB,CAAC;AAEH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,+DAA+D;IAC/D,KAAK,EAAE,SAAS,CAAC;IACjB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,gHAuCvB,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useRef } from 'react';
3
+ import { useFieldIds } from '../../internal/field-support.js';
4
+ import { useIsomorphicLayoutEffect } from '../../internal/isomorphic-layout-effect.js';
5
+ /**
6
+ * A checkbox. Renders a native `<input type="checkbox">`.
7
+ *
8
+ * Native on purpose: the space-bar toggle, the label-click target, form participation, the mixed
9
+ * state announcement and the disabled semantics all come from the platform. A `<div role="checkbox">`
10
+ * would have to reimplement every one of them and would still be worse.
11
+ *
12
+ * **Every rendered Checkbox must have an accessible name.** The primitive does not manufacture one —
13
+ * use `CheckboxItem` for the ordinary labelled composition, or supply `aria-label` /
14
+ * `aria-labelledby` for a checkbox whose meaning comes from elsewhere, such as a table row selector.
15
+ *
16
+ * `forwardRef` rather than a plain `ref` prop: this package supports React 18.3 as well as 19. The
17
+ * component keeps its own internal ref as well, so `indeterminate` works whether or not a consumer
18
+ * passes one.
19
+ */
20
+ export const Checkbox = forwardRef(function Checkbox({ indeterminate = false, invalid = false, className, ...nativeProps }, forwardedRef) {
21
+ const elementRef = useRef(null);
22
+ // The ref callback only records the element and forwards it. It deliberately does not depend on
23
+ // `indeterminate`, so changing that prop no longer detaches and reattaches the ref — which would
24
+ // hand a consumer's callback ref a spurious null on every change.
25
+ const setRef = useCallback((element) => {
26
+ elementRef.current = element;
27
+ if (typeof forwardedRef === 'function')
28
+ forwardedRef(element);
29
+ else if (forwardedRef)
30
+ forwardedRef.current = element;
31
+ }, [forwardedRef]);
32
+ // Synchronised after the DOM exists, not during render.
33
+ //
34
+ // `indeterminate` has no HTML attribute, so it can only be assigned to the element. Doing that in
35
+ // the render body would be a side effect in the render phase, which React is free to run twice,
36
+ // abandon, or replay — and under StrictMode it does. This runs after every render instead, with
37
+ // no dependency array, so it also covers the case where React swaps the underlying DOM node while
38
+ // the prop is unchanged.
39
+ //
40
+ // A layout effect rather than a passive one, so the property is set before the browser paints:
41
+ // with `useEffect` there is a frame showing a tick or an empty box where a dash belongs.
42
+ useIsomorphicLayoutEffect(() => {
43
+ const element = elementRef.current;
44
+ if (element)
45
+ element.indeterminate = indeterminate;
46
+ });
47
+ return (_jsx("input", { ...nativeProps, ref: setRef, type: "checkbox", className: className === undefined ? 'lutra-checkbox' : `lutra-checkbox ${className}`, "aria-invalid": invalid || undefined }));
48
+ });
49
+ /**
50
+ * A checkbox with its visible label, and optionally a description.
51
+ *
52
+ * This is the ordinary way to render a Checkbox — the composition the Figma `Checkbox Item`
53
+ * component draws. The label is a real `<label>` associated by `for`/`id`, so clicking or tapping
54
+ * anywhere on it toggles the box, which also makes the target far larger than the 20px box.
55
+ *
56
+ * The label and description grow with their content. Nothing sets a fixed height.
57
+ */
58
+ export const CheckboxItem = forwardRef(function CheckboxItem({ label, description, className, id, disabled = false, 'aria-describedby': describedBy, ...checkboxProps }, ref) {
59
+ const hasDescription = description !== undefined && description !== null && description !== false;
60
+ const ids = useFieldIds({ id, hasHint: hasDescription, hasError: false, describedBy });
61
+ return (_jsxs("div", { className: className === undefined ? 'lutra-choice' : `lutra-choice ${className}`, "data-disabled": disabled ? 'true' : undefined, children: [_jsx(Checkbox, { ...checkboxProps, ref: ref, id: ids.controlId, disabled: disabled, "aria-describedby": ids.describedBy }), _jsxs("div", { className: "lutra-choice__content", children: [_jsx("label", { className: "lutra-choice__label", htmlFor: ids.controlId, children: label }), hasDescription && (_jsx("p", { className: "lutra-choice__description", id: ids.hintId, children: description }))] })] }));
62
+ });
63
+ //# sourceMappingURL=checkbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.js","sourceRoot":"","sources":["../../../src/components/checkbox/checkbox.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAqCvF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAkC,SAAS,QAAQ,CACnF,EAAE,aAAa,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,EACrE,YAAY;IAEZ,MAAM,UAAU,GAAG,MAAM,CAA0B,IAAI,CAAC,CAAC;IAEzD,gGAAgG;IAChG,iGAAiG;IACjG,kEAAkE;IAClE,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,OAAgC,EAAE,EAAE;QACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAE7B,IAAI,OAAO,YAAY,KAAK,UAAU;YAAE,YAAY,CAAC,OAAO,CAAC,CAAC;aACzD,IAAI,YAAY;YAAE,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;IACxD,CAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,wDAAwD;IACxD,EAAE;IACF,kGAAkG;IAClG,gGAAgG;IAChG,gGAAgG;IAChG,kGAAkG;IAClG,yBAAyB;IACzB,EAAE;IACF,+FAA+F;IAC/F,yFAAyF;IACzF,yBAAyB,CAAC,GAAG,EAAE;QAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QACnC,IAAI,OAAO;YAAE,OAAO,CAAC,aAAa,GAAG,aAAa,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,mBACM,WAAW,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAC,UAAU,EACf,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,SAAS,EAAE,kBACvE,OAAO,IAAI,SAAS,GAIlC,CACH,CAAC;AACJ,CAAC,CAAC,CAAC;AAWH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAsC,SAAS,YAAY,CAC/F,EACE,KAAK,EACL,WAAW,EACX,SAAS,EACT,EAAE,EACF,QAAQ,GAAG,KAAK,EAChB,kBAAkB,EAAE,WAAW,EAC/B,GAAG,aAAa,EACjB,EACD,GAAG;IAEH,MAAM,cAAc,GAAG,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,KAAK,CAAC;IAClG,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAEvF,OAAO,CACL,eACE,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,gBAAgB,SAAS,EAAE,mBAClE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,aAE5C,KAAC,QAAQ,OACH,aAAa,EACjB,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,GAAG,CAAC,SAAS,EACjB,QAAQ,EAAE,QAAQ,sBACA,GAAG,CAAC,WAAW,GACjC,EACF,eAAK,SAAS,EAAC,uBAAuB,aACpC,gBAAO,SAAS,EAAC,qBAAqB,EAAC,OAAO,EAAE,GAAG,CAAC,SAAS,YAC1D,KAAK,GACA,EACP,cAAc,IAAI,CACjB,YAAG,SAAS,EAAC,2BAA2B,EAAC,EAAE,EAAE,GAAG,CAAC,MAAM,YACpD,WAAW,GACV,CACL,IACG,IACF,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Checkbox, CheckboxItem } from './checkbox.js';
2
+ export type { CheckboxProps, CheckboxItemProps } from './checkbox.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Checkbox, CheckboxItem } from './checkbox.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/checkbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Drawn width. Maps to the `size` axis of the Figma component set. */
3
+ export type DialogSize = 'sm' | 'md' | 'lg';
4
+ /**
5
+ * Where focus lands when the dialog opens.
6
+ *
7
+ * `'first'` gives it to the first interactive element, which is right for most dialogs.
8
+ * `'dialog'` gives it to the dialog container, which the annotation asks for when the content is
9
+ * long enough that starting at a control would skip the explanation — and is the safer choice for a
10
+ * destructive confirmation, where landing on Delete is exactly wrong.
11
+ *
12
+ * A control marked `autoFocus` wins over either, because that is native `<dialog>` behaviour.
13
+ */
14
+ export type DialogInitialFocus = 'first' | 'dialog';
15
+ export interface DialogProps {
16
+ /** Whether the dialog is open. Always controlled — the opener has to know. */
17
+ open: boolean;
18
+ /**
19
+ * Called on every close path: Escape, the close button, a footer action, or a backdrop press.
20
+ * The consumer sets `open` to false in response.
21
+ */
22
+ onClose: () => void;
23
+ /** The visible title. Rendered as a heading, and it is what names the dialog. */
24
+ title: ReactNode;
25
+ /**
26
+ * Short explanatory copy under the title, connected with `aria-describedby`.
27
+ *
28
+ * Omit it when the body is long or structured — a description that reads out several paragraphs
29
+ * is worse than none.
30
+ */
31
+ description?: ReactNode;
32
+ /** @default 'md' */
33
+ size?: DialogSize;
34
+ /** @default true */
35
+ showClose?: boolean;
36
+ /** Accessible name of the close control. @default 'Close' */
37
+ closeLabel?: string;
38
+ /** @default 'first' */
39
+ initialFocus?: DialogInitialFocus;
40
+ /**
41
+ * Whether Escape closes the dialog. Leave it true unless a critical operation is genuinely in
42
+ * progress, and if you turn it off, say why on screen — a dialog with no way out is a trap.
43
+ *
44
+ * @default true
45
+ */
46
+ dismissible?: boolean;
47
+ /**
48
+ * Whether pressing the backdrop closes the dialog. Off by default: it is fine for a low-risk
49
+ * dialog and wrong for one holding unsaved work, so it is opt-in per dialog.
50
+ *
51
+ * @default false
52
+ */
53
+ dismissOnBackdropPress?: boolean;
54
+ /** Footer actions. Kept visible while the body scrolls. */
55
+ footer?: ReactNode;
56
+ /** The body. */
57
+ children: ReactNode;
58
+ className?: string;
59
+ }
60
+ /**
61
+ * A modal dialog. Renders a native `<dialog>` opened with `showModal()`.
62
+ *
63
+ * ## Why native
64
+ *
65
+ * The annotation allows either a native `<dialog>` or a container with `role="dialog"` and
66
+ * `aria-modal="true"`, and asks for focus entry, containment, an inert background, Escape, and focus
67
+ * restoration. `showModal()` provides the first four *from the browser*: the dialog goes into the
68
+ * top layer, everything behind it becomes inert without a single `aria-hidden` being written across
69
+ * the page, Tab and Shift+Tab cannot leave, and Escape fires `cancel`.
70
+ *
71
+ * A hand-built version would reimplement all of that with a focus-trap loop and manual `inert`, and
72
+ * would be worse at it — brittle index arithmetic around focusable elements is precisely the failure
73
+ * this component must not have. So the browser does the containment and this component does only the
74
+ * parts that are genuinely its own: which element receives focus first, and returning focus to the
75
+ * opener on every close path.
76
+ *
77
+ * ## What that costs
78
+ *
79
+ * jsdom implements no part of `<dialog>` — no `showModal`, no `close`, no `inert`. The unit tests
80
+ * therefore prove semantics, wiring and this component's own focus restoration, and prove **nothing**
81
+ * about containment or inertness. Those need a real browser; the stories exist to make them
82
+ * checkable. See `dialog.md`.
83
+ *
84
+ * Where `showModal` is missing the component falls back to the `open` attribute so the content still
85
+ * renders and is still labelled, but it is **not modal** in that state.
86
+ */
87
+ export declare function Dialog({ open, onClose, title, description, size, showClose, closeLabel, initialFocus, dismissible, dismissOnBackdropPress, footer, children, className, }: DialogProps): import("react").JSX.Element;
88
+ //# sourceMappingURL=dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/dialog.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,uEAAuE;AACvE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,iFAAiF;IACjF,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,oBAAoB;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,2DAA2D;IAC3D,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,gBAAgB;IAChB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,MAAM,CAAC,EACrB,IAAI,EACJ,OAAO,EACP,KAAK,EACL,WAAW,EACX,IAAW,EACX,SAAgB,EAChB,UAAoB,EACpB,YAAsB,EACtB,WAAkB,EAClB,sBAA8B,EAC9B,MAAM,EACN,QAAQ,EACR,SAAS,GACV,EAAE,WAAW,+BAkKb"}
@@ -0,0 +1,156 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useId, useRef } from 'react';
3
+ import { isDevelopment } from '../../internal/development.js';
4
+ import { useIsomorphicLayoutEffect } from '../../internal/isomorphic-layout-effect.js';
5
+ import { Button } from '../button/index.js';
6
+ /** How many Lutra dialogs are open. Used only for the development warning about stacking. */
7
+ let openDialogCount = 0;
8
+ function CloseIcon() {
9
+ return (_jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", focusable: "false", children: _jsx("path", { d: "M4 4l8 8M12 4l-8 8", strokeLinecap: "round" }) }));
10
+ }
11
+ /**
12
+ * A modal dialog. Renders a native `<dialog>` opened with `showModal()`.
13
+ *
14
+ * ## Why native
15
+ *
16
+ * The annotation allows either a native `<dialog>` or a container with `role="dialog"` and
17
+ * `aria-modal="true"`, and asks for focus entry, containment, an inert background, Escape, and focus
18
+ * restoration. `showModal()` provides the first four *from the browser*: the dialog goes into the
19
+ * top layer, everything behind it becomes inert without a single `aria-hidden` being written across
20
+ * the page, Tab and Shift+Tab cannot leave, and Escape fires `cancel`.
21
+ *
22
+ * A hand-built version would reimplement all of that with a focus-trap loop and manual `inert`, and
23
+ * would be worse at it — brittle index arithmetic around focusable elements is precisely the failure
24
+ * this component must not have. So the browser does the containment and this component does only the
25
+ * parts that are genuinely its own: which element receives focus first, and returning focus to the
26
+ * opener on every close path.
27
+ *
28
+ * ## What that costs
29
+ *
30
+ * jsdom implements no part of `<dialog>` — no `showModal`, no `close`, no `inert`. The unit tests
31
+ * therefore prove semantics, wiring and this component's own focus restoration, and prove **nothing**
32
+ * about containment or inertness. Those need a real browser; the stories exist to make them
33
+ * checkable. See `dialog.md`.
34
+ *
35
+ * Where `showModal` is missing the component falls back to the `open` attribute so the content still
36
+ * renders and is still labelled, but it is **not modal** in that state.
37
+ */
38
+ export function Dialog({ open, onClose, title, description, size = 'md', showClose = true, closeLabel = 'Close', initialFocus = 'first', dismissible = true, dismissOnBackdropPress = false, footer, children, className, }) {
39
+ const base = useId();
40
+ const titleId = `lutra-dialog-title-${base}`;
41
+ const descriptionId = `lutra-dialog-description-${base}`;
42
+ const dialogRef = useRef(null);
43
+ // The element that had focus when the dialog opened, so it can be given focus back.
44
+ const invokerRef = useRef(null);
45
+ const hasDescription = description !== undefined && description !== null && description !== false;
46
+ // Opening and closing the real element. A layout effect so the dialog is in the top layer before
47
+ // the browser paints, rather than flashing the page underneath for a frame.
48
+ useIsomorphicLayoutEffect(() => {
49
+ const dialog = dialogRef.current;
50
+ if (!dialog)
51
+ return;
52
+ if (open) {
53
+ if (dialog.open)
54
+ return;
55
+ invokerRef.current =
56
+ document.activeElement instanceof HTMLElement ? document.activeElement : null;
57
+ if (typeof dialog.showModal === 'function') {
58
+ dialog.showModal();
59
+ }
60
+ else {
61
+ // No native dialog implementation — jsdom, or a browser old enough not to matter. The
62
+ // content renders and is labelled, but nothing here is modal.
63
+ dialog.setAttribute('open', '');
64
+ }
65
+ if (initialFocus === 'dialog')
66
+ dialog.focus();
67
+ return;
68
+ }
69
+ if (!dialog.open)
70
+ return;
71
+ if (typeof dialog.close === 'function')
72
+ dialog.close();
73
+ else
74
+ dialog.removeAttribute('open');
75
+ }, [open, initialFocus]);
76
+ // Focus restoration, on every close path, because this is the part the browser is least reliable
77
+ // about and the part most worth owning. A dialog that drops focus to the top of the document loses
78
+ // the user's place entirely.
79
+ useEffect(() => {
80
+ if (open)
81
+ return;
82
+ const invoker = invokerRef.current;
83
+ invokerRef.current = null;
84
+ if (!invoker)
85
+ return;
86
+ // The invoker may have been removed or disabled while the dialog was open — restoring focus to
87
+ // it would throw or silently do nothing, so fail gracefully instead.
88
+ if (!invoker.isConnected)
89
+ return;
90
+ if (invoker.hasAttribute('disabled'))
91
+ return;
92
+ invoker.focus();
93
+ }, [open]);
94
+ // Never stack dialogs. The annotation says to resolve or close the first one before opening
95
+ // another; the type system cannot see that, so it is a development warning.
96
+ useEffect(() => {
97
+ if (!open)
98
+ return;
99
+ openDialogCount += 1;
100
+ if (isDevelopment && openDialogCount > 1) {
101
+ console.warn('Dialog: more than one Lutra dialog is open at once. Resolve or close the first before ' +
102
+ 'opening another — stacked modals leave people with no way to tell what has focus.');
103
+ }
104
+ return () => {
105
+ openDialogCount -= 1;
106
+ };
107
+ }, [open]);
108
+ // Escape reaches the element as a `cancel` event rather than a keydown, because the browser owns
109
+ // the dismissal.
110
+ //
111
+ // The browser's own close is always prevented, because `open` is controlled here — letting the
112
+ // element close itself would leave the DOM and the prop disagreeing. `onClose` is then called, or
113
+ // not, which is what `dismissible` means.
114
+ //
115
+ // Nothing about layering lives here. A Tooltip, Dropdown or navigation panel open inside the
116
+ // dialog cancels the keydown itself, and the close-request algorithm does not reach this dialog
117
+ // for a press that was already cancelled.
118
+ const onCancel = useCallback((event) => {
119
+ event.preventDefault();
120
+ if (dismissible)
121
+ onClose();
122
+ }, [dismissible, onClose]);
123
+ useEffect(() => {
124
+ const dialog = dialogRef.current;
125
+ if (!dialog)
126
+ return;
127
+ dialog.addEventListener('cancel', onCancel);
128
+ return () => {
129
+ dialog.removeEventListener('cancel', onCancel);
130
+ };
131
+ }, [onCancel]);
132
+ // A press on the backdrop lands on the dialog element itself, because the backdrop is its
133
+ // pseudo-element rather than a separate node. Anything inside the panel has a different target.
134
+ //
135
+ // Attached as a native listener rather than a JSX `onClick`, for the same reason Dropdown
136
+ // delegates: a click handler in the markup makes `<dialog>` look like an interactive element that
137
+ // owes a keyboard equivalent, which `jsx-a11y` rightly objects to. It owes none — this is a
138
+ // pointer-only shortcut, and Escape is the keyboard way out, handled above.
139
+ useEffect(() => {
140
+ const dialog = dialogRef.current;
141
+ if (!open || !dialog || !dismissOnBackdropPress || !dismissible)
142
+ return;
143
+ const onClick = (event) => {
144
+ if (event.target === dialog)
145
+ onClose();
146
+ };
147
+ dialog.addEventListener('click', onClick);
148
+ return () => {
149
+ dialog.removeEventListener('click', onClick);
150
+ };
151
+ }, [open, dismissOnBackdropPress, dismissible, onClose]);
152
+ return (_jsx("dialog", { ref: dialogRef, className: className === undefined ? 'lutra-dialog' : `lutra-dialog ${className}`, "aria-labelledby": titleId, "aria-describedby": hasDescription ? descriptionId : undefined, "data-size": size,
153
+ // Focusable only as a target for `initialFocus="dialog"`; never in the tab sequence.
154
+ tabIndex: -1, children: _jsxs("div", { className: "lutra-dialog__panel", children: [_jsxs("div", { className: "lutra-dialog__header", children: [_jsxs("div", { className: "lutra-dialog__heading-group", children: [_jsx("h2", { className: "lutra-dialog__title", id: titleId, children: title }), hasDescription && (_jsx("p", { className: "lutra-dialog__description", id: descriptionId, children: description }))] }), showClose && (_jsx(Button, { variant: "secondary", contentType: "icon-only", "aria-label": closeLabel, onClick: onClose, children: _jsx(CloseIcon, {}) }))] }), _jsx("div", { className: "lutra-dialog__body", children: children }), footer !== undefined && footer !== null && footer !== false && (_jsx("div", { className: "lutra-dialog__footer", children: footer }))] }) }));
155
+ }
156
+ //# sourceMappingURL=dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../../src/components/dialog/dialog.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { Dialog } from './dialog.js';
2
+ export type { DialogProps, DialogSize, DialogInitialFocus } from './dialog.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Dialog } from './dialog.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,89 @@
1
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
2
+ import type { ButtonSize, ButtonVariant } from '../button/index.js';
3
+ export interface DropdownProps {
4
+ /**
5
+ * The trigger's accessible name. When `icon` is given the trigger is icon-only and this becomes
6
+ * its `aria-label`; otherwise it is the visible button text.
7
+ *
8
+ * Name what the menu acts on — "More actions for Invoice 4471" beats "More actions" when several
9
+ * triggers share a page.
10
+ */
11
+ label: string;
12
+ /** Renders an icon-only trigger. `label` then names it. */
13
+ icon?: ReactNode;
14
+ /** The items. `DropdownItem` and `DropdownSeparator`, or any interactive content. */
15
+ children: ReactNode;
16
+ /** Controlled open state. Pair with `onOpenChange`. */
17
+ open?: boolean;
18
+ /** Uncontrolled initial state. @default false */
19
+ defaultOpen?: boolean;
20
+ /** Called whenever the component wants the open state to change. */
21
+ onOpenChange?: (open: boolean) => void;
22
+ /** @default 'secondary' */
23
+ triggerVariant?: ButtonVariant;
24
+ /** @default 'md' */
25
+ triggerSize?: ButtonSize;
26
+ /** Extra class on the wrapper. */
27
+ className?: string;
28
+ }
29
+ /**
30
+ * A disclosure that reveals a panel of actions or links from a button.
31
+ *
32
+ * **It is a disclosure, not an ARIA menu, and that is deliberate.** The accessibility annotation is
33
+ * explicit: the trigger is a button, and the panel is a container of buttons or links depending on
34
+ * what each item does. No `role="menu"`, no `role="menuitem"`, and no arrow-key roving.
35
+ *
36
+ * The reason is that the two models cannot be mixed. A real ARIA menu promises arrow-key navigation
37
+ * *and* takes Tab away from the items inside it; a half-built one promises navigation that does not
38
+ * work, which is worse than no role at all. As a disclosure, everything inside keeps its native
39
+ * semantics and Tab keeps working, which is what the annotation asks for.
40
+ *
41
+ * Behaviour:
42
+ *
43
+ * - Enter or Space opens, because the trigger is a real button.
44
+ * - `aria-expanded` reflects the state and `aria-controls` points at the panel.
45
+ * - Focus moves to the first item on open.
46
+ * - Tab moves through the items in DOM order and out the other side.
47
+ * - Escape closes and returns focus to the trigger.
48
+ * - A pointer press outside closes it, without interfering with what was pressed.
49
+ * - Focus leaving the component closes it, so a Tab out does not strand an open panel.
50
+ *
51
+ * Open state is uncontrolled by default and controllable with `open` + `onOpenChange`, matching the
52
+ * pattern the form controls already use. There is no state machine beyond that.
53
+ */
54
+ export declare function Dropdown({ label, icon, children, open: controlledOpen, defaultOpen, onOpenChange, triggerVariant, triggerSize, className, }: DropdownProps): import("react").JSX.Element;
55
+ type NativeButtonItem = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'>;
56
+ type NativeAnchorItem = AnchorHTMLAttributes<HTMLAnchorElement>;
57
+ export interface DropdownItemProps {
58
+ /** Renders an anchor instead of a button. Items that navigate are links; items that act are not. */
59
+ href?: string;
60
+ /** Visual emphasis for a destructive action. */
61
+ tone?: 'default' | 'danger';
62
+ /** Leading icon. Decorative — the item's text is its accessible name. */
63
+ icon?: ReactNode;
64
+ /** The item's visible text, which is its accessible name. */
65
+ children: ReactNode;
66
+ disabled?: boolean;
67
+ className?: string;
68
+ }
69
+ /**
70
+ * One item in a Dropdown panel.
71
+ *
72
+ * Renders a `<button>`, or an `<a>` when `href` is given. That is the whole of the semantic
73
+ * decision, and it follows the annotation: items that navigate are anchors, items that act are
74
+ * buttons. Neither gets a menu role.
75
+ *
76
+ * `disabled` uses whichever real mechanism the element has — the `disabled` attribute on a button,
77
+ * `aria-disabled` plus a suppressed activation on a link, since an anchor has no disabled attribute
78
+ * and one that merely looks unavailable but still navigates is a trap.
79
+ *
80
+ * `tone="danger"` is a visual cue only. A destructive action still needs its own confirmation step;
81
+ * colour is not a safeguard.
82
+ */
83
+ export declare const DropdownItem: import("react").ForwardRefExoticComponent<(DropdownItemProps & (NativeButtonItem | NativeAnchorItem)) & import("react").RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
84
+ /** A visual divider between groups of items. Presentational only. */
85
+ export declare function DropdownSeparator({ className }: {
86
+ className?: string;
87
+ }): import("react").JSX.Element;
88
+ export {};
89
+ //# sourceMappingURL=dropdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAc,SAAS,EAAE,MAAM,OAAO,CAAC;AAG/F,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,qFAAqF;IACrF,QAAQ,EAAE,SAAS,CAAC;IACpB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,2BAA2B;IAC3B,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,oBAAoB;IACpB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EAAE,cAAc,EACpB,WAAmB,EACnB,YAAY,EACZ,cAA4B,EAC5B,WAAkB,EAClB,SAAS,GACV,EAAE,aAAa,+BAmIf;AAED,KAAK,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;AAC9E,KAAK,gBAAgB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,oGAAoG;IACpG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,yEAAyE;IACzE,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,6DAA6D;IAC7D,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,+KAgEvB,CAAC;AAEH,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAUtE"}