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,115 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Select as BaseUISelect } from '@base-ui/react/select';
|
|
4
|
-
import { Field } from '@base-ui/react/field';
|
|
5
|
-
import { CheckIcon, ChevronIcon } from '../../internal/icons.js';
|
|
6
|
-
import { WidthSizer } from '../../internal/sizer.js';
|
|
7
|
-
import { controlHeightClasses, controlTextLeadingClasses, disabledClasses, fieldReadOnlyClasses, fieldRestClasses, focusWithinRingClasses, gapClasses, glassClasses, hasContent, iconClasses, metaTextClasses, paddingXClasses, radiusClasses, stackGapClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
8
|
-
/** The trigger is a PlTextField's shell, to the pixel. */
|
|
9
|
-
const triggerBaseClasses = /* @__PURE__ */ [
|
|
10
|
-
'group relative flex w-full cursor-pointer items-center select-none',
|
|
11
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
12
|
-
transitionClasses,
|
|
13
|
-
focusWithinRingClasses,
|
|
14
|
-
iconClasses
|
|
15
|
-
].join(' ');
|
|
16
|
-
/**
|
|
17
|
-
* The popup is the one surface in the library that is *supposed* to float, so
|
|
18
|
-
* unlike everything else it carries a shadow by default — at level 3.
|
|
19
|
-
*
|
|
20
|
-
* It is the glass at its most opaque, because it has a page under it rather
|
|
21
|
-
* than a sheet: a 62%-translucent pane over arbitrary body copy is a pane you
|
|
22
|
-
* read the body copy through.
|
|
23
|
-
*
|
|
24
|
-
* Every `--p-*` it reads is set on the popup itself rather than inherited from
|
|
25
|
-
* the Field around it. A portalled popup renders at the end of `<body>`, so it
|
|
26
|
-
* is outside the element the slots were declared on, and a `var()` with nothing
|
|
27
|
-
* to resolve to is not a fallback — `border-color` collapses to `currentColor`
|
|
28
|
-
* (a black hairline) and `background-color` to transparent.
|
|
29
|
-
*/
|
|
30
|
-
const popupClasses = /* @__PURE__ */ [
|
|
31
|
-
glassClasses,
|
|
32
|
-
'max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain',
|
|
33
|
-
'min-w-[var(--anchor-width)] border bg-(--plass-glass-press) p-1',
|
|
34
|
-
'[border-color:var(--plass-glass-line)]',
|
|
35
|
-
'[box-shadow:var(--plass-shadow-3),var(--plass-gloss-glass)]',
|
|
36
|
-
'[outline:none]'
|
|
37
|
-
].join(' ');
|
|
38
|
-
const itemClasses = /* @__PURE__ */ [
|
|
39
|
-
'relative flex cursor-pointer items-center gap-2 select-none',
|
|
40
|
-
'rounded-(--plass-radius-xs) py-1.5 pe-2 ps-7',
|
|
41
|
-
transitionClasses,
|
|
42
|
-
// `data-highlighted` rather than `:hover`: it is also what the arrow keys
|
|
43
|
-
// move, so the mouse and the keyboard light the same row.
|
|
44
|
-
'data-[highlighted]:bg-(--p-soft-hover) data-[highlighted]:text-(--p-accent)',
|
|
45
|
-
'data-[selected]:font-semibold data-[selected]:text-(--p-accent)',
|
|
46
|
-
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
|
|
47
|
-
'[outline:none]'
|
|
48
|
-
].join(' ');
|
|
49
|
-
/**
|
|
50
|
-
* One value chosen from a list of them.
|
|
51
|
-
*
|
|
52
|
-
* The trigger is a PlTextField's shell wearing a chevron, on purpose: a form
|
|
53
|
-
* where the select is a different height, radius or material from the fields
|
|
54
|
-
* around it is a form that looks assembled rather than designed. That is why
|
|
55
|
-
* `fieldRestClasses` lives in `internal/styles` and not in either component.
|
|
56
|
-
*
|
|
57
|
-
* Base UI owns everything hard about this — the popup's positioning and
|
|
58
|
-
* flipping, the focus trap, typeahead, the hidden input that makes it submit —
|
|
59
|
-
* and the work here is the surface it all wears.
|
|
60
|
-
*/
|
|
61
|
-
export const PlSelect = /* @__PURE__ */ React.forwardRef(function PlSelect({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, items, value, defaultValue, onValueChange, placeholder, label, description, error, invalid, startIcon, fullWidth = false, disabled = false, readOnly = false, required = false, name, id, className, style, ...props }, ref) {
|
|
62
|
-
const hasError = hasContent(error);
|
|
63
|
-
const isInvalid = invalid ?? hasError;
|
|
64
|
-
// Invalid re-points the whole slot family at `danger`, so the edge, the ring
|
|
65
|
-
// and the message all turn over together.
|
|
66
|
-
const family = isInvalid ? 'danger' : color;
|
|
67
|
-
// Base UI reads this to render the chosen option's *label* in the trigger
|
|
68
|
-
// rather than its raw value, which is the only way `<Select.Value>` can show
|
|
69
|
-
// "Seoul" for `value="kr-11"` before the popup has ever been mounted.
|
|
70
|
-
const baseItems = React.useMemo(() => items.map((item) => ({ label: item.label ?? String(item.value), value: item.value })), [items]);
|
|
71
|
-
// Holds the trigger open at the width of the longest thing it could say, so
|
|
72
|
-
// choosing a shorter option does not shrink the field out from under the
|
|
73
|
-
// pointer that chose it.
|
|
74
|
-
const sizerSamples = React.useMemo(() => [
|
|
75
|
-
...items.map((item) => item.label ?? String(item.value)),
|
|
76
|
-
...(hasContent(placeholder) ? [placeholder] : [])
|
|
77
|
-
], [items, placeholder]);
|
|
78
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: [
|
|
79
|
-
'flex-col align-top',
|
|
80
|
-
stackGapClasses[size],
|
|
81
|
-
fullWidth ? 'flex w-full' : 'inline-flex',
|
|
82
|
-
className ?? ''
|
|
83
|
-
]
|
|
84
|
-
.filter(Boolean)
|
|
85
|
-
.join(' '), style: { ...surfaceSlots(family, elevation), ...style }, ...props, children: [label ? (_jsx(Field.Label, { className: [
|
|
86
|
-
metaTextClasses[size],
|
|
87
|
-
'font-semibold',
|
|
88
|
-
disabled ? 'text-(--plass-muted-fg)' : 'text-(--plass-fg)'
|
|
89
|
-
].join(' '), children: label })) : null, _jsxs(BaseUISelect.Root, { id: id, name: name, items: baseItems, value: value, defaultValue: defaultValue, onValueChange: (next) => onValueChange?.(next), disabled: disabled, readOnly: readOnly, required: required, children: [_jsxs(BaseUISelect.Trigger, { ref: ref, className: [
|
|
90
|
-
triggerBaseClasses,
|
|
91
|
-
controlHeightClasses[size],
|
|
92
|
-
controlTextLeadingClasses[size],
|
|
93
|
-
radiusClasses[size],
|
|
94
|
-
gapClasses[size],
|
|
95
|
-
paddingXClasses[density][size],
|
|
96
|
-
// An if/else rather than stacked variants: two Tailwind classes of
|
|
97
|
-
// equal specificity resolve by their order in the generated sheet.
|
|
98
|
-
disabled
|
|
99
|
-
? disabledClasses[variant]
|
|
100
|
-
: readOnly
|
|
101
|
-
? `${fieldReadOnlyClasses[variant]} cursor-default`
|
|
102
|
-
: fieldRestClasses[variant]
|
|
103
|
-
].join(' '), children: [startIcon ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)", children: startIcon })) : null, _jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx(BaseUISelect.Value, { className: [
|
|
104
|
-
'w-full truncate text-start',
|
|
105
|
-
// The placeholder is muted the same way a PlTextField's is, so
|
|
106
|
-
// an empty select and an empty field read as equally empty.
|
|
107
|
-
'data-[placeholder]:text-(--plass-muted-fg)'
|
|
108
|
-
].join(' '), placeholder: placeholder }), _jsx(WidthSizer, { samples: sizerSamples })] }), _jsx(BaseUISelect.Icon, { className: [
|
|
109
|
-
'flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)',
|
|
110
|
-
// The chevron is the one thing here that may turn: it is a glyph,
|
|
111
|
-
// not a label, and nothing about it resamples.
|
|
112
|
-
'[transition:rotate_var(--plass-duration)_var(--plass-ease)]',
|
|
113
|
-
'data-[popup-open]:rotate-180'
|
|
114
|
-
].join(' '), children: _jsx(ChevronIcon, {}) })] }), _jsx(BaseUISelect.Portal, { children: _jsx(BaseUISelect.Positioner, { className: "plass-portal z-50 [outline:none]", sideOffset: 6, alignItemWithTrigger: false, children: _jsx(BaseUISelect.Popup, { className: `${popupClasses} ${radiusClasses[size]} ${controlTextLeadingClasses[size]}`, style: surfaceSlots(family, 3), children: items.map((item) => (_jsxs(BaseUISelect.Item, { value: item.value, disabled: item.disabled, className: itemClasses, children: [_jsx(BaseUISelect.ItemIndicator, { className: "absolute start-1.5 flex size-4 items-center justify-center", children: _jsx(CheckIcon, {}) }), _jsx(BaseUISelect.ItemText, { className: "truncate", children: item.label ?? String(item.value) })] }, String(item.value)))) }) }) })] }), description ? (_jsx(Field.Description, { className: `${metaTextClasses[size]} text-(--plass-muted-fg)`, children: description })) : null, hasError ? (_jsx(Field.Error, { match: true, className: `${metaTextClasses[size]} text-(--p-accent)`, children: error })) : null] }));
|
|
115
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{Select as t}from"@base-ui/react/select";import{Field as s}from"@base-ui/react/field";import{CheckIcon as r,ChevronIcon as i}from"../../internal/icons.js";import{WidthSizer as n}from"../../internal/sizer.js";import{controlHeightClasses as o,controlTextLeadingClasses as c,disabledClasses as d,fieldReadOnlyClasses as m,fieldRestClasses as u,focusWithinRingClasses as p,gapClasses as h,glassClasses as f,hasContent as g,iconClasses as x,metaTextClasses as v,paddingXClasses as b,radiusClasses as j,stackGapClasses as N,surfaceSlots as w,transitionClasses as y}from"../../internal/styles.js";const I=/* @__PURE__ */["group relative flex w-full cursor-pointer items-center select-none","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",y,p,x].join(" "),S=/* @__PURE__ */[f,"max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain","min-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",y,"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(" ");export const PlSelect=/* @__PURE__ */l.forwardRef(function PlSelect({variant:p="glass",size:f="md",color:x="primary",density:y="default",elevation:P=0,items:V,value:z,defaultValue:k,onValueChange:O,placeholder:R,label:T,description:q,error:C,invalid:M,startIcon:W,fullWidth:_=!1,disabled:B=!1,readOnly:D=!1,required:E=!1,name:L,id:A,className:F,style:G,...H},J){const K=g(C),Q=M??K,U=Q?"danger":x,X=l.useMemo(()=>V.map(e=>({label:e.label??String(e.value),value:e.value})),[V]),Y=l.useMemo(()=>[...V.map(e=>e.label??String(e.value)),...g(R)?[R]:[]],[V,R]);return a(s.Root,{disabled:B,invalid:Q,className:["flex-col align-top",N[f],_?"flex w-full":"inline-flex",F??""].filter(Boolean).join(" "),style:{...w(U,P),...G},...H,children:[T?e(s.Label,{className:[v[f],"font-semibold",B?"text-(--plass-muted-fg)":"text-(--plass-fg)"].join(" "),children:T}):null,a(t.Root,{id:A,name:L,items:X,value:z,defaultValue:k,onValueChange:e=>O?.(e),disabled:B,readOnly:D,required:E,children:[a(t.Trigger,{ref:J,className:[I,o[f],c[f],j[f],h[f],b[y][f],B?d[p]:D?`${m[p]} cursor-default`:u[p]].join(" "),children:[W?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)",children:W}):null,a("span",{className:"flex min-w-0 flex-1 flex-col",children:[e(t.Value,{className:["w-full truncate text-start","data-[placeholder]:text-(--plass-muted-fg)"].join(" "),placeholder:R}),e(n,{samples:Y})]}),e(t.Icon,{className:["flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg)","[transition:rotate_var(--plass-duration)_var(--plass-ease)]","data-[popup-open]:rotate-180"].join(" "),children:e(i,{})})]}),e(t.Portal,{children:e(t.Positioner,{className:"plass-portal z-50 [outline:none]",sideOffset:6,alignItemWithTrigger:!1,children:e(t.Popup,{className:`${S} ${j[f]} ${c[f]}`,style:w(U,3),children:V.map(l=>a(t.Item,{value:l.value,disabled:l.disabled,className:$,children:[e(t.ItemIndicator,{className:"absolute start-1.5 flex size-4 items-center justify-center",children:e(r,{})}),e(t.ItemText,{className:"truncate",children:l.label??String(l.value)})]},String(l.value)))})})})]}),q?e(s.Description,{className:`${v[f]} text-(--plass-muted-fg)`,children:q}):null,K?e(s.Error,{match:!0,className:`${v[f]} text-(--p-accent)`,children:C}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlSelect}from"./PlSelect.js";
|
|
@@ -1,142 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { useRender } from '@base-ui/react/use-render';
|
|
4
|
-
import { controlHeightClasses, controlSquareClasses, surfaceSlots } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* A line's height is the type scale itself — the same lengths
|
|
7
|
-
* `sheetBodyClasses` sets as a font size — so a placeholder occupies the em box
|
|
8
|
-
* of the text that replaces it. The leading around it is `lineGapClasses`, and
|
|
9
|
-
* the two together add up to that ladder's line box.
|
|
10
|
-
*/
|
|
11
|
-
const lineHeightClasses = {
|
|
12
|
-
xs: 'h-[0.6875rem]',
|
|
13
|
-
sm: 'h-[0.75rem]',
|
|
14
|
-
md: 'h-[0.8125rem]',
|
|
15
|
-
lg: 'h-[0.9375rem]',
|
|
16
|
-
xl: 'h-[1.0625rem]'
|
|
17
|
-
};
|
|
18
|
-
/** The leading: the body ladder's line box less the bar drawn in it. */
|
|
19
|
-
const lineGapClasses = {
|
|
20
|
-
xs: 'gap-[0.3125rem]',
|
|
21
|
-
sm: 'gap-[0.375rem]',
|
|
22
|
-
md: 'gap-[0.5625rem]',
|
|
23
|
-
lg: 'gap-[0.5625rem]',
|
|
24
|
-
xl: 'gap-[0.6875rem]'
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* A bar's corner, at ~45% of its own height — held just short of the 50% that
|
|
28
|
-
* would make it a capsule.
|
|
29
|
-
*
|
|
30
|
-
* Not `radiusClasses`, which is ~30% of a *control's* height: 12px on a 13px bar
|
|
31
|
-
* is a capsule and a half. Not `tickRadiusClasses` either, which is sized
|
|
32
|
-
* against a box rather than against a run of text.
|
|
33
|
-
*/
|
|
34
|
-
const barRadiusClasses = {
|
|
35
|
-
xs: 'rounded-[0.3125rem]',
|
|
36
|
-
sm: 'rounded-[0.3125rem]',
|
|
37
|
-
md: 'rounded-[0.375rem]',
|
|
38
|
-
lg: 'rounded-[0.4375rem]',
|
|
39
|
-
xl: 'rounded-[0.5rem]'
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* What a `rect` is as tall as when nothing says otherwise: a thumbnail. Anything
|
|
43
|
-
* else wants `height`, and most uses of this shape pass one.
|
|
44
|
-
*/
|
|
45
|
-
const blockHeightClasses = {
|
|
46
|
-
xs: 'h-12',
|
|
47
|
-
sm: 'h-16',
|
|
48
|
-
md: 'h-20',
|
|
49
|
-
lg: 'h-28',
|
|
50
|
-
xl: 'h-36'
|
|
51
|
-
};
|
|
52
|
-
/** A block's corner is the sheet ladder, because a block stands for a sheet. */
|
|
53
|
-
const blockRadiusClasses = {
|
|
54
|
-
xs: 'rounded-(--plass-radius-xs)',
|
|
55
|
-
sm: 'rounded-(--plass-radius-sm)',
|
|
56
|
-
md: 'rounded-(--plass-radius-md)',
|
|
57
|
-
lg: 'rounded-(--plass-radius-lg)',
|
|
58
|
-
xl: 'rounded-(--plass-radius-xl)'
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* The surface, and it is deliberately **not** glass.
|
|
62
|
-
*
|
|
63
|
-
* Every other sheet in the library is translucent over a blurred backdrop,
|
|
64
|
-
* because it is a thing sitting on the page. A skeleton is the opposite: it is
|
|
65
|
-
* the shape of something that is not there yet, so it is a flat tint and nothing
|
|
66
|
-
* else — no blur, no hairline, no gloss, no shadow. It also keeps a page of
|
|
67
|
-
* thirty placeholders from asking for thirty backdrop filters.
|
|
68
|
-
*/
|
|
69
|
-
const fillClasses = 'relative overflow-hidden bg-(--p-soft-hover)';
|
|
70
|
-
/** Pixels for a bare number, and whatever was written for a string. */
|
|
71
|
-
function length(value) {
|
|
72
|
-
if (value === undefined) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
return typeof value === 'number' ? `${value}px` : value;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* The shape of something that has not loaded yet.
|
|
79
|
-
*
|
|
80
|
-
* It reserves the space the real thing will take, which is the whole job: a card
|
|
81
|
-
* that grows by 200px when its image arrives has moved everything below it while
|
|
82
|
-
* somebody was reading. A spinner cannot do that.
|
|
83
|
-
*
|
|
84
|
-
* The three shapes are the three things a layout is made of — a run of text, a
|
|
85
|
-
* block and a circle — and each is sized off the ladder the real component uses,
|
|
86
|
-
* so a `md` line is as tall as `md` type and a `md` circle is exactly a
|
|
87
|
-
* `PlAvatar` at `md`.
|
|
88
|
-
*/
|
|
89
|
-
export const PlSkeleton = /* @__PURE__ */ React.forwardRef(function PlSkeleton({ shape = 'line', lines = 1, size = 'md', color = 'secondary', width, height, animated = true, label, render, className, style, ...props }, ref) {
|
|
90
|
-
// The sweep lives in `styles.css` rather than in an arbitrary variant for the
|
|
91
|
-
// reason `.plass-glow` does: a keyframe is not a Tailwind variant, so there is
|
|
92
|
-
// nothing to express one with.
|
|
93
|
-
const sweep = animated ? 'plass-skeleton' : '';
|
|
94
|
-
const shapeClasses = shape === 'circle'
|
|
95
|
-
? `shrink-0 rounded-full ${controlHeightClasses[size]} ${controlSquareClasses[size]}`
|
|
96
|
-
: shape === 'rect'
|
|
97
|
-
? `w-full ${blockRadiusClasses[size]} ${height === undefined ? blockHeightClasses[size] : ''}`
|
|
98
|
-
: `w-full ${barRadiusClasses[size]} ${lineHeightClasses[size]}`;
|
|
99
|
-
// Unlabelled it is scenery and says nothing; labelled it is the one element
|
|
100
|
-
// that reports the wait for the region around it.
|
|
101
|
-
const announce = label
|
|
102
|
-
? { role: 'status', 'aria-busy': true, 'aria-label': label }
|
|
103
|
-
: { 'aria-hidden': true };
|
|
104
|
-
// A run of lines is a stack of bars rather than one box, so the gaps between
|
|
105
|
-
// them are real gaps: text has leading, and a striped gradient would not
|
|
106
|
-
// survive a caller putting the block in a flex row. The root then holds only
|
|
107
|
-
// the stacking, which is why it drops the fill and the sweep.
|
|
108
|
-
const stacked = shape === 'line' && lines > 1;
|
|
109
|
-
return useRender({
|
|
110
|
-
render,
|
|
111
|
-
ref,
|
|
112
|
-
props: {
|
|
113
|
-
className: (stacked
|
|
114
|
-
? ['flex w-full flex-col', lineGapClasses[size], className ?? '']
|
|
115
|
-
: [fillClasses, sweep, shapeClasses, className ?? ''])
|
|
116
|
-
.filter(Boolean)
|
|
117
|
-
.join(' '),
|
|
118
|
-
style: {
|
|
119
|
-
...surfaceSlots(color, 0),
|
|
120
|
-
width: length(width),
|
|
121
|
-
height: length(height),
|
|
122
|
-
...style
|
|
123
|
-
},
|
|
124
|
-
...announce,
|
|
125
|
-
...(stacked
|
|
126
|
-
? {
|
|
127
|
-
children: Array.from({ length: lines }, (_, index) => (_jsx("div", { className: [
|
|
128
|
-
fillClasses,
|
|
129
|
-
sweep,
|
|
130
|
-
barRadiusClasses[size],
|
|
131
|
-
lineHeightClasses[size],
|
|
132
|
-
// The last line of a paragraph does not reach the margin.
|
|
133
|
-
index === lines - 1 ? 'w-3/5' : 'w-full'
|
|
134
|
-
]
|
|
135
|
-
.filter(Boolean)
|
|
136
|
-
.join(' ') }, index)))
|
|
137
|
-
}
|
|
138
|
-
: null),
|
|
139
|
-
...props
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
});
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import*as r from"react";import{useRender as l}from"@base-ui/react/use-render";import{controlHeightClasses as s,controlSquareClasses as d,surfaceSlots as n}from"../../internal/styles.js";const o={xs:"h-[0.6875rem]",sm:"h-[0.75rem]",md:"h-[0.8125rem]",lg:"h-[0.9375rem]",xl:"h-[1.0625rem]"},a={xs:"gap-[0.3125rem]",sm:"gap-[0.375rem]",md:"gap-[0.5625rem]",lg:"gap-[0.5625rem]",xl:"gap-[0.6875rem]"},m={xs:"rounded-[0.3125rem]",sm:"rounded-[0.3125rem]",md:"rounded-[0.375rem]",lg:"rounded-[0.4375rem]",xl:"rounded-[0.5rem]"},i={xs:"h-12",sm:"h-16",md:"h-20",lg:"h-28",xl:"h-36"},t={xs:"rounded-(--plass-radius-xs)",sm:"rounded-(--plass-radius-sm)",md:"rounded-(--plass-radius-md)",lg:"rounded-(--plass-radius-lg)",xl:"rounded-(--plass-radius-xl)"},u="relative overflow-hidden bg-(--p-soft-hover)";function length(e){if(void 0!==e)return"number"==typeof e?`${e}px`:e}export const PlSkeleton=/* @__PURE__ */r.forwardRef(function PlSkeleton({shape:r="line",lines:h=1,size:f="md",color:p="secondary",width:c,height:g,animated:x=!0,label:w,render:y,className:$,style:b,...v},j){const k=x?"plass-skeleton":"",N="circle"===r?`shrink-0 rounded-full ${s[f]} ${d[f]}`:"rect"===r?`w-full ${t[f]} ${void 0===g?i[f]:""}`:`w-full ${m[f]} ${o[f]}`,B=w?{role:"status","aria-busy":!0,"aria-label":w}:{"aria-hidden":!0},P="line"===r&&h>1;return l({render:y,ref:j,props:{className:(P?["flex w-full flex-col",a[f],$??""]:[u,k,N,$??""]).filter(Boolean).join(" "),style:{...n(p,0),width:length(c),height:length(g),...b},...B,...P?{children:Array.from({length:h},(r,l)=>e("div",{className:[u,k,m[f],o[f],l===h-1?"w-3/5":"w-full"].filter(Boolean).join(" ")},l))}:null,...v}})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlSkeleton}from"./PlSkeleton.js";
|
|
@@ -1,132 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Slider as BaseUISlider } from '@base-ui/react/slider';
|
|
4
|
-
import { controlSlots, focusRingClasses, metaTextClasses, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* Groove thickness and thumb diameter.
|
|
7
|
-
*
|
|
8
|
-
* The thumb is deliberately far bigger than the groove — it is the only part of
|
|
9
|
-
* the control you can actually hit, and a thumb sized to match a 6px rail is a
|
|
10
|
-
* thumb nobody catches on a touchscreen.
|
|
11
|
-
*/
|
|
12
|
-
const trackThicknessClasses = {
|
|
13
|
-
xs: 'h-1',
|
|
14
|
-
sm: 'h-1.5',
|
|
15
|
-
md: 'h-1.5',
|
|
16
|
-
lg: 'h-2',
|
|
17
|
-
xl: 'h-2.5'
|
|
18
|
-
};
|
|
19
|
-
const verticalThicknessClasses = {
|
|
20
|
-
xs: 'w-1',
|
|
21
|
-
sm: 'w-1.5',
|
|
22
|
-
md: 'w-1.5',
|
|
23
|
-
lg: 'w-2',
|
|
24
|
-
xl: 'w-2.5'
|
|
25
|
-
};
|
|
26
|
-
const thumbSizeClasses = {
|
|
27
|
-
xs: 'size-3',
|
|
28
|
-
sm: 'size-3.5',
|
|
29
|
-
md: 'size-4.5',
|
|
30
|
-
lg: 'size-5',
|
|
31
|
-
xl: 'size-6'
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* The control is taller than the groove it holds so that the whole strip is a
|
|
35
|
-
* pointer target, not just the rail. Base UI presses the track wherever you
|
|
36
|
-
* click, and a 6px hit area would make that unusable.
|
|
37
|
-
*/
|
|
38
|
-
const trackBoxHeightClasses = {
|
|
39
|
-
xs: 'h-4',
|
|
40
|
-
sm: 'h-4.5',
|
|
41
|
-
md: 'h-5',
|
|
42
|
-
lg: 'h-6',
|
|
43
|
-
xl: 'h-7'
|
|
44
|
-
};
|
|
45
|
-
const trackBoxWidthClasses = {
|
|
46
|
-
xs: 'w-4',
|
|
47
|
-
sm: 'w-4.5',
|
|
48
|
-
md: 'w-5',
|
|
49
|
-
lg: 'w-6',
|
|
50
|
-
xl: 'w-7'
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* The rail is `--plass-track`, the same groove a PlSwitch's off state is — one
|
|
54
|
-
* neutral ink, so the two controls in a settings panel are visibly made of the
|
|
55
|
-
* same thing.
|
|
56
|
-
*
|
|
57
|
-
* It used to be the glass at its most opaque with `--plass-well` cut into it,
|
|
58
|
-
* on the reading that a rail and a filled field are the same recessed object.
|
|
59
|
-
* They are not: a field is a box you look *into* and a rail is a line you look
|
|
60
|
-
* *along*, and the part of it that matters is the part with nothing on it. A
|
|
61
|
-
* 4px white-on-white line with a shadow in it is the run a reader is supposed
|
|
62
|
-
* to be judging the value against, and on a light page there was nothing there
|
|
63
|
-
* to judge it against at all.
|
|
64
|
-
*
|
|
65
|
-
* The indicator over it is the gradient, so the filled part of the run is made
|
|
66
|
-
* of exactly the same material as the button that submits the form it is in.
|
|
67
|
-
*
|
|
68
|
-
* Both are pills rather than taking the radius ladder: this is a channel
|
|
69
|
-
* something travels along, not a sheet.
|
|
70
|
-
*/
|
|
71
|
-
const railClasses = 'rounded-full bg-(--plass-track)';
|
|
72
|
-
const indicatorClasses = `rounded-full [background-image:var(--p-fill)] ${transitionClasses}`;
|
|
73
|
-
/**
|
|
74
|
-
* The thumb is a key of tinted glass on the same gradient as the run behind it,
|
|
75
|
-
* separated from it by a ring in the page's own surface colour — white on a
|
|
76
|
-
* light page, near-black on a dark one — so it never dissolves into the
|
|
77
|
-
* indicator it is sitting on.
|
|
78
|
-
*
|
|
79
|
-
* It grows a halo on hover and while dragging rather than growing itself. The
|
|
80
|
-
* no-transform rule is not relaxed just because this particular part carries no
|
|
81
|
-
* label.
|
|
82
|
-
*/
|
|
83
|
-
const thumbClasses = /* @__PURE__ */ [
|
|
84
|
-
'rounded-full border-2 [background-image:var(--p-fill)]',
|
|
85
|
-
'[border-color:var(--plass-surface)]',
|
|
86
|
-
'[box-shadow:var(--p-elev),var(--p-lift)]',
|
|
87
|
-
'cursor-grab select-none active:cursor-grabbing',
|
|
88
|
-
transitionClasses,
|
|
89
|
-
'hover:[box-shadow:var(--p-elev-hover),var(--p-lift-hover),0_0_0_4px_var(--p-soft)]',
|
|
90
|
-
focusRingClasses,
|
|
91
|
-
'data-[dragging]:[box-shadow:var(--p-elev-press),var(--p-lift-press),0_0_0_6px_var(--p-soft-hover)]'
|
|
92
|
-
].join(' ');
|
|
93
|
-
/**
|
|
94
|
-
* Disabled is the light going out, exactly as it is on every other control: the
|
|
95
|
-
* shape and the place stay, the saturation and half the opacity go.
|
|
96
|
-
*/
|
|
97
|
-
const disabledSliderClasses = 'opacity-50 saturate-[0.35] [&_*]:cursor-not-allowed';
|
|
98
|
-
/**
|
|
99
|
-
* A value chosen along a range.
|
|
100
|
-
*
|
|
101
|
-
* Pass an array to `value` or `defaultValue` and it becomes a range slider with
|
|
102
|
-
* one thumb per entry — there is no separate `range` prop, because the shape of
|
|
103
|
-
* the value already says which one this is.
|
|
104
|
-
*/
|
|
105
|
-
export const PlSlider = /* @__PURE__ */ React.forwardRef(function PlSlider({ size = 'md', color = 'primary', elevation = 1, orientation = 'horizontal', label, description, showValue = false, disabled = false, className, style, ...props }, ref) {
|
|
106
|
-
const vertical = orientation === 'vertical';
|
|
107
|
-
// One thumb per value. The count comes off whichever of the two was given, so
|
|
108
|
-
// an uncontrolled range slider works without being told it is one.
|
|
109
|
-
const values = props.value ?? props.defaultValue;
|
|
110
|
-
const thumbCount = Array.isArray(values) ? values.length : 1;
|
|
111
|
-
return (_jsxs(BaseUISlider.Root, { ref: ref, orientation: orientation, disabled: disabled, className: [
|
|
112
|
-
'flex',
|
|
113
|
-
vertical ? 'w-fit flex-col items-center gap-2' : 'w-full flex-col gap-1.5',
|
|
114
|
-
disabled ? disabledSliderClasses : '',
|
|
115
|
-
className ?? ''
|
|
116
|
-
]
|
|
117
|
-
.filter(Boolean)
|
|
118
|
-
.join(' '),
|
|
119
|
-
// `solid`, always: the run and the thumb are the coloured thing, so the
|
|
120
|
-
// interaction light on them is white rather than the family's own tint.
|
|
121
|
-
style: { ...controlSlots(color, elevation, 'solid'), ...style }, ...props, children: [label || showValue ? (_jsxs("div", { className: `flex w-full items-baseline gap-2 ${metaTextClasses[size]}`, children: [label ? (_jsx(BaseUISlider.Label, { className: "font-semibold text-(--plass-fg)", children: label })) : null, showValue ? (_jsx(BaseUISlider.Value, { className: "ms-auto tabular-nums text-(--plass-muted-fg)", children: typeof showValue === 'function' ? showValue : null })) : null] })) : null, _jsx(BaseUISlider.Control, { className: [
|
|
122
|
-
'flex touch-none items-center justify-center select-none',
|
|
123
|
-
vertical
|
|
124
|
-
? `${trackBoxWidthClasses[size]} h-40 flex-col`
|
|
125
|
-
: `w-full ${trackBoxHeightClasses[size]}`
|
|
126
|
-
].join(' '), children: _jsxs(BaseUISlider.Track, { className: [
|
|
127
|
-
railClasses,
|
|
128
|
-
vertical
|
|
129
|
-
? `${verticalThicknessClasses[size]} h-full`
|
|
130
|
-
: `${trackThicknessClasses[size]} w-full`
|
|
131
|
-
].join(' '), children: [_jsx(BaseUISlider.Indicator, { className: indicatorClasses }), Array.from({ length: thumbCount }, (_, index) => (_jsx(BaseUISlider.Thumb, { index: index, className: `${thumbClasses} ${thumbSizeClasses[size]}` }, index)))] }) }), description ? (_jsx("div", { className: `${metaTextClasses[size]} text-(--plass-muted-fg)`, children: description })) : null] }));
|
|
132
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as s from"react";import{Slider as a}from"@base-ui/react/slider";import{controlSlots as r,focusRingClasses as o,metaTextClasses as t,transitionClasses as i}from"../../internal/styles.js";const n={xs:"h-1",sm:"h-1.5",md:"h-1.5",lg:"h-2",xl:"h-2.5"},c={xs:"w-1",sm:"w-1.5",md:"w-1.5",lg:"w-2",xl:"w-2.5"},d={xs:"size-3",sm:"size-3.5",md:"size-4.5",lg:"size-5",xl:"size-6"},m={xs:"h-4",sm:"h-4.5",md:"h-5",lg:"h-6",xl:"h-7"},u={xs:"w-4",sm:"w-4.5",md:"w-5",lg:"w-6",xl:"w-7"},f="rounded-full bg-(--plass-track)",h=`rounded-full [background-image:var(--p-fill)] ${i}`,p=/* @__PURE__ */["rounded-full border-2 [background-image:var(--p-fill)]","[border-color:var(--plass-surface)]","[box-shadow:var(--p-elev),var(--p-lift)]","cursor-grab select-none active:cursor-grabbing",i,"hover:[box-shadow:var(--p-elev-hover),var(--p-lift-hover),0_0_0_4px_var(--p-soft)]",o,"data-[dragging]:[box-shadow:var(--p-elev-press),var(--p-lift-press),0_0_0_6px_var(--p-soft-hover)]"].join(" ");export const PlSlider=/* @__PURE__ */s.forwardRef(function PlSlider({size:s="md",color:o="primary",elevation:i=1,orientation:x="horizontal",label:g,description:v,showValue:w=!1,disabled:b=!1,className:y,style:N,...j},$){const _="vertical"===x,z=j.value??j.defaultValue,k=Array.isArray(z)?z.length:1;return l(a.Root,{ref:$,orientation:x,disabled:b,className:["flex",_?"w-fit flex-col items-center gap-2":"w-full flex-col gap-1.5",b?"opacity-50 saturate-[0.35] [&_*]:cursor-not-allowed":"",y??""].filter(Boolean).join(" "),style:{...r(o,i,"solid"),...N},...j,children:[g||w?l("div",{className:`flex w-full items-baseline gap-2 ${t[s]}`,children:[g?e(a.Label,{className:"font-semibold text-(--plass-fg)",children:g}):null,w?e(a.Value,{className:"ms-auto tabular-nums text-(--plass-muted-fg)",children:"function"==typeof w?w:null}):null]}):null,e(a.Control,{className:["flex touch-none items-center justify-center select-none",_?`${u[s]} h-40 flex-col`:`w-full ${m[s]}`].join(" "),children:l(a.Track,{className:[f,_?`${c[s]} h-full`:`${n[s]} w-full`].join(" "),children:[e(a.Indicator,{className:h}),Array.from({length:k},(l,r)=>e(a.Thumb,{index:r,className:`${p} ${d[s]}`},r))]})}),v?e("div",{className:`${t[s]} text-(--plass-muted-fg)`,children:v}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlSlider}from"./PlSlider.js";
|
|
@@ -1,74 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { PlButton } from '../button/PlButton.js';
|
|
4
|
-
import { cx, hasContent, metaTextClasses, radiusClasses, sheetPaddingXClasses, sheetPaddingYClasses, sheetRestClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* The wash between the blurred content and the words on top of it.
|
|
7
|
-
*
|
|
8
|
-
* Blur alone is not cover. It takes a paragraph apart but leaves its colour and
|
|
9
|
-
* its rhythm — a photograph blurred at 10px is still recognisably a photograph
|
|
10
|
-
* of a face — and it leaves the button standing on whatever happened to be
|
|
11
|
-
* underneath it. Mixing the page's own surface over the top settles both: the
|
|
12
|
-
* content goes to a wash of its own colours, and the button gets something to
|
|
13
|
-
* stand on.
|
|
14
|
-
*/
|
|
15
|
-
const scrimClasses = '[background-color:color-mix(in_oklab,var(--plass-surface)_55%,transparent)]';
|
|
16
|
-
/**
|
|
17
|
-
* Content that is covered until somebody asks for it.
|
|
18
|
-
*
|
|
19
|
-
* The cover is a **blur** rather than a `display: none`, which is the whole
|
|
20
|
-
* point: a reader can see that there is something there, roughly how much of it
|
|
21
|
-
* there is, and — with `maxHeight` — that it has been clamped. What they cannot
|
|
22
|
-
* do is read it by accident, which is the one thing a spoiler is for.
|
|
23
|
-
*
|
|
24
|
-
* While it is covered the content is `inert`, so it is not tabbable, not
|
|
25
|
-
* readable by a screen reader and not selectable by a drag across the page. A
|
|
26
|
-
* spoiler that could be defeated by <kbd>Ctrl</kbd>+<kbd>A</kbd> is not a
|
|
27
|
-
* spoiler.
|
|
28
|
-
*
|
|
29
|
-
* The sheet is never dyed, exactly as on a `PlCard`: what a spoiler holds is a
|
|
30
|
-
* photograph, a paragraph, a plot twist, and it arrives with its own colours.
|
|
31
|
-
* The family shows up on the button and in the hairline and stops there.
|
|
32
|
-
*/
|
|
33
|
-
export const PlSpoiler = /* @__PURE__ */ React.forwardRef(function PlSpoiler({ revealed, defaultRevealed = false, onRevealedChange, label = 'Reveal', hideLabel = 'Hide', description = 'This may contain spoilers', action, reversible = false, maxHeight, blur = 10, padded = true, variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, className, style, children, ...props }, ref) {
|
|
34
|
-
const contentId = React.useId();
|
|
35
|
-
const [uncontrolled, setUncontrolled] = React.useState(defaultRevealed);
|
|
36
|
-
const open = revealed ?? uncontrolled;
|
|
37
|
-
const change = (next) => {
|
|
38
|
-
if (revealed === undefined) {
|
|
39
|
-
setUncontrolled(next);
|
|
40
|
-
}
|
|
41
|
-
onRevealedChange?.(next);
|
|
42
|
-
};
|
|
43
|
-
const insetX = sheetPaddingXClasses[density][size];
|
|
44
|
-
const insetY = sheetPaddingYClasses[density][size];
|
|
45
|
-
const notice = description === false ? null : description;
|
|
46
|
-
return (_jsxs("div", { ref: ref, className: cx(
|
|
47
|
-
// A grid rather than a box with an absolutely positioned cover on it,
|
|
48
|
-
// and the difference is what happens to a *short* spoiler: an
|
|
49
|
-
// absolute cover is laid out against a box the content alone decided
|
|
50
|
-
// the height of, so a one-line spoiler clips its own Reveal button.
|
|
51
|
-
// Both children are put in the same cell instead, so the row is as
|
|
52
|
-
// tall as whichever of them needs more and they stretch to match.
|
|
53
|
-
'isolate grid overflow-hidden', radiusClasses[size], sheetRestClasses[variant], transitionClasses, className), style: { ...surfaceSlots(color, elevation), ...style }, ...props, children: [_jsx("div", { id: contentId, className: cx('min-w-0 [grid-area:1/1]', padded ? `${insetX} ${insetY}` : '', '[transition:filter_var(--plass-duration-slow)_var(--plass-ease)]', 'motion-reduce:[transition-duration:0ms]', open ? '' : 'select-none'), style: {
|
|
54
|
-
filter: open ? undefined : `blur(${blur}px)`,
|
|
55
|
-
// The clamp is only ever on the covered state: revealing something
|
|
56
|
-
// and leaving it in a box with a scrollbar is answering the wrong
|
|
57
|
-
// question.
|
|
58
|
-
maxHeight: open ? undefined : maxHeight,
|
|
59
|
-
overflow: open ? undefined : 'hidden'
|
|
60
|
-
},
|
|
61
|
-
// `inert` rather than `aria-hidden`: it takes the content out of the
|
|
62
|
-
// tab order, off the accessibility tree and out of the selection in
|
|
63
|
-
// one attribute — and `aria-hidden` alone would leave a keyboard
|
|
64
|
-
// reader tabbing into a link their screen reader has been told is not
|
|
65
|
-
// there.
|
|
66
|
-
inert: !open, children: children }), open ? null : (_jsxs("div", { className: cx('z-10 flex flex-col items-center justify-center gap-2 text-center [grid-area:1/1]', insetX, insetY, scrimClasses), children: [hasContent(notice) ? (_jsx("p", { className: cx('m-0 text-(--plass-muted-fg)', metaTextClasses[size]), children: notice })) : null, action ?? (_jsx(PlButton, { size: size, color: color, density: density, onClick: () => change(true), "aria-expanded": false, "aria-controls": contentId, children: label }))] })), open && reversible ? (_jsx("div", { className: cx('flex justify-end', insetX,
|
|
67
|
-
// The row takes the sheet's padding on both axes and then gives
|
|
68
|
-
// the top back: `padded` content already ends with a full gap, and
|
|
69
|
-
// two of them stacked is a hole between the text and the way back
|
|
70
|
-
// out. `pt-0` beating `py-*` is Tailwind's own
|
|
71
|
-
// longhand-after-shorthand ordering rather than an accident of how
|
|
72
|
-
// these are concatenated.
|
|
73
|
-
insetY, 'pt-0'), children: _jsx(PlButton, { variant: "ghost", size: size, color: color, density: density, onClick: () => change(false), "aria-expanded": true, "aria-controls": contentId, children: hideLabel }) })) : null] }));
|
|
74
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import*as a from"react";import{PlButton as r}from"../button/PlButton.js";import{cx as l,hasContent as s,metaTextClasses as t,radiusClasses as n,sheetPaddingXClasses as o,sheetPaddingYClasses as d,sheetRestClasses as c,surfaceSlots as m,transitionClasses as u}from"../../internal/styles.js";export const PlSpoiler=/* @__PURE__ */a.forwardRef(function PlSpoiler({revealed:p,defaultRevealed:f=!1,onRevealedChange:v,label:h="Reveal",hideLabel:x="Hide",description:g="This may contain spoilers",action:y,reversible:b=!1,maxHeight:j,blur:N=10,padded:w=!0,variant:k="glass",size:z="md",color:R="primary",density:_="default",elevation:C=0,className:H,style:P,children:S,...$},B){const I=a.useId(),[L,T]=a.useState(f),q=p??L,change=e=>{void 0===p&&T(e),v?.(e)},A=o[_][z],D=d[_][z],E=!1===g?null:g;return i("div",{ref:B,className:l("isolate grid overflow-hidden",n[z],c[k],u,H),style:{...m(R,C),...P},...$,children:[e("div",{id:I,className:l("min-w-0 [grid-area:1/1]",w?`${A} ${D}`:"","[transition:filter_var(--plass-duration-slow)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]",q?"":"select-none"),style:{filter:q?void 0:`blur(${N}px)`,maxHeight:q?void 0:j,overflow:q?void 0:"hidden"},inert:!q,children:S}),q?null:i("div",{className:l("z-10 flex flex-col items-center justify-center gap-2 text-center [grid-area:1/1]",A,D,"[background-color:color-mix(in_oklab,var(--plass-surface)_55%,transparent)]"),children:[s(E)?e("p",{className:l("m-0 text-(--plass-muted-fg)",t[z]),children:E}):null,y??e(r,{size:z,color:R,density:_,onClick:()=>change(!0),"aria-expanded":!1,"aria-controls":I,children:h})]}),q&&b?e("div",{className:l("flex justify-end",A,D,"pt-0"),children:e(r,{variant:"ghost",size:z,color:R,density:_,onClick:()=>change(!1),"aria-expanded":!0,"aria-controls":I,children:x})}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlSpoiler}from"./PlSpoiler.js";
|