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.
- package/dist/components/accordion/PlAccordion.js +1 -131
- package/dist/components/accordion/index.js +1 -1
- package/dist/components/alert/PlAlert.js +1 -109
- package/dist/components/alert/index.js +1 -1
- package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
- package/dist/components/animate-appear/index.js +1 -1
- package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
- package/dist/components/animate-blink/index.js +1 -1
- package/dist/components/animate-fade/PlAnimateFade.js +1 -48
- package/dist/components/animate-fade/index.js +1 -1
- package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
- package/dist/components/animate-grow/index.js +1 -1
- package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
- package/dist/components/animate-headline/index.js +1 -1
- package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
- package/dist/components/animate-lighting/index.js +1 -1
- package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
- package/dist/components/animate-marquee/index.js +1 -1
- package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
- package/dist/components/animate-rotate/index.js +1 -1
- package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
- package/dist/components/animate-slide/index.js +1 -1
- package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
- package/dist/components/animate-typing/index.js +1 -1
- package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
- package/dist/components/animate-zoom/index.js +1 -1
- package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
- package/dist/components/aspect-ratio/index.js +1 -1
- package/dist/components/avatar/PlAvatar.js +1 -121
- package/dist/components/avatar/index.js +1 -1
- package/dist/components/badge/PlBadge.js +1 -168
- package/dist/components/badge/index.js +1 -1
- package/dist/components/blockquote/PlBlockquote.js +1 -112
- package/dist/components/blockquote/index.js +1 -1
- package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
- package/dist/components/bottom-navigation/index.js +1 -1
- package/dist/components/box/PlBox.js +1 -46
- package/dist/components/box/index.js +1 -1
- package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
- package/dist/components/breadcrumb/index.js +1 -1
- package/dist/components/button/PlButton.js +1 -196
- package/dist/components/button/index.js +1 -1
- package/dist/components/button-group/PlButtonGroup.js +1 -64
- package/dist/components/button-group/index.js +1 -1
- package/dist/components/card/PlCard.js +1 -102
- package/dist/components/card/index.js +1 -1
- package/dist/components/carousel/PlCarousel.js +1 -177
- package/dist/components/carousel/index.js +1 -1
- package/dist/components/chat-bubble/PlChatBubble.js +1 -206
- package/dist/components/chat-bubble/index.js +1 -1
- package/dist/components/checkbox/PlCheckbox.js +1 -100
- package/dist/components/checkbox/index.js +1 -1
- package/dist/components/chip/PlChip.js +1 -144
- package/dist/components/chip/index.js +1 -1
- package/dist/components/collapsible/PlCollapsible.js +1 -65
- package/dist/components/collapsible/index.js +1 -1
- package/dist/components/combobox/PlCombobox.js +1 -199
- package/dist/components/combobox/index.js +1 -1
- package/dist/components/container/PlContainer.js +1 -47
- package/dist/components/container/index.js +1 -1
- package/dist/components/date-picker/PlDatePicker.js +1 -87
- package/dist/components/date-picker/index.js +1 -1
- package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
- package/dist/components/date-range-picker/index.js +1 -1
- package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
- package/dist/components/date-time-picker/index.js +1 -1
- package/dist/components/divider/PlDivider.js +1 -107
- package/dist/components/divider/index.js +1 -1
- package/dist/components/drawer/PlDrawer.js +1 -190
- package/dist/components/drawer/index.js +1 -1
- package/dist/components/file-picker/PlFilePicker.js +1 -274
- package/dist/components/file-picker/index.js +1 -1
- package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
- package/dist/components/floating-bottom-navigation/index.js +1 -1
- package/dist/components/grid/PlGrid.js +1 -51
- package/dist/components/grid/PlGridItem.js +1 -34
- package/dist/components/grid/index.js +1 -2
- package/dist/components/highlight/PlHighlight.js +1 -185
- package/dist/components/highlight/index.js +1 -1
- package/dist/components/hot-keys/PlHotKeys.js +1 -294
- package/dist/components/hot-keys/index.js +1 -1
- package/dist/components/icon/PlIcon.js +1 -58
- package/dist/components/icon/index.js +1 -1
- package/dist/components/icon-button/PlIconButton.js +1 -37
- package/dist/components/icon-button/index.js +1 -1
- package/dist/components/list/PlList.js +1 -160
- package/dist/components/list/index.js +1 -1
- package/dist/components/menu/PlMenu.js +1 -209
- package/dist/components/menu/index.js +1 -1
- package/dist/components/modal/PlModal.js +1 -150
- package/dist/components/modal/index.js +1 -1
- package/dist/components/number-field/PlNumberField.js +1 -111
- package/dist/components/number-field/index.js +1 -1
- package/dist/components/otp-field/PlOtpField.js +1 -109
- package/dist/components/otp-field/index.js +1 -1
- package/dist/components/overlay/PlOverlay.js +1 -77
- package/dist/components/overlay/index.js +1 -1
- package/dist/components/pagination/PlPagination.js +1 -159
- package/dist/components/pagination/index.js +1 -1
- package/dist/components/panes/PlPanes.js +1 -324
- package/dist/components/panes/index.js +1 -1
- package/dist/components/pill/PlPill.js +1 -154
- package/dist/components/pill/index.js +1 -1
- package/dist/components/popover/PlPopover.js +1 -113
- package/dist/components/popover/index.js +1 -1
- package/dist/components/progress-box/PlProgressBox.js +1 -34
- package/dist/components/progress-box/index.js +1 -1
- package/dist/components/progress-circular/PlProgressCircular.js +1 -47
- package/dist/components/progress-circular/index.js +1 -1
- package/dist/components/progress-linear/PlProgressLinear.js +1 -40
- package/dist/components/progress-linear/index.js +1 -1
- package/dist/components/radio-group/PlRadioGroup.js +1 -99
- package/dist/components/radio-group/index.js +1 -1
- package/dist/components/rating/PlRating.js +1 -101
- package/dist/components/rating/index.js +1 -1
- package/dist/components/scroll-zone/PlScrollZone.js +1 -411
- package/dist/components/scroll-zone/index.js +1 -1
- package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
- package/dist/components/segmented-button/index.js +1 -1
- package/dist/components/select/PlSelect.js +1 -115
- package/dist/components/select/index.js +1 -1
- package/dist/components/skeleton/PlSkeleton.js +1 -142
- package/dist/components/skeleton/index.js +1 -1
- package/dist/components/slider/PlSlider.js +1 -132
- package/dist/components/slider/index.js +1 -1
- package/dist/components/spoiler/PlSpoiler.js +1 -74
- package/dist/components/spoiler/index.js +1 -1
- package/dist/components/switch/PlSwitch.js +1 -131
- package/dist/components/switch/index.js +1 -1
- package/dist/components/table/PlTable.js +1 -210
- package/dist/components/table/index.js +1 -1
- package/dist/components/tabs/PlTabs.js +1 -182
- package/dist/components/tabs/index.js +1 -1
- package/dist/components/text-field/PlTextField.js +1 -126
- package/dist/components/text-field/index.js +1 -1
- package/dist/components/text-link/PlTextLink.js +1 -136
- package/dist/components/text-link/index.js +1 -1
- package/dist/components/time-picker/PlTimePicker.js +1 -97
- package/dist/components/time-picker/index.js +1 -1
- package/dist/components/timeline/PlTimeline.js +1 -207
- package/dist/components/timeline/index.js +1 -1
- package/dist/components/toast/PlToast.js +1 -174
- package/dist/components/toast/index.js +1 -1
- package/dist/components/toolbar/PlToolbar.js +1 -65
- package/dist/components/toolbar/index.js +1 -1
- package/dist/components/tooltip/PlTooltip.js +1 -117
- package/dist/components/tooltip/index.js +1 -1
- package/dist/components/typography/PlTypography.js +1 -143
- package/dist/components/typography/index.js +1 -1
- package/dist/index.js +1 -75
- package/dist/internal/animate.js +1 -318
- package/dist/internal/button-group.js +2 -19
- package/dist/internal/calendar.js +1 -501
- package/dist/internal/date.js +1 -353
- package/dist/internal/format.js +1 -41
- package/dist/internal/grid.js +1 -143
- package/dist/internal/icons.js +1 -138
- package/dist/internal/menu.js +1 -18
- package/dist/internal/picker.js +1 -139
- package/dist/internal/progress.js +1 -167
- package/dist/internal/sizer.js +1 -35
- package/dist/internal/styles.js +1 -636
- package/dist/types.js +0 -8
- package/package.json +3 -2
|
@@ -1,177 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { PlIconButton } from '../icon-button/PlIconButton.js';
|
|
4
|
-
import { ChevronIcon } from '../../internal/icons.js';
|
|
5
|
-
import { cx, radiusClasses, sheetRestClasses, srOnlyClasses, surfaceSlots, transitionClasses } from '../../internal/styles.js';
|
|
6
|
-
/** How far the arrows sit in from the frame's edge. */
|
|
7
|
-
const arrowInsetClasses = {
|
|
8
|
-
xs: 'start-1 end-1',
|
|
9
|
-
sm: 'start-1.5 end-1.5',
|
|
10
|
-
md: 'start-2 end-2',
|
|
11
|
-
lg: 'start-3 end-3',
|
|
12
|
-
xl: 'start-4 end-4'
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* The dot ladder.
|
|
16
|
-
*
|
|
17
|
-
* A current dot is a short bar rather than a bigger circle: it grows along the
|
|
18
|
-
* row it is in, so the row's height never changes and the dots either side of
|
|
19
|
-
* it do not move. Width and colour are the only two things that travel, which
|
|
20
|
-
* is what keeps this inside the house rule against scaling anything.
|
|
21
|
-
*/
|
|
22
|
-
const dotClasses = {
|
|
23
|
-
xs: { rest: 'h-1 w-1', current: 'h-1 w-3', gap: 'gap-1' },
|
|
24
|
-
sm: { rest: 'h-1 w-1', current: 'h-1 w-3.5', gap: 'gap-1' },
|
|
25
|
-
md: { rest: 'h-1.5 w-1.5', current: 'h-1.5 w-4', gap: 'gap-1.5' },
|
|
26
|
-
lg: { rest: 'h-1.5 w-1.5', current: 'h-1.5 w-5', gap: 'gap-2' },
|
|
27
|
-
xl: { rest: 'h-2 w-2', current: 'h-2 w-6', gap: 'gap-2' }
|
|
28
|
-
};
|
|
29
|
-
/** How long a smooth scroll of our own is given to arrive. */
|
|
30
|
-
const SETTLE_MS = 700;
|
|
31
|
-
/**
|
|
32
|
-
* A strip of slides, one of which is in view.
|
|
33
|
-
*
|
|
34
|
-
* The mechanism is a scroll container with CSS scroll snapping, and everything
|
|
35
|
-
* good about this component follows from that one choice. Swiping on a phone
|
|
36
|
-
* and two-finger dragging on a trackpad both work because they are the
|
|
37
|
-
* browser's own scrolling and not a gesture handler pretending to be it. The
|
|
38
|
-
* strip runs the other way under RTL without being told, because scrolling is
|
|
39
|
-
* directional and `translate` is not. And nothing is transformed — the house
|
|
40
|
-
* rule against moving a surface holds here for free, where a translated track
|
|
41
|
-
* would have had to argue for an exception.
|
|
42
|
-
*
|
|
43
|
-
* The motion is `scroll-behavior: smooth`, which means a reader who has asked
|
|
44
|
-
* for reduced motion gets an instant cut from the same code path rather than
|
|
45
|
-
* from a second one written to remember them.
|
|
46
|
-
*
|
|
47
|
-
* Slides are not a sub-component. Every top-level child is wrapped in its own
|
|
48
|
-
* slide, so `<PlCarousel><img /><img /></PlCarousel>` is the whole API — and the
|
|
49
|
-
* wrapper is what carries the snap point, the width and the
|
|
50
|
-
* `role="group"` / `aria-roledescription="slide"` pair a screen reader needs,
|
|
51
|
-
* none of which a caller should have to remember to put on a photograph.
|
|
52
|
-
*/
|
|
53
|
-
export const PlCarousel = /* @__PURE__ */ React.forwardRef(function PlCarousel({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, value, defaultValue = 0, onValueChange, loop = true, autoPlay = false, interval = 5000, arrows = true, indicators = true, label = 'Carousel', previousLabel = 'Previous slide', nextLabel = 'Next slide', slideLabel, className, style, children, ...props }, ref) {
|
|
54
|
-
const nameSlide = slideLabel ?? ((index, total) => `Slide ${index} of ${total}`);
|
|
55
|
-
// `toArray` is what drops the `null`s and `false`s a conditional slide
|
|
56
|
-
// leaves behind, and what gives every remaining child a stable key.
|
|
57
|
-
const slides = React.Children.toArray(children);
|
|
58
|
-
const count = slides.length;
|
|
59
|
-
const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
|
|
60
|
-
const index = Math.min(Math.max(value ?? uncontrolled, 0), Math.max(count - 1, 0));
|
|
61
|
-
const trackRef = React.useRef(null);
|
|
62
|
-
const slideRefs = React.useRef([]);
|
|
63
|
-
// Set while the index is catching up with a scroll the reader performed. The
|
|
64
|
-
// effect below skips those, or every drag would be answered by a scroll back
|
|
65
|
-
// to where the browser had already put us.
|
|
66
|
-
const fromScroll = React.useRef(false);
|
|
67
|
-
const mounted = React.useRef(false);
|
|
68
|
-
// Raised while a smooth scroll of our own is still travelling. Without it
|
|
69
|
-
// the scroll events thrown on the way from slide 0 to slide 2 would each be
|
|
70
|
-
// read as the reader landing on slide 1.
|
|
71
|
-
const settling = React.useRef(false);
|
|
72
|
-
const [paused, setPaused] = React.useState(false);
|
|
73
|
-
const go = React.useCallback((next, viaScroll = false) => {
|
|
74
|
-
if (count === 0) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const wrapped = loop
|
|
78
|
-
? ((next % count) + count) % count
|
|
79
|
-
: Math.min(Math.max(next, 0), count - 1);
|
|
80
|
-
fromScroll.current = viaScroll;
|
|
81
|
-
if (value === undefined) {
|
|
82
|
-
setUncontrolled(wrapped);
|
|
83
|
-
}
|
|
84
|
-
if (wrapped !== index) {
|
|
85
|
-
onValueChange?.(wrapped);
|
|
86
|
-
}
|
|
87
|
-
}, [count, loop, value, index, onValueChange]);
|
|
88
|
-
React.useEffect(() => {
|
|
89
|
-
if (fromScroll.current) {
|
|
90
|
-
fromScroll.current = false;
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
// The first pass would otherwise scroll the page down to a carousel
|
|
94
|
-
// nobody has looked at yet, just to put slide 0 where the browser already
|
|
95
|
-
// had it.
|
|
96
|
-
if (!mounted.current) {
|
|
97
|
-
mounted.current = true;
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
slideRefs.current[index]?.scrollIntoView({ block: 'nearest', inline: 'start' });
|
|
101
|
-
settling.current = true;
|
|
102
|
-
const timer = window.setTimeout(() => {
|
|
103
|
-
settling.current = false;
|
|
104
|
-
}, SETTLE_MS);
|
|
105
|
-
return () => window.clearTimeout(timer);
|
|
106
|
-
}, [index]);
|
|
107
|
-
/**
|
|
108
|
-
* Where the strip has settled, read off the scroll offset rather than
|
|
109
|
-
* measured per slide: every slide is exactly the width of the frame, so the
|
|
110
|
-
* offset divided by that width *is* the index. `Math.abs` is what makes it
|
|
111
|
-
* hold under RTL, where a scroll position counts backwards from zero.
|
|
112
|
-
*/
|
|
113
|
-
const handleScroll = () => {
|
|
114
|
-
const track = trackRef.current;
|
|
115
|
-
// `settling` is tested before anything is measured, and that order is the
|
|
116
|
-
// point: a smooth scroll of our own throws events for most of a second,
|
|
117
|
-
// and those are exactly the ones with nothing to answer. Reading
|
|
118
|
-
// `clientWidth` first would force a layout on every one of them.
|
|
119
|
-
if (settling.current || !track || track.clientWidth === 0) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const nearest = Math.round(Math.abs(track.scrollLeft) / track.clientWidth);
|
|
123
|
-
if (nearest !== index && nearest >= 0 && nearest < count) {
|
|
124
|
-
go(nearest, true);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
React.useEffect(() => {
|
|
128
|
-
if (!autoPlay || paused || count < 2) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
// A reader who has asked for less motion has asked for this in particular.
|
|
132
|
-
if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const timer = window.setInterval(() => {
|
|
136
|
-
if (document.hidden) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
go(index + 1);
|
|
140
|
-
}, interval);
|
|
141
|
-
return () => window.clearInterval(timer);
|
|
142
|
-
}, [autoPlay, paused, count, interval, index, go]);
|
|
143
|
-
const atStart = index <= 0;
|
|
144
|
-
const atEnd = index >= count - 1;
|
|
145
|
-
return (_jsxs("div", { ref: ref, role: "region", "aria-roledescription": "carousel", "aria-label": label, className: cx('flex flex-col', className), style: { ...surfaceSlots(color, elevation), ...style },
|
|
146
|
-
// Hover and focus both stop the timer. The second one is the important
|
|
147
|
-
// one: a keyboard reader who has tabbed into a slide is reading it.
|
|
148
|
-
onPointerEnter: () => setPaused(true), onPointerLeave: () => setPaused(false), onFocus: () => setPaused(true), onBlur: () => setPaused(false), ...props, children: [_jsxs("div", { className: cx('relative min-w-0 overflow-hidden', radiusClasses[size], sheetRestClasses[variant], transitionClasses), children: [_jsx("div", { ref: trackRef,
|
|
149
|
-
// Focusable, so the strip can be scrolled with the arrow keys by
|
|
150
|
-
// whoever is not using a pointer. That is the browser's own key
|
|
151
|
-
// handling on a scroll container, which means it is already right
|
|
152
|
-
// under RTL — a handler of ours mapping ArrowRight to "next" would
|
|
153
|
-
// not have been.
|
|
154
|
-
tabIndex: 0, role: "group", "aria-label": label, className: cx('flex min-w-0 snap-x snap-mandatory overflow-x-auto scroll-smooth', 'motion-reduce:scroll-auto',
|
|
155
|
-
// The strip is driven by buttons and by dragging; a scrollbar
|
|
156
|
-
// under it is a third control saying the same thing.
|
|
157
|
-
'[scrollbar-width:none] [&::-webkit-scrollbar]:hidden', 'focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:[outline-offset:-2px]'), onScroll: handleScroll, children: slides.map((slide, slideIndex) => (_jsx("div", { ref: (element) => {
|
|
158
|
-
slideRefs.current[slideIndex] = element;
|
|
159
|
-
}, role: "group", "aria-roledescription": "slide", "aria-label": nameSlide(slideIndex + 1, count),
|
|
160
|
-
// Deliberately *not* `aria-hidden` when off-screen. A slide can
|
|
161
|
-
// hold a link or a button, and an `aria-hidden` subtree that is
|
|
162
|
-
// still in the tab order is the exact shape of the bug where a
|
|
163
|
-
// keyboard reader lands somewhere their screen reader refuses
|
|
164
|
-
// to describe. The strip is scrollable, so everything in it is
|
|
165
|
-
// genuinely reachable — hiding it would be a lie.
|
|
166
|
-
className: "w-full shrink-0 grow-0 basis-full snap-start", children: slide }, slideIndex))) }), arrows && count > 1 ? (_jsxs("div", { className: cx('pointer-events-none absolute inset-y-0 flex items-center', arrowInsetClasses[size]), children: [_jsx(PlIconButton, { variant: "glass", size: size, color: color, density: density, elevation: 1, label: previousLabel, disabled: !loop && atStart, className: "pointer-events-auto",
|
|
167
|
-
// Drawn pointing down and turned, which is the one allowance the
|
|
168
|
-
// no-transform rule makes — and turned the other way under RTL,
|
|
169
|
-
// where "previous" is on the other side of the frame.
|
|
170
|
-
icon: _jsx("span", { className: "flex rotate-90 items-center rtl:-rotate-90", children: _jsx(ChevronIcon, {}) }), onClick: () => go(index - 1) }), _jsx("span", { className: "flex-1" }), _jsx(PlIconButton, { variant: "glass", size: size, color: color, density: density, elevation: 1, label: nextLabel, disabled: !loop && atEnd, className: "pointer-events-auto", icon: _jsx("span", { className: "flex -rotate-90 items-center rtl:rotate-90", children: _jsx(ChevronIcon, {}) }), onClick: () => go(index + 1) })] })) : null] }), indicators && count > 1 ? (_jsx("div", { className: cx('flex shrink-0 items-center justify-center pt-2', dotClasses[size].gap), children: slides.map((_, dotIndex) => (_jsx("button", { type: "button", "aria-label": nameSlide(dotIndex + 1, count), "aria-current": dotIndex === index ? 'true' : undefined, className: cx('cursor-pointer rounded-full',
|
|
171
|
-
// Width and colour, never a transform: the current dot grows
|
|
172
|
-
// along the row instead of scaling, so nothing beside it
|
|
173
|
-
// moves.
|
|
174
|
-
'[transition-property:width,background-color]', '[transition-duration:var(--plass-duration)]', '[transition-timing-function:var(--plass-ease)]', 'focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:outline-offset-2', dotIndex === index
|
|
175
|
-
? `${dotClasses[size].current} bg-(--p-accent)`
|
|
176
|
-
: `${dotClasses[size].rest} bg-(--plass-border) hover:bg-(--p-accent)`), onClick: () => go(dotIndex) }, dotIndex))) })) : null, _jsx("span", { className: srOnlyClasses, "aria-live": autoPlay ? 'off' : 'polite', children: count > 0 ? nameSlide(index + 1, count) : '' })] }));
|
|
177
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{PlIconButton as a}from"../icon-button/PlIconButton.js";import{ChevronIcon as n}from"../../internal/icons.js";import{cx as s,radiusClasses as l,sheetRestClasses as i,srOnlyClasses as o,surfaceSlots as c,transitionClasses as u}from"../../internal/styles.js";const d={xs:"start-1 end-1",sm:"start-1.5 end-1.5",md:"start-2 end-2",lg:"start-3 end-3",xl:"start-4 end-4"},f={xs:{rest:"h-1 w-1",current:"h-1 w-3",gap:"gap-1"},sm:{rest:"h-1 w-1",current:"h-1 w-3.5",gap:"gap-1"},md:{rest:"h-1.5 w-1.5",current:"h-1.5 w-4",gap:"gap-1.5"},lg:{rest:"h-1.5 w-1.5",current:"h-1.5 w-5",gap:"gap-2"},xl:{rest:"h-2 w-2",current:"h-2 w-6",gap:"gap-2"}};export const PlCarousel=/* @__PURE__ */t.forwardRef(function PlCarousel({variant:m="glass",size:p="md",color:h="primary",density:v="default",elevation:b=0,value:w,defaultValue:g=0,onValueChange:x,loop:N=!0,autoPlay:y=!1,interval:k=5e3,arrows:C=!0,indicators:M=!0,label:j="Carousel",previousLabel:P="Previous slide",nextLabel:R="Next slide",slideLabel:I,className:L,style:S,children:$,..._},z){const E=I??((e,r)=>`Slide ${e} of ${r}`),V=t.Children.toArray($),B=V.length,[T,W]=t.useState(g),A=Math.min(Math.max(w??T,0),Math.max(B-1,0)),F=t.useRef(null),q=t.useRef([]),D=t.useRef(!1),G=t.useRef(!1),H=t.useRef(!1),[J,K]=t.useState(!1),O=t.useCallback((e,r=!1)=>{if(0===B)return;const t=N?(e%B+B)%B:Math.min(Math.max(e,0),B-1);D.current=r,void 0===w&&W(t),t!==A&&x?.(t)},[B,N,w,A,x]);t.useEffect(()=>{if(D.current)return void(D.current=!1);if(!G.current)return void(G.current=!0);q.current[A]?.scrollIntoView({block:"nearest",inline:"start"}),H.current=!0;const e=window.setTimeout(()=>{H.current=!1},700);return()=>window.clearTimeout(e)},[A]),t.useEffect(()=>{if(!y||J||B<2)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;const e=window.setInterval(()=>{document.hidden||O(A+1)},k);return()=>window.clearInterval(e)},[y,J,B,k,A,O]);const Q=A<=0,U=A>=B-1;return r("div",{ref:z,role:"region","aria-roledescription":"carousel","aria-label":j,className:s("flex flex-col",L),style:{...c(h,b),...S},onPointerEnter:()=>K(!0),onPointerLeave:()=>K(!1),onFocus:()=>K(!0),onBlur:()=>K(!1),..._,children:[r("div",{className:s("relative min-w-0 overflow-hidden",l[p],i[m],u),children:[e("div",{ref:F,tabIndex:0,role:"group","aria-label":j,className:s("flex min-w-0 snap-x snap-mandatory overflow-x-auto scroll-smooth","motion-reduce:scroll-auto","[scrollbar-width:none] [&::-webkit-scrollbar]:hidden","focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:[outline-offset:-2px]"),onScroll:()=>{const e=F.current;if(H.current||!e||0===e.clientWidth)return;const r=Math.round(Math.abs(e.scrollLeft)/e.clientWidth);r!==A&&r>=0&&r<B&&O(r,!0)},children:V.map((r,t)=>e("div",{ref:e=>{q.current[t]=e},role:"group","aria-roledescription":"slide","aria-label":E(t+1,B),className:"w-full shrink-0 grow-0 basis-full snap-start",children:r},t))}),C&&B>1?r("div",{className:s("pointer-events-none absolute inset-y-0 flex items-center",d[p]),children:[e(a,{variant:"glass",size:p,color:h,density:v,elevation:1,label:P,disabled:!N&&Q,className:"pointer-events-auto",icon:e("span",{className:"flex rotate-90 items-center rtl:-rotate-90",children:e(n,{})}),onClick:()=>O(A-1)}),e("span",{className:"flex-1"}),e(a,{variant:"glass",size:p,color:h,density:v,elevation:1,label:R,disabled:!N&&U,className:"pointer-events-auto",icon:e("span",{className:"flex -rotate-90 items-center rtl:rotate-90",children:e(n,{})}),onClick:()=>O(A+1)})]}):null]}),M&&B>1?e("div",{className:s("flex shrink-0 items-center justify-center pt-2",f[p].gap),children:V.map((r,t)=>e("button",{type:"button","aria-label":E(t+1,B),"aria-current":t===A?"true":void 0,className:s("cursor-pointer rounded-full","[transition-property:width,background-color]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]","focus-visible:[outline:2px_solid_var(--p-ring)] focus-visible:outline-offset-2",t===A?`${f[p].current} bg-(--p-accent)`:`${f[p].rest} bg-(--plass-border) hover:bg-(--p-accent)`),onClick:()=>O(t)},t))}):null,e("span",{className:o,"aria-live":y?"off":"polite",children:B>0?E(A+1,B):""})]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlCarousel}from"./PlCarousel.js";
|
|
@@ -1,206 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { CheckIcon, ClockIcon, LinkIcon, severityIcon } from '../../internal/icons.js';
|
|
4
|
-
import { controlSlots, focusRingClasses, glassClasses, hasContent, metaTextClasses, radiusClasses, sheetBodyClasses, sheetHeaderGapClasses, srOnlyClasses, transitionClasses } from '../../internal/styles.js';
|
|
5
|
-
/**
|
|
6
|
-
* A bubble's own padding track, tighter than a sheet's.
|
|
7
|
-
*
|
|
8
|
-
* A sheet is a region of a page and a bubble is a sentence with a surface behind
|
|
9
|
-
* it: 20px of padding around eight words is a card, not a message. Both axes,
|
|
10
|
-
* because unlike a control a bubble has no height to fight.
|
|
11
|
-
*/
|
|
12
|
-
const bubblePaddingClasses = {
|
|
13
|
-
default: {
|
|
14
|
-
xs: 'px-2 py-1',
|
|
15
|
-
sm: 'px-2.5 py-1.5',
|
|
16
|
-
md: 'px-3 py-2',
|
|
17
|
-
lg: 'px-3.5 py-2.5',
|
|
18
|
-
xl: 'px-4 py-3'
|
|
19
|
-
},
|
|
20
|
-
compact: {
|
|
21
|
-
xs: 'px-1.5 py-0.5',
|
|
22
|
-
sm: 'px-2 py-1',
|
|
23
|
-
md: 'px-2.5 py-1.5',
|
|
24
|
-
lg: 'px-3 py-1.5',
|
|
25
|
-
xl: 'px-3.5 py-2'
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* The corner nearest the speaker is cut short.
|
|
30
|
-
*
|
|
31
|
-
* This is the library's one piece of chat vocabulary, and it does the job a
|
|
32
|
-
* drawn tail does elsewhere: it says which end of the row the message came from,
|
|
33
|
-
* without hanging a triangle off a sheet of glass that is supposed to have been
|
|
34
|
-
* cut with a straight edge. Written as the logical properties rather than as
|
|
35
|
-
* `rounded-tl`, so a thread in Arabic squares the other corner without being
|
|
36
|
-
* told.
|
|
37
|
-
*
|
|
38
|
-
* A flat 4px rather than a step down the radius ladder, and that is the whole
|
|
39
|
-
* point of writing it out: the ladder runs 8px to 16px, so `--plass-radius-xs`
|
|
40
|
-
* against `--plass-radius-md` is a four-pixel difference nobody would ever read
|
|
41
|
-
* as meaning something. The cut has to be obvious at a glance in a column of
|
|
42
|
-
* forty messages or it is not saying anything.
|
|
43
|
-
*/
|
|
44
|
-
const tailClasses = {
|
|
45
|
-
start: '[border-start-start-radius:0.25rem]',
|
|
46
|
-
end: '[border-start-end-radius:0.25rem]'
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* The three materials.
|
|
50
|
-
*
|
|
51
|
-
* A bubble *is* the thing being coloured — unlike a `PlCard`, which holds other
|
|
52
|
-
* people's content and so keeps its sheet undyed — so `solid` floods it and the
|
|
53
|
-
* text switches to `--p-on-solid`. That is what makes a column of your own
|
|
54
|
-
* messages read as yours at a glance rather than one line at a time.
|
|
55
|
-
*/
|
|
56
|
-
const variantClasses = {
|
|
57
|
-
solid: /* @__PURE__ */ [
|
|
58
|
-
'text-(--p-on-solid) [background-image:var(--p-fill)]',
|
|
59
|
-
'[box-shadow:var(--p-elev),var(--p-lift)]'
|
|
60
|
-
].join(' '),
|
|
61
|
-
glass: /* @__PURE__ */ [
|
|
62
|
-
glassClasses,
|
|
63
|
-
'border text-(--plass-fg) bg-(--plass-glass)',
|
|
64
|
-
'[border-color:var(--plass-glass-line)]',
|
|
65
|
-
'[box-shadow:var(--p-elev),var(--plass-gloss-glass)]'
|
|
66
|
-
].join(' '),
|
|
67
|
-
ghost: 'text-(--plass-fg) bg-(--p-soft) [box-shadow:var(--p-elev)]'
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* The double tick, drawn here rather than in `internal/icons` because it is the
|
|
71
|
-
* one glyph in the library only one component has any use for.
|
|
72
|
-
*
|
|
73
|
-
* Two ticks overlapping by a third of their width, which is what says "two"
|
|
74
|
-
* without doubling the width of the mark — a delivered message and a sent one
|
|
75
|
-
* have to be told apart at 12px, side by side, in a column.
|
|
76
|
-
*/
|
|
77
|
-
function DoubleCheckIcon() {
|
|
78
|
-
return (_jsxs("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "m1.5 8.5 2.75 2.75L9.5 6", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M7 10.75 8.25 12l5.25-5.25", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
79
|
-
}
|
|
80
|
-
/** One glyph per step. `read` shares the mark with `delivered` and takes the colour. */
|
|
81
|
-
const statusIcons = {
|
|
82
|
-
sending: _jsx(ClockIcon, {}),
|
|
83
|
-
sent: _jsx(CheckIcon, {}),
|
|
84
|
-
delivered: _jsx(DoubleCheckIcon, {}),
|
|
85
|
-
read: _jsx(DoubleCheckIcon, {}),
|
|
86
|
-
failed: /* @__PURE__ */ severityIcon('danger')
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* What each mark is read out as. English, like every other default string in the
|
|
90
|
-
* library — a page already knows its own language, and `statusLabel` is where it
|
|
91
|
-
* says so.
|
|
92
|
-
*/
|
|
93
|
-
const statusLabels = {
|
|
94
|
-
sending: 'Sending',
|
|
95
|
-
sent: 'Sent',
|
|
96
|
-
delivered: 'Delivered',
|
|
97
|
-
read: 'Read',
|
|
98
|
-
failed: 'Not delivered'
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Only two of the five carry a colour: the one that arrived and the one that did
|
|
102
|
-
* not. The three in between are the ordinary course of events, and a thread
|
|
103
|
-
* where every message is marked in colour is a thread where the colour has
|
|
104
|
-
* stopped meaning anything.
|
|
105
|
-
*/
|
|
106
|
-
const statusToneClasses = {
|
|
107
|
-
sending: 'text-(--plass-muted-fg)',
|
|
108
|
-
sent: 'text-(--plass-muted-fg)',
|
|
109
|
-
delivered: 'text-(--plass-muted-fg)',
|
|
110
|
-
read: 'text-(--p-accent)',
|
|
111
|
-
failed: 'text-(--plass-danger-accent)'
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* The card a link unfurls into.
|
|
115
|
-
*
|
|
116
|
-
* Its surface is mixed out of `currentColor` rather than out of a token, because
|
|
117
|
-
* it is the one part of a bubble that has to work on both a filled surface and a
|
|
118
|
-
* bare one: on `solid` the text is white and the card is a white wash, on
|
|
119
|
-
* `glass` the text is the page's ink and the card is a grey one. A fixed token
|
|
120
|
-
* would be invisible against one of the two.
|
|
121
|
-
*/
|
|
122
|
-
const previewSurfaceClasses = /* @__PURE__ */ [
|
|
123
|
-
'block overflow-hidden rounded-(--plass-radius-sm) border no-underline',
|
|
124
|
-
'[border-color:color-mix(in_oklab,currentColor_18%,transparent)]',
|
|
125
|
-
'[background-color:color-mix(in_oklab,currentColor_7%,transparent)]',
|
|
126
|
-
'hover:[background-color:color-mix(in_oklab,currentColor_12%,transparent)]',
|
|
127
|
-
'[transition-property:background-color] [transition-duration:var(--plass-duration)]',
|
|
128
|
-
'[transition-timing-function:var(--plass-ease)]',
|
|
129
|
-
focusRingClasses
|
|
130
|
-
].join(' ');
|
|
131
|
-
/**
|
|
132
|
-
* The affordance stays out of the way of the message until the row is reached
|
|
133
|
-
* for — the same allowance a chip's × takes, and for the same reason: this is
|
|
134
|
-
* not a control changing what it is, it is a handle that would otherwise sit in
|
|
135
|
-
* the middle of a conversation being read.
|
|
136
|
-
*
|
|
137
|
-
* A pointer that cannot hover has nothing to reveal it, so it is simply always
|
|
138
|
-
* there on touch.
|
|
139
|
-
*/
|
|
140
|
-
const actionsClasses = /* @__PURE__ */ [
|
|
141
|
-
'shrink-0 opacity-0',
|
|
142
|
-
'[transition:opacity_var(--plass-duration)_var(--plass-ease)]',
|
|
143
|
-
'group-hover/bubble:opacity-100 group-focus-within/bubble:opacity-100',
|
|
144
|
-
'[@media(hover:none)]:opacity-100'
|
|
145
|
-
].join(' ');
|
|
146
|
-
/**
|
|
147
|
-
* One message in a conversation.
|
|
148
|
-
*
|
|
149
|
-
* Everything around the bubble is optional and nothing about it is fixed by
|
|
150
|
-
* `side`: the avatar, the sender's name, the time, the delivery mark, the media
|
|
151
|
-
* above the text and the link card below it are each drawn only when they are
|
|
152
|
-
* given something. What `side` decides is which way the row runs and which
|
|
153
|
-
* corner of the sheet is cut short.
|
|
154
|
-
*
|
|
155
|
-
* `variant` is what tells your own messages from everyone else's, and it is
|
|
156
|
-
* deliberately not tied to `side` — filling the right-hand column is a
|
|
157
|
-
* convention, not a law, and a thread that fills neither is a perfectly good
|
|
158
|
-
* thread.
|
|
159
|
-
*/
|
|
160
|
-
export const PlChatBubble = /* @__PURE__ */ React.forwardRef(function PlChatBubble({ side = 'start', name, time, avatar, status, statusLabel, typing = false, typingLabel = 'Typing…', media, preview, actions, variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 0, className, style, children, ...props }, ref) {
|
|
161
|
-
const end = side === 'end';
|
|
162
|
-
const hasHeader = hasContent(name) || hasContent(time);
|
|
163
|
-
const hasBody = typing || hasContent(children) || Boolean(preview);
|
|
164
|
-
const bubbleClasses = [
|
|
165
|
-
'flex min-w-0 flex-col overflow-hidden',
|
|
166
|
-
radiusClasses[size],
|
|
167
|
-
tailClasses[side],
|
|
168
|
-
variantClasses[variant],
|
|
169
|
-
transitionClasses
|
|
170
|
-
].join(' ');
|
|
171
|
-
return (_jsxs("div", { ref: ref, className: [
|
|
172
|
-
'group/bubble flex w-full items-start gap-2',
|
|
173
|
-
end ? 'flex-row-reverse' : '',
|
|
174
|
-
className ?? ''
|
|
175
|
-
]
|
|
176
|
-
.filter(Boolean)
|
|
177
|
-
.join(' '), style: { ...controlSlots(color, elevation, variant), ...style }, ...props, children: [hasContent(avatar) ? _jsx("div", { className: "shrink-0", children: avatar }) : null, _jsxs("div", { className: [
|
|
178
|
-
'flex max-w-[min(100%,32rem)] min-w-0 flex-col',
|
|
179
|
-
sheetHeaderGapClasses[size],
|
|
180
|
-
end ? 'items-end' : 'items-start'
|
|
181
|
-
].join(' '), children: [hasHeader ? (_jsxs("div", { className: `flex items-baseline gap-2 ${metaTextClasses[size]}`, children: [hasContent(name) ? _jsx("span", { className: "font-semibold", children: name }) : null, hasContent(time) ? _jsx("span", { className: "text-(--plass-muted-fg)", children: time }) : null] })) : null, _jsxs("div", { className: `flex min-w-0 items-center gap-1 ${end ? 'flex-row-reverse' : ''}`, children: [_jsxs("div", { className: bubbleClasses, children: [hasContent(media) ? (_jsx("div", { className: "[&_img]:block [&_img]:w-full [&_video]:block [&_video]:w-full", children: media })) : null, hasBody ? (_jsxs("div", { className: [
|
|
182
|
-
bubblePaddingClasses[density][size],
|
|
183
|
-
'flex min-w-0 flex-col gap-2',
|
|
184
|
-
sheetBodyClasses[size]
|
|
185
|
-
].join(' '), children: [typing ? (_jsx(TypingDots, { label: typingLabel })) : hasContent(children) ? (_jsx("div", { className: "min-w-0 break-words whitespace-pre-line", children: children })) : null, preview ? _jsx(LinkPreview, { preview: preview }) : null] })) : null] }), hasContent(actions) ? _jsx("div", { className: actionsClasses, children: actions }) : null] }), status ? (_jsxs("div", { className: [
|
|
186
|
-
'flex items-center gap-1',
|
|
187
|
-
metaTextClasses[size],
|
|
188
|
-
statusToneClasses[status],
|
|
189
|
-
'[&_svg]:size-[1.15em] [&_svg]:shrink-0'
|
|
190
|
-
].join(' '), children: [statusIcons[status], _jsx("span", { className: srOnlyClasses, children: statusLabel ?? statusLabels[status] })] })) : null] })] }));
|
|
191
|
-
});
|
|
192
|
-
/**
|
|
193
|
-
* Three dots that light in sequence.
|
|
194
|
-
*
|
|
195
|
-
* Colour only, like every other indeterminate indicator in the library — the
|
|
196
|
-
* dots never move, so a bubble that is being typed into does not bounce in a
|
|
197
|
-
* thread somebody is reading. The delay is carried per dot in `--p-i`.
|
|
198
|
-
*/
|
|
199
|
-
function TypingDots({ label }) {
|
|
200
|
-
return (_jsxs("div", { className: "flex items-center gap-1 py-[0.35em]", role: "status", children: [[0, 1, 2].map((index) => (_jsx("span", { "aria-hidden": "true", className: "plass-typing-dot size-[0.45em] rounded-full", style: { '--p-i': index } }, index))), _jsx("span", { className: srOnlyClasses, children: label })] }));
|
|
201
|
-
}
|
|
202
|
-
/** The unfurled link: an image, who published it, a title and two lines of summary. */
|
|
203
|
-
function LinkPreview({ preview }) {
|
|
204
|
-
const { url, title, description, image, site, newTab = false } = preview;
|
|
205
|
-
return (_jsxs("a", { href: url, target: newTab ? '_blank' : undefined, rel: newTab ? 'noopener noreferrer' : undefined, className: previewSurfaceClasses, children: [image ? (_jsx("img", { src: image, alt: "", className: "block h-28 w-full object-cover" })) : null, _jsxs("div", { className: "flex flex-col gap-0.5 p-2", children: [hasContent(site) ? (_jsxs("span", { className: "flex items-center gap-1 text-[0.85em] opacity-70 [&_svg]:size-[1em] [&_svg]:shrink-0", children: [_jsx(LinkIcon, {}), _jsx("span", { className: "truncate", children: site })] })) : null, hasContent(title) ? _jsx("span", { className: "font-semibold", children: title }) : null, hasContent(description) ? (_jsx("span", { className: "line-clamp-2 text-[0.9em] opacity-80", children: description })) : null] })] }));
|
|
206
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as s from"react";import{CheckIcon as a,ClockIcon as r,LinkIcon as n,severityIcon as i}from"../../internal/icons.js";import{controlSlots as t,focusRingClasses as o,glassClasses as d,hasContent as c,metaTextClasses as p,radiusClasses as m,sheetBodyClasses as u,sheetHeaderGapClasses as v,srOnlyClasses as f,transitionClasses as g}from"../../internal/styles.js";const x={default:{xs:"px-2 py-1",sm:"px-2.5 py-1.5",md:"px-3 py-2",lg:"px-3.5 py-2.5",xl:"px-4 py-3"},compact:{xs:"px-1.5 py-0.5",sm:"px-2 py-1",md:"px-2.5 py-1.5",lg:"px-3 py-1.5",xl:"px-3.5 py-2"}},h={start:"[border-start-start-radius:0.25rem]",end:"[border-start-end-radius:0.25rem]"},b={solid:/* @__PURE__ */["text-(--p-on-solid) [background-image:var(--p-fill)]","[box-shadow:var(--p-elev),var(--p-lift)]"].join(" "),glass:/* @__PURE__ */[d,"border text-(--plass-fg) bg-(--plass-glass)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:"text-(--plass-fg) bg-(--p-soft) [box-shadow:var(--p-elev)]"};function DoubleCheckIcon(){return l("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e("path",{d:"m1.5 8.5 2.75 2.75L9.5 6",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M7 10.75 8.25 12l5.25-5.25",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round"})]})}const y={sending:e(r,{}),sent:e(a,{}),delivered:e(DoubleCheckIcon,{}),read:e(DoubleCheckIcon,{}),failed:/* @__PURE__ */i("danger")},k={sending:"Sending",sent:"Sent",delivered:"Delivered",read:"Read",failed:"Not delivered"},w={sending:"text-(--plass-muted-fg)",sent:"text-(--plass-muted-fg)",delivered:"text-(--plass-muted-fg)",read:"text-(--p-accent)",failed:"text-(--plass-danger-accent)"},N=/* @__PURE__ */["block overflow-hidden rounded-(--plass-radius-sm) border no-underline","[border-color:color-mix(in_oklab,currentColor_18%,transparent)]","[background-color:color-mix(in_oklab,currentColor_7%,transparent)]","hover:[background-color:color-mix(in_oklab,currentColor_12%,transparent)]","[transition-property:background-color] [transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]",o].join(" "),j=/* @__PURE__ */["shrink-0 opacity-0","[transition:opacity_var(--plass-duration)_var(--plass-ease)]","group-hover/bubble:opacity-100 group-focus-within/bubble:opacity-100","[@media(hover:none)]:opacity-100"].join(" ");export const PlChatBubble=/* @__PURE__ */s.forwardRef(function PlChatBubble({side:s="start",name:a,time:r,avatar:n,status:i,statusLabel:o,typing:d=!1,typingLabel:N="Typing…",media:_,preview:C,actions:L,variant:D="glass",size:B="md",color:z="primary",density:P="default",elevation:T=0,className:I,style:R,children:S,...W},M){const $="end"===s,q=c(a)||c(r),A=d||c(S)||Boolean(C),E=["flex min-w-0 flex-col overflow-hidden",m[B],h[s],b[D],g].join(" ");return l("div",{ref:M,className:["group/bubble flex w-full items-start gap-2",$?"flex-row-reverse":"",I??""].filter(Boolean).join(" "),style:{...t(z,T,D),...R},...W,children:[c(n)?e("div",{className:"shrink-0",children:n}):null,l("div",{className:["flex max-w-[min(100%,32rem)] min-w-0 flex-col",v[B],$?"items-end":"items-start"].join(" "),children:[q?l("div",{className:`flex items-baseline gap-2 ${p[B]}`,children:[c(a)?e("span",{className:"font-semibold",children:a}):null,c(r)?e("span",{className:"text-(--plass-muted-fg)",children:r}):null]}):null,l("div",{className:"flex min-w-0 items-center gap-1 "+($?"flex-row-reverse":""),children:[l("div",{className:E,children:[c(_)?e("div",{className:"[&_img]:block [&_img]:w-full [&_video]:block [&_video]:w-full",children:_}):null,A?l("div",{className:[x[P][B],"flex min-w-0 flex-col gap-2",u[B]].join(" "),children:[d?e(TypingDots,{label:N}):c(S)?e("div",{className:"min-w-0 break-words whitespace-pre-line",children:S}):null,C?e(LinkPreview,{preview:C}):null]}):null]}),c(L)?e("div",{className:j,children:L}):null]}),i?l("div",{className:["flex items-center gap-1",p[B],w[i],"[&_svg]:size-[1.15em] [&_svg]:shrink-0"].join(" "),children:[y[i],e("span",{className:f,children:o??k[i]})]}):null]})]})});function TypingDots({label:s}){return l("div",{className:"flex items-center gap-1 py-[0.35em]",role:"status",children:[[0,1,2].map(l=>e("span",{"aria-hidden":"true",className:"plass-typing-dot size-[0.45em] rounded-full",style:{"--p-i":l}},l)),e("span",{className:f,children:s})]})}function LinkPreview({preview:s}){const{url:a,title:r,description:i,image:t,site:o,newTab:d=!1}=s;return l("a",{href:a,target:d?"_blank":void 0,rel:d?"noopener noreferrer":void 0,className:N,children:[t?e("img",{src:t,alt:"",className:"block h-28 w-full object-cover"}):null,l("div",{className:"flex flex-col gap-0.5 p-2",children:[c(o)?l("span",{className:"flex items-center gap-1 text-[0.85em] opacity-70 [&_svg]:size-[1em] [&_svg]:shrink-0",children:[e(n,{}),e("span",{className:"truncate",children:o})]}):null,c(r)?e("span",{className:"font-semibold",children:r}):null,c(i)?e("span",{className:"line-clamp-2 text-[0.9em] opacity-80",children:i}):null]})]})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlChatBubble}from"./PlChatBubble.js";
|
|
@@ -1,100 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { Checkbox as BaseUICheckbox } from '@base-ui/react/checkbox';
|
|
4
|
-
import { Field } from '@base-ui/react/field';
|
|
5
|
-
import { controlSlots, focusRingClasses, glassClasses, hasContent, metaTextClasses, tickRadiusClasses, tickRowTextClasses, tickSizeClasses, transitionClasses } from '../../internal/styles.js';
|
|
6
|
-
/**
|
|
7
|
-
* The tick.
|
|
8
|
-
*
|
|
9
|
-
* Unchecked it is a small pane of clear glass with a hairline round it — the
|
|
10
|
-
* same material a `glass` button is. Checked it fills with the family's
|
|
11
|
-
* gradient, which is the one place this library expresses a state by swapping
|
|
12
|
-
* the whole surface rather than shifting it a step: "on" and "off" are not two
|
|
13
|
-
* strengths of the same thing.
|
|
14
|
-
*/
|
|
15
|
-
const tickBaseClasses = /* @__PURE__ */ [
|
|
16
|
-
'relative inline-flex shrink-0 items-center justify-center border',
|
|
17
|
-
'[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]',
|
|
18
|
-
transitionClasses,
|
|
19
|
-
focusRingClasses
|
|
20
|
-
].join(' ');
|
|
21
|
-
/**
|
|
22
|
-
* No gloss line on a tick, and no tinted lift under it.
|
|
23
|
-
*
|
|
24
|
-
* `--plass-gloss-glass` is a 1px white edge along the top of a pane. On a 40px
|
|
25
|
-
* button that reads as light catching a cut edge; on an 18px square it is a
|
|
26
|
-
* bevel drawn at a twentieth of the object and it turns into a grey smudge.
|
|
27
|
-
* The tinted lift goes for the same reason from the other direction — a
|
|
28
|
-
* `0 6px 16px` shadow under an 18px box is a shadow bigger than the box.
|
|
29
|
-
*
|
|
30
|
-
* The material stays. It is only the two decorations that go.
|
|
31
|
-
*
|
|
32
|
-
* The edge is `--plass-border` rather than the sheet's own `--plass-glass-line`,
|
|
33
|
-
* and that is not a slip. The glass hairline is white light on a translucent
|
|
34
|
-
* pane, which is invisible the moment the tick is set on a light card rather
|
|
35
|
-
* than on the page wash — and a tick nobody can see is a control nobody can
|
|
36
|
-
* find. A neutral hairline reads on both.
|
|
37
|
-
*/
|
|
38
|
-
const restClasses = /* @__PURE__ */ [
|
|
39
|
-
glassClasses,
|
|
40
|
-
'cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
41
|
-
'hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]',
|
|
42
|
-
// `data-checked` rather than `:checked`: the visible tick is a `<span>`, and
|
|
43
|
-
// the real input is hidden beside it.
|
|
44
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
45
|
-
'data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105',
|
|
46
|
-
'data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)',
|
|
47
|
-
'data-[indeterminate]:[border-color:transparent]'
|
|
48
|
-
].join(' ');
|
|
49
|
-
const readOnlyClasses = /* @__PURE__ */ [
|
|
50
|
-
glassClasses,
|
|
51
|
-
'cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
52
|
-
'saturate-[0.55]',
|
|
53
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
54
|
-
'data-[checked]:[border-color:transparent]'
|
|
55
|
-
].join(' ');
|
|
56
|
-
/** Disabled is the light going out, exactly as it is on a PlButton. */
|
|
57
|
-
const disabledTickClasses = /* @__PURE__ */ [
|
|
58
|
-
glassClasses,
|
|
59
|
-
'cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]',
|
|
60
|
-
'opacity-50 saturate-[0.35]',
|
|
61
|
-
'data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)',
|
|
62
|
-
'data-[checked]:[border-color:transparent]',
|
|
63
|
-
'data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)'
|
|
64
|
-
].join(' ');
|
|
65
|
-
/** The mark is drawn at 70% of the box, so it never touches the corners. */
|
|
66
|
-
const markClasses = 'flex size-[70%] items-center justify-center';
|
|
67
|
-
function CheckMark() {
|
|
68
|
-
return (_jsx("svg", { viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", className: "size-full", children: _jsx("path", { d: "M2 6.2 4.6 8.8 10 3.4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
69
|
-
}
|
|
70
|
-
function DashMark() {
|
|
71
|
-
return (_jsx("svg", { viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", className: "size-full", children: _jsx("path", { d: "M2.5 6h7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* A single yes/no, or one member of a set of them.
|
|
75
|
-
*
|
|
76
|
-
* `label`, `description` and `error` are props rather than children for the same
|
|
77
|
-
* reason they are on PlTextField: the arrangement is fixed and what a caller
|
|
78
|
-
* wants to decide is what goes in each slot. `children` is not accepted at all —
|
|
79
|
-
* anything a checkbox has to say belongs in one of the three.
|
|
80
|
-
*/
|
|
81
|
-
export const PlCheckbox = /* @__PURE__ */ React.forwardRef(function PlCheckbox({ size = 'md', color = 'primary', label, description, error, invalid, disabled = false, readOnly = false, className, style, ...props }, ref) {
|
|
82
|
-
const hasError = hasContent(error);
|
|
83
|
-
const isInvalid = invalid ?? hasError;
|
|
84
|
-
// Invalid re-points the whole slot family at `danger`, so the tick, the ring
|
|
85
|
-
// and the message all turn over together.
|
|
86
|
-
const family = isInvalid ? 'danger' : color;
|
|
87
|
-
const tickClasses = [
|
|
88
|
-
tickBaseClasses,
|
|
89
|
-
tickSizeClasses[size],
|
|
90
|
-
tickRadiusClasses[size],
|
|
91
|
-
// An if/else rather than stacked variants: two Tailwind classes of equal
|
|
92
|
-
// specificity resolve by their order in the generated stylesheet.
|
|
93
|
-
disabled ? disabledTickClasses : readOnly ? readOnlyClasses : restClasses
|
|
94
|
-
].join(' ');
|
|
95
|
-
return (_jsxs(Field.Root, { disabled: disabled, invalid: isInvalid, className: ['inline-flex flex-col gap-1 align-top', className ?? '']
|
|
96
|
-
.filter(Boolean)
|
|
97
|
-
.join(' '),
|
|
98
|
-
// `solid`, because a checked tick *is* the coloured thing.
|
|
99
|
-
style: { ...controlSlots(family, 0, 'solid'), ...style }, children: [_jsxs("div", { className: `flex items-start gap-2 ${tickRowTextClasses[size]}`, children: [_jsx("span", { className: "flex h-[1lh] shrink-0 items-center", children: _jsx(BaseUICheckbox.Root, { ref: ref, className: tickClasses, disabled: disabled, readOnly: readOnly, ...props, children: _jsx(BaseUICheckbox.Indicator, { className: markClasses, children: props.indeterminate ? _jsx(DashMark, {}) : _jsx(CheckMark, {}) }) }) }), label || description ? (_jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [label ? (_jsx(Field.Label, { className: disabled ? 'text-(--plass-muted-fg)' : 'cursor-pointer text-(--plass-fg)', children: label })) : null, description ? (_jsx(Field.Description, { className: `${metaTextClasses[size]} text-(--plass-muted-fg)`, children: description })) : null] })) : null] }), hasError ? (_jsx(Field.Error, { match: true, className: `${metaTextClasses[size]} text-(--p-accent)`, children: error })) : null] }));
|
|
100
|
-
});
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as a from"react";import{Checkbox as t}from"@base-ui/react/checkbox";import{Field as o}from"@base-ui/react/field";import{controlSlots as l,focusRingClasses as s,glassClasses as i,hasContent as n,metaTextClasses as c,tickRadiusClasses as d,tickRowTextClasses as p,tickSizeClasses as h,transitionClasses as m}from"../../internal/styles.js";const u=/* @__PURE__ */["relative inline-flex shrink-0 items-center justify-center border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",m,s].join(" "),f=/* @__PURE__ */[i,"cursor-pointer bg-(--plass-glass) [border-color:var(--plass-border)]","hover:bg-(--plass-glass-hover) hover:[border-color:var(--p-line)]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent] data-[checked]:hover:brightness-105","data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)","data-[indeterminate]:[border-color:transparent]"].join(" "),k=/* @__PURE__ */[i,"cursor-default bg-(--plass-glass) [border-color:var(--plass-border)]","saturate-[0.55]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]"].join(" "),b=/* @__PURE__ */[i,"cursor-not-allowed bg-(--plass-glass) [border-color:var(--plass-border)]","opacity-50 saturate-[0.35]","data-[checked]:[background-image:var(--p-fill)] data-[checked]:text-(--p-on-solid)","data-[checked]:[border-color:transparent]","data-[indeterminate]:[background-image:var(--p-fill)] data-[indeterminate]:text-(--p-on-solid)"].join(" ");function CheckMark(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2 6.2 4.6 8.8 10 3.4",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function DashMark(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2.5 6h7",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}export const PlCheckbox=/* @__PURE__ */a.forwardRef(function PlCheckbox({size:a="md",color:s="primary",label:i,description:m,error:g,invalid:x,disabled:v=!1,readOnly:j=!1,className:N,style:y,...C},w){const M=n(g),z=x??M,L=z?"danger":s,B=[u,h[a],d[a],v?b:j?k:f].join(" ");return r(o.Root,{disabled:v,invalid:z,className:["inline-flex flex-col gap-1 align-top",N??""].filter(Boolean).join(" "),style:{...l(L,0,"solid"),...y},children:[r("div",{className:`flex items-start gap-2 ${p[a]}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(t.Root,{ref:w,className:B,disabled:v,readOnly:j,...C,children:e(t.Indicator,{className:"flex size-[70%] items-center justify-center",children:C.indeterminate?e(DashMark,{}):e(CheckMark,{})})})}),i||m?r("span",{className:"flex min-w-0 flex-col gap-0.5",children:[i?e(o.Label,{className:v?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:i}):null,m?e(o.Description,{className:`${c[a]} text-(--plass-muted-fg)`,children:m}):null]}):null]}),M?e(o.Error,{match:!0,className:`${c[a]} text-(--p-accent)`,children:g}):null]})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{PlCheckbox}from"./PlCheckbox.js";
|