terminoir 0.4.0 → 0.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 (98) hide show
  1. package/README.md +38 -28
  2. package/css/components.css +62 -0
  3. package/css/shell.css +32 -1
  4. package/dist/components/AccentSwatches.svelte +1 -1
  5. package/dist/components/AppShell.svelte +33 -5
  6. package/dist/components/AppShell.svelte.d.ts +10 -1
  7. package/dist/components/AppShellNavLink.svelte +2 -4
  8. package/dist/components/Button.svelte +32 -14
  9. package/dist/components/Button.svelte.d.ts +2 -0
  10. package/dist/components/CalendarGrid.svelte +245 -0
  11. package/dist/components/CalendarGrid.svelte.d.ts +12 -0
  12. package/dist/components/CanvasStage.svelte +81 -0
  13. package/dist/components/CanvasStage.svelte.d.ts +19 -0
  14. package/dist/components/Centered.svelte +36 -0
  15. package/dist/components/Centered.svelte.d.ts +10 -0
  16. package/dist/components/Checkbox.svelte +64 -12
  17. package/dist/components/Checkbox.svelte.d.ts +5 -0
  18. package/dist/components/Chip.svelte +90 -2
  19. package/dist/components/Chip.svelte.d.ts +8 -0
  20. package/dist/components/CommandPalette.svelte +178 -97
  21. package/dist/components/CommandPalette.svelte.d.ts +15 -8
  22. package/dist/components/ContextMenu.svelte +68 -0
  23. package/dist/components/ContextMenu.svelte.d.ts +12 -0
  24. package/dist/components/DataTable.svelte +139 -0
  25. package/dist/components/DataTable.svelte.d.ts +39 -0
  26. package/dist/components/DescriptionList.svelte +41 -0
  27. package/dist/components/DescriptionList.svelte.d.ts +8 -0
  28. package/dist/components/Dialog.svelte +2 -1
  29. package/dist/components/Disclosure.svelte +55 -0
  30. package/dist/components/Disclosure.svelte.d.ts +10 -0
  31. package/dist/components/DocumentViewer.svelte +105 -0
  32. package/dist/components/DocumentViewer.svelte.d.ts +23 -0
  33. package/dist/components/Drawer.svelte +119 -0
  34. package/dist/components/Drawer.svelte.d.ts +16 -0
  35. package/dist/components/DropdownMenu.svelte +46 -62
  36. package/dist/components/DropdownMenu.svelte.d.ts +7 -6
  37. package/dist/components/FieldMessage.svelte +9 -0
  38. package/dist/components/FieldMessage.svelte.d.ts +8 -0
  39. package/dist/components/FileButton.svelte +44 -0
  40. package/dist/components/FileButton.svelte.d.ts +16 -0
  41. package/dist/components/FormRow.svelte +62 -0
  42. package/dist/components/FormRow.svelte.d.ts +13 -0
  43. package/dist/components/IconField.svelte +80 -5
  44. package/dist/components/IconField.svelte.d.ts +2 -0
  45. package/dist/components/Inline.svelte +52 -0
  46. package/dist/components/Inline.svelte.d.ts +16 -0
  47. package/dist/components/Input.svelte +23 -3
  48. package/dist/components/Input.svelte.d.ts +9 -0
  49. package/dist/components/List.svelte +42 -0
  50. package/dist/components/List.svelte.d.ts +13 -0
  51. package/dist/components/ListItem.svelte +114 -0
  52. package/dist/components/ListItem.svelte.d.ts +17 -0
  53. package/dist/components/MenuItems.svelte +44 -0
  54. package/dist/components/MenuItems.svelte.d.ts +8 -0
  55. package/dist/components/NavSection.svelte +45 -0
  56. package/dist/components/NavSection.svelte.d.ts +12 -0
  57. package/dist/components/Page.svelte +53 -0
  58. package/dist/components/Page.svelte.d.ts +12 -0
  59. package/dist/components/PageHeader.svelte +91 -0
  60. package/dist/components/PageHeader.svelte.d.ts +15 -0
  61. package/dist/components/Prose.svelte +33 -0
  62. package/dist/components/Prose.svelte.d.ts +8 -0
  63. package/dist/components/RadioCards.svelte +68 -0
  64. package/dist/components/RadioCards.svelte.d.ts +11 -0
  65. package/dist/components/SearchPicker.svelte +171 -0
  66. package/dist/components/SearchPicker.svelte.d.ts +19 -0
  67. package/dist/components/Section.svelte +71 -0
  68. package/dist/components/Section.svelte.d.ts +12 -0
  69. package/dist/components/SegmentedControl.svelte +63 -0
  70. package/dist/components/SegmentedControl.svelte.d.ts +12 -0
  71. package/dist/components/Select.svelte +22 -4
  72. package/dist/components/Select.svelte.d.ts +9 -1
  73. package/dist/components/Spacer.svelte +6 -0
  74. package/dist/components/Spacer.svelte.d.ts +26 -0
  75. package/dist/components/Stack.svelte +37 -0
  76. package/dist/components/Stack.svelte.d.ts +12 -0
  77. package/dist/components/Switch.svelte +6 -2
  78. package/dist/components/Switch.svelte.d.ts +2 -0
  79. package/dist/components/Tabs.svelte +6 -4
  80. package/dist/components/Text.svelte +68 -0
  81. package/dist/components/Text.svelte.d.ts +15 -0
  82. package/dist/components/Textarea.svelte +19 -3
  83. package/dist/components/Textarea.svelte.d.ts +6 -0
  84. package/dist/components/TitleInput.svelte +62 -0
  85. package/dist/components/TitleInput.svelte.d.ts +10 -0
  86. package/dist/index.d.ts +35 -2
  87. package/dist/index.js +31 -0
  88. package/dist/utils/calendar.d.ts +35 -0
  89. package/dist/utils/calendar.js +61 -0
  90. package/dist/utils/commands.d.ts +22 -0
  91. package/dist/utils/commands.js +17 -0
  92. package/dist/utils/componentTypes.d.ts +41 -0
  93. package/dist/utils/componentTypes.js +1 -0
  94. package/dist/utils/listNav.d.ts +2 -0
  95. package/dist/utils/listNav.js +8 -0
  96. package/dist/utils/menu.d.ts +10 -0
  97. package/dist/utils/menu.js +1 -0
  98. package/package.json +5 -3
