style: automatically omit table cell content based on width (#1646)

* style: automatically omit cell content based on width

* fix: cell tooltips are only displayed when the width is exceeded

* chore: multiple select ellipsis in table collection

* fix: no automatic thumbnail when table column content exceeds width

* refactor: table ellipsis

* refactor: isOverflowTooltip
This commit is contained in:
katherinehhh 2023-04-22 10:36:21 +08:00 committed by GitHub
parent 3c884cdd36
commit 564fe24c8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 90 additions and 36 deletions

View File

@ -58,16 +58,6 @@ const InternalField: React.FC = (props) => {
const originalProps = compile(uiSchema['x-component-props']) || {}; const originalProps = compile(uiSchema['x-component-props']) || {};
const componentProps = merge(originalProps, field.componentProps || {}); const componentProps = merge(originalProps, field.componentProps || {});
field.component = [component, componentProps]; field.component = [component, componentProps];
// if (interfaceType === 'input') {
// field.componentProps.ellipsis = true;
// } else if (interfaceType === 'textarea') {
// field.componentProps.ellipsis = true;
// } else if (interfaceType === 'markdown') {
// field.componentProps.ellipsis = true;
// } else if (interfaceType === 'attachment') {
// field.componentProps.size = 'small';
// }
}, [JSON.stringify(uiSchema)]); }, [JSON.stringify(uiSchema)]);
if (!uiSchema) { if (!uiSchema) {
return null; return null;

View File

@ -1,3 +1,4 @@
import { ISchema } from '@formily/react';
import { defaultProps, operators } from './properties'; import { defaultProps, operators } from './properties';
import { IField } from './types'; import { IField } from './types';
@ -23,4 +24,10 @@ export const checkbox: IField = {
filterable: { filterable: {
operators: operators.boolean, operators: operators.boolean,
}, },
schemaInitialize(schema: ISchema, { block }) {
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
},
}; };

View File

@ -1,3 +1,4 @@
import { ISchema } from '@formily/react';
import { dataSource, defaultProps, operators } from './properties'; import { dataSource, defaultProps, operators } from './properties';
import { IField } from './types'; import { IField } from './types';
@ -17,7 +18,7 @@ export const checkboxGroup: IField = {
'x-component': 'Checkbox.Group', 'x-component': 'Checkbox.Group',
}, },
}, },
availableTypes:['array'], availableTypes: ['array'],
hasDefaultValue: true, hasDefaultValue: true,
properties: { properties: {
...defaultProps, ...defaultProps,
@ -26,4 +27,10 @@ export const checkboxGroup: IField = {
filterable: { filterable: {
operators: operators.array, operators: operators.array,
}, },
schemaInitialize(schema: ISchema, { block }) {
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
},
}; };

View File

@ -56,9 +56,13 @@ export const createdBy: IField = {
}, },
], ],
}, },
schemaInitialize(schema: ISchema, { readPretty }) { schemaInitialize(schema: ISchema, { block }) {
schema['properties'] = { schema['properties'] = {
viewer: cloneDeep(recordPickerViewer), viewer: cloneDeep(recordPickerViewer),
}; };
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
}, },
}; };

View File

