@marianmeres/stuic 2.65.0 → 3.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.
- package/README.md +292 -4
- package/dist/README.md +41 -18
- package/dist/actions/popover/README.md +19 -0
- package/dist/actions/popover/index.css +6 -9
- package/dist/actions/popover/popover.svelte.js +2 -2
- package/dist/actions/tooltip/README.md +18 -0
- package/dist/actions/tooltip/index.css +5 -8
- package/dist/actions/tooltip/tooltip.svelte.js +1 -1
- package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte +9 -10
- package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte.d.ts +3 -3
- package/dist/components/AlertConfirmPrompt/Current.svelte +15 -17
- package/dist/components/AlertConfirmPrompt/Current.svelte.d.ts +5 -3
- package/dist/components/AlertConfirmPrompt/acp-icons.js +5 -4
- package/dist/components/AlertConfirmPrompt/index.css +62 -0
- package/dist/components/AssetsPreview/AssetsPreview.svelte +92 -73
- package/dist/components/AssetsPreview/AssetsPreview.svelte.d.ts +1 -0
- package/dist/components/AssetsPreview/index.css +59 -0
- package/dist/components/Avatar/Avatar.svelte +32 -18
- package/dist/components/Avatar/Avatar.svelte.d.ts +1 -0
- package/dist/components/Avatar/README.md +166 -0
- package/dist/components/Avatar/index.css +128 -0
- package/dist/components/Backdrop/Backdrop.svelte +8 -2
- package/dist/components/Backdrop/Backdrop.svelte.d.ts +1 -0
- package/dist/components/Backdrop/README.md +71 -6
- package/dist/components/Backdrop/index.css +29 -0
- package/dist/components/Button/Button.svelte +117 -124
- package/dist/components/Button/Button.svelte.d.ts +35 -23
- package/dist/components/Button/README.md +87 -21
- package/dist/components/Button/index.css +473 -9
- package/dist/components/Button/index.d.ts +1 -1
- package/dist/components/Button/index.js +1 -1
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +7 -38
- package/dist/components/ButtonGroupRadio/README.md +82 -4
- package/dist/components/ButtonGroupRadio/index.css +152 -14
- package/dist/components/Collapsible/Collapsible.svelte +7 -7
- package/dist/components/Collapsible/Collapsible.svelte.d.ts +2 -2
- package/dist/components/Collapsible/README.md +34 -2
- package/dist/components/Collapsible/index.css +38 -0
- package/dist/components/CommandMenu/CommandMenu.svelte +13 -24
- package/dist/components/CommandMenu/README.md +39 -0
- package/dist/components/CommandMenu/index.css +45 -2
- package/dist/components/DismissibleMessage/DismissibleMessage.svelte +53 -50
- package/dist/components/DismissibleMessage/DismissibleMessage.svelte.d.ts +6 -5
- package/dist/components/DismissibleMessage/README.md +93 -11
- package/dist/components/DismissibleMessage/index.css +122 -8
- package/dist/components/DismissibleMessage/index.d.ts +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -50
- package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +6 -6
- package/dist/components/DropdownMenu/README.md +132 -0
- package/dist/components/DropdownMenu/index.css +231 -27
- package/dist/components/Input/FieldAssets.svelte +8 -5
- package/dist/components/Input/FieldCheckbox.svelte +7 -44
- package/dist/components/Input/FieldFile.svelte +1 -6
- package/dist/components/Input/FieldInput.svelte +1 -1
- package/dist/components/Input/FieldOptions.svelte +41 -38
- package/dist/components/Input/FieldRadios.svelte +7 -16
- package/dist/components/Input/FieldSelect.svelte +1 -1
- package/dist/components/Input/FieldSwitch.svelte +1 -5
- package/dist/components/Input/FieldTextarea.svelte +1 -1
- package/dist/components/Input/README.md +194 -0
- package/dist/components/Input/_internal/FieldRadioInternal.svelte +2 -40
- package/dist/components/Input/_internal/InputWrap.svelte +8 -48
- package/dist/components/Input/index.css +522 -127
- package/dist/components/ListItemButton/ListItemButton.svelte +37 -73
- package/dist/components/ListItemButton/ListItemButton.svelte.d.ts +1 -9
- package/dist/components/ListItemButton/README.md +100 -45
- package/dist/components/ListItemButton/index.css +175 -56
- package/dist/components/ListItemButton/index.d.ts +1 -1
- package/dist/components/ListItemButton/index.js +1 -1
- package/dist/components/Modal/Modal.svelte +2 -8
- package/dist/components/Modal/Modal.svelte.d.ts +1 -0
- package/dist/components/Modal/README.md +29 -0
- package/dist/components/Modal/index.css +36 -0
- package/dist/components/ModalDialog/ModalDialog.svelte +2 -21
- package/dist/components/ModalDialog/README.md +35 -0
- package/dist/components/ModalDialog/index.css +57 -0
- package/dist/components/Notifications/Notifications.svelte +44 -128
- package/dist/components/Notifications/Notifications.svelte.d.ts +9 -17
- package/dist/components/Notifications/README.md +186 -70
- package/dist/components/Notifications/index.css +212 -15
- package/dist/components/Progress/README.md +15 -0
- package/dist/components/Progress/_internal/Bar.svelte +2 -2
- package/dist/components/Progress/index.css +4 -4
- package/dist/components/Skeleton/Skeleton.svelte +3 -2
- package/dist/components/Skeleton/index.css +11 -14
- package/dist/components/Spinner/Spinner.svelte +2 -2
- package/dist/components/Spinner/SpinnerCircle.svelte +1 -1
- package/dist/components/Switch/README.md +15 -0
- package/dist/components/Switch/Switch.svelte +4 -7
- package/dist/components/Switch/Switch.svelte.d.ts +1 -1
- package/dist/components/Switch/SwitchButton.svelte +4 -5
- package/dist/components/Switch/index.css +3 -4
- package/dist/components/TabbedMenu/README.md +26 -21
- package/dist/components/TabbedMenu/TabbedMenu.svelte +5 -5
- package/dist/components/TabbedMenu/index.css +7 -22
- package/dist/components/ThemePreview/README.md +289 -0
- package/dist/components/ThemePreview/ThemePreview.svelte +341 -0
- package/dist/components/ThemePreview/ThemePreview.svelte.d.ts +33 -0
- package/dist/components/ThemePreview/index.css +493 -0
- package/dist/components/ThemePreview/index.d.ts +1 -0
- package/dist/components/ThemePreview/index.js +1 -0
- package/dist/components/TwCheck/TwCheck.svelte +4 -4
- package/dist/components/TwCheck/index.css +3 -2
- package/dist/components/TypeaheadInput/TypeaheadInput.svelte +1 -1
- package/dist/components/X/X.svelte +16 -4
- package/dist/components/X/X.svelte.d.ts +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.css +31 -16
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/themes/blue-orange.css +163 -0
- package/dist/themes/blue-orange.d.ts +6 -0
- package/dist/themes/blue-orange.js +151 -0
- package/dist/themes/cyan-red.css +163 -0
- package/dist/themes/cyan-red.d.ts +6 -0
- package/dist/themes/cyan-red.js +151 -0
- package/dist/themes/cyan-slate.css +163 -0
- package/dist/themes/cyan-slate.d.ts +6 -0
- package/dist/themes/cyan-slate.js +151 -0
- package/dist/themes/emerald-pink.css +163 -0
- package/dist/themes/emerald-pink.d.ts +6 -0
- package/dist/themes/emerald-pink.js +151 -0
- package/dist/themes/fuchsia-emerald.css +163 -0
- package/dist/themes/fuchsia-emerald.d.ts +6 -0
- package/dist/themes/fuchsia-emerald.js +151 -0
- package/dist/themes/gray.css +163 -0
- package/dist/themes/gray.d.ts +6 -0
- package/dist/themes/gray.js +151 -0
- package/dist/themes/indigo-amber.css +163 -0
- package/dist/themes/indigo-amber.d.ts +6 -0
- package/dist/themes/indigo-amber.js +151 -0
- package/dist/themes/neutral.css +163 -0
- package/dist/themes/neutral.d.ts +6 -0
- package/dist/themes/neutral.js +151 -0
- package/dist/themes/pink-emerald.css +163 -0
- package/dist/themes/pink-emerald.d.ts +6 -0
- package/dist/themes/pink-emerald.js +151 -0
- package/dist/themes/purple-yellow.css +163 -0
- package/dist/themes/purple-yellow.d.ts +6 -0
- package/dist/themes/purple-yellow.js +151 -0
- package/dist/themes/rainbow.css +163 -0
- package/dist/themes/rainbow.d.ts +6 -0
- package/dist/themes/rainbow.js +156 -0
- package/dist/themes/red-blue.css +163 -0
- package/dist/themes/red-blue.d.ts +6 -0
- package/dist/themes/red-blue.js +151 -0
- package/dist/themes/red-cyan.css +163 -0
- package/dist/themes/red-cyan.d.ts +6 -0
- package/dist/themes/red-cyan.js +151 -0
- package/dist/themes/rose-teal.css +163 -0
- package/dist/themes/rose-teal.d.ts +6 -0
- package/dist/themes/rose-teal.js +151 -0
- package/dist/themes/sky-amber.css +163 -0
- package/dist/themes/sky-amber.d.ts +6 -0
- package/dist/themes/sky-amber.js +151 -0
- package/dist/themes/slate-cyan.css +163 -0
- package/dist/themes/slate-cyan.d.ts +6 -0
- package/dist/themes/slate-cyan.js +151 -0
- package/dist/themes/tailwind-color-pairs.md +31 -0
- package/dist/themes/teal-rose.css +163 -0
- package/dist/themes/teal-rose.d.ts +6 -0
- package/dist/themes/teal-rose.js +151 -0
- package/dist/themes/violet-lime.css +163 -0
- package/dist/themes/violet-lime.d.ts +6 -0
- package/dist/themes/violet-lime.js +151 -0
- package/dist/utils/design-tokens.d.ts +43 -0
- package/dist/utils/design-tokens.js +100 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +22 -2
|
@@ -1,20 +1,158 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
BUTTON GROUP RADIO COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-button-group-radius: 0; }
|
|
4
|
+
Override locally: <ButtonGroupRadio style="--stuic-button-group-radius: 9999px;">
|
|
5
|
+
============================================================================ */
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
:root {
|
|
8
|
+
/* Structure - reference Tailwind vars where applicable */
|
|
9
|
+
--stuic-button-group-radius: var(--radius-md);
|
|
10
|
+
--stuic-button-group-padding: 0.375rem;
|
|
11
|
+
--stuic-button-group-gap: 0.25rem;
|
|
12
|
+
--stuic-button-group-border-width: 1px;
|
|
13
|
+
--stuic-button-group-transition: 150ms;
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
--
|
|
15
|
+
/* Button sizing - 44px is Apple HIG minimum touch target */
|
|
16
|
+
--stuic-button-group-button-padding-x: 0.75rem;
|
|
17
|
+
--stuic-button-group-button-padding-y: 0.5rem;
|
|
18
|
+
--stuic-button-group-button-min-height: 2.75rem; /* 44px */
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
--
|
|
20
|
+
/* Focus ring */
|
|
21
|
+
--stuic-button-group-ring-width: 3px;
|
|
22
|
+
--stuic-button-group-ring-color: var(--stuic-color-ring);
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
--
|
|
24
|
+
/* Container colors - theme vars */
|
|
25
|
+
--stuic-button-group-bg: var(--stuic-color-surface);
|
|
26
|
+
--stuic-button-group-text: var(--stuic-color-foreground);
|
|
27
|
+
--stuic-button-group-border: var(--stuic-color-border);
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
/* Button colors (inactive) */
|
|
30
|
+
--stuic-button-group-button-bg: transparent;
|
|
31
|
+
--stuic-button-group-button-text: var(--stuic-color-foreground);
|
|
32
|
+
--stuic-button-group-button-bg-hover: var(--stuic-color-muted);
|
|
33
|
+
--stuic-button-group-button-text-hover: var(--stuic-color-foreground);
|
|
34
|
+
|
|
35
|
+
/* Button colors (active) */
|
|
36
|
+
--stuic-button-group-button-bg-active: var(--stuic-color-primary);
|
|
37
|
+
--stuic-button-group-button-text-active: var(--stuic-color-primary-foreground);
|
|
38
|
+
--stuic-button-group-button-bg-active-hover: var(--stuic-color-primary-hover);
|
|
39
|
+
--stuic-button-group-button-text-active-hover: var(--stuic-color-primary-foreground-hover);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ============================================================================
|
|
43
|
+
BASE STYLES
|
|
44
|
+
============================================================================ */
|
|
45
|
+
|
|
46
|
+
.stuic-button-group {
|
|
47
|
+
/* Layout */
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: space-between;
|
|
51
|
+
gap: var(--stuic-button-group-gap);
|
|
52
|
+
width: 100%;
|
|
53
|
+
|
|
54
|
+
/* Box model */
|
|
55
|
+
padding: var(--stuic-button-group-padding);
|
|
56
|
+
border-width: var(--stuic-button-group-border-width);
|
|
57
|
+
border-style: solid;
|
|
58
|
+
border-radius: var(--stuic-button-group-radius);
|
|
59
|
+
|
|
60
|
+
/* Colors */
|
|
61
|
+
background: var(--stuic-button-group-bg);
|
|
62
|
+
color: var(--stuic-button-group-text);
|
|
63
|
+
border-color: var(--stuic-button-group-border);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Focus-within state */
|
|
67
|
+
.stuic-button-group:focus-within {
|
|
68
|
+
outline: var(--stuic-button-group-ring-width) solid var(--stuic-button-group-ring-color);
|
|
69
|
+
outline-offset: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ============================================================================
|
|
73
|
+
SIZE VARIANTS
|
|
74
|
+
============================================================================ */
|
|
75
|
+
|
|
76
|
+
.stuic-button-group[data-size="sm"] {
|
|
77
|
+
--stuic-button-group-padding: 0.25rem;
|
|
78
|
+
--stuic-button-group-gap: 0.125rem;
|
|
79
|
+
--stuic-button-group-button-padding-x: 0.5rem;
|
|
80
|
+
--stuic-button-group-button-padding-y: 0.375rem;
|
|
81
|
+
--stuic-button-group-button-min-height: 2.25rem; /* 36px - still touch-friendly */
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.stuic-button-group[data-size="lg"] {
|
|
85
|
+
--stuic-button-group-padding: 0.5rem;
|
|
86
|
+
--stuic-button-group-gap: 0.375rem;
|
|
87
|
+
--stuic-button-group-button-padding-x: 1rem;
|
|
88
|
+
--stuic-button-group-button-padding-y: 0.625rem;
|
|
89
|
+
--stuic-button-group-button-min-height: 3rem; /* 48px */
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ============================================================================
|
|
93
|
+
BUTTON STYLES
|
|
94
|
+
============================================================================ */
|
|
95
|
+
|
|
96
|
+
.stuic-button-group-button {
|
|
97
|
+
/* Layout */
|
|
98
|
+
display: inline-flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
flex: 1;
|
|
102
|
+
|
|
103
|
+
/* Box model - mobile-friendly touch targets */
|
|
104
|
+
padding: var(--stuic-button-group-button-padding-y) var(--stuic-button-group-button-padding-x);
|
|
105
|
+
min-height: var(--stuic-button-group-button-min-height);
|
|
106
|
+
border: none;
|
|
107
|
+
border-radius: var(--stuic-button-group-radius);
|
|
108
|
+
|
|
109
|
+
/* Typography */
|
|
110
|
+
white-space: nowrap;
|
|
111
|
+
line-height: 1;
|
|
112
|
+
text-align: center;
|
|
113
|
+
|
|
114
|
+
/* Colors */
|
|
115
|
+
background: var(--stuic-button-group-button-bg);
|
|
116
|
+
color: var(--stuic-button-group-button-text);
|
|
117
|
+
|
|
118
|
+
/* Interaction - mobile-friendly */
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
user-select: none;
|
|
121
|
+
-webkit-tap-highlight-color: transparent;
|
|
122
|
+
touch-action: manipulation;
|
|
123
|
+
|
|
124
|
+
/* Transition */
|
|
125
|
+
transition:
|
|
126
|
+
background var(--stuic-button-group-transition),
|
|
127
|
+
color var(--stuic-button-group-transition);
|
|
128
|
+
|
|
129
|
+
/* Focus */
|
|
130
|
+
outline: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.stuic-button-group-button:hover:not(:disabled):not([aria-checked="true"]) {
|
|
134
|
+
background: var(--stuic-button-group-button-bg-hover);
|
|
135
|
+
color: var(--stuic-button-group-button-text-hover);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.stuic-button-group-button:focus-visible {
|
|
139
|
+
outline: none; /* Focus handled by container */
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Active/selected button */
|
|
143
|
+
.stuic-button-group-button[aria-checked="true"] {
|
|
144
|
+
background: var(--stuic-button-group-button-bg-active);
|
|
145
|
+
color: var(--stuic-button-group-button-text-active);
|
|
146
|
+
box-shadow: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.stuic-button-group-button[aria-checked="true"]:hover:not(:disabled) {
|
|
150
|
+
background: var(--stuic-button-group-button-bg-active-hover);
|
|
151
|
+
color: var(--stuic-button-group-button-text-active-hover);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Disabled state */
|
|
155
|
+
.stuic-button-group-button:disabled {
|
|
156
|
+
opacity: 0.5;
|
|
157
|
+
cursor: not-allowed;
|
|
20
158
|
}
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
classContent?: string;
|
|
24
24
|
/** Toggle button class */
|
|
25
25
|
classToggle?: string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/** Inline styles (for CSS variable overrides) */
|
|
27
|
+
style?: string;
|
|
28
28
|
/** Bind reference to container element */
|
|
29
29
|
el?: HTMLDivElement;
|
|
30
30
|
/** Optional translate function */
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
i18nSpanWrap: boolean = true
|
|
40
40
|
) {
|
|
41
41
|
const m: Record<string, string> = {
|
|
42
|
-
more: "
|
|
43
|
-
less: "
|
|
42
|
+
more: "More...",
|
|
43
|
+
less: "Less...",
|
|
44
44
|
};
|
|
45
45
|
let out = m[k] ?? fallback ?? k;
|
|
46
46
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
class: classProp,
|
|
59
59
|
classContent,
|
|
60
60
|
classToggle,
|
|
61
|
-
|
|
61
|
+
style,
|
|
62
62
|
el = $bindable(),
|
|
63
63
|
t = t_default,
|
|
64
64
|
}: Props = $props();
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
bind:this={el}
|
|
87
87
|
bind:clientWidth={containerWidth}
|
|
88
88
|
class={twMerge("stuic-collapsible", classProp)}
|
|
89
|
+
{style}
|
|
89
90
|
>
|
|
90
91
|
<div class="flex items-end">
|
|
91
92
|
<div
|
|
@@ -98,8 +99,7 @@
|
|
|
98
99
|
<button
|
|
99
100
|
type="button"
|
|
100
101
|
class={twMerge(
|
|
101
|
-
|
|
102
|
-
"hover:opacity-100 cursor-pointer px-2 py-1 -my-1 -mr-2",
|
|
102
|
+
"stuic-collapsible-toggle cursor-pointer -my-1 -mr-2",
|
|
103
103
|
classToggle
|
|
104
104
|
)}
|
|
105
105
|
onclick={() => (expanded = !expanded)}
|
|
@@ -17,8 +17,8 @@ export interface Props {
|
|
|
17
17
|
classContent?: string;
|
|
18
18
|
/** Toggle button class */
|
|
19
19
|
classToggle?: string;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/** Inline styles (for CSS variable overrides) */
|
|
21
|
+
style?: string;
|
|
22
22
|
/** Bind reference to container element */
|
|
23
23
|
el?: HTMLDivElement;
|
|
24
24
|
/** Optional translate function */
|
|
@@ -14,8 +14,9 @@ A component that truncates content to a specified number of lines with an expand
|
|
|
14
14
|
| `class` | `string` | - | Container element class |
|
|
15
15
|
| `classContent` | `string` | - | Content wrapper class |
|
|
16
16
|
| `classToggle` | `string` | - | Toggle button class |
|
|
17
|
-
| `
|
|
17
|
+
| `style` | `string` | - | Inline styles (for CSS variable overrides) |
|
|
18
18
|
| `el` | `HTMLDivElement`| - | Bind reference to container element |
|
|
19
|
+
| `t` | `TranslateFn` | - | Optional translate function |
|
|
19
20
|
|
|
20
21
|
## Usage
|
|
21
22
|
|
|
@@ -74,8 +75,39 @@ A component that truncates content to a specified number of lines with an expand
|
|
|
74
75
|
class="bg-gray-100 p-4 rounded"
|
|
75
76
|
classContent="text-sm text-gray-600"
|
|
76
77
|
classToggle="text-blue-500 font-bold"
|
|
77
|
-
toggleOpacity="opacity-100"
|
|
78
78
|
>
|
|
79
79
|
Styled collapsible content.
|
|
80
80
|
</Collapsible>
|
|
81
81
|
```
|
|
82
|
+
|
|
83
|
+
### CSS Variable Overrides
|
|
84
|
+
|
|
85
|
+
```svelte
|
|
86
|
+
<!-- Local override via inline style -->
|
|
87
|
+
<Collapsible style="--stuic-collapsible-toggle-opacity: 1;">
|
|
88
|
+
Always fully visible toggle button.
|
|
89
|
+
</Collapsible>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## CSS Variables
|
|
93
|
+
|
|
94
|
+
Override to customize appearance:
|
|
95
|
+
|
|
96
|
+
| Variable | Default | Description |
|
|
97
|
+
|----------|---------|-------------|
|
|
98
|
+
| `--stuic-collapsible-toggle-opacity` | `0.7` | Toggle button opacity |
|
|
99
|
+
| `--stuic-collapsible-toggle-opacity-hover` | `1` | Hover opacity |
|
|
100
|
+
| `--stuic-collapsible-toggle-padding-x` | `calc(var(--spacing) * 2)` | Horizontal padding |
|
|
101
|
+
| `--stuic-collapsible-toggle-padding-y` | `0.25rem` | Vertical padding |
|
|
102
|
+
| `--stuic-collapsible-transition` | `150ms` | Transition duration |
|
|
103
|
+
| `--stuic-collapsible-ring-width` | `2px` | Focus ring width |
|
|
104
|
+
| `--stuic-collapsible-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
105
|
+
|
|
106
|
+
### Global Override
|
|
107
|
+
|
|
108
|
+
```css
|
|
109
|
+
:root {
|
|
110
|
+
--stuic-collapsible-toggle-opacity: 0.5;
|
|
111
|
+
--stuic-collapsible-toggle-opacity-hover: 0.8;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
COLLAPSIBLE COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-collapsible-toggle-opacity: 0.5; }
|
|
4
|
+
Override locally: <Collapsible style="--stuic-collapsible-toggle-opacity: 1;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Toggle button styling */
|
|
9
|
+
--stuic-collapsible-toggle-opacity: 0.7;
|
|
10
|
+
--stuic-collapsible-toggle-opacity-hover: 1;
|
|
11
|
+
--stuic-collapsible-toggle-padding-x: calc(var(--spacing) * 2);
|
|
12
|
+
--stuic-collapsible-toggle-padding-y: calc(var(--spacing) * 1);
|
|
13
|
+
--stuic-collapsible-transition: 150ms;
|
|
14
|
+
|
|
15
|
+
/* Focus ring (uses theme token) */
|
|
16
|
+
--stuic-collapsible-ring-width: 2px;
|
|
17
|
+
--stuic-collapsible-ring-color: var(--stuic-color-ring);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ============================================================================
|
|
21
|
+
TOGGLE BUTTON STYLES
|
|
22
|
+
============================================================================ */
|
|
23
|
+
|
|
24
|
+
.stuic-collapsible-toggle {
|
|
25
|
+
opacity: var(--stuic-collapsible-toggle-opacity);
|
|
26
|
+
padding: var(--stuic-collapsible-toggle-padding-y)
|
|
27
|
+
var(--stuic-collapsible-toggle-padding-x);
|
|
28
|
+
transition: opacity var(--stuic-collapsible-transition);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.stuic-collapsible-toggle:hover {
|
|
32
|
+
opacity: var(--stuic-collapsible-toggle-opacity-hover);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.stuic-collapsible-toggle:focus-visible {
|
|
36
|
+
outline: var(--stuic-collapsible-ring-width) solid var(--stuic-collapsible-ring-color);
|
|
37
|
+
outline-offset: 2px;
|
|
38
|
+
}
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
</script>
|
|
63
63
|
|
|
64
64
|
<script lang="ts">
|
|
65
|
+
import Button from "../Button/Button.svelte";
|
|
65
66
|
import "./index.css";
|
|
66
67
|
|
|
67
68
|
const clog = createClog("CommandMenu");
|
|
@@ -251,8 +252,8 @@
|
|
|
251
252
|
placeholder={searchPlaceholder ?? t("search_placeholder")}
|
|
252
253
|
classInputBoxWrap={twMerge(
|
|
253
254
|
// always look like focused
|
|
254
|
-
`border-primary border-
|
|
255
|
-
`ring-
|
|
255
|
+
`border-primary border-(--stuic-input-accent)`,
|
|
256
|
+
`ring-(--stuic-input-accent)/20 ring-4`
|
|
256
257
|
)}
|
|
257
258
|
onkeydown={(e) => {
|
|
258
259
|
if (e.key === "Enter") {
|
|
@@ -262,25 +263,22 @@
|
|
|
262
263
|
}}
|
|
263
264
|
>
|
|
264
265
|
{#snippet inputBefore()}
|
|
265
|
-
<div class="flex flex-col items-center justify-center pl-3
|
|
266
|
+
<div class="flex flex-col items-center justify-center pl-3 stuic-command-menu-muted">
|
|
266
267
|
{@html iconSearch({ size: 19, strokeWidth: 3 })}
|
|
267
268
|
</div>
|
|
268
269
|
{/snippet}
|
|
269
270
|
{#snippet inputAfter()}
|
|
270
|
-
<div class="flex pl-2 items-center justify-center
|
|
271
|
+
<div class="flex pl-2 items-center justify-center stuic-command-menu-placeholder">
|
|
271
272
|
{#if isFetching}
|
|
272
273
|
<Spinner class="w-4" />
|
|
273
274
|
{/if}
|
|
274
275
|
</div>
|
|
275
276
|
<div class="flex items-center justify-center">
|
|
276
|
-
<
|
|
277
|
+
<Button
|
|
278
|
+
x
|
|
279
|
+
variant="ghost"
|
|
280
|
+
roundedFull
|
|
277
281
|
type="button"
|
|
278
|
-
class={twMerge(
|
|
279
|
-
"rounded m-1 opacity-75",
|
|
280
|
-
"hover:opacity-100 hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
|
281
|
-
"focus-visible:opacity-100 focus-visible:outline-0",
|
|
282
|
-
"focus-visible:bg-neutral-200 dark:focus-visible:bg-neutral-800"
|
|
283
|
-
)}
|
|
284
282
|
onclick={(e) => {
|
|
285
283
|
e.preventDefault();
|
|
286
284
|
if (!`${q || ""}`.trim()) {
|
|
@@ -289,9 +287,7 @@
|
|
|
289
287
|
q = "";
|
|
290
288
|
input?.focus();
|
|
291
289
|
}}
|
|
292
|
-
|
|
293
|
-
<X class="m-2 size-6" />
|
|
294
|
-
</button>
|
|
290
|
+
/>
|
|
295
291
|
</div>
|
|
296
292
|
{/snippet}
|
|
297
293
|
{#snippet inputBelow()}
|
|
@@ -304,10 +300,9 @@
|
|
|
304
300
|
<div
|
|
305
301
|
class={twMerge(
|
|
306
302
|
"stuic-command-menu-options",
|
|
307
|
-
"
|
|
303
|
+
"block space-y-1 p-1",
|
|
308
304
|
"overflow-y-auto overflow-x-hidden mb-1",
|
|
309
|
-
"border-t
|
|
310
|
-
"max-h-60"
|
|
305
|
+
"border-t"
|
|
311
306
|
)}
|
|
312
307
|
bind:this={optionsBox}
|
|
313
308
|
tabindex="-1"
|
|
@@ -320,10 +315,7 @@
|
|
|
320
315
|
<div class="p-1">
|
|
321
316
|
{#if _optgroup}
|
|
322
317
|
<div
|
|
323
|
-
class=
|
|
324
|
-
"mb-1 p-1 text-xs font-semibold uppercase tracking-wide",
|
|
325
|
-
"text-neutral-500 dark:text-neutral-400",
|
|
326
|
-
]}
|
|
318
|
+
class="stuic-command-menu-group-header mb-1 p-1 font-semibold uppercase tracking-wide"
|
|
327
319
|
>
|
|
328
320
|
{_optgroup}
|
|
329
321
|
</div>
|
|
@@ -369,9 +361,6 @@
|
|
|
369
361
|
</ModalDialog>
|
|
370
362
|
|
|
371
363
|
<style>
|
|
372
|
-
.options {
|
|
373
|
-
scrollbar-width: thin;
|
|
374
|
-
}
|
|
375
364
|
.sr-only {
|
|
376
365
|
position: absolute;
|
|
377
366
|
width: 1px;
|
|
@@ -93,6 +93,45 @@ The command menu uses `ModalDialog` internally with top-aligned positioning:
|
|
|
93
93
|
- **Mobile**: Input at top of screen with 1rem margins from edges
|
|
94
94
|
- **Desktop (md+)**: Input positioned at ~20% from top, max-width 768px, centered horizontally
|
|
95
95
|
|
|
96
|
+
## CSS Variables
|
|
97
|
+
|
|
98
|
+
All styling can be customized via CSS variables. Define them on a parent element or in `:root` to override defaults.
|
|
99
|
+
|
|
100
|
+
### Structure Tokens
|
|
101
|
+
|
|
102
|
+
| Variable | Default | Description |
|
|
103
|
+
|----------|---------|-------------|
|
|
104
|
+
| `--stuic-command-menu-transition` | `150ms` | Transition duration for hover/focus states |
|
|
105
|
+
| `--stuic-command-menu-options-max-height` | `15rem` | Maximum height of options container |
|
|
106
|
+
|
|
107
|
+
### Color Tokens
|
|
108
|
+
|
|
109
|
+
| Variable | Default | Description |
|
|
110
|
+
|----------|---------|-------------|
|
|
111
|
+
| `--stuic-command-menu-divider-color` | `var(--stuic-color-border)` | Border color between input and options |
|
|
112
|
+
| `--stuic-command-menu-group-header-color` | `var(--stuic-color-muted-foreground)` | Option group header text color |
|
|
113
|
+
| `--stuic-command-menu-group-header-font-size` | `var(--text-xs)` | Option group header font size |
|
|
114
|
+
|
|
115
|
+
### Custom Theme Example
|
|
116
|
+
|
|
117
|
+
```svelte
|
|
118
|
+
<div style="
|
|
119
|
+
--stuic-command-menu-divider-color: var(--color-blue-200);
|
|
120
|
+
">
|
|
121
|
+
<CommandMenu ... />
|
|
122
|
+
</div>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Global Theme Override
|
|
126
|
+
|
|
127
|
+
```css
|
|
128
|
+
/* In your app.css */
|
|
129
|
+
:root {
|
|
130
|
+
--stuic-command-menu-options-max-height: 20rem;
|
|
131
|
+
--stuic-command-menu-group-header-color: var(--color-indigo-500);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
96
135
|
## Keyboard Navigation
|
|
97
136
|
|
|
98
137
|
- **Arrow Up/Down**: Navigate options
|
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
COMMAND MENU COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-command-menu-divider-color: red; }
|
|
4
|
+
Override locally: <div style="--stuic-command-menu-divider-color: blue;">
|
|
5
|
+
============================================================================= */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Structure */
|
|
9
|
+
--stuic-command-menu-transition: 150ms;
|
|
10
|
+
--stuic-command-menu-options-max-height: 15rem;
|
|
11
|
+
|
|
12
|
+
/* Colors */
|
|
13
|
+
--stuic-command-menu-divider-color: var(--stuic-color-border);
|
|
14
|
+
--stuic-command-menu-group-header-color: var(--stuic-color-muted-foreground);
|
|
15
|
+
--stuic-command-menu-group-header-font-size: var(--text-xs);
|
|
16
|
+
|
|
17
|
+
/* Muted/placeholder states (align with FieldOptions) */
|
|
18
|
+
--stuic-command-menu-muted-text: var(--stuic-color-muted-foreground);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* =============================================================================
|
|
22
|
+
BASE STYLES
|
|
23
|
+
============================================================================= */
|
|
24
|
+
|
|
25
|
+
/* Options container */
|
|
26
|
+
.stuic-command-menu-options {
|
|
27
|
+
scrollbar-width: thin;
|
|
28
|
+
max-height: var(--stuic-command-menu-options-max-height);
|
|
29
|
+
border-top-color: var(--stuic-command-menu-divider-color);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Option group header */
|
|
33
|
+
.stuic-command-menu-group-header {
|
|
34
|
+
color: var(--stuic-command-menu-group-header-color);
|
|
35
|
+
font-size: var(--stuic-command-menu-group-header-font-size);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Muted elements (search icon) */
|
|
39
|
+
.stuic-command-menu-muted {
|
|
40
|
+
color: var(--stuic-command-menu-muted-text);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Placeholder/loading states (spinner) */
|
|
44
|
+
.stuic-command-menu-placeholder {
|
|
45
|
+
color: var(--stuic-command-menu-muted-text);
|
|
3
46
|
}
|
|
@@ -1,92 +1,95 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
import type { TW_COLORS } from "../../types.js";
|
|
3
2
|
import type { THC } from "../Thc/Thc.svelte";
|
|
4
3
|
|
|
4
|
+
export type MessageIntent = "destructive" | "warning" | "success" | "info";
|
|
5
|
+
|
|
5
6
|
export interface Props {
|
|
6
7
|
class?: string;
|
|
7
8
|
classContent?: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
theme?: TW_COLORS;
|
|
9
|
+
classIcon?: string;
|
|
10
|
+
message: THC | Error | undefined | null;
|
|
11
|
+
intent?: MessageIntent;
|
|
12
12
|
forceAsHtml?: boolean;
|
|
13
13
|
duration?: number;
|
|
14
14
|
onDismiss?: (() => void) | null | false;
|
|
15
|
+
withIcon?: boolean;
|
|
16
|
+
iconFn?: (() => string) | false;
|
|
15
17
|
}
|
|
16
18
|
</script>
|
|
17
19
|
|
|
18
20
|
<script lang="ts">
|
|
19
21
|
import { slide } from "svelte/transition";
|
|
20
|
-
import Thc, { isTHCNotEmpty } from "../Thc/Thc.svelte";
|
|
21
|
-
import X from "../X/X.svelte";
|
|
22
22
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
23
|
+
import Thc, { isTHCNotEmpty } from "../Thc/Thc.svelte";
|
|
24
|
+
import Button from "../Button/Button.svelte";
|
|
25
|
+
import {
|
|
26
|
+
iconAlertWarning,
|
|
27
|
+
iconAlertSuccess,
|
|
28
|
+
iconAlertInfo,
|
|
29
|
+
iconAlertError,
|
|
30
|
+
} from "../../icons/index.js";
|
|
23
31
|
|
|
24
32
|
import "./index.css";
|
|
25
33
|
|
|
34
|
+
const INTENT_ICONS: Record<MessageIntent, () => string> = {
|
|
35
|
+
destructive: () => iconAlertError({ size: 29 }),
|
|
36
|
+
warning: () => iconAlertWarning({ size: 29 }),
|
|
37
|
+
success: () => iconAlertSuccess({ size: 29 }),
|
|
38
|
+
info: () => iconAlertInfo({ size: 29 }),
|
|
39
|
+
};
|
|
40
|
+
|
|
26
41
|
let {
|
|
27
42
|
class: classProps,
|
|
28
43
|
classContent,
|
|
29
|
-
|
|
30
|
-
classX,
|
|
44
|
+
classIcon,
|
|
31
45
|
message,
|
|
32
|
-
|
|
46
|
+
intent,
|
|
33
47
|
forceAsHtml = true,
|
|
34
48
|
duration = 150,
|
|
35
49
|
onDismiss = () => (message = ""),
|
|
50
|
+
withIcon,
|
|
51
|
+
iconFn,
|
|
36
52
|
}: Props = $props();
|
|
37
53
|
|
|
38
|
-
let _message = $derived(
|
|
54
|
+
let _message = $derived(message ? String(message) : "");
|
|
39
55
|
let _show = $derived(isTHCNotEmpty(_message));
|
|
56
|
+
|
|
57
|
+
let _iconHtml = $derived.by(() => {
|
|
58
|
+
if (iconFn === false) return "";
|
|
59
|
+
if (typeof iconFn === "function") return iconFn();
|
|
60
|
+
if (withIcon && intent) return INTENT_ICONS[intent]?.();
|
|
61
|
+
return "";
|
|
62
|
+
});
|
|
40
63
|
</script>
|
|
41
64
|
|
|
42
65
|
{#if _show}
|
|
43
66
|
<div
|
|
44
|
-
class={twMerge(
|
|
45
|
-
|
|
46
|
-
`mb-4 rounded flex text-sm
|
|
47
|
-
bg-dismiss-bg dark:bg-dismiss-bg-dark
|
|
48
|
-
border-dismiss-border dark:border-dismiss-border-dark
|
|
49
|
-
text-dismiss-text dark:text-dismiss-text-dark`,
|
|
50
|
-
classProps
|
|
51
|
-
)}
|
|
52
|
-
style={theme
|
|
53
|
-
? `
|
|
54
|
-
--color-dismiss-bg: var(--color-${theme}-100);
|
|
55
|
-
--color-dismiss-bg-dark: var(--color-${theme}-700);
|
|
56
|
-
|
|
57
|
-
--color-dismiss-text: var(--color-${theme}-800);
|
|
58
|
-
--color-dismiss-text-dark: var(--color-${theme}-50);
|
|
59
|
-
|
|
60
|
-
--color-dismiss-border: var(--color-${theme}-500);
|
|
61
|
-
--color-dismiss-border-dark: var(--color-${theme}-500);
|
|
62
|
-
`
|
|
63
|
-
: ``}
|
|
67
|
+
class={twMerge("stuic-dismissible-message", "mb-4", classProps)}
|
|
68
|
+
data-intent={intent}
|
|
64
69
|
transition:slide={{ duration }}
|
|
65
70
|
>
|
|
66
|
-
|
|
71
|
+
{#if _iconHtml}
|
|
72
|
+
<div class={twMerge("icon", classIcon)}>
|
|
73
|
+
{@html _iconHtml}
|
|
74
|
+
</div>
|
|
75
|
+
{/if}
|
|
76
|
+
|
|
77
|
+
<div class={twMerge("content", classContent)}>
|
|
67
78
|
<Thc thc={_message} {forceAsHtml} />
|
|
68
79
|
</div>
|
|
69
80
|
|
|
70
81
|
{#if typeof onDismiss === "function"}
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
group`,
|
|
81
|
-
classDismiss
|
|
82
|
-
)}
|
|
83
|
-
type="button"
|
|
84
|
-
>
|
|
85
|
-
<X
|
|
86
|
-
class={twMerge("x", "opacity-75 group-hover:opacity-100", classX)}
|
|
87
|
-
strokeWidth={1.5}
|
|
82
|
+
<div class="dismiss">
|
|
83
|
+
<Button
|
|
84
|
+
x
|
|
85
|
+
class="text-inherit!"
|
|
86
|
+
variant="ghost"
|
|
87
|
+
roundedFull
|
|
88
|
+
size="sm"
|
|
89
|
+
type="button"
|
|
90
|
+
onclick={() => onDismiss()}
|
|
88
91
|
/>
|
|
89
|
-
</
|
|
92
|
+
</div>
|
|
90
93
|
{/if}
|
|
91
94
|
</div>
|
|
92
95
|
{/if}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type { TW_COLORS } from "../../types.js";
|
|
2
1
|
import type { THC } from "../Thc/Thc.svelte";
|
|
2
|
+
export type MessageIntent = "destructive" | "warning" | "success" | "info";
|
|
3
3
|
export interface Props {
|
|
4
4
|
class?: string;
|
|
5
5
|
classContent?: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
theme?: TW_COLORS;
|
|
6
|
+
classIcon?: string;
|
|
7
|
+
message: THC | Error | undefined | null;
|
|
8
|
+
intent?: MessageIntent;
|
|
10
9
|
forceAsHtml?: boolean;
|
|
11
10
|
duration?: number;
|
|
12
11
|
onDismiss?: (() => void) | null | false;
|
|
12
|
+
withIcon?: boolean;
|
|
13
|
+
iconFn?: (() => string) | false;
|
|
13
14
|
}
|
|
14
15
|
import "./index.css";
|
|
15
16
|
declare const DismissibleMessage: import("svelte").Component<Props, {}, "">;
|