Skip to content

Commit

Permalink
feat(ui): add tree component
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Aug 4, 2022
1 parent 47679f4 commit fb8a7b3
Show file tree
Hide file tree
Showing 47 changed files with 1,358 additions and 246 deletions.
3 changes: 1 addition & 2 deletions packages/ui/src/components/_alert-popover/AlertPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { useRef } from 'react';
import React, { useRef } from 'react';

import { useAsync, useMount } from '../../hooks';

Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/_popup/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './Popup';

export * from './hooks';
11 changes: 5 additions & 6 deletions packages/ui/src/components/_transition/CollapseTransition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ export function DCollapseTransition(props: DCollapseTransitionProps): JSX.Elemen
transitionStyle,
{
[dHorizontal ? 'width' : 'height']: dSize,

overflow: 'hidden',
[dHorizontal ? 'overflowX' : 'overflowY']: 'hidden',
},
dHorizontal
? {
Expand All @@ -93,7 +92,7 @@ export function DCollapseTransition(props: DCollapseTransitionProps): JSX.Elemen
case 'entering':
Object.assign(transitionStyle, {
[dHorizontal ? 'width' : 'height']: dataRef.current[dHorizontal ? 'width' : 'height'],
overflow: 'hidden',
[dHorizontal ? 'overflowX' : 'overflowY']: 'hidden',
});
break;

Expand All @@ -105,7 +104,7 @@ export function DCollapseTransition(props: DCollapseTransitionProps): JSX.Elemen
}
Object.assign(transitionStyle, {
[dHorizontal ? 'width' : 'height']: dataRef.current[dHorizontal ? 'width' : 'height'],
overflow: 'hidden',
[dHorizontal ? 'overflowX' : 'overflowY']: 'hidden',
});
break;

