includio-cms 0.5.3 → 0.5.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/CHANGELOG.md +71 -0
- package/ROADMAP.md +33 -0
- package/dist/admin/api/rest/handler.d.ts +7 -0
- package/dist/admin/api/rest/handler.js +116 -0
- package/dist/admin/api/rest/middleware/apiKey.d.ts +6 -0
- package/dist/admin/api/rest/middleware/apiKey.js +45 -0
- package/dist/admin/api/rest/routes/collections.d.ts +5 -0
- package/dist/admin/api/rest/routes/collections.js +104 -0
- package/dist/admin/api/rest/routes/entries.d.ts +2 -0
- package/dist/admin/api/rest/routes/entries.js +37 -0
- package/dist/admin/api/rest/routes/languages.d.ts +1 -0
- package/dist/admin/api/rest/routes/languages.js +5 -0
- package/dist/admin/api/rest/routes/schema.d.ts +2 -0
- package/dist/admin/api/rest/routes/schema.js +78 -0
- package/dist/admin/api/rest/routes/singletons.d.ts +3 -0
- package/dist/admin/api/rest/routes/singletons.js +60 -0
- package/dist/admin/auth-client.d.ts +48 -48
- package/dist/admin/client/admin/admin-after-login-layout.svelte +5 -3
- package/dist/admin/client/admin/admin-layout.svelte +0 -2
- package/dist/admin/client/collection/bulk-actions-bar.svelte +3 -1
- package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +1 -1
- package/dist/admin/client/collection/collection-entries.svelte +334 -166
- package/dist/admin/client/collection/collection-page.svelte +6 -3
- package/dist/admin/client/collection/data-table.svelte +127 -18
- package/dist/admin/client/collection/data-table.svelte.d.ts +2 -0
- package/dist/admin/client/collection/row-actions.svelte +3 -1
- package/dist/admin/client/collection/row-actions.svelte.d.ts +1 -1
- package/dist/admin/client/entry/entry-page.svelte +5 -4
- package/dist/admin/client/form/form-config-wrapper.svelte +5 -3
- package/dist/admin/client/form/form-page.svelte +5 -5
- package/dist/admin/client/form/form-submission/form-submission-page.svelte +30 -32
- package/dist/admin/components/accessibility/accessibility-overview.svelte +7 -5
- package/dist/admin/components/accessibility/accessibility-overview.svelte.d.ts +2 -17
- package/dist/admin/components/dashboard/a11y-gauge.svelte +7 -4
- package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +2 -17
- package/dist/admin/components/dashboard/accessibility-hub.svelte +7 -4
- package/dist/admin/components/dashboard/accessibility-hub.svelte.d.ts +2 -17
- package/dist/admin/components/dashboard/form-submissions-widget.svelte +8 -3
- package/dist/admin/components/dashboard/form-submissions-widget.svelte.d.ts +2 -17
- package/dist/admin/components/dashboard/recent-activity.svelte +8 -3
- package/dist/admin/components/dashboard/recent-activity.svelte.d.ts +2 -17
- package/dist/admin/components/dashboard/recent-entries.svelte +8 -3
- package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +2 -17
- package/dist/admin/components/fields/image-field.svelte +21 -20
- package/dist/admin/components/fields/media-field.svelte +45 -44
- package/dist/admin/components/fields/relation-field.svelte +428 -149
- package/dist/admin/components/fields/relation-picker-dialog.svelte +112 -0
- package/dist/admin/components/fields/relation-picker-dialog.svelte.d.ts +22 -0
- package/dist/admin/components/fields/seo-field.svelte +66 -10
- package/dist/admin/components/icons/icon-map.d.ts +4 -0
- package/dist/admin/components/icons/icon-map.js +143 -0
- package/dist/admin/components/layout/header-actions.svelte +0 -10
- package/dist/admin/components/layout/nav-collections.svelte +55 -57
- package/dist/admin/components/layout/nav-collections.svelte.d.ts +2 -17
- package/dist/admin/components/layout/nav-forms.svelte +49 -49
- package/dist/admin/components/layout/nav-forms.svelte.d.ts +2 -17
- package/dist/admin/components/layout/nav-search.svelte +15 -22
- package/dist/admin/components/layout/nav-singletons.svelte +35 -38
- package/dist/admin/components/layout/nav-singletons.svelte.d.ts +2 -17
- package/dist/admin/components/media/file/file-preview.svelte +92 -91
- package/dist/admin/components/media/file-preview.svelte +17 -18
- package/dist/admin/components/media/media-library.svelte +39 -41
- package/dist/admin/components/media/media-selector.svelte +8 -8
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +6 -8
- package/dist/admin/components/tiptap/content-editor.svelte +2 -2
- package/dist/admin/components/tiptap/editor-toolbar.svelte +1 -1
- package/dist/admin/components/tiptap/inline-block-node.d.ts +1 -0
- package/dist/admin/components/tiptap/inline-block-node.js +12 -1
- package/dist/admin/components/tiptap/tiptap-editor.svelte +1 -1
- package/dist/admin/remote/entry.remote.d.ts +20 -1
- package/dist/admin/remote/entry.remote.js +24 -3
- package/dist/admin/remote/invite.d.ts +2 -2
- package/dist/cli/scaffold/admin.js +8 -0
- package/dist/cms/runtime/remote.d.ts +8 -0
- package/dist/cms/runtime/remote.js +7 -0
- package/dist/cms/runtime/types.d.ts +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/components/ui/sonner/sonner.svelte +1 -2
- package/dist/core/cms.d.ts +2 -1
- package/dist/core/cms.js +2 -0
- package/dist/core/server/entries/operations/get.d.ts +11 -0
- package/dist/core/server/entries/operations/get.js +63 -1
- package/dist/core/server/entries/operations/update.d.ts +1 -0
- package/dist/core/server/entries/operations/update.js +5 -1
- package/dist/core/server/fields/resolveUrlFields.js +49 -18
- package/dist/db-postgres/schema/entry.d.ts +17 -0
- package/dist/db-postgres/schema/entry.js +4 -2
- package/dist/server/auth.d.ts +4 -4
- package/dist/sveltekit/components/structured-content.svelte +18 -5
- package/dist/sveltekit/server/handle.js +1 -0
- package/dist/types/cms.d.ts +7 -0
- package/dist/types/collections.d.ts +2 -0
- package/dist/types/config.d.ts +2 -2
- package/dist/types/entries.d.ts +7 -1
- package/dist/types/forms.d.ts +2 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -0
- package/dist/updates/0.5.4/index.d.ts +2 -0
- package/dist/updates/0.5.4/index.js +15 -0
- package/dist/updates/0.5.5/index.d.ts +2 -0
- package/dist/updates/0.5.5/index.js +20 -0
- package/dist/updates/0.5.6/index.d.ts +2 -0
- package/dist/updates/0.5.6/index.js +17 -0
- package/dist/updates/0.5.7/index.d.ts +2 -0
- package/dist/updates/0.5.7/index.js +14 -0
- package/dist/updates/index.js +5 -1
- package/package.json +6 -2
|
@@ -6,29 +6,31 @@
|
|
|
6
6
|
import { getRemotes } from '../../../sveltekit/index.js';
|
|
7
7
|
import { sidebarLang } from './lang.js';
|
|
8
8
|
import ClipboardIcon from '@tabler/icons-svelte/icons/clipboard-list';
|
|
9
|
+
import { getIcon } from '../icons/icon-map.js';
|
|
9
10
|
import { page } from '$app/state';
|
|
10
11
|
|
|
11
12
|
const interfaceLanguage = useInterfaceLanguage();
|
|
12
13
|
const remotes = getRemotes();
|
|
13
14
|
|
|
14
15
|
function isActive(url: string) {
|
|
15
|
-
|
|
16
|
+
const pathname = page.url.pathname;
|
|
17
|
+
return pathname === url || pathname.startsWith(url + '/');
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
20
|
+
const formsQuery = $derived(remotes.getForms());
|
|
21
|
+
const overviewQuery = $derived(remotes.getSubmissionsOverview());
|
|
22
|
+
|
|
23
|
+
const countsMap = $derived.by(() => {
|
|
24
|
+
const map = new Map<string, number>();
|
|
25
|
+
if (!overviewQuery.ready || !overviewQuery.current) return map;
|
|
26
|
+
for (const item of overviewQuery.current) {
|
|
27
|
+
if (item.total > 0) map.set(item.slug, item.total);
|
|
28
|
+
}
|
|
29
|
+
return map;
|
|
30
|
+
});
|
|
29
31
|
</script>
|
|
30
32
|
|
|
31
|
-
{#
|
|
33
|
+
{#if !formsQuery.ready}
|
|
32
34
|
<Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
|
|
33
35
|
<Sidebar.GroupLabel>{sidebarLang[interfaceLanguage.current].forms.title}</Sidebar.GroupLabel>
|
|
34
36
|
<Sidebar.Menu>
|
|
@@ -38,40 +40,38 @@
|
|
|
38
40
|
</div>
|
|
39
41
|
</Sidebar.Menu>
|
|
40
42
|
</Sidebar.Group>
|
|
41
|
-
{:
|
|
42
|
-
|
|
43
|
-
<Sidebar.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<Sidebar.
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
{:else if formsQuery.current && formsQuery.current.length > 0}
|
|
44
|
+
<Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
|
|
45
|
+
<Sidebar.GroupLabel class="group-data-[collapsible=icon]:hidden"
|
|
46
|
+
>{sidebarLang[interfaceLanguage.current].forms.title}</Sidebar.GroupLabel
|
|
47
|
+
>
|
|
48
|
+
<Sidebar.Menu>
|
|
49
|
+
{#each formsQuery.current as item (item.slug)}
|
|
50
|
+
{@const name = getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug}
|
|
51
|
+
{@const url = `/admin/forms/${item.slug}`}
|
|
52
|
+
{@const active = isActive(url)}
|
|
53
|
+
<Sidebar.MenuItem>
|
|
54
|
+
<Sidebar.MenuButton isActive={active} tooltipContent={name}>
|
|
55
|
+
{#snippet child({ props })}
|
|
56
|
+
{@const ResolvedIcon = getIcon(item.sidebarIcon) ?? ClipboardIcon}
|
|
57
|
+
<a {...props} href={url}>
|
|
58
|
+
<ResolvedIcon
|
|
59
|
+
class="size-5! shrink-0 {active ? 'text-primary' : 'text-muted-foreground'}"
|
|
60
|
+
/>
|
|
61
|
+
<span class="flex-1 {active ? 'text-primary' : 'text-muted-foreground'} truncate"
|
|
62
|
+
>{name}</span
|
|
63
|
+
>
|
|
64
|
+
{#if countsMap.get(item.slug)}
|
|
65
|
+
<span
|
|
66
|
+
class="bg-lavender-lighter text-primary inline-flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full px-[5px] text-[10px] font-bold group-data-[collapsible=icon]:hidden"
|
|
67
|
+
>{countsMap.get(item.slug)}</span
|
|
61
68
|
>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
</Sidebar.MenuButton>
|
|
72
|
-
</Sidebar.MenuItem>
|
|
73
|
-
{/each}
|
|
74
|
-
</Sidebar.Menu>
|
|
75
|
-
</Sidebar.Group>
|
|
76
|
-
{/if}
|
|
77
|
-
{/await}
|
|
69
|
+
{/if}
|
|
70
|
+
</a>
|
|
71
|
+
{/snippet}
|
|
72
|
+
</Sidebar.MenuButton>
|
|
73
|
+
</Sidebar.MenuItem>
|
|
74
|
+
{/each}
|
|
75
|
+
</Sidebar.Menu>
|
|
76
|
+
</Sidebar.Group>
|
|
77
|
+
{/if}
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$$bindings?: Bindings;
|
|
4
|
-
} & Exports;
|
|
5
|
-
(internal: unknown, props: {
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const NavForms: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type NavForms = InstanceType<typeof NavForms>;
|
|
1
|
+
declare const NavForms: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type NavForms = ReturnType<typeof NavForms>;
|
|
18
3
|
export default NavForms;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import ClipboardIcon from '@tabler/icons-svelte/icons/clipboard-list';
|
|
6
6
|
import SettingsIcon from '@tabler/icons-svelte/icons/settings';
|
|
7
7
|
import FolderIcon from '@tabler/icons-svelte/icons/folder';
|
|
8
|
+
import { getIcon } from '../icons/icon-map.js';
|
|
8
9
|
import * as Command from '../../../components/ui/command/index.js';
|
|
9
10
|
import { goto } from '$app/navigation';
|
|
10
11
|
import { getRemotes } from '../../../sveltekit/index.js';
|
|
@@ -29,20 +30,17 @@
|
|
|
29
30
|
goto(url);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
remotes.getCollections(),
|
|
36
|
-
remotes.getForms()
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
return { singles, collections, forms };
|
|
40
|
-
}
|
|
33
|
+
const singlesQuery = $derived(remotes.getSingles());
|
|
34
|
+
const collectionsQuery = $derived(remotes.getCollections());
|
|
35
|
+
const formsQuery = $derived(remotes.getForms());
|
|
41
36
|
</script>
|
|
42
37
|
|
|
43
38
|
<svelte:window {onkeydown} />
|
|
44
39
|
|
|
45
|
-
{#
|
|
40
|
+
{#if singlesQuery.ready && collectionsQuery.ready && formsQuery.ready}
|
|
41
|
+
{@const singles = singlesQuery.current ?? []}
|
|
42
|
+
{@const collections = collectionsQuery.current ?? []}
|
|
43
|
+
{@const forms = formsQuery.current ?? []}
|
|
46
44
|
<button
|
|
47
45
|
onclick={() => (open = true)}
|
|
48
46
|
class="bg-muted text-text-light hover:border-sidebar-border hover:bg-lavender-lighter hover:text-muted-foreground focus-visible:outline-primary mx-2 mt-2 mb-1 flex min-h-9 cursor-pointer items-center gap-2 rounded-[8px] border border-transparent px-2.5 py-[7px] font-sans text-[13px] transition-all group-data-[collapsible=icon]:mx-1 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-[7px] focus-visible:outline-2 focus-visible:outline-offset-2"
|
|
@@ -75,11 +73,8 @@
|
|
|
75
73
|
{#each singles as item (item.slug)}
|
|
76
74
|
{@const name = getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug}
|
|
77
75
|
<Command.Item onSelect={() => navigate(`/admin/entries/${item.slug}`)}>
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
{:else}
|
|
81
|
-
<SettingsIcon class="mr-2 size-4" />
|
|
82
|
-
{/if}
|
|
76
|
+
{@const ResolvedIcon = getIcon(item.sidebarIcon) ?? SettingsIcon}
|
|
77
|
+
<ResolvedIcon class="mr-2 size-4" />
|
|
83
78
|
{name}
|
|
84
79
|
</Command.Item>
|
|
85
80
|
{/each}
|
|
@@ -91,11 +86,8 @@
|
|
|
91
86
|
{@const name =
|
|
92
87
|
getLocalizedLabel(item.labels?.plural, interfaceLanguage.current) ?? item.slug}
|
|
93
88
|
<Command.Item onSelect={() => navigate(`/admin/collections/${item.slug}`)}>
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
{:else}
|
|
97
|
-
<FolderIcon class="mr-2 size-4" />
|
|
98
|
-
{/if}
|
|
89
|
+
{@const ResolvedIcon = getIcon(item.sidebarIcon) ?? FolderIcon}
|
|
90
|
+
<ResolvedIcon class="mr-2 size-4" />
|
|
99
91
|
{name}
|
|
100
92
|
</Command.Item>
|
|
101
93
|
{/each}
|
|
@@ -107,7 +99,8 @@
|
|
|
107
99
|
{#each forms as item (item.slug)}
|
|
108
100
|
{@const name = getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug}
|
|
109
101
|
<Command.Item onSelect={() => navigate(`/admin/forms/${item.slug}`)}>
|
|
110
|
-
|
|
102
|
+
{@const ResolvedIcon = getIcon(item.sidebarIcon) ?? ClipboardIcon}
|
|
103
|
+
<ResolvedIcon class="mr-2 size-4" />
|
|
111
104
|
{name}
|
|
112
105
|
</Command.Item>
|
|
113
106
|
{/each}
|
|
@@ -115,4 +108,4 @@
|
|
|
115
108
|
{/if}
|
|
116
109
|
</Command.List>
|
|
117
110
|
</Command.Dialog>
|
|
118
|
-
{/
|
|
111
|
+
{/if}
|
|
@@ -7,16 +7,20 @@
|
|
|
7
7
|
import Skeleton from '../../../components/ui/skeleton/skeleton.svelte';
|
|
8
8
|
import { page } from '$app/state';
|
|
9
9
|
import SettingsIcon from '@tabler/icons-svelte/icons/settings';
|
|
10
|
+
import { getIcon } from '../icons/icon-map.js';
|
|
10
11
|
|
|
11
12
|
const interfaceLanguage = useInterfaceLanguage();
|
|
12
13
|
const remotes = getRemotes();
|
|
13
14
|
|
|
14
15
|
function isActive(url: string) {
|
|
15
|
-
|
|
16
|
+
const pathname = page.url.pathname;
|
|
17
|
+
return pathname === url || pathname.startsWith(url + '/');
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
const query = $derived(remotes.getSingles());
|
|
17
21
|
</script>
|
|
18
22
|
|
|
19
|
-
{#
|
|
23
|
+
{#if !query.ready}
|
|
20
24
|
<Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
|
|
21
25
|
<Sidebar.GroupLabel
|
|
22
26
|
>{sidebarLang[interfaceLanguage.current].singletons.title}</Sidebar.GroupLabel
|
|
@@ -28,39 +32,32 @@
|
|
|
28
32
|
</div>
|
|
29
33
|
</Sidebar.Menu>
|
|
30
34
|
</Sidebar.Group>
|
|
31
|
-
{:
|
|
32
|
-
|
|
33
|
-
<Sidebar.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<Sidebar.
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</Sidebar.MenuButton>
|
|
61
|
-
</Sidebar.MenuItem>
|
|
62
|
-
{/each}
|
|
63
|
-
</Sidebar.Menu>
|
|
64
|
-
</Sidebar.Group>
|
|
65
|
-
{/if}
|
|
66
|
-
{/await}
|
|
35
|
+
{:else if query.current && query.current.length > 0}
|
|
36
|
+
<Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
|
|
37
|
+
<Sidebar.GroupLabel class="group-data-[collapsible=icon]:hidden"
|
|
38
|
+
>{sidebarLang[interfaceLanguage.current].singletons.title}</Sidebar.GroupLabel
|
|
39
|
+
>
|
|
40
|
+
<Sidebar.Menu>
|
|
41
|
+
{#each query.current as item (item)}
|
|
42
|
+
{@const name = getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug}
|
|
43
|
+
{@const url = `/admin/entries/${item.slug}`}
|
|
44
|
+
{@const active = isActive(url)}
|
|
45
|
+
<Sidebar.MenuItem>
|
|
46
|
+
<Sidebar.MenuButton isActive={active} tooltipContent={name}>
|
|
47
|
+
{#snippet child({ props })}
|
|
48
|
+
{@const ResolvedIcon = getIcon(item.sidebarIcon) ?? SettingsIcon}
|
|
49
|
+
<a {...props} href={url}>
|
|
50
|
+
<ResolvedIcon
|
|
51
|
+
class="size-5! shrink-0 {active ? 'text-primary' : 'text-muted-foreground'}"
|
|
52
|
+
/>
|
|
53
|
+
<span class="flex-1 truncate {active ? 'text-primary' : 'text-muted-foreground'}"
|
|
54
|
+
>{name}</span
|
|
55
|
+
>
|
|
56
|
+
</a>
|
|
57
|
+
{/snippet}
|
|
58
|
+
</Sidebar.MenuButton>
|
|
59
|
+
</Sidebar.MenuItem>
|
|
60
|
+
{/each}
|
|
61
|
+
</Sidebar.Menu>
|
|
62
|
+
</Sidebar.Group>
|
|
63
|
+
{/if}
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$$bindings?: Bindings;
|
|
4
|
-
} & Exports;
|
|
5
|
-
(internal: unknown, props: {
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const NavSingletons: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type NavSingletons = InstanceType<typeof NavSingletons>;
|
|
1
|
+
declare const NavSingletons: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type NavSingletons = ReturnType<typeof NavSingletons>;
|
|
18
3
|
export default NavSingletons;
|
|
@@ -38,111 +38,112 @@
|
|
|
38
38
|
}
|
|
39
39
|
saving = false;
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
const query = $derived(remotes.getFileById(fileId));
|
|
41
43
|
</script>
|
|
42
44
|
|
|
43
|
-
{#
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
{#if query.ready && query.current}
|
|
46
|
+
{@const file = query.current}
|
|
47
|
+
<div class="group flex items-center gap-3 rounded-xl border bg-card p-2 shadow-sm transition-all hover:-translate-y-0.5 hover:shadow-md">
|
|
48
|
+
<!-- Miniaturka z lightbox -->
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
class="aspect-square w-14 shrink-0 overflow-hidden rounded-lg cursor-pointer hover:ring-2 hover:ring-primary/50 transition-all"
|
|
52
|
+
onclick={() => file.type === 'image' && (lightboxOpen = true)}
|
|
53
|
+
>
|
|
54
|
+
<FileMiniature {file} />
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
<div class="min-w-0 flex-1">
|
|
58
|
+
<div class="flex items-center gap-1">
|
|
59
|
+
<p class="truncate text-sm font-medium flex-1">{file.name}</p>
|
|
60
|
+
<Button
|
|
61
|
+
variant="ghost"
|
|
62
|
+
size="icon"
|
|
63
|
+
class="h-6 w-6 shrink-0"
|
|
64
|
+
onclick={() => {
|
|
65
|
+
altValue = file.alt || '';
|
|
66
|
+
altDialogOpen = true;
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<Pencil class="h-3 w-3" />
|
|
70
|
+
</Button>
|
|
71
|
+
</div>
|
|
72
|
+
{#if file.size}
|
|
73
|
+
<p class="text-xs text-muted-foreground">{(file.size / 1024).toFixed(1)} KB</p>
|
|
74
|
+
{/if}
|
|
75
|
+
{#if file.alt}
|
|
76
|
+
<p class="text-xs text-muted-foreground truncate" title={file.alt}>{file.alt}</p>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
variant="ghost"
|
|
60
|
-
size="icon"
|
|
61
|
-
class="h-6 w-6 shrink-0"
|
|
62
|
-
onclick={() => {
|
|
63
|
-
altValue = file.alt || '';
|
|
64
|
-
altDialogOpen = true;
|
|
65
|
-
}}
|
|
66
|
-
>
|
|
67
|
-
<Pencil class="h-3 w-3" />
|
|
68
|
-
</Button>
|
|
80
|
+
{#if index !== undefined && onDelete}
|
|
81
|
+
<div class="flex shrink-0 items-center gap-1">
|
|
82
|
+
<div use:draggable={{ container: index.toString(), dragData: file }} class="cursor-grab p-1 text-muted-foreground hover:text-foreground">
|
|
83
|
+
<GripVertical class="h-4 w-4" />
|
|
69
84
|
</div>
|
|
70
|
-
{
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
{#if file.alt}
|
|
74
|
-
<p class="text-xs text-muted-foreground truncate" title={file.alt}>{file.alt}</p>
|
|
75
|
-
{/if}
|
|
85
|
+
<Button variant="ghost" size="icon" class="h-7 w-7 text-muted-foreground hover:text-destructive" onclick={() => onDelete(index)}>
|
|
86
|
+
<X class="h-4 w-4" />
|
|
87
|
+
</Button>
|
|
76
88
|
</div>
|
|
89
|
+
{/if}
|
|
90
|
+
</div>
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
<!-- Lightbox dialog -->
|
|
93
|
+
{#if file.type === 'image'}
|
|
94
|
+
<Dialog.Root bind:open={lightboxOpen}>
|
|
95
|
+
<Dialog.Content class="max-w-[90vw] max-h-[90vh] p-2 bg-black/95 border-none">
|
|
96
|
+
<div class="checkered-lightbox flex items-center justify-center">
|
|
97
|
+
<img
|
|
98
|
+
src={file.url}
|
|
99
|
+
alt={file.alt || file.name}
|
|
100
|
+
class="max-w-full max-h-[85vh] object-contain"
|
|
101
|
+
/>
|
|
86
102
|
</div>
|
|
87
|
-
|
|
88
|
-
</
|
|
103
|
+
</Dialog.Content>
|
|
104
|
+
</Dialog.Root>
|
|
105
|
+
{/if}
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
<!-- Alt text edit dialog -->
|
|
108
|
+
<Dialog.Root bind:open={altDialogOpen}>
|
|
109
|
+
<Dialog.Content class="max-w-lg">
|
|
110
|
+
<Dialog.Header>
|
|
111
|
+
<Dialog.Title>Edytuj tekst alternatywny</Dialog.Title>
|
|
112
|
+
</Dialog.Header>
|
|
113
|
+
<div class="space-y-4 py-4">
|
|
114
|
+
{#if file.type === 'image'}
|
|
115
|
+
<div class="checkered-lightbox rounded-lg overflow-hidden">
|
|
95
116
|
<img
|
|
96
117
|
src={file.url}
|
|
97
118
|
alt={file.alt || file.name}
|
|
98
|
-
class="max-w-full
|
|
99
|
-
/>
|
|
100
|
-
</div>
|
|
101
|
-
</Dialog.Content>
|
|
102
|
-
</Dialog.Root>
|
|
103
|
-
{/if}
|
|
104
|
-
|
|
105
|
-
<!-- Alt text edit dialog -->
|
|
106
|
-
<Dialog.Root bind:open={altDialogOpen}>
|
|
107
|
-
<Dialog.Content class="max-w-lg">
|
|
108
|
-
<Dialog.Header>
|
|
109
|
-
<Dialog.Title>Edytuj tekst alternatywny</Dialog.Title>
|
|
110
|
-
</Dialog.Header>
|
|
111
|
-
<div class="space-y-4 py-4">
|
|
112
|
-
{#if file.type === 'image'}
|
|
113
|
-
<div class="checkered-lightbox rounded-lg overflow-hidden">
|
|
114
|
-
<img
|
|
115
|
-
src={file.url}
|
|
116
|
-
alt={file.alt || file.name}
|
|
117
|
-
class="max-h-48 w-full object-contain"
|
|
118
|
-
/>
|
|
119
|
-
</div>
|
|
120
|
-
{/if}
|
|
121
|
-
<div class="space-y-2">
|
|
122
|
-
<Label for="alt-text">Tekst alternatywny</Label>
|
|
123
|
-
<Textarea
|
|
124
|
-
id="alt-text"
|
|
125
|
-
bind:value={altValue}
|
|
126
|
-
placeholder="Opisz zawartość obrazu..."
|
|
127
|
-
class="min-h-[120px] resize-none"
|
|
119
|
+
class="max-h-48 w-full object-contain"
|
|
128
120
|
/>
|
|
129
|
-
<p class="text-xs text-muted-foreground">
|
|
130
|
-
Tekst alternatywny opisuje obraz dla osób niewidomych i wyszukiwarek.
|
|
131
|
-
</p>
|
|
132
121
|
</div>
|
|
122
|
+
{/if}
|
|
123
|
+
<div class="space-y-2">
|
|
124
|
+
<Label for="alt-text">Tekst alternatywny</Label>
|
|
125
|
+
<Textarea
|
|
126
|
+
id="alt-text"
|
|
127
|
+
bind:value={altValue}
|
|
128
|
+
placeholder="Opisz zawartość obrazu..."
|
|
129
|
+
class="min-h-[120px] resize-none"
|
|
130
|
+
/>
|
|
131
|
+
<p class="text-xs text-muted-foreground">
|
|
132
|
+
Tekst alternatywny opisuje obraz dla osób niewidomych i wyszukiwarek.
|
|
133
|
+
</p>
|
|
133
134
|
</div>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</
|
|
140
|
-
</Dialog.
|
|
141
|
-
</Dialog.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{/
|
|
135
|
+
</div>
|
|
136
|
+
<Dialog.Footer>
|
|
137
|
+
<Button variant="outline" onclick={() => altDialogOpen = false}>Anuluj</Button>
|
|
138
|
+
<Button onclick={() => saveAlt(file)} disabled={saving}>
|
|
139
|
+
{saving ? 'Zapisuję...' : 'Zapisz'}
|
|
140
|
+
</Button>
|
|
141
|
+
</Dialog.Footer>
|
|
142
|
+
</Dialog.Content>
|
|
143
|
+
</Dialog.Root>
|
|
144
|
+
{:else if query.ready}
|
|
145
|
+
<p class="text-sm text-muted-foreground">Plik nie istnieje.</p>
|
|
146
|
+
{/if}
|
|
146
147
|
|
|
147
148
|
<style>
|
|
148
149
|
.checkered-lightbox {
|
|
@@ -19,25 +19,24 @@
|
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<div class="border">
|
|
22
|
-
{#
|
|
22
|
+
{#if !fileQuery.ready}
|
|
23
23
|
Loading...
|
|
24
|
-
{:
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{/if}
|
|
24
|
+
{:else if fileQuery.current}
|
|
25
|
+
{@const file = fileQuery.current}
|
|
26
|
+
{#if file.type === 'image'}
|
|
27
|
+
<Image data={file} class={className} />
|
|
28
|
+
{:else if file.type === 'video'}
|
|
29
|
+
{#if file.thumbnailUrl}
|
|
30
|
+
<Image data={{ ...file, url: file.thumbnailUrl }} class={className} />
|
|
31
|
+
{:else}
|
|
32
|
+
<div class={cn('border-primary flex items-center justify-center border p-4', className)}>
|
|
33
|
+
<VideoOff />
|
|
34
|
+
</div>
|
|
36
35
|
{/if}
|
|
37
|
-
{:else}
|
|
38
|
-
<div class={cn('border-primary flex items-center justify-center border p-4', className)}>
|
|
39
|
-
<PhotoOff />
|
|
40
|
-
</div>
|
|
41
36
|
{/if}
|
|
42
|
-
{
|
|
37
|
+
{:else}
|
|
38
|
+
<div class={cn('border-primary flex items-center justify-center border p-4', className)}>
|
|
39
|
+
<PhotoOff />
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
43
42
|
</div>
|