@idyeah/diya 0.0.0 → 1.0.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 (190) hide show
  1. package/CHANGELOG.md +317 -0
  2. package/NOTICE +29 -0
  3. package/README.md +596 -5
  4. package/dist/DiyaProvider.d.ts +79 -0
  5. package/dist/DiyaProvider.js +77 -0
  6. package/dist/Icon.d.ts +10 -0
  7. package/dist/Icon.js +27 -0
  8. package/dist/accordion/Accordion.d.ts +34 -0
  9. package/dist/accordion/Accordion.js +15 -0
  10. package/dist/alert/Alert.d.ts +49 -0
  11. package/dist/alert/Alert.js +11 -0
  12. package/dist/alert/messages.d.ts +9 -0
  13. package/dist/alert/messages.js +2 -0
  14. package/dist/api.json +7277 -0
  15. package/dist/api.schema.json +214 -0
  16. package/dist/application-patterns/Chatbot.d.ts +114 -0
  17. package/dist/application-patterns/Chatbot.js +110 -0
  18. package/dist/application-patterns/SideNavigation.d.ts +74 -0
  19. package/dist/application-patterns/SideNavigation.js +31 -0
  20. package/dist/application-patterns/chatbot-messages.d.ts +20 -0
  21. package/dist/application-patterns/chatbot-messages.js +2 -0
  22. package/dist/application-patterns/side-navigation-messages.d.ts +5 -0
  23. package/dist/application-patterns/side-navigation-messages.js +2 -0
  24. package/dist/avatar/Avatar.d.ts +32 -0
  25. package/dist/avatar/Avatar.js +19 -0
  26. package/dist/avatar/messages.d.ts +7 -0
  27. package/dist/avatar/messages.js +2 -0
  28. package/dist/badge/Badge.d.ts +42 -0
  29. package/dist/badge/Badge.js +16 -0
  30. package/dist/badge/badge-utils.d.ts +6 -0
  31. package/dist/badge/badge-utils.js +24 -0
  32. package/dist/badge/messages.d.ts +4 -0
  33. package/dist/badge/messages.js +2 -0
  34. package/dist/breadcrumbs/Breadcrumbs.d.ts +34 -0
  35. package/dist/breadcrumbs/Breadcrumbs.js +19 -0
  36. package/dist/breadcrumbs/messages.d.ts +5 -0
  37. package/dist/breadcrumbs/messages.js +2 -0
  38. package/dist/button/Button.d.ts +46 -0
  39. package/dist/button/Button.js +44 -0
  40. package/dist/calendar/Calendar.d.ts +55 -0
  41. package/dist/calendar/Calendar.js +61 -0
  42. package/dist/calendar/messages.d.ts +8 -0
  43. package/dist/calendar/messages.js +2 -0
  44. package/dist/calendar/month.d.ts +61 -0
  45. package/dist/calendar/month.js +134 -0
  46. package/dist/card/Card.d.ts +46 -0
  47. package/dist/card/Card.js +8 -0
  48. package/dist/choice/Choice.d.ts +72 -0
  49. package/dist/choice/Choice.js +68 -0
  50. package/dist/choice/messages.d.ts +4 -0
  51. package/dist/choice/messages.js +2 -0
  52. package/dist/code-block/CodeBlock.d.ts +20 -0
  53. package/dist/code-block/CodeBlock.js +49 -0
  54. package/dist/code-block/messages.d.ts +8 -0
  55. package/dist/code-block/messages.js +2 -0
  56. package/dist/combobox/Combobox.d.ts +60 -0
  57. package/dist/combobox/Combobox.js +160 -0
  58. package/dist/combobox/messages.d.ts +9 -0
  59. package/dist/combobox/messages.js +2 -0
  60. package/dist/combobox/useAnchoredPopover.d.ts +5 -0
  61. package/dist/combobox/useAnchoredPopover.js +100 -0
  62. package/dist/data-viz/DataViz.d.ts +140 -0
  63. package/dist/data-viz/DataViz.js +469 -0
  64. package/dist/data-viz/index.d.ts +2 -0
  65. package/dist/data-viz/index.js +1 -0
  66. package/dist/data-viz/messages.d.ts +11 -0
  67. package/dist/data-viz/messages.js +2 -0
  68. package/dist/date-picker/DatePicker.d.ts +57 -0
  69. package/dist/date-picker/DatePicker.js +146 -0
  70. package/dist/date-picker/messages.d.ts +13 -0
  71. package/dist/date-picker/messages.js +2 -0
  72. package/dist/dialog/Dialog.d.ts +84 -0
  73. package/dist/dialog/Dialog.js +107 -0
  74. package/dist/dialog/messages.d.ts +9 -0
  75. package/dist/dialog/messages.js +2 -0
  76. package/dist/fonts/DIYASans-Italic.woff2 +0 -0
  77. package/dist/fonts/DIYASans.woff2 +0 -0
  78. package/dist/fonts/OFL.txt +94 -0
  79. package/dist/index.d.ts +50 -0
  80. package/dist/index.js +46 -0
  81. package/dist/internal/brand.d.ts +33 -0
  82. package/dist/internal/brand.js +57 -0
  83. package/dist/internal/cx.d.ts +2 -0
  84. package/dist/internal/cx.js +4 -0
  85. package/dist/internal/link.d.ts +14 -0
  86. package/dist/internal/link.js +7 -0
  87. package/dist/internal/slot.d.ts +15 -0
  88. package/dist/internal/slot.js +67 -0
  89. package/dist/layout/Inline.d.ts +4 -0
  90. package/dist/layout/Inline.js +7 -0
  91. package/dist/layout/Stack.d.ts +4 -0
  92. package/dist/layout/Stack.js +7 -0
  93. package/dist/layout/layout.d.ts +32 -0
  94. package/dist/layout/layout.js +12 -0
  95. package/dist/link/TextLink.d.ts +23 -0
  96. package/dist/link/TextLink.js +17 -0
  97. package/dist/list/List.d.ts +48 -0
  98. package/dist/list/List.js +17 -0
  99. package/dist/list/messages.d.ts +4 -0
  100. package/dist/list/messages.js +2 -0
  101. package/dist/menu/Menu.d.ts +49 -0
  102. package/dist/menu/Menu.js +54 -0
  103. package/dist/messages-defaults.d.ts +201 -0
  104. package/dist/messages-defaults.js +61 -0
  105. package/dist/messages.d.ts +12 -0
  106. package/dist/messages.js +1 -0
  107. package/dist/metric/Metric.d.ts +42 -0
  108. package/dist/metric/Metric.js +11 -0
  109. package/dist/metric/messages.d.ts +9 -0
  110. package/dist/metric/messages.js +2 -0
  111. package/dist/overlay/FloatingSurface.d.ts +29 -0
  112. package/dist/overlay/FloatingSurface.js +254 -0
  113. package/dist/page-header/PageHeader.d.ts +38 -0
  114. package/dist/page-header/PageHeader.js +10 -0
  115. package/dist/pagination/Pagination.d.ts +44 -0
  116. package/dist/pagination/Pagination.js +26 -0
  117. package/dist/pagination/messages.d.ts +11 -0
  118. package/dist/pagination/messages.js +2 -0
  119. package/dist/popover/Popover.d.ts +23 -0
  120. package/dist/popover/Popover.js +8 -0
  121. package/dist/progress/Progress.d.ts +33 -0
  122. package/dist/progress/Progress.js +14 -0
  123. package/dist/progress/messages.d.ts +4 -0
  124. package/dist/progress/messages.js +2 -0
  125. package/dist/prose/Prose.d.ts +14 -0
  126. package/dist/prose/Prose.js +7 -0
  127. package/dist/rating/Rating.d.ts +32 -0
  128. package/dist/rating/Rating.js +27 -0
  129. package/dist/rating/messages.d.ts +6 -0
  130. package/dist/rating/messages.js +2 -0
  131. package/dist/search/Search.d.ts +11 -0
  132. package/dist/search/Search.js +9 -0
  133. package/dist/select/Select.d.ts +65 -0
  134. package/dist/select/Select.js +19 -0
  135. package/dist/select/messages.d.ts +4 -0
  136. package/dist/select/messages.js +2 -0
  137. package/dist/site-header/SiteHeader.d.ts +88 -0
  138. package/dist/site-header/SiteHeader.js +211 -0
  139. package/dist/site-header/messages.d.ts +8 -0
  140. package/dist/site-header/messages.js +2 -0
  141. package/dist/slider/Slider.d.ts +54 -0
  142. package/dist/slider/Slider.js +89 -0
  143. package/dist/slider/messages.d.ts +5 -0
  144. package/dist/slider/messages.js +2 -0
  145. package/dist/styles.css +3379 -0
  146. package/dist/switch/Switch.d.ts +47 -0
  147. package/dist/switch/Switch.js +18 -0
  148. package/dist/table/Table.d.ts +99 -0
  149. package/dist/table/Table.js +158 -0
  150. package/dist/table/messages.d.ts +23 -0
  151. package/dist/table/messages.js +2 -0
  152. package/dist/tabs/Tabs.d.ts +49 -0
  153. package/dist/tabs/Tabs.js +26 -0
  154. package/dist/tag/Tag.d.ts +37 -0
  155. package/dist/tag/Tag.js +20 -0
  156. package/dist/tag/messages.d.ts +4 -0
  157. package/dist/tag/messages.js +2 -0
  158. package/dist/text-area/TextArea.d.ts +47 -0
  159. package/dist/text-area/TextArea.js +19 -0
  160. package/dist/text-input/TextInput.d.ts +53 -0
  161. package/dist/text-input/TextInput.js +35 -0
  162. package/dist/text-input/messages.d.ts +4 -0
  163. package/dist/text-input/messages.js +3 -0
  164. package/dist/text-input/native-field.d.ts +15 -0
  165. package/dist/text-input/native-field.js +56 -0
  166. package/dist/toast/Toast.d.ts +24 -0
  167. package/dist/toast/Toast.js +79 -0
  168. package/dist/toast/messages.d.ts +4 -0
  169. package/dist/toast/messages.js +2 -0
  170. package/dist/tooltip/Tooltip.d.ts +22 -0
  171. package/dist/tooltip/Tooltip.js +92 -0
  172. package/dist/workflow/ContentState.d.ts +44 -0
  173. package/dist/workflow/ContentState.js +11 -0
  174. package/dist/workflow/Drawer.d.ts +48 -0
  175. package/dist/workflow/Drawer.js +51 -0
  176. package/dist/workflow/FileUpload.d.ts +67 -0
  177. package/dist/workflow/FileUpload.js +47 -0
  178. package/dist/workflow/Form.d.ts +52 -0
  179. package/dist/workflow/Form.js +16 -0
  180. package/dist/workflow/Wizard.d.ts +76 -0
  181. package/dist/workflow/Wizard.js +31 -0
  182. package/dist/workflow/drawer-messages.d.ts +4 -0
  183. package/dist/workflow/drawer-messages.js +2 -0
  184. package/dist/workflow/file-upload-messages.d.ts +11 -0
  185. package/dist/workflow/file-upload-messages.js +2 -0
  186. package/dist/workflow/form-messages.d.ts +5 -0
  187. package/dist/workflow/form-messages.js +2 -0
  188. package/dist/workflow/wizard-messages.d.ts +9 -0
  189. package/dist/workflow/wizard-messages.js +2 -0
  190. package/package.json +82 -7
