Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tree-select): [tree-select] add data init and optimize demo docs #2538

Merged
merged 3 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { ref } from 'vue'
import { TinyTreeSelect } from '@opentiny/vue'

const value = ref('')
const value = ref(4)

const treeOp = ref({
data: [
Expand Down
2 changes: 1 addition & 1 deletion examples/sites/demos/pc/app/tree-select/basic-usage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
},
data() {
return {
value: '',
value: 10,
treeOp: {
data: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { ref } from 'vue'
import { TinyTreeSelect } from '@opentiny/vue'

const value = ref([])
const value = ref([9, 6])

const treeOp = ref({
data: [
Expand Down
2 changes: 1 addition & 1 deletion examples/sites/demos/pc/app/tree-select/collapse-tags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default {
},
data() {
return {
value: [],
value: [9, 6],
treeOp: {
data: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { ref } from 'vue'
import { TinyTreeSelect } from '@opentiny/vue'

const value = ref([])
const value = ref([9, 6])

const treeOp = ref({
data: [
Expand Down
2 changes: 1 addition & 1 deletion examples/sites/demos/pc/app/tree-select/multiple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
},
data() {
return {
value: [],
value: [9, 6],
treeOp: {
data: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<div>
<p>medium</p>
<tiny-tree-select v-model="value" size="medium" multiple :tree-op="treeOp"></tiny-tree-select>
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
<p>默认</p>
<tiny-tree-select v-model="value" multiple :tree-op="treeOp"></tiny-tree-select>
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reorder size variants from largest to smallest

The current order of size variants doesn't follow a logical progression. To improve user comprehension, consider reordering the components from largest to smallest size:

  1. medium
  2. default
  3. small
  4. mini

Apply this reordering to match the PR objective of displaying options from large to small:

  <p>medium</p>
  <tiny-tree-select v-model="value" size="medium" multiple :tree-op="treeOp"></tiny-tree-select>
  <p>默认</p>
  <tiny-tree-select v-model="value" multiple :tree-op="treeOp"></tiny-tree-select>
- <p>small</p>
- <tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
  <p>mini</p>
  <tiny-tree-select v-model="value" size="mini" multiple :tree-op="treeOp"> </tiny-tree-select>
+ <p>small</p>
+ <tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
<p>medium</p>
<tiny-tree-select v-model="value" size="medium" multiple :tree-op="treeOp"></tiny-tree-select>
<p>默认</p>
<tiny-tree-select v-model="value" multiple :tree-op="treeOp"></tiny-tree-select>
<p>mini</p>
<tiny-tree-select v-model="value" size="mini" multiple :tree-op="treeOp"> </tiny-tree-select>
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>

<p>mini</p>
<tiny-tree-select v-model="value" size="mini" multiple :tree-op="treeOp"> </tiny-tree-select>
</div>
Expand Down
4 changes: 2 additions & 2 deletions examples/sites/demos/pc/app/tree-select/size.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<div>
<p>medium</p>
<tiny-tree-select v-model="value" size="medium" multiple :tree-op="treeOp"></tiny-tree-select>
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
<p>默认</p>
<tiny-tree-select v-model="value" multiple :tree-op="treeOp"></tiny-tree-select>
<p>small</p>
<tiny-tree-select v-model="value" size="small" multiple :tree-op="treeOp"> </tiny-tree-select>
<p>mini</p>
<tiny-tree-select v-model="value" size="mini" multiple :tree-op="treeOp"> </tiny-tree-select>
</div>
Expand Down
123 changes: 123 additions & 0 deletions packages/renderless/src/tree-select/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { find } from '../common/array'

/**
* 树节点过滤事件
*/
export const filter =
({ vm }) =>
(value) => {
vm.$refs.treeRef.filter(value)
}

/**
* 单选,节点选择事件
*/
export const nodeClick =
({ props, vm, emit }) =>
(data) => {
Expand All @@ -24,6 +32,9 @@ export const nodeClick =
}
}

/**
* 多选,勾选事件
*/
export const check =
({ props, vm, emit }) =>
(data, { checkedNodes }) => {
Expand All @@ -47,3 +58,115 @@ export const check =
emit('update:modelValue', currentValue)
}
}

/**
* 嵌套树结构转成扁平树结构
* @params data 嵌套树结构 示例:[{ children: [ { label: '二级 1-1', value: 4 }, ... ], label: '一级 1', value: 1 }, ...]
* @return 扁平树结构 示例:[{ label: '一级 1', value: 1, pId: null }, { label: '二级 1-1', value: 4, pId: 1 }, ...]
*/
export const getTreeData =
({ props, state }) =>
(data) => {
const nodes = []
const getChild = (data, pId) => {
data.forEach((node) => {
node.pId = pId
nodes.push(node)

if (node[state.childrenName] && node[state.childrenName].length > 0) {
getChild(node[state.childrenName], node[props.valueField])
}
})
Comment on lines +72 to +79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid mutating input data in getTreeData function

The getTreeData function mutates the input data by assigning node.pId = pId. This could lead to unexpected side effects if the original data is used elsewhere. Consider creating a new node object to prevent data mutation.

Apply this diff to avoid mutating the input data:

const getChild = (data, pId) => {
  data.forEach((node) => {
+   const newNode = { ...node, pId }
+   nodes.push(newNode)
    nodes.push(node)
-   node.pId = pId
-   nodes.push(node)
-   if (node[state.childrenName] && node[state.childrenName].length > 0) {
+   if (newNode[state.childrenName] && newNode[state.childrenName].length > 0) {
      getChild(newNode[state.childrenName], newNode[props.valueField])
    }
  })
}

Committable suggestion skipped: line range outside the PR's diff.

}

getChild(data, null)

return nodes
}

/**
* 多选,获取匹配的树节点
* @params value 树节点 value
* @return 完整的树节点 示例:[{ label: "三级 1-1-2", value: 10, pId: 1, children: [...] }]
*/
export const getPluginOption =
({ api, props, state }) =>
(value) => {
const isRemote =
(props.filterable || props.searchable) &&
props.remote &&
(typeof props.remoteMethod === 'function' || typeof props.initQuery === 'function')
const { textField, valueField } = props
const sourceData = isRemote ? state.remoteData : api.getTreeData(state.treeData)
const selNode = find(sourceData, (item) => item[valueField] === value)
const items = []

if (selNode) {
selNode.currentLabel = selNode[textField]
items.push(selNode)
}

return items
}

/**
* 多选,获取选中的树节点 value 数组,用于初始化树节点的勾选状态
* @return 示例:[9, 6]
*/
export const getCheckedData =
({ props, state }) =>
() => {
const checkedKey = []

if (!Array.isArray(state.selected)) {
return props.modelValue ? [props.modelValue] : [state.selected[props.valueField]]
} else {
state.selected.length > 0 &&
state.selected.forEach((item) => {
checkedKey.push(item[props.valueField])
})

return checkedKey
}
}

export const mounted =
({ api, state, props, vm }) =>
() => {
if (!state.value || state.value.length === 0) return

if (props.multiple) {
let initialNodes = []
if (Array.isArray(state.value)) {
state.value.forEach((value) => {
const option = api.getPluginOption(value)
initialNodes = initialNodes.concat(option)
})
}

vm.$refs.baseSelectRef.updateSelectedData(
initialNodes.map((node) => {
return {
...node,
currentLabel: node[props.textField],
value: node[props.valueField],
isTree: true
}
})
)

state.defaultCheckedKeys = api.getCheckedData()[0]
} else {
const data = api.getPluginOption(state.value)[0]
vm.$refs.baseSelectRef.updateSelectedData({
...data,
currentLabel: data[props.textField],
value: data[props.valueField],
state: {
currentLabel: data[props.textField]
}
})

state.currentKey = data[props.valueField]
}
Comment on lines +136 to +171
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Safely handle undefined data in mounted function

In the mounted function, api.getPluginOption(state.value) may return an empty array if no matching node is found, resulting in data being undefined. Accessing properties of data without checking could lead to runtime errors. Please ensure data is defined before proceeding.

Apply this diff to handle potential undefined data:

const data = api.getPluginOption(state.value)[0]
+ if (data) {
  vm.$refs.baseSelectRef.updateSelectedData({
    ...data,
    currentLabel: data[props.textField],
    value: data[props.valueField],
    state: {
      currentLabel: data[props.textField]
    }
  })

  state.currentKey = data[props.valueField]
+ } else {
+   // Handle the case when data is undefined
+ }

Committable suggestion skipped: line range outside the PR's diff.

}
24 changes: 17 additions & 7 deletions packages/renderless/src/tree-select/vue.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { filter, nodeClick, check } from './index'
import { check, filter, getCheckedData, getPluginOption, getTreeData, mounted, nodeClick } from './index'

export const api = ['state', 'filter', 'nodeClick', 'check']
export const api = ['state', 'check', 'filter', 'nodeClick']

export const renderless = (props, { reactive, computed, watch }, { vm, emit }) => {
export const renderless = (props, { reactive, computed, watch, onMounted }, { vm, emit }) => {
const api = {}

const state = reactive({
value: computed(() => props.modelValue),
treeData: props.treeOp.data
childrenName: computed(() => (props.treeOp.props && props.treeOp.props.children) || 'children'),
currentKey: props.modelValue,
defaultCheckedKeys: [],
remoteData: [],
treeData: props.treeOp.data,
value: computed(() => props.modelValue)
})
Comment on lines +9 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve props access safety with optional chaining.

The childrenName computed property should use optional chaining to safely handle cases where props.treeOp.props might be undefined.

-    childrenName: computed(() => (props.treeOp.props && props.treeOp.props.children) || 'children'),
+    childrenName: computed(() => props.treeOp.props?.children || 'children'),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
childrenName: computed(() => (props.treeOp.props && props.treeOp.props.children) || 'children'),
currentKey: props.modelValue,
defaultCheckedKeys: [],
remoteData: [],
treeData: props.treeOp.data,
value: computed(() => props.modelValue)
childrenName: computed(() => props.treeOp.props?.children || 'children'),
currentKey: props.modelValue,
defaultCheckedKeys: [],
remoteData: [],
treeData: props.treeOp.data,
value: computed(() => props.modelValue)
🧰 Tools
🪛 Biome

[error] 9-9: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


Object.assign(api, {
state,
check: check({ props, vm, emit }),
filter: filter({ vm }),
nodeClick: nodeClick({ props, vm, emit }),
check: check({ props, vm, emit })
getCheckedData: getCheckedData({ props, state }),
getPluginOption: getPluginOption({ api, props, state }),
getTreeData: getTreeData({ props, state }),
mounted: mounted({ api, state, props, vm }),
nodeClick: nodeClick({ props, vm, emit })
})

watch(
Expand All @@ -23,5 +31,7 @@ export const renderless = (props, { reactive, computed, watch }, { vm, emit }) =
{ immediate: true, deep: true }
)

onMounted(api.mounted)

return api
}
14 changes: 8 additions & 6 deletions packages/vue/src/tree-select/src/pc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@
ref="baseSelectRef"
class="tiny-tree-select"
v-model="state.value"
:multiple="multiple"
:filterable="filterable"
:clearable="clearable"
:filterable="filterable"
:filter-method="filter"
:multiple="multiple"
>
<template #panel>
<tiny-tree
ref="treeRef"
:current-node-key="!multiple ? state.currentKey : ''"
:data="state.treeData"
:default-checked-keys="multiple ? state.defaultCheckedKeys : treeOp.defaultCheckedKeys || []"
:default-expand-all="true"
:expand-on-click-node="false"
:filter-node-method="filterMethod"
:icon-trigger-click-node="false"
:default-expand-all="true"
:props="{ label: textField }"
:node-key="valueField"
:props="{ label: textField }"
:show-checkbox="multiple"
:filter-node-method="filterMethod"
@node-click="nodeClick"
@check="check"
@node-click="nodeClick"
></tiny-tree>
</template>
</tiny-base-select>
Expand Down
Loading