@@ -0,0 +1,12 @@
1
+ import { type CalendarEvent, type CalendarMode } from '../utils/calendar.js';
2
+ type $$ComponentProps = {
3
+ mode?: CalendarMode;
4
+ /** Any date inside the week or month to show. */
5
+ anchor: Date;
6
+ events: CalendarEvent[];
7
+ onEventClick?: (event: CalendarEvent) => void;
8
+ class?: string;
9
+ };
10
+ declare const CalendarGrid: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type CalendarGrid = ReturnType<typeof CalendarGrid>;
12
+ export default CalendarGrid;
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils/cn.js';
4
+
5
+ let {
6
+ stage = $bindable(),
7
+ host = $bindable(),
8
+ label,
9
+ class: className = '',
10
+ overlay,
11
+ panel,
12
+ children,
13
+ }: {
14
+ /** The full-size stage element (observe it for size changes). */
15
+ stage?: HTMLDivElement;
16
+ /** Empty full-size element for a canvas library to mount into. */
17
+ host?: HTMLDivElement;
18
+ /** Accessible description of the canvas. */
19
+ label: string;
20
+ class?: string;
21
+ /** Small floating card, top right (filters, legend, counts). */
22
+ overlay?: Snippet;
23
+ /** Detail panel on the right; a bottom sheet on phones. */
24
+ panel?: Snippet;
25
+ /** Content drawn over the canvas instead of a library (e.g. an error). */
26
+ children?: Snippet;
27
+ } = $props();
28
+ </script>
29
+
30
+ <div bind:this={stage} class={cn('terminoir-canvas-stage', className)}>
31
+ <div bind:this={host} class="terminoir-canvas-stage__host" role="img" aria-label={label}></div>
32
+ {@render children?.()}
33
+ {#if overlay}
34
+ <div class="terminoir-canvas-stage__overlay glass-popover">{@render overlay()}</div>
35
+ {/if}
36
+ {#if panel}
37
+ <aside class="terminoir-canvas-stage__panel glass-popover">{@render panel()}</aside>
38
+ {/if}
39
+ </div>
40
+
41
+ <style>
42
+ .terminoir-canvas-stage {
43
+ position: relative;
44
+ width: 100%;
45
+ height: 100%;
46
+ min-height: 20rem;
47
+ overflow: hidden;
48
+ background: var(--background);
49
+ }
50
+ .terminoir-canvas-stage__host { position: absolute; inset: 0; }
51
+ .terminoir-canvas-stage__host :global(canvas) { display: block; }
52
+ .terminoir-canvas-stage__overlay,
53
+ .terminoir-canvas-stage__panel {
54
+ position: absolute;
55
+ z-index: 4;
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: var(--space-3);
59
+ padding: var(--space-4);
60
+ }
61
+ .terminoir-canvas-stage__overlay {
62
+ top: var(--space-3);
63
+ right: var(--space-3);
64
+ width: min(17rem, calc(100% - 2 * var(--space-3)));
65
+ }
66
+ .terminoir-canvas-stage__panel {
67
+ top: var(--space-3);
68
+ right: var(--space-3);
69
+ bottom: var(--space-3);
70
+ width: min(22rem, calc(100% - 2 * var(--space-3)));
71
+ overflow-y: auto;
72
+ }
73
+ @media (max-width: 700px) {
74
+ .terminoir-canvas-stage__panel {
75
+ top: auto;
76
+ left: var(--space-3);
77
+ width: auto;
78
+ max-height: 55%;
79
+ }
80
+ }
81
+ </style>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ /** The full-size stage element (observe it for size changes). */
4
+ stage?: HTMLDivElement;
5
+ /** Empty full-size element for a canvas library to mount into. */
6
+ host?: HTMLDivElement;
7
+ /** Accessible description of the canvas. */
8
+ label: string;
9
+ class?: string;
10
+ /** Small floating card, top right (filters, legend, counts). */
11
+ overlay?: Snippet;
12
+ /** Detail panel on the right; a bottom sheet on phones. */
13
+ panel?: Snippet;
14
+ /** Content drawn over the canvas instead of a library (e.g. an error). */
15
+ children?: Snippet;
16
+ };
17
+ declare const CanvasStage: import("svelte").Component<$$ComponentProps, {}, "stage" | "host">;
18
+ type CanvasStage = ReturnType<typeof CanvasStage>;
19
+ export default CanvasStage;
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils/cn.js';
4
+
5
+ let {
6
+ width = '24rem',
7
+ class: className = '',
8
+ children,
9
+ }: {
10
+ /** Max width of the centred column. */
11
+ width?: string;
12
+ class?: string;
13
+ children?: Snippet;
14
+ } = $props();
15
+ </script>
16
+
17
+ <!-- Full-viewport centred column: sign-in, loading and error screens. -->
18
+ <div class={cn('terminoir-centered', className)}>
19
+ <div class="terminoir-centered__column" style:max-width={width}>{@render children?.()}</div>
20
+ </div>
21
+
22
+ <style>
23
+ .terminoir-centered {
24
+ min-height: 100dvh;
25
+ display: grid;
26
+ place-items: center;
27
+ padding: var(--space-6) var(--space-4);
28
+ box-sizing: border-box;
29
+ }
30
+ .terminoir-centered__column {
31
+ width: 100%;
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: var(--space-4);
35
+ }
36
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ /** Max width of the centred column. */
4
+ width?: string;
5
+ class?: string;
6
+ children?: Snippet;
7
+ };
8
+ declare const Centered: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Centered = ReturnType<typeof Centered>;
10
+ export default Centered;
@@ -3,30 +3,82 @@
3
3
 
