@jonmilner/ui 0.8.0 → 0.8.1
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/dist/MultiSelect/MultiSelect.css +1 -1
- package/dist/MultiSelect/client.d.mts +1 -0
- package/dist/MultiSelect/client.mjs +5 -4
- package/dist/MultiSelect/index.d.mts +2 -1
- package/dist/MultiSelect/index.mjs +12 -10
- package/dist/docs/MultiSelect.md +4 -2
- package/dist/docs/manifest.json +11 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@layer tokens, reset, atoms, components.box, components.checkbox, components.clickable, components.icon, components.icon-button, components.inline, components.stack, components.text, components.avatar, components.badge, components.button, components.chip, components.field, components.color-field, components.heading, components.dialog, components.alert-dialog, components.menu, components.multiselect, components.search-field, components.tabs, components.tag, components.text-field, components.toast, components.visually-hidden;
|
|
2
|
-
@layer components.multiselect{._1seauxl0{text-align:start;justify-content:stretch}:where(._1seauxl0) ._1gwwloa0{flex:auto;justify-items:stretch;min-inline-size:0}:where(._1seauxl0) ._1gwwloa1{min-inline-size:0}:where(._1seauxl0) ._1gwwloa2{flex:auto;min-inline-size:0}._1seauxl1{min-inline-size:0;display:block}._1seauxl1[data-placeholder]{color:var(--color-fg-muted)}._1seauxl2{min-inline-size:0;display:grid}._1seauxl3{grid-area:1/1}._1seauxl3[aria-hidden=true]{visibility:hidden}._1seauxl4{grid-area:1/1}._1seauxl5{border:var(--space-1) solid var(--color-border);min-inline-size:var(--anchor-width);max-block-size:var(--available-height);transform-origin:var(--transform-origin);transition:opacity var(--duration-fast) var(--easing-standard), scale var(--duration-fast) var(--easing-enter);overflow-y:auto}._1seauxl5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._1seauxl5[data-starting-style],._1seauxl5[data-ending-style]{opacity:0;scale:calc(1 - var(--scale-med))}._1seauxl6{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}._1seauxl6:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._1seauxl6[data-highlighted]{background-color:light-dark(var(--color-bg-muted),color-mix(in srgb, var(--color-fg) 10%, var(--color-bg-raised)))}._1seauxl6[aria-disabled=true]{color:var(--color-fg-muted);cursor:not-allowed}._1seauxl7{visibility:hidden;flex-shrink:0}._1seauxl6[aria-selected=true] ._1seauxl7{visibility:visible}@media (forced-colors:active){._1seauxl6[data-highlighted]{forced-color-adjust:none;color:highlighttext;background-color:highlight}._1seauxl6[aria-disabled=true]{color:graytext}}}
|
|
2
|
+
@layer components.multiselect{._1seauxl0{text-align:start;justify-content:stretch}:where(._1seauxl0) ._1gwwloa0{flex:auto;justify-items:stretch;min-inline-size:0}:where(._1seauxl0) ._1gwwloa1{min-inline-size:0}:where(._1seauxl0) ._1gwwloa2{flex:auto;min-inline-size:0}._1seauxl1{min-inline-size:0;display:block}._1seauxl1[data-placeholder]{color:var(--color-fg-muted)}._1seauxl2{min-inline-size:0;display:grid}._1seauxl3{grid-area:1/1}._1seauxl3[aria-hidden=true]{visibility:hidden}._1seauxl4{grid-area:1/1}._1seauxl5{border:var(--space-1) solid var(--color-border);min-inline-size:var(--anchor-width);max-block-size:var(--available-height);transform-origin:var(--transform-origin);transition:opacity var(--duration-fast) var(--easing-standard), scale var(--duration-fast) var(--easing-enter);overflow-y:auto}._1seauxl5:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:var(--space-2)}._1seauxl5[data-starting-style],._1seauxl5[data-ending-style]{opacity:0;scale:calc(1 - var(--scale-med))}._1seauxl6{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}._1seauxl6:focus-visible{outline:var(--space-2) solid var(--color-focus);outline-offset:calc(-1 * var(--space-2))}._1seauxl6[data-highlighted]{background-color:light-dark(var(--color-bg-muted),color-mix(in srgb, var(--color-fg) 10%, var(--color-bg-raised)))}._1seauxl6[aria-disabled=true]{color:var(--color-fg-muted);cursor:not-allowed}._1seauxl7{visibility:hidden;flex-shrink:0}._1seauxl6[aria-selected=true] ._1seauxl7{visibility:visible}@media (forced-colors:active){._1seauxl6[data-highlighted]{forced-color-adjust:none;color:highlighttext;background-color:highlight}._1seauxl6[data-highlighted]:focus-visible{color:canvastext;background-color:canvas}._1seauxl6[aria-disabled=true]{color:graytext}}}
|
|
@@ -10,13 +10,14 @@ import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
|
10
10
|
import { createContext as _, use as v, useId as y, useLayoutEffect as b, useMemo as x, useRef as S, useState as C } from "react";
|
|
11
11
|
import { Select as w } from "@base-ui/react/select";
|
|
12
12
|
//#region src/MultiSelect/client.tsx
|
|
13
|
-
function T({ options: e, placeholder: t, countLabel: n, disabled: r }) {
|
|
14
|
-
let { ref:
|
|
13
|
+
function T({ options: e, placeholder: t, countLabel: n, disabled: r, size: a }) {
|
|
14
|
+
let { ref: s } = o(), c = x(() => new Map(e.map((e) => [e.value, e.label])), [e]);
|
|
15
15
|
return /* @__PURE__ */ h(w.Trigger, {
|
|
16
16
|
disabled: r,
|
|
17
|
-
ref:
|
|
17
|
+
ref: s,
|
|
18
18
|
render: i({
|
|
19
19
|
variant: "secondary",
|
|
20
|
+
size: a,
|
|
20
21
|
iconEnd: l,
|
|
21
22
|
className: p,
|
|
22
23
|
children: /* @__PURE__ */ h(w.Value, {
|
|
@@ -24,7 +25,7 @@ function T({ options: e, placeholder: t, countLabel: n, disabled: r }) {
|
|
|
24
25
|
placeholder: t,
|
|
25
26
|
children: (e) => e.length > 0 ? /* @__PURE__ */ h(E, {
|
|
26
27
|
count: n.replaceAll("{count}", String(e.length)),
|
|
27
|
-
labels: e.map((e) =>
|
|
28
|
+
labels: e.map((e) => c.get(e) ?? e)
|
|
28
29
|
}) : t
|
|
29
30
|
})
|
|
30
31
|
})
|
|
@@ -16,8 +16,9 @@ export type MultiSelectProps = StyleProps & {
|
|
|
16
16
|
options: readonly MultiSelectOption[];
|
|
17
17
|
placeholder?: string;
|
|
18
18
|
required?: boolean;
|
|
19
|
+
size?: "sm" | "med" | "lg";
|
|
19
20
|
style?: CSSProperties;
|
|
20
21
|
value?: readonly string[];
|
|
21
22
|
};
|
|
22
|
-
export declare function MultiSelect({ options, value, defaultValue, onValueChange, label, description, error, placeholder, countLabel, disabled, required, name, className, style, ...atoms }: MultiSelectProps): import("react").JSX.Element;
|
|
23
|
+
export declare function MultiSelect({ options, value, defaultValue, onValueChange, label, description, error, placeholder, countLabel, disabled, required, size, name, className, style, ...atoms }: MultiSelectProps): import("react").JSX.Element;
|
|
23
24
|
//#endregion
|
|
@@ -7,18 +7,18 @@ import { MultiSelectLabel as o, MultiSelectPopup as s, MultiSelectPortal as c, M
|
|
|
7
7
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
8
8
|
import { Select as h } from "@base-ui/react/select";
|
|
9
9
|
//#region src/MultiSelect/index.tsx
|
|
10
|
-
function g({ options: g, value: _, defaultValue: v, onValueChange: y, label: b, description: x, error: S, placeholder: C = "Select", countLabel: w = "{count} selected", disabled: T = !1, required: E = !1,
|
|
10
|
+
function g({ options: g, value: _, defaultValue: v, onValueChange: y, label: b, description: x, error: S, placeholder: C = "Select", countLabel: w = "{count} selected", disabled: T = !1, required: E = !1, size: D = "med", name: O, className: k, style: A, ...j }) {
|
|
11
11
|
return /* @__PURE__ */ m(r, {
|
|
12
|
-
...
|
|
13
|
-
className:
|
|
12
|
+
...j,
|
|
13
|
+
className: k,
|
|
14
14
|
disabled: T,
|
|
15
15
|
invalid: S != null && S !== !1 && S !== "",
|
|
16
|
-
style:
|
|
16
|
+
style: A,
|
|
17
17
|
children: [
|
|
18
18
|
/* @__PURE__ */ p(d, { children: /* @__PURE__ */ m(u, {
|
|
19
19
|
defaultValue: v,
|
|
20
20
|
disabled: T,
|
|
21
|
-
name:
|
|
21
|
+
name: O,
|
|
22
22
|
onValueChange: y,
|
|
23
23
|
required: E,
|
|
24
24
|
value: _,
|
|
@@ -28,7 +28,8 @@ function g({ options: g, value: _, defaultValue: v, onValueChange: y, label: b,
|
|
|
28
28
|
countLabel: w,
|
|
29
29
|
disabled: T,
|
|
30
30
|
options: g,
|
|
31
|
-
placeholder: C
|
|
31
|
+
placeholder: C,
|
|
32
|
+
size: D
|
|
32
33
|
}),
|
|
33
34
|
/* @__PURE__ */ p(c, { children: /* @__PURE__ */ p(l, { children: /* @__PURE__ */ p(s, { children: g.map((r) => /* @__PURE__ */ m(h.Item, {
|
|
34
35
|
disabled: r.disabled,
|
|
@@ -39,20 +40,21 @@ function g({ options: g, value: _, defaultValue: v, onValueChange: y, label: b,
|
|
|
39
40
|
className: a,
|
|
40
41
|
display: "flex",
|
|
41
42
|
gap: "8",
|
|
43
|
+
justifyContent: "space-between",
|
|
42
44
|
minBlockSize: "sm",
|
|
43
45
|
paddingInline: "12"
|
|
44
46
|
}),
|
|
45
47
|
value: r.value,
|
|
46
|
-
children: [/* @__PURE__ */ p(
|
|
47
|
-
className: i,
|
|
48
|
-
size: "sm"
|
|
49
|
-
}), /* @__PURE__ */ p(h.ItemText, {
|
|
48
|
+
children: [/* @__PURE__ */ p(h.ItemText, {
|
|
50
49
|
render: /* @__PURE__ */ p(t, {
|
|
51
50
|
as: "span",
|
|
52
51
|
font: "label",
|
|
53
52
|
size: "sm"
|
|
54
53
|
}),
|
|
55
54
|
children: r.label
|
|
55
|
+
}), /* @__PURE__ */ p(n, {
|
|
56
|
+
className: i,
|
|
57
|
+
size: "sm"
|
|
56
58
|
})]
|
|
57
59
|
}, r.value)) }) }) })
|
|
58
60
|
]
|
package/dist/docs/MultiSelect.md
CHANGED
|
@@ -25,6 +25,7 @@ A form field for picking any number of values from a list too long to show at on
|
|
|
25
25
|
- `value` with `onValueChange` controls it; `defaultValue` leaves it uncontrolled. Both are arrays of option values, and `onValueChange` gets the new array.
|
|
26
26
|
- `name` makes each selected value submit as its own `name=value` pair, as a `<select multiple>` does; read them with `formData.getAll(name)`. Nothing selected submits nothing.
|
|
27
27
|
- `placeholder` (`"Select"`) shows while nothing is selected. `countLabel` (`"{count} selected"`) is the summary once the joined labels do not fit; `{count}` is replaced with the number selected. Translate both. One selected label that does not fit is truncated, not replaced by a count.
|
|
28
|
+
- `size` (`sm`, `med`, `lg`; default `med`) is the trigger's, as Button's: it matches a SearchField, TextField or Button of the same size in height. The list's options do not change size.
|
|
28
29
|
- `disabled` takes the trigger out of the tab order and mutes the label. `required` sets `aria-required` on the trigger; report a missing value through `error`.
|
|
29
30
|
- The trigger and the open listbox are both named by `label`.
|
|
30
31
|
- Style props, `className` and `style` go to the `Field` around the label, trigger and messages, not to the trigger or the popup.
|
|
@@ -91,9 +92,9 @@ A form field for picking any number of values from a list too long to show at on
|
|
|
91
92
|
- The trigger is a button with `aria-haspopup="listbox"` and `aria-expanded`, labelled by the `Field` label and described by the description and error. The popup is `role="listbox"` with `aria-multiselectable="true"`, options are `role="option"` with `aria-selected`.
|
|
92
93
|
- Opening from the keyboard focuses an option; focus stays in the list until it closes. Choosing an option toggles it and leaves the list open, so several can be picked in one visit. Closed by Esc or the trigger, focus returns at once to the trigger, so a Tab pressed while the list fades goes on to the next control. A press outside or a Tab out leaves focus to Base UI.
|
|
93
94
|
- A closed list stays on the page while it fades out, but is `inert` and `aria-hidden` from the moment it closes, so a list opened during the fade (the next filter's) is the only one a screen reader or a role query finds, and nothing in the fading one takes focus or a click (ADR 0113).
|
|
94
|
-
- Selection is a check mark on the option as well as its highlight, so it is not color alone. The check
|
|
95
|
+
- Selection is a check mark on the option as well as its highlight, so it is not color alone. The check sits at the end of the row; its space is always kept, so the labels start at the option's padding and never shift.
|
|
95
96
|
- The trigger summary is text: the joined labels, or the count. When the count replaces the labels, the labels are hidden from assistive technology so the count is read once.
|
|
96
|
-
- The shared focus ring shows on the trigger and, from the keyboard, on the highlighted option. Under forced colors the highlighted option is `Highlight` with `HighlightText
|
|
97
|
+
- The shared focus ring shows on the trigger and, from the keyboard, on the highlighted option, drawn inset so the popup's edge does not crowd or clip it (ADR 0122). Under forced colors the pointer's highlighted option is `Highlight` with `HighlightText`; a keyboard-focused one keeps `Canvas` so the `Highlight` ring shows, and the popup keeps its border.
|
|
97
98
|
|
|
98
99
|
## Props
|
|
99
100
|
|
|
@@ -115,6 +116,7 @@ Generated from the TypeScript types.
|
|
|
115
116
|
| `options` | `readonly Option[]` | yes | |
|
|
116
117
|
| `placeholder` | `string` | no | |
|
|
117
118
|
| `required` | `boolean` | no | |
|
|
119
|
+
| `size` | `"lg" \| "med" \| "sm"` | no | |
|
|
118
120
|
| `style` | `CSSProperties` | no | |
|
|
119
121
|
| `value` | `readonly string[]` | no | |
|
|
120
122
|
|
package/dist/docs/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./manifest.schema.json",
|
|
3
3
|
"package": "@jonmilner/ui",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "AlertDialog",
|
|
@@ -1615,6 +1615,16 @@
|
|
|
1615
1615
|
"type": "boolean",
|
|
1616
1616
|
"required": false
|
|
1617
1617
|
},
|
|
1618
|
+
{
|
|
1619
|
+
"name": "size",
|
|
1620
|
+
"type": "\"lg\" | \"med\" | \"sm\"",
|
|
1621
|
+
"required": false,
|
|
1622
|
+
"values": [
|
|
1623
|
+
"lg",
|
|
1624
|
+
"med",
|
|
1625
|
+
"sm"
|
|
1626
|
+
]
|
|
1627
|
+
},
|
|
1618
1628
|
{
|
|
1619
1629
|
"name": "style",
|
|
1620
1630
|
"type": "CSSProperties",
|