@r2digisolutions/components 0.15.2 → 0.15.4
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/dist/components/organisms/CommandPalette/CommandPalette.svelte +290 -97
- package/dist/components/organisms/CommandPalette/CommandPalette.svelte.d.ts +14 -1
- package/dist/components/organisms/CommandPalette/CommandPaletteStory.svelte +29 -11
- package/dist/index.d.ts +1 -1
- package/dist/utils/i18n.d.ts +4 -0
- package/dist/utils/i18n.js +18 -0
- package/package.json +1 -1
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
import { untrack } from 'svelte';
|
|
2
4
|
import Kbd from '../../atoms/Kbd/Kbd.svelte';
|
|
5
|
+
import { i18n } from '../../../utils/i18n.svelte';
|
|
6
|
+
|
|
7
|
+
export type CommandIcon = Component<{
|
|
8
|
+
class?: string;
|
|
9
|
+
size?: number | string;
|
|
10
|
+
strokeWidth?: number | string;
|
|
11
|
+
}>;
|
|
3
12
|
|
|
4
13
|
export interface CommandItem {
|
|
5
14
|
id: string;
|
|
@@ -7,61 +16,127 @@
|
|
|
7
16
|
group?: string;
|
|
8
17
|
shortcut?: string[];
|
|
9
18
|
disabled?: boolean;
|
|
19
|
+
subtitle?: string;
|
|
20
|
+
keywords?: string;
|
|
21
|
+
href?: string;
|
|
22
|
+
icon?: CommandIcon;
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
interface CommandPaletteProps {
|
|
13
26
|
open?: boolean;
|
|
14
27
|
items?: CommandItem[];
|
|
15
28
|
placeholder?: string;
|
|
29
|
+
emptyLabel?: string;
|
|
30
|
+
loading?: boolean;
|
|
16
31
|
class?: string;
|
|
17
32
|
onselect?: (item: CommandItem) => void;
|
|
18
33
|
onclose?: () => void;
|
|
34
|
+
onquery?: (query: string) => void;
|
|
19
35
|
}
|
|
20
36
|
|
|
21
37
|
let {
|
|
22
38
|
open = $bindable(false),
|
|
23
39
|
items = [],
|
|
24
|
-
placeholder
|
|
40
|
+
placeholder,
|
|
41
|
+
emptyLabel,
|
|
42
|
+
loading = false,
|
|
25
43
|
class: className = '',
|
|
26
44
|
onselect,
|
|
27
|
-
onclose
|
|
45
|
+
onclose,
|
|
46
|
+
onquery
|
|
28
47
|
}: CommandPaletteProps = $props();
|
|
29
48
|
|
|
30
49
|
let query = $state('');
|
|
31
50
|
let activeIndex = $state(0);
|
|
32
|
-
let
|
|
51
|
+
let dialogEl = $state<HTMLDialogElement | null>(null);
|
|
52
|
+
let listEl = $state<HTMLDivElement | null>(null);
|
|
53
|
+
/** El click que abre la paleta llega al ::backdrop y la cerraría al instante. */
|
|
54
|
+
let ignoreBackdropUntil = 0;
|
|
55
|
+
const listId = 'command-palette-list';
|
|
33
56
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
const resolvedPlaceholder = $derived(placeholder ?? i18n.t('commandPalettePlaceholder'));
|
|
58
|
+
const resolvedEmpty = $derived(emptyLabel ?? i18n.t('noResults'));
|
|
59
|
+
const dialogLabel = $derived(i18n.t('commandPalette'));
|
|
60
|
+
|
|
61
|
+
function normalize(value: string) {
|
|
62
|
+
return value
|
|
63
|
+
.normalize('NFD')
|
|
64
|
+
.replace(/\p{Diacritic}/gu, '')
|
|
65
|
+
.toLowerCase();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function matches(item: CommandItem, haystack: string) {
|
|
69
|
+
if (!haystack) return true;
|
|
70
|
+
const blob = normalize([item.label, item.subtitle ?? '', item.keywords ?? ''].join(' '));
|
|
71
|
+
return blob.includes(haystack);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const filtered = $derived.by(() => {
|
|
75
|
+
const haystack = normalize(query.trim());
|
|
76
|
+
return items.filter((item) => matches(item, haystack));
|
|
77
|
+
});
|
|
40
78
|
|
|
41
79
|
const groups = $derived.by(() => {
|
|
42
|
-
const map = new Map<string, CommandItem[]>();
|
|
43
|
-
|
|
44
|
-
const key = item.group ||
|
|
80
|
+
const map = new Map<string, { item: CommandItem; index: number }[]>();
|
|
81
|
+
filtered.forEach((item, index) => {
|
|
82
|
+
const key = item.group || dialogLabel;
|
|
45
83
|
const list = map.get(key) ?? [];
|
|
46
|
-
list.push(item);
|
|
84
|
+
list.push({ item, index });
|
|
47
85
|
map.set(key, list);
|
|
48
|
-
}
|
|
86
|
+
});
|
|
49
87
|
return [...map.entries()];
|
|
50
88
|
});
|
|
51
89
|
|
|
52
90
|
const flat = $derived(filtered);
|
|
91
|
+
const showIconColumn = $derived(flat.some((item) => item.icon));
|
|
92
|
+
const activeItem = $derived(flat[activeIndex] ?? null);
|
|
93
|
+
const activeOptionId = $derived(activeItem ? optionId(activeItem.id) : undefined);
|
|
53
94
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
function optionId(id: string) {
|
|
96
|
+
return `${listId}-${id}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function firstEnabledIndex() {
|
|
100
|
+
return flat.findIndex((item) => !item.disabled);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function nextEnabled(from: number, dir: 1 | -1) {
|
|
104
|
+
if (!flat.length) return 0;
|
|
105
|
+
let i = from;
|
|
106
|
+
for (let n = 0; n < flat.length; n++) {
|
|
107
|
+
i = (i + dir + flat.length) % flat.length;
|
|
108
|
+
if (!flat[i]?.disabled) return i;
|
|
59
109
|
}
|
|
60
|
-
|
|
110
|
+
return from;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Desplaza solo el listado, no el <dialog> (scrollIntoView arrastra ancestros). */
|
|
114
|
+
function keepVisible(node: HTMLElement) {
|
|
115
|
+
const frame = requestAnimationFrame(() => {
|
|
116
|
+
const list = listEl;
|
|
117
|
+
if (!list) return;
|
|
118
|
+
const listRect = list.getBoundingClientRect();
|
|
119
|
+
const rect = node.getBoundingClientRect();
|
|
120
|
+
if (rect.bottom > listRect.bottom) {
|
|
121
|
+
list.scrollTop += rect.bottom - listRect.bottom;
|
|
122
|
+
} else if (rect.top < listRect.top) {
|
|
123
|
+
list.scrollTop -= listRect.top - rect.top;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return () => cancelAnimationFrame(frame);
|
|
127
|
+
}
|
|
61
128
|
|
|
62
129
|
$effect(() => {
|
|
63
|
-
|
|
64
|
-
|
|
130
|
+
if (!dialogEl) return;
|
|
131
|
+
if (open && !dialogEl.open) {
|
|
132
|
+
query = '';
|
|
133
|
+
activeIndex = Math.max(0, firstEnabledIndex());
|
|
134
|
+
untrack(() => onquery?.(''));
|
|
135
|
+
ignoreBackdropUntil = Date.now() + 400;
|
|
136
|
+
dialogEl.showModal();
|
|
137
|
+
} else if (!open && dialogEl.open) {
|
|
138
|
+
dialogEl.close();
|
|
139
|
+
}
|
|
65
140
|
});
|
|
66
141
|
|
|
67
142
|
function close() {
|
|
@@ -69,6 +144,22 @@
|
|
|
69
144
|
onclose?.();
|
|
70
145
|
}
|
|
71
146
|
|
|
147
|
+
function handleDialogClose() {
|
|
148
|
+
if (open) close();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function handleBackdropClick(event: MouseEvent) {
|
|
152
|
+
if (!dialogEl) return;
|
|
153
|
+
if (Date.now() < ignoreBackdropUntil) return;
|
|
154
|
+
const rect = dialogEl.getBoundingClientRect();
|
|
155
|
+
const inside =
|
|
156
|
+
event.clientX >= rect.left &&
|
|
157
|
+
event.clientX <= rect.right &&
|
|
158
|
+
event.clientY >= rect.top &&
|
|
159
|
+
event.clientY <= rect.bottom;
|
|
160
|
+
if (!inside) close();
|
|
161
|
+
}
|
|
162
|
+
|
|
72
163
|
function choose(item: CommandItem) {
|
|
73
164
|
if (item.disabled) return;
|
|
74
165
|
onselect?.(item);
|
|
@@ -76,94 +167,196 @@
|
|
|
76
167
|
}
|
|
77
168
|
|
|
78
169
|
function onKeydown(e: KeyboardEvent) {
|
|
79
|
-
if (!open) return;
|
|
80
|
-
if (e.key === 'Escape') {
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
close();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
170
|
+
if (!open || !flat.length) return;
|
|
85
171
|
if (e.key === 'ArrowDown') {
|
|
86
172
|
e.preventDefault();
|
|
87
|
-
activeIndex =
|
|
173
|
+
activeIndex = nextEnabled(activeIndex, 1);
|
|
88
174
|
} else if (e.key === 'ArrowUp') {
|
|
89
175
|
e.preventDefault();
|
|
90
|
-
activeIndex =
|
|
176
|
+
activeIndex = nextEnabled(activeIndex, -1);
|
|
177
|
+
} else if (e.key === 'Home') {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
activeIndex = nextEnabled(-1, 1);
|
|
180
|
+
} else if (e.key === 'End') {
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
activeIndex = nextEnabled(flat.length, -1);
|
|
91
183
|
} else if (e.key === 'Enter') {
|
|
92
184
|
e.preventDefault();
|
|
93
185
|
const item = flat[activeIndex];
|
|
94
186
|
if (item) choose(item);
|
|
95
187
|
}
|
|
96
188
|
}
|
|
189
|
+
|
|
190
|
+
function onInput() {
|
|
191
|
+
activeIndex = Math.max(0, firstEnabledIndex());
|
|
192
|
+
onquery?.(query);
|
|
193
|
+
}
|
|
97
194
|
</script>
|
|
98
195
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<input
|
|
123
|
-
bind:this={inputEl}
|
|
124
|
-
bind:value={query}
|
|
125
|
-
{placeholder}
|
|
126
|
-
class="h-12 w-full bg-transparent text-sm text-primary outline-none placeholder:text-muted"
|
|
196
|
+
<dialog
|
|
197
|
+
bind:this={dialogEl}
|
|
198
|
+
class={['command-palette', className]}
|
|
199
|
+
aria-label={dialogLabel}
|
|
200
|
+
onclose={handleDialogClose}
|
|
201
|
+
onclick={handleBackdropClick}
|
|
202
|
+
>
|
|
203
|
+
<div
|
|
204
|
+
class="rounded-2xl border-border bg-surface-elevated shadow-2xl w-full overflow-hidden border"
|
|
205
|
+
>
|
|
206
|
+
<div class="gap-2 border-border px-3 flex items-center border-b">
|
|
207
|
+
<svg
|
|
208
|
+
class="h-4 w-4 text-muted shrink-0"
|
|
209
|
+
viewBox="0 0 24 24"
|
|
210
|
+
fill="none"
|
|
211
|
+
stroke="currentColor"
|
|
212
|
+
stroke-width="2"
|
|
213
|
+
aria-hidden="true"
|
|
214
|
+
>
|
|
215
|
+
<path
|
|
216
|
+
stroke-linecap="round"
|
|
217
|
+
stroke-linejoin="round"
|
|
218
|
+
d="M21 21l-4.35-4.35M11 18a7 7 0 100-14 7 7 0 000 14z"
|
|
127
219
|
/>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
220
|
+
</svg>
|
|
221
|
+
<input
|
|
222
|
+
bind:value={query}
|
|
223
|
+
placeholder={resolvedPlaceholder}
|
|
224
|
+
class="h-12 text-sm text-primary placeholder:text-muted w-full bg-transparent outline-none"
|
|
225
|
+
autocomplete="off"
|
|
226
|
+
spellcheck="false"
|
|
227
|
+
role="combobox"
|
|
228
|
+
aria-autocomplete="list"
|
|
229
|
+
aria-expanded="true"
|
|
230
|
+
aria-controls={listId}
|
|
231
|
+
aria-activedescendant={activeOptionId}
|
|
232
|
+
oninput={onInput}
|
|
233
|
+
onkeydown={onKeydown}
|
|
234
|
+
/>
|
|
235
|
+
{#if loading}
|
|
236
|
+
<span
|
|
237
|
+
class="h-4 w-4 animate-spin border-border border-t-brand-500 shrink-0 rounded-full border-2"
|
|
238
|
+
aria-hidden="true"
|
|
239
|
+
></span>
|
|
240
|
+
{/if}
|
|
241
|
+
<Kbd keys={['Esc']} size="sm" />
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div bind:this={listEl} id={listId} class="max-h-80 p-2 overflow-y-auto" role="listbox">
|
|
245
|
+
{#if flat.length === 0}
|
|
246
|
+
<p class="px-3 py-6 text-sm text-muted text-center">{resolvedEmpty}</p>
|
|
247
|
+
{:else}
|
|
248
|
+
{#each groups as [groupName, groupItems] (groupName)}
|
|
249
|
+
<p class="px-2 py-1.5 font-semibold tracking-wide text-muted text-[11px] uppercase">
|
|
250
|
+
{groupName}
|
|
251
|
+
</p>
|
|
252
|
+
<ul class="mb-2 gap-0.5 flex flex-col">
|
|
253
|
+
{#each groupItems as { item, index } (item.id)}
|
|
254
|
+
{@const Icon = item.icon}
|
|
255
|
+
{@const active = index === activeIndex}
|
|
256
|
+
<li>
|
|
257
|
+
<button
|
|
258
|
+
id={optionId(item.id)}
|
|
259
|
+
type="button"
|
|
260
|
+
role="option"
|
|
261
|
+
tabindex="-1"
|
|
262
|
+
aria-selected={active}
|
|
263
|
+
disabled={item.disabled}
|
|
264
|
+
onclick={() => choose(item)}
|
|
265
|
+
onmouseenter={() => {
|
|
266
|
+
if (!item.disabled) activeIndex = index;
|
|
267
|
+
}}
|
|
268
|
+
{@attach active ? keepVisible : undefined}
|
|
269
|
+
class={[
|
|
270
|
+
'gap-3 rounded-xl px-3 py-2 text-sm flex w-full items-center justify-between text-left transition-colors',
|
|
271
|
+
active
|
|
272
|
+
? 'bg-brand-50 text-brand-700 dark:bg-brand-950/40 dark:text-brand-300'
|
|
273
|
+
: 'text-primary hover:bg-surface-overlay',
|
|
274
|
+
item.disabled && 'cursor-not-allowed opacity-40'
|
|
275
|
+
]}
|
|
276
|
+
>
|
|
277
|
+
<span class="gap-3 min-w-0 flex flex-1 items-center">
|
|
278
|
+
{#if showIconColumn}
|
|
279
|
+
<span
|
|
280
|
+
class={[
|
|
281
|
+
'h-8 w-8 rounded-lg flex shrink-0 items-center justify-center',
|
|
282
|
+
active
|
|
283
|
+
? 'bg-brand-100/80 text-brand-700 dark:bg-brand-900/50 dark:text-brand-300'
|
|
284
|
+
: 'bg-surface-overlay text-muted'
|
|
285
|
+
]}
|
|
286
|
+
aria-hidden="true"
|
|
287
|
+
>
|
|
288
|
+
{#if Icon}
|
|
289
|
+
<Icon size={16} strokeWidth={2} />
|
|
290
|
+
{/if}
|
|
291
|
+
</span>
|
|
159
292
|
{/if}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
293
|
+
<span class="min-w-0 flex-1">
|
|
294
|
+
<span class="block truncate">{item.label}</span>
|
|
295
|
+
{#if item.subtitle}
|
|
296
|
+
<span class="mt-0.5 text-xs text-muted block truncate">{item.subtitle}</span
|
|
297
|
+
>
|
|
298
|
+
{/if}
|
|
299
|
+
</span>
|
|
300
|
+
</span>
|
|
301
|
+
{#if item.shortcut?.length}
|
|
302
|
+
<Kbd keys={item.shortcut} size="sm" />
|
|
303
|
+
{/if}
|
|
304
|
+
</button>
|
|
305
|
+
</li>
|
|
306
|
+
{/each}
|
|
307
|
+
</ul>
|
|
308
|
+
{/each}
|
|
309
|
+
{/if}
|
|
167
310
|
</div>
|
|
168
311
|
</div>
|
|
169
|
-
|
|
312
|
+
</dialog>
|
|
313
|
+
|
|
314
|
+
<style>
|
|
315
|
+
.command-palette {
|
|
316
|
+
margin: 12vh auto auto;
|
|
317
|
+
padding: 0;
|
|
318
|
+
border: none;
|
|
319
|
+
background: transparent;
|
|
320
|
+
color: inherit;
|
|
321
|
+
width: calc(100% - 2rem);
|
|
322
|
+
max-width: 36rem;
|
|
323
|
+
overflow: visible;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.command-palette::backdrop {
|
|
327
|
+
background: oklch(15% 0.02 265 / 0.45);
|
|
328
|
+
backdrop-filter: blur(6px);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
:global(.dark) .command-palette::backdrop {
|
|
332
|
+
background: oklch(0% 0 0 / 0.65);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.command-palette[open] {
|
|
336
|
+
animation: palette-in 140ms ease-out;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.command-palette[open]::backdrop {
|
|
340
|
+
animation: palette-backdrop-in 140ms ease-out;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@keyframes palette-in {
|
|
344
|
+
from {
|
|
345
|
+
opacity: 0;
|
|
346
|
+
transform: translateY(6px) scale(0.98);
|
|
347
|
+
}
|
|
348
|
+
to {
|
|
349
|
+
opacity: 1;
|
|
350
|
+
transform: translateY(0) scale(1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@keyframes palette-backdrop-in {
|
|
355
|
+
from {
|
|
356
|
+
opacity: 0;
|
|
357
|
+
}
|
|
358
|
+
to {
|
|
359
|
+
opacity: 1;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
</style>
|
|
@@ -1,18 +1,31 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
export type CommandIcon = Component<{
|
|
3
|
+
class?: string;
|
|
4
|
+
size?: number | string;
|
|
5
|
+
strokeWidth?: number | string;
|
|
6
|
+
}>;
|
|
1
7
|
export interface CommandItem {
|
|
2
8
|
id: string;
|
|
3
9
|
label: string;
|
|
4
10
|
group?: string;
|
|
5
11
|
shortcut?: string[];
|
|
6
12
|
disabled?: boolean;
|
|
13
|
+
subtitle?: string;
|
|
14
|
+
keywords?: string;
|
|
15
|
+
href?: string;
|
|
16
|
+
icon?: CommandIcon;
|
|
7
17
|
}
|
|
8
18
|
interface CommandPaletteProps {
|
|
9
19
|
open?: boolean;
|
|
10
20
|
items?: CommandItem[];
|
|
11
21
|
placeholder?: string;
|
|
22
|
+
emptyLabel?: string;
|
|
23
|
+
loading?: boolean;
|
|
12
24
|
class?: string;
|
|
13
25
|
onselect?: (item: CommandItem) => void;
|
|
14
26
|
onclose?: () => void;
|
|
27
|
+
onquery?: (query: string) => void;
|
|
15
28
|
}
|
|
16
|
-
declare const CommandPalette:
|
|
29
|
+
declare const CommandPalette: Component<CommandPaletteProps, {}, "open">;
|
|
17
30
|
type CommandPalette = ReturnType<typeof CommandPalette>;
|
|
18
31
|
export default CommandPalette;
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { FileText, FolderPlus, Search, Settings, UserPlus, Users } from '@lucide/svelte';
|
|
2
3
|
import CommandPalette, { type CommandItem } from './CommandPalette.svelte';
|
|
3
4
|
import Button from '../../atoms/Button/Button.svelte';
|
|
4
5
|
import Kbd from '../../atoms/Kbd/Kbd.svelte';
|
|
5
6
|
|
|
6
7
|
const items: CommandItem[] = [
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
{
|
|
9
|
+
id: 'new',
|
|
10
|
+
label: 'Create project',
|
|
11
|
+
group: 'Actions',
|
|
12
|
+
shortcut: ['⌘', 'N'],
|
|
13
|
+
icon: FolderPlus
|
|
14
|
+
},
|
|
15
|
+
{ id: 'search', label: 'Search files', group: 'Actions', shortcut: ['⌘', 'P'], icon: Search },
|
|
16
|
+
{
|
|
17
|
+
id: 'invoice',
|
|
18
|
+
label: 'FAC-2026-0142',
|
|
19
|
+
subtitle: 'Invoice · Acme S.L.',
|
|
20
|
+
group: 'Documents',
|
|
21
|
+
keywords: 'factura invoice',
|
|
22
|
+
icon: FileText
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'settings',
|
|
26
|
+
label: 'Open settings',
|
|
27
|
+
group: 'Navigation',
|
|
28
|
+
shortcut: ['⌘', ','],
|
|
29
|
+
href: '/settings',
|
|
30
|
+
icon: Settings
|
|
31
|
+
},
|
|
32
|
+
{ id: 'team', label: 'Invite teammate', group: 'Navigation', icon: UserPlus },
|
|
33
|
+
{ id: 'billing', label: 'Billing', group: 'Navigation', disabled: true, icon: Users }
|
|
12
34
|
];
|
|
13
35
|
|
|
14
36
|
let open = $state(false);
|
|
@@ -24,7 +46,7 @@
|
|
|
24
46
|
|
|
25
47
|
<svelte:window onkeydown={onWindowKey} />
|
|
26
48
|
|
|
27
|
-
<div class="flex flex-col items-center
|
|
49
|
+
<div class="gap-3 flex flex-col items-center">
|
|
28
50
|
<Button size="sm" onclick={() => (open = true)}>
|
|
29
51
|
Open palette
|
|
30
52
|
<Kbd keys={['⌘', 'K']} size="sm" />
|
|
@@ -34,8 +56,4 @@
|
|
|
34
56
|
{/if}
|
|
35
57
|
</div>
|
|
36
58
|
|
|
37
|
-
<CommandPalette
|
|
38
|
-
bind:open
|
|
39
|
-
{items}
|
|
40
|
-
onselect={(item) => (last = item.label)}
|
|
41
|
-
/>
|
|
59
|
+
<CommandPalette bind:open {items} onselect={(item) => (last = item.label)} />
|
package/dist/index.d.ts
CHANGED
|
@@ -666,7 +666,7 @@ export type { NavbarLink } from './components/organisms/Navbar/Navbar.svelte';
|
|
|
666
666
|
export { default as BottomNav } from './components/organisms/BottomNav/BottomNav.svelte';
|
|
667
667
|
export type { BottomNavItem, BottomNavIcon } from './components/organisms/BottomNav/BottomNav.svelte';
|
|
668
668
|
export { default as CommandPalette } from './components/organisms/CommandPalette/CommandPalette.svelte';
|
|
669
|
-
export type { CommandItem } from './components/organisms/CommandPalette/CommandPalette.svelte';
|
|
669
|
+
export type { CommandItem, CommandIcon } from './components/organisms/CommandPalette/CommandPalette.svelte';
|
|
670
670
|
export { default as AppShell } from './components/organisms/AppShell/AppShell.svelte';
|
|
671
671
|
export { getAppChrome, setAppChrome, AppChrome } from './components/organisms/AppShell/app-chrome.svelte.js';
|
|
672
672
|
export type { AppShellContextual } from './components/organisms/AppShell/app-chrome.svelte.js';
|
package/dist/utils/i18n.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface UiMessages {
|
|
|
14
14
|
addItem: string;
|
|
15
15
|
removeItem: string;
|
|
16
16
|
noItems: string;
|
|
17
|
+
/** CommandPalette */
|
|
18
|
+
noResults: string;
|
|
19
|
+
commandPalette: string;
|
|
20
|
+
commandPalettePlaceholder: string;
|
|
17
21
|
/** FileUploader */
|
|
18
22
|
uploadFiles: string;
|
|
19
23
|
uploadHelper: string;
|
package/dist/utils/i18n.js
CHANGED
|
@@ -12,6 +12,9 @@ const en = {
|
|
|
12
12
|
addItem: 'Add item',
|
|
13
13
|
removeItem: 'Remove item',
|
|
14
14
|
noItems: 'No items yet',
|
|
15
|
+
noResults: 'No results',
|
|
16
|
+
commandPalette: 'Command palette',
|
|
17
|
+
commandPalettePlaceholder: 'Type a command or search…',
|
|
15
18
|
uploadFiles: 'Upload files',
|
|
16
19
|
uploadHelper: 'SVG, PNG, JPG or GIF (max. 10MB)',
|
|
17
20
|
dropToUpload: 'Drop files to upload',
|
|
@@ -56,6 +59,9 @@ const es = {
|
|
|
56
59
|
addItem: 'Añadir elemento',
|
|
57
60
|
removeItem: 'Eliminar elemento',
|
|
58
61
|
noItems: 'Sin elementos todavía',
|
|
62
|
+
noResults: 'Sin resultados',
|
|
63
|
+
commandPalette: 'Paleta de comandos',
|
|
64
|
+
commandPalettePlaceholder: 'Escribe un comando o busca…',
|
|
59
65
|
uploadFiles: 'Subir archivos',
|
|
60
66
|
uploadHelper: 'SVG, PNG, JPG o GIF (máx. 10MB)',
|
|
61
67
|
dropToUpload: 'Suelta los archivos para subirlos',
|
|
@@ -100,6 +106,9 @@ const pt = {
|
|
|
100
106
|
addItem: 'Adicionar item',
|
|
101
107
|
removeItem: 'Remover item',
|
|
102
108
|
noItems: 'Ainda sem itens',
|
|
109
|
+
noResults: 'Sem resultados',
|
|
110
|
+
commandPalette: 'Paleta de comandos',
|
|
111
|
+
commandPalettePlaceholder: 'Escreve um comando ou pesquisa…',
|
|
103
112
|
uploadFiles: 'Carregar ficheiros',
|
|
104
113
|
uploadHelper: 'SVG, PNG, JPG ou GIF (máx. 10MB)',
|
|
105
114
|
dropToUpload: 'Largue os ficheiros para carregar',
|
|
@@ -144,6 +153,9 @@ const fr = {
|
|
|
144
153
|
addItem: 'Ajouter un élément',
|
|
145
154
|
removeItem: 'Supprimer l’élément',
|
|
146
155
|
noItems: 'Aucun élément pour l’instant',
|
|
156
|
+
noResults: 'Aucun résultat',
|
|
157
|
+
commandPalette: 'Palette de commandes',
|
|
158
|
+
commandPalettePlaceholder: 'Tapez une commande ou recherchez…',
|
|
147
159
|
uploadFiles: 'Téléverser des fichiers',
|
|
148
160
|
uploadHelper: 'SVG, PNG, JPG ou GIF (max. 10MB)',
|
|
149
161
|
dropToUpload: 'Déposez les fichiers pour les téléverser',
|
|
@@ -188,6 +200,9 @@ const de = {
|
|
|
188
200
|
addItem: 'Element hinzufügen',
|
|
189
201
|
removeItem: 'Element entfernen',
|
|
190
202
|
noItems: 'Noch keine Elemente',
|
|
203
|
+
noResults: 'Keine Ergebnisse',
|
|
204
|
+
commandPalette: 'Befehlspalette',
|
|
205
|
+
commandPalettePlaceholder: 'Befehl eingeben oder suchen…',
|
|
191
206
|
uploadFiles: 'Dateien hochladen',
|
|
192
207
|
uploadHelper: 'SVG, PNG, JPG oder GIF (max. 10MB)',
|
|
193
208
|
dropToUpload: 'Dateien zum Hochladen ablegen',
|
|
@@ -232,6 +247,9 @@ const ca = {
|
|
|
232
247
|
addItem: 'Afegir element',
|
|
233
248
|
removeItem: 'Eliminar element',
|
|
234
249
|
noItems: 'Encara sense elements',
|
|
250
|
+
noResults: 'Sense resultats',
|
|
251
|
+
commandPalette: 'Paleta de comandes',
|
|
252
|
+
commandPalettePlaceholder: 'Escriu una comanda o cerca…',
|
|
235
253
|
uploadFiles: 'Pujar fitxers',
|
|
236
254
|
uploadHelper: 'SVG, PNG, JPG o GIF (màx. 10MB)',
|
|
237
255
|
dropToUpload: 'Deixa anar els fitxers per pujar-los',
|