@underverse-ui/underverse 2.0.35 → 2.0.37
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/api-reference.json +1 -1
- package/dist/DatePicker-FDOTQMNL.js +15 -0
- package/dist/{EmojiPicker-6H52Q4YE.js → EmojiPicker-Z7QPW4DA.js} +5 -4
- package/dist/EmojiPicker-Z7QPW4DA.js.map +1 -0
- package/dist/chunk-5GMQ23IU.js +719 -0
- package/dist/chunk-5GMQ23IU.js.map +1 -0
- package/dist/{chunk-L73HBI6E.js → chunk-5VCM2Z7M.js} +6 -4
- package/dist/{chunk-L73HBI6E.js.map → chunk-5VCM2Z7M.js.map} +1 -1
- package/dist/{chunk-H4AVZA4K.js → chunk-6K6CKZUL.js} +469 -360
- package/dist/chunk-6K6CKZUL.js.map +1 -0
- package/dist/{chunk-6FAHXNDQ.js → chunk-6XQTRIP6.js} +10 -349
- package/dist/chunk-6XQTRIP6.js.map +1 -0
- package/dist/chunk-EN33T3LJ.js +358 -0
- package/dist/chunk-EN33T3LJ.js.map +1 -0
- package/dist/{chunk-F2P3DJSC.js → chunk-GZ7EDNGF.js} +333 -1022
- package/dist/chunk-GZ7EDNGF.js.map +1 -0
- package/dist/{chunk-WFQV7XFC.js → chunk-JABD2ONI.js} +3 -3
- package/dist/chunk-JG3U26V6.js +1692 -0
- package/dist/chunk-JG3U26V6.js.map +1 -0
- package/dist/{chunk-W3QTP2DM.js → chunk-TSBKJELM.js} +2 -2
- package/dist/index.cjs +13513 -13323
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +1942 -3569
- package/dist/index.js.map +1 -1
- package/dist/{menu-bar-MUISXLMR.js → menu-bar-VXAMTP3E.js} +6 -4
- package/dist/{menu-bar-MUISXLMR.js.map → menu-bar-VXAMTP3E.js.map} +1 -1
- package/dist/ueditor.cjs +2803 -1098
- package/dist/ueditor.cjs.map +1 -1
- package/dist/ueditor.js +6 -4
- package/package.json +1 -1
- package/dist/chunk-6FAHXNDQ.js.map +0 -1
- package/dist/chunk-F2P3DJSC.js.map +0 -1
- package/dist/chunk-H4AVZA4K.js.map +0 -1
- /package/dist/{EmojiPicker-6H52Q4YE.js.map → DatePicker-FDOTQMNL.js.map} +0 -0
- /package/dist/{chunk-WFQV7XFC.js.map → chunk-JABD2ONI.js.map} +0 -0
- /package/dist/{chunk-W3QTP2DM.js.map → chunk-TSBKJELM.js.map} +0 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chainEventHandlers,
|
|
3
|
+
cn,
|
|
4
|
+
mergeRefs,
|
|
5
|
+
resolveUEditorPortalContainer,
|
|
6
|
+
useUEditorPortalContainerGetter
|
|
7
|
+
} from "./chunk-6XQTRIP6.js";
|
|
8
|
+
|
|
9
|
+
// src/utils/radius.ts
|
|
10
|
+
var STANDARD_BORDER_MODES = ["none", "sm", "md", "lg", "xl", "2xl", "3xl", "full"];
|
|
11
|
+
var CUSTOM_BORDER_MAP = {
|
|
12
|
+
daewoo: "rounded",
|
|
13
|
+
infiniq: "rounded-full"
|
|
14
|
+
};
|
|
15
|
+
var BORDER_MODE_DOCS_TYPE = `"${STANDARD_BORDER_MODES.join('" | "')}" | "${Object.keys(CUSTOM_BORDER_MAP).join('" | "')}"`;
|
|
16
|
+
function getBorderRadiusClass(borderMode = "full") {
|
|
17
|
+
if (!borderMode) return "rounded-full";
|
|
18
|
+
if (borderMode in CUSTOM_BORDER_MAP) {
|
|
19
|
+
return CUSTOM_BORDER_MAP[borderMode];
|
|
20
|
+
}
|
|
21
|
+
if (STANDARD_BORDER_MODES.includes(borderMode)) {
|
|
22
|
+
return `rounded-${borderMode}`;
|
|
23
|
+
}
|
|
24
|
+
return borderMode;
|
|
25
|
+
}
|
|
26
|
+
function getPanelBorderRadiusClass(borderMode = "lg") {
|
|
27
|
+
const resolved = borderMode ?? "lg";
|
|
28
|
+
if (resolved === "full" || resolved === "infiniq") {
|
|
29
|
+
return "rounded-2xl";
|
|
30
|
+
}
|
|
31
|
+
return getBorderRadiusClass(resolved);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/contexts/UnderverseConfigContext.tsx
|
|
35
|
+
import * as React from "react";
|
|
36
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
37
|
+
var UnderverseUIConfigContext = React.createContext(null);
|
|
38
|
+
function useUnderverseUIConfig() {
|
|
39
|
+
return React.useContext(UnderverseUIConfigContext) ?? {};
|
|
40
|
+
}
|
|
41
|
+
function UnderverseConfigProvider({ children, config }) {
|
|
42
|
+
if (!config) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
43
|
+
return /* @__PURE__ */ jsx(UnderverseUIConfigContext.Provider, { value: config, children });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/utils/animations.ts
|
|
47
|
+
import { useEffect } from "react";
|
|
48
|
+
var shadcnAnimationStyles = `
|
|
49
|
+
/* ============================================
|
|
50
|
+
* DROPDOWN / POPOVER ANIMATIONS
|
|
51
|
+
* Uses spring-like cubic-bezier for natural feel
|
|
52
|
+
* ============================================ */
|
|
53
|
+
|
|
54
|
+
/* Native-like Combobox Animation - Mimics browser default select */
|
|
55
|
+
[data-state="open"][data-combobox-dropdown] {
|
|
56
|
+
animation: comboboxOpen 150ms cubic-bezier(0.2, 0, 0, 1);
|
|
57
|
+
transform-origin: top center;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[data-state="closed"][data-combobox-dropdown] {
|
|
61
|
+
animation: comboboxClose 120ms cubic-bezier(0.4, 0, 1, 1);
|
|
62
|
+
transform-origin: top center;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes comboboxOpen {
|
|
66
|
+
0% {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transform: translateY(-4px) scaleY(0.9);
|
|
69
|
+
}
|
|
70
|
+
100% {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
transform: translateY(0) scaleY(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes comboboxClose {
|
|
77
|
+
0% {
|
|
78
|
+
opacity: 1;
|
|
79
|
+
transform: translateY(0) scaleY(1);
|
|
80
|
+
}
|
|
81
|
+
100% {
|
|
82
|
+
opacity: 0;
|
|
83
|
+
transform: translateY(-4px) scaleY(0.9);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Generic dropdown open/close */
|
|
88
|
+
[data-state="open"] {
|
|
89
|
+
animation: slideDownAndFade 220ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[data-state="closed"] {
|
|
93
|
+
animation: slideUpAndFade 180ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes slideDownAndFade {
|
|
97
|
+
from {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
transform: translateY(-4px) scale(0.98);
|
|
100
|
+
}
|
|
101
|
+
to {
|
|
102
|
+
opacity: 1;
|
|
103
|
+
transform: translateY(0) scale(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@keyframes slideUpAndFade {
|
|
108
|
+
from {
|
|
109
|
+
opacity: 1;
|
|
110
|
+
transform: translateY(0) scale(1);
|
|
111
|
+
}
|
|
112
|
+
to {
|
|
113
|
+
opacity: 0;
|
|
114
|
+
transform: translateY(-4px) scale(0.98);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ============================================
|
|
119
|
+
* DROPDOWN ITEMS - Native-like instant appearance
|
|
120
|
+
* ============================================ */
|
|
121
|
+
|
|
122
|
+
/* Fast staggered animation for native feel */
|
|
123
|
+
.dropdown-item {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
animation: itemFadeIn 120ms cubic-bezier(0.2, 0, 0, 1) forwards;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@keyframes itemFadeIn {
|
|
129
|
+
from {
|
|
130
|
+
opacity: 0;
|
|
131
|
+
transform: translateX(-4px);
|
|
132
|
+
}
|
|
133
|
+
to {
|
|
134
|
+
opacity: 1;
|
|
135
|
+
transform: translateX(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Item stagger delays - minimal for speed */
|
|
140
|
+
.dropdown-item:nth-child(1) { animation-delay: 0ms; }
|
|
141
|
+
.dropdown-item:nth-child(2) { animation-delay: 15ms; }
|
|
142
|
+
.dropdown-item:nth-child(3) { animation-delay: 30ms; }
|
|
143
|
+
.dropdown-item:nth-child(4) { animation-delay: 45ms; }
|
|
144
|
+
.dropdown-item:nth-child(5) { animation-delay: 60ms; }
|
|
145
|
+
.dropdown-item:nth-child(6) { animation-delay: 75ms; }
|
|
146
|
+
.dropdown-item:nth-child(7) { animation-delay: 90ms; }
|
|
147
|
+
.dropdown-item:nth-child(8) { animation-delay: 105ms; }
|
|
148
|
+
.dropdown-item:nth-child(n+9) { animation-delay: 120ms; }
|
|
149
|
+
|
|
150
|
+
/* ============================================
|
|
151
|
+
* DATEPICKER ANIMATIONS
|
|
152
|
+
* ============================================ */
|
|
153
|
+
|
|
154
|
+
.datepicker-day {
|
|
155
|
+
opacity: 0;
|
|
156
|
+
animation: dayFadeIn 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@keyframes dayFadeIn {
|
|
160
|
+
from {
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transform: scale(0.8);
|
|
163
|
+
}
|
|
164
|
+
to {
|
|
165
|
+
opacity: 1;
|
|
166
|
+
transform: scale(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ============================================
|
|
171
|
+
* TOOLTIP ANIMATIONS
|
|
172
|
+
* ============================================ */
|
|
173
|
+
|
|
174
|
+
[data-tooltip] {
|
|
175
|
+
animation: tooltipIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@keyframes tooltipIn {
|
|
179
|
+
from {
|
|
180
|
+
opacity: 0;
|
|
181
|
+
transform: scale(0.95);
|
|
182
|
+
}
|
|
183
|
+
to {
|
|
184
|
+
opacity: 1;
|
|
185
|
+
transform: scale(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ============================================
|
|
190
|
+
* MODAL / DIALOG ANIMATIONS
|
|
191
|
+
* ============================================ */
|
|
192
|
+
|
|
193
|
+
.modal-content {
|
|
194
|
+
animation: scaleIn 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@keyframes scaleIn {
|
|
198
|
+
from {
|
|
199
|
+
opacity: 0;
|
|
200
|
+
transform: scale(0.9);
|
|
201
|
+
}
|
|
202
|
+
to {
|
|
203
|
+
opacity: 1;
|
|
204
|
+
transform: scale(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Smooth backdrop blur transition */
|
|
209
|
+
.backdrop-animate {
|
|
210
|
+
transition: backdrop-filter 200ms ease, background-color 200ms ease;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
function ensureAnimationStylesInjected(targetDocument) {
|
|
214
|
+
const resolvedDocument = targetDocument ?? (typeof document !== "undefined" ? document : void 0);
|
|
215
|
+
if (resolvedDocument) {
|
|
216
|
+
const styleId = "shadcn-animations";
|
|
217
|
+
if (!resolvedDocument.getElementById(styleId)) {
|
|
218
|
+
const styleElement = resolvedDocument.createElement("style");
|
|
219
|
+
styleElement.id = styleId;
|
|
220
|
+
styleElement.textContent = shadcnAnimationStyles;
|
|
221
|
+
resolvedDocument.head.appendChild(styleElement);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function useShadCNAnimations(targetDocument) {
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (targetDocument === null) return;
|
|
228
|
+
ensureAnimationStylesInjected(targetDocument);
|
|
229
|
+
}, [targetDocument]);
|
|
230
|
+
}
|
|
231
|
+
function injectAnimationStyles(targetDocument) {
|
|
232
|
+
ensureAnimationStylesInjected(targetDocument);
|
|
233
|
+
}
|
|
234
|
+
function getAnimationStyles() {
|
|
235
|
+
return shadcnAnimationStyles;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/components/Popover.tsx
|
|
239
|
+
import * as React2 from "react";
|
|
240
|
+
import { createPortal } from "react-dom";
|
|
241
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
242
|
+
function getTransformOrigin(side, align) {
|
|
243
|
+
if (side === "top") return `${align === "end" ? "right" : "left"} bottom`;
|
|
244
|
+
if (side === "bottom") return `${align === "end" ? "right" : "left"} top`;
|
|
245
|
+
if (side === "left") return "right top";
|
|
246
|
+
return "left top";
|
|
247
|
+
}
|
|
248
|
+
function normalizePlacement(placement) {
|
|
249
|
+
switch (placement) {
|
|
250
|
+
case "top":
|
|
251
|
+
return { side: "top", align: "start" };
|
|
252
|
+
case "bottom":
|
|
253
|
+
return { side: "bottom", align: "start" };
|
|
254
|
+
case "left":
|
|
255
|
+
return { side: "left", align: "start" };
|
|
256
|
+
case "right":
|
|
257
|
+
return { side: "right", align: "start" };
|
|
258
|
+
default: {
|
|
259
|
+
const [side, align] = placement.split("-");
|
|
260
|
+
return { side, align };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
265
|
+
function overflowAmount(left, width, viewportWidth, padding) {
|
|
266
|
+
const min = padding;
|
|
267
|
+
const max = viewportWidth - padding;
|
|
268
|
+
const overflowLeft = Math.max(0, min - left);
|
|
269
|
+
const overflowRight = Math.max(0, left + width - max);
|
|
270
|
+
return overflowLeft + overflowRight;
|
|
271
|
+
}
|
|
272
|
+
function getScrollAncestors(trigger, ownerWindow) {
|
|
273
|
+
const ancestors = [];
|
|
274
|
+
let current = trigger.parentElement;
|
|
275
|
+
while (current) {
|
|
276
|
+
try {
|
|
277
|
+
const style = ownerWindow.getComputedStyle(current);
|
|
278
|
+
const overflow = `${style.overflow} ${style.overflowX} ${style.overflowY}`;
|
|
279
|
+
if (/(auto|scroll|overlay|hidden)/.test(overflow)) ancestors.push(current);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
current = current.parentElement;
|
|
283
|
+
}
|
|
284
|
+
return ancestors;
|
|
285
|
+
}
|
|
286
|
+
function isFiniteRect(rect) {
|
|
287
|
+
return [rect.top, rect.right, rect.bottom, rect.left, rect.width, rect.height].every(Number.isFinite);
|
|
288
|
+
}
|
|
289
|
+
function isFullyOutsideViewport(rect, viewportWidth, viewportHeight) {
|
|
290
|
+
return rect.bottom < 0 || rect.right < 0 || rect.top > viewportHeight || rect.left > viewportWidth;
|
|
291
|
+
}
|
|
292
|
+
function computePopoverPosition(args) {
|
|
293
|
+
const { triggerRect, contentSize, placement, offset, padding, viewport, constrainToAvailableHeight } = args;
|
|
294
|
+
let { side, align } = normalizePlacement(placement);
|
|
295
|
+
const availableBelow = Math.max(0, viewport.height - padding - triggerRect.bottom - offset);
|
|
296
|
+
const availableAbove = Math.max(0, triggerRect.top - padding - offset);
|
|
297
|
+
if (side === "bottom") {
|
|
298
|
+
if (constrainToAvailableHeight) {
|
|
299
|
+
if (contentSize.height > availableBelow && availableAbove > availableBelow) side = "top";
|
|
300
|
+
} else {
|
|
301
|
+
const bottomTop = triggerRect.bottom + offset;
|
|
302
|
+
const overflowsBottom = bottomTop + contentSize.height > viewport.height - padding;
|
|
303
|
+
const topTop = triggerRect.top - offset - contentSize.height;
|
|
304
|
+
const fitsTop = topTop >= padding;
|
|
305
|
+
if (overflowsBottom && fitsTop) side = "top";
|
|
306
|
+
}
|
|
307
|
+
} else if (side === "top") {
|
|
308
|
+
if (constrainToAvailableHeight) {
|
|
309
|
+
if (contentSize.height > availableAbove && availableBelow > availableAbove) side = "bottom";
|
|
310
|
+
} else {
|
|
311
|
+
const topTop = triggerRect.top - offset - contentSize.height;
|
|
312
|
+
const overflowsTop = topTop < padding;
|
|
313
|
+
const bottomTop = triggerRect.bottom + offset;
|
|
314
|
+
const fitsBottom = bottomTop + contentSize.height <= viewport.height - padding;
|
|
315
|
+
if (overflowsTop && fitsBottom) side = "bottom";
|
|
316
|
+
}
|
|
317
|
+
} else if (side === "right") {
|
|
318
|
+
const rightLeft = triggerRect.right + offset;
|
|
319
|
+
const overflowsRight = rightLeft + contentSize.width > viewport.width - padding;
|
|
320
|
+
const leftLeft = triggerRect.left - offset - contentSize.width;
|
|
321
|
+
const fitsLeft = leftLeft >= padding;
|
|
322
|
+
if (overflowsRight && fitsLeft) side = "left";
|
|
323
|
+
} else if (side === "left") {
|
|
324
|
+
const leftLeft = triggerRect.left - offset - contentSize.width;
|
|
325
|
+
const overflowsLeft = leftLeft < padding;
|
|
326
|
+
const rightLeft = triggerRect.right + offset;
|
|
327
|
+
const fitsRight = rightLeft + contentSize.width <= viewport.width - padding;
|
|
328
|
+
if (overflowsLeft && fitsRight) side = "right";
|
|
329
|
+
}
|
|
330
|
+
let top = 0;
|
|
331
|
+
let left = 0;
|
|
332
|
+
if (side === "top" || side === "bottom") {
|
|
333
|
+
const leftStart = triggerRect.left;
|
|
334
|
+
const leftEnd = triggerRect.right - contentSize.width;
|
|
335
|
+
const startOverflow = overflowAmount(leftStart, contentSize.width, viewport.width, padding);
|
|
336
|
+
const endOverflow = overflowAmount(leftEnd, contentSize.width, viewport.width, padding);
|
|
337
|
+
if (align === "start" && startOverflow > 0 && endOverflow < startOverflow) align = "end";
|
|
338
|
+
if (align === "end" && endOverflow > 0 && startOverflow < endOverflow) align = "start";
|
|
339
|
+
left = align === "end" ? leftEnd : leftStart;
|
|
340
|
+
const availableHeight = side === "top" ? availableAbove : availableBelow;
|
|
341
|
+
const renderedHeight = constrainToAvailableHeight ? Math.min(contentSize.height, availableHeight) : contentSize.height;
|
|
342
|
+
top = side === "top" ? triggerRect.top - offset - renderedHeight : triggerRect.bottom + offset;
|
|
343
|
+
left = clamp(left, padding, viewport.width - contentSize.width - padding);
|
|
344
|
+
top = clamp(top, padding, viewport.height - renderedHeight - padding);
|
|
345
|
+
return { top, left, side, align, availableHeight };
|
|
346
|
+
}
|
|
347
|
+
top = triggerRect.top;
|
|
348
|
+
left = side === "left" ? triggerRect.left - offset - contentSize.width : triggerRect.right + offset;
|
|
349
|
+
left = clamp(left, padding, viewport.width - contentSize.width - padding);
|
|
350
|
+
top = clamp(top, padding, viewport.height - contentSize.height - padding);
|
|
351
|
+
return { top, left, side, align, availableHeight: Math.max(0, viewport.height - padding * 2) };
|
|
352
|
+
}
|
|
353
|
+
var Popover = ({
|
|
354
|
+
trigger,
|
|
355
|
+
children,
|
|
356
|
+
className,
|
|
357
|
+
contentClassName,
|
|
358
|
+
contentProps,
|
|
359
|
+
contentScrollable = false,
|
|
360
|
+
placement = "bottom",
|
|
361
|
+
modal = false,
|
|
362
|
+
disabled = false,
|
|
363
|
+
open,
|
|
364
|
+
onOpenChange,
|
|
365
|
+
matchTriggerWidth = false,
|
|
366
|
+
contentWidth,
|
|
367
|
+
borderMode,
|
|
368
|
+
getPortalContainer,
|
|
369
|
+
constrainToAvailableHeight = false
|
|
370
|
+
}) => {
|
|
371
|
+
const isControlled = open !== void 0;
|
|
372
|
+
const [internalOpen, setInternalOpen] = React2.useState(false);
|
|
373
|
+
const triggerRef = React2.useRef(null);
|
|
374
|
+
const [triggerDocument, setTriggerDocument] = React2.useState(null);
|
|
375
|
+
const positionerRef = React2.useRef(null);
|
|
376
|
+
const panelRef = React2.useRef(null);
|
|
377
|
+
const pendingFrameRef = React2.useRef(null);
|
|
378
|
+
const closeRequestedRef = React2.useRef(false);
|
|
379
|
+
const lastAppliedRef = React2.useRef(null);
|
|
380
|
+
const globalConfig = useUnderverseUIConfig();
|
|
381
|
+
const resolvedBorderMode = borderMode ?? globalConfig.popover?.borderMode ?? globalConfig.borderMode;
|
|
382
|
+
const isOpen = isControlled ? open : internalOpen;
|
|
383
|
+
const contextGetPortalContainer = useUEditorPortalContainerGetter();
|
|
384
|
+
const requestedPortalContainer = isOpen && triggerDocument ? resolveUEditorPortalContainer(getPortalContainer, contextGetPortalContainer, triggerDocument) : null;
|
|
385
|
+
const portalContainer = requestedPortalContainer?.ownerDocument === triggerDocument ? requestedPortalContainer : triggerDocument?.body ?? null;
|
|
386
|
+
useShadCNAnimations(isOpen ? triggerDocument : null);
|
|
387
|
+
const setIsOpen = React2.useCallback(
|
|
388
|
+
(next) => {
|
|
389
|
+
if (!isControlled) setInternalOpen(next);
|
|
390
|
+
onOpenChange?.(next);
|
|
391
|
+
},
|
|
392
|
+
[isControlled, onOpenChange]
|
|
393
|
+
);
|
|
394
|
+
const offset = 4;
|
|
395
|
+
const padding = 8;
|
|
396
|
+
const initialPlacement = React2.useMemo(() => normalizePlacement(placement), [placement]);
|
|
397
|
+
const cancelScheduledPositionUpdate = React2.useCallback(() => {
|
|
398
|
+
const pending = pendingFrameRef.current;
|
|
399
|
+
if (!pending) return;
|
|
400
|
+
pendingFrameRef.current = null;
|
|
401
|
+
try {
|
|
402
|
+
pending.ownerWindow.cancelAnimationFrame(pending.frameId);
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
}, []);
|
|
406
|
+
const closeWhenPositioningIsUnsafe = React2.useCallback(() => {
|
|
407
|
+
const positionerEl = positionerRef.current;
|
|
408
|
+
if (positionerEl) {
|
|
409
|
+
positionerEl.style.visibility = "hidden";
|
|
410
|
+
positionerEl.style.pointerEvents = "none";
|
|
411
|
+
}
|
|
412
|
+
if (closeRequestedRef.current) return;
|
|
413
|
+
closeRequestedRef.current = true;
|
|
414
|
+
setIsOpen(false);
|
|
415
|
+
}, [setIsOpen]);
|
|
416
|
+
const updatePosition = React2.useCallback(() => {
|
|
417
|
+
const triggerEl = triggerRef.current;
|
|
418
|
+
const positionerEl = positionerRef.current;
|
|
419
|
+
const panelEl = panelRef.current;
|
|
420
|
+
if (!triggerEl || !positionerEl || !panelEl) return;
|
|
421
|
+
const ownerDocument = triggerEl.ownerDocument;
|
|
422
|
+
const ownerWindow = ownerDocument.defaultView;
|
|
423
|
+
if (!ownerWindow || ownerWindow.closed || !triggerEl.isConnected || !positionerEl.isConnected || positionerEl.ownerDocument !== ownerDocument || panelEl.ownerDocument !== ownerDocument) {
|
|
424
|
+
closeWhenPositioningIsUnsafe();
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const contentEl = panelEl.firstElementChild;
|
|
428
|
+
if (!contentEl) {
|
|
429
|
+
closeWhenPositioningIsUnsafe();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
const triggerRect = triggerEl.getBoundingClientRect();
|
|
434
|
+
const viewportWidth = ownerWindow.innerWidth;
|
|
435
|
+
const viewportHeight = ownerWindow.innerHeight;
|
|
436
|
+
if (!isFiniteRect(triggerRect) || !Number.isFinite(viewportWidth) || !Number.isFinite(viewportHeight) || viewportWidth <= 0 || viewportHeight <= 0 || isFullyOutsideViewport(triggerRect, viewportWidth, viewportHeight)) {
|
|
437
|
+
closeWhenPositioningIsUnsafe();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const widthWanted = matchTriggerWidth ? triggerRect.width : contentWidth;
|
|
441
|
+
const widthPx = widthWanted == null ? void 0 : Math.max(0, Math.round(widthWanted));
|
|
442
|
+
if (widthPx == null) {
|
|
443
|
+
if (positionerEl.style.width) positionerEl.style.width = "";
|
|
444
|
+
} else if (positionerEl.style.width !== `${widthPx}px`) {
|
|
445
|
+
positionerEl.style.width = `${widthPx}px`;
|
|
446
|
+
}
|
|
447
|
+
const prevApplied = lastAppliedRef.current;
|
|
448
|
+
const measuredWidth = positionerEl.offsetWidth;
|
|
449
|
+
const measuredHeight = positionerEl.offsetHeight;
|
|
450
|
+
const contentRect = positionerEl.getBoundingClientRect();
|
|
451
|
+
const contentBoxWidth = measuredWidth || contentRect.width;
|
|
452
|
+
const contentBoxHeight = Math.max(measuredHeight, contentRect.height);
|
|
453
|
+
const next = computePopoverPosition({
|
|
454
|
+
triggerRect,
|
|
455
|
+
contentSize: { width: contentBoxWidth, height: contentBoxHeight },
|
|
456
|
+
placement,
|
|
457
|
+
offset,
|
|
458
|
+
padding,
|
|
459
|
+
viewport: {
|
|
460
|
+
width: viewportWidth,
|
|
461
|
+
height: viewportHeight
|
|
462
|
+
},
|
|
463
|
+
constrainToAvailableHeight
|
|
464
|
+
});
|
|
465
|
+
const top = Math.round(next.top);
|
|
466
|
+
const left = Math.round(next.left);
|
|
467
|
+
const applied = prevApplied && prevApplied.positioner === positionerEl && Math.abs(prevApplied.top - top) < 0.5 && Math.abs(prevApplied.left - left) < 0.5 && prevApplied.side === next.side && prevApplied.align === next.align && prevApplied.width === widthPx && prevApplied.availableHeight === next.availableHeight;
|
|
468
|
+
if (applied) return;
|
|
469
|
+
lastAppliedRef.current = {
|
|
470
|
+
positioner: positionerEl,
|
|
471
|
+
top,
|
|
472
|
+
left,
|
|
473
|
+
side: next.side,
|
|
474
|
+
align: next.align,
|
|
475
|
+
width: widthPx,
|
|
476
|
+
availableHeight: next.availableHeight
|
|
477
|
+
};
|
|
478
|
+
positionerEl.style.transform = `translate3d(${left}px, ${top}px, 0)`;
|
|
479
|
+
positionerEl.style.setProperty("--underverse-popover-available-height", `${next.availableHeight}px`);
|
|
480
|
+
positionerEl.dataset.side = next.side;
|
|
481
|
+
positionerEl.dataset.align = next.align;
|
|
482
|
+
panelEl.style.transformOrigin = getTransformOrigin(next.side, next.align);
|
|
483
|
+
if (positionerEl.style.visibility !== "visible") positionerEl.style.visibility = "visible";
|
|
484
|
+
if (positionerEl.style.pointerEvents !== "auto") positionerEl.style.pointerEvents = "auto";
|
|
485
|
+
} catch {
|
|
486
|
+
closeWhenPositioningIsUnsafe();
|
|
487
|
+
}
|
|
488
|
+
}, [placement, matchTriggerWidth, contentWidth, constrainToAvailableHeight, closeWhenPositioningIsUnsafe]);
|
|
489
|
+
const schedulePositionUpdate = React2.useCallback(() => {
|
|
490
|
+
if (pendingFrameRef.current) return;
|
|
491
|
+
const triggerEl = triggerRef.current;
|
|
492
|
+
const ownerWindow = triggerEl?.ownerDocument.defaultView;
|
|
493
|
+
if (!triggerEl || !ownerWindow || ownerWindow.closed) {
|
|
494
|
+
closeWhenPositioningIsUnsafe();
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const frameId = ownerWindow.requestAnimationFrame(() => {
|
|
499
|
+
pendingFrameRef.current = null;
|
|
500
|
+
updatePosition();
|
|
501
|
+
});
|
|
502
|
+
pendingFrameRef.current = { ownerWindow, frameId };
|
|
503
|
+
} catch {
|
|
504
|
+
closeWhenPositioningIsUnsafe();
|
|
505
|
+
}
|
|
506
|
+
}, [closeWhenPositioningIsUnsafe, updatePosition]);
|
|
507
|
+
React2.useLayoutEffect(() => {
|
|
508
|
+
if (isOpen) {
|
|
509
|
+
closeRequestedRef.current = false;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
lastAppliedRef.current = null;
|
|
513
|
+
cancelScheduledPositionUpdate();
|
|
514
|
+
}, [cancelScheduledPositionUpdate, isOpen]);
|
|
515
|
+
React2.useLayoutEffect(() => {
|
|
516
|
+
if (!isOpen || !portalContainer) return;
|
|
517
|
+
updatePosition();
|
|
518
|
+
schedulePositionUpdate();
|
|
519
|
+
return cancelScheduledPositionUpdate;
|
|
520
|
+
}, [cancelScheduledPositionUpdate, isOpen, portalContainer, schedulePositionUpdate, updatePosition]);
|
|
521
|
+
React2.useEffect(() => {
|
|
522
|
+
const triggerEl = triggerRef.current;
|
|
523
|
+
const positionerEl = positionerRef.current;
|
|
524
|
+
if (!isOpen || !triggerEl || !positionerEl || !portalContainer) return;
|
|
525
|
+
const ownerDocument = triggerEl.ownerDocument;
|
|
526
|
+
const ownerWindow = ownerDocument.defaultView;
|
|
527
|
+
if (!ownerWindow || ownerWindow.closed || portalContainer.ownerDocument !== ownerDocument) {
|
|
528
|
+
closeWhenPositioningIsUnsafe();
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const scrollAncestors = getScrollAncestors(triggerEl, ownerWindow);
|
|
532
|
+
const passiveOptions = { passive: true };
|
|
533
|
+
for (const ancestor of scrollAncestors) {
|
|
534
|
+
ancestor.addEventListener("scroll", schedulePositionUpdate, passiveOptions);
|
|
535
|
+
}
|
|
536
|
+
ownerWindow.addEventListener("scroll", schedulePositionUpdate, passiveOptions);
|
|
537
|
+
ownerWindow.addEventListener("resize", schedulePositionUpdate);
|
|
538
|
+
ownerDocument.addEventListener("scroll", schedulePositionUpdate, { capture: true, passive: true });
|
|
539
|
+
const visualViewport = ownerWindow.visualViewport;
|
|
540
|
+
visualViewport?.addEventListener("scroll", schedulePositionUpdate, passiveOptions);
|
|
541
|
+
visualViewport?.addEventListener("resize", schedulePositionUpdate);
|
|
542
|
+
const ResizeObserverCtor = ownerWindow.ResizeObserver;
|
|
543
|
+
const resizeObserver = ResizeObserverCtor ? new ResizeObserverCtor(schedulePositionUpdate) : null;
|
|
544
|
+
resizeObserver?.observe(triggerEl);
|
|
545
|
+
resizeObserver?.observe(positionerEl);
|
|
546
|
+
const contentEl = panelRef.current?.firstElementChild;
|
|
547
|
+
if (contentEl) resizeObserver?.observe(contentEl);
|
|
548
|
+
const MutationObserverCtor = ownerWindow.MutationObserver;
|
|
549
|
+
const mutationObserver = MutationObserverCtor ? new MutationObserverCtor(() => {
|
|
550
|
+
const currentTrigger = triggerRef.current;
|
|
551
|
+
if (!currentTrigger || !currentTrigger.isConnected || currentTrigger.ownerDocument !== ownerDocument) {
|
|
552
|
+
closeWhenPositioningIsUnsafe();
|
|
553
|
+
}
|
|
554
|
+
}) : null;
|
|
555
|
+
if (mutationObserver && ownerDocument.documentElement) {
|
|
556
|
+
mutationObserver.observe(ownerDocument.documentElement, { childList: true, subtree: true });
|
|
557
|
+
}
|
|
558
|
+
schedulePositionUpdate();
|
|
559
|
+
return () => {
|
|
560
|
+
for (const ancestor of scrollAncestors) {
|
|
561
|
+
ancestor.removeEventListener("scroll", schedulePositionUpdate);
|
|
562
|
+
}
|
|
563
|
+
ownerWindow.removeEventListener("scroll", schedulePositionUpdate);
|
|
564
|
+
ownerWindow.removeEventListener("resize", schedulePositionUpdate);
|
|
565
|
+
ownerDocument.removeEventListener("scroll", schedulePositionUpdate, true);
|
|
566
|
+
visualViewport?.removeEventListener("scroll", schedulePositionUpdate);
|
|
567
|
+
visualViewport?.removeEventListener("resize", schedulePositionUpdate);
|
|
568
|
+
resizeObserver?.disconnect();
|
|
569
|
+
mutationObserver?.disconnect();
|
|
570
|
+
cancelScheduledPositionUpdate();
|
|
571
|
+
};
|
|
572
|
+
}, [
|
|
573
|
+
cancelScheduledPositionUpdate,
|
|
574
|
+
closeWhenPositioningIsUnsafe,
|
|
575
|
+
isOpen,
|
|
576
|
+
portalContainer,
|
|
577
|
+
schedulePositionUpdate
|
|
578
|
+
]);
|
|
579
|
+
React2.useEffect(() => {
|
|
580
|
+
const triggerEl = triggerRef.current;
|
|
581
|
+
if (!isOpen || !triggerEl || !portalContainer) return;
|
|
582
|
+
const ownerDocument = triggerEl.ownerDocument;
|
|
583
|
+
const ownerWindow = ownerDocument.defaultView;
|
|
584
|
+
if (!ownerWindow || portalContainer.ownerDocument !== ownerDocument) {
|
|
585
|
+
closeWhenPositioningIsUnsafe();
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const handleClickOutside = (event) => {
|
|
589
|
+
const target = event.target;
|
|
590
|
+
const currentTrigger = triggerRef.current;
|
|
591
|
+
const popoverEl = positionerRef.current;
|
|
592
|
+
if (!currentTrigger || !popoverEl) return;
|
|
593
|
+
if (currentTrigger.contains(target)) return;
|
|
594
|
+
if (popoverEl.contains(target)) return;
|
|
595
|
+
if (target instanceof ownerWindow.Element && target.closest("[data-popover]")) return;
|
|
596
|
+
setIsOpen(false);
|
|
597
|
+
};
|
|
598
|
+
const handleEscape = (event) => {
|
|
599
|
+
if (event.key === "Escape") {
|
|
600
|
+
setIsOpen(false);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
ownerDocument.addEventListener("mousedown", handleClickOutside);
|
|
604
|
+
ownerDocument.addEventListener("keydown", handleEscape);
|
|
605
|
+
return () => {
|
|
606
|
+
ownerDocument.removeEventListener("mousedown", handleClickOutside);
|
|
607
|
+
ownerDocument.removeEventListener("keydown", handleEscape);
|
|
608
|
+
};
|
|
609
|
+
}, [closeWhenPositioningIsUnsafe, isOpen, portalContainer, setIsOpen]);
|
|
610
|
+
const handleTriggerClick = () => {
|
|
611
|
+
if (!disabled) {
|
|
612
|
+
setIsOpen(!isOpen);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
const triggerProps = trigger.props;
|
|
616
|
+
const childRef = triggerProps.ref;
|
|
617
|
+
const setTriggerNode = React2.useCallback((node) => {
|
|
618
|
+
triggerRef.current = node;
|
|
619
|
+
if (node) {
|
|
620
|
+
setTriggerDocument((currentDocument) => currentDocument === node.ownerDocument ? currentDocument : node.ownerDocument);
|
|
621
|
+
}
|
|
622
|
+
}, []);
|
|
623
|
+
const composedTriggerRef = React2.useMemo(
|
|
624
|
+
() => mergeRefs(childRef, setTriggerNode),
|
|
625
|
+
[childRef, setTriggerNode]
|
|
626
|
+
);
|
|
627
|
+
const popoverContent = isOpen && portalContainer ? createPortal(
|
|
628
|
+
/* @__PURE__ */ jsx2(
|
|
629
|
+
"div",
|
|
630
|
+
{
|
|
631
|
+
ref: positionerRef,
|
|
632
|
+
"data-popover": true,
|
|
633
|
+
style: {
|
|
634
|
+
position: "fixed",
|
|
635
|
+
top: 0,
|
|
636
|
+
left: 0,
|
|
637
|
+
transform: "translate3d(0, 0, 0)",
|
|
638
|
+
zIndex: 99999,
|
|
639
|
+
visibility: "hidden",
|
|
640
|
+
pointerEvents: "none",
|
|
641
|
+
willChange: "transform"
|
|
642
|
+
},
|
|
643
|
+
className: "z-[99999]",
|
|
644
|
+
children: /* @__PURE__ */ jsx2(
|
|
645
|
+
"div",
|
|
646
|
+
{
|
|
647
|
+
ref: panelRef,
|
|
648
|
+
"data-state": "open",
|
|
649
|
+
role: modal ? "dialog" : void 0,
|
|
650
|
+
"aria-modal": modal || void 0,
|
|
651
|
+
style: {
|
|
652
|
+
transformOrigin: getTransformOrigin(initialPlacement.side, initialPlacement.align)
|
|
653
|
+
},
|
|
654
|
+
className: cn(
|
|
655
|
+
// shadcn-like enter animation
|
|
656
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
657
|
+
className
|
|
658
|
+
),
|
|
659
|
+
children: /* @__PURE__ */ jsx2(
|
|
660
|
+
"div",
|
|
661
|
+
{
|
|
662
|
+
...contentProps,
|
|
663
|
+
className: cn(
|
|
664
|
+
resolvedBorderMode ? getPanelBorderRadiusClass(resolvedBorderMode) : "rounded-2xl md:rounded-3xl",
|
|
665
|
+
"border bg-popover text-popover-foreground shadow-md",
|
|
666
|
+
"backdrop-blur-sm bg-popover/95 border-border/60 p-4",
|
|
667
|
+
contentProps?.className,
|
|
668
|
+
contentClassName
|
|
669
|
+
),
|
|
670
|
+
tabIndex: contentProps?.tabIndex ?? -1,
|
|
671
|
+
children
|
|
672
|
+
}
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
)
|
|
676
|
+
}
|
|
677
|
+
),
|
|
678
|
+
portalContainer
|
|
679
|
+
) : null;
|
|
680
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
681
|
+
(() => {
|
|
682
|
+
return React2.cloneElement(trigger, {
|
|
683
|
+
...triggerProps,
|
|
684
|
+
ref: composedTriggerRef,
|
|
685
|
+
onClick: chainEventHandlers(
|
|
686
|
+
(e) => {
|
|
687
|
+
triggerRef.current = e.currentTarget;
|
|
688
|
+
e.preventDefault();
|
|
689
|
+
e.stopPropagation();
|
|
690
|
+
handleTriggerClick();
|
|
691
|
+
},
|
|
692
|
+
triggerProps.onClick
|
|
693
|
+
),
|
|
694
|
+
onFocus: chainEventHandlers(
|
|
695
|
+
(e) => {
|
|
696
|
+
triggerRef.current = e.currentTarget;
|
|
697
|
+
},
|
|
698
|
+
triggerProps.onFocus
|
|
699
|
+
),
|
|
700
|
+
"aria-expanded": isOpen,
|
|
701
|
+
"aria-haspopup": triggerProps["aria-haspopup"] ?? "dialog"
|
|
702
|
+
});
|
|
703
|
+
})(),
|
|
704
|
+
popoverContent
|
|
705
|
+
] });
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
export {
|
|
709
|
+
getBorderRadiusClass,
|
|
710
|
+
getPanelBorderRadiusClass,
|
|
711
|
+
useUnderverseUIConfig,
|
|
712
|
+
UnderverseConfigProvider,
|
|
713
|
+
shadcnAnimationStyles,
|
|
714
|
+
useShadCNAnimations,
|
|
715
|
+
injectAnimationStyles,
|
|
716
|
+
getAnimationStyles,
|
|
717
|
+
Popover
|
|
718
|
+
};
|
|
719
|
+
//# sourceMappingURL=chunk-5GMQ23IU.js.map
|