Release 0.5.3
This commit is contained in:
1 parent
25da870601
commit
f466df2c5c
13 files changed
+1367
-771
No files matched your search
@@ -1,60 +0,0 @@
|
||||
<script lang="ts">
|
||||
import MarkdownPreview from './MarkdownPreview.svelte';
|
||||
|
||||
let {
|
||||
name,
|
||||
label,
|
||||
inputLabel = label,
|
||||
hint = '',
|
||||
emptyText,
|
||||
value = $bindable(),
|
||||
html,
|
||||
privateField = false,
|
||||
rows = 7
|
||||
}: {
|
||||
name: string;
|
||||
label: string;
|
||||
inputLabel?: string;
|
||||
hint?: string;
|
||||
emptyText: string;
|
||||
value: string;
|
||||
html: string;
|
||||
privateField?: boolean;
|
||||
rows?: number;
|
||||
} = $props();
|
||||
|
||||
let editing = $state(false);
|
||||
</script>
|
||||
|
||||
<section class:private-field={privateField} class:empty={!value.trim()} class="editorial-field">
|
||||
<header>
|
||||
<div>
|
||||
<h3>{label}</h3>
|
||||
{#if hint}<p>{hint}</p>{/if}
|
||||
</div>
|
||||
<button
|
||||
class="text-button editorial-edit-button"
|
||||
type="button"
|
||||
aria-expanded={editing}
|
||||
onclick={() => (editing = !editing)}
|
||||
>
|
||||
{editing ? 'Lesen' : value.trim() ? 'Bearbeiten' : 'Schreiben'}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{#if editing}
|
||||
<label class="editorial-input">
|
||||
<span class="sr-only">{inputLabel}</span>
|
||||
<textarea aria-label={inputLabel} {name} {rows} maxlength="50000" bind:value></textarea>
|
||||
</label>
|
||||
{:else}
|
||||
<input type="hidden" {name} {value} />
|
||||
{#if value.trim()}
|
||||
<MarkdownPreview {html} />
|
||||
{:else}
|
||||
<button class="editorial-empty" type="button" onclick={() => (editing = true)}>
|
||||
<span>{emptyText}</span><small>Zum Schreiben öffnen</small>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</section>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { tick } from 'svelte';
|
||||
|
||||
let {
|
||||
name,
|
||||
label,
|
||||
value = $bindable(),
|
||||
rows = 7,
|
||||
placeholder = '',
|
||||
privateField = false,
|
||||
form
|
||||
}: {
|
||||
name: string;
|
||||
label: string;
|
||||
value: string;
|
||||
rows?: number;
|
||||
placeholder?: string;
|
||||
privateField?: boolean;
|
||||
form?: string;
|
||||
} = $props();
|
||||
|
||||
let textarea: HTMLTextAreaElement;
|
||||
|
||||
async function replaceSelection(before: string, after = before, fallback = 'Text') {
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const selected = value.slice(start, end) || fallback;
|
||||
value = `${value.slice(0, start)}${before}${selected}${after}${value.slice(end)}`;
|
||||
await tick();
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(start + before.length, start + before.length + selected.length);
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
|
||||
async function addList() {
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const selected = value.slice(start, end) || 'Stichpunkt';
|
||||
const replacement = selected
|
||||
.split('\n')
|
||||
.map((line) => `- ${line}`)
|
||||
.join('\n');
|
||||
value = `${value.slice(0, start)}${replacement}${value.slice(end)}`;
|
||||
await tick();
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(start + 2, start + replacement.length);
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class:private-field={privateField} class="markdown-editor-field">
|
||||
<div class="markdown-editor-heading">
|
||||
<span
|
||||
>{label}{#if privateField}<small>Nur intern</small>{/if}</span
|
||||
>
|
||||
<span class="markdown-toolbar" role="toolbar" aria-label={`${label} formatieren`}>
|
||||
<button type="button" aria-label="Fett" title="Fett" onclick={() => replaceSelection('**')}
|
||||
><strong>B</strong></button
|
||||
>
|
||||
<button type="button" aria-label="Kursiv" title="Kursiv" onclick={() => replaceSelection('_')}
|
||||
><em>I</em></button
|
||||
>
|
||||
<button type="button" aria-label="Liste" title="Liste" onclick={addList}>• Liste</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Link"
|
||||
title="Link"
|
||||
onclick={() => replaceSelection('[', '](https://)', 'Linktext')}>↗ Link</button
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<textarea
|
||||
bind:this={textarea}
|
||||
aria-label={label}
|
||||
{name}
|
||||
{rows}
|
||||
{placeholder}
|
||||
{form}
|
||||
maxlength="50000"
|
||||
bind:value></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { tick, type Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
labelledby,
|
||||
activeSection = $bindable(),
|
||||
onrequestclose,
|
||||
header,
|
||||
children,
|
||||
footer
|
||||
}: {
|
||||
labelledby: string;
|
||||
activeSection: 'script' | 'research' | 'presentation';
|
||||
onrequestclose: () => void;
|
||||
header: Snippet;
|
||||
children: Snippet;
|
||||
footer: Snippet;
|
||||
} = $props();
|
||||
|
||||
let dialog: HTMLDialogElement;
|
||||
let workspace: HTMLDivElement;
|
||||
const scrollPositions = { script: 0, research: 0, presentation: 0 };
|
||||
|
||||
async function selectSection(section: typeof activeSection) {
|
||||
scrollPositions[activeSection] = workspace.scrollTop;
|
||||
activeSection = section;
|
||||
await tick();
|
||||
workspace.scrollTop = scrollPositions[section];
|
||||
}
|
||||
|
||||
export function showModal() {
|
||||
if (!dialog.open) dialog.showModal();
|
||||
}
|
||||
|
||||
export function close() {
|
||||
dialog.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog
|
||||
bind:this={dialog}
|
||||
class="piece-editor-dialog"
|
||||
aria-labelledby={labelledby}
|
||||
oncancel={(event) => {
|
||||
event.preventDefault();
|
||||
onrequestclose();
|
||||
}}
|
||||
onclick={(event) => event.target === dialog && onrequestclose()}
|
||||
>
|
||||
<header class="piece-dialog-header">
|
||||
<div class="piece-dialog-heading">{@render header()}</div>
|
||||
<button
|
||||
class="dialog-close"
|
||||
type="button"
|
||||
aria-label="Editor schließen"
|
||||
onclick={onrequestclose}>×</button
|
||||
>
|
||||
</header>
|
||||
<nav class="piece-dialog-tabs" aria-label="Editorbereiche">
|
||||
<button
|
||||
type="button"
|
||||
class:active={activeSection === 'script'}
|
||||
aria-current={activeSection === 'script' ? 'page' : undefined}
|
||||
onclick={() => selectSection('script')}>Sendungstext</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class:active={activeSection === 'research'}
|
||||
aria-current={activeSection === 'research' ? 'page' : undefined}
|
||||
onclick={() => selectSection('research')}>Recherche & Quellen</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class:active={activeSection === 'presentation'}
|
||||
aria-current={activeSection === 'presentation' ? 'page' : undefined}
|
||||
onclick={() => selectSection('presentation')}>Präsentation</button
|
||||
>
|
||||
</nav>
|
||||
<div bind:this={workspace} class="piece-dialog-workspace">{@render children()}</div>
|
||||
<footer class="piece-dialog-footer">{@render footer()}</footer>
|
||||
</dialog>
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { enhance, type SubmitFunction } from '$app/forms';
|
||||
import { untrack } from 'svelte';
|
||||
import MarkdownEditorField from './MarkdownEditorField.svelte';
|
||||
import NavIcon from './NavIcon.svelte';
|
||||
import PieceEditorDialog from './PieceEditorDialog.svelte';
|
||||
import type { PieceUsage } from '#lib/server/shows.js';
|
||||
|
||||
let {
|
||||
@@ -19,12 +21,30 @@
|
||||
error?: string;
|
||||
enhanceCommand: SubmitFunction;
|
||||
} = $props();
|
||||
let dialog: HTMLDialogElement;
|
||||
let presentationMode = $state(untrack(() => usage.presentationMode));
|
||||
|
||||
let formId = $derived(`usage-editor-form-${usage.id}`);
|
||||
let editor: { showModal: () => void; close: () => void };
|
||||
let activeSection = $state<'script' | 'research' | 'presentation'>('script');
|
||||
let draft = $state(untrack(() => ({ ...usage })));
|
||||
let dirty = $state(false);
|
||||
|
||||
function durationInMinutes(seconds: number) {
|
||||
return Number((seconds / 60).toFixed(2));
|
||||
}
|
||||
|
||||
function requestClose() {
|
||||
if (dirty && !confirm('Ungespeicherte Änderungen verwerfen und Editor schließen?')) return;
|
||||
dirty = false;
|
||||
editor.close();
|
||||
}
|
||||
|
||||
const enhanceEditor: SubmitFunction = async (input) => {
|
||||
const finish = await enhanceCommand(input);
|
||||
return async (result) => {
|
||||
if (finish) await finish(result);
|
||||
if (result.result.type === 'success') dirty = false;
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<button
|
||||
@@ -32,45 +52,79 @@
|
||||
type="button"
|
||||
aria-label={`${usage.title} bearbeiten und verschieben`}
|
||||
title="Bearbeiten und verschieben"
|
||||
onclick={() => dialog.showModal()}
|
||||
onclick={() => editor.showModal()}
|
||||
>
|
||||
<NavIcon name="edit" />
|
||||
</button>
|
||||
|
||||
<dialog
|
||||
bind:this={dialog}
|
||||
class="usage-editor-dialog"
|
||||
aria-labelledby={`usage-editor-title-${usage.id}`}
|
||||
onclick={(event) => event.target === dialog && dialog.close()}
|
||||
<PieceEditorDialog
|
||||
bind:this={editor}
|
||||
bind:activeSection
|
||||
labelledby={`usage-editor-title-${usage.id}`}
|
||||
onrequestclose={requestClose}
|
||||
>
|
||||
<header>
|
||||
<div>
|
||||
<p class="eyebrow">Sendungsspezifische Kopie</p>
|
||||
<h2 id={`usage-editor-title-${usage.id}`}>{usage.title}</h2>
|
||||
</div>
|
||||
<form method="dialog">
|
||||
<button class="dialog-close" type="submit" aria-label="Editor schließen">×</button>
|
||||
</form>
|
||||
</header>
|
||||
<div class="usage-dialog-scroll">
|
||||
{#if error}<div class="form-message error usage-dialog-message" role="alert">{error}</div>{/if}
|
||||
<form
|
||||
use:enhance={enhanceCommand}
|
||||
method="POST"
|
||||
action="?/updateUsage"
|
||||
class="usage-dialog-editor"
|
||||
>
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
<section class="editor-section">
|
||||
<div class="section-title">
|
||||
<p class="eyebrow">Grundlagen</p>
|
||||
<h3>Titel, Dauer und Notizen</h3>
|
||||
{#snippet header()}
|
||||
<p class="eyebrow">Verwendung in dieser Sendung</p>
|
||||
<label class="piece-dialog-title">
|
||||
<span class="sr-only">Titel</span>
|
||||
<textarea
|
||||
form={formId}
|
||||
name="title"
|
||||
required
|
||||
rows="1"
|
||||
maxlength="200"
|
||||
aria-label="Titel"
|
||||
bind:value={draft.title}
|
||||
oninput={() => (dirty = true)}></textarea>
|
||||
</label>
|
||||
<h2 class="sr-only" id={`usage-editor-title-${usage.id}`}>{draft.title}</h2>
|
||||
<small>Änderungen gelten nur für diese Einplanung.</small>
|
||||
{/snippet}
|
||||
|
||||
<form
|
||||
id={formId}
|
||||
use:enhance={enhanceEditor}
|
||||
method="POST"
|
||||
action="?/updateUsage"
|
||||
class="piece-dialog-form"
|
||||
oninput={() => (dirty = true)}
|
||||
onchange={() => (dirty = true)}
|
||||
>
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
|
||||
<section class="piece-editor-panel" hidden={activeSection !== 'script'}>
|
||||
<div class="piece-panel-heading">
|
||||
<div>
|
||||
<p class="eyebrow">Für die Moderation</p>
|
||||
<h3>Sendungstext</h3>
|
||||
</div>
|
||||
<div class="editor-fields two-columns">
|
||||
<label class="wide-field"
|
||||
>Titel<input name="title" required maxlength="200" value={usage.title} /></label
|
||||
>
|
||||
<small>Die drei Teile bleiben getrennt gespeichert.</small>
|
||||
</div>
|
||||
<div class="piece-writing-stack">
|
||||
<MarkdownEditorField
|
||||
name="intro"
|
||||
label="Einleitung"
|
||||
placeholder="Wie beginnt dieses Thema in der Sendung?"
|
||||
bind:value={draft.intro}
|
||||
/>
|
||||
<MarkdownEditorField
|
||||
name="moderation"
|
||||
label="Moderationsstichpunkte"
|
||||
placeholder="Argumente, Fragen und Ablaufpunkte"
|
||||
rows={10}
|
||||
bind:value={draft.moderation}
|
||||
/>
|
||||
<MarkdownEditorField
|
||||
name="transition"
|
||||
label="Überleitung"
|
||||
placeholder="Wie geht es zum nächsten Thema weiter?"
|
||||
bind:value={draft.transition}
|
||||
/>
|
||||
</div>
|
||||
<details class="piece-meta-details">
|
||||
<summary>Dauer und Einplanung</summary>
|
||||
<div class="piece-meta-grid">
|
||||
<label
|
||||
>Dauer in Minuten<input
|
||||
name="durationMinutes"
|
||||
@@ -81,136 +135,182 @@
|
||||
value={durationInMinutes(usage.durationSeconds)}
|
||||
/></label
|
||||
>
|
||||
<label class="wide-field"
|
||||
>Eigene Notizen<textarea name="ownNotes" rows="3" maxlength="50000"
|
||||
>{usage.ownNotes}</textarea
|
||||
></label
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="editor-section markdown-section">
|
||||
<div class="section-title">
|
||||
<p class="eyebrow">Redaktion</p>
|
||||
<h3>Getrennte Arbeitsbereiche</h3>
|
||||
<section class="piece-editor-panel" hidden={activeSection !== 'research'}>
|
||||
<div class="piece-panel-heading">
|
||||
<div>
|
||||
<p class="eyebrow">Nicht öffentlich</p>
|
||||
<h3>Recherche & Quellen</h3>
|
||||
</div>
|
||||
<div class="markdown-grid">
|
||||
<label
|
||||
><span>Einleitung</span><textarea name="intro" rows="7" maxlength="50000"
|
||||
>{usage.intro}</textarea
|
||||
></label
|
||||
>
|
||||
<label class="private-field"
|
||||
><span>Private Recherche</span><textarea name="research" rows="7" maxlength="50000"
|
||||
>{usage.research}</textarea
|
||||
></label
|
||||
>
|
||||
<label
|
||||
><span>Moderationsstichpunkte</span><textarea
|
||||
name="moderation"
|
||||
rows="7"
|
||||
maxlength="50000">{usage.moderation}</textarea
|
||||
></label
|
||||
>
|
||||
<label
|
||||
><span>Überleitung</span><textarea name="transition" rows="7" maxlength="50000"
|
||||
>{usage.transition}</textarea
|
||||
></label
|
||||
>
|
||||
<label class="wide-field"
|
||||
><span>Präsentationstext</span><textarea name="presentation" rows="7" maxlength="50000"
|
||||
>{usage.presentation}</textarea
|
||||
></label
|
||||
>
|
||||
<small>Diese Inhalte bleiben innerhalb der Redaktion.</small>
|
||||
</div>
|
||||
<div class="piece-writing-stack">
|
||||
<MarkdownEditorField
|
||||
name="research"
|
||||
label="Private Recherche"
|
||||
placeholder="Hintergrund, Prüfung und offene Fragen"
|
||||
rows={10}
|
||||
privateField
|
||||
bind:value={draft.research}
|
||||
/>
|
||||
<MarkdownEditorField
|
||||
name="ownNotes"
|
||||
label="Interne Notizen zu dieser Verwendung"
|
||||
placeholder="Absprachen und Hinweise nur für diese Sendung"
|
||||
privateField
|
||||
bind:value={draft.ownNotes}
|
||||
/>
|
||||
</div>
|
||||
<section class="piece-source-summary">
|
||||
<p class="eyebrow">Snapshot-Quelle</p>
|
||||
{#if draft.presentationSourceUrl}
|
||||
<strong>{draft.presentationSourceLabel || draft.presentationSourceUrl}</strong>
|
||||
<small>{draft.presentationSourceUrl}</small>
|
||||
{:else}
|
||||
<p>Für diese Verwendung wurde keine Präsentationsquelle kopiert.</p>
|
||||
{/if}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section class="piece-editor-panel" hidden={activeSection !== 'presentation'}>
|
||||
<div class="piece-panel-heading">
|
||||
<div>
|
||||
<p class="eyebrow">Öffentliche Ausgabe</p>
|
||||
<h3>Präsentation</h3>
|
||||
</div>
|
||||
<div class="presentation-settings">
|
||||
<label
|
||||
>Darstellung<select name="presentationMode" bind:value={presentationMode}>
|
||||
<option value="text">Text</option>
|
||||
<option value="link">Linkvorschau</option>
|
||||
<option value="video">Video</option>
|
||||
</select></label
|
||||
>
|
||||
{#if presentationMode !== 'text'}
|
||||
<input
|
||||
type="hidden"
|
||||
name="presentationSourceLabel"
|
||||
value={usage.presentationSourceLabel}
|
||||
/>
|
||||
<input type="hidden" name="presentationSourceUrl" value={usage.presentationSourceUrl} />
|
||||
<input
|
||||
type="hidden"
|
||||
name="presentationSourceTimecodeSeconds"
|
||||
value={usage.presentationSourceTimecodeSeconds ?? ''}
|
||||
/>
|
||||
<input type="hidden" name="presentationSiteName" value={usage.presentationSiteName} />
|
||||
<div class="presentation-source-summary wide-field">
|
||||
<span>Ausgewählte Quelle</span>
|
||||
<strong
|
||||
>{usage.presentationSourceLabel ||
|
||||
usage.presentationSourceUrl ||
|
||||
'Keine Quelle'}</strong
|
||||
<small>Private Recherche und Notizen werden nicht ausgegeben.</small>
|
||||
</div>
|
||||
<div class="piece-presentation-layout">
|
||||
<div class="piece-presentation-controls">
|
||||
<MarkdownEditorField
|
||||
name="presentation"
|
||||
label="Öffentlicher Text"
|
||||
placeholder="Text für die Publikumsausgabe"
|
||||
bind:value={draft.presentation}
|
||||
/>
|
||||
<div class="presentation-mode-picker" role="group" aria-label="Darstellung">
|
||||
{#each [['text', 'Text'], ['link', 'Linkvorschau'], ['video', 'Video']] as mode (mode[0])}
|
||||
<label
|
||||
><input
|
||||
type="radio"
|
||||
name="presentationMode"
|
||||
value={mode[0]}
|
||||
bind:group={draft.presentationMode}
|
||||
/>{mode[1]}</label
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
<input
|
||||
type="hidden"
|
||||
name="presentationSourceLabel"
|
||||
value={draft.presentationSourceLabel}
|
||||
/>
|
||||
<input type="hidden" name="presentationSourceUrl" value={draft.presentationSourceUrl} />
|
||||
<input
|
||||
type="hidden"
|
||||
name="presentationSourceTimecodeSeconds"
|
||||
value={draft.presentationSourceTimecodeSeconds ?? ''}
|
||||
/>
|
||||
<input type="hidden" name="presentationSiteName" value={draft.presentationSiteName} />
|
||||
{#if draft.presentationMode !== 'text'}
|
||||
<div class="presentation-settings">
|
||||
<div class="presentation-source-summary wide-field">
|
||||
<span>Ausgewählte Quelle</span>
|
||||
<strong
|
||||
>{draft.presentationSourceLabel ||
|
||||
draft.presentationSourceUrl ||
|
||||
'Keine Quelle'}</strong
|
||||
>
|
||||
{#if draft.presentationSourceUrl}<small>{draft.presentationSourceUrl}</small>{/if}
|
||||
</div>
|
||||
<label
|
||||
>Titel<input
|
||||
name="presentationTitle"
|
||||
maxlength="300"
|
||||
bind:value={draft.presentationTitle}
|
||||
/></label
|
||||
>
|
||||
<label
|
||||
>Bild-URL<input
|
||||
name="presentationImageUrl"
|
||||
type="url"
|
||||
maxlength="2000"
|
||||
bind:value={draft.presentationImageUrl}
|
||||
/></label
|
||||
>
|
||||
<label class="wide-field"
|
||||
>Anreißer<textarea
|
||||
name="presentationDescription"
|
||||
rows="3"
|
||||
maxlength="1000"
|
||||
bind:value={draft.presentationDescription}></textarea></label
|
||||
>
|
||||
{#if usage.presentationSourceUrl}<small>{usage.presentationSourceUrl}</small>{/if}
|
||||
</div>
|
||||
<label
|
||||
>Titel<input
|
||||
name="presentationTitle"
|
||||
maxlength="300"
|
||||
value={usage.presentationTitle}
|
||||
/></label
|
||||
>
|
||||
<label
|
||||
>Bild-URL<input
|
||||
name="presentationImageUrl"
|
||||
type="url"
|
||||
maxlength="2000"
|
||||
value={usage.presentationImageUrl}
|
||||
/></label
|
||||
>
|
||||
<label class="wide-field"
|
||||
>Anreißer<textarea name="presentationDescription" rows="3" maxlength="1000"
|
||||
>{usage.presentationDescription}</textarea
|
||||
></label
|
||||
>
|
||||
{:else}
|
||||
<input type="hidden" name="presentationTitle" value={draft.presentationTitle} />
|
||||
<input
|
||||
type="hidden"
|
||||
name="presentationDescription"
|
||||
value={draft.presentationDescription}
|
||||
/>
|
||||
<input type="hidden" name="presentationImageUrl" value={draft.presentationImageUrl} />
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<button class="primary-button" type="submit">Verwendung speichern</button>
|
||||
</form>
|
||||
|
||||
<section class="usage-dialog-actions" aria-label="Weitere Aktionen">
|
||||
<div>
|
||||
<form use:enhance={enhanceCommand} method="POST" action="?/refreshUsage">
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
<button class="secondary-button" type="submit" disabled={!usage.originalPieceId}
|
||||
>Aus Fundus aktualisieren</button
|
||||
>
|
||||
</form>
|
||||
<small
|
||||
>Übernimmt Titel, Dauer und Redaktionstexte. Eigene Notizen und Live-Daten bleiben.</small
|
||||
>
|
||||
<aside class="piece-presentation-preview" aria-label="Präsentationsvorschau">
|
||||
<p class="output-kicker">Vorschau · {draft.presentationMode}</p>
|
||||
<h4>{draft.presentationTitle || draft.title}</h4>
|
||||
{#if draft.presentationMode === 'text'}
|
||||
<p class="preview-markdown-source">
|
||||
{draft.presentation || 'Noch kein öffentlicher Text.'}
|
||||
</p>
|
||||
{:else}
|
||||
{#if draft.presentationImageUrl}<img src={draft.presentationImageUrl} alt="" />{/if}
|
||||
<p>
|
||||
{draft.presentationDescription ||
|
||||
draft.presentationSourceUrl ||
|
||||
'Noch keine Vorschauangaben.'}
|
||||
</p>
|
||||
{/if}
|
||||
</aside>
|
||||
</div>
|
||||
<form use:enhance={enhanceCommand} method="POST" action="?/duplicateUsage">
|
||||
</section>
|
||||
</form>
|
||||
|
||||
{#snippet footer()}
|
||||
<div class="piece-dialog-secondary-actions">
|
||||
<form use:enhance={enhanceCommand} method="POST" action="?/refreshUsage">
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
<button class="secondary-button" type="submit">Duplizieren</button>
|
||||
<button class="text-button" type="submit" disabled={!usage.originalPieceId || dirty}
|
||||
>Aus Fundus aktualisieren</button
|
||||
>
|
||||
</form>
|
||||
<form use:enhance={enhanceCommand} method="POST" action="?/duplicateUsage">
|
||||
<input type="hidden" name="revision" value={revision} /><input
|
||||
type="hidden"
|
||||
name="usageId"
|
||||
value={usage.id}
|
||||
/>
|
||||
<button class="text-button" type="submit" disabled={dirty}>Duplizieren</button>
|
||||
</form>
|
||||
<form use:enhance={enhanceCommand} method="POST" action="?/moveUsage">
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
<input type="hidden" name="position" value="9999" />
|
||||
<input type="hidden" name="revision" value={revision} /><input
|
||||
type="hidden"
|
||||
name="usageId"
|
||||
value={usage.id}
|
||||
/><input type="hidden" name="position" value="9999" />
|
||||
<label
|
||||
>Ziel<select name="sectionId">
|
||||
<option value="">Reservebank</option>
|
||||
{#each sections as target (target.id)}
|
||||
<option value={target.id} selected={target.id === sectionId}>{target.name}</option>
|
||||
{/each}
|
||||
</select></label
|
||||
><span class="sr-only">Ziel</span><select name="sectionId"
|
||||
><option value="">Reservebank</option>{#each sections as target (target.id)}<option
|
||||
value={target.id}
|
||||
selected={target.id === sectionId}>{target.name}</option
|
||||
>{/each}</select
|
||||
></label
|
||||
>
|
||||
<button class="secondary-button" type="submit">Verschieben</button>
|
||||
<button class="text-button" type="submit" disabled={dirty}>Verschieben</button>
|
||||
</form>
|
||||
<form
|
||||
use:enhance={enhanceCommand}
|
||||
@@ -219,10 +319,17 @@
|
||||
onsubmit={(event) =>
|
||||
!confirm('Verwendung aus der Sendung entfernen?') && event.preventDefault()}
|
||||
>
|
||||
<input type="hidden" name="revision" value={revision} />
|
||||
<input type="hidden" name="usageId" value={usage.id} />
|
||||
<button class="text-button danger-text" type="submit">Entfernen</button>
|
||||
<input type="hidden" name="revision" value={revision} /><input
|
||||
type="hidden"
|
||||
name="usageId"
|
||||
value={usage.id}
|
||||
/>
|
||||
<button class="text-button danger-text" type="submit" disabled={dirty}>Entfernen</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
<div class="piece-dialog-primary-action">
|
||||
{#if error}<span class="problem" role="alert">{error}</span>{/if}
|
||||
<button class="primary-button" type="submit" form={formId}>Verwendung speichern</button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</PieceEditorDialog>
|
||||
@@ -620,10 +620,11 @@ export function createPiece(template: PieceTemplateId = 'blank'): string {
|
||||
}
|
||||
|
||||
export function captureInbox(titleValue: string, urlValue: string): string {
|
||||
const title = cleanText(titleValue, 200, 'title');
|
||||
if (!title) throw new Error('INVALID_TITLE');
|
||||
let title = cleanText(titleValue, 200, 'title');
|
||||
const url = cleanText(urlValue, 2_000, 'source');
|
||||
if (url && !validUrl(url)) throw new Error('INVALID_SOURCES');
|
||||
if (!title && !url) throw new Error('INVALID_TITLE');
|
||||
if (!title) title = new URL(url).hostname;
|
||||
const id = createPiece('blank');
|
||||
const piece = getPiece(id)!;
|
||||
savePiece(id, piece.revision, {
|
||||
|
||||
Reference in new issue
Block a user