@allbluecn/ui 0.3.13 → 0.4.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/dist/ai-command-button.d.ts +10 -0
- package/dist/ai-command-button.js +12 -0
- package/dist/ai-stream-text.d.ts +9 -0
- package/dist/ai-stream-text.js +12 -0
- package/dist/alert-dialog.d.ts +20 -0
- package/dist/alert-dialog.js +101 -0
- package/dist/animated-theme-toggler.d.ts +11 -0
- package/dist/animated-theme-toggler.js +169 -0
- package/dist/auth-client.d.ts +27 -0
- package/dist/auth-client.js +86 -0
- package/dist/auth-layout.d.ts +14 -0
- package/dist/auth-layout.js +43 -0
- package/dist/avatar.d.ts +13 -0
- package/dist/avatar.js +51 -0
- package/dist/badge.d.ts +11 -0
- package/dist/badge.js +28 -0
- package/dist/button.d.ts +12 -0
- package/dist/button.js +48 -0
- package/dist/card.d.ts +13 -0
- package/dist/card.js +56 -0
- package/dist/checkbox.d.ts +6 -0
- package/dist/checkbox.js +22 -0
- package/dist/context-menu.d.ts +33 -0
- package/dist/context-menu.js +125 -0
- package/dist/dialog.d.ts +19 -0
- package/dist/dialog.js +95 -0
- package/dist/dropdown-menu.d.ts +31 -0
- package/dist/dropdown-menu.js +128 -0
- package/dist/empty.d.ts +13 -0
- package/dist/empty.js +59 -0
- package/dist/grid-pattern.d.ts +14 -0
- package/dist/grid-pattern.js +47 -0
- package/dist/icon-packs/index.d.ts +2 -0
- package/dist/icon-packs/index.js +4 -0
- package/dist/icon-packs/knowledge.d.ts +2 -0
- package/dist/icon-packs/knowledge.js +70 -0
- package/dist/icon-packs/prd.d.ts +2 -0
- package/dist/icon-packs/prd.js +86 -0
- package/dist/icon-packs/project.d.ts +2 -0
- package/dist/icon-packs/project.js +74 -0
- package/dist/icon-picker.d.ts +11 -0
- package/dist/icon-picker.js +39 -0
- package/dist/index-CQsbeClc.d.ts +17 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/input.d.ts +5 -0
- package/dist/input.js +14 -0
- package/dist/item.d.ts +25 -0
- package/dist/item.js +118 -0
- package/dist/kbd.d.ts +4 -0
- package/dist/kbd.js +12 -0
- package/dist/label.d.ts +6 -0
- package/dist/label.js +14 -0
- package/dist/layout-panel-top.d.ts +12 -0
- package/dist/layout-panel-top.js +127 -0
- package/dist/name-confirm-dialog.d.ts +15 -0
- package/dist/name-confirm-dialog.js +105 -0
- package/dist/nav-float-button.d.ts +12 -0
- package/dist/nav-float-button.js +249 -0
- package/dist/notion-avatar.d.ts +13 -0
- package/dist/notion-avatar.js +83 -0
- package/dist/placeholder-page.d.ts +10 -0
- package/dist/placeholder-page.js +28 -0
- package/dist/popover.d.ts +12 -0
- package/dist/popover.js +56 -0
- package/dist/reference-inline.d.ts +10 -0
- package/dist/reference-inline.js +35 -0
- package/dist/reference-search.d.ts +12 -0
- package/dist/reference-search.js +26 -0
- package/dist/resizable.d.ts +9 -0
- package/dist/resizable.js +28 -0
- package/dist/select.d.ts +17 -0
- package/dist/select.js +99 -0
- package/dist/separator.d.ts +6 -0
- package/dist/separator.js +17 -0
- package/dist/sheet.d.ts +16 -0
- package/dist/sheet.js +90 -0
- package/dist/sidebar.d.ts +71 -0
- package/dist/sidebar.js +369 -0
- package/dist/skeleton.d.ts +4 -0
- package/dist/skeleton.js +12 -0
- package/dist/sonner.d.ts +5 -0
- package/dist/sonner.js +19 -0
- package/dist/switch.d.ts +8 -0
- package/dist/switch.js +19 -0
- package/dist/textarea.d.ts +5 -0
- package/dist/textarea.js +13 -0
- package/dist/timezone-provider.d.ts +18 -0
- package/dist/timezone-provider.js +29 -0
- package/dist/tooltip.d.ts +9 -0
- package/dist/tooltip.js +35 -0
- package/dist/use-mobile.d.ts +4 -0
- package/dist/use-mobile.js +18 -0
- package/dist/user-avatar.d.ts +14 -0
- package/dist/user-avatar.js +34 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +8 -0
- package/package.json +178 -49
- package/src/ai-command-button.tsx +0 -36
- package/src/ai-stream-text.tsx +0 -37
- package/src/alert-dialog.tsx +0 -216
- package/src/animated-theme-toggler.tsx +0 -272
- package/src/auth-client.ts +0 -108
- package/src/auth-layout.tsx +0 -66
- package/src/avatar.tsx +0 -129
- package/src/badge.tsx +0 -66
- package/src/button.tsx +0 -84
- package/src/card.tsx +0 -120
- package/src/checkbox.tsx +0 -50
- package/src/context-menu.tsx +0 -280
- package/src/dialog.tsx +0 -183
- package/src/dropdown-menu.tsx +0 -286
- package/src/empty.tsx +0 -121
- package/src/grid-pattern.tsx +0 -87
- package/src/icon-packs/index.ts +0 -27
- package/src/icon-packs/knowledge.ts +0 -55
- package/src/icon-packs/prd.ts +0 -63
- package/src/icon-packs/project.ts +0 -59
- package/src/icon-picker.tsx +0 -68
- package/src/index.ts +0 -23
- package/src/input.tsx +0 -36
- package/src/item.tsx +0 -213
- package/src/kbd.tsx +0 -33
- package/src/label.tsx +0 -39
- package/src/layout-panel-top.tsx +0 -160
- package/src/name-confirm-dialog.tsx +0 -153
- package/src/nav-float-button.tsx +0 -301
- package/src/notion-avatar.tsx +0 -126
- package/src/placeholder-page.tsx +0 -42
- package/src/popover.tsx +0 -106
- package/src/reference-inline.tsx +0 -53
- package/src/reference-search.tsx +0 -52
- package/src/resizable.tsx +0 -67
- package/src/select.tsx +0 -207
- package/src/separator.tsx +0 -45
- package/src/sheet.tsx +0 -164
- package/src/sidebar.tsx +0 -708
- package/src/skeleton.tsx +0 -30
- package/src/sonner.tsx +0 -37
- package/src/switch.tsx +0 -48
- package/src/textarea.tsx +0 -35
- package/src/timezone-provider.tsx +0 -55
- package/src/tooltip.tsx +0 -72
- package/src/use-mobile.ts +0 -36
- package/src/user-avatar.tsx +0 -79
- package/src/utils.ts +0 -6
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { HTMLAttributes } from "react";
|
|
2
|
+
//#region src/layout-panel-top.d.ts
|
|
3
|
+
interface LayoutPanelTopIconHandle {
|
|
4
|
+
startAnimation: () => void;
|
|
5
|
+
stopAnimation: () => void;
|
|
6
|
+
}
|
|
7
|
+
interface LayoutPanelTopIconProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
size?: number;
|
|
9
|
+
}
|
|
10
|
+
declare const LayoutPanelTopIcon: import("react").ForwardRefExoticComponent<LayoutPanelTopIconProps & import("react").RefAttributes<LayoutPanelTopIconHandle>>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { LayoutPanelTopIcon, LayoutPanelTopIconHandle };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
|
|
5
|
+
import { m, useAnimation } from "motion/react";
|
|
6
|
+
//#region src/layout-panel-top.tsx
|
|
7
|
+
const LayoutPanelTopIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
|
|
8
|
+
const controls = useAnimation();
|
|
9
|
+
const isControlledRef = useRef(false);
|
|
10
|
+
useImperativeHandle(ref, () => {
|
|
11
|
+
isControlledRef.current = true;
|
|
12
|
+
return {
|
|
13
|
+
startAnimation: () => controls.start("animate"),
|
|
14
|
+
stopAnimation: () => controls.start("normal")
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
const handleMouseEnter = useCallback((e) => {
|
|
18
|
+
if (isControlledRef.current) onMouseEnter?.(e);
|
|
19
|
+
else controls.start("animate");
|
|
20
|
+
}, [controls, onMouseEnter]);
|
|
21
|
+
const handleMouseLeave = useCallback((e) => {
|
|
22
|
+
if (isControlledRef.current) onMouseLeave?.(e);
|
|
23
|
+
else controls.start("normal");
|
|
24
|
+
}, [controls, onMouseLeave]);
|
|
25
|
+
return /* @__PURE__ */ jsx("div", {
|
|
26
|
+
className: cn(className),
|
|
27
|
+
onMouseEnter: handleMouseEnter,
|
|
28
|
+
onMouseLeave: handleMouseLeave,
|
|
29
|
+
...props,
|
|
30
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
31
|
+
fill: "none",
|
|
32
|
+
height: size,
|
|
33
|
+
stroke: "currentColor",
|
|
34
|
+
strokeLinecap: "round",
|
|
35
|
+
strokeLinejoin: "round",
|
|
36
|
+
strokeWidth: "2",
|
|
37
|
+
viewBox: "0 0 24 24",
|
|
38
|
+
width: size,
|
|
39
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx(m.rect, {
|
|
42
|
+
animate: controls,
|
|
43
|
+
height: "7",
|
|
44
|
+
initial: "normal",
|
|
45
|
+
rx: "1",
|
|
46
|
+
variants: {
|
|
47
|
+
normal: {
|
|
48
|
+
opacity: 1,
|
|
49
|
+
translateY: 0
|
|
50
|
+
},
|
|
51
|
+
animate: {
|
|
52
|
+
opacity: [0, 1],
|
|
53
|
+
translateY: [-5, 0],
|
|
54
|
+
transition: {
|
|
55
|
+
opacity: {
|
|
56
|
+
duration: .5,
|
|
57
|
+
times: [.2, 1]
|
|
58
|
+
},
|
|
59
|
+
duration: .5
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
width: "18",
|
|
64
|
+
x: "3",
|
|
65
|
+
y: "3"
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ jsx(m.rect, {
|
|
68
|
+
animate: controls,
|
|
69
|
+
height: "7",
|
|
70
|
+
initial: "normal",
|
|
71
|
+
rx: "1",
|
|
72
|
+
variants: {
|
|
73
|
+
normal: {
|
|
74
|
+
opacity: 1,
|
|
75
|
+
translateX: 0
|
|
76
|
+
},
|
|
77
|
+
animate: {
|
|
78
|
+
opacity: [0, 1],
|
|
79
|
+
translateX: [-10, 0],
|
|
80
|
+
transition: {
|
|
81
|
+
opacity: {
|
|
82
|
+
duration: .7,
|
|
83
|
+
times: [.5, 1]
|
|
84
|
+
},
|
|
85
|
+
translateX: { delay: .3 },
|
|
86
|
+
duration: .5
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
width: "7",
|
|
91
|
+
x: "3",
|
|
92
|
+
y: "14"
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsx(m.rect, {
|
|
95
|
+
animate: controls,
|
|
96
|
+
height: "7",
|
|
97
|
+
initial: "normal",
|
|
98
|
+
rx: "1",
|
|
99
|
+
variants: {
|
|
100
|
+
normal: {
|
|
101
|
+
opacity: 1,
|
|
102
|
+
translateX: 0
|
|
103
|
+
},
|
|
104
|
+
animate: {
|
|
105
|
+
opacity: [0, 1],
|
|
106
|
+
translateX: [10, 0],
|
|
107
|
+
transition: {
|
|
108
|
+
opacity: {
|
|
109
|
+
duration: .8,
|
|
110
|
+
times: [.5, 1]
|
|
111
|
+
},
|
|
112
|
+
translateX: { delay: .4 },
|
|
113
|
+
duration: .5
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
width: "7",
|
|
118
|
+
x: "14",
|
|
119
|
+
y: "14"
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
LayoutPanelTopIcon.displayName = "LayoutPanelTopIcon";
|
|
126
|
+
//#endregion
|
|
127
|
+
export { LayoutPanelTopIcon };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/name-confirm-dialog.d.ts
|
|
3
|
+
interface NameConfirmDialogProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (open: boolean) => void;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
confirmText: string;
|
|
9
|
+
expectedName: string;
|
|
10
|
+
onConfirm: () => Promise<void>;
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
declare function NameConfirmDialog({ open, onOpenChange, title, description, confirmText, expectedName, onConfirm, children }: NameConfirmDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { NameConfirmDialog };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Button } from "./button.js";
|
|
2
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./dialog.js";
|
|
3
|
+
import { Input } from "./input.js";
|
|
4
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { useTranslation } from "react-i18next";
|
|
8
|
+
import { toast } from "sonner";
|
|
9
|
+
//#region src/name-confirm-dialog.tsx
|
|
10
|
+
function NameConfirmDialog({ open, onOpenChange, title, description, confirmText, expectedName, onConfirm, children }) {
|
|
11
|
+
const { t } = useTranslation("common");
|
|
12
|
+
const [inputValue, setInputValue] = useState("");
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState("");
|
|
15
|
+
const isValid = inputValue.trim() === expectedName;
|
|
16
|
+
const handleConfirm = async () => {
|
|
17
|
+
if (!isValid) {
|
|
18
|
+
setError(t("nameConfirm.invalidName"));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
setLoading(true);
|
|
22
|
+
setError("");
|
|
23
|
+
try {
|
|
24
|
+
await onConfirm();
|
|
25
|
+
setInputValue("");
|
|
26
|
+
onOpenChange(false);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
setError(err instanceof Error ? err.message : t("nameConfirm.operationFailed"));
|
|
29
|
+
} finally {
|
|
30
|
+
setLoading(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const handleOpenChange = (newOpen) => {
|
|
34
|
+
if (!newOpen) {
|
|
35
|
+
setInputValue("");
|
|
36
|
+
setError("");
|
|
37
|
+
}
|
|
38
|
+
onOpenChange(newOpen);
|
|
39
|
+
};
|
|
40
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
41
|
+
open,
|
|
42
|
+
onOpenChange: handleOpenChange,
|
|
43
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
44
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), /* @__PURE__ */ jsx(DialogDescription, { children: description })] }),
|
|
45
|
+
/* @__PURE__ */ jsxs("div", {
|
|
46
|
+
className: "space-y-4 py-4",
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ jsxs("div", {
|
|
49
|
+
className: "space-y-2",
|
|
50
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
51
|
+
className: "text-sm text-muted-foreground",
|
|
52
|
+
children: [
|
|
53
|
+
t("nameConfirm.inputInstruction"),
|
|
54
|
+
" ",
|
|
55
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
56
|
+
asChild: true,
|
|
57
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
58
|
+
type: "button",
|
|
59
|
+
className: "inline-flex cursor-pointer items-center font-medium text-foreground hover:text-primary transition-colors",
|
|
60
|
+
onClick: (e) => {
|
|
61
|
+
e.stopPropagation();
|
|
62
|
+
navigator.clipboard.writeText(expectedName);
|
|
63
|
+
toast.success(t("nameConfirm.copied"));
|
|
64
|
+
},
|
|
65
|
+
children: expectedName
|
|
66
|
+
})
|
|
67
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
68
|
+
side: "top",
|
|
69
|
+
className: "text-xs",
|
|
70
|
+
children: t("nameConfirm.clickToCopy")
|
|
71
|
+
})] }),
|
|
72
|
+
" ",
|
|
73
|
+
t("nameConfirm.confirmDelete")
|
|
74
|
+
]
|
|
75
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
76
|
+
value: inputValue,
|
|
77
|
+
onChange: (e) => {
|
|
78
|
+
setInputValue(e.target.value);
|
|
79
|
+
setError("");
|
|
80
|
+
},
|
|
81
|
+
placeholder: t("nameConfirm.inputPlaceholder", { expectedName })
|
|
82
|
+
})]
|
|
83
|
+
}),
|
|
84
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
85
|
+
className: "text-sm text-destructive",
|
|
86
|
+
children: error
|
|
87
|
+
}),
|
|
88
|
+
children
|
|
89
|
+
]
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
92
|
+
variant: "outline",
|
|
93
|
+
onClick: () => handleOpenChange(false),
|
|
94
|
+
children: t("nameConfirm.cancel")
|
|
95
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
96
|
+
variant: "destructive",
|
|
97
|
+
onClick: handleConfirm,
|
|
98
|
+
disabled: !isValid || loading,
|
|
99
|
+
children: loading ? t("nameConfirm.deleting") : confirmText
|
|
100
|
+
})] })
|
|
101
|
+
] })
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { NameConfirmDialog };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
//#region src/nav-float-button.d.ts
|
|
3
|
+
interface NavFloatButtonProps {
|
|
4
|
+
containerRef?: React.RefObject<HTMLDivElement | null>;
|
|
5
|
+
showExitFullscreen?: boolean;
|
|
6
|
+
showExpandHeader?: boolean;
|
|
7
|
+
onExitFullscreen?: () => void;
|
|
8
|
+
onExpandHeader?: () => void;
|
|
9
|
+
}
|
|
10
|
+
declare function NavFloatButton({ containerRef: externalContainerRef, showExitFullscreen, showExpandHeader, onExitFullscreen, onExpandHeader }: NavFloatButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { NavFloatButton };
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { Button } from "./button.js";
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
import { ArrowDownToLine, Minimize2 } from "lucide-react";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
//#region src/nav-float-button.tsx
|
|
8
|
+
const BUTTON_SIZE = 40;
|
|
9
|
+
const getCornerRadius = (corner, mode) => {
|
|
10
|
+
const radius = "14px";
|
|
11
|
+
const small = "14px";
|
|
12
|
+
if (mode === "expand") switch (corner) {
|
|
13
|
+
case "top-left": return `${radius} 0 ${radius} 0`;
|
|
14
|
+
case "top-right": return `0 ${radius} 0 ${radius}`;
|
|
15
|
+
case "bottom-left": return `0 ${radius} 0 ${radius}`;
|
|
16
|
+
case "bottom-right": return `${radius} 0 ${radius} 0`;
|
|
17
|
+
}
|
|
18
|
+
else switch (corner) {
|
|
19
|
+
case "top-left": return `0 0 ${small} 0`;
|
|
20
|
+
case "top-right": return `0 0 0 ${small}`;
|
|
21
|
+
case "bottom-left": return `0 ${small} 0 0`;
|
|
22
|
+
case "bottom-right": return `${small} 0 0 0`;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const getBorderStyle = (corner, fullBorder) => {
|
|
26
|
+
if (fullBorder) return {
|
|
27
|
+
borderLeftColor: "var(--border)",
|
|
28
|
+
borderRightColor: "var(--border)",
|
|
29
|
+
borderTopColor: "var(--border)",
|
|
30
|
+
borderBottomColor: "var(--border)"
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
borderLeftColor: corner === "top-left" || corner === "bottom-left" ? "var(--background)" : "var(--border)",
|
|
34
|
+
borderRightColor: corner === "top-right" || corner === "bottom-right" ? "var(--background)" : "var(--border)",
|
|
35
|
+
borderTopColor: corner === "top-left" || corner === "top-right" ? "var(--background)" : "var(--border)",
|
|
36
|
+
borderBottomColor: corner === "bottom-left" || corner === "bottom-right" ? "var(--background)" : "var(--border)"
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
function NavFloatButton({ containerRef: externalContainerRef, showExitFullscreen, showExpandHeader, onExitFullscreen, onExpandHeader }) {
|
|
40
|
+
const { t } = useTranslation("common");
|
|
41
|
+
const buttonRef = useRef(null);
|
|
42
|
+
const containerRef = externalContainerRef;
|
|
43
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
44
|
+
const [corner, setCorner] = useState("top-right");
|
|
45
|
+
const [openTooltip, setOpenTooltip] = useState(false);
|
|
46
|
+
const dragStartRef = useRef({
|
|
47
|
+
x: 0,
|
|
48
|
+
y: 0,
|
|
49
|
+
offsetX: 0,
|
|
50
|
+
offsetY: 0
|
|
51
|
+
});
|
|
52
|
+
const isMovedRef = useRef(false);
|
|
53
|
+
const [position, setPosition] = useState({
|
|
54
|
+
x: 0,
|
|
55
|
+
y: 0
|
|
56
|
+
});
|
|
57
|
+
const getInitialPosition = useCallback(() => {
|
|
58
|
+
const container = containerRef?.current;
|
|
59
|
+
if (!container) return {
|
|
60
|
+
x: 0,
|
|
61
|
+
y: 0
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
x: container.clientWidth - BUTTON_SIZE + 1,
|
|
65
|
+
y: -1
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!showExitFullscreen && !showExpandHeader) return;
|
|
70
|
+
const container = containerRef?.current;
|
|
71
|
+
if (!container) return;
|
|
72
|
+
const updatePosition = () => {
|
|
73
|
+
setPosition(getInitialPosition());
|
|
74
|
+
};
|
|
75
|
+
updatePosition();
|
|
76
|
+
const ro = new ResizeObserver(updatePosition);
|
|
77
|
+
ro.observe(container);
|
|
78
|
+
return () => ro.disconnect();
|
|
79
|
+
}, [
|
|
80
|
+
showExitFullscreen,
|
|
81
|
+
showExpandHeader,
|
|
82
|
+
getInitialPosition
|
|
83
|
+
]);
|
|
84
|
+
const handlePointerDown = useCallback((e) => {
|
|
85
|
+
const btn = buttonRef.current;
|
|
86
|
+
if (!btn) return;
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
setOpenTooltip(false);
|
|
90
|
+
btn.setPointerCapture(e.pointerId);
|
|
91
|
+
setIsDragging(true);
|
|
92
|
+
isMovedRef.current = false;
|
|
93
|
+
dragStartRef.current = {
|
|
94
|
+
x: e.clientX,
|
|
95
|
+
y: e.clientY,
|
|
96
|
+
offsetX: position.x,
|
|
97
|
+
offsetY: position.y
|
|
98
|
+
};
|
|
99
|
+
document.body.style.cursor = "grabbing";
|
|
100
|
+
document.body.style.userSelect = "none";
|
|
101
|
+
}, [position]);
|
|
102
|
+
const handlePointerMove = useCallback((e) => {
|
|
103
|
+
if (!isDragging) return;
|
|
104
|
+
const container = containerRef?.current;
|
|
105
|
+
if (!container) return;
|
|
106
|
+
const dx = e.clientX - dragStartRef.current.x;
|
|
107
|
+
const dy = e.clientY - dragStartRef.current.y;
|
|
108
|
+
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) isMovedRef.current = true;
|
|
109
|
+
const maxX = container.clientWidth - BUTTON_SIZE;
|
|
110
|
+
const maxY = container.clientHeight - BUTTON_SIZE;
|
|
111
|
+
const newX = Math.max(-1, Math.min(maxX + 1, dragStartRef.current.offsetX + dx));
|
|
112
|
+
const newY = Math.max(-1, Math.min(maxY + 1, dragStartRef.current.offsetY + dy));
|
|
113
|
+
setPosition({
|
|
114
|
+
x: newX,
|
|
115
|
+
y: newY
|
|
116
|
+
});
|
|
117
|
+
}, [isDragging]);
|
|
118
|
+
const handlePointerUp = useCallback((e) => {
|
|
119
|
+
const btn = buttonRef.current;
|
|
120
|
+
if (!btn || !isDragging) return;
|
|
121
|
+
btn.releasePointerCapture(e.pointerId);
|
|
122
|
+
setIsDragging(false);
|
|
123
|
+
document.body.style.cursor = "";
|
|
124
|
+
document.body.style.userSelect = "";
|
|
125
|
+
if (!isMovedRef.current) {
|
|
126
|
+
if (showExitFullscreen && onExitFullscreen) onExitFullscreen();
|
|
127
|
+
if (showExpandHeader && onExpandHeader) onExpandHeader();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const container = containerRef?.current;
|
|
131
|
+
if (!container) return;
|
|
132
|
+
const maxX = container.clientWidth - BUTTON_SIZE;
|
|
133
|
+
const maxY = container.clientHeight - BUTTON_SIZE;
|
|
134
|
+
const snapPoints = [
|
|
135
|
+
{
|
|
136
|
+
x: -1,
|
|
137
|
+
y: -1,
|
|
138
|
+
corner: "top-left"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
x: maxX + 1,
|
|
142
|
+
y: -1,
|
|
143
|
+
corner: "top-right"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
x: -1,
|
|
147
|
+
y: maxY + 1,
|
|
148
|
+
corner: "bottom-left"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
x: maxX + 1,
|
|
152
|
+
y: maxY + 1,
|
|
153
|
+
corner: "bottom-right"
|
|
154
|
+
}
|
|
155
|
+
];
|
|
156
|
+
let nearest = snapPoints[0];
|
|
157
|
+
let minDist = Infinity;
|
|
158
|
+
for (const sp of snapPoints) {
|
|
159
|
+
const dist = Math.hypot(position.x - sp.x, position.y - sp.y);
|
|
160
|
+
if (dist < minDist) {
|
|
161
|
+
minDist = dist;
|
|
162
|
+
nearest = sp;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
setCorner(nearest.corner);
|
|
166
|
+
setPosition({
|
|
167
|
+
x: nearest.x,
|
|
168
|
+
y: nearest.y
|
|
169
|
+
});
|
|
170
|
+
}, [
|
|
171
|
+
isDragging,
|
|
172
|
+
position,
|
|
173
|
+
showExitFullscreen,
|
|
174
|
+
showExpandHeader,
|
|
175
|
+
onExitFullscreen,
|
|
176
|
+
onExpandHeader
|
|
177
|
+
]);
|
|
178
|
+
const mode = showExpandHeader && !showExitFullscreen ? "expand" : "exit";
|
|
179
|
+
const borderStyle = getBorderStyle(corner, isDragging);
|
|
180
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", {
|
|
181
|
+
ref: buttonRef,
|
|
182
|
+
className: "absolute z-50 select-none",
|
|
183
|
+
style: {
|
|
184
|
+
left: 0,
|
|
185
|
+
top: 0,
|
|
186
|
+
transform: `translate(${position.x}px, ${position.y}px)`,
|
|
187
|
+
touchAction: "none",
|
|
188
|
+
cursor: isDragging ? "grabbing" : "grab",
|
|
189
|
+
transition: isDragging ? "none" : "transform 0.2s ease-out"
|
|
190
|
+
},
|
|
191
|
+
onPointerDown: handlePointerDown,
|
|
192
|
+
onPointerMove: handlePointerMove,
|
|
193
|
+
onPointerUp: handlePointerUp,
|
|
194
|
+
onPointerCancel: handlePointerUp,
|
|
195
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
196
|
+
className: "bg-background/95 backdrop-blur flex items-center justify-center",
|
|
197
|
+
style: {
|
|
198
|
+
width: BUTTON_SIZE,
|
|
199
|
+
height: BUTTON_SIZE,
|
|
200
|
+
borderRadius: isDragging ? "50%" : getCornerRadius(corner, mode),
|
|
201
|
+
borderWidth: 1,
|
|
202
|
+
borderStyle: "solid",
|
|
203
|
+
...borderStyle,
|
|
204
|
+
transition: "border-radius 0.2s ease-out, border-color 0.2s ease-out"
|
|
205
|
+
},
|
|
206
|
+
children: [showExitFullscreen && /* @__PURE__ */ jsxs(Tooltip, {
|
|
207
|
+
open: openTooltip,
|
|
208
|
+
onOpenChange: setOpenTooltip,
|
|
209
|
+
delayDuration: 0,
|
|
210
|
+
children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
211
|
+
asChild: true,
|
|
212
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
213
|
+
variant: "ghost",
|
|
214
|
+
size: "sm",
|
|
215
|
+
className: "h-8 w-8",
|
|
216
|
+
onMouseEnter: () => !isDragging && setOpenTooltip(true),
|
|
217
|
+
onMouseLeave: () => setOpenTooltip(false),
|
|
218
|
+
children: /* @__PURE__ */ jsx(Minimize2, { className: "h-4 w-4" })
|
|
219
|
+
})
|
|
220
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
221
|
+
side: "bottom",
|
|
222
|
+
className: "z-[9999]",
|
|
223
|
+
children: /* @__PURE__ */ jsx("p", { children: t("navFloat.exitFullscreen") })
|
|
224
|
+
})]
|
|
225
|
+
}), showExpandHeader && /* @__PURE__ */ jsxs(Tooltip, {
|
|
226
|
+
open: openTooltip,
|
|
227
|
+
onOpenChange: setOpenTooltip,
|
|
228
|
+
delayDuration: 0,
|
|
229
|
+
children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
230
|
+
asChild: true,
|
|
231
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
232
|
+
variant: "ghost",
|
|
233
|
+
size: "sm",
|
|
234
|
+
className: "h-8 w-8",
|
|
235
|
+
onMouseEnter: () => !isDragging && setOpenTooltip(true),
|
|
236
|
+
onMouseLeave: () => setOpenTooltip(false),
|
|
237
|
+
children: /* @__PURE__ */ jsx(ArrowDownToLine, { className: "h-4 w-4" })
|
|
238
|
+
})
|
|
239
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
240
|
+
side: "bottom",
|
|
241
|
+
className: "z-[9999]",
|
|
242
|
+
children: /* @__PURE__ */ jsx("p", { children: t("navFloat.showHeader") })
|
|
243
|
+
})]
|
|
244
|
+
})]
|
|
245
|
+
})
|
|
246
|
+
}) });
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
export { NavFloatButton };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AvatarConfig, ShapeType } from "@allbluecn/shared";
|
|
2
|
+
//#region src/notion-avatar.d.ts
|
|
3
|
+
type NotionAvatarProps = {
|
|
4
|
+
config: AvatarConfig;
|
|
5
|
+
bgColor?: string;
|
|
6
|
+
shape?: ShapeType;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
flip?: boolean;
|
|
10
|
+
};
|
|
11
|
+
declare function NotionAvatar({ config, bgColor, shape, className, style, flip }: NotionAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { NotionAvatar };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
//#region src/notion-avatar.tsx
|
|
5
|
+
const noneParts = [
|
|
6
|
+
"glasses",
|
|
7
|
+
"hair",
|
|
8
|
+
"accessories",
|
|
9
|
+
"details",
|
|
10
|
+
"beard"
|
|
11
|
+
];
|
|
12
|
+
const partOrder = [
|
|
13
|
+
"face",
|
|
14
|
+
"nose",
|
|
15
|
+
"mouth",
|
|
16
|
+
"eyes",
|
|
17
|
+
"eyebrows",
|
|
18
|
+
"glasses",
|
|
19
|
+
"hair",
|
|
20
|
+
"accessories",
|
|
21
|
+
"details",
|
|
22
|
+
"beard"
|
|
23
|
+
];
|
|
24
|
+
function getPreviewImagePath(part, index) {
|
|
25
|
+
return `/avatar/preview/${part}/${index}.svg`;
|
|
26
|
+
}
|
|
27
|
+
const SVGFilter = `<defs>
|
|
28
|
+
<filter id="notion-avatar-filter" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="linearRGB">
|
|
29
|
+
<feMorphology operator="dilate" radius="20" in="SourceAlpha" result="morphology"/>
|
|
30
|
+
<feFlood flood-color="#ffffff" flood-opacity="1" result="flood"/>
|
|
31
|
+
<feComposite in="flood" in2="morphology" operator="in" result="composite"/>
|
|
32
|
+
<feMerge result="merge">
|
|
33
|
+
<feMergeNode in="composite" result="mergeNode"/>
|
|
34
|
+
<feMergeNode in="SourceGraphic" result="mergeNode1"/>
|
|
35
|
+
</feMerge>
|
|
36
|
+
</filter>
|
|
37
|
+
</defs>`;
|
|
38
|
+
function extractInnerSvg(svgText) {
|
|
39
|
+
return svgText.replace(/<svg[^>]*>/i, "").replace(/<\/svg>/i, "").trim();
|
|
40
|
+
}
|
|
41
|
+
const shapeStyle = {
|
|
42
|
+
circle: "rounded-full",
|
|
43
|
+
rounded: "rounded-lg",
|
|
44
|
+
square: "rounded-none"
|
|
45
|
+
};
|
|
46
|
+
function NotionAvatar({ config, bgColor = "transparent", shape = "circle", className, style, flip = false }) {
|
|
47
|
+
const [svgContent, setSvgContent] = useState("");
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
async function buildAvatar() {
|
|
50
|
+
const groups = await Promise.all(partOrder.map(async (part) => {
|
|
51
|
+
const index = config[part];
|
|
52
|
+
if (noneParts.includes(part) && index === 0) return "";
|
|
53
|
+
try {
|
|
54
|
+
const inner = extractInnerSvg(await (await fetch(getPreviewImagePath(part, index))).text());
|
|
55
|
+
return `\n<g id="avatar-${part}"${part === "face" ? " fill=\"#ffffff\"" : ""}>${inner}</g>`;
|
|
56
|
+
} catch {
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
const avatarSvg = `<svg viewBox="0 0 1080 1080" fill="none" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%">${SVGFilter}<g id="avatar"${flip ? " transform=\"scale(-1,1) translate(-1080, 0)\"" : ""}>${groups.join("")}</g></svg>`;
|
|
61
|
+
setSvgContent(avatarSvg);
|
|
62
|
+
}
|
|
63
|
+
buildAvatar();
|
|
64
|
+
}, [config, flip]);
|
|
65
|
+
if (!svgContent) return /* @__PURE__ */ jsx("div", {
|
|
66
|
+
className: `${shapeStyle[shape]} overflow-hidden bg-muted animate-pulse ${className ?? ""}`,
|
|
67
|
+
style: {
|
|
68
|
+
aspectRatio: "1 / 1",
|
|
69
|
+
...style
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return /* @__PURE__ */ jsx("div", {
|
|
73
|
+
className: `${shapeStyle[shape]} ${className ?? ""}`,
|
|
74
|
+
style: {
|
|
75
|
+
backgroundColor: bgColor === "transparent" ? void 0 : bgColor,
|
|
76
|
+
aspectRatio: "1 / 1",
|
|
77
|
+
...style
|
|
78
|
+
},
|
|
79
|
+
dangerouslySetInnerHTML: { __html: svgContent }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { NotionAvatar };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LucideIcon } from "lucide-react";
|
|
2
|
+
//#region src/placeholder-page.d.ts
|
|
3
|
+
type PlaceholderPageProps = {
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
icon: LucideIcon;
|
|
7
|
+
};
|
|
8
|
+
declare function PlaceholderPage({ title, description, icon: Icon }: PlaceholderPageProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { PlaceholderPage };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
//#region src/placeholder-page.tsx
|
|
4
|
+
function PlaceholderPage({ title, description, icon: Icon }) {
|
|
5
|
+
const { t } = useTranslation("common");
|
|
6
|
+
return /* @__PURE__ */ jsx("div", {
|
|
7
|
+
className: "flex h-full min-h-[60vh] items-center justify-center",
|
|
8
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
9
|
+
className: "flex flex-col items-center gap-4 rounded-xl border bg-card p-12 text-center shadow-sm",
|
|
10
|
+
children: [
|
|
11
|
+
/* @__PURE__ */ jsx("div", {
|
|
12
|
+
className: "flex size-16 items-center justify-center rounded-full bg-muted",
|
|
13
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "size-8 text-muted-foreground" })
|
|
14
|
+
}),
|
|
15
|
+
/* @__PURE__ */ jsx("h1", {
|
|
16
|
+
className: "text-xl font-semibold",
|
|
17
|
+
children: title
|
|
18
|
+
}),
|
|
19
|
+
/* @__PURE__ */ jsx("p", {
|
|
20
|
+
className: "text-sm text-muted-foreground",
|
|
21
|
+
children: description ?? t("placeholder.developing")
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { PlaceholderPage };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
import { Popover as Popover$1 } from "radix-ui";
|
|
3
|
+
//#region src/popover.d.ts
|
|
4
|
+
declare function Popover({ ...props }: React$1.ComponentProps<typeof Popover$1.Root>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function PopoverTrigger({ ...props }: React$1.ComponentProps<typeof Popover$1.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function PopoverContent({ className, align, sideOffset, ...props }: React$1.ComponentProps<typeof Popover$1.Content>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function PopoverAnchor({ ...props }: React$1.ComponentProps<typeof Popover$1.Anchor>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function PopoverHeader({ className, ...props }: React$1.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function PopoverTitle({ className, ...props }: React$1.ComponentProps<"h2">): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
declare function PopoverDescription({ className, ...props }: React$1.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
|