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.
Files changed (165) hide show
  1. package/README.md +165 -0
  2. package/dist/components/accordion/PlAccordion.js +1 -131
  3. package/dist/components/accordion/index.js +1 -1
  4. package/dist/components/alert/PlAlert.js +1 -109
  5. package/dist/components/alert/index.js +1 -1
  6. package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
  7. package/dist/components/animate-appear/index.js +1 -1
  8. package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
  9. package/dist/components/animate-blink/index.js +1 -1
  10. package/dist/components/animate-fade/PlAnimateFade.js +1 -48
  11. package/dist/components/animate-fade/index.js +1 -1
  12. package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
  13. package/dist/components/animate-grow/index.js +1 -1
  14. package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
  15. package/dist/components/animate-headline/index.js +1 -1
  16. package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
  17. package/dist/components/animate-lighting/index.js +1 -1
  18. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
  19. package/dist/components/animate-marquee/index.js +1 -1
  20. package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
  21. package/dist/components/animate-rotate/index.js +1 -1
  22. package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
  23. package/dist/components/animate-slide/index.js +1 -1
  24. package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
  25. package/dist/components/animate-typing/index.js +1 -1
  26. package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
  27. package/dist/components/animate-zoom/index.js +1 -1
  28. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
  29. package/dist/components/aspect-ratio/index.js +1 -1
  30. package/dist/components/avatar/PlAvatar.js +1 -121
  31. package/dist/components/avatar/index.js +1 -1
  32. package/dist/components/badge/PlBadge.js +1 -168
  33. package/dist/components/badge/index.js +1 -1
  34. package/dist/components/blockquote/PlBlockquote.js +1 -112
  35. package/dist/components/blockquote/index.js +1 -1
  36. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
  37. package/dist/components/bottom-navigation/index.js +1 -1
  38. package/dist/components/box/PlBox.js +1 -46
  39. package/dist/components/box/index.js +1 -1
  40. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
  41. package/dist/components/breadcrumb/index.js +1 -1
  42. package/dist/components/button/PlButton.js +1 -196
  43. package/dist/components/button/index.js +1 -1
  44. package/dist/components/button-group/PlButtonGroup.js +1 -64
  45. package/dist/components/button-group/index.js +1 -1
  46. package/dist/components/card/PlCard.js +1 -102
  47. package/dist/components/card/index.js +1 -1
  48. package/dist/components/carousel/PlCarousel.js +1 -177
  49. package/dist/components/carousel/index.js +1 -1
  50. package/dist/components/chat-bubble/PlChatBubble.js +1 -206
  51. package/dist/components/chat-bubble/index.js +1 -1
  52. package/dist/components/checkbox/PlCheckbox.js +1 -100
  53. package/dist/components/checkbox/index.js +1 -1
  54. package/dist/components/chip/PlChip.js +1 -144
  55. package/dist/components/chip/index.js +1 -1
  56. package/dist/components/collapsible/PlCollapsible.js +1 -65
  57. package/dist/components/collapsible/index.js +1 -1
  58. package/dist/components/combobox/PlCombobox.js +1 -199
  59. package/dist/components/combobox/index.js +1 -1
  60. package/dist/components/container/PlContainer.js +1 -47
  61. package/dist/components/container/index.js +1 -1
  62. package/dist/components/date-picker/PlDatePicker.js +1 -87
  63. package/dist/components/date-picker/index.js +1 -1
  64. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
  65. package/dist/components/date-range-picker/index.js +1 -1
  66. package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
  67. package/dist/components/date-time-picker/index.js +1 -1
  68. package/dist/components/divider/PlDivider.js +1 -107
  69. package/dist/components/divider/index.js +1 -1
  70. package/dist/components/drawer/PlDrawer.js +1 -190
  71. package/dist/components/drawer/index.js +1 -1
  72. package/dist/components/file-picker/PlFilePicker.js +1 -274
  73. package/dist/components/file-picker/index.js +1 -1
  74. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
  75. package/dist/components/floating-bottom-navigation/index.js +1 -1
  76. package/dist/components/grid/PlGrid.js +1 -51
  77. package/dist/components/grid/PlGridItem.js +1 -34
  78. package/dist/components/grid/index.js +1 -2
  79. package/dist/components/highlight/PlHighlight.js +1 -185
  80. package/dist/components/highlight/index.js +1 -1
  81. package/dist/components/hot-keys/PlHotKeys.js +1 -294
  82. package/dist/components/hot-keys/index.js +1 -1
  83. package/dist/components/icon/PlIcon.js +1 -58
  84. package/dist/components/icon/index.js +1 -1
  85. package/dist/components/icon-button/PlIconButton.js +1 -37
  86. package/dist/components/icon-button/index.js +1 -1
  87. package/dist/components/list/PlList.js +1 -160
  88. package/dist/components/list/index.js +1 -1
  89. package/dist/components/menu/PlMenu.js +1 -209
  90. package/dist/components/menu/index.js +1 -1
  91. package/dist/components/modal/PlModal.js +1 -150
  92. package/dist/components/modal/index.js +1 -1
  93. package/dist/components/number-field/PlNumberField.js +1 -111
  94. package/dist/components/number-field/index.js +1 -1
  95. package/dist/components/otp-field/PlOtpField.js +1 -109
  96. package/dist/components/otp-field/index.js +1 -1
  97. package/dist/components/overlay/PlOverlay.js +1 -77
  98. package/dist/components/overlay/index.js +1 -1
  99. package/dist/components/pagination/PlPagination.js +1 -159
  100. package/dist/components/pagination/index.js +1 -1
  101. package/dist/components/panes/PlPanes.js +1 -324
  102. package/dist/components/panes/index.js +1 -1
  103. package/dist/components/pill/PlPill.js +1 -154
  104. package/dist/components/pill/index.js +1 -1
  105. package/dist/components/popover/PlPopover.js +1 -113
  106. package/dist/components/popover/index.js +1 -1
  107. package/dist/components/progress-box/PlProgressBox.js +1 -34
  108. package/dist/components/progress-box/index.js +1 -1
  109. package/dist/components/progress-circular/PlProgressCircular.js +1 -47
  110. package/dist/components/progress-circular/index.js +1 -1
  111. package/dist/components/progress-linear/PlProgressLinear.js +1 -40
  112. package/dist/components/progress-linear/index.js +1 -1
  113. package/dist/components/radio-group/PlRadioGroup.js +1 -99
  114. package/dist/components/radio-group/index.js +1 -1
  115. package/dist/components/rating/PlRating.js +1 -101
  116. package/dist/components/rating/index.js +1 -1
  117. package/dist/components/scroll-zone/PlScrollZone.js +1 -411
  118. package/dist/components/scroll-zone/index.js +1 -1
  119. package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
  120. package/dist/components/segmented-button/index.js +1 -1
  121. package/dist/components/select/PlSelect.js +1 -115
  122. package/dist/components/select/index.js +1 -1
  123. package/dist/components/skeleton/PlSkeleton.js +1 -142
  124. package/dist/components/skeleton/index.js +1 -1
  125. package/dist/components/slider/PlSlider.js +1 -132
  126. package/dist/components/slider/index.js +1 -1
  127. package/dist/components/spoiler/PlSpoiler.js +1 -74
  128. package/dist/components/spoiler/index.js +1 -1
  129. package/dist/components/switch/PlSwitch.js +1 -131
  130. package/dist/components/switch/index.js +1 -1
  131. package/dist/components/table/PlTable.js +1 -210
  132. package/dist/components/table/index.js +1 -1
  133. package/dist/components/tabs/PlTabs.js +1 -182
  134. package/dist/components/tabs/index.js +1 -1
  135. package/dist/components/text-field/PlTextField.js +1 -126
  136. package/dist/components/text-field/index.js +1 -1
  137. package/dist/components/text-link/PlTextLink.js +1 -136
  138. package/dist/components/text-link/index.js +1 -1
  139. package/dist/components/time-picker/PlTimePicker.js +1 -97
  140. package/dist/components/time-picker/index.js +1 -1
  141. package/dist/components/timeline/PlTimeline.js +1 -207
  142. package/dist/components/timeline/index.js +1 -1
  143. package/dist/components/toast/PlToast.js +1 -174
  144. package/dist/components/toast/index.js +1 -1
  145. package/dist/components/toolbar/PlToolbar.js +1 -65
  146. package/dist/components/toolbar/index.js +1 -1
  147. package/dist/components/tooltip/PlTooltip.js +1 -117
  148. package/dist/components/tooltip/index.js +1 -1
  149. package/dist/components/typography/PlTypography.js +1 -143
  150. package/dist/components/typography/index.js +1 -1
  151. package/dist/index.js +1 -75
  152. package/dist/internal/animate.js +1 -318
  153. package/dist/internal/button-group.js +2 -19
  154. package/dist/internal/calendar.js +1 -501
  155. package/dist/internal/date.js +1 -353
  156. package/dist/internal/format.js +1 -41
  157. package/dist/internal/grid.js +1 -143
  158. package/dist/internal/icons.js +1 -138
  159. package/dist/internal/menu.js +1 -18
  160. package/dist/internal/picker.js +1 -139
  161. package/dist/internal/progress.js +1 -167
  162. package/dist/internal/sizer.js +1 -35
  163. package/dist/internal/styles.js +1 -636
  164. package/dist/types.js +0 -8
  165. package/package.json +5 -3
@@ -1,324 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 { PlPane, PlPanes } from './PlPanes.js';
1
+ export{PlPane,PlPanes}from"./PlPanes.js";
@@ -1,154 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
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 { PlPill } from './PlPill.js';
1
+ export{PlPill}from"./PlPill.js";
@@ -1,113 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 { PlPopover, PlPopoverClose } from './PlPopover.js';
1
+ export{PlPopover,PlPopoverClose}from"./PlPopover.js";