plass-ui 1.0.0 → 1.0.1

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 (164) hide show
  1. package/dist/components/accordion/PlAccordion.js +1 -131
  2. package/dist/components/accordion/index.js +1 -1
  3. package/dist/components/alert/PlAlert.js +1 -109
  4. package/dist/components/alert/index.js +1 -1
  5. package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
  6. package/dist/components/animate-appear/index.js +1 -1
  7. package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
  8. package/dist/components/animate-blink/index.js +1 -1
  9. package/dist/components/animate-fade/PlAnimateFade.js +1 -48
  10. package/dist/components/animate-fade/index.js +1 -1
  11. package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
  12. package/dist/components/animate-grow/index.js +1 -1
  13. package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
  14. package/dist/components/animate-headline/index.js +1 -1
  15. package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
  16. package/dist/components/animate-lighting/index.js +1 -1
  17. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
  18. package/dist/components/animate-marquee/index.js +1 -1
  19. package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
  20. package/dist/components/animate-rotate/index.js +1 -1
  21. package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
  22. package/dist/components/animate-slide/index.js +1 -1
  23. package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
  24. package/dist/components/animate-typing/index.js +1 -1
  25. package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
  26. package/dist/components/animate-zoom/index.js +1 -1
  27. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
  28. package/dist/components/aspect-ratio/index.js +1 -1
  29. package/dist/components/avatar/PlAvatar.js +1 -121
  30. package/dist/components/avatar/index.js +1 -1
  31. package/dist/components/badge/PlBadge.js +1 -168
  32. package/dist/components/badge/index.js +1 -1
  33. package/dist/components/blockquote/PlBlockquote.js +1 -112
  34. package/dist/components/blockquote/index.js +1 -1
  35. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
  36. package/dist/components/bottom-navigation/index.js +1 -1
  37. package/dist/components/box/PlBox.js +1 -46
  38. package/dist/components/box/index.js +1 -1
  39. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
  40. package/dist/components/breadcrumb/index.js +1 -1
  41. package/dist/components/button/PlButton.js +1 -196
  42. package/dist/components/button/index.js +1 -1
  43. package/dist/components/button-group/PlButtonGroup.js +1 -64
  44. package/dist/components/button-group/index.js +1 -1
  45. package/dist/components/card/PlCard.js +1 -102
  46. package/dist/components/card/index.js +1 -1
  47. package/dist/components/carousel/PlCarousel.js +1 -177
  48. package/dist/components/carousel/index.js +1 -1
  49. package/dist/components/chat-bubble/PlChatBubble.js +1 -206
  50. package/dist/components/chat-bubble/index.js +1 -1
  51. package/dist/components/checkbox/PlCheckbox.js +1 -100
  52. package/dist/components/checkbox/index.js +1 -1
  53. package/dist/components/chip/PlChip.js +1 -144
  54. package/dist/components/chip/index.js +1 -1
  55. package/dist/components/collapsible/PlCollapsible.js +1 -65
  56. package/dist/components/collapsible/index.js +1 -1
  57. package/dist/components/combobox/PlCombobox.js +1 -199
  58. package/dist/components/combobox/index.js +1 -1
  59. package/dist/components/container/PlContainer.js +1 -47
  60. package/dist/components/container/index.js +1 -1
  61. package/dist/components/date-picker/PlDatePicker.js +1 -87
  62. package/dist/components/date-picker/index.js +1 -1
  63. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
  64. package/dist/components/date-range-picker/index.js +1 -1
  65. package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
  66. package/dist/components/date-time-picker/index.js +1 -1
  67. package/dist/components/divider/PlDivider.js +1 -107
  68. package/dist/components/divider/index.js +1 -1
  69. package/dist/components/drawer/PlDrawer.js +1 -190
  70. package/dist/components/drawer/index.js +1 -1
  71. package/dist/components/file-picker/PlFilePicker.js +1 -274
  72. package/dist/components/file-picker/index.js +1 -1
  73. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
  74. package/dist/components/floating-bottom-navigation/index.js +1 -1
  75. package/dist/components/grid/PlGrid.js +1 -51
  76. package/dist/components/grid/PlGridItem.js +1 -34
  77. package/dist/components/grid/index.js +1 -2
  78. package/dist/components/highlight/PlHighlight.js +1 -185
  79. package/dist/components/highlight/index.js +1 -1
  80. package/dist/components/hot-keys/PlHotKeys.js +1 -294
  81. package/dist/components/hot-keys/index.js +1 -1
  82. package/dist/components/icon/PlIcon.js +1 -58
  83. package/dist/components/icon/index.js +1 -1
  84. package/dist/components/icon-button/PlIconButton.js +1 -37
  85. package/dist/components/icon-button/index.js +1 -1
  86. package/dist/components/list/PlList.js +1 -160
  87. package/dist/components/list/index.js +1 -1
  88. package/dist/components/menu/PlMenu.js +1 -209
  89. package/dist/components/menu/index.js +1 -1
  90. package/dist/components/modal/PlModal.js +1 -150
  91. package/dist/components/modal/index.js +1 -1
  92. package/dist/components/number-field/PlNumberField.js +1 -111
  93. package/dist/components/number-field/index.js +1 -1
  94. package/dist/components/otp-field/PlOtpField.js +1 -109
  95. package/dist/components/otp-field/index.js +1 -1
  96. package/dist/components/overlay/PlOverlay.js +1 -77
  97. package/dist/components/overlay/index.js +1 -1
  98. package/dist/components/pagination/PlPagination.js +1 -159
  99. package/dist/components/pagination/index.js +1 -1
  100. package/dist/components/panes/PlPanes.js +1 -324
  101. package/dist/components/panes/index.js +1 -1
  102. package/dist/components/pill/PlPill.js +1 -154
  103. package/dist/components/pill/index.js +1 -1
  104. package/dist/components/popover/PlPopover.js +1 -113
  105. package/dist/components/popover/index.js +1 -1
  106. package/dist/components/progress-box/PlProgressBox.js +1 -34
  107. package/dist/components/progress-box/index.js +1 -1
  108. package/dist/components/progress-circular/PlProgressCircular.js +1 -47
  109. package/dist/components/progress-circular/index.js +1 -1
  110. package/dist/components/progress-linear/PlProgressLinear.js +1 -40
  111. package/dist/components/progress-linear/index.js +1 -1
  112. package/dist/components/radio-group/PlRadioGroup.js +1 -99
  113. package/dist/components/radio-group/index.js +1 -1
  114. package/dist/components/rating/PlRating.js +1 -101
  115. package/dist/components/rating/index.js +1 -1
  116. package/dist/components/scroll-zone/PlScrollZone.js +1 -411
  117. package/dist/components/scroll-zone/index.js +1 -1
  118. package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
  119. package/dist/components/segmented-button/index.js +1 -1
  120. package/dist/components/select/PlSelect.js +1 -115
  121. package/dist/components/select/index.js +1 -1
  122. package/dist/components/skeleton/PlSkeleton.js +1 -142
  123. package/dist/components/skeleton/index.js +1 -1
  124. package/dist/components/slider/PlSlider.js +1 -132
  125. package/dist/components/slider/index.js +1 -1
  126. package/dist/components/spoiler/PlSpoiler.js +1 -74
  127. package/dist/components/spoiler/index.js +1 -1
  128. package/dist/components/switch/PlSwitch.js +1 -131
  129. package/dist/components/switch/index.js +1 -1
  130. package/dist/components/table/PlTable.js +1 -210
  131. package/dist/components/table/index.js +1 -1
  132. package/dist/components/tabs/PlTabs.js +1 -182
  133. package/dist/components/tabs/index.js +1 -1
  134. package/dist/components/text-field/PlTextField.js +1 -126
  135. package/dist/components/text-field/index.js +1 -1
  136. package/dist/components/text-link/PlTextLink.js +1 -136
  137. package/dist/components/text-link/index.js +1 -1
  138. package/dist/components/time-picker/PlTimePicker.js +1 -97
  139. package/dist/components/time-picker/index.js +1 -1
  140. package/dist/components/timeline/PlTimeline.js +1 -207
  141. package/dist/components/timeline/index.js +1 -1
  142. package/dist/components/toast/PlToast.js +1 -174
  143. package/dist/components/toast/index.js +1 -1
  144. package/dist/components/toolbar/PlToolbar.js +1 -65
  145. package/dist/components/toolbar/index.js +1 -1
  146. package/dist/components/tooltip/PlTooltip.js +1 -117
  147. package/dist/components/tooltip/index.js +1 -1
  148. package/dist/components/typography/PlTypography.js +1 -143
  149. package/dist/components/typography/index.js +1 -1
  150. package/dist/index.js +1 -75
  151. package/dist/internal/animate.js +1 -318
  152. package/dist/internal/button-group.js +2 -19
  153. package/dist/internal/calendar.js +1 -501
  154. package/dist/internal/date.js +1 -353
  155. package/dist/internal/format.js +1 -41
  156. package/dist/internal/grid.js +1 -143
  157. package/dist/internal/icons.js +1 -138
  158. package/dist/internal/menu.js +1 -18
  159. package/dist/internal/picker.js +1 -139
  160. package/dist/internal/progress.js +1 -167
  161. package/dist/internal/sizer.js +1 -35
  162. package/dist/internal/styles.js +1 -636
  163. package/dist/types.js +0 -8
  164. package/package.json +3 -2
