Form pages create and edit entities. A single shared <XForm> component handles both flows; whether it acts as create or edit is derived from whether a starting value is provided.
The form is rendered inside a centered, max-width container. Form fields are grouped into glass-pane cards, one per logical panel.
| Component | Path |
|---|---|
| Glass Pane | src/lib/components/common/glass-pane/pane.svelte |
| Card | src/lib/components/common/card/card.svelte |
| Field | src/lib/components/controls/field/ |
| IconPicker | src/lib/components/forms/icon-picker/ |
type Props = {
value?: XFormValue;
cancelHref: string;
onsubmit: (value?: XFormValue) => void;
};
isEdit is derived from whether value is provided — undefined → create, defined → edit. The edit wrapper converts the loaded entity to a form value via to{X}FormValue before passing it in.
New {Entity} | Edit {Entity} [Cancel] [Create | Save Changes]
{mode} (edit mode only)
New {Entity} (create) or Edit {Entity} (edit)cancelHrefCreate (create) or Save Changes (edit); shows a spinner and is disabled while submittingThe same Cancel + Submit pair is rendered again at the bottom of the form, right-aligned.
The first panel in every form. The icon selector sits on the left as a square sidebar; basic identity fields stack on the right.
┌─────────────────────────────────────────────────────────────┐
│ ┌────────┐ Name │
│ │ icon │ [ ] │
│ │selector│ Mode │
│ └────────┘ [External | Managed] │
│ …entity-specific fields… │
└─────────────────────────────────────────────────────────────┘
<IconPicker> over the entity's icon registryexternal and managed variants): segmented toggle — External | Managed. Disabled in edit mode (mode is fixed once a resource is created). A description below the toggle explains what each mode means and updates as the user toggles.Entity-specific basic fields (description, type selectors, etc.) follow underneath. Optional sub-sections may be nested via <Optional>.
onsubmit is invoked when the submit button is clicked. The form itself does not navigate or surface errors — the wrapper page is expected to call the API, navigate to the entity's detail page on success, and surface failures via the notification store.
Each entity provides a validate{X}FormValue helper in its types.ts that converts a form value to the entity type and returns a list of validation error messages.
Each form is mounted via thin route wrappers:
routes/{entity}/new/+page.svelte — create wrapper, sets the breadcrumb to [New {Entity}]routes/{entity}/[id]/edit/+page.server.ts — loads the entity (404 if not found)routes/{entity}/[id]/edit/+page.svelte — edit wrapper, converts the loaded entity via to{X}FormValue, passes it to the form, and sets the breadcrumb to the entity nameNew {Entity} - LocoStackEdit {entity.name} - LocoStack