feat(core): adjust property table's style to match design (#10849)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/278b9dbf-a089-4007-bf35-088301a29d89.png)
This commit is contained in:
CatsJuice
2025-03-20 23:20:57 +00:00
parent e4bc43df67
commit 48f79d6467
12 changed files with 122 additions and 68 deletions
@@ -4,9 +4,9 @@ import { globalStyle, style } from '@vanilla-extract/css';
export const propertyRoot = style({
display: 'flex',
minHeight: 32,
gap: 4,
minHeight: 30,
position: 'relative',
padding: '2px 0px 2px 2px',
flexWrap: 'wrap',
selectors: {
'&[draggable="true"]': {
@@ -20,7 +20,7 @@ export const propertyRoot = style({
display: 'block',
position: 'absolute',
cursor: 'grab',
top: '50%',
top: 15,
left: 0,
borderRadius: '2px',
backgroundColor: cssVarV2('text/placeholder'),
@@ -36,7 +36,7 @@ export const propertyRoot = style({
display: 'block',
position: 'absolute',
cursor: 'grab',
top: '50%',
top: 15,
left: 0,
borderRadius: '2px',
backgroundColor: 'transparent',
@@ -51,6 +51,11 @@ export const propertyRoot = style({
},
},
});
export const propertyTableContent = style({
display: 'flex',
flexDirection: 'column',
gap: 8,
});
export const hide = style({
// Visually hide the property while maintaining its position in the layout.
@@ -61,21 +66,24 @@ export const hide = style({
padding: '0px',
visibility: 'hidden',
pointerEvents: 'none',
position: 'absolute',
});
export const propertyNameContainer = style({
display: 'flex',
alignSelf: 'flex-start',
flexDirection: 'column',
justifyContent: 'start',
position: 'relative',
borderRadius: 4,
fontSize: cssVar('fontSm'),
padding: `6px 6px 6px 4px`,
padding: `4px`,
flexShrink: 0,
lineHeight: '22px',
userSelect: 'none',
color: cssVarV2('text/secondary'),
color: cssVarV2.text.tertiary,
width: '160px',
height: 30,
selectors: {
'&[data-has-menu="true"]': {
cursor: 'pointer',
@@ -121,7 +129,7 @@ export const propertyValueContainer = style({
borderRadius: 4,
fontSize: cssVar('fontSm'),
lineHeight: '22px',
padding: `6px`,
padding: `4px`,
flex: 1,
':focus-visible': {
outline: 'none',
@@ -145,9 +153,9 @@ export const propertyValueContainer = style({
export const tableButton = style({
alignSelf: 'flex-start',
fontSize: cssVar('fontSm'),
color: `${cssVarV2('text/secondary')}`,
padding: '0 6px',
height: 36,
color: `${cssVarV2.text.tertiary}`,
padding: '0 8px 0px 4px',
height: 30,
fontWeight: 400,
gap: 6,
'@media': {
@@ -174,14 +182,14 @@ export const sectionHeader = style({
display: 'flex',
alignItems: 'center',
gap: 20,
padding: '4px 6px',
padding: '4px',
minHeight: 30,
});
export const sectionHeaderTrigger = style({
display: 'flex',
alignItems: 'center',
gap: 4,
gap: 6,
flex: 1,
overflow: 'hidden',
});
@@ -223,7 +231,6 @@ export const sectionContent = style({
display: 'flex',
flexDirection: 'column',
gap: 4,
marginTop: 4,
selectors: {
'&[hidden]': {
display: 'none',
@@ -137,6 +137,7 @@ export const PropertyCollapsibleContent = forwardRef<
defaultCollapsed,
onCollapseChange,
collapseButtonText,
className,
...props
},
ref
@@ -179,6 +180,7 @@ export const PropertyCollapsibleContent = forwardRef<
ref={ref}
data-property-collapsible={finalCollapsible}
data-property-collapsed={!finalShowAllHide}
className={clsx(styles.propertyTableContent, className)}
{...props}
>
<PropertyTableContext.Provider value={contextValue}>
@@ -121,9 +121,9 @@ export const actionContainer = style({
export const propertyActionButton = style({
fontSize: cssVar('fontSm'),
color: `${cssVarV2('text/secondary')}`,
padding: '0 6px',
height: 34,
color: `${cssVarV2.text.tertiary}`,
padding: '0 4px',
height: 30,
fontWeight: 400,
gap: 6,
width: '160px',
@@ -449,7 +449,9 @@ const DocPropertiesTableInner = ({
onOpenChange={setExpanded}
/>
<Collapsible.Content>
<DocIntegrationPropertiesTable />
<DocIntegrationPropertiesTable
divider={<div className={styles.tableHeaderDivider} />}
/>
<DocWorkspacePropertiesTableBody
defaultOpen={
!defaultOpenProperty || defaultOpenProperty.type === 'workspace'
@@ -1,15 +1,11 @@
import {
notify,
PropertyValue,
RadioGroup,
type RadioItem,
} from '@affine/component';
import { notify, PropertyValue, type RadioItem } from '@affine/component';
import { DocService } from '@affine/core/modules/doc';
import { useI18n } from '@affine/i18n';
import type { DocMode } from '@blocksuite/affine/model';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DocPropertyRadioGroup } from '../widgets/radio-group';
import * as styles from './doc-primary-mode.css';
import type { PropertyValueProps } from './types';
@@ -59,14 +55,11 @@ export const DocPrimaryModeValue = ({
hoverable={false}
readonly={readonly}
>
<RadioGroup
width={BUILD_CONFIG.isMobileEdition ? '100%' : 194}
itemHeight={24}
<DocPropertyRadioGroup
value={primaryMode}
onChange={handleChange}
items={DocModeItems}
disabled={readonly}
className={styles.radioGroup}
/>
</PropertyValue>
);
@@ -1,9 +1,10 @@
import { PropertyValue, RadioGroup, type RadioItem } from '@affine/component';
import { PropertyValue, type RadioItem } from '@affine/component';
import { DocService } from '@affine/core/modules/doc';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DocPropertyRadioGroup } from '../widgets/radio-group';
import * as styles from './edgeless-theme.css';
import type { PropertyValueProps } from './types';
@@ -46,9 +47,7 @@ export const EdgelessThemeValue = ({
hoverable={false}
readonly={readonly}
>
<RadioGroup
width={BUILD_CONFIG.isMobileEdition ? '100%' : 194}
itemHeight={24}
<DocPropertyRadioGroup
value={edgelessTheme || 'system'}
onChange={handleChange}
items={themeItems}
@@ -3,7 +3,7 @@ import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const property = style({
padding: 4,
padding: '3px 4px',
});
export const root = style({
@@ -1,10 +1,11 @@
import { PropertyValue, RadioGroup, type RadioItem } from '@affine/component';
import { PropertyValue, type RadioItem } from '@affine/component';
import { DocService } from '@affine/core/modules/doc';
import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DocPropertyRadioGroup } from '../widgets/radio-group';
import { container } from './page-width.css';
import type { PageLayoutMode, PropertyValueProps } from './types';
@@ -50,9 +51,7 @@ export const PageWidthValue = ({ readonly }: PropertyValueProps) => {
);
return (
<PropertyValue className={container} hoverable={false} readonly={readonly}>
<RadioGroup
width={BUILD_CONFIG.isMobileEdition ? '100%' : 194}
itemHeight={24}
<DocPropertyRadioGroup
value={radioValue}
onChange={handleChange}
items={radioItems}
@@ -2,10 +2,6 @@ import { style } from '@vanilla-extract/css';
export const tagInlineEditor = style({
width: '100%',
minHeight: 34,
padding: `6px`,
});
export const container = style({
padding: '0px !important',
});
export const container = style({});
@@ -0,0 +1,5 @@
import { style } from '@vanilla-extract/css';
export const label = style({
fontWeight: 400,
});
@@ -0,0 +1,41 @@
import { RadioGroup, type RadioItem, type RadioProps } from '@affine/component';
import clsx from 'clsx';
import { useMemo } from 'react';
import * as styles from './radio-group.css';
export const DocPropertyRadioGroup = ({
width = 194,
items,
value,
onChange,
disabled,
}: Pick<RadioProps, 'items' | 'value' | 'onChange' | 'disabled' | 'width'>) => {
const finalItems = useMemo(
() =>
items.map(item => {
let finalItem: RadioItem = { value: '' };
if (typeof item === 'string') {
finalItem.label = item;
finalItem.value = item;
} else {
finalItem = { ...item };
finalItem.className = clsx(styles.label, item.className);
}
return finalItem;
}),
[items]
);
return (
<RadioGroup
width={BUILD_CONFIG.isMobileEdition ? '100%' : width}
itemHeight={20}
borderRadius={8}
value={value}
onChange={onChange}
items={finalItems}
disabled={disabled}
/>
);
};
@@ -6,13 +6,17 @@ import {
} from '@affine/component';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useMemo } from 'react';
import { type ReactNode, useMemo } from 'react';
import { IntegrationPropertyService } from '../services/integration-property';
import type { IntegrationProperty } from '../type';
import { ValueRenderer } from './property-values';
export const DocIntegrationPropertiesTable = () => {
export const DocIntegrationPropertiesTable = ({
divider,
}: {
divider?: ReactNode;
}) => {
const t = useI18n();
const integrationPropertyService = useService(IntegrationPropertyService);
@@ -36,31 +40,37 @@ export const DocIntegrationPropertiesTable = () => {
if (!schema || !integrationType) return null;
return (
<PropertyCollapsibleSection
title={t['com.affine.integration.properties']()}
>
<PropertyCollapsibleContent>
{properties.map(property => {
const Icon = property.icon;
const key = property.key as string;
const label = property.label;
const displayName =
typeof label === 'string'
? t[label]()
: t.t(label?.i18nKey, label?.options);
<>
<PropertyCollapsibleSection
title={t['com.affine.integration.properties']()}
>
<PropertyCollapsibleContent>
{properties.map(property => {
const Icon = property.icon;
const key = property.key as string;
const label = property.label;
const displayName =
typeof label === 'string'
? t[label]()
: t.t(label?.i18nKey, label?.options);
return (
<PropertyRoot key={key}>
<PropertyName name={displayName} icon={Icon ? <Icon /> : null} />
<ValueRenderer
integration={integrationType}
type={property.type}
propertyKey={key}
/>
</PropertyRoot>
);
})}
</PropertyCollapsibleContent>
</PropertyCollapsibleSection>
return (
<PropertyRoot key={key}>
<PropertyName
name={displayName}
icon={Icon ? <Icon /> : null}
/>
<ValueRenderer
integration={integrationType}
type={property.type}
propertyKey={key}
/>
</PropertyRoot>
);
})}
</PropertyCollapsibleContent>
</PropertyCollapsibleSection>
{divider}
</>
);
};