Current status review of 0 tree components
I want to do open source live series so far have done 8 episodes:
- Phases 1-3 design and implementation of the Shared Tree component
- Phase 4-8 shared component library engineering, and the realization of a complete mini-vue-Devui component library
By issue 8, the component library infrastructure (Vite+Vue3+TypeScript+JSX with Monorepo modification), documentation system, unit testing, build-on-demand, and release processes were all in place, meaning that we could continue to refine components based on this. It also means that we can put the engineering of the component library aside for a while and continue to develop the Tree component.
Let’s take a look at the progress of the tree component:
- Render a layer of nodes
- Render multi-layer nodes
- Added expansion/collapse function
1 Do not unfold/fold
1.1 Added the function of disallowing expansion/folding
packages/devui-vue/devui/tree/src/composables/use-toggle.ts
const toggle = (item: TreeItem) => { if (! Item.children) return if (item.disabletoggle) return // Add item.open =! item.open openedData.value = openedTree(data) }Copy the code
Add the disableToggle data to the data passed to the Tree component
[{label: 'level 1, level: 1, the children: [{label:' secondary 1-1, level: 2, children: [{label: 'level 3' 1-1-1, level: 3,}}]]}, {label: 'level 2, level: 1, the open: true, children: [{label:' secondary 2-1, level: 2, children: [{label: }, {label: 'level 2-2', level: 2, disableToggle: true, // Add children: [{label: 3, level: 3,}]}, {label:' level 2-2', level: 2, disableToggle: true, 'level 3' 2-2-1, level: 3,}}]]}, {label: 'level 3, level: 1, the open: true, children: [{label:' secondary 3-1, level: 2, the children: [{label: '3-1-1', level: 3,}]}, {label:' 3-2-1', level: 2, open: true, children: [{label: '3-2-1', level: 3,}]}, {label:' 3-2-1', level: 2, open: true, children: [{label: '3-2-1', level: 3,}] 3,}]}], {label: '1 ', level: 1,}]Copy the code
Test function normal!
1.2 Added forbidden expansion/collapse styles
packages/devui-vue/devui/tree/src/tree.ts
const renderNode = (item: TreeItem) => { return ( <div class={['devui-tree-node', item.open && 'devui-tree-node__open']} style={{ paddingLeft: `${24 * (item.level - 1)}px` }} > <div class="devui-tree-node__content"> <div class="devui-tree-node__content--value-wrapper"> { item.children ? <span class={item.disabletoggle && 'toggle-disabled'}> // add {item.open? <IconOpen class='mr-xs' onClick={() => toggle(item)} /> : <IconClose class='mr-xs' onClick={() => toggle(item)} /> } </span> : <Indent /> } <span class="devui-tree-node__title">{item.label}</span> </div> </div> </div> ) }Copy the code
Increase the style
$devui-disabled-text: var(--devui-disabled-text, #adb0b8); .toggle-disabled { cursor: not-allowed; svg.svg-icon rect { stroke: $devui-disabled-text; } svg.svg-icon path { fill: $devui-disabled-text; }}Copy the code
The effect is as follows:
Done!
1.3 Code Refactoring
The logic to remove the icon in front of the render node:
const renderIcon = (item: TreeItem) => {
return item.children
? <span class={item.disableToggle && 'toggle-disabled'}>
{
item.open
? <IconOpen class='mr-xs' onClick={() => toggle(item)} />
: <IconClose class='mr-xs' onClick={() => toggle(item)} />
}
</span>
: <Indent />
}
Copy the code
The renderNode method replaces the corresponding code with renderIcon:
<div class="devui-tree-node__content--value-wrapper">
{ renderIcon(item) }
<span class="devui-tree-node__title">{item.label}</span>
</div>
Copy the code
Don’t forget to add types to tree-types.ts:
export interface TreeItem { label: string children? : TreeData disableToggle? : Boolean // add}Copy the code
2. Highlight
2.1 Implement useHighlightNode composable
Add use-highlight.ts file:
import { ref, Ref } from 'vue' interface TypeHighlightClass { [key: string]: 'active' | '' | 'devui-tree_isDisabledNode' } type TypeUseHighlightNode = () => { nodeClassNameReflect: Ref<TypeHighlightClass> handleClickOnNode: (index: string) => void handleInitNodeClassNameReflect: (isDisabled: boolean, ... keys: Array<string>) => string } const HIGHLIGHT_CLASS = 'active' const IS_DISABLED_FLAG = 'devui-tree_isDisabledNode' const useHighlightNode: TypeUseHighlightNode = () => { const nodeClassNameReflectRef = ref<TypeHighlightClass>({}) const handleInit = (isDisabled = false, ... keys) => { const key = keys.join('-') nodeClassNameReflectRef.value[key] = isDisabled ? IS_DISABLED_FLAG : (nodeClassNameReflectRef.value[key] || '') return key } const handleClick = (key) => { if (nodeClassNameReflectRef.value[key] === IS_DISABLED_FLAG) { return } nodeClassNameReflectRef.value = Object.fromEntries( Object .entries(nodeClassNameReflectRef.value) .map(([k]) => [k, k === key ? HIGHLIGHT_CLASS : '']) ) } return { nodeClassNameReflect: nodeClassNameReflectRef, handleClickOnNode: handleClick, handleInitNodeClassNameReflect: handleInit, } } export default useHighlightNodeCopy the code
2.2 Using useHighlightNode in Setup
Use the composable use-highlight.ts in tree.tsx
const { nodeClassNameReflect, handleInitNodeClassNameReflect, handleClickOnNode } = useHighlightNode()
Copy the code
const renderNode = (item: TreeItem) => { const { key = '', label, disabled, open, level, children } = item const nodeId = handleInitNodeClassNameReflect(disabled, key, <div class={['devui-tree-node', open && 'devui-tree-node__open']} style={{paddingLeft: 24 * (level 1) ` ${} px `}} > < div class = {` devui - tree - node__content ${value} [nodeId] nodeClassNameReflect. `} / / increase the highlight style OnClick ={() => handleClickOnNode(nodeId)} // Add node click events > <div class="devui-tree-node__content--value-wrapper"> { renderIcon(item) } <span class="devui-tree-node__title">{item.label}</span> </div> </div> </div> ) }Copy the code
3 Node selection is disabled
Similar to the disable expansion/collapse function, there are two steps:
- Add forbidden logic
- Add forbidden styles
3.1 Add ban logic
Const handleClick = (key) = > {/ / add the if (nodeClassNameReflectRef. Value [key] = = = IS_DISABLED_FLAG) {return} nodeClassNameReflectRef.value = Object.fromEntries( Object .entries(nodeClassNameReflectRef.value) .map(([k]) => [k, k === key ? HIGHLIGHT_CLASS : '']) ) }Copy the code
3.2 Added forbidden styles
<div class="devui-tree-node__content--value-wrapper">
{ renderIcon(item) }
<span class={['devui-tree-node__title', item.disabled && 'select-disabled']}> // 新增
{ label }
</span>
</div>
Copy the code