@rigel-cms/rigel 1.4.0-rc.2 → 1.4.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 +4 -2
- package/docs/README.md +28 -0
- package/docs/authors.md +133 -0
- package/docs/content-locales.md +1 -1
- package/docs/content-model.md +131 -0
- package/docs/extensions.md +1 -1
- package/docs/getting-started.md +139 -0
- package/docs/github.md +112 -0
- package/docs/media.md +75 -0
- package/docs/menus.md +95 -0
- package/docs/preview-hosts.md +78 -0
- package/docs/preview.md +102 -0
- package/docs/seo.md +87 -0
- package/docs/sidebar.md +97 -0
- package/package.json +2 -3
- package/src/article-render.ts +12 -5
- package/src/editor/App.svelte +36 -15
- package/src/editor/i18n/de/fields.json +2 -0
- package/src/editor/i18n/de/shell.json +2 -1
- package/src/editor/i18n/en/fields.json +2 -0
- package/src/editor/i18n/en/shell.json +2 -1
- package/src/editor/i18n/es/fields.json +2 -0
- package/src/editor/i18n/es/shell.json +2 -1
- package/src/editor/i18n/fr/fields.json +2 -0
- package/src/editor/i18n/fr/shell.json +2 -1
- package/src/editor/i18n/it/fields.json +2 -0
- package/src/editor/i18n/it/shell.json +2 -1
- package/src/editor/i18n/ja/fields.json +2 -0
- package/src/editor/i18n/ja/shell.json +2 -1
- package/src/editor/i18n/nl/fields.json +2 -0
- package/src/editor/i18n/nl/shell.json +2 -1
- package/src/editor/i18n/ru/fields.json +2 -0
- package/src/editor/i18n/ru/shell.json +2 -1
- package/src/editor/i18n/zh/fields.json +2 -0
- package/src/editor/i18n/zh/shell.json +2 -1
- package/src/editor/lib/BlockEditor.svelte +4 -3
- package/src/editor/lib/CollectionPanel.svelte +10 -2
- package/src/editor/lib/MarkdownEditor.svelte +38 -5
- package/src/editor/lib/PropertiesTabs.svelte +8 -1
- package/src/editor/lib/SchemaField.svelte +16 -2
- package/src/editor/lib/SchemaPanel.svelte +9 -3
- package/src/editor/lib/VisualEditor.svelte +64 -13
- package/src/editor/lib/markdown-blocks.ts +69 -0
- package/src/editor/lib/media-library.ts +2 -1
- package/src/editor/styles.css +7 -1
- package/src/schema/types.ts +1 -0
- package/src/schema/validate.ts +1 -0
package/src/editor/App.svelte
CHANGED
|
@@ -471,7 +471,9 @@
|
|
|
471
471
|
|
|
472
472
|
const sortedPosts = $derived.by(() => {
|
|
473
473
|
if (!coll) return [];
|
|
474
|
-
const
|
|
474
|
+
const query = (Object.hasOwn(collectionQueries, coll.name) ? collectionQueries[coll.name] : "").trim().toLocaleLowerCase();
|
|
475
|
+
const fields = coll.fields.filter(field => (field.key === "description" && (field.type === "string" || field.type === "text")) || (field.key === "tags" && field.type === "tags"));
|
|
476
|
+
const posts = coll.posts.filter(post => !query || [post.frontmatter.title, effectiveSlug(post.frontmatter, post.slug), ...fields.flatMap(field => post.frontmatter[field.key] ?? [])].some(value => typeof value === "string" && value.toLocaleLowerCase().includes(query)));
|
|
475
477
|
if (!sortBy) return posts;
|
|
476
478
|
return posts.sort((a, b) => {
|
|
477
479
|
const va = cellValue(a, sortBy);
|
|
@@ -483,6 +485,13 @@
|
|
|
483
485
|
const sidebar = $derived(buildSidebar(visibleContent, $t));
|
|
484
486
|
let pagesFilter = $state<string | null>(null);
|
|
485
487
|
let pagesQuery = $state("");
|
|
488
|
+
let collectionQueries = $state<Record<string, string>>({});
|
|
489
|
+
const tableQuery = $derived(sel?.kind === "collection" ? Object.hasOwn(collectionQueries, sel.name) ? collectionQueries[sel.name] : "" : pagesQuery);
|
|
490
|
+
const searchLabel = $derived(sel?.kind === "collection" && coll ? $t("shell.searchNamedEntries", { name: coll.label }) : $t("shell.searchPages"));
|
|
491
|
+
function setTableQuery(value: string) {
|
|
492
|
+
if (sel?.kind === "collection") collectionQueries[sel.name] = value;
|
|
493
|
+
else pagesQuery = value;
|
|
494
|
+
}
|
|
486
495
|
let pagesSearch: HTMLInputElement;
|
|
487
496
|
let entryList = $state<Sel | null>(null);
|
|
488
497
|
const categories = $derived(pageCategories(visibleContent));
|
|
@@ -643,10 +652,19 @@
|
|
|
643
652
|
action?.();
|
|
644
653
|
}
|
|
645
654
|
|
|
646
|
-
function select(next: Sel | null) {
|
|
655
|
+
function select(next: Sel | null, fromSidebar = false) {
|
|
647
656
|
if (manifest.contentLocales && !selectionInContent(visibleContent, next)) return;
|
|
648
|
-
|
|
649
|
-
|
|
657
|
+
const resetPages = fromSidebar && next?.kind === "pages";
|
|
658
|
+
const resetCollection = fromSidebar && next?.kind === "collection";
|
|
659
|
+
if (sel && next && sameSel(sel, next) && !resetPages && !resetCollection) return;
|
|
660
|
+
requestNavigation(() => {
|
|
661
|
+
if (resetPages) {
|
|
662
|
+
pagesFilter = next.category ?? null;
|
|
663
|
+
pagesQuery = "";
|
|
664
|
+
}
|
|
665
|
+
if (resetCollection) collectionQueries[next.name] = "";
|
|
666
|
+
openSelection(next);
|
|
667
|
+
});
|
|
650
668
|
}
|
|
651
669
|
|
|
652
670
|
function openSelection(next: Sel | null) {
|
|
@@ -915,7 +933,7 @@
|
|
|
915
933
|
data-tooltip={node.label}
|
|
916
934
|
class:active={navActive(node.target)}
|
|
917
935
|
aria-current={navActive(node.target) ? "page" : undefined}
|
|
918
|
-
onclick={() => select(node.target)}
|
|
936
|
+
onclick={() => select(node.target, true)}
|
|
919
937
|
>
|
|
920
938
|
<SidebarIcon name={node.icon ?? ({ pages: "files", page: "file-text", collection: "newspaper", documents: "squares-four", document: "file-text", menus: "list", media: "images" }[node.target.kind])} /><span class="sidebar-label">{node.label}</span>
|
|
921
939
|
</button>
|
|
@@ -1009,22 +1027,24 @@
|
|
|
1009
1027
|
{#if templates.length}
|
|
1010
1028
|
<button class="primary" onclick={() => newPage(pageDocType, true)}>{$t("shell.addNew")}</button>
|
|
1011
1029
|
{/if}
|
|
1030
|
+
{/if}
|
|
1031
|
+
{#if (sel?.kind === "collection" && coll) || (sel?.kind === "documents" && activeDocType && activeDocType.creatable !== false && !activeDocType.singleton)}
|
|
1032
|
+
<button class="primary" onclick={() => (sel?.kind === "collection" ? newPost(coll!) : newPage(activeDocType))}>{$t("shell.addNew")}</button>
|
|
1033
|
+
{/if}
|
|
1034
|
+
{#if sel.kind === "pages" || (sel.kind === "collection" && coll)}
|
|
1012
1035
|
<div class="pages-controls">
|
|
1013
|
-
{#if categories.length}
|
|
1036
|
+
{#if sel.kind === "pages" && categories.length}
|
|
1014
1037
|
<Select ariaLabel={$t("shell.category")} value={JSON.stringify(pageCategory)} options={[{ value: "null", label: $t("shell.allPages") }, { value: JSON.stringify(""), label: $t("shell.coreCategory") }, ...categories.map(category => ({ value: JSON.stringify(category), label: category }))]} onchange={(value) => { if (typeof value === "string") { pagesFilter = JSON.parse(value); select({ kind: "pages" }); } }} />
|
|
1015
1038
|
{/if}
|
|
1016
1039
|
<div class="pages-search">
|
|
1017
1040
|
<Icon name="magnifier" size={18} />
|
|
1018
|
-
<input type="search" aria-label={
|
|
1019
|
-
{#if
|
|
1020
|
-
<button type="button" class="icon-btn" aria-label={$t("fields.clear")} onclick={() => {
|
|
1041
|
+
<input type="search" aria-label={searchLabel} placeholder={searchLabel} value={tableQuery} oninput={(event) => setTableQuery(event.currentTarget.value)} bind:this={pagesSearch} />
|
|
1042
|
+
{#if tableQuery}
|
|
1043
|
+
<button type="button" class="icon-btn" aria-label={$t("fields.clear")} onclick={() => { setTableQuery(""); pagesSearch.focus(); }}><Icon name="x" size={16} /></button>
|
|
1021
1044
|
{/if}
|
|
1022
1045
|
</div>
|
|
1023
1046
|
</div>
|
|
1024
1047
|
{/if}
|
|
1025
|
-
{#if (sel?.kind === "collection" && coll) || (sel?.kind === "documents" && activeDocType && activeDocType.creatable !== false && !activeDocType.singleton)}
|
|
1026
|
-
<button class="primary" onclick={() => (sel?.kind === "collection" ? newPost(coll!) : newPage(sel?.kind === "documents" ? activeDocType : pageDocType, sel?.kind === "pages"))}>{$t("shell.addNew")}</button>
|
|
1027
|
-
{/if}
|
|
1028
1048
|
{#if sel?.kind === "post" && post}
|
|
1029
1049
|
<button class="icon-btn" aria-label={$t("shell.deletePost")} data-tooltip={$t("shell.deletePost")} disabled={saveState === "saving"} onclick={deletePost}>
|
|
1030
1050
|
<Icon name="trash" size={15} />
|
|
@@ -1086,7 +1106,7 @@
|
|
|
1086
1106
|
{/each}
|
|
1087
1107
|
</tr>
|
|
1088
1108
|
{:else}
|
|
1089
|
-
<tr><td colspan={columns.length}>{$t(sel?.kind === "pages" &&
|
|
1109
|
+
<tr><td colspan={columns.length}>{$t((sel?.kind === "pages" || sel?.kind === "collection") && tableQuery.trim() ? "shell.noResults" : "shell.noEntries")}</td></tr>
|
|
1090
1110
|
{/each}
|
|
1091
1111
|
</tbody>
|
|
1092
1112
|
</table>
|
|
@@ -1106,6 +1126,7 @@
|
|
|
1106
1126
|
documents={draftDocuments}
|
|
1107
1127
|
previewPath={documentPreview("draft")}
|
|
1108
1128
|
websitePath={documentPreview("website")}
|
|
1129
|
+
bodyInline={settings.userPref.editorExperience === "inline"}
|
|
1109
1130
|
creating={newDocument?.id === sel.id}
|
|
1110
1131
|
devMode={settings.devMode}
|
|
1111
1132
|
reloadToken={documentReload}
|
|
@@ -1161,8 +1182,8 @@
|
|
|
1161
1182
|
devMode={settings.devMode}
|
|
1162
1183
|
onchange={(next) => { draft = next; touch(); }}
|
|
1163
1184
|
>
|
|
1164
|
-
{#snippet properties(selected, focusToken, bodyInline, oneditbody, bodyLoading)}
|
|
1165
|
-
<CollectionPanel fields={coll.fields} values={draft} storageSlug={post.slug} documents={manifest.documents ?? []} body={bodyDraft as ArticleBody} {bodyInline} {bodyLoading} {oneditbody} {selected} {focusToken} devMode={settings.devMode} onchange={(key, value) => { draft[key] = value; touch(); }} onbody={(value) => { bodyDraft = value; touch(); }} />
|
|
1185
|
+
{#snippet properties(selected, focusToken, bodyInline, oneditbody, bodyLoading, inlineFields, oneditfield)}
|
|
1186
|
+
<CollectionPanel fields={coll.fields} values={draft} storageSlug={post.slug} documents={manifest.documents ?? []} body={bodyDraft as ArticleBody} {bodyInline} {bodyLoading} {inlineFields} {oneditbody} {oneditfield} {selected} {focusToken} devMode={settings.devMode} onchange={(key, value) => { draft[key] = value; touch(); }} onbody={(value) => { bodyDraft = value; touch(); }} />
|
|
1166
1187
|
{/snippet}
|
|
1167
1188
|
</VisualEditor>
|
|
1168
1189
|
{/key}
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Artikeltext in der Vorschau bearbeiten",
|
|
51
51
|
"fields.loadingPreview": "Vorschau wird geladen…",
|
|
52
52
|
"fields.editArticleBodyHint": "Klicken Sie zum Bearbeiten auf den Artikeltext in der Vorschau. Änderungen an den Inhalten müssen gespeichert werden.",
|
|
53
|
+
"fields.editRichText": "In der Vorschau bearbeiten",
|
|
54
|
+
"fields.editRichTextHint": "Klicke in der Vorschau auf diesen Text, um ihn zu bearbeiten. Änderungen müssen gespeichert werden.",
|
|
53
55
|
"fields.seoNotSet": "Nicht festgelegt",
|
|
54
56
|
"fields.seoTooShort": "Zu kurz",
|
|
55
57
|
"fields.seoTooLong": "Zu lang",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Aktuelle Inhaltssprache",
|
|
6
6
|
"shell.coreCategory": "Standard",
|
|
7
7
|
"shell.searchPages": "Seiten suchen",
|
|
8
|
+
"shell.searchNamedEntries": "In {name} suchen",
|
|
8
9
|
"shell.template": "Vorlage",
|
|
9
10
|
"shell.chooseTemplate": "Vorlage auswählen",
|
|
10
11
|
"shell.search": "Suchen",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Artikeltext bearbeiten",
|
|
50
51
|
"shell.sidebar": "Seitenleiste",
|
|
51
52
|
"shell.inlinePreview": "Inline-Vorschau",
|
|
52
|
-
"shell.editorExperienceHint": "Mit der Inline-Vorschau kannst du auf
|
|
53
|
+
"shell.editorExperienceHint": "Mit der Inline-Vorschau kannst du auf Rich-Text-Bereiche klicken und direkt auf der Seite schreiben. Felder ohne Vorschau-Bindung bleiben in der Seitenleiste. Diese Einstellung wird in diesem Browser gespeichert; Inhalte müssen weiterhin mit Speichern gesichert werden.",
|
|
53
54
|
"shell.githubAccount": "GitHub-Konto",
|
|
54
55
|
"shell.noGithubAccount": "Kein GitHub-Konto verbunden.",
|
|
55
56
|
"shell.repository": "Repository",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Edit article body in preview",
|
|
51
51
|
"fields.loadingPreview": "Loading preview…",
|
|
52
52
|
"fields.editArticleBodyHint": "Click the article body in the preview to edit. Content changes require Save.",
|
|
53
|
+
"fields.editRichText": "Edit in preview",
|
|
54
|
+
"fields.editRichTextHint": "Click this text in the preview to edit. Content changes require Save.",
|
|
53
55
|
"fields.seoNotSet": "Not set",
|
|
54
56
|
"fields.seoTooShort": "Too short",
|
|
55
57
|
"fields.seoTooLong": "Too long",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Current content language",
|
|
6
6
|
"shell.coreCategory": "Core",
|
|
7
7
|
"shell.searchPages": "Search pages",
|
|
8
|
+
"shell.searchNamedEntries": "Search {name}",
|
|
8
9
|
"shell.template": "Template",
|
|
9
10
|
"shell.chooseTemplate": "Choose a template",
|
|
10
11
|
"shell.search": "Search",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Article body editing",
|
|
50
51
|
"shell.sidebar": "Sidebar",
|
|
51
52
|
"shell.inlinePreview": "Inline preview",
|
|
52
|
-
"shell.editorExperienceHint": "Inline preview lets you click
|
|
53
|
+
"shell.editorExperienceHint": "Inline preview lets you click bound rich text and write directly on the page. Fields without a preview binding stay in the sidebar. This preference saves in this browser; content still requires Save.",
|
|
53
54
|
"shell.githubAccount": "GitHub account",
|
|
54
55
|
"shell.noGithubAccount": "No GitHub account connected.",
|
|
55
56
|
"shell.repository": "Repository",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Editar el cuerpo del artículo en la vista previa",
|
|
51
51
|
"fields.loadingPreview": "Cargando vista previa…",
|
|
52
52
|
"fields.editArticleBodyHint": "Haz clic en el cuerpo del artículo en la vista previa para editarlo. Los cambios en el contenido requieren pulsar Guardar.",
|
|
53
|
+
"fields.editRichText": "Editar en la vista previa",
|
|
54
|
+
"fields.editRichTextHint": "Haz clic en este texto de la vista previa para editarlo. Los cambios requieren pulsar Guardar.",
|
|
53
55
|
"fields.seoNotSet": "Sin definir",
|
|
54
56
|
"fields.seoTooShort": "Demasiado corto",
|
|
55
57
|
"fields.seoTooLong": "Demasiado largo",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Idioma actual del contenido",
|
|
6
6
|
"shell.coreCategory": "Principales",
|
|
7
7
|
"shell.searchPages": "Buscar páginas",
|
|
8
|
+
"shell.searchNamedEntries": "Buscar en {name}",
|
|
8
9
|
"shell.template": "Plantilla",
|
|
9
10
|
"shell.chooseTemplate": "Elige una plantilla",
|
|
10
11
|
"shell.search": "Buscar",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Edición del cuerpo del artículo",
|
|
50
51
|
"shell.sidebar": "Barra lateral",
|
|
51
52
|
"shell.inlinePreview": "Vista previa en línea",
|
|
52
|
-
"shell.editorExperienceHint": "La vista previa en línea permite hacer clic en
|
|
53
|
+
"shell.editorExperienceHint": "La vista previa en línea permite hacer clic en los textos enriquecidos vinculados y escribir directamente en la página. Los campos sin vínculo de vista previa permanecen en la barra lateral. Esta preferencia se guarda en este navegador; para guardar el contenido sigue siendo necesario pulsar Guardar.",
|
|
53
54
|
"shell.githubAccount": "Cuenta de GitHub",
|
|
54
55
|
"shell.noGithubAccount": "No hay ninguna cuenta de GitHub conectada.",
|
|
55
56
|
"shell.repository": "Repositorio",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Modifier le corps de l’article dans l’aperçu",
|
|
51
51
|
"fields.loadingPreview": "Chargement de l’aperçu…",
|
|
52
52
|
"fields.editArticleBodyHint": "Cliquez sur le corps de l’article dans l’aperçu pour le modifier. Enregistrez pour conserver les modifications du contenu.",
|
|
53
|
+
"fields.editRichText": "Modifier dans l’aperçu",
|
|
54
|
+
"fields.editRichTextHint": "Cliquez sur ce texte dans l’aperçu pour le modifier. Enregistrez pour conserver les modifications du contenu.",
|
|
53
55
|
"fields.seoNotSet": "Non défini",
|
|
54
56
|
"fields.seoTooShort": "Trop court",
|
|
55
57
|
"fields.seoTooLong": "Trop long",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Langue actuelle du contenu",
|
|
6
6
|
"shell.coreCategory": "Principales",
|
|
7
7
|
"shell.searchPages": "Rechercher des pages",
|
|
8
|
+
"shell.searchNamedEntries": "Rechercher dans {name}",
|
|
8
9
|
"shell.template": "Modèle",
|
|
9
10
|
"shell.chooseTemplate": "Choisir un modèle",
|
|
10
11
|
"shell.search": "Rechercher",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Édition du corps des articles",
|
|
50
51
|
"shell.sidebar": "Barre latérale",
|
|
51
52
|
"shell.inlinePreview": "Aperçu avec édition intégrée",
|
|
52
|
-
"shell.editorExperienceHint": "L’aperçu avec édition intégrée permet de cliquer sur
|
|
53
|
+
"shell.editorExperienceHint": "L’aperçu avec édition intégrée permet de cliquer sur les textes enrichis liés pour écrire directement sur la page. Les champs sans liaison d’aperçu restent dans la barre latérale. Cette préférence est enregistrée dans ce navigateur ; le contenu nécessite toujours un clic sur Enregistrer.",
|
|
53
54
|
"shell.githubAccount": "Compte GitHub",
|
|
54
55
|
"shell.noGithubAccount": "Aucun compte GitHub connecté.",
|
|
55
56
|
"shell.repository": "Dépôt",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Modifica il corpo dell’articolo nell’anteprima",
|
|
51
51
|
"fields.loadingPreview": "Caricamento dell’anteprima…",
|
|
52
52
|
"fields.editArticleBodyHint": "Fai clic sul corpo dell’articolo nell’anteprima per modificarlo. Premi Salva per salvare le modifiche ai contenuti.",
|
|
53
|
+
"fields.editRichText": "Modifica nell’anteprima",
|
|
54
|
+
"fields.editRichTextHint": "Fai clic su questo testo nell’anteprima per modificarlo. Premi Salva per salvare le modifiche ai contenuti.",
|
|
53
55
|
"fields.seoNotSet": "Non impostato",
|
|
54
56
|
"fields.seoTooShort": "Troppo breve",
|
|
55
57
|
"fields.seoTooLong": "Troppo lungo",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Lingua attuale del contenuto",
|
|
6
6
|
"shell.coreCategory": "Principali",
|
|
7
7
|
"shell.searchPages": "Cerca pagine",
|
|
8
|
+
"shell.searchNamedEntries": "Cerca in {name}",
|
|
8
9
|
"shell.template": "Modello",
|
|
9
10
|
"shell.chooseTemplate": "Scegli un modello",
|
|
10
11
|
"shell.search": "Cerca",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Modifica del corpo dell’articolo",
|
|
50
51
|
"shell.sidebar": "Barra laterale",
|
|
51
52
|
"shell.inlinePreview": "Anteprima con modifica diretta",
|
|
52
|
-
"shell.editorExperienceHint": "L’anteprima con modifica diretta permette di fare clic
|
|
53
|
+
"shell.editorExperienceHint": "L’anteprima con modifica diretta permette di fare clic sui testi formattati collegati e scrivere direttamente nella pagina. I campi senza collegamento all’anteprima restano nella barra laterale. Questa preferenza viene salvata in questo browser; per salvare i contenuti devi comunque premere Salva.",
|
|
53
54
|
"shell.githubAccount": "Account GitHub",
|
|
54
55
|
"shell.noGithubAccount": "Nessun account GitHub collegato.",
|
|
55
56
|
"shell.repository": "Repository",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "プレビューで記事本文を編集",
|
|
51
51
|
"fields.loadingPreview": "プレビューを読み込み中…",
|
|
52
52
|
"fields.editArticleBodyHint": "プレビューの記事本文をクリックすると編集できます。コンテンツの変更には保存が必要です。",
|
|
53
|
+
"fields.editRichText": "プレビューで編集",
|
|
54
|
+
"fields.editRichTextHint": "プレビューのこのテキストをクリックすると編集できます。変更には保存が必要です。",
|
|
53
55
|
"fields.seoNotSet": "未設定",
|
|
54
56
|
"fields.seoTooShort": "短すぎます",
|
|
55
57
|
"fields.seoTooLong": "長すぎます",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "現在のコンテンツ言語",
|
|
6
6
|
"shell.coreCategory": "基本",
|
|
7
7
|
"shell.searchPages": "ページを検索",
|
|
8
|
+
"shell.searchNamedEntries": "{name}内を検索",
|
|
8
9
|
"shell.template": "テンプレート",
|
|
9
10
|
"shell.chooseTemplate": "テンプレートを選択",
|
|
10
11
|
"shell.search": "検索",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "記事本文の編集",
|
|
50
51
|
"shell.sidebar": "サイドバー",
|
|
51
52
|
"shell.inlinePreview": "インラインプレビュー",
|
|
52
|
-
"shell.editorExperienceHint": "
|
|
53
|
+
"shell.editorExperienceHint": "インラインプレビューでは、プレビューに紐づいたリッチテキストをクリックしてページ上で直接編集できます。プレビューに紐づかないフィールドはサイドバーに表示されます。この設定はこのブラウザーに保存されますが、コンテンツには引き続き「保存」が必要です。",
|
|
53
54
|
"shell.githubAccount": "GitHubアカウント",
|
|
54
55
|
"shell.noGithubAccount": "GitHubアカウントが接続されていません。",
|
|
55
56
|
"shell.repository": "リポジトリ",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "Artikeltekst in het voorbeeld bewerken",
|
|
51
51
|
"fields.loadingPreview": "Voorbeeld laden…",
|
|
52
52
|
"fields.editArticleBodyHint": "Klik op de artikeltekst in het voorbeeld om deze te bewerken. Wijzigingen in de inhoud moeten worden opgeslagen.",
|
|
53
|
+
"fields.editRichText": "Bewerken in het voorbeeld",
|
|
54
|
+
"fields.editRichTextHint": "Klik op deze tekst in het voorbeeld om deze te bewerken. Wijzigingen moeten worden opgeslagen.",
|
|
53
55
|
"fields.seoNotSet": "Niet ingesteld",
|
|
54
56
|
"fields.seoTooShort": "Te kort",
|
|
55
57
|
"fields.seoTooLong": "Te lang",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Huidige inhoudstaal",
|
|
6
6
|
"shell.coreCategory": "Standaard",
|
|
7
7
|
"shell.searchPages": "Pagina’s zoeken",
|
|
8
|
+
"shell.searchNamedEntries": "Zoeken in {name}",
|
|
8
9
|
"shell.template": "Sjabloon",
|
|
9
10
|
"shell.chooseTemplate": "Kies een sjabloon",
|
|
10
11
|
"shell.search": "Zoeken",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Artikeltekst bewerken",
|
|
50
51
|
"shell.sidebar": "Zijbalk",
|
|
51
52
|
"shell.inlinePreview": "Inline voorbeeld",
|
|
52
|
-
"shell.editorExperienceHint": "Met het inline voorbeeld kun je op
|
|
53
|
+
"shell.editorExperienceHint": "Met het inline voorbeeld kun je op gekoppelde rich text klikken en direct op de pagina schrijven. Velden zonder koppeling met het voorbeeld blijven in de zijbalk. Deze voorkeur wordt in deze browser opgeslagen; inhoud moet je nog steeds opslaan met Opslaan.",
|
|
53
54
|
"shell.githubAccount": "GitHub-account",
|
|
54
55
|
"shell.noGithubAccount": "Er is geen GitHub-account gekoppeld.",
|
|
55
56
|
"shell.repository": "Repository",
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
"fields.editArticleBody": "Редактировать текст статьи в предпросмотре",
|
|
53
53
|
"fields.loadingPreview": "Загрузка предпросмотра…",
|
|
54
54
|
"fields.editArticleBodyHint": "Нажмите на текст статьи в предпросмотре, чтобы редактировать его. Изменения содержимого нужно сохранить.",
|
|
55
|
+
"fields.editRichText": "Редактировать в предпросмотре",
|
|
56
|
+
"fields.editRichTextHint": "Нажмите на этот текст в предпросмотре, чтобы редактировать его. Изменения нужно сохранить.",
|
|
55
57
|
"fields.seoNotSet": "Не задано",
|
|
56
58
|
"fields.seoTooShort": "Слишком коротко",
|
|
57
59
|
"fields.seoTooLong": "Слишком длинно",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "Текущий язык содержимого",
|
|
6
6
|
"shell.coreCategory": "Основные",
|
|
7
7
|
"shell.searchPages": "Поиск страниц",
|
|
8
|
+
"shell.searchNamedEntries": "Поиск в {name}",
|
|
8
9
|
"shell.template": "Шаблон",
|
|
9
10
|
"shell.chooseTemplate": "Выберите шаблон",
|
|
10
11
|
"shell.search": "Поиск",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "Редактирование текста статьи",
|
|
50
51
|
"shell.sidebar": "Боковая панель",
|
|
51
52
|
"shell.inlinePreview": "Прямо в предпросмотре",
|
|
52
|
-
"shell.editorExperienceHint": "Этот режим позволяет нажать на текст
|
|
53
|
+
"shell.editorExperienceHint": "Этот режим позволяет нажать на связанный форматированный текст и редактировать его прямо на странице. Поля без привязки к предпросмотру остаются на боковой панели. Настройка сохраняется в этом браузере; для сохранения содержимого по-прежнему нужно нажать «Сохранить».",
|
|
53
54
|
"shell.githubAccount": "Учётная запись GitHub",
|
|
54
55
|
"shell.noGithubAccount": "Учётная запись GitHub не подключена.",
|
|
55
56
|
"shell.repository": "Репозиторий",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"fields.editArticleBody": "在预览中编辑文章正文",
|
|
51
51
|
"fields.loadingPreview": "正在加载预览…",
|
|
52
52
|
"fields.editArticleBodyHint": "点击预览中的文章正文即可编辑。内容更改后需保存。",
|
|
53
|
+
"fields.editRichText": "在预览中编辑",
|
|
54
|
+
"fields.editRichTextHint": "点击预览中的这段文字即可编辑。更改后需保存。",
|
|
53
55
|
"fields.seoNotSet": "未设置",
|
|
54
56
|
"fields.seoTooShort": "过短",
|
|
55
57
|
"fields.seoTooLong": "过长",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"shell.searchScopeCurrent": "当前内容语言",
|
|
6
6
|
"shell.coreCategory": "基础",
|
|
7
7
|
"shell.searchPages": "搜索页面",
|
|
8
|
+
"shell.searchNamedEntries": "在{name}中搜索",
|
|
8
9
|
"shell.template": "模板",
|
|
9
10
|
"shell.chooseTemplate": "选择模板",
|
|
10
11
|
"shell.search": "搜索",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"shell.articleBodyEditing": "文章正文编辑",
|
|
50
51
|
"shell.sidebar": "侧边栏",
|
|
51
52
|
"shell.inlinePreview": "行内预览",
|
|
52
|
-
"shell.editorExperienceHint": "
|
|
53
|
+
"shell.editorExperienceHint": "行内预览让你点击预览中绑定的富文本,直接在页面上编辑。没有预览绑定的字段仍在侧边栏中。此偏好设置保存在当前浏览器中;内容仍需点击“保存”才会保存。",
|
|
53
54
|
"shell.githubAccount": "GitHub 账号",
|
|
54
55
|
"shell.noGithubAccount": "未连接 GitHub 账号。",
|
|
55
56
|
"shell.repository": "仓库",
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
import { articleMarkdown } from "../../article-render";
|
|
26
26
|
import Icon from "./Icon.svelte";
|
|
27
27
|
import Select from "./Select.svelte";
|
|
28
|
-
let { value, oninput, editableRoot }: { value: ArticleBody; oninput: (value: ArticleBody) => void; editableRoot?: HTMLElement } = $props();
|
|
28
|
+
let { value, oninput, editableRoot, label }: { value: ArticleBody; oninput: (value: ArticleBody) => void; editableRoot?: HTMLElement; label?: string } = $props();
|
|
29
29
|
const inline = $derived(!!editableRoot);
|
|
30
|
+
const regionLabel = $derived(label ?? $t("richtext.body"));
|
|
30
31
|
let root: HTMLElement;
|
|
31
32
|
let surface: HTMLDivElement;
|
|
32
33
|
let menu: HTMLDivElement;
|
|
@@ -203,7 +204,7 @@
|
|
|
203
204
|
onMount(() => {
|
|
204
205
|
if (editableRoot) {
|
|
205
206
|
root = editableRoot; root.classList.add("block-content", "rigel-article-content"); root.contentEditable = "true";
|
|
206
|
-
root.setAttribute("role", "textbox"); root.setAttribute("aria-label",
|
|
207
|
+
root.setAttribute("role", "textbox"); root.setAttribute("aria-label", regionLabel); root.setAttribute("aria-multiline", "true");
|
|
207
208
|
root.setAttribute("aria-keyshortcuts", "Alt+F10");
|
|
208
209
|
}
|
|
209
210
|
view = root.ownerDocument.defaultView!;
|
|
@@ -278,7 +279,7 @@
|
|
|
278
279
|
};
|
|
279
280
|
});
|
|
280
281
|
|
|
281
|
-
$effect(() => { if (editableRoot) editableRoot.setAttribute("aria-label",
|
|
282
|
+
$effect(() => { if (editableRoot) editableRoot.setAttribute("aria-label", regionLabel); });
|
|
282
283
|
|
|
283
284
|
$effect(() => { if (!linkOpen) view?.CSS.highlights?.delete("rigel-link-selection"); });
|
|
284
285
|
|
|
@@ -16,14 +16,16 @@
|
|
|
16
16
|
import type { ContentDocument } from "../../schema/types";
|
|
17
17
|
import { slugify } from "../../slugs";
|
|
18
18
|
|
|
19
|
-
let { fields, values, storageSlug, body, selected, focusToken, devMode, documents = [], bodyInline = false, bodyLoading = false, oneditbody = () => {}, onchange, onbody }: {
|
|
19
|
+
let { fields, values, storageSlug, body, selected, focusToken, devMode, documents = [], bodyInline = false, bodyLoading = false, inlineFields = [], oneditbody = () => {}, oneditfield = () => {}, onchange, onbody }: {
|
|
20
20
|
fields: CollectionField[];
|
|
21
21
|
values: Record<string, unknown>;
|
|
22
22
|
storageSlug: string;
|
|
23
23
|
body: ArticleBody;
|
|
24
24
|
bodyInline?: boolean;
|
|
25
25
|
bodyLoading?: boolean;
|
|
26
|
+
inlineFields?: string[];
|
|
26
27
|
oneditbody?: () => void;
|
|
28
|
+
oneditfield?: (path: string) => void;
|
|
27
29
|
selected: SelectedField | null;
|
|
28
30
|
focusToken: number;
|
|
29
31
|
devMode: boolean;
|
|
@@ -66,7 +68,13 @@
|
|
|
66
68
|
<span>{label}{devMode ? ` (${field.key})` : ""}</span>
|
|
67
69
|
</label>
|
|
68
70
|
{:else if field.type === "markdown"}
|
|
69
|
-
|
|
71
|
+
{#if inlineFields.includes(field.key)}
|
|
72
|
+
<span class="schema-label">{label}{devMode ? ` (${field.key})` : ""}</span>
|
|
73
|
+
<button type="button" class="outlined" onclick={() => oneditfield(field.key)}>{$t("fields.editRichText")}</button>
|
|
74
|
+
<p class="hint">{$t("fields.editRichTextHint")}</p>
|
|
75
|
+
{:else}
|
|
76
|
+
<MarkdownEditor {label} value={String(values[field.key] ?? "")} oninput={(value) => onchange(field.key, value)} />
|
|
77
|
+
{/if}
|
|
70
78
|
{:else if field.type === "image"}
|
|
71
79
|
<label><span>{label}{devMode ? ` (${field.key})` : ""}</span><ImagePicker value={String(values[field.key] ?? "")} onpick={(value) => onchange(field.key, value)} /></label>
|
|
72
80
|
{:else if field.type === "tags"}
|
|
@@ -28,6 +28,28 @@
|
|
|
28
28
|
let view: EditorView | undefined;
|
|
29
29
|
let lastValue: string | undefined;
|
|
30
30
|
let imageSelection: SelectionBookmark | undefined;
|
|
31
|
+
let toolbar: HTMLDivElement | undefined;
|
|
32
|
+
let floating = $state("");
|
|
33
|
+
let linkPosition = $state("");
|
|
34
|
+
let releaseFloating: (() => void) | undefined;
|
|
35
|
+
const inline = $derived(!!editableRoot);
|
|
36
|
+
function positionFloating() {
|
|
37
|
+
if (!inline || !editableRoot || !view || mode !== "visual") { floating = ""; linkPosition = ""; return; }
|
|
38
|
+
const owner = editableRoot.ownerDocument;
|
|
39
|
+
const active = owner.activeElement;
|
|
40
|
+
if (!editableRoot.contains(active) && !toolbar?.contains(active)) { floating = ""; linkPosition = ""; return; }
|
|
41
|
+
const anchor = view.state.selection.$from;
|
|
42
|
+
const block = anchor.depth > 0 ? view.nodeDOM(anchor.before(1)) : null;
|
|
43
|
+
const rect = block instanceof HTMLElement ? block.getBoundingClientRect() : editableRoot.getBoundingClientRect();
|
|
44
|
+
const { innerWidth, innerHeight } = owner.defaultView!;
|
|
45
|
+
if (rect.bottom < 0 || rect.top > innerHeight) { floating = ""; linkPosition = ""; return; }
|
|
46
|
+
const size = toolbar?.getBoundingClientRect();
|
|
47
|
+
const height = size?.height || 44, width = size?.width || 360;
|
|
48
|
+
const top = Math.max(8, Math.min(rect.top >= height + 12 ? rect.top - height - 8 : rect.bottom + 8, innerHeight - height - 8));
|
|
49
|
+
const left = Math.max(8, Math.min(rect.left, innerWidth - width - 8));
|
|
50
|
+
floating = `left:${left}px;top:${top}px`;
|
|
51
|
+
linkPosition = `left:${Math.max(8, Math.min(left, innerWidth - 356))}px;top:${Math.max(8, Math.min(top + height + 4, innerHeight - 128))}px`;
|
|
52
|
+
}
|
|
31
53
|
function insertImage(url: string, alt: string, credit?: StockResult) {
|
|
32
54
|
if (!view || !imageSelection || !safeImageUrl(url)) return;
|
|
33
55
|
let tr = view.state.tr.setSelection(imageSelection.resolve(view.state.doc));
|
|
@@ -76,6 +98,7 @@
|
|
|
76
98
|
state = view!.state.apply(transaction);
|
|
77
99
|
view!.updateState(state);
|
|
78
100
|
if (transaction.docChanged) emit(serializeMarkdown(state.doc));
|
|
101
|
+
positionFloating();
|
|
79
102
|
},
|
|
80
103
|
handlePaste(editor, event) {
|
|
81
104
|
event.preventDefault();
|
|
@@ -85,8 +108,14 @@
|
|
|
85
108
|
},
|
|
86
109
|
handleDrop(_editor, _event, _slice, moved) { return !moved; },
|
|
87
110
|
handleClickOn(_editor, _pos, node) { return node.type.name === "image"; },
|
|
88
|
-
handleDOMEvents: { click: (_editor, event) => { if ((event.target as Element).closest("a")) { event.preventDefault(); return true; } return false; } },
|
|
111
|
+
handleDOMEvents: { click: (_editor, event) => { if ((event.target as Element).closest("a")) { event.preventDefault(); return true; } return false; }, focus: () => { positionFloating(); return false; }, blur: () => { setTimeout(positionFloating); return false; } },
|
|
89
112
|
});
|
|
113
|
+
const target = editableRoot ?? host;
|
|
114
|
+
const owner = target.ownerDocument;
|
|
115
|
+
const reposition = () => positionFloating();
|
|
116
|
+
owner.defaultView?.addEventListener("scroll", reposition, true);
|
|
117
|
+
owner.defaultView?.addEventListener("resize", reposition);
|
|
118
|
+
releaseFloating = () => { owner.defaultView?.removeEventListener("scroll", reposition, true); owner.defaultView?.removeEventListener("resize", reposition); };
|
|
90
119
|
}
|
|
91
120
|
if (value !== lastValue) {
|
|
92
121
|
lastValue = value;
|
|
@@ -94,7 +123,8 @@
|
|
|
94
123
|
}
|
|
95
124
|
});
|
|
96
125
|
$effect(() => { const editable = mode === "visual"; view?.setProps({ editable: () => editable }); });
|
|
97
|
-
|
|
126
|
+
$effect(() => { void mode; void inline; positionFloating(); });
|
|
127
|
+
onDestroy(() => { releaseFloating?.(); view?.destroy(); });
|
|
98
128
|
|
|
99
129
|
function activeMark(type: string) {
|
|
100
130
|
if (!state) return false;
|
|
@@ -164,7 +194,8 @@
|
|
|
164
194
|
|
|
165
195
|
<svelte:window onpointerdown={event => { if (linkOpen && !document.getElementById(`${id}-link`)?.contains(event.target as Node) && !linkButton?.contains(event.target as Node)) linkOpen = false; }} />
|
|
166
196
|
|
|
167
|
-
<div class="markdown-editor body-editor" class:markdown-inline={
|
|
197
|
+
<div class="markdown-editor body-editor" class:markdown-inline={inline} class:markdown-floating={inline && mode === "visual"}>
|
|
198
|
+
{#if !inline || mode === "source"}
|
|
168
199
|
<div class="markdown-heading">
|
|
169
200
|
<div class="field-label" id={`${id}-label`}>{label}</div>
|
|
170
201
|
<div class="markdown-modes" role="group" aria-label={$t("richtext.editingMode", { label })}>
|
|
@@ -172,9 +203,10 @@
|
|
|
172
203
|
<button type="button" aria-pressed={mode === "source"} onclick={() => { mode = "source"; linkOpen = false; }}>{$t("richtext.markdown")}</button>
|
|
173
204
|
</div>
|
|
174
205
|
</div>
|
|
206
|
+
{/if}
|
|
175
207
|
{#if warning}<p class="markdown-warning" role="status">{warning}</p>{/if}
|
|
176
208
|
<div class="markdown-visual" hidden={mode !== "visual"}>
|
|
177
|
-
<div class="body-toolbar formatting-toolbar" role="group" aria-label={$t("richtext.formatting", { label })}>
|
|
209
|
+
<div class="body-toolbar formatting-toolbar" bind:this={toolbar} role="group" aria-label={$t("richtext.formatting", { label })} style={inline ? floating : undefined} hidden={inline && !floating}>
|
|
178
210
|
<Select ariaLabel={$t("richtext.textStyle")} data-tooltip={$t("richtext.textStyle")} value={format} options={[{ value: "paragraph", label: $t("richtext.paragraph") }, ...[1, 2, 3, 4, 5, 6].map(level => ({ value: `h${level}`, label: $t("richtext.heading", { level }) })), { value: "code_block", label: $t("richtext.codeBlock") }].map(option => ({ ...option, disabled: !!state && format !== option.value && !formatCommand(option.value)(state) }))} onchange={next => command(formatCommand(String(next)))} />
|
|
179
211
|
<div class="format-group" role="group" aria-label={$t("richtext.textFormatting")}>
|
|
180
212
|
{#each marks as item}
|
|
@@ -188,9 +220,10 @@
|
|
|
188
220
|
</div>
|
|
189
221
|
<button bind:this={linkButton} type="button" aria-label={$t("richtext.link")} data-tooltip={$t("richtext.link")} aria-pressed={activeMark("link")} aria-expanded={linkOpen} aria-controls={`${id}-link`} disabled={!state || !toggleMark(schema.marks.link)(state)} onmousedown={event => event.preventDefault()} onclick={openLink}><Icon name="link" size={18} /></button>
|
|
190
222
|
<ImagePicker toolbar onopen={() => { imageSelection = view?.state.selection.getBookmark(); linkOpen = false; }} onpick={insertImage} onclose={() => { imageSelection = undefined; }} />
|
|
223
|
+
{#if inline}<button type="button" class="markdown-toggle" aria-label={$t("richtext.markdown")} data-tooltip={$t("richtext.markdown")} onmousedown={event => event.preventDefault()} onclick={() => { mode = "source"; linkOpen = false; }}>{$t("richtext.markdown")}</button>{/if}
|
|
191
224
|
</div>
|
|
192
225
|
{#if linkOpen}
|
|
193
|
-
<div class="markdown-link" id={`${id}-link`} role="group" aria-label={$t("richtext.editLink")} onkeydown={event => { if (event.key === "Escape") { event.stopPropagation(); linkOpen = false; linkButton.focus(); } if (event.key === "Enter") { event.preventDefault(); link(); } }}>
|
|
226
|
+
<div class="markdown-link" id={`${id}-link`} role="group" aria-label={$t("richtext.editLink")} style={inline ? linkPosition : undefined} onkeydown={event => { if (event.key === "Escape") { event.stopPropagation(); linkOpen = false; linkButton.focus(); } if (event.key === "Enter") { event.preventDefault(); link(); } }}>
|
|
194
227
|
<label for={`${id}-url`}>{$t("richtext.linkUrl")}</label>
|
|
195
228
|
<div class="markdown-link-controls">
|
|
196
229
|
<input id={`${id}-url`} type="text" placeholder="https://…" bind:this={linkInput} bind:value={linkUrl} aria-invalid={!!linkError} aria-describedby={linkError ? `${id}-error` : undefined} />
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { t } from "../i18n";
|
|
3
3
|
let { tab = $bindable("content") }: { tab?: "content" | "seo" } = $props();
|
|
4
|
+
let tabs: HTMLDivElement;
|
|
5
|
+
let height = $state(0);
|
|
6
|
+
$effect(() => {
|
|
7
|
+
const panel = tabs?.parentElement;
|
|
8
|
+
panel?.style.setProperty("--properties-tabs-height", `${height}px`);
|
|
9
|
+
return () => panel?.style.removeProperty("--properties-tabs-height");
|
|
10
|
+
});
|
|
4
11
|
function onkey(event: KeyboardEvent) {
|
|
5
12
|
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
|
|
6
13
|
event.preventDefault();
|
|
@@ -9,7 +16,7 @@
|
|
|
9
16
|
}
|
|
10
17
|
</script>
|
|
11
18
|
|
|
12
|
-
<div class="panel-tabs" role="tablist" aria-label={$t("fields.properties")}>
|
|
19
|
+
<div class="panel-tabs" role="tablist" aria-label={$t("fields.properties")} bind:this={tabs} bind:offsetHeight={height}>
|
|
13
20
|
{#each ["content", "seo"] as value}
|
|
14
21
|
<button type="button" role="tab" id={`properties-tab-${value}`} aria-controls={`properties-${value}`} aria-selected={tab === value} tabindex={tab === value ? 0 : -1} data-tab={value} onclick={() => (tab = value as "content" | "seo")} onkeydown={onkey}>{$t(value === "content" ? "fields.content" : "fields.seo")}</button>
|
|
15
22
|
{/each}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type { ContentDocument, FieldDef, Schema } from "../../schema/types";
|
|
19
19
|
|
|
20
20
|
let {
|
|
21
|
-
schema, field, value, base, documents, devMode = false, onvalue,
|
|
21
|
+
schema, field, value, base, documents, devMode = false, inlineFields = [], oneditfield = () => {}, onvalue,
|
|
22
22
|
}: {
|
|
23
23
|
schema: Schema;
|
|
24
24
|
field: FieldDef;
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
base: string;
|
|
27
27
|
documents: ContentDocument[];
|
|
28
28
|
devMode?: boolean;
|
|
29
|
+
inlineFields?: string[];
|
|
30
|
+
oneditfield?: (path: string) => void;
|
|
29
31
|
onvalue: (next: unknown) => void;
|
|
30
32
|
} = $props();
|
|
31
33
|
|
|
@@ -85,7 +87,15 @@
|
|
|
85
87
|
<textarea rows="3" value={typeof value === "string" ? value : ""} oninput={(event) => onvalue(event.currentTarget.value)}></textarea>
|
|
86
88
|
</label>
|
|
87
89
|
{:else if field.type === "markdown"}
|
|
88
|
-
|
|
90
|
+
{#if !field.sidebarOnly && inlineFields.includes(base)}
|
|
91
|
+
<div class="schema-field">
|
|
92
|
+
<span class="schema-label">{label}</span>
|
|
93
|
+
<button type="button" class="outlined" onclick={() => oneditfield(base)}>{$t("fields.editRichText")}</button>
|
|
94
|
+
<p class="hint">{$t("fields.editRichTextHint")}</p>
|
|
95
|
+
</div>
|
|
96
|
+
{:else}
|
|
97
|
+
<MarkdownEditor label={fieldLabel} value={typeof value === "string" ? value : ""} oninput={(next) => onvalue(next)} />
|
|
98
|
+
{/if}
|
|
89
99
|
{:else if field.type === "image" || field.type === "file"}
|
|
90
100
|
<div class="schema-field">
|
|
91
101
|
<span class="schema-label">{label}</span>
|
|
@@ -113,6 +123,8 @@
|
|
|
113
123
|
{schema}
|
|
114
124
|
{documents}
|
|
115
125
|
{devMode}
|
|
126
|
+
{inlineFields}
|
|
127
|
+
{oneditfield}
|
|
116
128
|
field={sub}
|
|
117
129
|
base={`${base}.${sub.key}`}
|
|
118
130
|
value={value && typeof value === "object" ? (value as Record<string, unknown>)[sub.key] : undefined}
|
|
@@ -141,6 +153,8 @@
|
|
|
141
153
|
{schema}
|
|
142
154
|
{documents}
|
|
143
155
|
{devMode}
|
|
156
|
+
{inlineFields}
|
|
157
|
+
{oneditfield}
|
|
144
158
|
field={sub}
|
|
145
159
|
base={`${base}.${(item as Record<string, unknown>)._key}.${sub.key}`}
|
|
146
160
|
value={item && typeof item === "object" ? (item as Record<string, unknown>)[sub.key] : undefined}
|