@ternkit/tern 1.3.0 → 1.5.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 (131) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +2 -1
  3. package/dist/runtime/deferred.d.ts +5 -1
  4. package/dist/runtime/deferred.js +27 -11
  5. package/dist/runtime/validate.d.ts +31 -0
  6. package/dist/runtime/validate.js +168 -0
  7. package/dist/server/intl.js +55 -12
  8. package/dist/ui/AlertDialog.svelte +79 -0
  9. package/dist/ui/AlertDialog.svelte.d.ts +24 -0
  10. package/dist/ui/AreaChart.svelte +9 -0
  11. package/dist/ui/AreaChart.svelte.d.ts +26 -0
  12. package/dist/ui/AspectRatio.svelte +33 -0
  13. package/dist/ui/AspectRatio.svelte.d.ts +10 -0
  14. package/dist/ui/BarChart.svelte +170 -0
  15. package/dist/ui/BarChart.svelte.d.ts +40 -0
  16. package/dist/ui/Breadcrumb.svelte +92 -0
  17. package/dist/ui/Breadcrumb.svelte.d.ts +14 -0
  18. package/dist/ui/ButtonGroup.svelte +61 -0
  19. package/dist/ui/ButtonGroup.svelte.d.ts +12 -0
  20. package/dist/ui/Calendar.svelte +265 -0
  21. package/dist/ui/Calendar.svelte.d.ts +28 -0
  22. package/dist/ui/Carousel.svelte +169 -0
  23. package/dist/ui/Carousel.svelte.d.ts +36 -0
  24. package/dist/ui/ChartFrame.svelte +174 -0
  25. package/dist/ui/ChartFrame.svelte.d.ts +24 -0
  26. package/dist/ui/Checkbox.svelte +13 -1
  27. package/dist/ui/Collapsible.svelte +59 -0
  28. package/dist/ui/Collapsible.svelte.d.ts +12 -0
  29. package/dist/ui/Combobox.svelte +350 -0
  30. package/dist/ui/Combobox.svelte.d.ts +24 -0
  31. package/dist/ui/Command.svelte +268 -0
  32. package/dist/ui/Command.svelte.d.ts +28 -0
  33. package/dist/ui/ConsentScreen.svelte +125 -0
  34. package/dist/ui/ConsentScreen.svelte.d.ts +21 -0
  35. package/dist/ui/ContextMenu.svelte +78 -0
  36. package/dist/ui/ContextMenu.svelte.d.ts +13 -0
  37. package/dist/ui/DataTable.svelte +277 -0
  38. package/dist/ui/DataTable.svelte.d.ts +53 -0
  39. package/dist/ui/DatePicker.svelte +138 -0
  40. package/dist/ui/DatePicker.svelte.d.ts +25 -0
  41. package/dist/ui/Dialog.svelte +168 -27
  42. package/dist/ui/Dialog.svelte.d.ts +10 -0
  43. package/dist/ui/DonutChart.svelte +115 -0
  44. package/dist/ui/DonutChart.svelte.d.ts +40 -0
  45. package/dist/ui/Drawer.svelte +19 -0
  46. package/dist/ui/Drawer.svelte.d.ts +14 -0
  47. package/dist/ui/Empty.svelte +62 -0
  48. package/dist/ui/Empty.svelte.d.ts +15 -0
  49. package/dist/ui/Field.svelte +22 -2
  50. package/dist/ui/Field.svelte.d.ts +2 -0
  51. package/dist/ui/Form.svelte +159 -0
  52. package/dist/ui/Form.svelte.d.ts +21 -0
  53. package/dist/ui/HoverCard.svelte +106 -0
  54. package/dist/ui/HoverCard.svelte.d.ts +26 -0
  55. package/dist/ui/Input.svelte +2 -1
  56. package/dist/ui/InputGroup.svelte +80 -0
  57. package/dist/ui/InputGroup.svelte.d.ts +13 -0
  58. package/dist/ui/InputOTP.svelte +140 -0
  59. package/dist/ui/InputOTP.svelte.d.ts +18 -0
  60. package/dist/ui/Item.svelte +86 -0
  61. package/dist/ui/Item.svelte.d.ts +17 -0
  62. package/dist/ui/Label.svelte +29 -0
  63. package/dist/ui/Label.svelte.d.ts +10 -0
  64. package/dist/ui/LineChart.svelte +171 -0
  65. package/dist/ui/LineChart.svelte.d.ts +42 -0
  66. package/dist/ui/Menu.svelte +22 -151
  67. package/dist/ui/Menu.svelte.d.ts +3 -10
  68. package/dist/ui/MenuList.svelte +317 -0
  69. package/dist/ui/MenuList.svelte.d.ts +29 -0
  70. package/dist/ui/Menubar.svelte +133 -0
  71. package/dist/ui/Menubar.svelte.d.ts +14 -0
  72. package/dist/ui/NavigationMenu.svelte +208 -0
  73. package/dist/ui/NavigationMenu.svelte.d.ts +18 -0
  74. package/dist/ui/Pagination.svelte +114 -0
  75. package/dist/ui/Pagination.svelte.d.ts +16 -0
  76. package/dist/ui/PasswordInput.svelte +134 -0
  77. package/dist/ui/PasswordInput.svelte.d.ts +11 -0
  78. package/dist/ui/Popover.svelte +127 -0
  79. package/dist/ui/Popover.svelte.d.ts +29 -0
  80. package/dist/ui/Resizable.svelte +122 -0
  81. package/dist/ui/Resizable.svelte.d.ts +16 -0
  82. package/dist/ui/ScrollArea.svelte +43 -0
  83. package/dist/ui/ScrollArea.svelte.d.ts +13 -0
  84. package/dist/ui/Select.svelte +3 -2
  85. package/dist/ui/SelectMenu.svelte +301 -0
  86. package/dist/ui/SelectMenu.svelte.d.ts +18 -0
  87. package/dist/ui/Sheet.svelte +21 -0
  88. package/dist/ui/Sheet.svelte.d.ts +16 -0
  89. package/dist/ui/Sidebar.svelte +263 -0
  90. package/dist/ui/Sidebar.svelte.d.ts +35 -0
  91. package/dist/ui/SignIn.svelte +182 -0
  92. package/dist/ui/SignIn.svelte.d.ts +38 -0
  93. package/dist/ui/SignUp.svelte +106 -0
  94. package/dist/ui/SignUp.svelte.d.ts +23 -0
  95. package/dist/ui/Slider.svelte +196 -0
  96. package/dist/ui/Slider.svelte.d.ts +19 -0
  97. package/dist/ui/Sparkline.svelte +23 -0
  98. package/dist/ui/Sparkline.svelte.d.ts +11 -0
  99. package/dist/ui/Stat.svelte +91 -0
  100. package/dist/ui/Stat.svelte.d.ts +18 -0
  101. package/dist/ui/Table.svelte +70 -0
  102. package/dist/ui/Table.svelte.d.ts +36 -0
  103. package/dist/ui/Textarea.svelte +2 -1
  104. package/dist/ui/Toggle.svelte +82 -0
  105. package/dist/ui/Toggle.svelte.d.ts +17 -0
  106. package/dist/ui/ToggleGroup.svelte +143 -0
  107. package/dist/ui/ToggleGroup.svelte.d.ts +27 -0
  108. package/dist/ui/Tooltip.svelte +27 -19
  109. package/dist/ui/Tooltip.svelte.d.ts +2 -1
  110. package/dist/ui/chart.d.ts +26 -0
  111. package/dist/ui/chart.js +89 -0
  112. package/dist/ui/dates.d.ts +13 -0
  113. package/dist/ui/dates.js +64 -0
  114. package/dist/ui/field.d.ts +8 -0
  115. package/dist/ui/floating.d.ts +44 -0
  116. package/dist/ui/floating.js +97 -0
  117. package/dist/ui/form.d.ts +12 -0
  118. package/dist/ui/form.js +8 -0
  119. package/dist/ui/index.d.ts +54 -0
  120. package/dist/ui/index.js +43 -0
  121. package/dist/ui/menu.d.ts +25 -0
  122. package/dist/ui/menu.js +1 -0
  123. package/dist/ui/options.d.ts +17 -0
  124. package/dist/ui/options.js +22 -0
  125. package/dist/ui/table.d.ts +26 -0
  126. package/dist/ui/table.js +1 -0
  127. package/dist/ui/theme.css +25 -0
  128. package/dist/vite/index.js +12 -3
  129. package/dist/vite/native.d.ts +3 -0
  130. package/dist/vite/native.js +122 -0
  131. package/package.json +3 -2
