@idyeah/diya 0.0.0-stage → 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 (191) hide show
  1. package/CHANGELOG.md +317 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +29 -0
  4. package/README.md +599 -2
  5. package/dist/DiyaProvider.d.ts +79 -0
  6. package/dist/DiyaProvider.js +77 -0
  7. package/dist/Icon.d.ts +10 -0
  8. package/dist/Icon.js +27 -0
  9. package/dist/accordion/Accordion.d.ts +34 -0
  10. package/dist/accordion/Accordion.js +15 -0
  11. package/dist/alert/Alert.d.ts +49 -0
  12. package/dist/alert/Alert.js +11 -0
  13. package/dist/alert/messages.d.ts +9 -0
  14. package/dist/alert/messages.js +2 -0
  15. package/dist/api.json +7277 -0
  16. package/dist/api.schema.json +214 -0
  17. package/dist/application-patterns/Chatbot.d.ts +114 -0
  18. package/dist/application-patterns/Chatbot.js +110 -0
  19. package/dist/application-patterns/SideNavigation.d.ts +74 -0
  20. package/dist/application-patterns/SideNavigation.js +31 -0
  21. package/dist/application-patterns/chatbot-messages.d.ts +20 -0
  22. package/dist/application-patterns/chatbot-messages.js +2 -0
  23. package/dist/application-patterns/side-navigation-messages.d.ts +5 -0
  24. package/dist/application-patterns/side-navigation-messages.js +2 -0
  25. package/dist/avatar/Avatar.d.ts +32 -0
  26. package/dist/avatar/Avatar.js +19 -0
  27. package/dist/avatar/messages.d.ts +7 -0
  28. package/dist/avatar/messages.js +2 -0
  29. package/dist/badge/Badge.d.ts +42 -0
  30. package/dist/badge/Badge.js +16 -0
  31. package/dist/badge/badge-utils.d.ts +6 -0
  32. package/dist/badge/badge-utils.js +24 -0
  33. package/dist/badge/messages.d.ts +4 -0
  34. package/dist/badge/messages.js +2 -0
  35. package/dist/breadcrumbs/Breadcrumbs.d.ts +34 -0
  36. package/dist/breadcrumbs/Breadcrumbs.js +19 -0
  37. package/dist/breadcrumbs/messages.d.ts +5 -0
  38. package/dist/breadcrumbs/messages.js +2 -0
  39. package/dist/button/Button.d.ts +46 -0
  40. package/dist/button/Button.js +44 -0
  41. package/dist/calendar/Calendar.d.ts +55 -0
  42. package/dist/calendar/Calendar.js +61 -0
  43. package/dist/calendar/messages.d.ts +8 -0
  44. package/dist/calendar/messages.js +2 -0
  45. package/dist/calendar/month.d.ts +61 -0
  46. package/dist/calendar/month.js +134 -0
  47. package/dist/card/Card.d.ts +46 -0
  48. package/dist/card/Card.js +8 -0
  49. package/dist/choice/Choice.d.ts +72 -0
  50. package/dist/choice/Choice.js +68 -0
  51. package/dist/choice/messages.d.ts +4 -0
  52. package/dist/choice/messages.js +2 -0
  53. package/dist/code-block/CodeBlock.d.ts +20 -0
  54. package/dist/code-block/CodeBlock.js +49 -0
  55. package/dist/code-block/messages.d.ts +8 -0
  56. package/dist/code-block/messages.js +2 -0
  57. package/dist/combobox/Combobox.d.ts +60 -0
  58. package/dist/combobox/Combobox.js +160 -0
  59. package/dist/combobox/messages.d.ts +9 -0
  60. package/dist/combobox/messages.js +2 -0
  61. package/dist/combobox/useAnchoredPopover.d.ts +5 -0
  62. package/dist/combobox/useAnchoredPopover.js +100 -0
  63. package/dist/data-viz/DataViz.d.ts +140 -0
  64. package/dist/data-viz/DataViz.js +469 -0
  65. package/dist/data-viz/index.d.ts +2 -0
  66. package/dist/data-viz/index.js +1 -0
  67. package/dist/data-viz/messages.d.ts +11 -0
  68. package/dist/data-viz/messages.js +2 -0
  69. package/dist/date-picker/DatePicker.d.ts +57 -0
  70. package/dist/date-picker/DatePicker.js +146 -0
  71. package/dist/date-picker/messages.d.ts +13 -0
  72. package/dist/date-picker/messages.js +2 -0
  73. package/dist/dialog/Dialog.d.ts +84 -0
  74. package/dist/dialog/Dialog.js +107 -0
  75. package/dist/dialog/messages.d.ts +9 -0
  76. package/dist/dialog/messages.js +2 -0
  77. package/dist/fonts/DIYASans-Italic.woff2 +0 -0
  78. package/dist/fonts/DIYASans.woff2 +0 -0
  79. package/dist/fonts/OFL.txt +94 -0
  80. package/dist/index.d.ts +50 -0
  81. package/dist/index.js +46 -0
  82. package/dist/internal/brand.d.ts +33 -0
  83. package/dist/internal/brand.js +57 -0
  84. package/dist/internal/cx.d.ts +2 -0
  85. package/dist/internal/cx.js +4 -0
  86. package/dist/internal/link.d.ts +14 -0
  87. package/dist/internal/link.js +7 -0
  88. package/dist/internal/slot.d.ts +15 -0
  89. package/dist/internal/slot.js +67 -0
  90. package/dist/layout/Inline.d.ts +4 -0
  91. package/dist/layout/Inline.js +7 -0
  92. package/dist/layout/Stack.d.ts +4 -0
  93. package/dist/layout/Stack.js +7 -0
  94. package/dist/layout/layout.d.ts +32 -0
  95. package/dist/layout/layout.js +12 -0
  96. package/dist/link/TextLink.d.ts +23 -0
  97. package/dist/link/TextLink.js +17 -0
  98. package/dist/list/List.d.ts +48 -0
  99. package/dist/list/List.js +17 -0
  100. package/dist/list/messages.d.ts +4 -0
  101. package/dist/list/messages.js +2 -0
  102. package/dist/menu/Menu.d.ts +49 -0
  103. package/dist/menu/Menu.js +54 -0
  104. package/dist/messages-defaults.d.ts +201 -0
  105. package/dist/messages-defaults.js +61 -0
  106. package/dist/messages.d.ts +12 -0
  107. package/dist/messages.js +1 -0
  108. package/dist/metric/Metric.d.ts +42 -0
  109. package/dist/metric/Metric.js +11 -0
  110. package/dist/metric/messages.d.ts +9 -0
  111. package/dist/metric/messages.js +2 -0
  112. package/dist/overlay/FloatingSurface.d.ts +29 -0
  113. package/dist/overlay/FloatingSurface.js +254 -0
  114. package/dist/page-header/PageHeader.d.ts +38 -0
  115. package/dist/page-header/PageHeader.js +10 -0
  116. package/dist/pagination/Pagination.d.ts +44 -0
  117. package/dist/pagination/Pagination.js +26 -0
  118. package/dist/pagination/messages.d.ts +11 -0
  119. package/dist/pagination/messages.js +2 -0
  120. package/dist/popover/Popover.d.ts +23 -0
  121. package/dist/popover/Popover.js +8 -0
  122. package/dist/progress/Progress.d.ts +33 -0
  123. package/dist/progress/Progress.js +14 -0
  124. package/dist/progress/messages.d.ts +4 -0
  125. package/dist/progress/messages.js +2 -0
  126. package/dist/prose/Prose.d.ts +14 -0
  127. package/dist/prose/Prose.js +7 -0
  128. package/dist/rating/Rating.d.ts +32 -0
  129. package/dist/rating/Rating.js +27 -0
  130. package/dist/rating/messages.d.ts +6 -0
  131. package/dist/rating/messages.js +2 -0
  132. package/dist/search/Search.d.ts +11 -0
  133. package/dist/search/Search.js +9 -0
  134. package/dist/select/Select.d.ts +65 -0
  135. package/dist/select/Select.js +19 -0
  136. package/dist/select/messages.d.ts +4 -0
  137. package/dist/select/messages.js +2 -0
  138. package/dist/site-header/SiteHeader.d.ts +88 -0
  139. package/dist/site-header/SiteHeader.js +211 -0
  140. package/dist/site-header/messages.d.ts +8 -0
  141. package/dist/site-header/messages.js +2 -0
  142. package/dist/slider/Slider.d.ts +54 -0
  143. package/dist/slider/Slider.js +89 -0
  144. package/dist/slider/messages.d.ts +5 -0
  145. package/dist/slider/messages.js +2 -0
  146. package/dist/styles.css +3379 -0
  147. package/dist/switch/Switch.d.ts +47 -0
  148. package/dist/switch/Switch.js +18 -0
  149. package/dist/table/Table.d.ts +99 -0
  150. package/dist/table/Table.js +158 -0
  151. package/dist/table/messages.d.ts +23 -0
  152. package/dist/table/messages.js +2 -0
  153. package/dist/tabs/Tabs.d.ts +49 -0
  154. package/dist/tabs/Tabs.js +26 -0
  155. package/dist/tag/Tag.d.ts +37 -0
  156. package/dist/tag/Tag.js +20 -0
  157. package/dist/tag/messages.d.ts +4 -0
  158. package/dist/tag/messages.js +2 -0
  159. package/dist/text-area/TextArea.d.ts +47 -0
  160. package/dist/text-area/TextArea.js +19 -0
  161. package/dist/text-input/TextInput.d.ts +53 -0
  162. package/dist/text-input/TextInput.js +35 -0
  163. package/dist/text-input/messages.d.ts +4 -0
  164. package/dist/text-input/messages.js +3 -0
  165. package/dist/text-input/native-field.d.ts +15 -0
  166. package/dist/text-input/native-field.js +56 -0
  167. package/dist/toast/Toast.d.ts +24 -0
  168. package/dist/toast/Toast.js +79 -0
  169. package/dist/toast/messages.d.ts +4 -0
  170. package/dist/toast/messages.js +2 -0
  171. package/dist/tooltip/Tooltip.d.ts +22 -0
  172. package/dist/tooltip/Tooltip.js +92 -0
  173. package/dist/workflow/ContentState.d.ts +44 -0
  174. package/dist/workflow/ContentState.js +11 -0
  175. package/dist/workflow/Drawer.d.ts +48 -0
  176. package/dist/workflow/Drawer.js +51 -0
  177. package/dist/workflow/FileUpload.d.ts +67 -0
  178. package/dist/workflow/FileUpload.js +47 -0
  179. package/dist/workflow/Form.d.ts +52 -0
  180. package/dist/workflow/Form.js +16 -0
  181. package/dist/workflow/Wizard.d.ts +76 -0
  182. package/dist/workflow/Wizard.js +31 -0
  183. package/dist/workflow/drawer-messages.d.ts +4 -0
  184. package/dist/workflow/drawer-messages.js +2 -0
  185. package/dist/workflow/file-upload-messages.d.ts +11 -0
  186. package/dist/workflow/file-upload-messages.js +2 -0
  187. package/dist/workflow/form-messages.d.ts +5 -0
  188. package/dist/workflow/form-messages.js +2 -0
  189. package/dist/workflow/wizard-messages.d.ts +9 -0
  190. package/dist/workflow/wizard-messages.js +2 -0
  191. package/package.json +84 -4
