mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-10 14:22:46 +02:00
optimize various base components
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
import { Fragment, memo, ReactNode, useState } from 'react';
|
||||
import { Fragment, memo, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { generatePath, Link } from 'react-router';
|
||||
|
||||
import styles from './item-card.module.css';
|
||||
@@ -84,7 +84,7 @@ export const ItemCard = ({
|
||||
switch (type) {
|
||||
case 'compact':
|
||||
return (
|
||||
<CompactItemCard
|
||||
<MemoizedCompactItemCard
|
||||
controls={controls}
|
||||
data={data}
|
||||
enableDrag={enableDrag}
|
||||
@@ -101,7 +101,7 @@ export const ItemCard = ({
|
||||
);
|
||||
case 'poster':
|
||||
return (
|
||||
<PosterItemCard
|
||||
<MemoizedPosterItemCard
|
||||
controls={controls}
|
||||
data={data}
|
||||
enableDrag={enableDrag}
|
||||
@@ -119,7 +119,7 @@ export const ItemCard = ({
|
||||
case 'default':
|
||||
default:
|
||||
return (
|
||||
<DefaultItemCard
|
||||
<MemoizedDefaultItemCard
|
||||
controls={controls}
|
||||
data={data}
|
||||
enableDrag={enableDrag}
|
||||
@@ -167,46 +167,64 @@ const CompactItemCard = ({
|
||||
: undefined;
|
||||
const isSelected = useItemSelectionState(internalState, itemRowId || undefined);
|
||||
|
||||
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
|
||||
drag: {
|
||||
getId: () => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const getId = useCallback(() => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems.map((item) => item.id);
|
||||
},
|
||||
getItem: () => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems.map((item) => item.id);
|
||||
}, [data, internalState]);
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems;
|
||||
},
|
||||
const getItem = useCallback(() => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems;
|
||||
}, [data, internalState]);
|
||||
|
||||
const onDragStart = useCallback(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
if (internalState) {
|
||||
internalState.setDragging(draggedItems);
|
||||
}
|
||||
}, [data, internalState]);
|
||||
|
||||
const onDrop = useCallback(() => {
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
}, [internalState]);
|
||||
|
||||
const dragOperation = useMemo(
|
||||
() =>
|
||||
itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
[itemType],
|
||||
);
|
||||
|
||||
const drag = useMemo(
|
||||
() => ({
|
||||
getId,
|
||||
getItem,
|
||||
itemType,
|
||||
onDragStart: () => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
if (internalState) {
|
||||
internalState.setDragging(draggedItems);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
},
|
||||
operation:
|
||||
itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
onDragStart,
|
||||
onDrop,
|
||||
operation: dragOperation,
|
||||
target: DragTarget.ALBUM,
|
||||
},
|
||||
}),
|
||||
[getId, getItem, itemType, onDragStart, onDrop, dragOperation],
|
||||
);
|
||||
|
||||
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
|
||||
drag,
|
||||
isEnabled: !!enableDrag && !!data,
|
||||
});
|
||||
|
||||
@@ -649,46 +667,64 @@ const PosterItemCard = ({
|
||||
: undefined;
|
||||
const isSelected = useItemSelectionState(internalState, itemRowId || undefined);
|
||||
|
||||
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
|
||||
drag: {
|
||||
getId: () => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const getId = useCallback(() => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems.map((item) => item.id);
|
||||
},
|
||||
getItem: () => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems.map((item) => item.id);
|
||||
}, [data, internalState]);
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems;
|
||||
},
|
||||
const getItem = useCallback(() => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
return draggedItems;
|
||||
}, [data, internalState]);
|
||||
|
||||
const onDragStart = useCallback(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
if (internalState) {
|
||||
internalState.setDragging(draggedItems);
|
||||
}
|
||||
}, [data, internalState]);
|
||||
|
||||
const onDrop = useCallback(() => {
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
}, [internalState]);
|
||||
|
||||
const dragOperation = useMemo(
|
||||
() =>
|
||||
itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
[itemType],
|
||||
);
|
||||
|
||||
const drag = useMemo(
|
||||
() => ({
|
||||
getId,
|
||||
getItem,
|
||||
itemType,
|
||||
onDragStart: () => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draggedItems = getDraggedItems(data, internalState);
|
||||
if (internalState) {
|
||||
internalState.setDragging(draggedItems);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
if (internalState) {
|
||||
internalState.setDragging([]);
|
||||
}
|
||||
},
|
||||
operation:
|
||||
itemType === LibraryItem.QUEUE_SONG
|
||||
? [DragOperation.REORDER, DragOperation.ADD]
|
||||
: [DragOperation.ADD],
|
||||
onDragStart,
|
||||
onDrop,
|
||||
operation: dragOperation,
|
||||
target: DragTarget.ALBUM,
|
||||
},
|
||||
}),
|
||||
[getId, getItem, itemType, onDragStart, onDrop, dragOperation],
|
||||
);
|
||||
|
||||
const { isDragging: isDraggingLocal, ref } = useDragDrop<HTMLDivElement>({
|
||||
drag,
|
||||
isEnabled: !!enableDrag && !!data,
|
||||
});
|
||||
|
||||
@@ -896,6 +932,15 @@ const PosterItemCard = ({
|
||||
);
|
||||
};
|
||||
|
||||
const MemoizedPosterItemCard = memo(PosterItemCard);
|
||||
MemoizedPosterItemCard.displayName = 'MemoizedPosterItemCard';
|
||||
|
||||
const MemoizedCompactItemCard = memo(CompactItemCard);
|
||||
MemoizedCompactItemCard.displayName = 'MemoizedCompactItemCard';
|
||||
|
||||
const MemoizedDefaultItemCard = memo(DefaultItemCard);
|
||||
MemoizedDefaultItemCard.displayName = 'MemoizedDefaultItemCard';
|
||||
|
||||
export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[] => {
|
||||
return [
|
||||
{
|
||||
@@ -1160,56 +1205,67 @@ const getItemNavigationPath = (
|
||||
return getTitlePath(effectiveItemType, data.id);
|
||||
};
|
||||
|
||||
const ItemCardRow = ({
|
||||
data,
|
||||
index,
|
||||
row,
|
||||
type,
|
||||
}: {
|
||||
data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
|
||||
index: number;
|
||||
row: DataRow;
|
||||
type?: 'compact' | 'default' | 'poster';
|
||||
}) => {
|
||||
const alignmentClass =
|
||||
row.align === 'center'
|
||||
? styles['align-center']
|
||||
: row.align === 'end'
|
||||
? styles['align-end']
|
||||
: styles['align-start'];
|
||||
const ItemCardRow = memo(
|
||||
({
|
||||
data,
|
||||
index,
|
||||
row,
|
||||
type,
|
||||
}: {
|
||||
data: Album | AlbumArtist | Artist | Playlist | Song | undefined;
|
||||
index: number;
|
||||
row: DataRow;
|
||||
type?: 'compact' | 'default' | 'poster';
|
||||
}) => {
|
||||
const alignmentClass =
|
||||
row.align === 'center'
|
||||
? styles['align-center']
|
||||
: row.align === 'end'
|
||||
? styles['align-end']
|
||||
: styles['align-start'];
|
||||
|
||||
// All rows except the first one (index 0) should be muted
|
||||
const isMuted = index > 0 || row.isMuted;
|
||||
// All rows except the first one (index 0) should be muted
|
||||
const isMuted = index > 0 || row.isMuted;
|
||||
|
||||
const formattedContent = useMemo(() => {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
return row.format(data);
|
||||
}, [data, row]);
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.row, alignmentClass, {
|
||||
[styles.compact]: type === 'compact',
|
||||
[styles.default]: type === 'default',
|
||||
[styles.muted]: isMuted,
|
||||
[styles.poster]: type === 'poster',
|
||||
})}
|
||||
>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div
|
||||
<Text
|
||||
className={clsx(styles.row, alignmentClass, {
|
||||
[styles.bold]: index === 0,
|
||||
[styles.compact]: type === 'compact',
|
||||
[styles.default]: type === 'default',
|
||||
[styles.muted]: isMuted,
|
||||
[styles.poster]: type === 'poster',
|
||||
})}
|
||||
size={index > 0 ? 'sm' : 'md'}
|
||||
>
|
||||
|
||||
</div>
|
||||
{formattedContent}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<Text
|
||||
className={clsx(styles.row, alignmentClass, {
|
||||
[styles.bold]: index === 0,
|
||||
[styles.compact]: type === 'compact',
|
||||
[styles.default]: type === 'default',
|
||||
[styles.muted]: isMuted,
|
||||
[styles.poster]: type === 'poster',
|
||||
})}
|
||||
size={index > 0 ? 'sm' : 'md'}
|
||||
>
|
||||
{row.format(data)}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
ItemCardRow.displayName = 'ItemCardRow';
|
||||
|
||||
export const MemoizedItemCard = memo(ItemCard);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { QueryBuilderOption } from '/@/renderer/components/query-builder/query-builder-option';
|
||||
@@ -107,15 +108,17 @@ export const QueryBuilder = ({
|
||||
onChangeType({ groupIndex, level, value });
|
||||
};
|
||||
|
||||
const boxStyle = useMemo(
|
||||
() => ({
|
||||
border: '1px solid var(--theme-colors-border)',
|
||||
borderRadius: 'var(--theme-radius-md)',
|
||||
marginLeft: level > 0 ? '20px' : '0px',
|
||||
}),
|
||||
[level],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
p="md"
|
||||
style={{
|
||||
border: '1px solid var(--theme-colors-border)',
|
||||
borderRadius: 'var(--theme-radius-md)',
|
||||
marginLeft: level > 0 ? '20px' : '0px',
|
||||
}}
|
||||
>
|
||||
<Box p="md" style={boxStyle}>
|
||||
<Stack gap="sm">
|
||||
<Group gap="sm" justify="space-between" wrap="nowrap">
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
|
||||
@@ -67,16 +67,13 @@ export const MultiSelectWithInvalidData = ({ data, defaultValue, ...props }: Mul
|
||||
return [data, []];
|
||||
}, [data, defaultValue]);
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
data={fullData}
|
||||
defaultValue={defaultValue}
|
||||
error={
|
||||
missing.length
|
||||
? t('error.badValue', { postProcess: 'sentenceCase', value: missing })
|
||||
: undefined
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
const error = useMemo(
|
||||
() =>
|
||||
missing.length
|
||||
? t('error.badValue', { postProcess: 'sentenceCase', value: missing })
|
||||
: undefined,
|
||||
[missing, t],
|
||||
);
|
||||
|
||||
return <MultiSelect data={fullData} defaultValue={defaultValue} error={error} {...props} />;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user