plass-ui 1.8.1 → 1.8.2
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 +23 -0
- package/dist/components/checkbox/PlCheckbox.js +1 -1
- package/dist/components/combobox/PlCombobox.d.ts +61 -8
- package/dist/components/combobox/PlCombobox.js +1 -1
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/radio-group/PlRadioGroup.js +1 -1
- package/dist/components/switch/PlSwitch.js +1 -1
- package/dist/internal/form.d.ts +10 -7
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
## vNext (2026--)
|
|
6
6
|
|
|
7
|
+
## 1.8.2 (2026-10-09)
|
|
8
|
+
|
|
9
|
+
### Breaking changes
|
|
10
|
+
|
|
11
|
+
- **Escape no longer empties a `PlCombobox` whose list is closed.** One stray Escape took every chip off a `multiple` field, or the chosen value off a single one, and a caller could not stop it. The value now stays, and the key goes on to whatever the field sits in, so a `PlModal` round the field closes. Escape on an open list still closes it and lets go of the query. Pass `clearOnEscape` to have Escape empty the field as before.
|
|
12
|
+
- **`PlCombobox` calls `onValueChange`, `onInputValueChange` and `onOpenChange` with Base UI's event details as a second argument.** A caller could not tell what had changed the value, so an Escape and a press of the clear button looked the same. The second argument, typed `PlComboboxChangeEventDetails`, carries the `reason` (`'item-press'`, `'chip-remove-press'`, `'clear-press'`, `'escape-key'` and the rest) and the DOM `event`, and its `cancel()` turns the change away, so the field keeps what it held. A handler that takes one argument is unchanged. A test that checks a call with `toHaveBeenCalledWith(value)` now needs `expect.anything()` after the value.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`PlCombobox` takes `filter`, so a list a server has already searched is shown as it came.** Base UI filtered every row again by its label, so a row the server matched on another spelling, such as a city found by its English name and labelled in its own language, was hidden. `filter={null}` keeps every row as `items` gives it, and a function decides which rows a query keeps. The row that offers what was typed always stays.
|
|
17
|
+
- **`PlCombobox` takes `autoHighlight`.** The first row lit up only as the query changed and was not a setting. `true` stays the default, and now lights the first of the rows that arrive after the query changed as well, so a list a server fills a moment later is taken from with Enter. `'always'` also lights the first row whenever the open list has rows and none is lit, as when it opens with nothing typed, and `false` lights nothing until an arrow key or the pointer does.
|
|
18
|
+
- **A `multiple` `PlCombobox` takes a readonly array as `value` and `defaultValue`.** A caller holding the chosen values in a readonly array, or one written `as const`, had to copy it before handing it in, since TypeScript refused it for the mutable array the props asked for. The field never writes into it, and Base UI 1.9.0 takes one as it is.
|
|
19
|
+
- **A `PlComboboxOption` takes `content`, which its row draws in place of the label.** A row could only show its label, so a thumbnail or a second line had nowhere to go. The label is still what is filtered, written into the input and put on the chip.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`plass-ui` depends on `@base-ui/react` `^1.9.0`.** The fixes in Base UI 1.9.0 reach these components: a `PlCombobox` lights the first of the rows that arrive after the query changed and leaves no stale focus on a chip once the input has it back, a `PlSlider` ends a drag on `pointercancel` and `touchcancel`, and a `PlToastProvider` keeps a toast's timer paused through overlapping interactions. Base UI 1.9.0 also builds the filtering it adds to its menu into every menu, so a page with a `PlMenu` or a `PlContextMenu` grows by about 9 kB gzipped whether or not anything filters.
|
|
24
|
+
- **Enter on a `PlCombobox` list with no rows keeps the list open and the query in the input.** A list still waiting for its rows, or matching nothing, closed on Enter, which emptied the query in a `multiple` field and put the chosen label back in a single one, so the reader had to type the query again. Enter there now does nothing, and a form round the field is not sent with the list open. A list with rows and none lit still closes on Enter and lets the form be sent.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **A chosen `PlCombobox` value keeps its label once `items` no longer lists it.** A chip, and the text of a single field, read the label off the current `items`, so in a list a server answers, a value the latest query did not find was shown by its value, such as `prague` for Praha. The field now keeps the label each chosen value was last listed with, including one taken from a list that the next query emptied in the same press.
|
|
29
|
+
|
|
7
30
|
## 1.8.1 (2026-10-09)
|
|
8
31
|
|
|
9
32
|
### Breaking changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{useDisabled as t
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{useDisabled as t}from"../../internal/form.js";import{useDefaults as o}from"../../internal/defaults.js";import{Checkbox as l}from"@base-ui/react/checkbox";import{Field as n}from"@base-ui/react/field";import{controlSlots as i,cx as s,fieldDescriptionClasses as d,focusRingClasses as c,forcedCheckedClasses as p,forcedDisabledCheckedClasses as h,glassClasses as m,hasContent as f,metaTextClasses as u,tickRadiusClasses as b,tickRowTextClasses as k,tickSizeClasses as g,transitionClasses as x}from"../../internal/styles.js";const v=/* @__PURE__ */["relative inline-flex shrink-0 items-center justify-center border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","plass-fill [--p-fill-edge:1px]",x,c].join(" "),j=/* @__PURE__ */["forced-colors:data-[indeterminate]:[background-color:Highlight]","forced-colors:data-[indeterminate]:[border-color:Highlight]","forced-colors:data-[indeterminate]:[color:HighlightText]"].join(" "),N=/* @__PURE__ */[m,"cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]","hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105","data-[indeterminate]:[--p-fill-on:1] data-[indeterminate]:text-(--p-on-solid)","data-[indeterminate]:[border-color:transparent]",p,j].join(" "),y=/* @__PURE__ */[m,"cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]","saturate-[0.55]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]","data-[indeterminate]:[--p-fill-on:1] data-[indeterminate]:text-(--p-on-solid)","data-[indeterminate]:[border-color:transparent]",p,j].join(" "),C=/* @__PURE__ */[m,"cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]","opacity-50 saturate-[0.35]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]","data-[indeterminate]:[--p-fill-on:1] data-[indeterminate]:text-(--p-on-solid)","data-[indeterminate]:[border-color:transparent]",h,"forced-colors:data-[indeterminate]:[background-color:GrayText]","forced-colors:data-[indeterminate]:[color:Canvas]"].join(" "),M=/* @__PURE__ */["flex size-[70%] items-center justify-center","[&_path]:[stroke-dasharray:1] [&_path]:[stroke-dashoffset:0]","[&_path]:[transition:stroke-dashoffset_var(--plass-duration)_var(--plass-ease)]","motion-reduce:[&_path]:[transition-duration:0ms]","data-[unchecked]:[&_path]:[stroke-dashoffset:1]"].join(" ");function CheckMark(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2 6.2 4.6 8.8 10 3.4",pathLength:1,stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function DashMark(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2.5 6h7",pathLength:1,stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}export const PlCheckbox=/* @__PURE__ */r.forwardRef(function PlCheckbox({size:r,color:c,label:p,description:h,error:m,invalid:x,disabled:j=!1,readOnly:_=!1,className:w,classNames:L,style:z,"aria-label":B,"aria-labelledby":D,...H},R){const O=o(),P=t(j),T=r??O.size??"md",W=c??O.color??"primary",E=f(m),G=x??E,I=G?"danger":W,$=s(v,g[T],b[T],P?C:_?y:N,L?.control);return a(n.Root,{disabled:P,invalid:G,className:["inline-flex flex-col gap-1 align-top",w??""].filter(Boolean).join(" "),style:{...i(I,0,"solid"),...z},children:[a("div",{className:`flex items-start gap-2 ${k[T]}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(l.Root,{ref:R,className:$,disabled:P,readOnly:_,"aria-label":B,"aria-labelledby":D,...H,children:e(l.Indicator,{keepMounted:!0,className:M,children:H.indeterminate?e(DashMark,{}):e(CheckMark,{})})})}),p||h?a("span",{className:"flex min-w-0 flex-col gap-0.5",children:[p?e(n.Label,{className:s(P?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",L?.label),children:p}):null,h?e(n.Description,{className:s(d(T),L?.description),children:h}):null]}):null]}),e(n.Error,E?{match:!0,className:s(u[T],"text-(--p-accent)",L?.error),children:m}:{className:s(u[T],"text-(--p-accent)",L?.error)})]})});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { Combobox as BaseUICombobox } from '@base-ui/react/combobox';
|
|
2
3
|
import type { PlassElevation, PlassFieldClassNames, PlassHotKeys, PlassFieldLabelPlacement, PlassStyleProps } from '../../types.js';
|
|
3
4
|
/**
|
|
4
5
|
* What a combobox's value may be — the same two types a PlSelect submits, and
|
|
@@ -16,14 +17,35 @@ export interface PlComboboxOption {
|
|
|
16
17
|
*
|
|
17
18
|
* A `string` rather than a `ReactNode`, which is the one place this differs
|
|
18
19
|
* from PlSelect: the label is typed against by the filter and written into a
|
|
19
|
-
* text input, and neither of those can be done to an element.
|
|
20
|
+
* text input, and neither of those can be done to an element. `content` is
|
|
21
|
+
* where a row draws more than that.
|
|
20
22
|
*/
|
|
21
23
|
label?: string;
|
|
24
|
+
/**
|
|
25
|
+
* What the row draws in place of its label: a thumbnail, a glyph, a second
|
|
26
|
+
* line. The label is still what is filtered, written into the input and put
|
|
27
|
+
* on the chip, and the content's text is what a screen reader reads for the
|
|
28
|
+
* row, so keep the label's words in it.
|
|
29
|
+
*/
|
|
30
|
+
content?: React.ReactNode;
|
|
22
31
|
/** Unavailable, but still listed — the option exists, it just cannot be picked. */
|
|
23
32
|
disabled?: boolean;
|
|
24
33
|
}
|
|
25
34
|
/** One value, or an array of them, depending on `multiple`. */
|
|
26
35
|
type Selection<Multiple extends boolean | undefined> = Multiple extends true ? PlComboboxValue[] : PlComboboxValue | null;
|
|
36
|
+
/**
|
|
37
|
+
* What a caller hands in, which with `multiple` may be a readonly array: the
|
|
38
|
+
* field never writes into it, and Base UI takes one as it is.
|
|
39
|
+
*/
|
|
40
|
+
type HeldSelection<Multiple extends boolean | undefined> = Multiple extends true ? readonly PlComboboxValue[] : PlComboboxValue | null;
|
|
41
|
+
/**
|
|
42
|
+
* What caused a change, handed to `onValueChange`, `onInputValueChange` and
|
|
43
|
+
* `onOpenChange` as their second argument. It is Base UI's own: `reason` names
|
|
44
|
+
* the cause (`'item-press'`, `'chip-remove-press'`, `'clear-press'`,
|
|
45
|
+
* `'escape-key'` and the rest), `event` is the DOM event behind it, and
|
|
46
|
+
* `cancel()` turns the change away, so the field keeps what it held.
|
|
47
|
+
*/
|
|
48
|
+
export type PlComboboxChangeEventDetails = BaseUICombobox.Root.ChangeEventDetails;
|
|
27
49
|
export interface PlComboboxProps<Multiple extends boolean | undefined = false> extends PlassStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'children'> {
|
|
28
50
|
/** @default 'glass' */
|
|
29
51
|
variant?: PlassStyleProps['variant'];
|
|
@@ -52,12 +74,42 @@ export interface PlComboboxProps<Multiple extends boolean | undefined = false> e
|
|
|
52
74
|
*/
|
|
53
75
|
multiple?: Multiple;
|
|
54
76
|
/** The chosen value. Use with `onValueChange` for a controlled combobox. */
|
|
55
|
-
value?:
|
|
77
|
+
value?: HeldSelection<Multiple> | null;
|
|
56
78
|
/** The initially chosen value, for an uncontrolled combobox. */
|
|
57
|
-
defaultValue?:
|
|
58
|
-
|
|
79
|
+
defaultValue?: HeldSelection<Multiple> | null;
|
|
80
|
+
/** Called with the new value, and with what caused the change. */
|
|
81
|
+
onValueChange?: (value: Selection<Multiple>, eventDetails: PlComboboxChangeEventDetails) => void;
|
|
59
82
|
/** Called as the text in the input changes — the filter query, not the value. */
|
|
60
|
-
onInputValueChange?: (inputValue: string) => void;
|
|
83
|
+
onInputValueChange?: (inputValue: string, eventDetails: PlComboboxChangeEventDetails) => void;
|
|
84
|
+
/**
|
|
85
|
+
* Which rows a query keeps. Left out, a row stays while its label contains
|
|
86
|
+
* the query, compared in the runtime's locale. `null` keeps every row as
|
|
87
|
+
* `items` gives it, for a list a server has already searched, which may have
|
|
88
|
+
* matched a row on a spelling its label does not have.
|
|
89
|
+
*
|
|
90
|
+
* Receives the trimmed query, and is not asked about the row that offers
|
|
91
|
+
* what was typed: that row is the query itself, and always stays.
|
|
92
|
+
*/
|
|
93
|
+
filter?: ((option: PlComboboxOption, query: string) => boolean) | null;
|
|
94
|
+
/**
|
|
95
|
+
* Whether a row lights up on its own, so Enter takes it without an arrow key
|
|
96
|
+
* first. `true` lights the first row as the query changes, and the first of
|
|
97
|
+
* the rows that arrive after it, for a list filled after the reader stopped
|
|
98
|
+
* typing. `'always'` also lights the first row whenever the open list has
|
|
99
|
+
* rows and none is lit: as it opens with nothing typed, and once the pointer
|
|
100
|
+
* has left it. `false` lights nothing until an arrow key or the pointer
|
|
101
|
+
* does.
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
autoHighlight?: boolean | 'always';
|
|
105
|
+
/**
|
|
106
|
+
* Whether Escape with the list closed empties the field. Off by default, so
|
|
107
|
+
* a stray Escape cannot take every chip off, and the key goes on to whatever
|
|
108
|
+
* the field sits in, such as a modal. Escape on an open list closes it
|
|
109
|
+
* either way.
|
|
110
|
+
* @default false
|
|
111
|
+
*/
|
|
112
|
+
clearOnEscape?: boolean;
|
|
61
113
|
/**
|
|
62
114
|
* Whether a value the list does not contain may be committed.
|
|
63
115
|
*
|
|
@@ -129,7 +181,8 @@ export interface PlComboboxProps<Multiple extends boolean | undefined = false> e
|
|
|
129
181
|
open?: boolean;
|
|
130
182
|
/** Whether the popup starts open. */
|
|
131
183
|
defaultOpen?: boolean;
|
|
132
|
-
|
|
184
|
+
/** Called as the popup opens or closes, and with what caused it. */
|
|
185
|
+
onOpenChange?: (open: boolean, eventDetails: PlComboboxChangeEventDetails) => void;
|
|
133
186
|
/** Accessible name of the button that opens the list. @default 'Open' */
|
|
134
187
|
openLabel?: string;
|
|
135
188
|
/** Accessible name of the clear button. @default 'Clear' */
|
|
@@ -154,7 +207,7 @@ export interface PlComboboxProps<Multiple extends boolean | undefined = false> e
|
|
|
154
207
|
*
|
|
155
208
|
* With `multiple` the chosen values become PlChips inside the field and the
|
|
156
209
|
* input goes on filtering after each one. A row taken from a filtered list
|
|
157
|
-
* closes the list and empties the text, which is how Base UI
|
|
210
|
+
* closes the list and empties the text, which is how Base UI behaves,
|
|
158
211
|
* and a row taken with nothing typed leaves the list open for the next one.
|
|
159
212
|
*
|
|
160
213
|
* Base UI owns everything hard about this: the filtering and its collator, the
|
|
@@ -162,5 +215,5 @@ export interface PlComboboxProps<Multiple extends boolean | undefined = false> e
|
|
|
162
215
|
* navigation across both the list and the chips, and the hidden input that makes
|
|
163
216
|
* the whole thing submit with a form.
|
|
164
217
|
*/
|
|
165
|
-
export declare function PlCombobox<Multiple extends boolean | undefined = false>({ variant, size: sizeProp, color: colorProp, density: densityProp, elevation, items, multiple, value, defaultValue, onValueChange, onInputValueChange, allowCustom, customLabel, clearable, emptyMessage: emptyMessageProp, limit, placeholder, label, labelPlacement: labelPlacementProp, description, error, invalid, startIcon, fullWidth, disabled: disabledProp, readOnly, required, name, open, defaultOpen, onOpenChange, openLabel: openLabelProp, clearLabel: clearLabelProp, removeLabel: removeLabelProp, inputRef, id, className, hotKeys, classNames, style, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: PlComboboxProps<Multiple>): React.JSX.Element;
|
|
218
|
+
export declare function PlCombobox<Multiple extends boolean | undefined = false>({ variant, size: sizeProp, color: colorProp, density: densityProp, elevation, items, multiple, value, defaultValue, onValueChange, onInputValueChange, filter, autoHighlight, clearOnEscape, allowCustom, customLabel, clearable, emptyMessage: emptyMessageProp, limit, placeholder, label, labelPlacement: labelPlacementProp, description, error, invalid, startIcon, fullWidth, disabled: disabledProp, readOnly, required, name, open, defaultOpen, onOpenChange, openLabel: openLabelProp, clearLabel: clearLabelProp, removeLabel: removeLabelProp, inputRef, id, className, hotKeys, classNames, style, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: PlComboboxProps<Multiple>): React.JSX.Element;
|
|
166
219
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{FadedContext as t,controlNaming as
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{FadedContext as t,controlNaming as n,useDisabled as s}from"../../internal/form.js";import{useDefaults as r,useRuntimeLocale as o}from"../../internal/defaults.js";import{useLabels as i}from"../../internal/labels.js";import{Combobox as c}from"@base-ui/react/combobox";import{Field as d}from"@base-ui/react/field";import{PlChip as m}from"../chip/PlChip.js";import{CheckIcon as u,ChevronIcon as p,CloseIcon as h,PlusIcon as f}from"../../internal/icons.js";import{hotKeyHandler as b}from"../../internal/keys.js";import{useFieldLight as g}from"../../internal/glow.js";import{FieldNotch as v,floatControlClassName as y,notchShellStyle as x,resolveNotch as w}from"../../internal/notch.js";import{chipRemoveClasses as C,controlHeightClasses as N,controlTextLeadingClasses as j,cx as L,disabledClasses as S,fieldDescriptionClasses as I,fieldReadOnlyClasses as O,fieldRestClasses as z,fieldSlots as k,focusWithinRingClasses as M,forcedHighlightedClasses as P,gapClasses as R,glassClasses as _,hasContent as E,iconClasses as V,metaTextClasses as A,paddingXClasses as F,radiusClasses as D,stackGapClasses as T,surfaceSlots as $,targetClasses as q,transitionClasses as H}from"../../internal/styles.js";const K=/* @__PURE__ */["relative flex w-full cursor-text items-center","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",H,V].join(" "),G=M,W={xs:"min-h-6 py-0",sm:"min-h-8 py-0.5",md:"min-h-10 py-1",lg:"min-h-12 py-1.5",xl:"min-h-14 py-2"},B=/* @__PURE__ */[_,"max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain","w-[var(--anchor-width)] border bg-(--plass-glass-press) p-1","[border-color:var(--plass-glass-line)]","[box-shadow:var(--plass-shadow-3),var(--plass-gloss-glass)]","[outline:none]","[transition:opacity_var(--plass-duration)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" "),J=/* @__PURE__ */["relative flex cursor-pointer items-center gap-2 select-none","rounded-(--plass-radius-xs) py-1.5 pe-2 ps-7",H,"data-[highlighted]:bg-(--p-soft-hover) data-[highlighted]:text-(--p-accent)",P,"data-[selected]:font-semibold data-[selected]:text-(--p-accent)","data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50","[outline:none]"].join(" "),Q=/* @__PURE__ */["inline-flex h-[1lh] shrink-0 cursor-pointer items-center justify-center p-0","rounded-(--plass-radius-xs) text-(--plass-muted-fg)","[transition:color_var(--plass-duration)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]","enabled:hover:text-(--p-accent)","focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:[outline-offset:1px]","disabled:cursor-not-allowed"].join(" "),U=/* @__PURE__ */L(q,Q),X=[],Y=new Map;function labelsOf(e,a,l){const t=new Map;for(const n of a){const a=l.get(n)?.label??e.get(n);void 0!==a&&t.set(n,a)}return t.size===e.size&&[...t].every(([a,l])=>e.get(a)===l)?e:0===t.size?Y:t}function ShownRows({countRef:e}){const a=c.useFilteredItems().length;return l.useEffect(()=>{e.current=a},[e,a]),null}function toArray(e){return null==e?X:Array.isArray(e)?e:[e]}export function PlCombobox({variant:M="glass",size:P,color:_,density:V,elevation:q=0,items:H,multiple:X,value:Z,defaultValue:ee,onValueChange:ae,onInputValueChange:le,filter:te,autoHighlight:ne=!0,clearOnEscape:se=!1,allowCustom:re=!0,customLabel:oe,clearable:ie=!1,emptyMessage:ce,limit:de,placeholder:me,label:ue,labelPlacement:pe,description:he,error:fe,invalid:be,startIcon:ge,fullWidth:ve=!1,disabled:ye=!1,readOnly:xe=!1,required:we=!1,name:Ce,open:Ne,defaultOpen:je,onOpenChange:Le,openLabel:Se,clearLabel:Ie,removeLabel:Oe,inputRef:ze,id:ke,className:Me,hotKeys:Pe,classNames:Re,style:_e,"aria-label":Ee,"aria-labelledby":Ve,...Ae}){const Fe=r(),De=o(void 0),Te=s(ye),$e=i(),qe=Se??$e.open,He=Ie??$e.clear,Ke=ce??$e.empty,Ge=Oe??$e.removeItem,We=P??Fe.size??"md",Be=_??Fe.color??"primary",Je=V??Fe.density??"default",Qe=pe??Fe.labelPlacement??"top",{notched:Ue,float:Xe}=w(Qe,ue,E(ge)),Ye=E(fe),Ze=be??Ye,ea=Ze?"danger":Be,aa=!0===X,la=l.useMemo(()=>H.map(e=>({value:e.value,label:e.label??String(e.value),content:e.content,disabled:e.disabled,option:e})),[H]),ta=l.useMemo(()=>te?(e,a)=>void 0===e.option||te(e.option,a):te,[te]),[na,sa]=l.useState(()=>toArray(ee)),ra=void 0===Z?na:toArray(Z),[oa,ia]=l.useState(""),ca=l.useMemo(()=>new Map(la.map(e=>[e.value,e])),[la]),[da,ma]=l.useState(Y),ua=labelsOf(da,ra,ca);ua!==da&&ma(ua);const pa=l.useCallback(e=>ca.get(e)??{value:e,label:ua.get(e)??String(e),custom:!ua.has(e)},[ca,ua]),ha=l.useCallback(e=>ua.get(e)??String(e),[ua]),fa=oa.trim(),ba=fa.toLocaleLowerCase(De),ga=""===fa||la.some(e=>e.label.toLocaleLowerCase(De)===ba||String(e.value).toLocaleLowerCase(De)===ba)||ra.some(e=>String(e).toLocaleLowerCase(De)===ba||ua.get(e)?.toLocaleLowerCase(De)===ba),va=!re||xe||Te||ga?null:fa,ya=l.useMemo(()=>null===va?la:[...la,{value:va,label:va,custom:!0}],[la,va]),xa=l.useMemo(()=>c.createItems(ya,{getValue:e=>e.value,getLabel:e=>e.label}),[ya]),wa=aa?ra:ra[0]??null;function refusesEscape(e){return!se&&"escape-key"===e.reason&&(e.cancel(),e.allowPropagation(),!0)}const Ca=l.useRef(0),Na=!Te&&!xe,ja=g(Na),La=L(K,Ue?"":G,j[We],D[We],R[We],aa?W[We]:N[We],`${F[Je][We]} pe-1.5`,Te?S[M]:xe?O[M]:z[M],Na?"plass-glow":""),Sa=/* @__PURE__ */["min-w-0 flex-1 self-stretch bg-transparent p-0 [font:inherit] text-inherit","[outline:none]","placeholder:text-(--plass-muted-fg)","caret-(--p-accent) selection:bg-(--p-soft-press)","disabled:cursor-not-allowed"].join(" "),renderInput=a=>e(c.Input,{ref:ze,placeholder:Xe?me||" ":me,...n(Ee,Ve),onKeyDown:b(Pe,void 0),className:L(Sa,aa&&"min-w-16",aa&&a&&"ms-1.5",Xe&&!a&&y)}),Ia=e(d.Label,{className:L(A[We],"font-semibold",Te?"text-(--plass-muted-fg)":"text-(--plass-fg)",Re?.label),children:ue});return a(d.Root,{disabled:Te,invalid:Ze,className:L("flex-col align-top",T[We],ve?"flex w-full":"inline-flex",Me),style:{...k(ea,q),..._e},...Ae,children:[E(ue)&&!Ue?Ia:null,a(c.Root,{id:ke,name:Ce,items:xa,multiple:aa,value:wa,filter:ta,onValueChange:(e,a)=>{if(refusesEscape(a))return;const l=toArray(e);0===l.length&&0===ra.length||function commit(e,a){ae?.(aa?e:e[0]??null,a),a.isCanceled||(void 0===Z&&sa(e),ma(labelsOf(ua,[...ra,...e],ca)))}(l,a)},onInputValueChange:(e,a)=>{refusesEscape(a)||(le?.(e,a),a.isCanceled||ia(e))},open:Ne,defaultOpen:je,onOpenChange:(e,a)=>{!e&&function holdsOpen(e){const{event:a}=e;return!("none"!==e.reason||"keydown"!==a.type||"Enter"!==a.key||Ca.current>0||(e.cancel(),a.preventDefault(),0))}(a)||Le?.(e,a)},autoHighlight:ne,itemToStringLabel:ha,itemToStringValue:String,limit:de,locale:De,disabled:Te,readOnly:xe,required:we,children:[e(ShownRows,{countRef:Ca}),e(v,{notched:Ue,size:We,density:Je,variant:M,disabled:Te,readOnly:xe,float:Xe,label:Ia,children:a(c.InputGroup,{style:Ue?x:void 0,...ja,className:L(La,Re?.control),children:[ge?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)",children:ge}):null,aa?e(c.Chips,{className:"flex min-w-0 flex-1 flex-wrap items-center gap-1",children:e(t.Provider,{value:Te,children:e(c.Value,{children:t=>a(l.Fragment,{children:[t.map(pa).map(a=>e(c.Chip,{render:e(m,{variant:"glass",size:We,color:ea,density:"compact",disabled:Te,endIcon:xe||Te?null:e(c.ChipRemove,{"aria-label":Ge(a.label),className:C,children:e(h,{})})}),children:a.label},String(a.value))),renderInput(t.length>0)]})})})}):renderInput(!1),!ie||xe||Te?null:e(c.Clear,{"aria-label":He,className:U,children:e(h,{})}),e(c.Trigger,{"aria-label":qe,"aria-labelledby":void 0,className:Q,children:e(c.Icon,{className:L("flex items-center","[transition:rotate_var(--plass-duration)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]","data-[popup-open]:rotate-180"),children:e(p,{})})})]})}),e(c.Portal,{children:e(c.Positioner,{className:"plass-portal z-(--plass-z-portal) [outline:none]",sideOffset:6,children:a(c.Popup,{className:L(B,D[We],j[We]),style:$(ea,3),children:[e(c.Empty,{className:"px-2 py-1.5 text-(--plass-muted-fg) empty:hidden",children:Ke}),e(c.List,{children:t=>e(c.Item,{value:t.value,disabled:t.disabled,className:J,children:t.custom?a(l.Fragment,{children:[e("span",{className:"absolute start-1.5 flex size-4 items-center justify-center text-(--p-accent) [&_svg]:size-4",children:e(f,{})}),e("span",{className:"truncate",children:oe?oe(t.label):$e.addCustom(t.label)})]}):a(l.Fragment,{children:[e(c.ItemIndicator,{className:"absolute start-1.5 flex size-4 items-center justify-center",children:e(u,{})}),E(t.content)?e("span",{className:"min-w-0 flex-1",children:t.content}):e("span",{className:"truncate",children:t.label})]})},`${t.custom?"custom:":""}${String(t.value)}`)})]})})})]}),he?e(d.Description,{className:L(I(We),Re?.description),children:he}):null,e(d.Error,Ye?{match:!0,className:L(A[We],"text-(--p-accent)",Re?.error),children:fe}:{className:L(A[We],"text-(--p-accent)",Re?.error)})]})}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { PlCombobox } from './PlCombobox.js';
|
|
2
|
-
export type { PlComboboxOption, PlComboboxProps, PlComboboxValue } from './PlCombobox.js';
|
|
2
|
+
export type { PlComboboxChangeEventDetails, PlComboboxOption, PlComboboxProps, PlComboboxValue } from './PlCombobox.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{controlNaming as l,useDisabled as o
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{controlNaming as l,useDisabled as o}from"../../internal/form.js";import{useDefaults as s}from"../../internal/defaults.js";import{Radio as t}from"@base-ui/react/radio";import{RadioGroup as i}from"@base-ui/react/radio-group";import{Field as n}from"@base-ui/react/field";import{controlSlots as d,cx as c,fieldDescriptionClasses as p,focusRingClasses as m,forcedCheckedClasses as f,forcedDisabledCheckedClasses as u,glassClasses as b,hasContent as h,metaTextClasses as x,tickDotClasses as g,tickRowTextClasses as y,tickSizeClasses as v,transitionClasses as N}from"../../internal/styles.js";const j=/* @__PURE__ */r.createContext({size:"md",color:"primary",readOnly:!1,disabled:!1}),k=/* @__PURE__ */["relative inline-flex shrink-0 items-center justify-center rounded-full border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","plass-fill [--p-fill-edge:1px]",N,m].join(" "),R=/* @__PURE__ */[b,"cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]","hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105",f].join(" "),z=/* @__PURE__ */[b,"cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]","saturate-[0.55]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]",f].join(" "),w=/* @__PURE__ */[b,"cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]","opacity-50 saturate-[0.35]","data-[checked]:[--p-fill-on:1] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]",u].join(" "),O=/* @__PURE__ */["rounded-full bg-current","[transition-property:width,height]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]","motion-reduce:[transition-duration:0ms]"].join(" ");export const PlRadio=/* @__PURE__ */r.forwardRef(function PlRadio({label:l,description:o,disabled:s=!1,className:i,style:d,"aria-label":c,"aria-labelledby":p,...m},f){const u=r.useContext(j),b=m.readOnly??u.readOnly,h=s||u.disabled;return e(n.Root,{disabled:s,className:["flex flex-col",i??""].filter(Boolean).join(" "),style:d,children:a("div",{className:`flex items-start gap-2 ${y[u.size]}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(t.Root,{ref:f,className:[k,v[u.size],h?w:b?z:R].join(" "),disabled:s,"aria-label":c,"aria-labelledby":p,...m,children:e(t.Indicator,{keepMounted:!0,className:`${O} ${g[u.size]}`})})}),l||o?a("span",{className:"flex min-w-0 flex-col gap-0.5",children:[l?e(n.Label,{className:h?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:l}):null,o?e(n.Description,{className:`m-0 ${x[u.size]} text-(--plass-muted-fg)`,children:o}):null]}):null]})})});export const PlRadioGroup=/* @__PURE__ */r.forwardRef(function PlRadioGroup({size:t,color:m,orientation:f="vertical",label:u,description:b,error:g,invalid:y,disabled:v=!1,readOnly:N=!1,className:k,classNames:R,style:z,children:w,"aria-label":O,"aria-labelledby":P,...$},G){const B=s(),C=o(v),D=t??B.size??"md",L=m??B.color??"primary",M=h(g),E=y??M,I=E?"danger":L,q=r.useMemo(()=>({size:D,color:I,readOnly:N,disabled:C}),[D,I,N,C]);return e(j.Provider,{value:q,children:a(n.Root,{disabled:C,invalid:E,className:["flex flex-col gap-1.5",k??""].filter(Boolean).join(" "),style:{...d(I,0,"solid"),...z},children:[u?e(n.Label,{className:c(x[D],"font-semibold",C?"text-(--plass-muted-fg)":"text-(--plass-fg)",R?.label),children:u}):null,b?e(n.Description,{className:c(p(D),R?.description),children:b}):null,e(i,{ref:G,disabled:C,readOnly:N,className:c("flex","horizontal"===f?"flex-row flex-wrap gap-x-5 gap-y-2":"flex-col gap-2",R?.control),...l(O,P),...$,children:w}),e(n.Error,M?{match:!0,className:c(x[D],"text-(--p-accent)",R?.error),children:g}:{className:c(x[D],"text-(--p-accent)",R?.error)})]})})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as a,jsxs as e,Fragment as r}from"react/jsx-runtime";import*as t from"react";import{useDisabled as
|
|
1
|
+
"use client";import{jsx as a,jsxs as e,Fragment as r}from"react/jsx-runtime";import*as t from"react";import{useDisabled as o}from"../../internal/form.js";import{useDefaults as l}from"../../internal/defaults.js";import{Switch as s}from"@base-ui/react/switch";import{Field as c}from"@base-ui/react/field";import{controlSlots as i,cx as n,fieldDescriptionClasses as d,focusRingClasses as h,hasContent as m,metaTextClasses as p,tickRowTextClasses as f}from"../../internal/styles.js";const u={xs:"h-3.5 w-6",sm:"h-4 w-7",md:"h-5 w-9",lg:"h-6 w-11",xl:"h-7 w-13"},b={xs:"data-[checked]:start-[calc(100%-0.75rem)]",sm:"data-[checked]:start-[calc(100%-0.875rem)]",md:"data-[checked]:start-[calc(100%-1.125rem)]",lg:"data-[checked]:start-[calc(100%-1.375rem)]",xl:"data-[checked]:start-[calc(100%-1.625rem)]"},g=/* @__PURE__ */["relative inline-flex shrink-0 rounded-full","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","plass-fill","[transition-property:background-color,box-shadow,filter]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]","motion-reduce:[transition-duration:0ms]",h].join(" "),x=/* @__PURE__ */["forced-colors:border forced-colors:[border-color:CanvasText]","forced-colors:data-[checked]:[background-color:Highlight]"].join(" "),k=/* @__PURE__ */[x,"cursor-pointer bg-(--plass-track)","hover:brightness-[0.97] dark:hover:brightness-110","data-[checked]:[--p-fill-on:1]","data-[checked]:[box-shadow:var(--p-lift)] data-[checked]:hover:brightness-105"].join(" "),w=/* @__PURE__ */[x,"cursor-default bg-(--plass-track) saturate-[0.55]","data-[checked]:[--p-fill-on:1]","data-[checked]:shadow-none"].join(" "),j=/* @__PURE__ */["forced-colors:border forced-colors:[border-color:GrayText]","cursor-not-allowed bg-(--plass-track)","opacity-50 saturate-[0.35] shadow-none","data-[checked]:[--p-fill-on:1]"].join(" "),v=/* @__PURE__ */["absolute inset-y-0.5 start-0.5 aspect-square rounded-full bg-white","[box-shadow:0_1px_2px_rgb(20_40_90/0.25)]","forced-colors:[background-color:CanvasText] forced-colors:data-[checked]:[background-color:HighlightText]","[transition:inset-inline-start_var(--plass-duration)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]"].join(" ");export const PlSwitch=/* @__PURE__ */t.forwardRef(function PlSwitch({size:t,color:h,label:x,description:y,error:N,invalid:_,labelPlacement:T="end",disabled:P=!1,readOnly:R=!1,className:S,classNames:$,style:z,"aria-label":B,"aria-labelledby":C,...H},O){const q=l(),D=o(P),E=t??q.size??"md",F=h??q.color??"primary",G=m(N),L=_??G,A=L?"danger":F,I=a("span",{className:"flex h-[1lh] shrink-0 items-center",children:a(s.Root,{ref:O,className:n(g,u[E],D?j:R?w:k,$?.control),disabled:D,readOnly:R,"aria-label":B,"aria-labelledby":C,...H,children:a(s.Thumb,{className:`${v} ${b[E]}`})})}),J=x||y?e("span",{className:["flex min-w-0 flex-col gap-0.5","start"===T?"flex-1":""].filter(Boolean).join(" "),children:[x?a(c.Label,{className:n(D?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",$?.label),children:x}):null,y?a(c.Description,{className:n(d(E),$?.description),children:y}):null]}):null;return e(c.Root,{disabled:D,invalid:L,className:["inline-flex flex-col gap-1 align-top",S??""].filter(Boolean).join(" "),style:{...i(A,0,"solid"),...z},children:[a("div",{className:`flex items-start gap-2.5 ${f[E]}`,children:e(r,"start"===T?{children:[J,I]}:{children:[I,J]})}),a(c.Error,G?{match:!0,className:n(p[E],"text-(--p-accent)",$?.error),children:N}:{className:n(p[E],"text-(--p-accent)",$?.error)})]})});
|
package/dist/internal/form.d.ts
CHANGED
|
@@ -84,13 +84,16 @@ export declare function controlNaming(label: string | undefined, labelledBy: str
|
|
|
84
84
|
* the hidden words it points at, for a Base UI part `controlNaming` cannot
|
|
85
85
|
* reach.
|
|
86
86
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
87
|
+
* The group around a slider's thumbs or a code's slots reads `aria-labelledby`
|
|
88
|
+
* itself and falls back to the visible label for anything but a reference of
|
|
89
|
+
* the caller's, `undefined` included. So an `aria-label` given without an
|
|
90
|
+
* `aria-labelledby` is written into a hidden node as well, and the part is
|
|
91
|
+
* pointed at that, which leaves the `aria-label` as the name in the label's
|
|
92
|
+
* place. A caller's `aria-labelledby` comes back as it was, and with neither
|
|
93
|
+
* there is no reference and no node.
|
|
94
|
+
*
|
|
95
|
+
* A checkbox, a switch and a radio do not need it: since Base UI 1.9.0 they
|
|
96
|
+
* drop the label's reference for a non-blank `aria-label` of their own.
|
|
94
97
|
*/
|
|
95
98
|
export declare function useLabelReference(label: string | undefined, labelledBy: string | undefined): {
|
|
96
99
|
labelledBy: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plass-ui",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "A React UI component library made of glass and gradients — smooth tinted surfaces, shadows in their own colour, and light that follows the pointer. Accessible and themeable, ESM only, types included, dark mode built in.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
144
144
|
"globals": "^17.13.0",
|
|
145
145
|
"jiti": "^2.7.0",
|
|
146
|
-
"playwright": "^1.
|
|
146
|
+
"playwright": "^1.64.0",
|
|
147
147
|
"postcss": "^8.5.29",
|
|
148
148
|
"prettier": "^3.9.9",
|
|
149
149
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
|
@@ -153,12 +153,12 @@
|
|
|
153
153
|
"terser": "^5.51.2",
|
|
154
154
|
"typescript": "^6.0.3",
|
|
155
155
|
"typescript-eslint": "^8.71.1",
|
|
156
|
-
"vite": "^8.3.
|
|
156
|
+
"vite": "^8.3.4",
|
|
157
157
|
"vitest": "^5.0.3",
|
|
158
158
|
"vitest-browser-react": "^2.3.0"
|
|
159
159
|
},
|
|
160
160
|
"dependencies": {
|
|
161
|
-
"@base-ui/react": "^1.
|
|
161
|
+
"@base-ui/react": "^1.9.0",
|
|
162
162
|
"highlight.js": "^11.12.0"
|
|
163
163
|
},
|
|
164
164
|
"peerDependencies": {
|