plass-ui 1.0.0 → 1.0.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/README.md +165 -0
- package/dist/components/accordion/PlAccordion.js +1 -131
- package/dist/components/accordion/index.js +1 -1
- package/dist/components/alert/PlAlert.js +1 -109
- package/dist/components/alert/index.js +1 -1
- package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
- package/dist/components/animate-appear/index.js +1 -1
- package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
- package/dist/components/animate-blink/index.js +1 -1
- package/dist/components/animate-fade/PlAnimateFade.js +1 -48
- package/dist/components/animate-fade/index.js +1 -1
- package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
- package/dist/components/animate-grow/index.js +1 -1
- package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
- package/dist/components/animate-headline/index.js +1 -1
- package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
- package/dist/components/animate-lighting/index.js +1 -1
- package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
- package/dist/components/animate-marquee/index.js +1 -1
- package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
- package/dist/components/animate-rotate/index.js +1 -1
- package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
- package/dist/components/animate-slide/index.js +1 -1
- package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
- package/dist/components/animate-typing/index.js +1 -1
- package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
- package/dist/components/animate-zoom/index.js +1 -1
- package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
- package/dist/components/aspect-ratio/index.js +1 -1
- package/dist/components/avatar/PlAvatar.js +1 -121
- package/dist/components/avatar/index.js +1 -1
- package/dist/components/badge/PlBadge.js +1 -168
- package/dist/components/badge/index.js +1 -1
- package/dist/components/blockquote/PlBlockquote.js +1 -112
- package/dist/components/blockquote/index.js +1 -1
- package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
- package/dist/components/bottom-navigation/index.js +1 -1
- package/dist/components/box/PlBox.js +1 -46
- package/dist/components/box/index.js +1 -1
- package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
- package/dist/components/breadcrumb/index.js +1 -1
- package/dist/components/button/PlButton.js +1 -196
- package/dist/components/button/index.js +1 -1
- package/dist/components/button-group/PlButtonGroup.js +1 -64
- package/dist/components/button-group/index.js +1 -1
- package/dist/components/card/PlCard.js +1 -102
- package/dist/components/card/index.js +1 -1
- package/dist/components/carousel/PlCarousel.js +1 -177
- package/dist/components/carousel/index.js +1 -1
- package/dist/components/chat-bubble/PlChatBubble.js +1 -206
- package/dist/components/chat-bubble/index.js +1 -1
- package/dist/components/checkbox/PlCheckbox.js +1 -100
- package/dist/components/checkbox/index.js +1 -1
- package/dist/components/chip/PlChip.js +1 -144
- package/dist/components/chip/index.js +1 -1
- package/dist/components/collapsible/PlCollapsible.js +1 -65
- package/dist/components/collapsible/index.js +1 -1
- package/dist/components/combobox/PlCombobox.js +1 -199
- package/dist/components/combobox/index.js +1 -1
- package/dist/components/container/PlContainer.js +1 -47
- package/dist/components/container/index.js +1 -1
- package/dist/components/date-picker/PlDatePicker.js +1 -87
- package/dist/components/date-picker/index.js +1 -1
- package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
- package/dist/components/date-range-picker/index.js +1 -1
- package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
- package/dist/components/date-time-picker/index.js +1 -1
- package/dist/components/divider/PlDivider.js +1 -107
- package/dist/components/divider/index.js +1 -1
- package/dist/components/drawer/PlDrawer.js +1 -190
- package/dist/components/drawer/index.js +1 -1
- package/dist/components/file-picker/PlFilePicker.js +1 -274
- package/dist/components/file-picker/index.js +1 -1
- package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
- package/dist/components/floating-bottom-navigation/index.js +1 -1
- package/dist/components/grid/PlGrid.js +1 -51
- package/dist/components/grid/PlGridItem.js +1 -34
- package/dist/components/grid/index.js +1 -2
- package/dist/components/highlight/PlHighlight.js +1 -185
- package/dist/components/highlight/index.js +1 -1
- package/dist/components/hot-keys/PlHotKeys.js +1 -294
- package/dist/components/hot-keys/index.js +1 -1
- package/dist/components/icon/PlIcon.js +1 -58
- package/dist/components/icon/index.js +1 -1
- package/dist/components/icon-button/PlIconButton.js +1 -37
- package/dist/components/icon-button/index.js +1 -1
- package/dist/components/list/PlList.js +1 -160
- package/dist/components/list/index.js +1 -1
- package/dist/components/menu/PlMenu.js +1 -209
- package/dist/components/menu/index.js +1 -1
- package/dist/components/modal/PlModal.js +1 -150
- package/dist/components/modal/index.js +1 -1
- package/dist/components/number-field/PlNumberField.js +1 -111
- package/dist/components/number-field/index.js +1 -1
- package/dist/components/otp-field/PlOtpField.js +1 -109
- package/dist/components/otp-field/index.js +1 -1
- package/dist/components/overlay/PlOverlay.js +1 -77
- package/dist/components/overlay/index.js +1 -1
- package/dist/components/pagination/PlPagination.js +1 -159
- package/dist/components/pagination/index.js +1 -1
- package/dist/components/panes/PlPanes.js +1 -324
- package/dist/components/panes/index.js +1 -1
- package/dist/components/pill/PlPill.js +1 -154
- package/dist/components/pill/index.js +1 -1
- package/dist/components/popover/PlPopover.js +1 -113
- package/dist/components/popover/index.js +1 -1
- package/dist/components/progress-box/PlProgressBox.js +1 -34
- package/dist/components/progress-box/index.js +1 -1
- package/dist/components/progress-circular/PlProgressCircular.js +1 -47
- package/dist/components/progress-circular/index.js +1 -1
- package/dist/components/progress-linear/PlProgressLinear.js +1 -40
- package/dist/components/progress-linear/index.js +1 -1
- package/dist/components/radio-group/PlRadioGroup.js +1 -99
- package/dist/components/radio-group/index.js +1 -1
- package/dist/components/rating/PlRating.js +1 -101
- package/dist/components/rating/index.js +1 -1
- package/dist/components/scroll-zone/PlScrollZone.js +1 -411
- package/dist/components/scroll-zone/index.js +1 -1
- package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
- package/dist/components/segmented-button/index.js +1 -1
- package/dist/components/select/PlSelect.js +1 -115
- package/dist/components/select/index.js +1 -1
- package/dist/components/skeleton/PlSkeleton.js +1 -142
- package/dist/components/skeleton/index.js +1 -1
- package/dist/components/slider/PlSlider.js +1 -132
- package/dist/components/slider/index.js +1 -1
- package/dist/components/spoiler/PlSpoiler.js +1 -74
- package/dist/components/spoiler/index.js +1 -1
- package/dist/components/switch/PlSwitch.js +1 -131
- package/dist/components/switch/index.js +1 -1
- package/dist/components/table/PlTable.js +1 -210
- package/dist/components/table/index.js +1 -1
- package/dist/components/tabs/PlTabs.js +1 -182
- package/dist/components/tabs/index.js +1 -1
- package/dist/components/text-field/PlTextField.js +1 -126
- package/dist/components/text-field/index.js +1 -1
- package/dist/components/text-link/PlTextLink.js +1 -136
- package/dist/components/text-link/index.js +1 -1
- package/dist/components/time-picker/PlTimePicker.js +1 -97
- package/dist/components/time-picker/index.js +1 -1
- package/dist/components/timeline/PlTimeline.js +1 -207
- package/dist/components/timeline/index.js +1 -1
- package/dist/components/toast/PlToast.js +1 -174
- package/dist/components/toast/index.js +1 -1
- package/dist/components/toolbar/PlToolbar.js +1 -65
- package/dist/components/toolbar/index.js +1 -1
- package/dist/components/tooltip/PlTooltip.js +1 -117
- package/dist/components/tooltip/index.js +1 -1
- package/dist/components/typography/PlTypography.js +1 -143
- package/dist/components/typography/index.js +1 -1
- package/dist/index.js +1 -75
- package/dist/internal/animate.js +1 -318
- package/dist/internal/button-group.js +2 -19
- package/dist/internal/calendar.js +1 -501
- package/dist/internal/date.js +1 -353
- package/dist/internal/format.js +1 -41
- package/dist/internal/grid.js +1 -143
- package/dist/internal/icons.js +1 -138
- package/dist/internal/menu.js +1 -18
- package/dist/internal/picker.js +1 -139
- package/dist/internal/progress.js +1 -167
- package/dist/internal/sizer.js +1 -35
- package/dist/internal/styles.js +1 -636
- package/dist/types.js +0 -8
- package/package.json +5 -3
|
@@ -1,100 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Checkbox as BaseUICheckbox } from '@base-ui/react/checkbox';
|
|
4
|
-
import { Field } from '@base-ui/react/field';
|
|
5
|
-
import { controlSlots, focusRingClasses, glassClasses, hasContent, metaTextClasses, tickRadiusClasses, tickRowTextClasses, tickSizeClasses, transitionClasses } from '../../internal/styles.js';
|
|
6
|
-
/**
|
|
7
|
-
* The tick.
|
|
8
|
-
*
|
|
9
|
-
* Unchecked it is a small pane of clear glass with a hairline round it — the
|
|
10
|
-
* same material a `glass` button is. Checked it fills with the family's
|
|
11
|
-
* gradient, which is the one place this library expresses a state by swapping
|
|
12
|
-
* the whole surface rather than shifting it a step: "on" and "off" are not two
|
|
13
|
-
* strengths of the same thing.
|
|
14
|
-
*/
|
|
15
|
-
const tickBaseClasses = /* @__PURE__ */ [
|
|
16
|
-
'relative inline-flex shrink-0 items-center justify-center border',
|
|
17
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
18
|
-
transitionClasses,
|
|
19
|
-
focusRingClasses
|
|
20
|
-
].join(' ');
|
|
21
|
-
/**
|
|
22
|
-
* No gloss line on a tick, and no tinted lift under it.
|
|
23
|
-
*
|
|
24
|
-
* `--plass-gloss-glass` is a 1px white edge along the top of a pane. On a 40px
|
|
25
|
-
* button that reads as light catching a cut edge; on an 18px square it is a
|
|
26
|
-
* bevel drawn at a twentieth of the object and it turns into a grey smudge.
|
|
27
|
-
* The tinted lift goes for the same reason from the other direction — a
|
|
28
|
-
* `0 6px 16px` shadow under an 18px box is a shadow bigger than the box.
|
|
29
|
-
*
|
|
30
|
-
* The material stays. It is only the two decorations that go.
|
|
31
|
-
*
|
|
32
|
-
* The edge is `--plass-border` rather than the sheet's own `--plass-glass-line`,
|
|
33
|
-
* and that is not a slip. The glass hairline is white light on a translucent
|
|
34
|
-
* pane, which is invisible the moment the tick is set on a light card rather
|
|
35
|
-
* than on the page wash — and a tick nobody can see is a control nobody can
|
|
36
|
-
* find. A neutral hairline reads on both.
|
|
37
|
-
*/
|
|
38
|
-
const restClasses = /* @__PURE__ */ [
|
|
39
|
-
glassClasses,
|
|
40
|
-
'cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
41
|
-
'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]',
|
|
42
|
-
// `data-checked` rather than `:checked`: the visible tick is a `<span>`, and
|
|
43
|
-
// the real input is hidden beside it.
|
|
44
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
45
|
-
'data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105',
|
|
46
|
-
'data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)',
|
|
47
|
-
'data-[indeterminate]:[border-color:transparent]'
|
|
48
|
-
].join(' ');
|
|
49
|
-
const readOnlyClasses = /* @__PURE__ */ [
|
|
50
|
-
glassClasses,
|
|
51
|
-
'cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
52
|
-
'saturate-[0.55]',
|
|
53
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
54
|
-
'data-[checked]:[border-color:transparent]'
|
|
55
|
-
].join(' ');
|
|
56
|
-
/** Disabled is the light going out, exactly as it is on a PlButton. */
|
|
57
|
-
const disabledTickClasses = /* @__PURE__ */ [
|
|
58
|
-
glassClasses,
|
|
59
|
-
'cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
60
|
-
'opacity-50 saturate-[0.35]',
|
|
61
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
62
|
-
'data-[checked]:[border-color:transparent]',
|
|
63
|
-
'data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)'
|
|
64
|
-
].join(' ');
|
|
65
|
-
/** The mark is drawn at 70% of the box, so it never touches the corners. */
|
|
66
|
-
const markClasses = 'flex size-[70%] items-center justify-center';
|
|
67
|
-
function CheckMark() {
|
|
68
|
-
return (_jsx("svg", { viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", className: "size-full", children: _jsx("path", { d: "M2 6.2 4.6 8.8 10 3.4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
69
|
-
}
|
|
70
|
-
function DashMark() {
|
|
71
|
-
return (_jsx("svg", { viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", className: "size-full", children: _jsx("path", { d: "M2.5 6h7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* A single yes/no, or one member of a set of them.
|
|
75
|
-
*
|
|
76
|
-
* `label`, `description` and `error` are props rather than children for the same
|
|
77
|
-
* reason they are on PlTextField: the arrangement is fixed and what a caller
|
|
78
|
-
* wants to decide is what goes in each slot. `children` is not accepted at all —
|
|
79
|
-
* anything a checkbox has to say belongs in one of the three.
|
|
80
|
-
*/
|
|
81
|
-
export const PlCheckbox = /* @__PURE__ */ React.forwardRef(function PlCheckbox({ size = 'md', color = 'primary', label, description, error, invalid, disabled = false, readOnly = false, className, style, ...props }, ref) {
|
|
82
|
-
const hasError = hasContent(error);
|
|
83
|
-
const isInvalid = invalid ?? hasError;
|
|
84
|
-
// Invalid re-points the whole slot family at `danger`, so the tick, the ring
|
|
85
|
-
// and the message all turn over together.
|
|
86
|
-
const family = isInvalid ? 'danger' : color;
|
|
87
|
-
const tickClasses = [
|
|
88
|
-
tickBaseClasses,
|
|
89
|
-
tickSizeClasses[size],
|
|
90
|
-
tickRadiusClasses[size],
|
|
91
|
-
// An if/else rather than stacked variants: two Tailwind classes of equal
|
|
92
|
-
// specificity resolve by their order in the generated stylesheet.
|
|
93
|
-
disabled ? disabledTickClasses : readOnly ? readOnlyClasses : restClasses
|
|
94
|
-
].join(' ');
|
|
95
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: ['inline-flex flex-col gap-1 align-top', className ?? '']
|
|
96
|
-
.filter(Boolean)
|
|
97
|
-
.join(' '),
|
|
98
|
-
// `solid`, because a checked tick *is* the coloured thing.
|
|
99
|
-
style: { ...controlSlots(family, 0, 'solid'), ...style }, children: [_jsxs("div", { className: `flex items-start gap-2 ${tickRowTextClasses[size]}`, children: [_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: _jsx(BaseUICheckbox.Root, { ref: ref, className: tickClasses, disabled: disabled, readOnly: readOnly, ...props, children: _jsx(BaseUICheckbox.Indicator, { className: markClasses, children: props.indeterminate ? _jsx(DashMark, {}) : _jsx(CheckMark, {}) }) }) }), label || description ? (_jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [label ? (_jsx(Field.Label, { className: disabled ? 'text-(--plass-muted-fg)' : 'cursor-pointer text-(--plass-fg)', children: label })) : null, description ? (_jsx(Field.Description, { className: `${metaTextClasses[size]} text-(--plass-muted-fg)`, children: description })) : null] })) : null] }), hasError ? (_jsx(Field.Error, { match: true, className: `${metaTextClasses[size]} text-(--p-accent)`, children: error })) : null] }));
|
|
100
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as a from"react";import{Checkbox as t}from"@base-ui/react/checkbox";import{Field as o}from"@base-ui/react/field";import{controlSlots as l,focusRingClasses as s,glassClasses as i,hasContent as n,metaTextClasses as c,tickRadiusClasses as d,tickRowTextClasses as p,tickSizeClasses as h,transitionClasses as m}from"../../internal/styles.js";const u=/* @__PURE__ */["relative inline-flex shrink-0 items-center justify-center border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",m,s].join(" "),f=/* @__PURE__ */[i,"cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]","hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105","data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)","data-[indeterminate]:[border-color:transparent]"].join(" "),k=/* @__PURE__ */[i,"cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]","saturate-[0.55]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]"].join(" "),b=/* @__PURE__ */[i,"cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]","opacity-50 saturate-[0.35]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]","data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)"].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",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",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}export const PlCheckbox=/* @__PURE__ */a.forwardRef(function PlCheckbox({size:a="md",color:s="primary",label:i,description:m,error:g,invalid:x,disabled:v=!1,readOnly:j=!1,className:N,style:y,...C},w){const M=n(g),z=x??M,L=z?"danger":s,B=[u,h[a],d[a],v?b:j?k:f].join(" ");return r(o.Root,{disabled:v,invalid:z,className:["inline-flex flex-col gap-1 align-top",N??""].filter(Boolean).join(" "),style:{...l(L,0,"solid"),...y},children:[r("div",{className:`flex items-start gap-2 ${p[a]}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(t.Root,{ref:w,className:B,disabled:v,readOnly:j,...C,children:e(t.Indicator,{className:"flex size-[70%] items-center justify-center",children:C.indeterminate?e(DashMark,{}):e(CheckMark,{})})})}),i||m?r("span",{className:"flex min-w-0 flex-col gap-0.5",children:[i?e(o.Label,{className:v?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:i}):null,m?e(o.Description,{className:`${c[a]} text-(--plass-muted-fg)`,children:m}):null]}):null]}),M?e(o.Error,{match:!0,className:`${c[a]} text-(--p-accent)`,children:g}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlCheckbox}from"./PlCheckbox.js";
|
|
@@ -1,144 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { CloseIcon } from '../../internal/icons.js';
|
|
4
|
-
import { chipRemoveClasses, controlHeightClasses, controlSlots, controlTextClasses, disabledClasses, focusRingClasses, gapClasses, glassClasses, hasContent, iconClasses, paddingXClasses, radiusClasses, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* A chip sits one step down the control ladder from everything else: a `md` chip
|
|
7
|
-
* is a `sm` control — 32px, not 40px.
|
|
8
|
-
*
|
|
9
|
-
* This is the whole size difference between a chip and a button, and it is
|
|
10
|
-
* deliberate. A chip is a token *inside* a row of content, not a control the row
|
|
11
|
-
* lines up against; at full control height a `glass` chip and a `glass` button
|
|
12
|
-
* are the same object, and a screen full of them says nothing about which one
|
|
13
|
-
* can be pressed.
|
|
14
|
-
*
|
|
15
|
-
* Shifting the index rather than inventing a second set of numbers keeps a chip
|
|
16
|
-
* inside the same five-step vocabulary, and keeps `xs` from falling off the
|
|
17
|
-
* bottom of it.
|
|
18
|
-
*/
|
|
19
|
-
const chipScale = {
|
|
20
|
-
xs: 'xs',
|
|
21
|
-
sm: 'xs',
|
|
22
|
-
md: 'sm',
|
|
23
|
-
lg: 'md',
|
|
24
|
-
xl: 'lg'
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* The three materials. A chip **is** the thing being coloured — a tag names one
|
|
28
|
-
* particular thing — so unlike a `PlCard` its sheet takes the tint.
|
|
29
|
-
*
|
|
30
|
-
* `glass` is the default rather than `solid`: a filter bar is a row of chips,
|
|
31
|
-
* and a row of gradient keys is a row in which nothing is the primary action
|
|
32
|
-
* because everything is.
|
|
33
|
-
*/
|
|
34
|
-
const restClasses = {
|
|
35
|
-
solid: 'text-(--p-on-solid) [background-image:var(--p-fill)] [box-shadow:var(--p-elev)]',
|
|
36
|
-
glass: /* @__PURE__ */ [
|
|
37
|
-
glassClasses,
|
|
38
|
-
'border text-(--p-accent) bg-(--plass-glass)',
|
|
39
|
-
'[border-color:var(--plass-glass-line)]',
|
|
40
|
-
'[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
|
|
41
|
-
].join(' '),
|
|
42
|
-
ghost: 'text-(--p-accent) bg-(--p-soft)'
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Chosen is one step further up the ladder the chip is already on — the sheet
|
|
46
|
-
* holds more light. Deliberately not a different colour family: a filter that is
|
|
47
|
-
* on is still the same filter.
|
|
48
|
-
*
|
|
49
|
-
* `solid` has no opacity ladder to climb, because a gradient fill is the fill.
|
|
50
|
-
* So it answers the other way the design language allows: it casts its own
|
|
51
|
-
* colour onto the sheet under it. A chosen key lifts; an unchosen one lies flat.
|
|
52
|
-
*/
|
|
53
|
-
const selectedClasses = {
|
|
54
|
-
solid: '[box-shadow:var(--p-elev),var(--p-lift)]',
|
|
55
|
-
glass: 'bg-(--plass-glass-press) [border-color:var(--p-line-hover)]',
|
|
56
|
-
ghost: 'bg-(--p-soft-press)'
|
|
57
|
-
};
|
|
58
|
-
/**
|
|
59
|
-
* Only a chip that can be pressed answers the pointer, and it answers the way
|
|
60
|
-
* every other control does: `brightness()` on the gradient, a step up the
|
|
61
|
-
* opacity ladder on the other two.
|
|
62
|
-
*/
|
|
63
|
-
const hoverClasses = {
|
|
64
|
-
solid: 'hover:brightness-105 active:brightness-95',
|
|
65
|
-
glass: 'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)] active:bg-(--plass-glass-press)',
|
|
66
|
-
ghost: 'hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)'
|
|
67
|
-
};
|
|
68
|
-
const baseClasses = /* @__PURE__ */ [
|
|
69
|
-
// `items-center`, not `items-stretch`: everything in a chip — the icon, the
|
|
70
|
-
// label, the count plate, the × — is centred on one line. The pressable label
|
|
71
|
-
// asks for the height it needs with `self-stretch` instead, so making the
|
|
72
|
-
// shell stretch to suit it would knock every other child off the centre line.
|
|
73
|
-
'relative inline-flex max-w-full shrink-0 items-center select-none',
|
|
74
|
-
'align-middle leading-none font-medium whitespace-nowrap',
|
|
75
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
76
|
-
transitionClasses,
|
|
77
|
-
iconClasses
|
|
78
|
-
].join(' ');
|
|
79
|
-
/**
|
|
80
|
-
* The label, when the chip is pressable, is its own `<button>` inside the shell
|
|
81
|
-
* rather than the shell itself.
|
|
82
|
-
*
|
|
83
|
-
* That looks like indirection and is not: a chip can carry a delete affordance,
|
|
84
|
-
* which has to be a button too, and a `<button>` inside a `<button>` is invalid
|
|
85
|
-
* HTML that browsers un-nest on parse. Keeping the shell a `<span>` is what lets
|
|
86
|
-
* "activate this chip" and "remove this chip" both be real, focusable buttons.
|
|
87
|
-
*
|
|
88
|
-
* `self-stretch` so its hit area is the full height of the chip rather than the
|
|
89
|
-
* height of the words, and `rounded-[inherit]` so the focus ring traces the
|
|
90
|
-
* shell's corners rather than drawing a second, squarer rectangle inside them.
|
|
91
|
-
*/
|
|
92
|
-
const labelButtonClasses = /* @__PURE__ */ [
|
|
93
|
-
'flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch rounded-[inherit]',
|
|
94
|
-
focusRingClasses
|
|
95
|
-
].join(' ');
|
|
96
|
-
/**
|
|
97
|
-
* A compact token: a tag, a filter, a status, an entity plucked out of a list.
|
|
98
|
-
*
|
|
99
|
-
* The shell is always a `<span>`. What changes is what is inside it: a plain run
|
|
100
|
-
* of content, or — when `onClick` is given — a real `<button>` wrapping that
|
|
101
|
-
* content, plus a second button for `onDelete`. Both are reachable by keyboard,
|
|
102
|
-
* and neither is nested inside the other.
|
|
103
|
-
*
|
|
104
|
-
* An inert `<span>` carrying a click handler is the single most common way a
|
|
105
|
-
* component library loses its keyboard users, and a `<button>` inside a
|
|
106
|
-
* `<button>` is the most common way it invents a chip that Chrome silently
|
|
107
|
-
* rewrites. This shape is what avoids both.
|
|
108
|
-
*/
|
|
109
|
-
export const PlChip = /* @__PURE__ */ React.forwardRef(function PlChip({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, startIcon, endIcon, count, onDelete, deleteLabel = 'Remove', selected = false, disabled = false, className, style, children, onClick, ...props }, ref) {
|
|
110
|
-
const interactive = Boolean(onClick) && !disabled;
|
|
111
|
-
const step = chipScale[size];
|
|
112
|
-
const padX = paddingXClasses[density][step];
|
|
113
|
-
const shellClasses = [
|
|
114
|
-
baseClasses,
|
|
115
|
-
controlHeightClasses[step],
|
|
116
|
-
controlTextClasses[step],
|
|
117
|
-
gapClasses[step],
|
|
118
|
-
radiusClasses[step],
|
|
119
|
-
// An if/else rather than stacked variants: two Tailwind classes of equal
|
|
120
|
-
// specificity resolve by their order in the generated stylesheet.
|
|
121
|
-
disabled ? disabledClasses[variant] : restClasses[variant],
|
|
122
|
-
!disabled && selected ? selectedClasses[variant] : '',
|
|
123
|
-
interactive ? hoverClasses[variant] : '',
|
|
124
|
-
// With a pressable label the padding belongs to the button, so its hit area
|
|
125
|
-
// covers the whole chip rather than just the words.
|
|
126
|
-
interactive ? 'ps-0' : padX,
|
|
127
|
-
// The delete button brings its own padding; stacking the chip's on top of it
|
|
128
|
-
// would leave the × floating in the middle of a gap.
|
|
129
|
-
onDelete ? 'pe-1' : interactive ? 'pe-0' : '',
|
|
130
|
-
className ?? ''
|
|
131
|
-
]
|
|
132
|
-
.filter(Boolean)
|
|
133
|
-
.join(' ');
|
|
134
|
-
const label = (_jsxs(_Fragment, { children: [startIcon, hasContent(children) ? _jsx("span", { className: "min-w-0 truncate", children: children }) : null, endIcon, hasContent(count) ? (_jsx("span", { className: [
|
|
135
|
-
'ms-0.5 inline-flex shrink-0 items-center justify-center rounded-full px-1.5 py-px',
|
|
136
|
-
'text-[0.85em] leading-none font-semibold tabular-nums',
|
|
137
|
-
// On a filled chip the plate is light let through the fill; on a
|
|
138
|
-
// tinted or a bare one it is the accent showing under the words.
|
|
139
|
-
variant === 'solid'
|
|
140
|
-
? 'bg-(--plass-glow-on-fill) text-(--p-on-solid)'
|
|
141
|
-
: 'bg-(--p-soft-press) text-(--p-accent)'
|
|
142
|
-
].join(' '), children: count })) : null] }));
|
|
143
|
-
return (_jsxs("span", { ref: ref, className: shellClasses, style: { ...controlSlots(color, elevation, variant), ...style }, "aria-disabled": disabled && !interactive ? true : undefined, ...props, children: [interactive ? (_jsx("button", { type: "button", "aria-pressed": selected, className: `${labelButtonClasses} ${gapClasses[step]} ${padX}`, onClick: onClick, children: label })) : (label), onDelete ? (_jsx("button", { type: "button", "aria-label": deleteLabel, disabled: disabled, className: chipRemoveClasses, onClick: onDelete, children: _jsx(CloseIcon, {}) })) : null] }));
|
|
144
|
-
});
|
|
1
|
+
"use client";import{jsx as e,Fragment as s,jsxs as l}from"react/jsx-runtime";import*as t from"react";import{CloseIcon as o}from"../../internal/icons.js";import{chipRemoveClasses as a,controlHeightClasses as n,controlSlots as r,controlTextClasses as i,disabledClasses as c,focusRingClasses as p,gapClasses as d,glassClasses as m,hasContent as g,iconClasses as h,paddingXClasses as b,radiusClasses as f,transitionClasses as u}from"../../internal/styles.js";const v={xs:"xs",sm:"xs",md:"sm",lg:"md",xl:"lg"},x={solid:"text-(--p-on-solid) [background-image:var(--p-fill)] [box-shadow:var(--p-elev)]",glass:/* @__PURE__ */[m,"border text-(--p-accent) bg-(--plass-glass)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:"text-(--p-accent) bg-(--p-soft)"},j={solid:"[box-shadow:var(--p-elev),var(--p-lift)]",glass:"bg-(--plass-glass-press) [border-color:var(--p-line-hover)]",ghost:"bg-(--p-soft-press)"},w={solid:"hover:brightness-105 active:brightness-95",glass:"hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)] active:bg-(--plass-glass-press)",ghost:"hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)"},y=/* @__PURE__ */["relative inline-flex max-w-full shrink-0 items-center select-none","align-middle leading-none font-medium whitespace-nowrap","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",u,h].join(" "),k=/* @__PURE__ */["flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch rounded-[inherit]",p].join(" ");export const PlChip=/* @__PURE__ */t.forwardRef(function PlChip({variant:t="glass",size:p="md",color:m="primary",density:h="default",elevation:u=0,startIcon:N,endIcon:C,count:$,onDelete:B,deleteLabel:I="Remove",selected:P=!1,disabled:R=!1,className:z,style:D,children:F,onClick:L,...q},A){const E=Boolean(L)&&!R,G=v[p],H=b[h][G],J=[y,n[G],i[G],d[G],f[G],R?c[t]:x[t],!R&&P?j[t]:"",E?w[t]:"",E?"ps-0":H,B?"pe-1":E?"pe-0":"",z??""].filter(Boolean).join(" "),K=l(s,{children:[N,g(F)?e("span",{className:"min-w-0 truncate",children:F}):null,C,g($)?e("span",{className:["ms-0.5 inline-flex shrink-0 items-center justify-center rounded-full px-1.5 py-px","text-[0.85em] leading-none font-semibold tabular-nums","solid"===t?"bg-(--plass-glow-on-fill) text-(--p-on-solid)":"bg-(--p-soft-press) text-(--p-accent)"].join(" "),children:$}):null]});return l("span",{ref:A,className:J,style:{...r(m,u,t),...D},"aria-disabled":!(!R||E)||void 0,...q,children:[E?e("button",{type:"button","aria-pressed":P,className:`${k} ${d[G]} ${H}`,onClick:L,children:K}):K,B?e("button",{type:"button","aria-label":I,disabled:R,className:a,onClick:B,children:e(o,{})}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlChip}from"./PlChip.js";
|
|
@@ -1,65 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Collapsible as BaseUICollapsible } from '@base-ui/react/collapsible';
|
|
4
|
-
import { ChevronIcon } from '../../internal/icons.js';
|
|
5
|
-
import { cx, focusRingInsetClasses, gapClasses, hasContent, iconClasses, metaTextClasses, radiusClasses, sheetBodyClasses, sheetHeaderGapClasses, sheetPaddingXClasses, sheetPaddingYClasses, sheetRestClasses, sheetTitleClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
6
|
-
/**
|
|
7
|
-
* The space between the header and the body it opened.
|
|
8
|
-
*
|
|
9
|
-
* The header's own padding does not pay for it, which is the correction
|
|
10
|
-
* `PlAccordion` already carries: an open header is a tinted band with its own
|
|
11
|
-
* bottom edge, the body begins at that edge, and its first line lands against
|
|
12
|
-
* it with only half a leading in between — so the title and the paragraph
|
|
13
|
-
* explaining it read as one run of text broken by a colour change.
|
|
14
|
-
*/
|
|
15
|
-
const panelPaddingTopClasses = {
|
|
16
|
-
default: { xs: 'pt-1.5', sm: 'pt-2', md: 'pt-3', lg: 'pt-3.5', xl: 'pt-4' },
|
|
17
|
-
compact: { xs: 'pt-1', sm: 'pt-1.5', md: 'pt-2', lg: 'pt-2.5', xl: 'pt-3' }
|
|
18
|
-
};
|
|
19
|
-
/** And the space under it, on the same two tracks. */
|
|
20
|
-
const panelPaddingBottomClasses = {
|
|
21
|
-
default: { xs: 'pb-2.5', sm: 'pb-3', md: 'pb-5', lg: 'pb-6', xl: 'pb-7' },
|
|
22
|
-
compact: { xs: 'pb-2', sm: 'pb-2.5', md: 'pb-3.5', lg: 'pb-4', xl: 'pb-5' }
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* One section that folds, standing on its own.
|
|
26
|
-
*
|
|
27
|
-
* A `PlAccordion` is a *set* of these and owns which one of them is open; this
|
|
28
|
-
* is the same fold with nothing else beside it, so what it needs is an `open` of
|
|
29
|
-
* its own rather than a place in somebody's list. Reach for it for a "Show more"
|
|
30
|
-
* on a form, an optional block of settings, the details under a row.
|
|
31
|
-
*
|
|
32
|
-
* Base UI owns the parts that are genuinely hard: the `button` / panel pairing
|
|
33
|
-
* and the `aria-controls` / `aria-expanded` wiring between them,
|
|
34
|
-
* `hidden="until-found"`, and measuring the panel so it has a height to animate
|
|
35
|
-
* from.
|
|
36
|
-
*
|
|
37
|
-
* The panel's height *is* animated, which looks like an exception to the rule
|
|
38
|
-
* against moving things and is not: nothing is transformed, no text is
|
|
39
|
-
* resampled, and the content does not shift relative to the panel it is in — the
|
|
40
|
-
* panel is a window opening onto it. Content that appears instantly is a page
|
|
41
|
-
* that jumps, which is the failure the rule exists to prevent.
|
|
42
|
-
*/
|
|
43
|
-
export const PlCollapsible = /* @__PURE__ */ React.forwardRef(function PlCollapsible({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, open, defaultOpen = false, onOpenChange, title, subtitle, startIcon, action, trigger, indicator = true, disabled = false, padded = true, hiddenUntilFound = false, keepMounted = false, className, style, children, ...props }, ref) {
|
|
44
|
-
const padX = sheetPaddingXClasses[density][size];
|
|
45
|
-
const padY = sheetPaddingYClasses[density][size];
|
|
46
|
-
return (_jsxs(BaseUICollapsible.Root, { ref: ref, open: open, defaultOpen: defaultOpen, onOpenChange: (next) => onOpenChange?.(next), disabled: disabled, className: cx(
|
|
47
|
-
// `overflow-hidden` is what makes the panel a window rather than
|
|
48
|
-
// something that spills past the sheet's own corners while it moves.
|
|
49
|
-
'flex flex-col overflow-hidden', radiusClasses[size], sheetRestClasses[variant], transitionClasses, className), style: { ...surfaceSlots(color, elevation), ...style }, ...props, children: [trigger ? (_jsx(BaseUICollapsible.Trigger, { render: trigger })) : (_jsxs("div", { className: "flex w-full items-center", children: [_jsxs(BaseUICollapsible.Trigger, { className: cx('flex min-w-0 flex-1 cursor-pointer items-center text-start', padX, padY, gapClasses[size], transitionClasses, iconClasses,
|
|
50
|
-
// Inset rather than offset. The sheet clips its children so the
|
|
51
|
-
// panel can be a window, and `overflow: hidden` clips a
|
|
52
|
-
// descendant's outline along with everything else — an offset
|
|
53
|
-
// ring on a trigger that fills the top of the sheet would be
|
|
54
|
-
// shaved off on three sides.
|
|
55
|
-
focusRingInsetClasses, 'hover:bg-(--p-soft)', 'data-[panel-open]:bg-(--p-soft) data-[panel-open]:text-(--p-accent)', 'disabled:cursor-not-allowed disabled:bg-transparent disabled:opacity-50'), children: [hasContent(startIcon) ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)", children: startIcon })) : null, _jsxs("span", { className: cx('flex min-w-0 flex-1 flex-col', sheetHeaderGapClasses[size]), children: [hasContent(title) ? (_jsx("span", { className: cx('plass-title truncate font-semibold', sheetTitleClasses[size]), children: title })) : null, hasContent(subtitle) ? (_jsx("span", { className: cx('truncate text-(--plass-muted-fg)', metaTextClasses[size]), children: subtitle })) : null] }), indicator ? (_jsx("span", { className: cx('flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)', '[transition:rotate_var(--plass-duration)_var(--plass-ease)]', 'data-[panel-open]:rotate-180'), children: _jsx(ChevronIcon, {}) })) : null] }), hasContent(action) ? (_jsx("span", { className: cx('flex shrink-0 items-center', padX), children: action })) : null] })), _jsx(BaseUICollapsible.Panel, { hiddenUntilFound: hiddenUntilFound, keepMounted: keepMounted, className: cx('h-(--collapsible-panel-height) overflow-hidden', '[transition:height_var(--plass-duration-slow)_var(--plass-ease)]', 'data-[starting-style]:h-0 data-[ending-style]:h-0'), children: _jsx("div", { className: cx('min-w-0 text-(--plass-muted-fg)', sheetBodyClasses[size], padded ? padX : '',
|
|
56
|
-
// The default header already paid for the space above, so the
|
|
57
|
-
// body only owes what goes under it — otherwise a closed
|
|
58
|
-
// collapsible would look padded. A caller's own `trigger` has paid
|
|
59
|
-
// for nothing, so there the panel owes both.
|
|
60
|
-
padded
|
|
61
|
-
? trigger
|
|
62
|
-
? `${panelPaddingTopClasses[density][size]} ${panelPaddingBottomClasses[density][size]}`
|
|
63
|
-
: panelPaddingBottomClasses[density][size]
|
|
64
|
-
: ''), children: children }) })] }));
|
|
65
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as t from"react";import{Collapsible as s}from"@base-ui/react/collapsible";import{ChevronIcon as a}from"../../internal/icons.js";import{cx as n,focusRingInsetClasses as i,gapClasses as r,hasContent as p,iconClasses as d,metaTextClasses as o,radiusClasses as c,sheetBodyClasses as m,sheetHeaderGapClasses as f,sheetPaddingXClasses as h,sheetPaddingYClasses as u,sheetRestClasses as x,sheetTitleClasses as b,surfaceSlots as g,transitionClasses as N}from"../../internal/styles.js";const v={default:{xs:"pt-1.5",sm:"pt-2",md:"pt-3",lg:"pt-3.5",xl:"pt-4"},compact:{xs:"pt-1",sm:"pt-1.5",md:"pt-2",lg:"pt-2.5",xl:"pt-3"}},w={default:{xs:"pb-2.5",sm:"pb-3",md:"pb-5",lg:"pb-6",xl:"pb-7"},compact:{xs:"pb-2",sm:"pb-2.5",md:"pb-3.5",lg:"pb-4",xl:"pb-5"}};export const PlCollapsible=/* @__PURE__ */t.forwardRef(function PlCollapsible({variant:t="glass",size:y="md",color:j="primary",density:k="default",elevation:C=0,open:O,defaultOpen:_=!1,onOpenChange:P,title:F,subtitle:M,startIcon:R,action:T,trigger:U,indicator:$=!0,disabled:z=!1,padded:I=!0,hiddenUntilFound:q=!1,keepMounted:A=!1,className:B,style:D,children:E,...G},H){const J=h[k][y],K=u[k][y];return l(s.Root,{ref:H,open:O,defaultOpen:_,onOpenChange:e=>P?.(e),disabled:z,className:n("flex flex-col overflow-hidden",c[y],x[t],N,B),style:{...g(j,C),...D},...G,children:[U?e(s.Trigger,{render:U}):l("div",{className:"flex w-full items-center",children:[l(s.Trigger,{className:n("flex min-w-0 flex-1 cursor-pointer items-center text-start",J,K,r[y],N,d,i,"hover:bg-(--p-soft)","data-[panel-open]:bg-(--p-soft) data-[panel-open]:text-(--p-accent)","disabled:cursor-not-allowed disabled:bg-transparent disabled:opacity-50"),children:[p(R)?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)",children:R}):null,l("span",{className:n("flex min-w-0 flex-1 flex-col",f[y]),children:[p(F)?e("span",{className:n("plass-title truncate font-semibold",b[y]),children:F}):null,p(M)?e("span",{className:n("truncate text-(--plass-muted-fg)",o[y]),children:M}):null]}),$?e("span",{className:n("flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)","[transition:rotate_var(--plass-duration)_var(--plass-ease)]","data-[panel-open]:rotate-180"),children:e(a,{})}):null]}),p(T)?e("span",{className:n("flex shrink-0 items-center",J),children:T}):null]}),e(s.Panel,{hiddenUntilFound:q,keepMounted:A,className:n("h-(--collapsible-panel-height) overflow-hidden","[transition:height_var(--plass-duration-slow)_var(--plass-ease)]","data-[starting-style]:h-0 data-[ending-style]:h-0"),children:e("div",{className:n("min-w-0 text-(--plass-muted-fg)",m[y],I?J:"",I?U?`${v[k][y]} ${w[k][y]}`:w[k][y]:""),children:E})})]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlCollapsible}from"./PlCollapsible.js";
|
|
@@ -1,199 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Combobox as BaseUICombobox } from '@base-ui/react/combobox';
|
|
4
|
-
import { Field } from '@base-ui/react/field';
|
|
5
|
-
import { PlChip } from '../chip/PlChip.js';
|
|
6
|
-
import { CheckIcon, ChevronIcon, CloseIcon, PlusIcon } from '../../internal/icons.js';
|
|
7
|
-
import { chipRemoveClasses, controlHeightClasses, controlTextLeadingClasses, cx, disabledClasses, fieldReadOnlyClasses, fieldRestClasses, focusWithinRingClasses, gapClasses, glassClasses, hasContent, iconClasses, metaTextClasses, paddingXClasses, radiusClasses, stackGapClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
8
|
-
/** The field, and it is a PlTextField's shell to the pixel. */
|
|
9
|
-
const shellBaseClasses = /* @__PURE__ */ [
|
|
10
|
-
'group relative flex w-full cursor-text items-center',
|
|
11
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
12
|
-
transitionClasses,
|
|
13
|
-
focusWithinRingClasses,
|
|
14
|
-
iconClasses
|
|
15
|
-
].join(' ');
|
|
16
|
-
/**
|
|
17
|
-
* With chips in it the field cannot have a fixed height — the chips wrap. The
|
|
18
|
-
* padding is `(control height − chip height) / 2` instead, which makes a one-row
|
|
19
|
-
* combobox exactly as tall as the field beside it, and `min-h-*` catches the
|
|
20
|
-
* variant that carries no border.
|
|
21
|
-
*
|
|
22
|
-
* Keyed by `size` and never by `density`: density is horizontal padding only.
|
|
23
|
-
*/
|
|
24
|
-
const chipsInsetClasses = {
|
|
25
|
-
xs: 'min-h-6 py-0',
|
|
26
|
-
sm: 'min-h-8 py-0.5',
|
|
27
|
-
md: 'min-h-10 py-1',
|
|
28
|
-
lg: 'min-h-12 py-1.5',
|
|
29
|
-
xl: 'min-h-14 py-2'
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* The popup is one of the few surfaces in the library that is *supposed* to
|
|
33
|
-
* float, so unlike everything else it carries a shadow by default — at level 3.
|
|
34
|
-
* Identical to PlSelect's, because a combobox's list and a select's list are the
|
|
35
|
-
* same list.
|
|
36
|
-
*
|
|
37
|
-
* Every `--p-*` it reads is set on the popup itself rather than inherited from
|
|
38
|
-
* the Field around it. A portalled popup renders at the end of `<body>`, so it
|
|
39
|
-
* is outside the element the slots were declared on, and a `var()` with nothing
|
|
40
|
-
* to resolve to is not a fallback — `border-color` collapses to `currentColor`
|
|
41
|
-
* and `background-color` to transparent.
|
|
42
|
-
*/
|
|
43
|
-
const popupClasses = /* @__PURE__ */ [
|
|
44
|
-
glassClasses,
|
|
45
|
-
'max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain',
|
|
46
|
-
'w-[var(--anchor-width)] border bg-(--plass-glass-press) p-1',
|
|
47
|
-
'[border-color:var(--plass-glass-line)]',
|
|
48
|
-
'[box-shadow:var(--plass-shadow-3),var(--plass-gloss-glass)]',
|
|
49
|
-
'[outline:none]'
|
|
50
|
-
].join(' ');
|
|
51
|
-
const itemClasses = /* @__PURE__ */ [
|
|
52
|
-
'relative flex cursor-pointer items-center gap-2 select-none',
|
|
53
|
-
'rounded-(--plass-radius-xs) py-1.5 pe-2 ps-7',
|
|
54
|
-
transitionClasses,
|
|
55
|
-
// `data-highlighted` rather than `:hover`: it is also what the arrow keys
|
|
56
|
-
// move, so the mouse and the keyboard light the same row.
|
|
57
|
-
'data-[highlighted]:bg-(--p-soft-hover) data-[highlighted]:text-(--p-accent)',
|
|
58
|
-
'data-[selected]:font-semibold data-[selected]:text-(--p-accent)',
|
|
59
|
-
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
|
|
60
|
-
'[outline:none]'
|
|
61
|
-
].join(' ');
|
|
62
|
-
/** The chevron and the ×, which sit in the field rather than in the list. */
|
|
63
|
-
const adornmentClasses = /* @__PURE__ */ [
|
|
64
|
-
'inline-flex h-[1lh] shrink-0 cursor-pointer items-center justify-center',
|
|
65
|
-
'rounded-(--plass-radius-xs) text-(--plass-muted-fg)',
|
|
66
|
-
'[transition:color_var(--plass-duration)_var(--plass-ease)]',
|
|
67
|
-
'hover:text-(--p-accent)',
|
|
68
|
-
'focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:[outline-offset:1px]',
|
|
69
|
-
'disabled:cursor-not-allowed disabled:opacity-50'
|
|
70
|
-
].join(' ');
|
|
71
|
-
/** Always an array inside, however the caller spells it. */
|
|
72
|
-
function toArray(value) {
|
|
73
|
-
if (value === null || value === undefined) {
|
|
74
|
-
return [];
|
|
75
|
-
}
|
|
76
|
-
return Array.isArray(value) ? value.slice() : [value];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* A field you can type into and also choose from.
|
|
80
|
-
*
|
|
81
|
-
* The shell is a PlTextField's wearing a chevron, exactly as PlSelect's trigger
|
|
82
|
-
* is — the three have to be indistinguishable in a form, or the form looks
|
|
83
|
-
* assembled rather than designed. What is different is what the text does: it
|
|
84
|
-
* filters the list, and — unless `allowCustom` is off — it can become the value
|
|
85
|
-
* itself, offered as the last row rather than committed silently on blur.
|
|
86
|
-
*
|
|
87
|
-
* With `multiple` the chosen values become PlChips inside the field and the
|
|
88
|
-
* input goes on filtering after each one, so a set of tags is built without the
|
|
89
|
-
* field ever closing.
|
|
90
|
-
*
|
|
91
|
-
* Base UI owns everything hard about this: the filtering and its collator, the
|
|
92
|
-
* popup's positioning and flipping, the `combobox`/`listbox` wiring, arrow-key
|
|
93
|
-
* navigation across both the list and the chips, and the hidden input that makes
|
|
94
|
-
* the whole thing submit with a form.
|
|
95
|
-
*/
|
|
96
|
-
export function PlCombobox({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, items, multiple, value, defaultValue, onValueChange, onInputValueChange, allowCustom = true, customLabel, clearable = false, emptyMessage = 'No matches', limit, placeholder, label, description, error, invalid, startIcon, fullWidth = false, disabled = false, readOnly = false, required = false, name, open, defaultOpen, onOpenChange, openLabel = 'Open', clearLabel = 'Clear', removeLabel = (chip) => `Remove ${chip}`, inputRef, id, className, style, ...props }) {
|
|
97
|
-
const hasError = hasContent(error);
|
|
98
|
-
const isInvalid = invalid ?? hasError;
|
|
99
|
-
// Invalid re-points the whole slot family at `danger`, so the edge, the ring,
|
|
100
|
-
// the caret and the message all turn over together.
|
|
101
|
-
const family = isInvalid ? 'danger' : color;
|
|
102
|
-
const isMultiple = multiple === true;
|
|
103
|
-
const options = React.useMemo(() => items.map((item) => ({
|
|
104
|
-
value: item.value,
|
|
105
|
-
label: item.label ?? String(item.value),
|
|
106
|
-
disabled: item.disabled
|
|
107
|
-
})), [items]);
|
|
108
|
-
// The selection is mirrored internally even when the caller controls it. The
|
|
109
|
-
// "add this" row has to know what has already been chosen — otherwise a tag
|
|
110
|
-
// that was just added goes on being offered — and in uncontrolled mode there
|
|
111
|
-
// is nowhere else that knowledge lives.
|
|
112
|
-
const [ownSelection, setOwnSelection] = React.useState(() => toArray(defaultValue));
|
|
113
|
-
const selection = value === undefined ? ownSelection : toArray(value);
|
|
114
|
-
const [query, setQuery] = React.useState('');
|
|
115
|
-
/* A map rather than a `find`: this is called once per chosen item, and a
|
|
116
|
-
multi-select with a hundred options and twenty chips in it would otherwise
|
|
117
|
-
walk the list twenty times on every render. */
|
|
118
|
-
const byValue = React.useMemo(() => new Map(options.map((option) => [option.value, option])), [options]);
|
|
119
|
-
const entryFor = React.useCallback((item) => byValue.get(item) ?? { value: item, label: String(item), custom: true }, [byValue]);
|
|
120
|
-
// The row that offers what was typed. It is a real item rather than a special
|
|
121
|
-
// case in the keyboard handling, so Enter, a click and the arrow keys all
|
|
122
|
-
// reach it the same way every other row is reached — and Base UI's own filter
|
|
123
|
-
// keeps it visible, because its label *is* the query.
|
|
124
|
-
const trimmed = query.trim();
|
|
125
|
-
const folded = trimmed.toLocaleLowerCase();
|
|
126
|
-
const alreadyKnown = trimmed === '' ||
|
|
127
|
-
options.some((option) => option.label.toLocaleLowerCase() === folded ||
|
|
128
|
-
String(option.value).toLocaleLowerCase() === folded) ||
|
|
129
|
-
selection.some((item) => String(item).toLocaleLowerCase() === folded);
|
|
130
|
-
const customValue = allowCustom && !readOnly && !disabled && !alreadyKnown ? trimmed : null;
|
|
131
|
-
const listItems = React.useMemo(() => customValue === null
|
|
132
|
-
? options
|
|
133
|
-
: [...options, { value: customValue, label: customValue, custom: true }], [options, customValue]);
|
|
134
|
-
const baseValue = isMultiple
|
|
135
|
-
? selection.map(entryFor)
|
|
136
|
-
: selection.length > 0
|
|
137
|
-
? entryFor(selection[0])
|
|
138
|
-
: null;
|
|
139
|
-
function commit(next) {
|
|
140
|
-
if (value === undefined) {
|
|
141
|
-
setOwnSelection(next);
|
|
142
|
-
}
|
|
143
|
-
onValueChange?.((isMultiple ? next : (next[0] ?? null)));
|
|
144
|
-
}
|
|
145
|
-
const shellClasses = cx(shellBaseClasses, controlTextLeadingClasses[size], radiusClasses[size], gapClasses[size], isMultiple ? chipsInsetClasses[size] : controlHeightClasses[size],
|
|
146
|
-
// The chevron brings its own hit area; stacking the field's padding on top
|
|
147
|
-
// of it would leave the glyph floating in the middle of a gap.
|
|
148
|
-
`${paddingXClasses[density][size]} pe-1.5`,
|
|
149
|
-
// An if/else rather than stacked variants: two Tailwind classes of equal
|
|
150
|
-
// specificity resolve by their order in the generated stylesheet.
|
|
151
|
-
disabled
|
|
152
|
-
? disabledClasses[variant]
|
|
153
|
-
: readOnly
|
|
154
|
-
? fieldReadOnlyClasses[variant]
|
|
155
|
-
: fieldRestClasses[variant]);
|
|
156
|
-
const inputClasses = /* @__PURE__ */ [
|
|
157
|
-
// `self-stretch` and no height of its own, in both modes. An input centres
|
|
158
|
-
// its own text in its box, so letting the box be the full height of the row
|
|
159
|
-
// it sits on — the field in single mode, the chip line in multiple — is what
|
|
160
|
-
// puts the placeholder on the same baseline as the chips beside it.
|
|
161
|
-
'min-w-0 flex-1 self-stretch bg-transparent [font:inherit] text-inherit',
|
|
162
|
-
// Not `outline-none`: that utility zeroes `--tw-outline-style`, and the
|
|
163
|
-
// shell's focus ring is drawn from the same variable family.
|
|
164
|
-
'[outline:none]',
|
|
165
|
-
'placeholder:text-(--plass-muted-fg)',
|
|
166
|
-
'caret-(--p-accent) selection:bg-(--p-soft-press)',
|
|
167
|
-
'disabled:cursor-not-allowed'
|
|
168
|
-
].join(' ');
|
|
169
|
-
/**
|
|
170
|
-
* `afterChips` is the space between the last chip and where typing starts.
|
|
171
|
-
*
|
|
172
|
-
* The row's own `gap-1` is the distance between two chips, which is right
|
|
173
|
-
* between two things of the same kind and too little between a chip and a
|
|
174
|
-
* caret — the query reads as another chip's label rather than as the field's
|
|
175
|
-
* own text. It is only added when there is a chip to be clear of, so an empty
|
|
176
|
-
* multi-select lines its placeholder up with every other field in the form.
|
|
177
|
-
*/
|
|
178
|
-
const renderInput = (afterChips) => (_jsx(BaseUICombobox.Input, { ref: inputRef, placeholder: placeholder, className: cx(inputClasses, isMultiple && 'min-w-16', isMultiple && afterChips && 'ms-1.5') }));
|
|
179
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: cx('flex-col align-top', stackGapClasses[size], fullWidth ? 'flex w-full' : 'inline-flex', className), style: { ...surfaceSlots(family, elevation), ...style }, ...props, children: [label ? (_jsx(Field.Label, { className: cx(metaTextClasses[size], 'font-semibold', disabled ? 'text-(--plass-muted-fg)' : 'text-(--plass-fg)'), children: label })) : null, _jsxs(BaseUICombobox.Root, { id: id, name: name, items: listItems, multiple: isMultiple, value: baseValue, onValueChange: (next) => {
|
|
180
|
-
const chosen = next === null ? [] : Array.isArray(next) ? next : [next];
|
|
181
|
-
commit(chosen.map((entry) => entry.value));
|
|
182
|
-
},
|
|
183
|
-
// The text is Base UI's to own, not ours: in single mode it is the
|
|
184
|
-
// chosen option's label, which has to be there from the first paint, and
|
|
185
|
-
// in multiple mode it empties itself after each pick. What is kept here
|
|
186
|
-
// is a copy, and only so the "add this" row knows what was typed.
|
|
187
|
-
onInputValueChange: (next) => {
|
|
188
|
-
setQuery(next);
|
|
189
|
-
onInputValueChange?.(next);
|
|
190
|
-
}, open: open, defaultOpen: defaultOpen, onOpenChange: (next) => onOpenChange?.(next),
|
|
191
|
-
// The first match lights up as you type, so Enter commits without an
|
|
192
|
-
// arrow key first. This is what makes the "add this" row reachable from
|
|
193
|
-
// the keyboard at all: a value the list does not have is the only match
|
|
194
|
-
// there is, so it is the one Enter lands on.
|
|
195
|
-
autoHighlight: true, itemToStringLabel: (entry) => entry.label, itemToStringValue: (entry) => String(entry.value), isItemEqualToValue: (a, b) => a.value === b.value, limit: limit, disabled: disabled, readOnly: readOnly, required: required, children: [_jsxs(BaseUICombobox.InputGroup, { className: shellClasses, children: [startIcon ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)", children: startIcon })) : null, isMultiple ? (_jsx(BaseUICombobox.Chips, { className: "flex min-w-0 flex-1 flex-wrap items-center gap-1", children: _jsx(BaseUICombobox.Value, { children: (chosen) => (_jsxs(React.Fragment, { children: [chosen.map((entry) => (_jsx(BaseUICombobox.Chip, { render: _jsx(PlChip, { variant: "glass", size: size, color: family, density: "compact", disabled: disabled, endIcon: readOnly || disabled ? null : (_jsx(BaseUICombobox.ChipRemove, { "aria-label": removeLabel(entry.label), className: chipRemoveClasses, children: _jsx(CloseIcon, {}) })) }), children: entry.label }, String(entry.value)))), renderInput(chosen.length > 0)] })) }) })) : (renderInput(false)), clearable && !readOnly ? (_jsx(BaseUICombobox.Clear, { "aria-label": clearLabel, className: adornmentClasses, children: _jsx(CloseIcon, {}) })) : null, _jsx(BaseUICombobox.Trigger, { "aria-label": openLabel, className: adornmentClasses, children: _jsx(BaseUICombobox.Icon, { className: cx(
|
|
196
|
-
// The chevron is the one thing here that may turn: it is a
|
|
197
|
-
// glyph, not a label, and nothing about it resamples.
|
|
198
|
-
'flex items-center', '[transition:rotate_var(--plass-duration)_var(--plass-ease)]', 'data-[popup-open]:rotate-180'), children: _jsx(ChevronIcon, {}) }) })] }), _jsx(BaseUICombobox.Portal, { children: _jsx(BaseUICombobox.Positioner, { className: "plass-portal z-50 [outline:none]", sideOffset: 6, children: _jsxs(BaseUICombobox.Popup, { className: cx(popupClasses, radiusClasses[size], controlTextLeadingClasses[size]), style: surfaceSlots(family, 3), children: [_jsx(BaseUICombobox.Empty, { className: "px-2 py-1.5 text-(--plass-muted-fg) empty:hidden", children: emptyMessage }), _jsx(BaseUICombobox.List, { children: (entry) => (_jsx(BaseUICombobox.Item, { value: entry, disabled: entry.disabled, className: itemClasses, children: entry.custom ? (_jsxs(React.Fragment, { children: [_jsx("span", { className: "absolute start-1.5 flex size-4 items-center justify-center text-(--p-accent) [&_svg]:size-4", children: _jsx(PlusIcon, {}) }), _jsx("span", { className: "truncate", children: customLabel ? customLabel(entry.label) : `Add “${entry.label}”` })] })) : (_jsxs(React.Fragment, { children: [_jsx(BaseUICombobox.ItemIndicator, { className: "absolute start-1.5 flex size-4 items-center justify-center", children: _jsx(CheckIcon, {}) }), _jsx("span", { className: "truncate", children: entry.label })] })) }, `${entry.custom ? 'custom:' : ''}${String(entry.value)}`)) })] }) }) })] }), description ? (_jsx(Field.Description, { className: cx(metaTextClasses[size], 'text-(--plass-muted-fg)'), children: description })) : null, hasError ? (_jsx(Field.Error, { match: true, className: cx(metaTextClasses[size], 'text-(--p-accent)'), children: error })) : null] }));
|
|
199
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{Combobox as t}from"@base-ui/react/combobox";import{Field as s}from"@base-ui/react/field";import{PlChip as n}from"../chip/PlChip.js";import{CheckIcon as i,ChevronIcon as r,CloseIcon as o,PlusIcon as c}from"../../internal/icons.js";import{chipRemoveClasses as d,controlHeightClasses as m,controlTextLeadingClasses as u,cx as p,disabledClasses as h,fieldReadOnlyClasses as b,fieldRestClasses as g,focusWithinRingClasses as f,gapClasses as v,glassClasses as x,hasContent as y,iconClasses as N,metaTextClasses as w,paddingXClasses as C,radiusClasses as L,stackGapClasses as j,surfaceSlots as I,transitionClasses as S}from"../../internal/styles.js";const A=/* @__PURE__ */["group relative flex w-full cursor-text items-center","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",S,f,N].join(" "),O={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"},V=/* @__PURE__ */[x,"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]"].join(" "),_=/* @__PURE__ */["relative flex cursor-pointer items-center gap-2 select-none","rounded-(--plass-radius-xs) py-1.5 pe-2 ps-7",S,"data-[highlighted]:bg-(--p-soft-hover) data-[highlighted]:text-(--p-accent)","data-[selected]:font-semibold data-[selected]:text-(--p-accent)","data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50","[outline:none]"].join(" "),z=/* @__PURE__ */["inline-flex h-[1lh] shrink-0 cursor-pointer items-center justify-center","rounded-(--plass-radius-xs) text-(--plass-muted-fg)","[transition:color_var(--plass-duration)_var(--plass-ease)]","hover:text-(--p-accent)","focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:[outline-offset:1px]","disabled:cursor-not-allowed disabled:opacity-50"].join(" ");function toArray(e){return null==e?[]:Array.isArray(e)?e.slice():[e]}export function PlCombobox({variant:f="glass",size:x="md",color:N="primary",density:S="default",elevation:M=0,items:P,multiple:R,value:$,defaultValue:k,onValueChange:T,onInputValueChange:q,allowCustom:E=!0,customLabel:F,clearable:D=!1,emptyMessage:G="No matches",limit:H,placeholder:W,label:B,description:J,error:K,invalid:Q,startIcon:U,fullWidth:X=!1,disabled:Y=!1,readOnly:Z=!1,required:ee=!1,name:ae,open:le,defaultOpen:te,onOpenChange:se,openLabel:ne="Open",clearLabel:ie="Clear",removeLabel:re=e=>`Remove ${e}`,inputRef:oe,id:ce,className:de,style:me,...ue}){const pe=y(K),he=Q??pe,be=he?"danger":N,ge=!0===R,fe=l.useMemo(()=>P.map(e=>({value:e.value,label:e.label??String(e.value),disabled:e.disabled})),[P]),[ve,xe]=l.useState(()=>toArray(k)),ye=void 0===$?ve:toArray($),[Ne,we]=l.useState(""),Ce=l.useMemo(()=>new Map(fe.map(e=>[e.value,e])),[fe]),Le=l.useCallback(e=>Ce.get(e)??{value:e,label:String(e),custom:!0},[Ce]),je=Ne.trim(),Ie=je.toLocaleLowerCase(),Se=""===je||fe.some(e=>e.label.toLocaleLowerCase()===Ie||String(e.value).toLocaleLowerCase()===Ie)||ye.some(e=>String(e).toLocaleLowerCase()===Ie),Ae=!E||Z||Y||Se?null:je,Oe=l.useMemo(()=>null===Ae?fe:[...fe,{value:Ae,label:Ae,custom:!0}],[fe,Ae]),Ve=ge?ye.map(Le):ye.length>0?Le(ye[0]):null,_e=p(A,u[x],L[x],v[x],ge?O[x]:m[x],`${C[S][x]} pe-1.5`,Y?h[f]:Z?b[f]:g[f]),ze=/* @__PURE__ */["min-w-0 flex-1 self-stretch bg-transparent [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(t.Input,{ref:oe,placeholder:W,className:p(ze,ge&&"min-w-16",ge&&a&&"ms-1.5")});return a(s.Root,{disabled:Y,invalid:he,className:p("flex-col align-top",j[x],X?"flex w-full":"inline-flex",de),style:{...I(be,M),...me},...ue,children:[B?e(s.Label,{className:p(w[x],"font-semibold",Y?"text-(--plass-muted-fg)":"text-(--plass-fg)"),children:B}):null,a(t.Root,{id:ce,name:ae,items:Oe,multiple:ge,value:Ve,onValueChange:e=>{!function commit(e){void 0===$&&xe(e),T?.(ge?e:e[0]??null)}((null===e?[]:Array.isArray(e)?e:[e]).map(e=>e.value))},onInputValueChange:e=>{we(e),q?.(e)},open:le,defaultOpen:te,onOpenChange:e=>se?.(e),autoHighlight:!0,itemToStringLabel:e=>e.label,itemToStringValue:e=>String(e.value),isItemEqualToValue:(e,a)=>e.value===a.value,limit:H,disabled:Y,readOnly:Z,required:ee,children:[a(t.InputGroup,{className:_e,children:[U?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)",children:U}):null,ge?e(t.Chips,{className:"flex min-w-0 flex-1 flex-wrap items-center gap-1",children:e(t.Value,{children:s=>a(l.Fragment,{children:[s.map(a=>e(t.Chip,{render:e(n,{variant:"glass",size:x,color:be,density:"compact",disabled:Y,endIcon:Z||Y?null:e(t.ChipRemove,{"aria-label":re(a.label),className:d,children:e(o,{})})}),children:a.label},String(a.value))),renderInput(s.length>0)]})})}):renderInput(!1),D&&!Z?e(t.Clear,{"aria-label":ie,className:z,children:e(o,{})}):null,e(t.Trigger,{"aria-label":ne,className:z,children:e(t.Icon,{className:p("flex items-center","[transition:rotate_var(--plass-duration)_var(--plass-ease)]","data-[popup-open]:rotate-180"),children:e(r,{})})})]}),e(t.Portal,{children:e(t.Positioner,{className:"plass-portal z-50 [outline:none]",sideOffset:6,children:a(t.Popup,{className:p(V,L[x],u[x]),style:I(be,3),children:[e(t.Empty,{className:"px-2 py-1.5 text-(--plass-muted-fg) empty:hidden",children:G}),e(t.List,{children:s=>e(t.Item,{value:s,disabled:s.disabled,className:_,children:s.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(c,{})}),e("span",{className:"truncate",children:F?F(s.label):`Add “${s.label}”`})]}):a(l.Fragment,{children:[e(t.ItemIndicator,{className:"absolute start-1.5 flex size-4 items-center justify-center",children:e(i,{})}),e("span",{className:"truncate",children:s.label})]})},`${s.custom?"custom:":""}${String(s.value)}`)})]})})})]}),J?e(s.Description,{className:p(w[x],"text-(--plass-muted-fg)"),children:J}):null,pe?e(s.Error,{match:!0,className:p(w[x],"text-(--p-accent)"),children:K}):null]})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlCombobox}from"./PlCombobox.js";
|
|
@@ -1,47 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
-
import { cx, sheetPaddingXClasses } from '../../internal/styles.js';
|
|
4
|
-
/**
|
|
5
|
-
* The measure ladder, in `rem` rather than in Tailwind's named `max-w-*` steps,
|
|
6
|
-
* so that a container's `lg` and an `lg:` utility are the same 64rem.
|
|
7
|
-
*
|
|
8
|
-
* Tailwind's own container scale is a different set of numbers, and having two
|
|
9
|
-
* ladders called `lg` on one page is how a layout drifts by a few pixels for no
|
|
10
|
-
* reason anybody can find later.
|
|
11
|
-
*/
|
|
12
|
-
const maxWidthClasses = {
|
|
13
|
-
xs: 'max-w-[30rem]',
|
|
14
|
-
sm: 'max-w-[40rem]',
|
|
15
|
-
md: 'max-w-[48rem]',
|
|
16
|
-
lg: 'max-w-[64rem]',
|
|
17
|
-
xl: 'max-w-[80rem]'
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Horizontal breathing room, and optionally a measure.
|
|
21
|
-
*
|
|
22
|
-
* It draws nothing — no sheet, no hairline, no shadow — for the same reason
|
|
23
|
-
* `PlAspectRatio` does not. The outermost element on a page is the one thing
|
|
24
|
-
* that must not decide what the page looks like, and a container that carried a
|
|
25
|
-
* `variant` would put a second pane behind every card on it.
|
|
26
|
-
*
|
|
27
|
-
* The two questions it answers are deliberately separate from how the content
|
|
28
|
-
* inside divides itself up: a container holds a `PlGrid` as happily as it holds
|
|
29
|
-
* a single paragraph, and a grid needs no container around it.
|
|
30
|
-
*
|
|
31
|
-
* The gutter is the **sheet** padding track rather than the control one. What
|
|
32
|
-
* sits inside a container is a page, and the margin a page keeps from the edge
|
|
33
|
-
* of a window is the margin a card keeps around a paragraph — not the room a
|
|
34
|
-
* label needs beside the edge of the key it is printed on.
|
|
35
|
-
*/
|
|
36
|
-
export const PlContainer = /* @__PURE__ */ React.forwardRef(function PlContainer({ maxWidth = 'none', padded = true, size = 'md', density = 'default', centered = true, render, className, children, ...props }, ref) {
|
|
37
|
-
const classNames = cx('block w-full', maxWidth === 'none' ? '' : maxWidthClasses[maxWidth], centered ? 'mx-auto' : '', padded ? sheetPaddingXClasses[density][size] : '', className);
|
|
38
|
-
return useRender({
|
|
39
|
-
render,
|
|
40
|
-
ref,
|
|
41
|
-
props: {
|
|
42
|
-
className: classNames,
|
|
43
|
-
children,
|
|
44
|
-
...props
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
});
|
|
1
|
+
"use client";import*as e from"react";import{useRender as r}from"@base-ui/react/use-render";import{cx as n,sheetPaddingXClasses as m}from"../../internal/styles.js";const a={xs:"max-w-[30rem]",sm:"max-w-[40rem]",md:"max-w-[48rem]",lg:"max-w-[64rem]",xl:"max-w-[80rem]"};export const PlContainer=/* @__PURE__ */e.forwardRef(function PlContainer({maxWidth:e="none",padded:t=!0,size:o="md",density:s="default",centered:d=!0,render:l,className:i,children:c,...x},f){const u=n("block w-full","none"===e?"":a[e],d?"mx-auto":"",t?m[s][o]:"",i);return r({render:l,ref:f,props:{className:u,children:c,...x}})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlContainer}from"./PlContainer.js";
|