mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-04 23:21:41 +03:00
parent
4977055a2e
commit
ef82b9d3e7
@ -64,7 +64,7 @@ export class DocPropertiesStore extends Store {
|
|||||||
createDocPropertyInfo(
|
createDocPropertyInfo(
|
||||||
config: Omit<DocCustomPropertyInfo, 'id'> & { id?: string }
|
config: Omit<DocCustomPropertyInfo, 'id'> & { id?: string }
|
||||||
) {
|
) {
|
||||||
return this.dbService.db.docCustomPropertyInfo.create(config).id;
|
return this.dbService.db.docCustomPropertyInfo.create(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDocPropertyInfo(id: string) {
|
removeDocPropertyInfo(id: string) {
|
||||||
|
@ -3,11 +3,16 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
useLitPortalFactory,
|
useLitPortalFactory,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import type {
|
||||||
|
DatabaseRow,
|
||||||
|
DatabaseValueCell,
|
||||||
|
} from '@affine/core/modules/doc-info/types';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
import { JournalService } from '@affine/core/modules/journal';
|
import { JournalService } from '@affine/core/modules/journal';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
|
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
|
||||||
|
import track from '@affine/track';
|
||||||
import type { DocMode } from '@blocksuite/affine/blocks';
|
import type { DocMode } from '@blocksuite/affine/blocks';
|
||||||
import {
|
import {
|
||||||
DocTitle,
|
DocTitle,
|
||||||
@ -16,6 +21,7 @@ import {
|
|||||||
} from '@blocksuite/affine/presets';
|
} from '@blocksuite/affine/presets';
|
||||||
import type { Doc } from '@blocksuite/affine/store';
|
import type { Doc } from '@blocksuite/affine/store';
|
||||||
import {
|
import {
|
||||||
|
type DocCustomPropertyInfo,
|
||||||
DocService,
|
DocService,
|
||||||
DocsService,
|
DocsService,
|
||||||
FeatureFlagService,
|
FeatureFlagService,
|
||||||
@ -239,6 +245,28 @@ export const BlocksuiteDocEditor = forwardRef<
|
|||||||
editorSettingService.editorSetting.settings$.selector(s => s.displayDocInfo)
|
editorSettingService.editorSetting.settings$.selector(s => s.displayDocInfo)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onPropertyChange = useCallback((property: DocCustomPropertyInfo) => {
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: property.type,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onPropertyAdded = useCallback((property: DocCustomPropertyInfo) => {
|
||||||
|
track.doc.inlineDocInfo.property.addProperty({
|
||||||
|
type: property.type,
|
||||||
|
control: 'at menu',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onDatabasePropertyChange = useCallback(
|
||||||
|
(_row: DatabaseRow, cell: DatabaseValueCell) => {
|
||||||
|
track.doc.inlineDocInfo.databaseProperty.editProperty({
|
||||||
|
type: cell.property.type$.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.affineDocViewport} style={{ height: '100%' }}>
|
<div className={styles.affineDocViewport} style={{ height: '100%' }}>
|
||||||
@ -248,7 +276,12 @@ export const BlocksuiteDocEditor = forwardRef<
|
|||||||
<BlocksuiteEditorJournalDocTitle page={page} />
|
<BlocksuiteEditorJournalDocTitle page={page} />
|
||||||
)}
|
)}
|
||||||
{!shared && displayDocInfo ? (
|
{!shared && displayDocInfo ? (
|
||||||
<DocPropertiesTable defaultOpenProperty={defaultOpenProperty} />
|
<DocPropertiesTable
|
||||||
|
onDatabasePropertyChange={onDatabasePropertyChange}
|
||||||
|
onPropertyChange={onPropertyChange}
|
||||||
|
onPropertyAdded={onPropertyAdded}
|
||||||
|
defaultOpenProperty={defaultOpenProperty}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<adapted.DocEditor
|
<adapted.DocEditor
|
||||||
className={styles.docContainer}
|
className={styles.docContainer}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { MenuItem, MenuSeparator } from '@affine/component';
|
import { MenuItem, MenuSeparator } from '@affine/component';
|
||||||
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { DocsService, useLiveData, useService } from '@toeverything/infra';
|
import {
|
||||||
|
type DocCustomPropertyInfo,
|
||||||
|
DocsService,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -15,7 +20,7 @@ export const CreatePropertyMenuItems = ({
|
|||||||
onCreated,
|
onCreated,
|
||||||
}: {
|
}: {
|
||||||
at?: 'before' | 'after';
|
at?: 'before' | 'after';
|
||||||
onCreated?: (propertyId: string) => void;
|
onCreated?: (property: DocCustomPropertyInfo) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
@ -36,13 +41,13 @@ export const CreatePropertyMenuItems = ({
|
|||||||
? generateUniqueNameInSequence(option.name, allNames)
|
? generateUniqueNameInSequence(option.name, allNames)
|
||||||
: option.name;
|
: option.name;
|
||||||
const uniqueId = typeDefined.uniqueId;
|
const uniqueId = typeDefined.uniqueId;
|
||||||
const newPropertyId = propertyList.createProperty({
|
const newProperty = propertyList.createProperty({
|
||||||
id: uniqueId,
|
id: uniqueId,
|
||||||
name,
|
name,
|
||||||
type: option.type,
|
type: option.type,
|
||||||
index: propertyList.indexAt(at),
|
index: propertyList.indexAt(at),
|
||||||
});
|
});
|
||||||
onCreated?.(newPropertyId);
|
onCreated?.(newProperty);
|
||||||
},
|
},
|
||||||
[at, onCreated, propertyList, properties]
|
[at, onCreated, propertyList, properties]
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Divider, IconButton, Tooltip } from '@affine/component';
|
import { Divider, IconButton, Tooltip } from '@affine/component';
|
||||||
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
import { generateUniqueNameInSequence } from '@affine/core/utils/unique-name';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import {
|
||||||
Content as CollapsibleContent,
|
Content as CollapsibleContent,
|
||||||
@ -41,13 +42,17 @@ export const DocPropertySidebar = () => {
|
|||||||
const name = nameExists
|
const name = nameExists
|
||||||
? generateUniqueNameInSequence(option.name, allNames)
|
? generateUniqueNameInSequence(option.name, allNames)
|
||||||
: option.name;
|
: option.name;
|
||||||
const newPropertyId = propertyList.createProperty({
|
const newProperty = propertyList.createProperty({
|
||||||
id: typeDefined.uniqueId,
|
id: typeDefined.uniqueId,
|
||||||
name,
|
name,
|
||||||
type: option.type,
|
type: option.type,
|
||||||
index: propertyList.indexAt('after'),
|
index: propertyList.indexAt('after'),
|
||||||
});
|
});
|
||||||
setNewPropertyId(newPropertyId);
|
setNewPropertyId(newProperty.id);
|
||||||
|
track.doc.sidepanel.property.addProperty({
|
||||||
|
control: 'property list',
|
||||||
|
type: option.type,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[propertyList, properties]
|
[propertyList, properties]
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,10 @@ import {
|
|||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
||||||
|
import type {
|
||||||
|
DatabaseRow,
|
||||||
|
DatabaseValueCell,
|
||||||
|
} from '@affine/core/modules/doc-info/types';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
@ -26,7 +30,6 @@ import {
|
|||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { HTMLProps } from 'react';
|
|
||||||
import { forwardRef, useCallback, useState } from 'react';
|
import { forwardRef, useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { DocPropertyIcon } from './icons/doc-property-icon';
|
import { DocPropertyIcon } from './icons/doc-property-icon';
|
||||||
@ -47,6 +50,13 @@ export type DefaultOpenProperty =
|
|||||||
|
|
||||||
export interface DocPropertiesTableProps {
|
export interface DocPropertiesTableProps {
|
||||||
defaultOpenProperty?: DefaultOpenProperty;
|
defaultOpenProperty?: DefaultOpenProperty;
|
||||||
|
onPropertyAdded?: (property: DocCustomPropertyInfo) => void;
|
||||||
|
onPropertyChange?: (property: DocCustomPropertyInfo, value: unknown) => void;
|
||||||
|
onDatabasePropertyChange?: (
|
||||||
|
row: DatabaseRow,
|
||||||
|
cell: DatabaseValueCell,
|
||||||
|
value: unknown
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DocPropertiesTableHeaderProps {
|
interface DocPropertiesTableHeaderProps {
|
||||||
@ -95,11 +105,13 @@ interface DocPropertyRowProps {
|
|||||||
propertyInfo: DocCustomPropertyInfo;
|
propertyInfo: DocCustomPropertyInfo;
|
||||||
showAll?: boolean;
|
showAll?: boolean;
|
||||||
defaultOpenEditMenu?: boolean;
|
defaultOpenEditMenu?: boolean;
|
||||||
|
onChange?: (value: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DocPropertyRow = ({
|
export const DocPropertyRow = ({
|
||||||
propertyInfo,
|
propertyInfo,
|
||||||
defaultOpenEditMenu,
|
defaultOpenEditMenu,
|
||||||
|
onChange,
|
||||||
}: DocPropertyRowProps) => {
|
}: DocPropertyRowProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const docService = useService(DocService);
|
const docService = useService(DocService);
|
||||||
@ -123,8 +135,9 @@ export const DocPropertyRow = ({
|
|||||||
throw new Error('only allow string value');
|
throw new Error('only allow string value');
|
||||||
}
|
}
|
||||||
docService.doc.record.setCustomProperty(propertyInfo.id, value);
|
docService.doc.record.setCustomProperty(propertyInfo.id, value);
|
||||||
|
onChange?.(value);
|
||||||
},
|
},
|
||||||
[docService, propertyInfo]
|
[docService, onChange, propertyInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
const docId = docService.doc.id;
|
const docId = docService.doc.id;
|
||||||
@ -214,6 +227,8 @@ interface DocWorkspacePropertiesTableBodyProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
|
onChange?: (property: DocCustomPropertyInfo, value: unknown) => void;
|
||||||
|
onPropertyAdded?: (property: DocCustomPropertyInfo) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🏷️ Tags (⋅ xxx) (⋅ yyy)
|
// 🏷️ Tags (⋅ xxx) (⋅ yyy)
|
||||||
@ -221,104 +236,121 @@ interface DocWorkspacePropertiesTableBodyProps {
|
|||||||
// + Add a property
|
// + Add a property
|
||||||
const DocWorkspacePropertiesTableBody = forwardRef<
|
const DocWorkspacePropertiesTableBody = forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
DocWorkspacePropertiesTableBodyProps & HTMLProps<HTMLDivElement>
|
DocWorkspacePropertiesTableBodyProps
|
||||||
>(({ className, style, defaultOpen, ...props }, ref) => {
|
>(
|
||||||
const t = useI18n();
|
(
|
||||||
const docsService = useService(DocsService);
|
{ className, style, defaultOpen, onChange, onPropertyAdded, ...props },
|
||||||
const workbenchService = useService(WorkbenchService);
|
ref
|
||||||
const viewService = useServiceOptional(ViewService);
|
) => {
|
||||||
const properties = useLiveData(docsService.propertyList.sortedProperties$);
|
const t = useI18n();
|
||||||
const [propertyCollapsed, setPropertyCollapsed] = useState(true);
|
const docsService = useService(DocsService);
|
||||||
|
const workbenchService = useService(WorkbenchService);
|
||||||
|
const viewService = useServiceOptional(ViewService);
|
||||||
|
const properties = useLiveData(docsService.propertyList.sortedProperties$);
|
||||||
|
const [propertyCollapsed, setPropertyCollapsed] = useState(true);
|
||||||
|
|
||||||
const [newPropertyId, setNewPropertyId] = useState<string | null>(null);
|
const [newPropertyId, setNewPropertyId] = useState<string | null>(null);
|
||||||
|
|
||||||
return (
|
const handlePropertyAdded = useCallback(
|
||||||
<PropertyCollapsibleSection
|
(property: DocCustomPropertyInfo) => {
|
||||||
ref={ref}
|
setNewPropertyId(property.id);
|
||||||
className={clsx(styles.tableBodyRoot, className)}
|
onPropertyAdded?.(property);
|
||||||
style={style}
|
},
|
||||||
title={t.t('com.affine.workspace.properties')}
|
[onPropertyAdded]
|
||||||
defaultCollapsed={!defaultOpen}
|
);
|
||||||
{...props}
|
|
||||||
>
|
return (
|
||||||
<PropertyCollapsibleContent
|
<PropertyCollapsibleSection
|
||||||
collapsible
|
ref={ref}
|
||||||
collapsed={propertyCollapsed}
|
className={clsx(styles.tableBodyRoot, className)}
|
||||||
onCollapseChange={setPropertyCollapsed}
|
style={style}
|
||||||
className={styles.tableBodySortable}
|
title={t.t('com.affine.workspace.properties')}
|
||||||
collapseButtonText={({ hide, isCollapsed }) =>
|
defaultCollapsed={!defaultOpen}
|
||||||
isCollapsed
|
{...props}
|
||||||
? hide === 1
|
|
||||||
? t['com.affine.page-properties.more-property.one']({
|
|
||||||
count: hide.toString(),
|
|
||||||
})
|
|
||||||
: t['com.affine.page-properties.more-property.more']({
|
|
||||||
count: hide.toString(),
|
|
||||||
})
|
|
||||||
: hide === 1
|
|
||||||
? t['com.affine.page-properties.hide-property.one']({
|
|
||||||
count: hide.toString(),
|
|
||||||
})
|
|
||||||
: t['com.affine.page-properties.hide-property.more']({
|
|
||||||
count: hide.toString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{properties.map(property => (
|
<PropertyCollapsibleContent
|
||||||
<DocPropertyRow
|
collapsible
|
||||||
key={property.id}
|
collapsed={propertyCollapsed}
|
||||||
propertyInfo={property}
|
onCollapseChange={setPropertyCollapsed}
|
||||||
defaultOpenEditMenu={newPropertyId === property.id}
|
className={styles.tableBodySortable}
|
||||||
/>
|
collapseButtonText={({ hide, isCollapsed }) =>
|
||||||
))}
|
isCollapsed
|
||||||
<div className={styles.actionContainer}>
|
? hide === 1
|
||||||
<Menu
|
? t['com.affine.page-properties.more-property.one']({
|
||||||
items={
|
count: hide.toString(),
|
||||||
<CreatePropertyMenuItems
|
})
|
||||||
at="after"
|
: t['com.affine.page-properties.more-property.more']({
|
||||||
onCreated={setNewPropertyId}
|
count: hide.toString(),
|
||||||
/>
|
})
|
||||||
}
|
: hide === 1
|
||||||
contentOptions={{
|
? t['com.affine.page-properties.hide-property.one']({
|
||||||
onClick(e) {
|
count: hide.toString(),
|
||||||
e.stopPropagation();
|
})
|
||||||
},
|
: t['com.affine.page-properties.hide-property.more']({
|
||||||
}}
|
count: hide.toString(),
|
||||||
>
|
})
|
||||||
<Button
|
}
|
||||||
variant="plain"
|
>
|
||||||
prefix={<PlusIcon />}
|
{properties.map(property => (
|
||||||
className={styles.propertyActionButton}
|
<DocPropertyRow
|
||||||
data-testid="add-property-button"
|
key={property.id}
|
||||||
>
|
propertyInfo={property}
|
||||||
{t['com.affine.page-properties.add-property']()}
|
defaultOpenEditMenu={newPropertyId === property.id}
|
||||||
</Button>
|
onChange={value => onChange?.(property, value)}
|
||||||
</Menu>
|
/>
|
||||||
{viewService ? (
|
))}
|
||||||
<Button
|
<div className={styles.actionContainer}>
|
||||||
variant="plain"
|
<Menu
|
||||||
prefix={<PropertyIcon />}
|
items={
|
||||||
className={clsx(
|
<CreatePropertyMenuItems
|
||||||
styles.propertyActionButton,
|
at="after"
|
||||||
styles.propertyConfigButton
|
onCreated={handlePropertyAdded}
|
||||||
)}
|
/>
|
||||||
onClick={() => {
|
}
|
||||||
viewService.view.activeSidebarTab('properties');
|
contentOptions={{
|
||||||
workbenchService.workbench.openSidebar();
|
onClick(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t['com.affine.page-properties.config-properties']()}
|
<Button
|
||||||
</Button>
|
variant="plain"
|
||||||
) : null}
|
prefix={<PlusIcon />}
|
||||||
</div>
|
className={styles.propertyActionButton}
|
||||||
</PropertyCollapsibleContent>
|
data-testid="add-property-button"
|
||||||
</PropertyCollapsibleSection>
|
>
|
||||||
);
|
{t['com.affine.page-properties.add-property']()}
|
||||||
});
|
</Button>
|
||||||
|
</Menu>
|
||||||
|
{viewService ? (
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
prefix={<PropertyIcon />}
|
||||||
|
className={clsx(
|
||||||
|
styles.propertyActionButton,
|
||||||
|
styles.propertyConfigButton
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
viewService.view.activeSidebarTab('properties');
|
||||||
|
workbenchService.workbench.openSidebar();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t['com.affine.page-properties.config-properties']()}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</PropertyCollapsibleContent>
|
||||||
|
</PropertyCollapsibleSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
DocWorkspacePropertiesTableBody.displayName = 'PagePropertiesTableBody';
|
DocWorkspacePropertiesTableBody.displayName = 'PagePropertiesTableBody';
|
||||||
|
|
||||||
const DocPropertiesTableInner = ({
|
const DocPropertiesTableInner = ({
|
||||||
defaultOpenProperty,
|
defaultOpenProperty,
|
||||||
|
onPropertyAdded,
|
||||||
|
onPropertyChange,
|
||||||
|
onDatabasePropertyChange,
|
||||||
}: DocPropertiesTableProps) => {
|
}: DocPropertiesTableProps) => {
|
||||||
const [expanded, setExpanded] = useState(!!defaultOpenProperty);
|
const [expanded, setExpanded] = useState(!!defaultOpenProperty);
|
||||||
return (
|
return (
|
||||||
@ -334,9 +366,12 @@ const DocPropertiesTableInner = ({
|
|||||||
defaultOpen={
|
defaultOpen={
|
||||||
!defaultOpenProperty || defaultOpenProperty.type === 'workspace'
|
!defaultOpenProperty || defaultOpenProperty.type === 'workspace'
|
||||||
}
|
}
|
||||||
|
onPropertyAdded={onPropertyAdded}
|
||||||
|
onChange={onPropertyChange}
|
||||||
/>
|
/>
|
||||||
<div className={styles.tableHeaderDivider} />
|
<div className={styles.tableHeaderDivider} />
|
||||||
<DocDatabaseBacklinkInfo
|
<DocDatabaseBacklinkInfo
|
||||||
|
onChange={onDatabasePropertyChange}
|
||||||
defaultOpen={
|
defaultOpen={
|
||||||
defaultOpenProperty?.type === 'database'
|
defaultOpenProperty?.type === 'database'
|
||||||
? [
|
? [
|
||||||
@ -356,8 +391,6 @@ const DocPropertiesTableInner = ({
|
|||||||
|
|
||||||
// this is the main component that renders the page properties table at the top of the page below
|
// this is the main component that renders the page properties table at the top of the page below
|
||||||
// the page title
|
// the page title
|
||||||
export const DocPropertiesTable = ({
|
export const DocPropertiesTable = (props: DocPropertiesTableProps) => {
|
||||||
defaultOpenProperty,
|
return <DocPropertiesTableInner {...props} />;
|
||||||
}: DocPropertiesTableProps) => {
|
|
||||||
return <DocPropertiesTableInner defaultOpenProperty={defaultOpenProperty} />;
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { TagLike } from '@affine/component/ui/tags';
|
import type { TagLike } from '@affine/component/ui/tags';
|
||||||
import { TagsInlineEditor as TagsInlineEditorComponent } from '@affine/component/ui/tags';
|
import { TagsInlineEditor as TagsInlineEditorComponent } from '@affine/component/ui/tags';
|
||||||
import { TagService, useDeleteTagConfirmModal } from '@affine/core/modules/tag';
|
import { TagService, useDeleteTagConfirmModal } from '@affine/core/modules/tag';
|
||||||
|
import track from '@affine/track';
|
||||||
import {
|
import {
|
||||||
LiveData,
|
LiveData,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
@ -38,6 +39,9 @@ export const TagsInlineEditor = ({
|
|||||||
const onCreateTag = useCallback(
|
const onCreateTag = useCallback(
|
||||||
(name: string, color: string) => {
|
(name: string, color: string) => {
|
||||||
const newTag = tagService.tagList.createTag(name, color);
|
const newTag = tagService.tagList.createTag(name, color);
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: 'tags',
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
id: newTag.id,
|
id: newTag.id,
|
||||||
value: newTag.value$.value,
|
value: newTag.value$.value,
|
||||||
@ -50,6 +54,9 @@ export const TagsInlineEditor = ({
|
|||||||
const onSelectTag = useCallback(
|
const onSelectTag = useCallback(
|
||||||
(tagId: string) => {
|
(tagId: string) => {
|
||||||
tagService.tagList.tagByTagId$(tagId).value?.tag(pageId);
|
tagService.tagList.tagByTagId$(tagId).value?.tag(pageId);
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: 'tags',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[pageId, tagService.tagList]
|
[pageId, tagService.tagList]
|
||||||
);
|
);
|
||||||
@ -57,6 +64,9 @@ export const TagsInlineEditor = ({
|
|||||||
const onDeselectTag = useCallback(
|
const onDeselectTag = useCallback(
|
||||||
(tagId: string) => {
|
(tagId: string) => {
|
||||||
tagService.tagList.tagByTagId$(tagId).value?.untag(pageId);
|
tagService.tagList.tagByTagId$(tagId).value?.untag(pageId);
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: 'tags',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[pageId, tagService.tagList]
|
[pageId, tagService.tagList]
|
||||||
);
|
);
|
||||||
@ -68,6 +78,9 @@ export const TagsInlineEditor = ({
|
|||||||
} else if (property === 'color') {
|
} else if (property === 'color') {
|
||||||
tagService.tagList.tagByTagId$(id).value?.changeColor(value);
|
tagService.tagList.tagByTagId$(id).value?.changeColor(value);
|
||||||
}
|
}
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: 'tags',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[tagService.tagList]
|
[tagService.tagList]
|
||||||
);
|
);
|
||||||
@ -77,6 +90,9 @@ export const TagsInlineEditor = ({
|
|||||||
const onTagDelete = useAsyncCallback(
|
const onTagDelete = useAsyncCallback(
|
||||||
async (id: string) => {
|
async (id: string) => {
|
||||||
await deleteTags([id]);
|
await deleteTags([id]);
|
||||||
|
track.doc.inlineDocInfo.property.editProperty({
|
||||||
|
type: 'tags',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[deleteTags]
|
[deleteTags]
|
||||||
);
|
);
|
||||||
|
@ -8,16 +8,22 @@ import {
|
|||||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
||||||
import { DocPropertyRow } from '@affine/core/components/doc-properties/table';
|
import { DocPropertyRow } from '@affine/core/components/doc-properties/table';
|
||||||
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
||||||
|
import type {
|
||||||
|
DatabaseRow,
|
||||||
|
DatabaseValueCell,
|
||||||
|
} from '@affine/core/modules/doc-info/types';
|
||||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import track from '@affine/track';
|
||||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import {
|
||||||
|
type DocCustomPropertyInfo,
|
||||||
DocsService,
|
DocsService,
|
||||||
LiveData,
|
LiveData,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useServices,
|
useServices,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import * as styles from './info-modal.css';
|
import * as styles from './info-modal.css';
|
||||||
import { LinksRow } from './links-row';
|
import { LinksRow } from './links-row';
|
||||||
@ -49,6 +55,23 @@ export const InfoTable = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onBacklinkPropertyChange = useCallback(
|
||||||
|
(_row: DatabaseRow, cell: DatabaseValueCell, _value: unknown) => {
|
||||||
|
track.$.docInfoPanel.databaseProperty.editProperty({
|
||||||
|
type: cell.property.type$.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPropertyAdded = useCallback((property: DocCustomPropertyInfo) => {
|
||||||
|
setNewPropertyId(property.id);
|
||||||
|
track.$.docInfoPanel.property.addProperty({
|
||||||
|
type: property.type,
|
||||||
|
control: 'at menu',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{backlinks && backlinks.length > 0 ? (
|
{backlinks && backlinks.length > 0 ? (
|
||||||
@ -102,7 +125,7 @@ export const InfoTable = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Menu
|
<Menu
|
||||||
items={<CreatePropertyMenuItems onCreated={setNewPropertyId} />}
|
items={<CreatePropertyMenuItems onCreated={onPropertyAdded} />}
|
||||||
contentOptions={{
|
contentOptions={{
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -120,7 +143,7 @@ export const InfoTable = ({
|
|||||||
</PropertyCollapsibleContent>
|
</PropertyCollapsibleContent>
|
||||||
</PropertyCollapsibleSection>
|
</PropertyCollapsibleSection>
|
||||||
<Divider size="thinner" />
|
<Divider size="thinner" />
|
||||||
<DocDatabaseBacklinkInfo />
|
<DocDatabaseBacklinkInfo onChange={onBacklinkPropertyChange} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,13 @@ import { DocPropertyManager } from '@affine/core/components/doc-properties/manag
|
|||||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
||||||
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
|
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { FrameworkScope, type WorkspaceMetadata } from '@toeverything/infra';
|
import track from '@affine/track';
|
||||||
|
import {
|
||||||
|
type DocCustomPropertyInfo,
|
||||||
|
FrameworkScope,
|
||||||
|
type WorkspaceMetadata,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useWorkspace } from '../../../../../components/hooks/use-workspace';
|
import { useWorkspace } from '../../../../../components/hooks/use-workspace';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
@ -12,10 +18,17 @@ import * as styles from './styles.css';
|
|||||||
const WorkspaceSettingPropertiesMain = () => {
|
const WorkspaceSettingPropertiesMain = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
|
const onCreated = useCallback((property: DocCustomPropertyInfo) => {
|
||||||
|
track.$.settingsPanel.workspace.addProperty({
|
||||||
|
type: property.type,
|
||||||
|
control: 'at menu',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<div className={styles.listHeader}>
|
<div className={styles.listHeader}>
|
||||||
<Menu items={<CreatePropertyMenuItems />}>
|
<Menu items={<CreatePropertyMenuItems onCreated={onCreated} />}>
|
||||||
<Button variant="primary">
|
<Button variant="primary">
|
||||||
{t['com.affine.settings.workspace.properties.add_property']()}
|
{t['com.affine.settings.workspace.properties.add_property']()}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -32,4 +32,5 @@ export interface DatabaseCellRendererProps {
|
|||||||
rowId: string;
|
rowId: string;
|
||||||
cell: DatabaseValueCell;
|
cell: DatabaseValueCell;
|
||||||
dataSource: DatabaseBlockDataSource;
|
dataSource: DatabaseBlockDataSource;
|
||||||
|
onChange: (value: unknown) => void;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ export const CheckboxCell = ({
|
|||||||
cell,
|
cell,
|
||||||
rowId,
|
rowId,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const value = useLiveData(cell.value$ as LiveData<boolean>);
|
const value = useLiveData(cell.value$ as LiveData<boolean>);
|
||||||
return (
|
return (
|
||||||
@ -16,6 +17,7 @@ export const CheckboxCell = ({
|
|||||||
value={value ? 'true' : 'false'}
|
value={value ? 'true' : 'false'}
|
||||||
onChange={v => {
|
onChange={v => {
|
||||||
dataSource.cellValueChange(rowId, cell.property.id, v === 'true');
|
dataSource.cellValueChange(rowId, cell.property.id, v === 'true');
|
||||||
|
onChange?.(v === 'true');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -20,6 +20,7 @@ export const DateCell = ({
|
|||||||
cell,
|
cell,
|
||||||
rowId,
|
rowId,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const value = useLiveData(
|
const value = useLiveData(
|
||||||
cell.value$ as LiveData<number | string | undefined>
|
cell.value$ as LiveData<number | string | undefined>
|
||||||
@ -34,6 +35,7 @@ export const DateCell = ({
|
|||||||
cell.property.id,
|
cell.property.id,
|
||||||
fromInternalDateString(v)
|
fromInternalDateString(v)
|
||||||
);
|
);
|
||||||
|
onChange?.(fromInternalDateString(v));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -21,6 +21,7 @@ export const LinkCell = ({
|
|||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const isEmpty = useLiveData(
|
const isEmpty = useLiveData(
|
||||||
cell.value$.map(value => typeof value !== 'string' || !value)
|
cell.value$.map(value => typeof value !== 'string' || !value)
|
||||||
@ -35,7 +36,8 @@ export const LinkCell = ({
|
|||||||
dataSource.cellValueChange(rowId, cell.id, tempValue.trim());
|
dataSource.cellValueChange(rowId, cell.id, tempValue.trim());
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
setTempValue(tempValue.trim());
|
setTempValue(tempValue.trim());
|
||||||
}, [dataSource, rowId, cell.id, tempValue]);
|
onChange?.(tempValue.trim());
|
||||||
|
}, [dataSource, rowId, cell.id, onChange, tempValue]);
|
||||||
|
|
||||||
const handleOnChange: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
|
const handleOnChange: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
|
||||||
e => {
|
e => {
|
||||||
|
@ -7,6 +7,7 @@ export const NumberCell = ({
|
|||||||
cell,
|
cell,
|
||||||
rowId,
|
rowId,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const value = useLiveData(cell.value$);
|
const value = useLiveData(cell.value$);
|
||||||
return (
|
return (
|
||||||
@ -14,6 +15,7 @@ export const NumberCell = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={v => {
|
onChange={v => {
|
||||||
dataSource.cellValueChange(rowId, cell.property.id, v);
|
dataSource.cellValueChange(rowId, cell.property.id, v);
|
||||||
|
onChange?.(v);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ export const ProgressCell = ({
|
|||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const value = useLiveData(cell.value$ as LiveData<number>);
|
const value = useLiveData(cell.value$ as LiveData<number>);
|
||||||
const isEmpty = value === undefined;
|
const isEmpty = value === undefined;
|
||||||
@ -27,6 +28,7 @@ export const ProgressCell = ({
|
|||||||
}}
|
}}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
dataSource.cellValueChange(rowId, cell.id, localValue);
|
dataSource.cellValueChange(rowId, cell.id, localValue);
|
||||||
|
onChange?.(localValue);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PropertyValue>
|
</PropertyValue>
|
||||||
|
@ -40,6 +40,7 @@ const renderRichText = ({
|
|||||||
export const RichTextCell = ({
|
export const RichTextCell = ({
|
||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const std = useBlockStdScope(dataSource.doc);
|
const std = useBlockStdScope(dataSource.doc);
|
||||||
const text = useLiveData(cell.value$ as LiveData<Y.Text>);
|
const text = useLiveData(cell.value$ as LiveData<Y.Text>);
|
||||||
@ -49,14 +50,19 @@ export const RichTextCell = ({
|
|||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
ref.current.innerHTML = '';
|
ref.current.innerHTML = '';
|
||||||
const richText = renderRichText({ doc: dataSource.doc, std, text });
|
const richText = renderRichText({ doc: dataSource.doc, std, text });
|
||||||
|
const listener = () => {
|
||||||
|
onChange(text);
|
||||||
|
};
|
||||||
if (richText) {
|
if (richText) {
|
||||||
|
richText.addEventListener('change', listener);
|
||||||
ref.current.append(richText);
|
ref.current.append(richText);
|
||||||
return () => {
|
return () => {
|
||||||
|
richText.removeEventListener('change', listener);
|
||||||
richText.remove();
|
richText.remove();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [dataSource.doc, std, text]);
|
}, [dataSource.doc, onChange, std, text]);
|
||||||
return <PropertyValue ref={ref}></PropertyValue>;
|
return <PropertyValue ref={ref}></PropertyValue>;
|
||||||
};
|
};
|
||||||
|
@ -147,6 +147,7 @@ const BlocksuiteDatabaseSelector = ({
|
|||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
multiple,
|
multiple,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps & { multiple: boolean }) => {
|
}: DatabaseCellRendererProps & { multiple: boolean }) => {
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const selectCell = cell as any as SingleSelectCell | MultiSelectCell;
|
const selectCell = cell as any as SingleSelectCell | MultiSelectCell;
|
||||||
@ -177,21 +178,24 @@ const BlocksuiteDatabaseSelector = ({
|
|||||||
const onDeleteTag = useCallback(
|
const onDeleteTag = useCallback(
|
||||||
(tagId: string) => {
|
(tagId: string) => {
|
||||||
adapter.deleteTag(selectCell, dataSource, tagId);
|
adapter.deleteTag(selectCell, dataSource, tagId);
|
||||||
|
onChange?.(selectCell.value$.value);
|
||||||
},
|
},
|
||||||
[dataSource, selectCell]
|
[dataSource, selectCell, onChange]
|
||||||
);
|
);
|
||||||
const onDeselectTag = useCallback(
|
const onDeselectTag = useCallback(
|
||||||
(tagId: string) => {
|
(tagId: string) => {
|
||||||
adapter.deselectTag(rowId, selectCell, dataSource, tagId, multiple);
|
adapter.deselectTag(rowId, selectCell, dataSource, tagId, multiple);
|
||||||
|
onChange?.(selectCell.value$.value);
|
||||||
},
|
},
|
||||||
[selectCell, dataSource, rowId, multiple]
|
[rowId, selectCell, dataSource, multiple, onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectTag = useCallback(
|
const onSelectTag = useCallback(
|
||||||
(tagId: string) => {
|
(tagId: string) => {
|
||||||
adapter.selectTag(rowId, selectCell, dataSource, tagId, multiple);
|
adapter.selectTag(rowId, selectCell, dataSource, tagId, multiple);
|
||||||
|
onChange?.(selectCell.value$.value);
|
||||||
},
|
},
|
||||||
[rowId, selectCell, dataSource, multiple]
|
[rowId, selectCell, dataSource, multiple, onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tagColors = useMemo(() => {
|
const tagColors = useMemo(() => {
|
||||||
@ -237,6 +241,7 @@ export const SelectCell = ({
|
|||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const isEmpty = useLiveData(
|
const isEmpty = useLiveData(
|
||||||
cell.value$.map(value => Array.isArray(value) && value.length === 0)
|
cell.value$.map(value => Array.isArray(value) && value.length === 0)
|
||||||
@ -248,6 +253,7 @@ export const SelectCell = ({
|
|||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
rowId={rowId}
|
rowId={rowId}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</PropertyValue>
|
</PropertyValue>
|
||||||
);
|
);
|
||||||
@ -257,6 +263,7 @@ export const MultiSelectCell = ({
|
|||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
|
onChange,
|
||||||
}: DatabaseCellRendererProps) => {
|
}: DatabaseCellRendererProps) => {
|
||||||
const isEmpty = useLiveData(
|
const isEmpty = useLiveData(
|
||||||
cell.value$.map(value => Array.isArray(value) && value.length === 0)
|
cell.value$.map(value => Array.isArray(value) && value.length === 0)
|
||||||
@ -268,6 +275,7 @@ export const MultiSelectCell = ({
|
|||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
rowId={rowId}
|
rowId={rowId}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</PropertyValue>
|
</PropertyValue>
|
||||||
);
|
);
|
||||||
|
@ -46,10 +46,12 @@ const DatabaseBacklinkCell = ({
|
|||||||
cell,
|
cell,
|
||||||
dataSource,
|
dataSource,
|
||||||
rowId,
|
rowId,
|
||||||
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
cell: DatabaseValueCell;
|
cell: DatabaseValueCell;
|
||||||
dataSource: DatabaseBlockDataSource;
|
dataSource: DatabaseBlockDataSource;
|
||||||
rowId: string;
|
rowId: string;
|
||||||
|
onChange: (value: unknown) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const cellType = useLiveData(cell.property.type$);
|
const cellType = useLiveData(cell.property.type$);
|
||||||
|
|
||||||
@ -67,7 +69,12 @@ const DatabaseBacklinkCell = ({
|
|||||||
data-testid="database-backlink-cell"
|
data-testid="database-backlink-cell"
|
||||||
>
|
>
|
||||||
<DatabaseBacklinkCellName cell={cell} config={config} />
|
<DatabaseBacklinkCellName cell={cell} config={config} />
|
||||||
<config.Renderer cell={cell} dataSource={dataSource} rowId={rowId} />
|
<config.Renderer
|
||||||
|
cell={cell}
|
||||||
|
dataSource={dataSource}
|
||||||
|
rowId={rowId}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -79,9 +86,15 @@ const DatabaseBacklinkCell = ({
|
|||||||
const DatabaseBacklinkRow = ({
|
const DatabaseBacklinkRow = ({
|
||||||
defaultOpen = false,
|
defaultOpen = false,
|
||||||
row$,
|
row$,
|
||||||
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
defaultOpen: boolean;
|
defaultOpen: boolean;
|
||||||
row$: Observable<DatabaseRow | undefined>;
|
row$: Observable<DatabaseRow | undefined>;
|
||||||
|
onChange?: (
|
||||||
|
row: DatabaseRow,
|
||||||
|
cell: DatabaseValueCell,
|
||||||
|
value: unknown
|
||||||
|
) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const row = useLiveData(
|
const row = useLiveData(
|
||||||
useMemo(() => LiveData.from(row$, undefined), [row$])
|
useMemo(() => LiveData.from(row$, undefined), [row$])
|
||||||
@ -132,6 +145,7 @@ const DatabaseBacklinkRow = ({
|
|||||||
cell={cell}
|
cell={cell}
|
||||||
dataSource={row.dataSource}
|
dataSource={row.dataSource}
|
||||||
rowId={row.id}
|
rowId={row.id}
|
||||||
|
onChange={value => onChange?.(row, cell, value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -142,11 +156,17 @@ const DatabaseBacklinkRow = ({
|
|||||||
|
|
||||||
export const DocDatabaseBacklinkInfo = ({
|
export const DocDatabaseBacklinkInfo = ({
|
||||||
defaultOpen = [],
|
defaultOpen = [],
|
||||||
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
defaultOpen?: {
|
defaultOpen?: {
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
rowId: string;
|
rowId: string;
|
||||||
}[];
|
}[];
|
||||||
|
onChange?: (
|
||||||
|
row: DatabaseRow,
|
||||||
|
cell: DatabaseValueCell,
|
||||||
|
value: unknown
|
||||||
|
) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const doc = useService(DocService).doc;
|
const doc = useService(DocService).doc;
|
||||||
const docDatabaseBacklinks = useService(DocDatabaseBacklinksService);
|
const docDatabaseBacklinks = useService(DocDatabaseBacklinksService);
|
||||||
@ -173,6 +193,7 @@ export const DocDatabaseBacklinkInfo = ({
|
|||||||
backlink.rowId === rowId
|
backlink.rowId === rowId
|
||||||
)}
|
)}
|
||||||
row$={row$}
|
row$={row$}
|
||||||
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<Divider size="thinner" className={styles.divider} />
|
<Divider size="thinner" className={styles.divider} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -46,7 +46,9 @@ type DocEvents =
|
|||||||
| 'openDocOptionsMenu'
|
| 'openDocOptionsMenu'
|
||||||
| 'openDocInfo'
|
| 'openDocInfo'
|
||||||
| 'copyBlockToLink'
|
| 'copyBlockToLink'
|
||||||
| 'bookmark';
|
| 'bookmark'
|
||||||
|
| 'editProperty'
|
||||||
|
| 'addProperty';
|
||||||
type EditorEvents = 'bold' | 'italic' | 'underline' | 'strikeThrough';
|
type EditorEvents = 'bold' | 'italic' | 'underline' | 'strikeThrough';
|
||||||
// END SECTION
|
// END SECTION
|
||||||
|
|
||||||
@ -148,10 +150,12 @@ const PageEvents = {
|
|||||||
},
|
},
|
||||||
docInfoPanel: {
|
docInfoPanel: {
|
||||||
$: ['open'],
|
$: ['open'],
|
||||||
|
property: ['editProperty', 'addProperty'],
|
||||||
|
databaseProperty: ['editProperty'],
|
||||||
},
|
},
|
||||||
settingsPanel: {
|
settingsPanel: {
|
||||||
menu: ['openSettings'],
|
menu: ['openSettings'],
|
||||||
workspace: ['viewPlans', 'export'],
|
workspace: ['viewPlans', 'export', 'addProperty'],
|
||||||
profileAndBadge: ['viewPlans'],
|
profileAndBadge: ['viewPlans'],
|
||||||
accountUsage: ['viewPlans'],
|
accountUsage: ['viewPlans'],
|
||||||
accountSettings: ['uploadAvatar', 'removeAvatar', 'updateUserName'],
|
accountSettings: ['uploadAvatar', 'removeAvatar', 'updateUserName'],
|
||||||
@ -277,6 +281,11 @@ const PageEvents = {
|
|||||||
},
|
},
|
||||||
inlineDocInfo: {
|
inlineDocInfo: {
|
||||||
$: ['toggle'],
|
$: ['toggle'],
|
||||||
|
property: ['editProperty', 'addProperty'],
|
||||||
|
databaseProperty: ['editProperty'],
|
||||||
|
},
|
||||||
|
sidepanel: {
|
||||||
|
property: ['addProperty'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// remove when type added
|
// remove when type added
|
||||||
@ -413,6 +422,8 @@ export type EventArgs = {
|
|||||||
copyBlockToLink: {
|
copyBlockToLink: {
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
editProperty: { type: string };
|
||||||
|
addProperty: { type: string; control: 'at menu' | 'property list' };
|
||||||
};
|
};
|
||||||
|
|
||||||
// for type checking
|
// for type checking
|
||||||
|
Loading…
Reference in New Issue
Block a user