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,131 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Switch as BaseUISwitch } from '@base-ui/react/switch';
|
|
4
|
-
import { Field } from '@base-ui/react/field';
|
|
5
|
-
import { controlSlots, focusRingClasses, hasContent, metaTextClasses, tickRowTextClasses } from '../../internal/styles.js';
|
|
6
|
-
/**
|
|
7
|
-
* Track and thumb.
|
|
8
|
-
*
|
|
9
|
-
* The thumb is inset 2px on every side, so its diameter is the track's height
|
|
10
|
-
* minus 4 and the `left` it travels to is `100% − 2px − diameter`. That is the
|
|
11
|
-
* one number per step that has to be written out; everything else falls out of
|
|
12
|
-
* `inset-y-0.5` and `aspect-square`.
|
|
13
|
-
*/
|
|
14
|
-
const trackClasses = {
|
|
15
|
-
xs: 'h-3.5 w-6',
|
|
16
|
-
sm: 'h-4 w-7',
|
|
17
|
-
md: 'h-5 w-9',
|
|
18
|
-
lg: 'h-6 w-11',
|
|
19
|
-
xl: 'h-7 w-13'
|
|
20
|
-
};
|
|
21
|
-
const thumbTravelClasses = {
|
|
22
|
-
xs: 'data-[checked]:left-[calc(100%-0.75rem)]',
|
|
23
|
-
sm: 'data-[checked]:left-[calc(100%-0.875rem)]',
|
|
24
|
-
md: 'data-[checked]:left-[calc(100%-1.125rem)]',
|
|
25
|
-
lg: 'data-[checked]:left-[calc(100%-1.375rem)]',
|
|
26
|
-
xl: 'data-[checked]:left-[calc(100%-1.625rem)]'
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* A pill, and one of the two places in the library that is right.
|
|
30
|
-
*
|
|
31
|
-
* Everywhere else the radius stops well short of 50%, because the flat run
|
|
32
|
-
* along the top and bottom edge is what reads as a sheet with its corners cut
|
|
33
|
-
* off. A switch is not a sheet — it is a track something runs along, and a
|
|
34
|
-
* track with corners is a track the thumb would have to climb out of.
|
|
35
|
-
*/
|
|
36
|
-
const trackBaseClasses = /* @__PURE__ */ [
|
|
37
|
-
'relative inline-flex shrink-0 rounded-full',
|
|
38
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
39
|
-
// `background-image` is in the list because the on state is the gradient, and
|
|
40
|
-
// `left` deliberately is not: it belongs to the thumb, which is the one thing
|
|
41
|
-
// in the library that actually moves.
|
|
42
|
-
'[transition-property:background-color,background-image,box-shadow,filter]',
|
|
43
|
-
'[transition-duration:var(--plass-duration)]',
|
|
44
|
-
'[transition-timing-function:var(--plass-ease)]',
|
|
45
|
-
focusRingClasses
|
|
46
|
-
].join(' ');
|
|
47
|
-
/**
|
|
48
|
-
* Off, the track is the **groove** — `--plass-track`, the one neutral ink in the
|
|
49
|
-
* library whose job is to be seen from across a room. On, it is the family's
|
|
50
|
-
* gradient.
|
|
51
|
-
*
|
|
52
|
-
* Off used to be the glass at its most opaque with `--plass-well` falling into
|
|
53
|
-
* it, and it was wrong twice over. It was invisible: a 88%-white pill with a
|
|
54
|
-
* white thumb in it, set on a near-white page, is a switch a reader cannot find
|
|
55
|
-
* until they have already flipped it — and the off state is the one a settings
|
|
56
|
-
* list is mostly made of. And where it *was* visible, in the dark, an inset
|
|
57
|
-
* shadow under a thumb carrying a drop shadow of its own was a moulded rocker
|
|
58
|
-
* in a bevelled slot, which is the one picture this design language exists to
|
|
59
|
-
* not draw.
|
|
60
|
-
*
|
|
61
|
-
* So there is no well, no gloss and no edge either. A groove that is a *tone*
|
|
62
|
-
* rather than a recess needs no hairline to say where it ends, and the hairline
|
|
63
|
-
* it used to have was the second of two lines round the same object once the
|
|
64
|
-
* focus ring arrived.
|
|
65
|
-
*
|
|
66
|
-
* Dropping the border also squares the thumb's travel. `inset-y-0.5` and
|
|
67
|
-
* `left-0.5` are measured from the padding box; the `left` a checked thumb
|
|
68
|
-
* travels to is measured from the track's *width*. With a 1px edge between
|
|
69
|
-
* them the two disagreed, and every switch in the library sat 2px from its
|
|
70
|
-
* track on the left and 4px from it on the right.
|
|
71
|
-
*/
|
|
72
|
-
const restTrackClasses = /* @__PURE__ */ [
|
|
73
|
-
'cursor-pointer bg-(--plass-track)',
|
|
74
|
-
'hover:brightness-[0.97] dark:hover:brightness-110',
|
|
75
|
-
'data-[checked]:[background-image:var(--p-fill)]',
|
|
76
|
-
'data-[checked]:[box-shadow:var(--p-lift)] data-[checked]:hover:brightness-105'
|
|
77
|
-
].join(' ');
|
|
78
|
-
const readOnlyTrackClasses = /* @__PURE__ */ [
|
|
79
|
-
'cursor-default bg-(--plass-track) saturate-[0.55]',
|
|
80
|
-
'data-[checked]:[background-image:var(--p-fill)]',
|
|
81
|
-
'data-[checked]:shadow-none'
|
|
82
|
-
].join(' ');
|
|
83
|
-
const disabledTrackClasses = /* @__PURE__ */ [
|
|
84
|
-
'cursor-not-allowed bg-(--plass-track)',
|
|
85
|
-
'opacity-50 saturate-[0.35] shadow-none',
|
|
86
|
-
'data-[checked]:[background-image:var(--p-fill)]'
|
|
87
|
-
].join(' ');
|
|
88
|
-
/**
|
|
89
|
-
* The thumb is white in both themes — not `--plass-surface`, which is a near
|
|
90
|
-
* navy in the dark and left the off state as a grey lozenge in a grey slot.
|
|
91
|
-
*
|
|
92
|
-
* It keeps a shadow, and it is the smallest one on the ladder rather than the
|
|
93
|
-
* `--plass-shadow-1` it used to carry: the thumb is the one part of a switch
|
|
94
|
-
* that genuinely is above the surface it moves along, so it casts something —
|
|
95
|
-
* but a 14px disc under a 4px-blurred, 14px-wide shadow is a knob, not a light.
|
|
96
|
-
*/
|
|
97
|
-
const thumbClasses = /* @__PURE__ */ [
|
|
98
|
-
'absolute inset-y-0.5 left-0.5 aspect-square rounded-full bg-white',
|
|
99
|
-
'[box-shadow:0_1px_2px_rgb(20_40_90/0.25)]',
|
|
100
|
-
'[transition:left_var(--plass-duration)_var(--plass-ease)]'
|
|
101
|
-
].join(' ');
|
|
102
|
-
/**
|
|
103
|
-
* An immediate on/off.
|
|
104
|
-
*
|
|
105
|
-
* The difference from a PlCheckbox is not visual, it is temporal: a checkbox is
|
|
106
|
-
* a value that gets submitted with a form, a switch takes effect the moment it
|
|
107
|
-
* moves. If there is a Save button underneath, it should have been a checkbox.
|
|
108
|
-
*/
|
|
109
|
-
export const PlSwitch = /* @__PURE__ */ React.forwardRef(function PlSwitch({ size = 'md', color = 'primary', label, description, error, invalid, labelPlacement = 'end', disabled = false, readOnly = false, className, style, ...props }, ref) {
|
|
110
|
-
const hasError = hasContent(error);
|
|
111
|
-
const isInvalid = invalid ?? hasError;
|
|
112
|
-
const family = isInvalid ? 'danger' : color;
|
|
113
|
-
const track = (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: _jsx(BaseUISwitch.Root, { ref: ref, className: [
|
|
114
|
-
trackBaseClasses,
|
|
115
|
-
trackClasses[size],
|
|
116
|
-
disabled ? disabledTrackClasses : readOnly ? readOnlyTrackClasses : restTrackClasses
|
|
117
|
-
].join(' '), disabled: disabled, readOnly: readOnly, ...props, children: _jsx(BaseUISwitch.Thumb, { className: `${thumbClasses} ${thumbTravelClasses[size]}` }) }) }));
|
|
118
|
-
const text = label || description ? (_jsxs("span", { className: [
|
|
119
|
-
'flex min-w-0 flex-col gap-0.5',
|
|
120
|
-
// With the label on the left it has to take the slack, or the switch
|
|
121
|
-
// sits against the text instead of against the edge of the row.
|
|
122
|
-
labelPlacement === 'start' ? 'flex-1' : ''
|
|
123
|
-
]
|
|
124
|
-
.filter(Boolean)
|
|
125
|
-
.join(' '), 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;
|
|
126
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: ['inline-flex flex-col gap-1 align-top', className ?? '']
|
|
127
|
-
.filter(Boolean)
|
|
128
|
-
.join(' '),
|
|
129
|
-
// `solid`, because an on track *is* the coloured thing.
|
|
130
|
-
style: { ...controlSlots(family, 0, 'solid'), ...style }, children: [_jsx("div", { className: `flex items-start gap-2.5 ${tickRowTextClasses[size]}`, children: labelPlacement === 'start' ? (_jsxs(_Fragment, { children: [text, track] })) : (_jsxs(_Fragment, { children: [track, text] })) }), hasError ? (_jsx(Field.Error, { match: true, className: `${metaTextClasses[size]} text-(--p-accent)`, children: error })) : null] }));
|
|
131
|
-
});
|
|
1
|
+
"use client";import{jsx as a,jsxs as e,Fragment as t}from"react/jsx-runtime";import*as l from"react";import{Switch as r}from"@base-ui/react/switch";import{Field as s}from"@base-ui/react/field";import{controlSlots as i,focusRingClasses as c,hasContent as n,metaTextClasses as o,tickRowTextClasses as d}from"../../internal/styles.js";const h={xs:"h-3.5 w-6",sm:"h-4 w-7",md:"h-5 w-9",lg:"h-6 w-11",xl:"h-7 w-13"},m={xs:"data-[checked]:left-[calc(100%-0.75rem)]",sm:"data-[checked]:left-[calc(100%-0.875rem)]",md:"data-[checked]:left-[calc(100%-1.125rem)]",lg:"data-[checked]:left-[calc(100%-1.375rem)]",xl:"data-[checked]:left-[calc(100%-1.625rem)]"},p=/* @__PURE__ */["relative inline-flex shrink-0 rounded-full","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","[transition-property:background-color,background-image,box-shadow,filter]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]",c].join(" "),f=/* @__PURE__ */["cursor-pointer bg-(--plass-track)","hover:brightness-[0.97] dark:hover:brightness-110","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:[box-shadow:var(--p-lift)] data-[checked]:hover:brightness-105"].join(" "),u=/* @__PURE__ */["cursor-default bg-(--plass-track) saturate-[0.55]","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:shadow-none"].join(" "),g=/* @__PURE__ */["cursor-not-allowed bg-(--plass-track)","opacity-50 saturate-[0.35] shadow-none","data-[checked]:[background-image:var(--p-fill)]"].join(" "),b=/* @__PURE__ */["absolute inset-y-0.5 left-0.5 aspect-square rounded-full bg-white","[box-shadow:0_1px_2px_rgb(20_40_90/0.25)]","[transition:left_var(--plass-duration)_var(--plass-ease)]"].join(" ");export const PlSwitch=/* @__PURE__ */l.forwardRef(function PlSwitch({size:l="md",color:c="primary",label:x,description:k,error:w,invalid:v,labelPlacement:j="end",disabled:N=!1,readOnly:y=!1,className:_,style:$,...P},R){const S=n(w),B=v??S,O=B?"danger":c,q=a("span",{className:"flex h-[1lh] shrink-0 items-center",children:a(r.Root,{ref:R,className:[p,h[l],N?g:y?u:f].join(" "),disabled:N,readOnly:y,...P,children:a(r.Thumb,{className:`${b} ${m[l]}`})})}),z=x||k?e("span",{className:["flex min-w-0 flex-col gap-0.5","start"===j?"flex-1":""].filter(Boolean).join(" "),children:[x?a(s.Label,{className:N?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:x}):null,k?a(s.Description,{className:`${o[l]} text-(--plass-muted-fg)`,children:k}):null]}):null;return e(s.Root,{disabled:N,invalid:B,className:["inline-flex flex-col gap-1 align-top",_??""].filter(Boolean).join(" "),style:{...i(O,0,"solid"),...$},children:[a("div",{className:`flex items-start gap-2.5 ${d[l]}`,children:e(t,"start"===j?{children:[z,q]}:{children:[q,z]})}),S?a(s.Error,{match:!0,className:`${o[l]} text-(--p-accent)`,children:w}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlSwitch}from"./PlSwitch.js";
|
|
@@ -1,210 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { controlTextLeadingClasses, focusRingInsetClasses, metaTextClasses, paddingXValues, radiusClasses, sheetRestClasses, surfaceSlots } from '../../internal/styles.js';
|
|
4
|
-
/**
|
|
5
|
-
* Row height, as vertical padding — and, like the horizontal track, a raw
|
|
6
|
-
* length rather than a class. See the note on `paddingXValues`.
|
|
7
|
-
*/
|
|
8
|
-
const cellPaddingYValues = {
|
|
9
|
-
default: { xs: '0.375rem', sm: '0.5rem', md: '0.75rem', lg: '0.875rem', xl: '1rem' },
|
|
10
|
-
compact: { xs: '0.125rem', sm: '0.25rem', md: '0.4375rem', lg: '0.5625rem', xl: '0.6875rem' }
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Everything a table is painted with is written inline, `<table>` included.
|
|
14
|
-
*
|
|
15
|
-
* This is the one component in the library that has to do that, and the reason
|
|
16
|
-
* is the elements it renders. A PlButton owns its `<button>`; nobody else
|
|
17
|
-
* styles it. `<table>`, `<th>` and `<td>` are different — VitePress's
|
|
18
|
-
* `.vp-doc table`, Tailwind Typography's `.prose td` and every CSS framework in
|
|
19
|
-
* existence style them by tag name, at two-class specificity that a one-class
|
|
20
|
-
* Tailwind utility cannot outrank.
|
|
21
|
-
*
|
|
22
|
-
* Padding and alignment were already written inline for that reason. **The
|
|
23
|
-
* borders, the display mode and the margins were not**, and that omission was
|
|
24
|
-
* most of what a Plass table looked like on a documentation page: the host's
|
|
25
|
-
* `td { border: 1px solid }` drew a full grid of cell rules the design never
|
|
26
|
-
* asked for, its `table { display: block }` stopped `width: 100%` filling the
|
|
27
|
-
* sheet, and its `table { margin: 20px 0 }` pushed the whole thing off the
|
|
28
|
-
* top-left corner of the pane it is supposed to be flush inside. A grid of
|
|
29
|
-
* boxed cells floating inside a rounded sheet is not a restrained table with a
|
|
30
|
-
* problem — it is somebody else's table wearing this one's frame.
|
|
31
|
-
*
|
|
32
|
-
* So every cell states `border: 0` and then draws the one rule it actually
|
|
33
|
-
* wants, and the `<table>` states the four properties a prose stylesheet is
|
|
34
|
-
* known to take off it. It is verbose, and it is the only way the component
|
|
35
|
-
* looks the same in a bare app and inside `.prose`.
|
|
36
|
-
*
|
|
37
|
-
* What is *not* inline is the row's own background, because it has a hover
|
|
38
|
-
* state and inline styles have no `:hover`. It reads a `--p-row` slot instead,
|
|
39
|
-
* which classes then set — a custom property is invisible to a host stylesheet,
|
|
40
|
-
* so a one-class variant wins there without a fight.
|
|
41
|
-
*/
|
|
42
|
-
const tableStyle = {
|
|
43
|
-
display: 'table',
|
|
44
|
-
width: '100%',
|
|
45
|
-
margin: 0,
|
|
46
|
-
borderCollapse: 'collapse',
|
|
47
|
-
borderSpacing: 0
|
|
48
|
-
};
|
|
49
|
-
const rowClasses = /* @__PURE__ */ [
|
|
50
|
-
'[--p-row:transparent]',
|
|
51
|
-
'[transition:background-color_var(--plass-duration)_var(--plass-ease)]'
|
|
52
|
-
].join(' ');
|
|
53
|
-
/**
|
|
54
|
-
* What a row that answers a press needs beyond the pointer treatment.
|
|
55
|
-
*
|
|
56
|
-
* `tabIndex` is the whole point: a row whose only way in is `onClick` is a
|
|
57
|
-
* control a keyboard cannot reach at all, and `cursor-pointer` advertises it as
|
|
58
|
-
* one anyway. The ring is inset rather than offset, because the sheet clips at
|
|
59
|
-
* its own rounded edge and an outline drawn outside the first or last row is an
|
|
60
|
-
* outline with its top or bottom sliced off.
|
|
61
|
-
*
|
|
62
|
-
* The `role` is deliberately left alone. `role="button"` on a `<tr>` reads well
|
|
63
|
-
* in isolation and takes the row semantics off it — which orphans every `<td>`
|
|
64
|
-
* inside from the table they belong to, and costs a screen reader the column
|
|
65
|
-
* headers, the row position and the count.
|
|
66
|
-
*/
|
|
67
|
-
const clickableRowClasses = /* @__PURE__ */ [
|
|
68
|
-
'cursor-pointer [outline:none]',
|
|
69
|
-
focusRingInsetClasses
|
|
70
|
-
].join(' ');
|
|
71
|
-
/**
|
|
72
|
-
* Two weights of rule, and the whole of the table's structure.
|
|
73
|
-
*
|
|
74
|
-
* `--plass-divider` scores one row off the next, the same ink a PlCard and a
|
|
75
|
-
* PlList are scored with, so a table on a card and the card's own dividers are
|
|
76
|
-
* one family of lines. The header sits on `--plass-border`, one step firmer,
|
|
77
|
-
* because the break between the names of the columns and the data in them is
|
|
78
|
-
* the one division a reader has to find without looking for it.
|
|
79
|
-
*
|
|
80
|
-
* Both are neutral inks rather than the `--plass-glass-line` this used to use.
|
|
81
|
-
* That line is white, and a white rule between two rows of a white sheet is
|
|
82
|
-
* not a rule — which is why the table had no visible structure of its own in
|
|
83
|
-
* light mode, and why it looked like whatever grid the host page happened to
|
|
84
|
-
* be drawing over it.
|
|
85
|
-
*
|
|
86
|
-
* They are on the *cells* rather than on the `<tr>`, which is where a rule can
|
|
87
|
-
* be written inline and therefore where a host stylesheet cannot reach it.
|
|
88
|
-
*/
|
|
89
|
-
const rowRule = '1px solid var(--plass-divider)';
|
|
90
|
-
const headRule = '1px solid var(--plass-border)';
|
|
91
|
-
/**
|
|
92
|
-
* A grid of data on a sheet of glass.
|
|
93
|
-
*
|
|
94
|
-
* The sheet is the ordinary Plass container — `variant`, `size`, `color`,
|
|
95
|
-
* `density` and `elevation` all mean what they mean everywhere else, and it is
|
|
96
|
-
* never dyed. What the table adds is the part that is genuinely tabular: the
|
|
97
|
-
* columns, the rows, and the fact that the two cannot drift apart.
|
|
98
|
-
*
|
|
99
|
-
* Not a `forwardRef`, because it is generic: a component wrapped in
|
|
100
|
-
* `React.forwardRef` loses its type parameter, and `Row` is the whole point of
|
|
101
|
-
* the API. `ref` is not offered rather than being offered with `Row` widened to
|
|
102
|
-
* `any`.
|
|
103
|
-
*/
|
|
104
|
-
export function PlTable({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, columns, rows, getRowKey, caption, empty = 'No data', striped = false, hoverable = false, stickyHeader = false, maxHeight, onRowClick, className, style, ...props }) {
|
|
105
|
-
const padX = paddingXValues[density][size];
|
|
106
|
-
const padY = cellPaddingYValues[density][size];
|
|
107
|
-
const clickable = Boolean(onRowClick);
|
|
108
|
-
const lit = hoverable || clickable;
|
|
109
|
-
const capped = maxHeight !== undefined;
|
|
110
|
-
const captionId = React.useId();
|
|
111
|
-
// `border: 0` first, then the one edge this cell actually draws. Written as
|
|
112
|
-
// the longhand rather than a `border` shorthand carrying `0` so the rule and
|
|
113
|
-
// the reset cannot be reordered into each other.
|
|
114
|
-
const cellStyle = {
|
|
115
|
-
padding: `${padY} ${padX}`,
|
|
116
|
-
border: 0,
|
|
117
|
-
background: 'none'
|
|
118
|
-
};
|
|
119
|
-
const headCellStyle = {
|
|
120
|
-
...cellStyle,
|
|
121
|
-
// The rule under the column names, and the one place the table draws an
|
|
122
|
-
// edge as a shadow rather than as a border. `border-collapse: collapse`
|
|
123
|
-
// hands a cell's borders to the *table's* border grid, and that grid does
|
|
124
|
-
// not travel with a `position: sticky` cell — so a pinned header keeps its
|
|
125
|
-
// fill and leaves its underline behind at the top of the scroll. An inset
|
|
126
|
-
// shadow belongs to the cell's own box and goes where the cell goes.
|
|
127
|
-
...(stickyHeader
|
|
128
|
-
? { boxShadow: `inset 0 -1px 0 var(--plass-border)` }
|
|
129
|
-
: { borderBottom: headRule }),
|
|
130
|
-
// No band behind the column names.
|
|
131
|
-
//
|
|
132
|
-
// The header used to be `--plass-glass-press`, one step up the glass ladder
|
|
133
|
-
// — and one step up a ladder whose rungs are 62%, 76% and 88% white is, on
|
|
134
|
-
// a light page, a white bar. A filled strip across the top of a grid is the
|
|
135
|
-
// single fastest way to make data look like chrome, and it was doing that
|
|
136
|
-
// while the rules between the rows underneath were invisible: all of the
|
|
137
|
-
// table's weight in the one place that needed none of it. Muted, semibold
|
|
138
|
-
// names over a firmer rule say "these are the columns" with nothing filled
|
|
139
|
-
// in at all.
|
|
140
|
-
//
|
|
141
|
-
// A *pinned* header is the exception, and it is not decoration: rows pass
|
|
142
|
-
// directly underneath it, and a translucent fill lets them through. Two
|
|
143
|
-
// stacked opaque layers stop the light — the sheet's own densest glass over
|
|
144
|
-
// the page's surface colour — which is the same trick a pinned first
|
|
145
|
-
// column needs for the same reason.
|
|
146
|
-
...(stickyHeader
|
|
147
|
-
? {
|
|
148
|
-
background: 'linear-gradient(var(--plass-glass-press), var(--plass-glass-press)),' +
|
|
149
|
-
' linear-gradient(var(--plass-surface), var(--plass-surface))'
|
|
150
|
-
}
|
|
151
|
-
: null)
|
|
152
|
-
};
|
|
153
|
-
/** The rule above a row — every row but the first, which has the header's. */
|
|
154
|
-
const bodyCellStyle = (index) => index === 0 ? cellStyle : { ...cellStyle, borderTop: rowRule };
|
|
155
|
-
return (_jsxs("div", { className: [
|
|
156
|
-
// The sheet clips, and the box inside it scrolls. Two elements rather
|
|
157
|
-
// than one because a `caption` has to stay out of what scrolls: a
|
|
158
|
-
// pinned header over rows that pass under it is the point of
|
|
159
|
-
// `stickyHeader`, and a title that slid away above it would take the
|
|
160
|
-
// table's name off the screen with it.
|
|
161
|
-
'overflow-hidden',
|
|
162
|
-
radiusClasses[size],
|
|
163
|
-
sheetRestClasses[variant],
|
|
164
|
-
className ?? ''
|
|
165
|
-
]
|
|
166
|
-
.filter(Boolean)
|
|
167
|
-
.join(' '), style: { ...surfaceSlots(color, elevation), ...style }, ...props, children: [caption ? (_jsx("div", { id: captionId, className: `${metaTextClasses[size]} font-semibold text-(--plass-muted-fg)`, style: { ...cellStyle, borderBottom: rowRule, textAlign: 'start' }, children: caption })) : null, _jsx("div", { className: [
|
|
168
|
-
'overflow-x-auto',
|
|
169
|
-
// Only when there is a cap to scroll against. A box that is as tall
|
|
170
|
-
// as its content has nothing to scroll, and `overscroll-contain` on
|
|
171
|
-
// one of those would swallow the page's own scroll at its edges.
|
|
172
|
-
capped ? 'overflow-y-auto overscroll-contain' : ''
|
|
173
|
-
]
|
|
174
|
-
.filter(Boolean)
|
|
175
|
-
.join(' '), style: capped ? { maxHeight } : undefined, children: _jsxs("table", { "aria-labelledby": caption ? captionId : undefined, className: `text-start ${controlTextLeadingClasses[size]} text-(--plass-fg)`, style: tableStyle, children: [_jsx("colgroup", { children: columns.map((column) => (_jsx("col", { style: column.width === undefined
|
|
176
|
-
? undefined
|
|
177
|
-
: {
|
|
178
|
-
width: typeof column.width === 'number' ? `${column.width}px` : column.width
|
|
179
|
-
} }, column.key))) }), _jsx("thead", { children: _jsx("tr", { children: columns.map((column) => (_jsx("th", { scope: "col", className: [
|
|
180
|
-
'font-semibold whitespace-nowrap text-(--plass-muted-fg)',
|
|
181
|
-
stickyHeader ? 'sticky top-0 z-10' : ''
|
|
182
|
-
]
|
|
183
|
-
.filter(Boolean)
|
|
184
|
-
.join(' '), style: { ...headCellStyle, textAlign: column.align ?? 'start' }, children: column.header ?? column.key }, column.key))) }) }), _jsx("tbody", { children: rows.length === 0 ? (_jsx("tr", { className: rowClasses, children: _jsx("td", { colSpan: columns.length, className: "text-(--plass-muted-fg)", style: { ...cellStyle, padding: `2rem ${padX}`, textAlign: 'center' }, children: empty }) })) : (rows.map((row, index) => (_jsx("tr", { className: [
|
|
185
|
-
rowClasses,
|
|
186
|
-
striped && index % 2 === 1 ? '[--p-row:var(--plass-stripe)]' : '',
|
|
187
|
-
lit ? 'hover:[--p-row:var(--p-soft)]' : '',
|
|
188
|
-
clickable ? clickableRowClasses : ''
|
|
189
|
-
]
|
|
190
|
-
.filter(Boolean)
|
|
191
|
-
.join(' '), style: { backgroundColor: 'var(--p-row)' }, tabIndex: clickable ? 0 : undefined, onClick: onRowClick ? () => onRowClick(row, index) : undefined, onKeyDown: onRowClick
|
|
192
|
-
? (event) => {
|
|
193
|
-
// Only the row's own keys. A cell can hold a link or a
|
|
194
|
-
// button, and those have an Enter of their own — running
|
|
195
|
-
// both would open the row and follow the link at once.
|
|
196
|
-
if (event.target !== event.currentTarget) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
// Space scrolls the page otherwise, which is the one
|
|
203
|
-
// thing a reader pressing it on a row did not ask for.
|
|
204
|
-
event.preventDefault();
|
|
205
|
-
onRowClick(row, index);
|
|
206
|
-
}
|
|
207
|
-
: undefined, children: columns.map((column) => (_jsx("td", { style: { ...bodyCellStyle(index), textAlign: column.align ?? 'start' }, children: column.render
|
|
208
|
-
? column.render(row, index)
|
|
209
|
-
: row[column.key] }, column.key))) }, getRowKey ? getRowKey(row, index) : index)))) })] }) })] }));
|
|
210
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as a from"react";import{controlTextLeadingClasses as t,focusRingInsetClasses as l,metaTextClasses as o,paddingXValues as s,radiusClasses as i,sheetRestClasses as n,surfaceSlots as d}from"../../internal/styles.js";const c={default:{xs:"0.375rem",sm:"0.5rem",md:"0.75rem",lg:"0.875rem",xl:"1rem"},compact:{xs:"0.125rem",sm:"0.25rem",md:"0.4375rem",lg:"0.5625rem",xl:"0.6875rem"}},p={display:"table",width:"100%",margin:0,borderCollapse:"collapse",borderSpacing:0},m=/* @__PURE__ */["[--p-row:transparent]","[transition:background-color_var(--plass-duration)_var(--plass-ease)]"].join(" "),g=/* @__PURE__ */["cursor-pointer [outline:none]",l].join(" "),v="1px solid var(--plass-divider)";export function PlTable({variant:l="glass",size:h="md",color:y="primary",density:u="default",elevation:x=0,columns:b,rows:f,getRowKey:w,caption:k,empty:j="No data",striped:N=!1,hoverable:B=!1,stickyHeader:$=!1,maxHeight:A,onRowClick:C,className:H,style:S,...T}){const z=s[u][h],D=c[u][h],I=Boolean(C),K=B||I,R=void 0!==A,_=a.useId(),E={padding:`${D} ${z}`,border:0,background:"none"},P={...E,...$?{boxShadow:"inset 0 -1px 0 var(--plass-border)"}:{borderBottom:"1px solid var(--plass-border)"},...$?{background:"linear-gradient(var(--plass-glass-press), var(--plass-glass-press)), linear-gradient(var(--plass-surface), var(--plass-surface))"}:null},bodyCellStyle=e=>0===e?E:{...E,borderTop:v};return r("div",{className:["overflow-hidden",i[h],n[l],H??""].filter(Boolean).join(" "),style:{...d(y,x),...S},...T,children:[k?e("div",{id:_,className:`${o[h]} font-semibold text-(--plass-muted-fg)`,style:{...E,borderBottom:v,textAlign:"start"},children:k}):null,e("div",{className:["overflow-x-auto",R?"overflow-y-auto overscroll-contain":""].filter(Boolean).join(" "),style:R?{maxHeight:A}:void 0,children:r("table",{"aria-labelledby":k?_:void 0,className:`text-start ${t[h]} text-(--plass-fg)`,style:p,children:[e("colgroup",{children:b.map(r=>e("col",{style:void 0===r.width?void 0:{width:"number"==typeof r.width?`${r.width}px`:r.width}},r.key))}),e("thead",{children:e("tr",{children:b.map(r=>e("th",{scope:"col",className:["font-semibold whitespace-nowrap text-(--plass-muted-fg)",$?"sticky top-0 z-10":""].filter(Boolean).join(" "),style:{...P,textAlign:r.align??"start"},children:r.header??r.key},r.key))})}),e("tbody",{children:0===f.length?e("tr",{className:m,children:e("td",{colSpan:b.length,className:"text-(--plass-muted-fg)",style:{...E,padding:`2rem ${z}`,textAlign:"center"},children:j})}):f.map((r,a)=>e("tr",{className:[m,N&&a%2==1?"[--p-row:var(--plass-stripe)]":"",K?"hover:[--p-row:var(--p-soft)]":"",I?g:""].filter(Boolean).join(" "),style:{backgroundColor:"var(--p-row)"},tabIndex:I?0:void 0,onClick:C?()=>C(r,a):void 0,onKeyDown:C?e=>{e.target===e.currentTarget&&("Enter"!==e.key&&" "!==e.key||(e.preventDefault(),C(r,a)))}:void 0,children:b.map(t=>e("td",{style:{...bodyCellStyle(a),textAlign:t.align??"start"},children:t.render?t.render(r,a):r[t.key]},t.key))},w?w(r,a):a))})]})})]})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTable}from"./PlTable.js";
|
|
@@ -1,182 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Tabs as BaseUITabs } from '@base-ui/react/tabs';
|
|
4
|
-
import { controlHeightClasses, controlTextClasses, focusRingClasses, focusRingInsetClasses, gapClasses, glassClasses, hasContent, iconClasses, paddingXClasses, radiusClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
const TabsContext = /* @__PURE__ */ React.createContext({
|
|
6
|
-
variant: 'glass',
|
|
7
|
-
size: 'md',
|
|
8
|
-
density: 'default',
|
|
9
|
-
orientation: 'horizontal',
|
|
10
|
-
fullWidth: false
|
|
11
|
-
});
|
|
12
|
-
/* ---------------------------------------------------------------------------
|
|
13
|
-
* The bar
|
|
14
|
-
* ------------------------------------------------------------------------- */
|
|
15
|
-
/**
|
|
16
|
-
* What the bar itself looks like, per material and per axis.
|
|
17
|
-
*
|
|
18
|
-
* `glass` is one border on one edge rather than a box, which is why it needs the
|
|
19
|
-
* axis: the rule belongs under a horizontal bar and beside a vertical one. It is
|
|
20
|
-
* `--plass-border` rather than the sheet's own white hairline, because a bar
|
|
21
|
-
* drawn on a light card would otherwise have no rule at all.
|
|
22
|
-
*/
|
|
23
|
-
const listClasses = {
|
|
24
|
-
solid: {
|
|
25
|
-
horizontal: `${glassClasses} inline-flex bg-(--plass-glass) p-1 [box-shadow:var(--plass-well)]`,
|
|
26
|
-
vertical: `${glassClasses} inline-flex flex-col bg-(--plass-glass) p-1 [box-shadow:var(--plass-well)]`
|
|
27
|
-
},
|
|
28
|
-
glass: {
|
|
29
|
-
horizontal: 'flex border-b [border-color:var(--plass-border)]',
|
|
30
|
-
vertical: 'flex flex-col border-e [border-color:var(--plass-border)]'
|
|
31
|
-
},
|
|
32
|
-
ghost: {
|
|
33
|
-
horizontal: 'flex',
|
|
34
|
-
vertical: 'flex flex-col'
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* The indicator.
|
|
39
|
-
*
|
|
40
|
-
* `solid` fills the tab — a tile that slides between them. The other two draw a
|
|
41
|
-
* 2px bar along the bar's own edge. All three move by animating `left`/`top` and
|
|
42
|
-
* `width`/`height`, which Base UI measures onto `--active-tab-*`. That is a
|
|
43
|
-
* layout animation on an empty box, not a transform on a label: nothing with
|
|
44
|
-
* text in it moves, which is the distinction the house rule actually draws.
|
|
45
|
-
*
|
|
46
|
-
* `left`, not `inset-inline-start`, and this is one of the two places in the
|
|
47
|
-
* library that reaches for a physical property on purpose. `--active-tab-left`
|
|
48
|
-
* is a measurement — the distance from the list's left edge to the active tab's,
|
|
49
|
-
* in pixels — and it stays a distance from the *left* under RTL. Pairing a
|
|
50
|
-
* physical measurement with a logical property is what would break the
|
|
51
|
-
* direction, not what would fix it. The edge the bar sits on is logical, because
|
|
52
|
-
* that one genuinely flips.
|
|
53
|
-
*/
|
|
54
|
-
const indicatorClasses = {
|
|
55
|
-
solid: {
|
|
56
|
-
horizontal: 'absolute top-(--active-tab-top) left-(--active-tab-left) h-(--active-tab-height) w-(--active-tab-width)',
|
|
57
|
-
vertical: 'absolute top-(--active-tab-top) left-(--active-tab-left) h-(--active-tab-height) w-(--active-tab-width)'
|
|
58
|
-
},
|
|
59
|
-
glass: {
|
|
60
|
-
horizontal: 'absolute bottom-0 left-(--active-tab-left) h-0.5 w-(--active-tab-width)',
|
|
61
|
-
vertical: 'absolute end-0 top-(--active-tab-top) h-(--active-tab-height) w-0.5'
|
|
62
|
-
},
|
|
63
|
-
ghost: {
|
|
64
|
-
horizontal: 'absolute bottom-0 left-(--active-tab-left) h-0.5 w-(--active-tab-width)',
|
|
65
|
-
vertical: 'absolute end-0 top-(--active-tab-top) h-(--active-tab-height) w-0.5'
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const indicatorSurfaceClasses = {
|
|
69
|
-
solid: `${glassClasses} bg-(--plass-glass-press) [box-shadow:var(--plass-shadow-1),var(--plass-gloss-glass)]`,
|
|
70
|
-
glass: 'bg-(--p-accent)',
|
|
71
|
-
ghost: 'bg-(--p-accent)'
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* A tab is a control, so it takes the control height ladder — a `md` tab and a
|
|
75
|
-
* `md` PlButton are the same 40px, which is what lets a tab bar sit in a toolbar
|
|
76
|
-
* next to one without the row losing its baseline.
|
|
77
|
-
*
|
|
78
|
-
* `data-active`, not `data-selected` — Base UI spells a chosen tab's state that
|
|
79
|
-
* way, and the wrong attribute is a class that silently never matches.
|
|
80
|
-
*/
|
|
81
|
-
const tabStateClasses = 'text-(--plass-muted-fg) hover:text-(--plass-fg) data-[active]:text-(--p-accent)';
|
|
82
|
-
/**
|
|
83
|
-
* One tab, and one place a tab differs from a PlButton: `solid` puts the tile
|
|
84
|
-
* *behind* the tab rather than on it, so the tab needs a stacking context of its
|
|
85
|
-
* own or the indicator would cover the label it is meant to be under.
|
|
86
|
-
*/
|
|
87
|
-
export const PlTab = /* @__PURE__ */ React.forwardRef(function PlTab({ value, startIcon, endIcon, disabled = false, className, children, ...props }, ref) {
|
|
88
|
-
const { variant, size, density, fullWidth } = React.useContext(TabsContext);
|
|
89
|
-
return (_jsxs(BaseUITabs.Tab, { ref: ref, value: value, disabled: disabled, className: [
|
|
90
|
-
'relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center select-none',
|
|
91
|
-
'font-semibold whitespace-nowrap',
|
|
92
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
93
|
-
controlHeightClasses[size],
|
|
94
|
-
controlTextClasses[size],
|
|
95
|
-
gapClasses[size],
|
|
96
|
-
paddingXClasses[density][size],
|
|
97
|
-
variant === 'solid' ? radiusClasses[size] : '',
|
|
98
|
-
transitionClasses,
|
|
99
|
-
iconClasses,
|
|
100
|
-
tabStateClasses,
|
|
101
|
-
// The ring is inset rather than offset: an offset ring on a tab inside a
|
|
102
|
-
// `solid` groove is drawn on top of its neighbours.
|
|
103
|
-
focusRingInsetClasses,
|
|
104
|
-
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
|
|
105
|
-
fullWidth ? 'flex-1' : '',
|
|
106
|
-
className ?? ''
|
|
107
|
-
]
|
|
108
|
-
.filter(Boolean)
|
|
109
|
-
.join(' '), ...props, children: [hasContent(startIcon) ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: startIcon })) : null, children, hasContent(endIcon) ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: endIcon })) : null] }));
|
|
110
|
-
});
|
|
111
|
-
/** The content behind one tab. */
|
|
112
|
-
export const PlTabPanel = /* @__PURE__ */ React.forwardRef(function PlTabPanel({ value, keepMounted = false, className, children, ...props }, ref) {
|
|
113
|
-
const { size } = React.useContext(TabsContext);
|
|
114
|
-
return (_jsx(BaseUITabs.Panel, { ref: ref, value: value, keepMounted: keepMounted, className: [
|
|
115
|
-
'min-w-0 flex-1 text-(--plass-fg)',
|
|
116
|
-
// The panel takes focus when it holds nothing focusable of its own, so
|
|
117
|
-
// it is reachable by keyboard — and it gets the house ring rather than
|
|
118
|
-
// the browser's.
|
|
119
|
-
focusRingClasses,
|
|
120
|
-
radiusClasses[size],
|
|
121
|
-
className ?? ''
|
|
122
|
-
]
|
|
123
|
-
.filter(Boolean)
|
|
124
|
-
.join(' '), ...props, children: children }));
|
|
125
|
-
});
|
|
126
|
-
/**
|
|
127
|
-
* One set of panels, one of which is shown.
|
|
128
|
-
*
|
|
129
|
-
* Base UI owns everything that makes a tab bar a tab bar rather than a row of
|
|
130
|
-
* buttons: roving focus so the whole bar is one tab stop, the arrow keys on
|
|
131
|
-
* whichever axis the bar runs, Home and End, the `tab` / `tabpanel` roles and
|
|
132
|
-
* the `aria-controls` wiring between them, and the measurement that puts the
|
|
133
|
-
* indicator under the chosen tab. What is here is the surface and the ladders.
|
|
134
|
-
*
|
|
135
|
-
* The tabs and the panels are composed rather than passed as data, unlike
|
|
136
|
-
* `PlSelect` — because a panel is a subtree, and there is no useful shape for
|
|
137
|
-
* "an array of arbitrary React trees" that is not just children.
|
|
138
|
-
*/
|
|
139
|
-
export const PlTabs = /* @__PURE__ */ React.forwardRef(function PlTabs({ variant = 'glass', size = 'md', color = 'primary', density = 'default', value, defaultValue, onValueChange, orientation = 'horizontal', activateOnFocus = false, loopFocus = true, fullWidth = false, className, style, children, ...props }, ref) {
|
|
140
|
-
const context = React.useMemo(() => ({ variant, size, density, orientation, fullWidth }), [variant, size, density, orientation, fullWidth]);
|
|
141
|
-
// Everything a caller writes between the tags is either a tab or a panel, and
|
|
142
|
-
// the two go in different boxes — so they are sorted here rather than made the
|
|
143
|
-
// caller's problem with a `<PlTabList>` wrapper they would have to remember.
|
|
144
|
-
const tabs = [];
|
|
145
|
-
const panels = [];
|
|
146
|
-
React.Children.forEach(children, (child) => {
|
|
147
|
-
if (React.isValidElement(child) && child.type === PlTabPanel) {
|
|
148
|
-
panels.push(child);
|
|
149
|
-
}
|
|
150
|
-
else if (child !== null && child !== undefined && child !== false) {
|
|
151
|
-
tabs.push(child);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
return (_jsx(TabsContext.Provider, { value: context, children: _jsxs(BaseUITabs.Root, { ref: ref, value: value, defaultValue: defaultValue, onValueChange: (next) => onValueChange?.(next), orientation: orientation, className: [
|
|
155
|
-
'flex min-w-0',
|
|
156
|
-
orientation === 'vertical' ? 'flex-row gap-4' : 'flex-col gap-4',
|
|
157
|
-
className ?? ''
|
|
158
|
-
]
|
|
159
|
-
.filter(Boolean)
|
|
160
|
-
.join(' '), style: { ...surfaceSlots(color, 0), ...style }, ...props, children: [_jsxs(BaseUITabs.List, { activateOnFocus: activateOnFocus, loopFocus: loopFocus, className: [
|
|
161
|
-
'relative shrink-0',
|
|
162
|
-
listClasses[variant][orientation],
|
|
163
|
-
variant === 'solid' ? radiusClasses[size] : '',
|
|
164
|
-
fullWidth && orientation === 'horizontal' ? 'w-full' : '',
|
|
165
|
-
// A bar with more tabs than room scrolls rather than wrapping: a tab
|
|
166
|
-
// bar on two lines has stopped being a bar, and the indicator has
|
|
167
|
-
// nowhere sensible to sit.
|
|
168
|
-
orientation === 'horizontal' ? 'overflow-x-auto overflow-y-hidden' : ''
|
|
169
|
-
]
|
|
170
|
-
.filter(Boolean)
|
|
171
|
-
.join(' '), children: [tabs, _jsx(BaseUITabs.Indicator, { className: [
|
|
172
|
-
'pointer-events-none',
|
|
173
|
-
indicatorClasses[variant][orientation],
|
|
174
|
-
indicatorSurfaceClasses[variant],
|
|
175
|
-
variant === 'solid' ? radiusClasses[size] : 'rounded-full',
|
|
176
|
-
// The same easing everything else uses, on the four properties the
|
|
177
|
-
// measurement actually writes.
|
|
178
|
-
'[transition-property:left,top,width,height]',
|
|
179
|
-
'[transition-duration:var(--plass-duration)]',
|
|
180
|
-
'[transition-timing-function:var(--plass-ease)]'
|
|
181
|
-
].join(' ') })] }), panels] }) }));
|
|
182
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"react";import{Tabs as l}from"@base-ui/react/tabs";import{controlHeightClasses as o,controlTextClasses as s,focusRingClasses as i,focusRingInsetClasses as n,gapClasses as r,glassClasses as c,hasContent as d,iconClasses as h,paddingXClasses as f,radiusClasses as u,surfaceSlots as p,transitionClasses as b}from"../../internal/styles.js";const v=/* @__PURE__ */a.createContext({variant:"glass",size:"md",density:"default",orientation:"horizontal",fullWidth:!1}),m={solid:{horizontal:`${c} inline-flex bg-(--plass-glass) p-1 [box-shadow:var(--plass-well)]`,vertical:`${c} inline-flex flex-col bg-(--plass-glass) p-1 [box-shadow:var(--plass-well)]`},glass:{horizontal:"flex border-b [border-color:var(--plass-border)]",vertical:"flex flex-col border-e [border-color:var(--plass-border)]"},ghost:{horizontal:"flex",vertical:"flex flex-col"}},x={solid:{horizontal:"absolute top-(--active-tab-top) left-(--active-tab-left) h-(--active-tab-height) w-(--active-tab-width)",vertical:"absolute top-(--active-tab-top) left-(--active-tab-left) h-(--active-tab-height) w-(--active-tab-width)"},glass:{horizontal:"absolute bottom-0 left-(--active-tab-left) h-0.5 w-(--active-tab-width)",vertical:"absolute end-0 top-(--active-tab-top) h-(--active-tab-height) w-0.5"},ghost:{horizontal:"absolute bottom-0 left-(--active-tab-left) h-0.5 w-(--active-tab-width)",vertical:"absolute end-0 top-(--active-tab-top) h-(--active-tab-height) w-0.5"}},g={solid:`${c} bg-(--plass-glass-press) [box-shadow:var(--plass-shadow-1),var(--plass-gloss-glass)]`,glass:"bg-(--p-accent)",ghost:"bg-(--p-accent)"},w="text-(--plass-muted-fg) hover:text-(--plass-fg) data-[active]:text-(--p-accent)";export const PlTab=/* @__PURE__ */a.forwardRef(function PlTab({value:i,startIcon:c,endIcon:p,disabled:m=!1,className:x,children:g,...z},y){const{variant:P,size:j,density:N,fullWidth:T}=a.useContext(v);return t(l.Tab,{ref:y,value:i,disabled:m,className:["relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center select-none","font-semibold whitespace-nowrap","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",o[j],s[j],r[j],f[N][j],"solid"===P?u[j]:"",b,h,w,n,"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",T?"flex-1":"",x??""].filter(Boolean).join(" "),...z,children:[d(c)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:c}):null,g,d(p)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:p}):null]})});export const PlTabPanel=/* @__PURE__ */a.forwardRef(function PlTabPanel({value:t,keepMounted:o=!1,className:s,children:n,...r},c){const{size:d}=a.useContext(v);return e(l.Panel,{ref:c,value:t,keepMounted:o,className:["min-w-0 flex-1 text-(--plass-fg)",i,u[d],s??""].filter(Boolean).join(" "),...r,children:n})});export const PlTabs=/* @__PURE__ */a.forwardRef(function PlTabs({variant:o="glass",size:s="md",color:i="primary",density:n="default",value:r,defaultValue:c,onValueChange:d,orientation:h="horizontal",activateOnFocus:f=!1,loopFocus:b=!0,fullWidth:w=!1,className:z,style:y,children:P,...j},N){const T=a.useMemo(()=>({variant:o,size:s,density:n,orientation:h,fullWidth:w}),[o,s,n,h,w]),k=[],C=[];return a.Children.forEach(P,e=>{a.isValidElement(e)&&e.type===PlTabPanel?C.push(e):null!=e&&!1!==e&&k.push(e)}),e(v.Provider,{value:T,children:t(l.Root,{ref:N,value:r,defaultValue:c,onValueChange:e=>d?.(e),orientation:h,className:["flex min-w-0","vertical"===h?"flex-row gap-4":"flex-col gap-4",z??""].filter(Boolean).join(" "),style:{...p(i,0),...y},...j,children:[t(l.List,{activateOnFocus:f,loopFocus:b,className:["relative shrink-0",m[o][h],"solid"===o?u[s]:"",w&&"horizontal"===h?"w-full":"","horizontal"===h?"overflow-x-auto overflow-y-hidden":""].filter(Boolean).join(" "),children:[k,e(l.Indicator,{className:["pointer-events-none",x[o][h],g[o],"solid"===o?u[s]:"rounded-full","[transition-property:left,top,width,height]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]"].join(" ")})]}),C]})})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTab,PlTabPanel,PlTabs}from"./PlTabs.js";
|