initial commit after split

This commit is contained in:
2026-06-24 01:43:27 +02:00
parent 2406e70597
commit 7f25a3ccdf
125 changed files with 25551 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
import Button from "../../../components/Button";
import Dialog from "../../../components/Dialog";
import DismissibleAlert from "../../../components/DismissibleAlert";
import type { TemplateNamespace, TemplatePlaceholder, UndefinedPlaceholder } from "../utils/templatePlaceholders";
export type TemplateFieldOption = {
name: string;
label: string;
};
export function TemplateFieldChipList({
namespace,
fields,
usedPlaceholders = [],
empty,
disabled = false,
onInsert
}: {
namespace: TemplateNamespace;
fields: TemplateFieldOption[];
usedPlaceholders?: TemplatePlaceholder[];
empty: string;
disabled?: boolean;
onInsert: (namespace: TemplateNamespace, name: string) => void;
}) {
if (fields.length === 0) return <p className="muted">{empty}</p>;
return (
<div className="field-chip-list">
{fields.map((field) => {
const used = usedPlaceholders.some((placeholder) => placeholder.validNamespace && placeholder.namespace === namespace && placeholder.name === field.name);
return (
<button
type="button"
className={`field-chip field-chip-button ${used ? "used" : ""}`}
key={`${namespace}:${field.name}`}
disabled={disabled}
onClick={() => onInsert(namespace, field.name)}
>
<span className="field-chip-namespace">{namespace}</span><span title={field.label !== field.name ? field.label : undefined}>{field.name}</span>
</button>
);
})}
</div>
);
}
export function UndefinedPlaceholderList({
items,
empty = "No undefined placeholders detected.",
onSelect
}: {
items: UndefinedPlaceholder[];
empty?: string;
onSelect: (item: UndefinedPlaceholder) => void;
}) {
if (items.length === 0) return <p className="muted">{empty}</p>;
return (
<div className="field-chip-list">
{items.map((item) => (
<button
type="button"
className="field-chip field-chip-button undefined"
key={`${item.raw}:${item.reason}`}
onClick={() => onSelect(item)}
>
{item.display}
</button>
))}
</div>
);
}
export function UndefinedPlaceholderDecisionDialog({
field,
contextLabel = "template",
removeLabel = "Remove placeholder",
onCancel,
onRemove,
onAddField,
addDisabled = false
}: {
field: UndefinedPlaceholder | null;
contextLabel?: string;
removeLabel?: string;
onCancel: () => void;
onRemove: (field: UndefinedPlaceholder) => void;
onAddField: (field: UndefinedPlaceholder) => void;
addDisabled?: boolean;
}) {
return (
<Dialog
open={Boolean(field)}
title="Undefined field reference"
closeLabel="Return to editing"
className="template-action-dialog"
onClose={onCancel}
footer={field ? (
<>
<Button onClick={onCancel}>Continue editing</Button>
<Button onClick={() => onRemove(field)}>{removeLabel}</Button>
<Button variant="primary" onClick={() => onAddField(field)} disabled={!field.name || field.reason === "invalid-namespace" || addDisabled}>Add field</Button>
</>
) : undefined}
>
{field && (
<>
<p>The {contextLabel} uses <code>{`{{${field.raw}}}`}</code>, but it cannot be matched to a known field.</p>
{field.reason === "invalid-namespace" && (
<DismissibleAlert tone="warning">Use the namespace <code>global:</code> or <code>local:</code>.</DismissibleAlert>
)}
{field.reason === "missing-field" && (
<p className="muted">You can add <strong>{field.name}</strong> as a campaign field, remove this placeholder, or continue editing.</p>
)}
</>
)}
</Dialog>
);
}