添加 nestedCheckboxes

This commit is contained in:
2betop 2020-05-27 10:57:48 +08:00
parent 27962550e7
commit d807346e3d
3 changed files with 168 additions and 25 deletions

View File

@ -279,6 +279,15 @@
display: block;
}
@mixin checkboxes-placeholder {
height: $Form-input-height;
line-height: $Form-input-lineHeight;
font-size: $Form-input-fontSize;
padding: ($Form-input-height - $Form-input-lineHeight * $Form-input-fontSize)/2
$gap-sm;
color: $text--muted-color;
}
.#{$ns}Checkboxes {
> .#{$ns}Checkbox {
display: block;
@ -358,13 +367,7 @@
}
&-placeholder {
height: $Form-input-height;
line-height: $Form-input-lineHeight;
font-size: $Form-input-fontSize;
padding: (
$Form-input-height - $Form-input-lineHeight * $Form-input-fontSize
)/2 $gap-sm;
color: $text--muted-color;
@include checkboxes-placeholder();
}
}
@ -473,12 +476,59 @@
}
&-placeholder {
@include checkboxes-placeholder();
}
}
.#{$ns}NestedCheckboxes {
display: flex;
flex-direction: row;
&-col {
flex-grow: 1;
min-width: 150px;
}
&-col:not(:last-child) {
border-right: 1px solid $borderColor;
}
&-item {
display: flex;
height: $Form-input-height;
line-height: $Form-input-lineHeight;
font-size: $Form-input-fontSize;
padding: (
$Form-input-height - $Form-input-lineHeight * $Form-input-fontSize
)/2 $gap-sm;
color: $text--muted-color;
flex-direction: row;
> .#{$ns}Checkbox {
margin-right: 0;
}
cursor: pointer;
user-select: none;
&:hover {
background-color: $Tree-item-onHover-bg;
}
&.is-active {
color: $Form-select-menu-onActive-color;
background-color: $Form-select-menu-onActive-bg;
}
&.is-disabled {
pointer-events: none;
color: $text--muted-color;
}
}
&-itemLabel {
flex-grow: 1;
}
&-placeholder {
@include checkboxes-placeholder();
}
}

View File

@ -1,14 +1,36 @@
import {Checkboxes} from './Checkboxes';
import {Checkboxes, CheckboxesProps} from './Checkboxes';
import {themeable} from '../theme';
import React from 'react';
import uncontrollable from 'uncontrollable';
import Checkbox from './Checkbox';
import {Option} from './Select';
import {getTreeDepth} from '../utils/helper';
import times from 'lodash/times';
export class NestedCheckboxes extends Checkboxes {
export interface NestedCheckboxesState {
selected: Array<Option>;
}
export class NestedCheckboxes extends Checkboxes<
CheckboxesProps,
NestedCheckboxesState
> {
valueArray: Array<Option>;
state: NestedCheckboxesState = {
selected: []
};
renderOption(option: Option, index: number) {
selectOption(option: Option, depth: number) {
const selected = this.state.selected.concat();
selected.splice(depth, selected.length - depth);
selected.push(option);
this.setState({
selected
});
}
renderOption(option: Option, index: number, depth: number) {
const {
labelClassName,
disabled,
@ -22,17 +44,18 @@ export class NestedCheckboxes extends Checkboxes {
return (
<div
key={index}
className={cx('ListCheckboxes-group', option.className)}
className={cx(
'NestedCheckboxes-item',
itemClassName,
option.className,
disabled || option.disabled ? 'is-disabled' : '',
~this.state.selected.indexOf(option) ? 'is-active' : ''
)}
onClick={() => this.selectOption(option, depth)}
>
<div className={cx('ListCheckboxes-itemLabel')}>
<div className={cx('NestedCheckboxes-itemLabel')}>
{itemRender(option)}
</div>
<div className={cx('ListCheckboxes-items', option.className)}>
{option.children.map((child, index) =>
this.renderOption(child, index)
)}
</div>
</div>
);
}
@ -41,14 +64,14 @@ export class NestedCheckboxes extends Checkboxes {
<div
key={index}
className={cx(
'ListCheckboxes-item',
'NestedCheckboxes-item',
itemClassName,
option.className,
disabled || option.disabled ? 'is-disabled' : ''
)}
onClick={() => this.toggleOption(option)}
>
<div className={cx('ListCheckboxes-itemLabel')}>
<div className={cx('NestedCheckboxes-itemLabel')}>
{itemRender(option)}
</div>
@ -69,22 +92,76 @@ export class NestedCheckboxes extends Checkboxes {
className,
placeholder,
classnames: cx,
option2value
option2value,
itemRender
} = this.props;
this.valueArray = Checkboxes.value2array(value, options, option2value);
let body: Array<React.ReactNode> = [];
if (Array.isArray(options) && options.length) {
body = options.map((option, key) => this.renderOption(option, key));
const selected: Array<Option | null> = this.state.selected.concat();
const depth = getTreeDepth(options);
times(depth - selected.length, () => selected.push(null));
selected.reduce(
(
{
body,
options,
subTitle
}: {
body: Array<React.ReactNode>;
options: Array<Option> | null;
subTitle?: string;
},
selected,
depth
) => {
let nextOptions: Array<Option> = [];
let nextSubTitle: string = '';
body.push(
<div key={depth} className={cx('NestedCheckboxes-col')}>
{subTitle ? (
<div className={cx('NestedCheckboxes-subTitle')}>
{subTitle}
</div>
) : null}
{Array.isArray(options) && options.length
? options.map((option, index) => {
if (option === selected) {
nextSubTitle = option.subTitle;
nextOptions = option.children!;
}
return this.renderOption(option, index, depth);
})
: null}
</div>
);
return {
options: nextOptions,
subTitle: nextSubTitle,
body: body
};
},
{
options,
body
}
);
}
return (
<div className={cx('ListCheckboxes', className)}>
<div className={cx('NestedCheckboxes', className)}>
{body && body.length ? (
body
) : (
<div className={cx('ListCheckboxes-placeholder')}>{placeholder}</div>
<div className={cx('NestedCheckboxes-placeholder')}>
{placeholder}
</div>
)}
</div>
);

View File

@ -990,6 +990,22 @@ export function spliceTree<T extends TreeItem>(
return list;
}
/**
*
* @param tree
*/
export function getTreeDepth<T extends TreeItem>(tree: Array<T>): number {
return Math.max(
...tree.map(item => {
if (Array.isArray(item.children)) {
return 1 + getTreeDepth(item.children);
}
return 1;
})
);
}
export function ucFirst(str?: string) {
return str ? str.substring(0, 1).toUpperCase() + str.substring(1) : '';
}