@@ -0,0 +1,64 @@
1
+ // Calendar dates as ISO strings ("2026-11-10"), done in UTC so time zones
2
+ // and daylight saving never move a day.
3
+ export function parse(iso) {
4
+ const [y, m, d] = iso.split('-').map(Number);
5
+ return new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1));
6
+ }
7
+ export function iso(d) {
8
+ return d.toISOString().slice(0, 10);
9
+ }
10
+ export function today() {
11
+ const n = new Date();
12
+ return iso(new Date(Date.UTC(n.getFullYear(), n.getMonth(), n.getDate())));
13
+ }
14
+ export function addDays(date, n) {
15
+ const d = parse(date);
16
+ d.setUTCDate(d.getUTCDate() + n);
17
+ return iso(d);
18
+ }
19
+ /** Adds months, keeping the day where the month has it, else its last day. */
20
+ export function addMonths(date, n) {
21
+ const d = parse(date);
22
+ const day = d.getUTCDate();
23
+ d.setUTCDate(1);
24
+ d.setUTCMonth(d.getUTCMonth() + n);
25
+ const last = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0)).getUTCDate();
26
+ d.setUTCDate(Math.min(day, last));
27
+ return iso(d);
28
+ }
29
+ export function startOfMonth(date) {
30
+ return date.slice(0, 8) + '01';
31
+ }
32
+ /** The weeks shown for date's month, each 7 ISO dates, starting on weekStart (0 = Sunday). */
33
+ export function monthGrid(date, weekStart) {
34
+ const first = parse(startOfMonth(date));
35
+ const lead = (first.getUTCDay() - weekStart + 7) % 7;
36
+ let day = addDays(iso(first), -lead);
37
+ const weeks = [];
38
+ const month = date.slice(0, 7);
39
+ do {
40
+ const week = [];
41
+ for (let i = 0; i < 7; i++) {
42
+ week.push(day);
43
+ day = addDays(day, 1);
44
+ }
45
+ weeks.push(week);
46
+ } while (day.slice(0, 7) === month);
47
+ return weeks;
48
+ }
49
+ /** The first day of the week for a locale, from Intl where the browser knows it. */
50
+ export function localeWeekStart(locale) {
51
+ try {
52
+ const loc = new Intl.Locale(locale ?? navigator.language);
53
+ const info = loc.getWeekInfo?.() ?? loc.weekInfo;
54
+ if (info)
55
+ return info.firstDay % 7;
56
+ return ['US', 'CA', 'JP', 'BR', 'IL', 'MX', 'PH', 'KR', 'TW', 'IN'].includes(loc.maximize().region ?? '') ? 0 : 1;
57
+ }
58
+ catch {
59
+ return 1;
60
+ }
61
+ }
62
+ export function format(date, locale, options) {
63
+ return new Intl.DateTimeFormat(locale, { timeZone: 'UTC', ...options }).format(parse(date));
64
+ }
@@ -1,10 +1,18 @@
1
1
  /** What a Field tells the control inside it, so labels and messages are wired up. */
