tree数据操作换成hover显示

This commit is contained in:
catchonme 2019-09-10 19:42:18 +08:00
parent b64188f9dd
commit 310927b73f
9 changed files with 137 additions and 125 deletions

View File

@ -1,7 +1,9 @@
@mixin tree-input {
> i {
> svg {
display: inline-block;
cursor: pointer;
position: relative;
top: 2px;
margin-left: px2rem(5px);
}
@ -65,16 +67,27 @@
line-height: px2rem(30px);
position: relative;
.#{$ns}Tree-item-icons {
visibility: hidden;
transition: visibility .1s ease;
}
> a {
color: inherit;
&:hover {
text-decoration: none;
> span.#{$ns}Tree-item-icons {
visibility: visible;
}
}
> span > i {
> span > svg {
display: inline-block;
cursor: pointer;
position: relative;
top: 2px;
margin-left: px2rem(5px);
}
}
@ -99,8 +112,14 @@
line-height: px2rem(25px);
cursor: pointer;
padding-left: $Tree-indent;
> p > span {
padding-left: px2rem(5px);
> p {
> svg {
position: relative;
top: 2px;
}
> span {
padding-left: px2rem(5px);
}
}
&-input {

View File

@ -5,10 +5,11 @@
*/
import React from 'react';
import {eachTree, isVisible, isObject} from '../utils/helper';
import {eachTree, isVisible, isObject, autobind} from '../utils/helper';
import {Option, Options, value2array} from './Checkboxes';
import {ClassNamesFn, themeable} from '../theme';
import {highlight} from '../renderers/Form/Options';
import {Icon} from './icons';
interface TreeSelectorProps {
classPrefix: string;
@ -53,20 +54,21 @@ interface TreeSelectorProps {
selfDisabledAffectChildren?: boolean;
minLength?: number;
maxLength?: number;
addMode?: string;
addItem?: Function;
addMode?: 'dialog' | 'normal';
addable?: boolean;
onAdd?: Function;
openAddDialog?: Function;
editMode?: string;
editItem?: Function;
editMode?: 'dialog' | 'normal';
onEdit?: Function;
editable?: boolean;
openEditDialog?: Function;
deletable?: boolean;
deleteItem?: Function;
onDelete?: Function;
}
interface TreeSelectorState {
value: Array<any>;
unfolded: {[propName: string]: string};
hoverItem: Option | null;
editItem: Option | null;
addItem: Option | null;
addingItem: Option | null;
@ -101,24 +103,6 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
};
componentWillMount() {
this.renderList = this.renderList.bind(this);
this.handleSelect = this.handleSelect.bind(this);
this.clearSelect = this.clearSelect.bind(this);
this.handleCheck = this.handleCheck.bind(this);
this.toggleUnfolded = this.toggleUnfolded.bind(this);
this.handleEnter = this.handleEnter.bind(this);
this.handleMove = this.handleMove.bind(this);
this.handleLeave = this.handleLeave.bind(this);
this.addItem = this.addItem.bind(this);
this.onChangeAddItem = this.onChangeAddItem.bind(this);
this.confirmAddItem = this.confirmAddItem.bind(this);
this.cancelAddItem = this.cancelAddItem.bind(this);
this.editItem = this.editItem.bind(this);
this.onChangeEditItem = this.onChangeEditItem.bind(this);
this.confirmEditItem = this.confirmEditItem.bind(this);
this.cancelEditItem = this.cancelEditItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
const props = this.props;
this.setState({
@ -131,7 +115,6 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
options: props.data
}),
unfolded: this.syncUnFolded(props),
hoverItem: null, // 鼠标覆盖选中的 item
editItem: null, // 点击编辑时的 item
addItem: null, // 点击添加时的 item
addingItem: null, // 添加后的 item
@ -187,6 +170,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
return unfolded;
}
@autobind
toggleUnfolded(node: any) {
this.setState({
addItem: null,
@ -197,6 +181,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
});
}
@autobind
clearSelect() {
this.setState(
{
@ -210,6 +195,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
);
}
@autobind
handleSelect(node: any, value?: any) {
this.setState(
{
@ -223,6 +209,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
);
}
@autobind
handleCheck(item: any, checked: boolean) {
const props = this.props;
const value = this.state.value.concat();
@ -306,37 +293,13 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
);
}
handleEnter(item: Option) {
this.setState({
hoverItem: item
});
}
handleMove(e: MouseEvent, item: Option) {
const target = e.target as HTMLElement;
const tagName = target.tagName;
if (tagName === 'LI') {
const current = target.childNodes[0].textContent;
if (current == item['label'] && (!this.state.hoverItem || current !== (this.state.hoverItem as Option)['label'])) {
this.setState({
hoverItem: item
});
}
}
}
handleLeave() {
this.setState({
hoverItem: null
});
}
addItem(item: Option | null, isFolder: boolean) {
@autobind
handleAdd(item: Option | null, isFolder: boolean) {
const {addMode, openAddDialog, valueField} = this.props;
let {hoverItem, unfolded} = this.state;
let {unfolded} = this.state;
if (addMode === 'dialog') {
openAddDialog && openAddDialog(item ? hoverItem : null)
} else if (addMode === 'normal') {
openAddDialog && openAddDialog(item ? item : null)
} else if (addMode === 'normal') {
// item 为 null 时为添加一级
if (item) {
// 添加时,默认折叠的文件夹需要展开
@ -362,11 +325,12 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
}
}
editItem(item: Option) {
@autobind
handleEdit(item: Option) {
const {editMode, openEditDialog} = this.props;
const {hoverItem, addItem} = this.state;
const {addItem} = this.state;
if (editMode === 'dialog') {
openEditDialog && openEditDialog(hoverItem);
openEditDialog && openEditDialog(item);
addItem && this.setState({
addItem: null
});
@ -378,19 +342,17 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
}
}
deleteItem(item: Option) {
const {deleteItem} = this.props;
deleteItem && deleteItem(item);
this.setState({
hoverItem: null
});
@autobind
handleDelete(item: Option) {
const {onDelete} = this.props;
onDelete && onDelete(item);
}
confirmAddItem() {
const {addItem} = this.props;
@autobind
handleConfirmOnAdd() {
const {onAdd} = this.props;
const {addItem: parent, addingItem} = this.state;
addItem && addItem({
onAdd && onAdd({
...addingItem,
parent: parent
});
@ -402,17 +364,19 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
})
}
cancelAddItem() {
@autobind
handleCancelOnAdd() {
this.setState({
addItem: null,
addTop: false
});
}
confirmEditItem() {
const {editItem} = this.props;
@autobind
handleConfirmOnEdit() {
const {onEdit} = this.props;
let {editingItem, editItem: prevItem} = this.state;
editItem && editItem({
onEdit && onEdit({
...editingItem,
prev: prevItem
});
@ -422,13 +386,15 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
});
}
cancelEditItem() {
@autobind
handleCancelOnEdit() {
this.setState({
editItem: null
});
}
onChangeAddItem(value: string) {
@autobind
handleChangeOnAdd(value: string) {
this.setState({
addingItem: {
label: value
@ -436,7 +402,8 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
});
}
onChangeEditItem(item: Option, value: string) {
@autobind
handleChangeOnEdit(item: Option, value: string) {
let {editItem} = this.state;
this.setState({
editingItem: {
@ -446,6 +413,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
});
}
@autobind
renderList(
list: Options,
value: Option[],
@ -469,14 +437,13 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
data,
maxLength,
minLength,
addMode,
editMode,
addable,
editable,
deletable
} = this.props;
const {
addItem,
editItem,
hoverItem,
unfolded,
addTop,
value: stateValue
@ -553,15 +520,6 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
className={cx(`Tree-item ${itemClassName || ''}`, {
'Tree-item--isLeaf': isLeaf
})}
onMouseMove={(e) =>
!nodeDisabled && this.handleMove(e, item)
}
onMouseEnter={() =>
!nodeDisabled && this.handleEnter(item)
}
onMouseLeave={() =>
!nodeDisabled && this.handleLeave()
}
>
{!editItem || isObject(editItem) && (editItem as Option)[valueField] !== item[valueField] ? (
<a>
@ -598,24 +556,21 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
>
{highlightTxt ? highlight(item[nameField], highlightTxt) : item[nameField]}
</span>
{/* 非添加时 && 非编辑时 && 鼠标覆盖时是当前item时显示添加/编辑/删除图标 */}
{!addTop
&& !addItem
&& !editItem
&& isObject(hoverItem)
&& (hoverItem as Option)[valueField as string] === item[valueField] ? (
<span>
{addMode ? <i className="fa fa-plus" onClick={() => this.addItem(item, !isLeaf)}></i> : null}
{deletable ? <i className="fa fa-minus" onClick={() => this.deleteItem(item)}></i> : null}
{editMode ? <i className="fa fa-pencil" onClick={() => this.editItem(item)}></i> : null}
&& !editItem ? (
<span className={cx('Tree-item-icons')}>
{addable ? <Icon icon="plus" className="icon" onClick={() => this.handleAdd(item, !isLeaf)}/> : null}
{deletable ? <Icon icon="minus" className="icon" onClick={() => this.handleDelete(item)}/> : null}
{editable ? <Icon icon="pencil" className="icon" onClick={() => this.handleEdit(item)}/> : null}
</span>
) : null}
</a>
) : (
<div className={cx('Tree-item--isEdit')}>
<input defaultValue={item['label']} onChange={(e) => this.onChangeEditItem(item, e.currentTarget.value)}/>
<i className="fa fa-check" onClick={this.confirmEditItem}></i>
<i className="fa fa-close" onClick={this.cancelEditItem}></i>
<input defaultValue={item['label']} onChange={(e) => this.handleChangeOnEdit(item, e.currentTarget.value)}/>
<Icon icon="check" className="icon" onClick={this.handleConfirmOnEdit}/>
<Icon icon="tree-close" className="icon" onClick={this.handleCancelOnEdit}/>
</div>
)}
{/* 有children而且为展开状态 或者 添加child时 */}
@ -625,9 +580,9 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
>
{addItem && addItem[valueField] === item[valueField] ? (
<li>
<input onChange={(e) => this.onChangeAddItem(e.currentTarget.value)}/>
<i className="fa fa-check" onClick={this.confirmAddItem}></i>
<i className="fa fa-close" onClick={this.cancelAddItem}></i>
<input onChange={(e) => this.handleChangeOnAdd(e.currentTarget.value)}/>
<Icon icon="check" className="icon" onClick={this.handleConfirmOnAdd}/>
<Icon icon="tree-close" className="icon" onClick={this.handleCancelOnAdd}/>
</li>
) : null}
{childrenItems}
@ -644,7 +599,7 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
}
render() {
const {className, placeholder, hideRoot, rootLabel, showIcon, classnames: cx, addMode} = this.props;
const {className, placeholder, hideRoot, rootLabel, showIcon, classnames: cx, addable} = this.props;
let data = this.props.data;
const {value, addTop} = this.state;
@ -669,19 +624,19 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
</span>
</label>
</a>
{addMode ? (
{addable ? (
<div className={cx('Tree-addTop')}>
{!addTop ? (
<p onClick={() => this.addItem(null, false)}>
<i className="fa fa-plus"></i>
<p onClick={() => this.handleAdd(null, false)}>
<Icon icon="plus" className="icon" />
<span></span>
</p>
) : null }
) : null}
{addTop ? (
<div className={cx('Tree-addTop-input')}>
<input onChange={(e) => this.onChangeAddItem(e.currentTarget.value)}/>
<i className="fa fa-check" onClick={this.confirmAddItem}></i>
<i className="fa fa-close" onClick={this.cancelAddItem}></i>
<input onChange={(e) => this.handleChangeOnAdd(e.currentTarget.value)}/>
<Icon icon="check" className="icon" onClick={this.handleConfirmOnAdd}/>
<Icon icon="tree-close" className="icon" onClick={this.handleCancelOnAdd}/>
</div>
) : null}
</div>

View File

@ -25,6 +25,16 @@ import PauseIcon from '../icons/pause.svg';
import LeftArrowIcon from '../icons/left-arrow.svg';
// @ts-ignore
import RightArrowIcon from '../icons/right-arrow.svg';
// @ts-ignore
import CheckIcon from '../icons/check.svg';
// @ts-ignore
import PlusIcon from '../icons/plus.svg';
// @ts-ignore
import MinusIcon from '../icons/minus.svg';
// @ts-ignore
import PencilIcon from '../icons/pencil.svg';
// @ts-ignore
import TreeClose from '../icons/tree-close.svg';
// 兼容原来的用法,后续不直接试用。
// @ts-ignore
@ -70,6 +80,11 @@ registerIcon('play', PlayIcon);
registerIcon('pause', PauseIcon);
registerIcon('left-arrow', LeftArrowIcon);
registerIcon('right-arrow', RightArrowIcon);
registerIcon('check', CheckIcon);
registerIcon('plus', PlusIcon);
registerIcon('minus', MinusIcon);
registerIcon('pencil', PencilIcon);
registerIcon('tree-close', TreeClose);
export function Icon({
icon,
@ -91,5 +106,9 @@ export {
PlayIcon,
PauseIcon,
LeftArrowIcon,
RightArrowIcon
RightArrowIcon,
CheckIcon,
PlusIcon,
MinusIcon,
PencilIcon
};

3
src/icons/check.svg Normal file
View File

@ -0,0 +1,3 @@
<svg t="1568105259329" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="983" >
<path d="M954.848 323.424q0 22.848-16 38.848l-491.424 491.424q-16 16-38.848 16t-38.848-16l-284.576-284.576q-16-16-16-38.848t16-38.848l77.728-77.728q16-16 38.848-16t38.848 16l168 168.576 374.848-375.424q16-16 38.848-16t38.848 16l77.728 77.728q16 16 16 38.848z" p-id="984" fill="#606670"></path>
</svg>

After

Width:  |  Height:  |  Size: 456 B

3
src/icons/minus.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="850">
<path d="M911.232 420.565333l0 109.717333q0 22.848-16 38.848t-38.848 16l-694.848 0q-22.848 0-38.848-16t-16-38.848l0-109.717333q0-22.848 16-38.848t38.848-16l694.848 0q22.848 0 38.848 16t16 38.848z" p-id="851" fill="#606670"></path>
</svg>

After

Width:  |  Height:  |  Size: 374 B

3
src/icons/pencil.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="2332">
<path d="M768 0l-128 128 256 256 128-128-256-256zm-256 256l-512 512 0 256 256 0 512-512-256-256z" p-id="2333" fill="#606670"></path>
</svg>

After

Width:  |  Height:  |  Size: 277 B

3
src/icons/plus.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="717">
<path d="M914.285714 420.571429v109.714285q0 22.857143-16 38.857143t-38.857143 16h-237.714285v237.714286q0 22.857143-16 38.857143t-38.857143 16H457.142857q-22.857143 0-38.857143-16t-16-38.857143v-237.714286H164.571429q-22.857143 0-38.857143-16t-16-38.857143V420.571429q0-22.857143 16-38.857143t38.857143-16h237.714285V128q0-22.857143 16-38.857143t38.857143-16h109.714286q22.857143 0 38.857143 16t16 38.857143v237.714286h237.714285q22.857143 0 38.857143 16t16 38.857143z" p-id="718" fill="#606670"></path>
</svg>

After

Width:  |  Height:  |  Size: 649 B

3
src/icons/tree-close.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="1775" >
<path d="M853.728 755.424q0 22.848-16 38.848l-77.728 77.728q-16 16-38.848 16t-38.848-16l-168-168-168 168q-16 16-38.848 16t-38.848-16l-77.728-77.728q-16-16-16-38.848t16-38.848l168-168-168-168q-16-16-16-38.848t16-38.848l77.728-77.728q16-16 38.848-16t38.848 16l168 168 168-168q16-16 38.848-16t38.848 16l77.728 77.728q16 16 16 38.848t-16 38.848l-168 168 168 168q16 16 16 38.848z" p-id="1776" fill="#606670"></path>
</svg>

After

Width:  |  Height:  |  Size: 556 B

View File

@ -18,10 +18,10 @@ export interface TreeProps extends OptionsControlProps {
withChildren?: boolean; // 选父级的时候是否把子节点的值也包含在内。
onlyChildren?: boolean; // 选父级的时候,是否只把子节点的值包含在内
addApi?: Api;
addMode?: string;
addMode?: 'dialog' | 'normal';
addDialog?: Schema;
editApi?: Api;
editMode?: string;
editMode?: 'dialog' | 'normal';
editDialog?: Schema;
deleteApi?: Api;
deleteConfirmText?: string;
@ -57,7 +57,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
}
@autobind
addItem(values: PlainObject) {
handleAdd(values: PlainObject) {
this.saveRemote(values, 'add');
}
@ -71,7 +71,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
}
@autobind
editItem(values: PlainObject) {
handleEdit(values: PlainObject) {
this.saveRemote(values, 'add');
}
@ -80,7 +80,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
this.saveRemote({
...values,
prev: this.state.prev
}, 'add');
}, 'edit');
this.closeEditDialog();
}
@ -109,7 +109,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
}
@autobind
async deleteItem(item: any) {
async handleDelete(item: any) {
const {deleteConfirmText, deleteApi, data, env} = this.props;
const ctx = createObject(data, item);
if (isEffectiveApi(deleteApi, ctx)) {
@ -189,8 +189,10 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
showRadio,
render,
addMode,
addApi,
addDialog,
editMode,
editApi,
editDialog,
deleteApi
} = this.props;
@ -228,17 +230,19 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
nameField="label"
selfDisabledAffectChildren={false}
addMode={addMode}
addItem={this.addItem}
addable={isEffectiveApi(addApi)}
onAdd={this.handleAdd}
openAddDialog={this.openAddDialog}
editMode={editMode}
editItem={this.editItem}
editable={isEffectiveApi(editApi)}
onEdit={this.handleEdit}
openEditDialog={this.openEditDialog}
deleteItem={this.deleteItem}
onDelete={this.handleDelete}
deletable={isEffectiveApi(deleteApi)}
/>
)}
{render(
{addMode && render(
'modal',
{
type: 'dialog',
@ -252,7 +256,7 @@ export default class TreeControl extends React.Component<TreeProps, TreeState> {
}
)}
{render(
{editMode && render(
'modal',
{
type: 'dialog',