@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.
Files changed (146) hide show
  1. package/dist/ai-command-button.d.ts +10 -0
  2. package/dist/ai-command-button.js +12 -0
  3. package/dist/ai-stream-text.d.ts +9 -0
  4. package/dist/ai-stream-text.js +12 -0
  5. package/dist/alert-dialog.d.ts +20 -0
  6. package/dist/alert-dialog.js +101 -0
  7. package/dist/animated-theme-toggler.d.ts +11 -0
  8. package/dist/animated-theme-toggler.js +169 -0
  9. package/dist/auth-client.d.ts +27 -0
  10. package/dist/auth-client.js +86 -0
  11. package/dist/auth-layout.d.ts +14 -0
  12. package/dist/auth-layout.js +43 -0
  13. package/dist/avatar.d.ts +13 -0
  14. package/dist/avatar.js +51 -0
  15. package/dist/badge.d.ts +11 -0
  16. package/dist/badge.js +28 -0
  17. package/dist/button.d.ts +12 -0
  18. package/dist/button.js +48 -0
  19. package/dist/card.d.ts +13 -0
  20. package/dist/card.js +56 -0
  21. package/dist/checkbox.d.ts +6 -0
  22. package/dist/checkbox.js +22 -0
  23. package/dist/context-menu.d.ts +33 -0
  24. package/dist/context-menu.js +125 -0
  25. package/dist/dialog.d.ts +19 -0
  26. package/dist/dialog.js +95 -0
  27. package/dist/dropdown-menu.d.ts +31 -0
  28. package/dist/dropdown-menu.js +128 -0
  29. package/dist/empty.d.ts +13 -0
  30. package/dist/empty.js +59 -0
  31. package/dist/grid-pattern.d.ts +14 -0
  32. package/dist/grid-pattern.js +47 -0
  33. package/dist/icon-packs/index.d.ts +2 -0
  34. package/dist/icon-packs/index.js +4 -0
  35. package/dist/icon-packs/knowledge.d.ts +2 -0
  36. package/dist/icon-packs/knowledge.js +70 -0
  37. package/dist/icon-packs/prd.d.ts +2 -0
  38. package/dist/icon-packs/prd.js +86 -0
  39. package/dist/icon-packs/project.d.ts +2 -0
  40. package/dist/icon-packs/project.js +74 -0
  41. package/dist/icon-picker.d.ts +11 -0
  42. package/dist/icon-picker.js +39 -0
  43. package/dist/index-CQsbeClc.d.ts +17 -0
  44. package/dist/index.d.ts +7 -0
  45. package/dist/index.js +7 -0
  46. package/dist/input.d.ts +5 -0
  47. package/dist/input.js +14 -0
  48. package/dist/item.d.ts +25 -0
  49. package/dist/item.js +118 -0
  50. package/dist/kbd.d.ts +4 -0
  51. package/dist/kbd.js +12 -0
  52. package/dist/label.d.ts +6 -0
  53. package/dist/label.js +14 -0
  54. package/dist/layout-panel-top.d.ts +12 -0
  55. package/dist/layout-panel-top.js +127 -0
  56. package/dist/name-confirm-dialog.d.ts +15 -0
  57. package/dist/name-confirm-dialog.js +105 -0
  58. package/dist/nav-float-button.d.ts +12 -0
  59. package/dist/nav-float-button.js +249 -0
  60. package/dist/notion-avatar.d.ts +13 -0
  61. package/dist/notion-avatar.js +83 -0
  62. package/dist/placeholder-page.d.ts +10 -0
  63. package/dist/placeholder-page.js +28 -0
  64. package/dist/popover.d.ts +12 -0
  65. package/dist/popover.js +56 -0
  66. package/dist/reference-inline.d.ts +10 -0
  67. package/dist/reference-inline.js +35 -0
  68. package/dist/reference-search.d.ts +12 -0
  69. package/dist/reference-search.js +26 -0
  70. package/dist/resizable.d.ts +9 -0
  71. package/dist/resizable.js +28 -0
  72. package/dist/select.d.ts +17 -0
  73. package/dist/select.js +99 -0
  74. package/dist/separator.d.ts +6 -0
  75. package/dist/separator.js +17 -0
  76. package/dist/sheet.d.ts +16 -0
  77. package/dist/sheet.js +90 -0
  78. package/dist/sidebar.d.ts +71 -0
  79. package/dist/sidebar.js +369 -0
  80. package/dist/skeleton.d.ts +4 -0
  81. package/dist/skeleton.js +12 -0
  82. package/dist/sonner.d.ts +5 -0
  83. package/dist/sonner.js +19 -0
  84. package/dist/switch.d.ts +8 -0
  85. package/dist/switch.js +19 -0
  86. package/dist/textarea.d.ts +5 -0
  87. package/dist/textarea.js +13 -0
  88. package/dist/timezone-provider.d.ts +18 -0
  89. package/dist/timezone-provider.js +29 -0
  90. package/dist/tooltip.d.ts +9 -0
  91. package/dist/tooltip.js +35 -0
  92. package/dist/use-mobile.d.ts +4 -0
  93. package/dist/use-mobile.js +18 -0
  94. package/dist/user-avatar.d.ts +14 -0
  95. package/dist/user-avatar.js +34 -0
  96. package/dist/utils.d.ts +5 -0
  97. package/dist/utils.js +8 -0
  98. package/package.json +178 -49
  99. package/src/ai-command-button.tsx +0 -36
  100. package/src/ai-stream-text.tsx +0 -37
  101. package/src/alert-dialog.tsx +0 -216
  102. package/src/animated-theme-toggler.tsx +0 -272
  103. package/src/auth-client.ts +0 -108
  104. package/src/auth-layout.tsx +0 -66
  105. package/src/avatar.tsx +0 -129
  106. package/src/badge.tsx +0 -66
  107. package/src/button.tsx +0 -84
  108. package/src/card.tsx +0 -120
  109. package/src/checkbox.tsx +0 -50
  110. package/src/context-menu.tsx +0 -280
  111. package/src/dialog.tsx +0 -183
  112. package/src/dropdown-menu.tsx +0 -286
  113. package/src/empty.tsx +0 -121
  114. package/src/grid-pattern.tsx +0 -87
  115. package/src/icon-packs/index.ts +0 -27
  116. package/src/icon-packs/knowledge.ts +0 -55
  117. package/src/icon-packs/prd.ts +0 -63
  118. package/src/icon-packs/project.ts +0 -59
  119. package/src/icon-picker.tsx +0 -68
  120. package/src/index.ts +0 -23
  121. package/src/input.tsx +0 -36
  122. package/src/item.tsx +0 -213
  123. package/src/kbd.tsx +0 -33
  124. package/src/label.tsx +0 -39
  125. package/src/layout-panel-top.tsx +0 -160
  126. package/src/name-confirm-dialog.tsx +0 -153
  127. package/src/nav-float-button.tsx +0 -301
  128. package/src/notion-avatar.tsx +0 -126
  129. package/src/placeholder-page.tsx +0 -42
  130. package/src/popover.tsx +0 -106
  131. package/src/reference-inline.tsx +0 -53
  132. package/src/reference-search.tsx +0 -52
  133. package/src/resizable.tsx +0 -67
  134. package/src/select.tsx +0 -207
  135. package/src/separator.tsx +0 -45
  136. package/src/sheet.tsx +0 -164
  137. package/src/sidebar.tsx +0 -708
  138. package/src/skeleton.tsx +0 -30
  139. package/src/sonner.tsx +0 -37
  140. package/src/switch.tsx +0 -48
  141. package/src/textarea.tsx +0 -35
  142. package/src/timezone-provider.tsx +0 -55
  143. package/src/tooltip.tsx +0 -72
  144. package/src/use-mobile.ts +0 -36
  145. package/src/user-avatar.tsx +0 -79
  146. 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 };