4
4
  let {
5
5
  label,
6
+ hideLabel = false,
7
+ hint,
6
8
  checked = $bindable(false),
9
+ strikeWhenChecked = false,
7
10
  disabled = false,
8
11
  class: className = '',
9
12
  id,
10
13
  ...rest
11
14
  }: {
12
15
  label: string;
16
+ /** Keep the label for screen readers only. */
17
+ hideLabel?: boolean;
18
+ hint?: string;
13
19
  checked?: boolean;
20
+ /** Strike through the label when checked (task lists). */
21
+ strikeWhenChecked?: boolean;
14
22
  disabled?: boolean;
15
23
  class?: string;
16
24
  id?: string;
17
25
  } & Record<string, unknown> = $props();
18
26
 
19
- const inputId = $derived(id ?? `terminoir-checkbox-${Math.random().toString(36).slice(2, 8)}`);
27
+ const fallbackId = $props.id();
28
+ const inputId = $derived(id ?? fallbackId);
20
29
  </script>
21
30
 
22
- <label class={cn('terminoir-field', className)} style="flex-direction: row; align-items: center;">
23
- <input
24
- id={inputId}
25
- type="checkbox"
26
- class="terminoir-focusable"
27
- bind:checked
28
- {disabled}
29
- {...rest}
30
- />
31
- <span class="terminoir-field__label">{label}</span>
32
- </label>
31
+ <div class={cn('terminoir-checkbox', className)}>
32
+ <label class="terminoir-checkbox__row" for={inputId}>
33
+ <input
34
+ id={inputId}
35
+ type="checkbox"
36
+ class="terminoir-checkbox__input terminoir-focusable"
37
+ bind:checked
38
+ {disabled}
39
+ {...rest}
40
+ />
41
+ <span
42
+ class={cn(
43
+ 'terminoir-checkbox__label',
44
+ hideLabel && 'terminoir-sr-only',
45
+ strikeWhenChecked && checked && 'terminoir-checkbox__label--done',
46
+ )}
47
+ >
48
+ {label}
49
+ </span>
50
+ </label>
51
+ {#if hint}<p class="terminoir-field__hint terminoir-checkbox__hint">{hint}</p>{/if}
52
+ </div>
53
+
54
+ <style>
55
+ .terminoir-checkbox {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: var(--space-1);
59
+ min-width: 0;
60
+ }
61
+ .terminoir-checkbox__row {
62
+ display: inline-flex;
63
+ align-items: flex-start;
64
+ gap: var(--space-2);
65
+ cursor: pointer;
66
+ font-size: 0.875rem;
67
+ line-height: 1.4;
68
+ }
69
+ .terminoir-checkbox__input {
70
+ margin: 0.15rem 0 0;
71
+ accent-color: var(--primary);
72
+ flex-shrink: 0;
73
+ }
74
+ .terminoir-checkbox__label {
75
+ color: var(--foreground);
76
+ min-width: 0;
77
+ overflow-wrap: anywhere;
78
+ }
79
+ .terminoir-checkbox__label--done {
80
+ color: var(--muted-foreground);
81
+ text-decoration: line-through;
82
+ }
83
+ .terminoir-checkbox__hint { padding-left: 1.5rem; }
84
+ </style>
@@ -1,6 +1,11 @@
1
1
  type $$ComponentProps = {
2
2
  label: string;
3
+ /** Keep the label for screen readers only. */
4
+ hideLabel?: boolean;
5
+ hint?: string;
3
6
  checked?: boolean;
7
+ /** Strike through the label when checked (task lists). */
8
+ strikeWhenChecked?: boolean;
4
9
  disabled?: boolean;
5
10
  class?: string;
6
11
  id?: string;
@@ -5,19 +5,34 @@
5
5
 
6
6
  let {
7
7
  tone = 'default',
8
+ color,
9
+ onremove,
10
+ removeLabel = 'Remove',
11
+ onclick,
8
12
  class: className = '',
9
13
  children,
10
14
  ...rest
11
15
  }: {
12
16
  tone?: ChipTone;
17
+ /** Dot colour before the text (palette name or CSS colour), e.g. a select option colour. */
18
+ color?: string;
19
+ /** Shows a × button; called when it is pressed. */
20
+ onremove?: () => void;
21
+ /** Accessible name of the × button, e.g. "Remove Rosa". */
22
+ removeLabel?: string;
23
+ /** Makes the chip text a button (e.g. open the linked item). */
24
+ onclick?: (event: MouseEvent) => void;
13
25
  class?: string;
14
26
  children?: import('svelte').Snippet;
15
27
  } & Record<string, unknown> = $props();
28
+
29
+ const PALETTE = new Set(['grey', 'gray', 'blue', 'green', 'red', 'yellow', 'orange', 'purple', 'pink', 'teal']);
30
+ const dot = $derived(color ? (PALETTE.has(color) ? `var(--chip-${color === 'gray' ? 'grey' : color})` : color) : undefined);
16
31
  </script>
17
32
 
18
33
  <span
19
34
  class={cn(
20
- 'terminoir-card__chip glass-chip',
35
+ 'terminoir-card__chip terminoir-chip',
21
36
  tone === 'overdue' && 'terminoir-card__chip--overdue',
22
37
  tone === 'today' && 'terminoir-card__chip--today',
23
38
  tone === 'done' && 'terminoir-card__chip--done',
@@ -25,5 +40,78 @@
25
40
  )}
26
41
  {...rest}
27
42
  >
28
- {@render children?.()}
43
+ {#if dot}<span class="terminoir-chip__dot" style:background={dot} aria-hidden="true"></span>{/if}
44
+ {#if onclick}
45
+ <button type="button" class="terminoir-chip__label terminoir-focusable" {onclick}>{@render children?.()}</button>
46
+ {:else}
47
+ <span class="terminoir-chip__label">{@render children?.()}</span>
48
+ {/if}
49
+ {#if onremove}
50
+ <button type="button" class="terminoir-chip__remove terminoir-focusable" aria-label={removeLabel} onclick={onremove}>×</button>
51
+ {/if}
29
52
  </span>
53
+
54
+ <style>
55
+ .terminoir-chip {
56
+ --chip-grey: #8b8f98;
57
+ --chip-blue: #5b8def;
58
+ --chip-green: #3fae6a;
59
+ --chip-red: #e5534b;
60
+ --chip-yellow: #d4a72c;
61
+ --chip-orange: #e0823d;
62
+ --chip-purple: #9b6bdf;
63
+ --chip-pink: #d9669f;
64
+ --chip-teal: #2fa7a0;
65
+ display: inline-flex;
66
+ align-items: center;
67
+ gap: 0.35rem;
68
+ max-width: 100%;
69
+ min-width: 0;
70
+ line-height: 1.35;
71
+ }
72
+ .terminoir-chip:not(.terminoir-card__chip--overdue):not(.terminoir-card__chip--today):not(.terminoir-card__chip--done) {
73
+ background: color-mix(in srgb, var(--foreground) 7%, transparent);
74
+ border-color: color-mix(in srgb, var(--foreground) 10%, transparent);
75
+ }
76
+ .terminoir-chip__dot {
77
+ width: 0.45rem;
78
+ height: 0.45rem;
79
+ border-radius: 999px;
80
+ flex-shrink: 0;
81
+ }
82
+ .terminoir-chip__label {
83
+ min-width: 0;
84
+ overflow: hidden;
85
+ text-overflow: ellipsis;
86
+ white-space: nowrap;
87
+ }
88
+ button.terminoir-chip__label {
89
+ border: 0;
90
+ padding: 0;
91
+ background: none;
92
+ color: inherit;
93
+ font: inherit;
94
+ cursor: pointer;
95
+ }
96
+ button.terminoir-chip__label:hover { text-decoration: underline; }
97
+ .terminoir-chip__remove {
98
+ display: inline-flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ width: 1rem;
102
+ height: 1rem;
103
+ margin-right: -0.2rem;
104
+ padding: 0;
105
+ border: 0;
106
+ border-radius: 999px;
107
+ background: transparent;
108
+ color: var(--muted-foreground);
109
+ font: inherit;
110
+ line-height: 1;
111
+ cursor: pointer;
112
+ }
113
+ .terminoir-chip__remove:hover {
114
+ color: var(--foreground);
115
+ background: color-mix(in srgb, var(--foreground) 12%, transparent);
116
+ }
117
+ </style>
@@ -1,6 +1,14 @@
1
1
  type ChipTone = 'default' | 'overdue' | 'today' | 'done';
2
2
  type $$ComponentProps = {
3
3
  tone?: ChipTone;
4
+ /** Dot colour before the text (palette name or CSS colour), e.g. a select option colour. */
5
+ color?: string;
6
+ /** Shows a × button; called when it is pressed. */
7
+ onremove?: () => void;
8
+ /** Accessible name of the × button, e.g. "Remove Rosa". */
9
+ removeLabel?: string;
10
+ /** Makes the chip text a button (e.g. open the linked item). */
11
+ onclick?: (event: MouseEvent) => void;
4
12
  class?: string;
5
13
  children?: import('svelte').Snippet;
6
14
  } & Record<string, unknown>;