2
2
  export interface FieldContext {
3
3
  readonly id: string;
4
+ /** The label's id, for controls that aren't labelable elements and need aria-labelledby. */
5
+ readonly labelId: string;
4
6
  readonly describedBy: string | undefined;
5
7
  readonly invalid: boolean;
6
8
  readonly required: boolean;
7
9
  readonly disabled: boolean;
10
+ /** Custom controls call this: the label then focuses the element with the field's id instead of using for. */
11
+ custom(): void;
12
+ /** The name the control posts under, from the Field. */
13
+ readonly name: string | undefined;
14
+ /** What was posted before, to refill the control after a failed post. */
15
+ readonly value: string | undefined;
8
16
  }
9
17
  export declare function setField(ctx: FieldContext): void;
10
18
  export declare function getField(): FieldContext | undefined;
@@ -0,0 +1,44 @@
1
+ export type Side = 'top' | 'bottom' | 'left' | 'right';
2
+ export type Align = 'start' | 'center' | 'end';
3
+ export type FloatingOptions = {
4
+ side?: Side;
5
+ align?: Align;
6
+ /** The gap between the anchor and the floating element, in pixels. */
7
+ offset?: number;
8
+ /** The space kept free at the viewport's edges, in pixels. */
9
+ padding?: number;
10
+ /** Make the floating element at least as wide as the anchor. */
11
+ matchWidth?: boolean;
12
+ };
13
+ type Rect = {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ };
19
+ /** Computes where the floating element goes. Exported for tests. */
20
+ export declare function compute(anchor: Rect, size: {
21
+ width: number;
22
+ height: number;
23
+ }, viewport: {
24
+ width: number;
25
+ height: number;
26
+ }, o?: FloatingOptions): {
27
+ x: number;
28
+ y: number;
29
+ side: Side;
30
+ };
31
+ /**
32
+ * Keeps el next to anchor while it is shown: on scroll, resize and size
33
+ * changes. Returns a function that stops. el gets data-side with the side
34
+ * it ended up on, for styles.
35
+ */
36
+ export declare function float(el: HTMLElement, anchor: Element | {
37
+ x: number;
38
+ y: number;
39
+ }, o?: FloatingOptions): () => void;
40
+ /** The elements inside root that can take focus, in order. */
41
+ export declare function focusables(root: Element): HTMLElement[];
42
+ /** Keeps Tab inside root, as modal dialogs need. Call it from keydown. */
43
+ export declare function trapTab(root: Element, e: KeyboardEvent): void;
44
+ export {};
@@ -0,0 +1,97 @@
1
+ // Positions a floating element (a popover, menu or tooltip) next to an
2
+ // anchor element or a point, flipping to the other side and shifting along
3
+ // it to stay in the viewport. The floating element is position: fixed,
4
+ // usually in the top layer through the Popover API.
5
+ const opposite = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
6
+ /** Computes where the floating element goes. Exported for tests. */
7
+ export function compute(anchor, size, viewport, o = {}) {
8
+ const { side = 'bottom', align = 'start', offset = 6, padding = 8 } = o;
9
+ const place = (s) => {
10
+ const vertical = s === 'top' || s === 'bottom';
11
+ let x;
12
+ let y;
13
+ if (vertical) {
14
+ y = s === 'bottom' ? anchor.y + anchor.height + offset : anchor.y - size.height - offset;
15
+ x = align === 'start' ? anchor.x : align === 'end' ? anchor.x + anchor.width - size.width : anchor.x + (anchor.width - size.width) / 2;
16
+ }
17
+ else {
18
+ x = s === 'right' ? anchor.x + anchor.width + offset : anchor.x - size.width - offset;
19
+ y = align === 'start' ? anchor.y : align === 'end' ? anchor.y + anchor.height - size.height : anchor.y + (anchor.height - size.height) / 2;
20
+ }
21
+ return { x, y };
22
+ };
23
+ const fits = (s, p) => s === 'bottom' ? p.y + size.height <= viewport.height - padding : s === 'top' ? p.y >= padding : s === 'right' ? p.x + size.width <= viewport.width - padding : p.x >= padding;
24
+ let chosen = side;
25
+ let p = place(side);
26
+ if (!fits(side, p)) {
27
+ const q = place(opposite[side]);
28
+ if (fits(opposite[side], q)) {
29
+ chosen = opposite[side];
30
+ p = q;
31
+ }
32
+ }
33
+ // Shift along the side to stay on screen.
34
+ const clamp = (v, min, max) => Math.max(min, Math.min(v, Math.max(min, max)));
35
+ p.x = clamp(p.x, padding, viewport.width - size.width - padding);
36
+ p.y = clamp(p.y, padding, viewport.height - size.height - padding);
37
+ return { x: Math.round(p.x), y: Math.round(p.y), side: chosen };
38
+ }
39
+ /**
40
+ * Keeps el next to anchor while it is shown: on scroll, resize and size
41
+ * changes. Returns a function that stops. el gets data-side with the side
42
+ * it ended up on, for styles.
43
+ */
44
+ export function float(el, anchor, o = {}) {
45
+ el.style.position = 'fixed';
46
+ el.style.margin = '0';
47
+ el.style.inset = 'auto';
48
+ const update = () => {
49
+ const r = anchor instanceof Element ? anchor.getBoundingClientRect() : { x: anchor.x, y: anchor.y, width: 0, height: 0 };
50
+ if (o.matchWidth)
51
+ el.style.minWidth = `${r.width}px`;
52
+ const pos = compute(r, { width: el.offsetWidth, height: el.offsetHeight }, { width: document.documentElement.clientWidth, height: window.innerHeight }, o);
53
+ el.style.left = `${pos.x}px`;
54
+ el.style.top = `${pos.y}px`;
55
+ el.dataset.side = pos.side;
56
+ };
57
+ update();
58
+ let frame = 0;
59
+ const schedule = () => {
60
+ cancelAnimationFrame(frame);
61
+ frame = requestAnimationFrame(update);
62
+ };
63
+ window.addEventListener('scroll', schedule, { capture: true, passive: true });
64
+ window.addEventListener('resize', schedule);
65
+ const ro = new ResizeObserver(schedule);
66
+ ro.observe(el);
67
+ if (anchor instanceof Element)
68
+ ro.observe(anchor);
69
+ return () => {
70
+ cancelAnimationFrame(frame);
71
+ window.removeEventListener('scroll', schedule, { capture: true });
72
+ window.removeEventListener('resize', schedule);
73
+ ro.disconnect();
74
+ };
75
+ }
76
+ /** The elements inside root that can take focus, in order. */
77
+ export function focusables(root) {
78
+ return Array.from(root.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter((el) => el.offsetParent !== null || el === document.activeElement);
79
+ }
80
+ /** Keeps Tab inside root, as modal dialogs need. Call it from keydown. */
81
+ export function trapTab(root, e) {
82
+ if (e.key !== 'Tab')
83
+ return;
84
+ const list = focusables(root);
85
+ if (list.length === 0)
86
+ return;
87
+ const first = list[0];
88
+ const last = list[list.length - 1];
89
+ if (e.shiftKey && document.activeElement === first) {
90
+ e.preventDefault();
91
+ last.focus();
92
+ }
93
+ else if (!e.shiftKey && document.activeElement === last) {
94
+ e.preventDefault();
95
+ first.focus();
96
+ }
97
+ }
@@ -0,0 +1,12 @@
1
+ import type { FormField } from '../runtime/validate.js';
2
+ /** What a Form tells the Fields and controls inside it. */
3
+ export interface FormContext {
4
+ /** The message to show for a field, from the browser's checks or the server. */
5
+ error(name: string): string | undefined;
6
+ /** What was posted before, to refill a field after a failure without JavaScript. */
7
+ value(name: string): string | undefined;
8
+ /** A field's rules, such as whether it's required. */
9
+ field(name: string): FormField | undefined;
10
+ }
11
+ export declare function setForm(ctx: FormContext): void;
12
+ export declare function getForm(): FormContext | undefined;
@@ -0,0 +1,8 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const key = Symbol('tern-form');
3
+ export function setForm(ctx) {
4
+ setContext(key, ctx);
5
+ }
6
+ export function getForm() {
7
+ return getContext(key);
8
+ }
@@ -1,24 +1,78 @@
1
1
  export { default as Accordion } from './Accordion.svelte';
2
2
  export { default as Alert } from './Alert.svelte';
3
+ export { default as AlertDialog } from './AlertDialog.svelte';
4
+ export { default as AreaChart } from './AreaChart.svelte';
5
+ export { default as AspectRatio } from './AspectRatio.svelte';
3
6
  export { default as Avatar } from './Avatar.svelte';
4
7
  export { default as Badge } from './Badge.svelte';
8
+ export { default as BarChart } from './BarChart.svelte';
9
+ export { default as Breadcrumb } from './Breadcrumb.svelte';
5
10
  export { default as Button } from './Button.svelte';
11
+ export { default as ButtonGroup } from './ButtonGroup.svelte';
12
+ export { default as Calendar } from './Calendar.svelte';
6
13
  export { default as Card } from './Card.svelte';
14
+ export { default as Carousel } from './Carousel.svelte';
7
15
  export { default as Checkbox } from './Checkbox.svelte';
16
+ export { default as Collapsible } from './Collapsible.svelte';
17
+ export { default as Combobox } from './Combobox.svelte';
18
+ export { default as Command } from './Command.svelte';
19
+ export { default as ConsentScreen } from './ConsentScreen.svelte';
20
+ export { default as ContextMenu } from './ContextMenu.svelte';
21
+ export { default as DataTable } from './DataTable.svelte';
22
+ export { default as DatePicker } from './DatePicker.svelte';
8
23
  export { default as Dialog } from './Dialog.svelte';
24
+ export { default as DonutChart } from './DonutChart.svelte';
25
+ export { default as Drawer } from './Drawer.svelte';
26
+ export { default as Empty } from './Empty.svelte';
9
27
  export { default as Field } from './Field.svelte';
28
+ export { default as Form } from './Form.svelte';
29
+ export { default as HoverCard } from './HoverCard.svelte';
10
30
  export { default as Input } from './Input.svelte';
31
+ export { default as InputGroup } from './InputGroup.svelte';
32
+ export { default as InputOTP } from './InputOTP.svelte';
33
+ export { default as Item } from './Item.svelte';
11
34
  export { default as Kbd } from './Kbd.svelte';
35
+ export { default as Label } from './Label.svelte';
36
+ export { default as LineChart } from './LineChart.svelte';
12
37
  export { default as Menu } from './Menu.svelte';
38
+ export { default as Menubar } from './Menubar.svelte';
39
+ export { default as NavigationMenu } from './NavigationMenu.svelte';
40
+ export { default as Pagination } from './Pagination.svelte';
41
+ export { default as PasswordInput } from './PasswordInput.svelte';
42
+ export { default as Popover } from './Popover.svelte';
13
43
  export { default as Progress } from './Progress.svelte';
14
44
  export { default as RadioGroup } from './RadioGroup.svelte';
45
+ export { default as Resizable } from './Resizable.svelte';
46
+ export { default as ScrollArea } from './ScrollArea.svelte';
15
47
  export { default as Select } from './Select.svelte';
48
+ export { default as SelectMenu } from './SelectMenu.svelte';
16
49
  export { default as Separator } from './Separator.svelte';
50
+ export { default as Sheet } from './Sheet.svelte';
51
+ export { default as Sidebar } from './Sidebar.svelte';
52
+ export { default as SignIn } from './SignIn.svelte';
53
+ export { default as SignUp } from './SignUp.svelte';
17
54
  export { default as Skeleton } from './Skeleton.svelte';
55
+ export { default as Slider } from './Slider.svelte';
56
+ export { default as Sparkline } from './Sparkline.svelte';
18
57
  export { default as Spinner } from './Spinner.svelte';
58
+ export { default as Stat } from './Stat.svelte';
19
59
  export { default as Switch } from './Switch.svelte';
60
+ export { default as Table } from './Table.svelte';
20
61
  export { default as Tabs } from './Tabs.svelte';
21
62
  export { default as Textarea } from './Textarea.svelte';
22
63
  export { default as Toaster } from './Toaster.svelte';
64
+ export { default as Toggle } from './Toggle.svelte';
65
+ export { default as ToggleGroup } from './ToggleGroup.svelte';
23
66
  export { default as Tooltip } from './Tooltip.svelte';
24
67
  export { dismiss, toast, toasts, type Toast, type Tone } from './toast.svelte.js';
68
+ export type { MenuItem } from './menu.js';
69
+ export type { Align, Side } from './floating.js';
70
+ export type { Option } from './options.js';
71
+ export type { ISODate } from './dates.js';
72
+ export type { DateRange } from './Calendar.svelte';
73
+ export type { CommandItem } from './Command.svelte';
74
+ export type { PopoverTrigger } from './Popover.svelte';
75
+ export type { HoverCardTrigger } from './HoverCard.svelte';
76
+ export type { Column, DataQuery } from './table.js';
77
+ export type { SidebarGroup, SidebarLink } from './Sidebar.svelte';
78
+ export type { Series } from './chart.js';
package/dist/ui/index.js CHANGED
@@ -3,25 +3,68 @@
3
3
  // you use them. See docs/ui.md.
4
4
  export { default as Accordion } from './Accordion.svelte';
5
5
  export { default as Alert } from './Alert.svelte';
6
+ export { default as AlertDialog } from './AlertDialog.svelte';
7
+ export { default as AreaChart } from './AreaChart.svelte';
8
+ export { default as AspectRatio } from './AspectRatio.svelte';
6
9
  export { default as Avatar } from './Avatar.svelte';
7
10
  export { default as Badge } from './Badge.svelte';
11
+ export { default as BarChart } from './BarChart.svelte';
12
+ export { default as Breadcrumb } from './Breadcrumb.svelte';
8
13
  export { default as Button } from './Button.svelte';
14
+ export { default as ButtonGroup } from './ButtonGroup.svelte';
15
+ export { default as Calendar } from './Calendar.svelte';
9
16
  export { default as Card } from './Card.svelte';
17
+ export { default as Carousel } from './Carousel.svelte';
10
18
  export { default as Checkbox } from './Checkbox.svelte';
19
+ export { default as Collapsible } from './Collapsible.svelte';
20
+ export { default as Combobox } from './Combobox.svelte';
21
+ export { default as Command } from './Command.svelte';
22
+ export { default as ConsentScreen } from './ConsentScreen.svelte';
23
+ export { default as ContextMenu } from './ContextMenu.svelte';
24
+ export { default as DataTable } from './DataTable.svelte';
25
+ export { default as DatePicker } from './DatePicker.svelte';
11
26
  export { default as Dialog } from './Dialog.svelte';
27
+ export { default as DonutChart } from './DonutChart.svelte';
28
+ export { default as Drawer } from './Drawer.svelte';
29
+ export { default as Empty } from './Empty.svelte';
12
30
  export { default as Field } from './Field.svelte';
31
+ export { default as Form } from './Form.svelte';
32
+ export { default as HoverCard } from './HoverCard.svelte';
13
33
  export { default as Input } from './Input.svelte';
34
+ export { default as InputGroup } from './InputGroup.svelte';
35
+ export { default as InputOTP } from './InputOTP.svelte';
36
+ export { default as Item } from './Item.svelte';
14
37
  export { default as Kbd } from './Kbd.svelte';
38
+ export { default as Label } from './Label.svelte';
39
+ export { default as LineChart } from './LineChart.svelte';
15
40
  export { default as Menu } from './Menu.svelte';
41
+ export { default as Menubar } from './Menubar.svelte';
42
+ export { default as NavigationMenu } from './NavigationMenu.svelte';
43
+ export { default as Pagination } from './Pagination.svelte';
44
+ export { default as PasswordInput } from './PasswordInput.svelte';
45
+ export { default as Popover } from './Popover.svelte';
16
46
  export { default as Progress } from './Progress.svelte';
17
47
  export { default as RadioGroup } from './RadioGroup.svelte';
48
+ export { default as Resizable } from './Resizable.svelte';
49
+ export { default as ScrollArea } from './ScrollArea.svelte';
18
50
  export { default as Select } from './Select.svelte';
51
+ export { default as SelectMenu } from './SelectMenu.svelte';
19
52
  export { default as Separator } from './Separator.svelte';
53
+ export { default as Sheet } from './Sheet.svelte';
54
+ export { default as Sidebar } from './Sidebar.svelte';
55
+ export { default as SignIn } from './SignIn.svelte';
56
+ export { default as SignUp } from './SignUp.svelte';
20
57
  export { default as Skeleton } from './Skeleton.svelte';
58
+ export { default as Slider } from './Slider.svelte';
59
+ export { default as Sparkline } from './Sparkline.svelte';
21
60
  export { default as Spinner } from './Spinner.svelte';
61
+ export { default as Stat } from './Stat.svelte';
22
62
  export { default as Switch } from './Switch.svelte';
63
+ export { default as Table } from './Table.svelte';
23
64
  export { default as Tabs } from './Tabs.svelte';
24
65
  export { default as Textarea } from './Textarea.svelte';
25
66
  export { default as Toaster } from './Toaster.svelte';
67
+ export { default as Toggle } from './Toggle.svelte';
68
+ export { default as ToggleGroup } from './ToggleGroup.svelte';
26
69
  export { default as Tooltip } from './Tooltip.svelte';
27
70
  export { dismiss, toast, toasts } from './toast.svelte.js';
@@ -0,0 +1,25 @@
1
+ /** An entry in a Menu, ContextMenu or Menubar. */
2
+ export type MenuItem = {
3
+ /** The text; also what typing a letter jumps to. */
4
+ label?: string;
5
+ onselect?: () => void;
6
+ /** Makes the item a link. */
7
+ href?: string;
8
+ danger?: boolean;
9
+ disabled?: boolean;
10
+ /** A keyboard shortcut to show, such as "⌘K". It's only shown: bind the key yourself. */
11
+ shortcut?: string;
12
+ /** For type "checkbox" or "radio": whether it's checked. Flip it in onselect. */
13
+ checked?: boolean;
14
+ type?: 'checkbox' | 'radio';
15
+ /** Items of a submenu, opened with ArrowRight, Enter or by hovering. */
16
+ items?: MenuItem[];
17
+ /** Draws a line instead of an item. */
18
+ separator?: boolean;
19
+ /** Starts a labelled group of the items after it. */
20
+ heading?: string;
21
+ };
22
+ export type MenuClose = {
23
+ all: boolean;
24
+ returnFocus: boolean;
25
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ /** An option in a Combobox or SelectMenu. */
2
+ export type Option = {
3
+ value: string;
4
+ label: string;
5
+ /** A second line, such as an email under a name. */
6
+ description?: string;
7
+ disabled?: boolean;
8
+ /** Groups options under this heading. */
9
+ group?: string;
10
+ };
11
+ /** Matches when every word of the query starts a word of the text, or, from three letters, appears anywhere in it. Ignores case and accents. */
12
+ export declare function matches(text: string, query: string): boolean;
13
+ /** Splits options into groups, keeping their order. */
14
+ export declare function grouped(options: Option[]): {
15
+ group?: string;
16
+ options: Option[];
17
+ }[];
@@ -0,0 +1,22 @@
1
+ /** Matches when every word of the query starts a word of the text, or, from three letters, appears anywhere in it. Ignores case and accents. */
2
+ export function matches(text, query) {
3
+ const norm = (s) => s.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase();
4
+ const words = norm(text).split(/[\s\-_/.@]+/);
5
+ const hay = norm(text);
6
+ return norm(query)
7
+ .split(/\s+/)
8
+ .filter(Boolean)
9
+ .every((q) => words.some((w) => w.startsWith(q)) || (q.length >= 3 && hay.includes(q)));
10
+ }
11
+ /** Splits options into groups, keeping their order. */
12
+ export function grouped(options) {
13
+ const out = [];
14
+ for (const o of options) {
15
+ const last = out[out.length - 1];
16
+ if (last && last.group === o.group)
17
+ last.options.push(o);
18
+ else
19
+ out.push({ group: o.group, options: [o] });
20
+ }
21
+ return out;
22
+ }
@@ -0,0 +1,26 @@
1
+ import type { Snippet } from 'svelte';
2
+ /** A column of a Table or DataTable. */
3
+ export type Column<R> = {
4
+ /** The field shown, or an id when cell draws it. */
5
+ key: keyof R & string;
6
+ header: string;
7
+ /** end for numbers. */
8
+ align?: 'start' | 'end' | 'center';
9
+ /** Draws the cell instead of the value. */
10
+ cell?: Snippet<[R]>;
11
+ /** For DataTable: whether the column sorts. */
12
+ sortable?: boolean;
13
+ /** For DataTable: the value it sorts and filters by, if not the field. */
14
+ value?: (row: R) => string | number | Date | null | undefined;
15
+ width?: string;
16
+ };
17
+ /** What a DataTable shows: passed to onquery when the server sorts, searches and pages. */
18
+ export type DataQuery = {
19
+ page: number;
20
+ pageSize: number;
21
+ sort: {
22
+ key: string;
23
+ dir: 'asc' | 'desc';
24
+ } | null;
25
+ search: string;
26
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/ui/theme.css CHANGED
@@ -42,6 +42,15 @@
42
42
  --tern-shadow: 0 1px 2px oklch(0.24 0.02 240 / 0.06), 0 1px 1px oklch(0.24 0.02 240 / 0.04);
43
43
  --tern-shadow-lg: 0 12px 32px -8px oklch(0.24 0.02 240 / 0.18), 0 2px 6px oklch(0.24 0.02 240 / 0.08);
44
44
  --tern-control-height: 2.25rem;
45
+ /* Chart series, in order; validated for color vision deficiencies on these surfaces. */
46
+ --tern-chart-1: #2a78d6;
47
+ --tern-chart-2: #eb6834;
48
+ --tern-chart-3: #1baf7a;
49
+ --tern-chart-4: #eda100;
50
+ --tern-chart-5: #e87ba4;
51
+ --tern-chart-6: #008300;
52
+ --tern-chart-7: #6250d6;
53
+ --tern-chart-8: #e34948;
45
54
  --tern-duration: 140ms;
46
55
  }
47
56
 
@@ -70,6 +79,14 @@
70
79
  --tern-ring: oklch(0.75 0.11 200);
71
80
  --tern-shadow: 0 1px 2px oklch(0 0 0 / 0.4);
72
81
  --tern-shadow-lg: 0 16px 40px -8px oklch(0 0 0 / 0.6), 0 2px 8px oklch(0 0 0 / 0.4);
82
+ --tern-chart-1: #3987e5;
83
+ --tern-chart-2: #d95926;
84
+ --tern-chart-3: #199e70;
85
+ --tern-chart-4: #c98500;
86
+ --tern-chart-5: #d55181;
87
+ --tern-chart-6: #008300;
88
+ --tern-chart-7: #9085e9;
89
+ --tern-chart-8: #e66767;
73
90
  }
74
91
  }
