Commit 391a8e6
feat: v2.2 + v2.3 + v2.4 catch-up — extras CSS, command palette, uiSelect, uiSlider, uiSteps, complete form-binding family (#11)
* feat: v2.2.0-rc.1 — extras CSS for breadcrumb/pagination, command palette family, rich select combobox
basecoat-css 0.3.x ships no rules for `.breadcrumb` or `.pagination`,
so the existing helpers were rendering correct markup with no visual
defaults. v2.2 ships a small companion stylesheet (loaded by default
via the new `basecoatIncludes(extrasCSS=true)` flag) plus exposes the
two remaining basecoat-js components — command palette and rich
select combobox — as helpers.
Added:
* `assets/basecoat/wheels-basecoat-extras.min.css` — visual defaults
for `.breadcrumb` (flex `<ol>` with muted color, hover-darken,
aria-current emphasis) and `.pagination` (rounded buttons, active
state via `.pagination-item-active` or `[aria-current=page]`,
disabled state via `[aria-disabled=true]` or the legacy
`.opacity-50`). Inherits basecoat color tokens so it tracks
light/dark theme automatically.
* basecoatIncludes(extrasCssPath=, extrasCSS=true) — new args, loads
the extras stylesheet by default. `extrasCSS=false` opts out for
apps that ship their own breadcrumb/pagination styling.
* Command palette family (driven by basecoat-js's command.js):
- uiCommand / uiCommandEnd
- uiCommandInput(placeholder=, ariaLabel=) — the search input
that command.js wires up
- uiCommandList / uiCommandListEnd — opens [role=menu]
- uiCommandGroup(label=) / uiCommandGroupEnd
- uiCommandItem(text=, href=, keywords=, icon=, kbd=, force=,
keepOpen=, disabled=) — emits role=menuitem with the data-*
attributes the JS reads (data-keywords, data-force,
data-keep-command-open). With href -> <a>, without -> <button>.
- uiCommandSeparator() — <hr role="separator">
- uiCommandEmpty(text="No results.") — force-shown placeholder
- uiCommandDialog(triggerText=, triggerClass=, id=) /
uiCommandDialogEnd — wraps the palette in a
<dialog class="dialog command-dialog"> for ⌘K-style modals.
Trigger uses CSP-safe data-ui-dialog-open delegation handled
by wheels-basecoat-ui.js.
* uiSelect(name=, options=, value=, placeholder=, search=,
multiselect=, closeOnSelect=, id=, class=) — the basecoat-css 0.3.x
rich combobox, distinct from uiField(type="select")'s native
<select>. Single-call helper that:
- takes the same options="value:Label[:disabled],..." shape
- pre-renders the trigger label so there's no FOUC before
select.js initializes
- emits the four parts the JS queries (trigger button with span,
popover with optional search input, [role=listbox] with
[role=option] children, hidden input)
- multi-select serializes the value as a JSON array in the
hidden input; data-placeholder + aria-multiselectable wired
automatically
- third options segment ":disabled" sets aria-disabled
* tests/BasecoatV22Spec.cfc — snapshot-style coverage for the extras
CSS opt-out, every command-palette helper (including the data-*
attribute pass-through), and uiSelect's four-part structure +
pre-rendered label + multi-select serialization.
This was the v2.2 list called out as out-of-scope in #8 (built-in
CSS for breadcrumb/pagination, uiCommand, richer uiSelect). Nothing
breaking; v2.2 is purely additive. Helper API is stable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: v2.3.0-rc.1 — uiSlider, uiSteps, uiBoundSelect, uiBoundSlider
Four additions, one CSS extension, one JS extension. All purely additive
on top of v2.2.
Slider family
* uiSlider(name, value, min, max, step, label, showValue, disabled, id, class)
— basecoat-styled <input type="range">. Computes the --slider-value
CSS variable percentage server-side from the current value, so the
filled portion of the track renders correctly on first paint without
needing JS. Optional showValue=true renders an
<output data-ui-slider-output> mirror.
* wheels-basecoat-ui.js extended with an "input" listener that keeps
--slider-value in sync as the user drags + mirrors the live value
into any matching <output for="slider-id" data-ui-slider-output>.
* uiBoundSlider(objectName, property, ...) — Wheels-bound variant. Auto-
resolves the value from obj[property], emits name="<obj>[<prop>]",
humanizes the property name into the default label.
Steps / wizard progress indicator
* uiSteps(ariaLabel, class) — opens a labeled <nav><ol class="ui-steps">.
* uiStep(text, status="complete|current|upcoming", number, description, href)
— emits <li data-status="..."> with the matching ARIA. Auto-numbered
via a request-scoped counter when no `number` is passed. Complete
steps render a check icon in the marker; current steps emit
aria-current="step". Optional href wraps non-current steps in a link.
* uiStepsEnd() — closes + clears the counter.
* wheels-basecoat-extras.min.css extended with .ui-steps rules
(numbered circles connected by a colored progress line, mobile-stacked
layout below 640px, hover state on linked steps).
Wheels integration for the rich combobox
* uiBoundSelect(objectName, property, options, placeholder, search,
multiselect, closeOnSelect, id, class) — same options syntax as
uiSelect, but tolerates a real array on the model for multi-select
(auto-serializes to JSON for the hidden input). Throws
WheelsBasecoat.ObjectNotFound when the named object isn't in scope.
Fix
* Replace `??` with `?:` (CFML's null-coalescing operator) in two new
helpers — caught the typo on first reload (Lucee 7 reports the whole
component as failing to parse, mixin doesn't load, helpers go
undefined). Wheels Tutorial Finding for future reference.
Out of scope (potential v2.4+):
* Rich popover-based date picker (requires building a calendar grid +
JS that basecoat doesn't ship). Native <input type="date"> is
correctly styled by basecoat and works for now.
* Drag-and-drop file uploader.
* Code editor / syntax-highlighted textarea.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: v2.4.0-rc.1 — complete the form-binding round-trip + uiErrorSummary + uiRating
With v2.4 every common form input type has a Wheels-bound helper that
reads the current value, emits the canonical <obj>[<prop>] name, surfaces
validation errors, and humanizes the label.
Added:
* uiBoundCheckbox(objectName, property, label, switch, ...) — single
bound checkbox or switch. Solves the "unchecked checkbox submits
nothing" footgun by emitting a hidden value="0" companion input
under the same name BEFORE the checkbox, so params.<obj>.<prop> is
always defined as 0 or 1. Pass switch=true to render as a basecoat
.switch instead.
* uiCheckboxGroup(name, options, value, legend, description, inline) —
multi-checkbox collection emitting name="<name>[]" so Wheels arrays
the values. Tolerates a real CFML array, a JSON-array string, or a
CSV string for value.
* uiBoundCheckboxGroup(objectName, property, options, ...) — Wheels-
bound variant. Auto-resolves the value from the model, humanizes
the property into the legend.
* uiRadioGroup(name, options, value, legend, description, inline) —
radio-group container with role="radiogroup".
* uiBoundRadioGroup(objectName, property, options, ...) — Wheels-
bound variant.
* uiErrorSummary(model, title, description) — drop-in replacement
for Wheels' errorMessagesFor(). Renders model.allErrors() as a
basecoat destructive alert with a bullet list of field-prefixed
messages. Returns "" when no errors so it's safe to call
unconditionally at the top of a form. Auto-pluralizes the title
("1 error" vs "3 errors").
* uiRating(value, max, name, ariaLabel, class) — 1-to-N star rating
widget. Read-only display by default; pass name= to render as an
interactive radio group (CSS-only highlight via the bundled extras
stylesheet — no JS required). Renders highest-first internally so
the CSS sibling combinator can light earlier stars on hover/check.
* One new icon: star.
* wheels-basecoat-extras.min.css extended:
- .ui-rating rules (display + interactive variants)
- .radio rule (basecoat-css 0.3.x ships .checkbox and .switch but
no radio styling — the new helpers add a sized circle that
matches the checkbox treatment)
* tests/BasecoatV24Spec.cfc — snapshot-style coverage:
- falsy-companion hidden input + checked-state coercion
- JSON / CSV / real-array value resolution for the checkbox group
- role=radiogroup + name=plural[] for the multi helpers
- uiErrorSummary's empty-string fallback + plural wording
- uiRating's read-only fill count + interactive highest-first order
The form-binding family now spans:
uiBoundField (text, textarea, select, date, etc.)
uiBoundSelect (rich combobox, search, multi-select)
uiBoundSlider (range)
uiBoundCheckbox (single boolean)
uiBoundCheckboxGroup (multi-value)
uiBoundRadioGroup (single-choice)
uiErrorSummary (model-level rollup)
Note: this PR also brings v2.2 and v2.3 forward — those PRs (#9, #10)
were merged against feat/v2.1's branch instead of main, so main missed
them. The first two commits on this branch cherry-pick v2.2 (extras
CSS, command palette, uiSelect) and v2.3 (uiSlider, uiSteps,
uiBoundSelect, uiBoundSlider) onto current main. v2.4 builds on top.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Peter Amiri <petera@pai.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent cb18eeb commit 391a8e6
9 files changed
Lines changed: 1575 additions & 4 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
5 | 50 | | |
6 | 51 | | |
7 | 52 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
18 | 35 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
0 commit comments