@botiverse/raft-ui 0.95.3 → 0.96.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.
- package/CHANGELOG.md +135 -98
- package/dist/components/app-link-card/app-link-card.d.mts +17 -1
- package/dist/components/app-link-card/app-link-card.mjs +70 -2
- package/dist/components/app-link-card/index.d.mts +2 -2
- package/dist/components/combobox/combobox.mjs +1 -1
- package/dist/components/combobox-select/combobox-select.mjs +1 -1
- package/dist/components/context-menu/context-menu.mjs +1 -1
- package/dist/components/dialog/dialog-sheet-context.mjs +4 -2
- package/dist/components/dialog/dialog-sheet.mjs +9 -1
- package/dist/components/dialog/dialog.mjs +22 -10
- package/dist/components/drawer/drawer.d.mts +1 -1
- package/dist/components/drawer/drawer.mjs +17 -4
- package/dist/components/dropdown-menu/dropdown-menu.mjs +1 -1
- package/dist/components/lightbox/lightbox.d.mts +1 -1
- package/dist/components/lightbox/lightbox.mjs +11 -9
- package/dist/components/notification-center/notification-center.mjs +1 -1
- package/dist/components/popover/popover.mjs +1 -1
- package/dist/components/preview-card/preview-card.mjs +1 -1
- package/dist/components/select/select.mjs +1 -1
- package/dist/components/tooltip/tooltip.mjs +1 -1
- package/dist/lib/fill-contrast.mjs +63 -0
- package/dist/lib/frame-insets.mjs +56 -12
- package/dist/lib/overlay-depth.d.mts +5 -0
- package/dist/lib/overlay-depth.mjs +30 -11
- package/dist/lib/popup-boundary.d.mts +24 -0
- package/dist/lib/popup-boundary.mjs +256 -0
- package/dist/lint/no-raw-z-index.mjs +4 -4
- package/dist/lint/prefer-scroll-area.d.mts +60 -19
- package/dist/lint/prefer-scroll-area.mjs +393 -38
- package/dist/wip-exports.d.mts +6 -5
- package/dist/wip-exports.mjs +5 -4
- package/dist/wip.d.mts +3 -2
- package/package.json +1 -1
- package/skills/raft-ui-guide/SKILL.md +3 -1
- package/skills/raft-ui-guide/rules/layout.md +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BadgeProps } from "../badge/badge.mjs";
|
|
1
2
|
import { ComponentPropsWithRef } from "react";
|
|
2
3
|
export type AppLinkCardProps = ComponentPropsWithRef<"div">;
|
|
3
4
|
/**
|
|
@@ -44,8 +45,23 @@ export type AppLinkCardTitleProps = ComponentPropsWithRef<"p">;
|
|
|
44
45
|
*/
|
|
45
46
|
export declare function AppLinkCardTitle({ className, ...props }: AppLinkCardTitleProps): import("react").JSX.Element;
|
|
46
47
|
export type AppLinkCardMetaProps = ComponentPropsWithRef<"div">;
|
|
47
|
-
/** One state in
|
|
48
|
+
/** One state in an `AppLinkCardBadge`, then muted details. Wraps when it runs out of room. */
|
|
48
49
|
export declare function AppLinkCardMeta({ className, ...props }: AppLinkCardMetaProps): import("react").JSX.Element;
|
|
50
|
+
export type AppLinkCardBadgeProps = Omit<BadgeProps, "appearance"> & {
|
|
51
|
+
/**
|
|
52
|
+
* The app's colour for the badge, `#RRGGBB`. Only the fill changes: the
|
|
53
|
+
* type is black or white, whichever reads better on it, the same in light
|
|
54
|
+
* and dark mode, with a hairline ring where the fill is too close to the
|
|
55
|
+
* card's surface. Without it, or with any other value, and in forced
|
|
56
|
+
* colours, the badge is the soft Badge of its `variant`.
|
|
57
|
+
*/
|
|
58
|
+
fill?: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The item's state in `AppLinkCardMeta`: a soft Badge in the app's words.
|
|
62
|
+
* `variant` is its tone; `fill` takes the app's colour for it.
|
|
63
|
+
*/
|
|
64
|
+
export declare function AppLinkCardBadge({ fill, className, style, ...props }: AppLinkCardBadgeProps): import("react").JSX.Element;
|
|
49
65
|
export type AppLinkCardActionsProps = ComponentPropsWithRef<"div">;
|
|
50
66
|
/**
|
|
51
67
|
* The app's buttons at the card's bottom end: at most two, the bordered one
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
import { fillOnSurface, oklchToRgb, parseHexColor } from "../../lib/fill-contrast.mjs";
|
|
1
2
|
import { tv } from "../../lib/tv.mjs";
|
|
3
|
+
import { Badge } from "../badge/badge.mjs";
|
|
2
4
|
import { messageCard, messageEmbed } from "../message-embed/message-embed.recipe.mjs";
|
|
3
5
|
import { Skeleton } from "../skeleton/skeleton.mjs";
|
|
6
|
+
import { useMemo } from "react";
|
|
4
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
8
|
// src/components/app-link-card/app-link-card.tsx
|
|
6
9
|
const appLinkCard = tv({ slots: {
|
|
7
10
|
root: ["flex flex-col", "my-2 first:mt-0 last:mb-0"],
|
|
11
|
+
badgeFill: [
|
|
12
|
+
"not-forced-colors:bg-(--badge-fill) not-forced-colors:text-(--badge-fill-text)",
|
|
13
|
+
"not-forced-colors:inset-ring",
|
|
14
|
+
"[--badge-ring:var(--badge-ring-light)] dark:[--badge-ring:var(--badge-ring-dark)]",
|
|
15
|
+
"not-forced-colors:inset-ring-(--badge-ring)"
|
|
16
|
+
],
|
|
8
17
|
placeholderLine: ["flex h-lh items-center"],
|
|
9
18
|
placeholderButton: ["h-8 md:pointer-fine:h-control w-28 rounded-control"]
|
|
10
19
|
} });
|
|
@@ -164,7 +173,7 @@ function AppLinkCardTitle({ className, ...props }) {
|
|
|
164
173
|
...props
|
|
165
174
|
});
|
|
166
175
|
}
|
|
167
|
-
/** One state in
|
|
176
|
+
/** One state in an `AppLinkCardBadge`, then muted details. Wraps when it runs out of room. */
|
|
168
177
|
function AppLinkCardMeta({ className, ...props }) {
|
|
169
178
|
const { meta } = messageCard();
|
|
170
179
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -173,6 +182,65 @@ function AppLinkCardMeta({ className, ...props }) {
|
|
|
173
182
|
...props
|
|
174
183
|
});
|
|
175
184
|
}
|
|
185
|
+
const APP_LINK_CARD_SURFACE = {
|
|
186
|
+
light: [
|
|
187
|
+
1,
|
|
188
|
+
0,
|
|
189
|
+
0
|
|
190
|
+
],
|
|
191
|
+
dark: [
|
|
192
|
+
.2596,
|
|
193
|
+
.0037,
|
|
194
|
+
106.65
|
|
195
|
+
]
|
|
196
|
+
};
|
|
197
|
+
const CARD_SURFACE = {
|
|
198
|
+
light: oklchToRgb(...APP_LINK_CARD_SURFACE.light),
|
|
199
|
+
dark: oklchToRgb(...APP_LINK_CARD_SURFACE.dark)
|
|
200
|
+
};
|
|
201
|
+
const FILL_TEXT = {
|
|
202
|
+
black: "oklch(0 0 0)",
|
|
203
|
+
white: "oklch(1 0 0)"
|
|
204
|
+
};
|
|
205
|
+
/** The badge's custom properties for an app's fill, or null for no valid `#RRGGBB`. */
|
|
206
|
+
function badgeFillStyle(fill) {
|
|
207
|
+
const rgb = fill === void 0 ? null : parseHexColor(fill);
|
|
208
|
+
if (!rgb) return null;
|
|
209
|
+
const light = fillOnSurface(rgb, CARD_SURFACE.light);
|
|
210
|
+
const dark = fillOnSurface(rgb, CARD_SURFACE.dark);
|
|
211
|
+
return {
|
|
212
|
+
"--badge-fill": fill,
|
|
213
|
+
"--badge-fill-text": FILL_TEXT[light.text],
|
|
214
|
+
"--badge-ring-light": light.ring ? "var(--line-hairline)" : "transparent",
|
|
215
|
+
"--badge-ring-dark": dark.ring ? "var(--line-hairline)" : "transparent"
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The item's state in `AppLinkCardMeta`: a soft Badge in the app's words.
|
|
220
|
+
* `variant` is its tone; `fill` takes the app's colour for it.
|
|
221
|
+
*/
|
|
222
|
+
function AppLinkCardBadge({ fill, className, style, ...props }) {
|
|
223
|
+
const fillStyle = useMemo(() => {
|
|
224
|
+
const vars = badgeFillStyle(fill);
|
|
225
|
+
return vars && {
|
|
226
|
+
...vars,
|
|
227
|
+
...style
|
|
228
|
+
};
|
|
229
|
+
}, [fill, style]);
|
|
230
|
+
if (!fillStyle) return /* @__PURE__ */ jsx(Badge, {
|
|
231
|
+
appearance: "soft",
|
|
232
|
+
className,
|
|
233
|
+
style,
|
|
234
|
+
...props
|
|
235
|
+
});
|
|
236
|
+
const { badgeFill } = appLinkCard();
|
|
237
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
238
|
+
appearance: "soft",
|
|
239
|
+
className: badgeFill({ className }),
|
|
240
|
+
style: fillStyle,
|
|
241
|
+
...props
|
|
242
|
+
});
|
|
243
|
+
}
|
|
176
244
|
/**
|
|
177
245
|
* The app's buttons at the card's bottom end: at most two, the bordered one
|
|
178
246
|
* last, with a More menu (⋯) to their left for the rest.
|
|
@@ -185,4 +253,4 @@ function AppLinkCardActions({ className, ...props }) {
|
|
|
185
253
|
...props
|
|
186
254
|
});
|
|
187
255
|
}
|
|
188
|
-
export { AppLinkCard, AppLinkCardActions, AppLinkCardBody, AppLinkCardEyebrow, AppLinkCardHeader, AppLinkCardHost, AppLinkCardIcon, AppLinkCardMeta, AppLinkCardName, AppLinkCardPlaceholder, AppLinkCardTitle };
|
|
256
|
+
export { APP_LINK_CARD_SURFACE, AppLinkCard, AppLinkCardActions, AppLinkCardBadge, AppLinkCardBody, AppLinkCardEyebrow, AppLinkCardHeader, AppLinkCardHost, AppLinkCardIcon, AppLinkCardMeta, AppLinkCardName, AppLinkCardPlaceholder, AppLinkCardTitle };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AppLinkCard, AppLinkCardActions, AppLinkCardActionsProps, AppLinkCardBody, AppLinkCardBodyProps, AppLinkCardEyebrow, AppLinkCardEyebrowProps, AppLinkCardHeader, AppLinkCardHeaderProps, AppLinkCardHost, AppLinkCardHostProps, AppLinkCardIcon, AppLinkCardIconProps, AppLinkCardMeta, AppLinkCardMetaProps, AppLinkCardName, AppLinkCardNameProps, AppLinkCardPlaceholder, AppLinkCardPlaceholderProps, AppLinkCardTitle, AppLinkCardTitleProps } from "./app-link-card.mjs";
|
|
2
|
-
export { AppLinkCard, AppLinkCardActions, type AppLinkCardActionsProps, AppLinkCardBody, type AppLinkCardBodyProps, AppLinkCardEyebrow, type AppLinkCardEyebrowProps, AppLinkCardHeader, type AppLinkCardHeaderProps, AppLinkCardHost, type AppLinkCardHostProps, AppLinkCardIcon, type AppLinkCardIconProps, AppLinkCardMeta, type AppLinkCardMetaProps, AppLinkCardName, type AppLinkCardNameProps, AppLinkCardPlaceholder, type AppLinkCardPlaceholderProps, AppLinkCardTitle, type AppLinkCardTitleProps };
|
|
1
|
+
import { AppLinkCard, AppLinkCardActions, AppLinkCardActionsProps, AppLinkCardBadge, AppLinkCardBadgeProps, AppLinkCardBody, AppLinkCardBodyProps, AppLinkCardEyebrow, AppLinkCardEyebrowProps, AppLinkCardHeader, AppLinkCardHeaderProps, AppLinkCardHost, AppLinkCardHostProps, AppLinkCardIcon, AppLinkCardIconProps, AppLinkCardMeta, AppLinkCardMetaProps, AppLinkCardName, AppLinkCardNameProps, AppLinkCardPlaceholder, AppLinkCardPlaceholderProps, AppLinkCardTitle, AppLinkCardTitleProps } from "./app-link-card.mjs";
|
|
2
|
+
export { AppLinkCard, AppLinkCardActions, type AppLinkCardActionsProps, AppLinkCardBadge, type AppLinkCardBadgeProps, AppLinkCardBody, type AppLinkCardBodyProps, AppLinkCardEyebrow, type AppLinkCardEyebrowProps, AppLinkCardHeader, type AppLinkCardHeaderProps, AppLinkCardHost, type AppLinkCardHostProps, AppLinkCardIcon, type AppLinkCardIconProps, AppLinkCardMeta, type AppLinkCardMetaProps, AppLinkCardName, type AppLinkCardNameProps, AppLinkCardPlaceholder, type AppLinkCardPlaceholderProps, AppLinkCardTitle, type AppLinkCardTitleProps };
|
|
@@ -289,7 +289,7 @@ function ComboboxSeparator({ className, ...props }) {
|
|
|
289
289
|
});
|
|
290
290
|
}
|
|
291
291
|
function ComboboxFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
292
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container);
|
|
292
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container, void 0, props.anchor);
|
|
293
293
|
return /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
294
294
|
...collision,
|
|
295
295
|
...props
|
|
@@ -68,7 +68,7 @@ const PHONE_LIST_COLLISION = {
|
|
|
68
68
|
fallbackAxisSide: "none"
|
|
69
69
|
};
|
|
70
70
|
function ComboboxSelectFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
71
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container);
|
|
71
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container, void 0, props.anchor);
|
|
72
72
|
return /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
73
73
|
...collision,
|
|
74
74
|
...props
|
|
@@ -85,7 +85,7 @@ function ContextMenuPortal(props) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
function ContextMenuFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
88
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container);
|
|
88
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container, void 0, props.anchor);
|
|
89
89
|
return /* @__PURE__ */ jsx(ContextMenu.Positioner, {
|
|
90
90
|
...collision,
|
|
91
91
|
...props
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PopupSheetReset } from "../../lib/popup-boundary.mjs";
|
|
1
2
|
import { tv } from "../../lib/tv.mjs";
|
|
2
3
|
import { createContext, createElement, useContext } from "react";
|
|
3
4
|
// src/components/dialog/dialog-sheet-context.ts
|
|
@@ -25,10 +26,11 @@ function useInDialogSheetHead() {
|
|
|
25
26
|
/**
|
|
26
27
|
* Every overlay surface starts outside any sheet: a confirm, a dialog or a
|
|
27
28
|
* drawer opened from inside a sheet keeps its own head, footer and commit
|
|
28
|
-
* button instead of taking the sheet's
|
|
29
|
+
* button instead of taking the sheet's, and its popups aren't kept inside the
|
|
30
|
+
* sheet below it (PopupSheetContext).
|
|
29
31
|
*/
|
|
30
32
|
function DialogSheetReset({ children }) {
|
|
31
|
-
return createElement(DialogSheetSurfaceContext.Provider, { value: false }, createElement(DialogSheetContext.Provider, { value: NO_SHEET }, createElement(DialogSheetHeadContext.Provider, { value: false }, children)));
|
|
33
|
+
return createElement(DialogSheetSurfaceContext.Provider, { value: false }, createElement(DialogSheetContext.Provider, { value: NO_SHEET }, createElement(DialogSheetHeadContext.Provider, { value: false }, createElement(PopupSheetReset, null, children))));
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* A dialog drawn as a phone sheet (design: dialog.md "On a phone"). The
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../cn.mjs";
|
|
2
|
+
import { PopupSheetContext } from "../../lib/popup-boundary.mjs";
|
|
2
3
|
import { useMobileViewport } from "../../lib/use-mobile-viewport.mjs";
|
|
3
4
|
import { useSoftKeyboard } from "../../lib/use-soft-keyboard.mjs";
|
|
4
5
|
import { Button } from "../button/button.mjs";
|
|
@@ -40,6 +41,10 @@ function DialogSheetContent({ className, overlay, placement, style, ref, childre
|
|
|
40
41
|
setPopup(element);
|
|
41
42
|
assignRef(ref, element);
|
|
42
43
|
}, [ref]);
|
|
44
|
+
const popupSheet = useMemo(() => phone && popup ? {
|
|
45
|
+
head: () => popup.querySelector("[data-slot=\"dialog-header\"]"),
|
|
46
|
+
surface: () => popup
|
|
47
|
+
} : void 0, [phone, popup]);
|
|
43
48
|
const sheet = dialogSheet();
|
|
44
49
|
const sheetOverlay = useMemo(() => overlay === false ? false : {
|
|
45
50
|
...typeof overlay === "object" ? overlay : null,
|
|
@@ -65,7 +70,10 @@ function DialogSheetContent({ className, overlay, placement, style, ref, childre
|
|
|
65
70
|
"data-sheet": phone ? "" : void 0,
|
|
66
71
|
children: /* @__PURE__ */ jsx(DialogSheetContext.Provider, {
|
|
67
72
|
value: state,
|
|
68
|
-
children
|
|
73
|
+
children: /* @__PURE__ */ jsx(PopupSheetContext.Provider, {
|
|
74
|
+
value: popupSheet,
|
|
75
|
+
children
|
|
76
|
+
})
|
|
69
77
|
})
|
|
70
78
|
})
|
|
71
79
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../cn.mjs";
|
|
2
2
|
import { barButtonDesktop, headEdgePull } from "../../lib/head-edges.mjs";
|
|
3
3
|
import { decisionWidth } from "../../lib/decision-width.mjs";
|
|
4
|
+
import { PopupSheetReset } from "../../lib/popup-boundary.mjs";
|
|
4
5
|
import { ScrollArea } from "../scroll-area/scroll-area.mjs";
|
|
5
6
|
import { stackedActions, useStackedActions } from "../../lib/stacked-actions.mjs";
|
|
6
7
|
import { tv } from "../../lib/tv.mjs";
|
|
@@ -23,6 +24,7 @@ import { createPortal } from "react-dom";
|
|
|
23
24
|
const dialog = tv({
|
|
24
25
|
slots: {
|
|
25
26
|
overlay: ["fixed inset-0 z-dialog bg-layer-backdrop"],
|
|
27
|
+
portal: [],
|
|
26
28
|
content: [
|
|
27
29
|
"fixed top-1/2 left-1/2 z-dialog flex w-[calc(100%-2rem)] -translate-1/2 flex-col",
|
|
28
30
|
"max-h-[calc(100dvh-2rem)] outline-none",
|
|
@@ -84,9 +86,13 @@ const dialog = tv({
|
|
|
84
86
|
},
|
|
85
87
|
1: {
|
|
86
88
|
overlay: ["z-dialog-layer"],
|
|
87
|
-
content: [
|
|
89
|
+
content: []
|
|
88
90
|
}
|
|
89
91
|
},
|
|
92
|
+
lifted: {
|
|
93
|
+
false: {},
|
|
94
|
+
true: { portal: ["relative isolate z-dialog-layer"] }
|
|
95
|
+
},
|
|
90
96
|
nested: {
|
|
91
97
|
false: {},
|
|
92
98
|
true: { overlay: ["bg-layer-backdrop-nested"] }
|
|
@@ -141,19 +147,25 @@ function DialogContent({ className, children, portalProps, overlay, size, layer,
|
|
|
141
147
|
const sheetSurface = useContext(DialogSheetSurfaceContext);
|
|
142
148
|
const [keyboard, setKeyboard] = useState(null);
|
|
143
149
|
const raised = !sheetSurface && keyboard != null;
|
|
144
|
-
const
|
|
150
|
+
const lifted = layer === 1;
|
|
151
|
+
const { content, portal } = dialog({
|
|
145
152
|
size,
|
|
146
|
-
layer,
|
|
147
153
|
placement,
|
|
154
|
+
lifted,
|
|
148
155
|
keyboard: raised || void 0
|
|
149
156
|
});
|
|
150
|
-
const
|
|
157
|
+
const { layer: overlayLayer, ...overlayRest } = typeof overlay === "object" ? overlay : {};
|
|
158
|
+
const overlayProps = lifted ? overlayRest : {
|
|
159
|
+
layer: overlayLayer,
|
|
160
|
+
...overlayRest
|
|
161
|
+
};
|
|
162
|
+
const portalClassName = portalProps?.className;
|
|
163
|
+
const liftedPortalClassName = typeof portalClassName === "function" ? (state) => portal({ className: portalClassName(state) }) : portal({ className: portalClassName });
|
|
151
164
|
return /* @__PURE__ */ jsxs(DialogPortal, {
|
|
152
165
|
...portalProps,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
166
|
+
"data-layer": lifted ? 1 : void 0,
|
|
167
|
+
className: lifted ? liftedPortalClassName : portalClassName,
|
|
168
|
+
children: [overlay === false ? null : /* @__PURE__ */ jsx(DialogOverlay, { ...overlayProps }), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
157
169
|
"data-slot": "dialog-content",
|
|
158
170
|
"data-placement": top ? "top" : void 0,
|
|
159
171
|
"data-soft-keyboard": raised ? "" : void 0,
|
|
@@ -255,9 +267,9 @@ function DialogHeader({ className, bar = false, children, ...props }) {
|
|
|
255
267
|
"data-sheet": "",
|
|
256
268
|
className: dialog({ bar: false }).header({ className: cn(head.header(), className) }),
|
|
257
269
|
...props,
|
|
258
|
-
children: [/* @__PURE__ */
|
|
270
|
+
children: [/* @__PURE__ */ jsx(DialogSheetHeadContext.Provider, {
|
|
259
271
|
value: true,
|
|
260
|
-
children: [backs.length > 0 ? backs : closes, rest]
|
|
272
|
+
children: /* @__PURE__ */ jsxs(PopupSheetReset, { children: [backs.length > 0 ? backs : closes, rest] })
|
|
261
273
|
}), /* @__PURE__ */ jsx("div", {
|
|
262
274
|
ref: setCommitSlot,
|
|
263
275
|
"data-slot": "dialog-commit-slot",
|
|
@@ -18,7 +18,7 @@ export declare function DrawerContent({ className, children, portalProps, ...pro
|
|
|
18
18
|
export type DrawerCloseProps = StyledProps<Drawer.Close.Props> & Pick<ButtonProps, "variant" | "size">;
|
|
19
19
|
export declare function DrawerClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }: DrawerCloseProps): import("react").JSX.Element;
|
|
20
20
|
export type DrawerHeaderProps = useRender.ComponentProps<"div">;
|
|
21
|
-
export declare function DrawerHeader({ className, render, ...props }: DrawerHeaderProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
21
|
+
export declare function DrawerHeader({ className, render, children, ...props }: DrawerHeaderProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
22
22
|
export type DrawerFooterProps = useRender.ComponentProps<"div">;
|
|
23
23
|
export declare function DrawerFooter({ className, render, ...props }: DrawerFooterProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
24
24
|
export type DrawerBodyProps = useRender.ComponentProps<"div">;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { cn } from "../../cn.mjs";
|
|
2
|
+
import { PopupSheetContext, PopupSheetReset } from "../../lib/popup-boundary.mjs";
|
|
2
3
|
import { useThemeScopeProps } from "../../lib/theme/theme-scope.mjs";
|
|
3
4
|
import { Button } from "../button/button.mjs";
|
|
4
5
|
import { DialogSheetReset } from "../dialog/dialog-sheet-context.mjs";
|
|
5
6
|
import { drawer } from "./drawer.recipe.mjs";
|
|
6
7
|
import { useParentOverlayKind, withOverlayLayer } from "../../lib/overlay-depth.mjs";
|
|
7
8
|
import { X } from "lucide-react";
|
|
8
|
-
import { createContext, useContext, useMemo } from "react";
|
|
9
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
9
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
11
12
|
import { useRender } from "@base-ui/react/use-render";
|
|
@@ -89,6 +90,11 @@ function DrawerContent({ className, children, portalProps, ...props }) {
|
|
|
89
90
|
axis: swipeAxis,
|
|
90
91
|
snapPoints: hasSnapPoints
|
|
91
92
|
});
|
|
93
|
+
const [surface, setSurface] = useState(null);
|
|
94
|
+
const popupSheet = useMemo(() => surface ? {
|
|
95
|
+
head: () => surface.querySelector("[data-slot=\"drawer-header\"]"),
|
|
96
|
+
surface: () => surface
|
|
97
|
+
} : void 0, [surface]);
|
|
92
98
|
return /* @__PURE__ */ jsxs(DrawerPortal, {
|
|
93
99
|
...portalProps,
|
|
94
100
|
children: [modal === true ? /* @__PURE__ */ jsx(DrawerOverlay, {}) : null, /* @__PURE__ */ jsx(Drawer.Viewport, {
|
|
@@ -99,9 +105,13 @@ function DrawerContent({ className, children, portalProps, ...props }) {
|
|
|
99
105
|
className: popup({ className }),
|
|
100
106
|
...props,
|
|
101
107
|
children: [hasSnapPoints || swipeDirection === "down" ? /* @__PURE__ */ jsx(DrawerSwipeHandle, {}) : null, /* @__PURE__ */ jsx(Drawer.Content, {
|
|
108
|
+
ref: setSurface,
|
|
102
109
|
"data-slot": "drawer-content",
|
|
103
110
|
className: content(),
|
|
104
|
-
children: /* @__PURE__ */ jsx(DialogSheetReset, { children
|
|
111
|
+
children: /* @__PURE__ */ jsx(DialogSheetReset, { children: /* @__PURE__ */ jsx(PopupSheetContext.Provider, {
|
|
112
|
+
value: popupSheet,
|
|
113
|
+
children
|
|
114
|
+
}) })
|
|
105
115
|
})]
|
|
106
116
|
})
|
|
107
117
|
})]
|
|
@@ -130,14 +140,17 @@ function DrawerClose({ className, variant, size, children, render, "aria-label":
|
|
|
130
140
|
children: children ?? /* @__PURE__ */ jsx(X, { className: closeIcon() })
|
|
131
141
|
});
|
|
132
142
|
}
|
|
133
|
-
function DrawerHeader({ className, render, ...props }) {
|
|
143
|
+
function DrawerHeader({ className, render, children, ...props }) {
|
|
134
144
|
const { hasSnapPoints, swipeAxis, swipeDirection } = useDrawerConfig();
|
|
135
145
|
const { header } = drawer({
|
|
136
146
|
side: swipeDirection,
|
|
137
147
|
axis: swipeAxis,
|
|
138
148
|
snapPoints: hasSnapPoints
|
|
139
149
|
});
|
|
140
|
-
const defaultProps = {
|
|
150
|
+
const defaultProps = {
|
|
151
|
+
className: header({ className }),
|
|
152
|
+
children: /* @__PURE__ */ jsx(PopupSheetReset, { children })
|
|
153
|
+
};
|
|
141
154
|
return useRender({
|
|
142
155
|
defaultTagName: "div",
|
|
143
156
|
render,
|
|
@@ -101,7 +101,7 @@ function DropdownMenuPopup({ className, ...props }) {
|
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
103
|
function DropdownMenuFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
104
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container);
|
|
104
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container, void 0, props.anchor);
|
|
105
105
|
return /* @__PURE__ */ jsx(Menu.Positioner, {
|
|
106
106
|
...collision,
|
|
107
107
|
...props
|
|
@@ -61,7 +61,7 @@ export type LightboxContentProps = StyledProps<Dialog.Popup.Props>;
|
|
|
61
61
|
* A lightbox opened from state passes `finalFocus={openerRef}` so focus goes
|
|
62
62
|
* back to what opened it.
|
|
63
63
|
*/
|
|
64
|
-
export declare function LightboxContent({ className, initialFocus, finalFocus, onKeyDown, ref, ...props }: LightboxContentProps): import("react").JSX.Element;
|
|
64
|
+
export declare function LightboxContent({ className, initialFocus, finalFocus, onKeyDown, ref, children, ...props }: LightboxContentProps): import("react").JSX.Element;
|
|
65
65
|
export type LightboxHeaderProps = useRender.ComponentProps<"div">;
|
|
66
66
|
export declare function LightboxHeader({ render, className, ...props }: LightboxHeaderProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
67
67
|
export type LightboxToolbarProps = useRender.ComponentProps<"div">;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../cn.mjs";
|
|
2
2
|
import { headEdgePull } from "../../lib/head-edges.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { OverlayLayer, useParentOverlayKind } from "../../lib/overlay-depth.mjs";
|
|
4
|
+
import { PopupSheetReset } from "../../lib/popup-boundary.mjs";
|
|
4
5
|
import { tv } from "../../lib/tv.mjs";
|
|
5
6
|
import { useMergedRef } from "../../lib/use-merged-ref.mjs";
|
|
6
7
|
import { useThemeScopeProps } from "../../lib/theme/theme-scope.mjs";
|
|
@@ -25,9 +26,9 @@ const lightbox = tv({
|
|
|
25
26
|
header: [
|
|
26
27
|
"pointer-events-auto shrink-0",
|
|
27
28
|
"flex items-center gap-3 pb-3",
|
|
28
|
-
"pt-[calc(env(safe-area-inset-top,0px)
|
|
29
|
-
"pl-[calc(env(safe-area-inset-left,0px)
|
|
30
|
-
"pr-[calc(env(safe-area-inset-right,0px)
|
|
29
|
+
"pt-[calc(env(safe-area-inset-top,0px)+(--spacing(3)))]",
|
|
30
|
+
"pl-[calc(env(safe-area-inset-left,0px)+(--spacing(4)))]",
|
|
31
|
+
"pr-[calc(env(safe-area-inset-right,0px)+(--spacing(4)))]",
|
|
31
32
|
"bg-transparent shadow-none",
|
|
32
33
|
headEdgePull
|
|
33
34
|
],
|
|
@@ -71,7 +72,7 @@ const lightbox = tv({
|
|
|
71
72
|
"focus-visible:outline-transparent focus-visible:shadow-focus",
|
|
72
73
|
"[&_svg]:size-5.5"
|
|
73
74
|
],
|
|
74
|
-
infoDock: ["pointer-events-none absolute
|
|
75
|
+
infoDock: ["pointer-events-none absolute inset-x-6 bottom-20 z-raised flex justify-center"],
|
|
75
76
|
infoBar: [
|
|
76
77
|
"pointer-events-auto flex items-center",
|
|
77
78
|
"w-[clamp(144px,20vw,224px)] max-w-[calc(100vw-48px)] min-w-36",
|
|
@@ -87,7 +88,7 @@ const lightbox = tv({
|
|
|
87
88
|
"*:data-[slot=button]:after:inset-x-[-3px] *:data-[slot=button]:first:after:left-[-9.5px] *:data-[slot=button]:last:after:right-[-9.5px]",
|
|
88
89
|
"pointer-coarse:*:data-[slot=button]:after:inset-y-[-9.5px]"
|
|
89
90
|
],
|
|
90
|
-
thumbnailStrip: ["pointer-events-none absolute
|
|
91
|
+
thumbnailStrip: ["pointer-events-none absolute inset-x-0 bottom-4 z-raised", "flex items-center justify-center gap-1.5"],
|
|
91
92
|
thumbnail: [
|
|
92
93
|
"group/lightbox-thumb pointer-events-auto relative size-12 overflow-hidden",
|
|
93
94
|
"transition-[border-color,opacity,box-shadow] duration-150",
|
|
@@ -127,7 +128,7 @@ function Lightbox({ closeOnModW = true, defaultOpen = false, modal = true, onOpe
|
|
|
127
128
|
onOpenChange: handleOpenChange,
|
|
128
129
|
open,
|
|
129
130
|
...props,
|
|
130
|
-
children: [modWClose, /* @__PURE__ */ jsx(
|
|
131
|
+
children: [modWClose, /* @__PURE__ */ jsx(OverlayLayer, {
|
|
131
132
|
kind: "dialog",
|
|
132
133
|
modal: true,
|
|
133
134
|
children: /* @__PURE__ */ jsx(LightboxModalContext.Provider, {
|
|
@@ -281,7 +282,7 @@ function routeScrollKey(event) {
|
|
|
281
282
|
* A lightbox opened from state passes `finalFocus={openerRef}` so focus goes
|
|
282
283
|
* back to what opened it.
|
|
283
284
|
*/
|
|
284
|
-
function LightboxContent({ className, initialFocus, finalFocus, onKeyDown, ref, ...props }) {
|
|
285
|
+
function LightboxContent({ className, initialFocus, finalFocus, onKeyDown, ref, children, ...props }) {
|
|
285
286
|
const documentMode = useContext(LightboxModalContext) === "trap-focus";
|
|
286
287
|
const nested = useParentOverlayKind() != null;
|
|
287
288
|
const { backdrop, content } = lightbox({ nested });
|
|
@@ -307,7 +308,8 @@ function LightboxContent({ className, initialFocus, finalFocus, onKeyDown, ref,
|
|
|
307
308
|
finalFocus: finalFocus ?? documentMode,
|
|
308
309
|
className: content({ className }),
|
|
309
310
|
onKeyDown: handleKeyDown,
|
|
310
|
-
...props
|
|
311
|
+
...props,
|
|
312
|
+
children: /* @__PURE__ */ jsx(PopupSheetReset, { children })
|
|
311
313
|
})] });
|
|
312
314
|
}
|
|
313
315
|
function LightboxHeader({ render, className, ...props }) {
|
|
@@ -137,7 +137,7 @@ function NotificationCenterPortal(props) {
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
function NotificationCenterFramePositioner({ collisionBoundary, collisionPadding, container, defaultCollisionPadding, ...props }) {
|
|
140
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, defaultCollisionPadding, container);
|
|
140
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, defaultCollisionPadding, container, void 0, props.anchor);
|
|
141
141
|
return /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
142
142
|
...collision,
|
|
143
143
|
...props
|
|
@@ -71,7 +71,7 @@ function PopoverPopup({ className, ...props }) {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
function PopoverFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
74
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container);
|
|
74
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container, void 0, props.anchor);
|
|
75
75
|
return /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
76
76
|
...collision,
|
|
77
77
|
...props
|
|
@@ -70,7 +70,7 @@ function PreviewCardPopup({ className, ...props }) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
function PreviewCardFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
73
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container);
|
|
73
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 5, container, void 0, props.anchor);
|
|
74
74
|
return /* @__PURE__ */ jsx(PreviewCard.Positioner, {
|
|
75
75
|
...collision,
|
|
76
76
|
...props
|
|
@@ -344,7 +344,7 @@ function SelectPopup({ className, ...props }) {
|
|
|
344
344
|
});
|
|
345
345
|
}
|
|
346
346
|
function SelectFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
347
|
-
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container);
|
|
347
|
+
const collision = usePopupCollision(collisionPadding, collisionBoundary, 8, container, void 0, props.anchor);
|
|
348
348
|
return /* @__PURE__ */ jsx(Select.Positioner, {
|
|
349
349
|
...collision,
|
|
350
350
|
...props
|
|
@@ -169,7 +169,7 @@ const CONTENT_COLLISION_AVOIDANCE = {
|
|
|
169
169
|
* off center, by up to half the left and right insets.
|
|
170
170
|
*/
|
|
171
171
|
function TooltipFramePositioner({ collisionBoundary, collisionPadding, container, ...props }) {
|
|
172
|
-
const collision = usePopupCollision(void 0, collisionBoundary, collisionPadding ?? 5, container, 0);
|
|
172
|
+
const collision = usePopupCollision(void 0, collisionBoundary, collisionPadding ?? 5, container, 0, props.anchor);
|
|
173
173
|
return /* @__PURE__ */ jsx(Tooltip.Positioner, {
|
|
174
174
|
...collision,
|
|
175
175
|
...props
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// src/lib/fill-contrast.ts
|
|
2
|
+
/**
|
|
3
|
+
* Below this contrast against the surface, a fill gets a hairline ring.
|
|
4
|
+
* 1.5:1 is where a flat fill stops reading as a shape of its own: a pale
|
|
5
|
+
* yellow on a white card, a near-black on a dark one.
|
|
6
|
+
*/
|
|
7
|
+
const FILL_RING_CONTRAST = 1.5;
|
|
8
|
+
/** `#RRGGBB` (either case) as sRGB channels; anything else is null. */
|
|
9
|
+
function parseHexColor(value) {
|
|
10
|
+
const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(value);
|
|
11
|
+
if (!match) return null;
|
|
12
|
+
return [
|
|
13
|
+
parseInt(match[1], 16) / 255,
|
|
14
|
+
parseInt(match[2], 16) / 255,
|
|
15
|
+
parseInt(match[3], 16) / 255
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
/** An `oklch(l c h)` token value (as tokens.json stores its components) in sRGB, clamped. */
|
|
19
|
+
function oklchToRgb(l, c, h) {
|
|
20
|
+
const a = c * Math.cos(h * Math.PI / 180);
|
|
21
|
+
const b = c * Math.sin(h * Math.PI / 180);
|
|
22
|
+
const L = (l + .3963377774 * a + .2158037573 * b) ** 3;
|
|
23
|
+
const M = (l - .1055613458 * a - .0638541728 * b) ** 3;
|
|
24
|
+
const S = (l - .0894841775 * a - 1.291485548 * b) ** 3;
|
|
25
|
+
const [r, g, bl] = [
|
|
26
|
+
4.0767416621 * L - 3.3077115913 * M + .2309699292 * S,
|
|
27
|
+
-1.2684380046 * L + 2.6097574011 * M - .3413193965 * S,
|
|
28
|
+
-.0041960863 * L - .7034186147 * M + 1.707614701 * S
|
|
29
|
+
].map((x) => {
|
|
30
|
+
const y = Math.min(1, Math.max(0, x));
|
|
31
|
+
return y <= .0031308 ? 12.92 * y : 1.055 * y ** (1 / 2.4) - .055;
|
|
32
|
+
});
|
|
33
|
+
return [
|
|
34
|
+
r,
|
|
35
|
+
g,
|
|
36
|
+
bl
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
/** WCAG 2 relative luminance. */
|
|
40
|
+
function relativeLuminance([r, g, b]) {
|
|
41
|
+
const linear = (v) => v <= .04045 ? v / 12.92 : ((v + .055) / 1.055) ** 2.4;
|
|
42
|
+
return .2126 * linear(r) + .7152 * linear(g) + .0722 * linear(b);
|
|
43
|
+
}
|
|
44
|
+
/** WCAG 2 contrast ratio, from 1 to 21, in either order. */
|
|
45
|
+
function contrastRatio(a, b) {
|
|
46
|
+
const la = relativeLuminance(a);
|
|
47
|
+
const lb = relativeLuminance(b);
|
|
48
|
+
return (Math.max(la, lb) + .05) / (Math.min(la, lb) + .05);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The type and the ring for `fill` lying on `surface`: black or white type,
|
|
52
|
+
* whichever has the higher contrast (one of them always reaches 4.58:1), and
|
|
53
|
+
* a ring when the fill's contrast with the surface is below
|
|
54
|
+
* `FILL_RING_CONTRAST`.
|
|
55
|
+
*/
|
|
56
|
+
function fillOnSurface(fill, surface) {
|
|
57
|
+
const luminance = relativeLuminance(fill);
|
|
58
|
+
return {
|
|
59
|
+
text: (luminance + .05) / .05 >= 1.05 / (luminance + .05) ? "black" : "white",
|
|
60
|
+
ring: contrastRatio(fill, surface) < FILL_RING_CONTRAST
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export { FILL_RING_CONTRAST, contrastRatio, fillOnSurface, oklchToRgb, parseHexColor, relativeLuminance };
|