@@ -0,0 +1,134 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
4
+ export const iso = (date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
5
+ export const parse = (value) => new Date(`${value}T12:00:00`);
6
+ export const display = (value, locale) => value ? parse(value).toLocaleDateString(locale, { month: 'short', day: 'numeric', year: 'numeric' }) : '';
7
+ // Where Intl has no week data (older Firefox), CLDR's first day by region; everywhere else Monday.
8
+ const sundayFirst = new Set('AG AS BD BR BS BT BW BZ CA CO DM DO ET GT GU HK HN ID IL IN JM JP KE KH KR LA MH MM MO MT MX MZ NI NP PA PE PH PK PR PT PY SA SG SV TH TT TW UM US VE VI WS YE ZA ZW'.split(' '));
9
+ const saturdayFirst = new Set('AE AF BH DJ DZ EG IQ IR JO KW LY OM QA SD SY'.split(' '));
10
+ /** The locale's first day of the week, 0 (Sunday) to 6 (Saturday). */
11
+ function weekStartFor(locale) {
12
+ try {
13
+ const intl = new Intl.Locale(locale);
14
+ const info = typeof intl.getWeekInfo === 'function' ? intl.getWeekInfo() : intl.weekInfo;
15
+ if (info && info.firstDay >= 1 && info.firstDay <= 7)
16
+ return info.firstDay % 7;
17
+ const region = intl.region ?? intl.maximize().region ?? '';
18
+ return sundayFirst.has(region) ? 0 : saturdayFirst.has(region) ? 6 : region === 'MV' ? 5 : 1;
19
+ }
20
+ catch {
21
+ return 0;
22
+ }
23
+ }
24
+ // Headings start on the locale's first day. Two letters where that keeps all seven distinct (Mo Tu We… in English), else the locale's short name.
25
+ const weekdaysFor = (locale, start) => {
26
+ const format = new Intl.DateTimeFormat(locale, { weekday: 'short' });
27
+ // 1 January 2023 was a Sunday.
28
+ const names = Array.from({ length: 7 }, (_, index) => format.format(new Date(2023, 0, 1 + ((start + index) % 7))));
29
+ const short = names.map((name) => Array.from(name).slice(0, 2).join(''));
30
+ return new Set(short).size === 7 ? short : names;
31
+ };
32
+ // The full names, which name each column for assistive tech (the APG example's `abbr`).
33
+ const weekdayNamesFor = (locale, start) => {
34
+ const format = new Intl.DateTimeFormat(locale, { weekday: 'long' });
35
+ return Array.from({ length: 7 }, (_, index) => format.format(new Date(2023, 0, 1 + ((start + index) % 7))));
36
+ };
37
+ export const useClientLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
38
+ /** Whether `day` falls before `min` or after `max`. */
39
+ export const outsideLimits = (day, min, max) => (!!min && day < min) || (!!max && day > max);
40
+ /** `day` moved inside `min` and `max`. */
41
+ export const clampToLimits = (day, min, max) => min && day < min ? min : max && day > max ? max : day;
42
+ /** The first of the month that shows `day` (today when it is '' or invalid), kept inside `min` and `max`. */
43
+ export function monthOf(day, min, max) {
44
+ const date = day ? parse(day) : new Date();
45
+ const valid = Number.isFinite(date.getTime()) ? date : new Date();
46
+ const shown = parse(clampToLimits(iso(valid), min, max));
47
+ return new Date(shown.getFullYear(), shown.getMonth(), 1);
48
+ }
49
+ const subscribeNever = () => () => { };
50
+ /** Today as an ISO date; '' while hydrating server HTML, so a server in another time zone never marks the wrong day. */
51
+ export const useToday = () => useSyncExternalStore(subscribeNever, () => iso(new Date()), () => '');
52
+ /**
53
+ * The month grid DatePicker shows in its popup and Calendar shows on the page: the
54
+ * month heading with its buttons, the range hint, then the ARIA date grid of the APG
55
+ * date picker example. The grid is named by the month; a header row of weekday column
56
+ * headers, then a row per week of gridcells. Each day is a native button with the
57
+ * gridcell role, named with its weekday ("Thursday, November 12, 2026"): chosen days
58
+ * carry aria-selected, today aria-current="date". One day is a Tab stop; arrows,
59
+ * Home/End and PageUp/PageDown move it, kept inside min and max. Days outside them,
60
+ * or unavailable, say so in their name and aria-disabled, and can't be chosen.
61
+ */
62
+ export function useMonthGrid(options) {
63
+ const { locale, month, onMonthChange, value, range, choosingEnd, min, max, isDateUnavailable, disabled, active, strings: t, onChoose } = options;
64
+ const weekStart = useMemo(() => weekStartFor(locale), [locale]);
65
+ const weekdays = useMemo(() => weekdaysFor(locale, weekStart), [locale, weekStart]);
66
+ const weekdayNames = useMemo(() => weekdayNamesFor(locale, weekStart), [locale, weekStart]);
67
+ // Each day's accessible name, with its weekday: "Thursday, November 12, 2026" in en-US.
68
+ const dayNames = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }), [locale]);
69
+ const monthId = `${useId()}-month`;
70
+ const [cursor, setCursor] = useState('');
71
+ const grid = useRef(null);
72
+ const focusDate = useRef(null);
73
+ const today = useToday();
74
+ useClientLayoutEffect(() => {
75
+ if (!active || !focusDate.current)
76
+ return;
77
+ grid.current?.querySelector(`[data-date="${focusDate.current}"]`)?.focus();
78
+ focusDate.current = null;
79
+ }, [month, active]);
80
+ const blocked = (day) => outsideLimits(day, min, max) || !!isDateUnavailable?.(day);
81
+ function moveDay(event, day) {
82
+ const date = parse(day);
83
+ const fromStart = (date.getDay() - weekStart + 7) % 7;
84
+ const offsets = { ArrowRight: 1, ArrowLeft: -1, ArrowDown: 7, ArrowUp: -7, Home: -fromStart, End: 6 - fromStart };
85
+ if (event.key in offsets)
86
+ date.setDate(date.getDate() + offsets[event.key]);
87
+ else if (event.key === 'PageUp' || event.key === 'PageDown') {
88
+ const delta = event.key === 'PageUp' ? -1 : 1;
89
+ const nextMonth = date.getMonth() + delta;
90
+ const lastDay = new Date(date.getFullYear(), nextMonth + 1, 0).getDate();
91
+ date.setDate(1);
92
+ date.setMonth(nextMonth);
93
+ date.setDate(Math.min(parse(day).getDate(), lastDay));
94
+ }
95
+ else
96
+ return;
97
+ event.preventDefault();
98
+ const target = clampToLimits(iso(date), min, max);
99
+ setCursor(target);
100
+ const shown = parse(target);
101
+ if (shown.getMonth() !== month.getMonth() || shown.getFullYear() !== month.getFullYear()) {
102
+ focusDate.current = target;
103
+ onMonthChange(new Date(shown.getFullYear(), shown.getMonth(), 1));
104
+ }
105
+ else
106
+ grid.current?.querySelector(`[data-date="${target}"]`)?.focus();
107
+ }
108
+ function render() {
109
+ const first = new Date(month.getFullYear(), month.getMonth(), 1);
110
+ const days = new Date(month.getFullYear(), month.getMonth() + 1, 0).getDate();
111
+ const monthDays = Array.from({ length: days }, (_, index) => iso(new Date(month.getFullYear(), month.getMonth(), index + 1)));
112
+ // Roving tabindex: one day in the grid is a Tab stop; arrows move it.
113
+ const tabbable = cursor && monthDays.includes(cursor) ? cursor
114
+ : [value.start, value.end, today].find((day) => monthDays.includes(day) && !blocked(day)) ?? monthDays.find((day) => !blocked(day)) ?? monthDays[0];
115
+ // Weeks of seven cells, empty before the 1st and after the last day.
116
+ const cells = [...Array.from({ length: (first.getDay() - weekStart + 7) % 7 }, () => null), ...monthDays];
117
+ while (cells.length % 7)
118
+ cells.push(null);
119
+ const weeks = Array.from({ length: cells.length / 7 }, (_, week) => cells.slice(week * 7, week * 7 + 7));
120
+ const between = (day) => !!value.end && day > value.start && day < value.end;
121
+ const previousDisabled = !!min && iso(new Date(month.getFullYear(), month.getMonth(), 0)) < min;
122
+ const nextDisabled = !!max && iso(new Date(month.getFullYear(), month.getMonth() + 1, 1)) > max;
123
+ return _jsxs(_Fragment, { children: [_jsxs("div", { className: "diya-calendar-heading", children: [_jsx("button", { type: "button", "aria-label": t.previousMonth, disabled: disabled || previousDisabled, onClick: () => { setCursor(''); onMonthChange(new Date(month.getFullYear(), month.getMonth() - 1, 1)); }, children: "\u2039" }), _jsx("strong", { id: monthId, "aria-live": "polite", children: month.toLocaleDateString(locale, { month: 'long', year: 'numeric' }) }), _jsx("button", { type: "button", "aria-label": t.nextMonth, disabled: disabled || nextDisabled, onClick: () => { setCursor(''); onMonthChange(new Date(month.getFullYear(), month.getMonth() + 1, 1)); }, children: "\u203A" })] }), range && _jsx("p", { className: "diya-calendar-hint", children: choosingEnd ? t.chooseEnd : t.chooseStart }), _jsxs("div", { ref: grid, role: "grid", "aria-labelledby": monthId, className: "diya-calendar-grid", children: [_jsx("div", { role: "row", className: "diya-calendar-row", children: weekdays.map((day, index) => _jsx("span", { role: "columnheader", "aria-label": weekdayNames[index], className: "diya-calendar-weekday", children: day }, index)) }), weeks.map((week, row) => _jsx("div", { role: "row", className: "diya-calendar-row", children: week.map((day, column) => {
124
+ if (!day)
125
+ return _jsx("span", { role: "gridcell" }, `space-${column}`);
126
+ const unavailable = blocked(day);
127
+ // A range is chosen as a whole: its ends and the days between are selected.
128
+ const selected = day === value.start || day === value.end || between(day);
129
+ const name = dayNames.format(parse(day));
130
+ return _jsx("button", { type: "button", role: "gridcell", "data-date": day, "aria-label": unavailable ? t.unavailableDay(name) : name, "aria-selected": selected || undefined, "aria-current": day === today ? 'date' : undefined, "aria-disabled": unavailable || undefined, disabled: disabled || undefined, tabIndex: day === tabbable ? 0 : -1, "data-between": between(day), "data-today": day === today, onClick: () => onChoose(day), onFocus: () => setCursor(day), onKeyDown: (event) => moveDay(event, day), children: Number(day.slice(8)) }, day);
131
+ }) }, row))] })] });
132
+ }
133
+ return { render, resetCursor: () => setCursor(''), isBlocked: blocked };
134
+ }
@@ -0,0 +1,46 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ export type CardProps = Omit<HTMLAttributes<HTMLElement>, 'title' | 'children'> & {
3
+ /** Visible card heading; it is also the card's accessible name. */
4
+ title: string;
5
+ /** Heading level of the title, h1 to h6. Defaults to 3. */
6
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
7
+ /** Supporting text under the title. */
8
+ description?: string;
9
+ /** Slot above the title for Tag or Badge elements. */
10
+ tags?: ReactNode;
11
+ /** Slot in the top corner, usually a Menu or a quiet icon button. */
12
+ actions?: ReactNode;
13
+ /** Image or illustration shown edge to edge above the body. */
14
+ media?: ReactNode;
15
+ /** Main card content, under the header. */
16
+ children?: ReactNode;
17
+ /** Content pinned to the bottom, such as buttons or metadata. */
18
+ footer?: ReactNode;
19
+ /** Draws a line above the footer; defaults to false. */
20
+ divided?: boolean;
21
+ /** Lifts the card with a shadow instead of a flat border; defaults to false. */
22
+ shadow?: boolean;
23
+ };
24
+ /** A titled content container with optional media, tags, corner actions and a footer. */
25
+ export declare const Card: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "children" | "title"> & {
26
+ /** Visible card heading; it is also the card's accessible name. */
27
+ title: string;
28
+ /** Heading level of the title, h1 to h6. Defaults to 3. */
29
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
30
+ /** Supporting text under the title. */
31
+ description?: string;
32
+ /** Slot above the title for Tag or Badge elements. */
33
+ tags?: ReactNode;
34
+ /** Slot in the top corner, usually a Menu or a quiet icon button. */
35
+ actions?: ReactNode;
36
+ /** Image or illustration shown edge to edge above the body. */
37
+ media?: ReactNode;
38
+ /** Main card content, under the header. */
39
+ children?: ReactNode;
40
+ /** Content pinned to the bottom, such as buttons or metadata. */
41
+ footer?: ReactNode;
42
+ /** Draws a line above the footer; defaults to false. */
43
+ divided?: boolean;
44
+ /** Lifts the card with a shadow instead of a flat border; defaults to false. */
45
+ shadow?: boolean;
46
+ } & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cx } from '../internal/cx.js';
4
+ /** A titled content container with optional media, tags, corner actions and a footer. */
5
+ export const Card = forwardRef(function Card({ title, headingLevel = 3, description, tags, actions, media, children, footer, divided = false, shadow = false, className, ...props }, ref) {
6
+ const Heading = `h${headingLevel}`;
7
+ return _jsxs("article", { "aria-label": title, ...props, ref: ref, className: cx('diya-card', className), "data-shadow": shadow, children: [media && _jsx("div", { className: "diya-card-media", children: media }), _jsxs("div", { className: "diya-card-body", children: [_jsxs("header", { className: "diya-card-header", children: [_jsxs("div", { children: [tags && _jsx("div", { className: "diya-card-tag", children: tags }), _jsx(Heading, { className: "diya-card-title", children: title }), description && _jsx("p", { children: description })] }), actions && _jsx("div", { className: "diya-card-menu", children: actions })] }), children && _jsx("div", { className: "diya-card-content", children: children }), footer && _jsx("footer", { className: "diya-card-footer", "data-divider": divided, children: footer })] })] });
8
+ });
@@ -0,0 +1,72 @@
1
+ import { type ChangeEventHandler, type FieldsetHTMLAttributes, type FocusEventHandler, type ReactNode } from 'react';
2
+ import { choiceMessages } from './messages.js';
3
+ declare module '../messages.js' {
4
+ interface DiyaMessages {
5
+ choice: typeof choiceMessages;
6
+ }
7
+ }
8
+ export type ChoiceOption = {
9
+ /** The value reported when this option is chosen; unique within the group. */
10
+ value: string;
11
+ /** Visible option text beside the radio or checkbox: a string, or a node such as text with links ("I agree to the <a>Terms</a>") or a color swatch before a name. A link inside opens without toggling the option, and the option is named by the label's text, so give a swatch `aria-hidden` and visible words. */
12
+ label: ReactNode;
13
+ /** Visible secondary line under the label, announced as the option's description; shown whenever present. */
14
+ description?: string;
15
+ /** Shows the option but stops it being chosen. */
16
+ disabled?: boolean;
17
+ };
18
+ type ChoiceBase = Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'onChange' | 'onBlur' | 'defaultValue' | 'name' | 'disabled' | 'children'> & {
19
+ /** Visible group label (the fieldset legend); also names the group for assistive tech. Hide it with hideLabel. */
20
+ label: string;
21
+ /** The choices, in display order. */
22
+ options: ChoiceOption[];
23
+ /** Control and type size; defaults to 'medium'. */
24
+ size?: 'small' | 'medium' | 'large';
25
+ /** Hides the group label visually but keeps it as the group's accessible name. */
26
+ hideLabel?: boolean;
27
+ /** Disables every option in the group. */
28
+ disabled?: boolean;
29
+ /** Visible hint below the options, announced with the group; `error` replaces it while set. */
30
+ helperText?: string;
31
+ /** Visible error message below the options; marks the group invalid while set. */
32
+ error?: string;
33
+ /** Form field name shared by the inputs; defaults to a generated id. */
34
+ name?: string;
35
+ /** Native change event from the radio or checkbox that changed, as react-hook-form's register() expects; `onValueChange` still fires. */
36
+ onChange?: ChangeEventHandler<HTMLInputElement>;
37
+ /** Called when one of the inputs loses focus. */
38
+ onBlur?: FocusEventHandler<HTMLInputElement>;
39
+ /** Asks the browser to require a choice before the form submits; radios only. */
40
+ required?: boolean;
41
+ };
42
+ /** Radios: one value. */
43
+ export type ChoiceSingleProps = ChoiceBase & {
44
+ /** Single mode (the default): radios, leave unset or false. */
45
+ multiple?: false;
46
+ /** The chosen option's value, or '' for none, when controlled; pair it with `onValueChange`. */
47
+ value?: string;
48
+ /** The initially chosen value when uncontrolled; a form reset returns to it. */
49
+ defaultValue?: string;
50
+ /** Called with the chosen option's value. */
51
+ onValueChange?: (value: string) => void;
52
+ /** Only for `multiple`. */
53
+ selectAll?: never;
54
+ };
55
+ /** Checkboxes: an array of values, with an optional "Select all". */
56
+ export type ChoiceMultipleProps = ChoiceBase & {
57
+ /** Multiple mode: checkboxes, any number chosen. */
58
+ multiple: true;
59
+ /** The chosen options' values, when controlled; pair it with `onValueChange`. */
60
+ value?: string[];
61
+ /** The initially chosen values when uncontrolled; a form reset returns to them. */
62
+ defaultValue?: string[];
63
+ /** Called with the full new array of chosen values. */
64
+ onValueChange?: (value: string[]) => void;
65
+ /** Adds a visible "Select all" checkbox first that toggles every enabled option and shows a mixed state when some are chosen. */
66
+ selectAll?: boolean;
67
+ };
68
+ /** Radios by default (one string); `multiple` gives checkboxes and an array. */
69
+ export type ChoiceProps = ChoiceSingleProps | ChoiceMultipleProps;
70
+ /** A labeled group of radios for one choice, or checkboxes for several with `multiple`. The ref reaches the inputs: a callback ref gets each one, an object ref the first. */
71
+ export declare const Choice: import("react").ForwardRefExoticComponent<ChoiceProps & import("react").RefAttributes<HTMLInputElement>>;
72
+ export {};
@@ -0,0 +1,68 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useId, useRef } from 'react';
4
+ import { useDiyaMessages } from '../DiyaProvider.js';
5
+ import { cx } from '../internal/cx.js';
6
+ import { useFormReset, useNativeMirror } from '../text-input/native-field.js';
7
+ import { choiceMessages } from './messages.js';
8
+ const sameList = (a, b) => a.length === b.length && a.every((value, index) => value === b[index]);
9
+ /** A labeled group of radios for one choice, or checkboxes for several with `multiple`. The ref reaches the inputs: a callback ref gets each one, an object ref the first. */
10
+ export const Choice = forwardRef(function Choice(props, ref) {
11
+ const id = useId();
12
+ const t = useDiyaMessages('choice', choiceMessages);
13
+ const { label, options, size = 'medium', hideLabel = false, disabled = false, helperText, error, name = id, onChange, onBlur, required, className, multiple: _multiple, value: _value, defaultValue: _defaultValue, onValueChange: _onValueChange, selectAll: _selectAll, 'aria-describedby': describedBy, ...rest } = props;
14
+ const multiple = props.multiple === true;
15
+ const description = error || helperText;
16
+ const inputs = useRef([]);
17
+ const first = useRef(null);
18
+ const controlled = props.value !== undefined;
19
+ const fromDom = () => inputs.current.filter((input) => !!input?.checked).map((input) => input.value);
20
+ const initial = props.multiple === true ? props.value ?? props.defaultValue ?? [] : (props.value ?? props.defaultValue) ? [props.value ?? props.defaultValue ?? ''] : [];
21
+ const [current, sync] = useNativeMirror(() => inputs.current.some(Boolean) ? fromDom() : undefined, initial, sameList);
22
+ useFormReset(first, sync);
23
+ // Internally a selection is always a list; the public value matches the mode.
24
+ const selected = controlled ? props.multiple === true ? props.value ?? [] : props.value ? [props.value] : [] : current;
25
+ const report = (next) => props.multiple === true ? props.onValueChange?.(next) : props.onValueChange?.(next[0] ?? '');
26
+ const enabled = options.filter((option) => !option.disabled);
27
+ const allChosen = enabled.length > 0 && enabled.every((option) => selected.includes(option.value));
28
+ const someChosen = enabled.some((option) => selected.includes(option.value));
29
+ function attach(index) {
30
+ return (node) => {
31
+ inputs.current[index] = node;
32
+ if (index === 0)
33
+ first.current = node;
34
+ if (typeof ref === 'function')
35
+ ref(node);
36
+ else if (ref && index === 0)
37
+ ref.current = node;
38
+ };
39
+ }
40
+ function toggleAll(on) {
41
+ const values = enabled.map((option) => option.value);
42
+ if (controlled) {
43
+ report(on ? Array.from(new Set([...selected, ...values])) : selected.filter((value) => !values.includes(value)));
44
+ return;
45
+ }
46
+ // Uncontrolled: set the boxes, then click the last one so onChange (and any form library) sees one real change.
47
+ const changing = inputs.current.filter((input) => !!input && !input.disabled && values.includes(input.value) && input.checked !== on);
48
+ const last = changing.pop();
49
+ for (const input of changing)
50
+ input.checked = on;
51
+ last?.click();
52
+ }
53
+ return (_jsxs("fieldset", { ...rest, className: cx('diya-choice', className), "data-size": size, disabled: disabled, "aria-invalid": error ? true : props['aria-invalid'], "aria-describedby": [describedBy, description ? `${id}-help` : undefined].filter(Boolean).join(' ') || undefined, children: [_jsx("legend", { className: hideLabel ? 'diya-sr-only' : undefined, children: label }), _jsxs("div", { className: "diya-choice-items", children: [multiple && props.selectAll && _jsxs("label", { className: "diya-choice-option", children: [_jsx("input", { type: "checkbox", "aria-label": t.selectAll, disabled: disabled || !enabled.length, ref: node => { if (node)
54
+ node.indeterminate = someChosen && !allChosen; }, checked: allChosen, onChange: event => toggleAll(event.target.checked) }), _jsx("span", { className: "diya-choice-label", children: t.selectAll })] }), options.map((option, index) => (_jsxs("label", { className: "diya-choice-option", "data-disabled": disabled || option.disabled || undefined, children: [_jsx("input", { ref: attach(index), type: multiple ? 'checkbox' : 'radio', name: name, value: option.value, disabled: option.disabled, required: !multiple ? required : undefined, ...(controlled
55
+ ? { checked: selected.includes(option.value) }
56
+ : { defaultChecked: initial.includes(option.value) }), "aria-describedby": option.description ? `${id}-option-${index}-description` : undefined, onBlur: onBlur, onChange: (event) => {
57
+ onChange?.(event);
58
+ if (!controlled) {
59
+ sync();
60
+ report(fromDom());
61
+ return;
62
+ }
63
+ if (!multiple)
64
+ report([option.value]);
65
+ else
66
+ report(event.target.checked ? [...selected, option.value] : selected.filter((value) => value !== option.value));
67
+ } }), _jsxs("span", { children: [_jsx("span", { className: "diya-choice-label", children: option.label }), option.description && (_jsx("span", { className: "diya-choice-description", id: `${id}-option-${index}-description`, children: option.description }))] })] }, option.value)))] }), description && (_jsxs("p", { id: `${id}-help`, className: "diya-text-field-description", children: [error && _jsx("span", { "aria-hidden": "true", children: "!" }), description] }))] }));
68
+ });
@@ -0,0 +1,4 @@
1
+ /** choice: English defaults, overridable through DiyaProvider `messages`. */
2
+ export declare const choiceMessages: {
3
+ selectAll: string;
4
+ };
@@ -0,0 +1,2 @@
1
+ /** choice: English defaults, overridable through DiyaProvider `messages`. */
2
+ export const choiceMessages = { selectAll: 'Select all' };
@@ -0,0 +1,20 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { codeBlockMessages } from './messages.js';
3
+ declare module '../messages.js' {
4
+ interface DiyaMessages {
5
+ codeBlock: typeof codeBlockMessages;
6
+ }
7
+ }
8
+ export type CodeBlockProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
9
+ /** The code to show and copy, exactly as written. It is set in a monospace font, without syntax colors. */
10
+ code: string;
11
+ /** A short name above the code, such as its language ('TypeScript') or file ('app/page.tsx'). It also names the code for screen readers. */
12
+ label?: string;
13
+ };
14
+ /** A block of code with a Copy button. Long lines scroll sideways in a region that takes keyboard focus, and Copy announces its result politely. */
15
+ export declare const CodeBlock: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
16
+ /** The code to show and copy, exactly as written. It is set in a monospace font, without syntax colors. */
17
+ code: string;
18
+ /** A short name above the code, such as its language ('TypeScript') or file ('app/page.tsx'). It also names the code for screen readers. */
19
+ label?: string;
20
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useEffect, useRef, useState } from 'react';
4
+ import { Button } from '../button/Button.js';
5
+ import { useDiyaMessages } from '../DiyaProvider.js';
6
+ import { Icon } from '../Icon.js';
7
+ import { cx } from '../internal/cx.js';
8
+ import { codeBlockMessages } from './messages.js';
9
+ /** A block of code with a Copy button. Long lines scroll sideways in a region that takes keyboard focus, and Copy announces its result politely. */
10
+ export const CodeBlock = forwardRef(function CodeBlock({ code, label, className, ...props }, ref) {
11
+ const t = useDiyaMessages('codeBlock', codeBlockMessages);
12
+ const [status, setStatus] = useState('idle');
13
+ const content = useRef(null);
14
+ // Each copy, a new code string and unmounting start a new operation, so a late result never lands on the wrong code.
15
+ const operation = useRef(0);
16
+ useEffect(() => {
17
+ operation.current += 1;
18
+ setStatus('idle');
19
+ }, [code]);
20
+ useEffect(() => () => { operation.current += 1; }, []);
21
+ useEffect(() => {
22
+ if (status !== 'copied')
23
+ return;
24
+ const timer = setTimeout(() => setStatus('idle'), 2000);
25
+ return () => clearTimeout(timer);
26
+ }, [status]);
27
+ async function copy() {
28
+ const current = ++operation.current;
29
+ setStatus('idle');
30
+ try {
31
+ if (!navigator.clipboard?.writeText)
32
+ throw new Error('Clipboard unavailable');
33
+ await navigator.clipboard.writeText(code);
34
+ if (current === operation.current)
35
+ setStatus('copied');
36
+ }
37
+ catch {
38
+ if (current !== operation.current)
39
+ return;
40
+ // Without clipboard access, select the code so it can be copied by hand.
41
+ if (content.current)
42
+ window.getSelection()?.selectAllChildren(content.current);
43
+ setStatus('failed');
44
+ }
45
+ }
46
+ const copied = status === 'copied';
47
+ const button = (_jsxs(Button, { variant: "quiet", size: "small", className: "diya-code-block-copy", onClick: () => void copy(), children: [_jsx(Icon, { name: copied ? 'check' : 'copy', size: 16 }), copied ? t.copied : t.copy] }));
48
+ return (_jsxs("div", { ...props, ref: ref, className: cx('diya-code-block', className), "data-labelled": label ? 'true' : undefined, children: [label && _jsxs("div", { className: "diya-code-block-header", children: [_jsx("span", { className: "diya-code-block-label", children: label }), button] }), _jsx("pre", { className: "diya-code-block-code", tabIndex: 0, role: "region", "aria-label": label ? t.namedCode(label) : t.code, children: _jsx("code", { ref: content, children: code }) }), !label && button, _jsx("span", { className: "diya-code-block-status", role: "status", "data-failed": status === 'failed' ? 'true' : undefined, children: copied ? t.copied : status === 'failed' ? t.copyFailed : '' })] }));
49
+ });
@@ -0,0 +1,8 @@
1
+ /** codeBlock: English defaults, overridable through DiyaProvider `messages`. */
2
+ export declare const codeBlockMessages: {
3
+ code: string;
4
+ namedCode: (label: string) => string;
5
+ copy: string;
6
+ copied: string;
7
+ copyFailed: string;
8
+ };
@@ -0,0 +1,2 @@
1
+ /** codeBlock: English defaults, overridable through DiyaProvider `messages`. */
2
+ export const codeBlockMessages = { code: 'Code', namedCode: (label) => `${label} code`, copy: 'Copy', copied: 'Copied', copyFailed: 'Couldn’t copy. The code is selected, so you can copy it yourself.' };
@@ -0,0 +1,60 @@
1
+ import { type InputHTMLAttributes } from 'react';
2
+ import { comboboxMessages } from './messages.js';
3
+ declare module '../messages.js' {
4
+ interface DiyaMessages {
5
+ combobox: typeof comboboxMessages;
6
+ }
7
+ }
8
+ export type ComboboxOption = {
9
+ /** The value reported when this option is chosen; unique within the list. */
10
+ value: string;
11
+ /** Visible option text; typing filters options by it. */
12
+ label: string;
13
+ /** Shows the option but stops it being chosen. */
14
+ disabled?: boolean;
15
+ };
16
+ type ComboboxBase = Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'size' | 'type' | 'role' | 'multiple' | 'disabled' | 'name' | 'children'> & {
17
+ /** Visible label above the field; also names the input and the option list for assistive tech. Hide it with hideLabel. */
18
+ label: string;
19
+ /** The choices, in display order; typing filters them by label. */
20
+ options: ComboboxOption[];
21
+ /** Prevents typing and choosing, and dims the field. */
22
+ disabled?: boolean;
23
+ /** Visible error message below the field; marks it invalid while set. */
24
+ error?: string;
25
+ /** Visible hint below the field, announced with it; `error` replaces it while set. */
26
+ helperText?: string;
27
+ /** Hides the label visually but keeps it as the input's accessible name. */
28
+ hideLabel?: boolean;
29
+ /** Control height and type size; defaults to 'medium'. */
30
+ size?: 'small' | 'medium' | 'large';
31
+ /** Form field name: the chosen value, or each chosen value in `multiple` mode, is sent with the form under it. */
32
+ name?: string;
33
+ };
34
+ /** One option: a string, '' for none. */
35
+ export type ComboboxSingleProps = ComboboxBase & {
36
+ /** Single mode (the default): leave unset or false. Choosing an option closes the list. */
37
+ multiple?: false;
38
+ /** The chosen option's value, or '' for none, when controlled; pair it with `onValueChange`. */
39
+ value?: string;
40
+ /** The initially chosen value when uncontrolled; a form reset returns to it. */
41
+ defaultValue?: string;
42
+ /** Called with the chosen option's value. */
43
+ onValueChange?: (value: string) => void;
44
+ };
45
+ /** Several options: an array of values. */
46
+ export type ComboboxMultipleProps = ComboboxBase & {
47
+ /** Multiple mode: choices show as removable chips, choosing toggles an option and the list stays open. */
48
+ multiple: true;
49
+ /** The chosen options' values, in the order they were picked, when controlled; pair it with `onValueChange`. */
50
+ value?: string[];
51
+ /** The initially chosen values when uncontrolled; a form reset returns to them. */
52
+ defaultValue?: string[];
53
+ /** Called with the full new array of chosen values. */
54
+ onValueChange?: (value: string[]) => void;
55
+ };
56
+ /** One option is a string ('' for none); `multiple` takes and returns an array. Native input attributes and the ref go on the text input; `className` and `style` go on the outer field wrapper. */
57
+ export type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;
58
+ /** A labeled text field that filters a list of options as you type; picks one value, or several with `multiple`. */
59
+ export declare const Combobox: import("react").ForwardRefExoticComponent<ComboboxProps & import("react").RefAttributes<HTMLInputElement>>;
60
+ export {};