@tulipfarm/sprout 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +183 -2
- package/dist/components/accordion.d.ts +28 -0
- package/dist/components/accordion.js +26 -0
- package/dist/components/alert-dialog.d.ts +60 -0
- package/dist/components/alert-dialog.js +69 -0
- package/dist/components/alert.d.ts +30 -0
- package/dist/components/alert.js +35 -0
- package/dist/components/aspect-ratio.d.ts +7 -0
- package/dist/components/aspect-ratio.js +6 -0
- package/dist/components/attachment.d.ts +118 -0
- package/dist/components/attachment.js +285 -0
- package/dist/components/avatar.d.ts +32 -0
- package/dist/components/avatar.js +73 -0
- package/dist/components/badge.d.ts +21 -0
- package/dist/components/badge.js +27 -0
- package/dist/components/brand-icon.d.ts +28 -0
- package/dist/components/brand-icon.js +82 -0
- package/dist/components/breadcrumb.d.ts +29 -0
- package/dist/components/breadcrumb.js +44 -0
- package/dist/components/button-group.d.ts +28 -0
- package/dist/components/button-group.js +36 -0
- package/dist/components/button.d.ts +79 -0
- package/dist/components/button.js +93 -0
- package/dist/components/calendar.d.ts +27 -0
- package/dist/components/calendar.js +107 -0
- package/dist/components/canvas.d.ts +78 -0
- package/dist/components/canvas.js +180 -0
- package/dist/components/card.d.ts +43 -0
- package/dist/components/card.js +42 -0
- package/dist/components/carousel.d.ts +57 -0
- package/dist/components/carousel.js +422 -0
- package/dist/components/chart.d.ts +112 -0
- package/dist/components/chart.js +172 -0
- package/dist/components/chat-confirmation.d.ts +42 -0
- package/dist/components/chat-confirmation.js +62 -0
- package/dist/components/chat-input.d.ts +94 -0
- package/dist/components/chat-input.js +279 -0
- package/dist/components/chat-plan.d.ts +21 -0
- package/dist/components/chat-plan.js +35 -0
- package/dist/components/chat-sources.d.ts +64 -0
- package/dist/components/chat-sources.js +66 -0
- package/dist/components/checkbox.d.ts +16 -0
- package/dist/components/checkbox.js +17 -0
- package/dist/components/choice-list.d.ts +53 -0
- package/dist/components/choice-list.js +36 -0
- package/dist/components/code-block.d.ts +35 -0
- package/dist/components/code-block.js +64 -0
- package/dist/components/collapsible.d.ts +24 -0
- package/dist/components/collapsible.js +27 -0
- package/dist/components/combobox.d.ts +95 -0
- package/dist/components/combobox.js +106 -0
- package/dist/components/command.d.ts +94 -0
- package/dist/components/command.js +414 -0
- package/dist/components/context-menu.d.ts +57 -0
- package/dist/components/context-menu.js +60 -0
- package/dist/components/dialog.d.ts +60 -0
- package/dist/components/dialog.js +79 -0
- package/dist/components/direction.d.ts +6 -0
- package/dist/components/direction.js +6 -0
- package/dist/components/dropdown-menu.d.ts +62 -0
- package/dist/components/dropdown-menu.js +59 -0
- package/dist/components/field.d.ts +117 -0
- package/dist/components/field.js +150 -0
- package/dist/components/file-explorer.d.ts +27 -0
- package/dist/components/file-explorer.js +300 -0
- package/dist/components/file-viewer.d.ts +137 -0
- package/dist/components/file-viewer.js +400 -0
- package/dist/components/form.d.ts +87 -0
- package/dist/components/form.js +55 -0
- package/dist/components/glass-banner.d.ts +27 -0
- package/dist/components/glass-banner.js +116 -0
- package/dist/components/gradient-banner.d.ts +37 -0
- package/dist/components/gradient-banner.js +43 -0
- package/dist/components/hover-card.d.ts +27 -0
- package/dist/components/hover-card.js +26 -0
- package/dist/components/input-group.d.ts +68 -0
- package/dist/components/input-group.js +88 -0
- package/dist/components/input-otp.d.ts +38 -0
- package/dist/components/input-otp.js +43 -0
- package/dist/components/input.d.ts +16 -0
- package/dist/components/input.js +18 -0
- package/dist/components/kbd.d.ts +12 -0
- package/dist/components/kbd.js +11 -0
- package/dist/components/label.d.ts +8 -0
- package/dist/components/label.js +10 -0
- package/dist/components/mention-list.d.ts +54 -0
- package/dist/components/mention-list.js +31 -0
- package/dist/components/menubar.d.ts +64 -0
- package/dist/components/menubar.js +68 -0
- package/dist/components/message-actions.d.ts +34 -0
- package/dist/components/message-actions.js +68 -0
- package/dist/components/message-response.d.ts +20 -0
- package/dist/components/message-response.js +46 -0
- package/dist/components/message-scroller.d.ts +71 -0
- package/dist/components/message-scroller.js +217 -0
- package/dist/components/message.d.ts +49 -0
- package/dist/components/message.js +59 -0
- package/dist/components/navigation-menu.d.ts +45 -0
- package/dist/components/navigation-menu.js +54 -0
- package/dist/components/notice-banner.d.ts +14 -0
- package/dist/components/notice-banner.js +11 -0
- package/dist/components/page-content.d.ts +7 -0
- package/dist/components/page-content.js +9 -0
- package/dist/components/page-header.d.ts +37 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/pagination.d.ts +29 -0
- package/dist/components/pagination.js +29 -0
- package/dist/components/plan-outline.d.ts +60 -0
- package/dist/components/plan-outline.js +108 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +35 -0
- package/dist/components/progress.d.ts +27 -0
- package/dist/components/progress.js +35 -0
- package/dist/components/prompt-list.d.ts +23 -0
- package/dist/components/prompt-list.js +11 -0
- package/dist/components/questionnaire.d.ts +148 -0
- package/dist/components/questionnaire.js +1002 -0
- package/dist/components/radio-group.d.ts +16 -0
- package/dist/components/radio-group.js +21 -0
- package/dist/components/reasoning-effort.d.ts +24 -0
- package/dist/components/reasoning-effort.js +269 -0
- package/dist/components/resizable.d.ts +67 -0
- package/dist/components/resizable.js +536 -0
- package/dist/components/result-card.d.ts +58 -0
- package/dist/components/result-card.js +88 -0
- package/dist/components/review-card.d.ts +62 -0
- package/dist/components/review-card.js +49 -0
- package/dist/components/rich-text-editor.d.ts +51 -0
- package/dist/components/rich-text-editor.js +248 -0
- package/dist/components/schema-diagram.d.ts +52 -0
- package/dist/components/schema-diagram.js +226 -0
- package/dist/components/scroll-area.d.ts +22 -0
- package/dist/components/scroll-area.js +19 -0
- package/dist/components/search-dialog.d.ts +103 -0
- package/dist/components/search-dialog.js +65 -0
- package/dist/components/search-field.d.ts +17 -0
- package/dist/components/search-field.js +27 -0
- package/dist/components/select.d.ts +57 -0
- package/dist/components/select.js +57 -0
- package/dist/components/selection-slot.d.ts +18 -0
- package/dist/components/selection-slot.js +53 -0
- package/dist/components/separator.d.ts +48 -0
- package/dist/components/separator.js +54 -0
- package/dist/components/settings-list.d.ts +26 -0
- package/dist/components/settings-list.js +17 -0
- package/dist/components/sheet.d.ts +70 -0
- package/dist/components/sheet.js +77 -0
- package/dist/components/sidebar.d.ts +70 -0
- package/dist/components/sidebar.js +187 -0
- package/dist/components/skeleton.d.ts +31 -0
- package/dist/components/skeleton.js +32 -0
- package/dist/components/slider.d.ts +17 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/spinner.d.ts +37 -0
- package/dist/components/spinner.js +43 -0
- package/dist/components/state-panel-samples.d.ts +25 -0
- package/dist/components/state-panel-samples.js +423 -0
- package/dist/components/state-panel.d.ts +35 -0
- package/dist/components/state-panel.js +29 -0
- package/dist/components/state-sample.d.ts +63 -0
- package/dist/components/state-sample.js +42 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.js +14 -0
- package/dist/components/table-nuqs.d.ts +16 -0
- package/dist/components/table-nuqs.js +59 -0
- package/dist/components/table.d.ts +120 -0
- package/dist/components/table.js +663 -0
- package/dist/components/tabs.d.ts +37 -0
- package/dist/components/tabs.js +36 -0
- package/dist/components/textarea.d.ts +18 -0
- package/dist/components/textarea.js +19 -0
- package/dist/components/toast.d.ts +45 -0
- package/dist/components/toast.js +83 -0
- package/dist/components/toggle-group.d.ts +30 -0
- package/dist/components/toggle-group.js +39 -0
- package/dist/components/toggle.d.ts +27 -0
- package/dist/components/toggle.js +21 -0
- package/dist/components/tool-activity.d.ts +79 -0
- package/dist/components/tool-activity.js +226 -0
- package/dist/components/toolbar-status.d.ts +21 -0
- package/dist/components/toolbar-status.js +14 -0
- package/dist/components/tooltip.d.ts +31 -0
- package/dist/components/tooltip.js +99 -0
- package/dist/components/typed-text.d.ts +17 -0
- package/dist/components/typed-text.js +56 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +90 -0
- package/dist/internal/brand-glyphs.d.ts +370 -0
- package/dist/internal/brand-glyphs.js +262 -0
- package/dist/internal/classes.d.ts +3 -0
- package/dist/internal/classes.js +20 -0
- package/dist/internal/code-tokens.d.ts +4 -0
- package/dist/internal/code-tokens.js +34 -0
- package/dist/internal/fieldset.d.ts +6 -0
- package/dist/internal/fieldset.js +38 -0
- package/dist/internal/file-kind.d.ts +4 -0
- package/dist/internal/file-kind.js +62 -0
- package/dist/internal/languages.d.ts +7 -0
- package/dist/internal/languages.js +1010 -0
- package/dist/internal/message-scroller.d.ts +102 -0
- package/dist/internal/message-scroller.js +637 -0
- package/dist/internal/pressable.d.ts +2 -0
- package/dist/internal/pressable.js +29 -0
- package/dist/internal/syntax.d.ts +14 -0
- package/dist/internal/syntax.js +137 -0
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/alert-dialog.css +198 -0
- package/dist/styles/alert.css +149 -0
- package/dist/styles/aspect-ratio.css +8 -0
- package/dist/styles/attachment.css +700 -0
- package/dist/styles/avatar.css +186 -0
- package/dist/styles/badge.css +178 -0
- package/dist/styles/brand-icon.css +104 -0
- package/dist/styles/breadcrumb.css +121 -0
- package/dist/styles/button-group.css +113 -0
- package/dist/styles/button.css +380 -0
- package/dist/styles/calendar.css +295 -0
- package/dist/styles/canvas.css +313 -0
- package/dist/styles/card.css +146 -0
- package/dist/styles/carousel.css +107 -0
- package/dist/styles/chart.css +227 -0
- package/dist/styles/chat-confirmation.css +29 -0
- package/dist/styles/chat-input.css +240 -0
- package/dist/styles/chat-plan.css +109 -0
- package/dist/styles/chat-sources.css +191 -0
- package/dist/styles/checkbox.css +122 -0
- package/dist/styles/choice-list.css +343 -0
- package/dist/styles/code-block.css +211 -0
- package/dist/styles/collapsible.css +72 -0
- package/dist/styles/combobox.css +347 -0
- package/dist/styles/command.css +233 -0
- package/dist/styles/context-menu.css +151 -0
- package/dist/styles/dialog.css +237 -0
- package/dist/styles/dropdown-menu.css +150 -0
- package/dist/styles/field.css +340 -0
- package/dist/styles/file-explorer.css +517 -0
- package/dist/styles/file-viewer.css +375 -0
- package/dist/styles/form.css +155 -0
- package/dist/styles/glass-banner.css +308 -0
- package/dist/styles/gradient-banner.css +256 -0
- package/dist/styles/hover-card.css +95 -0
- package/dist/styles/index.css +268 -0
- package/dist/styles/input-group.css +285 -0
- package/dist/styles/input-otp.css +138 -0
- package/dist/styles/input.css +98 -0
- package/dist/styles/kbd.css +59 -0
- package/dist/styles/label.css +89 -0
- package/dist/styles/mention-list.css +166 -0
- package/dist/styles/menubar.css +226 -0
- package/dist/styles/message-actions.css +83 -0
- package/dist/styles/message-response.css +176 -0
- package/dist/styles/message-scroller.css +136 -0
- package/dist/styles/message.css +310 -0
- package/dist/styles/navigation-menu.css +291 -0
- package/dist/styles/notice-banner.css +68 -0
- package/dist/styles/page-content.css +21 -0
- package/dist/styles/page-header.css +156 -0
- package/dist/styles/pagination.css +72 -0
- package/dist/styles/plan-outline.css +393 -0
- package/dist/styles/popover.css +140 -0
- package/dist/styles/progress.css +93 -0
- package/dist/styles/prompt-list.css +165 -0
- package/dist/styles/questionnaire.css +308 -0
- package/dist/styles/radio-group.css +119 -0
- package/dist/styles/reasoning-effort.css +157 -0
- package/dist/styles/resizable.css +108 -0
- package/dist/styles/result-card.css +215 -0
- package/dist/styles/review-card.css +235 -0
- package/dist/styles/rich-text-editor.css +169 -0
- package/dist/styles/schema-diagram.css +419 -0
- package/dist/styles/scroll-area.css +77 -0
- package/dist/styles/search-dialog.css +280 -0
- package/dist/styles/search-field.css +17 -0
- package/dist/styles/select.css +285 -0
- package/dist/styles/selection-slot.css +31 -0
- package/dist/styles/separator.css +206 -0
- package/dist/styles/settings-list.css +81 -0
- package/dist/styles/sheet.css +361 -0
- package/dist/styles/sidebar.css +363 -0
- package/dist/styles/skeleton.css +80 -0
- package/dist/styles/slider.css +142 -0
- package/dist/styles/spinner.css +39 -0
- package/dist/styles/state-panel-samples.css +41 -0
- package/dist/styles/state-panel.css +173 -0
- package/dist/styles/state-sample.css +285 -0
- package/dist/styles/switch.css +128 -0
- package/dist/styles/table.css +604 -0
- package/dist/styles/tabs.css +223 -0
- package/dist/styles/textarea.css +77 -0
- package/dist/styles/themes/chatgpt.css +125 -0
- package/dist/styles/themes/claude.css +125 -0
- package/dist/styles/themes/cloudflare.css +125 -0
- package/dist/styles/themes/discord.css +125 -0
- package/dist/styles/themes/github.css +125 -0
- package/dist/styles/themes/grok.css +125 -0
- package/dist/styles/themes/linear.css +126 -0
- package/dist/styles/themes/neutral.css +125 -0
- package/dist/styles/themes/notion.css +125 -0
- package/dist/styles/themes/perplexity.css +125 -0
- package/dist/styles/themes/raycast.css +125 -0
- package/dist/styles/themes/spotify.css +125 -0
- package/dist/styles/themes/stripe.css +125 -0
- package/dist/styles/themes/supabase.css +125 -0
- package/dist/styles/themes/tulipfarm.css +115 -0
- package/dist/styles/themes/vercel.css +126 -0
- package/dist/styles/toast.css +197 -0
- package/dist/styles/toggle-group.css +161 -0
- package/dist/styles/toggle.css +119 -0
- package/dist/styles/tool-activity.css +396 -0
- package/dist/styles/toolbar-status.css +52 -0
- package/dist/styles/tooltip.css +122 -0
- package/dist/styles/typed-text.css +17 -0
- package/package.json +452 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useLayoutEffect, useRef, } from "react";
|
|
4
|
+
import { cn } from "../internal/classes.js";
|
|
5
|
+
const tabbable = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), [contenteditable="true"]';
|
|
6
|
+
function focusable(root) {
|
|
7
|
+
return Array.from(root?.querySelectorAll(tabbable) ?? []).find((node) => !node.closest("[inert]") && node.getClientRects().length > 0);
|
|
8
|
+
}
|
|
9
|
+
function lost() {
|
|
10
|
+
const current = document.activeElement;
|
|
11
|
+
return !current || current === document.body;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Swaps a toolbar for a selection toolbar in the same place. Both share one
|
|
15
|
+
* grid cell, so the row keeps the height of the taller one and nothing below
|
|
16
|
+
* it moves. The hidden tools stay mounted, so their state survives, but are
|
|
17
|
+
* inert. Focus moves into the selection when it came from the hidden tools,
|
|
18
|
+
* and returns to where it was when the selection closes. If that element is
|
|
19
|
+
* gone, focus that was in the selection goes to the first tool.
|
|
20
|
+
*/
|
|
21
|
+
export function SelectionSlot({ active, selection, children, className, ...props }) {
|
|
22
|
+
const tools = useRef(null);
|
|
23
|
+
const selected = useRef(null);
|
|
24
|
+
const origin = useRef(null);
|
|
25
|
+
const previous = useRef(false);
|
|
26
|
+
const inside = useRef(false);
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
if (active && !previous.current) {
|
|
29
|
+
const current = document.activeElement;
|
|
30
|
+
origin.current =
|
|
31
|
+
current instanceof HTMLElement && current !== document.body
|
|
32
|
+
? current
|
|
33
|
+
: null;
|
|
34
|
+
if (origin.current && tools.current?.contains(origin.current))
|
|
35
|
+
focusable(selected.current)?.focus({ preventScroll: true });
|
|
36
|
+
}
|
|
37
|
+
if (!active && previous.current && lost()) {
|
|
38
|
+
if (origin.current?.isConnected)
|
|
39
|
+
origin.current.focus({ preventScroll: true });
|
|
40
|
+
else if (inside.current)
|
|
41
|
+
focusable(tools.current)?.focus({ preventScroll: true });
|
|
42
|
+
}
|
|
43
|
+
if (!active)
|
|
44
|
+
inside.current = false;
|
|
45
|
+
previous.current = active;
|
|
46
|
+
}, [active]);
|
|
47
|
+
return (_jsxs("div", { "data-slot": "selection-slot", "data-active": active || undefined, className: cn("sprout-selection-slot", className), ...props, children: [_jsx("div", { ref: tools, "data-slot": "selection-slot-default", className: "sprout-selection-slot-layer", inert: active, "aria-hidden": active || undefined, children: children }), active && (_jsx("div", { ref: selected, onFocus: () => (inside.current = true), onBlur: (event) => {
|
|
48
|
+
// A null target means focus was dropped, as when the focused button is disabled.
|
|
49
|
+
const next = event.relatedTarget;
|
|
50
|
+
if (next && !event.currentTarget.contains(next))
|
|
51
|
+
inside.current = false;
|
|
52
|
+
}, "data-slot": "selection-slot-selection", className: "sprout-selection-slot-layer sprout-selection-slot-selection", children: selection }))] }));
|
|
53
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
import { Separator as BaseSeparator } from "@base-ui/react/separator";
|
|
3
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
4
|
+
export declare const separatorVariantNames: readonly ["line", "fade", "marker", "marker-border"];
|
|
5
|
+
export declare const separatorOrientationNames: readonly ["horizontal", "vertical"];
|
|
6
|
+
export declare const separatorToneNames: readonly ["default", "input"];
|
|
7
|
+
export type SeparatorVariant = (typeof separatorVariantNames)[number];
|
|
8
|
+
export type SeparatorOrientation = (typeof separatorOrientationNames)[number];
|
|
9
|
+
export type SeparatorTone = (typeof separatorToneNames)[number];
|
|
10
|
+
/** A bare 1px rule, horizontal or vertical. */
|
|
11
|
+
export interface SeparatorLineProps extends Omit<BaseSeparator.Props, "children"> {
|
|
12
|
+
variant?: "line" | undefined;
|
|
13
|
+
/** `"input"` matches control borders, for dividers inside grouped buttons. */
|
|
14
|
+
tone?: SeparatorTone | undefined;
|
|
15
|
+
children?: undefined;
|
|
16
|
+
}
|
|
17
|
+
/** A horizontal rule with centered content, such as "or". */
|
|
18
|
+
export interface SeparatorLabelProps extends Omit<ComponentProps<"div">, "children"> {
|
|
19
|
+
variant?: "line" | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Short content centered on the line. Content that renders nothing, such
|
|
22
|
+
* as `false`, leaves a spaced plain line.
|
|
23
|
+
*/
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/** A horizontal rule with faded ends and a centered text label. */
|
|
27
|
+
export interface SeparatorFadeProps extends Omit<BaseSeparator.Props, "children" | "orientation"> {
|
|
28
|
+
variant: "fade";
|
|
29
|
+
/** The label. It also names the separator for assistive technology. */
|
|
30
|
+
children: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SeparatorMarkerState extends Record<string, unknown> {
|
|
33
|
+
slot: "separator";
|
|
34
|
+
variant: "marker" | "marker-border";
|
|
35
|
+
}
|
|
36
|
+
/** A quiet, full-width timeline note, such as "Used skill" or "Today". */
|
|
37
|
+
export interface SeparatorMarkerProps extends useRender.ComponentProps<"div", SeparatorMarkerState> {
|
|
38
|
+
/** `"marker-border"` adds a line under the row. */
|
|
39
|
+
variant: "marker" | "marker-border";
|
|
40
|
+
}
|
|
41
|
+
export type SeparatorProps = SeparatorLineProps | SeparatorLabelProps | SeparatorFadeProps | SeparatorMarkerProps;
|
|
42
|
+
export declare function Separator(props: SeparatorProps): import("react").JSX.Element;
|
|
43
|
+
export type SeparatorIconProps = ComponentProps<"span">;
|
|
44
|
+
/** A 16px decorative icon slot. It is hidden from assistive technology. */
|
|
45
|
+
export declare function SeparatorIcon({ className, ...props }: SeparatorIconProps): import("react").JSX.Element;
|
|
46
|
+
export type SeparatorContentProps = ComponentProps<"span">;
|
|
47
|
+
/** Marker text. Long words wrap instead of overflowing. */
|
|
48
|
+
export declare function SeparatorContent({ className, ...props }: SeparatorContentProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
4
|
+
import { Separator as BaseSeparator } from "@base-ui/react/separator";
|
|
5
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
6
|
+
import { cn, stateClassName } from "../internal/classes.js";
|
|
7
|
+
export const separatorVariantNames = [
|
|
8
|
+
"line",
|
|
9
|
+
"fade",
|
|
10
|
+
"marker",
|
|
11
|
+
"marker-border",
|
|
12
|
+
];
|
|
13
|
+
export const separatorOrientationNames = ["horizontal", "vertical"];
|
|
14
|
+
export const separatorToneNames = ["default", "input"];
|
|
15
|
+
export function Separator(props) {
|
|
16
|
+
if (props.variant === "fade")
|
|
17
|
+
return _jsx(Fade, { ...props });
|
|
18
|
+
if (props.variant === "marker" || props.variant === "marker-border")
|
|
19
|
+
return _jsx(Marker, { ...props });
|
|
20
|
+
if (props.children !== undefined)
|
|
21
|
+
return _jsx(Labeled, { ...props });
|
|
22
|
+
return _jsx(Line, { ...props });
|
|
23
|
+
}
|
|
24
|
+
function Line({ className, orientation = "horizontal", tone = "default", variant: _variant, ...props }) {
|
|
25
|
+
return (_jsx(BaseSeparator, { "data-slot": "separator", "data-variant": "line", "data-tone": tone, orientation: orientation, className: stateClassName("sprout-separator", className), ...props }));
|
|
26
|
+
}
|
|
27
|
+
function Labeled({ children, className, variant: _variant, ...props }) {
|
|
28
|
+
const content = children !== null && children !== false && children !== "";
|
|
29
|
+
return (_jsxs("div", { "data-slot": "separator", "data-variant": "label", "data-content": content, className: cn("sprout-separator-label", className), ...props, children: [_jsx(Line, { className: "sprout-separator-label-line" }), content && (_jsxs(_Fragment, { children: [_jsx("span", { "data-slot": "separator-label-content", className: "sprout-separator-label-content", children: children }), _jsx("span", { "aria-hidden": "true", className: "sprout-separator sprout-separator-label-line", "data-orientation": "horizontal" })] }))] }));
|
|
30
|
+
}
|
|
31
|
+
function Fade({ children, className, variant: _variant, ...props }) {
|
|
32
|
+
return (_jsxs(BaseSeparator, { "aria-label": children, "data-slot": "separator", "data-variant": "fade", orientation: "horizontal", className: stateClassName("sprout-separator-fade", className), ...props, children: [_jsx("span", { className: "sprout-separator-fade-label", children: children }), _jsx("span", { "aria-hidden": "true", className: "sprout-separator-fade-shine", children: _jsx("span", { className: "sprout-separator-fade-label", children: children }) })] }));
|
|
33
|
+
}
|
|
34
|
+
function Marker({ className, variant, render, ref, ...props }) {
|
|
35
|
+
return useRender({
|
|
36
|
+
defaultTagName: "div",
|
|
37
|
+
ref,
|
|
38
|
+
render,
|
|
39
|
+
props: mergeProps({
|
|
40
|
+
"data-slot": "separator",
|
|
41
|
+
"data-variant": variant,
|
|
42
|
+
className: cn("sprout-separator-marker", variant === "marker-border" && "sprout-separator-marker-border", className),
|
|
43
|
+
}, props),
|
|
44
|
+
state: { slot: "separator", variant },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** A 16px decorative icon slot. It is hidden from assistive technology. */
|
|
48
|
+
export function SeparatorIcon({ className, ...props }) {
|
|
49
|
+
return (_jsx("span", { "data-slot": "separator-icon", "aria-hidden": "true", className: cn("sprout-separator-icon", className), ...props }));
|
|
50
|
+
}
|
|
51
|
+
/** Marker text. Long words wrap instead of overflowing. */
|
|
52
|
+
export function SeparatorContent({ className, ...props }) {
|
|
53
|
+
return (_jsx("span", { "data-slot": "separator-content", className: cn("sprout-separator-content", className), ...props }));
|
|
54
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
export declare const settingsGroupHeadingNames: readonly ["h2", "h3", "h4"];
|
|
3
|
+
export type SettingsGroupHeading = (typeof settingsGroupHeadingNames)[number];
|
|
4
|
+
export interface SettingsGroupProps extends Omit<ComponentProps<"section">, "title"> {
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
/** Id for the title. When set, the section is named by its title. */
|
|
7
|
+
titleId?: string;
|
|
8
|
+
heading?: SettingsGroupHeading;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A titled panel of settings rows. Children are stacked with a divider
|
|
12
|
+
* between them; rows, notes, forms, and alerts can sit side by side.
|
|
13
|
+
*/
|
|
14
|
+
export declare function SettingsGroup({ title, titleId, heading: Heading, className, children, ...props }: SettingsGroupProps): import("react").JSX.Element;
|
|
15
|
+
export interface SettingsRowProps extends Omit<ComponentProps<"div">, "children"> {
|
|
16
|
+
label: ReactNode;
|
|
17
|
+
description?: ReactNode;
|
|
18
|
+
/** Renders the label as a `<label>` for this control id. */
|
|
19
|
+
htmlFor?: string;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One setting: a label and description, with its value or control beside
|
|
24
|
+
* them from 36rem of group width and below them on narrower groups.
|
|
25
|
+
*/
|
|
26
|
+
export declare function SettingsRow({ label, description, htmlFor, className, children, ...props }: SettingsRowProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../internal/classes.js";
|
|
3
|
+
export const settingsGroupHeadingNames = ["h2", "h3", "h4"];
|
|
4
|
+
/**
|
|
5
|
+
* A titled panel of settings rows. Children are stacked with a divider
|
|
6
|
+
* between them; rows, notes, forms, and alerts can sit side by side.
|
|
7
|
+
*/
|
|
8
|
+
export function SettingsGroup({ title, titleId, heading: Heading = "h2", className, children, ...props }) {
|
|
9
|
+
return (_jsxs("section", { "data-slot": "settings-group", "aria-labelledby": titleId, className: cn("sprout-settings-group", className), ...props, children: [_jsx(Heading, { id: titleId, className: "sprout-settings-group-title", children: title }), _jsx("div", { "data-slot": "settings-group-content", className: "sprout-settings-group-content", children: children })] }));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One setting: a label and description, with its value or control beside
|
|
13
|
+
* them from 36rem of group width and below them on narrower groups.
|
|
14
|
+
*/
|
|
15
|
+
export function SettingsRow({ label, description, htmlFor, className, children, ...props }) {
|
|
16
|
+
return (_jsxs("div", { "data-slot": "settings-row", className: cn("sprout-settings-row", className), ...props, children: [_jsxs("div", { className: "sprout-settings-row-text", children: [htmlFor ? (_jsx("label", { htmlFor: htmlFor, className: "sprout-settings-row-label", children: label })) : (_jsx("p", { className: "sprout-settings-row-label", children: label })), description != null && description !== false && description !== "" && (_jsx("div", { className: "sprout-settings-row-description", children: description }))] }), children != null && children !== false && (_jsx("div", { "data-slot": "settings-row-control", className: "sprout-settings-row-control", children: children }))] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Drawer as SheetPrimitive } from "@base-ui/react/drawer";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
export declare const sheetSideNames: readonly ["top", "right", "bottom", "left"];
|
|
4
|
+
export declare const sheetSizeNames: readonly ["default", "lg", "xl", "full"];
|
|
5
|
+
export declare const sheetTitleSizeNames: readonly ["default", "lg"];
|
|
6
|
+
export type SheetSide = (typeof sheetSideNames)[number];
|
|
7
|
+
export type SheetSize = (typeof sheetSizeNames)[number];
|
|
8
|
+
export type SheetTitleSize = (typeof sheetTitleSizeNames)[number];
|
|
9
|
+
export type SheetProps = Omit<SheetPrimitive.Root.Props, "swipeDirection" | "snapPoints" | "snapPoint" | "defaultSnapPoint" | "onSnapPointChange" | "snapToSequentialPoints">;
|
|
10
|
+
/**
|
|
11
|
+
* Groups the parts of a sheet. Touch users can swipe it closed toward the
|
|
12
|
+
* edge set by `SheetContent`'s `side`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function Sheet(props: SheetProps): import("react").JSX.Element;
|
|
15
|
+
export type SheetTriggerProps = SheetPrimitive.Trigger.Props;
|
|
16
|
+
export declare function SheetTrigger(props: SheetTriggerProps): import("react").JSX.Element;
|
|
17
|
+
export type SheetCloseProps = SheetPrimitive.Close.Props;
|
|
18
|
+
export declare function SheetClose(props: SheetCloseProps): import("react").JSX.Element;
|
|
19
|
+
export type SheetPortalProps = SheetPrimitive.Portal.Props;
|
|
20
|
+
export declare function SheetPortal(props: SheetPortalProps): import("react").JSX.Element;
|
|
21
|
+
export type SheetOverlayProps = SheetPrimitive.Backdrop.Props;
|
|
22
|
+
export declare function SheetOverlay({ className, ...props }: SheetOverlayProps): import("react").JSX.Element;
|
|
23
|
+
export interface SheetContentProps extends SheetPrimitive.Popup.Props {
|
|
24
|
+
/** The viewport edge the sheet slides in from. Defaults to `"right"`. */
|
|
25
|
+
side?: SheetSide;
|
|
26
|
+
/**
|
|
27
|
+
* Width of a left or right sheet: `default` 24rem, `lg` 32rem, `xl` 42rem.
|
|
28
|
+
* `full` covers the whole viewport from any side.
|
|
29
|
+
*/
|
|
30
|
+
size?: SheetSize;
|
|
31
|
+
/** Adds a ghost icon button in the top-right corner. Defaults to `true`. */
|
|
32
|
+
showCloseButton?: boolean;
|
|
33
|
+
/** Accessible name of the corner close button. */
|
|
34
|
+
closeLabel?: string;
|
|
35
|
+
/** Keeps the sheet in the DOM while closed, so drafts and scroll survive. */
|
|
36
|
+
keepMounted?: boolean;
|
|
37
|
+
/** Where the portal renders. Defaults to `document.body`. */
|
|
38
|
+
container?: SheetPrimitive.Portal.Props["container"];
|
|
39
|
+
/** Shows a grab bar on the edge facing the page. Defaults to `false`. */
|
|
40
|
+
showSwipeHandle?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The sliding panel. It renders its own portal and overlay, so place it
|
|
44
|
+
* directly inside `Sheet`. A touch swipe toward `side` closes it. Mouse and pen
|
|
45
|
+
* drags only move it from the handle or empty panel space, so text selection
|
|
46
|
+
* and form fields work as usual.
|
|
47
|
+
*/
|
|
48
|
+
export declare function SheetContent({ className, children, side, size, showCloseButton, closeLabel, keepMounted, container, showSwipeHandle, ...props }: SheetContentProps): import("react").JSX.Element;
|
|
49
|
+
type PartSlot = "sheet-header" | "sheet-body" | "sheet-footer";
|
|
50
|
+
export interface SheetPartState extends Record<string, unknown> {
|
|
51
|
+
slot: PartSlot;
|
|
52
|
+
}
|
|
53
|
+
export type SheetPartProps = useRender.ComponentProps<"div", SheetPartState>;
|
|
54
|
+
/** Stacks the title and description with 16px padding. */
|
|
55
|
+
export declare const SheetHeader: ({ className, render, ref, ...props }: SheetPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
56
|
+
/** The scrolling middle. It fills the space between header and footer. */
|
|
57
|
+
export declare const SheetBody: ({ className, render, ref, ...props }: SheetPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
58
|
+
/**
|
|
59
|
+
* Actions pinned to the bottom edge. They stack full width in side sheets and
|
|
60
|
+
* align right in wide top and bottom sheets.
|
|
61
|
+
*/
|
|
62
|
+
export declare const SheetFooter: ({ className, render, ref, ...props }: SheetPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
63
|
+
export interface SheetTitleProps extends SheetPrimitive.Title.Props {
|
|
64
|
+
/** `lg` is the larger product-page title used in wide detail sheets. */
|
|
65
|
+
size?: SheetTitleSize;
|
|
66
|
+
}
|
|
67
|
+
export declare function SheetTitle({ className, size, ...props }: SheetTitleProps): import("react").JSX.Element;
|
|
68
|
+
export type SheetDescriptionProps = SheetPrimitive.Description.Props;
|
|
69
|
+
export declare function SheetDescription({ className, ...props }: SheetDescriptionProps): import("react").JSX.Element;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useLayoutEffect, useState, } from "react";
|
|
4
|
+
import { Drawer as SheetPrimitive } from "@base-ui/react/drawer";
|
|
5
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
6
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
7
|
+
import { X } from "reicon-react/icons/X";
|
|
8
|
+
import { cn, stateClassName } from "../internal/classes.js";
|
|
9
|
+
import { Button } from "./button.js";
|
|
10
|
+
export const sheetSideNames = ["top", "right", "bottom", "left"];
|
|
11
|
+
export const sheetSizeNames = ["default", "lg", "xl", "full"];
|
|
12
|
+
export const sheetTitleSizeNames = ["default", "lg"];
|
|
13
|
+
const swipeDirections = {
|
|
14
|
+
top: "up",
|
|
15
|
+
right: "right",
|
|
16
|
+
bottom: "down",
|
|
17
|
+
left: "left",
|
|
18
|
+
};
|
|
19
|
+
const SheetSideContext = createContext(null);
|
|
20
|
+
/**
|
|
21
|
+
* Groups the parts of a sheet. Touch users can swipe it closed toward the
|
|
22
|
+
* edge set by `SheetContent`'s `side`.
|
|
23
|
+
*/
|
|
24
|
+
export function Sheet(props) {
|
|
25
|
+
const [side, setSide] = useState("right");
|
|
26
|
+
return (_jsx(SheetSideContext.Provider, { value: setSide, children: _jsx(SheetPrimitive.Root, { "data-slot": "sheet", ...props, swipeDirection: swipeDirections[side] }) }));
|
|
27
|
+
}
|
|
28
|
+
export function SheetTrigger(props) {
|
|
29
|
+
return _jsx(SheetPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
30
|
+
}
|
|
31
|
+
export function SheetClose(props) {
|
|
32
|
+
return _jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", ...props });
|
|
33
|
+
}
|
|
34
|
+
export function SheetPortal(props) {
|
|
35
|
+
return _jsx(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
36
|
+
}
|
|
37
|
+
export function SheetOverlay({ className, ...props }) {
|
|
38
|
+
return (_jsx(SheetPrimitive.Backdrop, { "data-slot": "sheet-overlay", ...props, className: stateClassName("sprout-sheet-overlay", className) }));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The sliding panel. It renders its own portal and overlay, so place it
|
|
42
|
+
* directly inside `Sheet`. A touch swipe toward `side` closes it. Mouse and pen
|
|
43
|
+
* drags only move it from the handle or empty panel space, so text selection
|
|
44
|
+
* and form fields work as usual.
|
|
45
|
+
*/
|
|
46
|
+
export function SheetContent({ className, children, side = "right", size = "default", showCloseButton = true, closeLabel = "Close", keepMounted = false, container, showSwipeHandle = false, ...props }) {
|
|
47
|
+
const setSide = useContext(SheetSideContext);
|
|
48
|
+
// Base UI reads the swipe direction from the root, but `side` lives here.
|
|
49
|
+
useLayoutEffect(() => setSide?.(side), [setSide, side]);
|
|
50
|
+
return (_jsxs(SheetPortal, { keepMounted: keepMounted, container: container, children: [_jsx(SheetOverlay, {}), _jsx(SheetPrimitive.Viewport, { "data-slot": "sheet-viewport", className: "sprout-sheet-viewport", children: _jsxs(SheetPrimitive.Popup, { "data-slot": "sheet-content", "data-side": side, "data-size": size, ...props, className: stateClassName("sprout-sheet-content", className), children: [showSwipeHandle && (_jsx("div", { "data-slot": "sheet-swipe-handle", "aria-hidden": "true", className: "sprout-sheet-swipe-handle" })), _jsx(SheetPrimitive.Content, { className: "sprout-sheet-inner", children: children }), showCloseButton && (_jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", className: "sprout-sheet-close", "aria-label": closeLabel, render: _jsx(Button, { variant: "ghost", size: "icon-sm" }), children: _jsx(X, { size: 16, "aria-hidden": "true" }) }))] }) })] }));
|
|
51
|
+
}
|
|
52
|
+
function part(slot, base) {
|
|
53
|
+
return function SheetPart({ className, render, ref, ...props }) {
|
|
54
|
+
return useRender({
|
|
55
|
+
defaultTagName: "div",
|
|
56
|
+
ref,
|
|
57
|
+
render,
|
|
58
|
+
props: mergeProps({ "data-slot": slot, className: cn(base, className) }, props),
|
|
59
|
+
state: { slot },
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Stacks the title and description with 16px padding. */
|
|
64
|
+
export const SheetHeader = part("sheet-header", "sprout-sheet-header");
|
|
65
|
+
/** The scrolling middle. It fills the space between header and footer. */
|
|
66
|
+
export const SheetBody = part("sheet-body", "sprout-sheet-body");
|
|
67
|
+
/**
|
|
68
|
+
* Actions pinned to the bottom edge. They stack full width in side sheets and
|
|
69
|
+
* align right in wide top and bottom sheets.
|
|
70
|
+
*/
|
|
71
|
+
export const SheetFooter = part("sheet-footer", "sprout-sheet-footer");
|
|
72
|
+
export function SheetTitle({ className, size = "default", ...props }) {
|
|
73
|
+
return (_jsx(SheetPrimitive.Title, { "data-slot": "sheet-title", "data-size": size, ...props, className: stateClassName("sprout-sheet-title", className) }));
|
|
74
|
+
}
|
|
75
|
+
export function SheetDescription({ className, ...props }) {
|
|
76
|
+
return (_jsx(SheetPrimitive.Description, { "data-slot": "sheet-description", ...props, className: stateClassName("sprout-sheet-description", className) }));
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode, type RefObject, type SetStateAction } from "react";
|
|
2
|
+
import { Button } from "@base-ui/react/button";
|
|
3
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
4
|
+
import { type TooltipContentProps } from "./tooltip.js";
|
|
5
|
+
import { type InputProps } from "./input.js";
|
|
6
|
+
import { type SeparatorLineProps } from "./separator.js";
|
|
7
|
+
interface SidebarContextValue {
|
|
8
|
+
open: boolean;
|
|
9
|
+
setOpen: (open: SetStateAction<boolean>) => void;
|
|
10
|
+
openMobile: boolean;
|
|
11
|
+
setOpenMobile: (open: SetStateAction<boolean>) => void;
|
|
12
|
+
isMobile: boolean;
|
|
13
|
+
state: "expanded" | "collapsed";
|
|
14
|
+
toggleSidebar: () => void;
|
|
15
|
+
panelId: string;
|
|
16
|
+
triggerRef: RefObject<HTMLElement | null>;
|
|
17
|
+
}
|
|
18
|
+
export declare function useSidebar(): SidebarContextValue;
|
|
19
|
+
export interface SidebarProviderProps extends Omit<ComponentProps<"div">, "ref"> {
|
|
20
|
+
defaultOpen?: boolean;
|
|
21
|
+
open?: boolean;
|
|
22
|
+
onOpenChange?: (open: boolean) => void;
|
|
23
|
+
openMobile?: boolean;
|
|
24
|
+
onOpenMobileChange?: (open: boolean) => void;
|
|
25
|
+
keyboardShortcut?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function SidebarProvider({ defaultOpen, open: controlledOpen, onOpenChange, openMobile: controlledMobile, onOpenMobileChange, keyboardShortcut, className, children, ...props }: SidebarProviderProps): import("react").JSX.Element;
|
|
28
|
+
export interface SidebarProps extends ComponentProps<"aside"> {
|
|
29
|
+
side?: "left" | "right";
|
|
30
|
+
collapsible?: "icon" | "offcanvas" | "none";
|
|
31
|
+
label?: string;
|
|
32
|
+
portalContainer?: Dialog.Portal.Props["container"];
|
|
33
|
+
}
|
|
34
|
+
export declare function Sidebar({ side, collapsible, label, portalContainer, className, children, ...props }: SidebarProps): import("react").JSX.Element;
|
|
35
|
+
export declare function SidebarTrigger({ className, onClick, children, ...props }: Button.Props): import("react").JSX.Element;
|
|
36
|
+
export declare function SidebarHeader({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
37
|
+
/** A 32px search or filter field for the sidebar header, on the canvas color. */
|
|
38
|
+
export declare function SidebarInput({ className, ...props }: InputProps): import("react").JSX.Element;
|
|
39
|
+
export declare function SidebarFooter({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
40
|
+
export declare function SidebarSeparator({ className, ...props }: SeparatorLineProps): import("react").JSX.Element;
|
|
41
|
+
export declare function SidebarContent({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
42
|
+
export declare function SidebarGroup({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
43
|
+
export declare function SidebarGroupLabel({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
44
|
+
export declare function SidebarMenu({ className, ...props }: ComponentProps<"ul">): import("react").JSX.Element;
|
|
45
|
+
export declare function SidebarMenuItem({ className, ...props }: ComponentProps<"li">): import("react").JSX.Element;
|
|
46
|
+
export interface SidebarMenuButtonProps extends Button.Props {
|
|
47
|
+
icon?: ReactNode;
|
|
48
|
+
badge?: ReactNode;
|
|
49
|
+
isActive?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The label shown beside the collapsed icon rail. Pass `TooltipContent`
|
|
52
|
+
* props to change its content or placement; `hidden` overrides when it shows.
|
|
53
|
+
*/
|
|
54
|
+
tooltip?: string | TooltipContentProps;
|
|
55
|
+
}
|
|
56
|
+
export declare function SidebarMenuButton({ icon, badge, isActive, tooltip, className, children, onClick, ...props }: SidebarMenuButtonProps): import("react").JSX.Element;
|
|
57
|
+
export declare function SidebarMenuAction({ className, ...props }: Button.Props): import("react").JSX.Element;
|
|
58
|
+
export declare function SidebarMenuSub({ className, ...props }: ComponentProps<"ul">): import("react").JSX.Element;
|
|
59
|
+
export declare const SidebarMenuSubItem: typeof SidebarMenuItem;
|
|
60
|
+
export declare function SidebarMenuSubButton({ className, ...props }: SidebarMenuButtonProps): import("react").JSX.Element;
|
|
61
|
+
export interface SidebarSectionProps extends Omit<SidebarMenuButtonProps, "onChange"> {
|
|
62
|
+
label: string;
|
|
63
|
+
children: ReactNode;
|
|
64
|
+
open?: boolean;
|
|
65
|
+
defaultOpen?: boolean;
|
|
66
|
+
onOpenChange?: (open: boolean) => void;
|
|
67
|
+
}
|
|
68
|
+
export declare function SidebarSection({ label, children, open: controlledOpen, defaultOpen, onOpenChange, ...props }: SidebarSectionProps): import("react").JSX.Element;
|
|
69
|
+
export declare function SidebarMenuSkeleton({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronRight } from "reicon-react/icons/ChevronRight";
|
|
4
|
+
import { createContext, useContext, useEffect, useId, useRef, useState, useSyncExternalStore, } from "react";
|
|
5
|
+
import { Button } from "@base-ui/react/button";
|
|
6
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
7
|
+
import { Collapsible } from "@base-ui/react/collapsible";
|
|
8
|
+
import { cn, stateClassName } from "../internal/classes.js";
|
|
9
|
+
import { Pressable } from "../internal/pressable.js";
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip.js";
|
|
11
|
+
import { Input } from "./input.js";
|
|
12
|
+
import { Separator } from "./separator.js";
|
|
13
|
+
const SidebarContext = createContext(null);
|
|
14
|
+
const PanelContext = createContext(null);
|
|
15
|
+
export function useSidebar() {
|
|
16
|
+
const context = useContext(SidebarContext);
|
|
17
|
+
if (!context)
|
|
18
|
+
throw new Error("Sidebar parts must be inside SidebarProvider.");
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
function usePanel() {
|
|
22
|
+
const context = useContext(PanelContext);
|
|
23
|
+
if (!context)
|
|
24
|
+
throw new Error("Sidebar navigation parts must be inside Sidebar.");
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
27
|
+
function subscribeMobile(callback) {
|
|
28
|
+
const media = window.matchMedia("(max-width: 767px)");
|
|
29
|
+
media.addEventListener("change", callback);
|
|
30
|
+
return () => media.removeEventListener("change", callback);
|
|
31
|
+
}
|
|
32
|
+
export function SidebarProvider({ defaultOpen = true, open: controlledOpen, onOpenChange, openMobile: controlledMobile, onOpenMobileChange, keyboardShortcut = true, className, children, ...props }) {
|
|
33
|
+
const [localOpen, setLocalOpen] = useState(defaultOpen);
|
|
34
|
+
const [localMobile, setLocalMobile] = useState(false);
|
|
35
|
+
const isMobile = useSyncExternalStore(subscribeMobile, () => window.matchMedia("(max-width: 767px)").matches, () => false);
|
|
36
|
+
const open = controlledOpen ?? localOpen;
|
|
37
|
+
const openMobile = controlledMobile ?? localMobile;
|
|
38
|
+
const panelId = useId();
|
|
39
|
+
const triggerRef = useRef(null);
|
|
40
|
+
const wrapperRef = useRef(null);
|
|
41
|
+
function setOpen(value) {
|
|
42
|
+
const next = typeof value === "function" ? value(open) : value;
|
|
43
|
+
if (controlledOpen === undefined)
|
|
44
|
+
setLocalOpen(next);
|
|
45
|
+
onOpenChange?.(next);
|
|
46
|
+
}
|
|
47
|
+
function setOpenMobile(value) {
|
|
48
|
+
const next = typeof value === "function" ? value(openMobile) : value;
|
|
49
|
+
if (controlledMobile === undefined)
|
|
50
|
+
setLocalMobile(next);
|
|
51
|
+
onOpenMobileChange?.(next);
|
|
52
|
+
}
|
|
53
|
+
function toggleSidebar() {
|
|
54
|
+
if (isMobile)
|
|
55
|
+
setOpenMobile((value) => !value);
|
|
56
|
+
else
|
|
57
|
+
setOpen((value) => !value);
|
|
58
|
+
}
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!keyboardShortcut)
|
|
61
|
+
return;
|
|
62
|
+
function onKeyDown(event) {
|
|
63
|
+
if (event.defaultPrevented ||
|
|
64
|
+
event.repeat ||
|
|
65
|
+
event.altKey ||
|
|
66
|
+
!(event.metaKey || event.ctrlKey) ||
|
|
67
|
+
event.key.toLowerCase() !== "b")
|
|
68
|
+
return;
|
|
69
|
+
const target = event.target;
|
|
70
|
+
if (target instanceof Element) {
|
|
71
|
+
if (target.closest("input, textarea, select, [role=combobox], [role=listbox], [role=menu], [aria-haspopup=menu], [contenteditable]:not([contenteditable=false])"))
|
|
72
|
+
return;
|
|
73
|
+
const owner = target.closest("[data-sprout-sidebar-provider]");
|
|
74
|
+
if (owner && owner !== wrapperRef.current)
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
toggleSidebar();
|
|
79
|
+
}
|
|
80
|
+
document.addEventListener("keydown", onKeyDown);
|
|
81
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
82
|
+
});
|
|
83
|
+
return (_jsx(SidebarContext.Provider, { value: {
|
|
84
|
+
open,
|
|
85
|
+
setOpen,
|
|
86
|
+
openMobile,
|
|
87
|
+
setOpenMobile,
|
|
88
|
+
isMobile,
|
|
89
|
+
state: open ? "expanded" : "collapsed",
|
|
90
|
+
toggleSidebar,
|
|
91
|
+
panelId,
|
|
92
|
+
triggerRef,
|
|
93
|
+
}, children: _jsx(TooltipProvider, { delay: 200, children: _jsx("div", { ...props, ref: wrapperRef, "data-sprout-sidebar-provider": "", className: cn("sprout-sidebar-layout font-sprout text-sprout-text", className), children: children }) }) }));
|
|
94
|
+
}
|
|
95
|
+
export function Sidebar({ side = "left", collapsible = "icon", label = "Navigation", portalContainer, className, children, ...props }) {
|
|
96
|
+
const { open, openMobile, setOpenMobile, isMobile, panelId, triggerRef } = useSidebar();
|
|
97
|
+
const mobile = isMobile && collapsible !== "none";
|
|
98
|
+
const collapsed = !mobile && !open && collapsible === "icon";
|
|
99
|
+
const offcanvas = !open && collapsible === "offcanvas";
|
|
100
|
+
const content = (_jsx(PanelContext.Provider, { value: { collapsed, side, portalContainer }, children: children }));
|
|
101
|
+
if (mobile) {
|
|
102
|
+
return (_jsx(Dialog.Root, { open: openMobile, onOpenChange: setOpenMobile, children: _jsxs(Dialog.Portal, { container: portalContainer, children: [_jsx(Dialog.Backdrop, { className: "sprout-sidebar-backdrop" }), _jsxs(Dialog.Popup, { id: panelId, "data-side": side, className: "sprout-sidebar-drawer font-sprout text-sprout-text", finalFocus: triggerRef, children: [_jsxs("div", { className: "sprout-sidebar-drawer-heading", children: [_jsx(Dialog.Title, { children: label }), _jsx(Dialog.Close, { className: "sprout-sidebar-close", children: "Close" })] }), _jsx("aside", { ...props, "aria-label": label, "data-sprout-sidebar": "", "data-state": "expanded", className: cn("sprout-sidebar-panel sprout-sidebar-mobile bg-sprout-canvas", className), children: content })] })] }) }));
|
|
103
|
+
}
|
|
104
|
+
return (_jsx("aside", { ...props, id: panelId, "aria-label": label, "data-sprout-sidebar": "", "data-state": collapsed ? "collapsed" : "expanded", "data-side": side, "data-collapsible": collapsible, "data-offcanvas": offcanvas ? "" : undefined, "aria-hidden": offcanvas || undefined, inert: offcanvas, className: cn("sprout-sidebar-panel bg-sprout-canvas", className), children: content }));
|
|
105
|
+
}
|
|
106
|
+
export function SidebarTrigger({ className, onClick, children = "Toggle sidebar", ...props }) {
|
|
107
|
+
const { isMobile, open, openMobile, toggleSidebar, panelId, triggerRef } = useSidebar();
|
|
108
|
+
return (_jsx(Button, { "aria-label": "Toggle sidebar", "aria-expanded": isMobile ? openMobile : open, "aria-controls": panelId, ...props, className: stateClassName("sprout-sidebar-trigger", className), onClick: (event) => {
|
|
109
|
+
onClick?.(event);
|
|
110
|
+
if (event.defaultPrevented)
|
|
111
|
+
return;
|
|
112
|
+
triggerRef.current = event.currentTarget;
|
|
113
|
+
toggleSidebar();
|
|
114
|
+
}, children: children }));
|
|
115
|
+
}
|
|
116
|
+
export function SidebarHeader({ className, ...props }) {
|
|
117
|
+
return _jsx("div", { ...props, className: cn("sprout-sidebar-header", className) });
|
|
118
|
+
}
|
|
119
|
+
/** A 32px search or filter field for the sidebar header, on the canvas color. */
|
|
120
|
+
export function SidebarInput({ className, ...props }) {
|
|
121
|
+
return (_jsx(Input, { "data-slot": "sidebar-input", "data-sidebar": "input", ...props, className: stateClassName("sprout-sidebar-input", className) }));
|
|
122
|
+
}
|
|
123
|
+
export function SidebarFooter({ className, ...props }) {
|
|
124
|
+
return _jsx("div", { ...props, className: cn("sprout-sidebar-footer", className) });
|
|
125
|
+
}
|
|
126
|
+
export function SidebarSeparator({ className, ...props }) {
|
|
127
|
+
return (_jsx(Separator, { "data-sidebar": "separator", ...props, className: stateClassName("sprout-sidebar-separator", className) }));
|
|
128
|
+
}
|
|
129
|
+
export function SidebarContent({ className, ...props }) {
|
|
130
|
+
return _jsx("div", { ...props, className: cn("sprout-sidebar-content", className) });
|
|
131
|
+
}
|
|
132
|
+
export function SidebarGroup({ className, ...props }) {
|
|
133
|
+
return _jsx("div", { ...props, className: cn("sprout-sidebar-group", className) });
|
|
134
|
+
}
|
|
135
|
+
export function SidebarGroupLabel({ className, ...props }) {
|
|
136
|
+
return (_jsx("div", { ...props, className: cn("sprout-sidebar-group-label", className) }));
|
|
137
|
+
}
|
|
138
|
+
export function SidebarMenu({ className, ...props }) {
|
|
139
|
+
return _jsx("ul", { ...props, className: cn("sprout-sidebar-menu", className) });
|
|
140
|
+
}
|
|
141
|
+
export function SidebarMenuItem({ className, ...props }) {
|
|
142
|
+
return (_jsx("li", { ...props, className: cn("sprout-sidebar-menu-item", className) }));
|
|
143
|
+
}
|
|
144
|
+
export function SidebarMenuButton({ icon, badge, isActive = false, tooltip, className, children, onClick, ...props }) {
|
|
145
|
+
const { collapsed, side, portalContainer } = usePanel();
|
|
146
|
+
const { setOpenMobile } = useSidebar();
|
|
147
|
+
const content = typeof tooltip === "string" ? { children: tooltip } : tooltip;
|
|
148
|
+
const { hidden: hideTooltip = !collapsed, ...contentProps } = content ?? {};
|
|
149
|
+
const accessibleLabel = props["aria-label"] ??
|
|
150
|
+
(typeof content?.children === "string" ? content.children : undefined) ??
|
|
151
|
+
(typeof children === "string" ? children : undefined);
|
|
152
|
+
return (_jsxs(Tooltip, { disabled: hideTooltip || props.disabled, children: [_jsxs(TooltipTrigger, { render: _jsx(Pressable, { ...props, "aria-label": accessibleLabel, "aria-current": props["aria-current"] ??
|
|
153
|
+
(isActive && props.nativeButton === false ? "page" : undefined), "data-active": isActive ? "" : undefined, className: stateClassName("sprout-sidebar-menu-button", className), onClick: (event) => {
|
|
154
|
+
onClick?.(event);
|
|
155
|
+
if (!event.defaultPrevented &&
|
|
156
|
+
props.nativeButton === false &&
|
|
157
|
+
!event.metaKey &&
|
|
158
|
+
!event.ctrlKey &&
|
|
159
|
+
!event.shiftKey &&
|
|
160
|
+
!event.altKey)
|
|
161
|
+
setOpenMobile(false);
|
|
162
|
+
} }), children: [icon && (_jsx("span", { className: "sprout-sidebar-icon", "aria-hidden": "true", children: icon })), _jsx("span", { className: "sprout-sidebar-label", children: children }), badge != null && _jsx("span", { className: "sprout-sidebar-badge", children: badge })] }), _jsx(TooltipContent, { side: side === "left" ? "right" : "left", sideOffset: 8, container: portalContainer, ...contentProps, className: stateClassName("sprout-sidebar-tooltip", contentProps.className), children: contentProps.children ?? children })] }));
|
|
163
|
+
}
|
|
164
|
+
export function SidebarMenuAction({ className, ...props }) {
|
|
165
|
+
usePanel();
|
|
166
|
+
return (_jsx(Button, { ...props, className: stateClassName("sprout-sidebar-menu-action", className) }));
|
|
167
|
+
}
|
|
168
|
+
export function SidebarMenuSub({ className, ...props }) {
|
|
169
|
+
return (_jsx("ul", { ...props, className: cn("sprout-sidebar-menu sprout-sidebar-sub", className) }));
|
|
170
|
+
}
|
|
171
|
+
export const SidebarMenuSubItem = SidebarMenuItem;
|
|
172
|
+
export function SidebarMenuSubButton({ className, ...props }) {
|
|
173
|
+
return (_jsx(SidebarMenuButton, { ...props, className: stateClassName("sprout-sidebar-sub-button", className) }));
|
|
174
|
+
}
|
|
175
|
+
export function SidebarSection({ label, children, open: controlledOpen, defaultOpen = false, onOpenChange, ...props }) {
|
|
176
|
+
const [localOpen, setLocalOpen] = useState(defaultOpen);
|
|
177
|
+
const { collapsed } = usePanel();
|
|
178
|
+
const open = !collapsed && (controlledOpen ?? localOpen);
|
|
179
|
+
return (_jsx(SidebarMenuItem, { children: _jsxs(Collapsible.Root, { open: open, onOpenChange: (value) => {
|
|
180
|
+
if (controlledOpen === undefined)
|
|
181
|
+
setLocalOpen(value);
|
|
182
|
+
onOpenChange?.(value);
|
|
183
|
+
}, children: [_jsxs("div", { className: "sprout-sidebar-section-heading", children: [_jsx(SidebarMenuButton, { ...props, children: label }), _jsx(Collapsible.Trigger, { render: _jsx(SidebarMenuAction, { "aria-label": `${open ? "Collapse" : "Expand"} ${label}` }), children: _jsx(ChevronRight, { size: 16, "aria-hidden": "true", className: "sprout-sidebar-chevron" }) })] }), _jsx(Collapsible.Panel, { className: "sprout-sidebar-section-content", inert: !open, children: _jsx(SidebarMenuSub, { children: children }) })] }) }));
|
|
184
|
+
}
|
|
185
|
+
export function SidebarMenuSkeleton({ className, ...props }) {
|
|
186
|
+
return (_jsxs("div", { ...props, "aria-hidden": "true", className: cn("sprout-sidebar-skeleton", className), children: [_jsx("span", {}), _jsx("span", {})] }));
|
|
187
|
+
}
|