@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
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import { laidOutChildren } from "../../lib/laid-out-children.mjs";
|
|
2
|
+
import { Button } from "../button/button.mjs";
|
|
3
|
+
import { useMergedRef } from "../../lib/use-merged-ref.mjs";
|
|
4
|
+
import { ScrollArea } from "../scroll-area/scroll-area.mjs";
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip.mjs";
|
|
6
|
+
import { PickerTriggerButton } from "../picker-trigger-button/picker-trigger-button.mjs";
|
|
7
|
+
import { fitToolbar } from "./fit-toolbar.recipe.mjs";
|
|
8
|
+
import { ChevronDown, Plus } from "lucide-react";
|
|
9
|
+
import { Fragment, cloneElement, createContext, useCallback, useContext, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
10
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
12
|
+
//#region src/components/fit-toolbar/fit-toolbar.tsx
|
|
13
|
+
const NONE = {
|
|
14
|
+
items: /* @__PURE__ */ new Map(),
|
|
15
|
+
more: [],
|
|
16
|
+
wrapping: false
|
|
17
|
+
};
|
|
18
|
+
const STEPS = [
|
|
19
|
+
"label",
|
|
20
|
+
"more",
|
|
21
|
+
"collapse",
|
|
22
|
+
"wrap",
|
|
23
|
+
"plus"
|
|
24
|
+
];
|
|
25
|
+
const ID = "data-fit-id";
|
|
26
|
+
const DECLARED = "data-fit-steps";
|
|
27
|
+
const MORE_TRIGGER = "data-fit-more-trigger";
|
|
28
|
+
const SHOWN = "data-fit-shown";
|
|
29
|
+
const WRAPPING = "data-fit-wrapping";
|
|
30
|
+
const ROW = "data-fit-row";
|
|
31
|
+
const NO_WRAP = "data-fit-first-line";
|
|
32
|
+
const WRAP_LINE = "data-fit-wrap-line";
|
|
33
|
+
const BREAK = "data-fit-break";
|
|
34
|
+
const NEEDS_ICON = "data-fit-needs-icon";
|
|
35
|
+
const stepAttribute = (step) => `data-fit-${step}`;
|
|
36
|
+
const hasIcon = (item) => item.querySelector("svg, img") !== null;
|
|
37
|
+
/** The group has a `wrap` step it may take: the toolbar wraps and the page lets it. */
|
|
38
|
+
function canWrap(group, allowWrap) {
|
|
39
|
+
return allowWrap && !group.hasAttribute(NO_WRAP) && / wrap:/.test(` ${group.getAttribute(DECLARED) ?? ""}`);
|
|
40
|
+
}
|
|
41
|
+
function declare(steps) {
|
|
42
|
+
return STEPS.filter((step) => steps[step] !== void 0).map((step) => `${step}:${steps[step]}`).join(" ");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Nothing pokes out of the toolbar or out of any of its rows, and no line has
|
|
46
|
+
* broken by itself: while groups wrap, each line (one `order`) stays one line.
|
|
47
|
+
*/
|
|
48
|
+
function fits(root, allowWrap) {
|
|
49
|
+
if (![root, ...root.querySelectorAll(`[${ROW}]`)].every((row) => {
|
|
50
|
+
if (row.getClientRects().length === 0) return true;
|
|
51
|
+
const style = getComputedStyle(row);
|
|
52
|
+
const end = row.getBoundingClientRect().right - parseFloat(style.borderRightWidth) - parseFloat(style.paddingRight);
|
|
53
|
+
return laidOutChildren(row).every((box) => box.getBoundingClientRect().right <= end + .5);
|
|
54
|
+
})) return false;
|
|
55
|
+
for (const group of root.querySelectorAll("[data-fit-scroll]")) {
|
|
56
|
+
if (group.getClientRects().length === 0) continue;
|
|
57
|
+
const content = group.querySelector("[data-slot=scroll-area-content]") ?? group;
|
|
58
|
+
const [first] = laidOutChildren(content);
|
|
59
|
+
if (first && group.clientWidth + .5 < first.getBoundingClientRect().width) return false;
|
|
60
|
+
if (canWrap(group, allowWrap) && !group.hasAttribute(stepAttribute("wrap"))) {
|
|
61
|
+
const scroller = group.querySelector("[data-fit-scroller]") ?? group;
|
|
62
|
+
if (scroller.scrollWidth > scroller.clientWidth + .5) return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!root.hasAttribute(WRAPPING)) return true;
|
|
66
|
+
const lines = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const child of laidOutChildren(root)) {
|
|
68
|
+
if (child.hasAttribute(BREAK)) continue;
|
|
69
|
+
const line = getComputedStyle(child).order;
|
|
70
|
+
const rect = child.getBoundingClientRect();
|
|
71
|
+
const first = lines.get(line);
|
|
72
|
+
if (first === void 0) lines.set(line, rect);
|
|
73
|
+
else if (rect.top >= first.bottom - .5 || rect.bottom <= first.top + .5) return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Takes the steps one at a time, in their order, until the toolbar fits its
|
|
79
|
+
* region; writes each on the DOM as it goes (React renders the same after)
|
|
80
|
+
* and returns them. It starts from nothing taken every time, so the outcome
|
|
81
|
+
* depends on the width alone.
|
|
82
|
+
*/
|
|
83
|
+
function measure(root, allowWrap) {
|
|
84
|
+
const elements = Array.from(root.querySelectorAll(`[${ID}]`));
|
|
85
|
+
const scrolled = Array.from(root.querySelectorAll("[data-fit-scroller]"), (scroller) => [scroller, scroller.scrollLeft]);
|
|
86
|
+
const triggers = Array.from(root.querySelectorAll(`[${MORE_TRIGGER}]`));
|
|
87
|
+
for (const element of elements) {
|
|
88
|
+
for (const step of STEPS) element.removeAttribute(stepAttribute(step));
|
|
89
|
+
element.style.removeProperty("order");
|
|
90
|
+
}
|
|
91
|
+
for (const trigger of triggers) trigger.removeAttribute(SHOWN);
|
|
92
|
+
root.removeAttribute(WRAPPING);
|
|
93
|
+
for (const line of Array.from(root.querySelectorAll(`:scope > [${BREAK}]`))) line.remove();
|
|
94
|
+
root.style.setProperty("--fit-gap", getComputedStyle(root).columnGap);
|
|
95
|
+
const queue = elements.flatMap((element, index) => (element.getAttribute(DECLARED) ?? "").split(" ").filter(Boolean).map((entry) => {
|
|
96
|
+
const [step, order] = entry.split(":");
|
|
97
|
+
return {
|
|
98
|
+
element,
|
|
99
|
+
step,
|
|
100
|
+
order: Number(order),
|
|
101
|
+
index
|
|
102
|
+
};
|
|
103
|
+
}).filter(({ step, element: item }) => {
|
|
104
|
+
if (step === "more") return triggers.length > 0;
|
|
105
|
+
if (step === "wrap") return canWrap(item, allowWrap);
|
|
106
|
+
if (step === "label") return !item.hasAttribute(NEEDS_ICON) || hasIcon(item);
|
|
107
|
+
return true;
|
|
108
|
+
}));
|
|
109
|
+
queue.sort((a, b) => a.order - b.order || b.index - a.index);
|
|
110
|
+
const batches = [];
|
|
111
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const { element, step, order } of queue) {
|
|
113
|
+
const group = step === "label" || step === "plus" ? element.parentElement?.closest("[data-slot=fit-toolbar-group]") : null;
|
|
114
|
+
const kind = group ? `${group.getAttribute(ID)}|${step}|${order}` : void 0;
|
|
115
|
+
const existing = kind === void 0 ? void 0 : byKind.get(kind);
|
|
116
|
+
if (existing) {
|
|
117
|
+
existing.members.push(element);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const batch = {
|
|
121
|
+
members: [element],
|
|
122
|
+
step
|
|
123
|
+
};
|
|
124
|
+
batches.push(batch);
|
|
125
|
+
if (kind !== void 0) byKind.set(kind, batch);
|
|
126
|
+
}
|
|
127
|
+
const taken = /* @__PURE__ */ new Map();
|
|
128
|
+
const history = [];
|
|
129
|
+
const showMore = () => {
|
|
130
|
+
const shown = Array.from(taken.values()).some((steps) => steps.has("more"));
|
|
131
|
+
for (const trigger of triggers) trigger.toggleAttribute(SHOWN, shown);
|
|
132
|
+
};
|
|
133
|
+
const take = (element, step) => {
|
|
134
|
+
const steps = taken.get(element) ?? /* @__PURE__ */ new Set();
|
|
135
|
+
steps.add(step);
|
|
136
|
+
taken.set(element, steps);
|
|
137
|
+
element.setAttribute(stepAttribute(step), "");
|
|
138
|
+
if (step === "more") showMore();
|
|
139
|
+
if (step === "wrap") layOutLines(root, elements);
|
|
140
|
+
};
|
|
141
|
+
const undo = (element, step) => {
|
|
142
|
+
taken.get(element)?.delete(step);
|
|
143
|
+
element.removeAttribute(stepAttribute(step));
|
|
144
|
+
if (step === "more") showMore();
|
|
145
|
+
};
|
|
146
|
+
for (const { members, step } of batches) {
|
|
147
|
+
if (fits(root, allowWrap)) break;
|
|
148
|
+
const giving = members.filter((element) => !taken.get(element)?.has("more"));
|
|
149
|
+
if (giving.length === 0) continue;
|
|
150
|
+
for (const element of giving) take(element, step);
|
|
151
|
+
history.push({
|
|
152
|
+
members: giving,
|
|
153
|
+
step
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
157
|
+
const { members, step } = history[index];
|
|
158
|
+
if (step === "wrap") continue;
|
|
159
|
+
const group = members[0].closest(`[${stepAttribute("wrap")}]`);
|
|
160
|
+
if (!group || !root.contains(group)) continue;
|
|
161
|
+
if (history.findIndex((entry) => entry.members.includes(group) && entry.step === "wrap") < index) continue;
|
|
162
|
+
for (const element of members) undo(element, step);
|
|
163
|
+
if (!fits(root, allowWrap)) for (const element of members) take(element, step);
|
|
164
|
+
}
|
|
165
|
+
const items = /* @__PURE__ */ new Map();
|
|
166
|
+
const more = [];
|
|
167
|
+
let wrapping = false;
|
|
168
|
+
for (const element of elements) {
|
|
169
|
+
const steps = taken.get(element);
|
|
170
|
+
if (!steps || steps.size === 0) continue;
|
|
171
|
+
const id = element.getAttribute(ID);
|
|
172
|
+
items.set(id, steps);
|
|
173
|
+
if (steps.has("more")) more.push(id);
|
|
174
|
+
if (steps.has("wrap")) wrapping = true;
|
|
175
|
+
}
|
|
176
|
+
for (const [scroller, left] of scrolled) scroller.scrollLeft = left;
|
|
177
|
+
return {
|
|
178
|
+
items,
|
|
179
|
+
more,
|
|
180
|
+
wrapping
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Puts the wrapped groups on the lines after the first: groups with the same
|
|
185
|
+
* `wrapLine` share a line, in their order; any other takes one of its own.
|
|
186
|
+
* A zero-height break before each line ends the one before it.
|
|
187
|
+
*/
|
|
188
|
+
function layOutLines(root, elements) {
|
|
189
|
+
root.setAttribute(WRAPPING, "");
|
|
190
|
+
for (const line of Array.from(root.querySelectorAll(`:scope > [${BREAK}]`))) line.remove();
|
|
191
|
+
const lines = [];
|
|
192
|
+
for (const group of elements) {
|
|
193
|
+
if (!group.hasAttribute(stepAttribute("wrap"))) continue;
|
|
194
|
+
const line = group.getAttribute(WRAP_LINE) ?? `#${group.getAttribute(ID)}`;
|
|
195
|
+
let number = lines.indexOf(line);
|
|
196
|
+
if (number < 0) {
|
|
197
|
+
number = lines.push(line) - 1;
|
|
198
|
+
const lineBreak = document.createElement("span");
|
|
199
|
+
lineBreak.setAttribute(BREAK, "");
|
|
200
|
+
lineBreak.setAttribute("aria-hidden", "true");
|
|
201
|
+
lineBreak.style.cssText = `flex-basis: 100%; height: 0; order: ${2 * number + 1}`;
|
|
202
|
+
root.append(lineBreak);
|
|
203
|
+
}
|
|
204
|
+
group.style.order = String(2 * number + 2);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** What the arrow keys stop on: every control a person can reach, a segment each. */
|
|
208
|
+
const STOP = [
|
|
209
|
+
"button",
|
|
210
|
+
"a[href]",
|
|
211
|
+
"input",
|
|
212
|
+
"select",
|
|
213
|
+
"textarea",
|
|
214
|
+
"[role=button]",
|
|
215
|
+
"[role=radio]",
|
|
216
|
+
"[role=checkbox]",
|
|
217
|
+
"[role=switch]",
|
|
218
|
+
"[role=combobox]"
|
|
219
|
+
].join(", ");
|
|
220
|
+
/** The line an element is on: the CSS `order` of the toolbar's flex item that holds it. */
|
|
221
|
+
function lineOf(element, root) {
|
|
222
|
+
const chain = [];
|
|
223
|
+
for (let node = element; node && node !== root; node = node.parentElement) chain.unshift(node);
|
|
224
|
+
const item = chain.find((node) => getComputedStyle(node).display !== "contents") ?? element;
|
|
225
|
+
return Number(getComputedStyle(item).order) || 0;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The toolbar's controls in reading order: line by line as they show, in the
|
|
229
|
+
* order of the DOM on each line. Wrapped groups move with CSS `order`, which
|
|
230
|
+
* neither the browser's focus order nor Base UI's list follows (WCAG 2.4.3).
|
|
231
|
+
* Leaves out what isn't shown (in More, a collapsed group's items), what is
|
|
232
|
+
* disabled natively, hidden from assistive technology, or a popup's focus guard.
|
|
233
|
+
*/
|
|
234
|
+
function readingOrder(root) {
|
|
235
|
+
const controls = Array.from(root.querySelectorAll(STOP)).filter((control) => !control.matches(":disabled") && control.closest("[aria-hidden=true]") === null && !control.hasAttribute("data-base-ui-focus-guard") && control.getClientRects().length > 0);
|
|
236
|
+
const lines = new Map(controls.map((control) => [control, lineOf(control, root)]));
|
|
237
|
+
return controls.sort((a, b) => lines.get(a) - lines.get(b));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A text field keeps the arrow keys while its caret has somewhere to go, as
|
|
241
|
+
* in Base UI's composite: only at its start or end does an arrow leave it.
|
|
242
|
+
*/
|
|
243
|
+
function keepsArrow(target, forward) {
|
|
244
|
+
if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return false;
|
|
245
|
+
let start;
|
|
246
|
+
try {
|
|
247
|
+
start = target.selectionStart;
|
|
248
|
+
} catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
if (start === null) return false;
|
|
252
|
+
if (start !== target.selectionEnd) return true;
|
|
253
|
+
return forward ? start < target.value.length : start > 0;
|
|
254
|
+
}
|
|
255
|
+
const keyOf = (applied) => `${applied.wrapping}|${Array.from(applied.items, ([id, steps]) => `${id}:${[...steps].join(",")}`).join(";")}`;
|
|
256
|
+
/**
|
|
257
|
+
* What each item shows in More while it is there, outside React state: the
|
|
258
|
+
* items write it on every render, and only `FitToolbarMoreItems` (in an open
|
|
259
|
+
* menu) reads it.
|
|
260
|
+
*/
|
|
261
|
+
function createMoreItemStore() {
|
|
262
|
+
const items = /* @__PURE__ */ new Map();
|
|
263
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
264
|
+
let version = 0;
|
|
265
|
+
const notify = () => {
|
|
266
|
+
version += 1;
|
|
267
|
+
for (const listener of listeners) listener();
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
set(id, item) {
|
|
271
|
+
items.set(id, item);
|
|
272
|
+
notify();
|
|
273
|
+
},
|
|
274
|
+
delete(id) {
|
|
275
|
+
if (items.delete(id)) notify();
|
|
276
|
+
},
|
|
277
|
+
get: (id) => items.get(id),
|
|
278
|
+
subscribe: (listener) => {
|
|
279
|
+
listeners.add(listener);
|
|
280
|
+
return () => {
|
|
281
|
+
listeners.delete(listener);
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
version: () => version
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const FitToolbarContext = createContext(null);
|
|
288
|
+
/**
|
|
289
|
+
* A toolbar whose items give way, in named steps and an order the items
|
|
290
|
+
* declare, as the region it sits in narrows: it measures its content in its
|
|
291
|
+
* own width, never the window's. The steps are written on each item as
|
|
292
|
+
* `data-fit-<step>` for its styles. A Base UI Toolbar: one Tab stop, the
|
|
293
|
+
* arrow keys inside.
|
|
294
|
+
*/
|
|
295
|
+
function FitToolbar({ className, wrap = false, ref, ...props }) {
|
|
296
|
+
const { root } = fitToolbar();
|
|
297
|
+
const rootRef = useRef(null);
|
|
298
|
+
const [element, setElement] = useState(null);
|
|
299
|
+
const [setRef] = useMergedRef(rootRef, setElement, ref);
|
|
300
|
+
const [applied, setApplied] = useState(NONE);
|
|
301
|
+
const [store] = useState(createMoreItemStore);
|
|
302
|
+
const update = useCallback(() => {
|
|
303
|
+
const current = rootRef.current;
|
|
304
|
+
if (!current) return;
|
|
305
|
+
const next = measure(current, wrap);
|
|
306
|
+
setApplied((previous) => keyOf(previous) === keyOf(next) ? previous : next);
|
|
307
|
+
}, [wrap]);
|
|
308
|
+
useLayoutEffect(update);
|
|
309
|
+
useLayoutEffect(() => {
|
|
310
|
+
if (!element) return;
|
|
311
|
+
const region = element.parentElement;
|
|
312
|
+
const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
|
|
313
|
+
if (region) resize?.observe(region);
|
|
314
|
+
const content = typeof MutationObserver === "undefined" ? null : new MutationObserver((records) => {
|
|
315
|
+
const own = (node) => node instanceof Element && node.hasAttribute(BREAK);
|
|
316
|
+
if (records.some((record) => record.type === "characterData" || ![...record.addedNodes, ...record.removedNodes].every(own))) update();
|
|
317
|
+
});
|
|
318
|
+
content?.observe(element, {
|
|
319
|
+
childList: true,
|
|
320
|
+
subtree: true,
|
|
321
|
+
characterData: true
|
|
322
|
+
});
|
|
323
|
+
const fonts = typeof document === "undefined" ? void 0 : document.fonts;
|
|
324
|
+
fonts?.addEventListener?.("loadingdone", update);
|
|
325
|
+
return () => {
|
|
326
|
+
resize?.disconnect();
|
|
327
|
+
content?.disconnect();
|
|
328
|
+
fonts?.removeEventListener?.("loadingdone", update);
|
|
329
|
+
};
|
|
330
|
+
}, [element, update]);
|
|
331
|
+
const tabbing = useRef(false);
|
|
332
|
+
const entered = useRef(false);
|
|
333
|
+
useLayoutEffect(() => {
|
|
334
|
+
const onKey = (event) => {
|
|
335
|
+
tabbing.current = event.key === "Tab";
|
|
336
|
+
};
|
|
337
|
+
const off = () => {
|
|
338
|
+
tabbing.current = false;
|
|
339
|
+
};
|
|
340
|
+
document.addEventListener("keydown", onKey, true);
|
|
341
|
+
document.addEventListener("keyup", off, true);
|
|
342
|
+
document.addEventListener("pointerdown", off, true);
|
|
343
|
+
return () => {
|
|
344
|
+
document.removeEventListener("keydown", onKey, true);
|
|
345
|
+
document.removeEventListener("keyup", off, true);
|
|
346
|
+
document.removeEventListener("pointerdown", off, true);
|
|
347
|
+
};
|
|
348
|
+
}, []);
|
|
349
|
+
const horizontal = (props.orientation ?? "horizontal") === "horizontal";
|
|
350
|
+
const { onKeyDownCapture, onFocus, loopFocus = true } = props;
|
|
351
|
+
const handleKeyDown = useCallback((event) => {
|
|
352
|
+
onKeyDownCapture?.(event);
|
|
353
|
+
const current = rootRef.current;
|
|
354
|
+
if (!current || !horizontal || event.defaultPrevented) return;
|
|
355
|
+
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
|
|
356
|
+
const target = event.target;
|
|
357
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
358
|
+
if (target.closest("[role=radiogroup]") && current.contains(target)) {
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
event.stopPropagation();
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
365
|
+
const rtl = getComputedStyle(current).direction === "rtl";
|
|
366
|
+
const forward = event.key === "ArrowRight" !== rtl;
|
|
367
|
+
if (keepsArrow(target, forward)) return;
|
|
368
|
+
const order = readingOrder(current);
|
|
369
|
+
const index = order.findIndex((control) => control === target || control.contains(target));
|
|
370
|
+
if (index < 0) return;
|
|
371
|
+
let next = index + (forward ? 1 : -1);
|
|
372
|
+
if (next < 0 || next >= order.length) {
|
|
373
|
+
if (!loopFocus) return;
|
|
374
|
+
next = (next + order.length) % order.length;
|
|
375
|
+
}
|
|
376
|
+
event.preventDefault();
|
|
377
|
+
event.stopPropagation();
|
|
378
|
+
if (next !== index) order[next].focus();
|
|
379
|
+
}, [
|
|
380
|
+
horizontal,
|
|
381
|
+
loopFocus,
|
|
382
|
+
onKeyDownCapture
|
|
383
|
+
]);
|
|
384
|
+
const handleFocus = useCallback((event) => {
|
|
385
|
+
onFocus?.(event);
|
|
386
|
+
const current = rootRef.current;
|
|
387
|
+
if (!current) return;
|
|
388
|
+
const from = event.relatedTarget;
|
|
389
|
+
if (!(from instanceof Node && current.contains(from)) && tabbing.current && !entered.current) {
|
|
390
|
+
entered.current = true;
|
|
391
|
+
const [first] = readingOrder(current);
|
|
392
|
+
if (first && first !== event.target) {
|
|
393
|
+
first.focus();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
entered.current = true;
|
|
398
|
+
}, [onFocus]);
|
|
399
|
+
const lastFocused = useRef(null);
|
|
400
|
+
useLayoutEffect(() => {
|
|
401
|
+
if (!element) return;
|
|
402
|
+
const onFocusIn = (event) => {
|
|
403
|
+
lastFocused.current = event.target;
|
|
404
|
+
};
|
|
405
|
+
const onFocusOut = (event) => {
|
|
406
|
+
if (event.target.getClientRects().length > 0) lastFocused.current = null;
|
|
407
|
+
};
|
|
408
|
+
element.addEventListener("focusin", onFocusIn);
|
|
409
|
+
element.addEventListener("focusout", onFocusOut);
|
|
410
|
+
return () => {
|
|
411
|
+
element.removeEventListener("focusin", onFocusIn);
|
|
412
|
+
element.removeEventListener("focusout", onFocusOut);
|
|
413
|
+
};
|
|
414
|
+
}, [element]);
|
|
415
|
+
useLayoutEffect(() => {
|
|
416
|
+
const current = rootRef.current;
|
|
417
|
+
const last = lastFocused.current;
|
|
418
|
+
if (!current || !last || last.getClientRects().length > 0) return;
|
|
419
|
+
const active = document.activeElement;
|
|
420
|
+
if (active !== last && active !== document.body && active !== null) return;
|
|
421
|
+
const more = Array.from(current.querySelectorAll(`[${MORE_TRIGGER}]`)).find((trigger) => trigger.getClientRects().length > 0);
|
|
422
|
+
lastFocused.current = null;
|
|
423
|
+
more?.focus();
|
|
424
|
+
}, [applied]);
|
|
425
|
+
const context = useMemo(() => ({
|
|
426
|
+
applied,
|
|
427
|
+
store
|
|
428
|
+
}), [applied, store]);
|
|
429
|
+
return /* @__PURE__ */ jsx(FitToolbarContext.Provider, {
|
|
430
|
+
value: context,
|
|
431
|
+
children: /* @__PURE__ */ jsx(Toolbar.Root, {
|
|
432
|
+
ref: setRef,
|
|
433
|
+
"data-slot": "fit-toolbar",
|
|
434
|
+
"data-fit-wrapping": applied.wrapping ? "" : void 0,
|
|
435
|
+
className: root({ className }),
|
|
436
|
+
...props,
|
|
437
|
+
onKeyDownCapture: handleKeyDown,
|
|
438
|
+
onFocus: handleFocus
|
|
439
|
+
})
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Inside a FitToolbar: a control that would measure its own fit (a
|
|
444
|
+
* SegmentedControl's `iconsOnlyBelow`) leaves it to the toolbar's steps.
|
|
445
|
+
*/
|
|
446
|
+
function useInFitToolbar() {
|
|
447
|
+
return useContext(FitToolbarContext) !== null;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Makes an element an item of the nearest FitToolbar: spread `props` on it.
|
|
451
|
+
* Says which steps it has taken, for what it renders (a tooltip once its
|
|
452
|
+
* label is gone, say).
|
|
453
|
+
*/
|
|
454
|
+
function useFitItem({ moreItem, ...steps } = {}) {
|
|
455
|
+
const id = useId();
|
|
456
|
+
const context = useContext(FitToolbarContext);
|
|
457
|
+
const taken = context?.applied.items.get(id);
|
|
458
|
+
const store = context?.store;
|
|
459
|
+
const declared = declare(steps);
|
|
460
|
+
useLayoutEffect(() => {
|
|
461
|
+
if (moreItem !== void 0) store?.set(id, moreItem);
|
|
462
|
+
});
|
|
463
|
+
useLayoutEffect(() => () => store?.delete(id), [store, id]);
|
|
464
|
+
const props = {
|
|
465
|
+
[ID]: id,
|
|
466
|
+
[DECLARED]: declared || void 0
|
|
467
|
+
};
|
|
468
|
+
for (const step of STEPS) props[stepAttribute(step)] = taken?.has(step) ? "" : void 0;
|
|
469
|
+
return {
|
|
470
|
+
id,
|
|
471
|
+
props,
|
|
472
|
+
/** Steps it has taken. */
|
|
473
|
+
taken: (step) => taken?.has(step) ?? false
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Makes an element the toolbar's More: spread `props` on it. It shows only
|
|
478
|
+
* while an item is in More (`data-fit-shown`); hide it otherwise.
|
|
479
|
+
*/
|
|
480
|
+
function useFitMore() {
|
|
481
|
+
const shown = (useContext(FitToolbarContext)?.applied.more.length ?? 0) > 0;
|
|
482
|
+
return {
|
|
483
|
+
shown,
|
|
484
|
+
props: {
|
|
485
|
+
[MORE_TRIGGER]: "",
|
|
486
|
+
[SHOWN]: shown ? "" : void 0
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
/** The items in More, in their order, each as its `moreItem`: put it in More's menu. */
|
|
491
|
+
function FitToolbarMoreItems() {
|
|
492
|
+
const context = useContext(FitToolbarContext);
|
|
493
|
+
const store = context?.store;
|
|
494
|
+
useSyncExternalStore(store?.subscribe ?? noSubscription, store?.version ?? noVersion, store?.version ?? noVersion);
|
|
495
|
+
if (!context) return null;
|
|
496
|
+
return context.applied.more.map((id) => /* @__PURE__ */ jsx(Fragment, { children: context.store.get(id) }, id));
|
|
497
|
+
}
|
|
498
|
+
const noSubscription = () => () => {};
|
|
499
|
+
const noVersion = () => 0;
|
|
500
|
+
/**
|
|
501
|
+
* A scrolling group: which side hides more (`data-overflow`, for
|
|
502
|
+
* `mask-fade-x`), a vertical wheel scrolling it, and its `data-active` items
|
|
503
|
+
* and the focused one kept in view, clear of the fade, with no animation.
|
|
504
|
+
* The same as slock web's FilterStrip, so the two behave alike.
|
|
505
|
+
*/
|
|
506
|
+
function useScrollGroup(group) {
|
|
507
|
+
useLayoutEffect(() => {
|
|
508
|
+
if (!group) return;
|
|
509
|
+
const edges = () => {
|
|
510
|
+
const max = group.scrollWidth - group.clientWidth;
|
|
511
|
+
const start = group.scrollLeft > 1;
|
|
512
|
+
const end = max > 1 && group.scrollLeft < max - 1;
|
|
513
|
+
const next = start && end ? "both" : start ? "start" : end ? "end" : null;
|
|
514
|
+
if (next) group.setAttribute("data-overflow", next);
|
|
515
|
+
else group.removeAttribute("data-overflow");
|
|
516
|
+
};
|
|
517
|
+
let placed = group.scrollLeft;
|
|
518
|
+
let scrolledByPerson = false;
|
|
519
|
+
const onScroll = () => {
|
|
520
|
+
if (Math.abs(group.scrollLeft - placed) > 1) scrolledByPerson = true;
|
|
521
|
+
edges();
|
|
522
|
+
};
|
|
523
|
+
const reveal = (item) => {
|
|
524
|
+
if (!(item instanceof HTMLElement) || !group.contains(item)) return;
|
|
525
|
+
const fade = parseFloat(getComputedStyle(group).getPropertyValue("--fade-edge")) || 24;
|
|
526
|
+
const start = item.getBoundingClientRect().left - group.getBoundingClientRect().left + group.scrollLeft;
|
|
527
|
+
const end = start + item.offsetWidth;
|
|
528
|
+
if (start - fade < group.scrollLeft) group.scrollLeft = Math.max(0, start - fade);
|
|
529
|
+
else if (end + fade > group.scrollLeft + group.clientWidth) group.scrollLeft = end + fade - group.clientWidth;
|
|
530
|
+
placed = group.scrollLeft;
|
|
531
|
+
edges();
|
|
532
|
+
};
|
|
533
|
+
let target = group.querySelector("[data-active]");
|
|
534
|
+
const revealActive = () => {
|
|
535
|
+
if (!target || !group.contains(target) || !target.hasAttribute("data-active")) target = group.querySelector("[data-active]");
|
|
536
|
+
reveal(target);
|
|
537
|
+
};
|
|
538
|
+
const onWheel = (event) => {
|
|
539
|
+
if (group.scrollWidth <= group.clientWidth) return;
|
|
540
|
+
if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
|
|
541
|
+
const before = group.scrollLeft;
|
|
542
|
+
group.scrollLeft += event.deltaY;
|
|
543
|
+
if (group.scrollLeft !== before) event.preventDefault();
|
|
544
|
+
};
|
|
545
|
+
const onFocus = (event) => reveal(event.target);
|
|
546
|
+
revealActive();
|
|
547
|
+
edges();
|
|
548
|
+
group.addEventListener("scroll", onScroll, { passive: true });
|
|
549
|
+
group.addEventListener("wheel", onWheel, { passive: false });
|
|
550
|
+
group.addEventListener("focusin", onFocus);
|
|
551
|
+
const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
552
|
+
if (!scrolledByPerson) revealActive();
|
|
553
|
+
edges();
|
|
554
|
+
});
|
|
555
|
+
resize?.observe(group);
|
|
556
|
+
const activated = (node) => node instanceof Element ? node.hasAttribute("data-active") ? node : node.querySelector("[data-active]") : null;
|
|
557
|
+
const changes = new MutationObserver((records) => {
|
|
558
|
+
const newly = records.flatMap((record) => record.type === "attributes" ? [record.target instanceof Element && record.target.hasAttribute("data-active") ? record.target : null] : Array.from(record.addedNodes, activated)).filter((node) => node !== null).at(-1);
|
|
559
|
+
if (newly) {
|
|
560
|
+
target = newly;
|
|
561
|
+
scrolledByPerson = false;
|
|
562
|
+
revealActive();
|
|
563
|
+
}
|
|
564
|
+
edges();
|
|
565
|
+
});
|
|
566
|
+
changes.observe(group, {
|
|
567
|
+
childList: true,
|
|
568
|
+
subtree: true,
|
|
569
|
+
characterData: true,
|
|
570
|
+
attributes: true,
|
|
571
|
+
attributeFilter: ["data-active"]
|
|
572
|
+
});
|
|
573
|
+
return () => {
|
|
574
|
+
group.removeEventListener("scroll", onScroll);
|
|
575
|
+
group.removeEventListener("wheel", onWheel);
|
|
576
|
+
group.removeEventListener("focusin", onFocus);
|
|
577
|
+
resize?.disconnect();
|
|
578
|
+
changes.disconnect();
|
|
579
|
+
};
|
|
580
|
+
}, [group]);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* A group of items in one row, a fixed gap apart. A toolbar wraps only
|
|
584
|
+
* between groups, the last first; groups with the same `wrapLine` share a
|
|
585
|
+
* line, any other takes one of its own.
|
|
586
|
+
*/
|
|
587
|
+
function FitToolbarGroup({ className, wrapOrder = 0, firstLine = false, wrapLine, collapseOrder, collapsed, overflow, children, ref, ...props }) {
|
|
588
|
+
const { group, full, short, scroller, scrollContent } = fitToolbar({ scroll: overflow === "scroll" });
|
|
589
|
+
const [scrollerElement, setScroller] = useState(null);
|
|
590
|
+
useScrollGroup(overflow === "scroll" ? scrollerElement : null);
|
|
591
|
+
const item = useFitItem({
|
|
592
|
+
wrap: wrapOrder,
|
|
593
|
+
collapse: collapsed === void 0 ? void 0 : collapseOrder ?? 0
|
|
594
|
+
});
|
|
595
|
+
return /* @__PURE__ */ jsx(Toolbar.Group, {
|
|
596
|
+
ref,
|
|
597
|
+
"data-slot": "fit-toolbar-group",
|
|
598
|
+
"data-fit-row": overflow === "scroll" ? void 0 : "",
|
|
599
|
+
"data-fit-scroll": overflow === "scroll" ? "" : void 0,
|
|
600
|
+
"data-fit-first-line": firstLine ? "" : void 0,
|
|
601
|
+
"data-fit-wrap-line": wrapLine,
|
|
602
|
+
className: group({ className }),
|
|
603
|
+
...item.props,
|
|
604
|
+
...props,
|
|
605
|
+
children: overflow === "scroll" ? /* @__PURE__ */ jsx(ScrollArea, {
|
|
606
|
+
ref: setScroller,
|
|
607
|
+
orientation: "horizontal",
|
|
608
|
+
overscroll: "contain",
|
|
609
|
+
size: "small",
|
|
610
|
+
trigger: "scroll",
|
|
611
|
+
tabIndex: -1,
|
|
612
|
+
"data-fit-scroller": "",
|
|
613
|
+
className: scroller(),
|
|
614
|
+
contentClassName: scrollContent(),
|
|
615
|
+
children
|
|
616
|
+
}) : collapsed === void 0 ? children : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
617
|
+
"data-slot": "fit-toolbar-group-items",
|
|
618
|
+
className: full(),
|
|
619
|
+
children
|
|
620
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
621
|
+
"data-slot": "fit-toolbar-group-collapsed",
|
|
622
|
+
className: short(),
|
|
623
|
+
children: collapsed
|
|
624
|
+
})] })
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* A toolbar button that can give way: an `md` Button (ghost unless you say)
|
|
629
|
+
* with an icon and a label, an icon square with its label as a tooltip from
|
|
630
|
+
* `labelOrder` on, a + square with its label as a tooltip from `plusOrder`
|
|
631
|
+
* on, and hidden from `moreOrder` on.
|
|
632
|
+
*/
|
|
633
|
+
function FitToolbarButton({ icon, collapsedIcon, children, labelOrder, plusOrder, moreOrder, moreItem, variant = "ghost", className, ...props }) {
|
|
634
|
+
const { button, label, icon: iconSlot, plus, fullIcon, collapsedIcon: collapsedSlot } = fitToolbar();
|
|
635
|
+
const item = useFitItem({
|
|
636
|
+
label: labelOrder,
|
|
637
|
+
more: moreOrder,
|
|
638
|
+
plus: plusOrder,
|
|
639
|
+
moreItem
|
|
640
|
+
});
|
|
641
|
+
const icons = collapsedIcon === void 0 ? icon : /* @__PURE__ */ jsxs(Fragment$1, { children: [icon === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
642
|
+
"data-slot": "fit-toolbar-button-full-icon",
|
|
643
|
+
className: fullIcon(),
|
|
644
|
+
children: icon
|
|
645
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
646
|
+
"data-slot": "fit-toolbar-button-collapsed-icon",
|
|
647
|
+
className: collapsedSlot(),
|
|
648
|
+
children: collapsedIcon
|
|
649
|
+
})] });
|
|
650
|
+
return /* @__PURE__ */ jsxs(Tooltip, {
|
|
651
|
+
disabled: !item.taken("label") && !item.taken("plus"),
|
|
652
|
+
children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
|
|
653
|
+
"data-slot": "button",
|
|
654
|
+
render: /* @__PURE__ */ jsx(Toolbar.Button, { render: /* @__PURE__ */ jsx(Button, {
|
|
655
|
+
variant,
|
|
656
|
+
size: "md",
|
|
657
|
+
className: button({ className }),
|
|
658
|
+
"data-fit-needs-icon": "",
|
|
659
|
+
...item.props,
|
|
660
|
+
...props
|
|
661
|
+
}) }),
|
|
662
|
+
children: [plusOrder === void 0 ? icons : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
663
|
+
"data-slot": "fit-toolbar-button-icon",
|
|
664
|
+
className: iconSlot(),
|
|
665
|
+
children: icons
|
|
666
|
+
}), /* @__PURE__ */ jsx(Plus, {
|
|
667
|
+
"data-slot": "fit-toolbar-button-plus",
|
|
668
|
+
"aria-hidden": true,
|
|
669
|
+
className: plus()
|
|
670
|
+
})] }), /* @__PURE__ */ jsx("span", {
|
|
671
|
+
"data-slot": "fit-toolbar-button-label",
|
|
672
|
+
className: label(),
|
|
673
|
+
children
|
|
674
|
+
})]
|
|
675
|
+
}), /* @__PURE__ */ jsx(TooltipContent, { children })]
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
/** A count: a number, or a string of digits. Space Mono is for machine-written text only. */
|
|
679
|
+
const isCount = (node) => typeof node === "number" || typeof node === "string" && /^\d+$/.test(node.trim());
|
|
680
|
+
const text = (node) => typeof node === "string" || typeof node === "number" ? String(node) : void 0;
|
|
681
|
+
/**
|
|
682
|
+
* A picker's trigger as one toolbar item (a filter: Assignee, Channel): a
|
|
683
|
+
* PickerTriggerButton with an icon, a label, what is chosen and a chevron,
|
|
684
|
+
* filled `info-soft` once a value is chosen, as an applied filter is
|
|
685
|
+
* (Select › Filters). From `labelOrder` on its label gives way, while its
|
|
686
|
+
* icon, what is chosen and the chevron stay ("# ⌄", "👤 1 ⌄"); its label
|
|
687
|
+
* stays its accessible name, read before the value ("Assignee 1"), and shows
|
|
688
|
+
* as its tooltip.
|
|
689
|
+
*/
|
|
690
|
+
function FitToolbarPicker({ trigger, icon, children, value, labelOrder, moreOrder, moreItem, className, ...props }) {
|
|
691
|
+
const { picker, label, pickerValue, chevron } = fitToolbar({ count: isCount(value) });
|
|
692
|
+
const item = useFitItem({
|
|
693
|
+
label: labelOrder,
|
|
694
|
+
more: moreOrder,
|
|
695
|
+
moreItem
|
|
696
|
+
});
|
|
697
|
+
const hasValue = value !== void 0 && value !== null && value !== false && value !== "";
|
|
698
|
+
const name = text(children) !== void 0 && (!hasValue || text(value) !== void 0) ? [text(children), hasValue ? text(value) : void 0].filter(Boolean).join(" ") : void 0;
|
|
699
|
+
const ownTrigger = cloneElement(trigger, { render: /* @__PURE__ */ jsx(PickerTriggerButton, {
|
|
700
|
+
className: picker({ className }),
|
|
701
|
+
"aria-label": name,
|
|
702
|
+
"data-has-value": hasValue ? "" : void 0,
|
|
703
|
+
"data-fit-needs-icon": "",
|
|
704
|
+
...item.props,
|
|
705
|
+
...props
|
|
706
|
+
}) });
|
|
707
|
+
return /* @__PURE__ */ jsxs(Tooltip, {
|
|
708
|
+
disabled: !item.taken("label"),
|
|
709
|
+
children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
|
|
710
|
+
"data-slot": "button",
|
|
711
|
+
render: /* @__PURE__ */ jsx(Toolbar.Button, { render: ownTrigger }),
|
|
712
|
+
children: [
|
|
713
|
+
icon,
|
|
714
|
+
/* @__PURE__ */ jsxs("span", {
|
|
715
|
+
"data-slot": "fit-toolbar-picker-label",
|
|
716
|
+
className: label(),
|
|
717
|
+
children: [children, hasValue && name === void 0 ? " " : null]
|
|
718
|
+
}),
|
|
719
|
+
hasValue ? /* @__PURE__ */ jsx("span", {
|
|
720
|
+
"data-slot": "fit-toolbar-picker-value",
|
|
721
|
+
className: pickerValue(),
|
|
722
|
+
children: value
|
|
723
|
+
}) : null,
|
|
724
|
+
/* @__PURE__ */ jsx("span", {
|
|
725
|
+
"data-slot": "fit-toolbar-picker-chevron",
|
|
726
|
+
"data-picker-trigger-indicator": "",
|
|
727
|
+
"aria-hidden": true,
|
|
728
|
+
className: chevron(),
|
|
729
|
+
children: /* @__PURE__ */ jsx(ChevronDown, {})
|
|
730
|
+
})
|
|
731
|
+
]
|
|
732
|
+
}), /* @__PURE__ */ jsx(TooltipContent, { children })]
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
//#endregion
|
|
736
|
+
export { FitToolbar, FitToolbarButton, FitToolbarGroup, FitToolbarMoreItems, FitToolbarPicker, useFitItem, useFitMore, useInFitToolbar };
|