mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): keep sticker sizes fixed while resizing the expression picker
This commit is contained in:
1 parent
58732f7770
commit
154e223284
4 files changed
+55
-17
No files matched your search
@@ -14,10 +14,11 @@ import {useVirtualRows} from '@app/features/channel/components/sticker_picker/ho
|
|||||||
import {StickerPickerCategoryList} from '@app/features/channel/components/sticker_picker/StickerPickerCategoryList';
|
import {StickerPickerCategoryList} from '@app/features/channel/components/sticker_picker/StickerPickerCategoryList';
|
||||||
import {
|
import {
|
||||||
buildStickerRowOffsets,
|
buildStickerRowOffsets,
|
||||||
getStickerRowHeight,
|
getFixedStickerRowHeight,
|
||||||
|
getStickerGridColumns,
|
||||||
getStickerRowWindow,
|
getStickerRowWindow,
|
||||||
|
STICKER_GRID_TRACK_WIDTH,
|
||||||
STICKER_SECTION_GAP,
|
STICKER_SECTION_GAP,
|
||||||
STICKERS_PER_ROW,
|
|
||||||
type StickerRowKind,
|
type StickerRowKind,
|
||||||
} from '@app/features/channel/components/sticker_picker/StickerPickerConstants';
|
} from '@app/features/channel/components/sticker_picker/StickerPickerConstants';
|
||||||
import {StickerPickerInspector} from '@app/features/channel/components/sticker_picker/StickerPickerInspector';
|
import {StickerPickerInspector} from '@app/features/channel/components/sticker_picker/StickerPickerInspector';
|
||||||
@@ -74,12 +75,12 @@ export const StickersPicker = observer(
|
|||||||
const [selectedColumn, setSelectedColumn] = useState(-1);
|
const [selectedColumn, setSelectedColumn] = useState(-1);
|
||||||
const [shouldScrollOnSelection, setShouldScrollOnSelection] = useState(false);
|
const [shouldScrollOnSelection, setShouldScrollOnSelection] = useState(false);
|
||||||
const scrollerRef = useRef<ScrollerHandle>(null);
|
const scrollerRef = useRef<ScrollerHandle>(null);
|
||||||
const {scrollTop, handleScroll, handleResize} = useScrollerViewport(scrollerRef);
|
const {viewportSize, scrollTop, handleScroll, handleResize} = useScrollerViewport(scrollerRef);
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
const stickerRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
|
const stickerRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
|
||||||
const channel = channelId ? (Channels.getChannel(channelId) ?? null) : null;
|
const channel = channelId ? (Channels.getChannel(channelId) ?? null) : null;
|
||||||
const rowListRef = useRef<HTMLDivElement>(null);
|
const rowListRef = useRef<HTMLDivElement>(null);
|
||||||
const [listMetrics, setListMetrics] = useState({origin: 0, viewportHeight: 0, gridWidth: 0});
|
const [listMetrics, setListMetrics] = useState({origin: 0, viewportHeight: 0});
|
||||||
const [stickerDataVersion, setStickerDataVersion] = useState(0);
|
const [stickerDataVersion, setStickerDataVersion] = useState(0);
|
||||||
const permissionVersion = useSyncExternalStore(Permission.subscribe.bind(Permission), () => Permission.version);
|
const permissionVersion = useSyncExternalStore(Permission.subscribe.bind(Permission), () => Permission.version);
|
||||||
const {shouldAnimate: shouldAnimateStickerPreview} = useStickerAnimation();
|
const {shouldAnimate: shouldAnimateStickerPreview} = useStickerAnimation();
|
||||||
@@ -138,13 +139,26 @@ export const StickersPicker = observer(
|
|||||||
allUpsell.accessibleItems,
|
allUpsell.accessibleItems,
|
||||||
renderedStickers,
|
renderedStickers,
|
||||||
);
|
);
|
||||||
|
const zoomLevel = Accessibility.zoomLevel;
|
||||||
|
const gridColumns = useMemo(() => getStickerGridColumns(viewportSize.width), [viewportSize.width, zoomLevel]);
|
||||||
|
const previousGridColumnsRef = useRef(gridColumns);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (previousGridColumnsRef.current === gridColumns) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previousGridColumnsRef.current = gridColumns;
|
||||||
|
setHoveredSticker(null);
|
||||||
|
setSelectedRow(-1);
|
||||||
|
setSelectedColumn(-1);
|
||||||
|
setShouldScrollOnSelection(false);
|
||||||
|
}, [gridColumns]);
|
||||||
const pickerRows = useVirtualRows(
|
const pickerRows = useVirtualRows(
|
||||||
searchTerm,
|
searchTerm,
|
||||||
renderedStickers,
|
renderedStickers,
|
||||||
favoriteStickers,
|
favoriteStickers,
|
||||||
frequentlyUsedStickers,
|
frequentlyUsedStickers,
|
||||||
stickersByGuildId,
|
stickersByGuildId,
|
||||||
STICKERS_PER_ROW,
|
gridColumns,
|
||||||
);
|
);
|
||||||
const hasNoStickersAtAll = allItems.length === 0;
|
const hasNoStickersAtAll = allItems.length === 0;
|
||||||
const isSearching = searchTerm.trim().length > 0;
|
const isSearching = searchTerm.trim().length > 0;
|
||||||
@@ -198,12 +212,8 @@ export const StickersPicker = observer(
|
|||||||
}
|
}
|
||||||
return {stickerRowIndexes: indexes, stickerRowStarts: starts, categoryRowIndexes: categories};
|
return {stickerRowIndexes: indexes, stickerRowStarts: starts, categoryRowIndexes: categories};
|
||||||
}, [pickerRows]);
|
}, [pickerRows]);
|
||||||
const zoomLevel = Accessibility.zoomLevel;
|
|
||||||
const remScale = getAppRemScale();
|
const remScale = getAppRemScale();
|
||||||
const stickerRowHeight = useMemo(
|
const stickerRowHeight = useMemo(() => getFixedStickerRowHeight(remScale), [remScale, zoomLevel]);
|
||||||
() => getStickerRowHeight(listMetrics.gridWidth, STICKERS_PER_ROW, remScale),
|
|
||||||
[listMetrics.gridWidth, remScale, zoomLevel],
|
|
||||||
);
|
|
||||||
const rowOffsets = useMemo(() => {
|
const rowOffsets = useMemo(() => {
|
||||||
const rowKinds = pickerRows.map((row): StickerRowKind => row.type);
|
const rowKinds = pickerRows.map((row): StickerRowKind => row.type);
|
||||||
return buildStickerRowOffsets(rowKinds, {remScale, stickerRowHeight, sectionGap: STICKER_SECTION_GAP});
|
return buildStickerRowOffsets(rowKinds, {remScale, stickerRowHeight, sectionGap: STICKER_SECTION_GAP});
|
||||||
@@ -219,11 +229,8 @@ export const StickersPicker = observer(
|
|||||||
rowListNode.getBoundingClientRect().top - scrollerNode.getBoundingClientRect().top + scrollerNode.scrollTop,
|
rowListNode.getBoundingClientRect().top - scrollerNode.getBoundingClientRect().top + scrollerNode.scrollTop,
|
||||||
);
|
);
|
||||||
const viewportHeight = scrollerNode.clientHeight;
|
const viewportHeight = scrollerNode.clientHeight;
|
||||||
const gridWidth = rowListNode.clientWidth;
|
|
||||||
setListMetrics((current) =>
|
setListMetrics((current) =>
|
||||||
current.origin === origin && current.viewportHeight === viewportHeight && current.gridWidth === gridWidth
|
current.origin === origin && current.viewportHeight === viewportHeight ? current : {origin, viewportHeight},
|
||||||
? current
|
|
||||||
: {origin, viewportHeight, gridWidth},
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const rowWindow = useMemo(
|
const rowWindow = useMemo(
|
||||||
@@ -394,7 +401,8 @@ export const StickersPicker = observer(
|
|||||||
row={row}
|
row={row}
|
||||||
handleHover={handleHover}
|
handleHover={handleHover}
|
||||||
handleSelect={handleStickerSelect}
|
handleSelect={handleStickerSelect}
|
||||||
gridColumns={STICKERS_PER_ROW}
|
gridColumns={gridColumns}
|
||||||
|
cellTrackWidth={STICKER_GRID_TRACK_WIDTH}
|
||||||
selectedRow={selectedRow}
|
selectedRow={selectedRow}
|
||||||
selectedColumn={selectedColumn}
|
selectedColumn={selectedColumn}
|
||||||
stickerRowIndex={stickerRowIndexes[rowIndex]!}
|
stickerRowIndex={stickerRowIndexes[rowIndex]!}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {getRemScaleForDocument} from '@app/features/theme/layout/RemFromPx';
|
||||||
|
|
||||||
export const STICKERS_PER_ROW = 4;
|
export const STICKERS_PER_ROW = 4;
|
||||||
export const STICKERS_PER_ROW_MOBILE = 5;
|
export const STICKERS_PER_ROW_MOBILE = 5;
|
||||||
export const STICKER_CATEGORY_HEADER_HEIGHT = 32;
|
export const STICKER_CATEGORY_HEADER_HEIGHT = 32;
|
||||||
export const STICKER_SECTION_GAP = 12;
|
export const STICKER_SECTION_GAP = 12;
|
||||||
|
export const STICKER_GRID_TRACK_WIDTH = 120;
|
||||||
|
|
||||||
const STICKER_CATEGORY_HEADER_GAP = 8;
|
const STICKER_CATEGORY_HEADER_GAP = 8;
|
||||||
const STICKER_GRID_GAP = 8;
|
const STICKER_GRID_GAP = 8;
|
||||||
@@ -23,6 +26,20 @@ export interface StickerRowWindow {
|
|||||||
lastRow: number;
|
lastRow: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getStickerGridColumns(containerWidth: number): number {
|
||||||
|
if (containerWidth <= 0) {
|
||||||
|
return STICKERS_PER_ROW;
|
||||||
|
}
|
||||||
|
const remScale = getRemScaleForDocument(typeof document === 'undefined' ? null : document);
|
||||||
|
const trackWidth = STICKER_GRID_TRACK_WIDTH * remScale;
|
||||||
|
const gap = STICKER_GRID_GAP * remScale;
|
||||||
|
return Math.max(1, Math.floor((containerWidth + gap) / (trackWidth + gap)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFixedStickerRowHeight(remScale: number): number {
|
||||||
|
return (STICKER_GRID_TRACK_WIDTH + STICKER_GRID_BLOCK_END_PADDING) * remScale;
|
||||||
|
}
|
||||||
|
|
||||||
export function getStickerRowHeight(gridWidth: number, gridColumns: number, remScale: number): number {
|
export function getStickerRowHeight(gridWidth: number, gridColumns: number, remScale: number): number {
|
||||||
if (!(gridWidth > 0) || gridColumns <= 0) {
|
if (!(gridWidth > 0) || gridColumns <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -20,6 +20,11 @@
|
|||||||
padding: 0 0 0.5rem;
|
padding: 0 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stickerGridFixedTrack {
|
||||||
|
grid-auto-flow: column;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
.stickerButton {
|
.stickerButton {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ interface VirtualRowRendererProps {
|
|||||||
handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void;
|
handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void;
|
||||||
handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void;
|
handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void;
|
||||||
gridColumns?: number;
|
gridColumns?: number;
|
||||||
|
cellTrackWidth?: number;
|
||||||
selectedRow: number;
|
selectedRow: number;
|
||||||
selectedColumn: number;
|
selectedColumn: number;
|
||||||
stickerRowIndex: number;
|
stickerRowIndex: number;
|
||||||
@@ -126,6 +127,7 @@ const VirtualRowRendererBase: React.FC<VirtualRowRendererProps> = React.memo(
|
|||||||
handleHover,
|
handleHover,
|
||||||
handleSelect,
|
handleSelect,
|
||||||
gridColumns = 4,
|
gridColumns = 4,
|
||||||
|
cellTrackWidth,
|
||||||
selectedRow,
|
selectedRow,
|
||||||
selectedColumn,
|
selectedColumn,
|
||||||
stickerRowIndex,
|
stickerRowIndex,
|
||||||
@@ -220,11 +222,14 @@ const VirtualRowRendererBase: React.FC<VirtualRowRendererProps> = React.memo(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (row.type === 'sticker-row') {
|
if (row.type === 'sticker-row') {
|
||||||
|
const hasFixedTrack = cellTrackWidth != null && cellTrackWidth > 0;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.stickerGrid}
|
className={hasFixedTrack ? `${styles.stickerGrid} ${styles.stickerGridFixedTrack}` : styles.stickerGrid}
|
||||||
style={{
|
style={{
|
||||||
gridTemplateColumns: `repeat(${gridColumns}, minmax(0, 1fr))`,
|
gridTemplateColumns: hasFixedTrack
|
||||||
|
? `repeat(auto-fill, ${remFromPx(cellTrackWidth)})`
|
||||||
|
: `repeat(${gridColumns}, minmax(0, 1fr))`,
|
||||||
}}
|
}}
|
||||||
data-flx="channel.sticker-picker.virtual-row.virtual-row-renderer-base.sticker-grid"
|
data-flx="channel.sticker-picker.virtual-row.virtual-row-renderer-base.sticker-grid"
|
||||||
>
|
>
|
||||||
@@ -286,6 +291,7 @@ interface VirtualRowWrapperProps {
|
|||||||
handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void;
|
handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void;
|
||||||
handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void;
|
handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void;
|
||||||
gridColumns?: number;
|
gridColumns?: number;
|
||||||
|
cellTrackWidth?: number;
|
||||||
selectedRow: number;
|
selectedRow: number;
|
||||||
selectedColumn: number;
|
selectedColumn: number;
|
||||||
stickerRowIndex: number;
|
stickerRowIndex: number;
|
||||||
@@ -300,6 +306,7 @@ export const VirtualRowWrapper: React.FC<VirtualRowWrapperProps> = observer(
|
|||||||
handleHover,
|
handleHover,
|
||||||
handleSelect,
|
handleSelect,
|
||||||
gridColumns,
|
gridColumns,
|
||||||
|
cellTrackWidth,
|
||||||
selectedRow,
|
selectedRow,
|
||||||
selectedColumn,
|
selectedColumn,
|
||||||
stickerRowIndex,
|
stickerRowIndex,
|
||||||
@@ -313,6 +320,7 @@ export const VirtualRowWrapper: React.FC<VirtualRowWrapperProps> = observer(
|
|||||||
handleHover={handleHover}
|
handleHover={handleHover}
|
||||||
handleSelect={handleSelect}
|
handleSelect={handleSelect}
|
||||||
gridColumns={gridColumns}
|
gridColumns={gridColumns}
|
||||||
|
cellTrackWidth={cellTrackWidth}
|
||||||
selectedRow={selectedRow}
|
selectedRow={selectedRow}
|
||||||
selectedColumn={selectedColumn}
|
selectedColumn={selectedColumn}
|
||||||
stickerRowIndex={stickerRowIndex}
|
stickerRowIndex={stickerRowIndex}
|
||||||
|
|||||||
Reference in new issue
Block a user