@parallelworks/ui 0.54.0 → 0.56.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 (50) hide show
  1. package/README.md +65 -26
  2. package/dist/access/AccessDrawer.d.ts +30 -19
  3. package/dist/access/AccessDrawer.js +92 -67
  4. package/dist/access/AccessList.d.ts +17 -16
  5. package/dist/access/AccessList.js +218 -114
  6. package/dist/access/GrantView.d.ts +9 -8
  7. package/dist/access/GrantView.js +34 -36
  8. package/dist/access/PrincipalPicker.d.ts +38 -0
  9. package/dist/access/{GroupPicker.js → PrincipalPicker.js} +173 -80
  10. package/dist/access/index.d.ts +1 -1
  11. package/dist/access/model.d.ts +67 -24
  12. package/dist/access/model.js +111 -47
  13. package/dist/access/strings.d.ts +63 -39
  14. package/dist/access/strings.js +114 -84
  15. package/dist/access/types.d.ts +32 -14
  16. package/dist/ai/adapter/mock/mockAdapter.js +1 -1
  17. package/dist/ai/core/useSSEStream.d.ts +1 -1
  18. package/dist/components/Checkbox.js +1 -1
  19. package/dist/components/Combobox.js +25 -20
  20. package/dist/components/DatePicker.js +62 -58
  21. package/dist/components/Drawer.js +3 -1
  22. package/dist/components/Field.d.ts +8 -3
  23. package/dist/components/Field.js +32 -12
  24. package/dist/components/HoverCard.js +15 -10
  25. package/dist/components/InfoTip.d.ts +3 -1
  26. package/dist/components/InfoTip.js +2 -1
  27. package/dist/components/Menu.js +3 -0
  28. package/dist/components/Modal.js +32 -27
  29. package/dist/components/Popover.d.ts +4 -1
  30. package/dist/components/Popover.js +31 -26
  31. package/dist/components/Radio.js +1 -1
  32. package/dist/components/Select.js +47 -42
  33. package/dist/components/Slider.js +3 -3
  34. package/dist/components/Switch.js +1 -1
  35. package/dist/components/Tooltip.js +16 -11
  36. package/dist/components/fieldBox.js +1 -1
  37. package/dist/components/fullscreen.d.ts +4 -0
  38. package/dist/components/fullscreen.js +18 -0
  39. package/dist/editor/EditorField.js +1 -2
  40. package/dist/editor/Monaco.js +26 -2
  41. package/dist/editor/themes.js +1 -1
  42. package/dist/graph/GraphEditorDialogs.d.ts +1 -1
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.js +2 -2
  45. package/dist/styles/theme.css +10 -10
  46. package/dist/styles.css +1 -1
  47. package/dist/theme/derive.d.ts +2 -0
  48. package/dist/theme/derive.js +25 -14
  49. package/package.json +2 -2
  50. package/dist/access/GroupPicker.d.ts +0 -35
@@ -17,9 +17,14 @@ export declare const FieldRowContext: import("react").Context<boolean>;
17
17
  /** The surrounding Field's control props, for a control drawn further down than the Field's
18
18
  * own children. Null outside a Field. */
19
19
  export declare function useFieldControl(): FieldControlProps | null;
20
- /** The label's id, for a control that isn't a Base UI one and so isn't named by the label's
21
- * `for`, such as a button that opens a picker. */
22
- export declare function useFieldLabelId(): string | undefined;
20
+ /** The surrounding Field's control props for a control that isn't a Base UI one, such as a
21
+ * code editor, a button that opens a picker or an `<output>`, which the label's `for` can't
22
+ * name. The props name it by the label's id instead, and while it is mounted the label drops
23
+ * its `for`, which would point at no element, and focuses the control by its id when clicked.
24
+ * Null outside a Field. */
25
+ export declare function useFieldLabelledControl(): (FieldControlProps & {
26
+ 'aria-labelledby': string;
27
+ }) | null;
23
28
  export type FieldLayout = 'stacked' | 'inline';
