plass-ui 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +165 -0
  2. package/dist/components/accordion/PlAccordion.js +1 -131
  3. package/dist/components/accordion/index.js +1 -1
  4. package/dist/components/alert/PlAlert.js +1 -109
  5. package/dist/components/alert/index.js +1 -1
  6. package/dist/components/animate-appear/PlAnimateAppear.js +1 -71
  7. package/dist/components/animate-appear/index.js +1 -1
  8. package/dist/components/animate-blink/PlAnimateBlink.js +1 -47
  9. package/dist/components/animate-blink/index.js +1 -1
  10. package/dist/components/animate-fade/PlAnimateFade.js +1 -48
  11. package/dist/components/animate-fade/index.js +1 -1
  12. package/dist/components/animate-grow/PlAnimateGrow.js +1 -49
  13. package/dist/components/animate-grow/index.js +1 -1
  14. package/dist/components/animate-headline/PlAnimateHeadline.js +1 -120
  15. package/dist/components/animate-headline/index.js +1 -1
  16. package/dist/components/animate-lighting/PlAnimateLighting.js +1 -65
  17. package/dist/components/animate-lighting/index.js +1 -1
  18. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -94
  19. package/dist/components/animate-marquee/index.js +1 -1
  20. package/dist/components/animate-rotate/PlAnimateRotate.js +1 -50
  21. package/dist/components/animate-rotate/index.js +1 -1
  22. package/dist/components/animate-slide/PlAnimateSlide.js +1 -49
  23. package/dist/components/animate-slide/index.js +1 -1
  24. package/dist/components/animate-typing/PlAnimateTyping.js +1 -177
  25. package/dist/components/animate-typing/index.js +1 -1
  26. package/dist/components/animate-zoom/PlAnimateZoom.js +1 -45
  27. package/dist/components/animate-zoom/index.js +1 -1
  28. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -58
  29. package/dist/components/aspect-ratio/index.js +1 -1
  30. package/dist/components/avatar/PlAvatar.js +1 -121
  31. package/dist/components/avatar/index.js +1 -1
  32. package/dist/components/badge/PlBadge.js +1 -168
  33. package/dist/components/badge/index.js +1 -1
  34. package/dist/components/blockquote/PlBlockquote.js +1 -112
  35. package/dist/components/blockquote/index.js +1 -1
  36. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -141
  37. package/dist/components/bottom-navigation/index.js +1 -1
  38. package/dist/components/box/PlBox.js +1 -46
  39. package/dist/components/box/index.js +1 -1
  40. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -201
  41. package/dist/components/breadcrumb/index.js +1 -1
  42. package/dist/components/button/PlButton.js +1 -196
  43. package/dist/components/button/index.js +1 -1
  44. package/dist/components/button-group/PlButtonGroup.js +1 -64
  45. package/dist/components/button-group/index.js +1 -1
  46. package/dist/components/card/PlCard.js +1 -102
  47. package/dist/components/card/index.js +1 -1
  48. package/dist/components/carousel/PlCarousel.js +1 -177
  49. package/dist/components/carousel/index.js +1 -1
  50. package/dist/components/chat-bubble/PlChatBubble.js +1 -206
  51. package/dist/components/chat-bubble/index.js +1 -1
  52. package/dist/components/checkbox/PlCheckbox.js +1 -100
  53. package/dist/components/checkbox/index.js +1 -1
  54. package/dist/components/chip/PlChip.js +1 -144
  55. package/dist/components/chip/index.js +1 -1
  56. package/dist/components/collapsible/PlCollapsible.js +1 -65
  57. package/dist/components/collapsible/index.js +1 -1
  58. package/dist/components/combobox/PlCombobox.js +1 -199
  59. package/dist/components/combobox/index.js +1 -1
  60. package/dist/components/container/PlContainer.js +1 -47
  61. package/dist/components/container/index.js +1 -1
  62. package/dist/components/date-picker/PlDatePicker.js +1 -87
  63. package/dist/components/date-picker/index.js +1 -1
  64. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -152
  65. package/dist/components/date-range-picker/index.js +1 -1
  66. package/dist/components/date-time-picker/PlDateTimePicker.js +1 -106
  67. package/dist/components/date-time-picker/index.js +1 -1
  68. package/dist/components/divider/PlDivider.js +1 -107
  69. package/dist/components/divider/index.js +1 -1
  70. package/dist/components/drawer/PlDrawer.js +1 -190
  71. package/dist/components/drawer/index.js +1 -1
  72. package/dist/components/file-picker/PlFilePicker.js +1 -274
  73. package/dist/components/file-picker/index.js +1 -1
  74. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -291
  75. package/dist/components/floating-bottom-navigation/index.js +1 -1
  76. package/dist/components/grid/PlGrid.js +1 -51
  77. package/dist/components/grid/PlGridItem.js +1 -34
  78. package/dist/components/grid/index.js +1 -2
  79. package/dist/components/highlight/PlHighlight.js +1 -185
  80. package/dist/components/highlight/index.js +1 -1
  81. package/dist/components/hot-keys/PlHotKeys.js +1 -294
  82. package/dist/components/hot-keys/index.js +1 -1
  83. package/dist/components/icon/PlIcon.js +1 -58
  84. package/dist/components/icon/index.js +1 -1
  85. package/dist/components/icon-button/PlIconButton.js +1 -37
  86. package/dist/components/icon-button/index.js +1 -1
  87. package/dist/components/list/PlList.js +1 -160
  88. package/dist/components/list/index.js +1 -1
  89. package/dist/components/menu/PlMenu.js +1 -209
  90. package/dist/components/menu/index.js +1 -1
  91. package/dist/components/modal/PlModal.js +1 -150
  92. package/dist/components/modal/index.js +1 -1
  93. package/dist/components/number-field/PlNumberField.js +1 -111
  94. package/dist/components/number-field/index.js +1 -1
  95. package/dist/components/otp-field/PlOtpField.js +1 -109
  96. package/dist/components/otp-field/index.js +1 -1
  97. package/dist/components/overlay/PlOverlay.js +1 -77
  98. package/dist/components/overlay/index.js +1 -1
  99. package/dist/components/pagination/PlPagination.js +1 -159
  100. package/dist/components/pagination/index.js +1 -1
  101. package/dist/components/panes/PlPanes.js +1 -324
  102. package/dist/components/panes/index.js +1 -1
  103. package/dist/components/pill/PlPill.js +1 -154
  104. package/dist/components/pill/index.js +1 -1
  105. package/dist/components/popover/PlPopover.js +1 -113
  106. package/dist/components/popover/index.js +1 -1
  107. package/dist/components/progress-box/PlProgressBox.js +1 -34
  108. package/dist/components/progress-box/index.js +1 -1
  109. package/dist/components/progress-circular/PlProgressCircular.js +1 -47
  110. package/dist/components/progress-circular/index.js +1 -1
  111. package/dist/components/progress-linear/PlProgressLinear.js +1 -40
  112. package/dist/components/progress-linear/index.js +1 -1
  113. package/dist/components/radio-group/PlRadioGroup.js +1 -99
  114. package/dist/components/radio-group/index.js +1 -1
  115. package/dist/components/rating/PlRating.js +1 -101
  116. package/dist/components/rating/index.js +1 -1
  117. package/dist/components/scroll-zone/PlScrollZone.js +1 -411
  118. package/dist/components/scroll-zone/index.js +1 -1
  119. package/dist/components/segmented-button/PlSegmentedButton.js +1 -206
  120. package/dist/components/segmented-button/index.js +1 -1
  121. package/dist/components/select/PlSelect.js +1 -115
  122. package/dist/components/select/index.js +1 -1
  123. package/dist/components/skeleton/PlSkeleton.js +1 -142
  124. package/dist/components/skeleton/index.js +1 -1
  125. package/dist/components/slider/PlSlider.js +1 -132
  126. package/dist/components/slider/index.js +1 -1
  127. package/dist/components/spoiler/PlSpoiler.js +1 -74
  128. package/dist/components/spoiler/index.js +1 -1
  129. package/dist/components/switch/PlSwitch.js +1 -131
  130. package/dist/components/switch/index.js +1 -1
  131. package/dist/components/table/PlTable.js +1 -210
  132. package/dist/components/table/index.js +1 -1
  133. package/dist/components/tabs/PlTabs.js +1 -182
  134. package/dist/components/tabs/index.js +1 -1
  135. package/dist/components/text-field/PlTextField.js +1 -126
  136. package/dist/components/text-field/index.js +1 -1
  137. package/dist/components/text-link/PlTextLink.js +1 -136
  138. package/dist/components/text-link/index.js +1 -1
  139. package/dist/components/time-picker/PlTimePicker.js +1 -97
  140. package/dist/components/time-picker/index.js +1 -1
  141. package/dist/components/timeline/PlTimeline.js +1 -207
  142. package/dist/components/timeline/index.js +1 -1
  143. package/dist/components/toast/PlToast.js +1 -174
  144. package/dist/components/toast/index.js +1 -1
  145. package/dist/components/toolbar/PlToolbar.js +1 -65
  146. package/dist/components/toolbar/index.js +1 -1
  147. package/dist/components/tooltip/PlTooltip.js +1 -117
  148. package/dist/components/tooltip/index.js +1 -1
  149. package/dist/components/typography/PlTypography.js +1 -143
  150. package/dist/components/typography/index.js +1 -1
  151. package/dist/index.js +1 -75
  152. package/dist/internal/animate.js +1 -318
  153. package/dist/internal/button-group.js +2 -19
  154. package/dist/internal/calendar.js +1 -501
  155. package/dist/internal/date.js +1 -353
  156. package/dist/internal/format.js +1 -41
  157. package/dist/internal/grid.js +1 -143
  158. package/dist/internal/icons.js +1 -138
  159. package/dist/internal/menu.js +1 -18
  160. package/dist/internal/picker.js +1 -139
  161. package/dist/internal/progress.js +1 -167
  162. package/dist/internal/sizer.js +1 -35
  163. package/dist/internal/styles.js +1 -636
  164. package/dist/types.js +0 -8
  165. package/package.json +5 -3
