form.md 4.0 KB

Form Page

Overall Styling Guideline

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/

Component Props

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.

Page Header

New {Entity} | Edit {Entity}             [Cancel] [Create | Save Changes]
{mode}                                              (edit mode only)
  • Title: New {Entity} (create) or Edit {Entity} (edit)
  • Mode subtitle: shown only in edit mode, small capitalized text, reflecting the locked mode
  • Cancel button: outline, navigates to cancelHref
  • Submit button: primary; label is Create (create) or Save Changes (edit); shows a spinner and is disabled while submitting

The same Cancel + Submit pair is rendered again at the bottom of the form, right-aligned.

Basic Information Panel

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…                        │
└─────────────────────────────────────────────────────────────┘
  • Icon: <IconPicker> over the entity's icon registry
  • Name: text, maxlength 64
  • Mode (entities with both external 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>.

Submit

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.

Wrapper Files

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 name

Page Name

  • Create: New {Entity} - LocoStack
  • Edit: Edit {entity.name} - LocoStack