24
29
  export interface FieldProps {
25
30
  label: ReactNode;
@@ -5,7 +5,7 @@ import { focusCameFromKeyboard, trackFocusModality } from "./focus.js";
5
5
  import { InfoTip, formatInfoTipContent } from "./InfoTip.js";
6
6
  import { RequiredMark } from "./RequiredMark.js";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { createContext, use, useEffect, useId, useState } from "react";
8
+ import { createContext, use, useCallback, useEffect, useId, useLayoutEffect, useState } from "react";
9
9
  import { Field } from "@base-ui/react/field";
10
10
  import { Fieldset } from "@base-ui/react/fieldset";
11
11
  //#region src/components/Field.tsx
@@ -19,10 +19,19 @@ var FieldRowContext = createContext(false);
19
19
  function useFieldControl() {
20
20
  return use(ControlContext)?.control ?? null;
21
21
  }
22
- /** The label's id, for a control that isn't a Base UI one and so isn't named by the label's
23
- * `for`, such as a button that opens a picker. */
24
- function useFieldLabelId() {
25
- return use(ControlContext)?.labelId;
22
+ /** The surrounding Field's control props for a control that isn't a Base UI one, such as a
23
+ * code editor, a button that opens a picker or an `<output>`, which the label's `for` can't
24
+ * name. The props name it by the label's id instead, and while it is mounted the label drops
25
+ * its `for`, which would point at no element, and focuses the control by its id when clicked.
26
+ * Null outside a Field. */
27
+ function useFieldLabelledControl() {
28
+ const context = use(ControlContext);
29
+ const claim = context?.claimLabel;
30
+ useLayoutEffect(() => claim?.(), [claim]);
31
+ return context ? {
32
+ ...context.control,
33
+ "aria-labelledby": context.labelId
34
+ } : null;
26
35
  }
27
36
  var labelSizes = {
28
37
  sm: {
@@ -75,23 +84,31 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
75
84
  ...isInvalid && { "aria-invalid": true },
76
85
  ...required && { "aria-required": true }
77
86
  };
87
+ const [labelledBy, setLabelledBy] = useState(0);
78
88
  const controlContext = {
79
89
  control,
80
- labelId
90
+ labelId,
91
+ claimLabel: useCallback(() => {
92
+ setLabelledBy((n) => n + 1);
93
+ return () => setLabelledBy((n) => n - 1);
94
+ }, [])
81
95
  };
82
96
  const inline = layout === "inline";
83
97
  const inRow = use(FieldRowContext) && !inline && !group;
84
98
  const labelSize = labelSizes[size];
85
99
  const [helpOpen, setHelpOpen] = useState(false);
100
+ const [controlBox, setControlBox] = useState(null);
86
101
  useEffect(() => {
87
102
  if (help) trackFocusModality();
88
103
  }, [help]);
104
+ const besideControl = helpOpen === "focus";
89
105
  const helpTip = help && /* @__PURE__ */ jsx(InfoTip, {
90
106
  text: help,
91
107
  describesControl: true,
92
108
  open: Boolean(helpOpen),
93
109
  onOpenChange: (open) => setHelpOpen(open && "pointer"),
94
- keepSide: helpOpen === "focus"
110
+ side: besideControl ? "right" : "top",
111
+ anchor: besideControl ? controlBox : void 0
95
112
  });
96
113
  const helpFocus = help ? {
97
114
  onFocus: () => {
@@ -119,6 +136,7 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
119
136
  ] });
120
137
  const placed = typeof children === "function" ? children(control) : children;
121
138
  const body = /* @__PURE__ */ jsxs("div", {
139
+ ref: help ? setControlBox : void 0,
122
140
  className: cx("flex w-full min-w-0 flex-col gap-1", inRow && "row-start-2"),
123
141
  ...helpFocus,
124
142
  children: [
@@ -180,7 +198,10 @@ function Field$1({ label, hint, help, actions, description, error, invalid, requ
180
198
  className: labelBox,
181
199
  children: [
182
200
  /* @__PURE__ */ jsxs(Field.Label, {
183
- ...typeof children === "function" || ownId !== void 0 ? { htmlFor: id } : {},
201
+ ...labelledBy > 0 ? {
202
+ htmlFor: void 0,
203
+ onClick: () => document.getElementById(id)?.focus()
204
+ } : typeof children === "function" || ownId !== void 0 ? { htmlFor: id } : {},
184
205
  id: labelId,
185
206
  "data-field-label": true,
186
207
  children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
@@ -229,11 +250,10 @@ function RevealToggle({ revealed, onRevealedChange, disabled }) {
229
250
  * up with the fields around it. For a set of names and values on their own, use
230
251
  * DescriptionList. */
231
252
  function FieldValue({ size = "md", className, children, ...props }) {
232
- const control = useFieldControl();
233
- const labelId = useFieldLabelId();
253
+ const control = useFieldLabelledControl();
234
254
  return /* @__PURE__ */ jsx("output", {
235
255
  id: control?.id,
236
- "aria-labelledby": labelId,
256
+ "aria-labelledby": control?.["aria-labelledby"],
237
257
  "aria-describedby": control?.["aria-describedby"],
238
258
  ...props,
239
259
  className: cx("flex min-w-0 items-center break-words text-(--theme-app)", size === "sm" ? "min-h-7 text-xs" : "min-h-9 text-sm", className),
@@ -241,4 +261,4 @@ function FieldValue({ size = "md", className, children, ...props }) {
241
261
  });
242
262
  }
243
263
  //#endregion
244
- export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl, useFieldLabelId };
264
+ export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl, useFieldLabelledControl };
@@ -1,4 +1,5 @@
1
1
  import { cardClassName, positionerClassName } from "./floating.js";
2
+ import { useFullscreenContainer } from "./fullscreen.js";
2
3
  import { jsx, jsxs } from "react/jsx-runtime";
3
4
  import { PreviewCard } from "@base-ui/react/preview-card";
4
5
  //#region src/components/HoverCard.tsx
@@ -7,23 +8,27 @@ import { PreviewCard } from "@base-ui/react/preview-card";
7
8
  * reachable another way, such as the page the anchor links to. The card
8
9
  * stays open while the pointer is over it. */
9
10
  function HoverCard({ card, children, defaultOpen = false }) {
11
+ const fullscreen = useFullscreenContainer();
10
12
  return /* @__PURE__ */ jsxs(PreviewCard.Root, {
11
13
  defaultOpen,
12
14
  children: [/* @__PURE__ */ jsx(PreviewCard.Trigger, {
13
15
  delay: 350,
14
16
  closeDelay: 150,
15
17
  render: children
16
- }), /* @__PURE__ */ jsx(PreviewCard.Portal, { children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
17
- side: "bottom",
18
- align: "start",
19
- sideOffset: 8,
20
- collisionPadding: 8,
21
- className: positionerClassName,
22
- children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
23
- className: cardClassName,
24
- children: card
18
+ }), /* @__PURE__ */ jsx(PreviewCard.Portal, {
19
+ container: fullscreen,
20
+ children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
21
+ side: "bottom",
22
+ align: "start",
23
+ sideOffset: 8,
24
+ collisionPadding: 8,
25
+ className: positionerClassName,
26
+ children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
27
+ className: cardClassName,
28
+ children: card
29
+ })
25
30
  })
26
- }) })]
31
+ })]
27
32
  });
28
33
  }
29
34
  //#endregion
@@ -19,8 +19,10 @@ interface InfoTipProps {
19
19
  /** Stays on `side` rather than flipping where it doesn't fit, as while the control it
20
20
  * describes is typed in, which a flipped tip would cover. */
21
21
  keepSide?: boolean | undefined;
22
+ /** Placed against this element rather than the ?, such as beside the control it describes. */
23
+ anchor?: Element | null | undefined;
22
24
  }
23
25
  /** Extra detail behind an info trigger. A popover that also opens on hover, so
24
26
  * touch and screen reader users can reach it and its links. */
25
- export declare function InfoTip({ text, side, className, children, describesControl, open, onOpenChange, keepSide, }: InfoTipProps): import("react").JSX.Element | null;
27
+ export declare function InfoTip({ text, side, className, children, describesControl, open, onOpenChange, keepSide, anchor, }: InfoTipProps): import("react").JSX.Element | null;
26
28
  export {};
@@ -31,7 +31,7 @@ function formatInfoTipContent(content) {
31
31
  var hasLink = (content) => new RegExp(linkRegex.source).test(content);
32
32
  /** Extra detail behind an info trigger. A popover that also opens on hover, so
33
33
  * touch and screen reader users can reach it and its links. */
34
- function InfoTip({ text, side = "top", className, children, describesControl = false, open, onOpenChange, keepSide = false }) {
34
+ function InfoTip({ text, side = "top", className, children, describesControl = false, open, onOpenChange, keepSide = false, anchor }) {
35
35
  const t = useStrings().common;
36
36
  if (!text) return children ? /* @__PURE__ */ jsx("span", {
37
37
  className,
@@ -43,6 +43,7 @@ function InfoTip({ text, side = "top", className, children, describesControl = f
43
43
  openOnHover: true,
44
44
  side,
45
45
  keepSide,
46
+ anchor,
46
47
  ...open === void 0 ? {} : { open },
47
48
  ...onOpenChange ? { onOpenChange } : {},
48
49
  ...describesControl && { initialFocus: "none" },
@@ -1,13 +1,16 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { CheckIcon, ChevronRightIcon } from "../icons.js";
3
3
  import { panelClassName, panelItemClassName, positionerClassName } from "./floating.js";
4
+ import { useFullscreenContainer } from "./fullscreen.js";
4
5
  import { Menu } from "@base-ui/react/menu";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/components/Menu.tsx
7
8
  var itemClassName = panelItemClassName;
8
9
  /** The portal, positioner and popup every menu needs. */
9
10
  function MenuPopup({ children, className, side = "bottom", align = "start", anchor, keepMounted = false }) {
11
+ const fullscreen = useFullscreenContainer();
10
12
  return /* @__PURE__ */ jsx(Menu.Portal, {
13
+ container: fullscreen,
11
14
  keepMounted,
12
15
  children: /* @__PURE__ */ jsx(Menu.Positioner, {
13
16
  side,
@@ -1,4 +1,5 @@
1
1
  import { cx } from "../cx.js";
2
+ import { useFullscreenContainer } from "./fullscreen.js";
2
3
  import { closeHandler } from "./dismiss.js";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
5
  import { AlertDialog } from "@base-ui/react/alert-dialog";
@@ -21,37 +22,41 @@ var sizeClasses = {
21
22
  * and a `footer` for the standard layout, as a Drawer has, or lay out the whole
22
23
  * panel with the children. */
23
24
  function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-black/40", className, children, ...popupProps }) {
25
+ const fullscreen = useFullscreenContainer();
24
26
  const onOpenChange = closeHandler(dismissible, onClose);
25
27
  const header = title !== void 0 || description !== void 0;
26
28
  const body = children !== void 0 && children !== null && children !== false;
27
29
  const laidOut = header || footer !== void 0;
28
- const content = /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: cx("fixed inset-0 z-(--layer-modal)", backdropClassName) }), /* @__PURE__ */ jsx(Dialog.Viewport, {
29
- className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
30
- children: /* @__PURE__ */ jsx(Dialog.Popup, {
31
- ...popupProps,
32
- className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], laidOut && "flex max-h-full flex-col", "outline-none", className),
33
- children: laidOut ? /* @__PURE__ */ jsxs(Fragment, { children: [
34
- header && /* @__PURE__ */ jsxs("div", {
35
- className: cx("shrink-0 px-5 pt-5", !body && "pb-5"),
36
- children: [title !== void 0 && /* @__PURE__ */ jsx(ModalTitle, {
37
- className: "m-0 text-lg font-semibold",
38
- children: title
39
- }), description !== void 0 && /* @__PURE__ */ jsx(ModalDescription, {
40
- className: "m-0 mt-1.5 text-sm text-(--theme-muted-text-color)",
41
- children: description
42
- })]
43
- }),
44
- body && /* @__PURE__ */ jsx("div", {
45
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-4",
46
- children
47
- }),
48
- footer !== void 0 && /* @__PURE__ */ jsx("div", {
49
- className: "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3",
50
- children: footer
51
- })
52
- ] }) : children
53
- })
54
- })] });
30
+ const content = /* @__PURE__ */ jsxs(Dialog.Portal, {
31
+ container: fullscreen,
32
+ children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: cx("fixed inset-0 z-(--layer-modal)", backdropClassName) }), /* @__PURE__ */ jsx(Dialog.Viewport, {
33
+ className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
34
+ children: /* @__PURE__ */ jsx(Dialog.Popup, {
35
+ ...popupProps,
36
+ className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], laidOut && "flex max-h-full flex-col", "outline-none", className),
37
+ children: laidOut ? /* @__PURE__ */ jsxs(Fragment, { children: [
38
+ header && /* @__PURE__ */ jsxs("div", {
39
+ className: cx("shrink-0 px-5 pt-5", !body && "pb-5"),
40
+ children: [title !== void 0 && /* @__PURE__ */ jsx(ModalTitle, {
41
+ className: "m-0 text-lg font-semibold",
42
+ children: title
43
+ }), description !== void 0 && /* @__PURE__ */ jsx(ModalDescription, {
44
+ className: "m-0 mt-1.5 text-sm text-(--theme-muted-text-color)",
45
+ children: description
46
+ })]
47
+ }),
48
+ body && /* @__PURE__ */ jsx("div", {
49
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 py-4",
50
+ children
51
+ }),
52
+ footer !== void 0 && /* @__PURE__ */ jsx("div", {
53
+ className: "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3",
54
+ children: footer
55
+ })
56
+ ] }) : children
57
+ })
58
+ })]
59
+ });
55
60
  return alert ? /* @__PURE__ */ jsx(AlertDialog.Root, {
56
61
  open,
57
62
  onOpenChange,
@@ -33,6 +33,9 @@ export interface PopoverOptions extends Omit<BasePopover.Popup.Props, 'className
33
33
  * a list read before one item in it is picked, or nowhere, for a popover that
34
34
  * offers an action on what the reader is doing, such as copying a selection. */
35
35
  initialFocus?: 'first-control' | 'popup' | 'none';
36
+ /** Placed against this element rather than its trigger, such as a tip beside the field it
37
+ * describes. It still opens and closes from the trigger. */
38
+ anchor?: Element | null | undefined;
36
39
  /** Stays on `side` where it doesn't fit rather than flipping to the other, for a popup that
37
40
  * must not cover what is there, such as the control a tip describes while it is typed in. */
38
41
  keepSide?: boolean | undefined;
@@ -46,5 +49,5 @@ export interface PopoverOptions extends Omit<BasePopover.Popup.Props, 'className
46
49
  * flips and shifts to stay on screen, closes on Escape or a press outside, and
47
50
  * returns focus to the trigger. Use a Menu for a list of actions, and a
48
51
  * HoverCard for a preview a link already leads to. */
49
- export declare function Popover({ trigger, at, title, open, defaultOpen, onOpenChange, side, align, openOnHover, initialFocus, keepSide, variant, className, children, ...popupProps }: PopoverProps): import("react").JSX.Element;
52
+ export declare function Popover({ trigger, at, title, open, defaultOpen, onOpenChange, side, align, openOnHover, initialFocus, anchor, keepSide, variant, className, children, ...popupProps }: PopoverProps): import("react").JSX.Element;
50
53
  export {};
@@ -1,5 +1,6 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { panelClassName, positionerClassName, tipClassName, toPositionerAnchor } from "./floating.js";
3
+ import { useFullscreenContainer } from "./fullscreen.js";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  import { createContext, use, useCallback, useRef } from "react";
5
6
  import { Popover } from "@base-ui/react/popover";
@@ -16,7 +17,8 @@ function useClosePopover() {
16
17
  * flips and shifts to stay on screen, closes on Escape or a press outside, and
17
18
  * returns focus to the trigger. Use a Menu for a list of actions, and a
18
19
  * HoverCard for a preview a link already leads to. */
19
- function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side = "bottom", align = "center", openOnHover = false, initialFocus = "first-control", keepSide = false, variant = "panel", className, children, ...popupProps }) {
20
+ function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side = "bottom", align = "center", openOnHover = false, initialFocus = "first-control", anchor, keepSide = false, variant = "panel", className, children, ...popupProps }) {
21
+ const fullscreen = useFullscreenContainer();
20
22
  const tip = variant === "tip";
21
23
  const popup = useRef(null);
22
24
  const actions = useRef(null);
@@ -33,32 +35,35 @@ function Popover$1({ trigger, at, title, open, defaultOpen, onOpenChange, side =
33
35
  delay: 300,
34
36
  closeDelay: 150
35
37
  } : {}
36
- }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
37
- side,
38
- align,
39
- sideOffset: tip ? 8 : 6,
40
- collisionPadding: 8,
41
- ...keepSide && { collisionAvoidance: { side: "none" } },
42
- ...at ? { anchor: toPositionerAnchor(at) } : {},
43
- className: positionerClassName,
44
- children: /* @__PURE__ */ jsxs(Popover.Popup, {
45
- ...popupProps,
46
- ref: popup,
47
- initialFocus: initialFocus === "popup" ? popup : initialFocus === "first-control",
48
- className: cx(tip ? cx("max-w-sm text-left", tipClassName) : cx(panelClassName, "max-h-(--available-height) max-w-[calc(100vw-16px)] overflow-y-auto p-3"), className),
49
- children: [
50
- tip && /* @__PURE__ */ jsx(Popover.Arrow, { className: "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1" }),
51
- title && /* @__PURE__ */ jsx(Popover.Title, {
52
- className: "mb-2 text-sm font-semibold",
53
- children: title
54
- }),
55
- /* @__PURE__ */ jsx(CloseContext, {
56
- value: close,
57
- children
58
- })
59
- ]
38
+ }), /* @__PURE__ */ jsx(Popover.Portal, {
39
+ container: fullscreen,
40
+ children: /* @__PURE__ */ jsx(Popover.Positioner, {
41
+ side,
42
+ align,
43
+ sideOffset: tip ? 8 : 6,
44
+ collisionPadding: 8,
45
+ ...keepSide && { collisionAvoidance: { side: "none" } },
46
+ ...at ? { anchor: toPositionerAnchor(at) } : anchor ? { anchor } : {},
47
+ className: positionerClassName,
48
+ children: /* @__PURE__ */ jsxs(Popover.Popup, {
49
+ ...popupProps,
50
+ ref: popup,
51
+ initialFocus: initialFocus === "popup" ? popup : initialFocus === "first-control",
52
+ className: cx(tip ? cx("max-w-sm text-left", tipClassName) : cx(panelClassName, "max-h-(--available-height) max-w-[calc(100vw-16px)] overflow-y-auto p-3"), className),
53
+ children: [
54
+ tip && /* @__PURE__ */ jsx(Popover.Arrow, { className: "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1" }),
55
+ title && /* @__PURE__ */ jsx(Popover.Title, {
56
+ className: "mb-2 text-sm font-semibold",
57
+ children: title
58
+ }),
59
+ /* @__PURE__ */ jsx(CloseContext, {
60
+ value: close,
61
+ children
62
+ })
63
+ ]
64
+ })
60
65
  })
61
- }) })]
66
+ })]
62
67
  });
