plass-ui 1.0.3 → 1.1.0

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 (101) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +2 -0
  3. package/dist/components/avatar/PlAvatar.js +1 -1
  4. package/dist/components/avatar-group/PlAvatarGroup.d.ts +47 -0
  5. package/dist/components/avatar-group/PlAvatarGroup.js +1 -0
  6. package/dist/components/avatar-group/index.d.ts +2 -0
  7. package/dist/components/avatar-group/index.js +1 -0
  8. package/dist/components/checkbox/PlCheckbox.js +1 -1
  9. package/dist/components/color-picker/PlColorPicker.d.ts +88 -0
  10. package/dist/components/color-picker/PlColorPicker.js +1 -0
  11. package/dist/components/color-picker/index.d.ts +2 -0
  12. package/dist/components/color-picker/index.js +1 -0
  13. package/dist/components/combobox/PlCombobox.js +1 -1
  14. package/dist/components/command-palette/PlCommandPalette.d.ts +83 -0
  15. package/dist/components/command-palette/PlCommandPalette.js +1 -0
  16. package/dist/components/command-palette/index.d.ts +2 -0
  17. package/dist/components/command-palette/index.js +1 -0
  18. package/dist/components/divider/PlDivider.js +1 -1
  19. package/dist/components/fieldset/PlFieldset.d.ts +35 -0
  20. package/dist/components/fieldset/PlFieldset.js +1 -0
  21. package/dist/components/fieldset/index.d.ts +2 -0
  22. package/dist/components/fieldset/index.js +1 -0
  23. package/dist/components/footer/PlFooter.d.ts +90 -0
  24. package/dist/components/footer/PlFooter.js +1 -0
  25. package/dist/components/footer/index.d.ts +2 -0
  26. package/dist/components/footer/index.js +1 -0
  27. package/dist/components/form/PlForm.d.ts +61 -0
  28. package/dist/components/form/PlForm.js +1 -0
  29. package/dist/components/form/index.d.ts +2 -0
  30. package/dist/components/form/index.js +1 -0
  31. package/dist/components/header/PlHeader.d.ts +124 -0
  32. package/dist/components/header/PlHeader.js +1 -0
  33. package/dist/components/header/index.d.ts +2 -0
  34. package/dist/components/header/index.js +1 -0
  35. package/dist/components/menubar/PlMenubar.d.ts +56 -0
  36. package/dist/components/menubar/PlMenubar.js +1 -0
  37. package/dist/components/menubar/index.d.ts +2 -0
  38. package/dist/components/menubar/index.js +1 -0
  39. package/dist/components/navigation-menu/PlNavigationMenu.d.ts +99 -0
  40. package/dist/components/navigation-menu/PlNavigationMenu.js +1 -0
  41. package/dist/components/navigation-menu/index.d.ts +2 -0
  42. package/dist/components/navigation-menu/index.js +1 -0
  43. package/dist/components/number-field/PlNumberField.js +1 -1
  44. package/dist/components/otp-field/PlOtpField.js +1 -1
  45. package/dist/components/page-layout/PlPageLayout.d.ts +139 -0
  46. package/dist/components/page-layout/PlPageLayout.js +1 -0
  47. package/dist/components/page-layout/index.d.ts +2 -0
  48. package/dist/components/page-layout/index.js +1 -0
  49. package/dist/components/radio-group/PlRadioGroup.js +1 -1
  50. package/dist/components/scroll-zone/PlScrollZone.d.ts +35 -15
  51. package/dist/components/scroll-zone/PlScrollZone.js +1 -1
  52. package/dist/components/select/PlSelect.js +1 -1
  53. package/dist/components/sidebar/PlSidebar.d.ts +141 -0
  54. package/dist/components/sidebar/PlSidebar.js +1 -0
  55. package/dist/components/sidebar/PlSidebarTrigger.d.ts +34 -0
  56. package/dist/components/sidebar/PlSidebarTrigger.js +1 -0
  57. package/dist/components/sidebar/index.d.ts +4 -0
  58. package/dist/components/sidebar/index.js +1 -0
  59. package/dist/components/switch/PlSwitch.js +1 -1
  60. package/dist/components/text-field/PlTextField.js +1 -1
  61. package/dist/components/text-link/PlTextLink.d.ts +11 -2
  62. package/dist/components/text-link/PlTextLink.js +1 -1
  63. package/dist/components/toggle/PlToggle.d.ts +55 -0
  64. package/dist/components/toggle/PlToggle.js +1 -0
  65. package/dist/components/toggle/PlToggleGroup.d.ts +48 -0
  66. package/dist/components/toggle/PlToggleGroup.js +1 -0
  67. package/dist/components/toggle/index.d.ts +4 -0
  68. package/dist/components/toggle/index.js +1 -0
  69. package/dist/components/transfer/PlTransfer.d.ts +55 -0
  70. package/dist/components/transfer/PlTransfer.js +1 -0
  71. package/dist/components/transfer/index.d.ts +2 -0
  72. package/dist/components/transfer/index.js +1 -0
  73. package/dist/css/avatar-group.css +2 -0
  74. package/dist/css/color-picker.css +2 -0
  75. package/dist/css/command-palette.css +2 -0
  76. package/dist/css/fieldset.css +2 -0
  77. package/dist/css/footer.css +2 -0
  78. package/dist/css/form.css +2 -0
  79. package/dist/css/header.css +2 -0
  80. package/dist/css/menubar.css +2 -0
  81. package/dist/css/navigation-menu.css +2 -0
  82. package/dist/css/page-layout.css +2 -0
  83. package/dist/css/sidebar.css +2 -0
  84. package/dist/css/toggle.css +2 -0
  85. package/dist/css/transfer.css +2 -0
  86. package/dist/index.d.ts +13 -0
  87. package/dist/index.js +1 -1
  88. package/dist/internal/avatar-group.d.ts +25 -0
  89. package/dist/internal/avatar-group.js +2 -0
  90. package/dist/internal/color.d.ts +109 -0
  91. package/dist/internal/color.js +1 -0
  92. package/dist/internal/link.d.ts +18 -0
  93. package/dist/internal/link.js +1 -0
  94. package/dist/internal/page-layout.d.ts +140 -0
  95. package/dist/internal/page-layout.js +1 -0
  96. package/dist/internal/search.d.ts +31 -0
  97. package/dist/internal/search.js +1 -0
  98. package/dist/internal/styles.d.ts +9 -0
  99. package/dist/internal/styles.js +1 -1
  100. package/dist/styles.css +1 -1
  101. package/package.json +3 -3
