@shwfed/config 2.3.11 → 2.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp.mjs +664 -525
- package/dist/module.json +1 -1
- package/dist/preview/assets/{config-DNokxY7M.js → config-B-1L3Ra-.js} +1 -1
- package/dist/preview/assets/{config-_uPI8qV-.js → config-Cf5kO84H.js} +1 -1
- package/dist/preview/assets/{config-Bp91DUdU.js → config-DTQkLHVw.js} +1 -1
- package/dist/preview/assets/{config-_msO_f2R.js → config-DqxN0Byp.js} +1 -1
- package/dist/preview/assets/{config-BZahzuEc.js → config-QUQ71Eo6.js} +1 -1
- package/dist/preview/assets/{config-BnZQO-Sp.js → config-oO93sSer.js} +1 -1
- package/dist/preview/assets/{config-Be-2ZA2R.js → config-sJ5XKUJy.js} +1 -1
- package/dist/preview/assets/{config-57-v4VXo.js → config-zCMkbPQt.js} +1 -1
- package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js → definition.vue_vue_type_script_setup_true_lang-D-OVRv68.js} +1 -1
- package/dist/preview/assets/index-Br_eXThF.css +1 -0
- package/dist/preview/assets/index-D8QyOVfp.js +637 -0
- package/dist/preview/assets/index-b_t8yWJJ.js +1 -0
- package/dist/preview/assets/{runtime-BBms4myv.js → runtime-10_7L7Gz.js} +1 -1
- package/dist/preview/assets/{runtime-cKWSGFod.js → runtime-B2EP6060.js} +1 -1
- package/dist/preview/assets/{runtime-B9u14qqB.js → runtime-BxlgShjU.js} +1 -1
- package/dist/preview/assets/{runtime-Dk9u-Ybw.js → runtime-Cv2doZNu.js} +1 -1
- package/dist/preview/assets/{runtime-XXqIAt53.js → runtime-CzDUrqSa.js} +1 -1
- package/dist/preview/assets/{runtime-CfR7ZAND.js → runtime-DcYhXvSk.js} +1 -1
- package/dist/preview/assets/{runtime-w7V-p3t1.js → runtime-DlDhRVII.js} +1 -1
- package/dist/preview/assets/{runtime-D3EyeiyA.js → runtime-cmkN6aik.js} +1 -1
- package/dist/preview/index.html +2 -2
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +1 -1
- package/dist/runtime/components/config/blocks/2026-05-17/com.shwfed.block.chart.xy/schema.d.ts +4 -0
- package/dist/runtime/components/config/blocks/2026-05-17/com.shwfed.block.chart.xy/schema.js +6 -17
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +18 -18
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +18 -18
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.d.vue.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.vue.d.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +22 -22
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +22 -22
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +12 -12
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +12 -12
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.d.vue.ts +10 -10
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.vue.d.ts +10 -10
- package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.d.vue.ts +12 -12
- package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.vue.d.ts +12 -12
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +133 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue +605 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +133 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/runtime.d.vue.ts +8 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/runtime.vue +324 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/runtime.vue.d.ts +8 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/schema.d.ts +99 -0
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/schema.js +129 -0
- package/dist/runtime/components/form/unit-config.vue +0 -23
- package/dist/runtime/components/table/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/config.vue +5 -45
- package/dist/runtime/components/table/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/schema.d.ts +9 -2
- package/dist/runtime/components/table/schema.js +30 -14
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.vue.d.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +1 -1
- package/dist/runtime/components/ui/tree/Tree.d.vue.ts +57 -0
- package/dist/runtime/components/ui/tree/Tree.vue +325 -0
- package/dist/runtime/components/ui/tree/Tree.vue.d.ts +57 -0
- package/dist/runtime/components/ui/tree/TreeNode.d.vue.ts +53 -0
- package/dist/runtime/components/ui/tree/TreeNode.vue +299 -0
- package/dist/runtime/components/ui/tree/TreeNode.vue.d.ts +53 -0
- package/dist/runtime/components/ui/tree/index.d.ts +3 -0
- package/dist/runtime/components/ui/tree/index.js +2 -0
- package/dist/runtime/components/ui/tree/types.d.ts +120 -0
- package/dist/runtime/components/ui/tree/types.js +0 -0
- package/dist/runtime/components/ui/tree/useTreeState.d.ts +95 -0
- package/dist/runtime/components/ui/tree/useTreeState.js +369 -0
- package/dist/runtime/shims.d.ts +2 -0
- package/package.json +1 -1
- package/dist/preview/assets/index-DDbl2Atj.js +0 -1
- package/dist/preview/assets/index-DGa3Oj3y.js +0 -1075
- package/dist/preview/assets/index-mbGtsgdv.css +0 -1
- package/dist/runtime/components/form/ai/fields-button.d.vue.ts +0 -13
- package/dist/runtime/components/form/ai/fields-button.vue +0 -460
- package/dist/runtime/components/form/ai/fields-button.vue.d.ts +0 -13
- package/dist/runtime/components/form/ai/fields-task.md +0 -71
- package/dist/runtime/components/table/ai/columns-button.d.vue.ts +0 -12
- package/dist/runtime/components/table/ai/columns-button.vue +0 -492
- package/dist/runtime/components/table/ai/columns-button.vue.d.ts +0 -12
- package/dist/runtime/components/table/ai/columns-task.md +0 -53
- package/dist/runtime/components/table/ai/data-source-button.d.vue.ts +0 -20
- package/dist/runtime/components/table/ai/data-source-button.vue +0 -324
- package/dist/runtime/components/table/ai/data-source-button.vue.d.ts +0 -20
- package/dist/runtime/components/table/ai/data-source-task.md +0 -17
- package/dist/runtime/utils/ai/cel-prompt.d.ts +0 -11
- package/dist/runtime/utils/ai/cel-prompt.js +0 -27
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { Icon } from "@iconify/vue";
|
|
4
|
+
import { cn } from "../../../utils/cn";
|
|
5
|
+
defineOptions({ name: "UiTreeNode", inheritAttrs: false });
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
node: { type: null, required: true },
|
|
8
|
+
depth: { type: Number, required: true },
|
|
9
|
+
posInSet: { type: Number, required: true },
|
|
10
|
+
setSize: { type: Number, required: true },
|
|
11
|
+
parentKey: { type: [String, null], required: true },
|
|
12
|
+
getKey: { type: Function, required: true },
|
|
13
|
+
getChildren: { type: Function, required: false },
|
|
14
|
+
hasLoader: { type: Boolean, required: true },
|
|
15
|
+
selectionMode: { type: String, required: true },
|
|
16
|
+
cascade: { type: String, required: true },
|
|
17
|
+
selectable: { type: Function, required: true },
|
|
18
|
+
draggable: { type: Function, required: true },
|
|
19
|
+
isExpanded: { type: Function, required: true },
|
|
20
|
+
isSelectedKey: { type: Function, required: true },
|
|
21
|
+
isVisuallySelected: { type: Function, required: true },
|
|
22
|
+
isIndeterminate: { type: Function, required: true },
|
|
23
|
+
resolvedChildren: { type: Function, required: true },
|
|
24
|
+
loadStateOf: { type: Function, required: true },
|
|
25
|
+
filterPredicate: { type: Function, required: false },
|
|
26
|
+
filterQuery: { type: String, required: true },
|
|
27
|
+
isExpandable: { type: Function, required: true },
|
|
28
|
+
tree: { type: Object, required: true },
|
|
29
|
+
focusedKey: { type: [String, null], required: true }
|
|
30
|
+
});
|
|
31
|
+
const emit = defineEmits(["toggle-expand", "toggle-select", "focus-row"]);
|
|
32
|
+
defineSlots();
|
|
33
|
+
const key = computed(() => props.getKey(props.node));
|
|
34
|
+
const expanded = computed(() => props.isExpanded(key.value));
|
|
35
|
+
const expandable = computed(() => props.isExpandable(props.node));
|
|
36
|
+
const loadState = computed(() => props.loadStateOf(key.value));
|
|
37
|
+
const loading = computed(() => loadState.value?.status === "loading");
|
|
38
|
+
const errorState = computed(() => loadState.value?.status === "error" ? loadState.value.error : null);
|
|
39
|
+
const selectedFlag = computed(() => props.isVisuallySelected(props.node, props.depth));
|
|
40
|
+
const indeterminateFlag = computed(() => props.isIndeterminate(props.node, props.depth));
|
|
41
|
+
const userSelectable = computed(() => props.selectable(props.node, props.depth));
|
|
42
|
+
const subtreeMatches = computed(() => {
|
|
43
|
+
if (!props.filterPredicate || !props.filterQuery) return true;
|
|
44
|
+
if (props.filterPredicate(props.node, props.filterQuery)) return true;
|
|
45
|
+
const kids = props.resolvedChildren(props.node);
|
|
46
|
+
if (!kids || kids.length === 0) return false;
|
|
47
|
+
return kids.some((c) => deepMatches(c));
|
|
48
|
+
});
|
|
49
|
+
function deepMatches(n) {
|
|
50
|
+
if (!props.filterPredicate || !props.filterQuery) return true;
|
|
51
|
+
if (props.filterPredicate(n, props.filterQuery)) return true;
|
|
52
|
+
const kids = props.resolvedChildren(n);
|
|
53
|
+
if (!kids || kids.length === 0) return false;
|
|
54
|
+
return kids.some((c) => deepMatches(c));
|
|
55
|
+
}
|
|
56
|
+
const selfMatches = computed(() => {
|
|
57
|
+
if (!props.filterPredicate || !props.filterQuery) return true;
|
|
58
|
+
return props.filterPredicate(props.node, props.filterQuery);
|
|
59
|
+
});
|
|
60
|
+
const slotPayload = computed(() => ({
|
|
61
|
+
node: props.node,
|
|
62
|
+
depth: props.depth,
|
|
63
|
+
expanded: expanded.value,
|
|
64
|
+
loading: loading.value,
|
|
65
|
+
selected: selectedFlag.value,
|
|
66
|
+
indeterminate: indeterminateFlag.value,
|
|
67
|
+
tree: props.tree
|
|
68
|
+
}));
|
|
69
|
+
const actionsPayload = computed(() => ({
|
|
70
|
+
node: props.node,
|
|
71
|
+
depth: props.depth,
|
|
72
|
+
tree: props.tree
|
|
73
|
+
}));
|
|
74
|
+
function onToggleArrow() {
|
|
75
|
+
if (!expandable.value) return;
|
|
76
|
+
emit("toggle-expand", key.value);
|
|
77
|
+
}
|
|
78
|
+
function onToggleSelect() {
|
|
79
|
+
if (props.selectionMode === "none") return;
|
|
80
|
+
if (!userSelectable.value) return;
|
|
81
|
+
emit("toggle-select", key.value);
|
|
82
|
+
}
|
|
83
|
+
const ariaSelected = computed(() => {
|
|
84
|
+
if (props.selectionMode === "none") return void 0;
|
|
85
|
+
if (indeterminateFlag.value) return "mixed";
|
|
86
|
+
return selectedFlag.value ? "true" : "false";
|
|
87
|
+
});
|
|
88
|
+
const childArr = computed(() => props.resolvedChildren(props.node));
|
|
89
|
+
const showChildren = computed(() => expandable.value && expanded.value);
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<template>
|
|
93
|
+
<li
|
|
94
|
+
v-if="subtreeMatches"
|
|
95
|
+
role="treeitem"
|
|
96
|
+
:aria-expanded="expandable ? expanded : void 0"
|
|
97
|
+
:aria-selected="ariaSelected"
|
|
98
|
+
:aria-level="depth + 1"
|
|
99
|
+
:aria-setsize="setSize"
|
|
100
|
+
:aria-posinset="posInSet"
|
|
101
|
+
:data-tree-key="key"
|
|
102
|
+
:data-tree-focused="focusedKey === key ? '' : void 0"
|
|
103
|
+
class="select-none"
|
|
104
|
+
>
|
|
105
|
+
<div
|
|
106
|
+
v-if="selfMatches"
|
|
107
|
+
:class="cn(
|
|
108
|
+
'group flex items-center justify-between gap-2 rounded px-1 py-1 outline-none',
|
|
109
|
+
focusedKey === key && 'bg-(--primary)/10'
|
|
110
|
+
)"
|
|
111
|
+
tabindex="-1"
|
|
112
|
+
@click.stop="emit('focus-row', key)"
|
|
113
|
+
>
|
|
114
|
+
<!-- LEFT: indent + arrow + selector + #node slot -->
|
|
115
|
+
<div class="flex items-center gap-1 min-w-0 flex-1">
|
|
116
|
+
<span
|
|
117
|
+
aria-hidden="true"
|
|
118
|
+
class="inline-block shrink-0"
|
|
119
|
+
:style="{ width: `${depth * 16}px` }"
|
|
120
|
+
/>
|
|
121
|
+
<button
|
|
122
|
+
v-if="expandable"
|
|
123
|
+
type="button"
|
|
124
|
+
class="inline-flex size-4 shrink-0 items-center justify-center rounded text-zinc-500 hover:text-zinc-900"
|
|
125
|
+
:aria-label="expanded ? 'Collapse' : 'Expand'"
|
|
126
|
+
@click.stop="onToggleArrow"
|
|
127
|
+
>
|
|
128
|
+
<Icon
|
|
129
|
+
:icon="expanded ? 'fluent:chevron-down-16-filled' : 'fluent:chevron-right-16-filled'"
|
|
130
|
+
class="size-3"
|
|
131
|
+
/>
|
|
132
|
+
</button>
|
|
133
|
+
<span
|
|
134
|
+
v-else
|
|
135
|
+
aria-hidden="true"
|
|
136
|
+
class="inline-block size-4 shrink-0"
|
|
137
|
+
/>
|
|
138
|
+
|
|
139
|
+
<!-- selector: component-owned, vendored as <input> for testability;
|
|
140
|
+
host visuals come from the #node slot which sits next to it. -->
|
|
141
|
+
<template v-if="selectionMode !== 'none' && userSelectable">
|
|
142
|
+
<input
|
|
143
|
+
v-if="selectionMode === 'multi'"
|
|
144
|
+
type="checkbox"
|
|
145
|
+
class="size-4 shrink-0 accent-(--primary) cursor-pointer"
|
|
146
|
+
:checked="selectedFlag"
|
|
147
|
+
:data-state="indeterminateFlag ? 'indeterminate' : selectedFlag ? 'checked' : 'unchecked'"
|
|
148
|
+
:aria-checked="indeterminateFlag ? 'mixed' : selectedFlag"
|
|
149
|
+
data-tree-toggle="checkbox"
|
|
150
|
+
@click.stop="onToggleSelect"
|
|
151
|
+
>
|
|
152
|
+
<input
|
|
153
|
+
v-else
|
|
154
|
+
type="radio"
|
|
155
|
+
class="size-4 shrink-0 accent-(--primary) cursor-pointer"
|
|
156
|
+
:checked="selectedFlag"
|
|
157
|
+
data-tree-toggle="radio"
|
|
158
|
+
@click.stop="onToggleSelect"
|
|
159
|
+
>
|
|
160
|
+
</template>
|
|
161
|
+
|
|
162
|
+
<div class="min-w-0 flex-1">
|
|
163
|
+
<slot
|
|
164
|
+
name="node"
|
|
165
|
+
v-bind="slotPayload"
|
|
166
|
+
>
|
|
167
|
+
<!-- Sensible fallback: stringified node, mostly for debugging. -->
|
|
168
|
+
<span class="text-sm">{{ String(node) }}</span>
|
|
169
|
+
</slot>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- RIGHT: #actions slot (optional) -->
|
|
174
|
+
<div
|
|
175
|
+
v-if="$slots.actions"
|
|
176
|
+
class="flex items-center gap-1 shrink-0"
|
|
177
|
+
>
|
|
178
|
+
<slot
|
|
179
|
+
name="actions"
|
|
180
|
+
v-bind="actionsPayload"
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- Children container -->
|
|
186
|
+
<ul
|
|
187
|
+
v-if="showChildren"
|
|
188
|
+
role="group"
|
|
189
|
+
class="m-0 list-none p-0"
|
|
190
|
+
>
|
|
191
|
+
<template v-if="loading">
|
|
192
|
+
<li
|
|
193
|
+
role="treeitem"
|
|
194
|
+
aria-busy="true"
|
|
195
|
+
class="px-1 py-1"
|
|
196
|
+
:style="{ paddingLeft: `${(depth + 1) * 16 + 16}px` }"
|
|
197
|
+
>
|
|
198
|
+
<slot
|
|
199
|
+
name="loading-children"
|
|
200
|
+
:node="node"
|
|
201
|
+
>
|
|
202
|
+
<span class="text-xs text-zinc-500 inline-flex items-center gap-1">
|
|
203
|
+
<Icon
|
|
204
|
+
icon="svg-spinners:90-ring-with-bg"
|
|
205
|
+
class="size-3"
|
|
206
|
+
/>
|
|
207
|
+
Loading…
|
|
208
|
+
</span>
|
|
209
|
+
</slot>
|
|
210
|
+
</li>
|
|
211
|
+
</template>
|
|
212
|
+
<template v-else-if="errorState !== null">
|
|
213
|
+
<li
|
|
214
|
+
role="treeitem"
|
|
215
|
+
class="px-1 py-1"
|
|
216
|
+
:style="{ paddingLeft: `${(depth + 1) * 16 + 16}px` }"
|
|
217
|
+
>
|
|
218
|
+
<slot
|
|
219
|
+
name="error"
|
|
220
|
+
:node="node"
|
|
221
|
+
:error="errorState"
|
|
222
|
+
:retry="() => tree.retry(key)"
|
|
223
|
+
/>
|
|
224
|
+
</li>
|
|
225
|
+
</template>
|
|
226
|
+
<template v-else-if="childArr && childArr.length">
|
|
227
|
+
<UiTreeNode
|
|
228
|
+
v-for="(child, idx) in childArr"
|
|
229
|
+
:key="getKey(child)"
|
|
230
|
+
:node="child"
|
|
231
|
+
:depth="depth + 1"
|
|
232
|
+
:pos-in-set="idx + 1"
|
|
233
|
+
:set-size="childArr.length"
|
|
234
|
+
:parent-key="key"
|
|
235
|
+
:get-key="getKey"
|
|
236
|
+
:get-children="getChildren"
|
|
237
|
+
:has-loader="hasLoader"
|
|
238
|
+
:selection-mode="selectionMode"
|
|
239
|
+
:cascade="cascade"
|
|
240
|
+
:selectable="selectable"
|
|
241
|
+
:draggable="draggable"
|
|
242
|
+
:is-expanded="isExpanded"
|
|
243
|
+
:is-selected-key="isSelectedKey"
|
|
244
|
+
:is-visually-selected="isVisuallySelected"
|
|
245
|
+
:is-indeterminate="isIndeterminate"
|
|
246
|
+
:resolved-children="resolvedChildren"
|
|
247
|
+
:load-state-of="loadStateOf"
|
|
248
|
+
:filter-predicate="filterPredicate"
|
|
249
|
+
:filter-query="filterQuery"
|
|
250
|
+
:is-expandable="isExpandable"
|
|
251
|
+
:tree="tree"
|
|
252
|
+
:focused-key="focusedKey"
|
|
253
|
+
@toggle-expand="(k) => emit('toggle-expand', k)"
|
|
254
|
+
@toggle-select="(k) => emit('toggle-select', k)"
|
|
255
|
+
@focus-row="(k) => emit('focus-row', k)"
|
|
256
|
+
>
|
|
257
|
+
<!-- Forward each slot explicitly. Listing the names keeps vue-tsc
|
|
258
|
+
happy and matches the public slot surface in `types.ts`. -->
|
|
259
|
+
<template
|
|
260
|
+
v-if="$slots.node"
|
|
261
|
+
#node="slotProps"
|
|
262
|
+
>
|
|
263
|
+
<slot
|
|
264
|
+
name="node"
|
|
265
|
+
v-bind="slotProps"
|
|
266
|
+
/>
|
|
267
|
+
</template>
|
|
268
|
+
<template
|
|
269
|
+
v-if="$slots.actions"
|
|
270
|
+
#actions="slotProps"
|
|
271
|
+
>
|
|
272
|
+
<slot
|
|
273
|
+
name="actions"
|
|
274
|
+
v-bind="slotProps"
|
|
275
|
+
/>
|
|
276
|
+
</template>
|
|
277
|
+
<template
|
|
278
|
+
v-if="$slots.error"
|
|
279
|
+
#error="slotProps"
|
|
280
|
+
>
|
|
281
|
+
<slot
|
|
282
|
+
name="error"
|
|
283
|
+
v-bind="slotProps"
|
|
284
|
+
/>
|
|
285
|
+
</template>
|
|
286
|
+
<template
|
|
287
|
+
v-if="$slots['loading-children']"
|
|
288
|
+
#loading-children="slotProps"
|
|
289
|
+
>
|
|
290
|
+
<slot
|
|
291
|
+
name="loading-children"
|
|
292
|
+
v-bind="slotProps"
|
|
293
|
+
/>
|
|
294
|
+
</template>
|
|
295
|
+
</UiTreeNode>
|
|
296
|
+
</template>
|
|
297
|
+
</ul>
|
|
298
|
+
</li>
|
|
299
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ActionsSlotProps, CascadeMode, ErrorSlotProps, GetChildren, LoadingChildrenSlotProps, NodeKey, NodeSlotProps, SelectionMode, TreeHandle } from './types.js';
|
|
2
|
+
import type { LoadState } from './useTreeState.js';
|
|
3
|
+
declare const __VLS_export: <Node>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
5
|
+
node: Node;
|
|
6
|
+
depth: number;
|
|
7
|
+
posInSet: number;
|
|
8
|
+
setSize: number;
|
|
9
|
+
parentKey: NodeKey | null;
|
|
10
|
+
getKey: (n: Node) => NodeKey;
|
|
11
|
+
getChildren?: GetChildren<Node>;
|
|
12
|
+
hasLoader: boolean;
|
|
13
|
+
selectionMode: SelectionMode;
|
|
14
|
+
cascade: CascadeMode;
|
|
15
|
+
selectable: (n: Node, depth: number) => boolean;
|
|
16
|
+
draggable: (n: Node, depth: number) => boolean;
|
|
17
|
+
isExpanded: (key: NodeKey) => boolean;
|
|
18
|
+
isSelectedKey: (key: NodeKey) => boolean;
|
|
19
|
+
isVisuallySelected: (n: Node, depth: number) => boolean;
|
|
20
|
+
isIndeterminate: (n: Node, depth: number) => boolean;
|
|
21
|
+
resolvedChildren: (n: Node) => Node[] | undefined | null;
|
|
22
|
+
loadStateOf: (key: NodeKey) => LoadState<Node> | undefined;
|
|
23
|
+
filterPredicate?: (n: Node, query: string) => boolean;
|
|
24
|
+
filterQuery: string;
|
|
25
|
+
isExpandable: (n: Node) => boolean;
|
|
26
|
+
tree: TreeHandle<Node>;
|
|
27
|
+
focusedKey: NodeKey | null;
|
|
28
|
+
} & {
|
|
29
|
+
"onToggle-expand"?: ((key: string) => any) | undefined;
|
|
30
|
+
"onToggle-select"?: ((key: string) => any) | undefined;
|
|
31
|
+
"onFocus-row"?: ((key: string) => any) | undefined;
|
|
32
|
+
}> & (typeof globalThis extends {
|
|
33
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
34
|
+
} ? P : {});
|
|
35
|
+
expose: (exposed: {}) => void;
|
|
36
|
+
attrs: any;
|
|
37
|
+
slots: {
|
|
38
|
+
node?: (p: NodeSlotProps<Node>) => unknown;
|
|
39
|
+
actions?: (p: ActionsSlotProps<Node>) => unknown;
|
|
40
|
+
error?: (p: ErrorSlotProps<Node>) => unknown;
|
|
41
|
+
'loading-children'?: (p: LoadingChildrenSlotProps<Node>) => unknown;
|
|
42
|
+
};
|
|
43
|
+
emit: (e: "toggle-expand" | "toggle-select" | "focus-row", key: NodeKey) => void;
|
|
44
|
+
}>) => import("vue").VNode & {
|
|
45
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
46
|
+
};
|
|
47
|
+
declare const _default: typeof __VLS_export;
|
|
48
|
+
export default _default;
|
|
49
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
50
|
+
[K in keyof T]: T[K];
|
|
51
|
+
} : {
|
|
52
|
+
[K in keyof T as K]: T[K];
|
|
53
|
+
}) & {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default as Tree } from './Tree.vue.js';
|
|
2
|
+
export { default as TreeNode } from './TreeNode.vue.js';
|
|
3
|
+
export type { ActionsSlotProps, CascadeMode, DropContext, ErrorSlotProps, GetChildren, LoadChildren, LoadingChildrenSlotProps, MoveEventPayload, NodeKey, NodeSlotProps, SelectedPaneSlotProps, SelectionMode, TreeHandle, } from './types.js';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Effect } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Public types for the generic Tree UI primitive.
|
|
4
|
+
*
|
|
5
|
+
* Everything in this module is intentionally business-agnostic:
|
|
6
|
+
* - no HTTP, no CEL, no form-field knowledge
|
|
7
|
+
* - no judgement of "what is a leaf" — `getChildren` returning `undefined`
|
|
8
|
+
* is the only way to mark a terminal node
|
|
9
|
+
* - selection / filter are orthogonal: filter never mutates `modelValue`
|
|
10
|
+
*
|
|
11
|
+
* The component is the data plane; visuals come from slots.
|
|
12
|
+
*/
|
|
13
|
+
export type NodeKey = string;
|
|
14
|
+
export type SelectionMode = 'none' | 'single' | 'multi';
|
|
15
|
+
/**
|
|
16
|
+
* Cascade behaviour for `multi` selection mode. Ignored for `single` / `none`.
|
|
17
|
+
*
|
|
18
|
+
* - `independent` — clicking a node toggles only that node
|
|
19
|
+
* - `cascade-down` — toggling a parent toggles all selectable descendants
|
|
20
|
+
* - `cascade-both` — `cascade-down` + parents reflect descendant state via
|
|
21
|
+
* `selected`/`indeterminate` flags exposed to the
|
|
22
|
+
* `#node` slot. The `modelValue` array still only
|
|
23
|
+
* contains literally-clicked descendant keys.
|
|
24
|
+
*/
|
|
25
|
+
export type CascadeMode = 'independent' | 'cascade-down' | 'cascade-both';
|
|
26
|
+
/**
|
|
27
|
+
* Children semantics. See README/design doc — required to be locked down:
|
|
28
|
+
*
|
|
29
|
+
* - `undefined` — node is a terminal leaf, no children, do not call loader
|
|
30
|
+
* - `[]` — no inline children, ask `loadChildren`
|
|
31
|
+
* - `Node[]` — use these synchronously, do not call loader
|
|
32
|
+
*/
|
|
33
|
+
export type GetChildren<Node> = (node: Node) => Node[] | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Async loader for a single node's children. Returns an Effect so that the
|
|
36
|
+
* caller (e.g. the future form-field layer) can compose errors and contexts
|
|
37
|
+
* without the component locking the error channel.
|
|
38
|
+
*/
|
|
39
|
+
export type LoadChildren<Node, E = unknown> = (node: Node) => Effect.Effect<Node[], E>;
|
|
40
|
+
/**
|
|
41
|
+
* Slot prop bundle for `#node` — passed for every rendered row's left side.
|
|
42
|
+
*
|
|
43
|
+
* Selection / indeterminate are pre-computed by the component so that the
|
|
44
|
+
* consumer can bind straight to a `<Checkbox>` / `<Radio>` (the component
|
|
45
|
+
* already renders the toggle itself; these are exposed for visual feedback
|
|
46
|
+
* inside the slot body, e.g. greying the label).
|
|
47
|
+
*/
|
|
48
|
+
export interface NodeSlotProps<Node> {
|
|
49
|
+
node: Node;
|
|
50
|
+
depth: number;
|
|
51
|
+
expanded: boolean;
|
|
52
|
+
loading: boolean;
|
|
53
|
+
selected: boolean;
|
|
54
|
+
indeterminate: boolean;
|
|
55
|
+
tree: TreeHandle<Node>;
|
|
56
|
+
}
|
|
57
|
+
export interface ActionsSlotProps<Node> {
|
|
58
|
+
node: Node;
|
|
59
|
+
depth: number;
|
|
60
|
+
tree: TreeHandle<Node>;
|
|
61
|
+
}
|
|
62
|
+
export interface ErrorSlotProps<Node> {
|
|
63
|
+
node: Node;
|
|
64
|
+
error: unknown;
|
|
65
|
+
retry: () => void;
|
|
66
|
+
}
|
|
67
|
+
export interface LoadingChildrenSlotProps<Node> {
|
|
68
|
+
node: Node;
|
|
69
|
+
}
|
|
70
|
+
export interface SelectedPaneSlotProps<Node> {
|
|
71
|
+
selected: Node[];
|
|
72
|
+
tree: TreeHandle<Node>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Drop context handed to `canDrop`. `beforeKey === null` means "append to end
|
|
76
|
+
* of `intoParent`'s children". `intoParent === null` is a root-level drop.
|
|
77
|
+
*/
|
|
78
|
+
export interface DropContext<Node> {
|
|
79
|
+
drag: {
|
|
80
|
+
node: Node;
|
|
81
|
+
fromParent: Node | null;
|
|
82
|
+
fromIndex: number;
|
|
83
|
+
};
|
|
84
|
+
drop: {
|
|
85
|
+
intoParent: Node | null;
|
|
86
|
+
beforeKey: NodeKey | null;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export interface MoveEventPayload {
|
|
90
|
+
key: NodeKey;
|
|
91
|
+
fromParentKey: NodeKey | null;
|
|
92
|
+
fromIndex: number;
|
|
93
|
+
toParentKey: NodeKey | null;
|
|
94
|
+
toIndex: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Exposed handle. Surface area mirrors the design doc one-to-one.
|
|
98
|
+
*
|
|
99
|
+
* In controlled mode (`v-model:roots`) mutation methods emit
|
|
100
|
+
* `update:roots` with a freshly-spliced array; in uncontrolled mode they
|
|
101
|
+
* mutate the internal copy directly.
|
|
102
|
+
*/
|
|
103
|
+
export interface TreeHandle<Node> {
|
|
104
|
+
insert: (parentKey: NodeKey | null, node: Node, index?: number) => void;
|
|
105
|
+
remove: (key: NodeKey) => void;
|
|
106
|
+
move: (key: NodeKey, newParentKey: NodeKey | null, index: number) => void;
|
|
107
|
+
update: (key: NodeKey, patch: Partial<Node>) => void;
|
|
108
|
+
expand: (key: NodeKey) => void;
|
|
109
|
+
collapse: (key: NodeKey) => void;
|
|
110
|
+
select: (key: NodeKey) => void;
|
|
111
|
+
deselect: (key: NodeKey) => void;
|
|
112
|
+
invalidate: (key: NodeKey) => void;
|
|
113
|
+
retry: (key: NodeKey) => void;
|
|
114
|
+
findPath: (key: NodeKey) => Node[] | null;
|
|
115
|
+
snapshot: () => {
|
|
116
|
+
roots: Node[];
|
|
117
|
+
selected: NodeKey[];
|
|
118
|
+
expanded: NodeKey[];
|
|
119
|
+
};
|
|
120
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref } from 'vue';
|
|
2
|
+
import type { CascadeMode, GetChildren, LoadChildren, NodeKey, SelectionMode } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Internal load state for a single node's children.
|
|
5
|
+
*
|
|
6
|
+
* - `loading` — Effect in flight
|
|
7
|
+
* - `loaded(children)` — successful resolution, cached children array
|
|
8
|
+
* - `error(err)` — Effect failed; component does NOT auto-retry
|
|
9
|
+
*/
|
|
10
|
+
export type LoadState<Node> = {
|
|
11
|
+
status: 'loading';
|
|
12
|
+
} | {
|
|
13
|
+
status: 'loaded';
|
|
14
|
+
children: Node[];
|
|
15
|
+
} | {
|
|
16
|
+
status: 'error';
|
|
17
|
+
error: unknown;
|
|
18
|
+
};
|
|
19
|
+
export interface UseTreeStateOptions<Node, E = unknown> {
|
|
20
|
+
rootsRef: Ref<Node[]> | ComputedRef<Node[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Apply a structural mutation; for controlled mode the parent gets
|
|
23
|
+
* `update:roots`, for uncontrolled we mutate the internal copy.
|
|
24
|
+
*/
|
|
25
|
+
setRoots: (next: Node[]) => void;
|
|
26
|
+
getKey: (node: Node) => NodeKey;
|
|
27
|
+
getChildren?: GetChildren<Node>;
|
|
28
|
+
loadChildren?: LoadChildren<Node, E>;
|
|
29
|
+
selectionMode: () => SelectionMode;
|
|
30
|
+
cascade: () => CascadeMode;
|
|
31
|
+
selectable: (node: Node, depth: number) => boolean;
|
|
32
|
+
/** Initial selection — read once on init, then state is internal. */
|
|
33
|
+
initialSelection: () => NodeKey[];
|
|
34
|
+
/** Initial expanded set — read once on init. */
|
|
35
|
+
initialExpanded: () => NodeKey[];
|
|
36
|
+
/** Called whenever the selection changes (any cause). */
|
|
37
|
+
onSelectionChange: (next: NodeKey[]) => void;
|
|
38
|
+
}
|
|
39
|
+
interface LocatedNode<Node> {
|
|
40
|
+
node: Node;
|
|
41
|
+
parent: Node | null;
|
|
42
|
+
depth: number;
|
|
43
|
+
index: number;
|
|
44
|
+
/** Sibling array that contains `node` — used for in-place splice ops. */
|
|
45
|
+
siblings: Node[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* State machine for `<Tree>`. Pure-ish — only side-effect is running the
|
|
49
|
+
* user-supplied `loadChildren` Effect.
|
|
50
|
+
*
|
|
51
|
+
* Indexing invariants:
|
|
52
|
+
* - All maps/sets key by `NodeKey` (never node identity), so reorders /
|
|
53
|
+
* drag-drop preserve state.
|
|
54
|
+
* - Mutation methods splice in place on the existing children arrays,
|
|
55
|
+
* never recreating nodes.
|
|
56
|
+
*/
|
|
57
|
+
export declare function useTreeState<Node, E = unknown>(opts: UseTreeStateOptions<Node, E>): {
|
|
58
|
+
expanded: import("vue").Reactive<Set<string>>;
|
|
59
|
+
selected: import("vue").Reactive<Set<string>>;
|
|
60
|
+
loadState: Map<string, LoadState<Node>>;
|
|
61
|
+
focusedKey: Ref<string | null, string | null>;
|
|
62
|
+
rootsView: import("vue").ShallowRef<Node[], Node[]>;
|
|
63
|
+
locate: (key: NodeKey) => LocatedNode<Node> | null;
|
|
64
|
+
resolvedChildren: (node: Node) => Node[] | undefined | null;
|
|
65
|
+
isExpandable: (node: Node) => boolean;
|
|
66
|
+
isIndeterminate: (node: Node, depth: number) => boolean;
|
|
67
|
+
isVisuallySelected: (node: Node, depth: number) => boolean;
|
|
68
|
+
flattenVisible: (filter?: (node: Node) => boolean) => Array<{
|
|
69
|
+
node: Node;
|
|
70
|
+
depth: number;
|
|
71
|
+
key: NodeKey;
|
|
72
|
+
}>;
|
|
73
|
+
selectedNodes: ComputedRef<Node[]>;
|
|
74
|
+
syncRoots: () => void;
|
|
75
|
+
expand: (key: NodeKey) => void;
|
|
76
|
+
collapse: (key: NodeKey) => void;
|
|
77
|
+
toggleExpand: (key: NodeKey) => void;
|
|
78
|
+
selectKey: (key: NodeKey) => void;
|
|
79
|
+
deselectKey: (key: NodeKey) => void;
|
|
80
|
+
clearSelection: () => void;
|
|
81
|
+
toggleMulti: (key: NodeKey) => void;
|
|
82
|
+
insert: (parentKey: NodeKey | null, node: Node, index?: number) => void;
|
|
83
|
+
remove: (key: NodeKey) => void;
|
|
84
|
+
move: (key: NodeKey, newParentKey: NodeKey | null, index: number) => void;
|
|
85
|
+
update: (key: NodeKey, patch: Partial<Node>) => void;
|
|
86
|
+
invalidate: (key: NodeKey) => void;
|
|
87
|
+
retry: (key: NodeKey) => void;
|
|
88
|
+
findPath: (key: NodeKey) => Node[] | null;
|
|
89
|
+
snapshot: () => {
|
|
90
|
+
roots: Node[];
|
|
91
|
+
selected: string[];
|
|
92
|
+
expanded: string[];
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
export {};
|