@@ -0,0 +1,57 @@
1
+ // The brand color, worked out once per provider into the four accent tokens.
2
+ /** DIYA's own accent when the provider sets no `brandColor`: a true neutral ink in Light, its pale twin in Dark. */
3
+ export const DIYA_DEFAULT_BRAND = { light: '#262626', dark: '#e8e8e8' };
4
+ // DIYA's default surface, raised and text per theme, kept equal to styles.css by a test.
5
+ export const BRAND_GROUNDS = {
6
+ light: { surface: '#ffffff', raised: '#ffffff', text: '#202627' },
7
+ dark: { surface: '#111113', raised: '#18181b', text: '#f1f3f2' },
8
+ };
9
+ // Text on the fill: the first of these that reaches 4.5:1. White wins whenever it can.
10
+ const TEXT_ON_FILL = ['#fafafa', '#ffffff', '#1c1c1c', '#000000'];
11
+ // The same formula as Customize's theme export (apps/diya lib/customize/tokens.ts), so both give the same pixels.
12
+ // The alphas the soft tint renders at (#…1f and #…33), so contrast is checked on the pixels people see.
13
+ const SOFT_ALPHA = { light: 31 / 255, dark: 51 / 255 };
14
+ const AA = 4.5;
15
+ /** `#rgb` or `#rrggbb`, with or without the hash, as lowercase `#rrggbb`; anything else is undefined. */
16
+ export function normalizeHex(value) {
17
+ const hex = value.trim().replace(/^#/, '');
18
+ if (/^[\da-f]{3}$/i.test(hex))
19
+ return `#${[...hex].map(x => x + x).join('').toLowerCase()}`;
20
+ return /^[\da-f]{6}$/i.test(hex) ? `#${hex.toLowerCase()}` : undefined;
21
+ }
22
+ const rgb = (hex) => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
23
+ const toHex = (color) => `#${color.map(c => Math.round(Math.min(255, Math.max(0, c))).toString(16).padStart(2, '0')).join('')}`;
24
+ const linear = (c) => { const v = c / 255; return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; };
25
+ const luminance = ([r, g, b]) => 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
26
+ /** WCAG 2 contrast ratio between two opaque hex colors. */
27
+ export function contrast(a, b) {
28
+ const [x, y] = [luminance(rgb(a)), luminance(rgb(b))];
29
+ return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05);
30
+ }
31
+ const over = (top, alpha, ground) => top.map((c, i) => c * alpha + ground[i] * (1 - alpha));
32
+ /** The four accent tokens for one theme, from a hex brand color; contrast is checked against DIYA's default grounds. */
33
+ export function brandTokens(color, theme) {
34
+ const accent = normalizeHex(color) ?? DIYA_DEFAULT_BRAND[theme];
35
+ // One of pure white or black always reaches 4.5:1, so the search never falls through.
36
+ const accentText = TEXT_ON_FILL.find(text => contrast(accent, text) >= AA) ?? '#000000';
37
+ const alpha = SOFT_ALPHA[theme];
38
+ const accentSoft = `${accent}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`;
39
+ // Ink reads on the surface, raised surfaces and the soft tint over each; it moves toward the text color only as far as that needs.
40
+ const { surface, raised, text } = BRAND_GROUNDS[theme];
41
+ const grounds = [surface, raised].flatMap(ground => [ground, toHex(over(rgb(accent), alpha, rgb(ground)))]);
42
+ const passes = (candidate) => grounds.every(ground => contrast(candidate, ground) >= AA);
43
+ let accentInk = text;
44
+ for (let step = 0; step <= 40; step++) {
45
+ const candidate = toHex(over(rgb(text), step / 40, rgb(accent)));
46
+ if (passes(candidate)) {
47
+ accentInk = candidate;
48
+ break;
49
+ }
50
+ }
51
+ return { accent, accentText, accentSoft, accentInk };
52
+ }
53
+ /** A provider's `brandColor`, one color or one per theme, as the accent tokens for both themes. */
54
+ export function resolveBrand(brandColor) {
55
+ const source = brandColor === undefined ? DIYA_DEFAULT_BRAND : typeof brandColor === 'string' ? { light: brandColor, dark: brandColor } : brandColor;
56
+ return { light: brandTokens(source.light, 'light'), dark: brandTokens(source.dark, 'dark') };
57
+ }
@@ -0,0 +1,2 @@
1
+ /** Joins class names, skipping empty ones, so a caller's className never leaves stray spaces. */
2
+ export declare function cx(...names: Array<string | false | null | undefined>): string;
@@ -0,0 +1,4 @@
1
+ /** Joins class names, skipping empty ones, so a caller's className never leaves stray spaces. */
2
+ export function cx(...names) {
3
+ return names.filter(Boolean).join(' ');
4
+ }
@@ -0,0 +1,14 @@
1
+ import { type AnchorHTMLAttributes, type ComponentType, type ExoticComponent, type Ref } from 'react';
2
+ /** The props DIYA passes to a router link. */
3
+ export type DiyaLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
4
+ href: string;
5
+ ref?: Ref<HTMLAnchorElement>;
6
+ };
7
+ /**
8
+ * A router link that takes anchor props, such as Next.js `Link` or a React Router `Link` adapter.
9
+ * Exotic components (forwardRef, memo) count too: React 18's types keep them apart from ComponentType.
10
+ */
11
+ export type DiyaLinkComponent = ComponentType<DiyaLinkProps> | ExoticComponent<DiyaLinkProps>;
12
+ export declare const LinkComponentContext: import("react").Context<DiyaLinkComponent | "a">;
13
+ /** The link element DIYA renders for items with an `href`: the provider's `linkComponent`, or a plain anchor. */
14
+ export declare function useDiyaLinkComponent(): DiyaLinkComponent | "a";
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import { createContext, useContext } from 'react';
3
+ export const LinkComponentContext = createContext('a');
4
+ /** The link element DIYA renders for items with an `href`: the provider's `linkComponent`, or a plain anchor. */
5
+ export function useDiyaLinkComponent() {
6
+ return useContext(LinkComponentContext);
7
+ }
@@ -0,0 +1,15 @@
1
+ import { type ReactElement, type ReactNode, type Ref } from 'react';
2
+ type AnyProps = Record<string, unknown>;
3
+ /** The caller's single child element, unwrapped first if it arrived lazily from a Server Component. */
4
+ export declare function onlyChild(children: ReactNode): ReactNode;
5
+ /** Points every ref at the same node. */
6
+ export declare function composeRefs<T>(...refs: Array<Ref<T> | undefined>): (node: T | null) => void;
7
+ /** Merges DIYA's props into the caller's own element: classes join, styles merge, handlers both run (the child's first, and it can call preventDefault to stop DIYA's). */
8
+ export declare function mergeSlotProps(slot: AnyProps, child: AnyProps): AnyProps;
9
+ /**
10
+ * Renders the caller's single child element with DIYA's props merged in; powers `asChild`.
11
+ * `<Button asChild><Link href="/billing">Billing</Link></Button>` renders the Link, styled as a Button.
12
+ * A function, not a component, so a forwarded ref reaches the child on React 18 too.
13
+ */
14
+ export declare function renderSlot(children: ReactNode, slotProps: AnyProps, ref?: Ref<unknown>): ReactElement | null;
15
+ export {};
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { Children, cloneElement, isValidElement, version } from 'react';
3
+ import { cx } from './cx.js';
4
+ // In development, a Server Component hands elements to a client component as
5
+ // lazy references, which Children.only rejects; React 19's `use` unwraps them.
6
+ // The name is looked up at run time so React 18 builds don't expect the export.
7
+ const use = React[' use '.trim()];
8
+ const lazyType = Symbol.for('react.lazy');
9
+ /** The caller's single child element, unwrapped first if it arrived lazily from a Server Component. */
10
+ export function onlyChild(children) {
11
+ const node = children;
12
+ if (use && node && typeof node === 'object' && node.$$typeof === lazyType && typeof node._payload?.then === 'function')
13
+ return Children.only(use(node._payload));
14
+ return Children.only(children);
15
+ }
16
+ /** Points every ref at the same node. */
17
+ export function composeRefs(...refs) {
18
+ return (node) => {
19
+ for (const ref of refs) {
20
+ if (typeof ref === 'function')
21
+ ref(node);
22
+ else if (ref)
23
+ ref.current = node;
24
+ }
25
+ };
26
+ }
27
+ // React 19 keeps an element's ref in props; React 18 keeps it on the element.
28
+ const refInProps = Number(version.split('.')[0]) >= 19;
29
+ function childRef(child) {
30
+ return refInProps ? child.props.ref : child.ref;
31
+ }
32
+ /** Merges DIYA's props into the caller's own element: classes join, styles merge, handlers both run (the child's first, and it can call preventDefault to stop DIYA's). */
33
+ export function mergeSlotProps(slot, child) {
34
+ const merged = { ...slot, ...child };
35
+ for (const key of Object.keys(slot)) {
36
+ const ours = slot[key];
37
+ const theirs = child[key];
38
+ if (/^on[A-Z]/.test(key) && typeof ours === 'function' && typeof theirs === 'function') {
39
+ merged[key] = (event, ...rest) => {
40
+ ;
41
+ theirs(event, ...rest);
42
+ if (!event?.defaultPrevented)
43
+ ours(event, ...rest);
44
+ };
45
+ }
46
+ }
47
+ merged.className = cx(slot.className, child.className) || undefined;
48
+ if (slot.style || child.style)
49
+ merged.style = { ...slot.style, ...child.style };
50
+ return merged;
51
+ }
52
+ /**
53
+ * Renders the caller's single child element with DIYA's props merged in; powers `asChild`.
54
+ * `<Button asChild><Link href="/billing">Billing</Link></Button>` renders the Link, styled as a Button.
55
+ * A function, not a component, so a forwarded ref reaches the child on React 18 too.
56
+ */
57
+ export function renderSlot(children, slotProps, ref) {
58
+ const child = onlyChild(children);
59
+ if (!isValidElement(child))
60
+ return null;
61
+ const merged = mergeSlotProps(slotProps, child.props);
62
+ const theirRef = childRef(child);
63
+ const combined = ref && theirRef ? composeRefs(ref, theirRef) : (ref ?? theirRef);
64
+ if (combined)
65
+ merged.ref = combined;
66
+ return cloneElement(child, merged);
67
+ }
@@ -0,0 +1,4 @@
1
+ import { type LayoutProps } from './layout.js';
2
+ export type InlineProps = LayoutProps;
3
+ /** Lays children out side by side, centered on one line, wrapping onto the next when they run out of room. */
4
+ export declare const Inline: import("react").ForwardRefExoticComponent<LayoutProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,7 @@
1
+ import { createElement, forwardRef } from 'react';
2
+ import { cx } from '../internal/cx.js';
3
+ import { layoutAttributes } from './layout.js';
4
+ /** Lays children out side by side, centered on one line, wrapping onto the next when they run out of room. */
5
+ export const Inline = forwardRef(function Inline({ as = 'div', gap = 2, align = 'center', justify, wrap = true, className, style, children, ...rest }, ref) {
6
+ return createElement(as, { ...rest, ref, className: cx('diya-inline', className), ...layoutAttributes(gap, align, justify, wrap, style) }, children);
7
+ });
@@ -0,0 +1,4 @@
1
+ import { type LayoutProps } from './layout.js';
2
+ export type StackProps = LayoutProps;
3
+ /** Lays children out top to bottom with even space between them. */
4
+ export declare const Stack: import("react").ForwardRefExoticComponent<LayoutProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,7 @@
1
+ import { createElement, forwardRef } from 'react';
2
+ import { cx } from '../internal/cx.js';
3
+ import { layoutAttributes } from './layout.js';
4
+ /** Lays children out top to bottom with even space between them. */
5
+ export const Stack = forwardRef(function Stack({ as = 'div', gap = 4, align, justify, wrap = false, className, style, children, ...rest }, ref) {
6
+ return createElement(as, { ...rest, ref, className: cx('diya-stack', className), ...layoutAttributes(gap, align, justify, wrap, style) }, children);
7
+ });
@@ -0,0 +1,32 @@
1
+ import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
2
+ /** A step on DIYA's spacing scale (0 is none; 1 is 4px, 2 is 8px, 3 is 12px, 4 is 16px, 5 is 24px, 6 is 32px, 7 is 48px, 8 is 64px), or any CSS length such as '10px' or '1.5rem'. */
3
+ export type DiyaSpace = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | (string & {});
4
+ /** Cross-axis alignment of the children. */
5
+ export type DiyaAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
6
+ /** Main-axis distribution of the children. */
7
+ export type DiyaJustify = 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
8
+ /** Elements a layout primitive can render as. */
9
+ export type DiyaLayoutElement = 'div' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'main' | 'nav' | 'form' | 'fieldset' | 'ul' | 'ol' | 'li' | 'span';
10
+ /** Props shared by Stack and Inline. An interface, so declaration files keep these docs. */
11
+ export interface LayoutProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
12
+ /** What to lay out. */
13
+ children?: ReactNode;
14
+ /** Space between children: a scale step from 0 to 8, or a CSS length. */
15
+ gap?: DiyaSpace;
16
+ /** Cross-axis alignment of the children. */
17
+ align?: DiyaAlign;
18
+ /** Main-axis distribution of the children. */
19
+ justify?: DiyaJustify;
20
+ /** Whether children move onto a new line when they run out of room. */
21
+ wrap?: boolean;
22
+ /** The element to render, such as 'section' or 'ul'. Defaults to 'div'. */
23
+ as?: DiyaLayoutElement;
24
+ }
25
+ /** Data attributes and the custom gap for a Stack or Inline root. */
26
+ export declare function layoutAttributes(gap: DiyaSpace, align: DiyaAlign | undefined, justify: DiyaJustify | undefined, wrap: boolean, style: CSSProperties | undefined): {
27
+ 'data-gap': string;
28
+ 'data-align': DiyaAlign | undefined;
29
+ 'data-justify': DiyaJustify | undefined;
30
+ 'data-wrap': string | undefined;
31
+ style: CSSProperties | undefined;
32
+ };
@@ -0,0 +1,12 @@
1
+ const STEPS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
2
+ /** Data attributes and the custom gap for a Stack or Inline root. */
3
+ export function layoutAttributes(gap, align, justify, wrap, style) {
4
+ const step = STEPS.has(String(gap));
5
+ return {
6
+ 'data-gap': step ? String(gap) : 'custom',
7
+ 'data-align': align,
8
+ 'data-justify': justify,
9
+ 'data-wrap': wrap ? 'true' : undefined,
10
+ style: step ? style : { '--diya-internal-gap': String(gap), ...style },
11
+ };
12
+ }
@@ -0,0 +1,23 @@
1
+ import { type AnchorHTMLAttributes, type ReactNode } from 'react';
2
+ /** Native anchor attributes (href, target, rel…) pass through to the link. */
3
+ export type TextLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
4
+ /** Decorative icon before the text; hidden from assistive tech. */
5
+ leadingIcon?: ReactNode;
6
+ /** Decorative icon after the text; hidden from assistive tech. */
7
+ trailingIcon?: ReactNode;
8
+ /** Flows inside a sentence and inherits its type size; off, it is a standalone body-size link. */
9
+ inline?: boolean;
10
+ /** Styles your single child element as the link instead of rendering an anchor, such as a router `Link`; the icons go inside it. */
11
+ asChild?: boolean;
12
+ };
13
+ /** A styled anchor; its children are the visible link text. Needs no provider, so it renders in server components. */
14
+ export declare const TextLink: import("react").ForwardRefExoticComponent<AnchorHTMLAttributes<HTMLAnchorElement> & {
15
+ /** Decorative icon before the text; hidden from assistive tech. */
16
+ leadingIcon?: ReactNode;
17
+ /** Decorative icon after the text; hidden from assistive tech. */
18
+ trailingIcon?: ReactNode;
19
+ /** Flows inside a sentence and inherits its type size; off, it is a standalone body-size link. */
20
+ inline?: boolean;
21
+ /** Styles your single child element as the link instead of rendering an anchor, such as a router `Link`; the icons go inside it. */
22
+ asChild?: boolean;
23
+ } & import("react").RefAttributes<HTMLAnchorElement>>;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cloneElement, forwardRef, isValidElement } from 'react';
3
+ import { cx } from '../internal/cx.js';
4
+ import { onlyChild, renderSlot } from '../internal/slot.js';
5
+ /** A styled anchor; its children are the visible link text. Needs no provider, so it renders in server components. */
6
+ export const TextLink = forwardRef(function TextLink({ children, leadingIcon, trailingIcon, inline = false, asChild = false, className, ...props }, ref) {
7
+ const icons = (inner) => _jsxs(_Fragment, { children: [leadingIcon && _jsx("span", { "aria-hidden": "true", children: leadingIcon }), inner, trailingIcon && _jsx("span", { "aria-hidden": "true", children: trailingIcon })] });
8
+ if (asChild) {
9
+ const child = onlyChild(children);
10
+ if (!isValidElement(child))
11
+ return null;
12
+ const own = child;
13
+ const filled = leadingIcon || trailingIcon ? cloneElement(own, undefined, icons(own.props.children)) : own;
14
+ return renderSlot(filled, { ...props, className: cx('diya-text-link', className), 'data-inline': inline }, ref);
15
+ }
16
+ return _jsx("a", { ...props, ref: ref, className: cx('diya-text-link', className), "data-inline": inline, children: icons(children) });
17
+ });
@@ -0,0 +1,48 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ import { listMessages } from './messages.js';
3
+ declare module '../messages.js' {
4
+ interface DiyaMessages {
5
+ list: typeof listMessages;
6
+ }
7
+ }
8
+ export type ListItem = {
9
+ /** Unique key for the row. */
10
+ id: string;
11
+ /** Visible main text. */
12
+ label: string;
13
+ /** Visible secondary text under the label. */
14
+ description?: string;
15
+ /** Decorative content before the text, such as an icon or Avatar; hidden from screen readers. */
16
+ leading?: ReactNode;
17
+ /** Visible content at the end of the row, such as a Badge or a value; must be noninteractive. */
18
+ trailing?: ReactNode;
19
+ /** Makes the whole row a button that calls this. */
20
+ onSelect?: () => void;
21
+ /** Disables a selectable row. */
22
+ disabled?: boolean;
23
+ /** Marks the open item, such as the message being read: a selected background and `aria-current="true"`. Defaults to false. */
24
+ current?: boolean;
25
+ /** Marks the item as not yet seen: a heavier label and a dot at the end of the row, and "unread" after the label for screen readers. Defaults to false. */
26
+ unread?: boolean;
27
+ };
28
+ export type ListProps = Omit<HTMLAttributes<HTMLUListElement>, 'children'> & {
29
+ /** The rows, in order. */
30
+ items: ListItem[];
31
+ /** Accessible name for the list; not shown. */
32
+ label: string;
33
+ /** Draws lines between rows. */
34
+ divided?: boolean;
35
+ /** Row height and text size. Defaults to 'medium'. */
36
+ size?: 'small' | 'medium' | 'large';
37
+ };
38
+ /** A vertical list of rows. Selectable rows use a native button; leading/trailing slots must be noninteractive. */
39
+ export declare const List: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLUListElement>, "children"> & {
40
+ /** The rows, in order. */
41
+ items: ListItem[];
42
+ /** Accessible name for the list; not shown. */
43
+ label: string;
44
+ /** Draws lines between rows. */
45
+ divided?: boolean;
46
+ /** Row height and text size. Defaults to 'medium'. */
47
+ size?: "small" | "medium" | "large";
48
+ } & import("react").RefAttributes<HTMLUListElement>>;
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { useDiyaMessages } from '../DiyaProvider.js';
5
+ import { cx } from '../internal/cx.js';
6
+ import { listMessages } from './messages.js';
7
+ /** A vertical list of rows. Selectable rows use a native button; leading/trailing slots must be noninteractive. */
8
+ export const List = forwardRef(function List({ items, label, divided = false, size = 'medium', className, ...props }, ref) {
9
+ const t = useDiyaMessages('list', listMessages);
10
+ return _jsx("ul", { ...props, ref: ref, className: cx('diya-list', className), "aria-label": label, "data-divided": divided, "data-size": size, children: items.map((item) => {
11
+ // The open item is current on the row people reach: its button, or the list item itself.
12
+ const current = item.current ? 'true' : undefined;
13
+ const unread = item.unread ? true : undefined;
14
+ const content = _jsxs(_Fragment, { children: [item.leading && _jsx("span", { className: "diya-list-leading", "aria-hidden": "true", children: item.leading }), _jsxs("span", { className: "diya-list-copy", children: [_jsxs("span", { className: "diya-list-label", children: [item.label, unread && _jsxs("span", { className: "diya-sr-only", children: [", ", t.unread] })] }), item.description && _jsx("span", { className: "diya-list-description", children: item.description })] }), item.trailing && _jsx("span", { className: "diya-list-trailing", children: item.trailing }), unread && _jsx("span", { className: "diya-list-unread", "aria-hidden": "true" })] });
15
+ return _jsx("li", { "aria-current": item.onSelect ? undefined : current, children: item.onSelect ? _jsx("button", { type: "button", className: "diya-list-row", "aria-current": current, "data-unread": unread, disabled: item.disabled, onClick: item.onSelect, children: content }) : _jsx("div", { className: "diya-list-row", "data-unread": unread, children: content }) }, item.id);
16
+ }) });
17
+ });
@@ -0,0 +1,4 @@
1
+ /** list: English defaults, overridable through DiyaProvider `messages`. */
2
+ export declare const listMessages: {
3
+ unread: string;
4
+ };
@@ -0,0 +1,2 @@
1
+ /** list: English defaults, overridable through DiyaProvider `messages`. */
2
+ export const listMessages = { unread: 'unread' };
@@ -0,0 +1,49 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type FloatingTriggerProps } from '../overlay/FloatingSurface.js';
3
+ export type MenuItem = {
4
+ /** 'action' runs and closes; 'checkbox' toggles and keeps the menu open; 'radio' picks one of a set and closes. Defaults to 'action'. */
5
+ type?: 'action' | 'checkbox' | 'radio';
6
+ /** Whether a checkbox or radio item is on; you own this state. */
7
+ checked?: boolean;
8
+ /** Unique key for the item. */
9
+ id: string;
10
+ /** Visible item text; typing its first letter moves focus to it. */
11
+ label: string;
12
+ /** Decorative icon before the label; hidden from screen readers. */
13
+ icon?: ReactNode;
14
+ /** Visible keyboard hint, such as '⌘K'; it does not bind the key and is hidden from screen readers. */
15
+ shortcut?: string;
16
+ /** Shows the item but makes it unselectable. */
17
+ disabled?: boolean;
18
+ /** Styles the item as a destructive action, such as Delete. */
19
+ destructive?: boolean;
20
+ /** Draws a separator line above this item. */
21
+ separatorBefore?: boolean;
22
+ /** Makes the item a real link to this URL, through the provider's `linkComponent`; a plain click calls `onSelect` instead of navigating when both are given. */
23
+ href?: string;
24
+ /** Marks a link item as the page you're on (`aria-current="page"`). */
25
+ current?: boolean;
26
+ /** Called when the item is chosen; optional for link items. */
27
+ onSelect?: () => void;
28
+ };
29
+ export type MenuProps = FloatingTriggerProps & {
30
+ /** The trigger button's visible text, or only its accessible name when `triggerIcon` is set; also names the menu. */
31
+ label: string;
32
+ /** Shows this icon as an icon-only trigger instead of the label text. */
33
+ triggerIcon?: ReactNode;
34
+ /** The menu's items, in order. */
35
+ items: MenuItem[];
36
+ /** Disables the trigger. */
37
+ disabled?: boolean;
38
+ };
39
+ /** A button that opens a list of actions, links, checkboxes or radio choices, with arrow-key and typeahead navigation; `className`, `style`, other attributes and the ref go to the trigger. */
40
+ export declare const Menu: import("react").ForwardRefExoticComponent<FloatingTriggerProps & {
41
+ /** The trigger button's visible text, or only its accessible name when `triggerIcon` is set; also names the menu. */
42
+ label: string;
43
+ /** Shows this icon as an icon-only trigger instead of the label text. */
44
+ triggerIcon?: ReactNode;
45
+ /** The menu's items, in order. */
46
+ items: MenuItem[];
47
+ /** Disables the trigger. */
48
+ disabled?: boolean;
49
+ } & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { FloatingSurface } from '../overlay/FloatingSurface.js';
5
+ import { useDiyaLinkComponent } from '../internal/link.js';
6
+ /** A button that opens a list of actions, links, checkboxes or radio choices, with arrow-key and typeahead navigation; `className`, `style`, other attributes and the ref go to the trigger. */
7
+ export const Menu = forwardRef(function Menu({ label, items, disabled = false, triggerIcon, ...props }, ref) {
8
+ const Link = useDiyaLinkComponent();
9
+ function navigate(event) {
10
+ const entries = Array.from(event.currentTarget.querySelectorAll('[role^="menuitem"]:not(:disabled)'));
11
+ if (!entries.length)
12
+ return;
13
+ const current = entries.indexOf(document.activeElement);
14
+ let next = -1;
15
+ if (event.key === 'ArrowDown')
16
+ next = (current + 1) % entries.length;
17
+ else if (event.key === 'ArrowUp')
18
+ next = current < 0 ? entries.length - 1 : (current - 1 + entries.length) % entries.length;
19
+ else if (event.key === 'Home')
20
+ next = 0;
21
+ else if (event.key === 'End')
22
+ next = entries.length - 1;
23
+ else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && event.key !== ' ') {
24
+ const ordered = [...entries.slice(current + 1), ...entries.slice(0, current + 1)];
25
+ const match = ordered.find((entry) => entry.dataset.label?.toLowerCase().startsWith(event.key.toLowerCase()));
26
+ if (match) {
27
+ event.preventDefault();
28
+ match.focus();
29
+ }
30
+ ;
31
+ return;
32
+ }
33
+ else
34
+ return;
35
+ event.preventDefault();
36
+ entries[next]?.focus();
37
+ }
38
+ return _jsx(FloatingSurface, { ...props, ref: ref, triggerIcon: triggerIcon, label: label, role: "menu", disabled: disabled, onPanelKeyDown: navigate, children: (close) => items.map((item) => _jsxs("div", { role: "none", children: [item.separatorBefore && _jsx("div", { role: "separator", className: "diya-menu-separator" }), item.href && !item.disabled && item.type !== 'checkbox' && item.type !== 'radio'
39
+ ? _jsx(Link, { href: item.href, role: "menuitem", tabIndex: -1, "data-label": item.label, className: "diya-menu-item", "data-destructive": item.destructive, "aria-current": item.current ? 'page' : undefined, onKeyDown: (event) => { if (event.key === ' ') {
40
+ event.preventDefault();
41
+ event.currentTarget.click();
42
+ } }, onClick: (event) => {
43
+ if (item.onSelect && !event.defaultPrevented && !event.button && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) {
44
+ event.preventDefault();
45
+ item.onSelect();
46
+ }
47
+ close();
48
+ }, children: content(item) })
49
+ : _jsx("button", { type: "button", role: item.type === 'checkbox' ? 'menuitemcheckbox' : item.type === 'radio' ? 'menuitemradio' : 'menuitem', "aria-checked": item.type === 'checkbox' || item.type === 'radio' ? !!item.checked : undefined, tabIndex: -1, "data-label": item.label, className: "diya-menu-item", "data-destructive": item.destructive, disabled: item.disabled, onClick: () => { item.onSelect?.(); if (item.type !== 'checkbox')
50
+ close(); }, children: content(item) })] }, item.id)) });
51
+ });
52
+ function content(item) {
53
+ return _jsxs(_Fragment, { children: [(item.type === 'checkbox' || item.type === 'radio') && _jsx("span", { className: "diya-menu-icon", "aria-hidden": "true", children: item.checked ? (item.type === 'radio' ? '●' : '✓') : '' }), item.icon && _jsx("span", { className: "diya-menu-icon", "aria-hidden": "true", children: item.icon }), _jsx("span", { children: item.label }), item.shortcut && _jsx("span", { className: "diya-menu-shortcut", "aria-hidden": "true", children: item.shortcut })] });
54
+ }