@underverse-ui/underverse 1.0.202 → 1.0.204
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/README.md +8 -2
- package/api-reference.json +1 -1
- package/dist/EmojiPicker-UN6N7YVB.js +14 -0
- package/dist/EmojiPicker-UN6N7YVB.js.map +1 -0
- package/dist/chunk-4TYW5K4J.js +769 -0
- package/dist/chunk-4TYW5K4J.js.map +1 -0
- package/dist/chunk-CC3QO2YM.js +14506 -0
- package/dist/chunk-CC3QO2YM.js.map +1 -0
- package/dist/chunk-CKKLFVOY.js +206 -0
- package/dist/chunk-CKKLFVOY.js.map +1 -0
- package/dist/chunk-GSBRTFP2.js +2464 -0
- package/dist/chunk-GSBRTFP2.js.map +1 -0
- package/dist/chunk-KFTYWTJR.js +4481 -0
- package/dist/chunk-KFTYWTJR.js.map +1 -0
- package/dist/chunk-NSBPE2FW.js +17 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/chunk-QVQTWZ24.js +199 -0
- package/dist/chunk-QVQTWZ24.js.map +1 -0
- package/dist/chunk-ZUAIBY2Z.js +74 -0
- package/dist/chunk-ZUAIBY2Z.js.map +1 -0
- package/dist/emojis-I5AMZ3EE.js +8 -0
- package/dist/emojis-I5AMZ3EE.js.map +1 -0
- package/dist/index.cjs +36680 -35147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -134
- package/dist/index.d.ts +19 -134
- package/dist/index.js +2801 -25223
- package/dist/index.js.map +1 -1
- package/dist/lowlight-runtime-Y43Y7YTW.js +11 -0
- package/dist/lowlight-runtime-Y43Y7YTW.js.map +1 -0
- package/dist/menu-bar-6U5S4PS7.js +947 -0
- package/dist/menu-bar-6U5S4PS7.js.map +1 -0
- package/dist/ueditor.cjs +23503 -0
- package/dist/ueditor.cjs.map +1 -0
- package/dist/ueditor.d.cts +136 -0
- package/dist/ueditor.d.ts +136 -0
- package/dist/ueditor.js +20 -0
- package/dist/ueditor.js.map +1 -0
- package/package.json +54 -46
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
__require,
|
|
15
|
+
__export
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=chunk-NSBPE2FW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPanelBorderRadiusClass,
|
|
3
|
+
useUnderverseUIConfig
|
|
4
|
+
} from "./chunk-KFTYWTJR.js";
|
|
5
|
+
import {
|
|
6
|
+
cn,
|
|
7
|
+
useGlobalI18n
|
|
8
|
+
} from "./chunk-GSBRTFP2.js";
|
|
9
|
+
|
|
10
|
+
// src/components/Modal.tsx
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { createPortal } from "react-dom";
|
|
13
|
+
import { X } from "lucide-react";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
var sizeStyles = {
|
|
16
|
+
sm: "max-w-md",
|
|
17
|
+
md: "max-w-2xl",
|
|
18
|
+
lg: "max-w-4xl",
|
|
19
|
+
xl: "max-w-6xl",
|
|
20
|
+
full: "max-w-full"
|
|
21
|
+
};
|
|
22
|
+
var Modal = ({
|
|
23
|
+
isOpen,
|
|
24
|
+
onClose,
|
|
25
|
+
children,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
className,
|
|
29
|
+
contentClassName,
|
|
30
|
+
overlayClassName,
|
|
31
|
+
showCloseButton = true,
|
|
32
|
+
closeOnOverlayClick = true,
|
|
33
|
+
closeOnEsc = true,
|
|
34
|
+
size = "md",
|
|
35
|
+
noPadding = false,
|
|
36
|
+
fullWidth = false,
|
|
37
|
+
width,
|
|
38
|
+
height,
|
|
39
|
+
borderMode
|
|
40
|
+
}) => {
|
|
41
|
+
const gi18n = useGlobalI18n();
|
|
42
|
+
const globalConfig = useUnderverseUIConfig();
|
|
43
|
+
const resolvedBorderMode = borderMode ?? globalConfig.borderMode;
|
|
44
|
+
const [isMounted, setIsMounted] = React.useState(false);
|
|
45
|
+
const [isVisible, setIsVisible] = React.useState(false);
|
|
46
|
+
const [isAnimating, setIsAnimating] = React.useState(true);
|
|
47
|
+
const mouseDownTarget = React.useRef(null);
|
|
48
|
+
const modalContentRef = React.useRef(null);
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
setIsMounted(true);
|
|
51
|
+
return () => setIsMounted(false);
|
|
52
|
+
}, []);
|
|
53
|
+
const animationRef = React.useRef(false);
|
|
54
|
+
React.useEffect(() => {
|
|
55
|
+
if (isOpen) {
|
|
56
|
+
if (animationRef.current) return;
|
|
57
|
+
animationRef.current = true;
|
|
58
|
+
setIsVisible(true);
|
|
59
|
+
setIsAnimating(true);
|
|
60
|
+
requestAnimationFrame(() => {
|
|
61
|
+
setIsAnimating(false);
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
animationRef.current = false;
|
|
65
|
+
if (isVisible) {
|
|
66
|
+
setIsAnimating(true);
|
|
67
|
+
const hideTimer = setTimeout(() => {
|
|
68
|
+
setIsVisible(false);
|
|
69
|
+
}, 200);
|
|
70
|
+
return () => clearTimeout(hideTimer);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, [isOpen, isVisible]);
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (!isOpen || !closeOnEsc) return;
|
|
76
|
+
const handleEscape = (event) => {
|
|
77
|
+
if (event.key === "Escape") {
|
|
78
|
+
onClose();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
document.addEventListener("keydown", handleEscape);
|
|
82
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
83
|
+
}, [isOpen, closeOnEsc, onClose]);
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
if (isOpen) {
|
|
86
|
+
document.body.style.overflow = "hidden";
|
|
87
|
+
} else {
|
|
88
|
+
document.body.style.overflow = "unset";
|
|
89
|
+
}
|
|
90
|
+
return () => {
|
|
91
|
+
document.body.style.overflow = "unset";
|
|
92
|
+
};
|
|
93
|
+
}, [isOpen]);
|
|
94
|
+
const isInsidePortal = (element) => {
|
|
95
|
+
if (!element) return false;
|
|
96
|
+
let current = element;
|
|
97
|
+
while (current) {
|
|
98
|
+
if (current.hasAttribute?.("data-datepicker") || current.hasAttribute?.("data-popover") || current.hasAttribute?.("data-dropdown") || current.hasAttribute?.("data-dropdown-menu") || current.hasAttribute?.("data-radix-popper-content-wrapper") || current.hasAttribute?.("data-radix-portal") || current.hasAttribute?.("data-tippy-root") || current.getAttribute?.("role") === "listbox" || current.getAttribute?.("role") === "dialog" || current.classList?.contains("datepicker-portal") || current.classList?.contains("popover-portal") || current.classList?.contains("tippy-box") || current.classList?.contains("tippy-content")) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
current = current.parentElement;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
};
|
|
105
|
+
const handleOverlayMouseDown = (event) => {
|
|
106
|
+
mouseDownTarget.current = event.target;
|
|
107
|
+
};
|
|
108
|
+
const handleOverlayMouseUp = (event) => {
|
|
109
|
+
const modalContent2 = modalContentRef.current;
|
|
110
|
+
const mouseDownTarget_ = mouseDownTarget.current;
|
|
111
|
+
const mouseUpTarget = event.target;
|
|
112
|
+
if (isInsidePortal(mouseDownTarget_) || isInsidePortal(mouseUpTarget)) {
|
|
113
|
+
mouseDownTarget.current = null;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const mouseDownOutside = modalContent2 && !modalContent2.contains(mouseDownTarget_);
|
|
117
|
+
const mouseUpOutside = modalContent2 && !modalContent2.contains(mouseUpTarget);
|
|
118
|
+
if (closeOnOverlayClick && mouseDownOutside && mouseUpOutside) {
|
|
119
|
+
onClose();
|
|
120
|
+
}
|
|
121
|
+
mouseDownTarget.current = null;
|
|
122
|
+
};
|
|
123
|
+
if (!isMounted || !isOpen && !isVisible) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const maxWidthClass = width ? "max-w-none" : fullWidth ? "max-w-full" : sizeStyles[size];
|
|
127
|
+
const modalContent = /* @__PURE__ */ jsxs(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
className: cn("fixed inset-0 z-[9999] flex items-center justify-center p-4 md:p-6", overlayClassName),
|
|
131
|
+
style: { overscrollBehavior: "contain" },
|
|
132
|
+
onMouseDown: handleOverlayMouseDown,
|
|
133
|
+
onMouseUp: handleOverlayMouseUp,
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsx(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-200 ease-out",
|
|
139
|
+
style: {
|
|
140
|
+
opacity: isOpen && !isAnimating ? 1 : 0
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
/* @__PURE__ */ jsxs(
|
|
145
|
+
"div",
|
|
146
|
+
{
|
|
147
|
+
ref: modalContentRef,
|
|
148
|
+
className: cn(
|
|
149
|
+
"relative w-full border border-border/40 bg-card text-card-foreground shadow-xl",
|
|
150
|
+
resolvedBorderMode ? getPanelBorderRadiusClass(resolvedBorderMode) : "rounded-2xl md:rounded-3xl",
|
|
151
|
+
"transition-all duration-200 ease-out",
|
|
152
|
+
maxWidthClass,
|
|
153
|
+
fullWidth && "mx-0",
|
|
154
|
+
className
|
|
155
|
+
),
|
|
156
|
+
style: {
|
|
157
|
+
opacity: isOpen && !isAnimating ? 1 : 0,
|
|
158
|
+
transform: isOpen && !isAnimating ? "scale(1)" : "scale(0.9)",
|
|
159
|
+
// Thêm dòng này để tạo hiệu ứng nảy
|
|
160
|
+
transition: "all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)",
|
|
161
|
+
width,
|
|
162
|
+
height
|
|
163
|
+
},
|
|
164
|
+
onClick: (e) => e.stopPropagation(),
|
|
165
|
+
children: [
|
|
166
|
+
(title || description || showCloseButton) && /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between p-6 pb-0", children: [
|
|
167
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
168
|
+
title && /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: title }),
|
|
169
|
+
description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
|
|
170
|
+
] }),
|
|
171
|
+
showCloseButton && /* @__PURE__ */ jsx(
|
|
172
|
+
"button",
|
|
173
|
+
{
|
|
174
|
+
onClick: onClose,
|
|
175
|
+
"aria-label": gi18n.closeModal ?? "Close modal",
|
|
176
|
+
className: cn(
|
|
177
|
+
"rounded-lg opacity-70 ring-offset-background transition-opacity",
|
|
178
|
+
"hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
179
|
+
"disabled:pointer-events-none "
|
|
180
|
+
),
|
|
181
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4 cursor-pointer", "aria-hidden": "true" })
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
] }),
|
|
185
|
+
/* @__PURE__ */ jsx("div", { className: cn("p-6", noPadding && "p-0", contentClassName), children })
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
return typeof window !== "undefined" ? createPortal(modalContent, document.body) : null;
|
|
193
|
+
};
|
|
194
|
+
var Modal_default = Modal;
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
Modal_default
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=chunk-QVQTWZ24.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Modal.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../utils/cn\";\nimport { X } from \"lucide-react\";\nimport { useGlobalI18n } from \"../contexts/GlobalI18nContext\";\nimport { getBorderRadiusClass, getPanelBorderRadiusClass, type BorderMode } from \"../utils/radius\";\nimport { useUnderverseUIConfig } from \"../contexts/UnderverseConfigContext\";\n\n/** Public props for the `Modal` component. */\ninterface ModalProps {\n isOpen: boolean;\n onClose: () => void;\n children: React.ReactNode;\n title?: string;\n description?: string;\n className?: string;\n contentClassName?: string;\n overlayClassName?: string;\n showCloseButton?: boolean;\n closeOnOverlayClick?: boolean;\n closeOnEsc?: boolean;\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n noPadding?: boolean;\n fullWidth?: boolean;\n width?: string | number;\n height?: string | number;\n borderMode?: BorderMode;\n}\n\nconst sizeStyles = {\n sm: \"max-w-md\",\n md: \"max-w-2xl\",\n lg: \"max-w-4xl\",\n xl: \"max-w-6xl\",\n full: \"max-w-full\",\n};\n\nconst Modal: React.FC<ModalProps> = ({\n isOpen,\n onClose,\n children,\n title,\n description,\n className,\n contentClassName,\n overlayClassName,\n showCloseButton = true,\n closeOnOverlayClick = true,\n closeOnEsc = true,\n size = \"md\",\n noPadding = false,\n fullWidth = false,\n width,\n height,\n borderMode,\n}) => {\n const gi18n = useGlobalI18n();\n const globalConfig = useUnderverseUIConfig();\n const resolvedBorderMode = borderMode ?? globalConfig.borderMode;\n const [isMounted, setIsMounted] = React.useState(false);\n const [isVisible, setIsVisible] = React.useState(false);\n const [isAnimating, setIsAnimating] = React.useState(true);\n // Track if mousedown started outside modal content\n const mouseDownTarget = React.useRef<EventTarget | null>(null);\n const modalContentRef = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n setIsMounted(true);\n return () => setIsMounted(false);\n }, []);\n\n // Ref to prevent double animation trigger in React StrictMode\n const animationRef = React.useRef(false);\n\n // Animation handling\n React.useEffect(() => {\n if (isOpen) {\n if (animationRef.current) return; // Prevent double animation in StrictMode\n animationRef.current = true;\n setIsVisible(true);\n setIsAnimating(true);\n // Start animation on next frame\n requestAnimationFrame(() => {\n setIsAnimating(false);\n });\n } else {\n animationRef.current = false;\n if (isVisible) {\n setIsAnimating(true);\n // Hide after animation completes\n const hideTimer = setTimeout(() => {\n setIsVisible(false);\n }, 200);\n return () => clearTimeout(hideTimer);\n }\n }\n }, [isOpen, isVisible]);\n\n // Handle escape key\n React.useEffect(() => {\n if (!isOpen || !closeOnEsc) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onClose();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeOnEsc, onClose]);\n\n // Prevent body scroll when modal is open\n React.useEffect(() => {\n if (isOpen) {\n document.body.style.overflow = \"hidden\";\n } else {\n document.body.style.overflow = \"unset\";\n }\n\n return () => {\n document.body.style.overflow = \"unset\";\n };\n }, [isOpen]);\n\n // Check if an element is inside a portal (datepicker, popover, dropdown, etc.)\n const isInsidePortal = (element: Node | null): boolean => {\n if (!element) return false;\n let current = element as HTMLElement | null;\n while (current) {\n // Check for common portal indicators (data attributes, z-index patterns)\n if (\n current.hasAttribute?.(\"data-datepicker\") ||\n current.hasAttribute?.(\"data-popover\") ||\n current.hasAttribute?.(\"data-dropdown\") ||\n current.hasAttribute?.(\"data-dropdown-menu\") ||\n current.hasAttribute?.(\"data-radix-popper-content-wrapper\") ||\n current.hasAttribute?.(\"data-radix-portal\") ||\n current.hasAttribute?.(\"data-tippy-root\") ||\n current.getAttribute?.(\"role\") === \"listbox\" ||\n current.getAttribute?.(\"role\") === \"dialog\" ||\n current.classList?.contains(\"datepicker-portal\") ||\n current.classList?.contains(\"popover-portal\") ||\n current.classList?.contains(\"tippy-box\") ||\n current.classList?.contains(\"tippy-content\")\n ) {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n };\n\n const handleOverlayMouseDown = (event: React.MouseEvent) => {\n // Store the mousedown target\n mouseDownTarget.current = event.target;\n };\n\n const handleOverlayMouseUp = (event: React.MouseEvent) => {\n // Check if both mousedown and mouseup occurred outside modal content\n const modalContent = modalContentRef.current;\n const mouseDownTarget_ = mouseDownTarget.current as Node;\n const mouseUpTarget = event.target as Node;\n\n // Don't close if clicking inside a portal element (datepicker, popover, etc.)\n if (isInsidePortal(mouseDownTarget_) || isInsidePortal(mouseUpTarget)) {\n mouseDownTarget.current = null;\n return;\n }\n\n const mouseDownOutside = modalContent && !modalContent.contains(mouseDownTarget_);\n const mouseUpOutside = modalContent && !modalContent.contains(mouseUpTarget);\n\n if (closeOnOverlayClick && mouseDownOutside && mouseUpOutside) {\n onClose();\n }\n mouseDownTarget.current = null;\n };\n\n if (!isMounted || (!isOpen && !isVisible)) {\n return null;\n }\n\n const maxWidthClass = width ? \"max-w-none\" : fullWidth ? \"max-w-full\" : sizeStyles[size];\n\n const modalContent = (\n <div\n className={cn(\"fixed inset-0 z-[9999] flex items-center justify-center p-4 md:p-6\", overlayClassName)}\n style={{ overscrollBehavior: \"contain\" }}\n onMouseDown={handleOverlayMouseDown}\n onMouseUp={handleOverlayMouseUp}\n >\n {/* Overlay */}\n <div\n className=\"absolute inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-200 ease-out\"\n style={{\n opacity: isOpen && !isAnimating ? 1 : 0,\n }}\n />\n\n {/* Modal */}\n <div\n ref={modalContentRef}\n className={cn(\n \"relative w-full border border-border/40 bg-card text-card-foreground shadow-xl\",\n resolvedBorderMode ? getPanelBorderRadiusClass(resolvedBorderMode) : \"rounded-2xl md:rounded-3xl\",\n \"transition-all duration-200 ease-out\",\n maxWidthClass,\n fullWidth && \"mx-0\",\n className,\n )}\n style={{\n opacity: isOpen && !isAnimating ? 1 : 0,\n transform: isOpen && !isAnimating ? \"scale(1)\" : \"scale(0.9)\",\n // Thêm dòng này để tạo hiệu ứng nảy\n transition: \"all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)\",\n width,\n height,\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n {(title || description || showCloseButton) && (\n <div className=\"flex items-start justify-between p-6 pb-0\">\n <div className=\"space-y-1.5\">\n {title && <h2 className=\"text-lg font-semibold leading-none tracking-tight\">{title}</h2>}\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {showCloseButton && (\n <button\n onClick={onClose}\n aria-label={gi18n.closeModal ?? \"Close modal\"}\n className={cn(\n \"rounded-lg opacity-70 ring-offset-background transition-opacity\",\n \"hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n \"disabled:pointer-events-none \",\n )}\n >\n <X className=\"h-4 w-4 cursor-pointer\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n\n {/* Content */}\n <div className={cn(\"p-6\", noPadding && \"p-0\", contentClassName)}>{children}</div>\n </div>\n </div>\n );\n\n return typeof window !== \"undefined\" ? createPortal(modalContent, document.body) : null;\n};\n\nexport default Modal;\n"],"mappings":"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","names":["modalContent"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-GSBRTFP2.js";
|
|
4
|
+
|
|
5
|
+
// src/components/emoji-ui.tsx
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
function formatEmojiName(name) {
|
|
9
|
+
return name.replaceAll("_", " ");
|
|
10
|
+
}
|
|
11
|
+
function formatEmojiCountLabel(template, shown, total) {
|
|
12
|
+
return template.replace("{shown}", String(shown)).replace("{total}", String(total));
|
|
13
|
+
}
|
|
14
|
+
function getEmojiUnifiedCode(emoji) {
|
|
15
|
+
const codePoints = [];
|
|
16
|
+
for (let i = 0; i < emoji.length; ) {
|
|
17
|
+
const codePoint = emoji.codePointAt(i);
|
|
18
|
+
if (codePoint === void 0) break;
|
|
19
|
+
let hex = codePoint.toString(16).toLowerCase();
|
|
20
|
+
if (hex.length < 4) {
|
|
21
|
+
hex = hex.padStart(4, "0");
|
|
22
|
+
}
|
|
23
|
+
codePoints.push(hex);
|
|
24
|
+
i += codePoint > 65535 ? 2 : 1;
|
|
25
|
+
}
|
|
26
|
+
return codePoints.join("-");
|
|
27
|
+
}
|
|
28
|
+
var globalEmojiBaseUrl = "/emojis";
|
|
29
|
+
function setEmojiBaseUrl(url) {
|
|
30
|
+
globalEmojiBaseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
|
|
31
|
+
}
|
|
32
|
+
function getEmojiImageUrl(unified) {
|
|
33
|
+
return `${globalEmojiBaseUrl}/${unified}.png`;
|
|
34
|
+
}
|
|
35
|
+
var EmojiGridButton = ({ emoji, name, onClick, className, active = false }) => {
|
|
36
|
+
const [hasError, setHasError] = React.useState(false);
|
|
37
|
+
const unified = React.useMemo(() => getEmojiUnifiedCode(emoji), [emoji]);
|
|
38
|
+
return /* @__PURE__ */ jsx(
|
|
39
|
+
"button",
|
|
40
|
+
{
|
|
41
|
+
type: "button",
|
|
42
|
+
"aria-label": formatEmojiName(name),
|
|
43
|
+
title: formatEmojiName(name),
|
|
44
|
+
onClick,
|
|
45
|
+
className: cn(
|
|
46
|
+
"group flex h-9 w-9 items-center justify-center rounded-xl transition-all duration-300",
|
|
47
|
+
"bg-transparent hover:bg-primary/10 border border-transparent hover:border-primary/20",
|
|
48
|
+
"focus:outline-none focus:ring-2 focus:ring-primary/20",
|
|
49
|
+
"scale-100 hover:scale-110 active:scale-95",
|
|
50
|
+
active ? "bg-primary/15 border-primary/30 ring-2 ring-primary/20" : "",
|
|
51
|
+
className
|
|
52
|
+
),
|
|
53
|
+
children: hasError ? /* @__PURE__ */ jsx("span", { className: "text-2xl transition-transform duration-300 group-hover:rotate-6", children: emoji }) : /* @__PURE__ */ jsx(
|
|
54
|
+
"img",
|
|
55
|
+
{
|
|
56
|
+
src: getEmojiImageUrl(unified),
|
|
57
|
+
alt: emoji,
|
|
58
|
+
onError: () => setHasError(true),
|
|
59
|
+
className: "h-6.5 w-6.5 object-contain transition-all duration-300 group-hover:rotate-6 group-hover:drop-shadow-[0_2px_5px_rgba(0,0,0,0.15)]",
|
|
60
|
+
loading: "lazy"
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
formatEmojiCountLabel,
|
|
69
|
+
getEmojiUnifiedCode,
|
|
70
|
+
setEmojiBaseUrl,
|
|
71
|
+
getEmojiImageUrl,
|
|
72
|
+
EmojiGridButton
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=chunk-ZUAIBY2Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/emoji-ui.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport function formatEmojiName(name: string) {\n return name.replaceAll(\"_\", \" \");\n}\n\nexport function formatEmojiCountLabel(template: string, shown: number, total: number) {\n return template\n .replace(\"{shown}\", String(shown))\n .replace(\"{total}\", String(total));\n}\n\n/**\n * Converts a Unicode emoji string into a lowercase hyphenated hex code point format\n * used by emoji CDNs (e.g. \"😀\" -> \"1f600\", \"#️⃣\" -> \"0023-fe0f-20e3\").\n */\nexport function getEmojiUnifiedCode(emoji: string): string {\n const codePoints = [];\n for (let i = 0; i < emoji.length; ) {\n const codePoint = emoji.codePointAt(i);\n if (codePoint === undefined) break;\n let hex = codePoint.toString(16).toLowerCase();\n if (hex.length < 4) {\n hex = hex.padStart(4, \"0\");\n }\n codePoints.push(hex);\n i += codePoint > 0xffff ? 2 : 1;\n }\n return codePoints.join(\"-\");\n}\n\nlet globalEmojiBaseUrl = \"/emojis\";\n\n/**\n * Configure a custom base URL for loading emoji image assets (e.g. `/emojis` for local offline storage).\n * If not set, defaults to jsDelivr CDN.\n */\nexport function setEmojiBaseUrl(url: string) {\n globalEmojiBaseUrl = url.endsWith(\"/\") ? url.slice(0, -1) : url;\n}\n\n/**\n * Resolves the final image URL for a given unified emoji code.\n */\nexport function getEmojiImageUrl(unified: string): string {\n return `${globalEmojiBaseUrl}/${unified}.png`;\n}\n\nexport const EmojiGridButton: React.FC<{\n emoji: string;\n name: string;\n onClick: () => void;\n className?: string;\n active?: boolean;\n}> = ({ emoji, name, onClick, className, active = false }) => {\n const [hasError, setHasError] = React.useState(false);\n const unified = React.useMemo(() => getEmojiUnifiedCode(emoji), [emoji]);\n\n return (\n <button\n type=\"button\"\n aria-label={formatEmojiName(name)}\n title={formatEmojiName(name)}\n onClick={onClick}\n className={cn(\n \"group flex h-9 w-9 items-center justify-center rounded-xl transition-all duration-300\",\n \"bg-transparent hover:bg-primary/10 border border-transparent hover:border-primary/20\",\n \"focus:outline-none focus:ring-2 focus:ring-primary/20\",\n \"scale-100 hover:scale-110 active:scale-95\",\n active ? \"bg-primary/15 border-primary/30 ring-2 ring-primary/20\" : \"\",\n className,\n )}\n >\n {hasError ? (\n <span className=\"text-2xl transition-transform duration-300 group-hover:rotate-6\">{emoji}</span>\n ) : (\n <img\n src={getEmojiImageUrl(unified)}\n alt={emoji}\n onError={() => setHasError(true)}\n className=\"h-6.5 w-6.5 object-contain transition-all duration-300 group-hover:rotate-6 group-hover:drop-shadow-[0_2px_5px_rgba(0,0,0,0.15)]\"\n loading=\"lazy\"\n />\n )}\n </button>\n );\n};\n"],"mappings":";;;;;AAEA,OAAO,WAAW;AA2EV;AAxED,SAAS,gBAAgB,MAAc;AAC5C,SAAO,KAAK,WAAW,KAAK,GAAG;AACjC;AAEO,SAAS,sBAAsB,UAAkB,OAAe,OAAe;AACpF,SAAO,SACJ,QAAQ,WAAW,OAAO,KAAK,CAAC,EAChC,QAAQ,WAAW,OAAO,KAAK,CAAC;AACrC;AAMO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,aAAa,CAAC;AACpB,WAAS,IAAI,GAAG,IAAI,MAAM,UAAU;AAClC,UAAM,YAAY,MAAM,YAAY,CAAC;AACrC,QAAI,cAAc,OAAW;AAC7B,QAAI,MAAM,UAAU,SAAS,EAAE,EAAE,YAAY;AAC7C,QAAI,IAAI,SAAS,GAAG;AAClB,YAAM,IAAI,SAAS,GAAG,GAAG;AAAA,IAC3B;AACA,eAAW,KAAK,GAAG;AACnB,SAAK,YAAY,QAAS,IAAI;AAAA,EAChC;AACA,SAAO,WAAW,KAAK,GAAG;AAC5B;AAEA,IAAI,qBAAqB;AAMlB,SAAS,gBAAgB,KAAa;AAC3C,uBAAqB,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAC9D;AAKO,SAAS,iBAAiB,SAAyB;AACxD,SAAO,GAAG,kBAAkB,IAAI,OAAO;AACzC;AAEO,IAAM,kBAMR,CAAC,EAAE,OAAO,MAAM,SAAS,WAAW,SAAS,MAAM,MAAM;AAC5D,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,UAAU,MAAM,QAAQ,MAAM,oBAAoB,KAAK,GAAG,CAAC,KAAK,CAAC;AAEvE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,gBAAgB,IAAI;AAAA,MAChC,OAAO,gBAAgB,IAAI;AAAA,MAC3B;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,2DAA2D;AAAA,QACpE;AAAA,MACF;AAAA,MAEC,qBACC,oBAAC,UAAK,WAAU,mEAAmE,iBAAM,IAEzF;AAAA,QAAC;AAAA;AAAA,UACC,KAAK,iBAAiB,OAAO;AAAA,UAC7B,KAAK;AAAA,UACL,SAAS,MAAM,YAAY,IAAI;AAAA,UAC/B,WAAU;AAAA,UACV,SAAQ;AAAA;AAAA,MACV;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|