@ -1,3 +1,4 @@
import { ISchema } from '@formily/react';
import { defaultProps, operators, unique } from './properties'; import { defaultProps, operators, unique } from './properties';
import { IField } from './types'; import { IField } from './types';
@ -18,7 +19,7 @@ export const email: IField = {
'x-validator': 'email', 'x-validator': 'email',
}, },
}, },
availableTypes:['string'], availableTypes: ['string'],
hasDefaultValue: true, hasDefaultValue: true,
properties: { properties: {
...defaultProps, ...defaultProps,
@ -27,4 +28,10 @@ export const email: IField = {
filterable: { filterable: {
operators: operators.string, operators: operators.string,
}, },
schemaInitialize(schema: ISchema, { block }) {
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
},
}; };

View File

@ -1,3 +1,4 @@
import { ISchema } from '@formily/react';
import { dataSource, defaultProps, operators } from './properties'; import { dataSource, defaultProps, operators } from './properties';
import { IField } from './types'; import { IField } from './types';
@ -21,7 +22,7 @@ export const multipleSelect: IField = {
enum: [], enum: [],
}, },
}, },
availableTypes:['array'], availableTypes: ['array'],
hasDefaultValue: true, hasDefaultValue: true,
properties: { properties: {
...defaultProps, ...defaultProps,
@ -30,4 +31,10 @@ export const multipleSelect: IField = {
filterable: { filterable: {
operators: operators.array, operators: operators.array,
}, },
schemaInitialize(schema: ISchema, { block }) {
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
},
}; };

View File

@ -1,3 +1,4 @@
import { ISchema } from '@formily/react';
import { dataSource, defaultProps, operators } from './properties'; import { dataSource, defaultProps, operators } from './properties';
import { IField } from './types'; import { IField } from './types';
@ -27,4 +28,10 @@ export const select: IField = {
filterable: { filterable: {
operators: operators.enumType, operators: operators.enumType,
}, },
schemaInitialize(schema: ISchema, { block }) {
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
},
}; };

View File

@ -55,9 +55,13 @@ export const updatedBy: IField = {
}, },
], ],
}, },
schemaInitialize(schema: ISchema, { readPretty }) { schemaInitialize(schema: ISchema, { block }) {
schema['properties'] = { schema['properties'] = {
viewer: cloneDeep(recordPickerViewer), viewer: cloneDeep(recordPickerViewer),
}; };
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
}
}, },
}; };

View File

