شجرة Tree
تتم في دوال نقية بالطبقة الأساسية core كلٌّ من تحويل البيانات المسطحة إلى شجرة وانتشار التحديد، ولا يتحمَّل المكوّن سوى العرض والتنقّل بلوحة المفاتيح.
الاستخدام الأساسي
<script setup>
import { ref } from 'vue'
const treeData = [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
]
</script>
<template>
<!-- الاستخدام الأساسي -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template><script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
],
}
},
}
</script>
<template>
<!-- الاستخدام الأساسي -->
<LhTree :data="treeData" :default-expanded-keys="['a']" />
</template>قابل للتحديد (متسلسل)
يتسلسل تحديد العقدة الأصل إلى جميع أحفادها؛ وعند تحديد جميع الأبناء تُحدَّد الأصل تلقائيًا، ويعرض التحديد الجزئي الحالة الوسيطة (indeterminate).
<script setup lang="ts">
const treeData = [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
]
</script>
<template>
<!-- تسلسل التحديد -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template><script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
],
}
},
}
</script>
<template>
<!-- تسلسل التحديد -->
<LhTree :data="treeData" show-checkbox :default-expanded-keys="['a', 'b']" />
</template>التحميل الكسول
عند تفعيل lazy، يستدعي توسيع عقدة غير محمَّلة دالة الاستدعاء load (يُرسِل resolve العقد الفرعية ويوسِّعها، بينما يُلغي reject)؛ ويُظهر سهم التحميل حالة دوران أثناءه. ويجب التصريح صراحةً بـ isLeaf: false للعقد القابلة للتحميل (وإلّا فالعقد التي بلا children تُعدّ ورقية ولا يظهر لها سهم التوسيع)؛ وإذا كانت العقد الفرعية المحمَّلة فروعًا بانتظار التحميل فلتُصرَّح هي الأخرى بـ isLeaf: false، أما الأوراق الحقيقية فتُصرَّح بـ isLeaf: true.
<script setup>
import { ref } from 'vue'
const lazyData = ref([
{ key: 'lazy', label: 'انقر للتحميل', isLeaf: false },
])
function loadNodes(node, resolve, reject) {
// محاكاة طلب غير متزامن
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: 'عقدة فرعية أولى', isLeaf: true },
{ key: 'lazy-2', label: 'عقدة فرعية ثانية', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
}
</script>
<template>
<!-- تحميل كسول -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template><script>
export default {
data() {
return {
lazyData: [{ key: 'lazy', label: 'انقر للتحميل', isLeaf: false }],
}
},
methods: {
// محاكاة طلب غير متزامن
loadNodes(node, resolve, reject) {
setTimeout(() => {
if (node.key === 'lazy') {
resolve([
{ key: 'lazy-1', label: 'عقدة فرعية أولى', isLeaf: true },
{ key: 'lazy-2', label: 'عقدة فرعية ثانية', isLeaf: true },
])
} else {
reject(new Error('nothing to load'))
}
}, 600)
},
},
}
</script>
<template>
<!-- تحميل كسول -->
<LhTree :data="lazyData" lazy :load="loadNodes" />
</template>عدم ربط الأصل بالابن (checkStrictly)
عند تفعيل checkStrictly يعمل التحديد على العقدة نفسها فقط: بلا تسلسل للأحفاد، وبلا ارجاع للأصول، وبلا حالة وسيطة.
<script setup lang="ts">
const treeData = [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
]
</script>
<template>
<!-- عدم ربط الأصل بالابن -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template><script>
export default {
data() {
return {
treeData: [
{ key: 'a', label: 'الواجهات', children: [
{ key: 'a-1', label: 'Vue' },
{ key: 'a-2', label: 'React' },
] },
{ key: 'b', label: 'الخوادم', children: [{ key: 'b-1', label: 'Node' }] },
{ key: 'c', label: 'العمليات', disabled: true },
],
}
},
}
</script>
<template>
<!-- عدم ربط الأصل بالابن -->
<LhTree :data="treeData" show-checkbox check-strictly :default-expanded-keys="['a', 'b']" />
</template>التحديد والمفاتيح المتحكَّم بها
يسمح multiple بالتحديد المتعدد؛ ويُوسِّع expand-on-click-node العقدة أو يطويها عند النقر عليها؛ وتتحكَّم default-selected-keys / v-model:selected-keys في التحديد؛ وdefault-checked-keys / v-model:checked-keys في التحديد بخانات الاختيار؛ وdefault-expanded-keys / v-model:expanded-keys في التوسيع؛ ويعطّل disabled الشجرة بأكملها.
محدَّد: [ "x" ] | محدَّد بخانة: [ "x-1" ]
<script setup>
import { ref } from 'vue'
const selKeys = ref(['x'])
const chkKeys = ref(['x-1'])
const ctrlData = [
{ key: 'x', label: 'التصميم', children: [
{ key: 'x-1', label: 'الألوان' },
{ key: 'x-2', label: 'الخطوط' },
] },
{ key: 'y', label: 'الهندسة' },
]
</script>
<template>
<!-- تحديد وتحديد بخانات متحكَّم بهما -->
<LhTree
v-model:selected-keys="selKeys"
v-model:checked-keys="chkKeys"
:data="ctrlData"
multiple
expand-on-click-node
:default-expanded-keys="['x']"
:default-selected-keys="['x']"
:default-checked-keys="['x-1']"
/>
<p class="demo-tree-log">محدَّد: {{ selKeys }} | محدَّد بخانة: {{ chkKeys }}</p>
<!-- تعطيل الشجرة بأكملها -->
<LhTree :data="ctrlData" disabled />
</template><script>
export default {
data() {
return {
selKeys: ['x'],
chkKeys: ['x-1'],
ctrlData: [
{ key: 'x', label: 'التصميم', children: [
{ key: 'x-1', label: 'الألوان' },
{ key: 'x-2', label: 'الخطوط' },
] },
{ key: 'y', label: 'الهندسة' },
],
}
},
}
</script>
<template>
<!-- تحديد وتحديد بخانات متحكَّم بهما -->
<LhTree
v-model:selected-keys="selKeys"
v-model:checked-keys="chkKeys"
:data="ctrlData"
multiple
expand-on-click-node
:default-expanded-keys="['x']"
:default-selected-keys="['x']"
:default-checked-keys="['x-1']"
/>
<p class="demo-tree-log">محدَّد: {{ selKeys }} | محدَّد بخانة: {{ chkKeys }}</p>
<!-- تعطيل الشجرة بأكملها -->
<LhTree :data="ctrlData" disabled />
</template>API
LhTree Props
| اسم الخاصية | الوصف | النوع | القيمة الافتراضية |
|---|---|---|---|
| data | بيانات الشجرة | [LhTreeNode](#type-LhTreeNode)[] | — |
| showCheckbox | إظهار خانات الاختيار | boolean | false |
| multiple | تحديد متعدد (النقر على عقدة يبدِّل تحديدها بدلًا من استبداله) | boolean | false |
| expandOnClickNode | توسيع/طي العقدة غير الورقية عند النقر عليها | boolean | true |
| defaultExpandedKeys | مفاتيح التوسيع الافتراضية (غير متحكَّم بها) | string[] | () => [] |
| expandedKeys | مفاتيح التوسيع المتحكَّم بها | string[] | — |
| defaultSelectedKeys | مفاتيح التحديد الافتراضية (غير متحكَّم بها) | string[] | () => [] |
| selectedKeys | مفاتيح التحديد المتحكَّم بها | string[] | — |
| defaultCheckedKeys | مفاتيح تحديد الخانات الافتراضية (غير متحكَّم بها) | string[] | () => [] |
| checkedKeys | مفاتيح تحديد الخانات المتحكَّم بها | string[] | — |
| checkStrictly | عدم ربط تحديد الأصل بالابن (التحديد يعمل على العقدة نفسها فقط، بلا تسلسل وحالة وسيطة) | boolean | false |
| lazy | وضع التحميل الكسول (يستدعي load عند توسيع عقدة غير محمَّلة) | boolean | false |
| load | دالة الاستدعاء للتحميل الكسول (يُرسِل resolve العقد الفرعية ويوسِّعها، ويُلغي reject) | (node: LhTreeNode, resolve: (children: LhTreeNode[]) => void, reject: (reason?: unknown) => void) => void | — |
| disabled | التعطيل الكلي | boolean | false |
| sx | خصائص التخطيط السريع sx | LhSxProps | — |
Events
| اسم الحدث | الوصف | وسائط الاستدعاء |
|---|---|---|
| update:expandedKeys | يُطلَق عند تغيُّر قائمة مفاتيح التوسيع، ويُستخدم مع v-model:expanded-keys | (keys: string[]) |
| update:selectedKeys | يُطلَق عند تغيُّر قائمة مفاتيح التحديد، ويُستخدم مع v-model:selected-keys | (keys: string[]) |
| update:checkedKeys | يُطلَق عند تغيُّر قائمة مفاتيح تحديد الخانات، ويُستخدم مع v-model:checked-keys | (keys: string[]) |
| select | يُطلَق بعد تحديد عقدة بالنقر / تبديل تحديدها | (key: string, node: LhTreeNode) |
| check | يُطلَق بعد تغيُّر التحديد بخانة | (keys: string[], node: LhTreeNode) |
| expand | يُطلَق بعد توسيع / طي عقدة | (key: string, expanded: boolean) |
| nodeLoad | يُطلَق بعد عودة العقد الفرعية للتحميل الكسول | (node: LhTreeNode, children: LhTreeNode[]) |
الفتحات: node (نطاقها { node, level, expanded, selected })، وempty.
LhTreeNode
| الحقل | الوصف | النوع |
|---|---|---|
| key | مفتاح فريد | string |
| label | النص الظاهر | string |
| children | العقد الفرعية | LhTreeNode[] |
| disabled | التعطيل | boolean |
| isLeaf | هل هي ورقة (عند عدم التمرير يُستدلَّ عليها من children) | boolean |
إتاحة الوصول
role="tree"+role="treeitem"، مع إتاحةaria-level/aria-expanded/aria-selected/aria-disabledبالكامل- خانة الاختيار
role="checkbox"+aria-checked(الحالة الوسيطة هيmixed) - لوحة المفاتيح (وفق نمط APG tree): ↑/↓ للتنقّل بين العناصر (مع تخطّي المعطَّلة والتدوير)، → للتوسيع أو الدخول إلى أول ابن، ← للطي أو العودة إلى الأصل، وHome/End لأول وآخر عنصر، وEnter/Space للتنشيط؛ roving tabindex
تعريفات الأنواع
بنية الأنواع التي يُصدِّرها هذا المكوّن بلغة TypeScript كالتالي (مستخرجة آليًا من packages/components/tree/src/types.ts و@lh-ui/share/core/*؛ انقر كل عنصر لعرض التعريف الكامل؛ لأوصاف الخصائص انظر جدول Props أعلاه):
LhTreeProps
export interface LhTreeProps {
/** بيانات الشجرة */
data: LhTreeNode[]
/** إظهار خانات الاختيار */
showCheckbox?: boolean
/** تحديد متعدد (النقر على عقدة يبدِّل تحديدها بدلًا من استبداله) */
multiple?: boolean
/** توسيع/طي العقدة غير الورقية عند النقر عليها */
expandOnClickNode?: boolean
/** مفاتيح التوسيع الافتراضية (غير متحكَّم بها) */
defaultExpandedKeys?: string[]
/** مفاتيح التوسيع المتحكَّم بها */
expandedKeys?: string[]
/** مفاتيح التحديد الافتراضية (غير متحكَّم بها) */
defaultSelectedKeys?: string[]
/** مفاتيح التحديد المتحكَّم بها */
selectedKeys?: string[]
/** مفاتيح تحديد الخانات الافتراضية (غير متحكَّم بها) */
defaultCheckedKeys?: string[]
/** مفاتيح تحديد الخانات المتحكَّم بها */
checkedKeys?: string[]
/** عدم ربط تحديد الأصل بالابن (التحديد يعمل على العقدة نفسها فقط، بلا تسلسل وحالة وسيطة) */
checkStrictly?: boolean
/** وضع التحميل الكسول: يستدعي load عند توسيع عقدة غير ورقية غير محمَّلة */
lazy?: boolean
/** دالة الاستدعاء للتحميل الكسول: يُرسِل resolve العقد الفرعية ويوسِّعها، ويُلغي reject (أو رفع خطأ) */
load?: (
node: LhTreeNode,
resolve: (children: LhTreeNode[]) => void,
reject: (reason?: unknown) => void,
) => void
/** تعطيل كلي */
disabled?: boolean
/** خصائص التخطيط السريع sx */
sx?: LhSxProps
}LhFlatTreeNode (من @lh-ui/share/core/tree)
/** عقدة مسطَّحة (لأغراض العرض) */
export interface LhFlatTreeNode {
node: LhTreeNode
key: string
level: number
parentKey: string | null
/** سلسلة الأصول (لا تشمل العقدة نفسها) */
ancestorKeys: string[]
/** هل هي ورقة */
isLeaf: boolean
}LhTreeNode (من @lh-ui/share/core/tree)
/** بيانات عقدة الشجرة */
export interface LhTreeNode {
/** مفتاح فريد */
key: string
/** النص الظاهر */
label: string
/** العقد الفرعية */
children?: LhTreeNode[]
/** تعطيل */
disabled?: boolean
/** هل هي ورقة (عند عدم التمرير يُستدلَّ عليها من children) */
isLeaf?: boolean
}LhTreeCheckState (من @lh-ui/share/core/tree)
/** حالة التحديد */
export type LhTreeCheckState = 'checked' | 'indeterminate' | 'unchecked'