feat(core): reuse select-page for edit collection pages-mode (#7548)

This commit is contained in:
CatsJuice
2024-07-19 04:47:47 +00:00
parent 544236f1a0
commit 239de4c283
3 changed files with 55 additions and 206 deletions

View File

@@ -8,8 +8,8 @@ import type { ReactNode } from 'react';
import { useCallback, useMemo, useState } from 'react';
import * as styles from './edit-collection.css';
import { PagesMode } from './pages-mode';
import { RulesMode } from './rules-mode';
import { SelectPage } from './select-page';
export type EditCollectionMode = 'page' | 'rule';
@@ -110,6 +110,12 @@ export const EditCollection = ({
allowList: init.allowList,
});
}, [init.allowList, init.filterList, value]);
const onIdsChange = useCallback(
(ids: string[]) => {
onChange({ ...value, allowList: ids });
},
[value]
);
const buttons = useMemo(
() => (
<>
@@ -164,13 +170,13 @@ export const EditCollection = ({
className={styles.collectionEditContainer}
>
{mode === 'page' ? (
<PagesMode
collection={value}
updateCollection={onChange}
switchMode={switchMode}
buttons={buttons}
<SelectPage
allPageListConfig={config}
></PagesMode>
init={value.allowList}
onChange={onIdsChange}
header={switchMode}
buttons={buttons}
/>
) : (
<RulesMode
allPageListConfig={config}

View File

@@ -1,178 +0,0 @@
import { Menu } from '@affine/component';
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
import type { Collection } from '@affine/env/filter';
import { useI18n } from '@affine/i18n';
import { FilterIcon } from '@blocksuite/icons/rc';
import type { DocMeta } from '@blocksuite/store';
import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx';
import type { ReactNode } from 'react';
import { useCallback } from 'react';
import { FilterList } from '../../filter/filter-list';
import { VariableSelect } from '../../filter/vars';
import { usePageHeaderColsDef } from '../../header-col-def';
import { PageListItemRenderer } from '../../page-group';
import { ListTableHeader } from '../../page-header';
import type { ListItem } from '../../types';
import { VirtualizedList } from '../../virtualized-list';
import type { AllPageListConfig } from './edit-collection';
import * as styles from './edit-collection.css';
import { EmptyList } from './select-page';
import { useFilter } from './use-filter';
import { useSearch } from './use-search';
export const PagesMode = ({
switchMode,
collection,
updateCollection,
buttons,
allPageListConfig,
}: {
collection: Collection;
updateCollection: (collection: Collection) => void;
buttons: ReactNode;
switchMode: ReactNode;
allPageListConfig: AllPageListConfig;
}) => {
const t = useI18n();
const favAdapter = useService(FavoriteItemsAdapter);
const favorites = useLiveData(favAdapter.favorites$);
const {
showFilter,
filters,
updateFilters,
clickFilter,
createFilter,
filteredList,
} = useFilter(
allPageListConfig.allPages.map(meta => ({
meta,
publicMode: allPageListConfig.getPublicMode(meta.id),
favorite: favorites.some(f => f.id === meta.id),
}))
);
const pageHeaderColsDef = usePageHeaderColsDef();
const { searchText, updateSearchText, searchedList } =
useSearch(filteredList);
const clearSelected = useCallback(() => {
updateCollection({
...collection,
allowList: [],
});
}, [collection, updateCollection]);
const pageOperationsRenderer = useCallback(
(item: ListItem) => {
const page = item as DocMeta;
return allPageListConfig.favoriteRender(page);
},
[allPageListConfig]
);
const pageItemRenderer = useCallback((item: ListItem) => {
return <PageListItemRenderer {...item} />;
}, []);
const pageHeaderRenderer = useCallback(() => {
return <ListTableHeader headerCols={pageHeaderColsDef} />;
}, [pageHeaderColsDef]);
return (
<>
<input
value={searchText}
onChange={e => updateSearchText(e.target.value)}
className={styles.rulesTitle}
style={{
color: 'var(--affine-text-primary-color)',
}}
placeholder={t['com.affine.editCollection.search.placeholder']()}
></input>
<div className={styles.pagesList}>
<div className={styles.pagesTab}>
<div className={styles.pagesTabContent}>
{switchMode}
{!showFilter && filters.length === 0 ? (
<Menu
items={
<VariableSelect
propertiesMeta={
allPageListConfig.docCollection.meta.properties
}
selected={filters}
onSelect={createFilter}
/>
}
>
<div>
<FilterIcon
className={clsx(styles.icon, styles.button)}
onClick={clickFilter}
width={24}
height={24}
></FilterIcon>
</div>
</Menu>
) : (
<FilterIcon
className={clsx(styles.icon, styles.button)}
onClick={clickFilter}
width={24}
height={24}
></FilterIcon>
)}
</div>
{showFilter ? (
<div style={{ padding: '12px 16px 16px' }}>
<FilterList
propertiesMeta={allPageListConfig.docCollection.meta.properties}
value={filters}
onChange={updateFilters}
/>
</div>
) : null}
{searchedList.length ? (
<VirtualizedList
className={styles.pageList}
items={searchedList}
docCollection={allPageListConfig.docCollection}
selectable
onSelectedIdsChange={ids => {
updateCollection({
...collection,
allowList: ids,
});
}}
itemRenderer={pageItemRenderer}
operationsRenderer={pageOperationsRenderer}
headerRenderer={pageHeaderRenderer}
selectedIds={collection.allowList}
isPreferredEdgeless={allPageListConfig.isEdgeless}
/>
) : (
<EmptyList search={searchText} />
)}
</div>
</div>
<div className={styles.pagesBottom}>
<div className={styles.pagesBottomLeft}>
<div className={styles.selectedCountTips}>
{t['com.affine.selectPage.selected']()}
<span
style={{ marginLeft: 7 }}
className={styles.previewCountTipsHighlight}
>
{collection.allowList.length}
</span>
</div>
<div
className={clsx(styles.button, styles.bottomButton)}
style={{ fontSize: 12, lineHeight: '20px' }}
onClick={clearSelected}
>
{t['com.affine.editCollection.pages.clear']()}
</div>
</div>
<div>{buttons}</div>
</div>
</>
);
};

View File

@@ -5,7 +5,7 @@ import { FilterIcon } from '@blocksuite/icons/rc';
import type { DocMeta } from '@blocksuite/store';
import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx';
import { useCallback, useState } from 'react';
import { type ReactNode, useCallback, useState } from 'react';
import { FilterList } from '../../filter';
import { VariableSelect } from '../../filter/vars';
@@ -25,20 +25,35 @@ export const SelectPage = ({
init,
onConfirm,
onCancel,
onChange: propsOnChange,
confirmText,
header,
buttons,
}: {
allPageListConfig: AllPageListConfig;
init: string[];
onConfirm: (pageIds: string[]) => void;
onCancel: () => void;
onConfirm?: (pageIds: string[]) => void;
onCancel?: () => void;
onChange?: (values: string[]) => void;
confirmText?: ReactNode;
header?: ReactNode;
buttons?: ReactNode;
}) => {
const t = useI18n();
const [value, onChange] = useState(init);
const [value, setValue] = useState(init);
const onChange = useCallback(
(value: string[]) => {
propsOnChange?.(value);
setValue(value);
},
[propsOnChange]
);
const confirm = useCallback(() => {
onConfirm(value);
onConfirm?.(value);
}, [value, onConfirm]);
const clearSelected = useCallback(() => {
onChange([]);
}, []);
}, [onChange]);
const favAdapter = useService(FavoriteItemsAdapter);
const favourites = useLiveData(favAdapter.favorites$);
const pageHeaderColsDef = usePageHeaderColsDef();
@@ -85,9 +100,11 @@ export const SelectPage = ({
></input>
<div className={styles.pagesTab}>
<div className={styles.pagesTabContent}>
<div style={{ fontSize: 12, lineHeight: '20px', fontWeight: 600 }}>
{t['com.affine.selectPage.title']()}
</div>
{header ?? (
<div style={{ fontSize: 12, lineHeight: '20px', fontWeight: 600 }}>
{t['com.affine.selectPage.title']()}
</div>
)}
{!showFilter && filters.length === 0 ? (
<Menu
items={
@@ -164,18 +181,22 @@ export const SelectPage = ({
</div>
</div>
<div>
<Button size="large" onClick={onCancel}>
{t['com.affine.editCollection.button.cancel']()}
</Button>
<Button
className={styles.confirmButton}
size="large"
data-testid="save-collection"
type="primary"
onClick={confirm}
>
{t['Confirm']()}
</Button>
{buttons ?? (
<>
<Button size="large" onClick={onCancel}>
{t['com.affine.editCollection.button.cancel']()}
</Button>
<Button
className={styles.confirmButton}
size="large"
data-testid="save-collection"
type="primary"
onClick={confirm}
>
{confirmText ?? t['Confirm']()}
</Button>
</>
)}
</div>
</div>
</div>