@botiverse/raft-ui 0.82.0 → 0.84.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 +147 -0
- package/dist/components/app-link-card/app-link-card.mjs +19 -37
- package/dist/components/banner/banner.mjs +15 -2
- package/dist/components/dialog/dialog-sheet-context.mjs +7 -2
- package/dist/components/dialog/dialog-sheet.d.mts +3 -2
- package/dist/components/dialog/dialog-sheet.mjs +29 -43
- package/dist/components/dialog/dialog.d.mts +12 -1
- package/dist/components/dialog/dialog.mjs +50 -10
- package/dist/components/drag/drag.recipe.mjs +1 -1
- package/dist/components/fit-toolbar/fit-toolbar.d.mts +165 -0
- package/dist/components/fit-toolbar/fit-toolbar.mjs +736 -0
- package/dist/components/fit-toolbar/fit-toolbar.recipe.mjs +35 -0
- package/dist/components/fit-toolbar/index.d.mts +2 -0
- package/dist/components/lightbox/index.d.mts +2 -2
- package/dist/components/lightbox/lightbox.d.mts +70 -4
- package/dist/components/lightbox/lightbox.mjs +285 -91
- package/dist/components/list-row/list-row.d.mts +14 -1
- package/dist/components/list-row/list-row.mjs +4 -2
- package/dist/components/list-row/list-row.recipe.d.mts +15 -3
- package/dist/components/list-row/list-row.recipe.mjs +18 -1
- package/dist/components/message-action-card/message-action-card-content.recipe.mjs +3 -3
- package/dist/components/message-action-card/message-action-card.mjs +7 -3
- package/dist/components/message-action-card/message-action-card.recipe.mjs +2 -6
- package/dist/components/message-embed/index.d.mts +2 -1
- package/dist/components/message-embed/message-embed.d.mts +8 -1
- package/dist/components/message-embed/message-embed.mjs +2 -2
- package/dist/components/message-embed/message-embed.recipe.d.mts +160 -0
- package/dist/components/message-embed/message-embed.recipe.mjs +49 -10
- package/dist/components/message-forwarded-bundle/message-forwarded-bundle.mjs +1 -0
- package/dist/components/message-forwarded-bundle/message-forwarded-bundle.recipe.mjs +1 -1
- package/dist/components/message-quoted-preview/message-quoted-preview.mjs +2 -0
- package/dist/components/message-replies/message-replies.mjs +1 -1
- package/dist/components/message-replies/message-replies.recipe.mjs +1 -1
- package/dist/components/mobile-nav/mobile-nav-server.d.mts +21 -8
- package/dist/components/mobile-nav/mobile-nav-server.mjs +142 -13
- package/dist/components/popover/popover.d.mts +11 -2
- package/dist/components/popover/popover.mjs +51 -38
- package/dist/components/preview-card/preview-card.d.mts +5 -1
- package/dist/components/preview-card/preview-card.mjs +24 -27
- package/dist/components/save-status/save-status.d.mts +16 -9
- package/dist/components/save-status/save-status.mjs +19 -30
- package/dist/components/segmented-control/segmented-control.d.mts +1 -1
- package/dist/components/segmented-control/segmented-control.mjs +56 -10
- package/dist/components/selection-toolbar/index.d.mts +2 -0
- package/dist/components/selection-toolbar/selection-toolbar.d.mts +100 -0
- package/dist/components/selection-toolbar/selection-toolbar.mjs +217 -0
- package/dist/components/selection-toolbar/selection-toolbar.recipe.mjs +42 -0
- package/dist/components/task-board/task-board.d.mts +5 -0
- package/dist/components/task-board/task-board.mjs +8 -1
- package/dist/components/task-dnd/task-dnd.d.mts +10 -1
- package/dist/components/task-dnd/task-dnd.mjs +2 -1
- package/dist/components/toast/toast.mjs +2 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +4 -2
- package/dist/lib/label-value-fit.mjs +33 -6
- package/dist/lib/laid-out-children.mjs +32 -0
- package/dist/lib/stacked-actions.mjs +7 -4
- package/dist/lib/use-mobile-viewport.mjs +5 -2
- package/dist/lib/use-soft-keyboard.mjs +72 -0
- package/dist/wip.d.mts +8 -3
- package/dist/wip.mjs +5 -3
- package/package.json +1 -1
|
@@ -6,13 +6,15 @@ import { tv } from "../../lib/tv.mjs";
|
|
|
6
6
|
import { useThemeScopeProps } from "../../lib/theme/theme-scope.mjs";
|
|
7
7
|
import { useParentOverlayKind, withOverlayLayer } from "../../lib/overlay-depth.mjs";
|
|
8
8
|
import { Button } from "../button/button.mjs";
|
|
9
|
-
import { DialogSheetHeadContext, DialogSheetReset, dialogSheet, useDialogSheet, useInDialogSheetHead } from "./dialog-sheet-context.mjs";
|
|
9
|
+
import { DialogSheetHeadContext, DialogSheetReset, DialogSheetSurfaceContext, dialogSheet, useDialogSheet, useInDialogSheetHead } from "./dialog-sheet-context.mjs";
|
|
10
10
|
import { ScrollArea } from "../scroll-area/scroll-area.mjs";
|
|
11
|
+
import { useMobileViewport } from "../../lib/use-mobile-viewport.mjs";
|
|
12
|
+
import { useSoftKeyboard } from "../../lib/use-soft-keyboard.mjs";
|
|
11
13
|
import { TextLink } from "../text-link/text-link.mjs";
|
|
12
14
|
import { PanelAction } from "../panel/panel-action.mjs";
|
|
13
15
|
import { Dialog } from "@base-ui/react/dialog";
|
|
14
16
|
import { ArrowLeft, X } from "lucide-react";
|
|
15
|
-
import { Children, createContext, isValidElement, useContext, useEffect } from "react";
|
|
17
|
+
import { Children, createContext, isValidElement, useContext, useEffect, useState } from "react";
|
|
16
18
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
19
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
18
20
|
import { useRender } from "@base-ui/react/use-render";
|
|
@@ -59,12 +61,20 @@ const dialog = tv({
|
|
|
59
61
|
lg: { content: ["max-w-160"] },
|
|
60
62
|
wide: { content: ["max-w-4xl h-[min(760px,calc(100dvh-2rem))]"] }
|
|
61
63
|
},
|
|
64
|
+
placement: {
|
|
65
|
+
center: {},
|
|
66
|
+
top: { content: ["md:top-(--dialog-top,8vh) md:translate-y-0", "md:max-h-[calc(100dvh-var(--dialog-top,8vh)-16px)]"] }
|
|
67
|
+
},
|
|
68
|
+
keyboard: {
|
|
69
|
+
false: {},
|
|
70
|
+
true: { content: ["data-soft-keyboard:top-[calc(var(--dialog-visible-top)+env(safe-area-inset-top,0px)+var(--header-height))] data-soft-keyboard:translate-y-0", "data-soft-keyboard:max-h-[calc(var(--dialog-visible-height)-env(safe-area-inset-top,0px)-var(--header-height)-16px)]"] }
|
|
71
|
+
},
|
|
62
72
|
bar: {
|
|
63
73
|
false: {},
|
|
64
74
|
true: {
|
|
65
|
-
header: ["flex h-(--header-height) shrink-0 items-center gap-2 border-b px-(--surface-inset) py-0", "border-line-seam pt-0
|
|
66
|
-
body: ["flex min-h-0 flex-1 overflow-hidden p-0", "
|
|
67
|
-
footer: ["shrink-0 border-t px-(--surface-inset) py-3", "border-line-seam
|
|
75
|
+
header: ["flex h-(--header-height) shrink-0 items-center gap-2 border-b px-(--surface-inset) py-0", "border-line-seam pt-0 *:data-[slot=dialog-close]:my-0"],
|
|
76
|
+
body: ["flex min-h-0 flex-1 overflow-hidden p-0", "p-0 first:pt-0 text-foreground"],
|
|
77
|
+
footer: ["shrink-0 border-t px-(--surface-inset) py-3", "border-line-seam py-3 first:pt-3 [[data-slot=dialog-header]+&]:pt-3"]
|
|
68
78
|
}
|
|
69
79
|
},
|
|
70
80
|
layer: {
|
|
@@ -125,10 +135,16 @@ function DialogOverlay({ className, layer, ...props }) {
|
|
|
125
135
|
});
|
|
126
136
|
}
|
|
127
137
|
const DialogSizeContext = createContext(void 0);
|
|
128
|
-
function DialogContent({ className, children, portalProps, overlay, size, layer, ...props }) {
|
|
138
|
+
function DialogContent({ className, children, portalProps, overlay, size, layer, placement, ...props }) {
|
|
139
|
+
const top = placement === "top";
|
|
140
|
+
const sheetSurface = useContext(DialogSheetSurfaceContext);
|
|
141
|
+
const [keyboard, setKeyboard] = useState(null);
|
|
142
|
+
const raised = !sheetSurface && keyboard != null;
|
|
129
143
|
const { content } = dialog({
|
|
130
144
|
size,
|
|
131
|
-
layer
|
|
145
|
+
layer,
|
|
146
|
+
placement,
|
|
147
|
+
keyboard: raised || void 0
|
|
132
148
|
});
|
|
133
149
|
const overlayProps = typeof overlay === "object" ? overlay : {};
|
|
134
150
|
return /* @__PURE__ */ jsxs(DialogPortal, {
|
|
@@ -136,17 +152,41 @@ function DialogContent({ className, children, portalProps, overlay, size, layer,
|
|
|
136
152
|
children: [overlay === false ? null : /* @__PURE__ */ jsx(DialogOverlay, {
|
|
137
153
|
layer,
|
|
138
154
|
...overlayProps
|
|
139
|
-
}), /* @__PURE__ */
|
|
155
|
+
}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
140
156
|
"data-slot": "dialog-content",
|
|
157
|
+
"data-placement": top ? "top" : void 0,
|
|
158
|
+
"data-soft-keyboard": raised ? "" : void 0,
|
|
141
159
|
className: content({ className }),
|
|
142
160
|
...props,
|
|
143
|
-
|
|
161
|
+
...raised ? { style: withVisibleArea(props.style, keyboard) } : null,
|
|
162
|
+
children: [sheetSurface ? null : /* @__PURE__ */ jsx(SoftKeyboardWatch, { onChange: setKeyboard }), /* @__PURE__ */ jsx(DialogSizeContext.Provider, {
|
|
144
163
|
value: size ?? (/(^|\s)max-w-/.test(className ?? "") ? void 0 : "md"),
|
|
145
164
|
children: /* @__PURE__ */ jsx(DialogSheetReset, { children })
|
|
146
|
-
})
|
|
165
|
+
})]
|
|
147
166
|
})]
|
|
148
167
|
});
|
|
149
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Reports the on-screen keyboard on a phone to a centred dialog, which moves
|
|
171
|
+
* up while it is open (dialog.md, "On a phone"). Rendered inside the popup,
|
|
172
|
+
* so it listens only while the dialog is open, and only below 768px.
|
|
173
|
+
*/
|
|
174
|
+
function SoftKeyboardWatch({ onChange }) {
|
|
175
|
+
const keyboard = useSoftKeyboard(useMobileViewport());
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
onChange(keyboard);
|
|
178
|
+
}, [keyboard, onChange]);
|
|
179
|
+
useEffect(() => () => onChange(null), [onChange]);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/** The popup's style with the area the keyboard leaves visible, for its CSS. */
|
|
183
|
+
function withVisibleArea(style, keyboard) {
|
|
184
|
+
return {
|
|
185
|
+
...style,
|
|
186
|
+
"--dialog-visible-top": `${keyboard.top}px`,
|
|
187
|
+
"--dialog-visible-height": `${keyboard.height}px`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
150
190
|
function DialogClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }) {
|
|
151
191
|
const inSheetHead = useInDialogSheetHead();
|
|
152
192
|
if (render) return /* @__PURE__ */ jsx(Dialog.Close, {
|
|
@@ -18,7 +18,7 @@ const drag = tv({
|
|
|
18
18
|
"border-[1.5px] border-dashed border-line-strong bg-fill-subtle"
|
|
19
19
|
],
|
|
20
20
|
hint: [
|
|
21
|
-
"pointer-events-none fixed top-0 left-0 z-
|
|
21
|
+
"pointer-events-none fixed top-0 left-0 z-999 max-w-80",
|
|
22
22
|
"rounded-chip px-2 py-1",
|
|
23
23
|
"font-sans text-caption",
|
|
24
24
|
"bg-layer-hud text-foreground-on-hud shadow-float ring-1 ring-line-seam"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { StyledProps } from "../../lib/styled-props.mjs";
|
|
2
|
+
import { ButtonProps } from "../button/button.mjs";
|
|
3
|
+
import { PickerTriggerButtonProps } from "../picker-trigger-button/picker-trigger-button.mjs";
|
|
4
|
+
import { ReactElement, ReactNode, Ref } from "react";
|
|
5
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
6
|
+
//#region src/components/fit-toolbar/fit-toolbar.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* The ways an item gives way, each one step:
|
|
9
|
+
* - `label`: its label hides and its icon stays (the label stays its accessible name);
|
|
10
|
+
* - `more`: it moves into More;
|
|
11
|
+
* - `collapse`: a group shows its one button instead of its items (a Filter button for its filters);
|
|
12
|
+
* - `wrap`: a group moves to a line of its own, only in a toolbar that allows it;
|
|
13
|
+
* - `plus`: a primary action shows as + alone (the label stays its accessible name).
|
|
14
|
+
*/
|
|
15
|
+
export type FitStep = "label" | "more" | "collapse" | "wrap" | "plus";
|
|
16
|
+
/**
|
|
17
|
+
* When each of an item's steps comes, one order for the whole toolbar: lower
|
|
18
|
+
* gives way first; at the same number, the last item first, and one item's
|
|
19
|
+
* steps in the order of `FitStep`.
|
|
20
|
+
*/
|
|
21
|
+
export type FitSteps = Partial<Record<FitStep, number>>;
|
|
22
|
+
export type FitToolbarProps = StyledProps<Toolbar.Root.Props> & {
|
|
23
|
+
ref?: Ref<HTMLDivElement>;
|
|
24
|
+
/**
|
|
25
|
+
* Groups may move to a line of their own (the `wrap` step) when the row
|
|
26
|
+
* runs out of room. Off for a toolbar that floats over content: a second
|
|
27
|
+
* line would cover it, so its last step is into More.
|
|
28
|
+
*/
|
|
29
|
+
wrap?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A toolbar whose items give way, in named steps and an order the items
|
|
33
|
+
* declare, as the region it sits in narrows: it measures its content in its
|
|
34
|
+
* own width, never the window's. The steps are written on each item as
|
|
35
|
+
* `data-fit-<step>` for its styles. A Base UI Toolbar: one Tab stop, the
|
|
36
|
+
* arrow keys inside.
|
|
37
|
+
*/
|
|
38
|
+
export declare function FitToolbar({ className, wrap, ref, ...props }: FitToolbarProps): import("react").JSX.Element;
|
|
39
|
+
/**
|
|
40
|
+
* Inside a FitToolbar: a control that would measure its own fit (a
|
|
41
|
+
* SegmentedControl's `iconsOnlyBelow`) leaves it to the toolbar's steps.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useInFitToolbar(): boolean;
|
|
44
|
+
export type FitItemOptions = FitSteps & {
|
|
45
|
+
/** What the item is in More's menu while it is there (`FitToolbarMoreItems`). */
|
|
46
|
+
moreItem?: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Makes an element an item of the nearest FitToolbar: spread `props` on it.
|
|
50
|
+
* Says which steps it has taken, for what it renders (a tooltip once its
|
|
51
|
+
* label is gone, say).
|
|
52
|
+
*/
|
|
53
|
+
export declare function useFitItem({ moreItem, ...steps }?: FitItemOptions): {
|
|
54
|
+
id: string;
|
|
55
|
+
props: Record<string, string | undefined>;
|
|
56
|
+
/** Steps it has taken. */
|
|
57
|
+
taken: (step: FitStep) => boolean;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Makes an element the toolbar's More: spread `props` on it. It shows only
|
|
61
|
+
* while an item is in More (`data-fit-shown`); hide it otherwise.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useFitMore(): {
|
|
64
|
+
shown: boolean;
|
|
65
|
+
props: Record<string, string | undefined>;
|
|
66
|
+
};
|
|
67
|
+
/** The items in More, in their order, each as its `moreItem`: put it in More's menu. */
|
|
68
|
+
export declare function FitToolbarMoreItems(): import("react").JSX.Element[] | null;
|
|
69
|
+
export type FitToolbarGroupProps = StyledProps<Toolbar.Group.Props> & {
|
|
70
|
+
/** When it moves to a line of its own (the `wrap` step), in a toolbar that allows it. */
|
|
71
|
+
wrapOrder?: number;
|
|
72
|
+
/** Stays on the first line: it never wraps. The page decides which groups do. */
|
|
73
|
+
firstLine?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Groups with the same `wrapLine` wrap onto the same line, in their order, a
|
|
76
|
+
* fixed gap apart; a group without one takes a line of its own.
|
|
77
|
+
*/
|
|
78
|
+
wrapLine?: string;
|
|
79
|
+
/** When it shows `collapsed` instead of its items (the `collapse` step). */
|
|
80
|
+
collapseOrder?: number;
|
|
81
|
+
/** The group as one button (a Filter button for its filters), shown once it collapses. */
|
|
82
|
+
collapsed?: ReactNode;
|
|
83
|
+
/**
|
|
84
|
+
* `scroll`: the group takes the rest of its line and scrolls sideways
|
|
85
|
+
* instead of giving way, its items whole, the side that hides more fading.
|
|
86
|
+
* Mark an applied or chosen item `data-active`: it is kept in view.
|
|
87
|
+
*/
|
|
88
|
+
overflow?: "scroll";
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* A group of items in one row, a fixed gap apart. A toolbar wraps only
|
|
92
|
+
* between groups, the last first; groups with the same `wrapLine` share a
|
|
93
|
+
* line, any other takes one of its own.
|
|
94
|
+
*/
|
|
95
|
+
export declare function FitToolbarGroup({ className, wrapOrder, firstLine, wrapLine, collapseOrder, collapsed, overflow, children, ref, ...props }: FitToolbarGroupProps & {
|
|
96
|
+
ref?: Ref<HTMLDivElement>;
|
|
97
|
+
}): import("react").JSX.Element;
|
|
98
|
+
export type FitToolbarButtonProps = Omit<ButtonProps, "children" | "size"> & {
|
|
99
|
+
/** Its icon, which stays when its label gives way. A button with a `label` step needs one (or a `collapsedIcon`). */
|
|
100
|
+
icon?: ReactNode;
|
|
101
|
+
/**
|
|
102
|
+
* An icon shown only once its label has given way (the `label` step), in
|
|
103
|
+
* place of `icon`: a button that reads as text while it has room ("Clear
|
|
104
|
+
* filters") and as an icon (×) once it hasn't.
|
|
105
|
+
*/
|
|
106
|
+
collapsedIcon?: ReactNode;
|
|
107
|
+
/** Its label: its accessible name and, once it has given way, its tooltip. */
|
|
108
|
+
children: ReactNode;
|
|
109
|
+
/** When its label gives way (the `label` step). */
|
|
110
|
+
labelOrder?: number;
|
|
111
|
+
/**
|
|
112
|
+
* When it shows as + alone, its icon and label gone (the `plus` step): for
|
|
113
|
+
* the primary action, after the groups have wrapped.
|
|
114
|
+
*/
|
|
115
|
+
plusOrder?: number;
|
|
116
|
+
/** When it moves into More (the `more` step). */
|
|
117
|
+
moreOrder?: number;
|
|
118
|
+
/** What it is in More's menu. */
|
|
119
|
+
moreItem?: ReactNode;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* A toolbar button that can give way: an `md` Button (ghost unless you say)
|
|
123
|
+
* with an icon and a label, an icon square with its label as a tooltip from
|
|
124
|
+
* `labelOrder` on, a + square with its label as a tooltip from `plusOrder`
|
|
125
|
+
* on, and hidden from `moreOrder` on.
|
|
126
|
+
*/
|
|
127
|
+
export declare function FitToolbarButton({ icon, collapsedIcon, children, labelOrder, plusOrder, moreOrder, moreItem, variant, className, ...props }: FitToolbarButtonProps): import("react").JSX.Element;
|
|
128
|
+
export type FitToolbarPickerProps = Omit<PickerTriggerButtonProps, "children" | "render"> & {
|
|
129
|
+
/**
|
|
130
|
+
* The picker's trigger, which opens its popup: `<PopoverTrigger />`,
|
|
131
|
+
* `<DropdownMenuTrigger />` or a Base UI `Menu.Trigger`. The picker renders
|
|
132
|
+
* it as a toolbar button, so the page nests nothing itself.
|
|
133
|
+
*/
|
|
134
|
+
trigger: ReactElement<{
|
|
135
|
+
render?: unknown;
|
|
136
|
+
}>;
|
|
137
|
+
/** Its icon, which stays when its label gives way. */
|
|
138
|
+
icon: ReactNode;
|
|
139
|
+
/** Its label ("Assignee"): part of its accessible name and, once it has given way, its tooltip. */
|
|
140
|
+
children: ReactNode;
|
|
141
|
+
/**
|
|
142
|
+
* What is chosen: the value ("In progress", "Kirby") or a count (`1`). It
|
|
143
|
+
* stays in view when the label gives way, and follows the label in the
|
|
144
|
+
* accessible name. A count (a number, or a string of digits) is set in Space
|
|
145
|
+
* Mono, as machine-written text; any other value keeps the label's font.
|
|
146
|
+
*/
|
|
147
|
+
value?: ReactNode;
|
|
148
|
+
/** When its label gives way (the `label` step). */
|
|
149
|
+
labelOrder?: number;
|
|
150
|
+
/** When it moves into More (the `more` step). */
|
|
151
|
+
moreOrder?: number;
|
|
152
|
+
/** What it is in More's menu. */
|
|
153
|
+
moreItem?: ReactNode;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* A picker's trigger as one toolbar item (a filter: Assignee, Channel): a
|
|
157
|
+
* PickerTriggerButton with an icon, a label, what is chosen and a chevron,
|
|
158
|
+
* filled `info-soft` once a value is chosen, as an applied filter is
|
|
159
|
+
* (Select › Filters). From `labelOrder` on its label gives way, while its
|
|
160
|
+
* icon, what is chosen and the chevron stay ("# ⌄", "👤 1 ⌄"); its label
|
|
161
|
+
* stays its accessible name, read before the value ("Assignee 1"), and shows
|
|
162
|
+
* as its tooltip.
|
|
163
|
+
*/
|
|
164
|
+
export declare function FitToolbarPicker({ trigger, icon, children, value, labelOrder, moreOrder, moreItem, className, ...props }: FitToolbarPickerProps): import("react").JSX.Element;
|
|
165
|
+
//#endregion
|