63
68
  }
64
69
  //#endregion
@@ -9,7 +9,7 @@ import { RadioGroup } from "@base-ui/react/radio-group";
9
9
  function Radio$1({ className, ...props }) {
10
10
  return /* @__PURE__ */ jsx(Radio.Root, {
11
11
  ...props,
12
- className: cx("flex size-4 shrink-0 items-center justify-center rounded-full border border-(--theme-border-strong) bg-(--theme-input-bg) cursor-pointer transition-colors motion-reduce:transition-none", focusOutline, "data-checked:border-(--theme-accent) data-checked:bg-(--theme-accent)", "aria-invalid:border-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", className),
12
+ className: cx("flex size-4 shrink-0 items-center justify-center rounded-full border border-(--theme-border-strong) bg-(--theme-input-bg) cursor-pointer transition-colors motion-reduce:transition-none", focusOutline, "data-checked:border-(--theme-focus) data-checked:bg-(--theme-accent)", "aria-invalid:border-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", className),
13
13
  children: /* @__PURE__ */ jsx(Radio.Indicator, { className: "size-1.5 rounded-full bg-(--theme-accent-text)" })
14
14
  });
15
15
  }
@@ -1,6 +1,7 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { CheckIcon, ChevronDownIcon } from "../icons.js";
3
3
  import { panelClassName, positionerClassName } from "./floating.js";
