Last active: 2 years ago
import pubWorldConfig from '@/libs/pubWorldConfig';
import { NodeInfo, ResUtils, TMat } from '@/show/core';
import { Collapse, Empty, Segmented } from 'antd';
import { SegmentedValue } from 'antd/lib/segmented';
import { lazy, memo, Suspense, useMemo, useState } from 'react';
import Scrollbars from 'react-custom-scrollbars-2';
import * as S from './index.styles';
const { Panel } = Collapse;
const ListItem = lazy(() => import('./ListItem'));
const embedSegment = pubWorldConfig.embedTabs;
const segmentKeys = ['model', 'preset', 'shape', 'text'];
// just hack it. key is one of segmentKeys.
const checkKeys = (key: string): key is 'model' => segmentKeys.includes(key);
type Props = {
embeds: NodeInfo[];
onClickUpdate: (id: string) => void;
onClickRemove: (
e: React.MouseEvent<HTMLSpanElement, MouseEvent>,
id: string
) => void;
};
export type ListNode = {
type: 'video' | 'image' | NodeInfo['type'] | string;
} & Omit<NodeInfo, 'type'>;
const filterType = (type: 'model' | 'text') => (item: NodeInfo) =>
item.type === type;
/**
* 通过后缀名检查是否为视频节点
* @param item
* @returns
*/
const filterVideo = (item: NodeInfo) =>
item.content?.content &&
ResUtils.getMatType(item.content?.content) === TMat.VIDEO;
/**
* 通过后缀名检查是否为图片节点
* @param item
* @returns
*/
const filterImage = (item: NodeInfo) =>
item.content?.content &&
ResUtils.getMatType(item.content?.content) === TMat.IMAGE;
const filterEmpty = (item: NodeInfo) => !item.content?.content;
const generateList = (list: NodeInfo[]) =>
list
.filter(filterVideo)
.map(changeType('video'))
.reverse()
.concat(list.filter(filterImage).map(changeType('image')).reverse())
.concat(list.filter(filterEmpty).reverse());
const changeType =
(type: 'video' | 'image') =>
(item: NodeInfo): ListNode => ({
...item,
type,
});
/**
* 当折叠面板打开时,再处理状态
* @param key
* @returns
*/
const isRenderList = (key: string) => key === '1';
const EmbedsList = ({ embeds, onClickUpdate, onClickRemove }: Props) => {
// 折叠面板
const [key, setKey] = useState('');
const collapseChange = (key: string | string[]) => {
Array.isArray(key) ? setKey(key[0]) : setKey(key);
};
const preset = useMemo(
() =>
isRenderList(key) ? embeds.filter((item) => item.type === 'preset') : [],
[embeds, key]
);
const shape = useMemo(
() =>
isRenderList(key) ? embeds.filter((item) => item.type === 'shape') : [],
[embeds, key]
);
const list: {
model: ListNode[];
preset: ListNode[];
shape: ListNode[];
text: ListNode[];
} = useMemo(
() =>
isRenderList(key)
? {
model: embeds.filter(filterType('model')).reverse(),
preset: generateList(preset),
shape: generateList(shape),
text: embeds.filter(filterType('text')).reverse(),
}
: {
model: [],
preset: [],
shape: [],
text: [],
},
[embeds, preset, shape, key]
);
// 选择器
const [current, setCurrent] = useState<'model' | 'preset' | 'shape' | 'text'>(
'preset'
);
const handleChange = (v: SegmentedValue) => {
const key = v.toString();
if (!checkKeys(key)) return;
setCurrent(key);
};
return (
<>
<S.ListWrapper>
<Collapse
bordered={false}
style={{
userSelect: 'none',
}}
onChange={collapseChange}
>
<Panel header="内容列表" key="1">
<Segmented
block
options={embedSegment}
value={current}
onChange={handleChange}
style={{
userSelect: 'none',
}}
/>
<Scrollbars style={{ height: 280 }}>
<S.Content>
{!!list[current].length ? (
<Suspense fallback>
{list[current].map((item) => (
<ListItem
key={item.id}
item={item}
onClickRemove={onClickRemove}
onClickUpdate={onClickUpdate}
/>
))}
</Suspense>
) : (
<Empty />
)}
</S.Content>
</Scrollbars>
</Panel>
</Collapse>
</S.ListWrapper>
</>
);
};
export default memo(EmbedsList);