feat(core): replace emoji-mart with affine icon picker (#13644)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
  - Unified icon picker with consistent rendering across the app.
  - Picker can auto-close after selection.
  - “Remove” now clears the icon selection.

- Refactor
- Icon handling consolidated across editors, navigation, and document
titles for consistent behavior.
  - Picker now opens on the Emoji panel by default.

- Style
  - Adjusted line-height and selectors for icon picker visuals.

- Chores
  - Removed unused emoji-mart dependencies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Cats Juice
2025-09-26 06:41:29 +00:00
committed by GitHub
parent c540400496
commit d272c4342d
16 changed files with 121 additions and 138 deletions
-2
View File
@@ -28,8 +28,6 @@
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0", "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
"@blocksuite/icons": "^2.2.17", "@blocksuite/icons": "^2.2.17",
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-avatar": "^1.1.2",
@@ -15,6 +15,7 @@ export const contentRoot = style({
export const iconPicker = style({ export const iconPicker = style({
padding: 0, padding: 0,
lineHeight: 1,
}); });
globalStyle(`${iconPicker} span:has(svg)`, { globalStyle(`${iconPicker} span:has(svg)`, {
lineHeight: 0, lineHeight: 0,
@@ -2,12 +2,12 @@ import type { Meta, StoryFn } from '@storybook/react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { Button } from '../button'; import { Button } from '../button';
import { type IconData, IconType } from '../icon-picker';
import { ResizePanel } from '../resize-panel/resize-panel'; import { ResizePanel } from '../resize-panel/resize-panel';
import { import {
IconAndNameEditorMenu, IconAndNameEditorMenu,
type IconAndNameEditorMenuProps, type IconAndNameEditorMenuProps,
IconEditor, IconEditor,
type IconType,
} from './icon-name-editor'; } from './icon-name-editor';
export default { export default {
@@ -16,10 +16,13 @@ export default {
} satisfies Meta<typeof IconAndNameEditorMenu>; } satisfies Meta<typeof IconAndNameEditorMenu>;
export const Basic: StoryFn<IconAndNameEditorMenuProps> = () => { export const Basic: StoryFn<IconAndNameEditorMenuProps> = () => {
const [icon, setIcon] = useState<string | undefined>('👋'); const [icon, setIcon] = useState<IconData | undefined>({
type: IconType.Emoji,
unicode: '👋',
});
const [name, setName] = useState<string>('Hello'); const [name, setName] = useState<string>('Hello');
const handleIconChange = useCallback((_?: IconType, icon?: string) => { const handleIconChange = useCallback((icon?: IconData) => {
setIcon(icon); setIcon(icon);
}, []); }, []);
const handleNameChange = useCallback((name: string) => { const handleNameChange = useCallback((name: string) => {
@@ -28,7 +31,7 @@ export const Basic: StoryFn<IconAndNameEditorMenuProps> = () => {
return ( return (
<div> <div>
<p>Icon: {icon}</p> <p>Icon: {JSON.stringify(icon)}</p>
<p>Name: {name}</p> <p>Name: {name}</p>
<ResizePanel <ResizePanel
@@ -44,17 +47,16 @@ export const Basic: StoryFn<IconAndNameEditorMenuProps> = () => {
}} }}
> >
<IconAndNameEditorMenu <IconAndNameEditorMenu
iconType="emoji"
icon={icon} icon={icon}
name={name} name={name}
onIconChange={handleIconChange} onIconChange={handleIconChange}
onNameChange={handleNameChange} onNameChange={handleNameChange}
closeAfterSelect
> >
<Button>Edit Name and Icon</Button> <Button>Edit Name and Icon</Button>
</IconAndNameEditorMenu> </IconAndNameEditorMenu>
<IconEditor <IconEditor
iconType="emoji"
icon={icon} icon={icon}
onIconChange={handleIconChange} onIconChange={handleIconChange}
closeAfterSelect closeAfterSelect
@@ -1,22 +1,18 @@
import data from '@emoji-mart/data';
import Picker from '@emoji-mart/react';
import clsx from 'clsx'; import clsx from 'clsx';
import { useTheme } from 'next-themes';
import { type ReactNode, useCallback, useState } from 'react'; import { type ReactNode, useCallback, useState } from 'react';
import { Button, type ButtonProps } from '../button'; import { Button, type ButtonProps } from '../button';
import { type IconData, IconPicker } from '../icon-picker';
import { IconRenderer } from '../icon-picker/renderer';
import Input from '../input'; import Input from '../input';
import { Menu, type MenuProps } from '../menu'; import { Menu, type MenuProps } from '../menu';
import * as styles from './icon-name-editor.css'; import * as styles from './icon-name-editor.css';
export type IconType = 'emoji' | 'affine-icon' | 'blob';
export interface IconEditorProps { export interface IconEditorProps {
iconType?: IconType; icon?: IconData;
icon?: string;
closeAfterSelect?: boolean; closeAfterSelect?: boolean;
iconPlaceholder?: ReactNode; iconPlaceholder?: ReactNode;
onIconChange?: (type?: IconType, icon?: string) => void; onIconChange?: (data?: IconData) => void;
triggerClassName?: string; triggerClassName?: string;
} }
@@ -38,25 +34,7 @@ export interface IconAndNameEditorMenuProps
skipIfNotChanged?: boolean; skipIfNotChanged?: boolean;
} }
export const IconRenderer = ({
iconType,
icon,
fallback,
}: {
iconType: IconType;
icon: string;
fallback?: ReactNode;
}) => {
switch (iconType) {
case 'emoji':
return <div>{icon ?? fallback}</div>;
default:
return <div>{fallback}</div>;
}
};
export const IconEditor = ({ export const IconEditor = ({
iconType,
icon, icon,
closeAfterSelect, closeAfterSelect,
iconPlaceholder, iconPlaceholder,
@@ -71,16 +49,17 @@ export const IconEditor = ({
triggerVariant?: ButtonProps['variant']; triggerVariant?: ButtonProps['variant'];
}) => { }) => {
const [isPickerOpen, setIsPickerOpen] = useState(false); const [isPickerOpen, setIsPickerOpen] = useState(false);
const { resolvedTheme } = useTheme();
const handleEmojiClick = useCallback( const handleSelect = useCallback(
(emoji: any) => { (data?: IconData) => {
onIconChange?.('emoji', emoji.native); onIconChange?.(data);
if (closeAfterSelect) { if (closeAfterSelect) {
setIsPickerOpen(false); setIsPickerOpen(false);
} }
}, },
[closeAfterSelect, onIconChange] [closeAfterSelect, onIconChange]
); );
return ( return (
<Menu <Menu
rootOptions={{ rootOptions={{
@@ -97,30 +76,18 @@ export const IconEditor = ({
}} }}
items={ items={
<div onWheel={e => e.stopPropagation()}> <div onWheel={e => e.stopPropagation()}>
<Picker <IconPicker onSelect={handleSelect} />
data={data}
theme={resolvedTheme}
onEmojiSelect={handleEmojiClick}
/>
</div> </div>
} }
> >
<Button <Button
variant={triggerVariant} variant={triggerVariant}
className={clsx(styles.iconPicker, triggerClassName)} className={clsx(styles.iconPicker, triggerClassName)}
data-icon-type={iconType} data-icon-type={icon?.type}
aria-label={icon ? 'Change Icon' : 'Select Icon'} aria-label={icon ? 'Change Icon' : 'Select Icon'}
title={icon ? 'Change Icon' : 'Select Icon'} title={icon ? 'Change Icon' : 'Select Icon'}
> >
{icon && iconType ? ( <IconRenderer data={icon} fallback={iconPlaceholder} />
<IconRenderer
iconType={iconType}
icon={icon}
fallback={iconPlaceholder}
/>
) : (
iconPlaceholder
)}
</Button> </Button>
</Menu> </Menu>
); );
@@ -160,7 +127,6 @@ export const IconAndNameEditorMenu = ({
open, open,
onOpenChange, onOpenChange,
width = 300, width = 300,
iconType: initialIconType,
icon: initialIcon, icon: initialIcon,
name: initialName, name: initialName,
onIconChange, onIconChange,
@@ -169,15 +135,15 @@ export const IconAndNameEditorMenu = ({
iconPlaceholder, iconPlaceholder,
skipIfNotChanged = true, skipIfNotChanged = true,
inputTestId, inputTestId,
closeAfterSelect,
...menuProps ...menuProps
}: IconAndNameEditorMenuProps) => { }: IconAndNameEditorMenuProps) => {
const [iconType, setIconType] = useState(initialIconType);
const [icon, setIcon] = useState(initialIcon); const [icon, setIcon] = useState(initialIcon);
const [name, setName] = useState(initialName); const [name, setName] = useState(initialName);
const commit = useCallback(() => { const commit = useCallback(() => {
if (iconType !== initialIconType || icon !== initialIcon) { if (icon !== initialIcon) {
onIconChange?.(iconType, icon); onIconChange?.(icon);
} }
if (skipIfNotChanged) { if (skipIfNotChanged) {
if (name !== initialName) onNameChange?.(name); if (name !== initialName) onNameChange?.(name);
@@ -186,9 +152,7 @@ export const IconAndNameEditorMenu = ({
} }
}, [ }, [
icon, icon,
iconType,
initialIcon, initialIcon,
initialIconType,
initialName, initialName,
name, name,
onIconChange, onIconChange,
@@ -196,13 +160,11 @@ export const IconAndNameEditorMenu = ({
skipIfNotChanged, skipIfNotChanged,
]); ]);
const abort = useCallback(() => { const abort = useCallback(() => {
setIconType(initialIconType);
setIcon(initialIcon); setIcon(initialIcon);
setName(initialName); setName(initialName);
}, [initialIcon, initialIconType, initialName]); }, [initialIcon, initialName]);
const handleIconChange = useCallback((type?: IconType, icon?: string) => { const handleIconChange = useCallback((data?: IconData) => {
setIconType(type); setIcon(data);
setIcon(icon);
}, []); }, []);
const handleNameChange = useCallback((name: string) => { const handleNameChange = useCallback((name: string) => {
setName(name); setName(name);
@@ -210,13 +172,12 @@ export const IconAndNameEditorMenu = ({
const handleMenuOpenChange = useCallback( const handleMenuOpenChange = useCallback(
(open: boolean) => { (open: boolean) => {
if (open) { if (open) {
setIconType(initialIconType);
setIcon(initialIcon); setIcon(initialIcon);
setName(initialName); setName(initialName);
} }
onOpenChange?.(open); onOpenChange?.(open);
}, },
[initialIcon, initialIconType, initialName, onOpenChange] [initialIcon, initialName, onOpenChange]
); );
return ( return (
@@ -243,10 +204,10 @@ export const IconAndNameEditorMenu = ({
{...menuProps} {...menuProps}
items={ items={
<IconAndNameEditorContent <IconAndNameEditorContent
iconType={iconType}
icon={icon} icon={icon}
name={name} name={name}
iconPlaceholder={iconPlaceholder} iconPlaceholder={iconPlaceholder}
closeAfterSelect={closeAfterSelect}
onIconChange={handleIconChange} onIconChange={handleIconChange}
onNameChange={handleNameChange} onNameChange={handleNameChange}
inputTestId={inputTestId} inputTestId={inputTestId}
@@ -7,6 +7,7 @@ import { RadioGroup, type RadioItem } from '../radio';
import * as styles from './icon-picker.css'; import * as styles from './icon-picker.css';
import { AffineIconPicker } from './picker/affine-icon/affine-icon-picker'; import { AffineIconPicker } from './picker/affine-icon/affine-icon-picker';
import { EmojiPicker } from './picker/emoji/emoji-picker'; import { EmojiPicker } from './picker/emoji/emoji-picker';
import { type IconData, IconType } from './type';
const panels: Array<RadioItem> = [ const panels: Array<RadioItem> = [
{ value: 'Emoji', className: styles.headerNavItem }, { value: 'Emoji', className: styles.headerNavItem },
@@ -16,17 +17,11 @@ const panels: Array<RadioItem> = [
export const IconPicker = ({ export const IconPicker = ({
className, className,
style, style,
}: HTMLAttributes<HTMLDivElement> & { onSelect,
onSelect?: ( }: Omit<HTMLAttributes<HTMLDivElement>, 'onSelect'> & {
type: 'emoji' | 'affine-icon', onSelect?: (data?: IconData) => void;
data: { icon?: string; color?: string }
) => void;
}) => { }) => {
const [activePanel, setActivePanel] = useState<string>('Icons'); const [activePanel, setActivePanel] = useState<string>('Emoji');
// const ActivePanel = panels.find(
// panel => panel.value === activePanel
// )?.component;
return ( return (
<div className={clsx(styles.container, className)} style={{ ...style }}> <div className={clsx(styles.container, className)} style={{ ...style }}>
@@ -53,7 +48,7 @@ export const IconPicker = ({
<Button <Button
variant="plain" variant="plain"
style={{ color: cssVarV2.text.secondary, fontWeight: 500 }} style={{ color: cssVarV2.text.secondary, fontWeight: 500 }}
onClick={() => void 0} onClick={() => onSelect?.()}
> >
Remove Remove
</Button> </Button>
@@ -61,9 +56,17 @@ export const IconPicker = ({
</header> </header>
<main className={styles.main}> <main className={styles.main}>
{activePanel === 'Emoji' ? ( {activePanel === 'Emoji' ? (
<EmojiPicker /> <EmojiPicker
onSelect={emoji => {
onSelect?.({ type: IconType.Emoji, unicode: emoji });
}}
/>
) : activePanel === 'Icons' ? ( ) : activePanel === 'Icons' ? (
<AffineIconPicker /> <AffineIconPicker
onSelect={(icon, color) => {
onSelect?.({ type: IconType.AffineIcon, name: icon, color });
}}
/>
) : null} ) : null}
</main> </main>
</div> </div>
@@ -1 +1,3 @@
export * from './icon-picker'; export * from './icon-picker';
export * from './renderer';
export * from './type';
@@ -1,18 +1,29 @@
import type { ReactNode } from 'react';
import { AffineIconRenderer } from './renderer/affine-icon'; import { AffineIconRenderer } from './renderer/affine-icon';
import { type IconData, IconType } from './type';
export const IconRenderer = ({ export const IconRenderer = ({
iconType, data,
icon, fallback,
}: { }: {
iconType: 'emoji' | 'affine-icon'; data?: IconData;
icon: string; fallback?: ReactNode;
}) => { }) => {
if (iconType === 'emoji') { if (!data) {
return icon; return fallback ?? null;
}
if (iconType === 'affine-icon') {
return <AffineIconRenderer name={icon} />;
} }
return null; if (data.type === IconType.Emoji && data.unicode) {
return data.unicode;
}
if (data.type === IconType.AffineIcon && data.name) {
return <AffineIconRenderer name={data.name} color={data.color} />;
}
if (data.type === IconType.Blob) {
// Not supported yet
return null;
}
return fallback ?? null;
}; };
@@ -0,0 +1,20 @@
export enum IconType {
Emoji = 'emoji',
AffineIcon = 'affine-icon',
Blob = 'blob',
}
export type IconData =
| {
type: IconType.Emoji;
unicode: string;
}
| {
type: IconType.AffineIcon;
name: string;
color: string;
}
| {
type: IconType.Blob;
blob: Blob;
};
@@ -6,7 +6,7 @@ export const docIconPickerTrigger = style({
height: 64, height: 64,
padding: 2, padding: 2,
selectors: { selectors: {
'&[data-icon-type="emoji"]': { '&[data-icon-type="emoji"], &[data-icon-type="affine-icon"]': {
fontSize: 60, fontSize: 60,
lineHeight: 1, lineHeight: 1,
}, },
@@ -40,12 +40,15 @@ export const DocIconPicker = ({
const icon = useLiveData(explorerIconService.icon$('doc', docId)); const icon = useLiveData(explorerIconService.icon$('doc', docId));
const isPlaceholder = !icon?.type || !icon?.icon; const isPlaceholder = !icon?.icon;
if (readonly) { if (readonly) {
return isPlaceholder ? null : ( return isPlaceholder ? null : (
<div className={styles.docIconPickerTrigger} data-icon-type={icon?.type}> <div
<IconRenderer iconType={icon.type} icon={icon.icon} /> className={styles.docIconPickerTrigger}
data-icon-type={icon?.icon?.type}
>
<IconRenderer data={icon.icon} />
</div> </div>
); );
} }
@@ -53,14 +56,12 @@ export const DocIconPicker = ({
return ( return (
<TitleContainer isPlaceholder={isPlaceholder}> <TitleContainer isPlaceholder={isPlaceholder}>
<IconEditor <IconEditor
iconType={icon?.type}
icon={icon?.icon} icon={icon?.icon}
onIconChange={(type, icon) => { onIconChange={data => {
explorerIconService.setIcon({ explorerIconService.setIcon({
where: 'doc', where: 'doc',
id: docId, id: docId,
type, icon: data,
icon,
}); });
}} }}
closeAfterSelect={true} closeAfterSelect={true}
@@ -6,6 +6,8 @@ import {
type DropTargetTreeInstruction, type DropTargetTreeInstruction,
IconAndNameEditorMenu, IconAndNameEditorMenu,
IconButton, IconButton,
type IconData,
IconRenderer,
Menu, Menu,
MenuItem, MenuItem,
useDraggable, useDraggable,
@@ -13,7 +15,6 @@ import {
} from '@affine/component'; } from '@affine/component';
import { Guard } from '@affine/core/components/guard'; import { Guard } from '@affine/core/components/guard';
import { AppSidebarService } from '@affine/core/modules/app-sidebar'; import { AppSidebarService } from '@affine/core/modules/app-sidebar';
import type { ExplorerIconType } from '@affine/core/modules/db/schema/schema';
import { ExplorerIconService } from '@affine/core/modules/explorer-icon/services/explorer-icon'; import { ExplorerIconService } from '@affine/core/modules/explorer-icon/services/explorer-icon';
import type { ExplorerType } from '@affine/core/modules/explorer-icon/store/explorer-icon'; import type { ExplorerType } from '@affine/core/modules/explorer-icon/store/explorer-icon';
import type { DocPermissionActions } from '@affine/core/modules/permissions'; import type { DocPermissionActions } from '@affine/core/modules/permissions';
@@ -142,13 +143,12 @@ export const NavigationPanelTreeNodeRenameModal = ({
); );
const onIconChange = useCallback( const onIconChange = useCallback(
(type?: ExplorerIconType, icon?: string) => { (data?: IconData) => {
if (!explorerIconConfig) return; if (!explorerIconConfig) return;
explorerIconService.setIcon({ explorerIconService.setIcon({
where: explorerIconConfig.where, where: explorerIconConfig.where,
id: explorerIconConfig.id, id: explorerIconConfig.id,
type, icon: data,
icon,
}); });
}, },
[explorerIconConfig, explorerIconService] [explorerIconConfig, explorerIconService]
@@ -161,8 +161,7 @@ export const NavigationPanelTreeNodeRenameModal = ({
onIconChange={onIconChange} onIconChange={onIconChange}
onNameChange={handleRename} onNameChange={handleRename}
name={rawName ?? ''} name={rawName ?? ''}
iconType={explorerIcon?.type ?? 'emoji'} icon={explorerIcon?.icon}
icon={explorerIcon?.icon ?? ''}
width={sidebarWidth - 16} width={sidebarWidth - 16}
contentOptions={{ contentOptions={{
sideOffset: 36, sideOffset: 36,
@@ -461,10 +460,7 @@ export const NavigationPanelTreeNode = ({
/> />
</div> </div>
<div className={styles.iconContainer}> <div className={styles.iconContainer}>
{/* Only emoji icon is supported for now */} <IconRenderer data={explorerIcon?.icon} fallback={fallbackIcon} />
{explorerIcon && explorerIcon.type === 'emoji'
? explorerIcon.icon
: fallbackIcon}
</div> </div>
</div> </div>
@@ -1,3 +1,4 @@
import type { IconData } from '@affine/component';
import { import {
type DBSchemaBuilder, type DBSchemaBuilder,
f, f,
@@ -51,8 +52,7 @@ export const AFFiNE_WORKSPACE_DB_SCHEMA = {
* ${doc|collection|folder|tag}:${id} * ${doc|collection|folder|tag}:${id}
*/ */
id: f.string().primaryKey(), id: f.string().primaryKey(),
type: f.enum('emoji', 'affine-icon', 'blob'), icon: f.json<IconData>(),
icon: f.string(),
}, },
} as const satisfies DBSchemaBuilder; } as const satisfies DBSchemaBuilder;
export type AFFiNEWorkspaceDbSchema = typeof AFFiNE_WORKSPACE_DB_SCHEMA; export type AFFiNEWorkspaceDbSchema = typeof AFFiNE_WORKSPACE_DB_SCHEMA;
@@ -61,10 +61,6 @@ export type DocProperties = ORMEntity<AFFiNEWorkspaceDbSchema['docProperties']>;
export type DocCustomPropertyInfo = ORMEntity< export type DocCustomPropertyInfo = ORMEntity<
AFFiNEWorkspaceDbSchema['docCustomPropertyInfo'] AFFiNEWorkspaceDbSchema['docCustomPropertyInfo']
>; >;
export type ExplorerIcon = ORMEntity<AFFiNEWorkspaceDbSchema['explorerIcon']>;
export type ExplorerIconType = ORMEntity<
AFFiNEWorkspaceDbSchema['explorerIcon']
>['type'];
export const AFFiNE_WORKSPACE_USERDATA_DB_SCHEMA = { export const AFFiNE_WORKSPACE_USERDATA_DB_SCHEMA = {
favorite: { favorite: {
@@ -29,6 +29,7 @@ import type { DocRecord, DocsService } from '../../doc';
import type { ExplorerIconService } from '../../explorer-icon/services/explorer-icon'; import type { ExplorerIconService } from '../../explorer-icon/services/explorer-icon';
import type { I18nService } from '../../i18n'; import type { I18nService } from '../../i18n';
import type { JournalService } from '../../journal'; import type { JournalService } from '../../journal';
import { getDocIconComponent } from './icon';
type IconType = 'rc' | 'lit'; type IconType = 'rc' | 'lit';
interface DocDisplayIconOptions<T extends IconType> { interface DocDisplayIconOptions<T extends IconType> {
@@ -149,8 +150,10 @@ export class DocDisplayMetaService extends Service {
if (enableEmojiIcon) { if (enableEmojiIcon) {
// const { emoji } = extractEmojiIcon(title); // const { emoji } = extractEmojiIcon(title);
// if (emoji) return () => emoji; // if (emoji) return () => emoji;
const icon = get(this.explorerIconService.icon$('doc', docId)); const icon = get(this.explorerIconService.icon$('doc', docId))?.icon;
if (icon && icon.type === 'emoji') return () => icon.icon; if (icon) {
return getDocIconComponent(icon);
}
} }
// title alias // title alias
@@ -0,0 +1,7 @@
import { type IconData, IconRenderer } from '@affine/component';
export const getDocIconComponent = (icon: IconData) => {
const Icon = () => <IconRenderer data={icon} />;
Icon.displayName = 'DocIcon';
return Icon;
};
@@ -1,7 +1,7 @@
import type { IconData } from '@affine/component';
import { Store } from '@toeverything/infra'; import { Store } from '@toeverything/infra';
import type { WorkspaceDBService } from '../../db'; import type { WorkspaceDBService } from '../../db';
import type { ExplorerIconType } from '../../db/schema/schema';
export type ExplorerType = 'doc' | 'collection' | 'folder' | 'tag'; export type ExplorerType = 'doc' | 'collection' | 'folder' | 'tag';
@@ -18,21 +18,15 @@ export class ExplorerIconStore extends Store {
return this.dbService.db.explorerIcon.get(`${type}:${id}`); return this.dbService.db.explorerIcon.get(`${type}:${id}`);
} }
setIcon(options: { setIcon(options: { where: ExplorerType; id: string; icon?: IconData }) {
where: ExplorerType; const { where, id, icon } = options;
id: string;
type?: ExplorerIconType;
icon?: string;
}) {
const { where, id, type, icon } = options;
// remove icon // remove icon
if (!type || !icon) { if (!icon) {
return this.dbService.db.explorerIcon.delete(`${where}:${id}`); return this.dbService.db.explorerIcon.delete(`${where}:${id}`);
} }
// upsert icon // upsert icon
return this.dbService.db.explorerIcon.create({ return this.dbService.db.explorerIcon.create({
id: `${where}:${id}`, id: `${where}:${id}`,
type,
icon, icon,
}); });
} }
-12
View File
@@ -314,8 +314,6 @@ __metadata:
"@blocksuite/affine": "workspace:*" "@blocksuite/affine": "workspace:*"
"@blocksuite/icons": "npm:^2.2.17" "@blocksuite/icons": "npm:^2.2.17"
"@chromatic-com/storybook": "npm:^4.0.0" "@chromatic-com/storybook": "npm:^4.0.0"
"@emoji-mart/data": "npm:^1.2.1"
"@emoji-mart/react": "npm:^1.1.1"
"@emotion/react": "npm:^11.14.0" "@emotion/react": "npm:^11.14.0"
"@emotion/styled": "npm:^11.14.0" "@emotion/styled": "npm:^11.14.0"
"@radix-ui/react-avatar": "npm:^1.1.2" "@radix-ui/react-avatar": "npm:^1.1.2"
@@ -5532,16 +5530,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@emoji-mart/react@npm:^1.1.1":
version: 1.1.1
resolution: "@emoji-mart/react@npm:1.1.1"
peerDependencies:
emoji-mart: ^5.2
react: ^16.8 || ^17 || ^18
checksum: 10/def4dddaa01ce88c396510d84d1878b881fe0f9c484a836a50e3db784a91ab98edf94816cff503b4d1cab7b00400a01c87e32d19dee2d950f64ef9243f79101e
languageName: node
linkType: hard
"@emotion/babel-plugin@npm:^11.13.5": "@emotion/babel-plugin@npm:^11.13.5":
version: 11.13.5 version: 11.13.5
resolution: "@emotion/babel-plugin@npm:11.13.5" resolution: "@emotion/babel-plugin@npm:11.13.5"