@r2digisolutions/components 0.6.3 → 0.6.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/dist/components/molecules/Dock/Dock.stories.d.ts +22 -0
- package/dist/components/molecules/Dock/Dock.stories.js +14 -0
- package/dist/components/molecules/Dock/Dock.svelte +29 -0
- package/dist/components/molecules/Dock/Dock.svelte.d.ts +10 -0
- package/dist/components/molecules/Dock/DockItem.svelte +69 -0
- package/dist/components/molecules/Dock/DockItem.svelte.d.ts +16 -0
- package/dist/components/molecules/Dock/DockStory.svelte +25 -0
- package/dist/components/molecules/Dock/DockStory.svelte.d.ts +7 -0
- package/dist/components/molecules/SpeedDial/SpeedDial.stories.d.ts +1 -1
- package/dist/components/organisms/DataGrid/DataGrid.svelte.d.ts +2 -2
- package/dist/components/organisms/FileUploader/FileUploader.svelte +160 -120
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/utils/i18n.d.ts +23 -1
- package/dist/utils/i18n.js +150 -0
- package/package.json +2 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { StoryObj } from '@storybook/svelte';
|
|
2
|
+
declare const meta: {
|
|
3
|
+
title: string;
|
|
4
|
+
component: import("svelte").Component<{
|
|
5
|
+
size?: import("./Dock.svelte.ts").DockSize;
|
|
6
|
+
}, {}, "">;
|
|
7
|
+
tags: string[];
|
|
8
|
+
argTypes: {
|
|
9
|
+
size: {
|
|
10
|
+
control: "select";
|
|
11
|
+
options: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
args: {
|
|
15
|
+
size: "sm";
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export default meta;
|
|
19
|
+
type Story = StoryObj<typeof meta>;
|
|
20
|
+
export declare const Default: Story;
|
|
21
|
+
export declare const Mini: Story;
|
|
22
|
+
export declare const Medium: Story;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import DockStory from './DockStory.svelte';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Molecules/Dock',
|
|
4
|
+
component: DockStory,
|
|
5
|
+
tags: ['autodocs'],
|
|
6
|
+
argTypes: {
|
|
7
|
+
size: { control: 'select', options: ['mini', 'sm', 'md'] }
|
|
8
|
+
},
|
|
9
|
+
args: { size: 'sm' }
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
export const Default = {};
|
|
13
|
+
export const Mini = { args: { size: 'mini' } };
|
|
14
|
+
export const Medium = { args: { size: 'md' } };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export type DockSize = 'mini' | 'sm' | 'md';
|
|
5
|
+
|
|
6
|
+
interface DockProps {
|
|
7
|
+
size?: DockSize;
|
|
8
|
+
class?: string;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { size = 'mini', class: className = '', children }: DockProps = $props();
|
|
13
|
+
|
|
14
|
+
const sizes: Record<DockSize, string> = {
|
|
15
|
+
mini: 'rounded-full p-0.5',
|
|
16
|
+
sm: 'rounded-full p-0.5',
|
|
17
|
+
md: 'rounded-2xl p-1'
|
|
18
|
+
};
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
class={[
|
|
23
|
+
'border-border/70 bg-surface-overlay/80 shadow-sm backdrop-blur-md isolate inline-flex items-center overflow-hidden border',
|
|
24
|
+
sizes[size],
|
|
25
|
+
className
|
|
26
|
+
]}
|
|
27
|
+
>
|
|
28
|
+
{@render children?.()}
|
|
29
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type DockSize = 'mini' | 'sm' | 'md';
|
|
3
|
+
interface DockProps {
|
|
4
|
+
size?: DockSize;
|
|
5
|
+
class?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Dock: import("svelte").Component<DockProps, {}, "">;
|
|
9
|
+
type Dock = ReturnType<typeof Dock>;
|
|
10
|
+
export default Dock;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { DockSize } from './Dock.svelte';
|
|
4
|
+
|
|
5
|
+
interface DockItemProps {
|
|
6
|
+
as?: 'button' | 'a';
|
|
7
|
+
href?: string;
|
|
8
|
+
onclick?: (e: MouseEvent) => void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
active?: boolean;
|
|
11
|
+
size?: DockSize;
|
|
12
|
+
ariaLabel?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
as = 'button',
|
|
19
|
+
href,
|
|
20
|
+
onclick,
|
|
21
|
+
disabled = false,
|
|
22
|
+
active = false,
|
|
23
|
+
size = 'mini',
|
|
24
|
+
ariaLabel,
|
|
25
|
+
class: className = '',
|
|
26
|
+
children
|
|
27
|
+
}: DockItemProps = $props();
|
|
28
|
+
|
|
29
|
+
const sizes: Record<DockSize, string> = {
|
|
30
|
+
mini: 'h-7 px-2 text-[11px]',
|
|
31
|
+
sm: 'h-8 px-2.5 text-xs',
|
|
32
|
+
md: 'h-9 px-3 text-sm'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const classes = $derived([
|
|
36
|
+
'relative inline-flex items-center justify-center gap-1.5 whitespace-nowrap select-none font-medium transition-all duration-150',
|
|
37
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500/30',
|
|
38
|
+
sizes[size],
|
|
39
|
+
active
|
|
40
|
+
? 'bg-surface-elevated text-primary shadow-sm ring-1 ring-border'
|
|
41
|
+
: 'text-secondary hover:bg-surface-elevated/70 hover:text-primary',
|
|
42
|
+
disabled ? 'pointer-events-none opacity-50' : 'cursor-pointer',
|
|
43
|
+
'[&[data-active]:first-child]:rounded-l-full',
|
|
44
|
+
'[&[data-active]:last-child]:rounded-r-full',
|
|
45
|
+
'[&[data-active]:not(:first-child):not(:last-child)]:rounded-none',
|
|
46
|
+
'[&:not([data-active]):first-child:hover]:rounded-l-full',
|
|
47
|
+
'[&:not([data-active]):last-child:hover]:rounded-r-full',
|
|
48
|
+
'[&:not([data-active]):not(:first-child):not(:last-child):hover]:rounded-none',
|
|
49
|
+
className
|
|
50
|
+
]);
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
{#if as === 'a' || href}
|
|
54
|
+
<a {href} aria-label={ariaLabel} data-active={active || undefined} class={classes}>
|
|
55
|
+
{@render children?.()}
|
|
56
|
+
</a>
|
|
57
|
+
{:else}
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
{disabled}
|
|
61
|
+
aria-label={ariaLabel}
|
|
62
|
+
aria-pressed={active}
|
|
63
|
+
data-active={active || undefined}
|
|
64
|
+
{onclick}
|
|
65
|
+
class={classes}
|
|
66
|
+
>
|
|
67
|
+
{@render children?.()}
|
|
68
|
+
</button>
|
|
69
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { DockSize } from './Dock.svelte';
|
|
3
|
+
interface DockItemProps {
|
|
4
|
+
as?: 'button' | 'a';
|
|
5
|
+
href?: string;
|
|
6
|
+
onclick?: (e: MouseEvent) => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
active?: boolean;
|
|
9
|
+
size?: DockSize;
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
declare const DockItem: import("svelte").Component<DockItemProps, {}, "">;
|
|
15
|
+
type DockItem = ReturnType<typeof DockItem>;
|
|
16
|
+
export default DockItem;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Dock from './Dock.svelte';
|
|
3
|
+
import DockItem from './DockItem.svelte';
|
|
4
|
+
import type { DockSize } from './Dock.svelte';
|
|
5
|
+
|
|
6
|
+
let { size = 'sm' }: { size?: DockSize } = $props();
|
|
7
|
+
|
|
8
|
+
let value = $state('es');
|
|
9
|
+
const langs = ['es', 'en', 'ca', 'fr', 'de'];
|
|
10
|
+
const filled = new Set(['es', 'en']);
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class="gap-4 flex flex-col">
|
|
14
|
+
<Dock {size}>
|
|
15
|
+
{#each langs as lang (lang)}
|
|
16
|
+
<DockItem {size} active={value === lang} onclick={() => (value = lang)}>
|
|
17
|
+
{lang.toUpperCase()}
|
|
18
|
+
{#if filled.has(lang)}
|
|
19
|
+
<span class="h-1.5 w-1.5 bg-emerald-500 rounded-full" aria-hidden="true"></span>
|
|
20
|
+
{/if}
|
|
21
|
+
</DockItem>
|
|
22
|
+
{/each}
|
|
23
|
+
</Dock>
|
|
24
|
+
<p class="text-xs text-muted">Selected: {value.toUpperCase()}</p>
|
|
25
|
+
</div>
|
|
@@ -2,7 +2,7 @@ import type { StoryObj } from '@storybook/svelte';
|
|
|
2
2
|
declare const meta: {
|
|
3
3
|
title: string;
|
|
4
4
|
component: import("svelte").Component<{
|
|
5
|
-
example?: "destructive" | "
|
|
5
|
+
example?: "destructive" | "media" | "toolbar" | "compose";
|
|
6
6
|
position?: import("./SpeedDial.svelte.ts").SpeedDialPosition;
|
|
7
7
|
direction?: import("./SpeedDial.svelte.ts").SpeedDialDirection;
|
|
8
8
|
variant?: import("./SpeedDial.svelte.ts").SpeedDialVariant;
|
|
@@ -77,7 +77,7 @@ declare function $$render<T extends Record<string, unknown> = Record<string, unk
|
|
|
77
77
|
onrowdblclick?: (row: T) => void;
|
|
78
78
|
};
|
|
79
79
|
exports: {};
|
|
80
|
-
bindings: "notes" | "selection" | "marks" | "formatRules" | "filterQuery" | "columnFilters" | "expandedRows"
|
|
80
|
+
bindings: "viewMode" | "notes" | "selection" | "marks" | "formatRules" | "filterQuery" | "columnFilters" | "expandedRows";
|
|
81
81
|
slots: {};
|
|
82
82
|
events: {};
|
|
83
83
|
};
|
|
@@ -85,7 +85,7 @@ declare class __sveltets_Render<T extends Record<string, unknown> = Record<strin
|
|
|
85
85
|
props(): ReturnType<typeof $$render<T>>['props'];
|
|
86
86
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
87
87
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
88
|
-
bindings(): "notes" | "selection" | "marks" | "formatRules" | "filterQuery" | "columnFilters" | "expandedRows"
|
|
88
|
+
bindings(): "viewMode" | "notes" | "selection" | "marks" | "formatRules" | "filterQuery" | "columnFilters" | "expandedRows";
|
|
89
89
|
exports(): {};
|
|
90
90
|
}
|
|
91
91
|
interface $$IsomorphicComponent {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onDestroy } from 'svelte';
|
|
3
|
+
import { i18n } from '../../../utils/i18n.svelte.js';
|
|
3
4
|
|
|
4
5
|
type FileKind = 'image' | 'video' | 'audio' | 'pdf' | 'doc' | 'other';
|
|
5
6
|
type UploaderVariant = 'multiple' | 'single' | 'avatar';
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
|
|
51
52
|
let {
|
|
52
53
|
id = `upload-${Math.random().toString(36).slice(2, 9)}`,
|
|
53
|
-
label
|
|
54
|
-
helperText
|
|
54
|
+
label,
|
|
55
|
+
helperText,
|
|
55
56
|
accept = 'image/*',
|
|
56
57
|
variant = 'multiple',
|
|
57
58
|
view = $bindable('list'),
|
|
@@ -75,13 +76,13 @@
|
|
|
75
76
|
|
|
76
77
|
const isMultiple = $derived(variant === 'multiple');
|
|
77
78
|
const hasFile = $derived(fileList.length > 0);
|
|
79
|
+
const resolvedLabel = $derived(label ?? i18n.t('uploadFiles'));
|
|
80
|
+
const resolvedHelperText = $derived(helperText ?? i18n.t('uploadHelper'));
|
|
78
81
|
const primaryFile = $derived(fileList[0] as FileItem | undefined);
|
|
79
82
|
const externalSrc = $derived(src?.trim() || '');
|
|
80
|
-
const showSinglePreview = $derived(
|
|
81
|
-
variant === 'single' && (!!primaryFile || !!externalSrc)
|
|
82
|
-
);
|
|
83
|
+
const showSinglePreview = $derived(variant === 'single' && (!!primaryFile || !!externalSrc));
|
|
83
84
|
const singlePreviewUrl = $derived(primaryFile?.previewUrl || externalSrc);
|
|
84
|
-
const singlePreviewName = $derived(primaryFile?.name || srcName || '
|
|
85
|
+
const singlePreviewName = $derived(primaryFile?.name || srcName || i18n.t('media'));
|
|
85
86
|
const singlePreviewMeta = $derived(primaryFile?.sizeFormatted || '');
|
|
86
87
|
|
|
87
88
|
const labelId = $derived(`${id}-label`);
|
|
@@ -103,7 +104,8 @@
|
|
|
103
104
|
primaryFile?.kind === 'audio' ||
|
|
104
105
|
(!primaryFile &&
|
|
105
106
|
!!externalSrc &&
|
|
106
|
-
(resolvedAccept.includes('audio') ||
|
|
107
|
+
(resolvedAccept.includes('audio') ||
|
|
108
|
+
/\.(mp3|wav|ogg|m4a|aac|flac)(\?|$)/i.test(externalSrc)))
|
|
107
109
|
);
|
|
108
110
|
const dropLayout = $derived(layout === 'compact' ? 'compact' : layout);
|
|
109
111
|
|
|
@@ -142,9 +144,12 @@
|
|
|
142
144
|
function kindBadgeClass(kind: FileKind) {
|
|
143
145
|
if (kind === 'pdf') return 'bg-red-50 text-red-600 dark:bg-red-950/50 dark:text-red-300';
|
|
144
146
|
if (kind === 'doc') return 'bg-sky-50 text-sky-700 dark:bg-sky-950/50 dark:text-sky-300';
|
|
145
|
-
if (kind === 'image')
|
|
146
|
-
|
|
147
|
-
if (kind === '
|
|
147
|
+
if (kind === 'image')
|
|
148
|
+
return 'bg-brand-50 text-brand-700 dark:bg-brand-950/50 dark:text-brand-300';
|
|
149
|
+
if (kind === 'video')
|
|
150
|
+
return 'bg-violet-50 text-violet-700 dark:bg-violet-950/50 dark:text-violet-300';
|
|
151
|
+
if (kind === 'audio')
|
|
152
|
+
return 'bg-emerald-50 text-emerald-700 dark:bg-emerald-950/50 dark:text-emerald-300';
|
|
148
153
|
return 'bg-surface-overlay text-secondary';
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -180,15 +185,15 @@
|
|
|
180
185
|
|
|
181
186
|
for (const file of incoming) {
|
|
182
187
|
if (variant === 'avatar' && !file.type.startsWith('image/')) {
|
|
183
|
-
rejected.push(
|
|
188
|
+
rejected.push(i18n.t('imageRequired', { name: file.name }));
|
|
184
189
|
continue;
|
|
185
190
|
}
|
|
186
191
|
if (!matchesAccept(file)) {
|
|
187
|
-
rejected.push(
|
|
192
|
+
rejected.push(i18n.t('typeNotAllowed', { name: file.name }));
|
|
188
193
|
continue;
|
|
189
194
|
}
|
|
190
195
|
if (file.size > maxBytes) {
|
|
191
|
-
rejected.push(
|
|
196
|
+
rejected.push(i18n.t('exceedsMaxSize', { name: file.name, max: maxSizeMb }));
|
|
192
197
|
continue;
|
|
193
198
|
}
|
|
194
199
|
|
|
@@ -296,7 +301,7 @@
|
|
|
296
301
|
{#snippet fileBadge(kind: FileKind, className = '')}
|
|
297
302
|
<div
|
|
298
303
|
class={[
|
|
299
|
-
'flex items-center justify-center text-[10px]
|
|
304
|
+
'font-bold tracking-wide flex items-center justify-center text-[10px] uppercase',
|
|
300
305
|
kindBadgeClass(kind),
|
|
301
306
|
className
|
|
302
307
|
]}
|
|
@@ -313,8 +318,8 @@
|
|
|
313
318
|
<div
|
|
314
319
|
role="button"
|
|
315
320
|
tabindex={disabled ? -1 : 0}
|
|
316
|
-
aria-labelledby={
|
|
317
|
-
aria-describedby={[
|
|
321
|
+
aria-labelledby={resolvedLabel ? labelId : undefined}
|
|
322
|
+
aria-describedby={[resolvedHelperText ? helperId : '', errorMessage ? errorId : '']
|
|
318
323
|
.filter(Boolean)
|
|
319
324
|
.join(' ') || undefined}
|
|
320
325
|
aria-disabled={disabled || undefined}
|
|
@@ -332,27 +337,27 @@
|
|
|
332
337
|
onclick={openPicker}
|
|
333
338
|
class={[
|
|
334
339
|
'relative overflow-hidden border border-dashed transition-colors duration-150 outline-none',
|
|
335
|
-
'focus-visible:ring-
|
|
340
|
+
'focus-visible:ring-brand-500/30 focus-visible:border-brand-500 focus-visible:ring-2',
|
|
336
341
|
'rounded-xl',
|
|
337
342
|
isDragging
|
|
338
343
|
? 'border-brand-500 bg-brand-500/5 dark:bg-brand-950/40'
|
|
339
344
|
: 'border-border bg-surface-elevated hover:border-border-strong hover:bg-surface-overlay/60',
|
|
340
|
-
disabled ? '
|
|
345
|
+
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
341
346
|
]}
|
|
342
347
|
>
|
|
343
348
|
<div
|
|
344
349
|
class={[
|
|
345
350
|
'flex',
|
|
346
351
|
vertical
|
|
347
|
-
? '
|
|
352
|
+
? 'gap-2 px-3 py-5 flex-col items-center text-center'
|
|
348
353
|
: compact
|
|
349
|
-
? '
|
|
350
|
-
: '
|
|
354
|
+
? 'gap-3 px-3 py-2.5 flex-row items-center'
|
|
355
|
+
: 'gap-4 px-4 py-5 sm:px-5 flex-row items-center'
|
|
351
356
|
]}
|
|
352
357
|
>
|
|
353
358
|
<div
|
|
354
359
|
class={[
|
|
355
|
-
'flex shrink-0 items-center justify-center
|
|
360
|
+
'rounded-lg flex shrink-0 items-center justify-center transition-colors duration-150',
|
|
356
361
|
vertical ? 'h-10 w-10' : compact ? 'h-8 w-8' : 'h-11 w-11',
|
|
357
362
|
isDragging
|
|
358
363
|
? 'bg-brand-600 text-white'
|
|
@@ -384,47 +389,44 @@
|
|
|
384
389
|
]}
|
|
385
390
|
>
|
|
386
391
|
{#if isDragging}
|
|
387
|
-
|
|
392
|
+
{i18n.t('dropToUpload')}
|
|
388
393
|
{:else}
|
|
389
|
-
|
|
390
|
-
<span class="text-brand-700 dark:text-brand-300"> browse</span>
|
|
394
|
+
{i18n.t('dropOrBrowse')}
|
|
395
|
+
<span class="text-brand-700 dark:text-brand-300"> {i18n.t('browse')}</span>
|
|
391
396
|
{/if}
|
|
392
397
|
</p>
|
|
393
|
-
{#if
|
|
398
|
+
{#if resolvedHelperText}
|
|
394
399
|
<p
|
|
395
400
|
id={helperId}
|
|
396
|
-
class={[
|
|
397
|
-
'mt-0.5 text-[11px] leading-relaxed text-secondary',
|
|
398
|
-
vertical && 'text-center'
|
|
399
|
-
]}
|
|
401
|
+
class={['mt-0.5 leading-relaxed text-secondary text-[11px]', vertical && 'text-center']}
|
|
400
402
|
>
|
|
401
|
-
{
|
|
403
|
+
{resolvedHelperText}
|
|
402
404
|
</p>
|
|
403
405
|
{/if}
|
|
404
406
|
</div>
|
|
405
407
|
|
|
406
408
|
{#if !disabled && !compact && !vertical}
|
|
407
409
|
<span
|
|
408
|
-
class="
|
|
410
|
+
class="sm:inline-flex rounded-lg border-border bg-surface-elevated px-3 py-1.5 text-xs font-medium text-primary pointer-events-none hidden shrink-0 items-center border"
|
|
409
411
|
>
|
|
410
|
-
|
|
412
|
+
{i18n.t('browseButton')}
|
|
411
413
|
</span>
|
|
412
414
|
{/if}
|
|
413
415
|
</div>
|
|
414
416
|
|
|
415
417
|
{#if isDragging}
|
|
416
418
|
<div
|
|
417
|
-
class="pointer-events-none absolute
|
|
419
|
+
class="inset-0 ring-brand-500/40 rounded-xl pointer-events-none absolute ring-2 ring-inset"
|
|
418
420
|
aria-hidden="true"
|
|
419
421
|
></div>
|
|
420
422
|
{/if}
|
|
421
423
|
</div>
|
|
422
424
|
{/snippet}
|
|
423
425
|
|
|
424
|
-
<div class={['
|
|
426
|
+
<div class={['gap-3 flex w-full flex-col', className]}>
|
|
425
427
|
<input
|
|
426
428
|
bind:this={fileInputNode}
|
|
427
|
-
|
|
429
|
+
{id}
|
|
428
430
|
type="file"
|
|
429
431
|
accept={resolvedAccept}
|
|
430
432
|
multiple={isMultiple}
|
|
@@ -435,31 +437,35 @@
|
|
|
435
437
|
aria-hidden="true"
|
|
436
438
|
/>
|
|
437
439
|
|
|
438
|
-
{#if
|
|
439
|
-
<div class="flex items-center justify-between
|
|
440
|
-
<div id={labelId} class="text-sm font-medium text-primary">{
|
|
440
|
+
{#if resolvedLabel}
|
|
441
|
+
<div class="gap-3 flex items-center justify-between">
|
|
442
|
+
<div id={labelId} class="text-sm font-medium text-primary">{resolvedLabel}</div>
|
|
441
443
|
|
|
442
444
|
{#if isMultiple && hasFile}
|
|
443
|
-
<div class="flex items-center
|
|
445
|
+
<div class="gap-2 flex items-center">
|
|
444
446
|
{#if showViewToggle}
|
|
445
447
|
<div
|
|
446
|
-
class="
|
|
448
|
+
class="rounded-lg border-border bg-surface-elevated p-0.5 inline-flex items-center border"
|
|
447
449
|
role="group"
|
|
448
|
-
aria-label=
|
|
450
|
+
aria-label={i18n.t('viewMode')}
|
|
449
451
|
>
|
|
450
452
|
<button
|
|
451
453
|
type="button"
|
|
452
454
|
onclick={() => (view = 'list')}
|
|
453
455
|
class={[
|
|
454
456
|
'rounded-md p-1.5 transition-colors',
|
|
455
|
-
view === 'list'
|
|
456
|
-
? 'bg-brand-600 text-white'
|
|
457
|
-
: 'text-secondary hover:text-primary'
|
|
457
|
+
view === 'list' ? 'bg-brand-600 text-white' : 'text-secondary hover:text-primary'
|
|
458
458
|
]}
|
|
459
459
|
aria-pressed={view === 'list'}
|
|
460
|
-
aria-label=
|
|
460
|
+
aria-label={i18n.t('listView')}
|
|
461
461
|
>
|
|
462
|
-
<svg
|
|
462
|
+
<svg
|
|
463
|
+
class="h-3.5 w-3.5"
|
|
464
|
+
viewBox="0 0 24 24"
|
|
465
|
+
fill="none"
|
|
466
|
+
stroke="currentColor"
|
|
467
|
+
stroke-width="2"
|
|
468
|
+
>
|
|
463
469
|
<path stroke-linecap="round" d="M4 6h16M4 12h16M4 18h16" />
|
|
464
470
|
</svg>
|
|
465
471
|
</button>
|
|
@@ -468,17 +474,13 @@
|
|
|
468
474
|
onclick={() => (view = 'grid')}
|
|
469
475
|
class={[
|
|
470
476
|
'rounded-md p-1.5 transition-colors',
|
|
471
|
-
view === 'grid'
|
|
472
|
-
? 'bg-brand-600 text-white'
|
|
473
|
-
: 'text-secondary hover:text-primary'
|
|
477
|
+
view === 'grid' ? 'bg-brand-600 text-white' : 'text-secondary hover:text-primary'
|
|
474
478
|
]}
|
|
475
479
|
aria-pressed={view === 'grid'}
|
|
476
|
-
aria-label=
|
|
480
|
+
aria-label={i18n.t('gridView')}
|
|
477
481
|
>
|
|
478
482
|
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor">
|
|
479
|
-
<path
|
|
480
|
-
d="M4 4h6v6H4V4zm10 0h6v6h-6V4zM4 14h6v6H4v-6zm10 0h6v6h-6v-6z"
|
|
481
|
-
/>
|
|
483
|
+
<path d="M4 4h6v6H4V4zm10 0h6v6h-6V4zM4 14h6v6H4v-6zm10 0h6v6h-6v-6z" />
|
|
482
484
|
</svg>
|
|
483
485
|
</button>
|
|
484
486
|
</div>
|
|
@@ -490,7 +492,7 @@
|
|
|
490
492
|
onclick={clearAll}
|
|
491
493
|
class="text-xs font-medium text-secondary hover:text-primary transition-colors"
|
|
492
494
|
>
|
|
493
|
-
|
|
495
|
+
{i18n.t('clearAll')}
|
|
494
496
|
</button>
|
|
495
497
|
{/if}
|
|
496
498
|
</div>
|
|
@@ -500,13 +502,13 @@
|
|
|
500
502
|
|
|
501
503
|
<!-- Avatar variant -->
|
|
502
504
|
{#if variant === 'avatar'}
|
|
503
|
-
<div class="flex items-center
|
|
505
|
+
<div class="gap-4 flex items-center">
|
|
504
506
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
505
507
|
<div
|
|
506
508
|
role="button"
|
|
507
509
|
tabindex={disabled ? -1 : 0}
|
|
508
|
-
aria-labelledby={
|
|
509
|
-
aria-describedby={
|
|
510
|
+
aria-labelledby={resolvedLabel ? labelId : undefined}
|
|
511
|
+
aria-describedby={resolvedHelperText ? helperId : undefined}
|
|
510
512
|
aria-disabled={disabled || undefined}
|
|
511
513
|
ondragenter={handleDragEnter}
|
|
512
514
|
ondragover={handleDragOver}
|
|
@@ -521,14 +523,14 @@
|
|
|
521
523
|
}}
|
|
522
524
|
onclick={openPicker}
|
|
523
525
|
class={[
|
|
524
|
-
'
|
|
525
|
-
'focus-visible:ring-
|
|
526
|
+
'group h-24 w-24 relative shrink-0 overflow-hidden rounded-full border-2 border-dashed transition-colors outline-none',
|
|
527
|
+
'focus-visible:ring-brand-500/30 focus-visible:border-brand-500 focus-visible:ring-2',
|
|
526
528
|
isDragging
|
|
527
529
|
? 'border-brand-500 bg-brand-500/10'
|
|
528
530
|
: hasFile || externalSrc
|
|
529
531
|
? 'border-transparent'
|
|
530
532
|
: 'border-border bg-surface-elevated hover:border-border-strong',
|
|
531
|
-
disabled ? '
|
|
533
|
+
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
532
534
|
]}
|
|
533
535
|
>
|
|
534
536
|
{#if primaryFile?.previewUrl || externalSrc}
|
|
@@ -539,13 +541,19 @@
|
|
|
539
541
|
/>
|
|
540
542
|
{#if !disabled}
|
|
541
543
|
<div
|
|
542
|
-
class="
|
|
544
|
+
class="inset-0 bg-black/45 absolute flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100"
|
|
543
545
|
>
|
|
544
546
|
<span
|
|
545
|
-
class="
|
|
547
|
+
class="h-8 w-8 bg-white text-primary shadow-sm inline-flex items-center justify-center rounded-full"
|
|
546
548
|
aria-hidden="true"
|
|
547
549
|
>
|
|
548
|
-
<svg
|
|
550
|
+
<svg
|
|
551
|
+
class="h-4 w-4"
|
|
552
|
+
fill="none"
|
|
553
|
+
viewBox="0 0 24 24"
|
|
554
|
+
stroke="currentColor"
|
|
555
|
+
stroke-width="2"
|
|
556
|
+
>
|
|
549
557
|
<path
|
|
550
558
|
stroke-linecap="round"
|
|
551
559
|
stroke-linejoin="round"
|
|
@@ -556,115 +564,135 @@
|
|
|
556
564
|
</div>
|
|
557
565
|
{/if}
|
|
558
566
|
{:else}
|
|
559
|
-
<div class="flex h-full w-full flex-col items-center justify-center
|
|
560
|
-
<svg
|
|
567
|
+
<div class="gap-1 text-secondary flex h-full w-full flex-col items-center justify-center">
|
|
568
|
+
<svg
|
|
569
|
+
class="h-6 w-6"
|
|
570
|
+
fill="none"
|
|
571
|
+
viewBox="0 0 24 24"
|
|
572
|
+
stroke="currentColor"
|
|
573
|
+
stroke-width="1.75"
|
|
574
|
+
>
|
|
561
575
|
<path
|
|
562
576
|
stroke-linecap="round"
|
|
563
577
|
stroke-linejoin="round"
|
|
564
578
|
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0"
|
|
565
579
|
/>
|
|
566
580
|
</svg>
|
|
567
|
-
<span class="text-[10px]
|
|
581
|
+
<span class="font-medium text-[10px]">{i18n.t('upload')}</span>
|
|
568
582
|
</div>
|
|
569
583
|
{/if}
|
|
570
584
|
</div>
|
|
571
585
|
|
|
572
586
|
<div class="min-w-0 flex-1">
|
|
573
587
|
{#if primaryFile}
|
|
574
|
-
<p class="
|
|
588
|
+
<p class="text-sm font-medium text-primary truncate">{primaryFile.name}</p>
|
|
575
589
|
<p class="text-xs text-secondary">{primaryFile.sizeFormatted}</p>
|
|
576
|
-
<div class="mt-2 flex items-center
|
|
590
|
+
<div class="mt-2 gap-2 flex items-center">
|
|
577
591
|
<button
|
|
578
592
|
type="button"
|
|
579
593
|
onclick={openPicker}
|
|
580
|
-
|
|
581
|
-
class="
|
|
594
|
+
{disabled}
|
|
595
|
+
class="gap-1.5 rounded-lg border-border bg-surface-elevated px-2.5 py-1.5 text-xs font-medium text-primary hover:bg-surface-overlay inline-flex items-center border transition-colors disabled:opacity-50"
|
|
582
596
|
>
|
|
583
|
-
<svg
|
|
597
|
+
<svg
|
|
598
|
+
class="h-3.5 w-3.5"
|
|
599
|
+
fill="none"
|
|
600
|
+
viewBox="0 0 24 24"
|
|
601
|
+
stroke="currentColor"
|
|
602
|
+
stroke-width="2"
|
|
603
|
+
>
|
|
584
604
|
<path
|
|
585
605
|
stroke-linecap="round"
|
|
586
606
|
stroke-linejoin="round"
|
|
587
607
|
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931z"
|
|
588
608
|
/>
|
|
589
609
|
</svg>
|
|
590
|
-
|
|
610
|
+
{i18n.t('change')}
|
|
591
611
|
</button>
|
|
592
612
|
<button
|
|
593
613
|
type="button"
|
|
594
614
|
onclick={() => primaryFile && removeFile(primaryFile.id)}
|
|
595
|
-
|
|
596
|
-
class="
|
|
615
|
+
{disabled}
|
|
616
|
+
class="rounded-lg px-2.5 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40 inline-flex items-center transition-colors disabled:opacity-50"
|
|
597
617
|
>
|
|
598
|
-
|
|
618
|
+
{i18n.t('remove')}
|
|
599
619
|
</button>
|
|
600
620
|
</div>
|
|
601
621
|
{:else}
|
|
602
|
-
<p class="text-sm font-medium text-primary">
|
|
603
|
-
{#if
|
|
604
|
-
<p id={helperId} class="mt-0.5 text-xs text-secondary">{
|
|
622
|
+
<p class="text-sm font-medium text-primary">{i18n.t('addProfilePhoto')}</p>
|
|
623
|
+
{#if resolvedHelperText}
|
|
624
|
+
<p id={helperId} class="mt-0.5 text-xs text-secondary">{resolvedHelperText}</p>
|
|
605
625
|
{/if}
|
|
606
626
|
{/if}
|
|
607
627
|
</div>
|
|
608
628
|
</div>
|
|
609
629
|
|
|
610
|
-
|
|
630
|
+
<!-- Single variant: preview card with edit -->
|
|
611
631
|
{:else if showSinglePreview}
|
|
612
632
|
<div
|
|
613
633
|
class={[
|
|
614
|
-
'
|
|
615
|
-
isDragging && 'ring-
|
|
634
|
+
'rounded-xl border-border bg-surface-elevated shadow-sm overflow-hidden border',
|
|
635
|
+
isDragging && 'ring-brand-500/40 ring-2'
|
|
616
636
|
]}
|
|
617
637
|
ondragenter={handleDragEnter}
|
|
618
638
|
ondragover={handleDragOver}
|
|
619
639
|
ondragleave={handleDragLeave}
|
|
620
640
|
ondrop={handleDrop}
|
|
621
641
|
role="group"
|
|
622
|
-
aria-labelledby={
|
|
642
|
+
aria-labelledby={resolvedLabel ? labelId : undefined}
|
|
623
643
|
>
|
|
624
|
-
<div class="
|
|
644
|
+
<div class="aspect-video bg-surface-overlay relative">
|
|
625
645
|
{#if singlePreviewUrl && singleIsVideo}
|
|
626
|
-
<video
|
|
627
|
-
src={singlePreviewUrl}
|
|
628
|
-
class="h-full w-full object-cover"
|
|
629
|
-
muted
|
|
630
|
-
playsinline
|
|
646
|
+
<video src={singlePreviewUrl} class="h-full w-full object-cover" muted playsinline
|
|
631
647
|
></video>
|
|
632
648
|
{:else if singlePreviewUrl && singleIsAudio}
|
|
633
|
-
<div class="flex h-full w-full flex-col items-center justify-center
|
|
649
|
+
<div class="gap-3 px-4 flex h-full w-full flex-col items-center justify-center">
|
|
634
650
|
{@render fileBadge('audio', 'h-14 w-14 rounded-xl text-xs')}
|
|
635
|
-
<audio src={singlePreviewUrl} controls class="w-
|
|
651
|
+
<audio src={singlePreviewUrl} controls class="max-w-xs w-full"></audio>
|
|
636
652
|
</div>
|
|
637
653
|
{:else if singlePreviewUrl}
|
|
638
654
|
<img src={singlePreviewUrl} alt="" class="h-full w-full object-cover" />
|
|
639
655
|
{:else}
|
|
640
|
-
<div class="flex h-full w-full flex-col items-center justify-center
|
|
656
|
+
<div class="gap-2 flex h-full w-full flex-col items-center justify-center">
|
|
641
657
|
{@render fileBadge(primaryFile?.kind ?? 'other', 'h-14 w-14 rounded-xl text-xs')}
|
|
642
658
|
</div>
|
|
643
659
|
{/if}
|
|
644
660
|
|
|
645
661
|
{#if !disabled}
|
|
646
|
-
<div class="
|
|
662
|
+
<div class="right-3 top-3 gap-1.5 absolute flex items-center">
|
|
647
663
|
<button
|
|
648
664
|
type="button"
|
|
649
665
|
onclick={openPicker}
|
|
650
|
-
class="
|
|
666
|
+
class="gap-1.5 rounded-lg bg-white/95 px-2.5 py-1.5 text-xs font-medium text-primary shadow-sm backdrop-blur hover:bg-white dark:bg-slate-900/95 dark:hover:bg-slate-900 inline-flex items-center transition"
|
|
651
667
|
>
|
|
652
|
-
<svg
|
|
668
|
+
<svg
|
|
669
|
+
class="h-3.5 w-3.5"
|
|
670
|
+
fill="none"
|
|
671
|
+
viewBox="0 0 24 24"
|
|
672
|
+
stroke="currentColor"
|
|
673
|
+
stroke-width="2"
|
|
674
|
+
>
|
|
653
675
|
<path
|
|
654
676
|
stroke-linecap="round"
|
|
655
677
|
stroke-linejoin="round"
|
|
656
678
|
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931z"
|
|
657
679
|
/>
|
|
658
680
|
</svg>
|
|
659
|
-
|
|
681
|
+
{i18n.t('edit')}
|
|
660
682
|
</button>
|
|
661
683
|
<button
|
|
662
684
|
type="button"
|
|
663
685
|
onclick={clearSingle}
|
|
664
|
-
class="
|
|
665
|
-
aria-label={
|
|
686
|
+
class="rounded-lg bg-white/95 p-1.5 text-secondary shadow-sm backdrop-blur hover:bg-white hover:text-red-600 dark:bg-slate-900/95 dark:hover:bg-slate-900 dark:hover:text-red-400 inline-flex items-center justify-center transition"
|
|
687
|
+
aria-label={i18n.t('removeNamed', { name: singlePreviewName })}
|
|
666
688
|
>
|
|
667
|
-
<svg
|
|
689
|
+
<svg
|
|
690
|
+
class="h-3.5 w-3.5"
|
|
691
|
+
fill="none"
|
|
692
|
+
viewBox="0 0 24 24"
|
|
693
|
+
stroke="currentColor"
|
|
694
|
+
stroke-width="2"
|
|
695
|
+
>
|
|
668
696
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
669
697
|
</svg>
|
|
670
698
|
</button>
|
|
@@ -672,20 +700,20 @@
|
|
|
672
700
|
{/if}
|
|
673
701
|
</div>
|
|
674
702
|
|
|
675
|
-
<div class="
|
|
703
|
+
<div class="gap-3 border-border px-4 py-3 flex items-center justify-between border-t">
|
|
676
704
|
<div class="min-w-0">
|
|
677
|
-
<p class="
|
|
705
|
+
<p class="text-sm font-medium text-primary truncate">{singlePreviewName}</p>
|
|
678
706
|
{#if singlePreviewMeta}
|
|
679
707
|
<p class="text-xs text-secondary">{singlePreviewMeta}</p>
|
|
680
708
|
{/if}
|
|
681
709
|
</div>
|
|
682
|
-
{#if
|
|
683
|
-
<p id={helperId} class="
|
|
710
|
+
{#if resolvedHelperText}
|
|
711
|
+
<p id={helperId} class="text-xs text-secondary shrink-0">{resolvedHelperText}</p>
|
|
684
712
|
{/if}
|
|
685
713
|
</div>
|
|
686
714
|
</div>
|
|
687
715
|
|
|
688
|
-
|
|
716
|
+
<!-- Dropzone: hide the big one when multiple already has files (compact “add more” below) -->
|
|
689
717
|
{:else if !(isMultiple && hasFile && showFileList)}
|
|
690
718
|
{@render dropzone()}
|
|
691
719
|
{/if}
|
|
@@ -697,12 +725,12 @@
|
|
|
697
725
|
<!-- Multiple file cards (optional — MediaAssetBrowser owns its own grid) -->
|
|
698
726
|
{#if isMultiple && hasFile && showFileList}
|
|
699
727
|
{#if view === 'grid'}
|
|
700
|
-
<ul class="
|
|
728
|
+
<ul class="gap-2.5 sm:grid-cols-3 grid grid-cols-2" aria-label={i18n.t('selectedFiles')}>
|
|
701
729
|
{#each fileList as item (item.id)}
|
|
702
730
|
<li
|
|
703
|
-
class="group
|
|
731
|
+
class="group rounded-xl border-border bg-surface-elevated shadow-sm hover:border-border-strong relative overflow-hidden border transition"
|
|
704
732
|
>
|
|
705
|
-
<div class="relative aspect-square
|
|
733
|
+
<div class="bg-surface-overlay relative aspect-square">
|
|
706
734
|
{#if item.previewUrl}
|
|
707
735
|
<img src={item.previewUrl} alt="" class="h-full w-full object-cover" />
|
|
708
736
|
{:else}
|
|
@@ -715,51 +743,63 @@
|
|
|
715
743
|
<button
|
|
716
744
|
type="button"
|
|
717
745
|
onclick={() => removeFile(item.id)}
|
|
718
|
-
class="
|
|
719
|
-
aria-label={
|
|
746
|
+
class="right-2 top-2 h-7 w-7 rounded-lg bg-white/95 text-secondary shadow-sm backdrop-blur hover:text-red-600 dark:bg-slate-900/95 dark:hover:text-red-400 absolute inline-flex items-center justify-center opacity-0 transition group-hover:opacity-100 focus-visible:opacity-100"
|
|
747
|
+
aria-label={i18n.t('removeNamed', { name: item.name })}
|
|
720
748
|
>
|
|
721
|
-
<svg
|
|
749
|
+
<svg
|
|
750
|
+
class="h-3.5 w-3.5"
|
|
751
|
+
fill="none"
|
|
752
|
+
viewBox="0 0 24 24"
|
|
753
|
+
stroke="currentColor"
|
|
754
|
+
stroke-width="2"
|
|
755
|
+
>
|
|
722
756
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
723
757
|
</svg>
|
|
724
758
|
</button>
|
|
725
759
|
{/if}
|
|
726
760
|
</div>
|
|
727
|
-
<div class="border-
|
|
728
|
-
<p class="
|
|
761
|
+
<div class="border-border px-2.5 py-2 border-t">
|
|
762
|
+
<p class="text-xs font-medium text-primary truncate">{item.name}</p>
|
|
729
763
|
<p class="text-xs text-secondary">{item.sizeFormatted}</p>
|
|
730
764
|
</div>
|
|
731
765
|
</li>
|
|
732
766
|
{/each}
|
|
733
767
|
</ul>
|
|
734
768
|
{:else}
|
|
735
|
-
<ul class="flex flex-col
|
|
769
|
+
<ul class="gap-2 flex flex-col" aria-label={i18n.t('selectedFiles')}>
|
|
736
770
|
{#each fileList as item (item.id)}
|
|
737
771
|
<li
|
|
738
|
-
class="group
|
|
772
|
+
class="group gap-3 rounded-xl border-border bg-surface-elevated p-2.5 shadow-sm hover:border-border-strong flex items-center border transition"
|
|
739
773
|
>
|
|
740
774
|
{#if item.previewUrl}
|
|
741
775
|
<img
|
|
742
776
|
src={item.previewUrl}
|
|
743
777
|
alt=""
|
|
744
|
-
class="h-12 w-12
|
|
778
|
+
class="h-12 w-12 rounded-lg border-border shrink-0 border object-cover"
|
|
745
779
|
/>
|
|
746
780
|
{:else}
|
|
747
781
|
{@render fileBadge(item.kind, 'h-12 w-12 shrink-0 rounded-lg')}
|
|
748
782
|
{/if}
|
|
749
783
|
|
|
750
784
|
<div class="min-w-0 flex-1">
|
|
751
|
-
<p class="
|
|
785
|
+
<p class="text-sm font-medium text-primary truncate">{item.name}</p>
|
|
752
786
|
<p class="text-xs text-secondary">{item.sizeFormatted} · {kindLabel(item.kind)}</p>
|
|
753
787
|
</div>
|
|
754
788
|
|
|
755
789
|
<button
|
|
756
790
|
type="button"
|
|
757
791
|
onclick={() => removeFile(item.id)}
|
|
758
|
-
|
|
759
|
-
class="
|
|
760
|
-
aria-label={
|
|
792
|
+
{disabled}
|
|
793
|
+
class="rounded-lg p-2 text-secondary hover:bg-red-50 hover:text-red-600 focus-visible:ring-brand-500/30 dark:hover:bg-red-950/40 dark:hover:text-red-400 shrink-0 transition focus-visible:ring-2 focus-visible:outline-none disabled:opacity-50"
|
|
794
|
+
aria-label={i18n.t('removeNamed', { name: item.name })}
|
|
761
795
|
>
|
|
762
|
-
<svg
|
|
796
|
+
<svg
|
|
797
|
+
class="h-4 w-4"
|
|
798
|
+
fill="none"
|
|
799
|
+
viewBox="0 0 24 24"
|
|
800
|
+
stroke="currentColor"
|
|
801
|
+
stroke-width="2"
|
|
802
|
+
>
|
|
763
803
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
764
804
|
</svg>
|
|
765
805
|
</button>
|
package/dist/index.d.ts
CHANGED
|
@@ -186,6 +186,9 @@ export { default as PasswordInput } from './components/molecules/PasswordInput/P
|
|
|
186
186
|
export { default as CopyButton } from './components/molecules/CopyButton/CopyButton.svelte';
|
|
187
187
|
export { default as SegmentedControl } from './components/molecules/SegmentedControl/SegmentedControl.svelte';
|
|
188
188
|
export type { SegmentItem } from './components/molecules/SegmentedControl/SegmentedControl.svelte';
|
|
189
|
+
export { default as Dock } from './components/molecules/Dock/Dock.svelte';
|
|
190
|
+
export type { DockSize } from './components/molecules/Dock/Dock.svelte';
|
|
191
|
+
export { default as DockItem } from './components/molecules/Dock/DockItem.svelte';
|
|
189
192
|
export { default as Popover } from './components/molecules/Popover/Popover.svelte';
|
|
190
193
|
export type { PopoverPlacement, PopoverAlign } from './components/molecules/Popover/Popover.svelte';
|
|
191
194
|
export { default as HoverCard } from './components/molecules/HoverCard/HoverCard.svelte';
|
package/dist/index.js
CHANGED
|
@@ -150,6 +150,8 @@ export { default as SearchInput } from './components/molecules/SearchInput/Searc
|
|
|
150
150
|
export { default as PasswordInput } from './components/molecules/PasswordInput/PasswordInput.svelte';
|
|
151
151
|
export { default as CopyButton } from './components/molecules/CopyButton/CopyButton.svelte';
|
|
152
152
|
export { default as SegmentedControl } from './components/molecules/SegmentedControl/SegmentedControl.svelte';
|
|
153
|
+
export { default as Dock } from './components/molecules/Dock/Dock.svelte';
|
|
154
|
+
export { default as DockItem } from './components/molecules/Dock/DockItem.svelte';
|
|
153
155
|
export { default as Popover } from './components/molecules/Popover/Popover.svelte';
|
|
154
156
|
export { default as HoverCard } from './components/molecules/HoverCard/HoverCard.svelte';
|
|
155
157
|
export { default as Timeline } from './components/molecules/Timeline/Timeline.svelte';
|
package/dist/utils/i18n.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type LocaleCode = 'en' | 'es' | 'pt' | 'fr' | 'de' | (string & {});
|
|
1
|
+
export type LocaleCode = 'en' | 'es' | 'ca' | 'pt' | 'fr' | 'de' | (string & {});
|
|
2
2
|
/** Built-in UI / form / validation strings. Apps can merge overrides. */
|
|
3
3
|
export interface UiMessages {
|
|
4
4
|
save: string;
|
|
@@ -14,6 +14,28 @@ export interface UiMessages {
|
|
|
14
14
|
addItem: string;
|
|
15
15
|
removeItem: string;
|
|
16
16
|
noItems: string;
|
|
17
|
+
/** FileUploader */
|
|
18
|
+
uploadFiles: string;
|
|
19
|
+
uploadHelper: string;
|
|
20
|
+
dropToUpload: string;
|
|
21
|
+
dropOrBrowse: string;
|
|
22
|
+
browse: string;
|
|
23
|
+
browseButton: string;
|
|
24
|
+
viewMode: string;
|
|
25
|
+
listView: string;
|
|
26
|
+
gridView: string;
|
|
27
|
+
clearAll: string;
|
|
28
|
+
upload: string;
|
|
29
|
+
change: string;
|
|
30
|
+
remove: string;
|
|
31
|
+
edit: string;
|
|
32
|
+
removeNamed: string;
|
|
33
|
+
selectedFiles: string;
|
|
34
|
+
addProfilePhoto: string;
|
|
35
|
+
media: string;
|
|
36
|
+
imageRequired: string;
|
|
37
|
+
typeNotAllowed: string;
|
|
38
|
+
exceedsMaxSize: string;
|
|
17
39
|
/** Validation */
|
|
18
40
|
fieldRequired: string;
|
|
19
41
|
invalidEmail: string;
|
package/dist/utils/i18n.js
CHANGED
|
@@ -12,6 +12,27 @@ const en = {
|
|
|
12
12
|
addItem: 'Add item',
|
|
13
13
|
removeItem: 'Remove item',
|
|
14
14
|
noItems: 'No items yet',
|
|
15
|
+
uploadFiles: 'Upload files',
|
|
16
|
+
uploadHelper: 'SVG, PNG, JPG or GIF (max. 10MB)',
|
|
17
|
+
dropToUpload: 'Drop files to upload',
|
|
18
|
+
dropOrBrowse: 'Drop files or',
|
|
19
|
+
browse: 'browse',
|
|
20
|
+
browseButton: 'Browse',
|
|
21
|
+
viewMode: 'View mode',
|
|
22
|
+
listView: 'List view',
|
|
23
|
+
gridView: 'Grid view',
|
|
24
|
+
clearAll: 'Clear all',
|
|
25
|
+
upload: 'Upload',
|
|
26
|
+
change: 'Change',
|
|
27
|
+
remove: 'Remove',
|
|
28
|
+
edit: 'Edit',
|
|
29
|
+
removeNamed: 'Remove {name}',
|
|
30
|
+
selectedFiles: 'Selected files',
|
|
31
|
+
addProfilePhoto: 'Add a profile photo',
|
|
32
|
+
media: 'Media',
|
|
33
|
+
imageRequired: '{name}: an image is required',
|
|
34
|
+
typeNotAllowed: '{name}: file type not allowed',
|
|
35
|
+
exceedsMaxSize: '{name}: exceeds {max}MB',
|
|
15
36
|
fieldRequired: 'This field is required',
|
|
16
37
|
invalidEmail: 'Enter a valid email address',
|
|
17
38
|
invalidUrl: 'Enter a valid URL',
|
|
@@ -35,6 +56,27 @@ const es = {
|
|
|
35
56
|
addItem: 'Añadir elemento',
|
|
36
57
|
removeItem: 'Eliminar elemento',
|
|
37
58
|
noItems: 'Sin elementos todavía',
|
|
59
|
+
uploadFiles: 'Subir archivos',
|
|
60
|
+
uploadHelper: 'SVG, PNG, JPG o GIF (máx. 10MB)',
|
|
61
|
+
dropToUpload: 'Suelta los archivos para subirlos',
|
|
62
|
+
dropOrBrowse: 'Suelta archivos o',
|
|
63
|
+
browse: 'explora',
|
|
64
|
+
browseButton: 'Explorar',
|
|
65
|
+
viewMode: 'Modo de vista',
|
|
66
|
+
listView: 'Vista de lista',
|
|
67
|
+
gridView: 'Vista de cuadrícula',
|
|
68
|
+
clearAll: 'Borrar todo',
|
|
69
|
+
upload: 'Subir',
|
|
70
|
+
change: 'Cambiar',
|
|
71
|
+
remove: 'Quitar',
|
|
72
|
+
edit: 'Editar',
|
|
73
|
+
removeNamed: 'Quitar {name}',
|
|
74
|
+
selectedFiles: 'Archivos seleccionados',
|
|
75
|
+
addProfilePhoto: 'Añade una foto de perfil',
|
|
76
|
+
media: 'Multimedia',
|
|
77
|
+
imageRequired: '{name}: se requiere una imagen',
|
|
78
|
+
typeNotAllowed: '{name}: tipo no permitido',
|
|
79
|
+
exceedsMaxSize: '{name}: supera {max}MB',
|
|
38
80
|
fieldRequired: 'Este campo es obligatorio',
|
|
39
81
|
invalidEmail: 'Introduce un email válido',
|
|
40
82
|
invalidUrl: 'Introduce una URL válida',
|
|
@@ -58,6 +100,27 @@ const pt = {
|
|
|
58
100
|
addItem: 'Adicionar item',
|
|
59
101
|
removeItem: 'Remover item',
|
|
60
102
|
noItems: 'Ainda sem itens',
|
|
103
|
+
uploadFiles: 'Carregar ficheiros',
|
|
104
|
+
uploadHelper: 'SVG, PNG, JPG ou GIF (máx. 10MB)',
|
|
105
|
+
dropToUpload: 'Largue os ficheiros para carregar',
|
|
106
|
+
dropOrBrowse: 'Largue ficheiros ou',
|
|
107
|
+
browse: 'procurar',
|
|
108
|
+
browseButton: 'Procurar',
|
|
109
|
+
viewMode: 'Modo de visualização',
|
|
110
|
+
listView: 'Vista de lista',
|
|
111
|
+
gridView: 'Vista de grelha',
|
|
112
|
+
clearAll: 'Limpar tudo',
|
|
113
|
+
upload: 'Carregar',
|
|
114
|
+
change: 'Alterar',
|
|
115
|
+
remove: 'Remover',
|
|
116
|
+
edit: 'Editar',
|
|
117
|
+
removeNamed: 'Remover {name}',
|
|
118
|
+
selectedFiles: 'Ficheiros selecionados',
|
|
119
|
+
addProfilePhoto: 'Adicione uma foto de perfil',
|
|
120
|
+
media: 'Multimédia',
|
|
121
|
+
imageRequired: '{name}: é necessária uma imagem',
|
|
122
|
+
typeNotAllowed: '{name}: tipo não permitido',
|
|
123
|
+
exceedsMaxSize: '{name}: excede {max}MB',
|
|
61
124
|
fieldRequired: 'Este campo é obrigatório',
|
|
62
125
|
invalidEmail: 'Introduza um email válido',
|
|
63
126
|
invalidUrl: 'Introduza um URL válido',
|
|
@@ -81,6 +144,27 @@ const fr = {
|
|
|
81
144
|
addItem: 'Ajouter un élément',
|
|
82
145
|
removeItem: 'Supprimer l’élément',
|
|
83
146
|
noItems: 'Aucun élément pour l’instant',
|
|
147
|
+
uploadFiles: 'Téléverser des fichiers',
|
|
148
|
+
uploadHelper: 'SVG, PNG, JPG ou GIF (max. 10MB)',
|
|
149
|
+
dropToUpload: 'Déposez les fichiers pour les téléverser',
|
|
150
|
+
dropOrBrowse: 'Déposez des fichiers ou',
|
|
151
|
+
browse: 'parcourir',
|
|
152
|
+
browseButton: 'Parcourir',
|
|
153
|
+
viewMode: 'Mode d’affichage',
|
|
154
|
+
listView: 'Vue liste',
|
|
155
|
+
gridView: 'Vue grille',
|
|
156
|
+
clearAll: 'Tout effacer',
|
|
157
|
+
upload: 'Téléverser',
|
|
158
|
+
change: 'Changer',
|
|
159
|
+
remove: 'Retirer',
|
|
160
|
+
edit: 'Modifier',
|
|
161
|
+
removeNamed: 'Retirer {name}',
|
|
162
|
+
selectedFiles: 'Fichiers sélectionnés',
|
|
163
|
+
addProfilePhoto: 'Ajoutez une photo de profil',
|
|
164
|
+
media: 'Médias',
|
|
165
|
+
imageRequired: '{name} : une image est requise',
|
|
166
|
+
typeNotAllowed: '{name} : type non autorisé',
|
|
167
|
+
exceedsMaxSize: '{name} : dépasse {max}MB',
|
|
84
168
|
fieldRequired: 'Ce champ est obligatoire',
|
|
85
169
|
invalidEmail: 'Saisissez une adresse e-mail valide',
|
|
86
170
|
invalidUrl: 'Saisissez une URL valide',
|
|
@@ -104,6 +188,27 @@ const de = {
|
|
|
104
188
|
addItem: 'Element hinzufügen',
|
|
105
189
|
removeItem: 'Element entfernen',
|
|
106
190
|
noItems: 'Noch keine Elemente',
|
|
191
|
+
uploadFiles: 'Dateien hochladen',
|
|
192
|
+
uploadHelper: 'SVG, PNG, JPG oder GIF (max. 10MB)',
|
|
193
|
+
dropToUpload: 'Dateien zum Hochladen ablegen',
|
|
194
|
+
dropOrBrowse: 'Dateien ablegen oder',
|
|
195
|
+
browse: 'durchsuchen',
|
|
196
|
+
browseButton: 'Durchsuchen',
|
|
197
|
+
viewMode: 'Ansicht',
|
|
198
|
+
listView: 'Listenansicht',
|
|
199
|
+
gridView: 'Rasteransicht',
|
|
200
|
+
clearAll: 'Alle löschen',
|
|
201
|
+
upload: 'Hochladen',
|
|
202
|
+
change: 'Ändern',
|
|
203
|
+
remove: 'Entfernen',
|
|
204
|
+
edit: 'Bearbeiten',
|
|
205
|
+
removeNamed: '{name} entfernen',
|
|
206
|
+
selectedFiles: 'Ausgewählte Dateien',
|
|
207
|
+
addProfilePhoto: 'Profilfoto hinzufügen',
|
|
208
|
+
media: 'Medien',
|
|
209
|
+
imageRequired: '{name}: ein Bild ist erforderlich',
|
|
210
|
+
typeNotAllowed: '{name}: Dateityp nicht erlaubt',
|
|
211
|
+
exceedsMaxSize: '{name}: überschreitet {max}MB',
|
|
107
212
|
fieldRequired: 'Dieses Feld ist erforderlich',
|
|
108
213
|
invalidEmail: 'Geben Sie eine gültige E-Mail-Adresse ein',
|
|
109
214
|
invalidUrl: 'Geben Sie eine gültige URL ein',
|
|
@@ -113,10 +218,55 @@ const de = {
|
|
|
113
218
|
maxValue: 'Darf höchstens {max} sein',
|
|
114
219
|
patternMismatch: 'Ungültiges Format'
|
|
115
220
|
};
|
|
221
|
+
const ca = {
|
|
222
|
+
save: 'Desar',
|
|
223
|
+
cancel: 'Cancel·lar',
|
|
224
|
+
delete: 'Eliminar',
|
|
225
|
+
reset: 'Restablir',
|
|
226
|
+
submit: 'Enviar',
|
|
227
|
+
close: 'Tancar',
|
|
228
|
+
optional: 'Opcional',
|
|
229
|
+
required: 'Obligatori',
|
|
230
|
+
loading: 'S’està carregant…',
|
|
231
|
+
errorSummary: 'Corregeix el següent',
|
|
232
|
+
addItem: 'Afegir element',
|
|
233
|
+
removeItem: 'Eliminar element',
|
|
234
|
+
noItems: 'Encara sense elements',
|
|
235
|
+
uploadFiles: 'Pujar fitxers',
|
|
236
|
+
uploadHelper: 'SVG, PNG, JPG o GIF (màx. 10MB)',
|
|
237
|
+
dropToUpload: 'Deixa anar els fitxers per pujar-los',
|
|
238
|
+
dropOrBrowse: 'Deixa anar fitxers o',
|
|
239
|
+
browse: 'explora',
|
|
240
|
+
browseButton: 'Explorar',
|
|
241
|
+
viewMode: 'Mode de vista',
|
|
242
|
+
listView: 'Vista de llista',
|
|
243
|
+
gridView: 'Vista de graella',
|
|
244
|
+
clearAll: 'Esborrar-ho tot',
|
|
245
|
+
upload: 'Pujar',
|
|
246
|
+
change: 'Canviar',
|
|
247
|
+
remove: 'Treure',
|
|
248
|
+
edit: 'Editar',
|
|
249
|
+
removeNamed: 'Treure {name}',
|
|
250
|
+
selectedFiles: 'Fitxers seleccionats',
|
|
251
|
+
addProfilePhoto: 'Afegeix una foto de perfil',
|
|
252
|
+
media: 'Multimèdia',
|
|
253
|
+
imageRequired: '{name}: cal una imatge',
|
|
254
|
+
typeNotAllowed: '{name}: tipus no permès',
|
|
255
|
+
exceedsMaxSize: '{name}: supera {max}MB',
|
|
256
|
+
fieldRequired: 'Aquest camp és obligatori',
|
|
257
|
+
invalidEmail: 'Introdueix un email vàlid',
|
|
258
|
+
invalidUrl: 'Introdueix un URL vàlid',
|
|
259
|
+
minLength: 'Usa almenys {min} caràcters',
|
|
260
|
+
maxLength: 'Usa com a màxim {max} caràcters',
|
|
261
|
+
minValue: 'Ha de ser almenys {min}',
|
|
262
|
+
maxValue: 'Ha de ser com a màxim {max}',
|
|
263
|
+
patternMismatch: 'Format no vàlid'
|
|
264
|
+
};
|
|
116
265
|
/** Built-in dictionaries keyed by primary language subtag (`es-MX` → `es`). */
|
|
117
266
|
export const UI_DICTIONARIES = {
|
|
118
267
|
en,
|
|
119
268
|
es,
|
|
269
|
+
ca,
|
|
120
270
|
pt,
|
|
121
271
|
fr,
|
|
122
272
|
de
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@r2digisolutions/components",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "R2DigiSolutions Svelte 5 component library — Atomic Design, Tailwind 4, Light/Dark mode",
|
|
6
6
|
"license": "MIT",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@tailwindcss/vite": "^4.3.3",
|
|
82
82
|
"@types/node": "^26.2.0",
|
|
83
83
|
"@vitest/browser-playwright": "^4.1.11",
|
|
84
|
-
"bumpp": "
|
|
84
|
+
"bumpp": "~11.1.0",
|
|
85
85
|
"eslint": "^10.8.1",
|
|
86
86
|
"eslint-config-prettier": "^10.1.8",
|
|
87
87
|
"eslint-plugin-svelte": "^3.23.0",
|