4
+ import { useFullscreenContainer } from "./fullscreen.js";
4
5
  import { Tooltip } from "./Tooltip.js";
5
6
  import { fieldBox } from "./fieldBox.js";
6
7
  import { optionNaming, pickerItemClassName } from "./comboboxItems.js";
@@ -13,6 +14,7 @@ var triggerClassName = "flex cursor-pointer items-center justify-between gap-2 p
13
14
  /** Picks one value, or several with `multiple`, from a short list. Use
14
15
  * Combobox when the list is long enough to need filtering. */
15
16
  function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "aria-labelledby": labelledBy, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required, trigger, triggerTooltip, size = "md", onItemHighlighted, ...rootProps }) {
17
+ const fullscreen = useFullscreenContainer();
16
18
  const uid = useId();
17
19
  const labelOf = (value) => items.find((item) => Object.is(item.value, value))?.label;
18
20
  const triggerProps = {
@@ -46,51 +48,54 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
46
48
  className: "shrink-0 text-(--theme-muted-text-color)",
47
49
  children: /* @__PURE__ */ jsx(ChevronDownIcon, { "aria-hidden": "true" })
48
50
  })]
49
- }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
50
- alignItemWithTrigger: false,
51
- sideOffset: 4,
52
- className: positionerClassName,
53
- children: /* @__PURE__ */ jsx(Select.Popup, {
54
- className: cx("min-w-(--anchor-width) max-w-[min(20rem,var(--available-width))]", panelClassName),
55
- children: /* @__PURE__ */ jsx(Select.List, {
56
- className: cx("max-h-[min(15rem,var(--available-height))] overflow-y-auto", "p-1"),
57
- children: items.map((item, index) => /* @__PURE__ */ jsxs(Select.Item, {
58
- value: item.value,
59
- disabled: item.disabled,
60
- ...optionNaming(`${uid}${index}`, item),
61
- onFocus: onItemHighlighted && (() => onItemHighlighted(item.value)),
62
- className: pickerItemClassName,
63
- children: [
64
- item.icon && /* @__PURE__ */ jsx("span", {
65
- className: "flex shrink-0 items-center",
66
- children: item.icon
67
- }),
68
- /* @__PURE__ */ jsxs("span", {
69
- className: "flex min-w-0 flex-col",
70
- children: [/* @__PURE__ */ jsxs("span", {
71
- className: "flex min-w-0 items-center gap-2",
72
- children: [/* @__PURE__ */ jsx(Select.ItemText, {
73
- id: `${uid}${index}label`,
74
- className: "truncate",
75
- children: item.label
76
- }), item.badge]
77
- }), /* @__PURE__ */ jsx(OptionDescription, {
78
- id: `${uid}${index}description`,
79
- item
80
- })]
81
- }),
82
- /* @__PURE__ */ jsx(Select.ItemIndicator, {
83
- className: "ml-auto flex shrink-0 items-center",
84
- children: /* @__PURE__ */ jsx(CheckIcon, {
85
- "aria-hidden": "true",
86
- className: "size-3.5"
51
+ }), /* @__PURE__ */ jsx(Select.Portal, {
52
+ container: fullscreen,
53
+ children: /* @__PURE__ */ jsx(Select.Positioner, {
54
+ alignItemWithTrigger: false,
55
+ sideOffset: 4,
56
+ className: positionerClassName,
57
+ children: /* @__PURE__ */ jsx(Select.Popup, {
58
+ className: cx("min-w-(--anchor-width) max-w-[min(20rem,var(--available-width))]", panelClassName),
59
+ children: /* @__PURE__ */ jsx(Select.List, {
60
+ className: cx("max-h-[min(15rem,var(--available-height))] overflow-y-auto", "p-1"),
61
+ children: items.map((item, index) => /* @__PURE__ */ jsxs(Select.Item, {
62
+ value: item.value,
63
+ disabled: item.disabled,
64
+ ...optionNaming(`${uid}${index}`, item),
65
+ onFocus: onItemHighlighted && (() => onItemHighlighted(item.value)),
66
+ className: pickerItemClassName,
67
+ children: [
68
+ item.icon && /* @__PURE__ */ jsx("span", {
69
+ className: "flex shrink-0 items-center",
70
+ children: item.icon
71
+ }),
72
+ /* @__PURE__ */ jsxs("span", {
73
+ className: "flex min-w-0 flex-col",
74
+ children: [/* @__PURE__ */ jsxs("span", {
75
+ className: "flex min-w-0 items-center gap-2",
76
+ children: [/* @__PURE__ */ jsx(Select.ItemText, {
77
+ id: `${uid}${index}label`,
78
+ className: "truncate",
79
+ children: item.label
80
+ }), item.badge]
81
+ }), /* @__PURE__ */ jsx(OptionDescription, {
82
+ id: `${uid}${index}description`,
83
+ item
84
+ })]
85
+ }),
86
+ /* @__PURE__ */ jsx(Select.ItemIndicator, {
87
+ className: "ml-auto flex shrink-0 items-center",
88
+ children: /* @__PURE__ */ jsx(CheckIcon, {
89
+ "aria-hidden": "true",
90
+ className: "size-3.5"
91
+ })
87
92
  })
88
- })
89
- ]
90
- }, String(item.value)))
93
+ ]
94
+ }, String(item.value)))
95
+ })
91
96
  })
92
97
  })
93
- }) })]
98
+ })]
94
99
  });
