@happyvertical/smrt-svelte 0.55.6 → 0.55.7
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/AGENTS.md +17 -2
- package/README.md +83 -4
- package/agents/workspace.md +202 -5
- package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -1
- package/dist/browser-ai/adapters/stt/dictation-source.js +8 -2
- package/dist/browser-ai/adapters/stt/factory.d.ts +6 -3
- package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -1
- package/dist/browser-ai/adapters/stt/factory.js +19 -9
- package/dist/browser-ai/adapters/stt/index.d.ts +3 -1
- package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
- package/dist/browser-ai/adapters/stt/index.js +3 -1
- package/dist/browser-ai/adapters/stt/local-speech-model.d.ts +111 -0
- package/dist/browser-ai/adapters/stt/local-speech-model.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/local-speech-model.js +377 -0
- package/dist/browser-ai/adapters/stt/local-speech.d.ts +82 -0
- package/dist/browser-ai/adapters/stt/local-speech.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/local-speech.js +317 -0
- package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts +2 -0
- package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/local-speech.worker.js +32 -0
- package/dist/browser-ai/adapters/stt/transformers-env.d.ts +8 -0
- package/dist/browser-ai/adapters/stt/transformers-env.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/transformers-env.js +17 -0
- package/dist/browser-ai/adapters/stt/types.d.ts +54 -9
- package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -1
- package/dist/browser-ai/capabilities/speech-probe.d.ts +29 -0
- package/dist/browser-ai/capabilities/speech-probe.d.ts.map +1 -0
- package/dist/browser-ai/capabilities/speech-probe.js +19 -0
- package/dist/browser-ai/core/errors.d.ts +9 -0
- package/dist/browser-ai/core/errors.d.ts.map +1 -1
- package/dist/browser-ai/core/errors.js +12 -0
- package/dist/browser-ai/index.d.ts +1 -0
- package/dist/browser-ai/index.d.ts.map +1 -1
- package/dist/browser-ai/index.js +1 -0
- package/dist/components/app/AppShell.svelte +587 -33
- package/dist/components/app/AppShell.svelte.d.ts +75 -4
- package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
- package/dist/components/app/DockToggles.svelte +0 -1
- package/dist/components/app/dock-toggle.d.ts +7 -1
- package/dist/components/app/dock-toggle.d.ts.map +1 -1
- package/dist/components/app/index.d.ts +2 -1
- package/dist/components/app/index.d.ts.map +1 -1
- package/dist/components/app/slot-item.d.ts +21 -0
- package/dist/components/app/slot-item.d.ts.map +1 -0
- package/dist/components/app/slot-item.js +1 -0
- package/dist/components/forms/AddressInput.svelte +2 -15
- package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/DateRangeInput.svelte +2 -15
- package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/DateTimeInput.svelte +8 -15
- package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/MeasurementInput.svelte +2 -15
- package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/MoneyInput.svelte +8 -25
- package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/NumberInput.svelte +7 -15
- package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/PhoneInput.svelte +7 -15
- package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/SelectInput.svelte +34 -40
- package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/TextInput.svelte +20 -37
- package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
- package/dist/components/forms/TextareaInput.svelte +19 -35
- package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
- package/dist/components/sortable/Sortable.svelte +6 -33
- package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -1
- package/dist/components/sortable/announce.d.ts +5 -0
- package/dist/components/sortable/announce.d.ts.map +1 -0
- package/dist/components/sortable/announce.js +33 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte +424 -58
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +27 -2
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/ShellBrand.svelte +86 -0
- package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts +18 -0
- package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellIconButton.svelte +77 -0
- package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts +26 -0
- package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +88 -16
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/ShellNavEditor.svelte +531 -0
- package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts +26 -0
- package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte +57 -0
- package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts +22 -0
- package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte +290 -0
- package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts +32 -0
- package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/TenantNav.svelte +67 -2
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -0
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +67 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/layout-controller.svelte.js +129 -1
- package/dist/components/workspace/admin-shell/layout.d.ts +122 -0
- package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/layout.js +410 -10
- package/dist/components/workspace/admin-shell/settings.js +4 -4
- package/dist/components/workspace/admin-shell/shell-icons.d.ts +45 -0
- package/dist/components/workspace/admin-shell/shell-icons.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/shell-icons.js +66 -0
- package/dist/components/workspace/admin-shell/slot-labels.d.ts +33 -0
- package/dist/components/workspace/admin-shell/slot-labels.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/slot-labels.js +33 -0
- package/dist/components/workspace/admin-shell/slots.d.ts +50 -0
- package/dist/components/workspace/admin-shell/slots.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/slots.js +77 -0
- package/dist/components/workspace/admin-shell/state.svelte.d.ts +35 -0
- package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/state.svelte.js +111 -0
- package/dist/components/workspace/admin-shell/types.d.ts +101 -1
- package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
- package/dist/components/workspace/index.d.ts +9 -2
- package/dist/components/workspace/index.d.ts.map +1 -1
- package/dist/components/workspace/index.js +6 -0
- package/dist/i18n/strings.workspace.d.ts +49 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -1
- package/dist/i18n/strings.workspace.js +49 -0
- package/package.json +30 -19
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +0 -51
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +0 -1
- package/dist/browser-ai/adapters/stt/whisper-wasm.js +0 -380
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The navigation as it looks on the site, made editable in place: every
|
|
4
|
+
* section heading and item has a grip (drag, or Space plus arrow keys), section
|
|
5
|
+
* headings carry overlay icon buttons (show, title, host actions) and open a
|
|
6
|
+
* floating toolbar (rename, title, hide, delete, host actions), and a "New
|
|
7
|
+
* section" button ends the list. Rendered by `AppShell` in place of
|
|
8
|
+
* `TenantNav` while the layout is being edited.
|
|
9
|
+
*/
|
|
10
|
+
import { Input } from '@happyvertical/smrt-ui/forms';
|
|
11
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
12
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
13
|
+
import { type Component, type Snippet, tick, untrack } from 'svelte';
|
|
14
|
+
import { M } from '../../../i18n/strings.workspace.js';
|
|
15
|
+
import Sortable from '../../sortable/Sortable.svelte';
|
|
16
|
+
import type {
|
|
17
|
+
SortableContainer,
|
|
18
|
+
SortableContainerMove,
|
|
19
|
+
SortableItem,
|
|
20
|
+
SortableItemMove,
|
|
21
|
+
} from '../../sortable/types.js';
|
|
22
|
+
import { useShellLayout } from './layout-context.js';
|
|
23
|
+
import type { ShellLayoutController } from './layout-controller.svelte.js';
|
|
24
|
+
import ShellIconButton from './ShellIconButton.svelte';
|
|
25
|
+
import ShellSectionIcon from './ShellSectionIcon.svelte';
|
|
26
|
+
import {
|
|
27
|
+
SHELL_DEFAULT_SECTION_ICON,
|
|
28
|
+
SHELL_SECTION_ICONS,
|
|
29
|
+
} from './shell-icons.js';
|
|
30
|
+
import type { ShellSectionActionsContext } from './types.js';
|
|
31
|
+
|
|
32
|
+
interface Props {
|
|
33
|
+
/** The layout API to edit; defaults to the nearest `AppShell`'s. */
|
|
34
|
+
controller?: ShellLayoutController;
|
|
35
|
+
/** Accessible name for the navigation landmark. */
|
|
36
|
+
'aria-label'?: string;
|
|
37
|
+
/** Component used to render icon names, as for `TenantNav`. */
|
|
38
|
+
iconComponent?: Component<{ name: string; size?: number }>;
|
|
39
|
+
/** Host icon buttons for each section (overlay and floating toolbar). */
|
|
40
|
+
sectionActions?: Snippet<[ShellSectionActionsContext]>;
|
|
41
|
+
/**
|
|
42
|
+
* `'sections'` edits only the sections (grip, rename, show, delete, icon
|
|
43
|
+
* picker); their entries are edited on the section's own page with
|
|
44
|
+
* `ShellSectionMenu`. Default `'items'`.
|
|
45
|
+
*/
|
|
46
|
+
navMode?: 'items' | 'sections';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let {
|
|
50
|
+
controller: provided,
|
|
51
|
+
'aria-label': ariaLabel,
|
|
52
|
+
iconComponent: IconComponent,
|
|
53
|
+
sectionActions,
|
|
54
|
+
navMode = 'items',
|
|
55
|
+
}: Props = $props();
|
|
56
|
+
|
|
57
|
+
const { t } = useI18n();
|
|
58
|
+
const layout = untrack(() => provided ?? useShellLayout());
|
|
59
|
+
|
|
60
|
+
interface NavContainer extends SortableContainer {
|
|
61
|
+
hidden: boolean;
|
|
62
|
+
root: boolean;
|
|
63
|
+
custom: boolean;
|
|
64
|
+
titleVisible: boolean;
|
|
65
|
+
count: number;
|
|
66
|
+
icon: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const sections = $derived(layout.sections);
|
|
70
|
+
const containers = $derived<NavContainer[]>(
|
|
71
|
+
sections.map((section) => ({
|
|
72
|
+
id: section.id,
|
|
73
|
+
label: section.heading ?? t(M['ui.shell_layout_editor.top_level']),
|
|
74
|
+
fixed: section.group === null,
|
|
75
|
+
root: section.group === null,
|
|
76
|
+
hidden: section.hidden,
|
|
77
|
+
custom: section.custom,
|
|
78
|
+
titleVisible: section.titleVisible,
|
|
79
|
+
count: section.items.length,
|
|
80
|
+
icon: section.icon ?? SHELL_DEFAULT_SECTION_ICON,
|
|
81
|
+
})),
|
|
82
|
+
);
|
|
83
|
+
const items = $derived<SortableItem[]>(
|
|
84
|
+
(navMode === 'sections' ? [] : sections).flatMap((section) =>
|
|
85
|
+
section.items.map((entry) => ({
|
|
86
|
+
id: entry.id,
|
|
87
|
+
containerId: section.id,
|
|
88
|
+
label: entry.label,
|
|
89
|
+
})),
|
|
90
|
+
),
|
|
91
|
+
);
|
|
92
|
+
const itemDefaults = $derived(
|
|
93
|
+
new Map(
|
|
94
|
+
sections.flatMap((section) =>
|
|
95
|
+
section.items.map((entry) => [entry.id, entry.defaultLabel] as const),
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
const itemIcons = $derived(
|
|
100
|
+
new Map(
|
|
101
|
+
sections.flatMap((section) =>
|
|
102
|
+
section.items.map((entry) => [entry.id, entry.item.icon] as const),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
let rootEl: HTMLElement | undefined = $state();
|
|
108
|
+
|
|
109
|
+
// One floating toolbar at a time. `pinned` (opened by click/tap) stays until
|
|
110
|
+
// dismissed; an unpinned one (opened by hovering with a fine pointer) closes
|
|
111
|
+
// when the pointer leaves the section.
|
|
112
|
+
let open = $state<{ id: string; pinned: boolean } | null>(null);
|
|
113
|
+
|
|
114
|
+
function canHover(): boolean {
|
|
115
|
+
return (
|
|
116
|
+
typeof window !== 'undefined' &&
|
|
117
|
+
window.matchMedia?.('(hover: hover) and (pointer: fine)').matches === true
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function sectionEl(id: string): HTMLElement | null {
|
|
122
|
+
return (
|
|
123
|
+
rootEl?.querySelector<HTMLElement>(
|
|
124
|
+
`[data-nav-section="${CSS.escape(id)}"]`,
|
|
125
|
+
) ?? null
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function onhover(id: string, event: PointerEvent): void {
|
|
130
|
+
if (event.pointerType === 'touch' || !canHover()) return;
|
|
131
|
+
if (open?.pinned) return;
|
|
132
|
+
open = { id, pinned: false };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function onactivate(id: string): Promise<void> {
|
|
136
|
+
if (open?.id === id && open.pinned) {
|
|
137
|
+
open = null;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
open = { id, pinned: true };
|
|
141
|
+
await tick();
|
|
142
|
+
const input = sectionEl(id)?.querySelector<HTMLInputElement>(
|
|
143
|
+
'input[data-section-name]',
|
|
144
|
+
);
|
|
145
|
+
input?.focus();
|
|
146
|
+
input?.select();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function onleave(id: string): void {
|
|
150
|
+
if (open?.id === id && !open.pinned) open = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function onkeydown(id: string, event: KeyboardEvent): void {
|
|
154
|
+
if (event.key !== 'Escape' || open?.id !== id) return;
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
event.stopPropagation();
|
|
157
|
+
// An open icon picker closes first; the toolbar stays.
|
|
158
|
+
if (iconPicker === id) {
|
|
159
|
+
iconPicker = null;
|
|
160
|
+
pickerButton(id)?.focus();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
open = null;
|
|
164
|
+
sectionEl(id)
|
|
165
|
+
?.querySelector<HTMLElement>('button[data-section-title]')
|
|
166
|
+
?.focus();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function onfocusout(id: string, event: FocusEvent): void {
|
|
170
|
+
if (open?.id !== id || !open.pinned) return;
|
|
171
|
+
const next = event.relatedTarget;
|
|
172
|
+
if (next instanceof Node && !sectionEl(id)?.contains(next)) open = null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
$effect(() => {
|
|
176
|
+
const current = open;
|
|
177
|
+
if (!current?.pinned || typeof document === 'undefined') return;
|
|
178
|
+
const dismiss = (event: PointerEvent) => {
|
|
179
|
+
if (
|
|
180
|
+
event.target instanceof Node &&
|
|
181
|
+
!sectionEl(current.id)?.contains(event.target)
|
|
182
|
+
) {
|
|
183
|
+
open = null;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
document.addEventListener('pointerdown', dismiss, true);
|
|
187
|
+
return () => document.removeEventListener('pointerdown', dismiss, true);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// The section whose icon picker is open (inside its toolbar).
|
|
191
|
+
let iconPicker = $state<string | null>(null);
|
|
192
|
+
|
|
193
|
+
function pickerButton(id: string): HTMLElement | null {
|
|
194
|
+
return (
|
|
195
|
+
sectionEl(id)?.querySelector<HTMLElement>('button[data-section-icon]') ??
|
|
196
|
+
null
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function chooseIcon(id: string, icon: string): void {
|
|
201
|
+
layout.setSectionIcon(id, icon);
|
|
202
|
+
iconPicker = null;
|
|
203
|
+
pickerButton(id)?.focus();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// The picker lives in the toolbar: it closes with it.
|
|
207
|
+
$effect(() => {
|
|
208
|
+
if (iconPicker !== null && open?.id !== iconPicker) iconPicker = null;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
function humanizeIcon(name: string): string {
|
|
212
|
+
return name.replace(/([A-Z])/g, ' $1').toLowerCase();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// The item whose name is being edited in place (one at a time).
|
|
216
|
+
let renamingItem = $state<string | null>(null);
|
|
217
|
+
|
|
218
|
+
async function startRenameItem(id: string): Promise<void> {
|
|
219
|
+
renamingItem = id;
|
|
220
|
+
await tick();
|
|
221
|
+
const input = rootEl?.querySelector<HTMLInputElement>(
|
|
222
|
+
`input[data-item-name="${CSS.escape(id)}"]`,
|
|
223
|
+
);
|
|
224
|
+
input?.focus();
|
|
225
|
+
input?.select();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function commitRenameItem(id: string, value: string): void {
|
|
229
|
+
// Blur after Escape or a save lands here too; only the open editor commits.
|
|
230
|
+
if (renamingItem !== id) return;
|
|
231
|
+
renamingItem = null;
|
|
232
|
+
layout.renameItem(id, value);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function cancelRenameItem(id: string): void {
|
|
236
|
+
renamingItem = null;
|
|
237
|
+
rootEl
|
|
238
|
+
?.querySelector<HTMLElement>(`button[data-item-rename="${CSS.escape(id)}"]`)
|
|
239
|
+
?.focus();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function setShown(id: string, shown: boolean): void {
|
|
243
|
+
if (shown) layout.show(id);
|
|
244
|
+
else layout.hide(id);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function rename(id: string, input: HTMLInputElement): void {
|
|
248
|
+
layout.renameSection(id, input.value);
|
|
249
|
+
// Blank or rejected input snaps back to the section's real name.
|
|
250
|
+
input.value = containers.find((entry) => entry.id === id)?.label ?? '';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function createSection(): Promise<void> {
|
|
254
|
+
const id = layout.createSection(
|
|
255
|
+
t(M['ui.shell_layout_editor.new_section_label']),
|
|
256
|
+
);
|
|
257
|
+
if (!id) return;
|
|
258
|
+
await tick();
|
|
259
|
+
await onactivate(id);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function deleteSection(id: string, label: string, count: number): void {
|
|
263
|
+
if (
|
|
264
|
+
count > 0 &&
|
|
265
|
+
!globalThis.confirm?.(
|
|
266
|
+
t(M['ui.shell_layout_editor.delete_confirm'], { label }),
|
|
267
|
+
)
|
|
268
|
+
) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
open = null;
|
|
272
|
+
layout.deleteSection(id);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function onmove(move: SortableItemMove<SortableItem, NavContainer>): void {
|
|
276
|
+
layout.moveItem(move.item.id, move.target.containerId, move.target.index);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function oncontainermove(move: SortableContainerMove<NavContainer>): void {
|
|
280
|
+
layout.moveSection(move.container.id, move.target);
|
|
281
|
+
}
|
|
282
|
+
</script>
|
|
283
|
+
|
|
284
|
+
{#snippet toggles(entry: NavContainer)}
|
|
285
|
+
<ShellIconButton
|
|
286
|
+
icon={entry.hidden ? 'eyeOff' : 'eye'}
|
|
287
|
+
pressed={!entry.hidden}
|
|
288
|
+
label={t(M['ui.shell_layout_editor.show_entry'], { label: entry.label })}
|
|
289
|
+
onclick={() => setShown(entry.id, entry.hidden)}
|
|
290
|
+
/>
|
|
291
|
+
{#if navMode !== 'sections'}
|
|
292
|
+
<ShellIconButton
|
|
293
|
+
icon={entry.titleVisible ? 'heading' : 'headingOff'}
|
|
294
|
+
pressed={entry.titleVisible}
|
|
295
|
+
label={t(M['ui.shell_layout_editor.show_title_for'], { label: entry.label })}
|
|
296
|
+
onclick={() => layout.setSectionTitleVisible(entry.id, !entry.titleVisible)}
|
|
297
|
+
/>
|
|
298
|
+
{/if}
|
|
299
|
+
{/snippet}
|
|
300
|
+
|
|
301
|
+
{#snippet actions(entry: NavContainer)}
|
|
302
|
+
{@render sectionActions?.({
|
|
303
|
+
sectionId: entry.id,
|
|
304
|
+
label: entry.label,
|
|
305
|
+
custom: entry.custom,
|
|
306
|
+
editing: true,
|
|
307
|
+
})}
|
|
308
|
+
{/snippet}
|
|
309
|
+
|
|
310
|
+
<nav
|
|
311
|
+
class="smrt-nav-editor"
|
|
312
|
+
data-layout-editing=""
|
|
313
|
+
data-nav-mode={navMode}
|
|
314
|
+
aria-label={ariaLabel || t(M['ui.shell_layout_editor.sortable_label'])}
|
|
315
|
+
bind:this={rootEl}
|
|
316
|
+
>
|
|
317
|
+
<Sortable
|
|
318
|
+
{containers}
|
|
319
|
+
{items}
|
|
320
|
+
label={t(M['ui.shell_layout_editor.sortable_label'])}
|
|
321
|
+
reorderContainers={true}
|
|
322
|
+
{onmove}
|
|
323
|
+
{oncontainermove}
|
|
324
|
+
>
|
|
325
|
+
{#snippet containerHeader({ container })}
|
|
326
|
+
{@const entry = containers.find((candidate) => candidate.id === container.id)}
|
|
327
|
+
{#if !entry}
|
|
328
|
+
<strong>{container.label}</strong>
|
|
329
|
+
{:else if entry.root}
|
|
330
|
+
<span class="smrt-nav-editor__top">{container.label}</span>
|
|
331
|
+
{:else}
|
|
332
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
333
|
+
<div
|
|
334
|
+
class="smrt-nav-editor__head"
|
|
335
|
+
data-nav-section={entry.id}
|
|
336
|
+
data-toolbar-open={open?.id === entry.id ? '' : undefined}
|
|
337
|
+
onpointerleave={() => onleave(entry.id)}
|
|
338
|
+
onkeydown={(event) => onkeydown(entry.id, event)}
|
|
339
|
+
onfocusout={(event) => onfocusout(entry.id, event)}
|
|
340
|
+
>
|
|
341
|
+
<!-- raw-primitive-allow: heading button that opens the section toolbar -->
|
|
342
|
+
<button
|
|
343
|
+
type="button"
|
|
344
|
+
class="smrt-nav-editor__title"
|
|
345
|
+
class:smrt-nav-editor__title--off={!entry.titleVisible}
|
|
346
|
+
class:smrt-nav-editor__title--hidden={entry.hidden}
|
|
347
|
+
data-section-title=""
|
|
348
|
+
aria-haspopup="true"
|
|
349
|
+
aria-expanded={open?.id === entry.id}
|
|
350
|
+
aria-label={t(M['ui.layout_edit.edit_section'], { label: entry.label })}
|
|
351
|
+
onpointerenter={(event) => onhover(entry.id, event)}
|
|
352
|
+
onclick={() => onactivate(entry.id)}
|
|
353
|
+
>{#if navMode === 'sections'}<span class="smrt-nav-editor__section-icon"><ShellSectionIcon name={entry.icon} iconComponent={IconComponent} /></span>{/if}{entry.label}</button>
|
|
354
|
+
<span class="smrt-nav-editor__overlay">
|
|
355
|
+
{@render toggles(entry)}
|
|
356
|
+
{@render actions(entry)}
|
|
357
|
+
</span>
|
|
358
|
+
{#if open?.id === entry.id}
|
|
359
|
+
<div
|
|
360
|
+
class="smrt-nav-editor__toolbar"
|
|
361
|
+
role="group"
|
|
362
|
+
aria-label={t(M['ui.layout_edit.section_options'], { label: entry.label })}
|
|
363
|
+
data-testid="section-toolbar"
|
|
364
|
+
>
|
|
365
|
+
<Input
|
|
366
|
+
class="smrt-nav-editor__name"
|
|
367
|
+
interaction={false}
|
|
368
|
+
data-section-name={entry.id}
|
|
369
|
+
aria-label={t(M['ui.shell_layout_editor.section_name'], { label: entry.label })}
|
|
370
|
+
value={entry.label}
|
|
371
|
+
onchange={(event) => rename(entry.id, event.currentTarget)}
|
|
372
|
+
onkeydown={(event) => {
|
|
373
|
+
if (event.key === 'Enter') {
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
rename(entry.id, event.currentTarget);
|
|
376
|
+
}
|
|
377
|
+
}}
|
|
378
|
+
/>
|
|
379
|
+
{@render toggles(entry)}
|
|
380
|
+
{#if navMode === 'sections'}
|
|
381
|
+
<ShellIconButton
|
|
382
|
+
data-section-icon={entry.id}
|
|
383
|
+
aria-haspopup="true"
|
|
384
|
+
aria-expanded={iconPicker === entry.id}
|
|
385
|
+
label={t(M['ui.shell_layout_editor.section_icon'], { label: entry.label })}
|
|
386
|
+
onclick={() => (iconPicker = iconPicker === entry.id ? null : entry.id)}
|
|
387
|
+
>
|
|
388
|
+
<ShellSectionIcon name={entry.icon} iconComponent={IconComponent} />
|
|
389
|
+
</ShellIconButton>
|
|
390
|
+
{/if}
|
|
391
|
+
{#if entry.custom}
|
|
392
|
+
<ShellIconButton
|
|
393
|
+
icon="trash"
|
|
394
|
+
label={t(M['ui.shell_layout_editor.delete_section'], { label: entry.label })}
|
|
395
|
+
onclick={() => deleteSection(entry.id, entry.label, entry.count)}
|
|
396
|
+
/>
|
|
397
|
+
{/if}
|
|
398
|
+
{@render actions(entry)}
|
|
399
|
+
{#if navMode === 'sections' && iconPicker === entry.id}
|
|
400
|
+
<div
|
|
401
|
+
class="smrt-nav-editor__icons"
|
|
402
|
+
role="group"
|
|
403
|
+
aria-label={t(M['ui.shell_layout_editor.section_icon'], { label: entry.label })}
|
|
404
|
+
>
|
|
405
|
+
{#each SHELL_SECTION_ICONS as name (name)}
|
|
406
|
+
<ShellIconButton
|
|
407
|
+
data-icon-option={name}
|
|
408
|
+
pressed={entry.icon === name}
|
|
409
|
+
label={t(M['ui.shell_layout_editor.icon_option'], { icon: humanizeIcon(name) })}
|
|
410
|
+
onclick={() => chooseIcon(entry.id, name)}
|
|
411
|
+
>
|
|
412
|
+
<ShellSectionIcon {name} />
|
|
413
|
+
</ShellIconButton>
|
|
414
|
+
{/each}
|
|
415
|
+
</div>
|
|
416
|
+
{/if}
|
|
417
|
+
</div>
|
|
418
|
+
{/if}
|
|
419
|
+
</div>
|
|
420
|
+
{/if}
|
|
421
|
+
{/snippet}
|
|
422
|
+
{#snippet item({ item: entry, container })}
|
|
423
|
+
{@const hidden = layout.isHidden(entry.id)}
|
|
424
|
+
{@const icon = itemIcons.get(entry.id)}
|
|
425
|
+
{@const original = itemDefaults.get(entry.id)}
|
|
426
|
+
<span
|
|
427
|
+
class="smrt-nav-editor__row"
|
|
428
|
+
class:smrt-nav-editor__row--muted={hidden || layout.isHidden(container.id)}
|
|
429
|
+
>
|
|
430
|
+
{#if icon}
|
|
431
|
+
<span class="smrt-nav-editor__icon" aria-hidden="true">
|
|
432
|
+
{#if IconComponent}
|
|
433
|
+
<IconComponent name={icon} size={18} />
|
|
434
|
+
{:else}
|
|
435
|
+
{icon}
|
|
436
|
+
{/if}
|
|
437
|
+
</span>
|
|
438
|
+
{/if}
|
|
439
|
+
{#if renamingItem === entry.id}
|
|
440
|
+
<Input
|
|
441
|
+
class="smrt-nav-editor__item-name"
|
|
442
|
+
interaction={false}
|
|
443
|
+
data-item-name={entry.id}
|
|
444
|
+
aria-label={t(M['ui.shell_layout_editor.item_name'], { label: entry.label })}
|
|
445
|
+
value={entry.label}
|
|
446
|
+
onblur={(event) => commitRenameItem(entry.id, event.currentTarget.value)}
|
|
447
|
+
onkeydown={(event) => {
|
|
448
|
+
if (event.key === 'Enter') {
|
|
449
|
+
event.preventDefault();
|
|
450
|
+
event.stopPropagation();
|
|
451
|
+
const value = event.currentTarget.value;
|
|
452
|
+
commitRenameItem(entry.id, value);
|
|
453
|
+
cancelRenameItem(entry.id);
|
|
454
|
+
} else if (event.key === 'Escape') {
|
|
455
|
+
event.preventDefault();
|
|
456
|
+
event.stopPropagation();
|
|
457
|
+
cancelRenameItem(entry.id);
|
|
458
|
+
}
|
|
459
|
+
}}
|
|
460
|
+
/>
|
|
461
|
+
{:else}
|
|
462
|
+
<strong title={original !== entry.label ? original : undefined}>{entry.label}</strong>
|
|
463
|
+
{/if}
|
|
464
|
+
</span>
|
|
465
|
+
<ShellIconButton
|
|
466
|
+
icon="edit"
|
|
467
|
+
data-item-rename={entry.id}
|
|
468
|
+
label={t(M['ui.shell_layout_editor.rename_item'], { label: entry.label })}
|
|
469
|
+
onclick={() => startRenameItem(entry.id)}
|
|
470
|
+
/>
|
|
471
|
+
{#if original !== undefined && original !== entry.label}
|
|
472
|
+
<ShellIconButton
|
|
473
|
+
icon="undo"
|
|
474
|
+
label={t(M['ui.shell_layout_editor.reset_item'], { label: entry.label, original })}
|
|
475
|
+
onclick={() => layout.renameItem(entry.id, null)}
|
|
476
|
+
/>
|
|
477
|
+
{/if}
|
|
478
|
+
<ShellIconButton
|
|
479
|
+
icon={hidden ? 'eyeOff' : 'eye'}
|
|
480
|
+
pressed={!hidden}
|
|
481
|
+
label={t(M['ui.shell_layout_editor.show_entry'], { label: entry.label })}
|
|
482
|
+
onclick={() => setShown(entry.id, hidden)}
|
|
483
|
+
/>
|
|
484
|
+
{/snippet}
|
|
485
|
+
</Sortable>
|
|
486
|
+
<Button variant="secondary" size="sm" class="smrt-nav-editor__new" onclick={createSection}>
|
|
487
|
+
{t(M['ui.shell_layout_editor.new_section'])}
|
|
488
|
+
</Button>
|
|
489
|
+
</nav>
|
|
490
|
+
|
|
491
|
+
<style>
|
|
492
|
+
.smrt-nav-editor { display: grid; gap: var(--smrt-spacing-2); min-inline-size: 0; }
|
|
493
|
+
/* Make the generic Sortable read as the real navigation. */
|
|
494
|
+
.smrt-nav-editor :global(.smrt-sortable) { gap: var(--smrt-spacing-1); }
|
|
495
|
+
.smrt-nav-editor :global(.smrt-sortable__containers) { gap: var(--smrt-spacing-1); }
|
|
496
|
+
.smrt-nav-editor :global(.smrt-sortable__container) { padding: 0; border: 0; background: transparent; gap: var(--smrt-spacing-1); }
|
|
497
|
+
.smrt-nav-editor :global(.smrt-sortable__item) { padding: 0; border: 0; border-radius: var(--smrt-radius-medium); background: transparent; }
|
|
498
|
+
.smrt-nav-editor :global(.smrt-sortable__item:hover) { background: var(--smrt-color-surface-container-high); }
|
|
499
|
+
.smrt-nav-editor :global(.smrt-sortable__items) { padding-inline-start: var(--smrt-spacing-3); min-block-size: 0; }
|
|
500
|
+
.smrt-nav-editor :global(.smrt-sortable__header) { min-block-size: 0; }
|
|
501
|
+
.smrt-nav-editor :global(.smrt-sortable__empty) { padding-block: 0; }
|
|
502
|
+
.smrt-nav-editor :global(.smrt-sortable__handle) { inline-size: 1.5rem; block-size: 2rem; }
|
|
503
|
+
|
|
504
|
+
.smrt-nav-editor__top { color: var(--smrt-color-on-surface-variant); font: var(--smrt-typography-label-small-font); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
505
|
+
.smrt-nav-editor__head { position: relative; flex: 1 1 auto; display: flex; align-items: center; gap: var(--smrt-spacing-1); min-inline-size: 0; }
|
|
506
|
+
.smrt-nav-editor__title { flex: 1 1 auto; min-inline-size: 0; padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 0; border-radius: var(--smrt-radius-medium); background: transparent; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-label-large-size); font-weight: var(--smrt-typography-weight-semibold); text-align: start; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
507
|
+
.smrt-nav-editor__title:hover, .smrt-nav-editor__head[data-toolbar-open] .smrt-nav-editor__title { background: var(--smrt-color-surface-container-high); }
|
|
508
|
+
.smrt-nav-editor__title:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 2px; }
|
|
509
|
+
.smrt-nav-editor__title--off { font-style: italic; font-weight: var(--smrt-typography-weight-normal, normal); }
|
|
510
|
+
.smrt-nav-editor__title--hidden { opacity: 0.55; }
|
|
511
|
+
.smrt-nav-editor__overlay { display: inline-flex; align-items: center; flex: 0 0 auto; }
|
|
512
|
+
.smrt-nav-editor__toolbar { position: absolute; inset-block-start: 100%; inset-inline: 0; z-index: 40; display: flex; flex-wrap: wrap; align-items: center; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-2); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-medium); background: var(--smrt-color-surface-container-high); box-shadow: var(--smrt-elevation-2, 0 4px 12px rgb(0 0 0 / 0.2)); }
|
|
513
|
+
:global(.smrt-nav-editor__name) { flex: 1 1 100%; min-inline-size: 0; }
|
|
514
|
+
.smrt-nav-editor__row { flex: 1 1 auto; min-inline-size: 0; display: flex; align-items: center; gap: var(--smrt-spacing-2); padding: var(--smrt-spacing-2) var(--smrt-spacing-1); color: var(--smrt-color-on-surface); }
|
|
515
|
+
.smrt-nav-editor__row--muted { opacity: 0.55; }
|
|
516
|
+
.smrt-nav-editor__row strong { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--smrt-typography-weight-medium, 500); }
|
|
517
|
+
/* Keep editor rows inside the sidebar: the implicit grid column would size to
|
|
518
|
+
the rows' min-content and push the trailing icons past the clipped rail. */
|
|
519
|
+
.smrt-nav-editor :global(.smrt-sortable) { grid-template-columns: minmax(0, 1fr); }
|
|
520
|
+
.smrt-nav-editor :global(.smrt-sortable__container),
|
|
521
|
+
.smrt-nav-editor :global(.smrt-sortable__items),
|
|
522
|
+
.smrt-nav-editor :global(.smrt-sortable__item) { min-inline-size: 0; max-inline-size: 100%; box-sizing: border-box; }
|
|
523
|
+
.smrt-nav-editor__icon { display: inline-grid; place-items: center; inline-size: 1.25rem; block-size: 1.25rem; min-inline-size: 1.25rem; }
|
|
524
|
+
:global(.smrt-nav-editor__item-name) { flex: 1 1 auto; min-inline-size: 0; }
|
|
525
|
+
:global(.smrt-nav-editor__new) { justify-self: start; }
|
|
526
|
+
/* Sections mode: the entries are edited on the section's page. */
|
|
527
|
+
.smrt-nav-editor[data-nav-mode='sections'] :global(.smrt-sortable__items) { display: none; }
|
|
528
|
+
.smrt-nav-editor__section-icon { display: inline-flex; margin-inline-end: var(--smrt-spacing-2); vertical-align: middle; }
|
|
529
|
+
.smrt-nav-editor__icons { flex: 1 1 100%; display: grid; grid-template-columns: repeat(auto-fill, minmax(2rem, 1fr)); gap: var(--smrt-spacing-1); padding-block-start: var(--smrt-spacing-1); }
|
|
530
|
+
.smrt-nav-editor__icons :global(.smrt-shell-icon-button[aria-pressed='true']) { background: var(--smrt-color-primary-container); color: var(--smrt-color-on-primary-container); }
|
|
531
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Component, type Snippet } from 'svelte';
|
|
2
|
+
import type { ShellLayoutController } from './layout-controller.svelte.js';
|
|
3
|
+
import type { ShellSectionActionsContext } from './types.js';
|
|
4
|
+
interface Props {
|
|
5
|
+
/** The layout API to edit; defaults to the nearest `AppShell`'s. */
|
|
6
|
+
controller?: ShellLayoutController;
|
|
7
|
+
/** Accessible name for the navigation landmark. */
|
|
8
|
+
'aria-label'?: string;
|
|
9
|
+
/** Component used to render icon names, as for `TenantNav`. */
|
|
10
|
+
iconComponent?: Component<{
|
|
11
|
+
name: string;
|
|
12
|
+
size?: number;
|
|
13
|
+
}>;
|
|
14
|
+
/** Host icon buttons for each section (overlay and floating toolbar). */
|
|
15
|
+
sectionActions?: Snippet<[ShellSectionActionsContext]>;
|
|
16
|
+
/**
|
|
17
|
+
* `'sections'` edits only the sections (grip, rename, show, delete, icon
|
|
18
|
+
* picker); their entries are edited on the section's own page with
|
|
19
|
+
* `ShellSectionMenu`. Default `'items'`.
|
|
20
|
+
*/
|
|
21
|
+
navMode?: 'items' | 'sections';
|
|
22
|
+
}
|
|
23
|
+
declare const ShellNavEditor: Component<Props, {}, "">;
|
|
24
|
+
type ShellNavEditor = ReturnType<typeof ShellNavEditor>;
|
|
25
|
+
export default ShellNavEditor;
|
|
26
|
+
//# sourceMappingURL=ShellNavEditor.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellNavEditor.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/ShellNavEditor.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAiB,MAAM,QAAQ,CAAC;AAUrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAO3E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAG7D,UAAU,KAAK;IACb,oEAAoE;IACpE,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;CAChC;AA+WD,QAAA,MAAM,cAAc,0BAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Draws a section or entry icon by name: a built-in shell icon
|
|
4
|
+
* (`SHELL_ICON_PATHS`) as an inline SVG, else the host's `iconComponent`, else
|
|
5
|
+
* the name itself as text. Decorative (`aria-hidden`); callers name the
|
|
6
|
+
* control.
|
|
7
|
+
*/
|
|
8
|
+
import type { Component } from 'svelte';
|
|
9
|
+
import {
|
|
10
|
+
isShellIconName,
|
|
11
|
+
SHELL_DEFAULT_SECTION_ICON,
|
|
12
|
+
SHELL_ICON_PATHS,
|
|
13
|
+
} from './shell-icons.js';
|
|
14
|
+
|
|
15
|
+
const warned = new Set<string>();
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Icon name: a built-in shell icon, else whatever `iconComponent` knows. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Edge length in pixels (default 18). */
|
|
21
|
+
size?: number;
|
|
22
|
+
/** Renders names that are not built-in shell icons. */
|
|
23
|
+
iconComponent?: Component<{ name: string; size?: number }>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { name, size = 18, iconComponent: Host }: Props = $props();
|
|
27
|
+
|
|
28
|
+
// An unknown name with no host renderer never prints as text: it falls back to
|
|
29
|
+
// the default glyph, with one dev warning per name.
|
|
30
|
+
const fallback = $derived(!isShellIconName(name) && !Host);
|
|
31
|
+
$effect(() => {
|
|
32
|
+
if (
|
|
33
|
+
fallback &&
|
|
34
|
+
(import.meta as { env?: { DEV?: boolean } }).env?.DEV &&
|
|
35
|
+
!warned.has(name)
|
|
36
|
+
) {
|
|
37
|
+
warned.add(name);
|
|
38
|
+
console.warn(
|
|
39
|
+
`[smrt-svelte] Unknown shell icon "${name}"; using "${SHELL_DEFAULT_SECTION_ICON}".`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<span class="smrt-shell-section-icon" aria-hidden="true">
|
|
46
|
+
{#if isShellIconName(name)}
|
|
47
|
+
<svg viewBox="0 0 24 24" width={size} height={size} fill="currentColor" focusable="false"><path d={SHELL_ICON_PATHS[name]} /></svg>
|
|
48
|
+
{:else if Host}
|
|
49
|
+
<Host {name} {size} />
|
|
50
|
+
{:else}
|
|
51
|
+
<svg viewBox="0 0 24 24" width={size} height={size} fill="currentColor" focusable="false"><path d={SHELL_ICON_PATHS[SHELL_DEFAULT_SECTION_ICON]} /></svg>
|
|
52
|
+
{/if}
|
|
53
|
+
</span>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
.smrt-shell-section-icon { display: inline-grid; place-items: center; flex: 0 0 auto; }
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws a section or entry icon by name: a built-in shell icon
|
|
3
|
+
* (`SHELL_ICON_PATHS`) as an inline SVG, else the host's `iconComponent`, else
|
|
4
|
+
* the name itself as text. Decorative (`aria-hidden`); callers name the
|
|
5
|
+
* control.
|
|
6
|
+
*/
|
|
7
|
+
import type { Component } from 'svelte';
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Icon name: a built-in shell icon, else whatever `iconComponent` knows. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Edge length in pixels (default 18). */
|
|
12
|
+
size?: number;
|
|
13
|
+
/** Renders names that are not built-in shell icons. */
|
|
14
|
+
iconComponent?: Component<{
|
|
15
|
+
name: string;
|
|
16
|
+
size?: number;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
declare const ShellSectionIcon: Component<Props, {}, "">;
|
|
20
|
+
type ShellSectionIcon = ReturnType<typeof ShellSectionIcon>;
|
|
21
|
+
export default ShellSectionIcon;
|
|
22
|
+
//# sourceMappingURL=ShellSectionIcon.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellSectionIcon.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/ShellSectionIcon.svelte.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAQxC,UAAU,KAAK;IACb,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AA0CD,QAAA,MAAM,gBAAgB,0BAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|