@@ -0,0 +1,141 @@
1
+ import * as React from 'react';
2
+ import type { PlPageLayoutCollapse, PlassSidebarSide } from '../../internal/page-layout.js';
3
+ import type { PlassColor, PlassDensity, PlassElevation, PlassSize, PlassVariant } from '../../types.js';
4
+ export type { PlassSidebarSide } from '../../internal/page-layout.js';
5
+ export interface PlSidebarProps extends Omit<React.ComponentPropsWithoutRef<'aside'>, 'color' | 'title'> {
6
+ /**
7
+ * Which end of the band it takes. Logical rather than physical — `start` is
8
+ * the left of an English page and the right of an Arabic one — because a
9
+ * navigation rail is beside the text it belongs to in every writing
10
+ * direction.
11
+ *
12
+ * Inside a `PlPageLayout` this is already decided by which slot the sidebar
13
+ * was handed to, and setting it again is only a way of disagreeing with the
14
+ * layout.
15
+ * @default 'start'
16
+ */
17
+ side?: PlassSidebarSide;
18
+ /**
19
+ * How wide the column is — a number in pixels or any CSS length. Left out, it
20
+ * is the width `size` implies.
21
+ *
22
+ * With `resizable` it is only the width the sidebar *starts* at: dragging
23
+ * writes over it, and the caller hears about it through `onResize`.
24
+ */
25
+ width?: number | string;
26
+ /** How narrow it may be dragged. @default 160 */
27
+ minWidth?: number | string;
28
+ /** And how wide. @default 480 */
29
+ maxWidth?: number | string;
30
+ /**
31
+ * Lets the reader drag the inner edge to change the column's width.
32
+ *
33
+ * Off by default. A sidebar that can be resized is a sidebar whose width is
34
+ * the reader's to remember, which means a caller who turns this on usually
35
+ * also wants to store what `onResizeEnd` reports.
36
+ * @default false
37
+ */
38
+ resizable?: boolean;
39
+ /** Fires with the width in pixels while the edge is being dragged. */
40
+ onResize?: (width: number) => void;
41
+ /** Fires once, with the same number, when it is let go. */
42
+ onResizeEnd?: (width: number) => void;
43
+ /**
44
+ * The window width below which the sidebar stops being a column and becomes a
45
+ * drawer that is opened — with a `PlSidebarTrigger` somewhere on the page as
46
+ * the way to open it. `none` keeps it a column at every width.
47
+ *
48
+ * Defaults to the `PlPageLayout`'s own `collapseBelow`, and to `none` outside
49
+ * one: a sidebar that collapsed with nothing on the page able to bring it
50
+ * back would be a sidebar the reader has lost.
51
+ */
52
+ collapseBelow?: PlPageLayoutCollapse;
53
+ /**
54
+ * Whether the drawer is open. Only meaningful once the sidebar has collapsed;
55
+ * a column is not opened, it is there.
56
+ *
57
+ * Inside a `PlPageLayout` the layout owns this — it is what a trigger
58
+ * anywhere on the page talks to — so control it there rather than here.
59
+ * `onOpenChange` still fires either way.
60
+ */
61
+ open?: boolean;
62
+ /** Which state it starts in, for an uncontrolled standalone sidebar. @default false */
63
+ defaultOpen?: boolean;
64
+ onOpenChange?: (open: boolean) => void;
65
+ /**
66
+ * Whether the column holds its place while the page scrolls past it.
67
+ *
68
+ * On by default, and it costs nothing when it is not needed: with the page
69
+ * scrolling it becomes a `sticky` column as tall as what is left of the
70
+ * window under the header, and with only the content scrolling it is already
71
+ * as tall as the layout and this changes nothing.
72
+ * @default true
73
+ */
74
+ sticky?: boolean;
75
+ /**
76
+ * The heading, drawn only while the sidebar is a drawer. A column has the
77
+ * page around it to say what it is; a panel that has covered the page does
78
+ * not.
79
+ */
80
+ title?: React.ReactNode;
81
+ /**
82
+ * What the sheet is made of, said the way a **container** says it: the panel
83
+ * is never dyed, because what is on it arrives with colours of its own.
84
+ * @default 'glass'
85
+ */
86
+ variant?: PlassVariant;
87
+ /** The panel's scale — its default width and the air around its content. @default 'md' */
88
+ size?: PlassSize;
89
+ /** @default 'primary' */
90
+ color?: PlassColor;
91
+ /** @default 'default' */
92
+ density?: PlassDensity;
93
+ /** Drop shadow depth. `0` — the default — is flat. @default 0 */
94
+ elevation?: PlassElevation;
95
+ /**
96
+ * Draws a hairline down the inner edge — the one facing the content. The
97
+ * outer edge is against the window, where there is nothing on the other side
98
+ * to be separated from.
99
+ * @default true
100
+ */
101
+ divider?: boolean;
102
+ /** The gutter and the air above and below the content. @default true */
103
+ padded?: boolean;
104
+ /**
105
+ * The name the region is announced by. Every `<aside>` on a page should have
106
+ * one, and a page with two sidebars *must*, or a screen reader offers two
107
+ * regions called "complementary".
108
+ * @default 'Sidebar'
109
+ */
110
+ label?: string;
111
+ /** What the drawer's close button says, once the sidebar has collapsed. @default 'Close sidebar' */
112
+ closeLabel?: string;
113
+ /** What the drag handle is announced as. @default 'Resize sidebar' */
114
+ resizeLabel?: string;
115
+ /** Everything in it: a nav, a filter panel, a table of contents. */
116
+ children?: React.ReactNode;
117
+ }
118
+ /**
119
+ * A column beside the page's content, and a drawer once the window is too
120
+ * narrow to hold one.
121
+ *
122
+ * Two presentations of one panel, exactly as `PlDrawer` is: above
123
+ * `collapseBelow` it is an `<aside>` in the layout that the content is laid out
124
+ * around, and below it the same children are a drawer over a scrim with a focus
125
+ * trap, an Escape and a way back to the trigger. They are one component because
126
+ * they are one thing — a caller should not have to swap components at a
127
+ * breakpoint — and because the children only exist once either way, so nothing
128
+ * inside is rendered twice into the document for a screen reader to read twice.
129
+ *
130
+ * Which of the two is showing is a media query, and it is answered in CSS for
131
+ * the first paint and in JavaScript from then on. That split is deliberate: the
132
+ * markup a server sends is the column, so a narrow screen would draw a full
133
+ * width sidebar and throw it away a moment later — the class that hides it
134
+ * below the breakpoint is what stops that, and `matchMedia` is what decides,
135
+ * once there is a window to ask, that the drawer should exist at all.
136
+ *
137
+ * A real `<aside>`, which is the `complementary` landmark: the region a screen
138
+ * reader offers as "related to the page but not the page", and what a search
139
+ * engine reads as navigation chrome rather than as the article.
140
+ */
141
+ export declare const PlSidebar: React.ForwardRefExoticComponent<PlSidebarProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as r from"react";import{PlDrawer as n}from"../drawer/PlDrawer.js";import{drawerSide as o,expandedOnlyClasses as s,PlPageLayoutContext as i,PlassSidebarSideContext as a,useCollapsed as l}from"../../internal/page-layout.js";import{cx as d,sheetPaddingXClasses as c,sheetPaddingYClasses as u,sheetRestClasses as p,surfaceSlots as m,toLength as f,transitionClasses as y}from"../../internal/styles.js";const b={xs:"11rem",sm:"13rem",md:"16rem",lg:"18rem",xl:"21rem"},v=p;function toPixels(e,t){if(null==e)return t;if("number"==typeof e)return e;const r=/^\s*(-?[\d.]+)\s*(px|rem|em|%)\s*$/.exec(e);if(!r)return t;const n=Number(r[1]);if(Number.isNaN(n))return t;switch(r[2]){case"px":return n;case"%":return"undefined"==typeof window?t:window.innerWidth*n/100;default:return n*parseFloat(("undefined"==typeof document?"":getComputedStyle(document.documentElement).fontSize)||"16")}}export const PlSidebar=/* @__PURE__ */r.forwardRef(function PlSidebar({side:p,width:g,minWidth:w=160,maxWidth:h=480,resizable:x=!1,onResize:P,onResizeEnd:C,collapseBelow:k,open:z,defaultOpen:L=!1,onOpenChange:R,sticky:E=!0,title:N,variant:S="glass",size:j="md",color:B="primary",density:D="default",elevation:O=0,divider:A=!0,padded:M=!0,label:W="Sidebar",closeLabel:I="Close sidebar",resizeLabel:T="Resize sidebar",className:X,style:$,children:_,...F},K){const V=r.useContext(i),q=r.useContext(a),G=p??q??"start",H=k??(V.present?V.collapseBelow:"none"),J=l(H),[Q,U]=r.useState(L),Y=void 0!==z,Z=Y?z:V.present?V.open[G]:Q,ee=f(g)??b[j],te=r.useRef(null),re=r.useCallback(e=>{te.current=e,"function"==typeof K?K(e):K&&(K.current=e)},[K]),ne=r.useRef(null);r.useEffect(()=>()=>ne.current?.(),[]);const applyWidth=e=>{const t=te.current;if(!t)return e;const r=Math.min(toPixels(h,480),Math.max(toPixels(w,160),Math.round(e)));return t.style.setProperty("--p-sidebar-w",`${r}px`),r};return J?e(n,{side:o(G),mode:"overlay",open:Z,onOpenChange:e=>{Y||(V.present?V.setOpen(G,e):U(e)),R?.(e)},title:N,size:j,color:B,density:D,closeLabel:I,extent:void 0===g?void 0:ee,"aria-label":N?void 0:W,className:X,style:$,children:_}):t("aside",{ref:re,"aria-label":W,className:d("relative flex min-w-0 shrink-0 flex-col","w-(--p-sidebar-w)",v[S],A?d("start"===G?"border-e":"border-s","[border-color:var(--plass-divider)]"):"","content"===V.scroll?"h-full":E?d("self-start","sticky [top:var(--p-layout-header,0px)]","[height:calc(100dvh-var(--p-layout-header,0px)-var(--p-layout-footer,0px))]"):"",s[H],y,X),style:{...m(B,O),"--p-sidebar-w":ee,...$},...F,children:[e("div",{className:d("min-h-0 flex-1 overflow-y-auto overscroll-contain",M?d(c[D][j],u[D][j]):""),children:_}),x?e("div",{role:"separator","aria-orientation":"vertical","aria-label":T,tabIndex:0,className:d("absolute inset-y-0 z-1 w-2 cursor-col-resize","start"===G?"end-0 -me-1":"start-0 -ms-1","bg-transparent hover:bg-(--p-soft) data-[dragging]:bg-(--p-soft)",y,"[outline:none] focus-visible:[outline:2px_solid_var(--p-ring)]","focus-visible:[outline-offset:0px]"),onPointerDown:e=>{const t=te.current;if(!t||0!==e.button)return;const r=e.currentTarget;r.setPointerCapture(e.pointerId),r.dataset.dragging="true";const n=document.body.style.getPropertyValue("-webkit-user-select");document.body.style.setProperty("-webkit-user-select","none");const o=e.clientX,s=t.getBoundingClientRect().width,i="rtl"===getComputedStyle(t).direction,a="start"===G===i?-1:1;let l=s;const move=e=>{l=applyWidth(s+(e.clientX-o)*a),P?.(l)},release=()=>{ne.current=null,r.removeEventListener("pointermove",move),r.removeEventListener("pointerup",end),r.removeEventListener("pointercancel",end),delete r.dataset.dragging,n?document.body.style.setProperty("-webkit-user-select",n):document.body.style.removeProperty("-webkit-user-select")},end=()=>{release(),C?.(l)};ne.current=release,r.addEventListener("pointermove",move),r.addEventListener("pointerup",end),r.addEventListener("pointercancel",end)},onKeyDown:e=>{if("ArrowLeft"!==e.key&&"ArrowRight"!==e.key)return;e.preventDefault();const t="rtl"===getComputedStyle(e.currentTarget).direction,r="start"===G===t?-1:1;(e=>{const t=te.current;if(!t)return;const r=applyWidth(t.getBoundingClientRect().width+e);P?.(r),C?.(r)})(("ArrowRight"===e.key?16:-16)*r)}}):null]})});
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import { type PlIconButtonProps } from '../icon-button/PlIconButton.js';
3
+ import type { PlPageLayoutCollapse, PlassSidebarSide } from '../../internal/page-layout.js';
4
+ export interface PlSidebarTriggerProps extends Omit<PlIconButtonProps, 'icon' | 'label'> {
5
+ /** Which of the layout's two sidebars it opens. @default 'start' */
6
+ side?: PlassSidebarSide;
7
+ /**
8
+ * The width below which the button appears — the same one the sidebar
9
+ * collapses at, since the button exists exactly while the sidebar does not.
10
+ * Inherited from the `PlPageLayout`, which is where it should be set.
11
+ */
12
+ collapseBelow?: PlPageLayoutCollapse;
13
+ /** The glyph. A hamburger, drawn here, unless something else is given. */
14
+ icon?: React.ReactNode;
15
+ /** What it does, in words. @default 'Open sidebar' / 'Close sidebar' */
16
+ label?: string;
17
+ }
18
+ /**
19
+ * The button that brings back a `PlSidebar` the window has become too narrow to
20
+ * hold.
21
+ *
22
+ * It is drawn only while that is true, and the "while" is a media query rather
23
+ * than a piece of state: the button carries a class that hides it at and above
24
+ * the breakpoint. That matters more than it looks — a trigger whose presence
25
+ * depended on `matchMedia` would be absent from the markup a server sends and
26
+ * would pop into the header a moment after the page arrived, on every phone,
27
+ * every time.
28
+ *
29
+ * Put it in a `PlHeader`'s `brand` slot, ahead of the logo, which is where
30
+ * thirty years of hamburgers have taught readers to look for it. It has to be
31
+ * inside a `PlPageLayout` to have something to open; outside one there is no
32
+ * sidebar it could be talking about, and it renders nothing.
33
+ */
34
+ export declare const PlSidebarTrigger: React.ForwardRefExoticComponent<PlSidebarTriggerProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import*as r from"react";import{PlIconButton as n}from"../icon-button/PlIconButton.js";import{collapsedOnlyClasses as o,PlPageLayoutContext as t}from"../../internal/page-layout.js";import{cx as i}from"../../internal/styles.js";function MenuIcon(){return e("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":"true",children:e("path",{d:"M4 7h16M4 12h16M4 17h16",strokeLinecap:"round"})})}export const PlSidebarTrigger=/* @__PURE__ */r.forwardRef(function PlSidebarTrigger({side:a="start",collapseBelow:s,icon:l,label:c,variant:u="ghost",className:p,onClick:d,...m},f){const h=r.useContext(t);if(!h.present)return null;const b=s??h.collapseBelow,g=h.open[a];return e(n,{ref:f,variant:u,icon:l??e(MenuIcon,{}),label:c??(g?"Close sidebar":"Open sidebar"),"aria-expanded":g,className:i(o[b],p),onClick:e=>{h.setOpen(a,!g),d?.(e)},...m})});
@@ -0,0 +1,4 @@
1
+ export { PlSidebar } from './PlSidebar.js';
2
+ export type { PlSidebarProps, PlassSidebarSide } from './PlSidebar.js';
3
+ export { PlSidebarTrigger } from './PlSidebarTrigger.js';
4
+ export type { PlSidebarTriggerProps } from './PlSidebarTrigger.js';
@@ -0,0 +1 @@
1
+ export{PlSidebar}from"./PlSidebar.js";export{PlSidebarTrigger}from"./PlSidebarTrigger.js";
@@ -1 +1 @@
1
- "use client";import{jsx as a,jsxs as e,Fragment as t}from"react/jsx-runtime";import*as l from"react";import{Switch as r}from"@base-ui/react/switch";import{Field as s}from"@base-ui/react/field";import{controlSlots as i,focusRingClasses as c,hasContent as n,metaTextClasses as o,tickRowTextClasses as d}from"../../internal/styles.js";const h={xs:"h-3.5 w-6",sm:"h-4 w-7",md:"h-5 w-9",lg:"h-6 w-11",xl:"h-7 w-13"},m={xs:"data-[checked]:left-[calc(100%-0.75rem)]",sm:"data-[checked]:left-[calc(100%-0.875rem)]",md:"data-[checked]:left-[calc(100%-1.125rem)]",lg:"data-[checked]:left-[calc(100%-1.375rem)]",xl:"data-[checked]:left-[calc(100%-1.625rem)]"},p=/* @__PURE__ */["relative inline-flex shrink-0 rounded-full","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","[transition-property:background-color,background-image,box-shadow,filter]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]",c].join(" "),f=/* @__PURE__ */["cursor-pointer bg-(--plass-track)","hover:brightness-[0.97] dark:hover:brightness-110","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:[box-shadow:var(--p-lift)] data-[checked]:hover:brightness-105"].join(" "),u=/* @__PURE__ */["cursor-default bg-(--plass-track) saturate-[0.55]","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:shadow-none"].join(" "),g=/* @__PURE__ */["cursor-not-allowed bg-(--plass-track)","opacity-50 saturate-[0.35] shadow-none","data-[checked]:[background-image:var(--p-fill)]"].join(" "),b=/* @__PURE__ */["absolute inset-y-0.5 left-0.5 aspect-square rounded-full bg-white","[box-shadow:0_1px_2px_rgb(20_40_90/0.25)]","[transition:left_var(--plass-duration)_var(--plass-ease)]"].join(" ");export const PlSwitch=/* @__PURE__ */l.forwardRef(function PlSwitch({size:l="md",color:c="primary",label:x,description:k,error:w,invalid:v,labelPlacement:j="end",disabled:N=!1,readOnly:y=!1,className:_,style:$,...P},R){const S=n(w),B=v??S,O=B?"danger":c,q=a("span",{className:"flex h-[1lh] shrink-0 items-center",children:a(r.Root,{ref:R,className:[p,h[l],N?g:y?u:f].join(" "),disabled:N,readOnly:y,...P,children:a(r.Thumb,{className:`${b} ${m[l]}`})})}),z=x||k?e("span",{className:["flex min-w-0 flex-col gap-0.5","start"===j?"flex-1":""].filter(Boolean).join(" "),children:[x?a(s.Label,{className:N?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:x}):null,k?a(s.Description,{className:`${o[l]} text-(--plass-muted-fg)`,children:k}):null]}):null;return e(s.Root,{disabled:N,invalid:B,className:["inline-flex flex-col gap-1 align-top",_??""].filter(Boolean).join(" "),style:{...i(O,0,"solid"),...$},children:[a("div",{className:`flex items-start gap-2.5 ${d[l]}`,children:e(t,"start"===j?{children:[z,q]}:{children:[q,z]})}),S?a(s.Error,{match:!0,className:`${o[l]} text-(--p-accent)`,children:w}):null]})});
1
+ "use client";import{jsx as a,jsxs as e,Fragment as t}from"react/jsx-runtime";import*as l from"react";import{Switch as r}from"@base-ui/react/switch";import{Field as s}from"@base-ui/react/field";import{controlSlots as i,focusRingClasses as c,hasContent as n,metaTextClasses as o,tickRowTextClasses as d}from"../../internal/styles.js";const h={xs:"h-3.5 w-6",sm:"h-4 w-7",md:"h-5 w-9",lg:"h-6 w-11",xl:"h-7 w-13"},m={xs:"data-[checked]:left-[calc(100%-0.75rem)]",sm:"data-[checked]:left-[calc(100%-0.875rem)]",md:"data-[checked]:left-[calc(100%-1.125rem)]",lg:"data-[checked]:left-[calc(100%-1.375rem)]",xl:"data-[checked]:left-[calc(100%-1.625rem)]"},p=/* @__PURE__ */["relative inline-flex shrink-0 rounded-full","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]","[transition-property:background-color,background-image,box-shadow,filter]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]",c].join(" "),f=/* @__PURE__ */["cursor-pointer bg-(--plass-track)","hover:brightness-[0.97] dark:hover:brightness-110","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:[box-shadow:var(--p-lift)] data-[checked]:hover:brightness-105"].join(" "),u=/* @__PURE__ */["cursor-default bg-(--plass-track) saturate-[0.55]","data-[checked]:[background-image:var(--p-fill)]","data-[checked]:shadow-none"].join(" "),g=/* @__PURE__ */["cursor-not-allowed bg-(--plass-track)","opacity-50 saturate-[0.35] shadow-none","data-[checked]:[background-image:var(--p-fill)]"].join(" "),b=/* @__PURE__ */["absolute inset-y-0.5 left-0.5 aspect-square rounded-full bg-white","[box-shadow:0_1px_2px_rgb(20_40_90/0.25)]","[transition:left_var(--plass-duration)_var(--plass-ease)]"].join(" ");export const PlSwitch=/* @__PURE__ */l.forwardRef(function PlSwitch({size:l="md",color:c="primary",label:x,description:k,error:w,invalid:v,labelPlacement:j="end",disabled:N=!1,readOnly:y=!1,className:_,style:$,...P},R){const S=n(w),B=v??S,O=B?"danger":c,q=a("span",{className:"flex h-[1lh] shrink-0 items-center",children:a(r.Root,{ref:R,className:[p,h[l],N?g:y?u:f].join(" "),disabled:N,readOnly:y,...P,children:a(r.Thumb,{className:`${b} ${m[l]}`})})}),z=x||k?e("span",{className:["flex min-w-0 flex-col gap-0.5","start"===j?"flex-1":""].filter(Boolean).join(" "),children:[x?a(s.Label,{className:N?"text-(--plass-muted-fg)":"cursor-pointer text-(--plass-fg)",children:x}):null,k?a(s.Description,{className:`${o[l]} text-(--plass-muted-fg)`,children:k}):null]}):null;return e(s.Root,{disabled:N,invalid:B,className:["inline-flex flex-col gap-1 align-top",_??""].filter(Boolean).join(" "),style:{...i(O,0,"solid"),...$},children:[a("div",{className:`flex items-start gap-2.5 ${d[l]}`,children:e(t,"start"===j?{children:[z,q]}:{children:[q,z]})}),a(s.Error,S?{match:!0,className:`${o[l]} text-(--p-accent)`,children:w}:{className:`${o[l]} text-(--p-accent)`})]})});
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as l from"react";import{Field as n}from"@base-ui/react/field";import{Input as s}from"@base-ui/react/input";import{Spinner as r}from"../../internal/icons.js";import{controlHeightClasses as i,controlTextLeadingClasses as a,disabledClasses as o,fieldReadOnlyClasses as c,fieldRestClasses as d,focusWithinRingClasses as m,gapClasses as p,hasContent as u,iconClasses as x,metaTextClasses as f,paddingXClasses as h,radiusClasses as g,stackGapClasses as $,surfaceSlots as y,transitionClasses as b}from"../../internal/styles.js";const j={xs:`${p.xs} ${g.xs} ${a.xs}`,sm:`${p.sm} ${g.sm} ${a.sm}`,md:`${p.md} ${g.md} ${a.md}`,lg:`${p.lg} ${g.lg} ${a.lg}`,xl:`${p.xl} ${g.xl} ${a.xl}`},v={xs:"min-h-6 py-[3px]",sm:"min-h-8 py-[6px]",md:"min-h-10 py-[9px]",lg:"min-h-12 py-[11px]",xl:"min-h-14 py-[13px]"},w={none:"resize-none",vertical:"resize-y",horizontal:"resize-x",both:"resize"},N=/* @__PURE__ */["group relative flex w-full cursor-text","[-webkit-tap-highlight-color:transparent]",b,m,x].join(" "),z=d,k=c;export const PlTextField=/* @__PURE__ */l.forwardRef(function PlTextField({variant:a="glass",size:c="md",color:d="primary",density:m="default",elevation:p=0,multiline:x=!1,rows:g=3,resize:b="vertical",label:D,description:P,error:R,invalid:T,startIcon:B,endIcon:F,loading:I=!1,fullWidth:O=!1,readOnly:C=!1,disabled:E=!1,type:L="text",className:W,style:q,...A},G){const H=u(R),J=T??H,K=J?"danger":d,M=l.useRef(null),Q=l.useCallback(e=>{M.current=e,"function"==typeof G?G(e):G&&(G.current=e)},[G]),S=[N,j[c],x?`${v[c]} items-start`:`${i[c]} items-center`,h[m][c],E?o[a]:C?k[a]:z[a]].filter(Boolean).join(" "),U=["min-w-0 flex-1 bg-transparent [font:inherit] text-inherit","[outline:none]","placeholder:text-(--plass-muted-fg)","caret-(--p-accent) selection:bg-(--p-soft-press)","disabled:cursor-not-allowed",x?`block ${w[b]}`:"self-stretch"].join(" "),V="inline-flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg) transition-[color] duration-(--plass-duration) group-focus-within:text-(--p-accent)";return t(n.Root,{disabled:E,invalid:J,className:["flex-col align-top",$[c],O?"flex w-full":"inline-flex",W??""].filter(Boolean).join(" "),style:{...y(K,p),...q},children:[D?e(n.Label,{className:[f[c],"font-semibold",E?"text-(--plass-muted-fg)":"text-(--plass-fg)"].join(" "),children:D}):null,t("span",{className:S,onPointerDown:e=>{e.target!==e.currentTarget||E||(e.preventDefault(),M.current?.focus())},children:[B?e("span",{className:V,children:B}):null,e(s,{ref:Q,className:U,disabled:E,readOnly:C,"aria-busy":I||void 0,"data-loading":I||void 0,...x?{render:e("textarea",{rows:g})}:{type:L},...A}),I?e("span",{className:V,children:e(r,{})}):F?e("span",{className:V,children:F}):null]}),P?e(n.Description,{className:[f[c],"text-(--plass-muted-fg)"].join(" "),children:P}):null,H?e(n.Error,{match:!0,className:[f[c],"text-(--p-accent)"].join(" "),children:R}):null]})});
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as l from"react";import{Field as n}from"@base-ui/react/field";import{Input as s}from"@base-ui/react/input";import{Spinner as i}from"../../internal/icons.js";import{controlHeightClasses as r,controlTextLeadingClasses as a,disabledClasses as o,fieldReadOnlyClasses as c,fieldRestClasses as m,focusWithinRingClasses as d,gapClasses as p,hasContent as x,iconClasses as u,metaTextClasses as f,paddingXClasses as h,radiusClasses as g,stackGapClasses as $,surfaceSlots as y,transitionClasses as b}from"../../internal/styles.js";const j={xs:`${p.xs} ${g.xs} ${a.xs}`,sm:`${p.sm} ${g.sm} ${a.sm}`,md:`${p.md} ${g.md} ${a.md}`,lg:`${p.lg} ${g.lg} ${a.lg}`,xl:`${p.xl} ${g.xl} ${a.xl}`},N={xs:"min-h-6 py-[3px]",sm:"min-h-8 py-[6px]",md:"min-h-10 py-[9px]",lg:"min-h-12 py-[11px]",xl:"min-h-14 py-[13px]"},v={none:"resize-none",vertical:"resize-y",horizontal:"resize-x",both:"resize"},w=/* @__PURE__ */["group relative flex w-full cursor-text","[-webkit-tap-highlight-color:transparent]",b,d,u].join(" "),z=m,k=c;export const PlTextField=/* @__PURE__ */l.forwardRef(function PlTextField({variant:a="glass",size:c="md",color:m="primary",density:d="default",elevation:p=0,multiline:u=!1,rows:g=3,resize:b="vertical",label:D,description:P,error:R,invalid:T,startIcon:B,endIcon:F,loading:I=!1,fullWidth:O=!1,readOnly:C=!1,disabled:E=!1,type:L="text",className:W,style:q,...A},G){const H=x(R),J=T??H,K=J?"danger":m,M=l.useRef(null),Q=l.useCallback(e=>{M.current=e,"function"==typeof G?G(e):G&&(G.current=e)},[G]),S=[w,j[c],u?`${N[c]} items-start`:`${r[c]} items-center`,h[d][c],E?o[a]:C?k[a]:z[a]].filter(Boolean).join(" "),U=["min-w-0 flex-1 bg-transparent [font:inherit] text-inherit","[outline:none]","placeholder:text-(--plass-muted-fg)","caret-(--p-accent) selection:bg-(--p-soft-press)","disabled:cursor-not-allowed",u?`block ${v[b]}`:"self-stretch"].join(" "),V="inline-flex h-[1lh] shrink-0 items-center text-(--plass-muted-fg) transition-[color] duration-(--plass-duration) group-focus-within:text-(--p-accent)";return t(n.Root,{disabled:E,invalid:J,className:["flex-col align-top",$[c],O?"flex w-full":"inline-flex",W??""].filter(Boolean).join(" "),style:{...y(K,p),...q},children:[D?e(n.Label,{className:[f[c],"font-semibold",E?"text-(--plass-muted-fg)":"text-(--plass-fg)"].join(" "),children:D}):null,t("span",{className:S,onPointerDown:e=>{e.target!==e.currentTarget||E||(e.preventDefault(),M.current?.focus())},children:[B?e("span",{className:V,children:B}):null,e(s,{ref:Q,className:U,disabled:E,readOnly:C,"aria-busy":I||void 0,"data-loading":I||void 0,...u?{render:e("textarea",{rows:g})}:{type:L},...A}),I?e("span",{className:V,children:e(i,{})}):F?e("span",{className:V,children:F}):null]}),P?e(n.Description,{className:[f[c],"text-(--plass-muted-fg)"].join(" "),children:P}):null,e(n.Error,H?{match:!0,className:[f[c],"text-(--p-accent)"].join(" "),children:R}:{className:[f[c],"text-(--p-accent)"].join(" ")})]})});
@@ -71,8 +71,17 @@ export interface PlTextLinkProps extends Omit<React.ComponentPropsWithoutRef<'a'
71
71
  newTabLabel?: string;
