@ui-sdk/react 0.0.0-stage → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +10 -2
- package/dist/accordion.d.ts +331 -0
- package/dist/accordion.js +25 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +8 -0
- package/dist/alert-dialog.d.ts +56 -0
- package/dist/alert-dialog.js +45 -0
- package/dist/async-options.d.ts +22 -0
- package/dist/async-options.js +56 -0
- package/dist/autocomplete.d.ts +102 -0
- package/dist/autocomplete.js +195 -0
- package/dist/avatar.d.ts +301 -0
- package/dist/avatar.js +17 -0
- package/dist/button.d.ts +21 -0
- package/dist/button.js +45 -0
- package/dist/calendar-context.d.ts +10 -0
- package/dist/calendar-context.js +9 -0
- package/dist/calendar.d.ts +80 -0
- package/dist/calendar.js +257 -0
- package/dist/carousel.d.ts +52 -0
- package/dist/carousel.js +151 -0
- package/dist/checkbox.d.ts +24 -0
- package/dist/checkbox.js +98 -0
- package/dist/children.d.ts +38 -0
- package/dist/children.js +63 -0
- package/dist/collapsible.d.ts +302 -0
- package/dist/collapsible.js +19 -0
- package/dist/color-picker.d.ts +72 -0
- package/dist/color-picker.js +133 -0
- package/dist/color.d.ts +2 -0
- package/dist/color.js +22 -0
- package/dist/combobox.d.ts +152 -0
- package/dist/combobox.js +338 -0
- package/dist/command.d.ts +88 -0
- package/dist/command.js +233 -0
- package/dist/controller-target.d.ts +3 -0
- package/dist/controller-target.js +59 -0
- package/dist/date-input.d.ts +19 -0
- package/dist/date-input.js +58 -0
- package/dist/date-picker.d.ts +47 -0
- package/dist/date-picker.js +310 -0
- package/dist/date.d.ts +2 -0
- package/dist/date.js +60 -0
- package/dist/dialog-parts.d.ts +23 -0
- package/dist/dialog-parts.js +142 -0
- package/dist/dialog.d.ts +57 -0
- package/dist/dialog.js +32 -0
- package/dist/drawer.d.ts +70 -0
- package/dist/drawer.js +158 -0
- package/dist/editor-context.d.ts +4 -0
- package/dist/editor-context.js +9 -0
- package/dist/field-context.d.ts +47 -0
- package/dist/field-context.js +87 -0
- package/dist/field.d.ts +49 -0
- package/dist/field.js +169 -0
- package/dist/file-input.d.ts +46 -0
- package/dist/file-input.js +170 -0
- package/dist/form-inputs.d.ts +5 -0
- package/dist/form-inputs.js +22 -0
- package/dist/gesture.d.ts +1 -0
- package/dist/gesture.js +16 -0
- package/dist/history.d.ts +2 -0
- package/dist/history.js +12 -0
- package/dist/hover-card.d.ts +33 -0
- package/dist/hover-card.js +84 -0
- package/dist/image-cropper.d.ts +333 -0
- package/dist/image-cropper.js +37 -0
- package/dist/image.d.ts +2 -0
- package/dist/image.js +6 -0
- package/dist/input.d.ts +22 -0
- package/dist/input.js +51 -0
- package/dist/label.d.ts +294 -0
- package/dist/label.js +9 -0
- package/dist/list-items.d.ts +15 -0
- package/dist/list-items.js +39 -0
- package/dist/list-text.d.ts +4 -0
- package/dist/list-text.js +15 -0
- package/dist/listbox.d.ts +56 -0
- package/dist/listbox.js +122 -0
- package/dist/menu.d.ts +116 -0
- package/dist/menu.js +426 -0
- package/dist/menubar-context.d.ts +4 -0
- package/dist/menubar-context.js +7 -0
- package/dist/menubar.d.ts +11 -0
- package/dist/menubar.js +32 -0
- package/dist/meter.d.ts +312 -0
- package/dist/meter.js +30 -0
- package/dist/navigation-menu.d.ts +61 -0
- package/dist/navigation-menu.js +169 -0
- package/dist/number-input.d.ts +44 -0
- package/dist/number-input.js +185 -0
- package/dist/number.d.ts +2 -0
- package/dist/number.js +40 -0
- package/dist/observe.d.ts +2 -0
- package/dist/observe.js +8 -0
- package/dist/otp-input.d.ts +29 -0
- package/dist/otp-input.js +94 -0
- package/dist/overlays.d.ts +26 -0
- package/dist/overlays.js +82 -0
- package/dist/pagination.d.ts +336 -0
- package/dist/pagination.js +71 -0
- package/dist/password-input.d.ts +23 -0
- package/dist/password-input.js +65 -0
- package/dist/popover.d.ts +54 -0
- package/dist/popover.js +151 -0
- package/dist/popup.d.ts +41 -0
- package/dist/popup.js +180 -0
- package/dist/preferences.d.ts +35 -0
- package/dist/preferences.js +93 -0
- package/dist/primitive.d.ts +2 -0
- package/dist/primitive.js +14 -0
- package/dist/progress.d.ts +312 -0
- package/dist/progress.js +30 -0
- package/dist/radio.d.ts +22 -0
- package/dist/radio.js +86 -0
- package/dist/rating.d.ts +20 -0
- package/dist/rating.js +94 -0
- package/dist/render-element.d.ts +35 -0
- package/dist/render-element.js +103 -0
- package/dist/resizable.d.ts +302 -0
- package/dist/resizable.js +23 -0
- package/dist/scroll-area.d.ts +324 -0
- package/dist/scroll-area.js +49 -0
- package/dist/segmented-control.d.ts +24 -0
- package/dist/segmented-control.js +78 -0
- package/dist/segmented-input.d.ts +15 -0
- package/dist/segmented-input.js +48 -0
- package/dist/select.d.ts +125 -0
- package/dist/select.js +330 -0
- package/dist/separator.d.ts +290 -0
- package/dist/separator.js +9 -0
- package/dist/shortcuts.d.ts +32 -0
- package/dist/shortcuts.js +61 -0
- package/dist/slider.d.ts +38 -0
- package/dist/slider.js +163 -0
- package/dist/switch.d.ts +15 -0
- package/dist/switch.js +39 -0
- package/dist/tabs.d.ts +331 -0
- package/dist/tabs.js +28 -0
- package/dist/text-control.d.ts +22 -0
- package/dist/text-control.js +24 -0
- package/dist/text-match.d.ts +2 -0
- package/dist/text-match.js +8 -0
- package/dist/textarea.d.ts +10 -0
- package/dist/textarea.js +48 -0
- package/dist/time-input.d.ts +19 -0
- package/dist/time-input.js +63 -0
- package/dist/timeline.d.ts +327 -0
- package/dist/timeline.js +31 -0
- package/dist/toast-parts.d.ts +63 -0
- package/dist/toast-parts.js +144 -0
- package/dist/toast.d.ts +2 -0
- package/dist/toast.js +22 -0
- package/dist/toggle.d.ts +23 -0
- package/dist/toggle.js +76 -0
- package/dist/token-input.d.ts +35 -0
- package/dist/token-input.js +100 -0
- package/dist/toolbar-context.d.ts +6 -0
- package/dist/toolbar-context.js +16 -0
- package/dist/toolbar.d.ts +38 -0
- package/dist/toolbar.js +63 -0
- package/dist/tooltip.d.ts +39 -0
- package/dist/tooltip.js +108 -0
- package/dist/tree.d.ts +51 -0
- package/dist/tree.js +150 -0
- package/dist/trigger-menu.d.ts +83 -0
- package/dist/trigger-menu.js +167 -0
- package/dist/use-primitive.d.ts +178 -0
- package/dist/use-primitive.js +674 -0
- package/dist/waterfall.d.ts +80 -0
- package/dist/waterfall.js +108 -0
- package/docs/README.md +89 -0
- package/docs/agents.md +1523 -0
- package/docs/components/accordion.md +1391 -0
- package/docs/components/alert-dialog.md +850 -0
- package/docs/components/autocomplete.md +1963 -0
- package/docs/components/avatar.md +252 -0
- package/docs/components/button.md +444 -0
- package/docs/components/calendar.md +1687 -0
- package/docs/components/carousel.md +1244 -0
- package/docs/components/checkbox.md +1069 -0
- package/docs/components/collapsible.md +567 -0
- package/docs/components/color-picker.md +736 -0
- package/docs/components/combobox.md +3647 -0
- package/docs/components/command.md +1573 -0
- package/docs/components/date-input.md +608 -0
- package/docs/components/date-picker.md +1266 -0
- package/docs/components/dialog.md +1469 -0
- package/docs/components/drawer.md +1197 -0
- package/docs/components/field.md +549 -0
- package/docs/components/file-input.md +609 -0
- package/docs/components/hover-card.md +514 -0
- package/docs/components/image-cropper.md +579 -0
- package/docs/components/input.md +301 -0
- package/docs/components/label.md +217 -0
- package/docs/components/listbox.md +1130 -0
- package/docs/components/menu.md +1286 -0
- package/docs/components/menubar.md +455 -0
- package/docs/components/meter.md +304 -0
- package/docs/components/navigation-menu.md +1581 -0
- package/docs/components/number-input.md +1086 -0
- package/docs/components/otp-input.md +565 -0
- package/docs/components/pagination.md +648 -0
- package/docs/components/password-input.md +737 -0
- package/docs/components/popover.md +1552 -0
- package/docs/components/preferences.md +220 -0
- package/docs/components/progress.md +443 -0
- package/docs/components/radio.md +846 -0
- package/docs/components/rating.md +458 -0
- package/docs/components/resizable.md +982 -0
- package/docs/components/scroll-area.md +980 -0
- package/docs/components/segmented-control.md +465 -0
- package/docs/components/select.md +2133 -0
- package/docs/components/separator.md +243 -0
- package/docs/components/slider.md +1169 -0
- package/docs/components/switch.md +613 -0
- package/docs/components/tabs.md +1014 -0
- package/docs/components/textarea.md +318 -0
- package/docs/components/time-input.md +231 -0
- package/docs/components/timeline.md +677 -0
- package/docs/components/toast.md +1395 -0
- package/docs/components/toggle.md +541 -0
- package/docs/components/token-input.md +1007 -0
- package/docs/components/toolbar.md +787 -0
- package/docs/components/tooltip.md +884 -0
- package/docs/components/tree.md +1346 -0
- package/docs/components/trigger-menu.md +1257 -0
- package/docs/components/waterfall.md +754 -0
- package/docs/controllers.md +89 -0
- package/docs/core.md +8027 -0
- package/docs/custom-parts.md +120 -0
- package/docs/first-component.md +83 -0
- package/docs/forms.md +148 -0
- package/docs/hooks/async-options.md +366 -0
- package/docs/hooks/shortcuts.md +221 -0
- package/docs/installation.md +45 -0
- package/docs/introduction.md +32 -0
- package/docs/lists-and-menus.md +170 -0
- package/docs/llms.txt +1844 -0
- package/docs/parts-and-composition.md +93 -0
- package/docs/popups.md +117 -0
- package/docs/rendering.md +121 -0
- package/docs/server-rendering.md +253 -0
- package/docs/state-and-events.md +105 -0
- package/docs/styling-and-animation.md +176 -0
- package/package.json +333 -3
package/dist/popover.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/popover.tsx
|
|
3
|
+
import {
|
|
4
|
+
popover
|
|
5
|
+
} from "@ui-sdk/core/popover";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import {
|
|
8
|
+
Connected,
|
|
9
|
+
renderPopup,
|
|
10
|
+
sheetViewport,
|
|
11
|
+
useOwnContainer,
|
|
12
|
+
usePopupContainer,
|
|
13
|
+
useControllerEntry
|
|
14
|
+
} from "./popup.js";
|
|
15
|
+
import {
|
|
16
|
+
createParts,
|
|
17
|
+
HandleScope,
|
|
18
|
+
renderElement,
|
|
19
|
+
useHandle,
|
|
20
|
+
useNearest,
|
|
21
|
+
usePart,
|
|
22
|
+
usePrimitive
|
|
23
|
+
} from "./use-primitive.js";
|
|
24
|
+
import { createPopoverController } from "@ui-sdk/core/popover";
|
|
25
|
+
import { jsx } from "react/jsx-runtime";
|
|
26
|
+
var parts = createParts(popover, {
|
|
27
|
+
trigger: "button",
|
|
28
|
+
title: "h2",
|
|
29
|
+
description: "p",
|
|
30
|
+
close: "button",
|
|
31
|
+
arrow: "div"
|
|
32
|
+
});
|
|
33
|
+
var ContainerContext = React.createContext(void 0);
|
|
34
|
+
var TriggerContext = React.createContext(null);
|
|
35
|
+
function Popover({ children, container, ...options }) {
|
|
36
|
+
const handle = usePrimitive(popover, options);
|
|
37
|
+
useControllerEntry(options.controller, handle);
|
|
38
|
+
return /* @__PURE__ */ jsx(HandleScope, { handle, children: /* @__PURE__ */ jsx(ContainerContext.Provider, { value: container, children }) });
|
|
39
|
+
}
|
|
40
|
+
function PopoverTrigger({
|
|
41
|
+
controller,
|
|
42
|
+
...props
|
|
43
|
+
}) {
|
|
44
|
+
const {
|
|
45
|
+
disabled,
|
|
46
|
+
focusableWhenDisabled,
|
|
47
|
+
payload: _,
|
|
48
|
+
shortcut,
|
|
49
|
+
render,
|
|
50
|
+
...rest
|
|
51
|
+
} = props;
|
|
52
|
+
const unconnected = () => renderElement(
|
|
53
|
+
"button",
|
|
54
|
+
{
|
|
55
|
+
type: "button",
|
|
56
|
+
disabled: disabled && !focusableWhenDisabled,
|
|
57
|
+
"aria-disabled": disabled && focusableWhenDisabled || void 0,
|
|
58
|
+
...rest
|
|
59
|
+
},
|
|
60
|
+
render,
|
|
61
|
+
{
|
|
62
|
+
open: false,
|
|
63
|
+
disabled: !!disabled
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
return /* @__PURE__ */ jsx(TriggerContext.Provider, { value: { shortcut }, children: /* @__PURE__ */ jsx(
|
|
67
|
+
Connected,
|
|
68
|
+
{
|
|
69
|
+
primitive: popover,
|
|
70
|
+
controller,
|
|
71
|
+
fallback: unconnected,
|
|
72
|
+
children: /* @__PURE__ */ jsx(parts.Trigger, { ...props })
|
|
73
|
+
}
|
|
74
|
+
) });
|
|
75
|
+
}
|
|
76
|
+
function PopoverContent({
|
|
77
|
+
side,
|
|
78
|
+
align,
|
|
79
|
+
sideOffset,
|
|
80
|
+
alignOffset,
|
|
81
|
+
collisionPadding,
|
|
82
|
+
arrowPadding,
|
|
83
|
+
anchor,
|
|
84
|
+
initialFocus,
|
|
85
|
+
finalFocus,
|
|
86
|
+
keepMounted,
|
|
87
|
+
container,
|
|
88
|
+
render,
|
|
89
|
+
...props
|
|
90
|
+
}) {
|
|
91
|
+
const target = usePopupContainer(useOwnContainer(container, ContainerContext));
|
|
92
|
+
const content = {
|
|
93
|
+
side,
|
|
94
|
+
align,
|
|
95
|
+
sideOffset,
|
|
96
|
+
alignOffset,
|
|
97
|
+
collisionPadding,
|
|
98
|
+
arrowPadding,
|
|
99
|
+
anchor,
|
|
100
|
+
initialFocus,
|
|
101
|
+
finalFocus,
|
|
102
|
+
keepMounted
|
|
103
|
+
};
|
|
104
|
+
const handle = useHandle(popover);
|
|
105
|
+
const partProps = {
|
|
106
|
+
...props,
|
|
107
|
+
render: sheetViewport(handle, render)
|
|
108
|
+
};
|
|
109
|
+
const element = usePart(handle, "content", content, partProps);
|
|
110
|
+
return element && renderPopup(element, target);
|
|
111
|
+
}
|
|
112
|
+
var PopoverTitle = parts.Title;
|
|
113
|
+
var PopoverDescription = parts.Description;
|
|
114
|
+
var PopoverClose = parts.Close;
|
|
115
|
+
var PopoverArrow = parts.Arrow;
|
|
116
|
+
function PopoverShortcut(props) {
|
|
117
|
+
const trigger = React.useContext(TriggerContext);
|
|
118
|
+
if (useNearest(popover)) {
|
|
119
|
+
return /* @__PURE__ */ jsx(ShortcutPart, { inherited: trigger?.shortcut, ...props });
|
|
120
|
+
}
|
|
121
|
+
if (trigger) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
throw new Error("Popover parts must be used inside <Popover>.");
|
|
125
|
+
}
|
|
126
|
+
var text = (state) => state.text;
|
|
127
|
+
function ShortcutPart({
|
|
128
|
+
inherited,
|
|
129
|
+
shortcut,
|
|
130
|
+
children = text,
|
|
131
|
+
...props
|
|
132
|
+
}) {
|
|
133
|
+
return usePart(
|
|
134
|
+
popover,
|
|
135
|
+
"shortcut",
|
|
136
|
+
{ shortcut: shortcut ?? inherited },
|
|
137
|
+
{ ...props, children },
|
|
138
|
+
"span"
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
Popover,
|
|
143
|
+
PopoverArrow,
|
|
144
|
+
PopoverClose,
|
|
145
|
+
PopoverContent,
|
|
146
|
+
PopoverDescription,
|
|
147
|
+
PopoverShortcut,
|
|
148
|
+
PopoverTitle,
|
|
149
|
+
PopoverTrigger,
|
|
150
|
+
createPopoverController
|
|
151
|
+
};
|
package/dist/popup.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Primitive } from "@ui-sdk/core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { type PrimitiveHandle, type RenderProp } from "./use-primitive.js";
|
|
4
|
+
type PopupContainer = Element | DocumentFragment | null | undefined;
|
|
5
|
+
declare function usePopupContainer(own: PopupContainer): PopupContainer;
|
|
6
|
+
declare function useOwnContainer(own: PopupContainer, root: React.Context<PopupContainer>): PopupContainer;
|
|
7
|
+
declare function renderPopup(element: React.ReactElement, container: PopupContainer): React.JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* A popup content's `render`: the content, inside the full-screen viewport a
|
|
10
|
+
* sheet sits in while its `viewport` slot is present: always for a drawer,
|
|
11
|
+
* and for a popup while it presents as a sheet. The viewport is what goes
|
|
12
|
+
* into the top layer. Its attributes are a subset of the content's, so the
|
|
13
|
+
* content's render keeps it current.
|
|
14
|
+
*/
|
|
15
|
+
declare function sheetViewport<State>(popup: PrimitiveHandle<Primitive>, render?: RenderProp<State>): (props: Record<string, any>, state: State) => React.JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* Where an input renders while its popup presents as a sheet: in a header the
|
|
18
|
+
* content renders at its top. The root keeps the header element, the content
|
|
19
|
+
* renders it with `sheetHeader`, and the input renders into it with
|
|
20
|
+
* `inSheetHeader`, so there's one input at a time.
|
|
21
|
+
*/
|
|
22
|
+
declare const SheetHeaderContext: React.Context<[HTMLElement | null, (header: HTMLElement | null) => void]>;
|
|
23
|
+
/** The header at the top of a sheet's content, while the popup's `header` slot is present. */
|
|
24
|
+
declare function sheetHeader(popup: PrimitiveHandle<Primitive>, setHeader: (header: HTMLElement | null) => void): React.JSX.Element | null;
|
|
25
|
+
/** Renders an input into the sheet's header while there is one. */
|
|
26
|
+
declare function inSheetHeader(element: React.ReactElement, header: HTMLElement | null): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
27
|
+
/**
|
|
28
|
+
* A part that can sit outside its component, like a trigger in a table row:
|
|
29
|
+
* its children render with the instance `controller` connects them to, once
|
|
30
|
+
* that one mounts, or else with the nearest instance of `primitive`, and
|
|
31
|
+
* `fallback` renders until there is one. `fallback` is a function, so a
|
|
32
|
+
* connected part never builds it.
|
|
33
|
+
*/
|
|
34
|
+
declare function Connected({ primitive, controller, fallback, children, }: {
|
|
35
|
+
primitive: Primitive;
|
|
36
|
+
controller: object | undefined;
|
|
37
|
+
fallback: () => React.ReactNode;
|
|
38
|
+
children: React.ReactNode;
|
|
39
|
+
}): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
|
|
40
|
+
export { useControllerEntry, useControllerTarget } from "./controller-target.js";
|
|
41
|
+
export { Connected, inSheetHeader, type PopupContainer, renderPopup, sheetHeader, SheetHeaderContext, sheetViewport, useOwnContainer, usePopupContainer, };
|
package/dist/popup.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/popup.tsx
|
|
3
|
+
import { portalTarget } from "@ui-sdk/core/renderer";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
6
|
+
import { useControllerTarget } from "./controller-target.js";
|
|
7
|
+
import { mergeRefs } from "./render-element.js";
|
|
8
|
+
import {
|
|
9
|
+
HandleScope,
|
|
10
|
+
presentIn,
|
|
11
|
+
renderElement,
|
|
12
|
+
slotIn,
|
|
13
|
+
useNearest
|
|
14
|
+
} from "./use-primitive.js";
|
|
15
|
+
import { useControllerEntry, useControllerTarget as useControllerTarget2 } from "./controller-target.js";
|
|
16
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
var InheritedContainer = React.createContext(void 0);
|
|
18
|
+
function usePopupContainer(own) {
|
|
19
|
+
const inherited = React.useContext(InheritedContainer);
|
|
20
|
+
return own ?? inherited;
|
|
21
|
+
}
|
|
22
|
+
function useOwnContainer(own, root) {
|
|
23
|
+
const fromRoot = React.useContext(root);
|
|
24
|
+
return own ?? fromRoot;
|
|
25
|
+
}
|
|
26
|
+
function renderPopup(element, container) {
|
|
27
|
+
return /* @__PURE__ */ jsx(Popup, { element, container });
|
|
28
|
+
}
|
|
29
|
+
function Popup({
|
|
30
|
+
element,
|
|
31
|
+
container
|
|
32
|
+
}) {
|
|
33
|
+
const target = portalTarget(container);
|
|
34
|
+
const [tree] = React.useState(followTree);
|
|
35
|
+
React.useInsertionEffect(() => {
|
|
36
|
+
if (target) {
|
|
37
|
+
tree.placed(target);
|
|
38
|
+
}
|
|
39
|
+
}, [tree, target]);
|
|
40
|
+
const part = element;
|
|
41
|
+
const tracked = React.cloneElement(part, {
|
|
42
|
+
ref: mergeRefs(part.props.ref ?? null, tree.track)
|
|
43
|
+
});
|
|
44
|
+
const content = container ? /* @__PURE__ */ jsx(InheritedContainer.Provider, { value: container, children: tracked }) : tracked;
|
|
45
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46
|
+
/* @__PURE__ */ jsx(TreeEffect, { tree, target }),
|
|
47
|
+
target ? createPortal(content, target) : content
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
function followTree() {
|
|
51
|
+
let els = [];
|
|
52
|
+
let isShown = false;
|
|
53
|
+
let show;
|
|
54
|
+
let lastBefore = null;
|
|
55
|
+
const hideLater = () => queueMicrotask(() => {
|
|
56
|
+
if (isShown || show) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const shows = els.filter((el) => el.isConnected).map(hide);
|
|
60
|
+
if (shows.length) {
|
|
61
|
+
show = () => shows.forEach((run) => run());
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const track = (next) => {
|
|
65
|
+
if (next instanceof HTMLElement && !els.includes(next)) {
|
|
66
|
+
els = [...els.filter((el) => el.isConnected), next];
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
// The ref for the popup's element. React lets go of it while the tree is hidden, so it's kept.
|
|
71
|
+
track,
|
|
72
|
+
// Notes the container's last node before the portal appends the popup to it.
|
|
73
|
+
placing(target) {
|
|
74
|
+
lastBefore = target.lastChild;
|
|
75
|
+
},
|
|
76
|
+
// The nodes the portal appended after that one, if the popup rendered
|
|
77
|
+
// any. A render that waits a commit to render an element adds none, and
|
|
78
|
+
// the last node then belongs to someone else. They hide unless the tree shows.
|
|
79
|
+
placed(target) {
|
|
80
|
+
if (target.lastChild === lastBefore) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const first = !lastBefore ? target.firstChild : lastBefore.parentNode === target ? lastBefore.nextSibling : target.lastChild;
|
|
84
|
+
for (let node = first; node; node = node.nextSibling) {
|
|
85
|
+
track(node);
|
|
86
|
+
}
|
|
87
|
+
if (!isShown) {
|
|
88
|
+
hideLater();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
shown() {
|
|
92
|
+
isShown = true;
|
|
93
|
+
show?.();
|
|
94
|
+
show = void 0;
|
|
95
|
+
},
|
|
96
|
+
hidden() {
|
|
97
|
+
isShown = false;
|
|
98
|
+
hideLater();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function TreeEffect({
|
|
103
|
+
tree,
|
|
104
|
+
target
|
|
105
|
+
}) {
|
|
106
|
+
React.useInsertionEffect(() => {
|
|
107
|
+
if (target) {
|
|
108
|
+
tree.placing(target);
|
|
109
|
+
}
|
|
110
|
+
}, [tree, target]);
|
|
111
|
+
React.useLayoutEffect(() => {
|
|
112
|
+
tree.shown();
|
|
113
|
+
return tree.hidden;
|
|
114
|
+
}, [tree]);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function hide(el) {
|
|
118
|
+
const doc = el.ownerDocument;
|
|
119
|
+
if (el.contains(doc.activeElement)) {
|
|
120
|
+
;
|
|
121
|
+
doc.activeElement.blur();
|
|
122
|
+
}
|
|
123
|
+
for (const popover of [
|
|
124
|
+
el,
|
|
125
|
+
...el.querySelectorAll("[popover]")
|
|
126
|
+
]) {
|
|
127
|
+
try {
|
|
128
|
+
popover.hidePopover();
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const { style } = el;
|
|
133
|
+
if (style.display === "none") {
|
|
134
|
+
return () => {
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const value = style.getPropertyValue("display");
|
|
138
|
+
const priority = style.getPropertyPriority("display");
|
|
139
|
+
style.setProperty("display", "none", "important");
|
|
140
|
+
return () => style.setProperty("display", value, priority);
|
|
141
|
+
}
|
|
142
|
+
function sheetViewport(popup, render) {
|
|
143
|
+
return (props, state) => {
|
|
144
|
+
const element = renderElement("div", props, render, state);
|
|
145
|
+
return presentIn(popup, "viewport") ? /* @__PURE__ */ jsx("div", { ...slotIn(popup, "viewport", {}), children: element }) : element;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
var SheetHeaderContext = /* @__PURE__ */ React.createContext([null, () => {
|
|
149
|
+
}]);
|
|
150
|
+
function sheetHeader(popup, setHeader) {
|
|
151
|
+
return presentIn(popup, "header") ? /* @__PURE__ */ jsx("div", { ...slotIn(popup, "header", {}, { ref: setHeader }) }) : null;
|
|
152
|
+
}
|
|
153
|
+
function inSheetHeader(element, header) {
|
|
154
|
+
return header ? createPortal(element, header) : element;
|
|
155
|
+
}
|
|
156
|
+
function Connected({
|
|
157
|
+
primitive,
|
|
158
|
+
controller,
|
|
159
|
+
fallback,
|
|
160
|
+
children
|
|
161
|
+
}) {
|
|
162
|
+
const nearest = useNearest(primitive);
|
|
163
|
+
const handle = useControllerTarget(controller, nearest);
|
|
164
|
+
if (!handle) {
|
|
165
|
+
return fallback();
|
|
166
|
+
}
|
|
167
|
+
return controller ? /* @__PURE__ */ jsx(HandleScope, { handle, children }) : children;
|
|
168
|
+
}
|
|
169
|
+
export {
|
|
170
|
+
Connected,
|
|
171
|
+
SheetHeaderContext,
|
|
172
|
+
inSheetHeader,
|
|
173
|
+
renderPopup,
|
|
174
|
+
sheetHeader,
|
|
175
|
+
sheetViewport,
|
|
176
|
+
useControllerEntry,
|
|
177
|
+
useControllerTarget2 as useControllerTarget,
|
|
178
|
+
useOwnContainer,
|
|
179
|
+
usePopupContainer
|
|
180
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type PreferenceLike } from "@ui-sdk/core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Props of `Preferences`.
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
7
|
+
type PreferencesProps = Omit<React.ComponentProps<"div">, "children"> & {
|
|
8
|
+
/** The preferences to show, from `createPreference` or `createColorScheme`. */
|
|
9
|
+
preferences: readonly PreferenceLike[];
|
|
10
|
+
/** The nonce your Content Security Policy gives inline scripts. */
|
|
11
|
+
nonce?: string;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Shows preferences on `data-*` attributes, so CSS styles by them from the
|
|
16
|
+
* first paint: global ones on `<html>`, the rest on its own element, which
|
|
17
|
+
* has `display: contents`. On the server it renders one inline script as its
|
|
18
|
+
* first child, which puts every saved value on its attribute as the page
|
|
19
|
+
* loads, before hydration. Content that reads a preference, through
|
|
20
|
+
* `usePreference`, catches up after hydration.
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
declare function Preferences({ preferences, nonce, children, style, ref, ...props }: PreferencesProps): React.JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* A preference's state, like `{ value: "dark" }`, or a color scheme's. It's
|
|
26
|
+
* the server's while hydrating, so the page hydrates without a mismatch, and
|
|
27
|
+
* the saved value right after.
|
|
28
|
+
* @experimental
|
|
29
|
+
*/
|
|
30
|
+
declare function usePreference<State>(preference: {
|
|
31
|
+
get: () => State;
|
|
32
|
+
initialState: State;
|
|
33
|
+
subscribe: (listener: () => void) => () => void;
|
|
34
|
+
}): State;
|
|
35
|
+
export { Preferences, type PreferencesProps, usePreference };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/preferences.tsx
|
|
3
|
+
import { preferencesScript } from "@ui-sdk/core";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { mergeRefs } from "./render-element.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
var noSubscribe = () => () => {
|
|
8
|
+
};
|
|
9
|
+
function useStable(preferences) {
|
|
10
|
+
const ref = React.useRef(preferences);
|
|
11
|
+
const same = ref.current.length === preferences.length && ref.current.every((preference, index) => preference === preferences[index]);
|
|
12
|
+
if (!same) {
|
|
13
|
+
ref.current = preferences;
|
|
14
|
+
}
|
|
15
|
+
return ref.current;
|
|
16
|
+
}
|
|
17
|
+
function Preferences({
|
|
18
|
+
preferences,
|
|
19
|
+
nonce,
|
|
20
|
+
children,
|
|
21
|
+
style,
|
|
22
|
+
ref,
|
|
23
|
+
...props
|
|
24
|
+
}) {
|
|
25
|
+
const list = useStable(preferences);
|
|
26
|
+
const element = React.useRef(null);
|
|
27
|
+
const fromServer = React.useSyncExternalStore(
|
|
28
|
+
noSubscribe,
|
|
29
|
+
() => false,
|
|
30
|
+
() => true
|
|
31
|
+
);
|
|
32
|
+
const subscribe = React.useCallback(
|
|
33
|
+
(listener) => {
|
|
34
|
+
const stops = list.map((preference) => preference.subscribe(listener));
|
|
35
|
+
return () => stops.forEach((stop) => stop());
|
|
36
|
+
},
|
|
37
|
+
[list]
|
|
38
|
+
);
|
|
39
|
+
const values = React.useSyncExternalStore(
|
|
40
|
+
subscribe,
|
|
41
|
+
() => JSON.stringify(list.map((preference) => preference.get().value)),
|
|
42
|
+
() => JSON.stringify(list.map((preference) => preference.initialState.value))
|
|
43
|
+
);
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
for (const preference of list) {
|
|
46
|
+
;
|
|
47
|
+
(preference.global ? document.documentElement : element.current)?.setAttribute(preference.attribute, preference.get().value);
|
|
48
|
+
}
|
|
49
|
+
}, [list, values]);
|
|
50
|
+
const shown = JSON.parse(values);
|
|
51
|
+
const attributes = {};
|
|
52
|
+
list.forEach((preference, index) => {
|
|
53
|
+
if (!preference.global) {
|
|
54
|
+
attributes[preference.attribute] = shown[index];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return (
|
|
58
|
+
// The script changes these attributes before hydration, so they may differ from the server's HTML.
|
|
59
|
+
/* @__PURE__ */ jsxs(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
"data-slot": "preferences",
|
|
63
|
+
...attributes,
|
|
64
|
+
...props,
|
|
65
|
+
ref: mergeRefs(element, ref ?? null),
|
|
66
|
+
style: { display: "contents", ...style },
|
|
67
|
+
suppressHydrationWarning: true,
|
|
68
|
+
children: [
|
|
69
|
+
fromServer ? /* @__PURE__ */ jsx(
|
|
70
|
+
"script",
|
|
71
|
+
{
|
|
72
|
+
nonce,
|
|
73
|
+
suppressHydrationWarning: true,
|
|
74
|
+
dangerouslySetInnerHTML: { __html: preferencesScript(list) }
|
|
75
|
+
}
|
|
76
|
+
) : null,
|
|
77
|
+
children
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
function usePreference(preference) {
|
|
84
|
+
return React.useSyncExternalStore(
|
|
85
|
+
preference.subscribe,
|
|
86
|
+
preference.get,
|
|
87
|
+
() => preference.initialState
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
Preferences,
|
|
92
|
+
usePreference
|
|
93
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/primitive.ts
|
|
2
|
+
import { mergeProps, renderElement } from "./render-element.js";
|
|
3
|
+
import {
|
|
4
|
+
createParts,
|
|
5
|
+
usePart,
|
|
6
|
+
usePrimitive
|
|
7
|
+
} from "./use-primitive.js";
|
|
8
|
+
export {
|
|
9
|
+
createParts,
|
|
10
|
+
mergeProps,
|
|
11
|
+
renderElement,
|
|
12
|
+
usePart,
|
|
13
|
+
usePrimitive
|
|
14
|
+
};
|