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:
pengx17
2024-11-12 07:11:00 +00:00
parent 2ee2cbfe36
commit fa82842cd7
67 changed files with 1460 additions and 492 deletions
@@ -33,6 +33,26 @@ export const textarea = style({
},
});
const mobileTextareaBase = {
fontSize: 17,
lineHeight: '26px',
padding: 12,
};
export const mobileTextareaPlain = style([
textarea,
mobileTextareaBase,
{
position: 'relative',
fontSize: 14,
lineHeight: '22px',
height: 'auto',
padding: 0,
},
]);
export const mobileTextarea = style([textarea, mobileTextareaBase]);
export const container = style({
position: 'relative',
outline: `1px solid transparent`,
@@ -54,3 +74,11 @@ export const textInvisible = style({
fontSize: cssVar('fontSm'),
lineHeight: '22px',
});
export const mobileTextInvisible = style([textInvisible, mobileTextareaBase]);
export const mobileTextareaWrapper = style({
position: 'relative',
background: cssVarV2('layer/background/primary'),
borderRadius: 12,
});
@@ -1,7 +1,9 @@
import { PropertyValue } from '@affine/component';
import { AffinePageReference } from '@affine/core/components/affine/reference-link';
import { ConfigModal } from '@affine/core/components/mobile';
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
import { useI18n } from '@affine/i18n';
import { LinkIcon } from '@blocksuite/icons/rc';
import type { LiveData } from '@toeverything/infra';
import { useLiveData } from '@toeverything/infra';
import {
@@ -99,49 +101,84 @@ export const LinkCell = ({
const t = useI18n();
const editingElement = (
<>
<textarea
ref={ref}
onKeyDown={onKeydown}
className={
!BUILD_CONFIG.isMobileEdition
? styles.textarea
: styles.mobileTextarea
}
onBlur={commitChange}
value={tempValue || ''}
onChange={handleOnChange}
data-empty={!tempValue}
placeholder={t[
'com.affine.page-properties.property-value-placeholder'
]()}
/>
<div
className={
!BUILD_CONFIG.isMobileEdition
? styles.textInvisible
: styles.mobileTextInvisible
}
>
{tempValue}
{tempValue?.endsWith('\n') || !tempValue ? <br /> : null}
</div>
</>
);
const name = useLiveData(cell.property.name$);
return (
<PropertyValue
className={styles.container}
isEmpty={isEmpty}
onClick={onClick}
>
{!editing ? (
resolvedDocLink ? (
<AffinePageReference
pageId={resolvedDocLink.docId}
params={resolvedDocLink.params}
/>
) : (
<a
href={link}
target="_blank"
rel="noopener noreferrer"
onClick={onLinkClick}
className={styles.link}
>
{link?.replace(/^https?:\/\//, '').trim()}
</a>
)
) : (
<>
<textarea
ref={ref}
onKeyDown={onKeydown}
className={styles.textarea}
onBlur={commitChange}
value={tempValue || ''}
onChange={handleOnChange}
data-empty={!tempValue}
placeholder={t[
'com.affine.page-properties.property-value-placeholder'
]()}
/>
<div className={styles.textInvisible}>
{tempValue}
{tempValue?.endsWith('\n') || !tempValue ? <br /> : null}
</div>
</>
)}
</PropertyValue>
<>
<PropertyValue
className={styles.container}
isEmpty={isEmpty}
onClick={onClick}
>
{!editing ? (
resolvedDocLink ? (
<AffinePageReference
pageId={resolvedDocLink.docId}
params={resolvedDocLink.params}
/>
) : (
<a
href={link}
target="_blank"
rel="noopener noreferrer"
onClick={onLinkClick}
className={styles.link}
>
{link?.replace(/^https?:\/\//, '').trim()}
</a>
)
) : !BUILD_CONFIG.isMobileEdition ? (
editingElement
) : null}
</PropertyValue>
{BUILD_CONFIG.isMobileEdition ? (
<ConfigModal
open={editing}
onOpenChange={setEditing}
onBack={() => {
setEditing(false);
}}
title={
<>
<LinkIcon />
{name}
</>
}
>
<div className={styles.mobileTextareaWrapper}>{editingElement}</div>
</ConfigModal>
) : null}
</>
);
};
@@ -1,11 +1,13 @@
import { Progress, PropertyValue } from '@affine/component';
import { ConfigModal } from '@affine/core/components/mobile';
import { ProgressIcon } from '@blocksuite/icons/rc';
import type { LiveData } from '@toeverything/infra';
import { useLiveData } from '@toeverything/infra';
import { useEffect, useState } from 'react';
import type { DatabaseCellRendererProps } from '../../../types';
export const ProgressCell = ({
const DesktopProgressCell = ({
cell,
dataSource,
rowId,
@@ -34,3 +36,62 @@ export const ProgressCell = ({
</PropertyValue>
);
};
const MobileProgressCell = ({
cell,
dataSource,
rowId,
onChange,
}: DatabaseCellRendererProps) => {
const value = useLiveData(cell.value$ as LiveData<number>);
const isEmpty = value === undefined;
const [localValue, setLocalValue] = useState(value);
useEffect(() => {
setLocalValue(value);
}, [value]);
const [open, setOpen] = useState(false);
const name = useLiveData(cell.property.name$);
const commitChange = () => {
dataSource.cellValueChange(rowId, cell.id, localValue);
onChange?.(localValue);
setOpen(false);
};
return (
<>
<PropertyValue
isEmpty={isEmpty}
hoverable={false}
onClick={() => setOpen(true)}
>
<Progress value={value} />
</PropertyValue>
<ConfigModal
variant="popup"
open={open}
onOpenChange={setOpen}
onDone={commitChange}
title={
<>
<ProgressIcon />
{name}
</>
}
>
<Progress
value={localValue}
onChange={v => {
setLocalValue(v);
}}
/>
</ConfigModal>
</>
);
};
export const ProgressCell = BUILD_CONFIG.isMobileEdition
? MobileProgressCell
: DesktopProgressCell;
@@ -1,12 +1,14 @@
import { PropertyValue } from '@affine/component';
import { ConfigModal } from '@affine/core/components/mobile';
import type { BlockStdScope } from '@blocksuite/affine/block-std';
import {
DefaultInlineManagerExtension,
RichText,
} from '@blocksuite/affine/blocks';
import type { Doc } from '@blocksuite/affine/store';
import { TextIcon } from '@blocksuite/icons/rc';
import { type LiveData, useLiveData } from '@toeverything/infra';
import { useEffect, useRef } from 'react';
import { type CSSProperties, useEffect, useRef, useState } from 'react';
import type * as Y from 'yjs';
import type { DatabaseCellRendererProps } from '../../../types';
@@ -37,11 +39,12 @@ const renderRichText = ({
return richText;
};
export const RichTextCell = ({
const RichTextInput = ({
cell,
dataSource,
onChange,
}: DatabaseCellRendererProps) => {
style,
}: DatabaseCellRendererProps & { style?: CSSProperties }) => {
const std = useBlockStdScope(dataSource.doc);
const text = useLiveData(cell.value$ as LiveData<Y.Text>);
const ref = useRef<HTMLDivElement>(null);
@@ -64,5 +67,68 @@ export const RichTextCell = ({
}
return () => {};
}, [dataSource.doc, onChange, std, text]);
return <PropertyValue ref={ref}></PropertyValue>;
return <div ref={ref} style={style} />;
};
const DesktopRichTextCell = ({
cell,
dataSource,
onChange,
rowId,
}: DatabaseCellRendererProps) => {
return (
<PropertyValue>
<RichTextInput
cell={cell}
dataSource={dataSource}
onChange={onChange}
rowId={rowId}
/>
</PropertyValue>
);
};
const MobileRichTextCell = ({
cell,
dataSource,
onChange,
rowId,
}: DatabaseCellRendererProps) => {
const [open, setOpen] = useState(false);
const name = useLiveData(cell.property.name$);
return (
<PropertyValue onClick={() => setOpen(true)}>
<ConfigModal
onBack={() => setOpen(false)}
open={open}
onOpenChange={setOpen}
title={
<>
<TextIcon />
{name}
</>
}
>
<ConfigModal.RowGroup>
<RichTextInput
cell={cell}
dataSource={dataSource}
onChange={onChange}
rowId={rowId}
style={{ padding: 12 }}
/>
</ConfigModal.RowGroup>
</ConfigModal>
<RichTextInput
cell={cell}
dataSource={dataSource}
onChange={onChange}
rowId={rowId}
/>
</PropertyValue>
);
};
export const RichTextCell = BUILD_CONFIG.isMobileEdition
? MobileRichTextCell
: DesktopRichTextCell;
@@ -1,7 +1,7 @@
/* eslint-disable rxjs/finnish */
import { PropertyValue } from '@affine/component';
import { type TagLike, TagsInlineEditor } from '@affine/component/ui/tags';
import { type TagLike, TagsInlineEditor } from '@affine/core/components/tags';
import { TagService } from '@affine/core/modules/tag';
import {
affineLabelToDatabaseTagColor,
@@ -9,6 +9,7 @@ import {
} from '@affine/core/modules/tag/entities/utils';
import type { DatabaseBlockDataSource } from '@blocksuite/affine/blocks';
import type { SelectTag } from '@blocksuite/data-view';
import { MultiSelectIcon, SingleSelectIcon } from '@blocksuite/icons/rc';
import { LiveData, useLiveData, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useCallback, useMemo } from 'react';
@@ -221,6 +222,8 @@ const BlocksuiteDatabaseSelector = ({
[dataSource, selectCell]
);
const propertyName = useLiveData(cell.property.name$);
return (
<TagsInlineEditor
tagMode="db-label"
@@ -233,6 +236,12 @@ const BlocksuiteDatabaseSelector = ({
onSelectTag={onSelectTag}
tagColors={tagColors}
onTagChange={onTagChange}
title={
<>
{multiple ? <MultiSelectIcon /> : <SingleSelectIcon />}
{propertyName}
</>
}
/>
);
};
@@ -34,9 +34,31 @@ export const docRefLink = style({
color: cssVarV2('text/tertiary'),
});
export const mobileDocRefLink = style([
docRefLink,
{
maxWidth: '110px',
minWidth: '60px',
},
]);
export const cellList = style({
padding: '0 2px',
display: 'flex',
flexDirection: 'column',
gap: 4,
});
export const databaseNameWrapper = style({
display: 'flex',
alignItems: 'center',
gap: 4,
overflow: 'hidden',
});
export const databaseName = style({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
display: 'inline-block',
});
@@ -122,12 +122,24 @@ const DatabaseBacklinkRow = ({
return (
<PropertyCollapsibleSection
title={(row.databaseName || t['unnamed']()) + ' ' + t['properties']()}
// @ts-expect-error fix type
title={
<span className={styles.databaseNameWrapper}>
<span className={styles.databaseName}>
{row.databaseName || t['unnamed']()}
</span>
{t['properties']()}
</span>
}
defaultCollapsed={!defaultOpen}
icon={<DatabaseTableViewIcon />}
suffix={
<AffinePageReference
className={styles.docRefLink}
className={
BUILD_CONFIG.isMobileEdition
? styles.mobileDocRefLink
: styles.docRefLink
}
pageId={row.docId}
params={pageRefParams}
Icon={PageIcon}