Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/compact-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@wirecore/wirelayer-recipes': minor
'@wirecore/wirelayer-css': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
'@wirecore/wirelayer-native': minor
---

Display parts for dense desktop UIs, all opt-in:

- `Progress status="neutral"`: a grey fill, e.g. for a cancelled transfer (also React Native)
- `Avatar initials`: own initials instead of the ones from `name` ("MS" for "my-server"; also React Native)
- `Card size="sm"`: smaller radius, padding and title, for lists of cards inside a dialog (`wc-card-sm` in HTML);
`ConfigProvider size` leaves cards alone
- `Code` in the main package: code in a sentence with the look of `InlineCode`, without syntax colours and without the
optional `shiki`

`rounded-card` and `rounded-panel` now count as radii for tailwind-merge (like `rounded-popup`): another radius in
`class`/`classes` replaces them instead of both classes ending up on the element.
10 changes: 10 additions & 0 deletions .changeset/context-menu-keyboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@wirecore/wirelayer-recipes': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

`ContextMenu`'s `before-open` / `onBeforeOpen` gets a second argument `{ source: 'pointer' | 'keyboard', focused }`:
opened with Shift+F10 or the menu key, `focused` is the element with the focus – a list can tell a row from its empty
space even when the browser fires the event on the whole area. The first argument stays as it was.
`createContextMenuSource()` in the recipes tells the two apart.
15 changes: 15 additions & 0 deletions .changeset/dialog-focus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@wirecore/wirelayer-recipes': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

Focus for dialogs and wizards whose content changes:

- `data-autoselect` on a field in a `Dialog`: it gets the focus when the dialog opens, with its text selected (e.g. a
prefilled name). Without it Ark's choice stays (an alertdialog's close button, `data-autofocus`, the first element)
- `focusFirst(container)` (also from the component packages) focuses like opening does – `data-autoselect`,
`data-autofocus`/`autofocus`, otherwise the first field; Vue's `DialogContent` exposes it as `focusFirst()`
- `Wizard focusOnStepChange` moves the focus into each new step the same way (not after a failed check: the invalid
field keeps it)
- `autoselectElement(content)` in the recipes for a dialog's own initial focus
13 changes: 13 additions & 0 deletions .changeset/non-empty-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
'@wirecore/wirelayer-native': minor
---

Choices that cannot be empty give their value type without the empty value – no `$event && …` guard for a model that
is never empty. Only the types change, unless noted:

- `RadioGroup` and `SegmentedControl` with `required`: v-model / `onValueChange` get `V`, not `V | null`. A form reset
back to an empty start (the only way Ark empties the group) is no longer passed on for a required group.
`SegmentedControl` takes `required` now (`aria-required` on the group); React Native's `RadioGroup` is typed the same
- `NativeSelect` without `placeholder`: `V`, not `V | ''` (only the placeholder option gives `''`)
11 changes: 11 additions & 0 deletions .changeset/number-input-empty-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

`NumberInput` with `emptyFallback`, for fields that always need a number (e.g. a port): an emptied field keeps its value
while it is empty and falls back to `min` (or 0) or to the last valid number (`previous`) when it is left or Enter is
pressed. v-model / `onValueChange` is then typed `number`. Without it an emptied field still reports null.

Note for type code around the components: `NumberInput` (Vue) is generic now, so `InstanceType<typeof NumberInput>` and
Storybook's `Meta<typeof NumberInput>` need a cast (as for Select).
9 changes: 9 additions & 0 deletions .changeset/positioner-z-index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@wirecore/wirelayer-tokens': patch
'@wirecore/wirelayer-vue': patch
'@wirecore/wirelayer-react': patch
---

Popups with a positioner (Select, Combobox, Menu, Popover …) stay above a dialog: the theme gives Ark's positioners
`z-index: var(--z-index)` (in the base layer). Ark sets the z-index inline, but Vue removed it again whenever the popup
rendered anew – the list then lay behind the dialog, looked closed and the pointer hit the dialog.
10 changes: 10 additions & 0 deletions .changeset/resolve-tokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@wirecore/wirelayer-tokens': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

Token colours as fixed values for canvas libraries (xterm.js, charts) that cannot read CSS variables:
`resolveTokens(element?)` gives the colours that apply right now (mode, theme and own overrides) as hex – `#rrggbbaa` for
the translucent ones – and `watchTokens(onChange)` reports a change of mode, theme or system colour scheme. The
composable/hook `useResolvedTokens()` keeps them up to date.
8 changes: 8 additions & 0 deletions .changeset/select-empty-value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

`Select` with `emptyValue`, e.g. `""` for a model that uses an empty string for "nothing chosen": the value counts as
null – the placeholder shows, no development warning about an unknown value – and clearing gives it back instead of null
(typed `V | E`). Without it `""` is still an unknown value shown as text.
10 changes: 10 additions & 0 deletions .changeset/tabs-panels-elsewhere.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

`Tabs` for panels that live elsewhere (editor or terminal tabs, panes shown with `v-show`):

- `TabsTrigger controls="pane-id"` points `aria-controls` at that element; `null` removes it (tabs without panels)
- `Tabs` takes `null` as its value: no tab selected. The first enabled tab then stays reachable with Tab (Ark takes
every tab out of the Tab order without a selection); Tabs without a value stay as they were
8 changes: 8 additions & 0 deletions .changeset/vite-plugin-dev-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@wirecore/wirelayer-recipes': patch
---

