@tensor_1/react 2.26.1 → 2.26.3
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/components/EmojiPicker.d.ts +3 -0
- package/dist/components/EmojiPicker.js +109 -54
- package/dist/components/Popover.d.ts +15 -5
- package/dist/components/Popover.js +66 -6
- package/dist/{emoji-image-WQLh8ZQP.d.cts → emoji-image-6mPZ15Zy.d.cts} +4 -1
- package/dist/emoji.cjs +324 -74
- package/dist/emoji.d.cts +3 -3
- package/dist/hooks/useAnchoredPosition.d.ts +13 -5
- package/dist/hooks/useAnchoredPosition.js +146 -15
- package/dist/hooks/useDialog.js +1 -0
- package/dist/index.cjs +225 -29
- package/dist/index.d.cts +26 -11
- package/dist/{overlay-position-CEGSz6Z2.d.cts → overlay-position-Cjv5E7mn.d.cts} +7 -0
- package/dist/overlay-position.d.ts +7 -0
- package/dist/overlay-position.js +7 -2
- package/dist/rich-text.cjs +324 -74
- package/dist/rich-text.d.cts +3 -3
- package/package.json +1 -1
package/dist/emoji.cjs
CHANGED
|
@@ -83,10 +83,15 @@ function fitsOn(side, a, f, vp, offset, padding) {
|
|
|
83
83
|
return spaceOn(side, a, vp, offset) >= need + padding;
|
|
84
84
|
}
|
|
85
85
|
function computePosition(anchor, floating, viewport, options = {}) {
|
|
86
|
-
const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true } = options;
|
|
86
|
+
const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true, stickySide } = options;
|
|
87
87
|
const { side: preferred, align } = parsePlacement(placement);
|
|
88
88
|
let side = preferred;
|
|
89
|
-
|
|
89
|
+
const sticky = stickySide === preferred || stickySide === OPPOSITE[preferred] ? stickySide : void 0;
|
|
90
|
+
if (flip && sticky) {
|
|
91
|
+
side = sticky;
|
|
92
|
+
const opp = OPPOSITE[side];
|
|
93
|
+
if (!fitsOn(side, anchor, floating, viewport, offset, padding) && fitsOn(opp, anchor, floating, viewport, offset, padding)) side = opp;
|
|
94
|
+
} else if (flip && !fitsOn(side, anchor, floating, viewport, offset, padding)) {
|
|
90
95
|
const opp = OPPOSITE[side];
|
|
91
96
|
if (fitsOn(opp, anchor, floating, viewport, offset, padding) || spaceOn(opp, anchor, viewport, offset) > spaceOn(side, anchor, viewport, offset)) {
|
|
92
97
|
side = opp;
|
|
@@ -122,57 +127,206 @@ function computePosition(anchor, floating, viewport, options = {}) {
|
|
|
122
127
|
|
|
123
128
|
// src/hooks/useAnchoredPosition.ts
|
|
124
129
|
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
|
|
125
|
-
var
|
|
130
|
+
var resolveAnchor = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
|
|
131
|
+
var anchoringSupported;
|
|
132
|
+
function supportsAnchoring() {
|
|
133
|
+
if (anchoringSupported === void 0) {
|
|
134
|
+
try {
|
|
135
|
+
const css = typeof CSS !== "undefined" ? CSS : void 0;
|
|
136
|
+
anchoringSupported = !!css && typeof css.supports === "function" && css.supports("anchor-name", "--uix-a") && css.supports("position-anchor", "--uix-a") && css.supports("top", "round(anchor(bottom) + 1px, 1px)");
|
|
137
|
+
} catch {
|
|
138
|
+
anchoringSupported = false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return anchoringSupported;
|
|
142
|
+
}
|
|
143
|
+
var anchorCount = 0;
|
|
144
|
+
var namesOf = (value) => value.split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
|
|
145
|
+
function addAnchorName(el, name) {
|
|
146
|
+
const names = namesOf(getComputedStyle(el).getPropertyValue("anchor-name"));
|
|
147
|
+
if (!names.includes(name)) names.push(name);
|
|
148
|
+
el.style.setProperty("anchor-name", names.join(", "));
|
|
149
|
+
}
|
|
150
|
+
function removeAnchorName(el, name) {
|
|
151
|
+
const names = namesOf(el.style.getPropertyValue("anchor-name")).filter((n) => n !== name);
|
|
152
|
+
if (names.length) el.style.setProperty("anchor-name", names.join(", "));
|
|
153
|
+
else el.style.removeProperty("anchor-name");
|
|
154
|
+
}
|
|
155
|
+
function translationOf(el) {
|
|
156
|
+
const t = getComputedStyle(el).transform;
|
|
157
|
+
if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
|
|
158
|
+
try {
|
|
159
|
+
const m = new DOMMatrixReadOnly(t);
|
|
160
|
+
return { x: m.m41, y: m.m42 };
|
|
161
|
+
} catch {
|
|
162
|
+
return { x: 0, y: 0 };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
var signed = (n) => `${n < 0 ? "-" : "+"} ${Math.round(Math.abs(n) * 100) / 100}px`;
|
|
166
|
+
var prepared = /* @__PURE__ */ new WeakSet();
|
|
126
167
|
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
|
|
127
168
|
const opts = (0, import_react.useRef)({ placement, offset, padding });
|
|
128
169
|
opts.current = { placement, offset, padding };
|
|
170
|
+
const session = (0, import_react.useRef)(false);
|
|
171
|
+
const side = (0, import_react.useRef)(null);
|
|
172
|
+
const written = (0, import_react.useRef)("");
|
|
173
|
+
const anchoring = (0, import_react.useRef)(null);
|
|
174
|
+
const anchoringFailed = (0, import_react.useRef)(false);
|
|
175
|
+
const anchorName = (0, import_react.useRef)("");
|
|
176
|
+
const release = (0, import_react.useCallback)((keepPlace) => {
|
|
177
|
+
const current = anchoring.current;
|
|
178
|
+
if (!current) return;
|
|
179
|
+
anchoring.current = null;
|
|
180
|
+
written.current = "";
|
|
181
|
+
const floating = floatingRef.current;
|
|
182
|
+
if (floating) {
|
|
183
|
+
const s = floating.style;
|
|
184
|
+
if (keepPlace && current.anchor.isConnected) {
|
|
185
|
+
const a = current.anchor.getBoundingClientRect();
|
|
186
|
+
s.top = `${Math.round((current.yEdge === "bottom" ? a.bottom : a.top) + current.dy)}px`;
|
|
187
|
+
s.left = `${Math.round((current.xEdge === "right" ? a.right : a.left) + current.dx)}px`;
|
|
188
|
+
}
|
|
189
|
+
s.removeProperty("position-anchor");
|
|
190
|
+
}
|
|
191
|
+
removeAnchorName(current.anchor, current.name);
|
|
192
|
+
}, [floatingRef]);
|
|
129
193
|
const reposition = (0, import_react.useCallback)(() => {
|
|
130
|
-
const anchorEl =
|
|
194
|
+
const anchorEl = resolveAnchor(anchor);
|
|
131
195
|
const floating = floatingRef.current;
|
|
132
196
|
if (!anchorEl || !floating) return;
|
|
133
197
|
const s = floating.style;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
198
|
+
if (!prepared.has(floating)) {
|
|
199
|
+
s.position = "fixed";
|
|
200
|
+
s.inset = "auto";
|
|
201
|
+
s.margin = "0";
|
|
202
|
+
prepared.add(floating);
|
|
203
|
+
written.current = "";
|
|
204
|
+
}
|
|
137
205
|
const a = anchorEl.getBoundingClientRect();
|
|
206
|
+
const size = { width: floating.offsetWidth, height: floating.offsetHeight };
|
|
138
207
|
const { placement: p, offset: o, padding: pad } = opts.current;
|
|
139
208
|
const pos = computePosition(
|
|
140
209
|
{ x: a.x, y: a.y, width: a.width, height: a.height },
|
|
141
|
-
|
|
210
|
+
size,
|
|
142
211
|
{ width: window.innerWidth, height: window.innerHeight },
|
|
143
|
-
{ placement: p, offset: o, padding: pad }
|
|
212
|
+
{ placement: p, offset: o, padding: pad, stickySide: side.current ?? void 0 }
|
|
144
213
|
);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
floating.dataset.placement = pos.placement;
|
|
148
|
-
|
|
214
|
+
const measured = size.width > 0 || size.height > 0;
|
|
215
|
+
if (measured && session.current) side.current = pos.side;
|
|
216
|
+
if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
|
|
217
|
+
const writeFixed = (at) => {
|
|
218
|
+
const left2 = `${Math.round(at.x)}px`;
|
|
219
|
+
const top2 = `${Math.round(at.y)}px`;
|
|
220
|
+
const key2 = `fixed ${left2} ${top2}`;
|
|
221
|
+
if (key2 === written.current) return;
|
|
222
|
+
s.left = left2;
|
|
223
|
+
s.top = top2;
|
|
224
|
+
written.current = key2;
|
|
225
|
+
};
|
|
226
|
+
if (anchoringFailed.current || !supportsAnchoring()) {
|
|
227
|
+
writeFixed(pos);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (anchoring.current && anchoring.current.anchor !== anchorEl) release(false);
|
|
231
|
+
if (!anchoring.current) {
|
|
232
|
+
anchorName.current || (anchorName.current = `--uix-anchor-${++anchorCount}`);
|
|
233
|
+
addAnchorName(anchorEl, anchorName.current);
|
|
234
|
+
s.setProperty("position-anchor", anchorName.current);
|
|
235
|
+
anchoring.current = { anchor: anchorEl, name: anchorName.current, yEdge: "bottom", xEdge: "left", dy: 0, dx: 0, verified: false };
|
|
236
|
+
written.current = "";
|
|
237
|
+
}
|
|
238
|
+
const current = anchoring.current;
|
|
239
|
+
current.yEdge = pos.side === "bottom" ? "bottom" : "top";
|
|
240
|
+
current.xEdge = pos.side === "right" ? "right" : "left";
|
|
241
|
+
current.dy = pos.y - (current.yEdge === "bottom" ? a.bottom : a.top);
|
|
242
|
+
current.dx = pos.x - (current.xEdge === "right" ? a.right : a.left);
|
|
243
|
+
const top = `round(anchor(${current.yEdge}) ${signed(current.dy)}, 1px)`;
|
|
244
|
+
const left = `round(anchor(${current.xEdge}) ${signed(current.dx)}, 1px)`;
|
|
245
|
+
const key = `anchored ${top} ${left}`;
|
|
246
|
+
if (key !== written.current) {
|
|
247
|
+
s.top = top;
|
|
248
|
+
s.left = left;
|
|
249
|
+
written.current = key;
|
|
250
|
+
}
|
|
251
|
+
if (!current.verified && measured) {
|
|
252
|
+
current.verified = true;
|
|
253
|
+
const box = floating.getBoundingClientRect();
|
|
254
|
+
const t = translationOf(floating);
|
|
255
|
+
const dxc = box.left + box.width / 2 - t.x - (pos.x + size.width / 2);
|
|
256
|
+
const dyc = box.top + box.height / 2 - t.y - (pos.y + size.height / 2);
|
|
257
|
+
if (Math.abs(dxc) > 2 || Math.abs(dyc) > 2) {
|
|
258
|
+
anchoringFailed.current = true;
|
|
259
|
+
release(false);
|
|
260
|
+
writeFixed(pos);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}, [anchor, floatingRef, release]);
|
|
149
264
|
useIsomorphicLayoutEffect(() => {
|
|
150
265
|
if (!open) return;
|
|
266
|
+
session.current = true;
|
|
151
267
|
reposition();
|
|
152
|
-
const
|
|
268
|
+
const anchorEl = resolveAnchor(anchor);
|
|
269
|
+
const floating = floatingRef.current;
|
|
270
|
+
const onScroll = (event) => {
|
|
271
|
+
const target = event.target;
|
|
272
|
+
if (target && typeof target.contains === "function") {
|
|
273
|
+
if (floating?.contains(target)) return;
|
|
274
|
+
if (anchorEl && !target.contains(anchorEl)) return;
|
|
275
|
+
}
|
|
276
|
+
reposition();
|
|
277
|
+
};
|
|
278
|
+
const onResize = () => reposition();
|
|
153
279
|
window.addEventListener("scroll", onScroll, { passive: true, capture: true });
|
|
154
|
-
window.addEventListener("resize",
|
|
280
|
+
window.addEventListener("resize", onResize);
|
|
281
|
+
const observer = typeof ResizeObserver === "function" ? new ResizeObserver(() => reposition()) : null;
|
|
282
|
+
if (floating) observer?.observe(floating);
|
|
283
|
+
if (anchorEl) observer?.observe(anchorEl);
|
|
155
284
|
return () => {
|
|
156
285
|
window.removeEventListener("scroll", onScroll, true);
|
|
157
|
-
window.removeEventListener("resize",
|
|
286
|
+
window.removeEventListener("resize", onResize);
|
|
287
|
+
observer?.disconnect();
|
|
288
|
+
session.current = false;
|
|
289
|
+
side.current = null;
|
|
290
|
+
anchoringFailed.current = false;
|
|
291
|
+
release(true);
|
|
158
292
|
};
|
|
159
|
-
}, [open, reposition]);
|
|
293
|
+
}, [open, reposition, anchor, floatingRef, release]);
|
|
294
|
+
(0, import_react.useEffect)(() => () => release(false), [release]);
|
|
160
295
|
return reposition;
|
|
161
296
|
}
|
|
162
297
|
|
|
163
298
|
// src/components/Popover.tsx
|
|
164
299
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
300
|
+
var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? import_react2.useLayoutEffect : import_react2.useEffect;
|
|
301
|
+
var PLACING = "data-uix-placing";
|
|
302
|
+
var isPopoverOpen = (el) => {
|
|
303
|
+
try {
|
|
304
|
+
return el.matches(":popover-open");
|
|
305
|
+
} catch {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var settle = (el) => {
|
|
310
|
+
if (!el.hasAttribute(PLACING)) return;
|
|
311
|
+
getComputedStyle(el).getPropertyValue("transform");
|
|
312
|
+
el.removeAttribute(PLACING);
|
|
313
|
+
};
|
|
165
314
|
function Popover({
|
|
166
315
|
popover = "auto",
|
|
167
316
|
anchor,
|
|
168
317
|
placement = "bottom-start",
|
|
169
318
|
offset = 6,
|
|
319
|
+
closeWhenAnchorHidden = false,
|
|
320
|
+
onAnchorHidden,
|
|
170
321
|
className,
|
|
171
322
|
children,
|
|
172
323
|
...props
|
|
173
324
|
}) {
|
|
174
325
|
const ref = (0, import_react2.useRef)(null);
|
|
175
326
|
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
327
|
+
const openRef = (0, import_react2.useRef)(false);
|
|
328
|
+
const onAnchorHiddenRef = (0, import_react2.useRef)(onAnchorHidden);
|
|
329
|
+
onAnchorHiddenRef.current = onAnchorHidden;
|
|
176
330
|
const popoverAttr = { popover };
|
|
177
331
|
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
178
332
|
open: open && !!anchor,
|
|
@@ -182,14 +336,55 @@ function Popover({
|
|
|
182
336
|
(0, import_react2.useEffect)(() => {
|
|
183
337
|
const el = ref.current;
|
|
184
338
|
if (!el || !anchor) return;
|
|
339
|
+
const hold = (event) => {
|
|
340
|
+
if (event.newState !== "open") return;
|
|
341
|
+
el.setAttribute(PLACING, "");
|
|
342
|
+
window.setTimeout(() => settle(el), 150);
|
|
343
|
+
};
|
|
185
344
|
const sync = () => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
345
|
+
if (!isPopoverOpen(el)) {
|
|
346
|
+
el.removeAttribute(PLACING);
|
|
347
|
+
setOpen(false);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
reposition();
|
|
351
|
+
window.setTimeout(() => {
|
|
352
|
+
if (!isPopoverOpen(el)) return;
|
|
353
|
+
if (!openRef.current) {
|
|
354
|
+
setOpen(true);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
reposition();
|
|
358
|
+
settle(el);
|
|
359
|
+
}, 0);
|
|
189
360
|
};
|
|
361
|
+
el.addEventListener("beforetoggle", hold);
|
|
190
362
|
el.addEventListener("toggle", sync);
|
|
191
|
-
return () =>
|
|
363
|
+
return () => {
|
|
364
|
+
el.removeEventListener("beforetoggle", hold);
|
|
365
|
+
el.removeEventListener("toggle", sync);
|
|
366
|
+
el.removeAttribute(PLACING);
|
|
367
|
+
};
|
|
192
368
|
}, [anchor, reposition]);
|
|
369
|
+
useIsomorphicLayoutEffect2(() => {
|
|
370
|
+
openRef.current = open;
|
|
371
|
+
if (open && ref.current) settle(ref.current);
|
|
372
|
+
}, [open]);
|
|
373
|
+
(0, import_react2.useEffect)(() => {
|
|
374
|
+
const el = ref.current;
|
|
375
|
+
const anchorEl = resolveAnchor(anchor);
|
|
376
|
+
if (!closeWhenAnchorHidden || !open || !el || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
377
|
+
const observer = new IntersectionObserver((entries) => {
|
|
378
|
+
const latest = entries[entries.length - 1];
|
|
379
|
+
if (!latest || latest.isIntersecting || !isPopoverOpen(el)) return;
|
|
380
|
+
onAnchorHiddenRef.current?.();
|
|
381
|
+
const active = el.ownerDocument.activeElement;
|
|
382
|
+
if (active && el.contains(active)) active.blur();
|
|
383
|
+
el.hidePopover();
|
|
384
|
+
});
|
|
385
|
+
observer.observe(anchorEl);
|
|
386
|
+
return () => observer.disconnect();
|
|
387
|
+
}, [closeWhenAnchorHidden, open, anchor]);
|
|
193
388
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
|
|
194
389
|
}
|
|
195
390
|
|
|
@@ -386,6 +581,14 @@ var DEFAULT_EMOJI_PICKER_LABELS = {
|
|
|
386
581
|
loadFailed: "Emoji could not be loaded."
|
|
387
582
|
};
|
|
388
583
|
var EMOJI_GRID_COLUMNS = 8;
|
|
584
|
+
var useIsomorphicLayoutEffect3 = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
|
|
585
|
+
function revealInGrid(grid, cell) {
|
|
586
|
+
const g = grid.getBoundingClientRect();
|
|
587
|
+
const c = cell.getBoundingClientRect();
|
|
588
|
+
const top = g.top + (parseFloat(getComputedStyle(grid).scrollPaddingTop) || 0);
|
|
589
|
+
if (c.top < top) grid.scrollTop -= top - c.top;
|
|
590
|
+
else if (c.bottom > g.bottom) grid.scrollTop += c.bottom - g.bottom;
|
|
591
|
+
}
|
|
389
592
|
function EmojiPicker({
|
|
390
593
|
onSelect,
|
|
391
594
|
trigger,
|
|
@@ -407,6 +610,8 @@ function EmojiPicker({
|
|
|
407
610
|
const searchRef = (0, import_react4.useRef)(null);
|
|
408
611
|
const gridRef = (0, import_react4.useRef)(null);
|
|
409
612
|
const pointerWasOpen = (0, import_react4.useRef)(null);
|
|
613
|
+
const closedByAnchor = (0, import_react4.useRef)(false);
|
|
614
|
+
const pendingJump = (0, import_react4.useRef)(null);
|
|
410
615
|
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
411
616
|
const [data, setData] = (0, import_react4.useState)(null);
|
|
412
617
|
const [failed, setFailed] = (0, import_react4.useState)(false);
|
|
@@ -430,10 +635,14 @@ function EmojiPicker({
|
|
|
430
635
|
setOpen(nowOpen);
|
|
431
636
|
onOpenChange?.(nowOpen);
|
|
432
637
|
if (nowOpen) {
|
|
638
|
+
closedByAnchor.current = false;
|
|
639
|
+
pendingJump.current = null;
|
|
433
640
|
setRecent(readRecentEmoji(recentStorageKey));
|
|
434
641
|
setQuery("");
|
|
435
642
|
setActive(0);
|
|
436
|
-
requestAnimationFrame(() => searchRef.current?.focus());
|
|
643
|
+
requestAnimationFrame(() => searchRef.current?.focus({ preventScroll: true }));
|
|
644
|
+
} else if (closedByAnchor.current) {
|
|
645
|
+
closedByAnchor.current = false;
|
|
437
646
|
} else if (el.contains(document.activeElement) || document.activeElement === document.body) {
|
|
438
647
|
focusTrigger();
|
|
439
648
|
}
|
|
@@ -465,10 +674,7 @@ function EmojiPicker({
|
|
|
465
674
|
const el = popoverEl();
|
|
466
675
|
if (el && isOpen(el)) el.hidePopover();
|
|
467
676
|
};
|
|
468
|
-
const
|
|
469
|
-
if (query.trim()) {
|
|
470
|
-
return data ? [{ key: "results", title: labels.search, emojis: searchEmoji(data, query).map((e) => e.emoji) }] : [];
|
|
471
|
-
}
|
|
677
|
+
const browseSections = (0, import_react4.useMemo)(() => {
|
|
472
678
|
const list = [];
|
|
473
679
|
if (quickPicks?.length) list.push({ key: "quick", title: labels.quickPicks, emojis: [...new Set(quickPicks)] });
|
|
474
680
|
if (recent.length) list.push({ key: "recent", title: labels.recent, emojis: recent });
|
|
@@ -478,7 +684,11 @@ function EmojiPicker({
|
|
|
478
684
|
});
|
|
479
685
|
}
|
|
480
686
|
return list;
|
|
481
|
-
}, [
|
|
687
|
+
}, [data, quickPicks, recent, labels.quickPicks, labels.recent]);
|
|
688
|
+
const sections = (0, import_react4.useMemo)(() => {
|
|
689
|
+
if (!query.trim()) return browseSections;
|
|
690
|
+
return data ? [{ key: "results", title: labels.search, emojis: searchEmoji(data, query).map((e) => e.emoji) }] : [];
|
|
691
|
+
}, [query, data, browseSections, labels.search]);
|
|
482
692
|
const flat = (0, import_react4.useMemo)(() => sections.flatMap((s) => s.emojis), [sections]);
|
|
483
693
|
const sectionStart = (0, import_react4.useMemo)(() => {
|
|
484
694
|
const starts = /* @__PURE__ */ new Map();
|
|
@@ -495,13 +705,43 @@ function EmojiPicker({
|
|
|
495
705
|
onSelect(emoji);
|
|
496
706
|
hide();
|
|
497
707
|
};
|
|
498
|
-
const focusCell = (0, import_react4.useCallback)((index) => {
|
|
708
|
+
const focusCell = (0, import_react4.useCallback)((index, reveal = true) => {
|
|
499
709
|
setActive(index);
|
|
500
710
|
const find = () => gridRef.current?.querySelector(`[data-emoji-index="${index}"]`);
|
|
711
|
+
const move = (cell2) => {
|
|
712
|
+
cell2.focus({ preventScroll: true });
|
|
713
|
+
if (reveal && gridRef.current) revealInGrid(gridRef.current, cell2);
|
|
714
|
+
};
|
|
501
715
|
const cell = find();
|
|
502
|
-
if (cell) cell
|
|
503
|
-
else requestAnimationFrame(() =>
|
|
716
|
+
if (cell) move(cell);
|
|
717
|
+
else requestAnimationFrame(() => {
|
|
718
|
+
const later = find();
|
|
719
|
+
if (later) move(later);
|
|
720
|
+
});
|
|
504
721
|
}, []);
|
|
722
|
+
const showSection = (key) => {
|
|
723
|
+
const grid = gridRef.current;
|
|
724
|
+
const section = grid?.querySelector(`[data-section="${key}"]`);
|
|
725
|
+
if (!grid || !section) return;
|
|
726
|
+
grid.scrollTop += section.getBoundingClientRect().top - grid.getBoundingClientRect().top;
|
|
727
|
+
const start = sectionStart.get(key);
|
|
728
|
+
if (start !== void 0) focusCell(start, false);
|
|
729
|
+
};
|
|
730
|
+
const onCategory = (key) => {
|
|
731
|
+
if (!query.trim()) {
|
|
732
|
+
showSection(key);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
pendingJump.current = key;
|
|
736
|
+
setQuery("");
|
|
737
|
+
setActive(0);
|
|
738
|
+
};
|
|
739
|
+
useIsomorphicLayoutEffect3(() => {
|
|
740
|
+
const key = pendingJump.current;
|
|
741
|
+
if (key === null || query.trim()) return;
|
|
742
|
+
pendingJump.current = null;
|
|
743
|
+
showSection(key);
|
|
744
|
+
}, [query, sections]);
|
|
505
745
|
const onGridKeyDown = (event) => {
|
|
506
746
|
const target = event.target;
|
|
507
747
|
const index = Number(target.dataset.emojiIndex ?? -1);
|
|
@@ -509,7 +749,7 @@ function EmojiPicker({
|
|
|
509
749
|
const next = emojiGridMove(index, event.key, flat.length, EMOJI_GRID_COLUMNS);
|
|
510
750
|
if (event.key === "ArrowUp" && next === null) {
|
|
511
751
|
event.preventDefault();
|
|
512
|
-
searchRef.current?.focus();
|
|
752
|
+
searchRef.current?.focus({ preventScroll: true });
|
|
513
753
|
return;
|
|
514
754
|
}
|
|
515
755
|
if (next === null) return;
|
|
@@ -565,6 +805,10 @@ function EmojiPicker({
|
|
|
565
805
|
className: "uix-emoji-picker-popover",
|
|
566
806
|
role: "dialog",
|
|
567
807
|
"aria-label": labels.dialog,
|
|
808
|
+
closeWhenAnchorHidden: true,
|
|
809
|
+
onAnchorHidden: () => {
|
|
810
|
+
closedByAnchor.current = true;
|
|
811
|
+
},
|
|
568
812
|
children: open ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "uix-emoji-picker uix-emoji-picker--full", lang: locale, children: [
|
|
569
813
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
570
814
|
"input",
|
|
@@ -584,50 +828,56 @@ function EmojiPicker({
|
|
|
584
828
|
spellCheck: false
|
|
585
829
|
}
|
|
586
830
|
),
|
|
587
|
-
|
|
588
|
-
"
|
|
831
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
832
|
+
"div",
|
|
589
833
|
{
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
"aria-label":
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
834
|
+
className: "uix-emoji-picker__nav",
|
|
835
|
+
role: data ? "group" : void 0,
|
|
836
|
+
"aria-label": data ? labels.categories : void 0,
|
|
837
|
+
children: data ? browseSections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
838
|
+
"button",
|
|
839
|
+
{
|
|
840
|
+
type: "button",
|
|
841
|
+
className: "uix-emoji-picker__nav-btn",
|
|
842
|
+
"aria-label": section.title,
|
|
843
|
+
title: section.title,
|
|
844
|
+
onClick: () => onCategory(section.key),
|
|
845
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", children: section.emojis[0] ? renderEmoji(section.emojis[0]) : null })
|
|
846
|
+
},
|
|
847
|
+
section.key
|
|
848
|
+
)) : null
|
|
849
|
+
}
|
|
850
|
+
),
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "uix-emoji-picker__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
|
|
852
|
+
sections.map((section) => {
|
|
853
|
+
const start = offset;
|
|
854
|
+
offset += section.emojis.length;
|
|
855
|
+
if (!section.emojis.length) return null;
|
|
856
|
+
const headingId = `${id}-${section.key}`;
|
|
857
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "uix-emoji-picker__section", "data-section": section.key, "aria-labelledby": headingId, children: [
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { id: headingId, className: "uix-emoji-picker__heading", children: section.title }),
|
|
859
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "uix-emoji-picker__cells", role: "group", "aria-labelledby": headingId, children: section.emojis.map((emoji, i) => {
|
|
860
|
+
const index = start + i;
|
|
861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
862
|
+
"button",
|
|
863
|
+
{
|
|
864
|
+
type: "button",
|
|
865
|
+
className: "uix-emoji-picker__btn",
|
|
866
|
+
"data-emoji-index": index,
|
|
867
|
+
tabIndex: index === activeIndex ? 0 : -1,
|
|
868
|
+
"aria-label": names?.get(emoji) ?? emoji,
|
|
869
|
+
title: names?.get(emoji),
|
|
870
|
+
onClick: () => choose(emoji),
|
|
871
|
+
onFocus: () => setActive(index),
|
|
872
|
+
children: renderEmoji(emoji)
|
|
873
|
+
},
|
|
874
|
+
`${section.key}-${emoji}`
|
|
875
|
+
);
|
|
876
|
+
}) })
|
|
877
|
+
] }, section.key);
|
|
878
|
+
}),
|
|
879
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "uix-emoji-picker__status", role: "status", children: status })
|
|
880
|
+
] })
|
|
631
881
|
] }) : null
|
|
632
882
|
}
|
|
633
883
|
)
|
package/dist/emoji.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { E as EmojiPickerLabels, a as EmojiLocale, b as EmojiDataLoader } from './emoji-image-
|
|
2
|
-
export { C as CompactEmoji, D as DEFAULT_EMOJI_PICKER_LABELS, c as EMOJI_GRID_COLUMNS, d as EmojiData, e as EmojiEntry, f as EmojiGroup, g as EmojiMessages, h as EmojiPicker, i as EmojiPickerProps, j as buildEmojiData, k as canRenderEmoji, l as emojiImageFileName, m as loadEmojiData, n as normalizeEmojiImageBaseUrl, s as searchEmoji } from './emoji-image-
|
|
3
|
-
export { A as Align, P as Placement, S as Side } from './overlay-position-
|
|
1
|
+
import { E as EmojiPickerLabels, a as EmojiLocale, b as EmojiDataLoader } from './emoji-image-6mPZ15Zy.cjs';
|
|
2
|
+
export { C as CompactEmoji, D as DEFAULT_EMOJI_PICKER_LABELS, c as EMOJI_GRID_COLUMNS, d as EmojiData, e as EmojiEntry, f as EmojiGroup, g as EmojiMessages, h as EmojiPicker, i as EmojiPickerProps, j as buildEmojiData, k as canRenderEmoji, l as emojiImageFileName, m as loadEmojiData, n as normalizeEmojiImageBaseUrl, s as searchEmoji } from './emoji-image-6mPZ15Zy.cjs';
|
|
3
|
+
export { A as Align, P as Placement, S as Side } from './overlay-position-Cjv5E7mn.cjs';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
|
|
6
6
|
interface ReactionSummary {
|
|
@@ -8,15 +8,23 @@ interface UseAnchoredPositionOptions {
|
|
|
8
8
|
offset?: number;
|
|
9
9
|
padding?: number;
|
|
10
10
|
}
|
|
11
|
+
type AnchorArg = RefObject<HTMLElement | null> | HTMLElement | null | undefined;
|
|
12
|
+
/** @internal The element behind an anchor ref or element. */
|
|
13
|
+
declare const resolveAnchor: (a: AnchorArg) => HTMLElement | null;
|
|
11
14
|
/**
|
|
12
15
|
* Position a floating element (a native-popover overlay) against an anchor with
|
|
13
16
|
* cross-browser flip/shift — the DOM half of the overlay-position engine (UIX-FIX-02).
|
|
14
17
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* The engine decides the side, alignment and viewport clamp from getBoundingClientRect.
|
|
19
|
+
* Where the browser supports CSS anchor positioning, that result is written as an offset
|
|
20
|
+
* from the anchor (`position-anchor` + `anchor()`), so the browser keeps the overlay glued
|
|
21
|
+
* to its anchor while the page scrolls, with no frame of lag; elsewhere it is written as
|
|
22
|
+
* `position: fixed` + left/top and follows scroll/resize from JS. While `open`, the side
|
|
23
|
+
* chosen at open is kept until it no longer fits and the other side does, scrolls inside
|
|
24
|
+
* the overlay itself are ignored, and a size change of either element re-places it.
|
|
25
|
+
* Returns a `reposition` fn for callers that want to nudge it manually (e.g. right when a
|
|
26
|
+
* native popover's `toggle` fires).
|
|
19
27
|
*/
|
|
20
28
|
declare function useAnchoredPosition(anchor: RefObject<HTMLElement | null> | HTMLElement | null | undefined, floatingRef: RefObject<HTMLElement | null>, { open, placement, offset, padding }: UseAnchoredPositionOptions): () => void;
|
|
21
29
|
|
|
22
|
-
export { type UseAnchoredPositionOptions, useAnchoredPosition };
|
|
30
|
+
export { type UseAnchoredPositionOptions, resolveAnchor, useAnchoredPosition };
|