75
92
 
@@ -97,6 +114,14 @@
97
114
  --tern-ring: oklch(0.75 0.11 200);
98
115
  --tern-shadow: 0 1px 2px oklch(0 0 0 / 0.4);
99
116
  --tern-shadow-lg: 0 16px 40px -8px oklch(0 0 0 / 0.6), 0 2px 8px oklch(0 0 0 / 0.4);
117
+ --tern-chart-1: #3987e5;
118
+ --tern-chart-2: #d95926;
119
+ --tern-chart-3: #199e70;
120
+ --tern-chart-4: #c98500;
121
+ --tern-chart-5: #d55181;
122
+ --tern-chart-6: #008300;
123
+ --tern-chart-7: #9085e9;
124
+ --tern-chart-8: #e66767;
100
125
  }
101
126
 
102
127
  @media (prefers-reduced-motion: reduce) {
@@ -2,6 +2,7 @@ import { svelte } from '@sveltejs/vite-plugin-svelte';
2
2
  import fs from 'node:fs';
3
3
  import { createRequire } from 'node:module';
4
4
  import path from 'node:path';
5
+ import { nativePlugin } from './native.js';
5
6
  import { goScriptPlugin, goScriptPreprocessor } from './goscript.js';
6
7
  import { imagePlugin } from './image.js';
7
8
  /** Packages whose browser build works in the server renderer and whose default build needs Node. */
@@ -128,8 +129,16 @@ export function tern(options = {}) {
128
129
  }
129
130
  };
