@pienter/ui 0.3.0 → 0.5.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/CHANGELOG.md +200 -0
- package/CONVENTIONS.md +297 -385
- package/README.md +3 -18
- package/components/feedback/toast/toast.css +1 -1
- package/components/form/combobox/Combobox.vue +21 -38
- package/components/form/combobox/combobox.css +1 -1
- package/components/form/form/Form.vue +1 -2
- package/components/form/label/label.css +1 -1
- package/components/form/number-field/NumberField.vue +1 -1
- package/components/form/number-field/number-field.css +1 -1
- package/components/form/radio-group/RadioGroup.vue +2 -5
- package/components/form/slider/slider.css +2 -3
- package/components/form/tags-input/tags-input.css +1 -2
- package/components/form/textarea/textarea.css +1 -1
- package/components/layout/accordion/Accordion.vue +6 -27
- package/components/layout/collapsible/Collapsible.vue +9 -19
- package/components/layout/table/table.css +3 -3
- package/components/navigation/sidebar/Sidebar.vue +326 -84
- package/components/navigation/sidebar/SidebarMenuItem.vue +199 -0
- package/components/navigation/sidebar/context.ts +21 -0
- package/components/navigation/sidebar/sidebar.css +346 -78
- package/components/navigation/sidebar/types.ts +13 -1
- package/components/overlay/alert-dialog/AlertDialog.vue +10 -31
- package/components/overlay/command/Command.vue +10 -38
- package/components/overlay/command/command.css +1 -1
- package/components/overlay/dropdown-menu/DropdownMenu.vue +37 -62
- package/components/overlay/modal/Modal.vue +7 -28
- package/components/overlay/popover/Popover.vue +9 -31
- package/components/overlay/sheet/Sheet.vue +7 -28
- package/components/overlay/tooltip/Tooltip.vue +14 -19
- package/{utils/controllers/dialog.ts → composables/useDialog.ts} +90 -78
- package/composables/useDisclosure.ts +113 -0
- package/composables/useEventListener.ts +16 -0
- package/{utils/controllers/menu.ts → composables/useMenu.ts} +66 -102
- package/{utils/controllers/popover.ts → composables/usePopover.ts} +107 -120
- package/package.json +5 -39
- package/styles/0-settings/layout.css +18 -0
- package/styles/0-settings/motion.css +2 -2
- package/styles/0-settings/spacing.css +3 -1
- package/utils/navigation/sidebar.ts +97 -0
- package/utils/validation/form.ts +6 -9
- package/components/action/button/Button.astro +0 -95
- package/components/action/button/IconButton.astro +0 -86
- package/components/display/avatar/Avatar.astro +0 -17
- package/components/display/avatar/AvatarStack.astro +0 -9
- package/components/display/badge/Badge.astro +0 -15
- package/components/display/empty/Empty.astro +0 -9
- package/components/display/icon/Icon.astro +0 -52
- package/components/feedback/alert/Alert.astro +0 -52
- package/components/feedback/progress/Progress.astro +0 -68
- package/components/feedback/skeleton/Skeleton.astro +0 -32
- package/components/feedback/spinner/Spinner.astro +0 -25
- package/components/feedback/toast/Toast.astro +0 -50
- package/components/form/checkbox/Checkbox.astro +0 -79
- package/components/form/date-input/DateInput.astro +0 -105
- package/components/form/form/Form.astro +0 -106
- package/components/form/input-otp/InputOTP.astro +0 -147
- package/components/form/label/Label.astro +0 -13
- package/components/form/number-field/NumberField.astro +0 -142
- package/components/form/radio-group/RadioGroup.astro +0 -105
- package/components/form/select/Select.astro +0 -105
- package/components/form/slider/Slider.astro +0 -205
- package/components/form/switch/Switch.astro +0 -75
- package/components/form/tags-input/TagsInput.astro +0 -153
- package/components/form/text-input/TextInput.astro +0 -84
- package/components/form/textarea/Textarea.astro +0 -86
- package/components/layout/card/Card.astro +0 -13
- package/components/layout/separator/Separator.astro +0 -31
- package/components/layout/table/Table.astro +0 -116
- package/components/navigation/breadcrumb/Breadcrumb.astro +0 -36
- package/components/navigation/navbar/Navbar.astro +0 -62
- package/components/navigation/pagination/PaginationFooter.astro +0 -24
- package/components/navigation/sidebar/Sidebar.astro +0 -132
- package/components/navigation/sidebar/SidebarItemRender.astro +0 -83
- package/components/navigation/sidebar/SidebarItemRender.vue +0 -98
- package/components/overlay/alert-dialog/AlertDialog.astro +0 -112
- package/components/overlay/modal/Modal.astro +0 -66
- package/utils/controllers/disclosure.ts +0 -117
- package/utils/controllers/form.ts +0 -524
- package/utils/controllers/index.ts +0 -39
- package/utils/controllers/number-field.ts +0 -103
- package/utils/controllers/otp.ts +0 -252
- package/utils/controllers/sidebar.ts +0 -610
- package/utils/controllers/slider.ts +0 -336
- package/utils/controllers/tags-input.ts +0 -255
- package/utils/controllers/toast.ts +0 -426
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,206 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.5.0 - 2026-08-30
|
|
6
|
+
|
|
7
|
+
### Breaking
|
|
8
|
+
|
|
9
|
+
- The framework-agnostic controllers are gone; their behavior now lives in
|
|
10
|
+
Vue composables. `@pienter/ui/utils/controllers/*` (`mountDialog`,
|
|
11
|
+
`mountPopover`, `mountDisclosure`, `mountMenu` and their types) no longer
|
|
12
|
+
exists — import `useDialog`, `usePopover`, `useDisclosure`, and `useMenu`
|
|
13
|
+
from `@pienter/ui/composables/<name>` instead. The composables carry the
|
|
14
|
+
same behavior and options but own the lifecycle themselves: they take an
|
|
15
|
+
element ref plus options at setup, wire on mount, follow a controlled
|
|
16
|
+
`open` getter, and clean up on unmount, so the mount/watch/teardown dance
|
|
17
|
+
every component repeated is gone. `Placement` moved to
|
|
18
|
+
`@pienter/ui/composables/usePopover`. Component behavior is unchanged.
|
|
19
|
+
|
|
20
|
+
## 0.4.0 - 2026-08-28
|
|
21
|
+
|
|
22
|
+
### Breaking
|
|
23
|
+
|
|
24
|
+
- The library is now Vue-only. The Astro tier is removed in full: every
|
|
25
|
+
`*.astro` component export, the Astro-only controllers (`mountForm`,
|
|
26
|
+
`mountNumberField`, `mountInputOTP`, `mountSlider`, `mountTagsInput`,
|
|
27
|
+
`mountToast`) and their types, and the `Sidebar.mount` entrypoint no longer
|
|
28
|
+
exist, and `astro` is no longer an optional peer dependency. Every removed
|
|
29
|
+
component has a Vue counterpart — migrate
|
|
30
|
+
`@pienter/ui/components/X.astro` imports to
|
|
31
|
+
`@pienter/ui/components/X.vue`; there is no Astro migration path. The
|
|
32
|
+
shared controllers Vue consumes (`mountDialog`, `mountDisclosure`,
|
|
33
|
+
`mountPopover`, `mountMenu`) are unchanged.
|
|
34
|
+
- `Sidebar` follows the D13 layered split instead of a shared controller, so
|
|
35
|
+
`mountSidebar` is no longer exported from `@pienter/ui/utils/controllers`.
|
|
36
|
+
`utils/controllers/` is for code both tiers run; Sidebar's imperative
|
|
37
|
+
orchestration is Astro-only and now lives beside the component, exported as
|
|
38
|
+
`@pienter/ui/components/Sidebar.mount`. Vue no longer wraps it at all —
|
|
39
|
+
`Sidebar.vue` is reactive throughout, which is what D13 already required
|
|
40
|
+
("wrapping an imperative DOM controller from Vue fights Vue's data flow…
|
|
41
|
+
the controller mutates the DOM, Vue patches it back, and race conditions
|
|
42
|
+
ensue"). Both tiers now derive from shared primitives in
|
|
43
|
+
`@pienter/ui/utils/navigation/sidebar`: `isHrefActive`, `deriveGroupKey`,
|
|
44
|
+
`computeSubmenuPosition`, `isSubmenuActive`, `groupContainsActive`.
|
|
45
|
+
`isHrefActive` and `deriveGroupKey` moved there from the controller barrel.
|
|
46
|
+
- `Sidebar`'s `items` prop is now `topItems`, alongside a new `bottomItems`.
|
|
47
|
+
The column has two nav regions — the top nav, which takes the leftover
|
|
48
|
+
height and scrolls, and the bottom nav, pinned to the foot below a divider
|
|
49
|
+
for settings, account and the like. Entries in either get identical wiring:
|
|
50
|
+
active-href matching, keyboard navigation, and collapsed flyouts for their
|
|
51
|
+
groups. The `#footer` slot is renamed `#bottom` and still takes arbitrary
|
|
52
|
+
content beside `bottomItems`. Migrate `:items="nav"` to `:top-items="nav"`.
|
|
53
|
+
- The private recursive renderer is `SidebarMenuItem` (was
|
|
54
|
+
`SidebarItemRender`), matching the `data-sidebar-menu-item` hook it emits.
|
|
55
|
+
Internal to the component — no export changed — but the file names did.
|
|
56
|
+
- The nav list classes are `.pui-sidebar__top-nav` and
|
|
57
|
+
`.pui-sidebar__bottom-nav` (was `.pui-sidebar__menu`), and the foot wrapper
|
|
58
|
+
is `.pui-sidebar__bottom` (was `.pui-sidebar__footer`). Consumers styling
|
|
59
|
+
those internals will need to follow.
|
|
60
|
+
- `Sidebar` no longer uses a roving tabindex. Every menu entry is its own tab
|
|
61
|
+
stop, as an ordinary `<nav>` full of links should be. Roving left the whole
|
|
62
|
+
sidebar with a **single** tab stop, so a rail of four destinations had three
|
|
63
|
+
that Tab could never reach. This also matches CONVENTIONS.md, which reserves
|
|
64
|
+
roving for "single-focus-per-group controls … where one item represents the
|
|
65
|
+
entire group's focus target" — a navigation landmark is not that. Arrow keys,
|
|
66
|
+
Home and End still work, now as an accelerator on top of the natural order
|
|
67
|
+
rather than a replacement for it. Consumers relying on exactly one sidebar tab
|
|
68
|
+
stop will see more; nothing else changes.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- `--space-sidebar-width-collapsed` is `4.75rem` (was `4rem`). At 4rem the new
|
|
73
|
+
rail captions had about 2rem of usable width once the panel's padding was
|
|
74
|
+
taken out, and real labels ellipsised to two characters.
|
|
75
|
+
- The `Sidebar` focus ring colour follows the entry's tone via
|
|
76
|
+
`--pui-sidebar-ring`, matching how `--btn-ring` works for Button: neutral by
|
|
77
|
+
default, brand on the entry wearing the brand wash, so the ring no longer
|
|
78
|
+
fights the fill underneath it.
|
|
79
|
+
- The collapsed rail marks which icon the open flyout belongs to. Previously
|
|
80
|
+
only hover did, so a flyout opened from the keyboard left nothing pointing
|
|
81
|
+
back at its icon once focus moved into the panel.
|
|
82
|
+
- The collapsed submenu flyout now sits on `--layer-below`, so it genuinely
|
|
83
|
+
passes under the rail rather than across it — the panel's own background
|
|
84
|
+
hides where the slide starts. The open-flyout rule pairs the rung with
|
|
85
|
+
`isolation: isolate`, because "behind the rail" only holds while the sidebar
|
|
86
|
+
is the stacking context containing the flyout; without it the panel would go
|
|
87
|
+
behind the *page* in any shell that doesn't happen to give the root a
|
|
88
|
+
context.
|
|
89
|
+
- The collapsed submenu flyout slides in from behind the rail when it opens,
|
|
90
|
+
and disappears instantly when it closes. The transition is declared only on
|
|
91
|
+
the open state — a transition belongs to the state being moved *to* — so
|
|
92
|
+
there is nothing to undeclare for the closing direction. An exit animation on
|
|
93
|
+
a dismissal only delays the content behind it, and the panel covers page
|
|
94
|
+
content. `visibility` stays untransitioned so the entries are focusable the
|
|
95
|
+
instant the group takes focus; under `prefers-reduced-motion` the panel
|
|
96
|
+
simply appears.
|
|
97
|
+
- The collapsed `Sidebar` rail stacks a caption under each icon instead of
|
|
98
|
+
going icon-only, so it is navigable without hovering. The rail sets its own
|
|
99
|
+
inline padding rather than inheriting the expanded panel's, and captions wrap
|
|
100
|
+
to a second line instead of truncating — an ellipsis hides the one thing a
|
|
101
|
+
caption is for.
|
|
102
|
+
- A collapsed rail group whose subtree contains the current page carries the
|
|
103
|
+
brand wash. On the rail its children are hidden, so the group is the only
|
|
104
|
+
indicator of location, and a tint alone lost to the hover fill — a hovered
|
|
105
|
+
entry read as more current than the current one. The wash is reverted in
|
|
106
|
+
drawer mode, where the sidebar is a full expanded list: there the group sits
|
|
107
|
+
directly above its own current child and two identical fills merged into a
|
|
108
|
+
single block. Brand ink alone carries "this section holds the current page"
|
|
109
|
+
there, and the wash is left to the entry that actually is the current page.
|
|
110
|
+
- The `Sidebar` focus ring uses `--outline-width` / `--outline-offset` instead
|
|
111
|
+
of hard-coded `3px` / `2px`. It is the standard outset ring, so it reads as a
|
|
112
|
+
ring around the entry rather than a line cutting into it and stays legible
|
|
113
|
+
over the selected entry's wash; the collapsed rail reserves inline padding so
|
|
114
|
+
the column edge never clips it.
|
|
115
|
+
- A `Sidebar` link's selected state uses `--bg-clr-brand-soft` rather than
|
|
116
|
+
`--bg-clr-accent`, which is what hover already uses — the current page was
|
|
117
|
+
indistinguishable from whatever the pointer was over.
|
|
118
|
+
|
|
119
|
+
### Fixed
|
|
120
|
+
|
|
121
|
+
- `Sidebar`'s focus ring is no longer clipped in the top nav. That list is a
|
|
122
|
+
scroll container — `overflow-y: auto` forces `overflow-x: auto` — and a
|
|
123
|
+
scroll container clips at its padding box, so an entry filling it
|
|
124
|
+
edge-to-edge had the ring sliced off both sides. The list now reserves the
|
|
125
|
+
ring's width as `padding-inline` with a matching negative `margin-inline`,
|
|
126
|
+
so nothing moves.
|
|
127
|
+
- Consumers must import `@pienter/ui/styles` before any component. Each
|
|
128
|
+
component stylesheet is a bare `@layer components { … }`, and layer order is
|
|
129
|
+
fixed by first encounter — so a component loading first registers
|
|
130
|
+
`components` ahead of `base`, and every base element rule starts outranking
|
|
131
|
+
component rules. In the Vue playground that made base's `a { color: … }` beat
|
|
132
|
+
`.pui-sidebar__link`, turning sidebar links brand-coloured. The playground
|
|
133
|
+
entry point now imports the stylesheet first; the library contract is
|
|
134
|
+
unchanged (main.css declares the order once).
|
|
135
|
+
- The mobile `Sidebar` drawer no longer paints under the consumer's app header.
|
|
136
|
+
The rule that lifts the sidebar while a collapsed flyout is open applied at
|
|
137
|
+
every width, and a `z-index` other than `auto` makes a **grid or flex item** a
|
|
138
|
+
stacking context even while `position: static` — which is what a sidebar in a
|
|
139
|
+
layout shell normally is. The drawer's own `z-index: 50` was therefore sealed
|
|
140
|
+
inside that context and lost to any header above 30. The lift is now scoped
|
|
141
|
+
above the drawer breakpoint, where the flyout actually exists, and the drawer
|
|
142
|
+
lifts the root itself while open.
|
|
143
|
+
- `Sidebar`'s focus handler no longer rewrites every entry's `tabindex`. The
|
|
144
|
+
roving pattern was removed from the initial render but survived in the
|
|
145
|
+
`focusin` handler, which demoted every other entry to `-1` the moment one was
|
|
146
|
+
focused — so the sidebar looked fully tabbable until you actually tabbed into
|
|
147
|
+
it, and then collapsed to a single stop.
|
|
148
|
+
- A collapsed submenu flyout's entries are reachable by Tab. `visibility` was in
|
|
149
|
+
the flyout's transition list, so it flipped partway through the fade and the
|
|
150
|
+
entries were still unfocusable when Tab arrived — landing on a group jumped
|
|
151
|
+
straight past its flyout to the next rail item. `visibility` now steps
|
|
152
|
+
immediately on open and is delayed only on close, so the fade-out still plays.
|
|
153
|
+
- The collapsed rail no longer stays visible below `drawerBreakpoint`. The
|
|
154
|
+
drawer's `width: 0` sat on a bare `.pui-sidebar` inside a media query, which
|
|
155
|
+
grants no specificity, so `.pui-sidebar[data-state='collapsed']` outranked it
|
|
156
|
+
and left an empty bordered strip down the page on mobile.
|
|
157
|
+
- The collapsed submenu flyout aligns flush with the top of the sidebar column
|
|
158
|
+
instead of the menu region. The menu's box starts inside the panel's padding,
|
|
159
|
+
which left a visible seam above the flyout. With a `#brand` slot filled the
|
|
160
|
+
flyout starts below the brand, so it still never covers the wordmark.
|
|
161
|
+
- `Icon.astro` forwards a `class` prop instead of silently dropping it. It
|
|
162
|
+
hardcoded `class="pui-icon"` and spread the rest of the props after, so an
|
|
163
|
+
incoming class was discarded — Vue applied it, Astro did not. One casualty
|
|
164
|
+
was `Sidebar`'s `pui-sidebar__group-caret` hook, which meant the caret never
|
|
165
|
+
rotated on expand in Astro.
|
|
166
|
+
- A `Sidebar` brand slot no longer spills across the page when collapsed. The
|
|
167
|
+
rule that was meant to hide everything without `data-keep-when-collapsed` was
|
|
168
|
+
an empty placeholder, so a wordmark kept its full width in a rail narrower
|
|
169
|
+
than most product names.
|
|
170
|
+
- The collapsed submenu flyout no longer renders behind page content. It is
|
|
171
|
+
`position: fixed`, which escapes overflow but not a stacking context, and
|
|
172
|
+
consumers routinely make the sidebar `position: sticky` for scroll — which
|
|
173
|
+
creates one. The root now raises its own z-index while a flyout is open.
|
|
174
|
+
- A closed flyout is positioned as soon as it enters panel mode. A fixed
|
|
175
|
+
element with no coordinates resolves to its static position inside the rail,
|
|
176
|
+
so anything that made it briefly visible flashed the panel over the icons.
|
|
177
|
+
|
|
178
|
+
### Added
|
|
179
|
+
|
|
180
|
+
- `Sidebar` takes a `bottomItems` prop — the bottom nav. Same
|
|
181
|
+
`SidebarItem[]` shape as `topItems`, rendered at the foot of the column
|
|
182
|
+
below a divider, for settings / account / help. See the Breaking entry for
|
|
183
|
+
the accompanying `items` → `topItems` rename.
|
|
184
|
+
- `Sidebar` takes a `collapsedSubmenu` prop (`'hidden' | 'panel'`, default
|
|
185
|
+
`'hidden'`). With `'panel'`, a collapsed rail's groups are no longer a dead
|
|
186
|
+
end: hover, focus, or click opens a group's children in a full-height flyout
|
|
187
|
+
beside the rail. Escape closes it and returns focus to the group toggle;
|
|
188
|
+
clicking the toggle pins the flyout so it survives pointer-out; only one
|
|
189
|
+
flyout is open at a time. The default keeps the previous behavior, so
|
|
190
|
+
existing consumers are unaffected.
|
|
191
|
+
- `mountSidebar` gains `openSubmenu(key)`, `closeSubmenu()`, and
|
|
192
|
+
`openSubmenuKey()`, plus a `collapsedSubmenu` config field. They are no-ops
|
|
193
|
+
unless the policy is active — which needs the sidebar collapsed and the
|
|
194
|
+
viewport above `drawerBreakpoint`, since below it there is no rail.
|
|
195
|
+
- A stacking ladder: `--layer-below`, `--layer-0` … `--layer-5`, `--layer-top`.
|
|
196
|
+
Every positioned layer in the library names a rung instead of inventing a
|
|
197
|
+
number, so the whole order is legible in one place and two components cannot
|
|
198
|
+
accidentally tie. `Toast`, `Table`'s loading pane, `NumberField`'s focused
|
|
199
|
+
step button and every `Sidebar` layer moved onto it; the numbers they
|
|
200
|
+
resolve to are unchanged.
|
|
201
|
+
- `--shadow-lg`, for a surface that covers page content rather than sitting on
|
|
202
|
+
it. `--shadow-md` is tuned for a card in the flow and reads flat under
|
|
203
|
+
something as large as a sidebar flyout.
|
|
204
|
+
|
|
5
205
|
## 0.3.0 - 2026-08-26
|
|
6
206
|
|
|
7
207
|
### Added
|