Peng Xiao
2024-02-22 09:37:55 +00:00
parent bb8e601f82
commit 372b4da884
19 changed files with 574 additions and 114 deletions

View File

@@ -0,0 +1,58 @@
import { ConfirmModal } from '@affine/component';
import { WorkspacePropertiesAdapter } from '@affine/core/modules/workspace';
import type { PageInfoCustomPropertyMeta } from '@affine/core/modules/workspace/properties/schema';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useService } from '@toeverything/infra/di';
import { useMemo } from 'react';
import { PagePropertiesMetaManager } from './page-properties-manager';
export const ConfirmDeletePropertyModal = ({
onConfirm,
onCancel,
property,
show,
}: {
property: PageInfoCustomPropertyMeta;
show: boolean;
onConfirm: () => void;
onCancel: () => void;
}) => {
const t = useAFFiNEI18N();
const adapter = useService(WorkspacePropertiesAdapter);
const count = useMemo(() => {
const manager = new PagePropertiesMetaManager(adapter);
return manager.getPropertyRelatedPages(property.id)?.size || 0;
}, [adapter, property.id]);
return (
<ConfirmModal
open={show}
closeButtonOptions={{
onClick: onCancel,
}}
title={t['com.affine.settings.workspace.properties.delete-property']()}
description={
<Trans
values={{
name: property.name,
count,
}}
i18nKey="com.affine.settings.workspace.properties.delete-property-prompt"
>
The <strong>{{ name: property.name } as any}</strong> property will be
removed from count doc(s). This action cannot be undone.
</Trans>
}
onConfirm={onConfirm}
cancelButtonOptions={{
onClick: onCancel,
}}
confirmButtonOptions={{
type: 'error',
children: t['Confirm'](),
}}
/>
);
};

View File

