refactor: table cell text overflow that show ellipsis (#125)

* fix: table column text overflow auto hidden

* refactor: table column text overflow auto hidden

* refactor: table column text overflow auto hidden

* fix: popover text overflow handle

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
SemmyWong 2021-12-04 15:36:59 +08:00 committed by GitHub
parent 5dfa57581a
commit 62796f136c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 105 deletions

View File

@ -333,7 +333,6 @@ export function useDesignable(path?: any) {
// console.log('useDesignable', { schema, schemaPath, currentSchema }); // console.log('useDesignable', { schema, schemaPath, currentSchema });
const options = useContext(SchemaOptionsContext); const options = useContext(SchemaOptionsContext);
let DesignableBar = get(options.components, currentSchema['x-designable-bar']); let DesignableBar = get(options.components, currentSchema['x-designable-bar']);
debugger;
if (!designable) { if (!designable) {
DesignableBar = () => null; DesignableBar = () => null;
} }

View File

@ -0,0 +1,3 @@
.ant-popover-inner-content {
max-width: 500px;
}

View File

@ -1,3 +1,4 @@
import './index.less';
export * from './components/router-config'; export * from './components/router-config';
export * from './components/admin-layout'; export * from './components/admin-layout';
export * from './components/auth-layout'; export * from './components/auth-layout';

View File

@ -0,0 +1,9 @@
.input-ellipsis {
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
vertical-align: middle;
margin-right: 10;
}

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext, useEffect, useState } from 'react';
import { isArr, isValid } from '@formily/shared'; import { isArr, isValid } from '@formily/shared';
import { observer, useField } from '@formily/react'; import { observer, useField } from '@formily/react';
import { InputProps } from 'antd/lib/input'; import { InputProps } from 'antd/lib/input';
@ -6,21 +6,16 @@ import { InputNumberProps } from 'antd/lib/input-number';
import { SelectProps } from 'antd/lib/select'; import { SelectProps } from 'antd/lib/select';
import { TreeSelectProps } from 'antd/lib/tree-select'; import { TreeSelectProps } from 'antd/lib/tree-select';
import { CascaderProps } from 'antd/lib/cascader'; import { CascaderProps } from 'antd/lib/cascader';
import { import { DatePickerProps, RangePickerProps as DateRangePickerProps } from 'antd/lib/date-picker';
DatePickerProps,
RangePickerProps as DateRangePickerProps,
} from 'antd/lib/date-picker';
import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker'; import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker';
import { Tag, Space, Popover } from 'antd'; import { Tag, Space, Popover } from 'antd';
import cls from 'classnames'; import cls from 'classnames';
import { import { formatMomentValue, usePrefixCls } from '@formily/antd/esm/__builtins__';
formatMomentValue,
usePrefixCls,
} from '@formily/antd/esm/__builtins__';
import { FullscreenOutlined } from '@ant-design/icons'; import { FullscreenOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import { useCompile } from '../../hooks/useCompile'; import { useCompile } from '../../hooks/useCompile';
import { Field } from '@formily/core'; import { Field } from '@formily/core';
import './index.less';
const PlaceholderContext = createContext<string>(''); const PlaceholderContext = createContext<string>('');
@ -31,14 +26,27 @@ const usePlaceholder = (value?: any) => {
return isValid(value) && value !== '' ? value : placeholder; return isValid(value) && value !== '' ? value : placeholder;
}; };
const Input: React.FC<InputProps> = (props) => { const Input: React.FC<InputProps & { ellipsis: any }> = (props) => {
const prefixCls = usePrefixCls('description-input', props); const prefixCls = usePrefixCls('description-input', props);
const domRef = React.useRef<HTMLInputElement>(null);
const compile = useCompile(); const compile = useCompile();
const [ellipsis, setEllipsis] = useState(false);
const content = compile(usePlaceholder(props.value));
const ellipsisContent = (
<Popover content={usePlaceholder(props.value)} style={{ width: 100 }}>
<div className={'input-ellipsis'}>{content}</div>
</Popover>
);
useEffect(() => {
if (props.ellipsis && domRef.current?.scrollWidth > domRef.current?.clientWidth) {
setEllipsis(true);
}
}, []);
return ( return (
<div className={cls(prefixCls, props.className)} style={props.style}> <div className={cls(prefixCls, props.className)} style={props.style}>
{props.addonBefore} {props.addonBefore}
{props.prefix} {props.prefix}
{compile(usePlaceholder(props.value))} <div ref={domRef}>{ellipsis ? ellipsisContent : content}</div>
{props.suffix} {props.suffix}
{props.addonAfter} {props.addonAfter}
</div> </div>
@ -79,34 +87,32 @@ const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
const TextArea: React.FC<any> = (props) => { const TextArea: React.FC<any> = (props) => {
const prefixCls = usePrefixCls('description-textarea', props); const prefixCls = usePrefixCls('description-textarea', props);
const ellipsis = props.ellipsis === true ? {} : props.ellipsis; const domRef = React.useRef<HTMLInputElement>(null);
const content = props.ellipsis ? ( const [ellipsis, setEllipsis] = useState(false);
<div> const ellipsisProp = props.ellipsis === true ? {} : props.ellipsis;
<Popover content={usePlaceholder(props.value)}> const content = usePlaceholder(props.value);
<div const ellipsisContent = (
style={{ <Popover content={usePlaceholder(props.value)}>
display: 'inline-block', <div
textOverflow: 'ellipsis', className={'input-ellipsis'}
overflow: 'hidden', style={{
whiteSpace: 'nowrap', ...ellipsisProp,
width: 100, }}
verticalAlign: 'middle', >
marginRight: 10, {usePlaceholder(props.text || props.value)}
...ellipsis, </div>
}} </Popover>
>
{usePlaceholder(props.text || props.value)}
</div>
</Popover>
</div>
) : (
usePlaceholder(props.value)
); );
useEffect(() => {
if (props.ellipsis && domRef.current?.scrollWidth > domRef.current?.clientWidth) {
setEllipsis(true);
}
}, []);
return ( return (
<div className={cls(prefixCls, props.className)} style={props.style}> <div className={cls(prefixCls, props.className)} style={props.style}>
{props.addonBefore} {props.addonBefore}
{props.prefix} {props.prefix}
{content} <div ref={domRef}>{ellipsis ? ellipsisContent : content}</div>
{props.suffix} {props.suffix}
{props.addonAfter} {props.addonAfter}
</div> </div>
@ -116,11 +122,7 @@ const TextArea: React.FC<any> = (props) => {
const Select: React.FC<SelectProps<any>> = observer((props) => { const Select: React.FC<SelectProps<any>> = observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const prefixCls = usePrefixCls('description-select', props); const prefixCls = usePrefixCls('description-select', props);
const dataSource: any[] = field?.dataSource?.length const dataSource: any[] = field?.dataSource?.length ? field.dataSource : props?.options?.length ? props.options : [];
? field.dataSource
: props?.options?.length
? props.options
: [];
const placeholder = usePlaceholder(); const placeholder = usePlaceholder();
const getSelected = () => { const getSelected = () => {
const value = props.value; const value = props.value;
@ -128,9 +130,7 @@ const Select: React.FC<SelectProps<any>> = observer((props) => {
if (props.labelInValue) { if (props.labelInValue) {
return isArr(value) ? value : []; return isArr(value) ? value : [];
} else { } else {
return isArr(value) return isArr(value) ? value.map((val) => ({ label: val, value: val })) : [];
? value.map((val) => ({ label: val, value: val }))
: [];
} }
} else { } else {
if (props.labelInValue) { if (props.labelInValue) {
@ -145,8 +145,7 @@ const Select: React.FC<SelectProps<any>> = observer((props) => {
const selected = getSelected(); const selected = getSelected();
if (!selected.length) return <Tag>{placeholder}</Tag>; if (!selected.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => { return selected.map(({ value, label }, key) => {
const text = const text = dataSource?.find((item) => item.value == value)?.label || label;
dataSource?.find((item) => item.value == value)?.label || label;
return <Tag key={key}>{text || placeholder}</Tag>; return <Tag key={key}>{text || placeholder}</Tag>;
}); });
}; };
@ -160,11 +159,7 @@ const Select: React.FC<SelectProps<any>> = observer((props) => {
const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => { const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const prefixCls = usePrefixCls('description-select', props); const prefixCls = usePrefixCls('description-select', props);
const dataSource: any[] = field?.dataSource?.length const dataSource: any[] = field?.dataSource?.length ? field.dataSource : props?.options?.length ? props.options : [];
? field.dataSource
: props?.options?.length
? props.options
: [];
const placeholder = usePlaceholder(); const placeholder = usePlaceholder();
const getSelected = () => { const getSelected = () => {
const value = props.value; const value = props.value;
@ -172,9 +167,7 @@ const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
if (props.labelInValue) { if (props.labelInValue) {
return isArr(value) ? value : []; return isArr(value) ? value : [];
} else { } else {
return isArr(value) return isArr(value) ? value.map((val) => ({ label: val, value: val })) : [];
? value.map((val) => ({ label: val, value: val }))
: [];
} }
} else { } else {
if (props.labelInValue) { if (props.labelInValue) {
@ -189,8 +182,7 @@ const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
const selected = getSelected(); const selected = getSelected();
if (!selected.length) return <Tag>{placeholder}</Tag>; if (!selected.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => { return selected.map(({ value, label }, key) => {
const text = const text = dataSource?.find((item) => item.value == value)?.label || label;
dataSource?.find((item) => item.value == value)?.label || label;
return <Tag key={key}>{text || placeholder}</Tag>; return <Tag key={key}>{text || placeholder}</Tag>;
}); });
}; };
@ -205,20 +197,14 @@ const TreeSelect: React.FC<TreeSelectProps<any>> = observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const placeholder = usePlaceholder(); const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-tree-select', props); const prefixCls = usePrefixCls('description-tree-select', props);
const dataSource = field?.dataSource?.length const dataSource = field?.dataSource?.length ? field.dataSource : props?.options?.length ? props.options : [];
? field.dataSource
: props?.options?.length
? props.options
: [];
const getSelected = () => { const getSelected = () => {
const value = props.value; const value = props.value;
if (props.multiple) { if (props.multiple) {
if (props.labelInValue) { if (props.labelInValue) {
return isArr(value) ? value : []; return isArr(value) ? value : [];
} else { } else {
return isArr(value) return isArr(value) ? value.map((val) => ({ label: val, value: val })) : [];
? value.map((val) => ({ label: val, value: val }))
: [];
} }
} else { } else {
if (props.labelInValue) { if (props.labelInValue) {
@ -245,11 +231,7 @@ const TreeSelect: React.FC<TreeSelectProps<any>> = observer((props) => {
const selected = getSelected(); const selected = getSelected();
if (!selected?.length) return <Tag>{placeholder}</Tag>; if (!selected?.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => { return selected.map(({ value, label }, key) => {
return ( return <Tag key={key}>{findLabel(value, dataSource) || label || placeholder}</Tag>;
<Tag key={key}>
{findLabel(value, dataSource) || label || placeholder}
</Tag>
);
}); });
}; };
return ( return (
@ -263,11 +245,7 @@ const Cascader: React.FC<CascaderProps> = observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const placeholder = usePlaceholder(); const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-cascader', props); const prefixCls = usePrefixCls('description-cascader', props);
const dataSource: any[] = field?.dataSource?.length const dataSource: any[] = field?.dataSource?.length ? field.dataSource : props?.options?.length ? props.options : [];
? field.dataSource
: props?.options?.length
? props.options
: [];
const getSelected = () => { const getSelected = () => {
return isArr(props.value) ? props.value : []; return isArr(props.value) ? props.value : [];
}; };
@ -316,11 +294,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: any) => {
}; };
const getLabels = () => { const getLabels = () => {
const d = moment(props.value); const d = moment(props.value);
const labels = formatMomentValue( const labels = formatMomentValue(d.isValid() ? d : null, getDefaultFormat(), placeholder);
d.isValid() ? d : null,
getDefaultFormat(),
placeholder,
);
return isArr(labels) ? labels.join('~') : labels; return isArr(labels) ? labels.join('~') : labels;
}; };
return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>; return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>;

View File

@ -1,12 +1,6 @@
import React, { useContext, useMemo, useState } from 'react'; import React, { useContext, useMemo, useState } from 'react';
import { ISchema, SchemaField } from '..'; import { ISchema, SchemaField } from '..';
import { import { createForm, onFieldChange, onFieldReact, onFormValuesChange, onFormReset } from '@formily/core';
createForm,
onFieldChange,
onFieldReact,
onFormValuesChange,
onFormReset,
} from '@formily/core';
import { import {
FormProvider, FormProvider,
FormConsumer, FormConsumer,
@ -19,14 +13,7 @@ import {
} from '@formily/react'; } from '@formily/react';
import { Field } from '@formily/core/esm/models/Field'; import { Field } from '@formily/core/esm/models/Field';
import { Form } from '@formily/core/esm/models/Form'; import { Form } from '@formily/core/esm/models/Form';
import { import { Input, FormItem, FormLayout, FormButtonGroup, Submit, Space } from '@formily/antd';
Input,
FormItem,
FormLayout,
FormButtonGroup,
Submit,
Space,
} from '@formily/antd';
import { CloseCircleOutlined } from '@ant-design/icons'; import { CloseCircleOutlined } from '@ant-design/icons';
import { get } from 'lodash'; import { get } from 'lodash';
import { isValid, uid } from '@formily/shared'; import { isValid, uid } from '@formily/shared';
@ -75,9 +62,7 @@ export const FilterItem = (props) => {
} }
const operationValue = Object.keys(nested).shift(); const operationValue = Object.keys(nested).shift();
console.log('toValues', { operationValue }); console.log('toValues', { operationValue });
const operation = operations.find( const operation = operations.find((operation) => operation.value === operationValue);
(operation) => operation.value === operationValue,
);
console.log('toValues', { operation }); console.log('toValues', { operation });
if (!operation) { if (!operation) {
return { return {
@ -121,8 +106,7 @@ export const FilterItem = (props) => {
effects: (form) => { effects: (form) => {
onFieldChange('column', (field: Field, form: Form) => { onFieldChange('column', (field: Field, form: Form) => {
const column = (field.value || {}) as ISchema; const column = (field.value || {}) as ISchema;
const operations = const operations = column?.['x-component-props']?.['operations'] || [];
column?.['x-component-props']?.['operations'] || [];
field.query('operation').take((f: Field) => { field.query('operation').take((f: Field) => {
f.setDataSource(operations); f.setDataSource(operations);
f.value = get(operations, [0]); f.value = get(operations, [0]);
@ -149,7 +133,6 @@ export const FilterItem = (props) => {
return; return;
} }
const fieldName = Object.keys(column.properties).shift(); const fieldName = Object.keys(column.properties).shift();
debugger;
if (operation?.noValue) { if (operation?.noValue) {
onChange({ onChange({
[fieldName]: { [fieldName]: {
@ -174,9 +157,7 @@ export const FilterItem = (props) => {
[], [],
); );
const columnEnum: any = [...columns.values()].map((column) => const columnEnum: any = [...columns.values()].map((column) => column.toJSON());
column.toJSON(),
);
return ( return (
<FormProvider form={form}> <FormProvider form={form}>

View File

@ -20,7 +20,7 @@ export const TableCell = observer((props: any) => {
const componentProps = merge(uiSchema?.['x-component-props'] || {}, schema?.['x-component-props'] || {}, { const componentProps = merge(uiSchema?.['x-component-props'] || {}, schema?.['x-component-props'] || {}, {
arrayMerge: (t, s) => s, arrayMerge: (t, s) => s,
}); });
console.log('Table.Cell', collectionField?.interface, componentProps); console.log('Table.Cell', collectionField?.interface, componentProps, uiSchema);
return ( return (
<div className={`field-interface-${collectionField?.interface}`}> <div className={`field-interface-${collectionField?.interface}`}>
<RecursionField <RecursionField
@ -38,7 +38,7 @@ export const TableCell = observer((props: any) => {
feedbackLayout: 'popover', feedbackLayout: 'popover',
}, },
'x-decorator': 'FormilyFormItem', 'x-decorator': 'FormilyFormItem',
'x-component-props': componentProps, 'x-component-props': { ...componentProps },
properties: { properties: {
...schema?.properties, ...schema?.properties,
}, },

View File

@ -1,5 +1,5 @@
import { DndContext } from '@dnd-kit/core';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { DndContext } from '@dnd-kit/core';
import { Table as AntdTable } from 'antd'; import { Table as AntdTable } from 'antd';
import { findIndex } from 'lodash'; import { findIndex } from 'lodash';
import cls from 'classnames'; import cls from 'classnames';

View File

@ -22,6 +22,10 @@ export const useTableColumns = () => {
const columnSchemas = schema.reduceProperties((columns, current) => { const columnSchemas = schema.reduceProperties((columns, current) => {
if (isColumn(current)) { if (isColumn(current)) {
if (!current['x-component-props']) {
current['x-component-props'] = {};
}
current['x-component-props']['ellipsis'] = true;
if (current['x-hidden']) { if (current['x-hidden']) {
return columns; return columns;
} }
@ -45,6 +49,7 @@ export const useTableColumns = () => {
), ),
dataIndex: column.name, dataIndex: column.name,
...columnProps, ...columnProps,
ellipsis: true,
render: (_: any, record: any) => { render: (_: any, record: any) => {
const index = findIndex(field.value, (item) => item[rowKey] === record[rowKey]); const index = findIndex(field.value, (item) => item[rowKey] === record[rowKey]);
return ( return (