72
72
  /**
73
73
  * Renders something other than an `<a>` — the `Link` a router brings, most of
74
- * the time. `href` still goes through, so `render={<NextLink href="…" />}`
75
- * needs it written once, on the `PlTextLink`.
74
+ * the time.
75
+ *
76
+ * **An `href` on what comes back wins.** A router's `Link` does not pass its
77
+ * address through; it *resolves* one — a locale prefix, a base path, a typed
78
+ * route — and hands the anchor the result. So when the element carries an
79
+ * `href` of its own, this component's is not merged over it, and the router
80
+ * keeps what it worked out. Written the other way round, every localised link
81
+ * in an app silently lost its prefix.
82
+ *
83
+ * The element having none is the ordinary case (`render={<a />}`), and there
84
+ * the `href` above is used.
76
85
  */
77
86
  render?: useRender.RenderProp;
78
87
  /** The label. */
@@ -1 +1 @@
1
- "use client";import{jsx as n,Fragment as e,jsxs as r}from"react/jsx-runtime";import*as s from"react";import{useRender as i}from"@base-ui/react/use-render";import{ExternalLinkIcon as l,LinkIcon as a}from"../../internal/icons.js";import{controlTextLeadingClasses as o,focusRingClasses as t,srOnlyClasses as c}from"../../internal/styles.js";const p={always:"[&.plass-link]:underline",hover:"[&.plass-link]:no-underline [&.plass-link]:hover:underline",none:"[&.plass-link]:no-underline"},m=/* @__PURE__ */["plass-link","cursor-pointer","[&_svg]:pointer-events-none [&_svg]:inline [&_svg]:size-[0.95em] [&_svg]:shrink-0",/* @__PURE__ */["[transition-property:color,text-decoration-color]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]"].join(" "),t,"focus-visible:rounded-[0.25rem]"].join(" ");export const PlTextLink=/* @__PURE__ */s.forwardRef(function PlTextLink({href:s,underline:t="always",color:u,size:d,newTab:f=!1,icon:h,startIcon:v,newTabLabel:k="(opens in a new tab)",render:x,className:g,style:j,children:b,...w},y){const N=h??f,_=!0===N?n(f?l:a,{}):N,T=[m,d?o[d]:"",p[t],u?"[&.plass-link]:text-(--p-accent)":"[&.plass-link]:text-inherit",g??""].filter(Boolean).join(" "),L={"--p-underline":"color-mix(in oklab, currentColor 45%, transparent)","--p-underline-hover":"currentColor","--p-ring":`var(--plass-${u??"primary"}-ring)`,...u?{"--p-accent":`var(--plass-${u}-accent)`}:null},{rel:z,...B}=w,C=f?[...new Set([...(z??"").split(/\s+/).filter(Boolean),"noopener","noreferrer"])].join(" "):z;return i({render:x??n("a",{}),ref:y,props:{href:s,target:f?"_blank":void 0,className:T,style:{...L,...j},children:r(e,{children:[v?n("span",{className:"me-[0.25em]",children:v}):null,b,_?n("span",{className:"ms-[0.25em]",children:_}):null,f?r(e,{children:[" ",n("span",{className:c,children:k})]}):null]}),...B,rel:C}})});
1
+ "use client";import{jsx as n,Fragment as e,jsxs as r}from"react/jsx-runtime";import*as s from"react";import{useRender as i}from"@base-ui/react/use-render";import{ExternalLinkIcon as l,LinkIcon as a}from"../../internal/icons.js";import{controlTextLeadingClasses as o,focusRingClasses as t,srOnlyClasses as c}from"../../internal/styles.js";import{safeRel as p}from"../../internal/link.js";const m={always:"[&.plass-link]:underline",hover:"[&.plass-link]:no-underline [&.plass-link]:hover:underline",none:"[&.plass-link]:no-underline"},d=/* @__PURE__ */["plass-link","cursor-pointer","[&_svg]:pointer-events-none [&_svg]:inline [&_svg]:size-[0.95em] [&_svg]:shrink-0",/* @__PURE__ */["[transition-property:color,text-decoration-color]","[transition-duration:var(--plass-duration)]","[transition-timing-function:var(--plass-ease)]"].join(" "),t,"focus-visible:rounded-[0.25rem]"].join(" ");export const PlTextLink=/* @__PURE__ */s.forwardRef(function PlTextLink({href:t,underline:u="always",color:f,size:v,newTab:h=!1,icon:k,startIcon:x,newTabLabel:b="(opens in a new tab)",render:g,className:j,style:y,children:w,..._},N){const T=k??h,L=!0===T?n(h?l:a,{}):T,z=[d,v?o[v]:"",m[u],f?"[&.plass-link]:text-(--p-accent)":"[&.plass-link]:text-inherit",j??""].filter(Boolean).join(" "),C={"--p-underline":"color-mix(in oklab, currentColor 45%, transparent)","--p-underline-hover":"currentColor","--p-ring":`var(--plass-${f??"primary"}-ring)`,...f?{"--p-accent":`var(--plass-${f}-accent)`}:null},P=s.isValidElement(g)&&void 0!==g.props.href,{rel:$,...B}=_,E=p(h?"_blank":void 0,$);return i({render:g??n("a",{}),ref:N,props:{...P?null:{href:t},target:h?"_blank":void 0,className:z,style:{...C,...y},children:r(e,{children:[x?n("span",{className:"me-[0.25em]",children:x}):null,w,L?n("span",{className:"ms-[0.25em]",children:L}):null,h?r(e,{children:[" ",n("span",{className:c,children:b})]}):null]}),...B,rel:E}})});
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import type { PlassElevation, PlassStyleProps, PlassVariant } from '../../types.js';
3
+ export interface PlToggleProps extends PlassStyleProps, Omit<React.ComponentPropsWithoutRef<'button'>, 'color' | 'value'> {
4
+ /**
5
+ * What the key is made of while it is **off**. On is always the colour family
6
+ * asserting itself, whichever material was asked for.
7
+ *
8
+ * - `solid` — the densest clear glass, which fills with the family's gradient
9
+ * when it goes on. The loudest, for a toggle a screen is steered by.
10
+ * - `glass` — the canonical sheet with a hairline round it. The default.
11
+ * - `ghost` — nothing at all until the pointer arrives or it goes on. What a
12
+ * toolbar wants.
13
+ * @default 'glass'
14
+ */
15
+ variant?: PlassVariant;
16
+ /** Whether it is on. Use with `onPressedChange` for a controlled toggle. */
17
+ pressed?: boolean;
18
+ /** Whether it starts on, for an uncontrolled one. @default false */
19
+ defaultPressed?: boolean;
20
+ onPressedChange?: (pressed: boolean) => void;
21
+ /** Identifies the toggle inside a `PlToggleGroup`. */
22
+ value?: string;
23
+ /**
24
+ * Drop shadow depth. `0` — the default, and one below a `PlButton`'s — is
25
+ * flat: a toggle is a state rather than an action, and a state does not float
26
+ * off the page waiting to be taken.
27
+ * @default 0
28
+ */
29
+ elevation?: PlassElevation;
30
+ /** Content placed before the label. Sized in `em`, so it tracks the label. */
31
+ startIcon?: React.ReactNode;
32
+ /** Content placed after the label. */
33
+ endIcon?: React.ReactNode;
34
+ /** Stretches to the width of the container. */
35
+ fullWidth?: boolean;
36
+ /**
37
+ * The label. Left out, the toggle goes square around whatever icon it was
38
+ * given — which is what a toolbar toggle is. An icon-only toggle still needs
39
+ * an `aria-label`.
40
+ */
41
+ children?: React.ReactNode;
42
+ }
43
+ /**
44
+ * A button that stays down.
45
+ *
46
+ * The difference from a `PlSwitch` is what the press *is*: a switch changes a
47
+ * setting and the change is the point; a toggle changes the state of the thing
48
+ * beside it — bold on the selected words, the grid on the canvas, the filter on
49
+ * the list. The difference from a `PlCheckbox` is that this one is a control
50
+ * rather than an answer, so it never goes in a form.
51
+ *
52
+ * Base UI's Toggle owns `aria-pressed` and the controlled/uncontrolled pair.
53
+ * What is left here is the surface, and the rule that off is neutral.
54
+ */
55
+ export declare const PlToggle: React.ForwardRefExoticComponent<PlToggleProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsxs as s}from"react/jsx-runtime";import*as e from"react";import{Toggle as o}from"@base-ui/react/toggle";import{ButtonGroupContext as a}from"../../internal/button-group.js";import{controlHeightClasses as l,controlSlots as r,controlSquareClasses as t,controlTextClasses as p,disabledClasses as i,focusRingClasses as n,gapClasses as v,glassClasses as g,hasContent as d,iconClasses as h,paddingXClasses as x,radiusClasses as c,transitionClasses as f}from"../../internal/styles.js";const b={xs:`${l.xs} ${v.xs} ${c.xs} ${p.xs}`,sm:`${l.sm} ${v.sm} ${c.sm} ${p.sm}`,md:`${l.md} ${v.md} ${c.md} ${p.md}`,lg:`${l.lg} ${v.lg} ${c.lg} ${p.lg}`,xl:`${l.xl} ${v.xl} ${c.xl} ${p.xl}`},m={xs:`${t.xs} px-0`,sm:`${t.sm} px-0`,md:`${t.md} px-0`,lg:`${t.lg} px-0`,xl:`${t.xl} px-0`},$=/* @__PURE__ */["relative inline-flex shrink-0 select-none items-center justify-center","whitespace-nowrap align-middle font-semibold leading-none","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",f,n,h].join(" "),u={solid:/* @__PURE__ */[g,"text-(--plass-muted-fg) bg-(--plass-glass-hover)","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),glass:/* @__PURE__ */[g,"border text-(--plass-muted-fg) bg-(--plass-glass)","[border-color:var(--plass-glass-line)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:"text-(--plass-muted-fg) bg-transparent"},w={solid:/* @__PURE__ */["hover:bg-(--plass-glass-press) hover:text-(--plass-fg)","hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]","active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]"].join(" "),glass:/* @__PURE__ */["hover:bg-(--plass-glass-hover) hover:text-(--plass-fg) hover:[border-color:var(--p-line)]","hover:[box-shadow:var(--p-elev-hover),var(--plass-gloss-glass)]","active:bg-(--plass-glass-press)","active:[box-shadow:var(--p-elev-press),var(--plass-gloss-glass)]"].join(" "),ghost:"hover:bg-(--p-soft) hover:text-(--plass-fg) active:bg-(--p-soft-hover)"},j={solid:/* @__PURE__ */["text-(--p-on-solid) [background-image:var(--p-fill)]","[box-shadow:var(--p-elev),var(--p-lift)]"].join(" "),glass:/* @__PURE__ */[g,"border text-(--p-accent) bg-(--p-soft)","[border-color:var(--p-line-hover)]","[box-shadow:var(--p-elev),var(--plass-gloss-glass)]"].join(" "),ghost:"text-(--p-accent) bg-(--p-soft)"},y={solid:/* @__PURE__ */["hover:brightness-105 hover:[box-shadow:var(--p-elev-hover),var(--p-lift-hover)]","active:brightness-95 active:[box-shadow:var(--p-elev-press),var(--p-lift-press)]"].join(" "),glass:"hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)",ghost:"hover:bg-(--p-soft-hover) active:bg-(--p-soft-press)"};export const PlToggle=/* @__PURE__ */e.forwardRef(function PlToggle({variant:l,size:t,color:p,density:n,elevation:v,pressed:g,defaultPressed:h,onPressedChange:c,value:f,startIcon:P,endIcon:T,fullWidth:k=!1,disabled:C,className:z,style:E,children:I,onPointerMove:M,...N},B){const R=e.useContext(a),W=l??R?.variant??"glass",X=t??R?.size??"md",Y=p??R?.color??"primary",q=n??R?.density??"default",A=v??R?.elevation??0,D=C??R?.disabled??!1,F=!d(I);return s(o,{ref:B,value:f,pressed:g,defaultPressed:h,onPressedChange:s=>c?.(s),disabled:D,className:s=>[$,b[X],F?m[X]:x[q][X],D?i[W]:s.pressed?`${j[W]} ${y[W]}`:`${u[W]} ${w[W]}`,D?"":"plass-glow cursor-pointer",k?"w-full":"",z??""].filter(Boolean).join(" "),style:{...r(Y,A,W),...E},onPointerMove:s=>{const e=s.currentTarget;e.style.setProperty("--p-mx",`${s.nativeEvent.offsetX}px`),e.style.setProperty("--p-my",`${s.nativeEvent.offsetY}px`),M?.(s)},...N,children:[P,I,T]})});
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import type { PlassElevation, PlassOrientation, PlassStyleProps, PlassVariant } from '../../types.js';
3
+ export interface PlToggleGroupProps extends PlassStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
4
+ /** Passed to every toggle in the set. @default 'glass' */
5
+ variant?: PlassVariant;
6
+ /**
7
+ * Which toggles are on, by their `value`. An array in both the single and the
8
+ * multiple case — Base UI's own shape, and the one that does not change type
9
+ * when `multiple` is turned on.
10
+ */
11
+ value?: readonly string[];
12
+ /** Which start on, for an uncontrolled set. */
13
+ defaultValue?: readonly string[];
14
+ onValueChange?: (value: string[]) => void;
15
+ /**
16
+ * Whether more than one can be on at a time. Off, turning one on turns the
17
+ * last one off — which is a one-of-a-set, and worth a second thought: if the
18
+ * choice is a *value* rather than a state, that is a `PlSegmentedButton` or a
19
+ * `PlRadioGroup`.
20
+ * @default false
21
+ */
22
+ multiple?: boolean;
23
+ /** Which way the toggles run. @default 'horizontal' */
24
+ orientation?: PlassOrientation;
25
+ /** Drop shadow depth, passed to every toggle in the set. @default 0 */
26
+ elevation?: PlassElevation;
27
+ /** Disables every toggle in the set at once. */
28
+ disabled?: boolean;
29
+ /** Whether the arrow keys wrap around at the ends. @default true */
30
+ loopFocus?: boolean;
31
+ /** Stretches to the container and divides the width evenly between toggles. */
32
+ fullWidth?: boolean;
33
+ children?: React.ReactNode;
34
+ }
35
+ /**
36
+ * A set of toggles that share one state.
37
+ *
38
+ * Two things are happening and only one of them is visual. The corners facing a
39
+ * neighbour are squared off — that is the look. The other half is that the set
40
+ * owns the value: the toggles report into one array, `multiple` decides whether
41
+ * more than one of them can be on, and `variant`, `size`, `color`, `density`,
42
+ * `elevation` and `disabled` are set once here rather than on every toggle.
43
+ *
44
+ * Base UI owns the roving tab index — one tab stop for the whole set, arrow keys
45
+ * between the members — which is what makes a toolbar of eight toggles two key
46
+ * presses deep instead of eight.
47
+ */
48
+ export declare const PlToggleGroup: React.ForwardRefExoticComponent<PlToggleGroupProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import*as o from"react";import{ToggleGroup as l}from"@base-ui/react/toggle-group";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"},a=/* @__PURE__ */["inline-flex align-middle","[&>*]:relative [&>*:hover]:z-10 [&>*:focus-visible]:z-10","[&>*]:shrink-0"].join(" ");export const PlToggleGroup=/* @__PURE__ */o.forwardRef(function PlToggleGroup({variant:s,size:d,color:u,density:c,elevation:f,value:m,defaultValue:h,onValueChange:p,multiple:g=!1,orientation:v="horizontal",disabled:x,loopFocus:b=!0,fullWidth:z=!1,className:j,children:V,...w},y){const G=o.useMemo(()=>({variant:s,size:d,color:u,density:c,elevation:f,disabled:x}),[s,d,u,c,f,x]);return e(i.Provider,{value:G,children:e(l,{ref:y,value:m,defaultValue:h,onValueChange:e=>p?.(e),multiple:g,orientation:v,disabled:x,loopFocus:b,className:t(a,"vertical"===v?"flex-col":"flex-row",n[v],"glass"===(s??"glass")?r[v]:"",z?"flex w-full [&>*]:flex-1":"",j),...w,children:V})})});
@@ -0,0 +1,4 @@
1
+ export { PlToggle } from './PlToggle.js';
2
+ export type { PlToggleProps } from './PlToggle.js';
3
+ export { PlToggleGroup } from './PlToggleGroup.js';
4
+ export type { PlToggleGroupProps } from './PlToggleGroup.js';
@@ -0,0 +1 @@
1
+ export{PlToggle}from"./PlToggle.js";export{PlToggleGroup}from"./PlToggleGroup.js";
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import type { PlassStyleProps } from '../../types.js';
3
+ /** One thing that can be on either side. */
4
+ export interface PlTransferItem {
5
+ /** What identifies it, and what `value` is a list of. */
6
+ value: string;
7
+ /** What the row says. */
8
+ label: React.ReactNode;
9
+ /** In the list but not movable. */
10
+ disabled?: boolean;
11
+ }
12
+ export interface PlTransferProps extends PlassStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
13
+ /** Everything that can be on either side, in the order the lists show it. */
14
+ items: readonly PlTransferItem[];
15
+ /** What is on the trailing side. Use with `onValueChange` for a controlled pair. */
16
+ value?: readonly string[];
17
+ /** What starts there, for an uncontrolled one. */
18
+ defaultValue?: readonly string[];
19
+ onValueChange?: (value: string[]) => void;
20
+ /** The heading over the leading list. @default 'Available' */
21
+ sourceLabel?: React.ReactNode;
22
+ /** And over the trailing one. @default 'Selected' */
23
+ targetLabel?: React.ReactNode;
24
+ /** Puts a filter above each list. @default false */
25
+ searchable?: boolean;
26
+ /** What that filter says while it is empty. @default 'Search' */
27
+ searchLabel?: string;
28
+ /** What a list with nothing in it says. @default 'Nothing here' */
29
+ emptyLabel?: string;
30
+ /** What the tick in a list's heading is announced as. @default 'Select all' */
31
+ selectAllLabel?: string;
32
+ /** What the two arrows are announced as. */
33
+ toTargetLabel?: string;
34
+ toSourceLabel?: string;
35
+ /** How tall each list is. A number of pixels or any CSS length. @default 220 */
36
+ height?: number | string;
37
+ /** Nothing can be ticked or moved. */
38
+ disabled?: boolean;
39
+ }
40
+ /**
41
+ * Two lists and the arrows between them: everything that could be chosen on one
42
+ * side, everything that has been on the other.
43
+ *
44
+ * It is the shape for a choice that is *long* — the columns in a report, the
45
+ * permissions on a role, the people on a channel — where a `PlCombobox` with
46
+ * forty chips in its field stops being readable and a list of forty checkboxes
47
+ * gives no answer to "what did I actually pick". Below about a dozen options,
48
+ * one of those two is the smaller component.
49
+ *
50
+ * The order of `items` is the order both lists show, so a row does not move when
51
+ * it is sent across and back. What ticks are for is choosing which rows to move;
52
+ * `value` is which side they are on, and the two are deliberately separate —
53
+ * **ticking is not choosing**.
54
+ */
55
+ export declare const PlTransfer: React.ForwardRefExoticComponent<PlTransferProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as a from"react";import{PlCheckbox as t}from"../checkbox/PlCheckbox.js";import{PlIconButton as s}from"../icon-button/PlIconButton.js";import{PlTextField as r}from"../text-field/PlTextField.js";import{ArrowRightIcon as i}from"../../internal/icons.js";import{searchText as n}from"../../internal/search.js";import{cx as o,fieldRestClasses as c,hasContent as d,metaTextClasses as h,paddingXClasses as m,radiusClasses as u,surfaceSlots as b,toLength as f}from"../../internal/styles.js";const v={xs:"py-1",sm:"py-1.5",md:"py-2",lg:"py-2.5",xl:"py-3"},p={xs:"py-0.5",sm:"py-1",md:"py-1",lg:"py-1.5",xl:"py-2"};function Panel({title:a,rows:s,ticked:i,onTick:n,onTickAll:d,search:f,onSearch:g,searchable:y,disabled:x,height:k,emptyLabel:w,searchLabel:S,selectAllLabel:L,style:N}){const{variant:T,size:j,color:A,density:C}=N,z=s.filter(e=>!e.disabled),P=z.filter(e=>i.has(e.value)),M=z.length>0&&P.length===z.length,V=P.length>0&&!M,_=m[C][j];return l("div",{className:o("flex min-w-0 flex-col overflow-hidden",c[T],u[j]),style:b(A,0),children:[l("div",{className:o("flex items-center gap-2 border-b [border-color:var(--plass-divider)]",_,v[j]),children:[e(t,{size:j,color:A,checked:M,indeterminate:V,disabled:x||0===z.length,"aria-label":L,onCheckedChange:e=>d(!0===e)}),e("span",{className:o("min-w-0 flex-1 truncate font-medium",h[j]),children:a}),l("span",{className:o("shrink-0 tabular-nums text-(--plass-muted-fg)",h[j]),children:[P.length,"/",s.length]})]}),y?e("div",{className:o(_,v[j]),children:e(r,{size:j,color:A,density:C,fullWidth:!0,variant:"ghost",disabled:x,placeholder:S,"aria-label":S,value:f,onChange:e=>g(e.target.value)})}):null,e("div",{className:"min-h-0 flex-1 overflow-y-auto overscroll-contain",style:void 0===k?void 0:{height:k},children:e("div",{className:o("flex flex-col",_,v[j]),children:0===s.length?e("span",{className:o("text-(--plass-muted-fg)",h[j],p[j]),children:w}):s.map(l=>e(t,{size:j,color:A,className:p[j],label:l.label,checked:i.has(l.value),disabled:x||l.disabled,onCheckedChange:e=>n(l.value,!0===e)},l.value))})})]})}function narrow(e,l){const a=n(l);return""===a?e:e.filter(e=>"string"!=typeof e.label||n(e.label).includes(a))}export const PlTransfer=/* @__PURE__ */a.forwardRef(function PlTransfer({items:t,value:r,defaultValue:n,onValueChange:c,sourceLabel:h="Available",targetLabel:m="Selected",searchable:u=!1,searchLabel:b="Search",emptyLabel:v="Nothing here",selectAllLabel:p="Select all",toTargetLabel:g="Move to selected",toSourceLabel:y="Move to available",height:x=220,disabled:k=!1,variant:w="glass",size:S="md",color:L="primary",density:N="default",className:T,...j},A){const[C,z]=a.useState(n??[]),P=r??C,[M,V]=a.useState(()=>new Set),[_,B]=a.useState(""),[F,I]=a.useState(""),R=a.useMemo(()=>new Set(P),[P]),W=a.useMemo(()=>t.filter(e=>!R.has(e.value)),[t,R]),q=a.useMemo(()=>t.filter(e=>R.has(e.value)),[t,R]),tick=(e,l)=>{V(a=>{const t=new Set(a);return l?t.add(e):t.delete(e),t})},tickAll=(e,l)=>{V(a=>{const t=new Set(a);for(const a of e)a.disabled||(l?t.add(a.value):t.delete(a.value));return t})},move=(e,l)=>{const a=e.filter(e=>!e.disabled&&M.has(e.value));if(0===a.length)return;const s=new Set(a.map(e=>e.value)),i=l?t.filter(e=>R.has(e.value)||s.has(e.value)).map(e=>e.value):P.filter(e=>!s.has(e));V(e=>new Set([...e].filter(e=>!s.has(e)))),(e=>{void 0===r&&z(e),c?.(e)})(i)},D=narrow(W,_),E=narrow(q,F),G=D.some(e=>!e.disabled&&M.has(e.value)),H=E.some(e=>!e.disabled&&M.has(e.value)),J=f(x),K={variant:w,size:S,color:L,density:N},O="ghost"===w?"ghost":"glass";return l("div",{ref:A,className:o("grid w-full items-center gap-3","[grid-template-columns:minmax(0,1fr)_auto_minmax(0,1fr)]",T),...j,children:[e(Panel,{title:d(h)?h:"Available",rows:D,ticked:M,onTick:tick,onTickAll:e=>tickAll(D,e),search:_,onSearch:B,searchable:u,disabled:k,height:J,emptyLabel:v,searchLabel:b,selectAllLabel:p,style:K}),l("div",{className:"flex flex-col gap-2",children:[e(s,{size:S,color:L,variant:O,label:g,disabled:k||!G,onClick:()=>move(D,!0),icon:e(i,{})}),e(s,{size:S,color:L,variant:O,label:y,disabled:k||!H,onClick:()=>move(E,!1),icon:e("span",{className:"flex rotate-180",children:e(i,{})})})]}),e(Panel,{title:d(m)?m:"Selected",rows:E,ticked:M,onTick:tick,onTickAll:e=>tickAll(E,e),search:F,onSearch:I,searchable:u,disabled:k,height:J,emptyLabel:v,searchLabel:b,selectAllLabel:p,style:K})]})});
@@ -0,0 +1,2 @@
1
+ export { PlTransfer } from './PlTransfer.js';
2
+ export type { PlTransferProps, PlTransferItem } from './PlTransfer.js';
@@ -0,0 +1 @@
1
+ export{PlTransfer}from"./PlTransfer.js";
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <avatar-group>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/avatar-group';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <color-picker>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/color-picker';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <command-palette>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/command-palette';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <fieldset>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/fieldset';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <footer>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/footer';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <form>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/form';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <header>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/header';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <menubar>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/menubar';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <navigation-menu>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/navigation-menu';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <page-layout>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/page-layout';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <sidebar>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/sidebar';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <toggle>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/toggle';
@@ -0,0 +1,2 @@
1
+ /* Scan manifest for <transfer>. Needs `plass-ui/css/base.css` first. */
2
+ @source '../components/transfer';
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export * from './components/animate-typing/index.js';
14
14
  export * from './components/animate-zoom/index.js';
