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,126 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Field } from '@base-ui/react/field';
|
|
4
|
-
import { Input } from '@base-ui/react/input';
|
|
5
|
-
import { Spinner } from '../../internal/icons.js';
|
|
6
|
-
import { controlHeightClasses, controlTextLeadingClasses, disabledClasses, fieldReadOnlyClasses, fieldRestClasses, focusWithinRingClasses, gapClasses, hasContent, iconClasses, metaTextClasses, paddingXClasses, radiusClasses, stackGapClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
7
|
-
/**
|
|
8
|
-
* Type scale and radius, shared by both modes. The line height is explicit here
|
|
9
|
-
* rather than `leading-none` as on PlButton, because in multiline mode it is what
|
|
10
|
-
* decides the height of a row — and it has to agree with the single-line
|
|
11
|
-
* heights below or a one-row textarea would not line up with an input.
|
|
12
|
-
*/
|
|
13
|
-
const sizeClasses = {
|
|
14
|
-
xs: `${gapClasses.xs} ${radiusClasses.xs} ${controlTextLeadingClasses.xs}`,
|
|
15
|
-
sm: `${gapClasses.sm} ${radiusClasses.sm} ${controlTextLeadingClasses.sm}`,
|
|
16
|
-
md: `${gapClasses.md} ${radiusClasses.md} ${controlTextLeadingClasses.md}`,
|
|
17
|
-
lg: `${gapClasses.lg} ${radiusClasses.lg} ${controlTextLeadingClasses.lg}`,
|
|
18
|
-
xl: `${gapClasses.xl} ${radiusClasses.xl} ${controlTextLeadingClasses.xl}`
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Multiline cannot use a fixed height — `rows` decides it. Instead the vertical
|
|
22
|
-
* padding is `(height - border - line-height) / 2`, which makes a one-row
|
|
23
|
-
* textarea exactly as tall as the single-line field of the same size. The
|
|
24
|
-
* `min-h-*` catches the variants that carry no border.
|
|
25
|
-
*
|
|
26
|
-
* These are keyed by `size` and never by `density`: density is horizontal
|
|
27
|
-
* padding only, and letting it touch this would make the same `rows` produce
|
|
28
|
-
* two different heights.
|
|
29
|
-
*/
|
|
30
|
-
const multilineClasses = {
|
|
31
|
-
xs: 'min-h-6 py-[3px]',
|
|
32
|
-
sm: 'min-h-8 py-[6px]',
|
|
33
|
-
md: 'min-h-10 py-[9px]',
|
|
34
|
-
lg: 'min-h-12 py-[11px]',
|
|
35
|
-
xl: 'min-h-14 py-[13px]'
|
|
36
|
-
};
|
|
37
|
-
const resizeClasses = {
|
|
38
|
-
none: 'resize-none',
|
|
39
|
-
vertical: 'resize-y',
|
|
40
|
-
horizontal: 'resize-x',
|
|
41
|
-
both: 'resize'
|
|
42
|
-
};
|
|
43
|
-
const shellBaseClasses = /* @__PURE__ */ [
|
|
44
|
-
// `group` so the adornments can answer the control's focus; `cursor-text`
|
|
45
|
-
// because the whole shell behaves as the field, padding included.
|
|
46
|
-
'group relative flex w-full cursor-text',
|
|
47
|
-
'[-webkit-tap-highlight-color:transparent]',
|
|
48
|
-
transitionClasses,
|
|
49
|
-
// The ring belongs to the shell, not to the control inside it, so it traces
|
|
50
|
-
// the glass edge rather than a rectangle floating inside it.
|
|
51
|
-
focusWithinRingClasses,
|
|
52
|
-
iconClasses
|
|
53
|
-
].join(' ');
|
|
54
|
-
/**
|
|
55
|
-
* The shell, the read-only treatment and the disabled treatment are the ones
|
|
56
|
-
* `internal/styles` defines for every field-shaped control — a Select's trigger
|
|
57
|
-
* is drawn on exactly the same box, and the two have to be indistinguishable.
|
|
58
|
-
*/
|
|
59
|
-
const restClasses = fieldRestClasses;
|
|
60
|
-
const readOnlyClasses = fieldReadOnlyClasses;
|
|
61
|
-
export const PlTextField = /* @__PURE__ */ React.forwardRef(function PlTextField({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, multiline = false, rows = 3, resize = 'vertical', label, description, error, invalid, startIcon, endIcon, loading = false, fullWidth = false, readOnly = false, disabled = false, type = 'text', 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
|
-
// the caret and the message all turn over together and no state needs its own
|
|
66
|
-
// set of tokens.
|
|
67
|
-
const family = isInvalid ? 'danger' : color;
|
|
68
|
-
const controlRef = React.useRef(null);
|
|
69
|
-
const setControlRef = React.useCallback((node) => {
|
|
70
|
-
controlRef.current = node;
|
|
71
|
-
if (typeof ref === 'function') {
|
|
72
|
-
ref(node);
|
|
73
|
-
}
|
|
74
|
-
else if (ref) {
|
|
75
|
-
ref.current = node;
|
|
76
|
-
}
|
|
77
|
-
}, [ref]);
|
|
78
|
-
const shellClasses = [
|
|
79
|
-
shellBaseClasses,
|
|
80
|
-
sizeClasses[size],
|
|
81
|
-
multiline
|
|
82
|
-
? `${multilineClasses[size]} items-start`
|
|
83
|
-
: `${controlHeightClasses[size]} items-center`,
|
|
84
|
-
paddingXClasses[density][size],
|
|
85
|
-
// An if/else rather than stacked `data-*` variants: two Tailwind variants
|
|
86
|
-
// of equal specificity resolve by their order in the generated stylesheet.
|
|
87
|
-
disabled ? disabledClasses[variant] : readOnly ? readOnlyClasses[variant] : restClasses[variant]
|
|
88
|
-
]
|
|
89
|
-
.filter(Boolean)
|
|
90
|
-
.join(' ');
|
|
91
|
-
const controlClasses = [
|
|
92
|
-
'min-w-0 flex-1 bg-transparent [font:inherit] text-inherit',
|
|
93
|
-
// Not `outline-none`: that utility zeroes `--tw-outline-style`, and the
|
|
94
|
-
// shell's focus ring is drawn with the same variable family. The shorthand
|
|
95
|
-
// takes the outline off this element and leaves the ring alone.
|
|
96
|
-
'[outline:none]',
|
|
97
|
-
'placeholder:text-(--plass-muted-fg)',
|
|
98
|
-
'caret-(--p-accent) selection:bg-(--p-soft-press)',
|
|
99
|
-
'disabled:cursor-not-allowed',
|
|
100
|
-
multiline ? `block ${resizeClasses[resize]}` : 'self-stretch'
|
|
101
|
-
].join(' ');
|
|
102
|
-
// `1lh` keeps an adornment centred on the first line rather than on the whole
|
|
103
|
-
// box, which is the only way it stays put when the control grows to 5 rows.
|
|
104
|
-
const adornmentClasses = 'inline-flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg) transition-[color] duration-(--plass-duration) group-focus-within:text-(--p-accent)';
|
|
105
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: [
|
|
106
|
-
'flex-col align-top',
|
|
107
|
-
stackGapClasses[size],
|
|
108
|
-
fullWidth ? 'flex w-full' : 'inline-flex',
|
|
109
|
-
className ?? ''
|
|
110
|
-
]
|
|
111
|
-
.filter(Boolean)
|
|
112
|
-
.join(' '), style: { ...surfaceSlots(family, elevation), ...style }, children: [label ? (_jsx(Field.Label, { className: [
|
|
113
|
-
metaTextClasses[size],
|
|
114
|
-
'font-semibold',
|
|
115
|
-
disabled ? 'text-(--plass-muted-fg)' : 'text-(--plass-fg)'
|
|
116
|
-
].join(' '), children: label })) : null, _jsxs("span", { className: shellClasses, onPointerDown: (event) => {
|
|
117
|
-
// Clicking the shell's own padding should put the caret in the field,
|
|
118
|
-
// the way clicking anywhere inside a native input does. Only when the
|
|
119
|
-
// shell itself was hit — a click on the control or on an adornment is
|
|
120
|
-
// left alone so text selection still works.
|
|
121
|
-
if (event.target === event.currentTarget && !disabled) {
|
|
122
|
-
event.preventDefault();
|
|
123
|
-
controlRef.current?.focus();
|
|
124
|
-
}
|
|
125
|
-
}, children: [startIcon ? _jsx("span", { className: adornmentClasses, children: startIcon }) : null, _jsx(Input, { ref: setControlRef, className: controlClasses, disabled: disabled, readOnly: readOnly, "aria-busy": loading || undefined, "data-loading": loading || undefined, ...(multiline ? { render: _jsx("textarea", { rows: rows }) } : { type }), ...props }), loading ? (_jsx("span", { className: adornmentClasses, children: _jsx(Spinner, {}) })) : endIcon ? (_jsx("span", { className: adornmentClasses, children: endIcon })) : null] }), description ? (_jsx(Field.Description, { className: [metaTextClasses[size], 'text-(--plass-muted-fg)'].join(' '), children: description })) : null, hasError ? (_jsx(Field.Error, { match: true, className: [metaTextClasses[size], 'text-(--p-accent)'].join(' '), children: error })) : null] }));
|
|
126
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as l from"react";import{Field as n}from"@base-ui/react/field";import{Input as s}from"@base-ui/react/input";import{Spinner as r}from"../../internal/icons.js";import{controlHeightClasses as i,controlTextLeadingClasses as a,disabledClasses as o,fieldReadOnlyClasses as c,fieldRestClasses as d,focusWithinRingClasses as m,gapClasses as p,hasContent as u,iconClasses as x,metaTextClasses as f,paddingXClasses as h,radiusClasses as g,stackGapClasses as $,surfaceSlots as y,transitionClasses as b}from"../../internal/styles.js";const j={xs:`${p.xs} ${g.xs} ${a.xs}`,sm:`${p.sm} ${g.sm} ${a.sm}`,md:`${p.md} ${g.md} ${a.md}`,lg:`${p.lg} ${g.lg} ${a.lg}`,xl:`${p.xl} ${g.xl} ${a.xl}`},v={xs:"min-h-6 py-[3px]",sm:"min-h-8 py-[6px]",md:"min-h-10 py-[9px]",lg:"min-h-12 py-[11px]",xl:"min-h-14 py-[13px]"},w={none:"resize-none",vertical:"resize-y",horizontal:"resize-x",both:"resize"},N=/* @__PURE__ */["group relative flex w-full cursor-text","[-webkit-tap-highlight-color:transparent]",b,m,x].join(" "),z=d,k=c;export const PlTextField=/* @__PURE__ */l.forwardRef(function PlTextField({variant:a="glass",size:c="md",color:d="primary",density:m="default",elevation:p=0,multiline:x=!1,rows:g=3,resize:b="vertical",label:D,description:P,error:R,invalid:T,startIcon:B,endIcon:F,loading:I=!1,fullWidth:O=!1,readOnly:C=!1,disabled:E=!1,type:L="text",className:W,style:q,...A},G){const H=u(R),J=T??H,K=J?"danger":d,M=l.useRef(null),Q=l.useCallback(e=>{M.current=e,"function"==typeof G?G(e):G&&(G.current=e)},[G]),S=[N,j[c],x?`${v[c]} items-start`:`${i[c]} items-center`,h[m][c],E?o[a]:C?k[a]:z[a]].filter(Boolean).join(" "),U=["min-w-0 flex-1 bg-transparent [font:inherit] text-inherit","[outline:none]","placeholder:text-(--plass-muted-fg)","caret-(--p-accent) selection:bg-(--p-soft-press)","disabled:cursor-not-allowed",x?`block ${w[b]}`:"self-stretch"].join(" "),V="inline-flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg) transition-[color] duration-(--plass-duration) group-focus-within:text-(--p-accent)";return t(n.Root,{disabled:E,invalid:J,className:["flex-col align-top",$[c],O?"flex w-full":"inline-flex",W??""].filter(Boolean).join(" "),style:{...y(K,p),...q},children:[D?e(n.Label,{className:[f[c],"font-semibold",E?"text-(--plass-muted-fg)":"text-(--plass-fg)"].join(" "),children:D}):null,t("span",{className:S,onPointerDown:e=>{e.target!==e.currentTarget||E||(e.preventDefault(),M.current?.focus())},children:[B?e("span",{className:V,children:B}):null,e(s,{ref:Q,className:U,disabled:E,readOnly:C,"aria-busy":I||void 0,"data-loading":I||void 0,...x?{render:e("textarea",{rows:g})}:{type:L},...A}),I?e("span",{className:V,children:e(r,{})}):F?e("span",{className:V,children:F}):null]}),P?e(n.Description,{className:[f[c],"text-(--plass-muted-fg)"].join(" "),children:P}):null,H?e(n.Error,{match:!0,className:[f[c],"text-(--p-accent)"].join(" "),children:R}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTextField}from"./PlTextField.js";
|
|
@@ -1,136 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { useRender } from '@base-ui/react/use-render';
|
|
4
|
-
import { ExternalLinkIcon, LinkIcon } from '../../internal/icons.js';
|
|
5
|
-
import { controlTextLeadingClasses, focusRingClasses, srOnlyClasses } from '../../internal/styles.js';
|
|
6
|
-
/**
|
|
7
|
-
* Whether the line is drawn, and — with the colour below — the only two things
|
|
8
|
-
* about a link that a prop changes. Everything else about the line is in
|
|
9
|
-
* `styles.css`, under `.plass-link`.
|
|
10
|
-
*
|
|
11
|
-
* Every utility here is written through `[&.plass-link]`, which doubles the
|
|
12
|
-
* component's own class into the selector and takes it to two classes. That is
|
|
13
|
-
* not decoration: `<a>` is, with `<td>`, one of the two tags a host stylesheet
|
|
14
|
-
* still styles by name — `.prose a`, `.vp-doc a`, every CSS framework — and all
|
|
15
|
-
* of those are a class plus a type, which outranks a plain utility. A link that
|
|
16
|
-
* lost its colour and its line inside a `.prose` block would have lost the only
|
|
17
|
-
* two things it is.
|
|
18
|
-
*
|
|
19
|
-
* Hover deliberately leaves the *text* colour alone. A link inside running
|
|
20
|
-
* prose that changes colour under the pointer drags the reader's eye off the
|
|
21
|
-
* line they were reading — the same rule the library applies to a control's
|
|
22
|
-
* label, on the one component that lives inside a sentence.
|
|
23
|
-
*/
|
|
24
|
-
const underlineClasses = {
|
|
25
|
-
always: '[&.plass-link]:underline',
|
|
26
|
-
hover: '[&.plass-link]:no-underline [&.plass-link]:hover:underline',
|
|
27
|
-
// Nothing to hover, on purpose. A link with no line and no colour is a link
|
|
28
|
-
// whose surroundings are saying what it is.
|
|
29
|
-
none: '[&.plass-link]:no-underline'
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Only two properties move, so this is written out rather than taken from
|
|
33
|
-
* `transitionClasses` — the house list has no `text-decoration-color` in it,
|
|
34
|
-
* and adding one there would put a property on every control in the library
|
|
35
|
-
* that no control draws.
|
|
36
|
-
*/
|
|
37
|
-
const transition = /* @__PURE__ */ [
|
|
38
|
-
'[transition-property:color,text-decoration-color]',
|
|
39
|
-
'[transition-duration:var(--plass-duration)]',
|
|
40
|
-
'[transition-timing-function:var(--plass-ease)]'
|
|
41
|
-
].join(' ');
|
|
42
|
-
const baseClasses = /* @__PURE__ */ [
|
|
43
|
-
// Both a style hook and the specificity. `styles.css` doubles this class to
|
|
44
|
-
// write the parts of the line that never vary — its thickness, its offset,
|
|
45
|
-
// its colour — above whatever the host page says about an `<a>`, and every
|
|
46
|
-
// utility that *does* vary is written through it for the same reason.
|
|
47
|
-
'plass-link',
|
|
48
|
-
'cursor-pointer',
|
|
49
|
-
// The glyph rides on the label at just under its cap height, rather than at
|
|
50
|
-
// the `1.2em` an icon inside a control takes: this one sits in a sentence,
|
|
51
|
-
// and an icon as tall as the line spaces the words around it apart.
|
|
52
|
-
'[&_svg]:pointer-events-none [&_svg]:inline [&_svg]:size-[0.95em] [&_svg]:shrink-0',
|
|
53
|
-
transition,
|
|
54
|
-
focusRingClasses,
|
|
55
|
-
'focus-visible:rounded-[0.25rem]'
|
|
56
|
-
].join(' ');
|
|
57
|
-
/**
|
|
58
|
-
* A link, in a sentence or on its own.
|
|
59
|
-
*
|
|
60
|
-
* Everything about it is deliberately smaller than a PlButton. It has no
|
|
61
|
-
* surface, no height of its own and no colour unless asked — what it has is a
|
|
62
|
-
* line under it, which is the one mark a reader already knows means "this goes
|
|
63
|
-
* somewhere".
|
|
64
|
-
*
|
|
65
|
-
* The three things it does that a bare `<a>` does not: it draws that line on a
|
|
66
|
-
* schedule (`always`, or only under the pointer), it marks a link that opens a
|
|
67
|
-
* new tab both visibly and for a screen reader, and it takes `render`, so the
|
|
68
|
-
* `Link` a router brings can wear all of it.
|
|
69
|
-
*/
|
|
70
|
-
export const PlTextLink = /* @__PURE__ */ React.forwardRef(function PlTextLink({ href, underline = 'always', color, size, newTab = false, icon, newTabLabel = '(opens in a new tab)', render, className, style, children, ...props }, ref) {
|
|
71
|
-
// `icon` left out follows `newTab`, which is the whole reason it is not a
|
|
72
|
-
// plain boolean with a `false` default: a link that takes over the window
|
|
73
|
-
// should say so, and a caller should have to ask for the silent version.
|
|
74
|
-
const mark = icon ?? newTab;
|
|
75
|
-
const glyph = mark === true ? newTab ? _jsx(ExternalLinkIcon, {}) : _jsx(LinkIcon, {}) : mark;
|
|
76
|
-
const classNames = [
|
|
77
|
-
baseClasses,
|
|
78
|
-
size ? controlTextLeadingClasses[size] : '',
|
|
79
|
-
underlineClasses[underline],
|
|
80
|
-
color ? '[&.plass-link]:text-(--p-accent)' : '[&.plass-link]:text-inherit',
|
|
81
|
-
className ?? ''
|
|
82
|
-
]
|
|
83
|
-
.filter(Boolean)
|
|
84
|
-
.join(' ');
|
|
85
|
-
/*
|
|
86
|
-
* The two line colours and the focus ring, as slots.
|
|
87
|
-
*
|
|
88
|
-
* `--p-ring` is set even when no family was asked for, because the ring is
|
|
89
|
-
* written as the `outline` shorthand and an undefined `var()` inside it
|
|
90
|
-
* makes the browser drop the whole declaration — an uncoloured link would
|
|
91
|
-
* lose its focus ring entirely rather than fall back to something plainer.
|
|
92
|
-
*
|
|
93
|
-
* The line rests at 45% of whatever the text is and goes to the full colour
|
|
94
|
-
* on hover, so it works the same on an inherited colour as on an accent one.
|
|
95
|
-
*/
|
|
96
|
-
const slots = {
|
|
97
|
-
'--p-underline': 'color-mix(in oklab, currentColor 45%, transparent)',
|
|
98
|
-
'--p-underline-hover': 'currentColor',
|
|
99
|
-
'--p-ring': `var(--plass-${color ?? 'primary'}-ring)`,
|
|
100
|
-
...(color ? { '--p-accent': `var(--plass-${color}-accent)` } : null)
|
|
101
|
-
};
|
|
102
|
-
/*
|
|
103
|
-
* `rel` is the one thing here a caller's own value is merged with rather
|
|
104
|
-
* than replaced by, and the reason is that the two purposes of the
|
|
105
|
-
* attribute have nothing to do with each other.
|
|
106
|
-
*
|
|
107
|
-
* `noopener` is what stops the new page reaching back through
|
|
108
|
-
* `window.opener`; `noreferrer` sits beside it for the browsers that still
|
|
109
|
-
* need the pair. The common reason to write a `rel` by hand is `nofollow`
|
|
110
|
-
* or `sponsored`, which is an SEO decision — and spelled as a plain
|
|
111
|
-
* override it would silently take the protection off a link that still
|
|
112
|
-
* opens in a new tab. Whatever was asked for is kept, with the two tokens
|
|
113
|
-
* added if they are not already there.
|
|
114
|
-
*/
|
|
115
|
-
const { rel: askedFor, ...rest } = props;
|
|
116
|
-
const rel = newTab
|
|
117
|
-
? [
|
|
118
|
-
...new Set([...(askedFor ?? '').split(/\s+/).filter(Boolean), 'noopener', 'noreferrer'])
|
|
119
|
-
].join(' ')
|
|
120
|
-
: askedFor;
|
|
121
|
-
return useRender({
|
|
122
|
-
render: render ?? _jsx("a", {}),
|
|
123
|
-
ref,
|
|
124
|
-
props: {
|
|
125
|
-
href,
|
|
126
|
-
target: newTab ? '_blank' : undefined,
|
|
127
|
-
className: classNames,
|
|
128
|
-
style: { ...slots, ...style },
|
|
129
|
-
children: (_jsxs(_Fragment, { children: [children, glyph ? _jsx("span", { className: "ms-[0.25em]", children: glyph }) : null, newTab ? (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: srOnlyClasses, children: newTabLabel })] })) : null] })),
|
|
130
|
-
...rest,
|
|
131
|
-
// After the spread on purpose: this is the merge above, not an override
|
|
132
|
-
// for a caller to win.
|
|
133
|
-
rel
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
});
|
|
1
|
+
"use client";import{jsx as n,Fragment as e,jsxs as r}from"react/jsx-runtime";import*as s from"react";import{useRender as i}from"@base-ui/react/use-render";import{ExternalLinkIcon as l,LinkIcon as o}from"../../internal/icons.js";import{controlTextLeadingClasses as a,focusRingClasses as t,srOnlyClasses as c}from"../../internal/styles.js";const p={always:"[&.plass-link]:underline",hover:"[&.plass-link]:no-underline [&.plass-link]:hover:underline",none:"[&.plass-link]:no-underline"},u=/* @__PURE__ */["plass-link","cursor-pointer","[&_svg]:pointer-events-none [&_svg]:inline [&_svg]:size-[0.95em] [&_svg]:shrink-0",/* @__PURE__ */["[transition-property:color,text-decoration-color]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]"].join(" "),t,"focus-visible:rounded-[0.25rem]"].join(" ");export const PlTextLink=/* @__PURE__ */s.forwardRef(function PlTextLink({href:s,underline:t="always",color:d,size:m,newTab:f=!1,icon:v,newTabLabel:h="(opens in a new tab)",render:k,className:x,style:g,children:j,...b},w){const y=v??f,_=!0===y?n(f?l:o,{}):y,N=[u,m?a[m]:"",p[t],d?"[&.plass-link]:text-(--p-accent)":"[&.plass-link]:text-inherit",x??""].filter(Boolean).join(" "),T={"--p-underline":"color-mix(in oklab, currentColor 45%, transparent)","--p-underline-hover":"currentColor","--p-ring":`var(--plass-${d??"primary"}-ring)`,...d?{"--p-accent":`var(--plass-${d}-accent)`}:null},{rel:L,...z}=b,B=f?[...new Set([...(L??"").split(/\s+/).filter(Boolean),"noopener","noreferrer"])].join(" "):L;return i({render:k??n("a",{}),ref:w,props:{href:s,target:f?"_blank":void 0,className:N,style:{...T,...g},children:r(e,{children:[j,_?n("span",{className:"ms-[0.25em]",children:_}):null,f?r(e,{children:[" ",n("span",{className:c,children:h})]}):null]}),...z,rel:B}})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTextLink}from"./PlTextLink.js";
|
|
@@ -1,97 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { PlButton } from '../button/PlButton.js';
|
|
4
|
-
import { TimeGrid, usePickerLabels } from '../../internal/calendar.js';
|
|
5
|
-
import { ClockIcon } from '../../internal/icons.js';
|
|
6
|
-
import { PickerFooter, PickerShell } from '../../internal/picker.js';
|
|
7
|
-
import { displaySamples, formatDate, isHour12, isValidDate, secondsOfDay, timeUnitSpan, toISOTime, withPlaceholder, withTime } from '../../internal/date.js';
|
|
8
|
-
import { cx } from '../../internal/styles.js';
|
|
9
|
-
/**
|
|
10
|
-
* A time of day, chosen from columns.
|
|
11
|
-
*
|
|
12
|
-
* Columns because they are the shape that answers what a time picker is actually
|
|
13
|
-
* asked: "half past nine" is two glances, and "any time at all, on the hour" is
|
|
14
|
-
* a column you never touch. A clock face is prettier and needs a `transform` to
|
|
15
|
-
* read, which this library does not have.
|
|
16
|
-
*
|
|
17
|
-
* The bounds are checked at the granularity of the column being drawn, which is
|
|
18
|
-
* the detail that separates a working time picker from a frustrating one. With a
|
|
19
|
-
* `minTime` of 09:30 the hour `9` stays available — the hour *contains* allowed
|
|
20
|
-
* minutes — and it is the minute column that greys out `00` through `25`. The
|
|
21
|
-
* naive check compares the whole candidate instant, hides the 9 entirely, and
|
|
22
|
-
* makes half past nine unreachable.
|
|
23
|
-
*
|
|
24
|
-
* The value is a `Date` rather than a string or a number of minutes, because
|
|
25
|
-
* everything else in this library that carries a moment is one, and because a
|
|
26
|
-
* time on its own has nowhere to record that it crossed a DST boundary.
|
|
27
|
-
* `referenceDate` is the day a bare time is written onto.
|
|
28
|
-
*/
|
|
29
|
-
export const PlTimePicker = /* @__PURE__ */ React.forwardRef(function PlTimePicker({ value: valueProp, defaultValue, onValueChange, open: openProp, defaultOpen, onOpenChange, referenceDate, minTime, maxTime, shouldDisableTime, hour12: hour12Prop, showSeconds = false, hourStep = 1, minuteStep = 1, secondStep = 1, locale, format, placeholder, clearable = false, showNowButton = true, closeOnSelect = false, labels: labelOverrides, name, size = 'md', color = 'primary', density = 'default', readOnly = false, disabled = false, startIcon, ...shell }, ref) {
|
|
30
|
-
const labels = usePickerLabels(labelOverrides);
|
|
31
|
-
const hour12 = hour12Prop ?? isHour12(locale);
|
|
32
|
-
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? null);
|
|
33
|
-
const value = valueProp !== undefined ? valueProp : uncontrolledValue;
|
|
34
|
-
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
|
|
35
|
-
const open = openProp ?? uncontrolledOpen;
|
|
36
|
-
// Held still for as long as the picker is mounted, so a popup left open
|
|
37
|
-
// across midnight does not quietly move the value it is writing onto a new
|
|
38
|
-
// day.
|
|
39
|
-
const [fallbackDay] = React.useState(() => referenceDate ?? new Date());
|
|
40
|
-
const setOpen = (next) => {
|
|
41
|
-
if (next && (readOnly || disabled)) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (openProp === undefined) {
|
|
45
|
-
setUncontrolledOpen(next);
|
|
46
|
-
}
|
|
47
|
-
onOpenChange?.(next);
|
|
48
|
-
};
|
|
49
|
-
const commit = (next) => {
|
|
50
|
-
if (valueProp === undefined) {
|
|
51
|
-
setUncontrolledValue(next);
|
|
52
|
-
}
|
|
53
|
-
onValueChange?.(next);
|
|
54
|
-
};
|
|
55
|
-
const isBlocked = React.useCallback((candidate, unit) => {
|
|
56
|
-
const [from, to] = timeUnitSpan(unit, candidate);
|
|
57
|
-
if (isValidDate(minTime) && to < secondsOfDay(minTime)) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
if (isValidDate(maxTime) && from > secondsOfDay(maxTime)) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return shouldDisableTime?.(candidate, unit) ?? false;
|
|
64
|
-
}, [minTime, maxTime, shouldDisableTime]);
|
|
65
|
-
/* Memoised because `samples` below is keyed on it, and the fallback is a
|
|
66
|
-
fresh object every render — which is the case that runs by default, since
|
|
67
|
-
`format` is optional. Left bare, the sizer re-formatted all twenty-four
|
|
68
|
-
sample instants on every render the picker saw. */
|
|
69
|
-
const displayFormat = React.useMemo(() => format ?? {
|
|
70
|
-
hour: 'numeric',
|
|
71
|
-
minute: '2-digit',
|
|
72
|
-
...(showSeconds ? { second: '2-digit' } : {})
|
|
73
|
-
}, [format, showSeconds]);
|
|
74
|
-
const now = new Date();
|
|
75
|
-
const nowValue = withTime(referenceDate ?? fallbackDay, {
|
|
76
|
-
hours: now.getHours(),
|
|
77
|
-
minutes: now.getMinutes(),
|
|
78
|
-
seconds: showSeconds ? now.getSeconds() : 0
|
|
79
|
-
});
|
|
80
|
-
const hasFooter = showNowButton || clearable || !closeOnSelect;
|
|
81
|
-
// Holds the trigger open at the width of the longest time it could show.
|
|
82
|
-
const samples = React.useMemo(() => withPlaceholder(displaySamples(locale, displayFormat), placeholder), [locale, displayFormat, placeholder]);
|
|
83
|
-
return (_jsx(PickerShell, { ...shell, size: size, color: color, density: density, readOnly: readOnly, disabled: disabled, triggerRef: ref, startIcon: startIcon ?? _jsx(ClockIcon, {}), display: isValidDate(value) ? formatDate(value, locale, displayFormat) : (placeholder ?? ''), samples: samples, empty: !isValidDate(value), clearable: clearable, onClear: () => commit(null), open: open, onOpenChange: setOpen, labels: labels, hiddenValues: name
|
|
84
|
-
? [{ name, value: isValidDate(value) ? toISOTime(value, showSeconds) : '' }]
|
|
85
|
-
: undefined, children: _jsxs("div", { className: cx('flex flex-col', hasFooter && 'gap-1.5'), children: [_jsx(TimeGrid, { size: size, density: density, locale: locale, value: isValidDate(value) ? value : null, referenceDate: referenceDate ?? fallbackDay, onChange: (next) => {
|
|
86
|
-
commit(next);
|
|
87
|
-
if (closeOnSelect) {
|
|
88
|
-
setOpen(false);
|
|
89
|
-
}
|
|
90
|
-
}, hour12: hour12, showSeconds: showSeconds, hourStep: hourStep, minuteStep: minuteStep, secondStep: secondStep, shouldDisableTime: isBlocked, labels: labels, autoFocus: true }), hasFooter ? (_jsxs(PickerFooter, { size: size, children: [clearable ? (_jsx(PlButton, { variant: "ghost", size: size, color: color, density: "compact", onClick: () => {
|
|
91
|
-
commit(null);
|
|
92
|
-
setOpen(false);
|
|
93
|
-
}, children: labels.clear })) : null, showNowButton ? (_jsx(PlButton, { variant: "ghost", size: size, color: color, density: "compact", disabled: isBlocked(nowValue, 'second'), onClick: () => {
|
|
94
|
-
commit(nowValue);
|
|
95
|
-
setOpen(false);
|
|
96
|
-
}, children: labels.now })) : null, !closeOnSelect ? (_jsx(PlButton, { variant: "solid", size: size, color: color, density: "compact", onClick: () => setOpen(false), children: labels.done })) : null] })) : null] }) }));
|
|
97
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as n}from"react/jsx-runtime";import*as o from"react";import{PlButton as l}from"../button/PlButton.js";import{TimeGrid as t,usePickerLabels as s}from"../../internal/calendar.js";import{ClockIcon as a}from"../../internal/icons.js";import{PickerFooter as i,PickerShell as r}from"../../internal/picker.js";import{displaySamples as c,formatDate as d,isHour12 as u,isValidDate as m,secondsOfDay as p,timeUnitSpan as h,toISOTime as f,withPlaceholder as g,withTime as S}from"../../internal/date.js";import{cx as b}from"../../internal/styles.js";export const PlTimePicker=/* @__PURE__ */o.forwardRef(function PlTimePicker({value:y,defaultValue:v,onValueChange:j,open:C,defaultOpen:w,onOpenChange:k,referenceDate:x,minTime:z,maxTime:D,shouldDisableTime:O,hour12:T,showSeconds:P=!1,hourStep:M=1,minuteStep:V=1,secondStep:B=1,locale:I,format:N,placeholder:R,clearable:F=!1,showNowButton:H=!0,closeOnSelect:q=!1,labels:A,name:E,size:G="md",color:J="primary",density:K="default",readOnly:L=!1,disabled:Q=!1,startIcon:U,...W},X){const Y=s(A),Z=T??u(I),[$,_]=o.useState(v??null),ee=void 0!==y?y:$,[ne,oe]=o.useState(w??!1),le=C??ne,[te]=o.useState(()=>x??new Date),setOpen=e=>{e&&(L||Q)||(void 0===C&&oe(e),k?.(e))},commit=e=>{void 0===y&&_(e),j?.(e)},se=o.useCallback((e,n)=>{const[o,l]=h(n,e);return!!(m(z)&&l<p(z))||!!(m(D)&&o>p(D))||(O?.(e,n)??!1)},[z,D,O]),ae=o.useMemo(()=>N??{hour:"numeric",minute:"2-digit",...P?{second:"2-digit"}:{}},[N,P]),ie=new Date,re=S(x??te,{hours:ie.getHours(),minutes:ie.getMinutes(),seconds:P?ie.getSeconds():0}),ce=H||F||!q,de=o.useMemo(()=>g(c(I,ae),R),[I,ae,R]);return e(r,{...W,size:G,color:J,density:K,readOnly:L,disabled:Q,triggerRef:X,startIcon:U??e(a,{}),display:m(ee)?d(ee,I,ae):R??"",samples:de,empty:!m(ee),clearable:F,onClear:()=>commit(null),open:le,onOpenChange:setOpen,labels:Y,hiddenValues:E?[{name:E,value:m(ee)?f(ee,P):""}]:void 0,children:n("div",{className:b("flex flex-col",ce&&"gap-1.5"),children:[e(t,{size:G,density:K,locale:I,value:m(ee)?ee:null,referenceDate:x??te,onChange:e=>{commit(e),q&&setOpen(!1)},hour12:Z,showSeconds:P,hourStep:M,minuteStep:V,secondStep:B,shouldDisableTime:se,labels:Y,autoFocus:!0}),ce?n(i,{size:G,children:[F?e(l,{variant:"ghost",size:G,color:J,density:"compact",onClick:()=>{commit(null),setOpen(!1)},children:Y.clear}):null,H?e(l,{variant:"ghost",size:G,color:J,density:"compact",disabled:se(re,"second"),onClick:()=>{commit(re),setOpen(!1)},children:Y.now}):null,q?null:e(l,{variant:"solid",size:G,color:J,density:"compact",onClick:()=>setOpen(!1),children:Y.done})]}):null]})})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTimePicker}from"./PlTimePicker.js";
|
|
@@ -1,207 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { useRender } from '@base-ui/react/use-render';
|
|
4
|
-
import { cx, hasContent, iconClasses, metaTextClasses, sheetBodyClasses, sheetTitleClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
const TimelineContext = /* @__PURE__ */ React.createContext(null);
|
|
6
|
-
const TimelineItemContext = /* @__PURE__ */ React.createContext({
|
|
7
|
-
index: 0,
|
|
8
|
-
last: false
|
|
9
|
-
});
|
|
10
|
-
/* ---------------------------------------------------------------------------
|
|
11
|
-
* Scales
|
|
12
|
-
* ------------------------------------------------------------------------- */
|
|
13
|
-
/**
|
|
14
|
-
* The bullet.
|
|
15
|
-
*
|
|
16
|
-
* Its own ladder rather than a step off `controlHeightClasses`, for the reason
|
|
17
|
-
* `tickSizeClasses` has one: a bullet is not a control you can put a label
|
|
18
|
-
* inside. It is a mark beside one, sized against the title next to it — which is
|
|
19
|
-
* why the steps are close to the tick ladder and not to the control ladder.
|
|
20
|
-
*
|
|
21
|
-
* It is written as a custom property rather than as a class because the
|
|
22
|
-
* connector has to know it: the line is centred on the bullet, and centring is
|
|
23
|
-
* arithmetic on this number.
|
|
24
|
-
*/
|
|
25
|
-
const bulletSizeValues = {
|
|
26
|
-
xs: '0.875rem',
|
|
27
|
-
sm: '1rem',
|
|
28
|
-
md: '1.25rem',
|
|
29
|
-
lg: '1.5rem',
|
|
30
|
-
xl: '1.875rem'
|
|
31
|
-
};
|
|
32
|
-
/** Between the bullet column and the content beside it. */
|
|
33
|
-
const bulletGapClasses = {
|
|
34
|
-
xs: 'gap-2',
|
|
35
|
-
sm: 'gap-2.5',
|
|
36
|
-
md: 'gap-3',
|
|
37
|
-
lg: 'gap-3.5',
|
|
38
|
-
xl: 'gap-4'
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* How far apart two items sit, and the one thing `density` is allowed to touch
|
|
42
|
-
* here — a compact timeline is the same type at the same bullet size with less
|
|
43
|
-
* air between the steps.
|
|
44
|
-
*
|
|
45
|
-
* The floor is set by the item with nothing in it. A step that is only a title
|
|
46
|
-
* and a time is one line tall, so the gap is the *whole* of what separates it
|
|
47
|
-
* from the next one — where a step with a paragraph under it has the paragraph's
|
|
48
|
-
* own leading working for it as well. Tuned against that case, which is why even
|
|
49
|
-
* `compact` keeps more air than a list of one-liners would otherwise suggest.
|
|
50
|
-
*/
|
|
51
|
-
const itemGapClasses = {
|
|
52
|
-
default: { xs: 'pb-5', sm: 'pb-6', md: 'pb-7', lg: 'pb-8', xl: 'pb-10' },
|
|
53
|
-
compact: { xs: 'pb-3', sm: 'pb-3.5', md: 'pb-4', lg: 'pb-5', xl: 'pb-6' }
|
|
54
|
-
};
|
|
55
|
-
/** The same ladder across, for the horizontal form. */
|
|
56
|
-
const itemGapXClasses = {
|
|
57
|
-
default: { xs: 'pe-5', sm: 'pe-6', md: 'pe-7', lg: 'pe-8', xl: 'pe-10' },
|
|
58
|
-
compact: { xs: 'pe-3', sm: 'pe-3.5', md: 'pe-4', lg: 'pe-5', xl: 'pe-6' }
|
|
59
|
-
};
|
|
60
|
-
const borderStyleClasses = {
|
|
61
|
-
solid: 'border-solid',
|
|
62
|
-
dashed: 'border-dashed',
|
|
63
|
-
dotted: 'border-dotted',
|
|
64
|
-
none: ''
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* The bullet at each of the three states.
|
|
68
|
-
*
|
|
69
|
-
* Every one of them is a different axis, never a different opacity: `complete`
|
|
70
|
-
* is the family's gradient, `current` is that gradient with a halo of the soft
|
|
71
|
-
* tint around it, and `upcoming` is a hairline ring on the page's own surface. A
|
|
72
|
-
* reader who cannot tell the colours apart still has a filled shape, a haloed
|
|
73
|
-
* shape and an empty one.
|
|
74
|
-
*
|
|
75
|
-
* There is no gloss line on the two filled ones, for the reason a filled
|
|
76
|
-
* `PlButton` has none — the gradient is the form. The `upcoming` ring is drawn
|
|
77
|
-
* in the neutral hairline rather than the sheet's white one, the same call a
|
|
78
|
-
* checkbox's edge makes: a bullet is small enough that its edge *is* the object,
|
|
79
|
-
* and white light on a translucent pane disappears on a light card.
|
|
80
|
-
*/
|
|
81
|
-
const bulletStatusClasses = {
|
|
82
|
-
complete: '[background-image:var(--p-fill)] text-(--p-on-solid)',
|
|
83
|
-
current: '[background-image:var(--p-fill)] text-(--p-on-solid) [box-shadow:0_0_0_0.25rem_var(--p-soft)]',
|
|
84
|
-
upcoming: 'border-2 bg-(--plass-surface) text-(--plass-muted-fg) [border-color:var(--plass-border)]'
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* The line *after* an item, which is what makes it the item's own property: a
|
|
88
|
-
* connector is coloured by whether the step it leaves has been reached, not by
|
|
89
|
-
* where it arrives.
|
|
90
|
-
*/
|
|
91
|
-
const connectorColorClasses = {
|
|
92
|
-
complete: '[border-color:var(--p-line-hover)]',
|
|
93
|
-
current: '[border-color:var(--plass-border)]',
|
|
94
|
-
upcoming: '[border-color:var(--plass-border)]'
|
|
95
|
-
};
|
|
96
|
-
const titleStatusClasses = {
|
|
97
|
-
complete: 'text-(--plass-fg)',
|
|
98
|
-
current: 'text-(--p-accent)',
|
|
99
|
-
upcoming: 'text-(--plass-muted-fg)'
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* One step.
|
|
103
|
-
*
|
|
104
|
-
* Its index is not a prop and cannot be: an item that had to be told where it
|
|
105
|
-
* was in the list would be an item every caller could put in the wrong place,
|
|
106
|
-
* and `active={2}` would stop meaning anything. The `PlTimeline` numbers its
|
|
107
|
-
* children as it walks them, and hands each one its index through a context.
|
|
108
|
-
*/
|
|
109
|
-
export const PlTimelineItem = /* @__PURE__ */ React.forwardRef(function PlTimelineItem({ title, meta, bullet, status, color, connector = 'solid', className, children, ...props }, ref) {
|
|
110
|
-
const timeline = React.useContext(TimelineContext);
|
|
111
|
-
const { index, last } = React.useContext(TimelineItemContext);
|
|
112
|
-
// A bare item outside a timeline still renders — it is just one step with
|
|
113
|
-
// nothing before or after it. The defaults are the timeline's own.
|
|
114
|
-
const size = timeline?.size ?? 'md';
|
|
115
|
-
const density = timeline?.density ?? 'default';
|
|
116
|
-
const orientation = timeline?.orientation ?? 'vertical';
|
|
117
|
-
const family = color ?? timeline?.color ?? 'primary';
|
|
118
|
-
const active = timeline?.active ?? null;
|
|
119
|
-
const resolved = status ??
|
|
120
|
-
(active === null
|
|
121
|
-
? 'upcoming'
|
|
122
|
-
: index < active
|
|
123
|
-
? 'complete'
|
|
124
|
-
: index === active
|
|
125
|
-
? 'current'
|
|
126
|
-
: 'upcoming');
|
|
127
|
-
const horizontal = orientation === 'horizontal';
|
|
128
|
-
// The last item's line would run off the end of the sequence into nothing.
|
|
129
|
-
const drawsConnector = connector !== 'none' && !last;
|
|
130
|
-
const bulletBox = (_jsx("span", { "aria-hidden": "true", className: cx('relative z-10 flex shrink-0 items-center justify-center rounded-full',
|
|
131
|
-
// The label inside the bullet is sized off the bullet rather than off
|
|
132
|
-
// the page's own text, so a number in an `xs` bullet is not the same
|
|
133
|
-
// 8px it would be in an `xl` one.
|
|
134
|
-
'size-(--p-bullet) text-[calc(var(--p-bullet)*0.5)] leading-none font-semibold tabular-nums', bulletStatusClasses[resolved], transitionClasses, iconClasses), children: bullet }));
|
|
135
|
-
/*
|
|
136
|
-
* The line, drawn as one border edge on an absolutely positioned box rather
|
|
137
|
-
* than as a filled `<div>`, so `dashed` and `dotted` are the browser's own
|
|
138
|
-
* dashes and land on the device pixel grid the way every other edge in the
|
|
139
|
-
* library does.
|
|
140
|
-
*
|
|
141
|
-
* It starts at the far edge of the bullet and runs to the edge of the item,
|
|
142
|
-
* which is where the next bullet begins — so the arithmetic is the bullet
|
|
143
|
-
* size, and that is the whole reason it is a custom property.
|
|
144
|
-
*/
|
|
145
|
-
const connectorLine = drawsConnector ? (_jsx("span", { "aria-hidden": "true", className: cx('pointer-events-none absolute',
|
|
146
|
-
// Half the bullet, less half the line, so the 2px rule is centred on
|
|
147
|
-
// the bullet rather than starting at its centre.
|
|
148
|
-
horizontal
|
|
149
|
-
? 'top-[calc(var(--p-bullet)/2_-_1px)] end-0 start-(--p-bullet) border-t-2'
|
|
150
|
-
: 'top-(--p-bullet) bottom-0 start-[calc(var(--p-bullet)/2_-_1px)] border-s-2', borderStyleClasses[connector], connectorColorClasses[resolved], transitionClasses) })) : null;
|
|
151
|
-
const body = (_jsxs("div", { className: cx('flex min-w-0 flex-col gap-0.5', horizontal ? 'mt-2' : ''), children: [hasContent(title) || hasContent(meta) ? (_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [hasContent(title) ? (_jsx("span", { className: cx('font-semibold', sheetTitleClasses[size], titleStatusClasses[resolved], transitionClasses), children: title })) : null, hasContent(meta) ? (_jsx("span", { className: cx('text-(--plass-muted-fg)', metaTextClasses[size]), children: meta })) : null] })) : null, hasContent(children) ? (_jsx("div", { className: cx('text-(--plass-muted-fg)', sheetBodyClasses[size]), children: children })) : null] }));
|
|
152
|
-
return (_jsxs("li", { ref: ref, "aria-current": resolved === 'current' ? 'step' : undefined, "data-status": resolved, className: cx('relative', horizontal
|
|
153
|
-
? cx('flex min-w-0 flex-1 flex-col', last ? '' : itemGapXClasses[density][size])
|
|
154
|
-
: cx('flex', bulletGapClasses[size], last ? '' : itemGapClasses[density][size]), className), style: {
|
|
155
|
-
'--p-bullet': bulletSizeValues[size],
|
|
156
|
-
...surfaceSlots(family, 0),
|
|
157
|
-
// A container's slots leave the sheet undyed, which is right for the
|
|
158
|
-
// ground a bullet sits on — but a bullet *is* the thing being
|
|
159
|
-
// coloured, so the two fills it needs are put back.
|
|
160
|
-
'--p-fill': `var(--plass-${family}-fill)`,
|
|
161
|
-
'--p-on-solid': `var(--plass-${family}-on-solid)`
|
|
162
|
-
}, ...props, children: [connectorLine, bulletBox, body] }));
|
|
163
|
-
});
|
|
164
|
-
/**
|
|
165
|
-
* A sequence of steps, in the order they happen in.
|
|
166
|
-
*
|
|
167
|
-
* There is no Base UI primitive under this and there should not be: a timeline
|
|
168
|
-
* has no selection, no roving focus and no keyboard contract — it is a list, and
|
|
169
|
-
* reaching for a composite primitive to draw one would hand every consumer's
|
|
170
|
-
* record of events the semantics of a widget.
|
|
171
|
-
*
|
|
172
|
-
* It is an `<ol>` for the reason it exists at all: the order *is* the content. A
|
|
173
|
-
* screen reader announcing "list of 5 items" over an unordered list would be
|
|
174
|
-
* describing something else.
|
|
175
|
-
*
|
|
176
|
-
* The children are numbered here rather than by a prop on each item, so `active`
|
|
177
|
-
* has something to count against and inserting a step in the middle does not
|
|
178
|
-
* mean renumbering the ones after it.
|
|
179
|
-
*/
|
|
180
|
-
export const PlTimeline = /* @__PURE__ */ React.forwardRef(function PlTimeline({ active, size = 'md', color = 'primary', density = 'default', orientation = 'vertical', render, className, style, children, ...props }, ref) {
|
|
181
|
-
// `toArray` is what drops the `null`s and `false`s a conditional step leaves
|
|
182
|
-
// behind, so `active={2}` counts the steps that are actually on the page.
|
|
183
|
-
const items = React.Children.toArray(children);
|
|
184
|
-
const count = items.length;
|
|
185
|
-
const context = React.useMemo(() => ({
|
|
186
|
-
size,
|
|
187
|
-
density,
|
|
188
|
-
orientation,
|
|
189
|
-
color,
|
|
190
|
-
active: active ?? null
|
|
191
|
-
}), [size, density, orientation, color, active]);
|
|
192
|
-
const element = useRender({
|
|
193
|
-
render: render ?? _jsx("ol", {}),
|
|
194
|
-
ref,
|
|
195
|
-
props: {
|
|
196
|
-
// Tailwind's reset takes the markers off every `<ol>`, and Safari takes
|
|
197
|
-
// the list semantics off with them. Saying `role="list"` out loud is the
|
|
198
|
-
// one-line fix, and it costs nothing when the reset is not there.
|
|
199
|
-
role: 'list',
|
|
200
|
-
className: cx('flex', orientation === 'horizontal' ? 'flex-row' : 'flex-col', className),
|
|
201
|
-
style: { ...surfaceSlots(color, 0), ...style },
|
|
202
|
-
children: items.map((item, index) => (_jsx(TimelineItemContext.Provider, { value: { index, last: index === count - 1 }, children: item }, index))),
|
|
203
|
-
...props
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
return _jsx(TimelineContext.Provider, { value: context, children: element });
|
|
207
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as r from"react";import{useRender as t}from"@base-ui/react/use-render";import{cx as a,hasContent as s,iconClasses as o,metaTextClasses as n,sheetBodyClasses as i,sheetTitleClasses as d,surfaceSlots as p,transitionClasses as c}from"../../internal/styles.js";const m=/* @__PURE__ */r.createContext(null),u=/* @__PURE__ */r.createContext({index:0,last:!1}),x={xs:"0.875rem",sm:"1rem",md:"1.25rem",lg:"1.5rem",xl:"1.875rem"},f={xs:"gap-2",sm:"gap-2.5",md:"gap-3",lg:"gap-3.5",xl:"gap-4"},b={default:{xs:"pb-5",sm:"pb-6",md:"pb-7",lg:"pb-8",xl:"pb-10"},compact:{xs:"pb-3",sm:"pb-3.5",md:"pb-4",lg:"pb-5",xl:"pb-6"}},g={default:{xs:"pe-5",sm:"pe-6",md:"pe-7",lg:"pe-8",xl:"pe-10"},compact:{xs:"pe-3",sm:"pe-3.5",md:"pe-4",lg:"pe-5",xl:"pe-6"}},v={solid:"border-solid",dashed:"border-dashed",dotted:"border-dotted",none:""},h={complete:"[background-image:var(--p-fill)] text-(--p-on-solid)",current:"[background-image:var(--p-fill)] text-(--p-on-solid) [box-shadow:0_0_0_0.25rem_var(--p-soft)]",upcoming:"border-2 bg-(--plass-surface) text-(--plass-muted-fg) [border-color:var(--plass-border)]"},y={complete:"[border-color:var(--p-line-hover)]",current:"[border-color:var(--plass-border)]",upcoming:"[border-color:var(--plass-border)]"},N={complete:"text-(--plass-fg)",current:"text-(--p-accent)",upcoming:"text-(--plass-muted-fg)"};export const PlTimelineItem=/* @__PURE__ */r.forwardRef(function PlTimelineItem({title:t,meta:_,bullet:w,status:z,color:P,connector:j="solid",className:C,children:T,...k},I){const R=r.useContext(m),{index:$,last:A}=r.useContext(u),M=R?.size??"md",q=R?.density??"default",B=R?.orientation??"vertical",D=P??R?.color??"primary",E=R?.active??null,F=z??(null===E?"upcoming":$<E?"complete":$===E?"current":"upcoming"),G="horizontal"===B,H="none"!==j&&!A,J=e("span",{"aria-hidden":"true",className:a("relative z-10 flex shrink-0 items-center justify-center rounded-full","size-(--p-bullet) text-[calc(var(--p-bullet)*0.5)] leading-none font-semibold tabular-nums",h[F],c,o),children:w}),K=H?e("span",{"aria-hidden":"true",className:a("pointer-events-none absolute",G?"top-[calc(var(--p-bullet)/2_-_1px)] end-0 start-(--p-bullet) border-t-2":"top-(--p-bullet) bottom-0 start-[calc(var(--p-bullet)/2_-_1px)] border-s-2",v[j],y[F],c)}):null,L=l("div",{className:a("flex min-w-0 flex-col gap-0.5",G?"mt-2":""),children:[s(t)||s(_)?l("div",{className:"flex flex-wrap items-baseline gap-x-2",children:[s(t)?e("span",{className:a("font-semibold",d[M],N[F],c),children:t}):null,s(_)?e("span",{className:a("text-(--plass-muted-fg)",n[M]),children:_}):null]}):null,s(T)?e("div",{className:a("text-(--plass-muted-fg)",i[M]),children:T}):null]});return l("li",{ref:I,"aria-current":"current"===F?"step":void 0,"data-status":F,className:a("relative",G?a("flex min-w-0 flex-1 flex-col",A?"":g[q][M]):a("flex",f[M],A?"":b[q][M]),C),style:{"--p-bullet":x[M],...p(D,0),"--p-fill":`var(--plass-${D}-fill)`,"--p-on-solid":`var(--plass-${D}-on-solid)`},...k,children:[K,J,L]})});export const PlTimeline=/* @__PURE__ */r.forwardRef(function PlTimeline({active:l,size:s="md",color:o="primary",density:n="default",orientation:i="vertical",render:d,className:c,style:x,children:f,...b},g){const v=r.Children.toArray(f),h=v.length,y=r.useMemo(()=>({size:s,density:n,orientation:i,color:o,active:l??null}),[s,n,i,o,l]),N=t({render:d??e("ol",{}),ref:g,props:{role:"list",className:a("flex","horizontal"===i?"flex-row":"flex-col",c),style:{...p(o,0),...x},children:v.map((l,r)=>e(u.Provider,{value:{index:r,last:r===h-1},children:l},r)),...b}});return e(m.Provider,{value:y,children:N})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlTimeline,PlTimelineItem}from"./PlTimeline.js";
|