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,324 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { cx, transitionClasses } from '../../internal/styles.js';
|
|
4
|
-
const PaneContext = /* @__PURE__ */ React.createContext({ basis: null });
|
|
5
|
-
/**
|
|
6
|
-
* The width of a handle, and the width of the target the pointer has to hit.
|
|
7
|
-
*
|
|
8
|
-
* A visible line one pixel wide is a target one pixel wide, which is not a
|
|
9
|
-
* target. So the handle is a track several pixels across with the hairline drawn
|
|
10
|
-
* down the middle of it — the same split a scrollbar makes between what is drawn
|
|
11
|
-
* and what can be grabbed.
|
|
12
|
-
*/
|
|
13
|
-
const handleTrackClasses = {
|
|
14
|
-
xs: 'basis-1',
|
|
15
|
-
sm: 'basis-1.5',
|
|
16
|
-
md: 'basis-2',
|
|
17
|
-
lg: 'basis-2.5',
|
|
18
|
-
xl: 'basis-3'
|
|
19
|
-
};
|
|
20
|
-
/** The same numbers, as the total the panes have to give up to the handles. */
|
|
21
|
-
const handleTrackValues = {
|
|
22
|
-
xs: 4,
|
|
23
|
-
sm: 6,
|
|
24
|
-
md: 8,
|
|
25
|
-
lg: 10,
|
|
26
|
-
xl: 12
|
|
27
|
-
};
|
|
28
|
-
/** How far one arrow key press moves a handle. */
|
|
29
|
-
const KEYBOARD_STEP = 16;
|
|
30
|
-
/**
|
|
31
|
-
* A CSS length, in pixels.
|
|
32
|
-
*
|
|
33
|
-
* Only the four units a split is ever written in are accepted; anything else
|
|
34
|
-
* resolves to `undefined`, which every caller here reads as "no constraint"
|
|
35
|
-
* rather than as zero. A bad string should leave a pane unbounded, not pin it
|
|
36
|
-
* shut.
|
|
37
|
-
*/
|
|
38
|
-
function toPixels(value, extent, root) {
|
|
39
|
-
if (value === undefined || value === null)
|
|
40
|
-
return undefined;
|
|
41
|
-
if (typeof value === 'number')
|
|
42
|
-
return (extent * value) / 100;
|
|
43
|
-
const match = /^\s*(-?[\d.]+)\s*(px|rem|em|%)\s*$/.exec(value);
|
|
44
|
-
if (!match)
|
|
45
|
-
return undefined;
|
|
46
|
-
const amount = Number(match[1]);
|
|
47
|
-
if (Number.isNaN(amount))
|
|
48
|
-
return undefined;
|
|
49
|
-
switch (match[2]) {
|
|
50
|
-
case 'px':
|
|
51
|
-
return amount;
|
|
52
|
-
case '%':
|
|
53
|
-
return (extent * amount) / 100;
|
|
54
|
-
case 'rem':
|
|
55
|
-
return amount * parseFloat(getComputedStyle(document.documentElement).fontSize || '16');
|
|
56
|
-
case 'em':
|
|
57
|
-
return amount * parseFloat((root && getComputedStyle(root).fontSize) || '16');
|
|
58
|
-
default:
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/** Every pane's share of the space, summing to 1. */
|
|
63
|
-
function initialFractions(constraints, extent, root) {
|
|
64
|
-
const sizes = constraints.map((pane) => toPixels(pane.defaultSize, extent, root));
|
|
65
|
-
const named = sizes.reduce((total, size) => total + (size ?? 0), 0);
|
|
66
|
-
const unnamed = sizes.filter((size) => size === undefined).length;
|
|
67
|
-
// Whatever is left after the named panes, split evenly. Negative when the
|
|
68
|
-
// caller asked for more than there is, which the clamp below turns into zero.
|
|
69
|
-
const share = unnamed > 0 ? Math.max(0, extent - named) / unnamed : 0;
|
|
70
|
-
const resolved = sizes.map((size) => Math.max(0, size ?? share));
|
|
71
|
-
const total = resolved.reduce((sum, size) => sum + size, 0);
|
|
72
|
-
if (total <= 0)
|
|
73
|
-
return resolved.map(() => 1 / Math.max(1, resolved.length));
|
|
74
|
-
return resolved.map((size) => size / total);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* A set of panes with draggable handles between them.
|
|
78
|
-
*
|
|
79
|
-
* The panes are sized in **fractions**, written out as
|
|
80
|
-
* `flex-basis: calc((100% - gutters) * fraction)`. That is the one decision the
|
|
81
|
-
* rest of this file follows from: a split described in percentages survives the
|
|
82
|
-
* window being resized without a single line of JavaScript running, so the
|
|
83
|
-
* component measures itself only twice — once on mount, to turn a `'240px'`
|
|
84
|
-
* default into a fraction, and once at the start of each drag, to know what a
|
|
85
|
-
* pixel of pointer movement is worth.
|
|
86
|
-
*
|
|
87
|
-
* The handles are interleaved here rather than written by the caller, so the
|
|
88
|
-
* children of a `PlPanes` are just panes. That does mean the direct children have
|
|
89
|
-
* to *be* `PlPane`s: the constraints are read off their props, and a pane wrapped
|
|
90
|
-
* in something else is a pane with no minimum.
|
|
91
|
-
*/
|
|
92
|
-
export const PlPanes = /* @__PURE__ */ React.forwardRef(function PlPanes({ orientation = 'horizontal', resizable = true, color = 'primary', size = 'md', onResize, onResizeEnd, className, style, children, ...props }, ref) {
|
|
93
|
-
const items = React.Children.toArray(children).filter(React.isValidElement);
|
|
94
|
-
const count = items.length;
|
|
95
|
-
const rootRef = React.useRef(null);
|
|
96
|
-
const setRootRef = React.useCallback((node) => {
|
|
97
|
-
rootRef.current = node;
|
|
98
|
-
if (typeof ref === 'function')
|
|
99
|
-
ref(node);
|
|
100
|
-
else if (ref)
|
|
101
|
-
ref.current = node;
|
|
102
|
-
}, [ref]);
|
|
103
|
-
// The constraints are read during render and used inside pointer handlers that
|
|
104
|
-
// outlive it, so they go through a ref rather than through the closure.
|
|
105
|
-
const constraintsRef = React.useRef([]);
|
|
106
|
-
constraintsRef.current = items.map((item) => item.props);
|
|
107
|
-
const [stored, setFractions] = React.useState(null);
|
|
108
|
-
// A pane added or removed leaves the stored split a render behind the children
|
|
109
|
-
// — the effect below re-splits, but the render in between would be reading a
|
|
110
|
-
// share off the end of the list. Until the two agree, nobody has a size and
|
|
111
|
-
// every pane falls back to an even share.
|
|
112
|
-
const fractions = stored && stored.length === count ? stored : null;
|
|
113
|
-
const fractionsRef = React.useRef(null);
|
|
114
|
-
fractionsRef.current = fractions;
|
|
115
|
-
const horizontal = orientation === 'horizontal';
|
|
116
|
-
const gutter = handleTrackValues[size] * Math.max(0, count - 1);
|
|
117
|
-
/*
|
|
118
|
-
* One measurement, for one purpose: turning a `defaultSize` written as a
|
|
119
|
-
* length into a fraction. It is an observer rather than a single read because
|
|
120
|
-
* a split inside a closed `PlAccordion` or an unselected `PlTab` is zero wide when
|
|
121
|
-
* it mounts, and dividing by that would put every pane at nothing.
|
|
122
|
-
*/
|
|
123
|
-
React.useEffect(() => {
|
|
124
|
-
const root = rootRef.current;
|
|
125
|
-
if (!root)
|
|
126
|
-
return;
|
|
127
|
-
const measure = () => {
|
|
128
|
-
const rect = root.getBoundingClientRect();
|
|
129
|
-
const extent = (horizontal ? rect.width : rect.height) - gutter;
|
|
130
|
-
if (extent <= 0)
|
|
131
|
-
return;
|
|
132
|
-
setFractions((previous) => previous && previous.length === count
|
|
133
|
-
? previous
|
|
134
|
-
: initialFractions(constraintsRef.current, extent, root));
|
|
135
|
-
};
|
|
136
|
-
measure();
|
|
137
|
-
const observer = new ResizeObserver(measure);
|
|
138
|
-
observer.observe(root);
|
|
139
|
-
return () => observer.disconnect();
|
|
140
|
-
}, [count, horizontal, gutter]);
|
|
141
|
-
/**
|
|
142
|
-
* Everything a drag needs to know, measured at the moment it starts.
|
|
143
|
-
*
|
|
144
|
-
* A drag only ever moves the boundary between two panes, so their total is
|
|
145
|
-
* fixed and one pane's floor is the other's ceiling. Folding all four bounds
|
|
146
|
-
* into a single range on the first of the pair is what keeps every move to one
|
|
147
|
-
* clamp and one division.
|
|
148
|
-
*/
|
|
149
|
-
function grip(index) {
|
|
150
|
-
const root = rootRef.current;
|
|
151
|
-
const current = fractionsRef.current;
|
|
152
|
-
if (!resizable || !root || !current || current[index + 1] === undefined)
|
|
153
|
-
return null;
|
|
154
|
-
const rect = root.getBoundingClientRect();
|
|
155
|
-
const extent = (horizontal ? rect.width : rect.height) - gutter;
|
|
156
|
-
if (extent <= 0)
|
|
157
|
-
return null;
|
|
158
|
-
const before = constraintsRef.current[index];
|
|
159
|
-
const after = constraintsRef.current[index + 1];
|
|
160
|
-
const start = current[index] * extent;
|
|
161
|
-
const pair = start + current[index + 1] * extent;
|
|
162
|
-
const lower = Math.max(toPixels(before?.minSize, extent, root) ?? 0, pair - (toPixels(after?.maxSize, extent, root) ?? pair));
|
|
163
|
-
const upper = Math.min(toPixels(before?.maxSize, extent, root) ?? pair, pair - (toPixels(after?.minSize, extent, root) ?? 0));
|
|
164
|
-
if (upper < lower)
|
|
165
|
-
return null;
|
|
166
|
-
return {
|
|
167
|
-
root,
|
|
168
|
-
current,
|
|
169
|
-
extent,
|
|
170
|
-
start,
|
|
171
|
-
pair,
|
|
172
|
-
resize(delta) {
|
|
173
|
-
const sized = Math.min(upper, Math.max(lower, start + delta));
|
|
174
|
-
const next = [...current];
|
|
175
|
-
next[index] = sized / extent;
|
|
176
|
-
next[index + 1] = (pair - sized) / extent;
|
|
177
|
-
setFractions(next);
|
|
178
|
-
onResize?.(next.map((fraction) => fraction * 100));
|
|
179
|
-
return next;
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* How to take a drag in flight apart, held for as long as one is running.
|
|
185
|
-
*
|
|
186
|
-
* A drag is torn down by the `pointerup` that ends it, and that event never
|
|
187
|
-
* arrives if the split goes away first — a route change, a closed accordion, a
|
|
188
|
-
* pane list that shrank. What is left behind is not only two listeners on a
|
|
189
|
-
* detached node: the drag takes the whole document's text selection away while
|
|
190
|
-
* it runs, and nothing else ever puts it back.
|
|
191
|
-
*/
|
|
192
|
-
const teardownRef = React.useRef(null);
|
|
193
|
-
React.useEffect(() => () => teardownRef.current?.(), []);
|
|
194
|
-
function beginDrag(index, event) {
|
|
195
|
-
const held = grip(index);
|
|
196
|
-
if (!held)
|
|
197
|
-
return;
|
|
198
|
-
const handle = event.currentTarget;
|
|
199
|
-
handle.setPointerCapture(event.pointerId);
|
|
200
|
-
handle.dataset.dragging = 'true';
|
|
201
|
-
/*
|
|
202
|
-
* A drag across a page selects the text it passes over, and the obvious cure
|
|
203
|
-
* — `preventDefault` on the press — also stops the browser focusing the
|
|
204
|
-
* handle, which leaves the component focusing it by hand and every mouse
|
|
205
|
-
* press wearing a keyboard focus ring. Taking the selection off the document
|
|
206
|
-
* for the length of the drag fixes the selection without touching the focus.
|
|
207
|
-
*
|
|
208
|
-
* The property is written prefixed and through `setProperty`, because WebKit
|
|
209
|
-
* implements only `-webkit-user-select`: it has no `userSelect` on a style
|
|
210
|
-
* declaration, so `style.userSelect = 'none'` hangs a plain JS property off
|
|
211
|
-
* the object, changes nothing, and Safari selects text through the whole
|
|
212
|
-
* drag. Chromium and Firefox both read the prefixed name as the standard
|
|
213
|
-
* one. This is what Tailwind's own `select-none` emits, for the same reason.
|
|
214
|
-
*/
|
|
215
|
-
const selection = document.body.style.getPropertyValue('-webkit-user-select');
|
|
216
|
-
document.body.style.setProperty('-webkit-user-select', 'none');
|
|
217
|
-
const origin = horizontal ? event.clientX : event.clientY;
|
|
218
|
-
// Positive is always "toward the end", so a drag under RTL moves the
|
|
219
|
-
// boundary the way the pointer went rather than the way the axis is numbered.
|
|
220
|
-
const towardsEnd = horizontal && getComputedStyle(held.root).direction === 'rtl' ? -1 : 1;
|
|
221
|
-
let latest = held.current;
|
|
222
|
-
const move = (moveEvent) => {
|
|
223
|
-
const position = horizontal ? moveEvent.clientX : moveEvent.clientY;
|
|
224
|
-
latest = held.resize((position - origin) * towardsEnd);
|
|
225
|
-
};
|
|
226
|
-
// Everything the drag took from outside itself, given back. Split from `end`
|
|
227
|
-
// because unmounting has to run this half and must not run the other: a
|
|
228
|
-
// component that disappeared did not finish resizing, and telling a caller it
|
|
229
|
-
// did would set state on the way out of the tree.
|
|
230
|
-
const release = () => {
|
|
231
|
-
teardownRef.current = null;
|
|
232
|
-
handle.removeEventListener('pointermove', move);
|
|
233
|
-
handle.removeEventListener('pointerup', end);
|
|
234
|
-
handle.removeEventListener('pointercancel', end);
|
|
235
|
-
delete handle.dataset.dragging;
|
|
236
|
-
// Removed rather than set back to '', so a page that never wrote the
|
|
237
|
-
// property inline is left with the declaration it actually had.
|
|
238
|
-
if (selection)
|
|
239
|
-
document.body.style.setProperty('-webkit-user-select', selection);
|
|
240
|
-
else
|
|
241
|
-
document.body.style.removeProperty('-webkit-user-select');
|
|
242
|
-
};
|
|
243
|
-
const end = () => {
|
|
244
|
-
release();
|
|
245
|
-
onResizeEnd?.(latest.map((fraction) => fraction * 100));
|
|
246
|
-
};
|
|
247
|
-
teardownRef.current = release;
|
|
248
|
-
handle.addEventListener('pointermove', move);
|
|
249
|
-
handle.addEventListener('pointerup', end);
|
|
250
|
-
handle.addEventListener('pointercancel', end);
|
|
251
|
-
}
|
|
252
|
-
function nudge(index, pixels) {
|
|
253
|
-
const held = grip(index);
|
|
254
|
-
if (!held)
|
|
255
|
-
return;
|
|
256
|
-
const next = held.resize(pixels);
|
|
257
|
-
// A key press is a whole gesture on its own — there is no "let go" to wait
|
|
258
|
-
// for, so the settled callback fires with it.
|
|
259
|
-
onResizeEnd?.(next.map((fraction) => fraction * 100));
|
|
260
|
-
}
|
|
261
|
-
const handleClassNames = cx('group/handle relative z-1 flex shrink-0 grow-0 items-center justify-center', handleTrackClasses[size], transitionClasses, '[outline:none] focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:outline-offset-0', resizable
|
|
262
|
-
? cx(horizontal ? 'cursor-col-resize' : 'cursor-row-resize', 'hover:bg-(--p-soft) data-[dragging]:bg-(--p-soft)')
|
|
263
|
-
: '');
|
|
264
|
-
return (_jsx("div", { ref: setRootRef, className: cx('flex h-full w-full', horizontal ? 'flex-row' : 'flex-col',
|
|
265
|
-
// A flex item refuses to go below its content's intrinsic size unless it
|
|
266
|
-
// is told to, which is what turns a pane holding a long line into a pane
|
|
267
|
-
// that cannot be dragged narrower.
|
|
268
|
-
'min-h-0 min-w-0', className),
|
|
269
|
-
// Three slots rather than the whole `surfaceSlots` set: a split draws no
|
|
270
|
-
// sheet, so the family only ever shows up in the handle's hairline, the
|
|
271
|
-
// tint under a hovered handle and the focus ring.
|
|
272
|
-
style: {
|
|
273
|
-
'--p-accent': `var(--plass-${color}-accent)`,
|
|
274
|
-
'--p-soft': `var(--plass-${color}-soft)`,
|
|
275
|
-
'--p-ring': `var(--plass-${color}-ring)`,
|
|
276
|
-
...style
|
|
277
|
-
}, ...props, children: items.map((item, index) => (_jsxs(React.Fragment, { children: [index > 0 ? (_jsx("div", { role: "separator", "aria-orientation": horizontal ? 'vertical' : 'horizontal', "aria-valuenow": fractions ? Math.round(fractions[index - 1] * 100) : undefined, "aria-valuemin": 0, "aria-valuemax": 100, "aria-disabled": !resizable || undefined, tabIndex: resizable ? 0 : -1, className: handleClassNames,
|
|
278
|
-
// No `preventDefault` and no explicit focus: the browser focuses
|
|
279
|
-
// the handle on a press by itself, and it knows that a press is
|
|
280
|
-
// not a keystroke — which is what keeps the focus ring off a
|
|
281
|
-
// handle somebody merely dragged. `beginDrag` takes the page's
|
|
282
|
-
// text selection away for the length of the drag instead.
|
|
283
|
-
onPointerDown: (event) => {
|
|
284
|
-
if (event.button !== 0)
|
|
285
|
-
return;
|
|
286
|
-
beginDrag(index - 1, event);
|
|
287
|
-
}, onKeyDown: (event) => {
|
|
288
|
-
const back = horizontal ? 'ArrowLeft' : 'ArrowUp';
|
|
289
|
-
const forward = horizontal ? 'ArrowRight' : 'ArrowDown';
|
|
290
|
-
if (event.key !== back && event.key !== forward)
|
|
291
|
-
return;
|
|
292
|
-
event.preventDefault();
|
|
293
|
-
nudge(index - 1, event.key === forward ? KEYBOARD_STEP : -KEYBOARD_STEP);
|
|
294
|
-
}, children: _jsx("span", { "aria-hidden": "true", className: cx('pointer-events-none bg-(--plass-border)', transitionClasses, horizontal ? 'h-full w-px' : 'h-px w-full', resizable
|
|
295
|
-
? 'group-hover/handle:bg-(--p-accent) group-focus-visible/handle:bg-(--p-accent) group-data-[dragging]/handle:bg-(--p-accent)'
|
|
296
|
-
: '') }) })) : null, _jsx(PaneContext.Provider, { value: {
|
|
297
|
-
basis: fractions
|
|
298
|
-
? `calc((100% - ${gutter}px) * ${fractions[index].toFixed(6)})`
|
|
299
|
-
: null
|
|
300
|
-
}, children: item })] }, item.key ?? index))) }));
|
|
301
|
-
});
|
|
302
|
-
/**
|
|
303
|
-
* One region of a split.
|
|
304
|
-
*
|
|
305
|
-
* It carries no surface of its own on purpose: a split is layout, and the moment
|
|
306
|
-
* a pane drew a sheet it would stop being usable as the thing a `PlCard`, a
|
|
307
|
-
* `PlTable` or an editor is put inside. Put a `PlCard` in it when a surface is
|
|
308
|
-
* wanted.
|
|
309
|
-
*
|
|
310
|
-
* `defaultSize`, `minSize` and `maxSize` are read by the `PlPanes` around it
|
|
311
|
-
* rather than used here — a pane cannot know what "half" is, only the split can.
|
|
312
|
-
*/
|
|
313
|
-
export const PlPane = /* @__PURE__ */ React.forwardRef(function PlPane(
|
|
314
|
-
// The three sizing props are named here only so they are taken out of the
|
|
315
|
-
// rest, which is spread onto a `<div>` — `defaultSize` on a div is an
|
|
316
|
-
// attribute React does not know and would hand straight to the DOM.
|
|
317
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
318
|
-
{ defaultSize, minSize, maxSize, className, style, children, ...props }, ref) {
|
|
319
|
-
const { basis } = React.useContext(PaneContext);
|
|
320
|
-
return (_jsx("div", { ref: ref, className: cx('relative min-h-0 min-w-0 overflow-auto', className),
|
|
321
|
-
// `1 1 0%` before the split has measured itself, so a pane renders at an
|
|
322
|
-
// even share on the first paint instead of at nothing and then jumping.
|
|
323
|
-
style: { flex: basis ? `0 0 ${basis}` : '1 1 0%', ...style }, ...props, children: children }));
|
|
324
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{cx as r,transitionClasses as i}from"../../internal/styles.js";const s=/* @__PURE__ */n.createContext({basis:null}),o={xs:"basis-1",sm:"basis-1.5",md:"basis-2",lg:"basis-2.5",xl:"basis-3"},a={xs:4,sm:6,md:8,lg:10,xl:12};function toPixels(e,t,n){if(null==e)return;if("number"==typeof e)return t*e/100;const r=/^\s*(-?[\d.]+)\s*(px|rem|em|%)\s*$/.exec(e);if(!r)return;const i=Number(r[1]);if(!Number.isNaN(i))switch(r[2]){case"px":return i;case"%":return t*i/100;case"rem":return i*parseFloat(getComputedStyle(document.documentElement).fontSize||"16");case"em":return i*parseFloat(n&&getComputedStyle(n).fontSize||"16");default:return}}export const PlPanes=/* @__PURE__ */n.forwardRef(function PlPanes({orientation:l="horizontal",resizable:c=!0,color:u="primary",size:d="md",onResize:m,onResizeEnd:p,className:f,style:g,children:h,...x},v){const b=n.Children.toArray(h).filter(n.isValidElement),y=b.length,w=n.useRef(null),z=n.useCallback(e=>{w.current=e,"function"==typeof v?v(e):v&&(v.current=e)},[v]),P=n.useRef([]);P.current=b.map(e=>e.props);const[S,R]=n.useState(null),E=S&&S.length===y?S:null,k=n.useRef(null);k.current=E;const C="horizontal"===l,N=a[d]*Math.max(0,y-1);function grip(e){const t=w.current,n=k.current;if(!c||!t||!n||void 0===n[e+1])return null;const r=t.getBoundingClientRect(),i=(C?r.width:r.height)-N;if(i<=0)return null;const s=P.current[e],o=P.current[e+1],a=n[e]*i,l=a+n[e+1]*i,u=Math.max(toPixels(s?.minSize,i,t)??0,l-(toPixels(o?.maxSize,i,t)??l)),d=Math.min(toPixels(s?.maxSize,i,t)??l,l-(toPixels(o?.minSize,i,t)??0));return d<u?null:{root:t,current:n,extent:i,start:a,pair:l,resize(t){const r=Math.min(d,Math.max(u,a+t)),s=[...n];return s[e]=r/i,s[e+1]=(l-r)/i,R(s),m?.(s.map(e=>100*e)),s}}}n.useEffect(()=>{const e=w.current;if(!e)return;const measure=()=>{const t=e.getBoundingClientRect(),n=(C?t.width:t.height)-N;n<=0||R(t=>t&&t.length===y?t:function initialFractions(e,t,n){const r=e.map(e=>toPixels(e.defaultSize,t,n)),i=r.reduce((e,t)=>e+(t??0),0),s=r.filter(e=>void 0===e).length,o=s>0?Math.max(0,t-i)/s:0,a=r.map(e=>Math.max(0,e??o)),l=a.reduce((e,t)=>e+t,0);return l<=0?a.map(()=>1/Math.max(1,a.length)):a.map(e=>e/l)}(P.current,n,e))};measure();const t=new ResizeObserver(measure);return t.observe(e),()=>t.disconnect()},[y,C,N]);const M=n.useRef(null);n.useEffect(()=>()=>M.current?.(),[]);const L=r("group/handle relative z-1 flex shrink-0 grow-0 items-center justify-center",o[d],i,"[outline:none] focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:outline-offset-0",c?r(C?"cursor-col-resize":"cursor-row-resize","hover:bg-(--p-soft) data-[dragging]:bg-(--p-soft)"):"");return e("div",{ref:z,className:r("flex h-full w-full",C?"flex-row":"flex-col","min-h-0 min-w-0",f),style:{"--p-accent":`var(--plass-${u}-accent)`,"--p-soft":`var(--plass-${u}-soft)`,"--p-ring":`var(--plass-${u}-ring)`,...g},...x,children:b.map((o,a)=>t(n.Fragment,{children:[a>0?e("div",{role:"separator","aria-orientation":C?"vertical":"horizontal","aria-valuenow":E?Math.round(100*E[a-1]):void 0,"aria-valuemin":0,"aria-valuemax":100,"aria-disabled":!c||void 0,tabIndex:c?0:-1,className:L,onPointerDown:e=>{0===e.button&&function beginDrag(e,t){const n=grip(e);if(!n)return;const r=t.currentTarget;r.setPointerCapture(t.pointerId),r.dataset.dragging="true";const i=document.body.style.getPropertyValue("-webkit-user-select");document.body.style.setProperty("-webkit-user-select","none");const s=C?t.clientX:t.clientY,o=C&&"rtl"===getComputedStyle(n.root).direction?-1:1;let a=n.current;const move=e=>{const t=C?e.clientX:e.clientY;a=n.resize((t-s)*o)},release=()=>{M.current=null,r.removeEventListener("pointermove",move),r.removeEventListener("pointerup",end),r.removeEventListener("pointercancel",end),delete r.dataset.dragging,i?document.body.style.setProperty("-webkit-user-select",i):document.body.style.removeProperty("-webkit-user-select")},end=()=>{release(),p?.(a.map(e=>100*e))};M.current=release,r.addEventListener("pointermove",move),r.addEventListener("pointerup",end),r.addEventListener("pointercancel",end)}(a-1,e)},onKeyDown:e=>{const t=C?"ArrowLeft":"ArrowUp",n=C?"ArrowRight":"ArrowDown";e.key!==t&&e.key!==n||(e.preventDefault(),function nudge(e,t){const n=grip(e);if(!n)return;const r=n.resize(t);p?.(r.map(e=>100*e))}(a-1,e.key===n?16:-16))},children:e("span",{"aria-hidden":"true",className:r("pointer-events-none bg-(--plass-border)",i,C?"h-full w-px":"h-px w-full",c?"group-hover/handle:bg-(--p-accent) group-focus-visible/handle:bg-(--p-accent) group-data-[dragging]/handle:bg-(--p-accent)":"")})}):null,e(s.Provider,{value:{basis:E?`calc((100% - ${N}px) * ${E[a].toFixed(6)})`:null},children:o})]},o.key??a))})});export const PlPane=/* @__PURE__ */n.forwardRef(function PlPane({defaultSize:t,minSize:i,maxSize:o,className:a,style:l,children:c,...u},d){const{basis:m}=n.useContext(s);return e("div",{ref:d,className:r("relative min-h-0 min-w-0 overflow-auto",a),style:{flex:m?`0 0 ${m}`:"1 1 0%",...l},...u,children:c})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlPane,PlPanes}from"./PlPanes.js";
|
|
@@ -1,154 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { controlSlots, controlTextClasses, cx, focusRingClasses, gapClasses, glassClasses, hasContent, iconClasses, iconSizeClasses, metaTextClasses, paddingXClasses, sheetBodyClasses, transitionClasses } from '../../internal/styles.js';
|
|
4
|
-
/**
|
|
5
|
-
* The three materials, said the way a *control* says them — the surface takes
|
|
6
|
-
* the tint, as on a `PlButton` and a `PlChip`, because a pill is the thing being
|
|
7
|
-
* coloured rather than a sheet holding somebody else's content.
|
|
8
|
-
*/
|
|
9
|
-
const restClasses = {
|
|
10
|
-
solid: 'text-(--p-on-solid) [background-image:var(--p-fill)] [box-shadow:var(--p-elev),var(--p-lift)]',
|
|
11
|
-
glass: /* @__PURE__ */ [
|
|
12
|
-
glassClasses,
|
|
13
|
-
'border text-(--p-accent) bg-(--plass-glass)',
|
|
14
|
-
'[border-color:var(--plass-glass-line)]',
|
|
15
|
-
'[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
|
|
16
|
-
].join(' '),
|
|
17
|
-
ghost: `${glassClasses} text-(--p-accent) bg-(--p-soft)`
|
|
18
|
-
};
|
|
19
|
-
const hoverClasses = {
|
|
20
|
-
solid: 'hover:brightness-105 active:brightness-95',
|
|
21
|
-
glass: 'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)] active:bg-(--plass-glass-press)',
|
|
22
|
-
ghost: 'hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)'
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* The row's floor, as a minimum rather than as a height.
|
|
26
|
-
*
|
|
27
|
-
* The numbers are `controlHeightClasses`' — a collapsed pill lines up with a
|
|
28
|
-
* `PlButton` of the same `size` beside it — but a pill carrying a description is
|
|
29
|
-
* two lines tall and a fixed height would clip the second.
|
|
30
|
-
*/
|
|
31
|
-
const rowMinHeightClasses = {
|
|
32
|
-
xs: 'min-h-5.5',
|
|
33
|
-
sm: 'min-h-6.5',
|
|
34
|
-
md: 'min-h-8',
|
|
35
|
-
lg: 'min-h-10',
|
|
36
|
-
xl: 'min-h-12'
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Exactly half the row's minimum height at every step — 22, 26, 32, 40 and 48px
|
|
40
|
-
* — so a collapsed pill is a true stadium.
|
|
41
|
-
*
|
|
42
|
-
* Written as a length rather than as `rounded-full`, and the difference only
|
|
43
|
-
* shows once the pill grows: `rounded-full` on a box that has taken a second
|
|
44
|
-
* line, or opened its `details`, is a corner half its new height, and a corner
|
|
45
|
-
* that big eats the first two words of every line. Pinning the radius to the
|
|
46
|
-
* *row* is what lets the lozenge grow into a rounded rectangle with the same
|
|
47
|
-
* corner it always had.
|
|
48
|
-
*/
|
|
49
|
-
const pillRadiusClasses = {
|
|
50
|
-
xs: 'rounded-[0.6875rem]',
|
|
51
|
-
sm: 'rounded-[0.8125rem]',
|
|
52
|
-
md: 'rounded-[1rem]',
|
|
53
|
-
lg: 'rounded-[1.25rem]',
|
|
54
|
-
xl: 'rounded-[1.5rem]'
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* The air either side of the middle, and the thing that makes this shape read as
|
|
58
|
-
* the lozenge it is rather than as a wide `PlChip`.
|
|
59
|
-
*
|
|
60
|
-
* Roughly double the control padding at every step. The leading glyph and the
|
|
61
|
-
* trailing slot are the pill's furniture; what it is *about* is the column
|
|
62
|
-
* between them, and giving that column noticeably more room than either
|
|
63
|
-
* neighbour is what puts the eye there first. `density` halves it, exactly as it
|
|
64
|
-
* does everywhere else.
|
|
65
|
-
*/
|
|
66
|
-
const centerPaddingClasses = {
|
|
67
|
-
default: { xs: 'px-3', sm: 'px-4', md: 'px-5', lg: 'px-6', xl: 'px-8' },
|
|
68
|
-
compact: { xs: 'px-1.5', sm: 'px-2', md: 'px-2.5', lg: 'px-3', xl: 'px-4' }
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* The leading box: a square the size of a standalone glyph, clipped round.
|
|
72
|
-
*
|
|
73
|
-
* `iconSizeClasses` rather than a ladder of its own, because that table already
|
|
74
|
-
* answers the same question — how big is a glyph that is not riding on a label —
|
|
75
|
-
* and the leading slot of a pill is exactly that.
|
|
76
|
-
*/
|
|
77
|
-
const mediaClasses = 'flex shrink-0 items-center justify-center overflow-hidden rounded-full [&_img]:size-full [&_img]:object-cover';
|
|
78
|
-
/**
|
|
79
|
-
* The description under the title.
|
|
80
|
-
*
|
|
81
|
-
* Mixed toward transparent rather than pointed at `--plass-muted-fg`: the middle
|
|
82
|
-
* of a pill sits on the colour family's own fill as often as on a bare surface,
|
|
83
|
-
* and a fixed grey that reads as secondary on white reads as dirt on `primary`.
|
|
84
|
-
* Taking the ink that is already there and letting some of the surface through
|
|
85
|
-
* is the one form of "one step quieter" that holds on all three materials.
|
|
86
|
-
*/
|
|
87
|
-
const descriptionClasses = '[color:color-mix(in_oklab,currentColor_72%,transparent)]';
|
|
88
|
-
/** Where a pinned pill hangs, and how far in from the edge. */
|
|
89
|
-
const positionClasses = {
|
|
90
|
-
static: { top: '', bottom: '' },
|
|
91
|
-
sticky: { top: 'sticky top-3 z-20', bottom: 'sticky bottom-3 z-20' },
|
|
92
|
-
fixed: {
|
|
93
|
-
// Centred by stretching the box across the viewport and letting `mx-auto`
|
|
94
|
-
// shrink it back, not by translating it half its own width. The house rule
|
|
95
|
-
// against transforming a surface holds here too, and `auto` margins are
|
|
96
|
-
// direction-agnostic, so the lozenge stays centred under RTL.
|
|
97
|
-
top: 'fixed inset-x-0 top-3 z-30 mx-auto w-fit',
|
|
98
|
-
bottom: 'fixed inset-x-0 bottom-3 z-30 mx-auto w-fit'
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* A floating lozenge holding a small amount of live information.
|
|
103
|
-
*
|
|
104
|
-
* The shape is a **stadium**, which the house radius rule otherwise forbids:
|
|
105
|
-
* every control is held just short of the 50% that would make it a pill, because
|
|
106
|
-
* the flat run along its top and bottom edge is what still reads as a sheet with
|
|
107
|
-
* the corners cut off it. This is the exception the rule is drawn against, and
|
|
108
|
-
* it works for the same reason the rule does — this is not a sheet lying on the
|
|
109
|
-
* page. It is an object hovering over one, and an object hovering over the page
|
|
110
|
-
* should not look as though it was cut from the same material.
|
|
111
|
-
*
|
|
112
|
-
* `details` is revealed by animating a measured height, exactly as a
|
|
113
|
-
* `PlAccordion` panel is: nothing is transformed and no text is resampled, the
|
|
114
|
-
* pill is simply a window that opens. The measurement is a `ResizeObserver`
|
|
115
|
-
* rather than a hardcoded height, so a details area whose content changes —
|
|
116
|
-
* which is what live information does — grows with it.
|
|
117
|
-
*/
|
|
118
|
-
export const PlPill = /* @__PURE__ */ React.forwardRef(function PlPill({ variant = 'solid', size = 'md', color = 'secondary', density = 'default', elevation = 2, startIcon, endIcon, title, description, details, expanded = false, position = 'static', side = 'top', className, style, children, onClick, ...props }, ref) {
|
|
119
|
-
const detailsRef = React.useRef(null);
|
|
120
|
-
const [detailsHeight, setDetailsHeight] = React.useState(0);
|
|
121
|
-
React.useEffect(() => {
|
|
122
|
-
const element = detailsRef.current;
|
|
123
|
-
if (!element || typeof ResizeObserver === 'undefined') {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const observer = new ResizeObserver(() => setDetailsHeight(element.scrollHeight));
|
|
127
|
-
observer.observe(element);
|
|
128
|
-
setDetailsHeight(element.scrollHeight);
|
|
129
|
-
return () => observer.disconnect();
|
|
130
|
-
}, [details]);
|
|
131
|
-
const interactive = Boolean(onClick);
|
|
132
|
-
const padX = paddingXClasses[density][size];
|
|
133
|
-
const row = (_jsxs(_Fragment, { children: [hasContent(startIcon) ? (_jsx("span", { className: cx(mediaClasses, iconSizeClasses[size]), children: startIcon })) : null, hasContent(title) || hasContent(description) || hasContent(children) ? (_jsxs("span", { className: cx('flex min-w-0 flex-1 flex-col items-center justify-center text-center', centerPaddingClasses[density][size]), children: [hasContent(title) ? _jsx("span", { className: "max-w-full truncate", children: title }) : null, hasContent(description) ? (_jsx("span", { className: cx('max-w-full truncate font-normal', metaTextClasses[size], descriptionClasses), children: description })) : null, children] })) : null] }));
|
|
134
|
-
return (_jsxs("div", { ref: ref, className: cx('inline-flex max-w-full flex-col overflow-hidden align-middle', pillRadiusClasses[size], 'font-medium whitespace-nowrap select-none', controlTextClasses[size], restClasses[variant], transitionClasses, iconClasses, interactive ? `plass-glow ${hoverClasses[variant]}` : '', positionClasses[position][side], className), style: { ...controlSlots(color, elevation, variant), ...style }, ...props, children: [_jsxs("div", { className: cx(
|
|
135
|
-
// `min-h` rather than a fixed height: one line keeps the stadium the
|
|
136
|
-
// radius ladder is cut for, and a title with a description under it
|
|
137
|
-
// grows into a rounded rectangle instead of being clipped. `py-1`
|
|
138
|
-
// costs nothing in the one-line case — the minimum is taller than
|
|
139
|
-
// the line plus the padding — and is what keeps two lines off the
|
|
140
|
-
// edges.
|
|
141
|
-
'flex shrink-0 items-center py-1', rowMinHeightClasses[size], gapClasses[size],
|
|
142
|
-
// With a pressable middle the padding belongs to the button, so its
|
|
143
|
-
// hit area covers the whole row rather than just the words.
|
|
144
|
-
interactive ? 'ps-0' : padX, hasContent(endIcon) ? 'pe-1' : interactive ? 'pe-0' : ''), children: [interactive ? (_jsx("button", { type: "button", className: cx('flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch',
|
|
145
|
-
// `inherit`, so the focus ring traces the lozenge's own corners
|
|
146
|
-
// rather than drawing a second, squarer rectangle inside them.
|
|
147
|
-
'rounded-[inherit]', gapClasses[size], padX, focusRingClasses), onClick: onClick, children: row })) : (row), hasContent(endIcon) ? (_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: endIcon })) : null] }), hasContent(details) ? (_jsx("div", { className: cx('overflow-hidden', '[transition:height_var(--plass-duration-slow)_var(--plass-ease)]', 'motion-reduce:[transition-duration:0ms]'), style: { height: expanded ? detailsHeight : 0 },
|
|
148
|
-
// `inert` rather than `aria-hidden`: a collapsed panel is a
|
|
149
|
-
// zero-height box that its content is still perfectly focusable
|
|
150
|
-
// inside, and `aria-hidden` alone would leave a keyboard reader
|
|
151
|
-
// tabbing into something their screen reader has been told does not
|
|
152
|
-
// exist.
|
|
153
|
-
inert: !expanded, children: _jsx("div", { ref: detailsRef, className: cx('whitespace-normal pb-2', padX, sheetBodyClasses[size]), children: details }) })) : null] }));
|
|
154
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as s,Fragment as t}from"react/jsx-runtime";import*as l from"react";import{controlSlots as n,controlTextClasses as r,cx as o,focusRingClasses as i,gapClasses as a,glassClasses as c,hasContent as d,iconClasses as m,iconSizeClasses as p,metaTextClasses as f,paddingXClasses as x,sheetBodyClasses as u,transitionClasses as h}from"../../internal/styles.js";const g={solid:"text-(--p-on-solid) [background-image:var(--p-fill)] [box-shadow:var(--p-elev),var(--p-lift)]",glass:/* @__PURE__ */[c,"border text-(--p-accent) bg-(--plass-glass)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:`${c} text-(--p-accent) bg-(--p-soft)`},v={solid:"hover:brightness-105 active:brightness-95",glass:"hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)] active:bg-(--plass-glass-press)",ghost:"hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)"},b={xs:"min-h-5.5",sm:"min-h-6.5",md:"min-h-8",lg:"min-h-10",xl:"min-h-12"},w={xs:"rounded-[0.6875rem]",sm:"rounded-[0.8125rem]",md:"rounded-[1rem]",lg:"rounded-[1.25rem]",xl:"rounded-[1.5rem]"},y={default:{xs:"px-3",sm:"px-4",md:"px-5",lg:"px-6",xl:"px-8"},compact:{xs:"px-1.5",sm:"px-2",md:"px-2.5",lg:"px-3",xl:"px-4"}},N={static:{top:"",bottom:""},sticky:{top:"sticky top-3 z-20",bottom:"sticky bottom-3 z-20"},fixed:{top:"fixed inset-x-0 top-3 z-30 mx-auto w-fit",bottom:"fixed inset-x-0 bottom-3 z-30 mx-auto w-fit"}};export const PlPill=/* @__PURE__ */l.forwardRef(function PlPill({variant:c="solid",size:k="md",color:j="secondary",density:z="default",elevation:_=2,startIcon:P,endIcon:R,title:C,description:H,details:I,expanded:O=!1,position:$="static",side:B="top",className:E,style:F,children:S,onClick:q,...A},D){const G=l.useRef(null),[J,K]=l.useState(0);l.useEffect(()=>{const e=G.current;if(!e||"undefined"==typeof ResizeObserver)return;const s=new ResizeObserver(()=>K(e.scrollHeight));return s.observe(e),K(e.scrollHeight),()=>s.disconnect()},[I]);const L=Boolean(q),M=x[z][k],Q=s(t,{children:[d(P)?e("span",{className:o("flex shrink-0 items-center justify-center overflow-hidden rounded-full [&_img]:size-full [&_img]:object-cover",p[k]),children:P}):null,d(C)||d(H)||d(S)?s("span",{className:o("flex min-w-0 flex-1 flex-col items-center justify-center text-center",y[z][k]),children:[d(C)?e("span",{className:"max-w-full truncate",children:C}):null,d(H)?e("span",{className:o("max-w-full truncate font-normal",f[k],"[color:color-mix(in_oklab,currentColor_72%,transparent)]"),children:H}):null,S]}):null]});return s("div",{ref:D,className:o("inline-flex max-w-full flex-col overflow-hidden align-middle",w[k],"font-medium whitespace-nowrap select-none",r[k],g[c],h,m,L?`plass-glow ${v[c]}`:"",N[$][B],E),style:{...n(j,_,c),...F},...A,children:[s("div",{className:o("flex shrink-0 items-center py-1",b[k],a[k],L?"ps-0":M,d(R)?"pe-1":L?"pe-0":""),children:[L?e("button",{type:"button",className:o("flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch","rounded-[inherit]",a[k],M,i),onClick:q,children:Q}):Q,d(R)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:R}):null]}),d(I)?e("div",{className:o("overflow-hidden","[transition:height_var(--plass-duration-slow)_var(--plass-ease)]","motion-reduce:[transition-duration:0ms]"),style:{height:O?J:0},inert:!O,children:e("div",{ref:G,className:o("whitespace-normal pb-2",M,u[k]),children:I})}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlPill}from"./PlPill.js";
|
|
@@ -1,113 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Popover as BaseUIPopover } from '@base-ui/react/popover';
|
|
3
|
-
import { CloseIcon } from '../../internal/icons.js';
|
|
4
|
-
import { cx, focusRingClasses, glassClasses, hasContent, metaTextClasses, radiusClasses, sheetBodyClasses, sheetHeaderGapClasses, sheetPaddingXClasses, sheetPaddingYClasses, sheetSectionGapClasses, sheetTitleClasses, surfaceSlots } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* How wide the popup is allowed to get, per `size` — the same axis `PlModal`
|
|
7
|
-
* folds its width into, one rung narrower at every step because a popover is a
|
|
8
|
-
* detail beside a control rather than a sheet in the middle of the page.
|
|
9
|
-
*/
|
|
10
|
-
const maxWidthClasses = {
|
|
11
|
-
xs: 'max-w-56',
|
|
12
|
-
sm: 'max-w-64',
|
|
13
|
-
md: 'max-w-80',
|
|
14
|
-
lg: 'max-w-96',
|
|
15
|
-
xl: 'max-w-lg'
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* The sheet. The same frosted panel a `PlSelect` popup and a `PlModal` draw, and
|
|
19
|
-
* for the same reason it carries the top of the shadow ladder: it is one of the
|
|
20
|
-
* few surfaces in the library that is genuinely meant to float.
|
|
21
|
-
*/
|
|
22
|
-
const popupClasses = /* @__PURE__ */ [
|
|
23
|
-
glassClasses,
|
|
24
|
-
'relative flex flex-col',
|
|
25
|
-
'border text-(--plass-fg) bg-(--plass-glass-press)',
|
|
26
|
-
'[border-color:var(--plass-glass-line)]',
|
|
27
|
-
'[box-shadow:var(--plass-shadow-3),var(--plass-gloss-glass)]',
|
|
28
|
-
'[outline:none]',
|
|
29
|
-
// Opacity only. A popup that scales or slides in drags its own text across the
|
|
30
|
-
// screen for the length of the transition, which is the one thing the house
|
|
31
|
-
// style is against — and unlike a control, this one is full of text.
|
|
32
|
-
'[transition:opacity_var(--plass-duration)_var(--plass-ease)]',
|
|
33
|
-
'data-[starting-style]:opacity-0 data-[ending-style]:opacity-0'
|
|
34
|
-
].join(' ');
|
|
35
|
-
/** The wedge, at roughly a third of the sheet's corner radius per step. */
|
|
36
|
-
const arrowSizes = {
|
|
37
|
-
xs: 8,
|
|
38
|
-
sm: 9,
|
|
39
|
-
md: 10,
|
|
40
|
-
lg: 11,
|
|
41
|
-
xl: 12
|
|
42
|
-
};
|
|
43
|
-
/** The × in the corner, the same one a `PlModal` draws. */
|
|
44
|
-
const closeButtonClasses = /* @__PURE__ */ [
|
|
45
|
-
'flex size-[1.6em] shrink-0 cursor-pointer items-center justify-center',
|
|
46
|
-
'rounded-full text-(--plass-muted-fg)',
|
|
47
|
-
'[&_svg]:size-[1.1em] [&_svg]:shrink-0',
|
|
48
|
-
'[transition:background-color_var(--plass-duration)_var(--plass-ease),color_var(--plass-duration)_var(--plass-ease)]',
|
|
49
|
-
'hover:bg-(--p-soft) hover:text-(--plass-fg)',
|
|
50
|
-
focusRingClasses
|
|
51
|
-
].join(' ');
|
|
52
|
-
/**
|
|
53
|
-
* Closes the popover it is inside.
|
|
54
|
-
*
|
|
55
|
-
* Exported for `PlModalClose`'s reason: an uncontrolled popover has no `setOpen`
|
|
56
|
-
* for its Cancel button to call, and making every popover controlled is a piece
|
|
57
|
-
* of state per popover that exists only to answer a button.
|
|
58
|
-
*
|
|
59
|
-
* `render` is Base UI's own escape hatch, so a real Plass button dismisses:
|
|
60
|
-
* `<PlPopoverClose render={<PlButton variant="ghost">Cancel</PlButton>} />`.
|
|
61
|
-
*/
|
|
62
|
-
export const PlPopoverClose = BaseUIPopover.Close;
|
|
63
|
-
/**
|
|
64
|
-
* A sheet that opens beside the thing that opened it.
|
|
65
|
-
*
|
|
66
|
-
* The difference from a `PlTooltip` is that this one can be **reached**: it
|
|
67
|
-
* stays up until it is dismissed, it can be entered with the pointer or the
|
|
68
|
-
* keyboard, and what is inside it can be clicked and typed into. The difference
|
|
69
|
-
* from a `PlModal` is that it does not take the page — it is anchored to a
|
|
70
|
-
* control, and the page behind goes on working.
|
|
71
|
-
*
|
|
72
|
-
* Base UI owns everything hard about it: the anchoring and the flip at the
|
|
73
|
-
* window edge, the click-outside and Escape, returning focus to the trigger, and
|
|
74
|
-
* the `aria-labelledby` / `aria-describedby` wiring. What is left here is the
|
|
75
|
-
* surface, the width ladder and the header.
|
|
76
|
-
*/
|
|
77
|
-
export function PlPopover({ size = 'md', color = 'primary', density = 'default', trigger, title, description, children, side = 'bottom', align = 'center', sideOffset = 6, alignOffset = 0, arrow = false, open, defaultOpen, onOpenChange, modal = false, dismissible = true, showClose = false, closeLabel = 'Close', width, className, style, ...props }) {
|
|
78
|
-
const insetX = sheetPaddingXClasses[density][size];
|
|
79
|
-
const insetY = sheetPaddingYClasses[density][size];
|
|
80
|
-
const arrowSize = arrowSizes[size];
|
|
81
|
-
const hasHeader = hasContent(title) || hasContent(description);
|
|
82
|
-
return (_jsxs(BaseUIPopover.Root, { open: open, defaultOpen: defaultOpen, modal: modal, onOpenChange: (next, details) => {
|
|
83
|
-
// Base UI has no `disablePointerDismissal` on a popover, so both ways
|
|
84
|
-
// out are cancelled here by the reason the change arrives with. An
|
|
85
|
-
// imperative close and a `PlPopoverClose` press still get through, which
|
|
86
|
-
// is what keeps `dismissible={false}` from being a trap.
|
|
87
|
-
if (!dismissible &&
|
|
88
|
-
!next &&
|
|
89
|
-
(details.reason === 'escape-key' ||
|
|
90
|
-
details.reason === 'outside-press' ||
|
|
91
|
-
details.reason === 'focus-out')) {
|
|
92
|
-
details.cancel();
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
onOpenChange?.(next);
|
|
96
|
-
}, children: [trigger ? _jsx(BaseUIPopover.Trigger, { render: trigger }) : null, _jsx(BaseUIPopover.Portal, { children: _jsx(BaseUIPopover.Positioner, { className: "plass-portal z-50 [outline:none]", side: side, align: align, sideOffset: sideOffset, alignOffset: alignOffset, children: _jsxs(BaseUIPopover.Popup, { className: cx(popupClasses, radiusClasses[size], sheetBodyClasses[size], sheetSectionGapClasses[size], insetX, insetY, width === undefined ? maxWidthClasses[size] : '', className), style: {
|
|
97
|
-
...surfaceSlots(color, 3),
|
|
98
|
-
...(width === undefined
|
|
99
|
-
? null
|
|
100
|
-
: { maxWidth: typeof width === 'number' ? `${width}px` : width }),
|
|
101
|
-
...style
|
|
102
|
-
}, ...props, children: [arrow ? (_jsx(BaseUIPopover.Arrow
|
|
103
|
-
// Base UI positions the wedge and reports which side it ended up
|
|
104
|
-
// on. It is drawn pointing down once and turned to match — a
|
|
105
|
-
// rotation of a glyph, which is the one allowance the
|
|
106
|
-
// no-transform rule makes.
|
|
107
|
-
, {
|
|
108
|
-
// Base UI positions the wedge and reports which side it ended up
|
|
109
|
-
// on. It is drawn pointing down once and turned to match — a
|
|
110
|
-
// rotation of a glyph, which is the one allowance the
|
|
111
|
-
// no-transform rule makes.
|
|
112
|
-
className: cx('data-[side=top]:bottom-[-1px]', 'data-[side=bottom]:top-[-1px] data-[side=bottom]:rotate-180', 'data-[side=left]:right-[-1px] data-[side=left]:-rotate-90', 'data-[side=right]:left-[-1px] data-[side=right]:rotate-90'), children: _jsxs("svg", { width: arrowSize, height: arrowSize / 2, viewBox: "0 0 10 5", "aria-hidden": "true", className: "block", children: [_jsx("path", { d: "M0 0h10L5 5z", fill: "var(--plass-glass-press)" }), _jsx("path", { d: "M0 0 5 5 10 0", fill: "none", stroke: "var(--plass-glass-line)", strokeWidth: "1", vectorEffect: "non-scaling-stroke" })] }) })) : null, hasHeader || showClose ? (_jsxs("div", { className: "flex shrink-0 items-start gap-3", children: [_jsxs("div", { className: cx('flex min-w-0 flex-1 flex-col', sheetHeaderGapClasses[size]), children: [hasContent(title) ? (_jsx(BaseUIPopover.Title, { className: cx('m-0 font-semibold', sheetTitleClasses[size]), children: title })) : null, hasContent(description) ? (_jsx(BaseUIPopover.Description, { className: cx('m-0 text-(--plass-muted-fg)', metaTextClasses[size]), children: description })) : null] }), showClose ? (_jsx(BaseUIPopover.Close, { "aria-label": closeLabel, className: closeButtonClasses, children: _jsx(CloseIcon, {}) })) : null] })) : null, hasContent(children) ? _jsx("div", { className: "min-w-0", children: children }) : null] }) }) })] }));
|
|
113
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as s}from"react/jsx-runtime";import{Popover as a}from"@base-ui/react/popover";import{CloseIcon as l}from"../../internal/icons.js";import{cx as t,focusRingClasses as o,glassClasses as r,hasContent as i,metaTextClasses as n,radiusClasses as d,sheetBodyClasses as p,sheetHeaderGapClasses as c,sheetPaddingXClasses as m,sheetPaddingYClasses as f,sheetSectionGapClasses as g,sheetTitleClasses as h,surfaceSlots as u}from"../../internal/styles.js";const x={xs:"max-w-56",sm:"max-w-64",md:"max-w-80",lg:"max-w-96",xl:"max-w-lg"},v=/* @__PURE__ */[r,"relative flex flex-col","border text-(--plass-fg) bg-(--plass-glass-press)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--plass-shadow-3),var(--plass-gloss-glass)]","[outline:none]","[transition:opacity_var(--plass-duration)_var(--plass-ease)]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" "),b={xs:8,sm:9,md:10,lg:11,xl:12},w=/* @__PURE__ */["flex size-[1.6em] shrink-0 cursor-pointer items-center justify-center","rounded-full text-(--plass-muted-fg)","[&_svg]:size-[1.1em] [&_svg]:shrink-0","[transition:background-color_var(--plass-duration)_var(--plass-ease),color_var(--plass-duration)_var(--plass-ease)]","hover:bg-(--p-soft) hover:text-(--plass-fg)",o].join(" ");export const PlPopoverClose=a.Close;export function PlPopover({size:o="md",color:r="primary",density:y="default",trigger:N,title:k,description:j,children:O,side:P="bottom",align:_="center",sideOffset:C=6,alignOffset:z=0,arrow:L=!1,open:M,defaultOpen:T,onOpenChange:W,modal:A=!1,dismissible:B=!0,showClose:D=!1,closeLabel:E="Close",width:R,className:$,style:q,...F}){const G=m[y][o],H=f[y][o],I=b[o],J=i(k)||i(j);return s(a.Root,{open:M,defaultOpen:T,modal:A,onOpenChange:(e,s)=>{B||e||"escape-key"!==s.reason&&"outside-press"!==s.reason&&"focus-out"!==s.reason?W?.(e):s.cancel()},children:[N?e(a.Trigger,{render:N}):null,e(a.Portal,{children:e(a.Positioner,{className:"plass-portal z-50 [outline:none]",side:P,align:_,sideOffset:C,alignOffset:z,children:s(a.Popup,{className:t(v,d[o],p[o],g[o],G,H,void 0===R?x[o]:"",$),style:{...u(r,3),...void 0===R?null:{maxWidth:"number"==typeof R?`${R}px`:R},...q},...F,children:[L?e(a.Arrow,{className:t("data-[side=top]:bottom-[-1px]","data-[side=bottom]:top-[-1px] data-[side=bottom]:rotate-180","data-[side=left]:right-[-1px] data-[side=left]:-rotate-90","data-[side=right]:left-[-1px] data-[side=right]:rotate-90"),children:s("svg",{width:I,height:I/2,viewBox:"0 0 10 5","aria-hidden":"true",className:"block",children:[e("path",{d:"M0 0h10L5 5z",fill:"var(--plass-glass-press)"}),e("path",{d:"M0 0 5 5 10 0",fill:"none",stroke:"var(--plass-glass-line)",strokeWidth:"1",vectorEffect:"non-scaling-stroke"})]})}):null,J||D?s("div",{className:"flex shrink-0 items-start gap-3",children:[s("div",{className:t("flex min-w-0 flex-1 flex-col",c[o]),children:[i(k)?e(a.Title,{className:t("m-0 font-semibold",h[o]),children:k}):null,i(j)?e(a.Description,{className:t("m-0 text-(--plass-muted-fg)",n[o]),children:j}):null]}),D?e(a.Close,{"aria-label":E,className:w,children:e(l,{})}):null]}):null,i(O)?e("div",{className:"min-w-0",children:O}):null]})})})]})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlPopover,PlPopoverClose}from"./PlPopover.js";
|