@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,132 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useContext,
7
+ useRef,
8
+ useState
9
+ } from "react";
10
+ import { menu as menuRecipe } from "@becket-ui/tokens/recipes";
11
+ const MenuContext = createContext(null);
12
+ function mergeClassName(...classes) {
13
+ return classes.filter(Boolean).join(" ");
14
+ }
15
+ function menuItems(root) {
16
+ return [...root?.querySelectorAll('[role="menuitem"]:not([disabled])') ?? []];
17
+ }
18
+ const Menu = forwardRef(
19
+ ({ label, className, children, onKeyDown, ...props }, ref) => {
20
+ const detailsRef = useRef(null);
21
+ const [expanded, setExpanded] = useState(false);
22
+ const styles = menuRecipe();
23
+ const close = () => {
24
+ if (detailsRef.current) detailsRef.current.open = false;
25
+ };
26
+ const focusItem = (index) => {
27
+ const items = menuItems(detailsRef.current);
28
+ if (items.length === 0) return;
29
+ const next = items[(index + items.length) % items.length];
30
+ next?.focus();
31
+ };
32
+ const onMenuKeyDown = (event) => {
33
+ onKeyDown?.(event);
34
+ if (event.defaultPrevented) return;
35
+ const details = detailsRef.current;
36
+ if (!details) return;
37
+ if (event.key === "Escape") {
38
+ close();
39
+ details.querySelector("summary")?.focus();
40
+ return;
41
+ }
42
+ if (event.key === "Tab" && details.open) {
43
+ close();
44
+ return;
45
+ }
46
+ if (event.key === "Home" && details.open) {
47
+ event.preventDefault();
48
+ focusItem(0);
49
+ return;
50
+ }
51
+ if (event.key === "End" && details.open) {
52
+ event.preventDefault();
53
+ const items2 = menuItems(details);
54
+ focusItem(items2.length - 1);
55
+ return;
56
+ }
57
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
58
+ event.preventDefault();
59
+ const items = menuItems(details);
60
+ if (items.length === 0) return;
61
+ if (!details.open) {
62
+ details.open = true;
63
+ requestAnimationFrame(() => focusItem(event.key === "ArrowUp" ? -1 : 0));
64
+ return;
65
+ }
66
+ const current = document.activeElement;
67
+ const index = items.findIndex((item) => item === current);
68
+ const delta = event.key === "ArrowDown" ? 1 : -1;
69
+ focusItem(index < 0 ? 0 : index + delta);
70
+ };
71
+ return /* @__PURE__ */ jsx(MenuContext.Provider, { value: { close }, children: /* @__PURE__ */ jsxs(
72
+ "details",
73
+ {
74
+ ref: (node) => {
75
+ detailsRef.current = node;
76
+ if (typeof ref === "function") ref(node);
77
+ else if (ref) ref.current = node;
78
+ },
79
+ className: mergeClassName(styles.root, className),
80
+ ...props,
81
+ onToggle: (event) => {
82
+ const next = event.currentTarget.open;
83
+ setExpanded(next);
84
+ if (next) {
85
+ requestAnimationFrame(() => focusItem(0));
86
+ }
87
+ },
88
+ onKeyDown: onMenuKeyDown,
89
+ children: [
90
+ /* @__PURE__ */ jsx(
91
+ "summary",
92
+ {
93
+ className: styles.trigger,
94
+ "aria-haspopup": "menu",
95
+ "aria-expanded": expanded,
96
+ children: label
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsx("div", { className: styles.content, role: "menu", children })
100
+ ]
101
+ }
102
+ ) });
103
+ }
104
+ );
105
+ Menu.displayName = "Menu";
106
+ const MenuItem = forwardRef(
107
+ ({ className, onClick, type = "button", tabIndex = -1, ...props }, ref) => {
108
+ const menu = useContext(MenuContext);
109
+ const styles = menuRecipe();
110
+ return /* @__PURE__ */ jsx(
111
+ "button",
112
+ {
113
+ ref,
114
+ type,
115
+ role: "menuitem",
116
+ ...props,
117
+ tabIndex,
118
+ className: mergeClassName(styles.item, className),
119
+ onClick: (event) => {
120
+ onClick?.(event);
121
+ if (!event.defaultPrevented) menu?.close();
122
+ }
123
+ }
124
+ );
125
+ }
126
+ );
127
+ MenuItem.displayName = "MenuItem";
128
+ export {
129
+ Menu,
130
+ MenuItem
131
+ };
132
+ //# sourceMappingURL=Menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Menu.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n forwardRef,\n useContext,\n useRef,\n useState,\n type ButtonHTMLAttributes,\n type DetailsHTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n type ToggleEvent,\n} from 'react';\nimport { menu as menuRecipe } from '@becket-ui/tokens/recipes';\n\ntype MenuContextValue = {\n close: () => void;\n};\n\nconst MenuContext = createContext<MenuContextValue | null>(null);\n\nfunction mergeClassName(...classes: (string | undefined)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction menuItems(root: HTMLDetailsElement | null) {\n return [...(root?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([disabled])') ?? [])];\n}\n\nexport type MenuProps = Omit<DetailsHTMLAttributes<HTMLDetailsElement>, 'onToggle'> & {\n label: ReactNode;\n};\n\nexport const Menu = forwardRef<HTMLDetailsElement, MenuProps>(\n ({ label, className, children, onKeyDown, ...props }, ref) => {\n const detailsRef = useRef<HTMLDetailsElement>(null);\n const [expanded, setExpanded] = useState(false);\n const styles = menuRecipe();\n\n const close = () => {\n if (detailsRef.current) detailsRef.current.open = false;\n };\n\n const focusItem = (index: number) => {\n const items = menuItems(detailsRef.current);\n if (items.length === 0) return;\n const next = items[(index + items.length) % items.length];\n next?.focus();\n };\n\n const onMenuKeyDown = (event: KeyboardEvent<HTMLDetailsElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n const details = detailsRef.current;\n if (!details) return;\n\n if (event.key === 'Escape') {\n close();\n details.querySelector('summary')?.focus();\n return;\n }\n\n if (event.key === 'Tab' && details.open) {\n close();\n return;\n }\n\n if (event.key === 'Home' && details.open) {\n event.preventDefault();\n focusItem(0);\n return;\n }\n\n if (event.key === 'End' && details.open) {\n event.preventDefault();\n const items = menuItems(details);\n focusItem(items.length - 1);\n return;\n }\n\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n event.preventDefault();\n const items = menuItems(details);\n if (items.length === 0) return;\n if (!details.open) {\n details.open = true;\n requestAnimationFrame(() => focusItem(event.key === 'ArrowUp' ? -1 : 0));\n return;\n }\n const current = document.activeElement;\n const index = items.findIndex((item) => item === current);\n const delta = event.key === 'ArrowDown' ? 1 : -1;\n focusItem(index < 0 ? 0 : index + delta);\n };\n\n return (\n <MenuContext.Provider value={{ close }}>\n <details\n ref={(node) => {\n detailsRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n }}\n className={mergeClassName(styles.root, className)}\n {...props}\n onToggle={(event: ToggleEvent<HTMLDetailsElement>) => {\n const next = event.currentTarget.open;\n setExpanded(next);\n if (next) {\n requestAnimationFrame(() => focusItem(0));\n }\n }}\n onKeyDown={onMenuKeyDown}\n >\n <summary\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={expanded}\n >\n {label}\n </summary>\n <div className={styles.content} role=\"menu\">\n {children}\n </div>\n </details>\n </MenuContext.Provider>\n );\n },\n);\n\nMenu.displayName = 'Menu';\n\ntype MenuItemProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport const MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(\n ({ className, onClick, type = 'button', tabIndex = -1, ...props }, ref) => {\n const menu = useContext(MenuContext);\n const styles = menuRecipe();\n return (\n <button\n ref={ref}\n type={type}\n role=\"menuitem\"\n {...props}\n tabIndex={tabIndex}\n className={mergeClassName(styles.item, className)}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) menu?.close();\n }}\n />\n );\n },\n);\n\nMenuItem.displayName = 'MenuItem';\n"],"mappings":"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","names":["items"]}
@@ -0,0 +1,69 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { describe, expect, it } from "vitest";
3
+ import { render, screen, waitFor } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { assertNoAxeViolations } from "@becket-ui/a11y";
6
+ import { Menu, MenuItem } from "./Menu";
7
+ describe("Menu", () => {
8
+ it("exposes expanded and haspopup on the trigger", async () => {
9
+ const user = userEvent.setup();
10
+ render(
11
+ /* @__PURE__ */ jsxs(Menu, { label: "Actions", children: [
12
+ /* @__PURE__ */ jsx(MenuItem, { children: "Edit" }),
13
+ /* @__PURE__ */ jsx(MenuItem, { children: "Duplicate" })
14
+ ] })
15
+ );
16
+ const trigger = screen.getByText("Actions");
17
+ expect(trigger.tagName).toBe("SUMMARY");
18
+ expect(trigger.getAttribute("aria-haspopup")).toBe("menu");
19
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
20
+ await user.click(trigger);
21
+ expect(trigger.getAttribute("aria-expanded")).toBe("true");
22
+ });
23
+ it("keeps open items out of Tab order", async () => {
24
+ const user = userEvent.setup();
25
+ render(
26
+ /* @__PURE__ */ jsxs(Fragment, { children: [
27
+ /* @__PURE__ */ jsx("button", { type: "button", children: "Before" }),
28
+ /* @__PURE__ */ jsxs(Menu, { label: "Actions", children: [
29
+ /* @__PURE__ */ jsx(MenuItem, { children: "Edit" }),
30
+ /* @__PURE__ */ jsx(MenuItem, { children: "Duplicate" })
31
+ ] }),
32
+ /* @__PURE__ */ jsx("button", { type: "button", children: "After" })
33
+ ] })
34
+ );
35
+ await user.click(screen.getByText("Actions"));
36
+ expect(screen.getByRole("menuitem", { name: "Edit" }).getAttribute("tabindex")).toBe("-1");
37
+ expect(screen.getByRole("menuitem", { name: "Duplicate" }).getAttribute("tabindex")).toBe("-1");
38
+ await waitFor(() => {
39
+ expect(document.activeElement).toBe(screen.getByRole("menuitem", { name: "Edit" }));
40
+ });
41
+ await user.tab();
42
+ expect(document.activeElement).toBe(screen.getByRole("button", { name: "After" }));
43
+ });
44
+ it("moves between items with arrows", async () => {
45
+ const user = userEvent.setup();
46
+ render(
47
+ /* @__PURE__ */ jsxs(Menu, { label: "Actions", children: [
48
+ /* @__PURE__ */ jsx(MenuItem, { children: "Edit" }),
49
+ /* @__PURE__ */ jsx(MenuItem, { children: "Duplicate" })
50
+ ] })
51
+ );
52
+ await user.click(screen.getByText("Actions"));
53
+ await waitFor(() => {
54
+ expect(document.activeElement).toBe(screen.getByRole("menuitem", { name: "Edit" }));
55
+ });
56
+ await user.keyboard("{ArrowDown}");
57
+ expect(document.activeElement).toBe(screen.getByRole("menuitem", { name: "Duplicate" }));
58
+ });
59
+ it("has no axe violations", async () => {
60
+ const { container } = render(
61
+ /* @__PURE__ */ jsxs(Menu, { label: "Actions", children: [
62
+ /* @__PURE__ */ jsx(MenuItem, { children: "Edit" }),
63
+ /* @__PURE__ */ jsx(MenuItem, { children: "Duplicate" })
64
+ ] })
65
+ );
66
+ await assertNoAxeViolations(container);
67
+ });
68
+ });
69
+ //# sourceMappingURL=Menu.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Menu.test.tsx"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { render, screen, waitFor } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { assertNoAxeViolations } from '@becket-ui/a11y';\n\nimport { Menu, MenuItem } from './Menu';\n\ndescribe('Menu', () => {\n it('exposes expanded and haspopup on the trigger', async () => {\n const user = userEvent.setup();\n render(\n <Menu label=\"Actions\">\n <MenuItem>Edit</MenuItem>\n <MenuItem>Duplicate</MenuItem>\n </Menu>,\n );\n const trigger = screen.getByText('Actions');\n expect(trigger.tagName).toBe('SUMMARY');\n expect(trigger.getAttribute('aria-haspopup')).toBe('menu');\n expect(trigger.getAttribute('aria-expanded')).toBe('false');\n\n await user.click(trigger);\n expect(trigger.getAttribute('aria-expanded')).toBe('true');\n });\n\n it('keeps open items out of Tab order', async () => {\n const user = userEvent.setup();\n render(\n <>\n <button type=\"button\">Before</button>\n <Menu label=\"Actions\">\n <MenuItem>Edit</MenuItem>\n <MenuItem>Duplicate</MenuItem>\n </Menu>\n <button type=\"button\">After</button>\n </>,\n );\n\n await user.click(screen.getByText('Actions'));\n expect(screen.getByRole('menuitem', { name: 'Edit' }).getAttribute('tabindex')).toBe('-1');\n expect(screen.getByRole('menuitem', { name: 'Duplicate' }).getAttribute('tabindex')).toBe('-1');\n\n await waitFor(() => {\n expect(document.activeElement).toBe(screen.getByRole('menuitem', { name: 'Edit' }));\n });\n await user.tab();\n expect(document.activeElement).toBe(screen.getByRole('button', { name: 'After' }));\n });\n\n it('moves between items with arrows', async () => {\n const user = userEvent.setup();\n render(\n <Menu label=\"Actions\">\n <MenuItem>Edit</MenuItem>\n <MenuItem>Duplicate</MenuItem>\n </Menu>,\n );\n await user.click(screen.getByText('Actions'));\n await waitFor(() => {\n expect(document.activeElement).toBe(screen.getByRole('menuitem', { name: 'Edit' }));\n });\n await user.keyboard('{ArrowDown}');\n expect(document.activeElement).toBe(screen.getByRole('menuitem', { name: 'Duplicate' }));\n });\n\n it('has no axe violations', async () => {\n const { container } = render(\n <Menu label=\"Actions\">\n <MenuItem>Edit</MenuItem>\n <MenuItem>Duplicate</MenuItem>\n </Menu>,\n );\n await assertNoAxeViolations(container);\n });\n});\n"],"mappings":"AAWM,SAiBA,UAhBE,KADF;AAXN,SAAS,UAAU,QAAQ,UAAU;AACrC,SAAS,QAAQ,QAAQ,eAAe;AACxC,OAAO,eAAe;AACtB,SAAS,6BAA6B;AAEtC,SAAS,MAAM,gBAAgB;AAE/B,SAAS,QAAQ,MAAM;AACrB,KAAG,gDAAgD,YAAY;AAC7D,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,qBAAC,QAAK,OAAM,WACV;AAAA,4BAAC,YAAS,kBAAI;AAAA,QACd,oBAAC,YAAS,uBAAS;AAAA,SACrB;AAAA,IACF;AACA,UAAM,UAAU,OAAO,UAAU,SAAS;AAC1C,WAAO,QAAQ,OAAO,EAAE,KAAK,SAAS;AACtC,WAAO,QAAQ,aAAa,eAAe,CAAC,EAAE,KAAK,MAAM;AACzD,WAAO,QAAQ,aAAa,eAAe,CAAC,EAAE,KAAK,OAAO;AAE1D,UAAM,KAAK,MAAM,OAAO;AACxB,WAAO,QAAQ,aAAa,eAAe,CAAC,EAAE,KAAK,MAAM;AAAA,EAC3D,CAAC;AAED,KAAG,qCAAqC,YAAY;AAClD,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,iCACE;AAAA,4BAAC,YAAO,MAAK,UAAS,oBAAM;AAAA,QAC5B,qBAAC,QAAK,OAAM,WACV;AAAA,8BAAC,YAAS,kBAAI;AAAA,UACd,oBAAC,YAAS,uBAAS;AAAA,WACrB;AAAA,QACA,oBAAC,YAAO,MAAK,UAAS,mBAAK;AAAA,SAC7B;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,OAAO,UAAU,SAAS,CAAC;AAC5C,WAAO,OAAO,UAAU,YAAY,EAAE,MAAM,OAAO,CAAC,EAAE,aAAa,UAAU,CAAC,EAAE,KAAK,IAAI;AACzF,WAAO,OAAO,UAAU,YAAY,EAAE,MAAM,YAAY,CAAC,EAAE,aAAa,UAAU,CAAC,EAAE,KAAK,IAAI;AAE9F,UAAM,QAAQ,MAAM;AAClB,aAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,IACpF,CAAC;AACD,UAAM,KAAK,IAAI;AACf,WAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,EACnF,CAAC;AAED,KAAG,mCAAmC,YAAY;AAChD,UAAM,OAAO,UAAU,MAAM;AAC7B;AAAA,MACE,qBAAC,QAAK,OAAM,WACV;AAAA,4BAAC,YAAS,kBAAI;AAAA,QACd,oBAAC,YAAS,uBAAS;AAAA,SACrB;AAAA,IACF;AACA,UAAM,KAAK,MAAM,OAAO,UAAU,SAAS,CAAC;AAC5C,UAAM,QAAQ,MAAM;AAClB,aAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,IACpF,CAAC;AACD,UAAM,KAAK,SAAS,aAAa;AACjC,WAAO,SAAS,aAAa,EAAE,KAAK,OAAO,UAAU,YAAY,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA,EACzF,CAAC;AAED,KAAG,yBAAyB,YAAY;AACtC,UAAM,EAAE,UAAU,IAAI;AAAA,MACpB,qBAAC,QAAK,OAAM,WACV;AAAA,4BAAC,YAAS,kBAAI;AAAA,QACd,oBAAC,YAAS,uBAAS;AAAA,SACrB;AAAA,IACF;AACA,UAAM,sBAAsB,SAAS;AAAA,EACvC,CAAC;AACH,CAAC;","names":[]}
@@ -0,0 +1,38 @@
1
+ import { type ComponentPropsWithoutRef, type InputHTMLAttributes, type ReactNode } from 'react';
2
+ type RadioSize = 'sm' | 'md';
3
+ export type RadioGroupProps = Omit<ComponentPropsWithoutRef<'fieldset'>, 'onChange'> & {
4
+ name?: string;
5
+ value?: string;
6
+ defaultValue?: string;
7
+ disabled?: boolean;
8
+ invalid?: boolean;
9
+ size?: RadioSize;
10
+ legend?: ReactNode;
11
+ onValueChange?: (value: string) => void;
12
+ children?: ReactNode;
13
+ };
14
+ export declare const RadioGroup: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>, "ref">, "onChange"> & {
15
+ name?: string;
16
+ value?: string;
17
+ defaultValue?: string;
18
+ disabled?: boolean;
19
+ invalid?: boolean;
20
+ size?: RadioSize;
21
+ legend?: ReactNode;
22
+ onValueChange?: (value: string) => void;
23
+ children?: ReactNode;
24
+ } & import("react").RefAttributes<HTMLFieldSetElement>>;
25
+ export type RadioProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> & {
26
+ value: string;
27
+ label?: ReactNode;
28
+ description?: ReactNode;
29
+ size?: RadioSize;
30
+ };
31
+ export declare const Radio: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> & {
32
+ value: string;
33
+ label?: ReactNode;
34
+ description?: ReactNode;
35
+ size?: RadioSize;
36
+ } & import("react").RefAttributes<HTMLInputElement>>;
37
+ export {};
38
+ //# sourceMappingURL=Radio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../src/components/Radio.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;AAuB7B,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,wBAAwB,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,GAAG;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,UAAU;WAXd,MAAM;YACL,MAAM;mBACC,MAAM;eACV,OAAO;cACR,OAAO;WACV,SAAS;aACP,SAAS;oBACF,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;eAC5B,SAAS;uDA0DrB,CAAC;AAIF,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,KAAK;WANT,MAAM;YACL,SAAS;kBACH,SAAS;WAChB,SAAS;oDA8EjB,CAAC"}
@@ -0,0 +1,144 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useContext,
7
+ useId,
8
+ useMemo
9
+ } from "react";
10
+ import { radio as radioRecipe } from "@becket-ui/tokens/recipes";
11
+ import { useOptionalField } from "./Field";
12
+ const RadioGroupContext = createContext(null);
13
+ function mergeClassName(...classes) {
14
+ return classes.filter(Boolean).join(" ");
15
+ }
16
+ function fieldRadioSize(size) {
17
+ if (size == null) return void 0;
18
+ return size === "lg" ? "md" : size;
19
+ }
20
+ const RadioGroup = forwardRef(
21
+ ({
22
+ name: nameProp,
23
+ value,
24
+ defaultValue,
25
+ disabled = false,
26
+ invalid: invalidProp,
27
+ size: sizeProp,
28
+ legend,
29
+ onValueChange,
30
+ className,
31
+ children,
32
+ "aria-describedby": describedByProp,
33
+ ...props
34
+ }, ref) => {
35
+ const autoName = useId();
36
+ const field = useOptionalField();
37
+ const name = nameProp ?? autoName;
38
+ const invalid = invalidProp ?? field?.invalid ?? false;
39
+ const size = sizeProp ?? fieldRadioSize(field?.size) ?? "md";
40
+ const describedBy = describedByProp ?? field?.describedBy;
41
+ const styles = radioRecipe({ size });
42
+ const ctx = useMemo(
43
+ () => ({
44
+ name,
45
+ value,
46
+ defaultValue,
47
+ disabled,
48
+ invalid,
49
+ size,
50
+ onValueChange
51
+ }),
52
+ [defaultValue, disabled, invalid, name, onValueChange, size, value]
53
+ );
54
+ return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
55
+ "fieldset",
56
+ {
57
+ ref,
58
+ ...props,
59
+ disabled,
60
+ "aria-invalid": invalid || void 0,
61
+ "aria-describedby": describedBy,
62
+ className: mergeClassName(styles.group, className),
63
+ children: [
64
+ legend != null ? /* @__PURE__ */ jsx("legend", { className: styles.legend, children: legend }) : null,
65
+ children
66
+ ]
67
+ }
68
+ ) });
69
+ }
70
+ );
71
+ RadioGroup.displayName = "RadioGroup";
72
+ const Radio = forwardRef(
73
+ ({
74
+ id,
75
+ name: nameProp,
76
+ value,
77
+ label,
78
+ description,
79
+ disabled: disabledProp,
80
+ className,
81
+ size: sizeProp,
82
+ checked: checkedProp,
83
+ defaultChecked: defaultCheckedProp,
84
+ onChange,
85
+ ...props
86
+ }, ref) => {
87
+ const autoId = useId();
88
+ const group = useContext(RadioGroupContext);
89
+ const inputId = id ?? autoId;
90
+ const descriptionId = description != null ? `${inputId}-desc` : void 0;
91
+ const name = group?.name ?? nameProp;
92
+ const disabled = Boolean(disabledProp || group?.disabled);
93
+ const invalid = group?.invalid;
94
+ const size = sizeProp ?? group?.size ?? "md";
95
+ const groupControlled = group != null && group.value !== void 0;
96
+ const checked = groupControlled ? group.value === value : checkedProp;
97
+ const defaultChecked = groupControlled ? void 0 : group?.defaultValue != null ? group.defaultValue === value : defaultCheckedProp;
98
+ const styles = radioRecipe({ size });
99
+ return /* @__PURE__ */ jsxs(
100
+ "label",
101
+ {
102
+ className: mergeClassName(styles.root, className),
103
+ htmlFor: inputId,
104
+ "data-disabled": disabled ? "" : void 0,
105
+ children: [
106
+ /* @__PURE__ */ jsx(
107
+ "input",
108
+ {
109
+ ref,
110
+ id: inputId,
111
+ type: "radio",
112
+ name,
113
+ value,
114
+ disabled,
115
+ "aria-invalid": invalid || void 0,
116
+ "aria-describedby": descriptionId,
117
+ ...props,
118
+ ...checked !== void 0 ? { checked } : {},
119
+ ...checked === void 0 && defaultChecked !== void 0 ? { defaultChecked } : {},
120
+ className: styles.control,
121
+ onChange: (event) => {
122
+ onChange?.(event);
123
+ if (event.target.checked) {
124
+ group?.onValueChange?.(event.target.value);
125
+ }
126
+ }
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsx("span", { className: styles.indicator, "aria-hidden": "true" }),
130
+ label != null || description != null ? /* @__PURE__ */ jsxs("span", { className: styles.text, children: [
131
+ label != null ? /* @__PURE__ */ jsx("span", { className: styles.label, children: label }) : null,
132
+ description != null ? /* @__PURE__ */ jsx("span", { id: descriptionId, className: styles.description, children: description }) : null
133
+ ] }) : null
134
+ ]
135
+ }
136
+ );
137
+ }
138
+ );
139
+ Radio.displayName = "Radio";
140
+ export {
141
+ Radio,
142
+ RadioGroup
143
+ };
144
+ //# sourceMappingURL=Radio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Radio.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n forwardRef,\n useContext,\n useId,\n useMemo,\n type ChangeEvent,\n type ComponentPropsWithoutRef,\n type InputHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { radio as radioRecipe } from '@becket-ui/tokens/recipes';\nimport { useOptionalField } from './Field';\n\ntype RadioSize = 'sm' | 'md';\n\ntype RadioGroupContextValue = {\n name: string;\n value?: string;\n defaultValue?: string;\n disabled: boolean;\n invalid: boolean;\n size: RadioSize;\n onValueChange?: (value: string) => void;\n};\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\nfunction mergeClassName(...classes: (string | undefined)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\nfunction fieldRadioSize(size?: 'sm' | 'md' | 'lg'): RadioSize | undefined {\n if (size == null) return undefined;\n return size === 'lg' ? 'md' : size;\n}\n\nexport type RadioGroupProps = Omit<ComponentPropsWithoutRef<'fieldset'>, 'onChange'> & {\n name?: string;\n value?: string;\n defaultValue?: string;\n disabled?: boolean;\n invalid?: boolean;\n size?: RadioSize;\n legend?: ReactNode;\n onValueChange?: (value: string) => void;\n children?: ReactNode;\n};\n\nexport const RadioGroup = forwardRef<HTMLFieldSetElement, RadioGroupProps>(\n (\n {\n name: nameProp,\n value,\n defaultValue,\n disabled = false,\n invalid: invalidProp,\n size: sizeProp,\n legend,\n onValueChange,\n className,\n children,\n 'aria-describedby': describedByProp,\n ...props\n },\n ref,\n ) => {\n const autoName = useId();\n const field = useOptionalField();\n const name = nameProp ?? autoName;\n const invalid = invalidProp ?? field?.invalid ?? false;\n const size = sizeProp ?? fieldRadioSize(field?.size) ?? 'md';\n const describedBy = describedByProp ?? field?.describedBy;\n const styles = radioRecipe({ size });\n\n const ctx = useMemo(\n () => ({\n name,\n value,\n defaultValue,\n disabled,\n invalid,\n size,\n onValueChange,\n }),\n [defaultValue, disabled, invalid, name, onValueChange, size, value],\n );\n\n return (\n <RadioGroupContext.Provider value={ctx}>\n <fieldset\n ref={ref}\n {...props}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n className={mergeClassName(styles.group, className)}\n >\n {legend != null ? <legend className={styles.legend}>{legend}</legend> : null}\n {children}\n </fieldset>\n </RadioGroupContext.Provider>\n );\n },\n);\n\nRadioGroup.displayName = 'RadioGroup';\n\nexport type RadioProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> & {\n value: string;\n label?: ReactNode;\n description?: ReactNode;\n size?: RadioSize;\n};\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(\n (\n {\n id,\n name: nameProp,\n value,\n label,\n description,\n disabled: disabledProp,\n className,\n size: sizeProp,\n checked: checkedProp,\n defaultChecked: defaultCheckedProp,\n onChange,\n ...props\n },\n ref,\n ) => {\n const autoId = useId();\n const group = useContext(RadioGroupContext);\n const inputId = id ?? autoId;\n const descriptionId = description != null ? `${inputId}-desc` : undefined;\n const name = group?.name ?? nameProp;\n const disabled = Boolean(disabledProp || group?.disabled);\n const invalid = group?.invalid;\n const size = sizeProp ?? group?.size ?? 'md';\n const groupControlled = group != null && group.value !== undefined;\n const checked = groupControlled ? group.value === value : checkedProp;\n const defaultChecked = groupControlled\n ? undefined\n : group?.defaultValue != null\n ? group.defaultValue === value\n : defaultCheckedProp;\n const styles = radioRecipe({ size });\n\n return (\n <label\n className={mergeClassName(styles.root, className)}\n htmlFor={inputId}\n data-disabled={disabled ? '' : undefined}\n >\n <input\n ref={ref}\n id={inputId}\n type=\"radio\"\n name={name}\n value={value}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n aria-describedby={descriptionId}\n {...props}\n {...(checked !== undefined ? { checked } : {})}\n {...(checked === undefined && defaultChecked !== undefined ? { defaultChecked } : {})}\n className={styles.control}\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n onChange?.(event);\n if (event.target.checked) {\n group?.onValueChange?.(event.target.value);\n }\n }}\n />\n <span className={styles.indicator} aria-hidden=\"true\" />\n {label != null || description != null ? (\n <span className={styles.text}>\n {label != null ? <span className={styles.label}>{label}</span> : null}\n {description != null ? (\n <span id={descriptionId} className={styles.description}>\n {description}\n </span>\n ) : null}\n </span>\n ) : null}\n </label>\n );\n },\n);\n\nRadio.displayName = 'Radio';\n"],"mappings":"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","names":[]}
@@ -0,0 +1,17 @@
1
+ import { type ReactNode, type SelectHTMLAttributes } from 'react';
2
+ import { type FieldSize } from './Field';
3
+ export type SelectProps = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'> & {
4
+ label?: ReactNode;
5
+ helperText?: ReactNode;
6
+ size?: FieldSize;
7
+ invalid?: boolean;
8
+ fullWidth?: boolean;
9
+ };
10
+ export declare const Select: import("react").ForwardRefExoticComponent<Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"> & {
11
+ label?: ReactNode;
12
+ helperText?: ReactNode;
13
+ size?: FieldSize;
14
+ invalid?: boolean;
15
+ fullWidth?: boolean;
16
+ } & import("react").RefAttributes<HTMLSelectElement>>;
17
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAML,KAAK,SAAS,EACf,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IAChF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,MAAM;YAPT,SAAS;iBACJ,SAAS;WACf,SAAS;cACN,OAAO;gBACL,OAAO;qDAiCpB,CAAC"}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import {
5
+ Field,
6
+ FieldError,
7
+ FieldHelper,
8
+ FieldLabel,
9
+ FieldSelect
10
+ } from "./Field";
11
+ const Select = forwardRef(
12
+ ({
13
+ label,
14
+ helperText,
15
+ size = "md",
16
+ invalid = false,
17
+ fullWidth = false,
18
+ id,
19
+ children,
20
+ ...props
21
+ }, ref) => {
22
+ return /* @__PURE__ */ jsxs(Field, { id, size, invalid, fullWidth, children: [
23
+ label != null ? /* @__PURE__ */ jsx(FieldLabel, { children: label }) : null,
24
+ /* @__PURE__ */ jsx(FieldSelect, { ref, ...props, children }),
25
+ helperText != null ? invalid ? /* @__PURE__ */ jsx(FieldError, { children: helperText }) : /* @__PURE__ */ jsx(FieldHelper, { children: helperText }) : null
26
+ ] });
27
+ }
28
+ );
29
+ Select.displayName = "Select";
30
+ export {
31
+ Select
32
+ };
33
+ //# sourceMappingURL=Select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Select.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ReactNode, type SelectHTMLAttributes } from 'react';\nimport {\n Field,\n FieldError,\n FieldHelper,\n FieldLabel,\n FieldSelect,\n type FieldSize,\n} from './Field';\n\nexport type SelectProps = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'> & {\n label?: ReactNode;\n helperText?: ReactNode;\n size?: FieldSize;\n invalid?: boolean;\n fullWidth?: boolean;\n};\n\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(\n (\n {\n label,\n helperText,\n size = 'md',\n invalid = false,\n fullWidth = false,\n id,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <Field id={id} size={size} invalid={invalid} fullWidth={fullWidth}>\n {label != null ? <FieldLabel>{label}</FieldLabel> : null}\n <FieldSelect ref={ref} {...props}>\n {children}\n </FieldSelect>\n {helperText != null ? (\n invalid ? (\n <FieldError>{helperText}</FieldError>\n ) : (\n <FieldHelper>{helperText}</FieldHelper>\n )\n ) : null}\n </Field>\n );\n },\n);\n\nSelect.displayName = 'Select';\n"],"mappings":";AAmCM,SACmB,KADnB;AAjCN,SAAS,kBAA6D;AACtE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAUA,MAAM,SAAS;AAAA,EACpB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,WACE,qBAAC,SAAM,IAAQ,MAAY,SAAkB,WAC1C;AAAA,eAAS,OAAO,oBAAC,cAAY,iBAAM,IAAgB;AAAA,MACpD,oBAAC,eAAY,KAAW,GAAG,OACxB,UACH;AAAA,MACC,cAAc,OACb,UACE,oBAAC,cAAY,sBAAW,IAExB,oBAAC,eAAa,sBAAW,IAEzB;AAAA,OACN;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
@@ -0,0 +1,14 @@
1
+ import { type ComponentPropsWithoutRef } from 'react';
2
+ type SeparatorOrientation = 'horizontal' | 'vertical';
3
+ export type SeparatorProps = ComponentPropsWithoutRef<'hr'> & {
4
+ orientation?: SeparatorOrientation;
5
+ /** When true, hide from the accessibility tree (decorative). */
6
+ decorative?: boolean;
7
+ };
8
+ export declare const Separator: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHRElement>, HTMLHRElement>, "ref"> & {
9
+ orientation?: SeparatorOrientation;
10
+ /** When true, hide from the accessibility tree (decorative). */
11
+ decorative?: boolean;
12
+ } & import("react").RefAttributes<HTMLHRElement>>;
13
+ export {};
14
+ //# sourceMappingURL=Separator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Separator.d.ts","sourceRoot":"","sources":["../../src/components/Separator.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAGlE,KAAK,oBAAoB,GAAG,YAAY,GAAG,UAAU,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,wBAAwB,CAAC,IAAI,CAAC,GAAG;IAC5D,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAMF,eAAO,MAAM,SAAS;kBATN,oBAAoB;IAClC,gEAAgE;iBACnD,OAAO;iDAmBrB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { separator as separatorRecipe } from "@becket-ui/tokens/recipes";
4
+ function mergeClassName(...classes) {
5
+ return classes.filter(Boolean).join(" ");
6
+ }
7
+ const Separator = forwardRef(
8
+ ({ orientation = "horizontal", decorative = false, className, ...props }, ref) => {
9
+ return /* @__PURE__ */ jsx(
10
+ "hr",
11
+ {
12
+ ref,
13
+ ...props,
14
+ className: mergeClassName(separatorRecipe({ orientation }), className),
15
+ "aria-orientation": orientation,
16
+ "aria-hidden": decorative ? true : void 0
17
+ }
18
+ );
19
+ }
20
+ );
21
+ Separator.displayName = "Separator";
22
+ export {
23
+ Separator
24
+ };
25
+ //# sourceMappingURL=Separator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Separator.tsx"],"sourcesContent":["import { forwardRef, type ComponentPropsWithoutRef } from 'react';\nimport { separator as separatorRecipe } from '@becket-ui/tokens/recipes';\n\ntype SeparatorOrientation = 'horizontal' | 'vertical';\n\nexport type SeparatorProps = ComponentPropsWithoutRef<'hr'> & {\n orientation?: SeparatorOrientation;\n /** When true, hide from the accessibility tree (decorative). */\n decorative?: boolean;\n};\n\nfunction mergeClassName(...classes: (string | undefined)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\nexport const Separator = forwardRef<HTMLHRElement, SeparatorProps>(\n ({ orientation = 'horizontal', decorative = false, className, ...props }, ref) => {\n return (\n <hr\n ref={ref}\n {...props}\n className={mergeClassName(separatorRecipe({ orientation }), className)}\n aria-orientation={orientation}\n aria-hidden={decorative ? true : undefined}\n />\n );\n },\n);\n\nSeparator.displayName = 'Separator';\n"],"mappings":"AAkBM;AAlBN,SAAS,kBAAiD;AAC1D,SAAS,aAAa,uBAAuB;AAU7C,SAAS,kBAAkB,SAAyC;AAClE,SAAO,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;AAEO,MAAM,YAAY;AAAA,EACvB,CAAC,EAAE,cAAc,cAAc,aAAa,OAAO,WAAW,GAAG,MAAM,GAAG,QAAQ;AAChF,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACC,GAAG;AAAA,QACJ,WAAW,eAAe,gBAAgB,EAAE,YAAY,CAAC,GAAG,SAAS;AAAA,QACrE,oBAAkB;AAAA,QAClB,eAAa,aAAa,OAAO;AAAA;AAAA,IACnC;AAAA,EAEJ;AACF;AAEA,UAAU,cAAc;","names":[]}
@@ -0,0 +1,26 @@
1
+ import { type ComponentPropsWithoutRef, type ElementType } from 'react';
2
+ import { grid } from '@becket-ui/tokens/patterns';
3
+ import { type GapValue, type ResponsiveValue } from '../helpers/responsive';
4
+ type GridOptions = NonNullable<Parameters<typeof grid>[0]>;
5
+ export type SimpleGridProps = ComponentPropsWithoutRef<'div'> & {
6
+ columns?: ResponsiveValue<number> | GridOptions['columns'];
7
+ minChildWidth?: GridOptions['minChildWidth'];
8
+ gap?: GapValue;
9
+ rowGap?: GapValue;
10
+ columnGap?: GapValue;
11
+ as?: ElementType;
12
+ };
13
+ /**
14
+ * Responsive CSS grid layout. Pass `columns` (number or breakpoint object)
15
+ * or `minChildWidth` for auto-fit tracks. App-specific presets belong in consumers.
16
+ */
17
+ export declare const SimpleGrid: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
18
+ columns?: ResponsiveValue<number> | GridOptions['columns'];
19
+ minChildWidth?: GridOptions['minChildWidth'];
20
+ gap?: GapValue;
21
+ rowGap?: GapValue;
22
+ columnGap?: GapValue;
23
+ as?: ElementType;
24
+ } & import("react").RefAttributes<HTMLDivElement>>;
25
+ export {};
26
+ //# sourceMappingURL=SimpleGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SimpleGrid.d.ts","sourceRoot":"","sources":["../../src/components/SimpleGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,wBAAwB,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpF,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAGlD,OAAO,EAA+C,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzH,KAAK,WAAW,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AA4D3D,MAAM,MAAM,eAAe,GAAG,wBAAwB,CAAC,KAAK,CAAC,GAAG;IAC9D,OAAO,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAC3D,aAAa,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC7C,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,EAAE,CAAC,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU;cAZX,eAAe,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC;oBAC1C,WAAW,CAAC,eAAe,CAAC;UACtC,QAAQ;aACL,QAAQ;gBACL,QAAQ;SACf,WAAW;kDAmDjB,CAAC"}