15
15
  export * from './components/aspect-ratio/index.js';
16
16
  export * from './components/avatar/index.js';
17
+ export * from './components/avatar-group/index.js';
17
18
  export * from './components/badge/index.js';
18
19
  export * from './components/blockquote/index.js';
19
20
  export * from './components/bottom-navigation/index.js';
@@ -27,26 +28,35 @@ export * from './components/chat-bubble/index.js';
27
28
  export * from './components/checkbox/index.js';
28
29
  export * from './components/chip/index.js';
29
30
  export * from './components/collapsible/index.js';
31
+ export * from './components/color-picker/index.js';
30
32
  export * from './components/combobox/index.js';
33
+ export * from './components/command-palette/index.js';
31
34
  export * from './components/container/index.js';
32
35
  export * from './components/date-picker/index.js';
33
36
  export * from './components/date-range-picker/index.js';
34
37
  export * from './components/date-time-picker/index.js';
35
38
  export * from './components/divider/index.js';
36
39
  export * from './components/drawer/index.js';
40
+ export * from './components/fieldset/index.js';
37
41
  export * from './components/file-picker/index.js';
38
42
  export * from './components/floating-bottom-navigation/index.js';
43
+ export * from './components/footer/index.js';
44
+ export * from './components/form/index.js';
39
45
  export * from './components/grid/index.js';