Expand All @@ -114,7 +113,7 @@ export function DCollapseTransition(props: DCollapseTransitionProps): JSX.Elemen
transitionStyle,
{
[dHorizontal ? 'width' : 'height']: dSize,
overflow: 'hidden',
[dHorizontal ? 'overflowX' : 'overflowY']: 'hidden',
},
dHorizontal
? {
Expand All @@ -135,7 +134,7 @@ export function DCollapseTransition(props: DCollapseTransitionProps): JSX.Elemen
case 'leaved':
Object.assign(transitionStyle, {
[dHorizontal ? 'width' : 'height']: dSize,
overflow: 'hidden',
[dHorizontal ? 'overflowX' : 'overflowY']: 'hidden',
});
break;

Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/components/auto-complete/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ function AutoComplete<T extends DAutoCompleteItem>(
role="listbox"
aria-activedescendant={isUndefined(focusItem) ? undefined : getItemId(focusItem.value)}
dList={dList}
dItemRender={(item, index, renderProps, parent) => {
dItemRender={(item, index, { iARIA, iChildren }, parent) => {
const { value: itemValue, disabled: itemDisabled, children } = item;

const itemNode = dCustomItem ? dCustomItem(item) : itemValue;
Expand All @@ -385,15 +385,15 @@ function AutoComplete<T extends DAutoCompleteItem>(
<div className={`${dPrefix}auto-complete__option-content`}>{t('No Data')}</div>
</li>
) : (
renderProps.children
iChildren
)}
</ul>
);
}

return (
<li
{...renderProps}
{...iARIA}
key={itemValue}
id={getItemId(itemValue)}
className={getClassName(`${dPrefix}auto-complete__option`, {
Expand Down Expand Up @@ -423,7 +423,7 @@ function AutoComplete<T extends DAutoCompleteItem>(
return 32;
}}
dItemNested={(item) => item.children}
dCompareItem={(a, b) => a.value === b.value}
dItemKey={(item) => item.value}
dFocusable={canSelectItem}
dFocusItem={focusItem}
dSize={264}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/auto-complete/demos/2.Group.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function Demo() {
.fill(0)
.map((cn, ci) => ({
value:
`G${i + 1} ` +
`G${i + 1}-` +
Array(ci + 1)
.fill(val)
.join(''),
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/auto-complete/demos/3.Loading.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title:
en-US: Loading
zh-Hant: 加载
en-US: Dynamic loading
zh-Hant: 动态加载
---

# en-US
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/auto-complete/demos/4.Custom.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title:
en-US: Customize
zh-Hant: 自定义
en-US: Custom display
zh-Hant: 自定义显示
---

# en-US
Expand Down
30 changes: 20 additions & 10 deletions packages/ui/src/components/cascader/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import type { DId, DNestedChildren, DSize } from '../../utils/global';
import type { DDropdownItem } from '../dropdown';
import type { DFormControl } from '../form';
import type { DSelectItem } from '../select';
import type { AbstractTreeNode } from '../tree';
import type { AbstractTreeNode } from '../tree/node';

import { isNull } from 'lodash';
import React, { useCallback, useState, useId, useMemo } from 'react';
import React, { useCallback, useState, useId, useMemo, useRef } from 'react';

import { usePrefixConfig, useComponentConfig, useGeneralContext, useDValue, useEventNotify } from '../../hooks';
import { LoadingOutlined } from '../../icons';
Expand All @@ -14,7 +14,7 @@ import { DSelectbox } from '../_selectbox';
import { DDropdown } from '../dropdown';
import { useFormControl } from '../form';
import { DTag } from '../tag';
import { MultipleTreeNode, SingleTreeNode } from '../tree';
import { MultipleTreeNode, SingleTreeNode } from '../tree/node';
import { DList } from './List';
import { DSearchList } from './SearchList';
import { getText, TREE_NODE_KEY } from './utils';
Expand All @@ -34,8 +34,8 @@ export interface DCascaderItem<V extends DId> {

export interface DCascaderProps<V extends DId, T extends DCascaderItem<V>> extends React.HTMLAttributes<HTMLDivElement> {
dFormControl?: DFormControl;
dModel?: V | null | V[];
dList: DNestedChildren<T>[];
dModel?: V | null | V[];
dVisible?: boolean;
dPlaceholder?: string;
dSize?: DSize;
Expand All @@ -59,7 +59,7 @@ export interface DCascaderProps<V extends DId, T extends DCascaderItem<V>> exten
afterVisibleChange?: (visible: boolean) => void;
onSearch?: (value: string) => void;
onClear?: () => void;
onFocusChange?: (value: V, item: DNestedChildren<T>) => void;
onFirstFocus?: (value: V, item: DNestedChildren<T>) => void;
}

const { COMPONENT_NAME } = registerComponentMate({ COMPONENT_NAME: 'DCascader' });
Expand All @@ -69,8 +69,8 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
): JSX.Element | null {
const {
dFormControl,
dModel,
dList,
dModel,
dVisible,
dPlaceholder,
dSize,
Expand All @@ -91,7 +91,7 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
afterVisibleChange,
onSearch,
onClear,
onFocusChange,
onFirstFocus,

...restProps
} = useComponentConfig(COMPONENT_NAME, props);
Expand All @@ -101,6 +101,10 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
const { gSize, gDisabled } = useGeneralContext();
//#endregion

const dataRef = useRef<{
focusList: Set<V>;
}>({ focusList: new Set() });

const onKeyDown$ = useEventNotify<React.KeyboardEvent<HTMLInputElement>>();

const uniqueId = useId();
Expand Down Expand Up @@ -220,7 +224,7 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
}

if (hasSelected) {
return findNested(renderNodes as AbstractTreeNode<V, T>[], (node) => node.enabled && node.checked);
return findNested(renderNodes, (node) => node.enabled && node.checked);
}
})();

Expand Down Expand Up @@ -393,7 +397,10 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
changeVisible(false);
}}
onFocusChange={(item) => {
onFocusChange?.(item.value, item[TREE_NODE_KEY].origin);
if (!dataRef.current.focusList.has(item.value)) {
dataRef.current.focusList.add(item.value);
onFirstFocus?.(item.value, item[TREE_NODE_KEY].origin);
}

setSearchFocusItem(item);
}}
Expand All @@ -416,7 +423,10 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
changeVisible(false);
}}
onFocusChange={(node) => {
onFocusChange?.(node.id, node.origin);
if (!dataRef.current.focusList.has(node.id)) {
dataRef.current.focusList.add(node.id);
onFirstFocus?.(node.id, node.origin);
}

setNoSearchFocusNode(node);
}}
Expand Down
88 changes: 42 additions & 46 deletions packages/ui/src/components/cascader/List.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DId } from '../../utils/global';
import type { AbstractTreeNode, MultipleTreeNode } from '../tree';
import type { AbstractTreeNode, MultipleTreeNode } from '../tree/node';
import type { DVirtualScrollRef } from '../virtual-scroll';
import type { DCascaderItem } from './Cascader';
import type { Subject } from 'rxjs';
Expand Down Expand Up @@ -171,54 +171,50 @@ export function DList<ID extends DId, T extends DCascaderItem<ID>>(props: DListP
className={`${dPrefix}cascader__list`}
role="listbox"
aria-multiselectable={dMultiple}
aria-activedescendant={dRoot && dFocusNode ? dGetItemId(dFocusNode.id) : undefined}
aria-activedescendant={dRoot && !isUndefined(dFocusNode) ? dGetItemId(dFocusNode.id) : undefined}
dList={dNodes}
dItemRender={(item, index, renderProps) => {
return (
<li
{...renderProps}
key={item.id}
id={dGetItemId(item.id)}
className={getClassName(`${dPrefix}cascader__option`, {
'is-focus': item.id === inFocusNode?.id,
'is-selected': !dMultiple && item.checked,
'is-disabled': item.disabled,
})}
title={item.origin.label}
role="option"
aria-selected={item.checked}
aria-disabled={item.disabled}
onClick={() => {
onFocusChange(item);
if (!dMultiple || item.isLeaf) {
dItemRender={(item, index, { iARIA }) => (
<li
{...iARIA}
key={item.id}
id={dGetItemId(item.id)}
className={getClassName(`${dPrefix}cascader__option`, {
'is-focus': item.id === inFocusNode?.id,
'is-selected': !dMultiple && item.checked,
'is-disabled': item.disabled,
})}
title={item.origin.label}
role="option"
aria-selected={item.checked}
aria-disabled={item.disabled}
onClick={() => {
onFocusChange(item);
if (!dMultiple || item.isLeaf) {
changeSelectByClick(item);
}
}}
>
{dFocusVisible && item.id === dFocusNode?.id && <div className={`${dPrefix}focus-outline`}></div>}
{dMultiple && (
<DCheckbox
dModel={item.checked}
dDisabled={item.disabled}
dIndeterminate={item.indeterminate}
onClick={(e) => {
e.stopPropagation();
onFocusChange(item);
changeSelectByClick(item);
}
}}
>
{dFocusVisible && item.id === dFocusNode?.id && <div className={`${dPrefix}focus-outline`}></div>}
{dMultiple && (
<DCheckbox
dModel={item.checked}
dDisabled={item.disabled}
dIndeterminate={item.indeterminate}
onClick={(e) => {
e.stopPropagation();
onFocusChange(item);
changeSelectByClick(item);
}}
></DCheckbox>
)}
<div className={`${dPrefix}cascader__option-content`}>{dCustomItem ? dCustomItem(item.origin) : item.origin.label}</div>
{!item.isLeaf && (
<div className={`${dPrefix}cascader__option-icon`}>
{item.origin.loading ? <LoadingOutlined dSpin /> : <RightOutlined />}
</div>
)}
</li>
);
}}
}}
></DCheckbox>
)}
<div className={`${dPrefix}cascader__option-content`}>{dCustomItem ? dCustomItem(item.origin) : item.origin.label}</div>
{!item.isLeaf && (
<div className={`${dPrefix}cascader__option-icon`}>{item.origin.loading ? <LoadingOutlined dSpin /> : <RightOutlined />}</div>
)}
</li>
)}
dItemSize={32}
dCompareItem={(a, b) => a.id === b.id}
dItemKey={(item) => item.id}
dFocusable={(item) => item.enabled}
dFocusItem={inFocusNode}
dSize={264}
Expand Down
11 changes: 6 additions & 5 deletions packages/ui/src/components/cascader/SearchList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { DId } from '../../utils/global';
import type { MultipleTreeNode } from '../tree';
import type { MultipleTreeNode } from '../tree/node';
import type { DVirtualScrollRef } from '../virtual-scroll';
import type { DCascaderItem, DSearchItem } from './Cascader';
import type { Subject } from 'rxjs';

import { isUndefined } from 'lodash';
import React, { useEffect, useRef } from 'react';

import { useEventCallback, usePrefixConfig, useTranslation } from '../../hooks';
Expand Down Expand Up @@ -126,9 +127,9 @@ export function DSearchList<ID extends DId, T extends DCascaderItem<ID>>(props:
className={`${dPrefix}cascader__list`}
role="listbox"
aria-multiselectable={dMultiple}
aria-activedescendant={dFocusItem ? dGetItemId(dFocusItem.value) : undefined}
aria-activedescendant={isUndefined(dFocusItem) ? undefined : dGetItemId(dFocusItem.value)}
dList={dList}
dItemRender={(item, index, renderProps) => {
dItemRender={(item, index, { iARIA }) => {
const node = item[TREE_NODE_KEY];
let inSelected = node.checked;
if (!dOnlyLeafSelectable) {
Expand All @@ -144,7 +145,7 @@ export function DSearchList<ID extends DId, T extends DCascaderItem<ID>>(props:

return (
<li
{...renderProps}
{...iARIA}
key={item.value}
id={dGetItemId(item.value)}
className={getClassName(`${dPrefix}cascader__option`, {
Expand All @@ -167,7 +168,7 @@ export function DSearchList<ID extends DId, T extends DCascaderItem<ID>>(props:
);
}}
dItemSize={32}
dCompareItem={(a, b) => a.value === b.value}
dItemKey={(item) => item.value}
dFocusable={(item) => item[TREE_NODE_KEY].enabled}
dFocusItem={dFocusItem}
dSize={264}
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/components/cascader/demos/5.Loading.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title:
en-US: Loading
zh-Hant: 加载
en-US: Dynamic loading
zh-Hant: 动态加载
---

# en-US
Expand Down Expand Up @@ -82,7 +82,7 @@ export default function Demo() {
dList={list2}
dPlaceholder="Focus"
dClearable
onFocusChange={(value) => {
onFirstFocus={(value) => {
const findIndex = list2.findIndex((item) => item.value === value);
if (findIndex !== -1) {
list2[findIndex].loading = true;
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/cascader/demos/6.Custom.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title:
en-US: Customize
zh-Hant: 自定义
en-US: Custom display
zh-Hant: 自定义显示
---

# en-US
Expand Down
Loading

0 comments on commit fb8a7b3

Please sign in to comment.