130
131
  },
131
- resolveId(id) {
132
- return id === SW_MODULE ? '\0' + SW_MODULE : undefined;
132
+ resolveId(id, importer) {
133
+ if (id === SW_MODULE)
134
+ return '\0' + SW_MODULE;
135
+ // ./$forms sits next to ./$types, in .tern/types, written by tern gen.
136
+ if (id === './$forms' && importer) {
137
+ const dir = path.relative(root, path.dirname(importer.split('?')[0]));
138
+ if (!dir.startsWith('..'))
139
+ return path.join(root, '.tern/types', dir, '$forms.js');
140
+ }
141
+ return undefined;
133
142
  },
134
143
  load(id) {
135
144
  if (id !== '\0' + SW_MODULE)
@@ -210,7 +219,7 @@ export function tern(options = {}) {
210
219
  return user;
211
220
  }
212
221
  };
213
- return [browserBuildsPlugin, imagePlugin(), goScriptPlugin(() => root), ...svelte(svelteOptions), plugin];
222
+ return [browserBuildsPlugin, imagePlugin(), goScriptPlugin(() => root), ...svelte(svelteOptions), plugin, nativePlugin(() => root, SERVER_ENTRY)];
214
223
  }
215
224
  /** The $service-worker module: what this build produced, for caching. */
216
225
  function serviceWorkerModule(root, base) {
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ export declare const NATIVE_IR = ".tern/native/ir.json";
3
+ export declare function nativePlugin(getRoot: () => string, entry: string): Plugin;