Vite plugin: a component imported for the first time while `vite dev` runs gets its classes right away. The plugin added
its sources file to the CSS, but Tailwind's Vite plugin only builds a CSS file again when a file it imports changes on
disk – the classes were missing until a restart (cards without padding, tiny avatars). In the dev server the imports now
live in a file in Vite's cache folder; the build is unchanged.
8 changes: 8 additions & 0 deletions .changeset/wizard-texts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

Own texts in `Wizard` (and `Stepper`): `finishLabel` names the last button (default "Finish"), and a step's `validate`
may return a text (also async) – it keeps the step open and is shown as its error instead of "Please check the entries
in this step".
16 changes: 16 additions & 0 deletions apps/storybook/src/Migration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,19 @@ by side, then the places where the two behave differently. The React package has
numbers); `Checkbox` with a boolean model gives booleans.
- **Compact parts:** `Progress size="xs"` with `hide-label`, `bar-text` and `status`; `Alert #actions`; `mono` on text
fields; `DialogContent title-position="header"` puts the title next to the close button.
- **Values that are never empty:** with `required`, `RadioGroup` and `SegmentedControl` give v-model `V` instead of
`V | null`; `NativeSelect` without `placeholder` gives `V` instead of `V | ''`; `NumberInput empty-fallback="min"` or
`"previous"` refills an emptied field when it is left (typed `number`). `Select empty-value=""` treats an empty string
like null – the placeholder shows, clearing gives `""` back.
- **Focus in dialogs and wizards:** `data-autoselect` focuses a field and selects its text when the dialog opens (e.g. a
prefilled name). For content that changes while it is open, `DialogContent` exposes `focusFirst()` (React:
`focusFirst(element)`), and `Wizard focus-on-step-change` moves the focus into each new step. `Wizard finish-label`
names the last button; `validate` may return a text, shown as the step's error.
- **Panels elsewhere:** `TabsTrigger controls="pane-id"` points a tab at a pane outside the Tabs (`:controls="null"`
for none); `Tabs` takes `null` for "no tab selected" – the first tab stays reachable with Tab.
- **Context menus by keyboard:** `@before-open` gets a second argument `{ source: 'pointer' | 'keyboard', focused }` –
with Shift+F10 or the menu key `focused` is the element with the focus (e.g. the list row).
- **Canvas libraries** (xterm.js, charts): `resolveTokens()` gives the token colours of the current mode and theme as hex;
`useResolvedTokens()` updates when the mode, the theme or the system colour scheme changes.
- **More compact parts:** `Card size="sm"` for lists in dialogs, `Progress status="neutral"` for cancelled work,
`Avatar initials="MS"`, and `Code` for code in a sentence without shiki (the look of `InlineCode` from `/code`).
18 changes: 18 additions & 0 deletions docs/implementation-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@ Worth reading before changing the affected areas.
or an effect (and React's `autoFocus`, which only calls `focus()`) is overridden. The trap looks for
`[data-autofocus]`/`[autofocus]`; an `alertdialog` starts on its close button. In jsdom no field counts as focusable
(no layout), so the test runs in the browser.
- **Positioner z-index in Vue:** Ark copies the z-index of a popup's content to its positioner as an inline style
(`z-index: var(--z-index)`), after the render. Vue patches the positioner's `style` object on every render and clears
keys that are null there – `zIndex` is one of them, so the inline z-index was gone after the first update and the list
lay behind a dialog. The theme sets the same z-index for `[data-scope][data-part='positioner']` in the base layer
(a z-index utility on a positioner still wins).
- **`data-autoselect` and the alertdialog:** the Dialog passes its own `initialFocusEl` to find the marked field – that
replaces Ark's default, which starts an `alertdialog` on its close button, so the Dialog takes the close button itself
for an alertdialog without a marked field.
- **Tabs without a selected tab:** Ark makes only the selected tab tabbable – with the value null no tab is in the Tab
order. When the value is null on purpose, the first enabled tab gets `tabIndex` 0 (found in the DOM after rendering);
Tabs without a value (uncontrolled, no default) stay as they were.
- **Tailwind's Vite plugin and added imports:** `@tailwindcss/vite` keeps the compiled CSS of a file until one of the
files it imports changes on disk – imports that a plugin adds to the code alone are ignored. In the dev server the
WireLayer plugin therefore writes them to a file in Vite's cache folder and imports that file.
- **Narrower emit types:** `required` (RadioGroup, SegmentedControl), the placeholder (NativeSelect), `emptyFallback`
(NumberInput) and `emptyValue` (Select) are extra generics that only change the type of v-model/onValueChange.
`emptyValue` is `NoInfer` in the value types – otherwise the bound value would be taken as `E` and remove null from
the type even without `emptyValue`.
- **Controlled form elements:** Vue additionally writes the value of a `<select>` as the attribute `value` (which
`<select>` does not know), React only sets the property – the DOM comparison ignores this attribute. For `<textarea>`,
React writes the value as text content and Ark Vue's factory additionally as an attribute – the passed value is
Expand Down
Loading
Loading