@becket-ui/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 (146) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Badge.d.ts +22 -0
  3. package/dist/components/Badge.d.ts.map +1 -0
  4. package/dist/components/Badge.js +39 -0
  5. package/dist/components/Badge.js.map +1 -0
  6. package/dist/components/Button.d.ts +24 -0
  7. package/dist/components/Button.d.ts.map +1 -0
  8. package/dist/components/Button.js +46 -0
  9. package/dist/components/Button.js.map +1 -0
  10. package/dist/components/Button.test.js +45 -0
  11. package/dist/components/Button.test.js.map +1 -0
  12. package/dist/components/Card.d.ts +37 -0
  13. package/dist/components/Card.d.ts.map +1 -0
  14. package/dist/components/Card.js +75 -0
  15. package/dist/components/Card.js.map +1 -0
  16. package/dist/components/Checkbox.d.ts +14 -0
  17. package/dist/components/Checkbox.d.ts.map +1 -0
  18. package/dist/components/Checkbox.js +46 -0
  19. package/dist/components/Checkbox.js.map +1 -0
  20. package/dist/components/Dialog.d.ts +41 -0
  21. package/dist/components/Dialog.d.ts.map +1 -0
  22. package/dist/components/Dialog.js +173 -0
  23. package/dist/components/Dialog.js.map +1 -0
  24. package/dist/components/Dialog.test.js +89 -0
  25. package/dist/components/Dialog.test.js.map +1 -0
  26. package/dist/components/Drawer.d.ts +38 -0
  27. package/dist/components/Drawer.d.ts.map +1 -0
  28. package/dist/components/Drawer.js +174 -0
  29. package/dist/components/Drawer.js.map +1 -0
  30. package/dist/components/Drawer.test.js +50 -0
  31. package/dist/components/Drawer.test.js.map +1 -0
  32. package/dist/components/Field.d.ts +37 -0
  33. package/dist/components/Field.d.ts.map +1 -0
  34. package/dist/components/Field.js +186 -0
  35. package/dist/components/Field.js.map +1 -0
  36. package/dist/components/Field.test.js +33 -0
  37. package/dist/components/Field.test.js.map +1 -0
  38. package/dist/components/Flex.d.ts +38 -0
  39. package/dist/components/Flex.d.ts.map +1 -0
  40. package/dist/components/Flex.js +85 -0
  41. package/dist/components/Flex.js.map +1 -0
  42. package/dist/components/Heading.d.ts +14 -0
  43. package/dist/components/Heading.d.ts.map +1 -0
  44. package/dist/components/Heading.js +33 -0
  45. package/dist/components/Heading.js.map +1 -0
  46. package/dist/components/Hide.d.ts +39 -0
  47. package/dist/components/Hide.d.ts.map +1 -0
  48. package/dist/components/Hide.js +51 -0
  49. package/dist/components/Hide.js.map +1 -0
  50. package/dist/components/Link.d.ts +14 -0
  51. package/dist/components/Link.d.ts.map +1 -0
  52. package/dist/components/Link.js +23 -0
  53. package/dist/components/Link.js.map +1 -0
  54. package/dist/components/Menu.d.ts +11 -0
  55. package/dist/components/Menu.d.ts.map +1 -0
  56. package/dist/components/Menu.js +132 -0
  57. package/dist/components/Menu.js.map +1 -0
  58. package/dist/components/Menu.test.js +69 -0
  59. package/dist/components/Menu.test.js.map +1 -0
  60. package/dist/components/Radio.d.ts +38 -0
  61. package/dist/components/Radio.d.ts.map +1 -0
  62. package/dist/components/Radio.js +144 -0
  63. package/dist/components/Radio.js.map +1 -0
  64. package/dist/components/Select.d.ts +17 -0
  65. package/dist/components/Select.d.ts.map +1 -0
  66. package/dist/components/Select.js +33 -0
  67. package/dist/components/Select.js.map +1 -0
  68. package/dist/components/Separator.d.ts +14 -0
  69. package/dist/components/Separator.d.ts.map +1 -0
  70. package/dist/components/Separator.js +25 -0
  71. package/dist/components/Separator.js.map +1 -0
  72. package/dist/components/SimpleGrid.d.ts +26 -0
  73. package/dist/components/SimpleGrid.d.ts.map +1 -0
  74. package/dist/components/SimpleGrid.js +98 -0
  75. package/dist/components/SimpleGrid.js.map +1 -0
  76. package/dist/components/Spinner.d.ts +10 -0
  77. package/dist/components/Spinner.d.ts.map +1 -0
  78. package/dist/components/Spinner.js +25 -0
  79. package/dist/components/Spinner.js.map +1 -0
  80. package/dist/components/Stack.d.ts +32 -0
  81. package/dist/components/Stack.d.ts.map +1 -0
  82. package/dist/components/Stack.js +81 -0
  83. package/dist/components/Stack.js.map +1 -0
  84. package/dist/components/Switch.d.ts +20 -0
  85. package/dist/components/Switch.d.ts.map +1 -0
  86. package/dist/components/Switch.js +83 -0
  87. package/dist/components/Switch.js.map +1 -0
  88. package/dist/components/Switch.test.js +28 -0
  89. package/dist/components/Switch.test.js.map +1 -0
  90. package/dist/components/Table.d.ts +23 -0
  91. package/dist/components/Table.d.ts.map +1 -0
  92. package/dist/components/Table.js +90 -0
  93. package/dist/components/Table.js.map +1 -0
  94. package/dist/components/Tabs.d.ts +21 -0
  95. package/dist/components/Tabs.d.ts.map +1 -0
  96. package/dist/components/Tabs.js +140 -0
  97. package/dist/components/Tabs.js.map +1 -0
  98. package/dist/components/Tabs.test.js +41 -0
  99. package/dist/components/Tabs.test.js.map +1 -0
  100. package/dist/components/Tag.d.ts +22 -0
  101. package/dist/components/Tag.d.ts.map +1 -0
  102. package/dist/components/Tag.js +39 -0
  103. package/dist/components/Tag.js.map +1 -0
  104. package/dist/components/Text.d.ts +9 -0
  105. package/dist/components/Text.d.ts.map +1 -0
  106. package/dist/components/Text.js +18 -0
  107. package/dist/components/Text.js.map +1 -0
  108. package/dist/components/TextField.d.ts +19 -0
  109. package/dist/components/TextField.d.ts.map +1 -0
  110. package/dist/components/TextField.js +32 -0
  111. package/dist/components/TextField.js.map +1 -0
  112. package/dist/components/Textarea.d.ts +17 -0
  113. package/dist/components/Textarea.d.ts.map +1 -0
  114. package/dist/components/Textarea.js +34 -0
  115. package/dist/components/Textarea.js.map +1 -0
  116. package/dist/components/Tooltip.d.ts +17 -0
  117. package/dist/components/Tooltip.d.ts.map +1 -0
  118. package/dist/components/Tooltip.js +217 -0
  119. package/dist/components/Tooltip.js.map +1 -0
  120. package/dist/components/Tooltip.test.js +58 -0
  121. package/dist/components/Tooltip.test.js.map +1 -0
  122. package/dist/helpers/gradientBorder.d.ts +5 -0
  123. package/dist/helpers/gradientBorder.d.ts.map +1 -0
  124. package/dist/helpers/gradientBorder.js +25 -0
  125. package/dist/helpers/gradientBorder.js.map +1 -0
  126. package/dist/helpers/labelColors.d.ts +17 -0
  127. package/dist/helpers/labelColors.d.ts.map +1 -0
  128. package/dist/helpers/labelColors.js +54 -0
  129. package/dist/helpers/labelColors.js.map +1 -0
  130. package/dist/helpers/nativeDialog.d.ts +23 -0
  131. package/dist/helpers/nativeDialog.d.ts.map +1 -0
  132. package/dist/helpers/nativeDialog.js +85 -0
  133. package/dist/helpers/nativeDialog.js.map +1 -0
  134. package/dist/helpers/responsive.d.ts +63 -0
  135. package/dist/helpers/responsive.d.ts.map +1 -0
  136. package/dist/helpers/responsive.js +78 -0
  137. package/dist/helpers/responsive.js.map +1 -0
  138. package/dist/helpers/storybookControls.d.ts +16 -0
  139. package/dist/helpers/storybookControls.d.ts.map +1 -0
  140. package/dist/helpers/storybookControls.js +40 -0
  141. package/dist/helpers/storybookControls.js.map +1 -0
  142. package/dist/index.d.ts +35 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +158 -0
  145. package/dist/index.js.map +1 -0
  146. package/package.json +79 -0
