mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
fix(core): confirm the tag name before creating a new tag (#11724)
close AF-1569 - Show rename modal below the "Add Tag" button instead of at the new tag node - Tag is created only after the user confirms the name in the modal - Improves sidebar tag creation flow and user experience 
This commit is contained in:
@@ -28,10 +28,8 @@ export const ExplorerTagNode = ({
|
||||
operations: additionalOperations,
|
||||
dropEffect,
|
||||
canDrop,
|
||||
defaultRenaming,
|
||||
}: {
|
||||
tagId: string;
|
||||
defaultRenaming?: boolean;
|
||||
} & GenericExplorerNode) => {
|
||||
const t = useI18n();
|
||||
const { tagService, globalContextService } = useServices({
|
||||
@@ -179,7 +177,6 @@ export const ExplorerTagNode = ({
|
||||
setCollapsed={setCollapsed}
|
||||
to={`/tag/${tagId}`}
|
||||
active={active}
|
||||
defaultRenaming={defaultRenaming}
|
||||
reorderable={reorderable}
|
||||
onRename={handleRename}
|
||||
canDrop={handleCanDrop}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
||||
import type { Tag } from '@affine/core/modules/tag';
|
||||
import { TagService } from '@affine/core/modules/tag';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
@@ -11,6 +10,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { ExplorerService } from '../../../services/explorer';
|
||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||
import { ExplorerTagNode } from '../../nodes/tag';
|
||||
import { ExplorerTreeNodeRenameModal as CreateTagModal } from '../../tree/node';
|
||||
import { RootEmpty } from './empty';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
@@ -21,25 +21,28 @@ export const ExplorerTags = () => {
|
||||
});
|
||||
const explorerSection = explorerService.sections.tags;
|
||||
const collapsed = useLiveData(explorerSection.collapsed$);
|
||||
const [createdTag, setCreatedTag] = useState<Tag | null>(null);
|
||||
const [creating, setCreating] = useState(false);
|
||||
const tags = useLiveData(tagService.tagList.tags$);
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const handleCreateNewFavoriteDoc = useCallback(() => {
|
||||
const newTags = tagService.tagList.createTag(
|
||||
t['com.affine.rootAppSidebar.tags.new-tag'](),
|
||||
tagService.randomTagColor()
|
||||
);
|
||||
setCreatedTag(newTags);
|
||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
||||
explorerSection.setCollapsed(false);
|
||||
}, [explorerSection, t, tagService]);
|
||||
const handleCreateNewTag = useCallback(
|
||||
(name: string) => {
|
||||
tagService.tagList.createTag(name, tagService.randomTagColor());
|
||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
||||
explorerSection.setCollapsed(false);
|
||||
},
|
||||
[explorerSection, tagService]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (collapsed) setCreatedTag(null); // reset created tag to clear the renaming state
|
||||
if (collapsed) setCreating(false);
|
||||
}, [collapsed]);
|
||||
|
||||
const handleOpenCreateModal = useCallback(() => {
|
||||
setCreating(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CollapsibleSection
|
||||
name="tags"
|
||||
@@ -47,16 +50,26 @@ export const ExplorerTags = () => {
|
||||
headerClassName={styles.draggedOverHighlight}
|
||||
title={t['com.affine.rootAppSidebar.tags']()}
|
||||
actions={
|
||||
<IconButton
|
||||
data-testid="explorer-bar-add-favorite-button"
|
||||
onClick={handleCreateNewFavoriteDoc}
|
||||
size="16"
|
||||
tooltip={t[
|
||||
'com.affine.rootAppSidebar.explorer.tag-section-add-tooltip'
|
||||
]()}
|
||||
>
|
||||
<AddTagIcon />
|
||||
</IconButton>
|
||||
<div className={styles.iconContainer}>
|
||||
<IconButton
|
||||
data-testid="explorer-bar-add-tag-button"
|
||||
onClick={handleOpenCreateModal}
|
||||
size="16"
|
||||
tooltip={t[
|
||||
'com.affine.rootAppSidebar.explorer.tag-section-add-tooltip'
|
||||
]()}
|
||||
>
|
||||
<AddTagIcon />
|
||||
</IconButton>
|
||||
{creating && (
|
||||
<CreateTagModal
|
||||
setRenaming={setCreating}
|
||||
handleRename={handleCreateNewTag}
|
||||
rawName={t['com.affine.rootAppSidebar.tags.new-tag']()}
|
||||
className={styles.createModalAnchor}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<ExplorerTreeRoot placeholder={<RootEmpty />}>
|
||||
@@ -68,7 +81,6 @@ export const ExplorerTags = () => {
|
||||
location={{
|
||||
at: 'explorer:tags:list',
|
||||
}}
|
||||
defaultRenaming={createdTag?.id === tag.id}
|
||||
/>
|
||||
))}
|
||||
</ExplorerTreeRoot>
|
||||
|
||||
@@ -9,3 +9,15 @@ export const draggedOverHighlight = style({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const iconContainer = style({
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
});
|
||||
|
||||
export const createModalAnchor = style({
|
||||
top: 20,
|
||||
left: 'auto',
|
||||
right: 0,
|
||||
transform: 'translateX(6px)',
|
||||
});
|
||||
|
||||
@@ -98,14 +98,16 @@ interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {
|
||||
* specific rename modal for explorer tree node,
|
||||
* Separate it into a separate component to prevent re-rendering the entire component when width changes.
|
||||
*/
|
||||
const ExplorerTreeNodeRenameModal = ({
|
||||
export const ExplorerTreeNodeRenameModal = ({
|
||||
setRenaming,
|
||||
handleRename,
|
||||
rawName,
|
||||
className,
|
||||
}: {
|
||||
setRenaming: (renaming: boolean) => void;
|
||||
handleRename: (newName: string) => void;
|
||||
rawName: string | undefined;
|
||||
className?: string;
|
||||
}) => {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||
@@ -117,7 +119,7 @@ const ExplorerTreeNodeRenameModal = ({
|
||||
onRename={handleRename}
|
||||
currentName={rawName ?? ''}
|
||||
>
|
||||
<div className={styles.itemRenameAnchor} />
|
||||
<div className={clsx(styles.itemRenameAnchor, className)} />
|
||||
</RenameModal>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user