@ -5,6 +5,7 @@ import { Checkbox as AntdCheckbox, Tag } from 'antd';
import type { CheckboxGroupProps, CheckboxProps } from 'antd/lib/checkbox'; import type { CheckboxGroupProps, CheckboxProps } from 'antd/lib/checkbox';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import React from 'react'; import React from 'react';
import { EllipsisWithTooltip } from '../input/EllipsisWithTooltip';
type ComposedCheckbox = React.FC<CheckboxProps> & { type ComposedCheckbox = React.FC<CheckboxProps> & {
Group?: React.FC<CheckboxGroupProps>; Group?: React.FC<CheckboxGroupProps>;
@ -18,12 +19,10 @@ export const Checkbox: ComposedCheckbox = connect(
}; };
return <AntdCheckbox {...props} onChange={changeHandler} />; return <AntdCheckbox {...props} onChange={changeHandler} />;
}, },
mapProps( mapProps({
{ value: 'checked',
value: 'checked', onInput: 'onChange',
onInput: 'onChange', }),
},
),
mapReadPretty((props) => { mapReadPretty((props) => {
if (props.value) { if (props.value) {
return <CheckOutlined style={{ color: '#52c41a' }} />; return <CheckOutlined style={{ color: '#52c41a' }} />;
@ -36,21 +35,18 @@ Checkbox.__ANT_CHECKBOX = true;
Checkbox.Group = connect( Checkbox.Group = connect(
AntdCheckbox.Group, AntdCheckbox.Group,
mapProps( mapProps({
{ dataSource: 'options',
dataSource: 'options', }),
},
),
mapReadPretty((props) => { mapReadPretty((props) => {
if (!isValid(props.value)) { if (!isValid(props.value)) {
return null; return null;
} }
const { options = [] } = props;
const field = useField<any>(); const field = useField<any>();
const dataSource = field.dataSource || []; const dataSource = field.dataSource || [];
const value = uniq(field.value ? field.value : []); const value = uniq(field.value ? field.value : []);
return ( return (
<div> <EllipsisWithTooltip ellipsis={props.ellipsis}>
{dataSource {dataSource
.filter((option) => value.includes(option.value)) .filter((option) => value.includes(option.value))
.map((option, key) => ( .map((option, key) => (
@ -58,7 +54,7 @@ Checkbox.Group = connect(
{option.label} {option.label}
</Tag> </Tag>
))} ))}
</div> </EllipsisWithTooltip>
); );
}), }),
); );

View File

@ -1,6 +1,14 @@
import { Popover } from 'antd'; import { Popover } from 'antd';
import React, { CSSProperties, forwardRef, useImperativeHandle, useState } from 'react'; import React, { CSSProperties, forwardRef, useImperativeHandle, useState, useRef } from 'react';
const getContentWidth = (element) => {
if (element) {
const range = document.createRange();
range.selectNodeContents(element);
const contentWidth = range.getBoundingClientRect().width;
return contentWidth;
}
};
const ellipsisDefaultStyle: CSSProperties = { const ellipsisDefaultStyle: CSSProperties = {
overflow: 'hidden', overflow: 'hidden',
overflowWrap: 'break-word', overflowWrap: 'break-word',
@ -14,9 +22,11 @@ interface IEllipsisWithTooltipProps {
popoverContent: unknown; popoverContent: unknown;
children: any; children: any;
} }
export const EllipsisWithTooltip = forwardRef((props: Partial<IEllipsisWithTooltipProps>, ref: any) => { export const EllipsisWithTooltip = forwardRef((props: Partial<IEllipsisWithTooltipProps>, ref: any) => {
const [ellipsis, setEllipsis] = useState(false); const [ellipsis, setEllipsis] = useState(false);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const elRef: any = useRef();
useImperativeHandle(ref, () => { useImperativeHandle(ref, () => {
return { return {
setPopoverVisible: setVisible, setPopoverVisible: setVisible,
@ -26,6 +36,13 @@ export const EllipsisWithTooltip = forwardRef((props: Partial<IEllipsisWithToolt
return <>{props.children}</>; return <>{props.children}</>;
} }
const { popoverContent } = props; const { popoverContent } = props;
const isOverflowTooltip = () => {
const contentWidth = getContentWidth(elRef.current);
const offsetWidth = elRef.current?.offsetWidth;
return contentWidth > offsetWidth;
};
return ( return (
<Popover <Popover
visible={ellipsis && visible} visible={ellipsis && visible}
@ -44,10 +61,14 @@ export const EllipsisWithTooltip = forwardRef((props: Partial<IEllipsisWithToolt
} }
> >
<div <div
ref={elRef}
style={{ ...ellipsisDefaultStyle }} style={{ ...ellipsisDefaultStyle }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
const el = e.target as any; const el = e.target as any;
setEllipsis(el.scrollWidth > el.clientWidth); const isShowTooltips = isOverflowTooltip();
if (isShowTooltips) {
setEllipsis(el.scrollWidth >= el.clientWidth);
}
}} }}
> >
{props.children} {props.children}

View File

@ -5,6 +5,7 @@ import { Tag } from 'antd';
import React from 'react'; import React from 'react';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { defaultFieldNames, getCurrentOptions } from './shared'; import { defaultFieldNames, getCurrentOptions } from './shared';
import { EllipsisWithTooltip } from '../input/EllipsisWithTooltip';
type Composed = { type Composed = {
Select?: React.FC<any>; Select?: React.FC<any>;
@ -24,13 +25,16 @@ export const ReadPretty = observer((props: any) => {
} }
const dataSource = field.dataSource || props.options || []; const dataSource = field.dataSource || props.options || [];
const options = getCurrentOptions(field.value, dataSource, fieldNames); const options = getCurrentOptions(field.value, dataSource, fieldNames);
return ( return (
<div> <div>
{options.map((option, key) => ( <EllipsisWithTooltip ellipsis={props.ellipsis}>
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}> {options.map((option, key) => (
{compile(option[fieldNames.label])} <Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
</Tag> {compile(option[fieldNames.label])}
))} </Tag>
))}
</EllipsisWithTooltip>
</div> </div>
); );
}); });