@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,2 @@
1
+ export { Tooltip } from './tooltip.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/tooltip/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,74 @@
1
+ import type { FocusEvent, KeyboardEvent, PointerEvent, ReactElement, ReactNode } from 'react';
2
+ /** Side of the trigger the tooltip prefers. Maps to the `placement` axis of the Figma set. */
3
+ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
4
+ /** Alignment along that side. Maps to the `align` axis of the Figma set. */
5
+ export type TooltipAlign = 'start' | 'center' | 'end';
6
+ /**
7
+ * Props the tooltip merges onto its trigger. The trigger must forward them to a DOM element.
8
+ *
9
+ * Every member admits `undefined` explicitly because `exactOptionalPropertyTypes` is on: clearing
10
+ * `aria-describedby` when the tooltip closes means passing `undefined`, and an optional property
11
+ * that cannot hold `undefined` would reject exactly that.
12
+ */
13
+ interface TriggerProps {
14
+ 'aria-describedby'?: string | undefined;
15
+ onPointerEnter?: ((event: PointerEvent<HTMLElement>) => void) | undefined;
16
+ onPointerLeave?: ((event: PointerEvent<HTMLElement>) => void) | undefined;
17
+ onFocus?: ((event: FocusEvent<HTMLElement>) => void) | undefined;
18
+ onBlur?: ((event: FocusEvent<HTMLElement>) => void) | undefined;
19
+ onKeyDown?: ((event: KeyboardEvent<HTMLElement>) => void) | undefined;
20
+ }
21
+ export interface TooltipProps {
22
+ /**
23
+ * The tooltip text. **Supplementary only.**
24
+ *
25
+ * Nothing essential belongs here: touch devices have no hover, so anything that matters must also
26
+ * exist in visible text, help text, or the control's own name. Never put links or buttons in it —
27
+ * the tooltip cannot be reached to use them.
28
+ */
29
+ content: ReactNode;
30
+ /**
31
+ * The trigger. A single element that forwards props to a DOM node, and that already has its own
32
+ * accessible name — the tooltip describes a control, it never names one.
33
+ */
34
+ children: ReactElement<TriggerProps>;
35
+ /** @default 'top' */
36
+ placement?: TooltipPlacement;
37
+ /** @default 'center' */
38
+ align?: TooltipAlign;
39
+ /**
40
+ * Milliseconds before an open triggered by pointer or focus takes effect. From the Figma API
41
+ * contract. Closing is always immediate.
42
+ *
43
+ * @default 120
44
+ */
45
+ delay?: number;
46
+ /** Extra class on the tooltip surface. */
47
+ className?: string;
48
+ }
49
+ /**
50
+ * Supplementary information anchored to a trigger.
51
+ *
52
+ * The contract comes from the Figma API note and the accessibility annotation, and is deliberately
53
+ * narrow:
54
+ *
55
+ * - **It opens on focus as well as hover.** A tooltip that only answers to the pointer is invisible
56
+ * to keyboard users, which is the single most common way this component is got wrong.
57
+ * - **It is connected with `aria-describedby`, and only while open**, so it is announced after the
58
+ * trigger's own name rather than instead of it. It never supplies the name: a control that needs a
59
+ * tooltip to be identifiable is a control that needs a label.
60
+ * - **Escape dismisses it** and focus stays on the trigger.
61
+ * - **It is never a tab stop and never traps focus.** There is no backdrop.
62
+ * - **It holds no interactive content.** If content needs to be operated, it is not a tooltip.
63
+ *
64
+ * `placement` and `align` are a preference. When there is not enough room on the preferred side the
65
+ * tooltip flips to the opposite one, which is the whole of the positioning logic — no dependency,
66
+ * and no attempt at a general floating-element engine.
67
+ *
68
+ * The trigger is cloned so the describedby relationship and the open/close handlers can be attached
69
+ * to the real focusable element. Existing values are merged rather than replaced: a consumer's own
70
+ * `aria-describedby` and handlers keep working.
71
+ */
72
+ export declare function Tooltip({ content, children, placement, align, delay, className, }: TooltipProps): import("react").JSX.Element;
73
+ export {};
74
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI9F,8FAA8F;AAC9F,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnE,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtD;;;;;;GAMG;AACH,UAAU,YAAY;IACpB,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1E,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACjE,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACvE;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC;IACrC,qBAAqB;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,wBAAwB;IACxB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAeD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,QAAQ,EACR,SAAiB,EACjB,KAAgB,EAChB,KAAW,EACX,SAAS,GACV,EAAE,YAAY,+BAkLd"}
@@ -0,0 +1,180 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cloneElement, isValidElement, useCallback, useEffect, useId, useRef, useState, } from 'react';
3
+ import { isDevelopment } from '../../internal/development.js';
4
+ /** Everything a tooltip must not contain, because none of it can be reached inside one. */
5
+ const FOCUSABLE = 'a[href], button, input, select, textarea, [tabindex], [contenteditable="true"]';
6
+ /**
7
+ * How long the pointer may be over neither the trigger nor the tooltip before it closes.
8
+ *
9
+ * NO FIGMA VARIABLE, and not the same thing as `delay`: this is the time it takes to cross the 8px
10
+ * gap between the trigger and the tooltip, not a deliberate wait. It is the smallest value that
11
+ * makes the crossing reliable — long enough to survive the pointer being over neither element for a
12
+ * frame or two, short enough that a tooltip left behind does not linger.
13
+ */
14
+ const POINTER_GRACE = 150;
15
+ /**
16
+ * Supplementary information anchored to a trigger.
17
+ *
18
+ * The contract comes from the Figma API note and the accessibility annotation, and is deliberately
19
+ * narrow:
20
+ *
21
+ * - **It opens on focus as well as hover.** A tooltip that only answers to the pointer is invisible
22
+ * to keyboard users, which is the single most common way this component is got wrong.
23
+ * - **It is connected with `aria-describedby`, and only while open**, so it is announced after the
24
+ * trigger's own name rather than instead of it. It never supplies the name: a control that needs a
25
+ * tooltip to be identifiable is a control that needs a label.
26
+ * - **Escape dismisses it** and focus stays on the trigger.
27
+ * - **It is never a tab stop and never traps focus.** There is no backdrop.
28
+ * - **It holds no interactive content.** If content needs to be operated, it is not a tooltip.
29
+ *
30
+ * `placement` and `align` are a preference. When there is not enough room on the preferred side the
31
+ * tooltip flips to the opposite one, which is the whole of the positioning logic — no dependency,
32
+ * and no attempt at a general floating-element engine.
33
+ *
34
+ * The trigger is cloned so the describedby relationship and the open/close handlers can be attached
35
+ * to the real focusable element. Existing values are merged rather than replaced: a consumer's own
36
+ * `aria-describedby` and handlers keep working.
37
+ */
38
+ export function Tooltip({ content, children, placement = 'top', align = 'center', delay = 120, className, }) {
39
+ const id = `lutra-tooltip-${useId()}`;
40
+ const [open, setOpen] = useState(false);
41
+ const [resolvedPlacement, setResolvedPlacement] = useState(placement);
42
+ const wrapperRef = useRef(null);
43
+ const tooltipRef = useRef(null);
44
+ const openTimer = useRef(undefined);
45
+ const closeTimer = useRef(undefined);
46
+ const cancelPendingOpen = useCallback(() => {
47
+ if (openTimer.current !== undefined) {
48
+ clearTimeout(openTimer.current);
49
+ openTimer.current = undefined;
50
+ }
51
+ }, []);
52
+ const cancelPendingClose = useCallback(() => {
53
+ if (closeTimer.current !== undefined) {
54
+ clearTimeout(closeTimer.current);
55
+ closeTimer.current = undefined;
56
+ }
57
+ }, []);
58
+ const openAfterDelay = useCallback(() => {
59
+ cancelPendingClose();
60
+ cancelPendingOpen();
61
+ openTimer.current = setTimeout(() => {
62
+ setOpen(true);
63
+ }, delay);
64
+ }, [cancelPendingClose, cancelPendingOpen, delay]);
65
+ const closeNow = useCallback(() => {
66
+ cancelPendingOpen();
67
+ cancelPendingClose();
68
+ setOpen(false);
69
+ }, [cancelPendingClose, cancelPendingOpen]);
70
+ /**
71
+ * Closes after a grace period rather than at once, which is what makes the tooltip hoverable.
72
+ *
73
+ * WCAG 2.2 SC 1.4.13 requires content shown on hover to stay visible while the pointer is on it,
74
+ * so the pointer has to be able to leave the trigger, cross the gap and land on the tooltip
75
+ * without it vanishing on the way. Entering either the trigger or the tooltip cancels the pending
76
+ * close; leaving both lets it run.
77
+ */
78
+ const closeAfterGrace = useCallback(() => {
79
+ cancelPendingOpen();
80
+ cancelPendingClose();
81
+ closeTimer.current = setTimeout(() => {
82
+ setOpen(false);
83
+ }, POINTER_GRACE);
84
+ }, [cancelPendingClose, cancelPendingOpen]);
85
+ // A pending open or close must not fire after the component has gone.
86
+ useEffect(() => () => {
87
+ cancelPendingOpen();
88
+ cancelPendingClose();
89
+ }, [cancelPendingClose, cancelPendingOpen]);
90
+ // Flip to the opposite side when the preferred one does not fit. Measured once per open, against
91
+ // the viewport, which is as far as the contract asks positioning to go.
92
+ useEffect(() => {
93
+ if (!open) {
94
+ setResolvedPlacement(placement);
95
+ return;
96
+ }
97
+ const trigger = wrapperRef.current;
98
+ const tooltip = tooltipRef.current;
99
+ if (!trigger || !tooltip)
100
+ return;
101
+ const anchor = trigger.getBoundingClientRect();
102
+ const box = tooltip.getBoundingClientRect();
103
+ const room = {
104
+ top: anchor.top,
105
+ bottom: window.innerHeight - anchor.bottom,
106
+ left: anchor.left,
107
+ right: window.innerWidth - anchor.right,
108
+ };
109
+ const opposite = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
110
+ const needed = placement === 'top' || placement === 'bottom' ? box.height : box.width;
111
+ setResolvedPlacement(room[placement] < needed && room[opposite[placement]] >= needed
112
+ ? opposite[placement]
113
+ : placement);
114
+ }, [open, placement]);
115
+ // Interactive content cannot be reached inside a tooltip, so it is a defect rather than a
116
+ // variation. The type system cannot see into `content`, so this is checked at runtime in
117
+ // development, the same way Button checks its icon-only content.
118
+ useEffect(() => {
119
+ if (!isDevelopment || !open)
120
+ return;
121
+ if (tooltipRef.current?.querySelector(FOCUSABLE)) {
122
+ console.warn('Tooltip: interactive content found inside a tooltip. A tooltip cannot be reached to ' +
123
+ 'operate it — move links, buttons and fields into the page, or use a different component.');
124
+ }
125
+ }, [open]);
126
+ if (isDevelopment && !isValidElement(children)) {
127
+ console.warn('Tooltip: `children` must be a single element that forwards props to a DOM node.');
128
+ }
129
+ const triggerProps = children.props;
130
+ const trigger = cloneElement(children, {
131
+ // Merged, not replaced: a consumer's own description keeps working alongside this one, and the
132
+ // relationship exists only while the tooltip is open.
133
+ 'aria-describedby': [triggerProps['aria-describedby'], open ? id : undefined].filter(Boolean).join(' ') ||
134
+ undefined,
135
+ onPointerEnter: (event) => {
136
+ triggerProps.onPointerEnter?.(event);
137
+ openAfterDelay();
138
+ },
139
+ onPointerLeave: (event) => {
140
+ triggerProps.onPointerLeave?.(event);
141
+ // Not immediate: the pointer has to be able to reach the tooltip. See `closeAfterGrace`.
142
+ closeAfterGrace();
143
+ },
144
+ onFocus: (event) => {
145
+ triggerProps.onFocus?.(event);
146
+ openAfterDelay();
147
+ },
148
+ onBlur: (event) => {
149
+ // Focus is discrete — it does not travel across a gap — so losing it closes at once.
150
+ triggerProps.onBlur?.(event);
151
+ closeNow();
152
+ },
153
+ onKeyDown: (event) => {
154
+ triggerProps.onKeyDown?.(event);
155
+ if (event.key !== 'Escape')
156
+ return;
157
+ // Nothing is showing, so the press is not this component's to take. Cancelling it here would
158
+ // stop an ancestor dialog closing for no reason.
159
+ if (!open)
160
+ return;
161
+ // Escape dismisses the tooltip while focus stays where it is.
162
+ //
163
+ // `preventDefault()` is what keeps one press to one layer. Escape reaching a native modal
164
+ // `<dialog>` is a *close request*, and the close-request algorithm only runs the close watcher
165
+ // when the keydown was not cancelled — so cancelling it here is the platform's own way of
166
+ // saying this press has been spent, and no shared state is needed to say it.
167
+ //
168
+ // Propagation is deliberately left alone. A tooltip is not a layer that owns the keyboard, and
169
+ // nothing else in this library needs to be shielded from a press a tooltip handled.
170
+ event.preventDefault();
171
+ closeNow();
172
+ },
173
+ });
174
+ return (_jsxs("span", { className: "lutra-tooltip-wrapper", ref: wrapperRef, children: [trigger, open && (_jsx("span", { ref: tooltipRef, id: id, role: "tooltip", className: className === undefined ? 'lutra-tooltip' : `lutra-tooltip ${className}`, "data-placement": resolvedPlacement, "data-align": align,
175
+ // The tooltip is hoverable: reaching it keeps it open, and leaving it starts the same
176
+ // grace period as leaving the trigger. Required by SC 1.4.13, and the reason the surface
177
+ // no longer sets `pointer-events: none`.
178
+ onPointerEnter: cancelPendingClose, onPointerLeave: closeAfterGrace, children: content }))] }));
179
+ }
180
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The public API of `@lutra-ui-system/react`.
3
+ *
4
+ * Everything a consumer is allowed to import is named here explicitly. Adding
5
+ * an export is a deliberate, reviewed act: once a name leaves this file it is
6
+ * a supported part of the library's contract. Do not re-export whole modules
7
+ * with `export *`, and do not export internal helpers or types that only exist
8
+ * to support an implementation.
9
+ *
10
+ * Exports come from each component's own `index.ts` barrel, never from a
11
+ * component implementation file directly.
12
+ *
13
+ * Consumers must also import the stylesheet once, from `@lutra-ui-system/react/styles.css`.
14
+ * The package never imports CSS from JavaScript, so the stylesheet cannot be
15
+ * pulled into a server bundle by accident.
16
+ *
17
+ * See `docs/engineering-guidelines.md` for the rules that govern this file.
18
+ */
19
+ export { Button } from './components/button/index.js';
20
+ export type { ButtonProps, ButtonVariant, ButtonSize, ButtonContentType, } from './components/button/index.js';
21
+ export { Dialog } from './components/dialog/index.js';
22
+ export type { DialogProps, DialogSize, DialogInitialFocus } from './components/dialog/index.js';
23
+ export { Dropdown, DropdownItem, DropdownSeparator } from './components/dropdown/index.js';
24
+ export type { DropdownProps, DropdownItemProps } from './components/dropdown/index.js';
25
+ export { Tooltip } from './components/tooltip/index.js';
26
+ export type { TooltipProps, TooltipPlacement, TooltipAlign } from './components/tooltip/index.js';
27
+ export { Link } from './components/link/index.js';
28
+ export type { LinkProps, LinkVariant } from './components/link/index.js';
29
+ export { NavigationItem } from './components/navigation-item/index.js';
30
+ export type { NavigationItemProps } from './components/navigation-item/index.js';
31
+ export { MobileNavigation } from './components/mobile-navigation/index.js';
32
+ export type { MobileNavigationProps, MobileNavigationAlign, } from './components/mobile-navigation/index.js';
33
+ export { Card, CardFooter } from './components/card/index.js';
34
+ export type { CardProps, CardElevation, CardFooterProps, CardFooterAlign, HeadingLevel, } from './components/card/index.js';
35
+ export { LinkCard } from './components/link-card/index.js';
36
+ export type { LinkCardProps } from './components/link-card/index.js';
37
+ export { Table, TableRow, TableHeaderCell, TableCell, TableSelectionCell, TableRowActions, TableEmptyState, } from './components/table/index.js';
38
+ export type { TableProps, TableRowProps, TableHeaderCellProps, TableCellProps, TableSelectionCellProps, TableRowActionsProps, TableEmptyStateProps, TableAlign, TableSortState, } from './components/table/index.js';
39
+ export { ErrorSummary } from './components/error-summary/index.js';
40
+ export type { ErrorSummaryProps, ErrorSummaryItem } from './components/error-summary/index.js';
41
+ export { Input } from './components/input/index.js';
42
+ export type { InputProps, InputSize, InputType } from './components/input/index.js';
43
+ export { Select } from './components/select/index.js';
44
+ export type { SelectProps, SelectSize } from './components/select/index.js';
45
+ export { Field } from './components/field/index.js';
46
+ export type { FieldProps, FieldIndicator } from './components/field/index.js';
47
+ export { Checkbox, CheckboxItem } from './components/checkbox/index.js';
48
+ export type { CheckboxProps, CheckboxItemProps } from './components/checkbox/index.js';
49
+ export { Radio, RadioItem } from './components/radio/index.js';
50
+ export type { RadioProps, RadioItemProps } from './components/radio/index.js';
51
+ export { Toggle, ToggleItem } from './components/toggle/index.js';
52
+ export type { ToggleProps, ToggleItemProps } from './components/toggle/index.js';
53
+ export { FieldGroup } from './components/field-group/index.js';
54
+ export type { FieldGroupProps } from './components/field-group/index.js';
55
+ export { SelectField } from './components/select-field/index.js';
56
+ export type { SelectFieldProps } from './components/select-field/index.js';
57
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The public API of `@lutra-ui-system/react`.
3
+ *
4
+ * Everything a consumer is allowed to import is named here explicitly. Adding
5
+ * an export is a deliberate, reviewed act: once a name leaves this file it is
6
+ * a supported part of the library's contract. Do not re-export whole modules
7
+ * with `export *`, and do not export internal helpers or types that only exist
8
+ * to support an implementation.
9
+ *
10
+ * Exports come from each component's own `index.ts` barrel, never from a
11
+ * component implementation file directly.
12
+ *
13
+ * Consumers must also import the stylesheet once, from `@lutra-ui-system/react/styles.css`.
14
+ * The package never imports CSS from JavaScript, so the stylesheet cannot be
15
+ * pulled into a server bundle by accident.
16
+ *
17
+ * See `docs/engineering-guidelines.md` for the rules that govern this file.
18
+ */
19
+ export { Button } from './components/button/index.js';
20
+ export { Dialog } from './components/dialog/index.js';
21
+ export { Dropdown, DropdownItem, DropdownSeparator } from './components/dropdown/index.js';
22
+ export { Tooltip } from './components/tooltip/index.js';
23
+ export { Link } from './components/link/index.js';
24
+ export { NavigationItem } from './components/navigation-item/index.js';
25
+ export { MobileNavigation } from './components/mobile-navigation/index.js';
26
+ export { Card, CardFooter } from './components/card/index.js';
27
+ export { LinkCard } from './components/link-card/index.js';
28
+ export { Table, TableRow, TableHeaderCell, TableCell, TableSelectionCell, TableRowActions, TableEmptyState, } from './components/table/index.js';
29
+ export { ErrorSummary } from './components/error-summary/index.js';
30
+ export { Input } from './components/input/index.js';
31
+ export { Select } from './components/select/index.js';
32
+ export { Field } from './components/field/index.js';
33
+ export { Checkbox, CheckboxItem } from './components/checkbox/index.js';
34
+ export { Radio, RadioItem } from './components/radio/index.js';
35
+ export { Toggle, ToggleItem } from './components/toggle/index.js';
36
+ export { FieldGroup } from './components/field-group/index.js';
37
+ export { SelectField } from './components/select-field/index.js';
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAQtD,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAGtD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAG3F,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAGxD,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAGlD,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAGvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAM3E,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAS9D,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAG3D,OAAO,EACL,KAAK,EACL,QAAQ,EACR,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,6BAA6B,CAAC;AAarC,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGnE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAGpD,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAGtD,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAGpD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const isDevelopment: boolean;
2
+ //# sourceMappingURL=development.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"development.d.ts","sourceRoot":"","sources":["../../src/internal/development.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,aAAa,SACiD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const isDevelopment = typeof process === 'undefined' || process?.env?.NODE_ENV !== 'production';
2
+ //# sourceMappingURL=development.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"development.js","sourceRoot":"","sources":["../../src/internal/development.ts"],"names":[],"mappings":"AAqBA,MAAM,CAAC,MAAM,aAAa,GACxB,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,EAAE,GAAG,EAAE,QAAQ,KAAK,YAAY,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * The label, indicator, hint and error scaffolding shared by `Field` and `SelectField`.
4
+ *
5
+ * This exists because two components need identical composition around different controls — two
6
+ * real call sites, which is the bar the guidelines set for extracting anything. It is deliberately
7
+ * not a form-control framework: it knows nothing about the control it wraps, has no registry, no
8
+ * context and no validation, and it renders the control as `children`. A third control that needs
9
+ * the same shell should use it; a control that needs something different should not be forced
10
+ * through it.
11
+ *
12
+ * Nothing here is exported from the package root.
13
+ */
14
+ /** Which word, if any, appears after the label text. */
15
+ export type FieldIndicator = 'none' | 'required' | 'optional';
16
+ interface FieldIdsInput {
17
+ /** Consumer-supplied control id, if any. */
18
+ id: string | undefined;
19
+ hasHint: boolean;
20
+ hasError: boolean;
21
+ /** Anything the consumer already pointed the control at. */
22
+ describedBy: string | undefined;
23
+ }
24
+ export interface FieldIds {
25
+ controlId: string;
26
+ hintId: string | undefined;
27
+ errorId: string | undefined;
28
+ /** The full `aria-describedby` value, or undefined when there is nothing to describe. */
29
+ describedBy: string | undefined;
30
+ }
31
+ /**
32
+ * Derives the ids that tie a field's parts together.
33
+ *
34
+ * `useId` rather than a counter or a random value: it is stable across re-renders and consistent
35
+ * between the server and client render, so hydration does not mismatch. A render-time random id
36
+ * would break both.
37
+ *
38
+ * The consumer's own `aria-describedby` is preserved and listed first, not replaced. A field that
39
+ * silently dropped a relationship the consumer established — a shared format note, a character
40
+ * counter — would be a data-loss bug that only shows up in a screen reader.
41
+ */
42
+ export declare function useFieldIds({ id, hasHint, hasError, describedBy }: FieldIdsInput): FieldIds;
43
+ interface FieldShellProps {
44
+ controlId: string;
45
+ label: ReactNode;
46
+ indicator: FieldIndicator;
47
+ hint: ReactNode;
48
+ hintId: string | undefined;
49
+ error: ReactNode;
50
+ errorId: string | undefined;
51
+ disabled: boolean;
52
+ className: string | undefined;
53
+ /** The control itself, already wired with the ids this shell produced. */
54
+ children: ReactNode;
55
+ }
56
+ /**
57
+ * Renders the visible parts around a control, in the documented order:
58
+ * label → hint → control → error.
59
+ *
60
+ * The hint sits above the control so it is read before the control is operated; the error sits
61
+ * below it, which is where the Figma annotation places it and where Field Group puts its own.
62
+ *
63
+ * The indicator word is rendered *inside* the `<label>`, so it becomes part of the accessible name
64
+ * and is announced with it rather than being a separate visual flourish the label does not carry.
65
+ */
66
+ export declare function FieldShell({ controlId, label, indicator, hint, hintId, error, errorId, disabled, className, children, }: FieldShellProps): import("react").JSX.Element;
67
+ export {};
68
+ //# sourceMappingURL=field-support.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-support.d.ts","sourceRoot":"","sources":["../../src/internal/field-support.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;;;;GAWG;AAEH,wDAAwD;AACxD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9D,UAAU,aAAa;IACrB,4CAA4C;IAC5C,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,yFAAyF;IACzF,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,QAAQ,CAiB3F;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,cAAc,CAAC;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,KAAK,EACL,SAAS,EACT,IAAI,EACJ,MAAM,EACN,KAAK,EACL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,eAAe,+BAmCjB"}
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from 'react';
3
+ /**
4
+ * Derives the ids that tie a field's parts together.
5
+ *
6
+ * `useId` rather than a counter or a random value: it is stable across re-renders and consistent
7
+ * between the server and client render, so hydration does not mismatch. A render-time random id
8
+ * would break both.
9
+ *
10
+ * The consumer's own `aria-describedby` is preserved and listed first, not replaced. A field that
11
+ * silently dropped a relationship the consumer established — a shared format note, a character
12
+ * counter — would be a data-loss bug that only shows up in a screen reader.
13
+ */
14
+ export function useFieldIds({ id, hasHint, hasError, describedBy }) {
15
+ const generated = useId();
16
+ const controlId = id ?? `lutra-field-${generated}`;
17
+ const hintId = hasHint ? `${controlId}-hint` : undefined;
18
+ const errorId = hasError ? `${controlId}-error` : undefined;
19
+ // Order matters: the consumer's own description first, then the hint, then the error. A screen
20
+ // reader reads them in this order, and the error is the thing to leave ringing in the ear.
21
+ const parts = [describedBy, hintId, errorId].filter((part) => Boolean(part));
22
+ return {
23
+ controlId,
24
+ hintId,
25
+ errorId,
26
+ describedBy: parts.length > 0 ? parts.join(' ') : undefined,
27
+ };
28
+ }
29
+ /**
30
+ * Renders the visible parts around a control, in the documented order:
31
+ * label → hint → control → error.
32
+ *
33
+ * The hint sits above the control so it is read before the control is operated; the error sits
34
+ * below it, which is where the Figma annotation places it and where Field Group puts its own.
35
+ *
36
+ * The indicator word is rendered *inside* the `<label>`, so it becomes part of the accessible name
37
+ * and is announced with it rather than being a separate visual flourish the label does not carry.
38
+ */
39
+ export function FieldShell({ controlId, label, indicator, hint, hintId, error, errorId, disabled, className, children, }) {
40
+ return (_jsxs("div", { className: className === undefined ? 'lutra-field' : `lutra-field ${className}`, "data-invalid": error ? 'true' : undefined, "data-disabled": disabled ? 'true' : undefined, children: [_jsxs("label", { className: "lutra-field__label", htmlFor: controlId, children: [label, indicator !== 'none' && (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "lutra-field__indicator", "data-indicator": indicator, children: indicator === 'required' ? 'Required' : 'Optional' })] }))] }), hint !== undefined && hint !== null && hint !== false && (_jsx("p", { className: "lutra-field__hint", id: hintId, children: hint })), children, error !== undefined && error !== null && error !== false && (_jsx("p", { className: "lutra-field__error", id: errorId, children: error }))] }));
41
+ }
42
+ //# sourceMappingURL=field-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-support.js","sourceRoot":"","sources":["../../src/internal/field-support.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAoC9B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAiB;IAC/E,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,EAAE,IAAI,eAAe,SAAS,EAAE,CAAC;IAEnD,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAE5D,+FAA+F;IAC/F,2FAA2F;IAC3F,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;QACL,SAAS;QACT,MAAM;QACN,OAAO;QACP,WAAW,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC;AACJ,CAAC;AAgBD;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,KAAK,EACL,SAAS,EACT,IAAI,EACJ,MAAM,EACN,KAAK,EACL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,GACQ;IAChB,OAAO,CACL,eACE,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,SAAS,EAAE,kBACjE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,mBACzB,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,aAE5C,iBAAO,SAAS,EAAC,oBAAoB,EAAC,OAAO,EAAE,SAAS,aACrD,KAAK,EACL,SAAS,KAAK,MAAM,IAAI,CACvB,8BAEyF,GAAG,EAC1F,eAAM,SAAS,EAAC,wBAAwB,oBAAiB,SAAS,YAC/D,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,GAC9C,IACN,CACJ,IACK,EAEP,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,IAAI,CACxD,YAAG,SAAS,EAAC,mBAAmB,EAAC,EAAE,EAAE,MAAM,YACxC,IAAI,GACH,CACL,EAEA,QAAQ,EAER,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI,CAC3D,YAAG,SAAS,EAAC,oBAAoB,EAAC,EAAE,EAAE,OAAO,YAC1C,KAAK,GACJ,CACL,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { useEffect } from 'react';
2
+ /**
3
+ * `useLayoutEffect` in the browser, `useEffect` on the server.
4
+ *
5
+ * Some DOM state has no HTML attribute behind it and can only be assigned to the element — a
6
+ * checkbox's `indeterminate` property is the example this library has. Writing it during render
7
+ * would be a side effect in the render phase, which React is entitled to run more than once, to
8
+ * abandon, or to replay; it has to happen after the DOM exists instead.
9
+ *
10
+ * `useLayoutEffect` is the right one of the two, because it runs before the browser paints. With
11
+ * `useEffect` there is a frame in which the element is on screen without the property set, and for
12
+ * `indeterminate` that frame shows the wrong control — a tick or an empty box where a dash belongs.
13
+ *
14
+ * The catch is that React warns when `useLayoutEffect` runs during server rendering, where there is
15
+ * no layout to read and nothing to paint. This package must render on a server without complaint,
16
+ * so the hook is chosen once, at module scope, from whether a document exists.
17
+ *
18
+ * The `typeof` guard matters for the same reason it does in `development.ts`: `document` is not
19
+ * defined in a plain Node process, and an unguarded reference would throw while the module is being
20
+ * evaluated. Choosing between the two hooks once, rather than per render, keeps the rules of hooks
21
+ * satisfied — the same hook is called on every render of a given component.
22
+ *
23
+ * Nothing here is exported from the package root.
24
+ */
25
+ export declare const useIsomorphicLayoutEffect: typeof useEffect;
26
+ //# sourceMappingURL=isomorphic-layout-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isomorphic-layout-effect.d.ts","sourceRoot":"","sources":["../../src/internal/isomorphic-layout-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,OAAO,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,kBACyB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { useEffect, useLayoutEffect } from 'react';
2
+ /**
3
+ * `useLayoutEffect` in the browser, `useEffect` on the server.
4
+ *
5
+ * Some DOM state has no HTML attribute behind it and can only be assigned to the element — a
6
+ * checkbox's `indeterminate` property is the example this library has. Writing it during render
7
+ * would be a side effect in the render phase, which React is entitled to run more than once, to
8
+ * abandon, or to replay; it has to happen after the DOM exists instead.
9
+ *
10
+ * `useLayoutEffect` is the right one of the two, because it runs before the browser paints. With
11
+ * `useEffect` there is a frame in which the element is on screen without the property set, and for
12
+ * `indeterminate` that frame shows the wrong control — a tick or an empty box where a dash belongs.
13
+ *
14
+ * The catch is that React warns when `useLayoutEffect` runs during server rendering, where there is
15
+ * no layout to read and nothing to paint. This package must render on a server without complaint,
16
+ * so the hook is chosen once, at module scope, from whether a document exists.
17
+ *
18
+ * The `typeof` guard matters for the same reason it does in `development.ts`: `document` is not
19
+ * defined in a plain Node process, and an unguarded reference would throw while the module is being
20
+ * evaluated. Choosing between the two hooks once, rather than per render, keeps the rules of hooks
21
+ * satisfied — the same hook is called on every render of a given component.
22
+ *
23
+ * Nothing here is exported from the package root.
24
+ */
25
+ export const useIsomorphicLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect;
26
+ //# sourceMappingURL=isomorphic-layout-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isomorphic-layout-effect.js","sourceRoot":"","sources":["../../src/internal/isomorphic-layout-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GACpC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC"}