@@ -0,0 +1,217 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ Children,
5
+ cloneElement,
6
+ forwardRef,
7
+ isValidElement,
8
+ useCallback,
9
+ useEffect,
10
+ useId,
11
+ useLayoutEffect,
12
+ useRef,
13
+ useState
14
+ } from "react";
15
+ import { createPortal } from "react-dom";
16
+ import { tooltip as tooltipRecipe } from "@becket-ui/tokens/recipes";
17
+ const OFFSET_PX = 8;
18
+ const NATIVE_FOCUSABLE = /* @__PURE__ */ new Set(["a", "button", "input", "select", "summary", "textarea"]);
19
+ function mergeClassName(...classes) {
20
+ return classes.filter(Boolean).join(" ");
21
+ }
22
+ function assignRef(ref, value) {
23
+ if (typeof ref === "function") {
24
+ ref(value);
25
+ } else if (ref) {
26
+ ref.current = value;
27
+ }
28
+ }
29
+ function mergeRefs(...refs) {
30
+ return (node) => {
31
+ for (const ref of refs) {
32
+ assignRef(ref, node);
33
+ }
34
+ };
35
+ }
36
+ function chain(their, ours) {
37
+ return (event) => {
38
+ their?.(event);
39
+ ours?.(event);
40
+ };
41
+ }
42
+ function triggerNeedsTabIndex(element) {
43
+ if (typeof element.type !== "string") {
44
+ return false;
45
+ }
46
+ if (NATIVE_FOCUSABLE.has(element.type)) {
47
+ return false;
48
+ }
49
+ if (element.props.tabIndex != null) {
50
+ return false;
51
+ }
52
+ return true;
53
+ }
54
+ function computePosition(trigger, tip, placement) {
55
+ const cx = trigger.left + trigger.width / 2;
56
+ const cy = trigger.top + trigger.height / 2;
57
+ switch (placement) {
58
+ case "bottom":
59
+ return {
60
+ top: trigger.bottom + OFFSET_PX,
61
+ left: cx - tip.width / 2
62
+ };
63
+ case "left":
64
+ return {
65
+ top: cy - tip.height / 2,
66
+ left: trigger.left - tip.width - OFFSET_PX
67
+ };
68
+ case "right":
69
+ return {
70
+ top: cy - tip.height / 2,
71
+ left: trigger.right + OFFSET_PX
72
+ };
73
+ case "top":
74
+ default:
75
+ return {
76
+ top: trigger.top - tip.height - OFFSET_PX,
77
+ left: cx - tip.width / 2
78
+ };
79
+ }
80
+ }
81
+ function clampToViewport(top, left, tip) {
82
+ const pad = 4;
83
+ const maxLeft = window.innerWidth - tip.width - pad;
84
+ const maxTop = window.innerHeight - tip.height - pad;
85
+ return {
86
+ top: Math.min(Math.max(pad, top), Math.max(pad, maxTop)),
87
+ left: Math.min(Math.max(pad, left), Math.max(pad, maxLeft))
88
+ };
89
+ }
90
+ const Tooltip = forwardRef(
91
+ ({
92
+ content,
93
+ children,
94
+ placement = "top",
95
+ delayMs = 200,
96
+ disabled = false
97
+ }, ref) => {
98
+ const tipId = useId();
99
+ const triggerRef = useRef(null);
100
+ const tipRef = useRef(null);
101
+ const showTimer = useRef(null);
102
+ const [open, setOpen] = useState(false);
103
+ const [coords, setCoords] = useState(null);
104
+ const setTriggerRef = useCallback(
105
+ (node) => {
106
+ triggerRef.current = node;
107
+ assignRef(ref, node);
108
+ },
109
+ [ref]
110
+ );
111
+ const clearShowTimer = useCallback(() => {
112
+ if (showTimer.current != null) {
113
+ clearTimeout(showTimer.current);
114
+ showTimer.current = null;
115
+ }
116
+ }, []);
117
+ const scheduleOpen = useCallback(() => {
118
+ if (disabled || content == null || content === "") return;
119
+ clearShowTimer();
120
+ showTimer.current = setTimeout(() => setOpen(true), delayMs);
121
+ }, [clearShowTimer, content, delayMs, disabled]);
122
+ const close = useCallback(() => {
123
+ clearShowTimer();
124
+ setOpen(false);
125
+ setCoords(null);
126
+ }, [clearShowTimer]);
127
+ useEffect(() => () => clearShowTimer(), [clearShowTimer]);
128
+ useEffect(() => {
129
+ if (!open) return;
130
+ const onKey = (event) => {
131
+ if (event.key === "Escape") close();
132
+ };
133
+ const onScroll = () => close();
134
+ window.addEventListener("keydown", onKey);
135
+ window.addEventListener("scroll", onScroll, true);
136
+ window.addEventListener("resize", onScroll);
137
+ return () => {
138
+ window.removeEventListener("keydown", onKey);
139
+ window.removeEventListener("scroll", onScroll, true);
140
+ window.removeEventListener("resize", onScroll);
141
+ };
142
+ }, [close, open]);
143
+ useLayoutEffect(() => {
144
+ if (!open || !triggerRef.current || !tipRef.current) return;
145
+ const trigger2 = triggerRef.current.getBoundingClientRect();
146
+ const tip = tipRef.current.getBoundingClientRect();
147
+ const raw = computePosition(trigger2, tip, placement);
148
+ setCoords(clampToViewport(raw.top, raw.left, tip));
149
+ }, [open, placement, content]);
150
+ const tipStyle = {
151
+ top: coords?.top ?? -9999,
152
+ left: coords?.left ?? -9999,
153
+ visibility: coords ? "visible" : "hidden"
154
+ };
155
+ const describedByWhenOpen = open ? tipId : void 0;
156
+ const hoverHandlers = {
157
+ onMouseEnter: scheduleOpen,
158
+ onMouseLeave: close,
159
+ onFocus: scheduleOpen,
160
+ onBlur: close
161
+ };
162
+ const childArray = Children.toArray(children);
163
+ const onlyChild = childArray.length === 1 ? childArray[0] : void 0;
164
+ let trigger;
165
+ if (isValidElement(onlyChild)) {
166
+ const child = onlyChild;
167
+ const childRef = child.ref ?? child.props.ref;
168
+ const describedBy = [child.props["aria-describedby"], describedByWhenOpen].filter(Boolean).join(" ");
169
+ trigger = cloneElement(child, {
170
+ ref: mergeRefs(childRef, setTriggerRef),
171
+ onMouseEnter: chain(child.props.onMouseEnter, hoverHandlers.onMouseEnter),
172
+ onMouseLeave: chain(child.props.onMouseLeave, hoverHandlers.onMouseLeave),
173
+ onFocus: chain(child.props.onFocus, hoverHandlers.onFocus),
174
+ onBlur: chain(child.props.onBlur, hoverHandlers.onBlur),
175
+ "aria-describedby": describedBy || void 0,
176
+ ...triggerNeedsTabIndex(child) ? { tabIndex: 0 } : {}
177
+ });
178
+ } else {
179
+ trigger = /* @__PURE__ */ jsx(
180
+ "span",
181
+ {
182
+ ref: setTriggerRef,
183
+ tabIndex: 0,
184
+ "aria-describedby": describedByWhenOpen,
185
+ onMouseEnter: hoverHandlers.onMouseEnter,
186
+ onMouseLeave: hoverHandlers.onMouseLeave,
187
+ onFocus: hoverHandlers.onFocus,
188
+ onBlur: hoverHandlers.onBlur,
189
+ style: { display: "inline-flex", maxWidth: "100%", verticalAlign: "middle" },
190
+ children
191
+ }
192
+ );
193
+ }
194
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
195
+ trigger,
196
+ open && typeof document !== "undefined" ? createPortal(
197
+ /* @__PURE__ */ jsx(
198
+ "div",
199
+ {
200
+ ref: tipRef,
201
+ id: tipId,
202
+ role: "tooltip",
203
+ className: mergeClassName(tooltipRecipe({ placement })),
204
+ style: tipStyle,
205
+ children: content
206
+ }
207
+ ),
208
+ document.body
209
+ ) : null
210
+ ] });
211
+ }
212
+ );
213
+ Tooltip.displayName = "Tooltip";
214
+ export {
215
+ Tooltip
216
+ };
217
+ //# sourceMappingURL=Tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Tooltip.tsx"],"sourcesContent":["'use client';\n\n/**\n * Hover / focus tooltip (generic DS primitive).\n * Default placement top, open delay 200ms.\n */\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type FocusEventHandler,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { tooltip as tooltipRecipe } from '@becket-ui/tokens/recipes';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport type TooltipProps = {\n /** Short glossary / hint text (or node). */\n content: ReactNode;\n children: ReactNode;\n placement?: TooltipPlacement;\n /** Delay before open on hover/focus (ms). */\n delayMs?: number;\n disabled?: boolean;\n};\n\nconst OFFSET_PX = 8;\n\nconst NATIVE_FOCUSABLE = new Set(['a', 'button', 'input', 'select', 'summary', 'textarea']);\n\ntype TriggerProps = {\n ref?: Ref<HTMLElement>;\n tabIndex?: number;\n href?: string;\n onMouseEnter?: MouseEventHandler<HTMLElement>;\n onMouseLeave?: MouseEventHandler<HTMLElement>;\n onFocus?: FocusEventHandler<HTMLElement>;\n onBlur?: FocusEventHandler<HTMLElement>;\n 'aria-describedby'?: string;\n};\n\nfunction mergeClassName(...classes: (string | undefined)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === 'function') {\n ref(value);\n } else if (ref) {\n ref.current = value;\n }\n}\n\nfunction mergeRefs<T>(...refs: Array<Ref<T> | undefined>) {\n return (node: T | null) => {\n for (const ref of refs) {\n assignRef(ref, node);\n }\n };\n}\n\nfunction chain<E>(their?: (event: E) => void, ours?: (event: E) => void) {\n return (event: E) => {\n their?.(event);\n ours?.(event);\n };\n}\n\nfunction triggerNeedsTabIndex(element: ReactElement<TriggerProps>): boolean {\n if (typeof element.type !== 'string') {\n return false;\n }\n if (NATIVE_FOCUSABLE.has(element.type)) {\n return false;\n }\n if (element.props.tabIndex != null) {\n return false;\n }\n return true;\n}\n\nfunction computePosition(\n trigger: DOMRect,\n tip: DOMRect,\n placement: TooltipPlacement,\n): { top: number; left: number } {\n const cx = trigger.left + trigger.width / 2;\n const cy = trigger.top + trigger.height / 2;\n\n switch (placement) {\n case 'bottom':\n return {\n top: trigger.bottom + OFFSET_PX,\n left: cx - tip.width / 2,\n };\n case 'left':\n return {\n top: cy - tip.height / 2,\n left: trigger.left - tip.width - OFFSET_PX,\n };\n case 'right':\n return {\n top: cy - tip.height / 2,\n left: trigger.right + OFFSET_PX,\n };\n case 'top':\n default:\n return {\n top: trigger.top - tip.height - OFFSET_PX,\n left: cx - tip.width / 2,\n };\n }\n}\n\nfunction clampToViewport(top: number, left: number, tip: DOMRect): { top: number; left: number } {\n const pad = 4;\n const maxLeft = window.innerWidth - tip.width - pad;\n const maxTop = window.innerHeight - tip.height - pad;\n return {\n top: Math.min(Math.max(pad, top), Math.max(pad, maxTop)),\n left: Math.min(Math.max(pad, left), Math.max(pad, maxLeft)),\n };\n}\n\nexport const Tooltip = forwardRef<HTMLElement, TooltipProps>(\n (\n {\n content,\n children,\n placement = 'top',\n delayMs = 200,\n disabled = false,\n },\n ref,\n ) => {\n const tipId = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const tipRef = useRef<HTMLDivElement | null>(null);\n const showTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [open, setOpen] = useState(false);\n const [coords, setCoords] = useState<{ top: number; left: number } | null>(null);\n\n const setTriggerRef = useCallback(\n (node: HTMLElement | null) => {\n triggerRef.current = node;\n assignRef(ref, node);\n },\n [ref],\n );\n\n const clearShowTimer = useCallback(() => {\n if (showTimer.current != null) {\n clearTimeout(showTimer.current);\n showTimer.current = null;\n }\n }, []);\n\n const scheduleOpen = useCallback(() => {\n if (disabled || content == null || content === '') return;\n clearShowTimer();\n showTimer.current = setTimeout(() => setOpen(true), delayMs);\n }, [clearShowTimer, content, delayMs, disabled]);\n\n const close = useCallback(() => {\n clearShowTimer();\n setOpen(false);\n setCoords(null);\n }, [clearShowTimer]);\n\n useEffect(() => () => clearShowTimer(), [clearShowTimer]);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') close();\n };\n const onScroll = () => close();\n window.addEventListener('keydown', onKey);\n window.addEventListener('scroll', onScroll, true);\n window.addEventListener('resize', onScroll);\n return () => {\n window.removeEventListener('keydown', onKey);\n window.removeEventListener('scroll', onScroll, true);\n window.removeEventListener('resize', onScroll);\n };\n }, [close, open]);\n\n useLayoutEffect(() => {\n if (!open || !triggerRef.current || !tipRef.current) return;\n const trigger = triggerRef.current.getBoundingClientRect();\n const tip = tipRef.current.getBoundingClientRect();\n const raw = computePosition(trigger, tip, placement);\n setCoords(clampToViewport(raw.top, raw.left, tip));\n }, [open, placement, content]);\n\n const tipStyle: CSSProperties = {\n top: coords?.top ?? -9999,\n left: coords?.left ?? -9999,\n visibility: coords ? 'visible' : 'hidden',\n };\n\n const describedByWhenOpen = open ? tipId : undefined;\n const hoverHandlers = {\n onMouseEnter: scheduleOpen,\n onMouseLeave: close,\n onFocus: scheduleOpen,\n onBlur: close,\n };\n\n const childArray = Children.toArray(children);\n const onlyChild = childArray.length === 1 ? childArray[0] : undefined;\n let trigger: ReactNode;\n\n if (isValidElement(onlyChild)) {\n const child = onlyChild as ReactElement<TriggerProps>;\n const childRef = (child as { ref?: Ref<HTMLElement> }).ref ?? child.props.ref;\n const describedBy = [child.props['aria-describedby'], describedByWhenOpen]\n .filter(Boolean)\n .join(' ');\n trigger = cloneElement(child, {\n ref: mergeRefs(childRef, setTriggerRef),\n onMouseEnter: chain(child.props.onMouseEnter, hoverHandlers.onMouseEnter),\n onMouseLeave: chain(child.props.onMouseLeave, hoverHandlers.onMouseLeave),\n onFocus: chain(child.props.onFocus, hoverHandlers.onFocus),\n onBlur: chain(child.props.onBlur, hoverHandlers.onBlur),\n 'aria-describedby': describedBy || undefined,\n ...(triggerNeedsTabIndex(child) ? { tabIndex: 0 } : {}),\n });\n } else {\n trigger = (\n <span\n ref={setTriggerRef}\n tabIndex={0}\n aria-describedby={describedByWhenOpen}\n onMouseEnter={hoverHandlers.onMouseEnter}\n onMouseLeave={hoverHandlers.onMouseLeave}\n onFocus={hoverHandlers.onFocus}\n onBlur={hoverHandlers.onBlur}\n style={{ display: 'inline-flex', maxWidth: '100%', verticalAlign: 'middle' }}\n >\n {children}\n </span>\n );\n }\n\n return (\n <>\n {trigger}\n {open && typeof document !== 'undefined'\n ? createPortal(\n <div\n ref={tipRef}\n id={tipId}\n role=\"tooltip\"\n className={mergeClassName(tooltipRecipe({ placement }))}\n style={tipStyle}\n >\n {content}\n </div>,\n document.body,\n )\n : null}\n </>\n );\n },\n);\n\nTooltip.displayName = 'Tooltip';\n"],"mappings":"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","names":["trigger"]}
@@ -0,0 +1,58 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from "vitest";
3
+ import { render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { assertNoAxeViolations } from "@becket-ui/a11y";
6
+ import { Button } from "./Button";
7
+ import { Tooltip } from "./Tooltip";
8
+ describe("Tooltip", () => {
9
+ it("does not add an extra tab stop around Button", async () => {
10
+ const user = userEvent.setup();
11
+ render(
12
+ /* @__PURE__ */ jsxs(Fragment, { children: [
13
+ /* @__PURE__ */ jsx("button", { type: "button", children: "Before" }),
14
+ /* @__PURE__ */ jsx(Tooltip, { content: "Short glossary hint.", delayMs: 0, children: /* @__PURE__ */ jsx(Button, { children: "Hover me" }) }),
15
+ /* @__PURE__ */ jsx("button", { type: "button", children: "After" })
16
+ ] })
17
+ );
18
+ const trigger = screen.getByRole("button", { name: "Hover me" });
19
+ expect(trigger.parentElement?.getAttribute("tabindex")).not.toBe("0");
20
+ await user.tab();
21
+ expect(document.activeElement).toBe(screen.getByRole("button", { name: "Before" }));
22
+ await user.tab();
23
+ expect(document.activeElement).toBe(trigger);
24
+ await user.tab();
25
+ expect(document.activeElement).toBe(screen.getByRole("button", { name: "After" }));
26
+ });
27
+ it("has no axe violations when wrapping a named Button", async () => {
28
+ const { container } = render(
29
+ /* @__PURE__ */ jsx(Tooltip, { content: "Short glossary hint.", delayMs: 0, children: /* @__PURE__ */ jsx(Button, { children: "Hover me" }) })
30
+ );
31
+ await assertNoAxeViolations(container);
32
+ });
33
+ it("sets aria-describedby on the Button while open and closes on Escape", async () => {
34
+ const user = userEvent.setup();
35
+ render(
36
+ /* @__PURE__ */ jsx(Tooltip, { content: "Short glossary hint.", delayMs: 0, children: /* @__PURE__ */ jsx(Button, { children: "Hover me" }) })
37
+ );
38
+ const trigger = screen.getByRole("button", { name: "Hover me" });
39
+ await user.hover(trigger);
40
+ const tip = await screen.findByRole("tooltip");
41
+ expect(trigger.getAttribute("aria-describedby")).toBe(tip.id);
42
+ await user.keyboard("{Escape}");
43
+ expect(screen.queryByRole("tooltip")).toBeNull();
44
+ });
45
+ it("wraps plain text so it is keyboard reachable", async () => {
46
+ const user = userEvent.setup();
47
+ render(
48
+ /* @__PURE__ */ jsx(Tooltip, { content: "Hint", delayMs: 0, children: "glossary" })
49
+ );
50
+ const trigger = screen.getByText("glossary");
51
+ expect(trigger.tagName).toBe("SPAN");
52
+ expect(trigger.getAttribute("tabindex")).toBe("0");
53
+ trigger.focus();
54
+ await user.hover(trigger);
55
+ expect(await screen.findByRole("tooltip")).toBeTruthy();
56
+ });
57
+ });
58
+ //# sourceMappingURL=Tooltip.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Tooltip.test.tsx"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { render, screen } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { assertNoAxeViolations } from '@becket-ui/a11y';\n\nimport { Button } from './Button';\nimport { Tooltip } from './Tooltip';\n\ndescribe('Tooltip', () => {\n it('does not add an extra tab stop around Button', async () => {\n const user = userEvent.setup();\n render(\n <>\n <button type=\"button\">Before</button>\n <Tooltip content=\"Short glossary hint.\" delayMs={0}>\n <Button>Hover me</Button>\n </Tooltip>\n <button type=\"button\">After</button>\n </>,\n );\n\n const trigger = screen.getByRole('button', { name: 'Hover me' });\n expect(trigger.parentElement?.getAttribute('tabindex')).not.toBe('0');\n\n await user.tab();\n expect(document.activeElement).toBe(screen.getByRole('button', { name: 'Before' }));\n await user.tab();\n expect(document.activeElement).toBe(trigger);\n await user.tab();\n expect(document.activeElement).toBe(screen.getByRole('button', { name: 'After' }));\n });\n\n it('has no axe violations when wrapping a named Button', async () => {\n const { container } = render(\n <Tooltip content=\"Short glossary hint.\" delayMs={0}>\n <Button>Hover me</Button>\n </Tooltip>,\n );\n await assertNoAxeViolations(container);\n });\n\n it('sets aria-describedby on the Button while open and closes on Escape', async () => {\n const user = userEvent.setup();\n render(\n <Tooltip content=\"Short glossary hint.\" delayMs={0}>\n <Button>Hover me</Button>\n </Tooltip>,\n );\n\n const trigger = screen.getByRole('button', { name: 'Hover me' });\n await user.hover(trigger);\n const tip = await screen.findByRole('tooltip');\n expect(trigger.getAttribute('aria-describedby')).toBe(tip.id);\n\n await user.keyboard('{Escape}');\n expect(screen.queryByRole('tooltip')).toBeNull();\n });\n\n it('wraps plain text so it is keyboard reachable', async () => {\n const user = userEvent.setup();\n render(\n <Tooltip content=\"Hint\" delayMs={0}>\n glossary\n </Tooltip>,\n );\n\n const trigger = screen.getByText('glossary');\n expect(trigger.tagName).toBe('SPAN');\n expect(trigger.getAttribute('tabindex')).toBe('0');\n\n trigger.focus();\n await user.hover(trigger);\n expect(await screen.findByRole('tooltip')).toBeTruthy();\n });\n});\n"],"mappings":"AAYM,mBACE,KADF;AAZN,SAAS,UAAU,QAAQ,UAAU;AACrC,SAAS,QAAQ,cAAc;AAC/B,OAAO,eAAe;AACtB,SAAS,6BAA6B;AAEtC,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,SAAS,WAAW,MAAM;AACxB,KAAG,gDAAgD,YAAY;AAC7D,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,iCACE;AAAA,4BAAC,YAAO,MAAK,UAAS,oBAAM;AAAA,QAC5B,oBAAC,WAAQ,SAAQ,wBAAuB,SAAS,GAC/C,8BAAC,UAAO,sBAAQ,GAClB;AAAA,QACA,oBAAC,YAAO,MAAK,UAAS,mBAAK;AAAA,SAC7B;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,UAAU,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,WAAO,QAAQ,eAAe,aAAa,UAAU,CAAC,EAAE,IAAI,KAAK,GAAG;AAEpE,UAAM,KAAK,IAAI;AACf,WAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,UAAU,EAAE,MAAM,SAAS,CAAC,CAAC;AAClF,UAAM,KAAK,IAAI;AACf,WAAO,SAAS,aAAa,EAAE,KAAK,OAAO;AAC3C,UAAM,KAAK,IAAI;AACf,WAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,EACnF,CAAC;AAED,KAAG,sDAAsD,YAAY;AACnE,UAAM,EAAE,UAAU,IAAI;AAAA,MACpB,oBAAC,WAAQ,SAAQ,wBAAuB,SAAS,GAC/C,8BAAC,UAAO,sBAAQ,GAClB;AAAA,IACF;AACA,UAAM,sBAAsB,SAAS;AAAA,EACvC,CAAC;AAED,KAAG,uEAAuE,YAAY;AACpF,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,oBAAC,WAAQ,SAAQ,wBAAuB,SAAS,GAC/C,8BAAC,UAAO,sBAAQ,GAClB;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,UAAU,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,UAAM,KAAK,MAAM,OAAO;AACxB,UAAM,MAAM,MAAM,OAAO,WAAW,SAAS;AAC7C,WAAO,QAAQ,aAAa,kBAAkB,CAAC,EAAE,KAAK,IAAI,EAAE;AAE5D,UAAM,KAAK,SAAS,UAAU;AAC9B,WAAO,OAAO,YAAY,SAAS,CAAC,EAAE,SAAS;AAAA,EACjD,CAAC;AAED,KAAG,gDAAgD,YAAY;AAC7D,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,oBAAC,WAAQ,SAAQ,QAAO,SAAS,GAAG,sBAEpC;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,UAAU,UAAU;AAC3C,WAAO,QAAQ,OAAO,EAAE,KAAK,MAAM;AACnC,WAAO,QAAQ,aAAa,UAAU,CAAC,EAAE,KAAK,GAAG;AAEjD,YAAQ,MAAM;AACd,UAAM,KAAK,MAAM,OAAO;AACxB,WAAO,MAAM,OAAO,WAAW,SAAS,CAAC,EAAE,WAAW;AAAA,EACxD,CAAC;AACH,CAAC;","names":[]}
@@ -0,0 +1,5 @@
1
+ import type { GradientToken } from '@becket-ui/tokens/tokens';
2
+ export type GradientBorderToken = Exclude<GradientToken, 'primaryHover'>;
3
+ export declare function isGradientBorderToken(value: unknown): value is GradientBorderToken;
4
+ export declare function getGradientBorderClassName(token: GradientBorderToken): string;
5
+ //# sourceMappingURL=gradientBorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradientBorder.d.ts","sourceRoot":"","sources":["../../src/helpers/gradientBorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;AAmBzE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAElF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAE7E"}
@@ -0,0 +1,25 @@
1
+ import { css } from "@becket-ui/tokens";
2
+ const outlineGradientBorder = (image) => css({
3
+ borderWidth: "2px",
4
+ borderStyle: "solid",
5
+ borderColor: "transparent",
6
+ backgroundImage: `linear-gradient({colors.background}, {colors.background}), ${image}`,
7
+ backgroundOrigin: "border-box",
8
+ backgroundClip: "padding-box, border-box"
9
+ });
10
+ const gradientBorderClassMap = {
11
+ primary: outlineGradientBorder("{gradients.primary}"),
12
+ secondary: outlineGradientBorder("{gradients.secondary}"),
13
+ neutral: outlineGradientBorder("{gradients.neutral}")
14
+ };
15
+ function isGradientBorderToken(value) {
16
+ return typeof value === "string" && value in gradientBorderClassMap;
17
+ }
18
+ function getGradientBorderClassName(token) {
19
+ return gradientBorderClassMap[token];
20
+ }
21
+ export {
22
+ getGradientBorderClassName,
23
+ isGradientBorderToken
24
+ };
25
+ //# sourceMappingURL=gradientBorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/gradientBorder.ts"],"sourcesContent":["import { css } from '@becket-ui/tokens';\nimport type { GradientToken } from '@becket-ui/tokens/tokens';\n\nexport type GradientBorderToken = Exclude<GradientToken, 'primaryHover'>;\n\n/** Double-layer border-box fill so left/right caps match the radius (no ::before sliver). */\nconst outlineGradientBorder = (image: `{gradients.${GradientBorderToken}}`) =>\n css({\n borderWidth: '2px',\n borderStyle: 'solid',\n borderColor: 'transparent',\n backgroundImage: `linear-gradient({colors.background}, {colors.background}), ${image}`,\n backgroundOrigin: 'border-box',\n backgroundClip: 'padding-box, border-box',\n });\n\nconst gradientBorderClassMap: Record<GradientBorderToken, string> = {\n primary: outlineGradientBorder('{gradients.primary}'),\n secondary: outlineGradientBorder('{gradients.secondary}'),\n neutral: outlineGradientBorder('{gradients.neutral}'),\n};\n\nexport function isGradientBorderToken(value: unknown): value is GradientBorderToken {\n return typeof value === 'string' && value in gradientBorderClassMap;\n}\n\nexport function getGradientBorderClassName(token: GradientBorderToken): string {\n return gradientBorderClassMap[token];\n}\n"],"mappings":"AAAA,SAAS,WAAW;AAMpB,MAAM,wBAAwB,CAAC,UAC7B,IAAI;AAAA,EACF,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,iBAAiB,8DAA8D,KAAK;AAAA,EACpF,kBAAkB;AAAA,EAClB,gBAAgB;AAClB,CAAC;AAEH,MAAM,yBAA8D;AAAA,EAClE,SAAS,sBAAsB,qBAAqB;AAAA,EACpD,WAAW,sBAAsB,uBAAuB;AAAA,EACxD,SAAS,sBAAsB,qBAAqB;AACtD;AAEO,SAAS,sBAAsB,OAA8C;AAClF,SAAO,OAAO,UAAU,YAAY,SAAS;AAC/C;AAEO,SAAS,2BAA2B,OAAoC;AAC7E,SAAO,uBAAuB,KAAK;AACrC;","names":[]}
@@ -0,0 +1,17 @@
1
+ import type { CSSProperties } from 'react';
2
+ export declare const LABEL_COLOR_VARS: {
3
+ readonly background: '--mav-label-bg';
4
+ readonly border: '--mav-label-border';
5
+ readonly color: '--mav-label-color';
6
+ readonly gradient: '--mav-label-gradient';
7
+ };
8
+ /** Downstream apps pass CSS values (tokens, vars, or literals) for label surfaces. */
9
+ export type LabelCustomColors = {
10
+ background?: string;
11
+ border?: string;
12
+ color?: string;
13
+ gradient?: string;
14
+ };
15
+ export type LabelVisual = 'primary' | 'secondary' | 'neutral' | 'outline';
16
+ export declare function resolveLabelColorVars(visual: LabelVisual, withGradient: boolean, customColors?: LabelCustomColors): CSSProperties;
17
+ //# sourceMappingURL=labelColors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labelColors.d.ts","sourceRoot":"","sources":["../../src/helpers/labelColors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,eAAO,MAAM,gBAAgB;aAC3B,UAAU,EAAE,gBAAgB;aAC5B,MAAM,EAAE,oBAAoB;aAC5B,KAAK,EAAE,mBAAmB;aAC1B,QAAQ,EAAE,sBAAsB;CACxB,CAAC;AAEX,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAgC1E,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EACnB,YAAY,EAAE,OAAO,EACrB,YAAY,CAAC,EAAE,iBAAiB,GAC/B,aAAa,CAkBf"}
@@ -0,0 +1,54 @@
1
+ const LABEL_COLOR_VARS = {
2
+ background: "--mav-label-bg",
3
+ border: "--mav-label-border",
4
+ color: "--mav-label-color",
5
+ gradient: "--mav-label-gradient"
6
+ };
7
+ const VISUAL_DEFAULTS = {
8
+ primary: {
9
+ background: "var(--beckui--colors-primary)",
10
+ color: "var(--beckui--colors-neutral-50)",
11
+ border: "transparent"
12
+ },
13
+ secondary: {
14
+ background: "var(--beckui--colors-secondary)",
15
+ color: "var(--beckui--colors-neutral-50)",
16
+ border: "transparent"
17
+ },
18
+ neutral: {
19
+ background: "var(--beckui--colors-neutral-800)",
20
+ color: "var(--beckui--colors-text)",
21
+ border: "transparent"
22
+ },
23
+ outline: {
24
+ background: "transparent",
25
+ color: "var(--beckui--colors-secondary)",
26
+ border: "var(--beckui--colors-secondary)"
27
+ }
28
+ };
29
+ const GRADIENT_DEFAULTS = {
30
+ primary: "var(--beckui--gradients-primary)",
31
+ secondary: "var(--beckui--gradients-secondary)",
32
+ neutral: "var(--beckui--gradients-neutral)",
33
+ outline: "var(--beckui--gradients-primary)"
34
+ };
35
+ function resolveLabelColorVars(visual, withGradient, customColors) {
36
+ const defaults = VISUAL_DEFAULTS[visual];
37
+ const merged = { ...defaults, ...customColors };
38
+ const vars = {
39
+ [LABEL_COLOR_VARS.background]: merged.background,
40
+ [LABEL_COLOR_VARS.border]: merged.border,
41
+ [LABEL_COLOR_VARS.color]: merged.color
42
+ };
43
+ if (withGradient) {
44
+ vars[LABEL_COLOR_VARS.gradient] = customColors?.gradient ?? GRADIENT_DEFAULTS[visual];
45
+ }
46
+ return Object.fromEntries(
47
+ Object.entries(vars).filter((entry) => Boolean(entry[1]))
48
+ );
49
+ }
50
+ export {
51
+ LABEL_COLOR_VARS,
52
+ resolveLabelColorVars
53
+ };
54
+ //# sourceMappingURL=labelColors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/labelColors.ts"],"sourcesContent":["import type { CSSProperties } from 'react';\n\nexport const LABEL_COLOR_VARS = {\n background: '--mav-label-bg',\n border: '--mav-label-border',\n color: '--mav-label-color',\n gradient: '--mav-label-gradient',\n} as const;\n\n/** Downstream apps pass CSS values (tokens, vars, or literals) for label surfaces. */\nexport type LabelCustomColors = {\n background?: string;\n border?: string;\n color?: string;\n gradient?: string;\n};\n\nexport type LabelVisual = 'primary' | 'secondary' | 'neutral' | 'outline';\n\nconst VISUAL_DEFAULTS: Record<LabelVisual, LabelCustomColors> = {\n primary: {\n background: 'var(--beckui--colors-primary)',\n color: 'var(--beckui--colors-neutral-50)',\n border: 'transparent',\n },\n secondary: {\n background: 'var(--beckui--colors-secondary)',\n color: 'var(--beckui--colors-neutral-50)',\n border: 'transparent',\n },\n neutral: {\n background: 'var(--beckui--colors-neutral-800)',\n color: 'var(--beckui--colors-text)',\n border: 'transparent',\n },\n outline: {\n background: 'transparent',\n color: 'var(--beckui--colors-secondary)',\n border: 'var(--beckui--colors-secondary)',\n },\n};\n\nconst GRADIENT_DEFAULTS: Record<LabelVisual, string> = {\n primary: 'var(--beckui--gradients-primary)',\n secondary: 'var(--beckui--gradients-secondary)',\n neutral: 'var(--beckui--gradients-neutral)',\n outline: 'var(--beckui--gradients-primary)',\n};\n\nexport function resolveLabelColorVars(\n visual: LabelVisual,\n withGradient: boolean,\n customColors?: LabelCustomColors,\n): CSSProperties {\n const defaults = VISUAL_DEFAULTS[visual];\n const merged: LabelCustomColors = { ...defaults, ...customColors };\n\n const vars: Record<string, string | undefined> = {\n [LABEL_COLOR_VARS.background]: merged.background,\n [LABEL_COLOR_VARS.border]: merged.border,\n [LABEL_COLOR_VARS.color]: merged.color,\n };\n\n if (withGradient) {\n vars[LABEL_COLOR_VARS.gradient] =\n customColors?.gradient ?? GRADIENT_DEFAULTS[visual];\n }\n\n return Object.fromEntries(\n Object.entries(vars).filter((entry): entry is [string, string] => Boolean(entry[1])),\n ) as CSSProperties;\n}\n"],"mappings":"AAEO,MAAM,mBAAmB;AAAA,EAC9B,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,UAAU;AACZ;AAYA,MAAM,kBAA0D;AAAA,EAC9D,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,WAAW;AAAA,IACT,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACF;AAEA,MAAM,oBAAiD;AAAA,EACrD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AACX;AAEO,SAAS,sBACd,QACA,cACA,cACe;AACf,QAAM,WAAW,gBAAgB,MAAM;AACvC,QAAM,SAA4B,EAAE,GAAG,UAAU,GAAG,aAAa;AAEjE,QAAM,OAA2C;AAAA,IAC/C,CAAC,iBAAiB,UAAU,GAAG,OAAO;AAAA,IACtC,CAAC,iBAAiB,MAAM,GAAG,OAAO;AAAA,IAClC,CAAC,iBAAiB,KAAK,GAAG,OAAO;AAAA,EACnC;AAEA,MAAI,cAAc;AAChB,SAAK,iBAAiB,QAAQ,IAC5B,cAAc,YAAY,kBAAkB,MAAM;AAAA,EACtD;AAEA,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,UAAqC,QAAQ,MAAM,CAAC,CAAC,CAAC;AAAA,EACrF;AACF;","names":[]}
@@ -0,0 +1,23 @@
1
+ import { type Ref, type RefObject } from 'react';
2
+ type NativeDialogOptions = {
3
+ open?: boolean;
4
+ defaultOpen?: boolean;
5
+ onOpenChange?: (open: boolean) => void;
6
+ /** Composed title is in the tree (skip waiting on DialogTitle mount). */
7
+ titlePresent?: boolean;
8
+ /** Composed body has content (skip waiting on DialogBody mount). */
9
+ descriptionPresent?: boolean;
10
+ };
11
+ export declare function useNativeDialog(dialogRef: RefObject<HTMLDialogElement | null>, { open, defaultOpen, onOpenChange, titlePresent, descriptionPresent, }: NativeDialogOptions): {
12
+ isOpen: boolean;
13
+ setOpen: (next: boolean) => void;
14
+ titleId: string;
15
+ descriptionId: string;
16
+ labelledBy: string | undefined;
17
+ describedBy: string | undefined;
18
+ registerTitle: (present: boolean) => void;
19
+ registerDescription: (present: boolean) => void;
20
+ };
21
+ export declare function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void;
22
+ export {};
23
+ //# sourceMappingURL=nativeDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeDialog.d.ts","sourceRoot":"","sources":["../../src/helpers/nativeDialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,GAAG,EACR,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,KAAK,mBAAmB,GAAG;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EAC9C,EACE,IAAI,EACJ,WAAmB,EACnB,YAAY,EACZ,YAAoB,EACpB,kBAA0B,GAC3B,EAAE,mBAAmB;;oBAkBb,OAAO;;;;;6BAT4B,OAAO;mCAID,OAAO;EAwD1D;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,QAQpE"}
@@ -0,0 +1,85 @@
1
+ import {
2
+ useCallback,
3
+ useEffect,
4
+ useId,
5
+ useState
6
+ } from "react";
7
+ function useNativeDialog(dialogRef, {
8
+ open,
9
+ defaultOpen = false,
10
+ onOpenChange,
11
+ titlePresent = false,
12
+ descriptionPresent = false
13
+ }) {
14
+ const isControlled = open !== void 0;
15
+ const [uncontrolled, setUncontrolled] = useState(defaultOpen);
16
+ const isOpen = isControlled ? Boolean(open) : uncontrolled;
17
+ const titleId = useId();
18
+ const descriptionId = useId();
19
+ const [slots, setSlots] = useState({ title: false, description: false });
20
+ const registerTitle = useCallback((present) => {
21
+ setSlots((prev) => prev.title === present ? prev : { ...prev, title: present });
22
+ }, []);
23
+ const registerDescription = useCallback((present) => {
24
+ setSlots((prev) => prev.description === present ? prev : { ...prev, description: present });
25
+ }, []);
26
+ const setOpen = useCallback(
27
+ (next) => {
28
+ if (!isControlled) setUncontrolled(next);
29
+ onOpenChange?.(next);
30
+ },
31
+ [isControlled, onOpenChange]
32
+ );
33
+ useEffect(() => {
34
+ const el = dialogRef.current;
35
+ if (!el) return;
36
+ if (!isOpen) {
37
+ if (el.open) el.close();
38
+ return;
39
+ }
40
+ if (!el.open) {
41
+ if (typeof el.showModal === "function") {
42
+ el.showModal();
43
+ } else {
44
+ el.setAttribute("open", "");
45
+ }
46
+ }
47
+ const focusInitial = () => {
48
+ if (!el.open && !el.hasAttribute("open")) return;
49
+ const marked = el.querySelector("[data-becket-initial-focus]");
50
+ const footerBtn = el.querySelector("[data-becket-dialog-footer] button:not([disabled])");
51
+ const target = marked ?? footerBtn ?? el;
52
+ target.focus();
53
+ };
54
+ const frame = requestAnimationFrame(() => {
55
+ requestAnimationFrame(focusInitial);
56
+ });
57
+ return () => cancelAnimationFrame(frame);
58
+ }, [dialogRef, isOpen]);
59
+ const labelledBy = titlePresent || slots.title ? titleId : void 0;
60
+ const describedBy = descriptionPresent || slots.description ? descriptionId : void 0;
61
+ return {
62
+ isOpen,
63
+ setOpen,
64
+ titleId,
65
+ descriptionId,
66
+ labelledBy,
67
+ describedBy,
68
+ registerTitle,
69
+ registerDescription
70
+ };
71
+ }
72
+ function assignRef(ref, value) {
73
+ if (typeof ref === "function") {
74
+ ref(value);
75
+ return;
76
+ }
77
+ if (ref) {
78
+ ref.current = value;
79
+ }
80
+ }
81
+ export {
82
+ assignRef,
83
+ useNativeDialog
84
+ };
85
+ //# sourceMappingURL=nativeDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/nativeDialog.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useState,\n type MutableRefObject,\n type Ref,\n type RefObject,\n} from 'react';\n\ntype NativeDialogOptions = {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Composed title is in the tree (skip waiting on DialogTitle mount). */\n titlePresent?: boolean;\n /** Composed body has content (skip waiting on DialogBody mount). */\n descriptionPresent?: boolean;\n};\n\nexport function useNativeDialog(\n dialogRef: RefObject<HTMLDialogElement | null>,\n {\n open,\n defaultOpen = false,\n onOpenChange,\n titlePresent = false,\n descriptionPresent = false,\n }: NativeDialogOptions,\n) {\n const isControlled = open !== undefined;\n const [uncontrolled, setUncontrolled] = useState(defaultOpen);\n const isOpen = isControlled ? Boolean(open) : uncontrolled;\n const titleId = useId();\n const descriptionId = useId();\n const [slots, setSlots] = useState({ title: false, description: false });\n\n const registerTitle = useCallback((present: boolean) => {\n setSlots((prev) => (prev.title === present ? prev : { ...prev, title: present }));\n }, []);\n\n const registerDescription = useCallback((present: boolean) => {\n setSlots((prev) => (prev.description === present ? prev : { ...prev, description: present }));\n }, []);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setUncontrolled(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n useEffect(() => {\n const el = dialogRef.current;\n if (!el) return;\n\n if (!isOpen) {\n if (el.open) el.close();\n return;\n }\n\n if (!el.open) {\n if (typeof el.showModal === 'function') {\n el.showModal();\n } else {\n el.setAttribute('open', '');\n }\n }\n\n const focusInitial = () => {\n if (!el.open && !el.hasAttribute('open')) return;\n const marked = el.querySelector<HTMLElement>('[data-becket-initial-focus]');\n const footerBtn = el.querySelector<HTMLElement>('[data-becket-dialog-footer] button:not([disabled])');\n const target = marked ?? footerBtn ?? el;\n target.focus();\n };\n\n const frame = requestAnimationFrame(() => {\n requestAnimationFrame(focusInitial);\n });\n return () => cancelAnimationFrame(frame);\n }, [dialogRef, isOpen]);\n\n const labelledBy = titlePresent || slots.title ? titleId : undefined;\n const describedBy = descriptionPresent || slots.description ? descriptionId : undefined;\n\n return {\n isOpen,\n setOpen,\n titleId,\n descriptionId,\n labelledBy,\n describedBy,\n registerTitle,\n registerDescription,\n };\n}\n\nexport function assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === 'function') {\n ref(value);\n return;\n }\n if (ref) {\n (ref as MutableRefObject<T | null>).current = value;\n }\n}\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAYA,SAAS,gBACd,WACA;AAAA,EACE;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,eAAe;AAAA,EACf,qBAAqB;AACvB,GACA;AACA,QAAM,eAAe,SAAS;AAC9B,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,WAAW;AAC5D,QAAM,SAAS,eAAe,QAAQ,IAAI,IAAI;AAC9C,QAAM,UAAU,MAAM;AACtB,QAAM,gBAAgB,MAAM;AAC5B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE,OAAO,OAAO,aAAa,MAAM,CAAC;AAEvE,QAAM,gBAAgB,YAAY,CAAC,YAAqB;AACtD,aAAS,CAAC,SAAU,KAAK,UAAU,UAAU,OAAO,EAAE,GAAG,MAAM,OAAO,QAAQ,CAAE;AAAA,EAClF,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,YAAY,CAAC,YAAqB;AAC5D,aAAS,CAAC,SAAU,KAAK,gBAAgB,UAAU,OAAO,EAAE,GAAG,MAAM,aAAa,QAAQ,CAAE;AAAA,EAC9F,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU;AAAA,IACd,CAAC,SAAkB;AACjB,UAAI,CAAC,aAAc,iBAAgB,IAAI;AACvC,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,cAAc,YAAY;AAAA,EAC7B;AAEA,YAAU,MAAM;AACd,UAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AAET,QAAI,CAAC,QAAQ;AACX,UAAI,GAAG,KAAM,IAAG,MAAM;AACtB;AAAA,IACF;AAEA,QAAI,CAAC,GAAG,MAAM;AACZ,UAAI,OAAO,GAAG,cAAc,YAAY;AACtC,WAAG,UAAU;AAAA,MACf,OAAO;AACL,WAAG,aAAa,QAAQ,EAAE;AAAA,MAC5B;AAAA,IACF;AAEA,UAAM,eAAe,MAAM;AACzB,UAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,aAAa,MAAM,EAAG;AAC1C,YAAM,SAAS,GAAG,cAA2B,6BAA6B;AAC1E,YAAM,YAAY,GAAG,cAA2B,oDAAoD;AACpG,YAAM,SAAS,UAAU,aAAa;AACtC,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,QAAQ,sBAAsB,MAAM;AACxC,4BAAsB,YAAY;AAAA,IACpC,CAAC;AACD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,WAAW,MAAM,CAAC;AAEtB,QAAM,aAAa,gBAAgB,MAAM,QAAQ,UAAU;AAC3D,QAAM,cAAc,sBAAsB,MAAM,cAAc,gBAAgB;AAE9E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,UAAa,KAAyB,OAAiB;AACrE,MAAI,OAAO,QAAQ,YAAY;AAC7B,QAAI,KAAK;AACT;AAAA,EACF;AACA,MAAI,KAAK;AACP,IAAC,IAAmC,UAAU;AAAA,EAChD;AACF;","names":[]}
@@ -0,0 +1,63 @@
1
+ import type { ConditionalValue } from '@becket-ui/tokens/types';
2
+ /**
3
+ * Responsive props — Chakra-aligned, mobile-first (min-width).
4
+ *
5
+ * Prefer **object syntax** with a `base`/`mobile` value (Chakra docs + community
6
+ * best practice). Arrays are supported for parity but are harder to read.
7
+ *
8
+ * @see https://chakra-ui.com/docs/styling/responsive-design
9
+ */
10
+ /** Token breakpoints (Panda / Chakra keys). */
11
+ export type Breakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
12
+ /**
13
+ * Semantic aliases for layout in consuming apps.
14
+ * Map onto token breakpoints — do not invent separate media queries.
15
+ */
16
+ export type SemanticBreakpoint = 'mobile' | 'tablet' | 'desktop' | 'largeDesktop';
17
+ export type BreakpointAlias = Breakpoint | SemanticBreakpoint;
18
+ /** Breakpoint-only keys for responsive object props (token + semantic). */
19
+ export type BreakpointMap<T> = Partial<Record<BreakpointAlias, T>>;
20
+ /**
21
+ * Chakra-style responsive value: single value, per-breakpoint object, or array
22
+ * (index 0 = base, 1 = sm, 2 = md, …).
23
+ */
24
+ export type ResponsiveValue<T> = ConditionalValue<T> | BreakpointMap<T>;
25
+ export declare const BREAKPOINTS: readonly ["base", "sm", "md", "lg", "xl", "2xl"];
26
+ export declare const SEMANTIC_BREAKPOINTS: readonly ["mobile", "tablet", "desktop", "largeDesktop"];
27
+ /** Semantic → token breakpoint (Chakra mobile-first scale). */
28
+ export declare const SEMANTIC_TO_BREAKPOINT: {
29
+ readonly mobile: 'base';
30
+ readonly tablet: 'md';
31
+ readonly desktop: 'lg';
32
+ readonly largeDesktop: 'xl';
33
+ };
34
+ /** Becket-authored spacing steps (Fibonacci + zero). */
35
+ export declare const SPACING_SCALE: readonly [0, 1, 2, 3, 5, 8, 13, 21, 34];
36
+ export type SpacingStep = (typeof SPACING_SCALE)[number];
37
+ /** Semantic aliases preferred in apps. */
38
+ export type SpacingSemantic = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xs';
39
+ /**
40
+ * Values that show up in IDE autocomplete for `gap` / spacing props.
41
+ * Numeric literals + string keys + semantic tokens.
42
+ * `(string & {})` / `(number & {})` keep IntelliSense while allowing escapes.
43
+ */
44
+ export type SpacingScale = SpacingSemantic | SpacingStep | `${SpacingStep}` | (string & {}) | (number & {});
45
+ /** Gap prop: single value or responsive ConditionalValue. */
46
+ export type GapValue = ResponsiveValue<SpacingScale>;
47
+ export declare function isSemanticBreakpoint(key: string): key is SemanticBreakpoint;
48
+ export declare function isTokenBreakpoint(key: string): key is Breakpoint;
49
+ /** Resolve `desktop` → `lg`, leave `md` unchanged. */
50
+ export declare function resolveBreakpointAlias(key: string): string;
51
+ /**
52
+ * Expand semantic keys in a responsive object to token breakpoints.
53
+ * Token keys already present win (not overwritten by semantic aliases).
54
+ * Non-objects / arrays pass through.
55
+ */
56
+ export declare function normalizeResponsiveValue<T>(value: T): T;
57
+ /**
58
+ * Coerce gap values so `gap={3}` and `gap="3"` both resolve to token key `"3"`.
59
+ * Walks responsive objects/arrays. Leaves semantic tokens unchanged.
60
+ * Expands semantic breakpoint keys (`mobile` → `base`, etc.).
61
+ */
62
+ export declare function normalizeGapValue<T>(gap: T): T;
63
+ //# sourceMappingURL=responsive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responsive.d.ts","sourceRoot":"","sources":["../../src/helpers/responsive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;;;;;GAOG;AAEH,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,cAAc,CAAC;AAElF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,kBAAkB,CAAC;AAE9D,2EAA2E;AAC3E,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;AAExE,eAAO,MAAM,WAAW,kDAAmF,CAAC;AAE5G,eAAO,MAAM,oBAAoB,0DAKiB,CAAC;AAEnD,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB;qBACzB,MAAM;qBACN,IAAI;sBACH,IAAI;2BACC,IAAI;CACuC,CAAC;AAE5D,wDAAwD;AACxD,eAAO,MAAM,aAAa,YAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAU,CAAC;AAErE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,0CAA0C;AAC1C,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,WAAW,GACX,GAAG,WAAW,EAAE,GAChB,CAAC,MAAM,GAAG,EAAE,CAAC,GACb,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,6DAA6D;AAC7D,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;AAErD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,UAAU,CAEhE;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CA4BvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAgB9C"}