46
+ export * from './components/header/index.js';
40
47
  export * from './components/highlight/index.js';
41
48
  export * from './components/hot-keys/index.js';
42
49
  export * from './components/icon/index.js';
43
50
  export * from './components/icon-button/index.js';
44
51
  export * from './components/list/index.js';
45
52
  export * from './components/menu/index.js';
53
+ export * from './components/menubar/index.js';
46
54
  export * from './components/modal/index.js';
55
+ export * from './components/navigation-menu/index.js';
47
56
  export * from './components/number-field/index.js';
48
57
  export * from './components/otp-field/index.js';
49
58
  export * from './components/overlay/index.js';
59
+ export * from './components/page-layout/index.js';
50
60
  export * from './components/pagination/index.js';
51
61
  export * from './components/panes/index.js';
52
62
  export * from './components/pill/index.js';
@@ -59,6 +69,7 @@ export * from './components/rating/index.js';
59
69
  export * from './components/scroll-zone/index.js';
60
70
  export * from './components/segmented-button/index.js';
61
71
  export * from './components/select/index.js';
72
+ export * from './components/sidebar/index.js';
62
73
  export * from './components/skeleton/index.js';
63
74
  export * from './components/slider/index.js';
64
75
  export * from './components/spoiler/index.js';
@@ -70,6 +81,8 @@ export * from './components/text-link/index.js';
70
81
  export * from './components/time-picker/index.js';
71
82
  export * from './components/timeline/index.js';
72
83
  export * from './components/toast/index.js';
84
+ export * from './components/toggle/index.js';
73
85
  export * from './components/toolbar/index.js';
74
86
  export * from './components/tooltip/index.js';
87
+ export * from './components/transfer/index.js';
75
88
  export * from './components/typography/index.js';