95
100
  }
96
101
  //#endregion
@@ -17,10 +17,10 @@ function Slider$1({ value, onValueChange, min, max, step = 1, disabled = false,
17
17
  children: /* @__PURE__ */ jsx(Slider.Control, {
18
18
  className: "flex w-full touch-none select-none items-center py-2 data-disabled:cursor-not-allowed data-disabled:opacity-50",
19
19
  children: /* @__PURE__ */ jsxs(Slider.Track, {
20
- className: "h-1 w-full rounded-full bg-(--theme-border)",
21
- children: [/* @__PURE__ */ jsx(Slider.Indicator, { className: "rounded-full bg-(--theme-accent)" }), /* @__PURE__ */ jsx(Slider.Thumb, {
20
+ className: "h-1 w-full rounded-full bg-(--theme-border-strong)",
21
+ children: [/* @__PURE__ */ jsx(Slider.Indicator, { className: "rounded-full bg-(--theme-focus)" }), /* @__PURE__ */ jsx(Slider.Thumb, {
22
22
  "aria-label": ariaLabel,
23
- className: "size-4 rounded-full border-2 border-(--theme-accent) bg-(--theme-panel-bg) shadow-sm has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-(--theme-focus)"
23
+ className: "size-4 rounded-full border-2 border-(--theme-focus) bg-(--theme-panel-bg) shadow-sm has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-(--theme-focus)"
24
24
  })]
25
25
  })
26
26
  })
@@ -21,7 +21,7 @@ var sizes = {
21
21
  function Switch$1({ size = "md", className, onLabel, offLabel, ...props }) {
22
22
  const control = /* @__PURE__ */ jsx(Switch.Root, {
23
23
  ...props,
24
- className: cx("peer inline-flex shrink-0 items-center rounded-full p-0.5 bg-(--theme-border-strong) transition-colors duration-200 cursor-pointer motion-reduce:transition-none", "data-checked:bg-(--theme-accent)", focusOutline, "aria-invalid:ring-2 aria-invalid:ring-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", sizes[size].root, className),
24
+ className: cx("peer inline-flex shrink-0 items-center rounded-full p-0.5 bg-(--theme-border-strong) transition-colors duration-200 cursor-pointer motion-reduce:transition-none", "data-checked:bg-(--theme-focus)", focusOutline, "aria-invalid:ring-2 aria-invalid:ring-(--theme-error)", "data-disabled:cursor-not-allowed data-disabled:opacity-50", sizes[size].root, className),
25
25
  children: /* @__PURE__ */ jsx(Switch.Thumb, { className: cx("rounded-full bg-white shadow-sm transition-transform duration-200 motion-reduce:transition-none", sizes[size].thumb) })
26
26
  });
27
27
  if (onLabel === void 0 && offLabel === void 0) return control;
@@ -1,4 +1,5 @@
1
1
  import { positionerClassName, tipArrowClassName, tipClassName } from "./floating.js";
2
+ import { useFullscreenContainer } from "./fullscreen.js";
2
3
  import { keyedByContent } from "./keys.js";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  import { Fragment as Fragment$1, createContext, use, useState } from "react";
@@ -19,18 +20,22 @@ function KbdShortcut({ keys }) {
19
20
  });
20
21
  }
21
22
  function TooltipPopup({ content, shortcut, side }) {
22
- return /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
23
- side,
24
- sideOffset: 8,
25
- className: positionerClassName,
26
- children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
27
- className: `max-w-xs ${tipClassName}`,
28
- children: [/* @__PURE__ */ jsx(Tooltip.Arrow, { className: tipArrowClassName }), shortcut ? /* @__PURE__ */ jsxs("span", {
29
- className: "inline-flex items-center gap-2",
30
- children: [content, /* @__PURE__ */ jsx(KbdShortcut, { keys: shortcut })]
31
- }) : content]
23
+ const fullscreen = useFullscreenContainer();
24
+ return /* @__PURE__ */ jsx(Tooltip.Portal, {
25
+ container: fullscreen,
26
+ children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
27
+ side,
28
+ sideOffset: 8,
29
+ className: positionerClassName,
30
+ children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
31
+ className: `max-w-xs ${tipClassName}`,
32
+ children: [/* @__PURE__ */ jsx(Tooltip.Arrow, { className: tipArrowClassName }), shortcut ? /* @__PURE__ */ jsxs("span", {
33
+ className: "inline-flex items-center gap-2",
34
+ children: [content, /* @__PURE__ */ jsx(KbdShortcut, { keys: shortcut })]
35
+ }) : content]
36
+ })
32
37
  })
33
- }) });
38
+ });
34
39
  }
