@velastack/cms 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -18
- package/dist/backend/index.d.ts +1 -1
- package/dist/backend/routes/seed.d.ts +6 -0
- package/dist/backend/routes/seed.js +96 -0
- package/dist/backend/routes/table.js +3 -0
- package/dist/backend/store/queries.d.ts +6 -1
- package/dist/backend/store/queries.js +89 -0
- package/dist/cms.js +13 -16
- package/dist/components/admin-bar/admin-bar-internal.svelte +4 -4
- package/dist/components/admin-bar/admin-bar.css +1 -1
- package/dist/components/admin-bar/field-input.svelte +220 -0
- package/dist/components/admin-bar/field-input.svelte.d.ts +29 -0
- package/dist/components/admin-bar/icons/pencil.svelte +19 -0
- package/dist/components/admin-bar/icons/pencil.svelte.d.ts +3 -0
- package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte +1 -1
- package/dist/components/admin-bar/locales-panel.svelte +42 -1
- package/dist/components/admin-bar/page-config.d.ts +8 -1
- package/dist/components/admin-bar/page-config.js +15 -2
- package/dist/components/admin-bar/reorder-buttons.svelte +52 -0
- package/dist/components/admin-bar/reorder-buttons.svelte.d.ts +10 -0
- package/dist/components/admin-bar/seo-panel.svelte +144 -189
- package/dist/components/admin-bar/site-options-panel.svelte +115 -0
- package/dist/components/admin-bar/site-options-panel.svelte.d.ts +7 -0
- package/dist/components/admin-bar/structured-editor-popover.svelte +250 -0
- package/dist/components/admin-bar/structured-editor-popover.svelte.d.ts +48 -0
- package/dist/components/admin-bar/ui/checkbox/checkbox.svelte +36 -0
- package/dist/components/admin-bar/ui/checkbox/checkbox.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/checkbox/index.d.ts +2 -0
- package/dist/components/admin-bar/ui/checkbox/index.js +2 -0
- package/dist/components/admin-bar/ui/popover/index.d.ts +5 -0
- package/dist/components/admin-bar/ui/popover/index.js +7 -0
- package/dist/components/admin-bar/ui/popover/popover-close.svelte +7 -0
- package/dist/components/admin-bar/ui/popover/popover-close.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/popover/popover-content.svelte +35 -0
- package/dist/components/admin-bar/ui/popover/popover-content.svelte.d.ts +10 -0
- package/dist/components/admin-bar/ui/popover/popover-trigger.svelte +7 -0
- package/dist/components/admin-bar/ui/popover/popover-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/popover/popover.svelte +7 -0
- package/dist/components/admin-bar/ui/popover/popover.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/sheet/index.d.ts +10 -0
- package/dist/components/admin-bar/ui/sheet/index.js +12 -0
- package/dist/components/admin-bar/ui/sheet/sheet-close.svelte +7 -0
- package/dist/components/admin-bar/ui/sheet/sheet-close.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/sheet/sheet-content.svelte +64 -0
- package/dist/components/admin-bar/ui/sheet/sheet-content.svelte.d.ts +17 -0
- package/dist/components/admin-bar/ui/sheet/sheet-description.svelte +17 -0
- package/dist/components/admin-bar/ui/sheet/sheet-description.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte +23 -0
- package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/sheet/sheet-header.svelte +20 -0
- package/dist/components/admin-bar/ui/sheet/sheet-header.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte +20 -0
- package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/sheet/sheet-title.svelte +17 -0
- package/dist/components/admin-bar/ui/sheet/sheet-title.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte +7 -0
- package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/sheet/sheet.svelte +7 -0
- package/dist/components/admin-bar/ui/sheet/sheet.svelte.d.ts +3 -0
- package/dist/components/admin-bar/ui/switch/index.d.ts +2 -0
- package/dist/components/admin-bar/ui/switch/index.js +2 -0
- package/dist/components/admin-bar/ui/switch/switch.svelte +27 -0
- package/dist/components/admin-bar/ui/switch/switch.svelte.d.ts +5 -0
- package/dist/components/admin-bar/ui/tabs/index.d.ts +5 -0
- package/dist/components/admin-bar/ui/tabs/index.js +7 -0
- package/dist/components/admin-bar/ui/tabs/tabs-content.svelte +17 -0
- package/dist/components/admin-bar/ui/tabs/tabs-content.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/tabs/tabs-list.svelte +16 -0
- package/dist/components/admin-bar/ui/tabs/tabs-list.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte +20 -0
- package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
- package/dist/components/admin-bar/ui/tabs/tabs.svelte +19 -0
- package/dist/components/admin-bar/ui/tabs/tabs.svelte.d.ts +4 -0
- package/dist/components/cms/cms-boolean.svelte +9 -18
- package/dist/components/cms/cms-boolean.svelte.d.ts +1 -0
- package/dist/components/cms/cms-date-time-editable.svelte +3 -3
- package/dist/components/cms/cms-date-time.svelte +17 -17
- package/dist/components/cms/cms-date-time.svelte.d.ts +2 -1
- package/dist/components/cms/cms-image-editable.svelte +13 -4
- package/dist/components/cms/cms-image.svelte +37 -30
- package/dist/components/cms/cms-image.svelte.d.ts +11 -4
- package/dist/components/cms/cms-link.svelte +26 -19
- package/dist/components/cms/cms-link.svelte.d.ts +2 -0
- package/dist/components/cms/cms-markdown.svelte +15 -16
- package/dist/components/cms/cms-markdown.svelte.d.ts +1 -0
- package/dist/components/cms/cms-number-editable.svelte +4 -2
- package/dist/components/cms/cms-number.svelte +18 -17
- package/dist/components/cms/cms-number.svelte.d.ts +2 -1
- package/dist/components/cms/cms-repeater.svelte +15 -18
- package/dist/components/cms/cms-repeater.svelte.d.ts +1 -0
- package/dist/components/cms/cms-rich-text.svelte +17 -22
- package/dist/components/cms/cms-rich-text.svelte.d.ts +1 -0
- package/dist/components/cms/cms-store.svelte.d.ts +14 -0
- package/dist/components/cms/cms-store.svelte.js +39 -2
- package/dist/components/cms/cms-text.svelte +23 -30
- package/dist/components/cms/cms-text.svelte.d.ts +3 -1
- package/dist/components/cms/scope.d.ts +25 -5
- package/dist/components/cms/structured-registry.d.ts +10 -0
- package/dist/components/cms/structured-registry.js +6 -0
- package/dist/components/cms/use-cms-field.svelte.d.ts +54 -0
- package/dist/components/cms/use-cms-field.svelte.js +83 -0
- package/dist/core/metadata.d.ts +44 -0
- package/dist/core/metadata.js +59 -0
- package/dist/core/structured.d.ts +108 -0
- package/dist/core/structured.js +184 -0
- package/dist/core/wire.d.ts +31 -1
- package/dist/editor.d.ts +22 -0
- package/dist/editor.js +21 -0
- package/dist/index.d.ts +12 -4
- package/dist/index.js +13 -3
- package/dist/server/load-cms.js +1 -0
- package/package.json +12 -3
- package/plugin/README.md +22 -2
- package/plugin/dist/build.d.ts +74 -0
- package/plugin/dist/build.js +210 -0
- package/plugin/dist/build.js.map +1 -0
- package/plugin/dist/index.d.ts +10 -1
- package/plugin/dist/index.js +63 -4
- package/plugin/dist/index.js.map +1 -1
- package/plugin/dist/manifest.d.ts +31 -0
- package/plugin/dist/manifest.js +146 -41
- package/plugin/dist/manifest.js.map +1 -1
- package/plugin/dist/parse-svelte.d.ts +15 -0
- package/plugin/dist/parse-svelte.js +26 -16
- package/plugin/dist/parse-svelte.js.map +1 -1
- package/dist/components/admin-bar/site-settings-panel.svelte +0 -225
- package/dist/components/admin-bar/site-settings-panel.svelte.d.ts +0 -7
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends Record<string, unknown>">
|
|
2
|
+
/**
|
|
3
|
+
* The editor every structured component opens: a popover anchored to the
|
|
4
|
+
* slot (a bottom sheet under 600px, per DESIGN.md §8.2) holding a form
|
|
5
|
+
* over a local draft, committed to the CMS draft on Done.
|
|
6
|
+
*
|
|
7
|
+
* Locales are tabs. The default locale's tab is the component's own form
|
|
8
|
+
* (`editor` snippet) and owns the structure; every other locale's tab
|
|
9
|
+
* lists the translatable strings with the default text as reference and
|
|
10
|
+
* writes only `<name>.$t.<id>.<field>` (see `core/structured.ts`). The
|
|
11
|
+
* popover opens on the locale the editor is previewing.
|
|
12
|
+
*/
|
|
13
|
+
import { onMount, type Snippet } from 'svelte';
|
|
14
|
+
import { cmsStore } from '../cms/cms-store.svelte.js';
|
|
15
|
+
import type { CmsField } from '../cms/use-cms-field.svelte.js';
|
|
16
|
+
import {
|
|
17
|
+
stripTranslations,
|
|
18
|
+
translatableFields,
|
|
19
|
+
translationPath,
|
|
20
|
+
type Structured,
|
|
21
|
+
type TranslatableField
|
|
22
|
+
} from '../../core/structured.js';
|
|
23
|
+
import CssRoot from './css-root.svelte';
|
|
24
|
+
import PencilIcon from './icons/pencil.svelte';
|
|
25
|
+
import { Button } from './ui/button/index.js';
|
|
26
|
+
import { Input } from './ui/input/index.js';
|
|
27
|
+
import * as Popover from './ui/popover/index.js';
|
|
28
|
+
import * as Sheet from './ui/sheet/index.js';
|
|
29
|
+
import * as Tabs from './ui/tabs/index.js';
|
|
30
|
+
|
|
31
|
+
type Props = {
|
|
32
|
+
field: CmsField<T>;
|
|
33
|
+
schema: Structured<T>;
|
|
34
|
+
/** Human label for the header, e.g. "Opening hours". */
|
|
35
|
+
label: string;
|
|
36
|
+
open?: boolean;
|
|
37
|
+
/** What the visitor sees; rendered in place with the edit affordance. */
|
|
38
|
+
preview: Snippet;
|
|
39
|
+
/** The default-locale form over a local draft. */
|
|
40
|
+
editor: Snippet<[T, (next: T) => void]>;
|
|
41
|
+
};
|
|
42
|
+
let { field, schema, label, open = $bindable(false), preview, editor }: Props = $props();
|
|
43
|
+
|
|
44
|
+
const locales = $derived(cmsStore.locales);
|
|
45
|
+
const defaultLocale = $derived(cmsStore.defaultLocale);
|
|
46
|
+
const otherLocales = $derived(locales.filter((l) => l !== defaultLocale));
|
|
47
|
+
|
|
48
|
+
// Local drafts: structure for the default locale, strings per other locale.
|
|
49
|
+
let draft = $state<T>({} as T);
|
|
50
|
+
let translations = $state<Record<string, Record<string, string>>>({});
|
|
51
|
+
let tab = $state('');
|
|
52
|
+
|
|
53
|
+
const reset = () => {
|
|
54
|
+
draft = structuredClone($state.snapshot(field.current)) as T;
|
|
55
|
+
translations = {};
|
|
56
|
+
tab = locales.includes(cmsStore.activeLocale) ? cmsStore.activeLocale : defaultLocale;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
$effect(() => {
|
|
60
|
+
if (open) reset();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
$effect(() => {
|
|
64
|
+
if (open && tab && tab !== defaultLocale) void cmsStore.loadLocaleOverlay(tab);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const update = (next: T) => {
|
|
68
|
+
draft = next;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const fields = $derived<TranslatableField[]>(translatableFields(draft, schema));
|
|
72
|
+
|
|
73
|
+
/** Stored translation for a field in `locale`, unless edited in this session. */
|
|
74
|
+
const translationValue = (locale: string, f: TranslatableField): string => {
|
|
75
|
+
const local = translations[locale]?.[`${f.id}.${f.field}`];
|
|
76
|
+
if (local !== undefined) return local;
|
|
77
|
+
const stored = field.getIn(locale, `$t.${f.id}.${f.field}`);
|
|
78
|
+
return typeof stored === 'string' ? stored : '';
|
|
79
|
+
};
|
|
80
|
+
const setTranslation = (locale: string, f: TranslatableField, value: string) => {
|
|
81
|
+
translations = {
|
|
82
|
+
...translations,
|
|
83
|
+
[locale]: { ...(translations[locale] ?? {}), [`${f.id}.${f.field}`]: value }
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const done = () => {
|
|
88
|
+
field.set(stripTranslations($state.snapshot(draft)), defaultLocale);
|
|
89
|
+
for (const [locale, strings] of Object.entries(translations)) {
|
|
90
|
+
for (const [key, value] of Object.entries(strings)) {
|
|
91
|
+
const dot = key.indexOf('.');
|
|
92
|
+
const id = key.slice(0, dot);
|
|
93
|
+
const f = key.slice(dot + 1);
|
|
94
|
+
field.setAt(translationPath('', id, f).slice(1), value === '' ? null : value, locale);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
open = false;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const clear = () => {
|
|
101
|
+
field.set(null, defaultLocale);
|
|
102
|
+
open = false;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Under 600px the popover becomes a bottom sheet (DESIGN.md §8.2).
|
|
106
|
+
let isMobile = $state(false);
|
|
107
|
+
onMount(() => {
|
|
108
|
+
const mq = window.matchMedia('(max-width: 600px)');
|
|
109
|
+
const sync = () => (isMobile = mq.matches);
|
|
110
|
+
sync();
|
|
111
|
+
mq.addEventListener('change', sync);
|
|
112
|
+
return () => mq.removeEventListener('change', sync);
|
|
113
|
+
});
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
{#snippet body()}
|
|
117
|
+
<div class="vela:flex vela:items-center vela:justify-between vela:px-4 vela:pt-3.5 vela:pb-2">
|
|
118
|
+
<div class="vela:flex vela:items-baseline vela:gap-2 vela:min-w-0">
|
|
119
|
+
<h2 class="vela:text-[14px] vela:font-semibold vela:m-0">{label}</h2>
|
|
120
|
+
<span class="vela:font-mono vela:text-[11px] vela:text-bar-text-tertiary vela:truncate">
|
|
121
|
+
{field.name}
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{#if otherLocales.length > 0}
|
|
127
|
+
<Tabs.Root bind:value={tab} class="vela:px-4">
|
|
128
|
+
<Tabs.List aria-label="Language">
|
|
129
|
+
{#each locales as locale (locale)}
|
|
130
|
+
<Tabs.Trigger value={locale} class="vela:font-mono vela:uppercase">{locale}</Tabs.Trigger>
|
|
131
|
+
{/each}
|
|
132
|
+
</Tabs.List>
|
|
133
|
+
</Tabs.Root>
|
|
134
|
+
{/if}
|
|
135
|
+
|
|
136
|
+
<div
|
|
137
|
+
class="vela:overflow-y-auto vela:max-h-[60vh] vela:px-4 vela:py-3 vela:flex vela:flex-col vela:gap-3"
|
|
138
|
+
>
|
|
139
|
+
{#if tab === defaultLocale || otherLocales.length === 0}
|
|
140
|
+
{@render editor(draft, update)}
|
|
141
|
+
{:else}
|
|
142
|
+
{#if fields.length === 0}
|
|
143
|
+
<p class="vela:text-[12px] vela:text-bar-text-tertiary vela:m-0">
|
|
144
|
+
Nothing to translate yet. Add content in
|
|
145
|
+
<span class="vela:font-mono vela:uppercase">{defaultLocale}</span> first.
|
|
146
|
+
</p>
|
|
147
|
+
{:else}
|
|
148
|
+
<p class="vela:text-[11px] vela:text-bar-text-tertiary vela:m-0">
|
|
149
|
+
Structure is shared with
|
|
150
|
+
<span class="vela:font-mono vela:uppercase">{defaultLocale}</span>; only the text
|
|
151
|
+
translates. Blank fields show the original.
|
|
152
|
+
</p>
|
|
153
|
+
{#each fields as f (`${f.id}.${f.field}`)}
|
|
154
|
+
<label class="vela:flex vela:flex-col vela:gap-1">
|
|
155
|
+
<span class="vela:text-[12px] vela:text-bar-text-secondary vela:truncate">
|
|
156
|
+
{f.source}
|
|
157
|
+
</span>
|
|
158
|
+
<Input
|
|
159
|
+
type="text"
|
|
160
|
+
value={translationValue(tab, f)}
|
|
161
|
+
placeholder={f.source}
|
|
162
|
+
oninput={(e) => setTranslation(tab, f, e.currentTarget.value)}
|
|
163
|
+
/>
|
|
164
|
+
</label>
|
|
165
|
+
{/each}
|
|
166
|
+
{/if}
|
|
167
|
+
{/if}
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div
|
|
171
|
+
class="vela:flex vela:items-center vela:justify-between vela:px-4 vela:py-2.5 vela:border-t vela:border-[var(--cms-bar-divider)] vela:text-[12px] vela:text-bar-text-tertiary"
|
|
172
|
+
>
|
|
173
|
+
<Button variant="ghost" size="pill" onclick={clear}>Clear</Button>
|
|
174
|
+
<div class="vela:flex vela:items-center vela:gap-1.5">
|
|
175
|
+
<Button variant="ghost" size="pill" onclick={() => (open = false)}>Cancel</Button>
|
|
176
|
+
<Button size="pill" onclick={done}>Done</Button>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
{/snippet}
|
|
180
|
+
|
|
181
|
+
<CssRoot>
|
|
182
|
+
<span class="cms-structured-edit" data-open={open ? '' : undefined} data-cms-name={field.name}>
|
|
183
|
+
<span class="cms-structured-edit__preview">{@render preview()}</span>
|
|
184
|
+
{#if isMobile}
|
|
185
|
+
<Sheet.Root bind:open>
|
|
186
|
+
<Sheet.Trigger class="cms-structured-edit__trigger" aria-label={`Edit ${label}`}>
|
|
187
|
+
<PencilIcon class="vela:size-3" />
|
|
188
|
+
{label}
|
|
189
|
+
</Sheet.Trigger>
|
|
190
|
+
<Sheet.Content side="bottom" class="vela:gap-0" showCloseButton={false}>
|
|
191
|
+
{@render body()}
|
|
192
|
+
</Sheet.Content>
|
|
193
|
+
</Sheet.Root>
|
|
194
|
+
{:else}
|
|
195
|
+
<Popover.Root bind:open>
|
|
196
|
+
<Popover.Trigger class="cms-structured-edit__trigger" aria-label={`Edit ${label}`}>
|
|
197
|
+
<PencilIcon class="vela:size-3" />
|
|
198
|
+
{label}
|
|
199
|
+
</Popover.Trigger>
|
|
200
|
+
<Popover.Content class="vela:w-[420px] vela:max-w-[calc(100vw-2rem)]">
|
|
201
|
+
{@render body()}
|
|
202
|
+
</Popover.Content>
|
|
203
|
+
</Popover.Root>
|
|
204
|
+
{/if}
|
|
205
|
+
</span>
|
|
206
|
+
</CssRoot>
|
|
207
|
+
|
|
208
|
+
<style>
|
|
209
|
+
.cms-structured-edit {
|
|
210
|
+
position: relative;
|
|
211
|
+
display: block;
|
|
212
|
+
border-radius: 0.125rem;
|
|
213
|
+
outline: 1px dashed rgba(127, 127, 127, 0.6);
|
|
214
|
+
outline-offset: 4px;
|
|
215
|
+
}
|
|
216
|
+
.cms-structured-edit:hover,
|
|
217
|
+
.cms-structured-edit:focus-within,
|
|
218
|
+
.cms-structured-edit[data-open] {
|
|
219
|
+
outline: 2px solid var(--cms-accent, #534ab7);
|
|
220
|
+
outline-offset: 4px;
|
|
221
|
+
}
|
|
222
|
+
.cms-structured-edit__preview {
|
|
223
|
+
display: contents;
|
|
224
|
+
}
|
|
225
|
+
:global(.cms-structured-edit__trigger) {
|
|
226
|
+
position: absolute;
|
|
227
|
+
top: -0.5rem;
|
|
228
|
+
right: -0.5rem;
|
|
229
|
+
z-index: 40;
|
|
230
|
+
display: none;
|
|
231
|
+
align-items: center;
|
|
232
|
+
gap: 0.25rem;
|
|
233
|
+
padding: 0.125rem 0.5rem;
|
|
234
|
+
background: var(--cms-bar-bg, #1f1f1f);
|
|
235
|
+
color: var(--cms-bar-text, #f5f5f5);
|
|
236
|
+
border: 1px solid var(--cms-bar-divider, #3a3a3a);
|
|
237
|
+
border-radius: 999px;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
font: inherit;
|
|
240
|
+
font-size: 0.6875rem;
|
|
241
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
242
|
+
line-height: 1.4;
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
}
|
|
245
|
+
.cms-structured-edit:hover :global(.cms-structured-edit__trigger),
|
|
246
|
+
.cms-structured-edit:focus-within :global(.cms-structured-edit__trigger),
|
|
247
|
+
.cms-structured-edit[data-open] :global(.cms-structured-edit__trigger) {
|
|
248
|
+
display: inline-flex;
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor every structured component opens: a popover anchored to the
|
|
3
|
+
* slot (a bottom sheet under 600px, per DESIGN.md §8.2) holding a form
|
|
4
|
+
* over a local draft, committed to the CMS draft on Done.
|
|
5
|
+
*
|
|
6
|
+
* Locales are tabs. The default locale's tab is the component's own form
|
|
7
|
+
* (`editor` snippet) and owns the structure; every other locale's tab
|
|
8
|
+
* lists the translatable strings with the default text as reference and
|
|
9
|
+
* writes only `<name>.$t.<id>.<field>` (see `core/structured.ts`). The
|
|
10
|
+
* popover opens on the locale the editor is previewing.
|
|
11
|
+
*/
|
|
12
|
+
import { type Snippet } from 'svelte';
|
|
13
|
+
import type { CmsField } from '../cms/use-cms-field.svelte.js';
|
|
14
|
+
import { type Structured } from '../../core/structured.js';
|
|
15
|
+
declare function $$render<T extends Record<string, unknown>>(): {
|
|
16
|
+
props: {
|
|
17
|
+
field: CmsField<T>;
|
|
18
|
+
schema: Structured<T>;
|
|
19
|
+
/** Human label for the header, e.g. "Opening hours". */
|
|
20
|
+
label: string;
|
|
21
|
+
open?: boolean;
|
|
22
|
+
/** What the visitor sees; rendered in place with the edit affordance. */
|
|
23
|
+
preview: Snippet;
|
|
24
|
+
/** The default-locale form over a local draft. */
|
|
25
|
+
editor: Snippet<[T, (next: T) => void]>;
|
|
26
|
+
};
|
|
27
|
+
exports: {};
|
|
28
|
+
bindings: "open";
|
|
29
|
+
slots: {};
|
|
30
|
+
events: {};
|
|
31
|
+
};
|
|
32
|
+
declare class __sveltets_Render<T extends Record<string, unknown>> {
|
|
33
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
34
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
35
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
36
|
+
bindings(): "open";
|
|
37
|
+
exports(): {};
|
|
38
|
+
}
|
|
39
|
+
interface $$IsomorphicComponent {
|
|
40
|
+
new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
41
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
42
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
43
|
+
<T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
44
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
45
|
+
}
|
|
46
|
+
declare const StructuredEditorPopover: $$IsomorphicComponent;
|
|
47
|
+
type StructuredEditorPopover<T extends Record<string, unknown>> = InstanceType<typeof StructuredEditorPopover<T>>;
|
|
48
|
+
export default StructuredEditorPopover;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
|
3
|
+
import CheckIcon from '../../icons/check.svelte';
|
|
4
|
+
import MinusIcon from '../../icons/minus.svelte';
|
|
5
|
+
import { cn, type WithoutChildrenOrChild } from '../../utils.js';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
checked = $bindable(false),
|
|
10
|
+
indeterminate = $bindable(false),
|
|
11
|
+
class: className,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<CheckboxPrimitive.Root
|
|
17
|
+
bind:ref
|
|
18
|
+
bind:checked
|
|
19
|
+
bind:indeterminate
|
|
20
|
+
data-slot="checkbox"
|
|
21
|
+
class={cn(
|
|
22
|
+
'vela:peer vela:size-4 vela:shrink-0 vela:rounded-[4px] vela:border vela:border-[var(--cms-bar-divider)] vela:bg-transparent vela:cursor-pointer vela:transition-colors vela:focus-visible:outline-none vela:focus-visible:ring-2 vela:focus-visible:ring-[var(--cms-accent)] vela:disabled:cursor-not-allowed vela:disabled:opacity-50 vela:data-[state=checked]:bg-[var(--cms-accent)] vela:data-[state=checked]:border-[var(--cms-accent)] vela:data-[state=checked]:text-white vela:data-[state=indeterminate]:bg-[var(--cms-accent)] vela:data-[state=indeterminate]:text-white',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...restProps}
|
|
26
|
+
>
|
|
27
|
+
{#snippet children({ checked, indeterminate })}
|
|
28
|
+
<span class="vela:flex vela:size-full vela:items-center vela:justify-center vela:text-current">
|
|
29
|
+
{#if indeterminate}
|
|
30
|
+
<MinusIcon class="vela:size-3" />
|
|
31
|
+
{:else if checked}
|
|
32
|
+
<CheckIcon class="vela:size-3" />
|
|
33
|
+
{/if}
|
|
34
|
+
</span>
|
|
35
|
+
{/snippet}
|
|
36
|
+
</CheckboxPrimitive.Root>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
|
2
|
+
import { type WithoutChildrenOrChild } from '../../utils.js';
|
|
3
|
+
declare const Checkbox: import("svelte").Component<WithoutChildrenOrChild<CheckboxPrimitive.RootProps>, {}, "ref" | "checked" | "indeterminate">;
|
|
4
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
5
|
+
export default Checkbox;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import Root from './popover.svelte';
|
|
2
|
+
import Content from './popover-content.svelte';
|
|
3
|
+
import Trigger from './popover-trigger.svelte';
|
|
4
|
+
import Close from './popover-close.svelte';
|
|
5
|
+
export { Root, Content, Trigger, Close, Root as Popover, Content as PopoverContent, Trigger as PopoverTrigger, Close as PopoverClose };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Root from './popover.svelte';
|
|
2
|
+
import Content from './popover-content.svelte';
|
|
3
|
+
import Trigger from './popover-trigger.svelte';
|
|
4
|
+
import Close from './popover-close.svelte';
|
|
5
|
+
export { Root, Content, Trigger, Close,
|
|
6
|
+
//
|
|
7
|
+
Root as Popover, Content as PopoverContent, Trigger as PopoverTrigger, Close as PopoverClose };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Popover as PopoverPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn, type WithoutChildrenOrChild } from '../../utils.js';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
sideOffset = 6,
|
|
10
|
+
align = 'start',
|
|
11
|
+
portalProps,
|
|
12
|
+
children,
|
|
13
|
+
...restProps
|
|
14
|
+
}: WithoutChildrenOrChild<PopoverPrimitive.ContentProps> & {
|
|
15
|
+
portalProps?: PopoverPrimitive.PortalProps;
|
|
16
|
+
children?: Snippet;
|
|
17
|
+
} = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<!-- Portal targets `.vela-admin-bar` (not body) so the @scope-wrapped utilities apply. -->
|
|
21
|
+
<PopoverPrimitive.Portal to=".vela-admin-bar" {...portalProps}>
|
|
22
|
+
<PopoverPrimitive.Content
|
|
23
|
+
bind:ref
|
|
24
|
+
data-slot="popover-content"
|
|
25
|
+
{sideOffset}
|
|
26
|
+
{align}
|
|
27
|
+
class={cn(
|
|
28
|
+
'vela:z-[9999] vela:w-80 vela:rounded-xl vela:border vela:border-[var(--cms-bar-divider)] vela:bg-bar-bg vela:text-bar-text vela:shadow-[0_12px_32px_rgba(0,0,0,0.45)] vela:outline-hidden vela:data-[state=open]:animate-in vela:data-[state=closed]:animate-out vela:data-[state=closed]:fade-out-0 vela:data-[state=open]:fade-in-0 vela:data-[state=closed]:zoom-out-95 vela:data-[state=open]:zoom-in-95 vela:duration-150',
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
{...restProps}
|
|
32
|
+
>
|
|
33
|
+
{@render children?.()}
|
|
34
|
+
</PopoverPrimitive.Content>
|
|
35
|
+
</PopoverPrimitive.Portal>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Popover as PopoverPrimitive } from 'bits-ui';
|
|
2
|
+
import { type WithoutChildrenOrChild } from '../../utils.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
type $$ComponentProps = WithoutChildrenOrChild<PopoverPrimitive.ContentProps> & {
|
|
5
|
+
portalProps?: PopoverPrimitive.PortalProps;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
};
|
|
8
|
+
declare const PopoverContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
9
|
+
type PopoverContent = ReturnType<typeof PopoverContent>;
|
|
10
|
+
export default PopoverContent;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import Root from './sheet.svelte';
|
|
2
|
+
import Close from './sheet-close.svelte';
|
|
3
|
+
import Trigger from './sheet-trigger.svelte';
|
|
4
|
+
import Overlay from './sheet-overlay.svelte';
|
|
5
|
+
import Content from './sheet-content.svelte';
|
|
6
|
+
import Header from './sheet-header.svelte';
|
|
7
|
+
import Footer from './sheet-footer.svelte';
|
|
8
|
+
import Title from './sheet-title.svelte';
|
|
9
|
+
import Description from './sheet-description.svelte';
|
|
10
|
+
export { Root, Close, Trigger, Overlay, Content, Header, Footer, Title, Description, Root as Sheet, Close as SheetClose, Trigger as SheetTrigger, Overlay as SheetOverlay, Content as SheetContent, Header as SheetHeader, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Root from './sheet.svelte';
|
|
2
|
+
import Close from './sheet-close.svelte';
|
|
3
|
+
import Trigger from './sheet-trigger.svelte';
|
|
4
|
+
import Overlay from './sheet-overlay.svelte';
|
|
5
|
+
import Content from './sheet-content.svelte';
|
|
6
|
+
import Header from './sheet-header.svelte';
|
|
7
|
+
import Footer from './sheet-footer.svelte';
|
|
8
|
+
import Title from './sheet-title.svelte';
|
|
9
|
+
import Description from './sheet-description.svelte';
|
|
10
|
+
export { Root, Close, Trigger, Overlay, Content, Header, Footer, Title, Description,
|
|
11
|
+
//
|
|
12
|
+
Root as Sheet, Close as SheetClose, Trigger as SheetTrigger, Overlay as SheetOverlay, Content as SheetContent, Header as SheetHeader, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { variants, type VariantProps } from '../../variants.js';
|
|
3
|
+
|
|
4
|
+
export const sheetVariants = variants({
|
|
5
|
+
base: 'vela:fixed vela:z-[9999] vela:flex vela:flex-col vela:gap-4 vela:bg-bar-bg vela:text-bar-text vela:shadow-[0_12px_32px_rgba(0,0,0,0.45)] vela:transition vela:ease-in-out vela:data-[state=open]:animate-in vela:data-[state=closed]:animate-out vela:data-[state=closed]:duration-200 vela:data-[state=open]:duration-300',
|
|
6
|
+
variants: {
|
|
7
|
+
side: {
|
|
8
|
+
top: 'vela:inset-x-0 vela:top-0 vela:border-b vela:border-[var(--cms-bar-divider)] vela:data-[state=closed]:slide-out-to-top vela:data-[state=open]:slide-in-from-top',
|
|
9
|
+
bottom:
|
|
10
|
+
'vela:inset-x-0 vela:bottom-0 vela:max-h-[85vh] vela:rounded-t-2xl vela:border-t vela:border-[var(--cms-bar-divider)] vela:data-[state=closed]:slide-out-to-bottom vela:data-[state=open]:slide-in-from-bottom',
|
|
11
|
+
left: 'vela:inset-y-0 vela:left-0 vela:h-full vela:w-3/4 vela:max-w-sm vela:border-r vela:border-[var(--cms-bar-divider)] vela:data-[state=closed]:slide-out-to-left vela:data-[state=open]:slide-in-from-left',
|
|
12
|
+
right:
|
|
13
|
+
'vela:inset-y-0 vela:right-0 vela:h-full vela:w-3/4 vela:max-w-sm vela:border-l vela:border-[var(--cms-bar-divider)] vela:data-[state=closed]:slide-out-to-right vela:data-[state=open]:slide-in-from-right'
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: { side: 'bottom' }
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type Side = VariantProps<typeof sheetVariants>['side'];
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import { Dialog as SheetPrimitive } from 'bits-ui';
|
|
24
|
+
import type { Snippet } from 'svelte';
|
|
25
|
+
import XIcon from '../../icons/x.svelte';
|
|
26
|
+
import { cn, type WithoutChildrenOrChild } from '../../utils.js';
|
|
27
|
+
import SheetOverlay from './sheet-overlay.svelte';
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
ref = $bindable(null),
|
|
31
|
+
class: className,
|
|
32
|
+
side = 'bottom',
|
|
33
|
+
portalProps,
|
|
34
|
+
showCloseButton = true,
|
|
35
|
+
children,
|
|
36
|
+
...restProps
|
|
37
|
+
}: WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
|
|
38
|
+
portalProps?: SheetPrimitive.PortalProps;
|
|
39
|
+
side?: Side;
|
|
40
|
+
showCloseButton?: boolean;
|
|
41
|
+
children: Snippet;
|
|
42
|
+
} = $props();
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<SheetPrimitive.Portal to=".vela-admin-bar" {...portalProps}>
|
|
46
|
+
<SheetOverlay />
|
|
47
|
+
<SheetPrimitive.Content
|
|
48
|
+
bind:ref
|
|
49
|
+
data-slot="sheet-content"
|
|
50
|
+
class={cn(sheetVariants({ side }), className)}
|
|
51
|
+
{...restProps}
|
|
52
|
+
>
|
|
53
|
+
{@render children?.()}
|
|
54
|
+
{#if showCloseButton}
|
|
55
|
+
<SheetPrimitive.Close
|
|
56
|
+
data-slot="sheet-close"
|
|
57
|
+
class="vela:absolute vela:top-3 vela:right-3 vela:inline-flex vela:items-center vela:justify-center vela:w-7 vela:h-7 vela:rounded-full vela:text-bar-text-secondary vela:hover:text-bar-text vela:hover:bg-[var(--cms-bar-bg-hover)] vela:focus:outline-none vela:focus-visible:ring-2 vela:focus-visible:ring-[var(--cms-accent)] vela:cursor-pointer"
|
|
58
|
+
>
|
|
59
|
+
<XIcon class="vela:size-4" />
|
|
60
|
+
<span class="vela:sr-only">Close</span>
|
|
61
|
+
</SheetPrimitive.Close>
|
|
62
|
+
{/if}
|
|
63
|
+
</SheetPrimitive.Content>
|
|
64
|
+
</SheetPrimitive.Portal>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type VariantProps } from '../../variants.js';
|
|
2
|
+
export declare const sheetVariants: (props?: {
|
|
3
|
+
side?: "bottom" | "left" | "right" | "top" | undefined;
|
|
4
|
+
}) => string;
|
|
5
|
+
export type Side = VariantProps<typeof sheetVariants>['side'];
|
|
6
|
+
import { Dialog as SheetPrimitive } from 'bits-ui';
|
|
7
|
+
import type { Snippet } from 'svelte';
|
|
8
|
+
import { type WithoutChildrenOrChild } from '../../utils.js';
|
|
9
|
+
type $$ComponentProps = WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
|
|
10
|
+
portalProps?: SheetPrimitive.PortalProps;
|
|
11
|
+
side?: Side;
|
|
12
|
+
showCloseButton?: boolean;
|
|
13
|
+
children: Snippet;
|
|
14
|
+
};
|
|
15
|
+
declare const SheetContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
16
|
+
type SheetContent = ReturnType<typeof SheetContent>;
|
|
17
|
+
export default SheetContent;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Dialog as SheetPrimitive } from 'bits-ui';
|
|
3
|
+
import { cn } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: SheetPrimitive.DescriptionProps = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<SheetPrimitive.Description
|
|
13
|
+
bind:ref
|
|
14
|
+
data-slot="sheet-description"
|
|
15
|
+
class={cn('vela:text-[12px] vela:text-bar-text-secondary vela:m-0', className)}
|
|
16
|
+
{...restProps}
|
|
17
|
+
/>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="sheet-footer"
|
|
16
|
+
class={cn(
|
|
17
|
+
'vela:mt-auto vela:flex vela:items-center vela:justify-end vela:gap-1.5 vela:px-4 vela:py-2.5 vela:border-t vela:border-[var(--cms-bar-divider)]',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type WithElementRef } from '../../utils.js';
|
|
3
|
+
declare const SheetFooter: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLDivElement>>, {}, "ref">;
|
|
4
|
+
type SheetFooter = ReturnType<typeof SheetFooter>;
|
|
5
|
+
export default SheetFooter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="sheet-header"
|
|
16
|
+
class={cn('vela:flex vela:flex-col vela:gap-1 vela:px-4 vela:pt-3.5 vela:pb-3', className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|