mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08: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:
+28
@@ -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,
|
||||
});
|
||||
|
||||
+80
-43
@@ -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}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+62
-1
@@ -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;
|
||||
|
||||
+70
-4
@@ -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;
|
||||
|
||||
+10
-1
@@ -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}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+22
@@ -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',
|
||||
});
|
||||
|
||||
+14
-2
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user