@@ -34,7 +34,7 @@ export const IconsSelectorPanel = ({
const t = useAFFiNEI18N();
return (
<Scrollable.Root>
<div className={styles.menuHeader}>
<div role="heading" className={styles.menuHeader}>
{t['com.affine.page-properties.icons']()}
</div>
<Scrollable.Viewport className={styles.iconsContainerScrollable}>

View File

@@ -90,7 +90,9 @@ export const EditPropertyNameMenuItem = ({
const iconName = getSafeIconName(property.icon, property.type);
const onKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(
e => {
e.stopPropagation();
if (e.key !== 'Escape') {
e.stopPropagation();
}
if (e.key === 'Enter') {
e.preventDefault();
onBlur(e.currentTarget.value);

View File

@@ -132,6 +132,10 @@ export class PagePropertiesMetaManager {
}
return mapping;
}
getPropertyRelatedPages(id: string) {
return this.getPropertyStatistics().get(id);
}
}
export class PagePropertiesManager {

View File

@@ -212,7 +212,7 @@ export const propertyRowCell = style({
position: 'relative',
borderRadius: 4,
fontSize: cssVar('fontSm'),
lineHeight: '20px',
lineHeight: '22px',
userSelect: 'none',
':focus-visible': {
outline: 'none',

View File

@@ -60,6 +60,7 @@ import {
import { AffinePageReference } from '../reference-link';
import { managerContext, pageInfoCollapsedAtom } from './common';
import { ConfirmDeletePropertyModal } from './confirm-delete-property-modal';
import {
getDefaultIconName,
nameToIcon,
@@ -281,7 +282,11 @@ const VisibilityModeSelector = ({
},
}}
>
<div data-required={required} className={styles.selectorButton}>
<div
role="button"
data-required={required}
className={styles.selectorButton}
>
{required ? (
t['com.affine.page-properties.property.required']()
) : (
@@ -305,7 +310,11 @@ export const PagePropertiesSettingsPopup = ({
const menuItems = useMemo(() => {
const options: MenuItemOption[] = [];
options.push(
<div className={styles.menuHeader} style={{ minWidth: 320 }}>
<div
role="heading"
className={styles.menuHeader}
style={{ minWidth: 320 }}
>
{t['com.affine.page-properties.settings.title']()}
</div>
);
@@ -327,7 +336,12 @@ export const PagePropertiesSettingsPopup = ({
<MenuIcon>
<Icon />
</MenuIcon>
<div className={styles.propertyRowName}>{name}</div>
<div
data-testid="page-property-setting-row-name"
className={styles.propertyRowName}
>
{name}
</div>
<VisibilityModeSelector property={property} />
</SortablePropertyRow>
);
@@ -407,6 +421,8 @@ export const PagePropertyRowNameMenu = ({
const [localProperty, setLocalProperty] = useState(() => ({ ...property }));
const nextVisibility = rotateVisibility(localProperty.visibility);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const handleFinishEditing = useCallback(() => {
onFinishEditing();
manager.updateCustomPropertyMeta(meta.id, localPropertyMeta);
@@ -445,14 +461,9 @@ export const PagePropertyRowNameMenu = ({
},
[nextVisibility]
);
const handleDelete = useCallback(
(e: MouseEvent) => {
e.stopPropagation();
e.preventDefault();
manager.removeCustomProperty(property.id);
},
[manager, property.id]
);
const handleDelete = useCallback(() => {
manager.removeCustomProperty(property.id);
}, [manager, property.id]);
const handleIconChange = useCallback(
(icon: PagePropertyIcon) => {
@@ -496,12 +507,11 @@ export const PagePropertyRowNameMenu = ({
type: 'danger',
icon: <DeleteIcon />,
text: t['com.affine.page-properties.property.remove-property'](),
onClick: handleDelete,
onClick: () => setShowDeleteModal(true),
});
}
return renderMenuItemOptions(options);
}, [
handleDelete,
handleIconChange,
handleNameBlur,
handleNameChange,
@@ -513,20 +523,36 @@ export const PagePropertyRowNameMenu = ({
]);
return (
<Menu
rootOptions={{
open: editing,
}}
contentOptions={{
onInteractOutside: handleFinishEditing,
onClick(e) {
e.stopPropagation();
},
}}
items={menuItems}
>
{children}
</Menu>
<>
<Menu
rootOptions={{
open: editing,
}}
contentOptions={{
onInteractOutside: handleFinishEditing,
onClick(e) {
e.stopPropagation();
},
onKeyDown(e) {
if (e.key === 'Escape') {
handleFinishEditing();
}
},
}}
items={menuItems}
>
{children}
</Menu>
<ConfirmDeletePropertyModal
onConfirm={() => {
setShowDeleteModal(false);
handleDelete();
}}
onCancel={() => setShowDeleteModal(false)}
show={showDeleteModal}
property={meta}
/>
</>
);
};
@@ -607,7 +633,11 @@ export const PagePropertiesTableHeader = ({
</div>
{properties.length === 0 || manager.readonly ? null : (
<PagePropertiesSettingsPopup>
<IconButton type="plain" icon={<MoreHorizontalIcon />} />
<IconButton
data-testid="page-info-show-more"
type="plain"
icon={<MoreHorizontalIcon />}
/>
</PagePropertiesSettingsPopup>
)}
<div className={styles.spacer} />
@@ -802,7 +832,7 @@ export const PagePropertiesCreatePropertyMenuItems = ({
return useMemo(() => {
const options: MenuItemOption[] = [];
options.push(
<div className={styles.menuHeader}>
<div role="heading" className={styles.menuHeader}>
{t['com.affine.page-properties.create-property.menu.header']()}
</div>
);
@@ -864,7 +894,7 @@ const PagePropertiesAddPropertyMenuItems = ({
const menuItems = useMemo(() => {
const options: MenuItemOption[] = [];
options.push(
<div className={styles.menuHeader}>
<div role="heading" className={styles.menuHeader}>
{t['com.affine.page-properties.add-property.menu.header']()}
</div>
);

View File

@@ -47,7 +47,7 @@ const InlineTagsList = ({
children,
}: PropsWithChildren<InlineTagsListProps>) => {
return (
<div className={styles.inlineTagsContainer}>
<div className={styles.inlineTagsContainer} data-testid="inline-tags-list">
{value.map((tagId, idx) => {
const tag = options.find(t => t.id === tagId);
if (!tag) {
@@ -251,7 +251,7 @@ export const TagsEditor = ({
);
return (
<div className={styles.tagsEditorRoot}>
<div data-testid="tags-editor-popup" className={styles.tagsEditorRoot}>
<div className={styles.tagsEditorSelectedTags}>
<InlineTagsList
options={options}
@@ -282,6 +282,9 @@ export const TagsEditor = ({
<div
key={tag.id}
className={styles.tagSelectorItem}
data-testid="tag-selector-item"
data-tag-id={tag.id}
data-tag-value={tag.value}
onClick={() => {
onAddTag(tag.id);
}}
@@ -296,6 +299,7 @@ export const TagsEditor = ({
})}
{exactMatch || !inputValue ? null : (
<div
data-testid="tag-selector-item"
className={styles.tagSelectorItem}
onClick={() => {
setInputValue('');

View File

@@ -269,6 +269,7 @@ const WorkspaceListItem = ({
.map(({ key, title }) => {
return (
<div
data-testid={`workspace-list-item-${key}`}
onClick={() => {
onClick(key);
}}

View File

@@ -1,4 +1,4 @@
import { Button, ConfirmModal, IconButton, Menu } from '@affine/component';
import { Button, IconButton, Menu } from '@affine/component';
import { SettingHeader } from '@affine/component/setting-components';
import { useWorkspacePropertiesAdapter } from '@affine/core/hooks/use-affine-adapter';
import { useWorkspace } from '@affine/core/hooks/use-workspace';
@@ -28,6 +28,7 @@ import {
PagePropertiesMetaManager,
type PagePropertyIcon,
} from '../../../page-properties';
import { ConfirmDeletePropertyModal } from '../../../page-properties/confirm-delete-property-modal';
import {
EditPropertyNameMenuItem,
type MenuItemOption,
@@ -54,57 +55,9 @@ const Divider = () => {
return <div className={styles.divider} />;
};
const ConfirmDeletePropertyModal = ({
onConfirm,
onCancel,
property,
count,
show,
}: {
property: PageInfoCustomPropertyMeta;
count: number;
show: boolean;
onConfirm: () => void;
onCancel: () => void;
}) => {
const t = useAFFiNEI18N();
return (
<ConfirmModal
open={show}
closeButtonOptions={{
onClick: onCancel,
}}
title={t['com.affine.settings.workspace.properties.delete-property']()}
description={
<Trans
values={{
name: property.name,
count,
}}
i18nKey="com.affine.settings.workspace.properties.delete-property-prompt"
>
The <strong>{{ name: property.name } as any}</strong> property will be
removed from count doc(s). This action cannot be undone.
</Trans>
}
onConfirm={onConfirm}
cancelButtonOptions={{
onClick: onCancel,
}}
confirmButtonOptions={{
type: 'error',
children: t['Confirm'](),
}}
/>
);
};
const EditPropertyButton = ({
property,
count,
}: {
count: number;
property: PageInfoCustomPropertyMeta;
}) => {
const t = useAFFiNEI18N();
@@ -234,7 +187,6 @@ const EditPropertyButton = ({
onCancel={() => setShowDeleteModal(false)}
show={showDeleteModal}
property={property}
count={count}
/>
</>
);
@@ -281,7 +233,7 @@ const CustomPropertyRow = ({
{t['com.affine.page-properties.property.required']()}
</div>
) : null}
<EditPropertyButton property={property} count={relatedPages.length} />
<EditPropertyButton property={property} />
</div>
);
};

View File

@@ -44,6 +44,8 @@ export const TagItem = ({
data-testid="page-tag"
className={styles.tag}
data-idx={idx}
data-tag-id={tag.id}
data-tag-value={tag.value}
title={tag.value}
style={style}
>
@@ -59,7 +61,11 @@ export const TagItem = ({
/>
<div className={styles.tagLabel}>{tag.value}</div>
{onRemoved ? (
<div className={styles.tagRemove} onClick={handleRemove}>
<div
data-testid="remove-tag-button"
className={styles.tagRemove}
onClick={handleRemove}
>
<CloseIcon />
</div>
) : null}