@panphora/hyper-cms 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,12 +75,111 @@ The full grammar lives in the [hyper-html-api docs](https://github.com/panphora/
75
75
  |---|---|---|
76
76
  | Scalar (text) | `"title": ".page-title"` | input |
77
77
  | Scalar (attr) | `"href": "a.link@href"` | input |
78
- | Scalar (prop) | `"checked": "input.cb@checked"` | checkbox |
78
+ | Scalar (`@checked`) | `"checked": "input.cb@checked"` | [checkbox](#checkbox-and-toggle-checkbox-toggle) (inferred) |
79
+ | Scalar (`@src`) | `"avatar": "img.avatar@src"` | [image upload](#image-upload-image) (inferred) |
79
80
  | Scalar array | `"tags": "ul.tags li[]"` | list of inputs |
80
81
  | Object | `"author": { "name": ".name", "email": ".email" }` | grouped fields |
81
82
  | Object array | `"products": [".product", { "name": ".name" }]` | list of cards |
82
83
  | Nested | object arrays inside object arrays | recurses |
83
84
 
85
+ ## Field controls
86
+
87
+ How the sidebar picks a control for each leaf, in order:
88
+
89
+ 1. If the page element the rule points at carries `data-hcms-component`, that control wins. Explicit beats inferred. Values: `checkbox`, `toggle`, `select`, `radio`, `textarea`, `number`, `image`, `file` on scalar fields, `chips` on list containers.
90
+ 2. Otherwise a rule ending in `@src` gets the **image upload** control, and a rule ending in `@checked` gets a **checkbox**.
91
+ 3. Otherwise the rule's shape picks one of the defaults below, unless a [custom template](#custom-form-templates) overrides it.
92
+
93
+ A custom `<template data-hcms-tpl>` matching the field's path always outranks a named component (the author's template is the stronger signal); when that happens the sidebar logs a `console.info` naming the shadowed component.
94
+
95
+ One component per element: `data-hcms-component` applies to every rule bound to that element, so give each controlled field its own element (e.g. one hidden `<span data-priority="medium" data-hcms-component="select">` per value, not three values on one element).
96
+
97
+ One control per rule: in a card list the same rule binds one element per card, and `data-hcms-component`, `data-hcms-options`, and `data-hcms-crop` are sampled from the first matching element and apply to every card — there's no per-card variation. Set them on the first card (or all cards, identically). The number and checkbox/toggle value guards, by contrast, check **every** card's value: one card holding `TBD` falls the whole rule back to text inputs so nothing gets blanked.
98
+
99
+ The six shape templates are injected into every managed page; all opt-in components are injected on demand the first time a field selects one, so pages using none stay clean.
100
+
101
+ ### Text input (`@scalar`)
102
+
103
+ The default for every scalar leaf — element text (`".page-title"`), attributes (`"a.link@href"`), props (`"input.name@value"`). One label, one input, inline error slot.
104
+
105
+ Use it for: headlines, taglines, button labels, prices, link URLs — any one-line copy.
106
+
107
+ ### Field group (`@object`)
108
+
109
+ An object rule renders a titled section wrapping its children, which each pick their own control.
110
+
111
+ Use it for: a hero block (title + subtitle + CTA), an author card, a footer contact block — copy that belongs together and should read as one unit in the sidebar.
112
+
113
+ ### List of inputs (`@scalar-array`)
114
+
115
+ A `[]` rule renders one input per item with add, remove, and reorder.
116
+
117
+ Use it for: tags, feature bullets, badge lists, nav labels — repeated single values.
118
+
119
+ ### Card list (`@object-array`)
120
+
121
+ An `["item", { … }]` rule renders draggable cards — grip, per-card fields, add/remove/move controls. Removing a card asks for confirmation by default (`confirmRemove`); scalar-array chips remove instantly.
122
+
123
+ Use it for: pricing plans, products, testimonials, FAQ entries, team members — repeated structures with several fields each.
124
+
125
+ ### Checkbox and toggle (`checkbox`, `toggle`)
126
+
127
+ A rule ending in `@checked` gets a checkbox automatically; any other boolean-ish field opts in with `data-hcms-component="checkbox"`, or `"toggle"` for a switch. Both bind the same machinery; the difference is purely the chrome. Booleans round-trip as real booleans (`"true"`/`"false"` page attributes coerce both ways). An opted-in field whose current value isn't `true`/`false` (e.g. `yes`) falls back to a text input with a console note — a checkbox can only ever write back `true`/`false`, which would clobber the value on the next commit.
128
+
129
+ Use it for: published/draft flags, feature switches, "show section" booleans stored in a `data-*` attribute.
130
+
131
+ ### Dropdown (`select`)
132
+
133
+ `data-hcms-component="select"` plus `data-hcms-options="low medium high"` (space-separated values; labels derive automatically, `in-progress` reads as "In progress"). If the page's current value isn't in the list it's prepended, so opening the editor never silently changes data. Missing options render an inline field error instead of a dead control.
134
+
135
+ Use it for: a status, a priority, a size, a theme name — one value from a known short list.
136
+
137
+ ### Radio group (`radio`)
138
+
139
+ Same opt-in and options syntax as select: `data-hcms-component="radio"` + `data-hcms-options`. Renders one themed radio per value with a per-field group name, so sibling fields and array items never cross-group.
140
+
141
+ Use it for: the same data as select when the choices deserve to be visible at a glance (2 to 5 options).
142
+
143
+ ### Big text (`textarea`)
144
+
145
+ `data-hcms-component="textarea"` renders a multi-line box (3 rows) instead of the one-line input.
146
+
147
+ Use it for: bios, descriptions, testimonial quotes, any copy that wraps.
148
+
149
+ ### Number (`number`)
150
+
151
+ `data-hcms-component="number"` renders a numeric input. Values still round-trip as strings (the engine stores what the page stores). If any bound value isn't a plain number (`$5`, `7 items` — in a card list, any card's value), the field falls back to a text input with a console note, because a number input physically can't hold such a value and would blank it on the next commit.
152
+
153
+ Use it for: quantities, prices without currency symbols, ratings, column counts.
154
+
155
+ ### Chip list (`chips`)
156
+
157
+ `data-hcms-component="chips"` on the list container (the `<ul>` the array rule's items live in) swaps the stacked input list for inline editable chips with add/remove.
158
+
159
+ Use it for: tags, keywords, badge lists — short repeated values where a full row each is overkill.
160
+
161
+ ### Image upload (`@image`)
162
+
163
+ Selected automatically for `@src` rules; forced anywhere else with `data-hcms-component="image"` on the page element — e.g. a logo URL stored in a `data-icon` attribute, where the suffix can't be inferred.
164
+
165
+ An "Upload image" button (`accept="image/*"`) sends the file through the host uploader (`window.hyperclay.uploadFileBasic` in a hyperclay app) and writes the served URL into the page's `img src`; the field shows a thumbnail with an × that clears it. Standalone (no host uploader) the widget still works with a temporary `blob:` preview — deliberately non-persistent. Upload errors render inline in the field and fire `hcms:error`; closing the editor mid-upload aborts cleanly.
166
+
167
+ **Crop on upload:** add `data-hcms-crop` to the page element and the picked image routes through [quickcrop](https://github.com/panphora/quickcrop) (`window.hyperclay.quickcrop`, bundled in hyperclayjs's `smooth-sailing`/`everything` presets) before uploading. Values: `"1:1"`, `"16:9"` (any `W:H`), or `"free"` for an unconstrained crop. Cancelling the crop aborts the upload. Output is webp, longest edge capped at 2048px. Without quickcrop on the page the file uploads uncropped — the attribute never blocks.
168
+
169
+ Use it for: avatars, hero/cover images, per-card product photos (each card in an object array uploads independently), any image an owner should swap without touching URLs. Add crop for anything that must fit a fixed frame: avatars (`1:1`), hero banners (`16:9`).
170
+
171
+ ### File upload (`@file`)
172
+
173
+ Never inferred — opt in with `data-hcms-component="file"` on the element. A bare `a@href` stays a plain URL field: editing a link is far more common than uploading a file.
174
+
175
+ A "Choose" button uploads the file, writes the URL into the `a href`, and shows the decoded filename as the link text; × clears both.
176
+
177
+ Use it for: a resume PDF, a downloadable spec or whitepaper, a press kit — links whose target the owner should replace by picking a file.
178
+
179
+ ### Everything else: custom templates
180
+
181
+ Anything past the built-ins — multi-select, sliders, color pickers, bespoke layouts, option labels that differ from their values — is [custom template](#custom-form-templates) territory over the same machinery. Living examples: `demo/index.html` (the `quantity` template, a wildcard-path override) and `demo/pixel-quiet.html` (the `bio` template).
182
+
84
183
  ## Attributes you can use in your page HTML
85
184
 
86
185
  | Attribute | On | Effect |
@@ -88,11 +187,15 @@ The full grammar lives in the [hyper-html-api docs](https://github.com/panphora/
88
187
  | `save-ignore` | any element | Engine skips during extract and apply. Mutation observer ignores changes inside. The shell sets this on itself. |
89
188
  | `save-freeze`, `save-remove`, `mutations-ignore` | any element | Same observer-ignore behavior as `save-ignore`. Use whichever fits your save-pipeline convention. |
90
189
  | `cms-template` | any element matching a list rule | Engine treats it as a seed template, not real data. Used to grow lists from empty. See [Template seeding](#template-seeding) below. |
190
+ | `data-hcms-component="…"` | the page element a rule points at | Picks a [named control](#field-controls): `checkbox`, `toggle`, `select`, `radio`, `textarea`, `number`, `image`, `file` (scalars) or `chips` (list containers). Explicit beats inferred; `@src`/`@checked` rules upgrade automatically without it. |
191
+ | `data-hcms-options="low medium high"` | the same element, with `select`/`radio` | Space-separated values for the dropdown/radio group. Labels derive automatically (`in-progress` → "In progress"). |
192
+ | `data-hcms-crop="1:1"` | the page element of an image field | Crop-on-upload via quickcrop. Any `W:H` ratio, or `free`. See [Image upload](#image-upload-image). |
91
193
  | `data-hcms-min-items="N"` | a `<template data-hcms-tpl>` | Sidebar refuses to remove below N items. Hides the remove button when at the floor. |
92
194
  | `data-hcms-max-items="N"` | a `<template data-hcms-tpl>` | Sidebar refuses to add above N items. Hides the add button at the ceiling. |
93
195
  | `data-hcms-no-add` | a `<template data-hcms-tpl>` | No add button. |
94
196
  | `data-hcms-no-remove` | a `<template data-hcms-tpl>` | No remove buttons. |
95
197
  | `data-hcms-no-reorder` | a `<template data-hcms-tpl>` | No drag handles or move buttons. |
198
+ | `data-hcms-confirm-remove` | the root element inside a custom array template | Per-list delete confirmation. A string replaces the prompt; `off` (or `false`/`no`/`0`) disables asking for that list. Wins over the `confirmRemove` open() option. |
96
199
 
97
200
  ## Template seeding
98
201
 
@@ -171,6 +274,7 @@ cms.api.removeItem(itemPath) // drop an item by index
171
274
  | `side` | `'right'` | `'right'` or `'left'`. |
172
275
  | `overlay` | `false` | If true, sidebar covers the page; if false, page content gets a padding offset. |
173
276
  | `showSaveButton` | `false` | Renders a Save button in the shell footer. The button carries the `trigger-save` attribute: in hyperclayjs apps the host save system handles the click (with a "changes are local only" notice for non-owners). Standalone hosts delegate it themselves — see below. |
277
+ | `confirmRemove` | confirm cards, not chips | Delete-confirmation policy for all lists: `true` confirms every removal, `false` never asks, a string confirms with that prompt. Per-list `data-hcms-confirm-remove` wins over this. |
174
278
  | `onChange(data, info)` | none | Per-commit callback. |
175
279
  | `onError(err)` | none | Per-commit error callback. |
176
280