dishui 0.0.104 → 0.0.106
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/dist/.release-verified.json +2 -2
- package/dist/components/ChoicePicker/index.js +8 -6
- package/dist/components/ChoicePicker/index.js.map +1 -1
- package/dist/components/Column/index.d.ts +3 -2
- package/dist/components/Column/index.js +5 -17
- package/dist/components/Column/index.js.map +1 -1
- package/dist/components/ConfirmDialog/index.d.ts +20 -0
- package/dist/components/ConfirmDialog/index.js +84 -0
- package/dist/components/ConfirmDialog/index.js.map +1 -0
- package/dist/components/DateTimeInput/index.js +28 -26
- package/dist/components/DateTimeInput/index.js.map +1 -1
- package/dist/components/Input/index.js +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/MultipleChoice/index.js +2 -1
- package/dist/components/MultipleChoice/index.js.map +1 -1
- package/dist/components/Row/index.d.ts +3 -2
- package/dist/components/Row/index.js +5 -17
- package/dist/components/Row/index.js.map +1 -1
- package/dist/components/Tab/index.js +80 -0
- package/dist/components/Tab/index.js.map +1 -0
- package/dist/components/Tabs/index.js +28 -33
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/tabNavigation.d.ts +2 -0
- package/dist/components/Tabs/tabNavigation.js +14 -0
- package/dist/components/Tabs/tabNavigation.js.map +1 -0
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.d.ts +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +25 -26
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js +8 -11
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js +184 -182
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/archify/convert.js +26 -26
- package/dist/components/pro/Flow/archify/convert.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js +13 -11
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.d.ts +2 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js +15 -15
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js +8 -7
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js +73 -83
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js +73 -49
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js +12 -14
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js +113 -113
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/css.d.ts +1 -0
- package/dist/dishui.css +1 -1
- package/dist/index-nostyled.d.ts +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +113 -111
- package/dist/layouts/Sidebar.js +201 -107
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/utils/flexLayout.d.ts +5 -0
- package/dist/utils/flexLayout.js +19 -0
- package/dist/utils/flexLayout.js.map +1 -0
- package/package.json +63 -57
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { isTabNavigationKey as t, nextTab as n } from "../Tabs/tabNavigation.js";
|
|
3
|
+
import { Children as r, isValidElement as i, useCallback as a, useId as o, useMemo as s, useRef as c, useState as l } from "react";
|
|
4
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/Tab/index.tsx
|
|
6
|
+
var f = () => null;
|
|
7
|
+
function p(e, t) {
|
|
8
|
+
return i(e) && e.type === t;
|
|
9
|
+
}
|
|
10
|
+
var m = {
|
|
11
|
+
square: {},
|
|
12
|
+
rounded: { borderRadius: "0.5rem 0.5rem 0 0" },
|
|
13
|
+
pill: { borderRadius: "9999px" },
|
|
14
|
+
trapezoid: {
|
|
15
|
+
clipPath: "polygon(8% 0%, 92% 0%, 100% 100%, 0% 100%)",
|
|
16
|
+
borderRadius: 0
|
|
17
|
+
},
|
|
18
|
+
angled: {
|
|
19
|
+
clipPath: "polygon(0 0, calc(100% - 10px) 0, 100% 100%, 0 100%)",
|
|
20
|
+
borderRadius: 0
|
|
21
|
+
}
|
|
22
|
+
}, h = ({ children: i, defaultValue: h, value: g, onValueChange: _, variant: v = "line", shape: y, size: b = "md", className: x = "", tabListClassName: S = "", panelClassName: C = "", fullWidth: w = !1 }) => {
|
|
23
|
+
let T = s(() => {
|
|
24
|
+
let e = [];
|
|
25
|
+
return r.forEach(i, (t) => {
|
|
26
|
+
p(t, f) && e.push(t);
|
|
27
|
+
}), e;
|
|
28
|
+
}, [i]), E = T.find((e) => !e.props.disabled)?.props.id ?? "", D = g !== void 0, [O, k] = l(h), A = D ? g : O, j = T.find((e) => e.props.id === A && !e.props.disabled)?.props.id ?? E, M = o(), N = c(/* @__PURE__ */ new Map()), [P, F] = l(null), I = T.find((e) => e.props.id === P && !e.props.disabled)?.props.id ?? j, L = T.filter((e) => !e.props.disabled), R = (e, r) => {
|
|
29
|
+
if (e.altKey || e.ctrlKey || e.metaKey || e.nativeEvent.isComposing) return;
|
|
30
|
+
let i = L.findIndex((e) => e.props.id === r);
|
|
31
|
+
if (!t(e.key)) return;
|
|
32
|
+
let a = n(L, i, e.key);
|
|
33
|
+
e.preventDefault(), N.current.get(a?.props.id ?? "")?.focus();
|
|
34
|
+
}, z = a((e) => {
|
|
35
|
+
D || k(e), _?.(e);
|
|
36
|
+
}, [D, _]), B = v === "line" ? "tabs gap-0 border-b border-base-300 w-full" : `tabs ${v === "boxed" ? "tabs-boxed" : v === "lifted" ? "tabs-lifted" : v === "bordered" ? "border border-base-300 rounded-t-lg p-0 overflow-hidden" : ""} w-full flex-wrap`, V = b === "sm" ? "tab-sm" : b === "lg" ? "tab-lg" : "", H = (e) => e ? v === "line" ? "tab-active z-[1] -mb-px font-semibold text-primary border-b-2 border-primary bg-base-100/80" : v === "boxed" || v === "lifted" ? "tab-active font-semibold text-primary ring-1 ring-inset ring-primary/30 bg-primary/10" : v === "bordered" ? "tab-active -mb-px z-[1] font-semibold text-primary border-b-2 border-primary bg-base-200/90" : "tab-active font-semibold text-primary" : v === "line" ? "border-b-2 border-transparent font-normal text-base-content/50 hover:text-base-content/75" : "font-normal text-base-content/50 hover:text-base-content/80 hover:bg-base-200/50", U = T.find((e) => e.props.id === j), W = U?.props.children, G = y ?? (v === "lifted" ? "rounded" : void 0);
|
|
37
|
+
return /* @__PURE__ */ d("div", {
|
|
38
|
+
className: e("w-full", x),
|
|
39
|
+
children: [/* @__PURE__ */ u("div", {
|
|
40
|
+
role: "tablist",
|
|
41
|
+
onBlur: (e) => {
|
|
42
|
+
e.currentTarget.contains(e.relatedTarget) || F(null);
|
|
43
|
+
},
|
|
44
|
+
className: e(B, w && "grid", S),
|
|
45
|
+
style: w ? { gridTemplateColumns: `repeat(${T.length}, minmax(0, 1fr))` } : void 0,
|
|
46
|
+
children: T.map((t) => {
|
|
47
|
+
let { id: n, label: r, disabled: i, tabClassName: a = "" } = t.props, o = n === j, s = o && G && G !== "square";
|
|
48
|
+
return /* @__PURE__ */ u("button", {
|
|
49
|
+
ref: (e) => {
|
|
50
|
+
e ? N.current.set(n, e) : N.current.delete(n);
|
|
51
|
+
},
|
|
52
|
+
type: "button",
|
|
53
|
+
id: `${M}-tab-${encodeURIComponent(n)}`,
|
|
54
|
+
role: "tab",
|
|
55
|
+
"aria-controls": `${M}-panel`,
|
|
56
|
+
"aria-selected": o,
|
|
57
|
+
tabIndex: !i && n === I ? 0 : -1,
|
|
58
|
+
disabled: i,
|
|
59
|
+
className: e("tab", "transition-all duration-150", V, w && "w-full justify-center", H(o), i ? "tab-disabled pointer-events-none opacity-50" : "cursor-pointer", a),
|
|
60
|
+
style: s ? m[G] : void 0,
|
|
61
|
+
onFocus: () => F(n),
|
|
62
|
+
onKeyDown: (e) => R(e, n),
|
|
63
|
+
onClick: () => z(n),
|
|
64
|
+
children: r
|
|
65
|
+
}, n);
|
|
66
|
+
})
|
|
67
|
+
}), /* @__PURE__ */ u("div", {
|
|
68
|
+
role: "tabpanel",
|
|
69
|
+
id: `${M}-panel`,
|
|
70
|
+
"aria-labelledby": U ? `${M}-tab-${encodeURIComponent(U.props.id)}` : void 0,
|
|
71
|
+
className: e("mt-3 w-full", C, U?.props.className),
|
|
72
|
+
children: W
|
|
73
|
+
})]
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
h.Pane = f;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { h as Tab, h as default };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Tab/index.tsx"],"sourcesContent":["import React, { Children, isValidElement, useCallback, useId, useMemo, useRef, useState } from 'react';\nimport { cn } from '../../utils/cn';\nimport { isTabNavigationKey, nextTab } from '../Tabs/tabNavigation';\n\nexport type TabPaneProps = {\n /** Stable id for the pane (used with controlled / default value) */\n id: string;\n label: React.ReactNode;\n children: React.ReactNode;\n disabled?: boolean;\n /** className on the tab button */\n tabClassName?: string;\n /** className on the active panel */\n className?: string;\n};\n\nconst TabPane: React.FC<TabPaneProps> = () => null;\n\nfunction isTabPane(\n child: React.ReactNode,\n Pane: typeof TabPane\n): child is React.ReactElement<TabPaneProps> {\n return isValidElement(child) && child.type === Pane;\n}\n\nexport type TabShape = 'square' | 'rounded' | 'pill' | 'trapezoid' | 'angled';\n\nexport type TabProps = {\n children: React.ReactNode;\n defaultValue?: string;\n value?: string;\n onValueChange?: (id: string) => void;\n variant?: 'line' | 'boxed' | 'lifted' | 'bordered';\n /** Shape of the selected-tab background.\n * - square: 0 radius (default for boxed/bordered)\n * - rounded: small radius\n * - pill: full radius capsule\n * - trapezoid: bottom-wider CSS clip-path trapezoid\n * - angled: slanted right edge\n */\n shape?: TabShape;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n tabListClassName?: string;\n panelClassName?: string;\n fullWidth?: boolean;\n};\n\nconst shapeStyle: Record<TabShape, React.CSSProperties> = {\n square: {},\n rounded: { borderRadius: '0.5rem 0.5rem 0 0' },\n pill: { borderRadius: '9999px' },\n trapezoid: {\n clipPath: 'polygon(8% 0%, 92% 0%, 100% 100%, 0% 100%)',\n borderRadius: 0,\n },\n angled: {\n clipPath: 'polygon(0 0, calc(100% - 10px) 0, 100% 100%, 0 100%)',\n borderRadius: 0,\n },\n};\n\nconst TabRoot: React.FC<TabProps> = ({\n children,\n defaultValue,\n value: valueProp,\n onValueChange,\n variant = 'line',\n shape,\n size = 'md',\n className = '',\n tabListClassName = '',\n panelClassName = '',\n fullWidth = false,\n}) => {\n const panes = useMemo(() => {\n const out: React.ReactElement<TabPaneProps>[] = [];\n Children.forEach(children, (child) => {\n if (isTabPane(child, TabPane)) {\n out.push(child);\n }\n });\n return out;\n }, [children]);\n\n const firstId = panes.find(p => !p.props.disabled)?.props.id ?? '';\n const isControlled = valueProp !== undefined;\n const [uncontrolled, setUncontrolled] = useState<string | undefined>(defaultValue);\n const requested = isControlled ? valueProp : uncontrolled;\n const active = panes.find(p => p.props.id === requested && !p.props.disabled)?.props.id ?? firstId;\n const baseId = useId();\n const tabRefs = useRef(new Map<string, HTMLButtonElement>());\n const [focusedId, setFocusedId] = useState<string | null>(null);\n const focusable = panes.find(p => p.props.id === focusedId && !p.props.disabled)?.props.id ?? active;\n const enabledPanes = panes.filter(p => !p.props.disabled);\n const onTabKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, id: string) => {\n if (event.altKey || event.ctrlKey || event.metaKey || event.nativeEvent.isComposing) return;\n const index = enabledPanes.findIndex(p => p.props.id === id);\n if (!isTabNavigationKey(event.key)) return;\n const target = nextTab(enabledPanes, index, event.key);\n event.preventDefault();\n tabRefs.current.get(target?.props.id ?? '')?.focus();\n };\n\n const setActive = useCallback(\n (id: string) => {\n if (!isControlled) setUncontrolled(id);\n onValueChange?.(id);\n },\n [isControlled, onValueChange]\n );\n\n const listVariantClass =\n variant === 'boxed'\n ? 'tabs-boxed'\n : variant === 'lifted'\n ? 'tabs-lifted'\n : variant === 'bordered'\n ? 'border border-base-300 rounded-t-lg p-0 overflow-hidden'\n : '';\n\n const listBase =\n variant === 'line' ? 'tabs gap-0 border-b border-base-300 w-full' : `tabs ${listVariantClass} w-full flex-wrap`;\n const sizeClass = size === 'sm' ? 'tab-sm' : size === 'lg' ? 'tab-lg' : '';\n\n const getTriggerStateClass = (selected: boolean) => {\n if (selected) {\n if (variant === 'line') {\n return 'tab-active z-[1] -mb-px font-semibold text-primary border-b-2 border-primary bg-base-100/80';\n }\n if (variant === 'boxed' || variant === 'lifted') {\n return 'tab-active font-semibold text-primary ring-1 ring-inset ring-primary/30 bg-primary/10';\n }\n if (variant === 'bordered') {\n return 'tab-active -mb-px z-[1] font-semibold text-primary border-b-2 border-primary bg-base-200/90';\n }\n return 'tab-active font-semibold text-primary';\n }\n if (variant === 'line') {\n return 'border-b-2 border-transparent font-normal text-base-content/50 hover:text-base-content/75';\n }\n return 'font-normal text-base-content/50 hover:text-base-content/80 hover:bg-base-200/50';\n };\n\n const activePane = panes.find((p) => p.props.id === active);\n const panel = activePane?.props.children;\n\n const resolvedShape = shape ?? (variant === 'lifted' ? 'rounded' : undefined);\n\n return (\n <div className={cn('w-full', className)}>\n <div\n role=\"tablist\"\n onBlur={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setFocusedId(null);\n }}\n className={cn(listBase, fullWidth && 'grid', tabListClassName)}\n style={fullWidth ? { gridTemplateColumns: `repeat(${panes.length}, minmax(0, 1fr))` } : undefined}\n >\n {panes.map((p) => {\n const { id, label, disabled, tabClassName = '' } = p.props;\n const selected = id === active;\n const needShape = selected && resolvedShape && resolvedShape !== 'square';\n return (\n <button\n key={id}\n ref={element => {\n if (element) tabRefs.current.set(id, element);\n else tabRefs.current.delete(id);\n }}\n type=\"button\"\n id={`${baseId}-tab-${encodeURIComponent(id)}`}\n role=\"tab\"\n aria-controls={`${baseId}-panel`}\n aria-selected={selected}\n tabIndex={!disabled && id === focusable ? 0 : -1}\n disabled={disabled}\n className={cn(\n 'tab',\n 'transition-all duration-150',\n sizeClass,\n fullWidth && 'w-full justify-center',\n getTriggerStateClass(selected),\n disabled ? 'tab-disabled pointer-events-none opacity-50' : 'cursor-pointer',\n tabClassName,\n )}\n style={needShape ? shapeStyle[resolvedShape!] : undefined}\n onFocus={() => setFocusedId(id)}\n onKeyDown={event => onTabKeyDown(event, id)}\n onClick={() => setActive(id)}\n >\n {label}\n </button>\n );\n })}\n </div>\n <div\n role=\"tabpanel\"\n id={`${baseId}-panel`}\n aria-labelledby={activePane ? `${baseId}-tab-${encodeURIComponent(activePane.props.id)}` : undefined}\n className={cn('mt-3 w-full', panelClassName, activePane?.props.className)}\n >\n {panel}\n </div>\n </div>\n );\n};\n\nexport type TabComponent = React.FC<TabProps> & { Pane: typeof TabPane };\nconst Tab = TabRoot as TabComponent;\nTab.Pane = TabPane;\n\nexport { Tab };\n\nexport default Tab;\n"],"mappings":"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"}
|
|
@@ -1,52 +1,47 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { isTabNavigationKey as t, nextTab as n } from "./tabNavigation.js";
|
|
3
|
+
import { useId as r, useRef as i, useState as a } from "react";
|
|
4
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/Tabs/index.tsx
|
|
5
|
-
var
|
|
6
|
+
var c = {
|
|
6
7
|
xs: "tabs-xs",
|
|
7
8
|
sm: "tabs-sm",
|
|
8
9
|
md: "tabs-md",
|
|
9
10
|
lg: "tabs-lg",
|
|
10
11
|
xl: "tabs-xl"
|
|
11
12
|
};
|
|
12
|
-
function
|
|
13
|
-
let
|
|
14
|
-
if (e.target !== e.currentTarget || e.defaultPrevented || e.nativeEvent.isComposing || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
a = w[(i + t + w.length) % w.length];
|
|
21
|
-
} else return;
|
|
22
|
-
e.preventDefault(), e.stopPropagation(), a && (x.current.get(a.key)?.focus(), _ === "automatic" && a.key !== c && l(a.key));
|
|
23
|
-
}, D = u === "border" ? "tabs-border" : u === "lift" ? "tabs-lift" : u === "pill" ? "tabs-box dishui-tabs-pill" : "tabs-box";
|
|
24
|
-
return /* @__PURE__ */ i("div", {
|
|
25
|
-
id: b,
|
|
13
|
+
function l({ items: l, value: u, onChange: d, variant: f = "box", size: p = "sm", block: m = !1, id: h, "aria-label": g, "aria-labelledby": _, orientation: v = "horizontal", activationMode: y = "manual", className: b = "" }) {
|
|
14
|
+
let x = r(), S = h ?? `tabs-${x}`, C = i(/* @__PURE__ */ new Map()), [w, T] = a(null), E = l.filter((e) => !e.disabled), D = E.find((e) => e.key === w)?.key ?? E.find((e) => e.key === u)?.key ?? E[0]?.key, O = (e, r) => {
|
|
15
|
+
if (e.target !== e.currentTarget || e.defaultPrevented || e.nativeEvent.isComposing || e.altKey || e.ctrlKey || e.metaKey || !t(e.key, v)) return;
|
|
16
|
+
let i = n(E, E.findIndex((e) => e.key === r), e.key, v);
|
|
17
|
+
e.preventDefault(), e.stopPropagation(), i && (C.current.get(i.key)?.focus(), y === "automatic" && i.key !== u && d(i.key));
|
|
18
|
+
}, k = f === "border" ? "tabs-border" : f === "lift" ? "tabs-lift" : f === "pill" ? "tabs-box dishui-tabs-pill" : "tabs-box";
|
|
19
|
+
return /* @__PURE__ */ o("div", {
|
|
20
|
+
id: S,
|
|
26
21
|
role: "tablist",
|
|
27
|
-
"aria-label":
|
|
28
|
-
"aria-labelledby":
|
|
29
|
-
"aria-orientation":
|
|
22
|
+
"aria-label": g,
|
|
23
|
+
"aria-labelledby": _,
|
|
24
|
+
"aria-orientation": v,
|
|
30
25
|
onBlur: (e) => {
|
|
31
|
-
e.currentTarget.contains(e.relatedTarget) ||
|
|
26
|
+
e.currentTarget.contains(e.relatedTarget) || T(null);
|
|
32
27
|
},
|
|
33
|
-
className: e("tabs h-auto min-h-0",
|
|
34
|
-
children:
|
|
28
|
+
className: e("tabs h-auto min-h-0", c[p], k, m && "w-full", v === "vertical" && "flex flex-col", b),
|
|
29
|
+
children: l.map((t) => /* @__PURE__ */ s("button", {
|
|
35
30
|
ref: (e) => {
|
|
36
|
-
e ?
|
|
31
|
+
e ? C.current.set(t.key, e) : C.current.delete(t.key);
|
|
37
32
|
},
|
|
38
|
-
id: t.id ?? `${
|
|
33
|
+
id: t.id ?? `${S}-tab-${encodeURIComponent(t.key)}`,
|
|
39
34
|
role: "tab",
|
|
40
35
|
type: "button",
|
|
41
|
-
"aria-selected":
|
|
36
|
+
"aria-selected": u === t.key,
|
|
42
37
|
"aria-controls": t.panelId,
|
|
43
|
-
tabIndex: !t.disabled && t.key ===
|
|
38
|
+
tabIndex: !t.disabled && t.key === D ? 0 : -1,
|
|
44
39
|
disabled: t.disabled,
|
|
45
|
-
onFocus: () =>
|
|
46
|
-
onKeyDown: (e) =>
|
|
47
|
-
onClick: () =>
|
|
48
|
-
className: e("tab min-h-0 gap-1.5",
|
|
49
|
-
children: [t.icon && /* @__PURE__ */
|
|
40
|
+
onFocus: () => T(t.key),
|
|
41
|
+
onKeyDown: (e) => O(e, t.key),
|
|
42
|
+
onClick: () => d(t.key),
|
|
43
|
+
className: e("tab min-h-0 gap-1.5", m && "flex-1", u === t.key && "tab-active", t.disabled && "tab-disabled"),
|
|
44
|
+
children: [t.icon && /* @__PURE__ */ o("span", {
|
|
50
45
|
className: "[&>svg]:h-[1.1em] [&>svg]:w-[1.1em]",
|
|
51
46
|
children: t.icon
|
|
52
47
|
}), t.label]
|
|
@@ -54,6 +49,6 @@ function s({ items: s, value: c, onChange: l, variant: u = "box", size: d = "sm"
|
|
|
54
49
|
});
|
|
55
50
|
}
|
|
56
51
|
//#endregion
|
|
57
|
-
export {
|
|
52
|
+
export { l as default };
|
|
58
53
|
|
|
59
54
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Tabs/index.tsx"],"sourcesContent":["import React, { useId, useRef, useState } from 'react';\nimport { cn } from '../../utils/cn';\n\nexport type TabItem<T extends string = string> = {\n key: T;\n label: React.ReactNode;\n /** 可选:标签前的图标 */\n icon?: React.ReactNode;\n /** 禁用该标签 */\n disabled?: boolean;\n /** Stable tab ID; use as the external panel's aria-labelledby. Generated when omitted. */\n id?: string;\n /** ID of a caller-rendered role=\"tabpanel\". Omit when used only as a filter. */\n panelId?: string;\n};\n\nexport type TabsProps<T extends string = string> = {\n items: readonly TabItem<T>[];\n /** 当前激活的 tab key */\n value: T;\n /** 切换回调 */\n onChange: (key: T) => void;\n /**\n * 外观(基于 daisyUI 5 的 tabs 修饰类):\n * - `box` 实心分段(tabs-box)\n * - `border` 底部下划线(tabs-border)\n * - `lift` 抬升的文件夹页签(tabs-lift)\n * - `pill` 胶囊/圆角实心(DishUI 扩展)\n */\n variant?: 'box' | 'border' | 'lift' | 'pill';\n /** 尺寸:xs / sm / md / lg / xl */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** 占满可用宽度,各标签平分 */\n block?: boolean;\n /** Tablist ID and prefix for generated tab IDs. */\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n orientation?: 'horizontal' | 'vertical';\n /** Manual (default): arrows move focus; Enter/Space activate. Automatic: arrows also activate. */\n activationMode?: 'manual' | 'automatic';\n className?: string;\n};\n\nconst SIZE_CLASS: Record<NonNullable<TabsProps['size']>, string> = {\n xs: 'tabs-xs',\n sm: 'tabs-sm',\n md: 'tabs-md',\n lg: 'tabs-lg',\n xl: 'tabs-xl',\n};\n\n/**\n * 标签页切换条。基于 daisyUI 5 的 `tabs` 系列修饰类,\n * 支持 box / border / lift / pill 四种外观、五档尺寸与等宽(block)模式。\n */\nfunction Tabs<T extends string = string>({\n items,\n value,\n onChange,\n variant = 'box',\n size = 'sm',\n block = false,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n orientation = 'horizontal',\n activationMode = 'manual',\n className = '',\n}: TabsProps<T>) {\n const generatedId = useId();\n const listId = id ?? `tabs-${generatedId}`;\n const buttons = useRef(new Map<T, HTMLButtonElement>());\n const [focusedKey, setFocusedKey] = useState<T | null>(null);\n const enabledItems = items.filter(item => !item.disabled);\n const tabStop = enabledItems.find(item => item.key === focusedKey)?.key\n ?? enabledItems.find(item => item.key === value)?.key\n ?? enabledItems[0]?.key;\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, key: T) => {\n if (event.target !== event.currentTarget || event.defaultPrevented || event.nativeEvent.isComposing || event.altKey || event.ctrlKey || event.metaKey) return;\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Tabs/index.tsx"],"sourcesContent":["import React, { useId, useRef, useState } from 'react';\nimport { cn } from '../../utils/cn';\nimport { isTabNavigationKey, nextTab } from './tabNavigation';\n\nexport type TabItem<T extends string = string> = {\n key: T;\n label: React.ReactNode;\n /** 可选:标签前的图标 */\n icon?: React.ReactNode;\n /** 禁用该标签 */\n disabled?: boolean;\n /** Stable tab ID; use as the external panel's aria-labelledby. Generated when omitted. */\n id?: string;\n /** ID of a caller-rendered role=\"tabpanel\". Omit when used only as a filter. */\n panelId?: string;\n};\n\nexport type TabsProps<T extends string = string> = {\n items: readonly TabItem<T>[];\n /** 当前激活的 tab key */\n value: T;\n /** 切换回调 */\n onChange: (key: T) => void;\n /**\n * 外观(基于 daisyUI 5 的 tabs 修饰类):\n * - `box` 实心分段(tabs-box)\n * - `border` 底部下划线(tabs-border)\n * - `lift` 抬升的文件夹页签(tabs-lift)\n * - `pill` 胶囊/圆角实心(DishUI 扩展)\n */\n variant?: 'box' | 'border' | 'lift' | 'pill';\n /** 尺寸:xs / sm / md / lg / xl */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** 占满可用宽度,各标签平分 */\n block?: boolean;\n /** Tablist ID and prefix for generated tab IDs. */\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n orientation?: 'horizontal' | 'vertical';\n /** Manual (default): arrows move focus; Enter/Space activate. Automatic: arrows also activate. */\n activationMode?: 'manual' | 'automatic';\n className?: string;\n};\n\nconst SIZE_CLASS: Record<NonNullable<TabsProps['size']>, string> = {\n xs: 'tabs-xs',\n sm: 'tabs-sm',\n md: 'tabs-md',\n lg: 'tabs-lg',\n xl: 'tabs-xl',\n};\n\n/**\n * 标签页切换条。基于 daisyUI 5 的 `tabs` 系列修饰类,\n * 支持 box / border / lift / pill 四种外观、五档尺寸与等宽(block)模式。\n */\nfunction Tabs<T extends string = string>({\n items,\n value,\n onChange,\n variant = 'box',\n size = 'sm',\n block = false,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n orientation = 'horizontal',\n activationMode = 'manual',\n className = '',\n}: TabsProps<T>) {\n const generatedId = useId();\n const listId = id ?? `tabs-${generatedId}`;\n const buttons = useRef(new Map<T, HTMLButtonElement>());\n const [focusedKey, setFocusedKey] = useState<T | null>(null);\n const enabledItems = items.filter(item => !item.disabled);\n const tabStop = enabledItems.find(item => item.key === focusedKey)?.key\n ?? enabledItems.find(item => item.key === value)?.key\n ?? enabledItems[0]?.key;\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, key: T) => {\n if (event.target !== event.currentTarget || event.defaultPrevented || event.nativeEvent.isComposing || event.altKey || event.ctrlKey || event.metaKey) return;\n if (!isTabNavigationKey(event.key, orientation)) return; // Native button Enter/Space activation goes through onClick once.\n const target = nextTab(enabledItems, enabledItems.findIndex(item => item.key === key), event.key, orientation);\n event.preventDefault();\n event.stopPropagation();\n if (!target) return;\n buttons.current.get(target.key)?.focus();\n if (activationMode === 'automatic' && target.key !== value) onChange(target.key);\n };\n // pill 是 DishUI 扩展:在 tabs-box 基础上追加胶囊圆角类(见 components.css)\n const variantClass =\n variant === 'border' ? 'tabs-border'\n : variant === 'lift' ? 'tabs-lift'\n : variant === 'pill' ? 'tabs-box dishui-tabs-pill'\n : 'tabs-box';\n\n return (\n <div\n id={listId}\n role=\"tablist\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-orientation={orientation}\n onBlur={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setFocusedKey(null);\n }}\n className={cn(\n 'tabs h-auto min-h-0',\n SIZE_CLASS[size],\n variantClass,\n block && 'w-full',\n orientation === 'vertical' && 'flex flex-col',\n className,\n )}\n >\n {items.map((item) => (\n <button\n key={item.key}\n ref={element => {\n if (element) buttons.current.set(item.key, element);\n else buttons.current.delete(item.key);\n }}\n id={item.id ?? `${listId}-tab-${encodeURIComponent(item.key)}`}\n role=\"tab\"\n type=\"button\"\n aria-selected={value === item.key}\n aria-controls={item.panelId}\n tabIndex={!item.disabled && item.key === tabStop ? 0 : -1}\n disabled={item.disabled}\n onFocus={() => setFocusedKey(item.key)}\n onKeyDown={event => handleKeyDown(event, item.key)}\n onClick={() => onChange(item.key)}\n className={cn(\n 'tab min-h-0 gap-1.5',\n block && 'flex-1',\n value === item.key && 'tab-active',\n item.disabled && 'tab-disabled',\n )}\n >\n {item.icon && <span className=\"[&>svg]:h-[1.1em] [&>svg]:w-[1.1em]\">{item.icon}</span>}\n {item.label}\n </button>\n ))}\n </div>\n );\n}\n\nexport default Tabs;\n"],"mappings":";;;;;AA6CA,IAAM,IAA6D;CAC/D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACR;AAMA,SAAS,EAAgC,EACrC,UACA,UACA,aACA,aAAU,OACV,UAAO,MACP,WAAQ,IACR,OACA,cAAc,GACd,mBAAmB,GACnB,iBAAc,cACd,oBAAiB,UACjB,eAAY,MACC;CACb,IAAM,IAAc,EAAM,GACpB,IAAS,KAAM,QAAQ,KACvB,IAAU,kBAAO,IAAI,IAA0B,CAAC,GAChD,CAAC,GAAY,KAAiB,EAAmB,IAAI,GACrD,IAAe,EAAM,QAAO,MAAQ,CAAC,EAAK,QAAQ,GAClD,IAAU,EAAa,MAAK,MAAQ,EAAK,QAAQ,CAAU,CAAC,EAAE,OAC7D,EAAa,MAAK,MAAQ,EAAK,QAAQ,CAAK,CAAC,EAAE,OAC/C,EAAa,EAAE,EAAE,KAElB,KAAiB,GAA+C,MAAW;EAE7E,IADI,EAAM,WAAW,EAAM,iBAAiB,EAAM,oBAAoB,EAAM,YAAY,eAAe,EAAM,UAAU,EAAM,WAAW,EAAM,WAC1I,CAAC,EAAmB,EAAM,KAAK,CAAW,GAAG;EACjD,IAAM,IAAS,EAAQ,GAAc,EAAa,WAAU,MAAQ,EAAK,QAAQ,CAAG,GAAG,EAAM,KAAK,CAAW;EAC7G,EAAM,eAAe,GACrB,EAAM,gBAAgB,GACjB,MACL,EAAQ,QAAQ,IAAI,EAAO,GAAG,CAAC,EAAE,MAAM,GACnC,MAAmB,eAAe,EAAO,QAAQ,KAAO,EAAS,EAAO,GAAG;CACnF,GAEM,IACF,MAAY,WAAW,gBACrB,MAAY,SAAS,cACrB,MAAY,SAAS,8BACrB;CAEN,OACI,kBAAC,OAAD;EACI,IAAI;EACJ,MAAK;EACL,cAAY;EACZ,mBAAiB;EACjB,oBAAkB;EAClB,SAAQ,MAAS;GACb,AAAK,EAAM,cAAc,SAAS,EAAM,aAA4B,KAAG,EAAc,IAAI;EAC7F;EACA,WAAW,EACP,uBACA,EAAW,IACX,GACA,KAAS,UACT,MAAgB,cAAc,iBAC9B,CACJ;EAEC,UAAA,EAAM,KAAK,MACR,kBAAC,UAAD;GAEI,MAAK,MAAW;IACZ,AAAI,IAAS,EAAQ,QAAQ,IAAI,EAAK,KAAK,CAAO,IAC7C,EAAQ,QAAQ,OAAO,EAAK,GAAG;GACxC;GACA,IAAI,EAAK,MAAM,GAAG,EAAO,OAAO,mBAAmB,EAAK,GAAG;GAC3D,MAAK;GACL,MAAK;GACL,iBAAe,MAAU,EAAK;GAC9B,iBAAe,EAAK;GACpB,UAAU,CAAC,EAAK,YAAY,EAAK,QAAQ,IAAU,IAAI;GACvD,UAAU,EAAK;GACf,eAAe,EAAc,EAAK,GAAG;GACrC,YAAW,MAAS,EAAc,GAAO,EAAK,GAAG;GACjD,eAAe,EAAS,EAAK,GAAG;GAChC,WAAW,EACP,uBACA,KAAS,UACT,MAAU,EAAK,OAAO,cACtB,EAAK,YAAY,cACrB;GArBJ,UAAA,CAuBK,EAAK,QAAQ,kBAAC,QAAD;IAAM,WAAU;IAAuC,UAAA,EAAK;GAAW,CAAA,GACpF,EAAK,KACF;EAxBC,GAAA,EAAK,GAwBN,CACX;CACA,CAAA;AAEb"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/components/Tabs/tabNavigation.ts
|
|
2
|
+
function e(e, t = "horizontal") {
|
|
3
|
+
return e === "Home" || e === "End" || e === (t === "vertical" ? "ArrowUp" : "ArrowLeft") || e === (t === "vertical" ? "ArrowDown" : "ArrowRight");
|
|
4
|
+
}
|
|
5
|
+
function t(e, t, n, r = "horizontal") {
|
|
6
|
+
if (n === "Home") return e[0];
|
|
7
|
+
if (n === "End") return e[e.length - 1];
|
|
8
|
+
let i = r === "vertical" ? "ArrowUp" : "ArrowLeft", a = r === "vertical" ? "ArrowDown" : "ArrowRight";
|
|
9
|
+
if (n === i || n === a) return e[(t + (n === a ? 1 : -1) + e.length) % e.length];
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { e as isTabNavigationKey, t as nextTab };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=tabNavigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabNavigation.js","names":[],"sources":["../../../src/components/Tabs/tabNavigation.ts"],"sourcesContent":["export function isTabNavigationKey(key: string, orientation: 'horizontal' | 'vertical' = 'horizontal'): boolean {\n return key === 'Home' || key === 'End' || key === (orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft') || key === (orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight');\n}\n\nexport function nextTab<T>(items: readonly T[], index: number, key: string, orientation: 'horizontal' | 'vertical' = 'horizontal'): T | undefined {\n if (key === 'Home') return items[0];\n if (key === 'End') return items[items.length - 1];\n const previous = orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft';\n const next = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight';\n if (key !== previous && key !== next) return undefined;\n return items[(index + (key === next ? 1 : -1) + items.length) % items.length];\n}\n"],"mappings":";AAAA,SAAgB,EAAmB,GAAa,IAAyC,cAAuB;CAC5G,OAAO,MAAQ,UAAU,MAAQ,SAAS,OAAS,MAAgB,aAAa,YAAY,gBAAgB,OAAS,MAAgB,aAAa,cAAc;AACpK;AAEA,SAAgB,EAAW,GAAqB,GAAe,GAAa,IAAyC,cAA6B;CAC9I,IAAI,MAAQ,QAAQ,OAAO,EAAM;CACjC,IAAI,MAAQ,OAAO,OAAO,EAAM,EAAM,SAAS;CAC/C,IAAM,IAAW,MAAgB,aAAa,YAAY,aACpD,IAAO,MAAgB,aAAa,cAAc;CACpD,UAAQ,KAAY,MAAQ,GAChC,OAAO,GAAO,KAAS,MAAQ,IAAO,IAAI,MAAM,EAAM,UAAU,EAAM;AAC1E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { CodeEditorProps, ContextMenuItem } from './CodeEditor';
|
|
2
|
+
import { type CodeEditorProps, type ContextMenuItem } from './CodeEditor';
|
|
3
3
|
import { MarkdownSection, FootnoteInfo } from './markdownExtensions';
|
|
4
4
|
export interface MarkdownEditorProps extends Omit<CodeEditorProps, 'language' | 'extensions' | 'autocompletion' | 'contextMenu'> {
|
|
5
5
|
showTableOfContents?: boolean;
|
|
@@ -36,22 +36,20 @@ var f = (e) => {
|
|
|
36
36
|
text: e.state.doc.sliceString(s.from, c.to)
|
|
37
37
|
};
|
|
38
38
|
}, m = ({ value: m = "", onChange: h, showTableOfContents: g = !0, showFootnotes: _ = !0, onSectionsChange: v, onFootnotesChange: y, tableOfContentsPosition: b = "right", customExtensions: x = [], customContextMenu: S = [], ...C }) => {
|
|
39
|
-
let [w, T] = l([]), [E, D] = l([]), [O, k] = l(1)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
]);
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
}, []), j = [
|
|
39
|
+
let [w, T] = l([]), [E, D] = l([]), [O, k] = l(1), A = c(null), j = c(null), M = c(null), N = o((e) => {
|
|
40
|
+
if (M.current === e) return;
|
|
41
|
+
M.current = e;
|
|
42
|
+
let t = i(e), n = r(e);
|
|
43
|
+
T(t), D(n), v?.(t), y?.(n);
|
|
44
|
+
}, [v, y]);
|
|
45
|
+
s(() => {
|
|
46
|
+
j.current !== m && (j.current = m, N(m));
|
|
47
|
+
}, [m, N]);
|
|
48
|
+
let P = o((e) => {
|
|
49
|
+
N(e), h?.(e);
|
|
50
|
+
}, [N, h]), F = o((e) => {
|
|
51
|
+
A.current?.jumpToLine(e);
|
|
52
|
+
}, []), I = [
|
|
55
53
|
{
|
|
56
54
|
label: "格式化表格",
|
|
57
55
|
action: (e) => {
|
|
@@ -115,7 +113,7 @@ var f = (e) => {
|
|
|
115
113
|
}
|
|
116
114
|
},
|
|
117
115
|
...S
|
|
118
|
-
],
|
|
116
|
+
], L = [...a(), ...x], R = () => {
|
|
119
117
|
let e = [
|
|
120
118
|
"dark",
|
|
121
119
|
"retro",
|
|
@@ -127,17 +125,17 @@ var f = (e) => {
|
|
|
127
125
|
style: { width: "320px" },
|
|
128
126
|
children: [g && /* @__PURE__ */ u(t, {
|
|
129
127
|
sections: w,
|
|
130
|
-
onSectionClick:
|
|
128
|
+
onSectionClick: F,
|
|
131
129
|
currentLine: O,
|
|
132
130
|
theme: C.theme
|
|
133
131
|
}), _ && E.length > 0 && /* @__PURE__ */ u(n, {
|
|
134
132
|
footnotes: E,
|
|
135
|
-
onFootnoteClick:
|
|
133
|
+
onFootnoteClick: F,
|
|
136
134
|
onJumpToDefinition: (e, t) => {
|
|
137
|
-
console.log(`Jumping to definition of footnote ${e} at line ${t}`),
|
|
135
|
+
console.log(`Jumping to definition of footnote ${e} at line ${t}`), F(t);
|
|
138
136
|
},
|
|
139
137
|
onJumpToReference: (e, t) => {
|
|
140
|
-
console.log(`Jumping to reference of footnote ${e} at line ${t}`),
|
|
138
|
+
console.log(`Jumping to reference of footnote ${e} at line ${t}`), F(t);
|
|
141
139
|
},
|
|
142
140
|
theme: C.theme
|
|
143
141
|
})]
|
|
@@ -146,20 +144,21 @@ var f = (e) => {
|
|
|
146
144
|
return /* @__PURE__ */ d("div", {
|
|
147
145
|
className: "markdown-editor-wrapper flex gap-4",
|
|
148
146
|
children: [
|
|
149
|
-
b === "left" && (g || _) &&
|
|
147
|
+
b === "left" && (g || _) && R(),
|
|
150
148
|
/* @__PURE__ */ u("div", {
|
|
151
149
|
className: "markdown-editor-main flex-1",
|
|
152
150
|
children: /* @__PURE__ */ u(e, {
|
|
153
151
|
...C,
|
|
152
|
+
ref: A,
|
|
154
153
|
value: m,
|
|
155
|
-
onChange:
|
|
154
|
+
onChange: P,
|
|
156
155
|
language: "markdown",
|
|
157
156
|
autocompletion: !1,
|
|
158
|
-
extensions:
|
|
159
|
-
contextMenu:
|
|
157
|
+
extensions: L,
|
|
158
|
+
contextMenu: I
|
|
160
159
|
})
|
|
161
160
|
}),
|
|
162
|
-
b === "right" && (g || _) &&
|
|
161
|
+
b === "right" && (g || _) && R(),
|
|
163
162
|
/* @__PURE__ */ u("style", { children: "\n .markdown-sidebar-light {\n color: #000000;\n }\n .markdown-sidebar-dark {\n color: #d4d4d4;\n }\n .markdown-sidebar-dark .card,\n .markdown-sidebar-dark .toc-panel,\n .markdown-sidebar-dark .footnote-panel,\n .markdown-sidebar-dark .outline-panel,\n .markdown-sidebar-dark .variable-panel {\n background-color: #1e1e1e !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .border-base-300 {\n border-color: #3e3e42 !important;\n }\n .markdown-sidebar-dark input,\n .markdown-sidebar-dark .input {\n background-color: #2d2d2d !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark button,\n .markdown-sidebar-dark .btn {\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .hover\\:bg-base-200:hover,\n .markdown-sidebar-dark .toc-item:hover,\n .markdown-sidebar-dark .outline-item:hover,\n .markdown-sidebar-dark .variable-item:hover,\n .markdown-sidebar-dark .footnote-item:hover {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-primary\\/20 {\n background-color: rgba(96, 165, 250, 0.2) !important;\n }\n .markdown-sidebar-dark .bg-base-200 {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-warning\\/10 {\n background-color: rgba(251, 191, 36, 0.1) !important;\n }\n .markdown-sidebar-dark .border-warning\\/30 {\n border-color: rgba(251, 191, 36, 0.3) !important;\n }\n .markdown-sidebar-dark .text-warning {\n color: #fbbf24 !important;\n }\n " })
|
|
164
163
|
]
|
|
165
164
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../../../src/components/pro/CodeEditor/editors/MarkdownEditor.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback, useRef } from 'react';\nimport CodeEditor, { CodeEditorProps, ContextMenuItem } from './CodeEditor';\nimport TableOfContentsPanel from '../panels/TableOfContentsPanel';\nimport FootnotePanel from '../panels/FootnotePanel';\nimport {\n markdownExtensions,\n extractMarkdownSections,\n extractFootnotes,\n MarkdownSection,\n FootnoteInfo,\n} from './markdownExtensions';\nimport { EditorView } from '@codemirror/view';\n\nexport interface MarkdownEditorProps extends Omit<CodeEditorProps, 'language' | 'extensions' | 'autocompletion' | 'contextMenu'> {\n showTableOfContents?: boolean;\n showFootnotes?: boolean;\n onSectionsChange?: (sections: MarkdownSection[]) => void;\n onFootnotesChange?: (footnotes: FootnoteInfo[]) => void;\n tableOfContentsPosition?: 'left' | 'right';\n customExtensions?: CodeEditorProps['extensions'];\n customContextMenu?: ContextMenuItem[];\n}\n\n// 表格格式化函数\nconst formatMarkdownTable = (tableText: string): string => {\n const lines = tableText.split('\\n').filter(line => line.trim());\n if (lines.length < 2) return tableText;\n\n // 解析表格\n const rows = lines.map(line => {\n return line.split('|')\n .map(cell => cell.trim())\n .filter((_, i, arr) => i > 0 && i < arr.length - 1); // 移除首尾空元素\n });\n\n // 计算每列的最大宽度\n const colWidths: number[] = [];\n rows.forEach(row => {\n row.forEach((cell, i) => {\n colWidths[i] = Math.max(colWidths[i] || 0, cell.length);\n });\n });\n\n // 格式化每一行\n const formattedRows = rows.map((row, rowIndex) => {\n const cells = row.map((cell, colIndex) => {\n // 对于分隔行,保持对齐标记\n if (rowIndex === 1 && cell.match(/^:?-+:?$/)) {\n const hasLeft = cell.startsWith(':');\n const hasRight = cell.endsWith(':');\n const width = colWidths[colIndex];\n\n if (hasLeft && hasRight) {\n return ':' + '-'.repeat(width - 2) + ':';\n } else if (hasLeft) {\n return ':' + '-'.repeat(width - 1);\n } else if (hasRight) {\n return '-'.repeat(width - 1) + ':';\n } else {\n return '-'.repeat(width);\n }\n }\n\n return cell.padEnd(colWidths[colIndex], ' ');\n });\n\n return '| ' + cells.join(' | ') + ' |';\n });\n\n return formattedRows.join('\\n');\n};\n\n// 检测是否在表格中\nconst isInTable = (view: EditorView, pos: number): { start: number; end: number; text: string } | null => {\n const line = view.state.doc.lineAt(pos);\n const lineText = line.text;\n\n if (!lineText.includes('|')) return null;\n\n // 向上查找表格开始\n let startLine = line.number;\n let currentLine = line.number - 1;\n while (currentLine >= 1) {\n const l = view.state.doc.line(currentLine);\n if (!l.text.includes('|')) break;\n startLine = currentLine;\n currentLine--;\n }\n\n // 向下查找表格结束\n let endLine = line.number;\n currentLine = line.number + 1;\n const totalLines = view.state.doc.lines;\n while (currentLine <= totalLines) {\n const l = view.state.doc.line(currentLine);\n if (!l.text.includes('|')) break;\n endLine = currentLine;\n currentLine++;\n }\n\n const startLineObj = view.state.doc.line(startLine);\n const endLineObj = view.state.doc.line(endLine);\n\n return {\n start: startLineObj.from,\n end: endLineObj.to,\n text: view.state.doc.sliceString(startLineObj.from, endLineObj.to),\n };\n};\n\nconst MarkdownEditor: React.FC<MarkdownEditorProps> = ({\n value = '',\n onChange,\n showTableOfContents = true,\n showFootnotes = true,\n onSectionsChange,\n onFootnotesChange,\n tableOfContentsPosition = 'right',\n customExtensions = [],\n customContextMenu = [],\n ...codeEditorProps\n}) => {\n const [sections, setSections] = useState<MarkdownSection[]>([]);\n const [footnotes, setFootnotes] = useState<FootnoteInfo[]>([]);\n const [currentLine, setCurrentLine] = useState(1);\n const editorViewRef = useRef<EditorView | null>(null);\n\n // 提取章节和脚注\n useEffect(() => {\n const newSections = extractMarkdownSections(value);\n const newFootnotes = extractFootnotes(value);\n\n setSections(newSections);\n setFootnotes(newFootnotes);\n\n if (onSectionsChange) {\n onSectionsChange(newSections);\n }\n\n if (onFootnotesChange) {\n onFootnotesChange(newFootnotes);\n }\n }, [value, onSectionsChange, onFootnotesChange]);\n\n // 跳转到指定行\n const jumpToLine = useCallback((line: number) => {\n const lineElement = document.querySelector(`.cm-line:nth-child(${line})`);\n if (lineElement) {\n lineElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }, []);\n\n // Markdown 专属上下文菜单\n const markdownContextMenu: ContextMenuItem[] = [\n {\n label: '格式化表格',\n action: (view) => {\n const selection = view.state.selection.main;\n const tableInfo = isInTable(view, selection.from);\n\n if (tableInfo) {\n const formatted = formatMarkdownTable(tableInfo.text);\n view.dispatch({\n changes: {\n from: tableInfo.start,\n to: tableInfo.end,\n insert: formatted,\n },\n });\n } else {\n alert('请将光标放在表格中');\n }\n },\n },\n { label: '', action: () => { }, separator: true },\n {\n label: '剪切',\n action: (view) => {\n const selection = view.state.selection.main;\n const text = view.state.sliceDoc(selection.from, selection.to);\n navigator.clipboard.writeText(text);\n view.dispatch({\n changes: { from: selection.from, to: selection.to, insert: '' },\n });\n },\n },\n {\n label: '复制',\n action: (view) => {\n const selection = view.state.selection.main;\n const text = view.state.sliceDoc(selection.from, selection.to);\n navigator.clipboard.writeText(text);\n },\n },\n {\n label: '粘贴',\n action: async (view) => {\n const text = await navigator.clipboard.readText();\n const selection = view.state.selection.main;\n view.dispatch({\n changes: { from: selection.from, to: selection.to, insert: text },\n });\n },\n },\n { label: '', action: () => { }, separator: true },\n {\n label: '全选',\n action: (view) => {\n view.dispatch({\n selection: { anchor: 0, head: view.state.doc.length },\n });\n },\n },\n ...customContextMenu,\n ];\n\n // 合并扩展\n const allExtensions = [...markdownExtensions(), ...customExtensions];\n\n // 侧边栏内容\n const renderSidebar = () => {\n // 根据主题决定侧边栏的样式类\n const darkThemes = ['dark', 'retro', 'cyberpunk', 'matrix'];\n const sidebarThemeClass =\n darkThemes.includes(codeEditorProps.theme || 'light')\n ? 'markdown-sidebar-dark'\n : 'markdown-sidebar-light';\n\n return (\n <div className={`markdown-sidebar space-y-4 ${sidebarThemeClass}`} style={{ width: '320px' }}>\n {showTableOfContents && (\n <TableOfContentsPanel\n sections={sections}\n onSectionClick={jumpToLine}\n currentLine={currentLine}\n theme={codeEditorProps.theme}\n />\n )}\n {showFootnotes && footnotes.length > 0 && (\n <FootnotePanel\n footnotes={footnotes}\n onFootnoteClick={jumpToLine}\n onJumpToDefinition={(id, line) => {\n console.log(`Jumping to definition of footnote ${id} at line ${line}`);\n jumpToLine(line);\n }}\n onJumpToReference={(id, line) => {\n console.log(`Jumping to reference of footnote ${id} at line ${line}`);\n jumpToLine(line);\n }}\n theme={codeEditorProps.theme}\n />\n )}\n </div>\n );\n };\n\n return (\n <div className=\"markdown-editor-wrapper flex gap-4\">\n {/* 左侧边栏 */}\n {tableOfContentsPosition === 'left' && (showTableOfContents || showFootnotes) && renderSidebar()}\n\n {/* 编辑器 */}\n <div className=\"markdown-editor-main flex-1\">\n <CodeEditor\n {...codeEditorProps}\n value={value}\n onChange={onChange}\n language=\"markdown\"\n autocompletion={false}\n extensions={allExtensions}\n contextMenu={markdownContextMenu}\n />\n </div>\n\n {/* 右侧边栏 */}\n {tableOfContentsPosition === 'right' && (showTableOfContents || showFootnotes) && renderSidebar()}\n\n {/* 侧边栏主题样式 */}\n <style>{`\n .markdown-sidebar-light {\n color: #000000;\n }\n .markdown-sidebar-dark {\n color: #d4d4d4;\n }\n .markdown-sidebar-dark .card,\n .markdown-sidebar-dark .toc-panel,\n .markdown-sidebar-dark .footnote-panel,\n .markdown-sidebar-dark .outline-panel,\n .markdown-sidebar-dark .variable-panel {\n background-color: #1e1e1e !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .border-base-300 {\n border-color: #3e3e42 !important;\n }\n .markdown-sidebar-dark input,\n .markdown-sidebar-dark .input {\n background-color: #2d2d2d !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark button,\n .markdown-sidebar-dark .btn {\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .hover\\\\:bg-base-200:hover,\n .markdown-sidebar-dark .toc-item:hover,\n .markdown-sidebar-dark .outline-item:hover,\n .markdown-sidebar-dark .variable-item:hover,\n .markdown-sidebar-dark .footnote-item:hover {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-primary\\\\/20 {\n background-color: rgba(96, 165, 250, 0.2) !important;\n }\n .markdown-sidebar-dark .bg-base-200 {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-warning\\\\/10 {\n background-color: rgba(251, 191, 36, 0.1) !important;\n }\n .markdown-sidebar-dark .border-warning\\\\/30 {\n border-color: rgba(251, 191, 36, 0.3) !important;\n }\n .markdown-sidebar-dark .text-warning {\n color: #fbbf24 !important;\n }\n `}</style>\n </div>\n );\n};\n\nexport default MarkdownEditor;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../../../src/components/pro/CodeEditor/editors/MarkdownEditor.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback, useRef } from 'react';\nimport CodeEditor, { type CodeEditorProps, type CodeEditorHandle, type ContextMenuItem } from './CodeEditor';\nimport TableOfContentsPanel from '../panels/TableOfContentsPanel';\nimport FootnotePanel from '../panels/FootnotePanel';\nimport {\n markdownExtensions,\n extractMarkdownSections,\n extractFootnotes,\n MarkdownSection,\n FootnoteInfo,\n} from './markdownExtensions';\nimport { EditorView } from '@codemirror/view';\n\nexport interface MarkdownEditorProps extends Omit<CodeEditorProps, 'language' | 'extensions' | 'autocompletion' | 'contextMenu'> {\n showTableOfContents?: boolean;\n showFootnotes?: boolean;\n onSectionsChange?: (sections: MarkdownSection[]) => void;\n onFootnotesChange?: (footnotes: FootnoteInfo[]) => void;\n tableOfContentsPosition?: 'left' | 'right';\n customExtensions?: CodeEditorProps['extensions'];\n customContextMenu?: ContextMenuItem[];\n}\n\n// 表格格式化函数\nconst formatMarkdownTable = (tableText: string): string => {\n const lines = tableText.split('\\n').filter(line => line.trim());\n if (lines.length < 2) return tableText;\n\n // 解析表格\n const rows = lines.map(line => {\n return line.split('|')\n .map(cell => cell.trim())\n .filter((_, i, arr) => i > 0 && i < arr.length - 1); // 移除首尾空元素\n });\n\n // 计算每列的最大宽度\n const colWidths: number[] = [];\n rows.forEach(row => {\n row.forEach((cell, i) => {\n colWidths[i] = Math.max(colWidths[i] || 0, cell.length);\n });\n });\n\n // 格式化每一行\n const formattedRows = rows.map((row, rowIndex) => {\n const cells = row.map((cell, colIndex) => {\n // 对于分隔行,保持对齐标记\n if (rowIndex === 1 && cell.match(/^:?-+:?$/)) {\n const hasLeft = cell.startsWith(':');\n const hasRight = cell.endsWith(':');\n const width = colWidths[colIndex];\n\n if (hasLeft && hasRight) {\n return ':' + '-'.repeat(width - 2) + ':';\n } else if (hasLeft) {\n return ':' + '-'.repeat(width - 1);\n } else if (hasRight) {\n return '-'.repeat(width - 1) + ':';\n } else {\n return '-'.repeat(width);\n }\n }\n\n return cell.padEnd(colWidths[colIndex], ' ');\n });\n\n return '| ' + cells.join(' | ') + ' |';\n });\n\n return formattedRows.join('\\n');\n};\n\n// 检测是否在表格中\nconst isInTable = (view: EditorView, pos: number): { start: number; end: number; text: string } | null => {\n const line = view.state.doc.lineAt(pos);\n const lineText = line.text;\n\n if (!lineText.includes('|')) return null;\n\n // 向上查找表格开始\n let startLine = line.number;\n let currentLine = line.number - 1;\n while (currentLine >= 1) {\n const l = view.state.doc.line(currentLine);\n if (!l.text.includes('|')) break;\n startLine = currentLine;\n currentLine--;\n }\n\n // 向下查找表格结束\n let endLine = line.number;\n currentLine = line.number + 1;\n const totalLines = view.state.doc.lines;\n while (currentLine <= totalLines) {\n const l = view.state.doc.line(currentLine);\n if (!l.text.includes('|')) break;\n endLine = currentLine;\n currentLine++;\n }\n\n const startLineObj = view.state.doc.line(startLine);\n const endLineObj = view.state.doc.line(endLine);\n\n return {\n start: startLineObj.from,\n end: endLineObj.to,\n text: view.state.doc.sliceString(startLineObj.from, endLineObj.to),\n };\n};\n\nconst MarkdownEditor: React.FC<MarkdownEditorProps> = ({\n value = '',\n onChange,\n showTableOfContents = true,\n showFootnotes = true,\n onSectionsChange,\n onFootnotesChange,\n tableOfContentsPosition = 'right',\n customExtensions = [],\n customContextMenu = [],\n ...codeEditorProps\n}) => {\n const [sections, setSections] = useState<MarkdownSection[]>([]);\n const [footnotes, setFootnotes] = useState<FootnoteInfo[]>([]);\n const [currentLine, setCurrentLine] = useState(1);\n const editorRef = useRef<CodeEditorHandle>(null);\n const lastPropValue = useRef<string | null>(null);\n const lastAnalyzedValue = useRef<string | null>(null);\n\n const updateMetadata = useCallback((content: string) => {\n if (lastAnalyzedValue.current === content) return;\n lastAnalyzedValue.current = content;\n const newSections = extractMarkdownSections(content);\n const newFootnotes = extractFootnotes(content);\n setSections(newSections);\n setFootnotes(newFootnotes);\n onSectionsChange?.(newSections);\n onFootnotesChange?.(newFootnotes);\n }, [onSectionsChange, onFootnotesChange]);\n\n // The editor can change without a parent echoing value; a new callback alone\n // must not re-analyze the stale value prop and overwrite the live sidebar.\n useEffect(() => {\n if (lastPropValue.current !== value) {\n lastPropValue.current = value;\n updateMetadata(value);\n }\n }, [value, updateMetadata]);\n\n const handleChange = useCallback((content: string) => {\n updateMetadata(content);\n onChange?.(content);\n }, [updateMetadata, onChange]);\n\n const jumpToLine = useCallback((line: number) => {\n editorRef.current?.jumpToLine(line);\n }, []);\n\n // Markdown 专属上下文菜单\n const markdownContextMenu: ContextMenuItem[] = [\n {\n label: '格式化表格',\n action: (view) => {\n const selection = view.state.selection.main;\n const tableInfo = isInTable(view, selection.from);\n\n if (tableInfo) {\n const formatted = formatMarkdownTable(tableInfo.text);\n view.dispatch({\n changes: {\n from: tableInfo.start,\n to: tableInfo.end,\n insert: formatted,\n },\n });\n } else {\n alert('请将光标放在表格中');\n }\n },\n },\n { label: '', action: () => { }, separator: true },\n {\n label: '剪切',\n action: (view) => {\n const selection = view.state.selection.main;\n const text = view.state.sliceDoc(selection.from, selection.to);\n navigator.clipboard.writeText(text);\n view.dispatch({\n changes: { from: selection.from, to: selection.to, insert: '' },\n });\n },\n },\n {\n label: '复制',\n action: (view) => {\n const selection = view.state.selection.main;\n const text = view.state.sliceDoc(selection.from, selection.to);\n navigator.clipboard.writeText(text);\n },\n },\n {\n label: '粘贴',\n action: async (view) => {\n const text = await navigator.clipboard.readText();\n const selection = view.state.selection.main;\n view.dispatch({\n changes: { from: selection.from, to: selection.to, insert: text },\n });\n },\n },\n { label: '', action: () => { }, separator: true },\n {\n label: '全选',\n action: (view) => {\n view.dispatch({\n selection: { anchor: 0, head: view.state.doc.length },\n });\n },\n },\n ...customContextMenu,\n ];\n\n // 合并扩展\n const allExtensions = [...markdownExtensions(), ...customExtensions];\n\n // 侧边栏内容\n const renderSidebar = () => {\n // 根据主题决定侧边栏的样式类\n const darkThemes = ['dark', 'retro', 'cyberpunk', 'matrix'];\n const sidebarThemeClass =\n darkThemes.includes(codeEditorProps.theme || 'light')\n ? 'markdown-sidebar-dark'\n : 'markdown-sidebar-light';\n\n return (\n <div className={`markdown-sidebar space-y-4 ${sidebarThemeClass}`} style={{ width: '320px' }}>\n {showTableOfContents && (\n <TableOfContentsPanel\n sections={sections}\n onSectionClick={jumpToLine}\n currentLine={currentLine}\n theme={codeEditorProps.theme}\n />\n )}\n {showFootnotes && footnotes.length > 0 && (\n <FootnotePanel\n footnotes={footnotes}\n onFootnoteClick={jumpToLine}\n onJumpToDefinition={(id, line) => {\n console.log(`Jumping to definition of footnote ${id} at line ${line}`);\n jumpToLine(line);\n }}\n onJumpToReference={(id, line) => {\n console.log(`Jumping to reference of footnote ${id} at line ${line}`);\n jumpToLine(line);\n }}\n theme={codeEditorProps.theme}\n />\n )}\n </div>\n );\n };\n\n return (\n <div className=\"markdown-editor-wrapper flex gap-4\">\n {/* 左侧边栏 */}\n {tableOfContentsPosition === 'left' && (showTableOfContents || showFootnotes) && renderSidebar()}\n\n {/* 编辑器 */}\n <div className=\"markdown-editor-main flex-1\">\n <CodeEditor\n {...codeEditorProps}\n ref={editorRef}\n value={value}\n onChange={handleChange}\n language=\"markdown\"\n autocompletion={false}\n extensions={allExtensions}\n contextMenu={markdownContextMenu}\n />\n </div>\n\n {/* 右侧边栏 */}\n {tableOfContentsPosition === 'right' && (showTableOfContents || showFootnotes) && renderSidebar()}\n\n {/* 侧边栏主题样式 */}\n <style>{`\n .markdown-sidebar-light {\n color: #000000;\n }\n .markdown-sidebar-dark {\n color: #d4d4d4;\n }\n .markdown-sidebar-dark .card,\n .markdown-sidebar-dark .toc-panel,\n .markdown-sidebar-dark .footnote-panel,\n .markdown-sidebar-dark .outline-panel,\n .markdown-sidebar-dark .variable-panel {\n background-color: #1e1e1e !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .border-base-300 {\n border-color: #3e3e42 !important;\n }\n .markdown-sidebar-dark input,\n .markdown-sidebar-dark .input {\n background-color: #2d2d2d !important;\n border-color: #3e3e42 !important;\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark button,\n .markdown-sidebar-dark .btn {\n color: #d4d4d4 !important;\n }\n .markdown-sidebar-dark .hover\\\\:bg-base-200:hover,\n .markdown-sidebar-dark .toc-item:hover,\n .markdown-sidebar-dark .outline-item:hover,\n .markdown-sidebar-dark .variable-item:hover,\n .markdown-sidebar-dark .footnote-item:hover {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-primary\\\\/20 {\n background-color: rgba(96, 165, 250, 0.2) !important;\n }\n .markdown-sidebar-dark .bg-base-200 {\n background-color: #2d2d2d !important;\n }\n .markdown-sidebar-dark .bg-warning\\\\/10 {\n background-color: rgba(251, 191, 36, 0.1) !important;\n }\n .markdown-sidebar-dark .border-warning\\\\/30 {\n border-color: rgba(251, 191, 36, 0.3) !important;\n }\n .markdown-sidebar-dark .text-warning {\n color: #fbbf24 !important;\n }\n `}</style>\n </div>\n );\n};\n\nexport default MarkdownEditor;\n"],"mappings":";;;;;;;AAwBA,IAAM,KAAuB,MAA8B;CACzD,IAAM,IAAQ,EAAU,MAAM,IAAI,CAAC,CAAC,QAAO,MAAQ,EAAK,KAAK,CAAC;CAC9D,IAAI,EAAM,SAAS,GAAG,OAAO;CAG7B,IAAM,IAAO,EAAM,KAAI,MACd,EAAK,MAAM,GAAG,CAAC,CACnB,KAAI,MAAQ,EAAK,KAAK,CAAC,CAAC,CACxB,QAAQ,GAAG,GAAG,MAAQ,IAAI,KAAK,IAAI,EAAI,SAAS,CAAC,CACrD,GAGK,IAAsB,CAAC;CAiC7B,OAhCA,EAAK,SAAQ,MAAO;EAClB,EAAI,SAAS,GAAM,MAAM;GACvB,EAAU,KAAK,KAAK,IAAI,EAAU,MAAM,GAAG,EAAK,MAAM;EACxD,CAAC;CACH,CAAC,GAGqB,EAAK,KAAK,GAAK,MAsB5B,OArBO,EAAI,KAAK,GAAM,MAAa;EAExC,IAAI,MAAa,KAAK,EAAK,MAAM,UAAU,GAAG;GAC5C,IAAM,IAAU,EAAK,WAAW,GAAG,GAC7B,IAAW,EAAK,SAAS,GAAG,GAC5B,IAAQ,EAAU;GAStB,OAPE,KAAW,IACN,MAAM,IAAI,OAAO,IAAQ,CAAC,IAAI,MAC5B,IACF,MAAM,IAAI,OAAO,IAAQ,CAAC,IACxB,IACF,IAAI,OAAO,IAAQ,CAAC,IAAI,MAExB,IAAI,OAAO,CAAK;EAE3B;EAEA,OAAO,EAAK,OAAO,EAAU,IAAW,GAAG;CAC7C,CAEc,CAAA,CAAM,KAAK,KAAK,IAAI,IAG7B,CAAA,CAAc,KAAK,IAAI;AAChC,GAGM,KAAa,GAAkB,MAAqE;CACxG,IAAM,IAAO,EAAK,MAAM,IAAI,OAAO,CAAG;CAGtC,IAAI,CAFa,EAAK,KAER,SAAS,GAAG,GAAG,OAAO;CAGpC,IAAI,IAAY,EAAK,QACjB,IAAc,EAAK,SAAS;CAChC,OAAO,KAAe,KACV,EAAK,MAAM,IAAI,KAAK,CACzB,CAAA,CAAE,KAAK,SAAS,GAAG,IAExB,AADA,IAAY,GACZ;CAIF,IAAI,IAAU,EAAK;CACnB,IAAc,EAAK,SAAS;CAC5B,IAAM,IAAa,EAAK,MAAM,IAAI;CAClC,OAAO,KAAe,KACV,EAAK,MAAM,IAAI,KAAK,CACzB,CAAA,CAAE,KAAK,SAAS,GAAG,IAExB,AADA,IAAU,GACV;CAGF,IAAM,IAAe,EAAK,MAAM,IAAI,KAAK,CAAS,GAC5C,IAAa,EAAK,MAAM,IAAI,KAAK,CAAO;CAE9C,OAAO;EACL,OAAO,EAAa;EACpB,KAAK,EAAW;EAChB,MAAM,EAAK,MAAM,IAAI,YAAY,EAAa,MAAM,EAAW,EAAE;CACnE;AACF,GAEM,KAAiD,EACrD,WAAQ,IACR,aACA,yBAAsB,IACtB,mBAAgB,IAChB,qBACA,sBACA,6BAA0B,SAC1B,sBAAmB,CAAC,GACpB,uBAAoB,CAAC,GACrB,GAAG,QACC;CACJ,IAAM,CAAC,GAAU,KAAe,EAA4B,CAAC,CAAC,GACxD,CAAC,GAAW,KAAgB,EAAyB,CAAC,CAAC,GACvD,CAAC,GAAa,KAAkB,EAAS,CAAC,GAC1C,IAAY,EAAyB,IAAI,GACzC,IAAgB,EAAsB,IAAI,GAC1C,IAAoB,EAAsB,IAAI,GAE9C,IAAiB,GAAa,MAAoB;EACtD,IAAI,EAAkB,YAAY,GAAS;EAC3C,EAAkB,UAAU;EAC5B,IAAM,IAAc,EAAwB,CAAO,GAC7C,IAAe,EAAiB,CAAO;EAI7C,AAHA,EAAY,CAAW,GACvB,EAAa,CAAY,GACzB,IAAmB,CAAW,GAC9B,IAAoB,CAAY;CAClC,GAAG,CAAC,GAAkB,CAAiB,CAAC;CAIxC,QAAgB;EACd,AAAI,EAAc,YAAY,MAC5B,EAAc,UAAU,GACxB,EAAe,CAAK;CAExB,GAAG,CAAC,GAAO,CAAc,CAAC;CAE1B,IAAM,IAAe,GAAa,MAAoB;EAEpD,AADA,EAAe,CAAO,GACtB,IAAW,CAAO;CACpB,GAAG,CAAC,GAAgB,CAAQ,CAAC,GAEvB,IAAa,GAAa,MAAiB;EAC/C,EAAU,SAAS,WAAW,CAAI;CACpC,GAAG,CAAC,CAAC,GAGC,IAAyC;EAC7C;GACE,OAAO;GACP,SAAS,MAAS;IAChB,IAAM,IAAY,EAAK,MAAM,UAAU,MACjC,IAAY,EAAU,GAAM,EAAU,IAAI;IAEhD,IAAI,GAAW;KACb,IAAM,IAAY,EAAoB,EAAU,IAAI;KACpD,EAAK,SAAS,EACZ,SAAS;MACP,MAAM,EAAU;MAChB,IAAI,EAAU;MACd,QAAQ;KACV,EACF,CAAC;IACH,OACE,MAAM,WAAW;GAErB;EACF;EACA;GAAE,OAAO;GAAI,cAAc,CAAE;GAAG,WAAW;EAAK;EAChD;GACE,OAAO;GACP,SAAS,MAAS;IAChB,IAAM,IAAY,EAAK,MAAM,UAAU,MACjC,IAAO,EAAK,MAAM,SAAS,EAAU,MAAM,EAAU,EAAE;IAE7D,AADA,UAAU,UAAU,UAAU,CAAI,GAClC,EAAK,SAAS,EACZ,SAAS;KAAE,MAAM,EAAU;KAAM,IAAI,EAAU;KAAI,QAAQ;IAAG,EAChE,CAAC;GACH;EACF;EACA;GACE,OAAO;GACP,SAAS,MAAS;IAChB,IAAM,IAAY,EAAK,MAAM,UAAU,MACjC,IAAO,EAAK,MAAM,SAAS,EAAU,MAAM,EAAU,EAAE;IAC7D,UAAU,UAAU,UAAU,CAAI;GACpC;EACF;EACA;GACE,OAAO;GACP,QAAQ,OAAO,MAAS;IACtB,IAAM,IAAO,MAAM,UAAU,UAAU,SAAS,GAC1C,IAAY,EAAK,MAAM,UAAU;IACvC,EAAK,SAAS,EACZ,SAAS;KAAE,MAAM,EAAU;KAAM,IAAI,EAAU;KAAI,QAAQ;IAAK,EAClE,CAAC;GACH;EACF;EACA;GAAE,OAAO;GAAI,cAAc,CAAE;GAAG,WAAW;EAAK;EAChD;GACE,OAAO;GACP,SAAS,MAAS;IAChB,EAAK,SAAS,EACZ,WAAW;KAAE,QAAQ;KAAG,MAAM,EAAK,MAAM,IAAI;IAAO,EACtD,CAAC;GACH;EACF;EACA,GAAG;CACL,GAGM,IAAgB,CAAC,GAAG,EAAmB,GAAG,GAAG,CAAgB,GAG7D,UAAsB;EAG1B,IAAM,IACJ;GAFkB;GAAQ;GAAS;GAAa;EAEhD,CAAA,CAAW,SAAS,EAAgB,SAAS,OAAO,IAChD,0BACA;EAEN,OACE,kBAAC,OAAD;GAAK,WAAW,8BAA8B;GAAqB,OAAO,EAAE,OAAO,QAAQ;GAA3F,UAAA,CACG,KACC,kBAAC,GAAD;IACY;IACV,gBAAgB;IACH;IACb,OAAO,EAAgB;GACxB,CAAA,GAEF,KAAiB,EAAU,SAAS,KACnC,kBAAC,GAAD;IACa;IACX,iBAAiB;IACjB,qBAAqB,GAAI,MAAS;KAEhC,AADA,QAAQ,IAAI,qCAAqC,EAAG,WAAW,GAAM,GACrE,EAAW,CAAI;IACjB;IACA,oBAAoB,GAAI,MAAS;KAE/B,AADA,QAAQ,IAAI,oCAAoC,EAAG,WAAW,GAAM,GACpE,EAAW,CAAI;IACjB;IACA,OAAO,EAAgB;GACxB,CAAA,CAEA;;CAET;CAEA,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GAEG,MAA4B,WAAW,KAAuB,MAAkB,EAAc;GAG/F,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KACE,GAAI;KACJ,KAAK;KACE;KACP,UAAU;KACV,UAAS;KACT,gBAAgB;KAChB,YAAY;KACZ,aAAa;IACd,CAAA;GACE,CAAA;GAGJ,MAA4B,YAAY,KAAuB,MAAkB,EAAc;GAGhG,kBAAC,SAAD,EAAA,UAAQ,+2DAmDC,CAAA;EACN;;AAET"}
|
|
@@ -81,17 +81,14 @@ var l = { root: "Root" }, u = () => /* @__PURE__ */ s("svg", {
|
|
|
81
81
|
return null;
|
|
82
82
|
}, i = r(c, n);
|
|
83
83
|
if (i && i.children?.length === 1 && String(i.children[0].id).endsWith("_dummy")) {
|
|
84
|
-
let e = await m(n)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} : t);
|
|
93
|
-
return r(t);
|
|
94
|
-
});
|
|
84
|
+
let e = await m(n), t = (r) => r.map((r) => r.id === n ? {
|
|
85
|
+
...r,
|
|
86
|
+
children: e
|
|
87
|
+
} : r.children ? {
|
|
88
|
+
...r,
|
|
89
|
+
children: t(r.children)
|
|
90
|
+
} : r);
|
|
91
|
+
l((e) => t(e));
|
|
95
92
|
}
|
|
96
93
|
}, [c, m]), g = r((e) => {
|
|
97
94
|
String(e.id).endsWith("_dummy") || e.meta?.type !== "file" && a(String(e.id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb.js","names":[],"sources":["../../../../src/components/pro/FileExplorer/Breadcrumb.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { BreadcrumbProps } from './types';\nimport { useClickOutside } from '../../../hooks/useClickOutside';\nimport { Tree, TreeNode } from '../../Tree';\n\nconst defaultLabels = {\n root: 'Root',\n};\n\nconst HomeIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\" />\n </svg>\n);\n\nconst ChevronIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n);\n\nconst FolderIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 text-blue-500 opacity-80\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M2 6a2 2 0 012-2h4l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6z\" />\n </svg>\n);\n\nconst DocumentIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 text-gray-500 opacity-80\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z\" clipRule=\"evenodd\" />\n </svg>\n);\n\nconst BreadcrumbDropTree: React.FC<{\n path: string;\n adapter: any;\n onNavigate: (path: string) => void;\n}> = ({ path, adapter, onNavigate }) => {\n const [treeData, setTreeData] = useState<TreeNode[]>([]);\n const [loading, setLoading] = useState(false);\n\n const loadPath = useCallback(async (dirPath: string): Promise<TreeNode[]> => {\n try {\n const entries = await adapter.list(dirPath);\n return entries\n .sort((a: any, b: any) => {\n if (a.type !== b.type) return a.type === 'folder' ? -1 : 1;\n return String(a.name).localeCompare(String(b.name));\n })\n .map((e: any) => ({\n id: e.path,\n label: e.name,\n icon: e.type === 'folder' ? <FolderIcon /> : <DocumentIcon />,\n meta: { type: e.type },\n children: e.type === 'folder' ? [{ id: `${e.path}/_dummy`, label: 'Loading...' }] : undefined\n }));\n } catch (e) {\n return [];\n }\n }, [adapter]);\n\n useEffect(() => {\n let mounted = true;\n setLoading(true);\n loadPath(path).then(nodes => {\n if (mounted) {\n setTreeData(nodes);\n setLoading(false);\n }\n });\n return () => { mounted = false; };\n }, [path, loadPath]);\n\n const handleToggle = useCallback(async (nodeId: string | number, expanded: boolean) => {\n if (!expanded) return;\n const idStr = String(nodeId);\n\n const findNode = (nodes: TreeNode[], targetId: string): TreeNode | null => {\n for (const n of nodes) {\n if (n.id === targetId) return n;\n if (n.children) {\n const found = findNode(n.children, targetId);\n if (found) return found;\n }\n }\n return null;\n };\n\n const node = findNode(treeData, idStr);\n if (node && node.children?.length === 1 && String(node.children[0].id).endsWith('_dummy')) {\n const childrenNodes = await loadPath(idStr);\n setTreeData(prev => {\n const updateNodes = (nodes: TreeNode[]): TreeNode[] => {\n return nodes.map(n => {\n if (n.id === idStr) {\n return { ...n, children: childrenNodes };\n }\n if (n.children) return { ...n, children: updateNodes(n.children) };\n return n;\n });\n };\n return updateNodes(prev);\n });\n }\n }, [treeData, loadPath]);\n\n const handleClick = useCallback((node: TreeNode) => {\n if (String(node.id).endsWith('_dummy')) return;\n if (node.meta?.type === 'file') return;\n onNavigate(String(node.id));\n }, [onNavigate]);\n\n if (loading) return <div className=\"px-4 py-3 w-48 text-center text-xs opacity-50\">Loading...</div>;\n if (treeData.length === 0) return <div className=\"px-4 py-3 w-48 text-center text-xs opacity-50\">Empty Folder</div>;\n\n return (\n <div className=\"p-1 w-64 max-h-[300px] overflow-y-auto sm:min-w-[200px] max-w-[80vw]\">\n <Tree\n data={treeData}\n onNodeClick={handleClick}\n onNodeToggle={handleToggle}\n showSearch={false}\n showExpandAll={false}\n showLine={false}\n showIcon={true}\n showCount={false}\n className=\"!bg-transparent !border-none !p-1 shadow-none overflow-visible\"\n indent={12}\n />\n </div>\n );\n};\n\nconst ChevronDrop: React.FC<{\n path: string;\n adapter?: any;\n onNavigate: (p: string) => void;\n}> = ({ path, adapter, onNavigate }) => {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useClickOutside(ref, () => setOpen(false), open);\n\n return (\n <div className=\"relative flex items-center\" ref={ref}>\n {adapter ? (\n <button\n className={`btn btn-ghost btn-xs px-0.5 mx-0.5 min-h-0 h-5 rounded hover:bg-base-200 transition-colors ${open ? 'bg-base-200' : 'opacity-50'}`}\n onClick={(e) => {\n e.stopPropagation();\n setOpen(!open);\n }}\n >\n <ChevronIcon />\n </button>\n ) : (\n <span className=\"opacity-40 mx-1\"><ChevronIcon /></span>\n )}\n\n {open && adapter && (\n <div\n className=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 bg-base-100 shadow-2xl border border-base-200 rounded-xl z-[9999]\"\n onClick={(e) => e.stopPropagation()}\n >\n <BreadcrumbDropTree\n path={path}\n adapter={adapter}\n onNavigate={(p) => {\n onNavigate(p);\n setOpen(false);\n }}\n />\n </div>\n )}\n </div>\n );\n};\n\n/** Split a path like \"/a/b/c\" into breadcrumb segments */\nfunction pathSegments(path: string): { label: string; path: string; basePath: string }[] {\n const parts = path.split('/').filter(Boolean);\n const segments: { label: string; path: string; basePath: string }[] = [];\n for (let i = 0; i < parts.length; i++) {\n segments.push({\n label: parts[i],\n path: '/' + parts.slice(0, i + 1).join('/'),\n basePath: i === 0 ? '/' : '/' + parts.slice(0, i).join('/')\n });\n }\n return segments;\n}\n\nexport const Breadcrumb: React.FC<BreadcrumbProps> = ({\n path,\n labels: userLabels,\n adapter,\n onNavigate,\n}) => {\n const labels = { ...defaultLabels, ...userLabels };\n const segments = pathSegments(path);\n const isRoot = segments.length === 0;\n\n return (\n <nav className=\"flex items-center text-sm overflow-x-visible py-2 px-1 gap-0.5 min-h-[36px]\">\n <button\n className={`btn btn-ghost btn-xs gap-1 flex-shrink-0 ${isRoot ? 'font-semibold' : ''}`}\n onClick={() => onNavigate('/')}\n >\n <HomeIcon />\n {labels.root}\n </button>\n\n {segments.map((seg, idx) => {\n const isLast = idx === segments.length - 1;\n return (\n <React.Fragment key={seg.path}>\n <ChevronDrop path={seg.basePath} adapter={adapter} onNavigate={onNavigate} />\n <button\n className={`btn btn-ghost btn-xs flex-shrink-0 truncate max-w-[160px] ${isLast ? 'font-semibold' : ''}`}\n onClick={() => onNavigate(seg.path)}\n disabled={isLast}\n >\n {seg.label}\n </button>\n </React.Fragment>\n );\n })}\n </nav>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Breadcrumb.js","names":[],"sources":["../../../../src/components/pro/FileExplorer/Breadcrumb.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { BreadcrumbProps } from './types';\nimport { useClickOutside } from '../../../hooks/useClickOutside';\nimport { Tree, TreeNode } from '../../Tree';\n\nconst defaultLabels = {\n root: 'Root',\n};\n\nconst HomeIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\" />\n </svg>\n);\n\nconst ChevronIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n);\n\nconst FolderIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 text-blue-500 opacity-80\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M2 6a2 2 0 012-2h4l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6z\" />\n </svg>\n);\n\nconst DocumentIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-4 w-4 text-gray-500 opacity-80\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z\" clipRule=\"evenodd\" />\n </svg>\n);\n\nconst BreadcrumbDropTree: React.FC<{\n path: string;\n adapter: any;\n onNavigate: (path: string) => void;\n}> = ({ path, adapter, onNavigate }) => {\n const [treeData, setTreeData] = useState<TreeNode[]>([]);\n const [loading, setLoading] = useState(false);\n\n const loadPath = useCallback(async (dirPath: string): Promise<TreeNode[]> => {\n try {\n const entries = await adapter.list(dirPath);\n return entries\n .sort((a: any, b: any) => {\n if (a.type !== b.type) return a.type === 'folder' ? -1 : 1;\n return String(a.name).localeCompare(String(b.name));\n })\n .map((e: any) => ({\n id: e.path,\n label: e.name,\n icon: e.type === 'folder' ? <FolderIcon /> : <DocumentIcon />,\n meta: { type: e.type },\n children: e.type === 'folder' ? [{ id: `${e.path}/_dummy`, label: 'Loading...' }] : undefined\n }));\n } catch (e) {\n return [];\n }\n }, [adapter]);\n\n useEffect(() => {\n let mounted = true;\n setLoading(true);\n loadPath(path).then(nodes => {\n if (mounted) {\n setTreeData(nodes);\n setLoading(false);\n }\n });\n return () => { mounted = false; };\n }, [path, loadPath]);\n\n const handleToggle = useCallback(async (nodeId: string | number, expanded: boolean) => {\n if (!expanded) return;\n const idStr = String(nodeId);\n\n const findNode = (nodes: TreeNode[], targetId: string): TreeNode | null => {\n for (const n of nodes) {\n if (n.id === targetId) return n;\n if (n.children) {\n const found = findNode(n.children, targetId);\n if (found) return found;\n }\n }\n return null;\n };\n\n const node = findNode(treeData, idStr);\n if (node && node.children?.length === 1 && String(node.children[0].id).endsWith('_dummy')) {\n const childrenNodes = await loadPath(idStr);\n const updateNodes = (nodes: TreeNode[]): TreeNode[] => {\n return nodes.map(n => {\n if (n.id === idStr) {\n return { ...n, children: childrenNodes };\n }\n if (n.children) return { ...n, children: updateNodes(n.children) };\n return n;\n });\n };\n setTreeData(prev => updateNodes(prev));\n }\n }, [treeData, loadPath]);\n\n const handleClick = useCallback((node: TreeNode) => {\n if (String(node.id).endsWith('_dummy')) return;\n if (node.meta?.type === 'file') return;\n onNavigate(String(node.id));\n }, [onNavigate]);\n\n if (loading) return <div className=\"px-4 py-3 w-48 text-center text-xs opacity-50\">Loading...</div>;\n if (treeData.length === 0) return <div className=\"px-4 py-3 w-48 text-center text-xs opacity-50\">Empty Folder</div>;\n\n return (\n <div className=\"p-1 w-64 max-h-[300px] overflow-y-auto sm:min-w-[200px] max-w-[80vw]\">\n <Tree\n data={treeData}\n onNodeClick={handleClick}\n onNodeToggle={handleToggle}\n showSearch={false}\n showExpandAll={false}\n showLine={false}\n showIcon={true}\n showCount={false}\n className=\"!bg-transparent !border-none !p-1 shadow-none overflow-visible\"\n indent={12}\n />\n </div>\n );\n};\n\nconst ChevronDrop: React.FC<{\n path: string;\n adapter?: any;\n onNavigate: (p: string) => void;\n}> = ({ path, adapter, onNavigate }) => {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useClickOutside(ref, () => setOpen(false), open);\n\n return (\n <div className=\"relative flex items-center\" ref={ref}>\n {adapter ? (\n <button\n className={`btn btn-ghost btn-xs px-0.5 mx-0.5 min-h-0 h-5 rounded hover:bg-base-200 transition-colors ${open ? 'bg-base-200' : 'opacity-50'}`}\n onClick={(e) => {\n e.stopPropagation();\n setOpen(!open);\n }}\n >\n <ChevronIcon />\n </button>\n ) : (\n <span className=\"opacity-40 mx-1\"><ChevronIcon /></span>\n )}\n\n {open && adapter && (\n <div\n className=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 bg-base-100 shadow-2xl border border-base-200 rounded-xl z-[9999]\"\n onClick={(e) => e.stopPropagation()}\n >\n <BreadcrumbDropTree\n path={path}\n adapter={adapter}\n onNavigate={(p) => {\n onNavigate(p);\n setOpen(false);\n }}\n />\n </div>\n )}\n </div>\n );\n};\n\n/** Split a path like \"/a/b/c\" into breadcrumb segments */\nfunction pathSegments(path: string): { label: string; path: string; basePath: string }[] {\n const parts = path.split('/').filter(Boolean);\n const segments: { label: string; path: string; basePath: string }[] = [];\n for (let i = 0; i < parts.length; i++) {\n segments.push({\n label: parts[i],\n path: '/' + parts.slice(0, i + 1).join('/'),\n basePath: i === 0 ? '/' : '/' + parts.slice(0, i).join('/')\n });\n }\n return segments;\n}\n\nexport const Breadcrumb: React.FC<BreadcrumbProps> = ({\n path,\n labels: userLabels,\n adapter,\n onNavigate,\n}) => {\n const labels = { ...defaultLabels, ...userLabels };\n const segments = pathSegments(path);\n const isRoot = segments.length === 0;\n\n return (\n <nav className=\"flex items-center text-sm overflow-x-visible py-2 px-1 gap-0.5 min-h-[36px]\">\n <button\n className={`btn btn-ghost btn-xs gap-1 flex-shrink-0 ${isRoot ? 'font-semibold' : ''}`}\n onClick={() => onNavigate('/')}\n >\n <HomeIcon />\n {labels.root}\n </button>\n\n {segments.map((seg, idx) => {\n const isLast = idx === segments.length - 1;\n return (\n <React.Fragment key={seg.path}>\n <ChevronDrop path={seg.basePath} adapter={adapter} onNavigate={onNavigate} />\n <button\n className={`btn btn-ghost btn-xs flex-shrink-0 truncate max-w-[160px] ${isLast ? 'font-semibold' : ''}`}\n onClick={() => onNavigate(seg.path)}\n disabled={isLast}\n >\n {seg.label}\n </button>\n </React.Fragment>\n );\n })}\n </nav>\n );\n};\n"],"mappings":"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"}
|