mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
fix(mobile): doc property styles (#8760)
fix AF-1582 fix AF-1671 - mobile doc info dialog styles - added ConfigModal for editing property values in modal, including: - workspace properties: text, number, tags - db properties: text, number, label, link
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
export * from './app-tabs';
|
||||
export * from './doc-card';
|
||||
export * from './page-header';
|
||||
export * from './rename';
|
||||
export * from './search-input';
|
||||
export * from './search-result';
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import { IconButton, SafeArea } from '@affine/component';
|
||||
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
forwardRef,
|
||||
type HtmlHTMLAttributes,
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export interface PageHeaderProps
|
||||
extends Omit<HtmlHTMLAttributes<HTMLHeadElement>, 'prefix'> {
|
||||
/**
|
||||
* whether to show back button
|
||||
*/
|
||||
back?: boolean;
|
||||
/**
|
||||
* Override back button action
|
||||
*/
|
||||
backAction?: () => void;
|
||||
|
||||
/**
|
||||
* prefix content, shown after back button(if exists)
|
||||
*/
|
||||
prefix?: ReactNode;
|
||||
|
||||
/**
|
||||
* suffix content
|
||||
*/
|
||||
suffix?: ReactNode;
|
||||
|
||||
/**
|
||||
* Weather to center the content
|
||||
* @default true
|
||||
*/
|
||||
centerContent?: boolean;
|
||||
|
||||
prefixClassName?: string;
|
||||
prefixStyle?: React.CSSProperties;
|
||||
suffixClassName?: string;
|
||||
suffixStyle?: React.CSSProperties;
|
||||
}
|
||||
export const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
|
||||
function PageHeader(
|
||||
{
|
||||
back,
|
||||
backAction,
|
||||
prefix,
|
||||
suffix,
|
||||
children,
|
||||
className,
|
||||
centerContent = true,
|
||||
prefixClassName,
|
||||
prefixStyle,
|
||||
suffixClassName,
|
||||
suffixStyle,
|
||||
...attrs
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const handleRouteBack = useCallback(() => {
|
||||
backAction ? backAction() : history.back();
|
||||
}, [backAction]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SafeArea
|
||||
top
|
||||
ref={ref}
|
||||
className={clsx(styles.root, className)}
|
||||
data-testid="mobile-page-header"
|
||||
{...attrs}
|
||||
>
|
||||
<header className={styles.inner}>
|
||||
<section
|
||||
className={clsx(styles.prefix, prefixClassName)}
|
||||
style={prefixStyle}
|
||||
>
|
||||
{back ? (
|
||||
<IconButton
|
||||
size={24}
|
||||
style={{ padding: 10 }}
|
||||
onClick={handleRouteBack}
|
||||
icon={<ArrowLeftSmallIcon />}
|
||||
data-testid="page-header-back"
|
||||
/>
|
||||
) : null}
|
||||
{prefix}
|
||||
</section>
|
||||
|
||||
<section
|
||||
className={clsx(styles.content, { center: centerContent })}
|
||||
>
|
||||
{children}
|
||||
</section>
|
||||
|
||||
<section
|
||||
className={clsx(styles.suffix, suffixClassName)}
|
||||
style={suffixStyle}
|
||||
>
|
||||
{suffix}
|
||||
</section>
|
||||
</header>
|
||||
</SafeArea>
|
||||
|
||||
{/* Spacer */}
|
||||
<SafeArea top>
|
||||
<div className={styles.headerSpacer} />
|
||||
</SafeArea>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1,52 +0,0 @@
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const root = style({
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
backgroundColor: cssVarV2('layer/background/secondary'),
|
||||
});
|
||||
export const headerSpacer = style({
|
||||
height: 44,
|
||||
});
|
||||
export const inner = style({
|
||||
height: 44,
|
||||
padding: '0 6px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
export const content = style({
|
||||
selectors: {
|
||||
'&.center': {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
width: 'fit-content',
|
||||
maxWidth: 'calc(100% - 12px - 88px - 16px)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
'&:not(.center)': {
|
||||
width: 0,
|
||||
flex: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
export const spacer = style({
|
||||
width: 0,
|
||||
flex: 1,
|
||||
});
|
||||
export const prefix = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 0,
|
||||
});
|
||||
export const suffix = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Scrollable,
|
||||
useThemeColorMeta,
|
||||
} from '@affine/component';
|
||||
import { PageHeader } from '@affine/core/components/mobile';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { PageHeader } from '../../components/page-header';
|
||||
import * as styles from './generic.css';
|
||||
|
||||
export interface GenericSelectorProps {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { footnoteRegular } from '@toeverything/theme/typography';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const group = style({
|
||||
@@ -8,20 +6,3 @@ export const group = style({
|
||||
gap: 4,
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
export const title = style([
|
||||
footnoteRegular,
|
||||
{
|
||||
padding: '0px 8px',
|
||||
color: cssVarV2('text/tertiary'),
|
||||
},
|
||||
]);
|
||||
|
||||
export const content = style({
|
||||
background: cssVarV2('layer/background/primary'),
|
||||
borderRadius: 12,
|
||||
padding: '10px 16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ConfigModal } from '@affine/core/components/mobile';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
type CSSProperties,
|
||||
@@ -21,15 +22,16 @@ export const SettingGroup = forwardRef<HTMLDivElement, SettingGroupProps>(
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div className={clsx(styles.group, className)} ref={ref} {...attrs}>
|
||||
{title ? <h6 className={styles.title}>{title}</h6> : null}
|
||||
<div
|
||||
className={clsx(styles.content, contentClassName)}
|
||||
style={contentStyle}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<ConfigModal.RowGroup
|
||||
{...attrs}
|
||||
ref={ref}
|
||||
title={title}
|
||||
className={clsx(styles.group, className)}
|
||||
contentClassName={contentClassName}
|
||||
contentStyle={contentStyle}
|
||||
>
|
||||
{children}
|
||||
</ConfigModal.RowGroup>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Modal } from '@affine/component';
|
||||
import { ConfigModal } from '@affine/core/components/mobile';
|
||||
import { AuthService } from '@affine/core/modules/cloud';
|
||||
import type {
|
||||
DialogComponentProps,
|
||||
@@ -6,10 +6,8 @@ import type {
|
||||
} from '@affine/core/modules/dialogs';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { PageHeader } from '../../components';
|
||||
import { AboutGroup } from './about';
|
||||
import { AppearanceGroup } from './appearance';
|
||||
import { OthersGroup } from './others';
|
||||
@@ -17,50 +15,34 @@ import * as styles from './style.css';
|
||||
import { UserProfile } from './user-profile';
|
||||
import { UserUsage } from './user-usage';
|
||||
|
||||
const MobileSetting = ({ onClose }: { onClose: () => void }) => {
|
||||
const t = useI18n();
|
||||
const MobileSetting = () => {
|
||||
const session = useService(AuthService).session;
|
||||
|
||||
useEffect(() => session.revalidate(), [session]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader back backAction={onClose}>
|
||||
<span className={styles.pageTitle}>
|
||||
{t['com.affine.mobile.setting.header-title']()}
|
||||
</span>
|
||||
</PageHeader>
|
||||
|
||||
<div className={styles.root}>
|
||||
<UserProfile />
|
||||
<UserUsage />
|
||||
<AppearanceGroup />
|
||||
<AboutGroup />
|
||||
<OthersGroup />
|
||||
</div>
|
||||
</>
|
||||
<div className={styles.root}>
|
||||
<UserProfile />
|
||||
<UserUsage />
|
||||
<AppearanceGroup />
|
||||
<AboutGroup />
|
||||
<OthersGroup />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingDialog = ({
|
||||
close,
|
||||
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['setting']>) => {
|
||||
const t = useI18n();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
fullScreen
|
||||
animation="slideBottom"
|
||||
<ConfigModal
|
||||
title={t['com.affine.mobile.setting.header-title']()}
|
||||
open
|
||||
onOpenChange={() => close()}
|
||||
contentOptions={{
|
||||
style: {
|
||||
padding: 0,
|
||||
overflowY: 'auto',
|
||||
backgroundColor: cssVarV2('layer/background/secondary'),
|
||||
},
|
||||
}}
|
||||
withoutCloseButton
|
||||
onBack={close}
|
||||
>
|
||||
<MobileSetting onClose={close} />
|
||||
</Modal>
|
||||
<MobileSetting />
|
||||
</ConfigModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ConfigModal } from '@affine/core/components/mobile';
|
||||
import { DualLinkIcon } from '@blocksuite/icons/rc';
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
@@ -9,13 +10,13 @@ export const RowLayout = ({
|
||||
href,
|
||||
}: PropsWithChildren<{ label: ReactNode; href?: string }>) => {
|
||||
const content = (
|
||||
<div className={styles.baseSettingItem}>
|
||||
<ConfigModal.Row className={styles.baseSettingItem}>
|
||||
<div className={styles.baseSettingItemName}>{label}</div>
|
||||
<div className={styles.baseSettingItemAction}>
|
||||
{children ||
|
||||
(href ? <DualLinkIcon className={styles.linkIcon} /> : null)}
|
||||
</div>
|
||||
</div>
|
||||
</ConfigModal.Row>
|
||||
);
|
||||
|
||||
return href ? (
|
||||
|
||||
@@ -5,7 +5,6 @@ import { style } from '@vanilla-extract/css';
|
||||
export const pageTitle = style([bodyEmphasized]);
|
||||
|
||||
export const root = style({
|
||||
padding: '24px 16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 16,
|
||||
@@ -16,8 +15,9 @@ export const baseSettingItem = style({
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 32,
|
||||
padding: '8px 0',
|
||||
padding: 8,
|
||||
});
|
||||
|
||||
export const baseSettingItemName = style([
|
||||
bodyRegular,
|
||||
{
|
||||
@@ -26,6 +26,7 @@ export const baseSettingItemName = style([
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
]);
|
||||
|
||||
export const baseSettingItemAction = style([
|
||||
baseSettingItemName,
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ const UsagePanel = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingGroup title="Storage">
|
||||
<SettingGroup title="Storage" contentStyle={{ padding: '10px 16px' }}>
|
||||
<CloudUsage />
|
||||
{serverFeatures?.copilot ? <AiUsage /> : null}
|
||||
</SettingGroup>
|
||||
|
||||
@@ -2,9 +2,7 @@ import { bodyRegular, caption1Regular } from '@toeverything/theme/typography';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const progressRoot = style({
|
||||
paddingBottom: 8,
|
||||
});
|
||||
export const progressRoot = style({});
|
||||
export const progressInfoRow = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-
|
||||
import { usePageDocumentTitle } from '@affine/core/components/hooks/use-global-state';
|
||||
import { useJournalRouteHelper } from '@affine/core/components/hooks/use-journal';
|
||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||
import { PageHeader } from '@affine/core/components/mobile';
|
||||
import { PageDetailEditor } from '@affine/core/components/page-detail-editor';
|
||||
import { DetailPageWrapper } from '@affine/core/desktop/pages/workspace/detail-page/detail-page-wrapper';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
@@ -42,7 +43,7 @@ import dayjs from 'dayjs';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { AppTabs, PageHeader } from '../../../components';
|
||||
import { AppTabs } from '../../../components';
|
||||
import { JournalDatePicker } from './journal-date-picker';
|
||||
import * as styles from './mobile-detail-page.css';
|
||||
import { PageHeaderMenuButton } from './page-header-more-button';
|
||||
@@ -215,12 +216,12 @@ const notFound = (
|
||||
</>
|
||||
);
|
||||
|
||||
const JournalDetailPage = ({
|
||||
const MobileDetailPage = ({
|
||||
pageId,
|
||||
date,
|
||||
}: {
|
||||
pageId: string;
|
||||
date: string;
|
||||
date?: string;
|
||||
}) => {
|
||||
const journalService = useService(JournalService);
|
||||
const { openJournal } = useJournalRouteHelper();
|
||||
@@ -250,40 +251,23 @@ const JournalDetailPage = ({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<span className={bodyEmphasized}>
|
||||
{i18nTime(dayjs(date), { absolute: { accuracy: 'month' } })}
|
||||
</span>
|
||||
{date ? (
|
||||
<span className={bodyEmphasized}>
|
||||
{i18nTime(dayjs(date), { absolute: { accuracy: 'month' } })}
|
||||
</span>
|
||||
) : null}
|
||||
</PageHeader>
|
||||
<JournalDatePicker
|
||||
date={date}
|
||||
onChange={handleDateChange}
|
||||
withDotDates={allJournalDates}
|
||||
/>
|
||||
<DetailPageImpl />
|
||||
<AppTabs background={cssVarV2('layer/background/primary')} />
|
||||
</DetailPageWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const NormalDetailPage = ({ pageId }: { pageId: string }) => {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<DetailPageWrapper
|
||||
skeleton={skeleton}
|
||||
notFound={notFound}
|
||||
pageId={pageId}
|
||||
>
|
||||
<PageHeader
|
||||
back
|
||||
className={styles.header}
|
||||
suffix={
|
||||
<>
|
||||
<PageHeaderShareButton />
|
||||
<PageHeaderMenuButton />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{date ? (
|
||||
<JournalDatePicker
|
||||
date={date}
|
||||
onChange={handleDateChange}
|
||||
withDotDates={allJournalDates}
|
||||
/>
|
||||
) : null}
|
||||
<DetailPageImpl />
|
||||
{date ? (
|
||||
<AppTabs background={cssVarV2('layer/background/primary')} />
|
||||
) : null}
|
||||
</DetailPageWrapper>
|
||||
</div>
|
||||
);
|
||||
@@ -300,9 +284,5 @@ export const Component = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return journalDate ? (
|
||||
<JournalDetailPage pageId={pageId} date={journalDate} />
|
||||
) : (
|
||||
<NormalDetailPage pageId={pageId} />
|
||||
);
|
||||
return <MobileDetailPage pageId={pageId} date={journalDate} />;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconButton, notify } from '@affine/component';
|
||||
import {
|
||||
MenuSeparator,
|
||||
MenuSub,
|
||||
MobileMenu,
|
||||
MobileMenuItem,
|
||||
} from '@affine/component/ui/menu';
|
||||
@@ -42,6 +43,7 @@ export const PageHeaderMenuButton = () => {
|
||||
editorService.editor.doc.meta$.map(meta => meta.trash)
|
||||
);
|
||||
const primaryMode = useLiveData(editorService.editor.doc.primaryMode$);
|
||||
const title = useLiveData(editorService.editor.doc.title$);
|
||||
|
||||
const { favorite, toggleFavorite } = useFavorite(docId);
|
||||
|
||||
@@ -104,14 +106,16 @@ export const PageHeaderMenuButton = () => {
|
||||
: t['com.affine.favoritePageOperation.add']()}
|
||||
</MobileMenuItem>
|
||||
<MenuSeparator />
|
||||
<MobileMenu items={<DocInfoSheet docId={docId} />}>
|
||||
<MobileMenuItem
|
||||
prefixIcon={<InformationIcon />}
|
||||
onClick={preventDefault}
|
||||
>
|
||||
<span>{t['com.affine.page-properties.page-info.view']()}</span>
|
||||
</MobileMenuItem>
|
||||
</MobileMenu>
|
||||
<MenuSub
|
||||
triggerOptions={{
|
||||
prefixIcon: <InformationIcon />,
|
||||
onClick: preventDefault,
|
||||
}}
|
||||
title={title ?? t['unnamed']()}
|
||||
items={<DocInfoSheet docId={docId} />}
|
||||
>
|
||||
<span>{t['com.affine.page-properties.page-info.view']()}</span>
|
||||
</MenuSub>
|
||||
<MobileMenu
|
||||
items={
|
||||
<div className={styles.outlinePanel}>
|
||||
|
||||
@@ -1,13 +1,49 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
export const linksRow = style({
|
||||
export const scrollableRoot = style({
|
||||
padding: '0 16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
export const timeRow = style({
|
||||
padding: '0 16px',
|
||||
});
|
||||
export const linksRow = style({});
|
||||
|
||||
export const timeRow = style({});
|
||||
export const scrollBar = style({
|
||||
width: 6,
|
||||
transform: 'translateX(-4px)',
|
||||
});
|
||||
|
||||
export const tableBodyRoot = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
});
|
||||
export const addPropertyButton = style({
|
||||
alignSelf: 'flex-start',
|
||||
fontSize: cssVar('fontSm'),
|
||||
color: `${cssVarV2('text/secondary')}`,
|
||||
padding: '0 4px',
|
||||
height: 36,
|
||||
fontWeight: 400,
|
||||
gap: 6,
|
||||
'@media': {
|
||||
print: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
[`[data-property-collapsed="true"] &`]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
globalStyle(`${addPropertyButton} svg`, {
|
||||
fontSize: 16,
|
||||
color: cssVarV2('icon/secondary'),
|
||||
});
|
||||
globalStyle(`${addPropertyButton}:hover svg`, {
|
||||
color: cssVarV2('icon/primary'),
|
||||
});
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import { Divider, Scrollable } from '@affine/component';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Menu,
|
||||
PropertyCollapsibleContent,
|
||||
PropertyCollapsibleSection,
|
||||
Scrollable,
|
||||
} from '@affine/component';
|
||||
import {
|
||||
type DefaultOpenProperty,
|
||||
DocPropertiesTable,
|
||||
DocPropertyRow,
|
||||
} from '@affine/core/components/doc-properties';
|
||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
||||
import { LinksRow } from '@affine/core/desktop/dialogs/doc-info/links-row';
|
||||
import { TimeRow } from '@affine/core/desktop/dialogs/doc-info/time-row';
|
||||
import { DocDatabaseBacklinkInfo } from '@affine/core/modules/doc-info';
|
||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { LiveData, useLiveData, useService } from '@toeverything/infra';
|
||||
import { Suspense, useMemo } from 'react';
|
||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||
import {
|
||||
type DocCustomPropertyInfo,
|
||||
DocsService,
|
||||
LiveData,
|
||||
useLiveData,
|
||||
useServices,
|
||||
} from '@toeverything/infra';
|
||||
import { Suspense, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import * as styles from './doc-info.css';
|
||||
|
||||
export const DocInfoSheet = ({
|
||||
docId,
|
||||
defaultOpenProperty,
|
||||
}: {
|
||||
docId: string;
|
||||
defaultOpenProperty?: DefaultOpenProperty;
|
||||
}) => {
|
||||
const docsSearchService = useService(DocsSearchService);
|
||||
const { docsSearchService, docsService } = useServices({
|
||||
DocsSearchService,
|
||||
DocsService,
|
||||
});
|
||||
const t = useI18n();
|
||||
|
||||
const links = useLiveData(
|
||||
@@ -35,8 +53,16 @@ export const DocInfoSheet = ({
|
||||
)
|
||||
);
|
||||
|
||||
const [newPropertyId, setNewPropertyId] = useState<string | null>(null);
|
||||
|
||||
const onPropertyAdded = useCallback((property: DocCustomPropertyInfo) => {
|
||||
setNewPropertyId(property.id);
|
||||
}, []);
|
||||
|
||||
const properties = useLiveData(docsService.propertyList.sortedProperties$);
|
||||
|
||||
return (
|
||||
<Scrollable.Root>
|
||||
<Scrollable.Root className={styles.scrollableRoot}>
|
||||
<Scrollable.Viewport data-testid="doc-info-menu">
|
||||
<Suspense>
|
||||
<TimeRow docId={docId} className={styles.timeRow} />
|
||||
@@ -61,7 +87,58 @@ export const DocInfoSheet = ({
|
||||
<Divider size="thinner" />
|
||||
</>
|
||||
) : null}
|
||||
<DocPropertiesTable defaultOpenProperty={defaultOpenProperty} />
|
||||
|
||||
<PropertyCollapsibleSection
|
||||
title={t.t('com.affine.workspace.properties')}
|
||||
>
|
||||
<PropertyCollapsibleContent
|
||||
className={styles.tableBodyRoot}
|
||||
collapseButtonText={({ hide, isCollapsed }) =>
|
||||
isCollapsed
|
||||
? 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 => (
|
||||
<DocPropertyRow
|
||||
key={property.id}
|
||||
propertyInfo={property}
|
||||
defaultOpenEditMenu={newPropertyId === property.id}
|
||||
/>
|
||||
))}
|
||||
<Menu
|
||||
items={<CreatePropertyMenuItems onCreated={onPropertyAdded} />}
|
||||
contentOptions={{
|
||||
onClick(e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="plain"
|
||||
prefix={<PlusIcon />}
|
||||
className={styles.addPropertyButton}
|
||||
>
|
||||
{t['com.affine.page-properties.add-property']()}
|
||||
</Button>
|
||||
</Menu>
|
||||
</PropertyCollapsibleContent>
|
||||
</PropertyCollapsibleSection>
|
||||
<Divider size="thinner" />
|
||||
|
||||
<DocDatabaseBacklinkInfo />
|
||||
</Suspense>
|
||||
</Scrollable.Viewport>
|
||||
<Scrollable.Scrollbar className={styles.scrollBar} />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { IconButton, MobileMenu } from '@affine/component';
|
||||
import { EmptyCollectionDetail } from '@affine/core/components/affine/empty';
|
||||
import { PageHeader } from '@affine/core/components/mobile';
|
||||
import { isEmptyCollection } from '@affine/core/desktop/pages/workspace/collection';
|
||||
import type { Collection } from '@affine/env/filter';
|
||||
import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons/rc';
|
||||
|
||||
import { PageHeader } from '../../../components';
|
||||
import { AllDocList } from '../doc/list';
|
||||
import { AllDocsMenu } from '../doc/menu';
|
||||
import * as styles from './detail.css';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { IconButton, MobileMenu } from '@affine/component';
|
||||
import { PageHeader } from '@affine/core/components/mobile';
|
||||
import type { Tag } from '@affine/core/modules/tag';
|
||||
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData } from '@toeverything/infra';
|
||||
|
||||
import { PageHeader } from '../../../components';
|
||||
import { AllDocsMenu } from '../doc';
|
||||
import * as styles from './detail.css';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user