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,109 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Field } from '@base-ui/react/field';
|
|
4
|
-
import { OTPField } from '@base-ui/react/otp-field';
|
|
5
|
-
import { cx, disabledClasses, fieldReadOnlyClasses, fieldRestClasses, hasContent, metaTextClasses, stackGapClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
6
|
-
/** How many slots a code may have. Two is the shortest thing worth splitting. */
|
|
7
|
-
const MIN_LENGTH = 2;
|
|
8
|
-
const MAX_LENGTH = 12;
|
|
9
|
-
/**
|
|
10
|
-
* A slot's box.
|
|
11
|
-
*
|
|
12
|
-
* Its own ladder rather than `controlHeightClasses`, for the reason a tick box
|
|
13
|
-
* has one: a slot is not a control in a row of controls, it is a character
|
|
14
|
-
* standing on its own, and an `md` slot the height of an `md` `PlButton` would
|
|
15
|
-
* be too small to read a code out of across a desk. Every step is taller than it
|
|
16
|
-
* is wide, which is what makes a row of them read as places for one character
|
|
17
|
-
* each rather than as a row of tiny fields.
|
|
18
|
-
*/
|
|
19
|
-
const slotSizeClasses = {
|
|
20
|
-
xs: 'h-7 w-6',
|
|
21
|
-
sm: 'h-8 w-7',
|
|
22
|
-
md: 'h-10 w-8',
|
|
23
|
-
lg: 'h-12 w-10',
|
|
24
|
-
xl: 'h-14 w-12'
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* And its own radius, for the reason a tick box has one.
|
|
28
|
-
*
|
|
29
|
-
* `radiusClasses` is a percentage of a *control's height*, which is a cut corner
|
|
30
|
-
* on something wide and a lozenge on something nearly square: `md` is 12px, and
|
|
31
|
-
* a 12px corner on a 32px-wide box is most of the way to a pill. These are
|
|
32
|
-
* ~22% of the slot's width instead, which is the same amount of cut on this
|
|
33
|
-
* shape — a sheet with the corners taken off, never a pill.
|
|
34
|
-
*/
|
|
35
|
-
const slotRadiusClasses = {
|
|
36
|
-
xs: 'rounded-[0.3125rem]',
|
|
37
|
-
sm: 'rounded-[0.375rem]',
|
|
38
|
-
md: 'rounded-[0.4375rem]',
|
|
39
|
-
lg: 'rounded-[0.5625rem]',
|
|
40
|
-
xl: 'rounded-[0.6875rem]'
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* And its own type scale, two steps up the control ladder. A verification code
|
|
44
|
-
* is read out loud off a phone and typed with the other hand; it is the one
|
|
45
|
-
* piece of text in a form that should be bigger than the label above it.
|
|
46
|
-
*/
|
|
47
|
-
const slotTextClasses = {
|
|
48
|
-
xs: 'text-[0.8125rem]',
|
|
49
|
-
sm: 'text-[0.9375rem]',
|
|
50
|
-
md: 'text-[1.0625rem]',
|
|
51
|
-
lg: 'text-[1.25rem]',
|
|
52
|
-
xl: 'text-[1.5rem]'
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Between the slots, and the only thing `density` touches here — spacing, never
|
|
56
|
-
* the box and never the type scale, exactly as everywhere else in the library.
|
|
57
|
-
*/
|
|
58
|
-
const slotGapClasses = {
|
|
59
|
-
default: { xs: 'gap-1', sm: 'gap-1', md: 'gap-1.5', lg: 'gap-2', xl: 'gap-2.5' },
|
|
60
|
-
compact: { xs: 'gap-0.5', sm: 'gap-0.5', md: 'gap-1', lg: 'gap-1', xl: 'gap-1.5' }
|
|
61
|
-
};
|
|
62
|
-
/** `charset` is the library's word; Base UI's is `validationType`. */
|
|
63
|
-
const validationTypes = {
|
|
64
|
-
numeric: 'numeric',
|
|
65
|
-
alpha: 'alpha',
|
|
66
|
-
alphanumeric: 'alphanumeric',
|
|
67
|
-
any: 'none'
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* A row of one-character slots: a PIN, a texted verification code, an invite
|
|
71
|
-
* key.
|
|
72
|
-
*
|
|
73
|
-
* Base UI owns everything that makes this harder than it looks — one hidden
|
|
74
|
-
* value behind however many inputs, paste spread across the slots from wherever
|
|
75
|
-
* the caret was, backspace stepping back a box, a click landing on the first
|
|
76
|
-
* empty slot rather than the one under the pointer, and the autofill hook that
|
|
77
|
-
* lets a phone offer the code straight from the message.
|
|
78
|
-
*
|
|
79
|
-
* What is here is the ladder a slot is drawn on and the field shell it shares
|
|
80
|
-
* with `PlTextField` and `PlSelect` — a slot is a field-shaped box, and a form
|
|
81
|
-
* holding both should not look like two form kits stacked on each other.
|
|
82
|
-
*/
|
|
83
|
-
export const PlOtpField = /* @__PURE__ */ React.forwardRef(function PlOtpField({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, length = 6, charset = 'numeric', mask = false, groupSize, separator = '–', value, defaultValue, onValueChange, onComplete, onValueInvalid, autoSubmit = false, label, description, error, invalid, name, required = false, disabled = false, readOnly = false, autoFocus = false, className, style, ...props }, ref) {
|
|
84
|
-
const slots = Math.min(MAX_LENGTH, Math.max(MIN_LENGTH, Math.round(length)));
|
|
85
|
-
const hasError = hasContent(error);
|
|
86
|
-
const isInvalid = invalid ?? hasError;
|
|
87
|
-
// Invalid re-points the whole slot family at `danger`, exactly as on
|
|
88
|
-
// PlTextField, so the edge, the ring, the caret and the message all turn over
|
|
89
|
-
// together and no state needs tokens of its own.
|
|
90
|
-
const family = isInvalid ? 'danger' : color;
|
|
91
|
-
const slotClassNames = cx(
|
|
92
|
-
// `font-family` rather than the `font` shorthand: the shorthand would take
|
|
93
|
-
// the inherited font *size* with it and undo the ladder set on the next
|
|
94
|
-
// line.
|
|
95
|
-
'text-center font-medium [font-family:inherit]', slotSizeClasses[size], slotTextClasses[size], slotRadiusClasses[size], transitionClasses, 'caret-(--p-accent) selection:bg-(--p-soft-press)',
|
|
96
|
-
// `focus` rather than `focus-visible`: a slot is put in focus by clicking it
|
|
97
|
-
// as often as by typing into it, and the ring is the only thing saying which
|
|
98
|
-
// character the next keystroke lands on.
|
|
99
|
-
'focus:[outline:2px_solid_var(--p-ring)] focus:[outline-offset:0px]', 'focus:[transition-duration:0ms]',
|
|
100
|
-
// An if/else rather than stacked variants: two Tailwind classes of equal
|
|
101
|
-
// specificity resolve by their order in the generated stylesheet.
|
|
102
|
-
disabled
|
|
103
|
-
? disabledClasses[variant]
|
|
104
|
-
: readOnly
|
|
105
|
-
? fieldReadOnlyClasses[variant]
|
|
106
|
-
: fieldRestClasses[variant]);
|
|
107
|
-
const separatorEvery = groupSize && groupSize > 0 ? Math.round(groupSize) : 0;
|
|
108
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: cx('inline-flex flex-col align-top', stackGapClasses[size], className), style: { ...surfaceSlots(family, elevation), ...style }, children: [hasContent(label) ? (_jsx(Field.Label, { className: cx(metaTextClasses[size], 'font-medium', disabled ? 'text-(--plass-muted-fg)' : 'text-(--plass-fg)'), children: label })) : null, _jsx(OTPField.Root, { ref: ref, length: slots, validationType: validationTypes[charset], mask: mask, name: name, required: required, disabled: disabled, readOnly: readOnly, autoSubmit: autoSubmit, value: value, defaultValue: defaultValue, onValueChange: (next) => onValueChange?.(next), onValueComplete: (next) => onComplete?.(next), onValueInvalid: (next) => onValueInvalid?.(next), className: cx('flex items-center', slotGapClasses[density][size]), ...props, children: Array.from({ length: slots }, (_, index) => (_jsxs(React.Fragment, { children: [separatorEvery > 0 && index > 0 && index % separatorEvery === 0 ? (_jsx("span", { "aria-hidden": "true", className: cx('select-none px-0.5 text-(--plass-muted-fg)', slotTextClasses[size]), children: separator })) : null, _jsx(OTPField.Input, { className: slotClassNames, autoFocus: autoFocus && index === 0 })] }, index))) }), hasContent(description) ? (_jsx(Field.Description, { className: cx(metaTextClasses[size], 'text-(--plass-muted-fg)'), children: description })) : null, hasError ? (_jsx(Field.Error, { match: true, className: cx(metaTextClasses[size], 'text-(--p-accent)'), children: error })) : null] }));
|
|
109
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as t from"react";import{Field as l}from"@base-ui/react/field";import{OTPField as r}from"@base-ui/react/otp-field";import{cx as n,disabledClasses as s,fieldReadOnlyClasses as i,fieldRestClasses as o,hasContent as m,metaTextClasses as d,stackGapClasses as u,surfaceSlots as c,transitionClasses as p}from"../../internal/styles.js";const f={xs:"h-7 w-6",sm:"h-8 w-7",md:"h-10 w-8",lg:"h-12 w-10",xl:"h-14 w-12"},x={xs:"rounded-[0.3125rem]",sm:"rounded-[0.375rem]",md:"rounded-[0.4375rem]",lg:"rounded-[0.5625rem]",xl:"rounded-[0.6875rem]"},g={xs:"text-[0.8125rem]",sm:"text-[0.9375rem]",md:"text-[1.0625rem]",lg:"text-[1.25rem]",xl:"text-[1.5rem]"},h={default:{xs:"gap-1",sm:"gap-1",md:"gap-1.5",lg:"gap-2",xl:"gap-2.5"},compact:{xs:"gap-0.5",sm:"gap-0.5",md:"gap-1",lg:"gap-1",xl:"gap-1.5"}},y={numeric:"numeric",alpha:"alpha",alphanumeric:"alphanumeric",any:"none"};export const PlOtpField=/* @__PURE__ */t.forwardRef(function PlOtpField({variant:b="glass",size:v="md",color:N="primary",density:V="default",elevation:w=0,length:F=6,charset:j="numeric",mask:C=!1,groupSize:M,separator:O="–",value:I,defaultValue:R,onValueChange:S,onComplete:k,onValueInvalid:q,autoSubmit:z=!1,label:P,description:_,error:A,invalid:D,name:E,required:L=!1,disabled:T=!1,readOnly:B=!1,autoFocus:G=!1,className:H,style:J,...K},Q){const U=Math.min(12,Math.max(2,Math.round(F))),W=m(A),X=D??W,Y=X?"danger":N,Z=n("text-center font-medium [font-family:inherit]",f[v],g[v],x[v],p,"caret-(--p-accent) selection:bg-(--p-soft-press)","focus:[outline:2px_solid_var(--p-ring)] focus:[outline-offset:0px]","focus:[transition-duration:0ms]",T?s[b]:B?i[b]:o[b]),$=M&&M>0?Math.round(M):0;return a(l.Root,{disabled:T,invalid:X,className:n("inline-flex flex-col align-top",u[v],H),style:{...c(Y,w),...J},children:[m(P)?e(l.Label,{className:n(d[v],"font-medium",T?"text-(--plass-muted-fg)":"text-(--plass-fg)"),children:P}):null,e(r.Root,{ref:Q,length:U,validationType:y[j],mask:C,name:E,required:L,disabled:T,readOnly:B,autoSubmit:z,value:I,defaultValue:R,onValueChange:e=>S?.(e),onValueComplete:e=>k?.(e),onValueInvalid:e=>q?.(e),className:n("flex items-center",h[V][v]),...K,children:Array.from({length:U},(l,s)=>a(t.Fragment,{children:[$>0&&s>0&&s%$===0?e("span",{"aria-hidden":"true",className:n("select-none px-0.5 text-(--plass-muted-fg)",g[v]),children:O}):null,e(r.Input,{className:Z,autoFocus:G&&0===s})]},s))}),m(_)?e(l.Description,{className:n(d[v],"text-(--plass-muted-fg)"),children:_}):null,W?e(l.Error,{match:!0,className:n(d[v],"text-(--p-accent)"),children:A}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlOtpField}from"./PlOtpField.js";
|
|
@@ -1,77 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Dialog as BaseUIDialog } from '@base-ui/react/dialog';
|
|
3
|
-
import { surfaceSlots } from '../../internal/styles.js';
|
|
4
|
-
/**
|
|
5
|
-
* The four tones.
|
|
6
|
-
*
|
|
7
|
-
* `scrim` matches `PlModal`'s backdrop exactly — the two have to, or a modal
|
|
8
|
-
* opened over an overlay would show a seam. `clear` draws nothing and still
|
|
9
|
-
* covers the viewport, so it goes on catching pointer events.
|
|
10
|
-
*/
|
|
11
|
-
const toneClasses = {
|
|
12
|
-
scrim: 'bg-(--plass-scrim) [backdrop-filter:blur(2px)] [-webkit-backdrop-filter:blur(2px)]',
|
|
13
|
-
glass: /* @__PURE__ */ [
|
|
14
|
-
'[background-color:color-mix(in_oklab,var(--plass-scrim)_45%,transparent)]',
|
|
15
|
-
'[backdrop-filter:var(--plass-blur)] [-webkit-backdrop-filter:var(--plass-blur)]'
|
|
16
|
-
].join(' '),
|
|
17
|
-
solid: 'bg-(--plass-surface)',
|
|
18
|
-
clear: ''
|
|
19
|
-
};
|
|
20
|
-
const alignClasses = {
|
|
21
|
-
start: 'items-start',
|
|
22
|
-
center: 'items-center',
|
|
23
|
-
end: 'items-end'
|
|
24
|
-
};
|
|
25
|
-
/** The padding between the content and the edge of the viewport. */
|
|
26
|
-
const insetClasses = {
|
|
27
|
-
xs: 'p-3',
|
|
28
|
-
sm: 'p-4',
|
|
29
|
-
md: 'p-6',
|
|
30
|
-
lg: 'p-8',
|
|
31
|
-
xl: 'p-10'
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Opacity only, on both the scrim and the content. An overlay that scales or
|
|
35
|
-
* slides drags whatever is written on it across the screen, which is the one
|
|
36
|
-
* thing the house style is against — and unlike a control, this one is usually
|
|
37
|
-
* carrying a sentence.
|
|
38
|
-
*/
|
|
39
|
-
const fadeClasses = /* @__PURE__ */ [
|
|
40
|
-
'[transition:opacity_var(--plass-duration)_var(--plass-ease)]',
|
|
41
|
-
'data-[starting-style]:opacity-0 data-[ending-style]:opacity-0'
|
|
42
|
-
].join(' ');
|
|
43
|
-
/**
|
|
44
|
-
* A sheet over the whole page that stops it being used.
|
|
45
|
-
*
|
|
46
|
-
* The difference from a `PlModal` is what is *not* here: no surface, no border,
|
|
47
|
-
* no title, no actions. An overlay is the scrim on its own, with whatever the
|
|
48
|
-
* caller puts on top of it — most often a spinner and a line saying what is
|
|
49
|
-
* being waited for.
|
|
50
|
-
*
|
|
51
|
-
* It is not dismissible by default, and that is the one prop worth reading
|
|
52
|
-
* twice. Everything else Base UI owns: the portal, the scroll lock, the focus
|
|
53
|
-
* held inside, the page behind going inert, and focus returning to wherever it
|
|
54
|
-
* came from when the overlay closes.
|
|
55
|
-
*/
|
|
56
|
-
export function PlOverlay({ open, defaultOpen, onOpenChange, tone = 'scrim', dismissible = false, modal = true, align = 'center', size = 'md', color = 'primary', label = 'Overlay', className, style, children, ...props }) {
|
|
57
|
-
return (_jsx(BaseUIDialog.Root, { open: open, defaultOpen: defaultOpen, modal: modal, disablePointerDismissal: !dismissible, onOpenChange: (next, details) => {
|
|
58
|
-
// `disablePointerDismissal` covers the click; Escape has no prop of its
|
|
59
|
-
// own, so it is cancelled here by the reason the change arrives with.
|
|
60
|
-
if (!dismissible && !next && details.reason === 'escape-key') {
|
|
61
|
-
details.cancel();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
onOpenChange?.(next);
|
|
65
|
-
}, children: _jsxs(BaseUIDialog.Portal, { children: [_jsx(BaseUIDialog.Backdrop, { className: `plass-portal fixed inset-0 z-50 ${fadeClasses} ${toneClasses[tone]}` }), _jsx(BaseUIDialog.Viewport, { className: [
|
|
66
|
-
'plass-portal fixed inset-0 z-50 flex justify-center',
|
|
67
|
-
alignClasses[align],
|
|
68
|
-
insetClasses[size]
|
|
69
|
-
].join(' '), children: _jsx(BaseUIDialog.Popup, { "aria-label": label, className: [
|
|
70
|
-
'flex max-h-full max-w-full flex-col items-center justify-center',
|
|
71
|
-
'[outline:none]',
|
|
72
|
-
fadeClasses,
|
|
73
|
-
className ?? ''
|
|
74
|
-
]
|
|
75
|
-
.filter(Boolean)
|
|
76
|
-
.join(' '), style: { ...surfaceSlots(color, 0), ...style }, ...props, children: children }) })] }) }));
|
|
77
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Dialog as s}from"@base-ui/react/dialog";import{surfaceSlots as l}from"../../internal/styles.js";const r={scrim:"bg-(--plass-scrim) [backdrop-filter:blur(2px)] [-webkit-backdrop-filter:blur(2px)]",glass:/* @__PURE__ */["[background-color:color-mix(in_oklab,var(--plass-scrim)_45%,transparent)]","[backdrop-filter:var(--plass-blur)] [-webkit-backdrop-filter:var(--plass-blur)]"].join(" "),solid:"bg-(--plass-surface)",clear:""},i={start:"items-start",center:"items-center",end:"items-end"},t={xs:"p-3",sm:"p-4",md:"p-6",lg:"p-8",xl:"p-10"},n=/* @__PURE__ */["[transition:opacity_var(--plass-duration)_var(--plass-ease)]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" ");export function PlOverlay({open:o,defaultOpen:c,onOpenChange:p,tone:m="scrim",dismissible:d=!1,modal:f=!0,align:u="center",size:b="md",color:x="primary",label:y="Overlay",className:g,style:j,children:k,...h}){return e(s.Root,{open:o,defaultOpen:c,modal:f,disablePointerDismissal:!d,onOpenChange:(e,a)=>{d||e||"escape-key"!==a.reason?p?.(e):a.cancel()},children:a(s.Portal,{children:[e(s.Backdrop,{className:`plass-portal fixed inset-0 z-50 ${n} ${r[m]}`}),e(s.Viewport,{className:["plass-portal fixed inset-0 z-50 flex justify-center",i[u],t[b]].join(" "),children:e(s.Popup,{"aria-label":y,className:["flex max-h-full max-w-full flex-col items-center justify-center","[outline:none]",n,g??""].filter(Boolean).join(" "),style:{...l(x,0),...j},...h,children:k})})]})})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlOverlay}from"./PlOverlay.js";
|
|
@@ -1,159 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { PlButton } from '../button/PlButton.js';
|
|
4
|
-
import { ChevronIcon } from '../../internal/icons.js';
|
|
5
|
-
import { controlTextClasses, gapClasses, srOnlyClasses } from '../../internal/styles.js';
|
|
6
|
-
function range(start, end) {
|
|
7
|
-
return Array.from({ length: Math.max(0, end - start + 1) }, (_, index) => start + index);
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Which pages the row actually shows.
|
|
11
|
-
*
|
|
12
|
-
* The shape every pagination converges on — a fixed run at each end, a window
|
|
13
|
-
* around the current page, and an ellipsis wherever those leave a gap — with one
|
|
14
|
-
* detail that is easy to get wrong and matters: a gap of exactly one page is
|
|
15
|
-
* filled with that page rather than with an ellipsis. `1 … 3 … 9` hides a single
|
|
16
|
-
* number behind a symbol wider than the number it replaced.
|
|
17
|
-
*
|
|
18
|
-
* The row is also pinned to a constant number of slots, whatever page it is on:
|
|
19
|
-
* the window slides toward whichever end it is near instead of being clipped by
|
|
20
|
-
* it, so page 1 shows `1 2 3 4 5 … 20` and page 10 shows `1 … 9 10 11 … 20`.
|
|
21
|
-
* Which slots are pages and which are ellipses changes; how many there are does
|
|
22
|
-
* not. Without that, stepping from page 1 to page 2 would relayout the row and
|
|
23
|
-
* every button would move out from under the pointer that just pressed one.
|
|
24
|
-
*/
|
|
25
|
-
function paginationRange(count, page, siblingCount, boundaryCount) {
|
|
26
|
-
const startPages = range(1, Math.min(boundaryCount, count));
|
|
27
|
-
const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count);
|
|
28
|
-
const siblingsStart = Math.max(Math.min(page - siblingCount, count - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
|
|
29
|
-
const siblingsEnd = Math.min(Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2), endPages.length > 0 ? endPages[0] - 2 : count - 1);
|
|
30
|
-
return [
|
|
31
|
-
...startPages,
|
|
32
|
-
// An ellipsis when more than one page is hidden, the page itself when
|
|
33
|
-
// exactly one is, and nothing when none is.
|
|
34
|
-
...(siblingsStart > boundaryCount + 2
|
|
35
|
-
? ['start-ellipsis']
|
|
36
|
-
: boundaryCount + 1 < count - boundaryCount
|
|
37
|
-
? [boundaryCount + 1]
|
|
38
|
-
: []),
|
|
39
|
-
...range(siblingsStart, siblingsEnd),
|
|
40
|
-
...(siblingsEnd < count - boundaryCount - 1
|
|
41
|
-
? ['end-ellipsis']
|
|
42
|
-
: count - boundaryCount > boundaryCount
|
|
43
|
-
? [count - boundaryCount]
|
|
44
|
-
: []),
|
|
45
|
-
...endPages
|
|
46
|
-
];
|
|
47
|
-
}
|
|
48
|
-
/** Two chevrons, for the steppers that jump to an end rather than by one page. */
|
|
49
|
-
function DoubleChevronIcon() {
|
|
50
|
-
return (_jsx("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "m7.5 4.5 3.5 3.5-3.5 3.5M3.5 4.5 7 8l-3.5 3.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* The ellipsis. A `<span>`, not a button and not a disabled button: it is not a
|
|
54
|
-
* control that happens to be unavailable, it is punctuation.
|
|
55
|
-
*/
|
|
56
|
-
const ellipsisClasses = {
|
|
57
|
-
xs: 'h-6 min-w-6',
|
|
58
|
-
sm: 'h-8 min-w-8',
|
|
59
|
-
md: 'h-10 min-w-10',
|
|
60
|
-
lg: 'h-12 min-w-12',
|
|
61
|
-
xl: 'h-14 min-w-14'
|
|
62
|
-
};
|
|
63
|
-
/**
|
|
64
|
-
* A row of page numbers.
|
|
65
|
-
*
|
|
66
|
-
* Every button in it is a real `PlButton`, which is the point: a pagination is
|
|
67
|
-
* not a new kind of control, it is buttons in a row that happen to know about
|
|
68
|
-
* each other. Reusing the component means the row inherits the glass, the
|
|
69
|
-
* pointer bloom, the press signature, the focus ring and every future change to
|
|
70
|
-
* any of them for free — and it means an `lg` pagination lines up with an `lg`
|
|
71
|
-
* button beside it, because it *is* one.
|
|
72
|
-
*
|
|
73
|
-
* `variant` sets how the pages at rest look; the current page is always `solid`,
|
|
74
|
-
* which is the one thing the row has to say without being read. That is why the
|
|
75
|
-
* default here is `ghost` rather than the `solid` a lone PlButton takes — nine
|
|
76
|
-
* panes of tinted glass in a row say that all nine are the primary action.
|
|
77
|
-
*
|
|
78
|
-
* The markup is a `<nav>` around a `<ul>` because that is what a screen reader
|
|
79
|
-
* needs to hear: a named landmark it can skip, holding a list whose length says
|
|
80
|
-
* how far the pages go, with `aria-current="page"` marking where it is.
|
|
81
|
-
*/
|
|
82
|
-
export const PlPagination = /* @__PURE__ */ React.forwardRef(function PlPagination({ variant = 'ghost', size = 'md', color = 'primary', density = 'compact', elevation = 0, count, page: pageProp, defaultPage = 1, onPageChange, siblingCount = 1, boundaryCount = 1, showEdges = false, showArrows = true, disabled = false, getPageHref, label = 'Pagination', pageLabel = (value) => `Page ${value}`, previousLabel = 'Previous page', nextLabel = 'Next page', firstLabel = 'First page', lastLabel = 'Last page', statusLabel = (value, total) => `Page ${value} of ${total}`, className, children, ...props }, ref) {
|
|
83
|
-
const [uncontrolled, setUncontrolled] = React.useState(defaultPage);
|
|
84
|
-
const current = Math.min(Math.max(pageProp ?? uncontrolled, 1), Math.max(count, 1));
|
|
85
|
-
const go = (next) => {
|
|
86
|
-
const clamped = Math.min(Math.max(next, 1), count);
|
|
87
|
-
if (clamped === current) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (pageProp === undefined) {
|
|
91
|
-
setUncontrolled(clamped);
|
|
92
|
-
}
|
|
93
|
-
onPageChange?.(clamped);
|
|
94
|
-
};
|
|
95
|
-
// One page is not a set of pages, and no pages is not a thing to say out
|
|
96
|
-
// loud. A row that renders a lone disabled "1" is a control advertising
|
|
97
|
-
// that it has nothing to do.
|
|
98
|
-
if (count < 2) {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
const slots = paginationRange(count, current, siblingCount, boundaryCount);
|
|
102
|
-
const atStart = current <= 1;
|
|
103
|
-
const atEnd = current >= count;
|
|
104
|
-
/*
|
|
105
|
-
* A link only where there is somewhere to go. The page being read and a
|
|
106
|
-
* stepper at the end of the row are both `disabled`, and `disabled` is not
|
|
107
|
-
* something an `<a>` can be — a link that only looks unavailable is one a
|
|
108
|
-
* keyboard still lands on and a crawler still follows.
|
|
109
|
-
*/
|
|
110
|
-
const linkProps = (to, inert, rel) => getPageHref && !inert ? { render: _jsx("a", { href: getPageHref(to), rel: rel }) } : null;
|
|
111
|
-
/*
|
|
112
|
-
* Who answers the press.
|
|
113
|
-
*
|
|
114
|
-
* With an `href` and a handler both, the handler wins and the navigation is
|
|
115
|
-
* cancelled: that is a client-side router keeping the page it already has.
|
|
116
|
-
* With an `href` and no handler, the link is left to do what a link does —
|
|
117
|
-
* which is also what makes the row work with JavaScript still loading.
|
|
118
|
-
*/
|
|
119
|
-
const press = (event, to) => {
|
|
120
|
-
// A press carrying a modifier is the reader asking the browser for it: a
|
|
121
|
-
// new tab, a new window, a saved copy. Never ours to cancel.
|
|
122
|
-
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (getPageHref && !onPageChange) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
event.preventDefault();
|
|
129
|
-
go(to);
|
|
130
|
-
};
|
|
131
|
-
/**
|
|
132
|
-
* The steppers. Icon-only PlButtons, so they go square and land on exactly
|
|
133
|
-
* the same footprint as a single-digit page — a row whose ends are a
|
|
134
|
-
* different width from its middle reads as two controls pushed together.
|
|
135
|
-
*/
|
|
136
|
-
const stepper = (key, accessibleName, to, inert, rotation, glyph, rel) => (_jsx("li", { className: "flex", children: _jsx(PlButton, { variant: variant, size: size, color: color, density: density, elevation: elevation, disabled: disabled || inert, "aria-label": accessibleName, startIcon: _jsx("span", { className: `flex items-center ${rotation}`, children: glyph }), onClick: (event) => press(event, to), ...linkProps(to, disabled || inert, rel) }) }, key));
|
|
137
|
-
return (_jsxs("nav", { ref: ref, "aria-label": label, className: ['flex items-center', className ?? ''].filter(Boolean).join(' '), ...props, children: [_jsxs("ul", { role: "list", className: `m-0 flex list-none items-center p-0 ${gapClasses[size]}`, children: [showEdges
|
|
138
|
-
? stepper('first', firstLabel, 1, atStart, 'rotate-180 rtl:rotate-0', _jsx(DoubleChevronIcon, {}))
|
|
139
|
-
: null, showArrows
|
|
140
|
-
? stepper('previous', previousLabel, current - 1, atStart, 'rotate-90 rtl:-rotate-90', _jsx(ChevronIcon, {}), 'prev')
|
|
141
|
-
: null, slots.map((slot, index) => typeof slot === 'number' ? (_jsx("li", { className: "flex", children: _jsx(PlButton
|
|
142
|
-
// The current page is always filled, whatever the row's
|
|
143
|
-
// resting variant is: it is the one thing here that has to be
|
|
144
|
-
// legible without being read.
|
|
145
|
-
, {
|
|
146
|
-
// The current page is always filled, whatever the row's
|
|
147
|
-
// resting variant is: it is the one thing here that has to be
|
|
148
|
-
// legible without being read.
|
|
149
|
-
variant: slot === current ? 'solid' : variant, size: size, color: color, density: density, elevation: elevation, disabled: disabled, "aria-label": pageLabel(slot), "aria-current": slot === current ? 'page' : undefined, className: "tabular-nums", onClick: (event) => press(event, slot), ...linkProps(slot, disabled || slot === current), children: slot }) }, `slot-${index}`)) : (_jsx("li", { "aria-hidden": "true", className: [
|
|
150
|
-
'flex select-none items-center justify-center',
|
|
151
|
-
'text-(--plass-muted-fg)',
|
|
152
|
-
controlTextClasses[size],
|
|
153
|
-
ellipsisClasses[size]
|
|
154
|
-
].join(' '), children: "\u2026" }, `slot-${index}`))), showArrows
|
|
155
|
-
? stepper('next', nextLabel, current + 1, atEnd, '-rotate-90 rtl:rotate-90', _jsx(ChevronIcon, {}), 'next')
|
|
156
|
-
: null, showEdges
|
|
157
|
-
? stepper('last', lastLabel, count, atEnd, 'rtl:rotate-180', _jsx(DoubleChevronIcon, {}))
|
|
158
|
-
: null] }), _jsx("span", { className: srOnlyClasses, "aria-live": "polite", children: statusLabel(current, count) }), children] }));
|
|
159
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as t from"react";import{PlButton as n}from"../button/PlButton.js";import{ChevronIcon as r}from"../../internal/icons.js";import{controlTextClasses as l,gapClasses as i,srOnlyClasses as o}from"../../internal/styles.js";function range(e,a){return Array.from({length:Math.max(0,a-e+1)},(a,t)=>e+t)}function DoubleChevronIcon(){return e("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:e("path",{d:"m7.5 4.5 3.5 3.5-3.5 3.5M3.5 4.5 7 8l-3.5 3.5",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}const s={xs:"h-6 min-w-6",sm:"h-8 min-w-8",md:"h-10 min-w-10",lg:"h-12 min-w-12",xl:"h-14 min-w-14"};export const PlPagination=/* @__PURE__ */t.forwardRef(function PlPagination({variant:c="ghost",size:m="md",color:u="primary",density:h="compact",elevation:d=0,count:f,page:g,defaultPage:p=1,onPageChange:x,siblingCount:b=1,boundaryCount:v=1,showEdges:y=!1,showArrows:M=!0,disabled:P=!1,getPageHref:j,label:w="Pagination",pageLabel:N=e=>`Page ${e}`,previousLabel:C="Previous page",nextLabel:L="Next page",firstLabel:k="First page",lastLabel:$="Last page",statusLabel:D=(e,a)=>`Page ${e} of ${a}`,className:I,children:K,...z},B){const[A,R]=t.useState(p),E=Math.min(Math.max(g??A,1),Math.max(f,1));if(f<2)return null;const F=function paginationRange(e,a,t,n){const r=range(1,Math.min(n,e)),l=range(Math.max(e-n+1,n+1),e),i=Math.max(Math.min(a-t,e-n-2*t-1),n+2),o=Math.min(Math.max(a+t,n+2*t+2),l.length>0?l[0]-2:e-1);return[...r,...i>n+2?["start-ellipsis"]:n+1<e-n?[n+1]:[],...range(i,o),...o<e-n-1?["end-ellipsis"]:e-n>n?[e-n]:[],...l]}(f,E,b,v),H=E<=1,S=E>=f,linkProps=(a,t,n)=>j&&!t?{render:e("a",{href:j(a),rel:n})}:null,press=(e,a)=>{e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||j&&!x||(e.preventDefault(),(e=>{const a=Math.min(Math.max(e,1),f);a!==E&&(void 0===g&&R(a),x?.(a))})(a))},stepper=(a,t,r,l,i,o,s)=>e("li",{className:"flex",children:e(n,{variant:c,size:m,color:u,density:h,elevation:d,disabled:P||l,"aria-label":t,startIcon:e("span",{className:`flex items-center ${i}`,children:o}),onClick:e=>press(e,r),...linkProps(r,P||l,s)})},a);return a("nav",{ref:B,"aria-label":w,className:["flex items-center",I??""].filter(Boolean).join(" "),...z,children:[a("ul",{role:"list",className:`m-0 flex list-none items-center p-0 ${i[m]}`,children:[y?stepper("first",k,1,H,"rotate-180 rtl:rotate-0",e(DoubleChevronIcon,{})):null,M?stepper("previous",C,E-1,H,"rotate-90 rtl:-rotate-90",e(r,{}),"prev"):null,F.map((a,t)=>e("li","number"==typeof a?{className:"flex",children:e(n,{variant:a===E?"solid":c,size:m,color:u,density:h,elevation:d,disabled:P,"aria-label":N(a),"aria-current":a===E?"page":void 0,className:"tabular-nums",onClick:e=>press(e,a),...linkProps(a,P||a===E),children:a})}:{"aria-hidden":"true",className:["flex select-none items-center justify-center","text-(--plass-muted-fg)",l[m],s[m]].join(" "),children:"…"},`slot-${t}`)),M?stepper("next",L,E+1,S,"-rotate-90 rtl:rotate-90",e(r,{}),"next"):null,y?stepper("last",$,f,S,"rtl:rotate-180",e(DoubleChevronIcon,{})):null]}),e("span",{className:o,"aria-live":"polite",children:D(E,f)}),K]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlPagination}from"./PlPagination.js";
|