Add the Neutral template family, template picker and text-only logo allowances
Linea (the vendor's original format.pdf design), Monogram and Serenity are rebuilt from the reference PDFs as one parametric family on the engine, with the user's logo in the measured slot, a home for every GST field and A4/Letter support. Adds static Montserrat, Open Sans and Poppins fonts and a generated Ms Madi outline for Serenity's vertical word. Text-only logo placeholders (Monolith, Tangerine) use a doc-derived allowance box. A Carbon TemplatePicker with committed thumbnails opens from the page setup controls.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { Button, ContentSwitcher, InlineLoading, Select, SelectItem, Switch } from "@carbon/react";
|
||||
import { useState } from "react";
|
||||
import { Button, ContentSwitcher, InlineLoading, Select, SelectItem, Switch, Toggle } from "@carbon/react";
|
||||
import { Catalog } from "@carbon/icons-react";
|
||||
import { api } from "../lib/api";
|
||||
import type { Settings } from "../lib/types";
|
||||
import type { MarginPreset, PageId } from "../pdf/engine/geometry";
|
||||
import { parsePrefs, prefsToJson, type RenderPrefsV1 } from "../pdf/model/prefs";
|
||||
import { catalogEntry } from "../pdf/templates/catalog";
|
||||
import TemplatePicker from "./TemplatePicker";
|
||||
|
||||
const PAGES: { id: PageId; label: string }[] = [
|
||||
{ id: "A4", label: "A4" },
|
||||
@@ -24,17 +27,17 @@ interface Props {
|
||||
/** Called after the default was saved so the app reloads its settings. */
|
||||
onSaved: () => void;
|
||||
disabled?: boolean;
|
||||
/** Where a TemplatePicker goes once there is more than one template. */
|
||||
templateSlot?: ReactNode;
|
||||
}
|
||||
|
||||
/** Page size and margins for the invoice being edited, plus "Save as default" for new invoices. */
|
||||
export default function PageSetupControls({ prefs, onChange, settings, onSaved, disabled, templateSlot }: Props) {
|
||||
export default function PageSetupControls({ prefs, onChange, settings, onSaved, disabled }: Props) {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const saved = prefsToJson(parsePrefs(settings.renderPrefs));
|
||||
const isDefault = prefsToJson(prefs) === saved;
|
||||
const template = catalogEntry(prefs.templateId);
|
||||
|
||||
const saveDefault = async () => {
|
||||
setSaving(true);
|
||||
@@ -52,7 +55,12 @@ export default function PageSetupControls({ prefs, onChange, settings, onSaved,
|
||||
return (
|
||||
<div className="voiced-page-setup">
|
||||
<div className="voiced-page-setup__row">
|
||||
{templateSlot}
|
||||
<div className="voiced-page-setup__field">
|
||||
<span className="cds--label">Template</span>
|
||||
<Button kind="tertiary" size="md" renderIcon={Catalog} disabled={disabled} onClick={() => setPickerOpen(true)}>
|
||||
{template.name}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="voiced-page-setup__field">
|
||||
<span className="cds--label">Page size</span>
|
||||
<ContentSwitcher
|
||||
@@ -80,6 +88,31 @@ export default function PageSetupControls({ prefs, onChange, settings, onSaved,
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
{template.logoOptIn ? (
|
||||
<div className="voiced-page-setup__row voiced-page-setup__extra">
|
||||
<Toggle
|
||||
id="template-logo-show"
|
||||
size="sm"
|
||||
labelText="Logo"
|
||||
labelA="Hidden"
|
||||
labelB="Shown above the title"
|
||||
toggled={prefs.logo?.show === true}
|
||||
disabled={disabled}
|
||||
onToggle={(on) => onChange({ ...prefs, logo: { ...prefs.logo, show: on } })}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<TemplatePicker
|
||||
open={pickerOpen}
|
||||
onClose={() => setPickerOpen(false)}
|
||||
selectedId={template.id}
|
||||
onSelect={(id) => onChange({ ...prefs, templateId: id })}
|
||||
isDefault={isDefault}
|
||||
saving={saving}
|
||||
error={error}
|
||||
onSaveDefault={() => void saveDefault()}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<div className="voiced-page-setup__footer">
|
||||
{saving ? (
|
||||
<InlineLoading status="active" description="Saving…" />
|
||||
|
||||
Reference in New Issue
Block a user