@@ -1,274 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { CloseIcon } from '../../internal/icons.js';
4
- import { controlTextLeadingClasses, disabledClasses, focusRingClasses, gapClasses, glassClasses, hasContent, iconClasses, metaTextClasses, radiusClasses, readOnlyFilterClasses, sheetTitleClasses, stackGapClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
5
- /**
6
- * The box's inner padding. Its own ladder rather than the sheet's, because a
7
- * dropzone is sized by the gesture it has to catch rather than by what is
8
- * written in it: a target the height of one line of text is a target you miss.
9
- */
10
- const zonePaddingClasses = {
11
- default: { xs: 'p-4', sm: 'p-5', md: 'p-6', lg: 'p-8', xl: 'p-10' },
12
- compact: { xs: 'p-2', sm: 'p-3', md: 'p-4', lg: 'p-5', xl: 'p-6' }
13
- };
14
- /**
15
- * The three materials, said the way a *container* says them — the sheet is
16
- * never dyed. What is dropped on it is other people's content.
17
- *
18
- * The dashed edge is the one thing all three share, and the one place the
19
- * library draws a line that is not solid. It is not decoration: a dashed
20
- * rectangle is the established sign for "this area accepts a drop", and a
21
- * dropzone that looks like a PlCard is a PlCard nobody tries to drop on.
22
- *
23
- * The edge is neutral at rest and takes the family only once the pointer is on
24
- * it, which is the same arrangement a `glass` PlButton has: the colour is what
25
- * happens when you engage with the thing, not what the thing is.
26
- */
27
- const zoneRestClasses = {
28
- solid: /* @__PURE__ */ [
29
- glassClasses,
30
- 'border-2 border-dashed text-(--plass-fg) bg-(--plass-glass-press)',
31
- '[border-color:var(--plass-border)]',
32
- '[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
33
- ].join(' '),
34
- glass: /* @__PURE__ */ [
35
- glassClasses,
36
- 'border-2 border-dashed text-(--plass-fg) bg-(--plass-glass)',
37
- '[border-color:var(--plass-border)]',
38
- '[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
39
- ].join(' '),
40
- ghost: 'border-2 border-dashed text-(--plass-fg) bg-transparent [border-color:var(--plass-border)]'
41
- };
42
- const zoneHoverClasses = {
43
- solid: 'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line-hover)]',
44
- glass: 'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line-hover)]',
45
- ghost: 'hover:bg-(--p-soft) hover:[border-color:var(--p-line-hover)]'
46
- };
47
- /**
48
- * While a file is over the box.
49
- *
50
- * Colour and edge only, and the same two the hover state already uses, one step
51
- * further along — a dropzone that grows or lifts under the pointer moves the
52
- * target while the reader is aiming at it.
53
- */
54
- const zoneOverClasses = 'bg-(--p-soft-hover) [border-color:var(--p-ring)]';
55
- function UploadIcon() {
56
- return (_jsxs("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M8 10.5V2.75m0 0L5.25 5.5M8 2.75 10.75 5.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M2.75 9.75v1.75a1.75 1.75 0 0 0 1.75 1.75h7a1.75 1.75 0 0 0 1.75-1.75V9.75", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }));
57
- }
58
- /**
59
- * `1.4 MB`, in the units a person reading a file list expects.
60
- *
61
- * Base 1000 rather than 1024, and `MB` rather than `MiB`: it is the number
62
- * every operating system's file browser shows, and a picker that disagrees with
63
- * the Finder about how big the file is has picked a fight it cannot win.
64
- */
65
- export function formatFileSize(bytes) {
66
- if (bytes < 1000) {
67
- return `${bytes} B`;
68
- }
69
- const units = ['kB', 'MB', 'GB', 'TB'];
70
- let value = bytes / 1000;
71
- let unit = 0;
72
- while (value >= 1000 && unit < units.length - 1) {
73
- value /= 1000;
74
- unit += 1;
75
- }
76
- return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;
77
- }
78
- /**
79
- * Whether a file matches an `accept` string.
80
- *
81
- * The browser applies `accept` to its own picker and to nothing else, so a file
82
- * that arrives by drag has never been checked against it. This is that check:
83
- * the same three forms the attribute takes — `.ext`, `type/subtype`, `type/*`.
84
- */
85
- function matchesAccept(file, accept) {
86
- const name = file.name.toLowerCase();
87
- const type = file.type.toLowerCase();
88
- return accept
89
- .split(',')
90
- .map((entry) => entry.trim().toLowerCase())
91
- .filter(Boolean)
92
- .some((entry) => {
93
- if (entry.startsWith('.')) {
94
- return name.endsWith(entry);
95
- }
96
- if (entry.endsWith('/*')) {
97
- return type.startsWith(entry.slice(0, -1));
98
- }
99
- return type === entry;
100
- });
101
- }
102
- /**
103
- * A box you drop files on, or click to open the file dialog.
104
- *
105
- * There is no Base UI primitive under this, and the fallback the library allows
106
- * — plain React and DOM — is the right one: a dropzone is a `<div>` listening
107
- * for four drag events and an `<input type="file">` it clicks for you. There is
108
- * no popup to position, no focus to trap and no roving anything.
109
- *
110
- * What that leaves is the part every hand-rolled dropzone gets wrong. The drag
111
- * counter is one: `dragenter` and `dragleave` both fire as the pointer crosses
112
- * a *child* of the zone, so a zone that toggles a boolean flickers the whole
113
- * time a file is dragged across its own contents. Counting them is the fix. The
114
- * other is `accept`, which the browser enforces on its own dialog and never on
115
- * a drop, so a dropzone that only sets the attribute accepts anything the
116
- * moment a file arrives by drag.
117
- *
118
- * The shell is a `<div>` and the pressable area inside it is a real `<button>`,
119
- * with the file list outside that button — the remove buttons cannot be nested
120
- * inside the browse button.
121
- */
122
- export const PlFilePicker = /* @__PURE__ */ React.forwardRef(function PlFilePicker({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, accept, multiple = false, maxSize, maxFiles, value, defaultValue, onFilesChange, onReject, label, description, error, invalid, title, hint, icon, showList = true, removeLabel = (name) => `Remove ${name}`, fullWidth = true, disabled = false, readOnly = false, required = false, name, id, className, style, ...props }, ref) {
123
- const inputRef = React.useRef(null);
124
- React.useImperativeHandle(ref, () => inputRef.current);
125
- const [uncontrolled, setUncontrolled] = React.useState(() => [...(defaultValue ?? [])]);
126
- // The copy is what stops a caller's array being the one held here, and it
127
- // has to be memoised: the handlers below close over this list, and a fresh
128
- // array every render would rebuild all of them every render.
129
- const files = React.useMemo(() => (value ? [...value] : uncontrolled), [value, uncontrolled]);
130
- // `dragenter`/`dragleave` fire for every child the pointer crosses, so a
131
- // boolean flickers the whole time a file is over the box. The depth counter
132
- // is the only thing that survives a zone with content in it.
133
- const dragDepth = React.useRef(0);
134
- const [over, setOver] = React.useState(false);
135
- const hasError = hasContent(error);
136
- const isInvalid = invalid ?? hasError;
137
- // Invalid re-points the whole slot family at `danger`, so the edge, the ring
138
- // and the message all turn over together — the same wiring PlTextField uses.
139
- const family = isInvalid ? 'danger' : color;
140
- const inert = disabled || readOnly;
141
- const descriptionId = React.useId();
142
- const commit = React.useCallback((next) => {
143
- if (!value) {
144
- setUncontrolled(next);
145
- }
146
- onFilesChange?.(next);
147
- }, [onFilesChange, value]);
148
- /**
149
- * Sorts an incoming batch into kept and rejected.
150
- *
151
- * The count is checked against what is *already* held rather than against
152
- * the batch, which is the difference between "you may drop five files" and
153
- * "you may end up with five files" — only the second is what `maxFiles`
154
- * means.
155
- */
156
- const accepting = React.useCallback((incoming) => {
157
- const kept = [];
158
- const rejections = [];
159
- const room = multiple ? (maxFiles ?? Number.POSITIVE_INFINITY) : 1;
160
- const held = multiple ? files.length : 0;
161
- for (const file of incoming) {
162
- if (accept && !matchesAccept(file, accept)) {
163
- rejections.push({ file, reason: 'type' });
164
- }
165
- else if (maxSize !== undefined && file.size > maxSize) {
166
- rejections.push({ file, reason: 'size' });
167
- }
168
- else if (held + kept.length >= room) {
169
- rejections.push({ file, reason: 'count' });
170
- }
171
- else {
172
- kept.push(file);
173
- }
174
- }
175
- return { kept, rejections };
176
- }, [accept, files.length, maxFiles, maxSize, multiple]);
177
- const add = React.useCallback((incoming) => {
178
- const { kept, rejections } = accepting(incoming);
179
- if (rejections.length > 0) {
180
- onReject?.(rejections);
181
- }
182
- if (kept.length > 0) {
183
- commit(multiple ? [...files, ...kept] : kept);
184
- }
185
- }, [accepting, commit, files, multiple, onReject]);
186
- const browse = () => {
187
- if (inert) {
188
- return;
189
- }
190
- // Cleared first, so choosing the same file twice in a row still fires
191
- // `change` — the input holds its value otherwise and the second pick is
192
- // silently dropped.
193
- if (inputRef.current) {
194
- inputRef.current.value = '';
195
- inputRef.current.click();
196
- }
197
- };
198
- const zoneClassNames = [
199
- 'flex w-full flex-col items-center justify-center text-center',
200
- 'cursor-pointer select-none',
201
- zonePaddingClasses[density][size],
202
- radiusClasses[size],
203
- gapClasses[size],
204
- transitionClasses,
205
- iconClasses,
206
- focusRingClasses,
207
- // An if/else rather than stacked variants: two Tailwind classes of equal
208
- // specificity resolve by their order in the generated stylesheet.
209
- disabled
210
- ? `${disabledClasses[variant]} border-2 border-dashed`
211
- : readOnly
212
- ? `${zoneRestClasses[variant]} ${readOnlyFilterClasses} cursor-default`
213
- : zoneRestClasses[variant],
214
- !inert ? zoneHoverClasses[variant] : '',
215
- over && !inert ? zoneOverClasses : ''
216
- ]
217
- .filter(Boolean)
218
- .join(' ');
219
- return (_jsxs("div", { className: [
220
- 'flex-col align-top',
221
- stackGapClasses[size],
222
- fullWidth ? 'flex w-full' : 'inline-flex',
223
- className ?? ''
224
- ]
225
- .filter(Boolean)
226
- .join(' '), style: { ...surfaceSlots(family, elevation), ...style }, ...props, children: [hasContent(label) ? (_jsx("span", { className: [
227
- metaTextClasses[size],
228
- 'font-semibold',
229
- disabled ? 'text-(--plass-muted-fg)' : 'text-(--plass-fg)'
230
- ].join(' '), children: label })) : null, _jsxs("div", { className: "flex w-full flex-col", onDragEnter: (event) => {
231
- if (inert) {
232
- return;
233
- }
234
- event.preventDefault();
235
- dragDepth.current += 1;
236
- setOver(true);
237
- }, onDragOver: (event) => {
238
- if (inert) {
239
- return;
240
- }
241
- // Without this the browser navigates to the file instead of
242
- // dropping it, which is the default and is never what anybody wants.
243
- event.preventDefault();
244
- event.dataTransfer.dropEffect = 'copy';
245
- }, onDragLeave: () => {
246
- if (inert) {
247
- return;
248
- }
249
- dragDepth.current = Math.max(0, dragDepth.current - 1);
250
- if (dragDepth.current === 0) {
251
- setOver(false);
252
- }
253
- }, onDrop: (event) => {
254
- if (inert) {
255
- return;
256
- }
257
- event.preventDefault();
258
- dragDepth.current = 0;
259
- setOver(false);
260
- add(Array.from(event.dataTransfer.files));
261
- }, children: [_jsxs("button", { type: "button", id: id, disabled: disabled, "aria-describedby": hasContent(description) || hasError ? descriptionId : undefined, "aria-invalid": isInvalid || undefined, className: zoneClassNames, onClick: browse, children: [icon === undefined ? (_jsx("span", { className: "flex items-center text-(--p-accent) [&_svg]:size-[1.8em]", children: _jsx(UploadIcon, {}) })) : hasContent(icon) ? (_jsx("span", { className: "flex items-center text-(--p-accent) [&_svg]:size-[1.8em]", children: icon })) : null, _jsx("span", { className: `font-semibold ${sheetTitleClasses[size]}`, children: title ?? 'Drop files here, or click to browse' }), hasContent(hint) ? (_jsx("span", { className: `text-(--plass-muted-fg) ${metaTextClasses[size]}`, children: hint })) : null] }), _jsx("input", { ref: inputRef, type: "file", name: name, accept: accept, multiple: multiple, required: required && files.length === 0, disabled: inert, tabIndex: -1, "aria-hidden": "true", className: "absolute size-px overflow-hidden opacity-0 [clip-path:inset(50%)]", onChange: (event) => add(Array.from(event.target.files ?? [])) })] }), showList && files.length > 0 ? (_jsx("ul", { role: "list", className: `flex w-full flex-col ${stackGapClasses[size]} m-0 list-none p-0`, children: files.map((file, index) => (_jsxs("li", { className: [
262
- 'flex w-full items-center gap-2 px-2 py-1.5',
263
- radiusClasses.xs,
264
- controlTextLeadingClasses[size],
265
- 'bg-(--p-soft) text-(--plass-fg)'
266
- ].join(' '), children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: file.name }), _jsx("span", { className: `shrink-0 text-(--plass-muted-fg) tabular-nums ${metaTextClasses[size]}`, children: formatFileSize(file.size) }), inert ? null : (_jsx("button", { type: "button", "aria-label": removeLabel(file.name), className: [
267
- 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full',
268
- 'size-[1.3em] text-(--plass-muted-fg) opacity-70',
269
- '[transition:opacity_var(--plass-duration)_var(--plass-ease),color_var(--plass-duration)_var(--plass-ease)]',
270
- '[&_svg]:size-[0.9em]',
271
- 'hover:text-(--plass-fg) hover:opacity-100 focus-visible:opacity-100',
272
- focusRingClasses
273
- ].join(' '), onClick: () => commit(files.filter((_, at) => at !== index)), children: _jsx(CloseIcon, {}) }))] }, `${file.name}-${file.size}-${file.lastModified}-${index}`))) })) : null, hasContent(description) && !hasError ? (_jsx("span", { id: descriptionId, className: `${metaTextClasses[size]} text-(--plass-muted-fg)`, children: description })) : null, hasError ? (_jsx("span", { id: descriptionId, className: `${metaTextClasses[size]} text-(--p-accent)`, children: error })) : null] }));
274
- });
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as s from"react";import{CloseIcon as l}from"../../internal/icons.js";import{controlTextLeadingClasses as t,disabledClasses as a,focusRingClasses as o,gapClasses as n,glassClasses as i,hasContent as c,iconClasses as d,metaTextClasses as p,radiusClasses as u,readOnlyFilterClasses as f,sheetTitleClasses as m,stackGapClasses as h,surfaceSlots as v,transitionClasses as g}from"../../internal/styles.js";const b={default:{xs:"p-4",sm:"p-5",md:"p-6",lg:"p-8",xl:"p-10"},compact:{xs:"p-2",sm:"p-3",md:"p-4",lg:"p-5",xl:"p-6"}},x={solid:/* @__PURE__ */[i,"border-2 border-dashed text-(--plass-fg) bg-(--plass-glass-press)","[border-color:var(--plass-border)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),glass:/* @__PURE__ */[i,"border-2 border-dashed text-(--plass-fg) bg-(--plass-glass)","[border-color:var(--plass-border)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:"border-2 border-dashed text-(--plass-fg) bg-transparent [border-color:var(--plass-border)]"},y={solid:"hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line-hover)]",glass:"hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line-hover)]",ghost:"hover:bg-(--p-soft) hover:[border-color:var(--p-line-hover)]"};function UploadIcon(){return r("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e("path",{d:"M8 10.5V2.75m0 0L5.25 5.5M8 2.75 10.75 5.5",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M2.75 9.75v1.75a1.75 1.75 0 0 0 1.75 1.75h7a1.75 1.75 0 0 0 1.75-1.75V9.75",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]})}export function formatFileSize(e){if(e<1e3)return`${e} B`;const r=["kB","MB","GB","TB"];let s=e/1e3,l=0;for(;s>=1e3&&l<r.length-1;)s/=1e3,l+=1;return`${s<10?s.toFixed(1):Math.round(s)} ${r[l]}`}function matchesAccept(e,r){const s=e.name.toLowerCase(),l=e.type.toLowerCase();return r.split(",").map(e=>e.trim().toLowerCase()).filter(Boolean).some(e=>e.startsWith(".")?s.endsWith(e):e.endsWith("/*")?l.startsWith(e.slice(0,-1)):l===e)}export const PlFilePicker=/* @__PURE__ */s.forwardRef(function PlFilePicker({variant:i="glass",size:k="md",color:N="primary",density:j="default",elevation:$=0,accept:w,multiple:z=!1,maxSize:C,maxFiles:I,value:L,defaultValue:B,onFilesChange:D,onReject:F,label:M,description:_,error:W,invalid:S,title:P,hint:R,icon:T,showList:A=!0,removeLabel:V=e=>`Remove ${e}`,fullWidth:E=!0,disabled:O=!1,readOnly:q=!1,required:U=!1,name:G,id:H,className:Y,style:J,...K},Q){const X=s.useRef(null);s.useImperativeHandle(Q,()=>X.current);const[Z,ee]=s.useState(()=>[...B??[]]),re=s.useMemo(()=>L?[...L]:Z,[L,Z]),se=s.useRef(0),[le,te]=s.useState(!1),ae=c(W),oe=S??ae,ne=oe?"danger":N,ie=O||q,ce=s.useId(),de=s.useCallback(e=>{L||ee(e),D?.(e)},[D,L]),pe=s.useCallback(e=>{const r=[],s=[],l=z?I??Number.POSITIVE_INFINITY:1,t=z?re.length:0;for(const a of e)w&&!matchesAccept(a,w)?s.push({file:a,reason:"type"}):void 0!==C&&a.size>C?s.push({file:a,reason:"size"}):t+r.length>=l?s.push({file:a,reason:"count"}):r.push(a);return{kept:r,rejections:s}},[w,re.length,I,C,z]),ue=s.useCallback(e=>{const{kept:r,rejections:s}=pe(e);s.length>0&&F?.(s),r.length>0&&de(z?[...re,...r]:r)},[pe,de,re,z,F]),fe=["flex w-full flex-col items-center justify-center text-center","cursor-pointer select-none",b[j][k],u[k],n[k],g,d,o,O?`${a[i]} border-2 border-dashed`:q?`${x[i]} ${f} cursor-default`:x[i],ie?"":y[i],le&&!ie?"bg-(--p-soft-hover) [border-color:var(--p-ring)]":""].filter(Boolean).join(" ");return r("div",{className:["flex-col align-top",h[k],E?"flex w-full":"inline-flex",Y??""].filter(Boolean).join(" "),style:{...v(ne,$),...J},...K,children:[c(M)?e("span",{className:[p[k],"font-semibold",O?"text-(--plass-muted-fg)":"text-(--plass-fg)"].join(" "),children:M}):null,r("div",{className:"flex w-full flex-col",onDragEnter:e=>{ie||(e.preventDefault(),se.current+=1,te(!0))},onDragOver:e=>{ie||(e.preventDefault(),e.dataTransfer.dropEffect="copy")},onDragLeave:()=>{ie||(se.current=Math.max(0,se.current-1),0===se.current&&te(!1))},onDrop:e=>{ie||(e.preventDefault(),se.current=0,te(!1),ue(Array.from(e.dataTransfer.files)))},children:[r("button",{type:"button",id:H,disabled:O,"aria-describedby":c(_)||ae?ce:void 0,"aria-invalid":oe||void 0,className:fe,onClick:()=>{ie||X.current&&(X.current.value="",X.current.click())},children:[void 0===T?e("span",{className:"flex items-center text-(--p-accent) [&_svg]:size-[1.8em]",children:e(UploadIcon,{})}):c(T)?e("span",{className:"flex items-center text-(--p-accent) [&_svg]:size-[1.8em]",children:T}):null,e("span",{className:`font-semibold ${m[k]}`,children:P??"Drop files here, or click to browse"}),c(R)?e("span",{className:`text-(--plass-muted-fg) ${p[k]}`,children:R}):null]}),e("input",{ref:X,type:"file",name:G,accept:w,multiple:z,required:U&&0===re.length,disabled:ie,tabIndex:-1,"aria-hidden":"true",className:"absolute size-px overflow-hidden opacity-0 [clip-path:inset(50%)]",onChange:e=>ue(Array.from(e.target.files??[]))})]}),A&&re.length>0?e("ul",{role:"list",className:`flex w-full flex-col ${h[k]} m-0 list-none p-0`,children:re.map((s,a)=>r("li",{className:["flex w-full items-center gap-2 px-2 py-1.5",u.xs,t[k],"bg-(--p-soft) text-(--plass-fg)"].join(" "),children:[e("span",{className:"min-w-0 flex-1 truncate",children:s.name}),e("span",{className:`shrink-0 text-(--plass-muted-fg) tabular-nums ${p[k]}`,children:formatFileSize(s.size)}),ie?null:e("button",{type:"button","aria-label":V(s.name),className:["inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full","size-[1.3em] text-(--plass-muted-fg) opacity-70","[transition:opacity_var(--plass-duration)_var(--plass-ease),color_var(--plass-duration)_var(--plass-ease)]","[&_svg]:size-[0.9em]","hover:text-(--plass-fg) hover:opacity-100 focus-visible:opacity-100",o].join(" "),onClick:()=>de(re.filter((e,r)=>r!==a)),children:e(l,{})})]},`${s.name}-${s.size}-${s.lastModified}-${a}`))}):null,c(_)&&!ae?e("span",{id:ce,className:`${p[k]} text-(--plass-muted-fg)`,children:_}):null,ae?e("span",{id:ce,className:`${p[k]} text-(--p-accent)`,children:W}):null]})});
@@ -1 +1 @@
1
- export { PlFilePicker, formatFileSize } from './PlFilePicker.js';
1
+ export{PlFilePicker,formatFileSize}from"./PlFilePicker.js";
@@ -1,291 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { useRender } from '@base-ui/react/use-render';
4
- import { controlHeightClasses, controlSlots, controlSquareClasses, cx, focusRingClasses, glassClasses, hasContent, iconSizeClasses, srOnlyClasses, transitionClasses } from '../../internal/styles.js';
5
- const FloatingBarContext = /* @__PURE__ */ React.createContext({
6
- value: null,
7
- change: () => { },
8
- size: 'md',
9
- disabled: false
10
- });
11
- /** How far off the floor the capsule sits, on the size ladder. */
12
- const floatGapClasses = {
13
- xs: 'pb-2',
14
- sm: 'pb-3',
15
- md: 'pb-4',
16
- lg: 'pb-5',
17
- xl: 'pb-6'
18
- };
19
- /** The same, with the home indicator added underneath it. */
20
- const floatGapSafeClasses = {
21
- xs: 'pb-[calc(env(safe-area-inset-bottom)+0.5rem)]',
22
- sm: 'pb-[calc(env(safe-area-inset-bottom)+0.75rem)]',
23
- md: 'pb-[calc(env(safe-area-inset-bottom)+1rem)]',
24
- lg: 'pb-[calc(env(safe-area-inset-bottom)+1.25rem)]',
25
- xl: 'pb-[calc(env(safe-area-inset-bottom)+1.5rem)]'
26
- };
27
- /** The air inside the capsule, around the discs. */
28
- const capsulePaddingClasses = {
29
- default: { xs: 'p-1', sm: 'p-1', md: 'p-1.5', lg: 'p-2', xl: 'p-2.5' },
30
- compact: { xs: 'p-0.5', sm: 'p-0.5', md: 'p-1', lg: 'p-1', xl: 'p-1.5' }
31
- };
32
- /** Between two discs. */
33
- const discGapClasses = {
34
- default: { xs: 'gap-1', sm: 'gap-1', md: 'gap-1.5', lg: 'gap-2', xl: 'gap-2.5' },
35
- compact: { xs: 'gap-0.5', sm: 'gap-0.5', md: 'gap-1', lg: 'gap-1', xl: 'gap-1.5' }
36
- };
37
- const positionClasses = {
38
- static: '',
39
- sticky: 'sticky bottom-0 z-20',
40
- fixed: 'fixed inset-x-0 bottom-0 z-30'
41
- };
42
- /**
43
- * The capsule.
44
- *
45
- * `rounded-full` is one of the very few places the library allows a pill, and it
46
- * is allowed for the reason a `PlSegmentedButton`'s groove is: this is not a
47
- * sheet lying on the page, it is an object floating clear of one. The house
48
- * fillet is about a sheet with its corners cut, and a sheet that is not on
49
- * anything has no corners to cut.
50
- */
51
- const capsuleClasses = {
52
- solid: /* @__PURE__ */ [
53
- glassClasses,
54
- 'bg-(--plass-glass-press)',
55
- '[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
56
- ].join(' '),
57
- glass: /* @__PURE__ */ [
58
- glassClasses,
59
- 'border bg-(--plass-glass)',
60
- '[border-color:var(--plass-glass-line)]',
61
- '[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
62
- ].join(' '),
63
- ghost: ''
64
- };
65
- /**
66
- * The key that slides.
67
- *
68
- * Always the family's own gradient with that family's tinted shadow under it,
69
- * whatever the capsule is made of — a key of tinted glass riding in a clear
70
- * sheet is the design language's own sentence, and `variant` is a statement
71
- * about the *capsule*. A `ghost` bar is a row of discs with no sheet behind
72
- * them; the one that is current is still the one that is current.
73
- *
74
- * `left`, `top`, `width` and `height` rather than a `translate`: the key is
75
- * measured off the disc it belongs under, and moving it by its own box is what
76
- * keeps every glyph in the row unresampled while it travels. `left` and not
77
- * `inset-inline-start`, because `offsetLeft` is a distance from the left edge
78
- * and stays one under RTL — pairing a physical measurement with a logical
79
- * property is what would break the direction.
80
- *
81
- * `data-quiet` is the current destination being unavailable, which is the one
82
- * state the key cannot read off its own props: the light goes out on the key
83
- * the same way it goes out on the disc over it, or a disabled destination is a
84
- * dimmed glyph on a fully lit gradient.
85
- */
86
- const keyClasses = /* @__PURE__ */ [
87
- 'pointer-events-none absolute rounded-full',
88
- 'top-(--p-disc-y) left-(--p-disc-x) h-(--p-disc-h) w-(--p-disc-w)',
89
- '[background-image:var(--p-fill)]',
90
- '[box-shadow:var(--plass-shadow-1),var(--p-lift)]',
91
- 'data-[quiet]:opacity-50 data-[quiet]:saturate-[0.35]',
92
- '[transition-property:left,top,width,height,opacity,filter]',
93
- '[transition-timing-function:var(--plass-ease)]',
94
- // Nothing until the first measurement has landed; the house duration from
95
- // then on.
96
- '[transition-duration:0ms] data-[ready]:[transition-duration:var(--plass-duration)]'
97
- ].join(' ');
98
- /**
99
- * A row of round destinations floating clear of the bottom edge of the window.
100
- *
101
- * The other half of `PlBottomNavigation`, and a different object rather than a
102
- * variant of one: that bar is **attached** to the edge of the window — full
103
- * width, a hairline against the content, its sheet running under the home
104
- * indicator — and this one is **not part of the page at all**. Everything that
105
- * follows comes from that one difference: the capsule, the gap under it, the
106
- * shadow it defaults to, and the pill corners it is allowed.
107
- *
108
- * Every destination is a disc with a glyph in it and no name drawn, which is
109
- * what keeps a row of five inside the width of a phone. The name is still
110
- * required and still read out — a row of glyphs with no accessible names is the
111
- * defect `PlIconButton`'s `label` exists to make impossible, and it would be
112
- * exactly as easy to ship here.
113
- *
114
- * The current destination is a key of **tinted glass** riding in the clear
115
- * sheet, which is the design language's own sentence with nothing added to it.
116
- * The key is **one element that travels**, measured off whichever disc is
117
- * current and animated the way a `PlSegmentedButton`'s tile is — not a fill
118
- * that appears on one disc while it disappears from another. Two discs
119
- * cross-fading is two objects; a bar with a key in it has one, and where it
120
- * goes is the whole of what the component has to say.
121
- *
122
- * Nothing is transformed: the key is an empty box, and no glyph is resampled
123
- * while it travels. That is what lets the house no-transform rule survive a
124
- * component whose entire point is that something moves.
125
- *
126
- * It reads `controlSlots` and not the `surfaceSlots` every other container
127
- * takes, which is the one place this bar is not a container: the slots a
128
- * container is given are deliberately undyed — no `--p-fill`, no `--p-on-solid`
129
- * and no `--p-lift` — and the key is made of all three. Asking a container's
130
- * slot set for a control's colours is asking for `var(--p-fill)` to resolve to
131
- * nothing, which is a `background-image` the browser drops on the floor and a
132
- * current destination that looks exactly like the four beside it.
133
- */
134
- export const PlFloatingBottomNavigation = /* @__PURE__ */ React.forwardRef(function PlFloatingBottomNavigation({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 2, value: valueProp, defaultValue = null, onValueChange, position = 'fixed', safeArea = true, disabled = false, label, render, className, style, children, ...props }, ref) {
135
- const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
136
- const controlled = valueProp !== undefined;
137
- const value = controlled ? valueProp : uncontrolled;
138
- const capsuleRef = React.useRef(null);
139
- const keyRef = React.useRef(null);
140
- /**
141
- * Writes the current disc's box onto the key as four custom properties.
142
- *
143
- * Written straight to the element rather than held in state, exactly as
144
- * `PlSegmentedButton` writes its tile: a `setState` here would re-render
145
- * every disc on every resize, and nothing in the tree depends on the numbers
146
- * except four CSS declarations.
147
- *
148
- * `animate` is what separates the two callers. A change of destination is the
149
- * thing this exists to animate; a resize is the capsule moving under a key
150
- * that was already in the right place, and animating that is a key that lags
151
- * behind the window being dragged.
152
- */
153
- const measure = React.useCallback((animate) => {
154
- const capsule = capsuleRef.current;
155
- const disc = keyRef.current;
156
- if (!capsule || !disc) {
157
- return;
158
- }
159
- const current = capsule.querySelector('[data-disc][data-current]');
160
- if (!current) {
161
- return;
162
- }
163
- // A key that has only just mounted has nowhere to travel *from*, so its
164
- // first placement is instant however it was asked for — that is what makes
165
- // the first destination appear under its disc rather than fly in from the
166
- // left edge of the capsule.
167
- const instant = !animate || !disc.hasAttribute('data-ready');
168
- if (instant) {
169
- disc.removeAttribute('data-ready');
170
- }
171
- // `offsetLeft`/`offsetTop` are measured from the offsetParent's padding
172
- // edge, and `left`/`top` on an absolutely positioned child resolve against
173
- // the same box — so the capsule's own padding is already accounted for and
174
- // must not be subtracted again.
175
- disc.style.setProperty('--p-disc-x', `${current.offsetLeft}px`);
176
- disc.style.setProperty('--p-disc-y', `${current.offsetTop}px`);
177
- disc.style.setProperty('--p-disc-w', `${current.offsetWidth}px`);
178
- disc.style.setProperty('--p-disc-h', `${current.offsetHeight}px`);
179
- // Read off the disc rather than off a prop, because the bar does not know
180
- // which of its children is unavailable — the items are `children` and the
181
- // disabled one is whichever of them said so. The same query that measures
182
- // the box answers it, so there is no second pass.
183
- disc.toggleAttribute('data-quiet', current.hasAttribute('data-disabled'));
184
- if (instant) {
185
- // Reading a layout property commits the four writes above while the
186
- // duration is still 0ms, so turning the transition back on cannot
187
- // animate a move that has already happened.
188
- void disc.offsetWidth;
189
- }
190
- disc.setAttribute('data-ready', '');
191
- }, []);
192
- // Before the browser paints, or the key is visibly at nothing for a frame.
193
- React.useLayoutEffect(() => {
194
- measure(true);
195
- }, [measure, value, variant, size, density, disabled, children]);
196
- React.useEffect(() => {
197
- const capsule = capsuleRef.current;
198
- if (!capsule || typeof ResizeObserver === 'undefined') {
199
- return;
200
- }
201
- const observer = new ResizeObserver(() => measure(false));
202
- observer.observe(capsule);
203
- return () => observer.disconnect();
204
- }, [measure]);
205
- const change = React.useCallback((next) => {
206
- if (!controlled) {
207
- setUncontrolled(next);
208
- }
209
- onValueChange?.(next);
210
- }, [controlled, onValueChange]);
211
- const context = React.useMemo(() => ({ value: value ?? null, change, size, disabled }), [value, change, size, disabled]);
212
- const classNames = cx('flex w-full justify-center px-4',
213
- // The strip the capsule is centred in spans the window, and a transparent
214
- // band across the bottom of a page that swallowed presses would be a band
215
- // nobody could scroll through. Only the capsule takes them back.
216
- position === 'static' ? '' : 'pointer-events-none', safeArea ? floatGapSafeClasses[size] : floatGapClasses[size], positionClasses[position], className);
217
- return useRender({
218
- render: render ?? _jsx("nav", {}),
219
- ref,
220
- props: {
221
- 'aria-label': label,
222
- className: classNames,
223
- // `controlSlots` and not `surfaceSlots`: the capsule is undyed like every
224
- // other container, but the key riding in it is a control's surface and is
225
- // made of three slots a container's set does not carry.
226
- style: { ...controlSlots(color, elevation, 'solid'), ...style },
227
- children: (_jsx(FloatingBarContext.Provider, { value: context, children: _jsxs("div", { ref: capsuleRef, className: cx(
228
- // `relative` is load-bearing twice over: it is what makes the
229
- // capsule the discs' offsetParent, and what the key is positioned
230
- // in.
231
- 'pointer-events-auto relative inline-flex items-center rounded-full', capsuleClasses[variant], capsulePaddingClasses[density][size], discGapClasses[density][size], transitionClasses), children: [value !== null && value !== undefined ? (_jsx("span", { ref: keyRef, "aria-hidden": "true", className: keyClasses })) : null, children] }) })),
232
- ...props
233
- }
234
- });
235
- });
236
- /**
237
- * One destination, as a disc.
238
- *
239
- * It has no `size`, no `color` and no `variant` of its own: all three belong to
240
- * the bar, which is the only place they can be set once and mean the same thing
241
- * for every disc.
242
- */
243
- export const PlFloatingBottomNavigationItem = /* @__PURE__ */ React.forwardRef(function PlFloatingBottomNavigationItem({ value, icon, href, disabled: disabledProp = false, className, children, onClick, ...props }, ref) {
244
- const bar = React.useContext(FloatingBarContext);
245
- const disabled = disabledProp || bar.disabled;
246
- const selected = bar.value !== null && bar.value === value;
247
- const classNames = cx(
248
- // `z-10` and a stacking context of its own: the key is painted behind the
249
- // discs, and without this it would cover the glyph it is under.
250
- 'relative z-10 inline-flex shrink-0 items-center justify-center rounded-full', controlHeightClasses[bar.size], controlSquareClasses[bar.size], '[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]', transitionClasses,
251
- // Offset rather than inset: a disc inside a capsule that is only padding has
252
- // room around it, and a flush ring on a circle is the circle's own edge
253
- // thickening — which is the one shape where that reads as a border rather
254
- // than as focus.
255
- focusRingClasses, disabled
256
- ? 'cursor-not-allowed opacity-50 saturate-[0.35] text-(--plass-muted-fg)'
257
- : selected
258
- ? // No surface of its own. What is under the glyph is the key, which
259
- // belongs to the bar and is the one thing in the row that travels;
260
- // a disc that drew its own fill would be a second key appearing
261
- // wherever the first one had just left.
262
- 'cursor-pointer text-(--p-on-solid)'
263
- : 'cursor-pointer text-(--plass-muted-fg) hover:text-(--plass-fg) hover:bg-(--plass-glass-hover)', className);
264
- const body = (_jsxs(_Fragment, { children: [hasContent(icon) ? (_jsx("span", { className: cx('flex shrink-0 items-center justify-center', iconSizeClasses[bar.size]), children: icon })) : null, hasContent(children) ? _jsx("span", { className: srOnlyClasses, children: children }) : null] }));
265
- /**
266
- * The hooks the key is measured from.
267
- *
268
- * Attributes rather than a ref per disc, for the reason a `PlSegment` carries
269
- * `data-segment`: a ref array would mean keeping it in step with however the
270
- * caller composed the items — through a `.map()`, through a fragment, through
271
- * a component of their own — and an attribute is the version of that which
272
- * cannot fall out of step.
273
- */
274
- const discAttributes = {
275
- 'data-disc': '',
276
- 'data-current': selected ? '' : undefined,
277
- 'data-disabled': disabled ? '' : undefined
278
- };
279
- const press = (event) => {
280
- if (disabled) {
281
- event.preventDefault();
282
- return;
283
- }
284
- bar.change(value);
285
- onClick?.(event);
286
- };
287
- if (href) {
288
- return (_jsx("a", { ref: ref, href: disabled ? undefined : href, "aria-current": selected ? 'page' : undefined, "aria-disabled": disabled || undefined, ...discAttributes, className: classNames, onClick: press, ...props, children: body }));
289
- }
290
- return (_jsx("button", { ref: ref, type: "button", disabled: disabled, "aria-current": selected ? 'page' : undefined, ...discAttributes, className: classNames, onClick: press, ...props, children: body }));
291
- });
1
+ "use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import*as s from"react";import{useRender as r}from"@base-ui/react/use-render";import{controlHeightClasses as i,controlSlots as l,controlSquareClasses as o,cx as n,focusRingClasses as d,glassClasses as c,hasContent as p,iconSizeClasses as u,srOnlyClasses as f,transitionClasses as m}from"../../internal/styles.js";const g=/* @__PURE__ */s.createContext({value:null,change:()=>{},size:"md",disabled:!1}),v={xs:"pb-2",sm:"pb-3",md:"pb-4",lg:"pb-5",xl:"pb-6"},b={xs:"pb-[calc(env(safe-area-inset-bottom)+0.5rem)]",sm:"pb-[calc(env(safe-area-inset-bottom)+0.75rem)]",md:"pb-[calc(env(safe-area-inset-bottom)+1rem)]",lg:"pb-[calc(env(safe-area-inset-bottom)+1.25rem)]",xl:"pb-[calc(env(safe-area-inset-bottom)+1.5rem)]"},h={default:{xs:"p-1",sm:"p-1",md:"p-1.5",lg:"p-2",xl:"p-2.5"},compact:{xs:"p-0.5",sm:"p-0.5",md:"p-1",lg:"p-1",xl:"p-1.5"}},x={default:{xs:"gap-1",sm:"gap-1",md:"gap-1.5",lg:"gap-2",xl:"gap-2.5"},compact:{xs:"gap-0.5",sm:"gap-0.5",md:"gap-1",lg:"gap-1",xl:"gap-1.5"}},y={static:"",sticky:"sticky bottom-0 z-20",fixed:"fixed inset-x-0 bottom-0 z-30"},w={solid:/* @__PURE__ */[c,"bg-(--plass-glass-press)","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),glass:/* @__PURE__ */[c,"border bg-(--plass-glass)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:""},N=/* @__PURE__ */["pointer-events-none absolute rounded-full","top-(--p-disc-y) left-(--p-disc-x) h-(--p-disc-h) w-(--p-disc-w)","[background-image:var(--p-fill)]","[box-shadow:var(--plass-shadow-1),var(--p-lift)]","data-[quiet]:opacity-50 data-[quiet]:saturate-[0.35]","[transition-property:left,top,width,height,opacity,filter]","[transition-timing-function:var(--plass-ease)]","[transition-duration:0ms] data-[ready]:[transition-duration:var(--plass-duration)]"].join(" ");export const PlFloatingBottomNavigation=/* @__PURE__ */s.forwardRef(function PlFloatingBottomNavigation({variant:a="glass",size:i="md",color:o="primary",density:d="default",elevation:c=2,value:p,defaultValue:u=null,onValueChange:f,position:k="fixed",safeArea:z=!0,disabled:j=!1,label:P,render:C,className:A,style:R,children:F,...q},B){const[$,E]=s.useState(u),I=void 0!==p,L=I?p:$,O=s.useRef(null),S=s.useRef(null),V=s.useCallback(e=>{const t=O.current,a=S.current;if(!t||!a)return;const s=t.querySelector("[data-disc][data-current]");if(!s)return;const r=!e||!a.hasAttribute("data-ready");r&&a.removeAttribute("data-ready"),a.style.setProperty("--p-disc-x",`${s.offsetLeft}px`),a.style.setProperty("--p-disc-y",`${s.offsetTop}px`),a.style.setProperty("--p-disc-w",`${s.offsetWidth}px`),a.style.setProperty("--p-disc-h",`${s.offsetHeight}px`),a.toggleAttribute("data-quiet",s.hasAttribute("data-disabled")),r&&a.offsetWidth,a.setAttribute("data-ready","")},[]);s.useLayoutEffect(()=>{V(!0)},[V,L,a,i,d,j,F]),s.useEffect(()=>{const e=O.current;if(!e||"undefined"==typeof ResizeObserver)return;const t=new ResizeObserver(()=>V(!1));return t.observe(e),()=>t.disconnect()},[V]);const W=s.useCallback(e=>{I||E(e),f?.(e)},[I,f]),D=s.useMemo(()=>({value:L??null,change:W,size:i,disabled:j}),[L,W,i,j]),H=n("flex w-full justify-center px-4","static"===k?"":"pointer-events-none",z?b[i]:v[i],y[k],A);return r({render:C??e("nav",{}),ref:B,props:{"aria-label":P,className:H,style:{...l(o,c,"solid"),...R},children:e(g.Provider,{value:D,children:t("div",{ref:O,className:n("pointer-events-auto relative inline-flex items-center rounded-full",w[a],h[d][i],x[d][i],m),children:[null!=L?e("span",{ref:S,"aria-hidden":"true",className:N}):null,F]})}),...q}})});export const PlFloatingBottomNavigationItem=/* @__PURE__ */s.forwardRef(function PlFloatingBottomNavigationItem({value:r,icon:l,href:c,disabled:v=!1,className:b,children:h,onClick:x,...y},w){const N=s.useContext(g),k=v||N.disabled,z=null!==N.value&&N.value===r,j=n("relative z-10 inline-flex shrink-0 items-center justify-center rounded-full",i[N.size],o[N.size],"[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",m,d,k?"cursor-not-allowed opacity-50 saturate-[0.35] text-(--plass-muted-fg)":z?"cursor-pointer text-(--p-on-solid)":"cursor-pointer text-(--plass-muted-fg) hover:text-(--plass-fg) hover:bg-(--plass-glass-hover)",b),P=t(a,{children:[p(l)?e("span",{className:n("flex shrink-0 items-center justify-center",u[N.size]),children:l}):null,p(h)?e("span",{className:f,children:h}):null]}),C={"data-disc":"","data-current":z?"":void 0,"data-disabled":k?"":void 0},press=e=>{k?e.preventDefault():(N.change(r),x?.(e))};return c?e("a",{ref:w,href:k?void 0:c,"aria-current":z?"page":void 0,"aria-disabled":k||void 0,...C,className:j,onClick:press,...y,children:P}):e("button",{ref:w,type:"button",disabled:k,"aria-current":z?"page":void 0,...C,className:j,onClick:press,...y,children:P})});
@@ -1 +1 @@
1
- export { PlFloatingBottomNavigation, PlFloatingBottomNavigationItem } from './PlFloatingBottomNavigation.js';
1
+ export{PlFloatingBottomNavigation,PlFloatingBottomNavigationItem}from"./PlFloatingBottomNavigation.js";