@pienter/ui 0.22.0 → 0.23.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
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.23.0 - 2026-09-16
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- `SelectButton` (`@pienter/ui/components/SelectButton.vue`), a native
|
|
9
|
+
`<select>` dressed as a Button for chrome such as a header toolbar: `label`
|
|
10
|
+
(required, becomes the `aria-label` and is never rendered, as on
|
|
11
|
+
`IconButton`), `options`, `modelValue` with `update:modelValue`, `name`,
|
|
12
|
+
`disabled`, and Button's own `variant` and `size` unions and defaults, so
|
|
13
|
+
a `sm` `ghost` picker sits beside a `sm` `ghost` Button at the same height,
|
|
14
|
+
padding, colours and focus ring. The root is a `.pui-btn` mix and the
|
|
15
|
+
select lies invisibly over it, so the whole button opens the native picker
|
|
16
|
+
and keyboard and screen-reader behaviour stay the browser's. The form
|
|
17
|
+
`Select` is deliberately unchanged: it keeps no `size` prop and no hidden
|
|
18
|
+
label, so forms stay one consistent scaffold.
|
|
19
|
+
|
|
5
20
|
## 0.22.0 - 2026-09-16
|
|
6
21
|
### Added
|
|
7
22
|
|
package/CONVENTIONS.md
CHANGED
|
@@ -1305,6 +1305,7 @@ follow the shape of an existing one.
|
|
|
1305
1305
|
| RadioGroup | [`components/form/radio-group/AUDIT.md`](./components/form/radio-group/AUDIT.md) |
|
|
1306
1306
|
| Segmented | [`components/form/select/AUDIT.md`](./components/form/select/AUDIT.md) |
|
|
1307
1307
|
| Select | [`components/form/select/AUDIT.md`](./components/form/select/AUDIT.md) |
|
|
1308
|
+
| SelectButton | [`components/action/select-button/AUDIT.md`](./components/action/select-button/AUDIT.md) |
|
|
1308
1309
|
| Separator | [`components/layout/separator/AUDIT.md`](./components/layout/separator/AUDIT.md) |
|
|
1309
1310
|
| Sheet | [`components/overlay/sheet/AUDIT.md`](./components/overlay/sheet/AUDIT.md) |
|
|
1310
1311
|
| Sidebar | [`components/navigation/sidebar/AUDIT.md`](./components/navigation/sidebar/AUDIT.md) |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class="pui-btn pui-select-btn"
|
|
4
|
+
:data-variant="variant"
|
|
5
|
+
:data-size="size"
|
|
6
|
+
>
|
|
7
|
+
<span class="pui-select-btn__label" aria-hidden="true">
|
|
8
|
+
<span
|
|
9
|
+
v-for="opt in options"
|
|
10
|
+
:key="opt.value"
|
|
11
|
+
class="pui-select-btn__option"
|
|
12
|
+
:data-state="opt.value === modelValue ? 'selected' : undefined"
|
|
13
|
+
>{{ opt.label }}</span
|
|
14
|
+
>
|
|
15
|
+
</span>
|
|
16
|
+
<span class="pui-btn__icon" aria-hidden="true">
|
|
17
|
+
<Icon name="chevron-down" size="sm" />
|
|
18
|
+
</span>
|
|
19
|
+
<select
|
|
20
|
+
v-bind="$attrs"
|
|
21
|
+
class="pui-select-btn__control"
|
|
22
|
+
:name="name"
|
|
23
|
+
:disabled="disabled"
|
|
24
|
+
:value="modelValue"
|
|
25
|
+
:aria-label="label"
|
|
26
|
+
@change="onChange"
|
|
27
|
+
>
|
|
28
|
+
<option v-for="opt in options" :key="opt.value" :value="opt.value">
|
|
29
|
+
{{ opt.label }}
|
|
30
|
+
</option>
|
|
31
|
+
</select>
|
|
32
|
+
</span>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import Icon from '../../display/icon/Icon.vue';
|
|
37
|
+
|
|
38
|
+
defineOptions({ inheritAttrs: false });
|
|
39
|
+
|
|
40
|
+
withDefaults(
|
|
41
|
+
defineProps<{
|
|
42
|
+
/** Accessible name for the control (required — the label is never rendered). */
|
|
43
|
+
label: string;
|
|
44
|
+
modelValue?: string;
|
|
45
|
+
options: { value: string; label: string }[];
|
|
46
|
+
name?: string;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'link';
|
|
49
|
+
size?: 'sm' | 'md';
|
|
50
|
+
}>(),
|
|
51
|
+
{
|
|
52
|
+
modelValue: undefined,
|
|
53
|
+
name: undefined,
|
|
54
|
+
disabled: false,
|
|
55
|
+
variant: 'secondary',
|
|
56
|
+
size: 'md',
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const emit = defineEmits<{
|
|
61
|
+
'update:modelValue': [value: string];
|
|
62
|
+
}>();
|
|
63
|
+
|
|
64
|
+
function onChange(event: Event): void {
|
|
65
|
+
emit('update:modelValue', (event.target as HTMLSelectElement).value);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style>
|
|
70
|
+
@import './select-button.css';
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* The block is a `.pui-btn` mix: button.css draws the box, this file only
|
|
3
|
+
lays the native select over it as the hit area and focus target. */
|
|
4
|
+
.pui-select-btn {
|
|
5
|
+
/* the select is transparent, so the wrapper wears its ring */
|
|
6
|
+
&:has(.pui-select-btn__control:focus-visible) {
|
|
7
|
+
outline: var(--outline-width) solid
|
|
8
|
+
var(--btn-ring, var(--border-clr-strong));
|
|
9
|
+
outline-offset: var(--outline-offset);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* a span never matches :disabled; no pointer events keeps the shared hover and active rules off */
|
|
13
|
+
&:has(.pui-select-btn__control:disabled) {
|
|
14
|
+
opacity: var(--opacity-disabled);
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* every option label stacked in one cell holds the widest width, as a native select does */
|
|
19
|
+
.pui-select-btn__label {
|
|
20
|
+
display: inline-grid;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.pui-select-btn__option {
|
|
24
|
+
grid-area: 1 / 1;
|
|
25
|
+
visibility: hidden;
|
|
26
|
+
|
|
27
|
+
&[data-state='selected'] {
|
|
28
|
+
visibility: visible;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.pui-select-btn__control {
|
|
33
|
+
position: absolute;
|
|
34
|
+
inset: 0;
|
|
35
|
+
inline-size: 100%;
|
|
36
|
+
block-size: 100%;
|
|
37
|
+
margin: 0;
|
|
38
|
+
padding: 0;
|
|
39
|
+
border: 0;
|
|
40
|
+
appearance: none;
|
|
41
|
+
opacity: 0;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pienter/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Shared Pienter UI components, styles, icons, and browser utilities.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"./styles/*": "./styles/*",
|
|
30
30
|
"./components/Button.vue": "./components/action/button/Button.vue",
|
|
31
31
|
"./components/IconButton.vue": "./components/action/button/IconButton.vue",
|
|
32
|
+
"./components/SelectButton.vue": "./components/action/select-button/SelectButton.vue",
|
|
32
33
|
"./components/Toggle.vue": "./components/action/toggle/Toggle.vue",
|
|
33
34
|
"./components/ToggleGroup.vue": "./components/action/toggle-group/ToggleGroup.vue",
|
|
34
35
|
"./components/Avatar.vue": "./components/display/avatar/Avatar.vue",
|