@@ -1,64 +1 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { ButtonGroupContext } from '../../internal/button-group.js';
4
- import { cx } from '../../internal/styles.js';
5
- /**
6
- * The corners that face a neighbour are squared off, so the run reads as one
7
- * piece scored into segments rather than as three keys that happen to be
8
- * touching.
9
- *
10
- * Logical properties (`s`/`e`) rather than left/right: under RTL the first
11
- * button is on the right, and `rounded-l-none` would flatten the wrong side.
12
- */
13
- const joinClasses = {
14
- horizontal: '[&>*:not(:first-child)]:rounded-s-none [&>*:not(:last-child)]:rounded-e-none',
15
- vertical: '[&>*:not(:first-child)]:rounded-t-none [&>*:not(:last-child)]:rounded-b-none'
16
- };
17
- /**
18
- * Only `glass` needs the overlap, because it is the only variant that draws an
19
- * edge. Two glass keys meeting would otherwise show both of their hairlines and
20
- * the seam would be twice as heavy as every other edge on the page; pulling the
21
- * second one back a pixel makes the two share one line.
22
- *
23
- * A `solid` group must **not** do this. Its keys have no border to double up,
24
- * and overlapping would put one gradient over the start of the next — which is
25
- * exactly the join the squared corners are drawing.
26
- */
27
- const overlapClasses = {
28
- horizontal: '[&>*:not(:first-child)]:-ms-px',
29
- vertical: '[&>*:not(:first-child)]:-mt-px'
30
- };
31
- const baseClasses = /* @__PURE__ */ [
32
- 'inline-flex align-middle',
33
- // Every child gets a stacking context so the hovered or focused one can come
34
- // forward. Without it the focus ring — which is drawn outside the border box —
35
- // is painted over by whichever neighbour happens to come after it.
36
- '[&>*]:relative [&>*:hover]:z-10 [&>*:focus-visible]:z-10',
37
- // A group is a set of equal actions, so they stay the same height even when
38
- // one of them has an icon and the others do not.
39
- '[&>*]:shrink-0'
40
- ].join(' ');
41
- /**
42
- * A run of buttons that belong together.
43
- *
44
- * Two things are happening here and only one of them is visual. The corners
45
- * that face a neighbour are squared off — that is the look. The other half is
46
- * that `variant`, `size`, `color`, `density`, `elevation` and `disabled` are
47
- * stated once for the set rather than repeated on every button; a group where
48
- * one button is a size out is the failure this exists to prevent.
49
- *
50
- * The buttons stay real `PlButton`s and nothing about them is replaced. Which
51
- * also means this is **not** a segmented control and it does not manage
52
- * selection: for one-of-a-set reach for `PlSegmentedButton`, which is what that
53
- * actually is.
54
- */
55
- export const PlButtonGroup = /* @__PURE__ */ React.forwardRef(function PlButtonGroup({ variant, size, color, density, elevation, orientation = 'horizontal', disabled, fullWidth = false, className, children, ...props }, ref) {
56
- // Every value is passed through as it arrived, `undefined` included. A
57
- // PlButton reads the group only as a fallback, so "not set here" goes on
58
- // meaning "use the button's own default" rather than turning into one.
59
- const context = React.useMemo(() => ({ variant, size, color, density, elevation, disabled }), [variant, size, color, density, elevation, disabled]);
60
- return (_jsx(ButtonGroupContext.Provider, { value: context, children: _jsx("div", { ref: ref, role: "group", className: cx(baseClasses, orientation === 'vertical' ? 'flex-col' : 'flex-row', joinClasses[orientation],
61
- // `variant` defaults to `solid` on a PlButton, so a group that says
62
- // nothing is a solid group and must not overlap.
63
- (variant ?? 'solid') === 'glass' ? overlapClasses[orientation] : '', fullWidth ? 'flex w-full [&>*]:flex-1' : '', className), ...props, children: children }) }));
64
- });
1
+ "use client";import{jsx as o}from"react/jsx-runtime";import*as e from"react";import{ButtonGroupContext as i}from"../../internal/button-group.js";import{cx as t}from"../../internal/styles.js";const n={horizontal:"[&>*:not(:first-child)]:rounded-s-none [&>*:not(:last-child)]:rounded-e-none",vertical:"[&>*:not(:first-child)]:rounded-t-none [&>*:not(:last-child)]:rounded-b-none"},r={horizontal:"[&>*:not(:first-child)]:-ms-px",vertical:"[&>*:not(:first-child)]:-mt-px"},l=/* @__PURE__ */["inline-flex align-middle","[&>*]:relative [&>*:hover]:z-10 [&>*:focus-visible]:z-10","[&>*]:shrink-0"].join(" ");export const PlButtonGroup=/* @__PURE__ */e.forwardRef(function PlButtonGroup({variant:s,size:a,color:d,density:c,elevation:f,orientation:u="horizontal",disabled:h,fullWidth:m=!1,className:v,children:p,...x},z){const b=e.useMemo(()=>({variant:s,size:a,color:d,density:c,elevation:f,disabled:h}),[s,a,d,c,f,h]);return o(i.Provider,{value:b,children:o("div",{ref:z,role:"group",className:t(l,"vertical"===u?"flex-col":"flex-row",n[u],"glass"===(s??"solid")?r[u]:"",m?"flex w-full [&>*]:flex-1":"",v),...x,children:p})})});
@@ -1 +1 @@
1
- export { PlButtonGroup } from './PlButtonGroup.js';
1
+ export{PlButtonGroup}from"./PlButtonGroup.js";
@@ -1,102 +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 { focusRingClasses, hasContent, metaTextClasses, radiusClasses, sheetBodyClasses, sheetHeaderGapClasses, sheetLineClasses, sheetPaddingXClasses, sheetPaddingYClasses, sheetRestClasses, sheetSectionGapClasses, sheetTitleClasses, surfaceSlots } from '../../internal/styles.js';
5
- /**
6
- * The house transition with `transform` added to the property list.
7
- *
8
- * `transitionClasses` deliberately leaves it out, because no *control* may be
9
- * transformed. A card is the other kind of surface, so it writes its own list
10
- * rather than stacking a `transition-[transform]` on top of the shared one —
11
- * two `transition-property` declarations of equal specificity resolve by their
12
- * order in the generated stylesheet, which is not something a component should
13
- * depend on.
14
- */
15
- const cardTransitionClasses = /* @__PURE__ */ [
16
- '[transition-property:background-color,border-color,box-shadow,color,transform]',
17
- '[transition-duration:var(--plass-duration)]',
18
- '[transition-timing-function:var(--plass-ease)]'
19
- ].join(' ');
20
- /**
21
- * Hover lifts the sheet and puts a level of shadow under it; the press sets it
22
- * back down. Both directions take the house duration, and no `filter` is
23
- * involved — a card is not a coloured surface, so there is no brightness to
24
- * turn up.
25
- */
26
- const interactiveClasses = {
27
- solid: /* @__PURE__ */ [
28
- 'cursor-pointer hover:-translate-y-0.5',
29
- 'hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]',
30
- 'active:translate-y-0 active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]'
31
- ].join(' '),
32
- glass: /* @__PURE__ */ [
33
- 'cursor-pointer hover:-translate-y-0.5 hover:bg-(--plass-glass-hover)',
34
- 'hover:[border-color:var(--p-line)]',
35
- 'hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]',
36
- 'active:translate-y-0 active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]'
37
- ].join(' '),
38
- ghost: 'cursor-pointer hover:bg-(--p-soft) active:bg-(--p-soft-hover)'
39
- };
40
- /**
41
- * The sheet everything else on a screen is grouped onto, with the parts a card
42
- * is made of laid out on it: a title, a subtitle, a body and a footer.
43
- *
44
- * The sections are props rather than compound sub-components — `<Card.Header>`,
45
- * `<Card.Title>` — for the same reason `PlTextField` takes `label` and
46
- * `description` as props: the arrangement is fixed, and what a caller wants to
47
- * decide is what goes in each slot, not what order the slots come in.
48
- *
49
- * There is no Base UI primitive under this, and there should not be. A card has
50
- * no interaction to delegate: it is a surface with a layout, and the moment it
51
- * becomes something you press it is a `render` away from being a real one.
52
- */
53
- export const PlCard = /* @__PURE__ */ React.forwardRef(function PlCard({ variant = 'glass', size = 'md', color = 'primary', density = 'default', elevation = 1, title, subtitle, headerAction, footer, dividers = false, padded = true, interactive = false, render, className, style, children, ...props }, ref) {
54
- const insetX = padded ? sheetPaddingXClasses[density][size] : '';
55
- const insetY = padded ? sheetPaddingYClasses[density][size] : '';
56
- // Scored, the rules have to reach both edges, so the sheet gives up its
57
- // padding and every section takes it on instead. Unscored, the sheet keeps
58
- // its vertical padding and the sections are told apart by a gap.
59
- const sectionClasses = dividers ? `${insetX} ${insetY}` : insetX;
60
- const hasHeader = hasContent(title) || hasContent(subtitle) || hasContent(headerAction);
61
- const header = (_jsxs(_Fragment, { children: [hasContent(title) || hasContent(subtitle) ? (_jsxs("div", { className: `flex min-w-0 flex-1 flex-col ${sheetHeaderGapClasses[size]}`, children: [hasContent(title) ? (_jsx("div", { className: `plass-title font-semibold ${sheetTitleClasses[size]}`, children: title })) : null, hasContent(subtitle) ? (_jsx("div", { className: `text-(--plass-muted-fg) ${metaTextClasses[size]}`, children: subtitle })) : null] })) : null, hasContent(headerAction) ? _jsx("div", { className: "ml-auto shrink-0", children: headerAction }) : null] }));
62
- const sections = [
63
- hasHeader ? { key: 'header', className: 'flex items-start gap-3', content: header } : null,
64
- hasContent(children)
65
- ? { key: 'content', className: sheetBodyClasses[size], content: children }
66
- : null,
67
- hasContent(footer)
68
- ? {
69
- key: 'footer',
70
- className: `flex flex-wrap items-center gap-2 ${sheetBodyClasses[size]}`,
71
- content: footer
72
- }
73
- : null
74
- ].filter((section) => section !== null);
75
- const classNames = [
76
- 'flex flex-col',
77
- radiusClasses[size],
78
- sheetRestClasses[variant],
79
- cardTransitionClasses,
80
- interactive ? `${interactiveClasses[variant]} ${focusRingClasses}` : '',
81
- dividers ? 'overflow-hidden' : `${insetY} ${sheetSectionGapClasses[size]}`,
82
- className ?? ''
83
- ]
84
- .filter(Boolean)
85
- .join(' ');
86
- return useRender({
87
- render: render ?? _jsx("div", {}),
88
- ref,
89
- props: {
90
- className: classNames,
91
- style: { ...surfaceSlots(color, elevation), ...style },
92
- ...props,
93
- children: sections.map((section, index) => (_jsx("div", { className: [
94
- sectionClasses,
95
- section.className,
96
- dividers && index > 0 ? sheetLineClasses : ''
97
- ]
98
- .filter(Boolean)
99
- .join(' '), children: section.content }, section.key)))
100
- }
101
- });
102
- });
1
+ "use client";import{jsx as e,jsxs as s,Fragment as r}from"react/jsx-runtime";import*as a from"react";import{useRender as l}from"@base-ui/react/use-render";import{focusRingClasses as o,hasContent as t,metaTextClasses as n,radiusClasses as i,sheetBodyClasses as c,sheetHeaderGapClasses as d,sheetLineClasses as v,sheetPaddingXClasses as p,sheetPaddingYClasses as m,sheetRestClasses as f,sheetSectionGapClasses as h,sheetTitleClasses as u,surfaceSlots as g}from"../../internal/styles.js";const x=/* @__PURE__ */["[transition-property:background-color,border-color,box-shadow,color,transform]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]"].join(" "),b={solid:/* @__PURE__ */["cursor-pointer hover:-translate-y-0.5","hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]","active:translate-y-0 active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]"].join(" "),glass:/* @__PURE__ */["cursor-pointer hover:-translate-y-0.5 hover:bg-(--plass-glass-hover)","hover:[border-color:var(--p-line)]","hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]","active:translate-y-0 active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]"].join(" "),ghost:"cursor-pointer hover:bg-(--p-soft) active:bg-(--p-soft-hover)"};export const PlCard=/* @__PURE__ */a.forwardRef(function PlCard({variant:a="glass",size:y="md",color:N="primary",density:$="default",elevation:j=1,title:w,subtitle:k,headerAction:B,footer:C,dividers:P=!1,padded:z=!0,interactive:A=!1,render:F,className:R,style:q,children:D,...E},G){const H=z?p[$][y]:"",I=z?m[$][y]:"",J=P?`${H} ${I}`:H,K=t(w)||t(k)||t(B),L=s(r,{children:[t(w)||t(k)?s("div",{className:`flex min-w-0 flex-1 flex-col ${d[y]}`,children:[t(w)?e("div",{className:`plass-title font-semibold ${u[y]}`,children:w}):null,t(k)?e("div",{className:`text-(--plass-muted-fg) ${n[y]}`,children:k}):null]}):null,t(B)?e("div",{className:"ml-auto shrink-0",children:B}):null]}),M=[K?{key:"header",className:"flex items-start gap-3",content:L}:null,t(D)?{key:"content",className:c[y],content:D}:null,t(C)?{key:"footer",className:`flex flex-wrap items-center gap-2 ${c[y]}`,content:C}:null].filter(e=>null!==e),O=["flex flex-col",i[y],f[a],x,A?`${b[a]} ${o}`:"",P?"overflow-hidden":`${I} ${h[y]}`,R??""].filter(Boolean).join(" ");return l({render:F??e("div",{}),ref:G,props:{className:O,style:{...g(N,j),...q},...E,children:M.map((s,r)=>e("div",{className:[J,s.className,P&&r>0?v:""].filter(Boolean).join(" "),children:s.content},s.key))}})});
@@ -1 +1 @@
1
- export { PlCard } from './PlCard.js';
1
+ export{PlCard}from"./PlCard.js";
@@ -1,177 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 { PlCarousel } from './PlCarousel.js';
1
+ export{PlCarousel}from"./PlCarousel.js";
@@ -1,206 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 { PlChatBubble } from './PlChatBubble.js';
1
+ export{PlChatBubble}from"./PlChatBubble.js";