feat(component): virtual scroll emoji groups in emoji picker (#13671)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
- Revamped Emoji Picker: grouped browsing with sticky group headers,
footer navigation, and a new EmojiButton for quicker selection.
  - Recent emojis with persisted history and single-tap add.
- Programmatic group navigation and callbacks for sticky-group changes.

- Style
  - Updated scroll area paddings for emoji and icon pickers.
  - Enhanced group header background for better contrast.

- Refactor
- Simplified emoji picker internals for leaner, more responsive
rendering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Cats Juice
2025-09-30 09:59:39 +08:00
committed by GitHub
parent 123d50a484
commit b44fdbce0c
9 changed files with 380 additions and 268 deletions
@@ -112,12 +112,18 @@ export const calcLayout = (
const ratioMode = 'ratio' in item;
const height = ratioMode ? item.ratio * width : item.height;
const aroundGapXValue =
columns > 1
? (totalWidth - paddingX * 2 - width * columns) / (columns - 1)
: 0;
const gapXValue = Math.max(gapX, aroundGapXValue);
if (ratioMode) {
const minRatio = Math.min(...ratioStack);
const minRatioIndex = ratioStack.indexOf(minRatio);
const minHeight = heightStack[minRatioIndex];
const hasGap = heightStack[minRatioIndex] ? gapY : 0;
const x = minRatioIndex * (width + gapX) + paddingX;
const x = minRatioIndex * (width + gapXValue) + paddingX;
const y = finalHeight + minHeight + hasGap;
ratioStack[minRatioIndex] += item.ratio * 10000;
@@ -133,7 +139,7 @@ export const calcLayout = (
const minHeight = Math.min(...heightStack);
const minHeightIndex = heightStack.indexOf(minHeight);
const hasGap = heightStack[minHeightIndex] ? gapY : 0;
const x = minHeightIndex * (width + gapX) + paddingX;
const x = minHeightIndex * (width + gapXValue) + paddingX;
const y = finalHeight + minHeight + hasGap;
const ratio = height / width;
@@ -193,7 +199,7 @@ export const calcSticky = (options: {
const stickyGroupEntry = groupEntries.find(([_, xywh], index) => {
const next = groupEntries[index + 1];
return xywh.y < scrollY && (!next || next[1].y > scrollY);
return xywh.y <= scrollY && (!next || next[1].y > scrollY);
});
return stickyGroupEntry