35
40
  /** One popup serves every Tooltip below it, so each anchor costs only a trigger.
36
41
  * UIProvider mounts it; nothing else needs to. */
@@ -20,7 +20,7 @@ var sizes = {
20
20
  * thumb: an outline in the theme's focus color, which is solved for 3:1 on every surface a
21
21
  * control sits on, so it shows on a checked control as well as an empty one. */
22
22
  var focusOutline = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)";
23
- var base = "w-full min-w-0 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
23
+ var base = "w-full min-w-0 rounded-md border border-(--theme-border-strong) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
24
24
  var own = cx("focus-visible:border-(--theme-focus) focus-visible:ring-1 focus-visible:ring-(--theme-focus)", "aria-invalid:border-(--theme-error) aria-invalid:focus-visible:ring-(--theme-error)", "disabled:cursor-not-allowed disabled:bg-(--theme-input-disabled-bg) disabled:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
25
25
  var inside = cx("has-[:focus-visible]:border-(--theme-focus) has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-(--theme-focus)", "has-[[aria-invalid=true]]:border-(--theme-error) has-[[aria-invalid=true]:focus-visible]:ring-(--theme-error)", "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-(--theme-input-disabled-bg) has-[input:disabled]:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
26
26
  /** The box every input-like control draws: text inputs, textareas, and the triggers of
@@ -0,0 +1,4 @@
1
+ /** Where a popup portals while an element is fullscreen: that element. The browser
2
+ * shows only the fullscreen element's subtree, so a popup portaled to the body
3
+ * would open unseen behind it. Undefined otherwise, which keeps the body. */
4
+ export declare function useFullscreenContainer(): HTMLElement | undefined;
@@ -0,0 +1,18 @@
1
+ import { useSyncExternalStore } from "react";
2
+ //#region src/components/fullscreen.ts
3
+ function subscribe(onChange) {
4
+ document.addEventListener("fullscreenchange", onChange);
5
+ return () => document.removeEventListener("fullscreenchange", onChange);
6
+ }
7
+ function snapshot() {
8
+ const element = document.fullscreenElement;
9
+ return element instanceof HTMLElement ? element : void 0;
10
+ }
11
+ /** Where a popup portals while an element is fullscreen: that element. The browser
12
+ * shows only the fullscreen element's subtree, so a popup portaled to the body
13
+ * would open unseen behind it. Undefined otherwise, which keeps the body. */
14
+ function useFullscreenContainer() {
15
+ return useSyncExternalStore(subscribe, snapshot, () => void 0);
16
+ }
17
+ //#endregion
18
+ export { useFullscreenContainer };