@shwfed/config 2.12.1 → 2.12.3
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/module.json +1 -1
- package/dist/module.mjs +10 -0
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +68 -68
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +68 -68
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +68 -68
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +68 -68
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +34 -34
- package/dist/runtime/components/form/config.vue +17 -6
- package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +6 -6
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +6 -6
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +8 -8
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +8 -8
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +8 -1
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +6 -6
- package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +6 -6
- package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/config.d.vue.ts +32 -32
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/config.vue.d.ts +32 -32
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.multi/runtime.vue +10 -2
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/config.d.vue.ts +32 -32
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/config.vue.d.ts +32 -32
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.combobox.single/runtime.vue +10 -2
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/runtime.vue +19 -1
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/runtime.vue +19 -1
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +24 -24
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/runtime.vue +20 -1
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +22 -22
- package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/runtime.vue +20 -1
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/config.d.vue.ts +32 -32
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/config.vue.d.ts +32 -32
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/runtime.vue +10 -2
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/config.d.vue.ts +32 -32
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/config.vue.d.ts +32 -32
- package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/runtime.vue +10 -2
- package/dist/runtime/components/form/index.vue +6 -3
- package/dist/runtime/components/form/utils/form-vars.js +2 -1
- package/dist/runtime/components/head.d.vue.ts +13 -0
- package/dist/runtime/components/head.vue +69 -0
- package/dist/runtime/components/head.vue.d.ts +13 -0
- package/dist/runtime/components/sidebar/config.d.vue.ts +7 -0
- package/dist/runtime/components/sidebar/config.vue +302 -0
- package/dist/runtime/components/sidebar/config.vue.d.ts +7 -0
- package/dist/runtime/components/sidebar/favorites.d.ts +18 -0
- package/dist/runtime/components/sidebar/favorites.js +20 -0
- package/dist/runtime/components/sidebar/footer.d.vue.ts +11 -0
- package/dist/runtime/components/sidebar/footer.vue +49 -0
- package/dist/runtime/components/sidebar/footer.vue.d.ts +11 -0
- package/dist/runtime/components/sidebar/index.d.vue.ts +50 -0
- package/dist/runtime/components/sidebar/index.vue +100 -0
- package/dist/runtime/components/sidebar/index.vue.d.ts +50 -0
- package/dist/runtime/components/sidebar/node.d.vue.ts +9 -0
- package/dist/runtime/components/sidebar/node.vue +102 -0
- package/dist/runtime/components/sidebar/node.vue.d.ts +9 -0
- package/dist/runtime/components/sidebar/schema.d.ts +72 -0
- package/dist/runtime/components/sidebar/schema.js +84 -0
- package/dist/runtime/components/sidebar/tree.d.vue.ts +10 -0
- package/dist/runtime/components/sidebar/tree.vue +260 -0
- package/dist/runtime/components/sidebar/tree.vue.d.ts +10 -0
- package/dist/runtime/components/sidebar/types.d.ts +19 -0
- package/dist/runtime/components/sidebar/types.js +0 -0
- package/dist/runtime/components/sidebar/use-editor.d.ts +18 -0
- package/dist/runtime/components/sidebar/use-editor.js +43 -0
- package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.d.vue.ts +36 -36
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.vue.d.ts +36 -36
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +40 -40
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +40 -40
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.d.vue.ts +36 -36
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.vue.d.ts +36 -36
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +40 -40
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +40 -40
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +26 -26
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +26 -26
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +26 -26
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +26 -26
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +26 -26
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +26 -26
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +4 -4
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +4 -4
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
- package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
- package/dist/runtime/components/table/schema.d.ts +68 -68
- package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
- package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
- package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.d.vue.ts +1 -0
- package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue +8 -0
- package/dist/runtime/components/ui/expression-editor/CodeMirrorInput.vue.d.ts +1 -0
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +3 -2
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +51 -9
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +3 -2
- package/dist/runtime/components/ui/expression-editor/option-chip-extension.d.ts +4 -0
- package/dist/runtime/components/ui/expression-editor/option-chip-extension.js +119 -0
- package/dist/runtime/components/ui/expression-editor/option-refs.d.ts +6 -0
- package/dist/runtime/components/ui/expression-editor/option-refs.js +24 -0
- package/dist/runtime/components/ui/expression-editor/picker-entries.d.ts +2 -1
- package/dist/runtime/components/ui/expression-editor/picker-entries.js +12 -2
- package/dist/runtime/utils/cel-context.d.ts +1 -0
- package/dist/runtime/utils/cel-context.js +1 -0
- package/dist/runtime/utils/selections-registry.d.ts +40 -6
- package/dist/runtime/utils/selections-registry.js +19 -4
- package/dist/runtime/vendor/cel-js/CLAUDE.md +2 -0
- package/dist/runtime/vendor/cel-js/PROMPT.md +2 -0
- package/dist/runtime/vendor/cel-js/lib/functions.js +33 -0
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Icon } from "@iconify/vue";
|
|
3
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible";
|
|
4
|
+
import {
|
|
5
|
+
SidebarMenuAction,
|
|
6
|
+
SidebarMenuButton,
|
|
7
|
+
SidebarMenuItem,
|
|
8
|
+
SidebarMenuSub
|
|
9
|
+
} from "../ui/sidebar";
|
|
10
|
+
defineOptions({ name: "ShwfedSidebarNode" });
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
node: { type: Object, required: true },
|
|
13
|
+
favorites: { type: null, required: true }
|
|
14
|
+
});
|
|
15
|
+
function onOpenUpdate(open) {
|
|
16
|
+
props.favorites.setOpen(props.node.key, open);
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<SidebarMenuItem>
|
|
22
|
+
<!-- Group: collapsible, recurses into a sub-menu -->
|
|
23
|
+
<Collapsible
|
|
24
|
+
v-if="node.children.length"
|
|
25
|
+
class="group/collapsible"
|
|
26
|
+
:open="favorites.isOpen(node.key, true)"
|
|
27
|
+
@update:open="onOpenUpdate"
|
|
28
|
+
>
|
|
29
|
+
<CollapsibleTrigger as-child>
|
|
30
|
+
<SidebarMenuButton>
|
|
31
|
+
<Icon
|
|
32
|
+
v-if="node.icon"
|
|
33
|
+
:icon="node.icon"
|
|
34
|
+
/>
|
|
35
|
+
<span>{{ node.label }}</span>
|
|
36
|
+
<Icon
|
|
37
|
+
icon="fluent:chevron-right-20-regular"
|
|
38
|
+
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
|
|
39
|
+
/>
|
|
40
|
+
</SidebarMenuButton>
|
|
41
|
+
</CollapsibleTrigger>
|
|
42
|
+
<CollapsibleContent>
|
|
43
|
+
<SidebarMenuSub>
|
|
44
|
+
<ShwfedSidebarNode
|
|
45
|
+
v-for="child in node.children"
|
|
46
|
+
:key="child.key"
|
|
47
|
+
:node="child"
|
|
48
|
+
:favorites="favorites"
|
|
49
|
+
/>
|
|
50
|
+
</SidebarMenuSub>
|
|
51
|
+
</CollapsibleContent>
|
|
52
|
+
</Collapsible>
|
|
53
|
+
|
|
54
|
+
<!-- Link: navigable leaf with a route, star-favoritable -->
|
|
55
|
+
<template v-else-if="node.route">
|
|
56
|
+
<NuxtLink
|
|
57
|
+
:to="node.route"
|
|
58
|
+
custom
|
|
59
|
+
>
|
|
60
|
+
<template #default="{ navigate, isExactActive }">
|
|
61
|
+
<SidebarMenuButton
|
|
62
|
+
as-child
|
|
63
|
+
:is-active="isExactActive"
|
|
64
|
+
>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
@click="navigate"
|
|
68
|
+
>
|
|
69
|
+
<Icon
|
|
70
|
+
v-if="node.icon"
|
|
71
|
+
:icon="node.icon"
|
|
72
|
+
/>
|
|
73
|
+
<span>{{ node.label }}</span>
|
|
74
|
+
</button>
|
|
75
|
+
</SidebarMenuButton>
|
|
76
|
+
</template>
|
|
77
|
+
</NuxtLink>
|
|
78
|
+
<SidebarMenuAction
|
|
79
|
+
v-if="node.favoritable"
|
|
80
|
+
:show-on-hover="!favorites.isFavorite(node.key)"
|
|
81
|
+
class="text-yellow-500!"
|
|
82
|
+
@click.stop="favorites.toggleFavorite(node.key)"
|
|
83
|
+
>
|
|
84
|
+
<Icon :icon="favorites.isFavorite(node.key) ? 'fluent:star-20-filled' : 'fluent:star-20-regular'" />
|
|
85
|
+
</SidebarMenuAction>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<!-- Plain label: childless, no route -->
|
|
89
|
+
<SidebarMenuButton
|
|
90
|
+
v-else
|
|
91
|
+
as-child
|
|
92
|
+
>
|
|
93
|
+
<span>
|
|
94
|
+
<Icon
|
|
95
|
+
v-if="node.icon"
|
|
96
|
+
:icon="node.icon"
|
|
97
|
+
/>
|
|
98
|
+
<span>{{ node.label }}</span>
|
|
99
|
+
</span>
|
|
100
|
+
</SidebarMenuButton>
|
|
101
|
+
</SidebarMenuItem>
|
|
102
|
+
</template>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NavNode } from './types.js';
|
|
2
|
+
import type { SidebarFavorites } from './favorites.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
node: NavNode;
|
|
5
|
+
favorites: SidebarFavorites;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
3
|
+
import type { LocaleValue } from '../../share/locale.js';
|
|
4
|
+
export { getStructFieldDescription, getStructFieldTitle } from '../table/utils/schema-meta.js';
|
|
5
|
+
declare const KIND = "shwfed.component.sidebar";
|
|
6
|
+
/**
|
|
7
|
+
* Standalone, data-driven navigation sidebar. A PEER of the page config
|
|
8
|
+
* (`ShwfedConfig`), NOT a block inside it: its own serializable `kind`, its own
|
|
9
|
+
* editor modal, its own CEL context. It never touches `blockRef` / `PageConfig`
|
|
10
|
+
* / the block registry.
|
|
11
|
+
*
|
|
12
|
+
* The navigation TREE is data-driven, modeled on the `tree.single` block: one
|
|
13
|
+
* `DataSource` returns the whole node payload, and per-node CEL expressions
|
|
14
|
+
* (sharing a `node` var) walk and render it. Arbitrary-depth nesting and
|
|
15
|
+
* CEL-computed link targets fall out of `nodeChildren` / `nodeRoute` — there is
|
|
16
|
+
* no hand-authored static tree. A node with children renders collapsible; a
|
|
17
|
+
* node whose `nodeRoute` resolves non-empty renders as a navigable link (and is
|
|
18
|
+
* favoritable — the star is a runtime affordance, not a schema field).
|
|
19
|
+
*/
|
|
20
|
+
export declare function SidebarConfig(configure: (env: Environment) => void): Schema.Struct<{
|
|
21
|
+
kind: Schema.tag<"shwfed.component.sidebar">;
|
|
22
|
+
dataSource: Schema.Struct<{
|
|
23
|
+
request: Schema.optional<Schema.Schema<string, string, never>>;
|
|
24
|
+
data: Schema.Schema<string, string, never>;
|
|
25
|
+
}>;
|
|
26
|
+
nodeKey: Schema.Schema<string, string, never>;
|
|
27
|
+
nodeChildren: Schema.Schema<string, string, never>;
|
|
28
|
+
nodeLabel: Schema.refine<readonly [{
|
|
29
|
+
readonly locale: "zh";
|
|
30
|
+
readonly message: string;
|
|
31
|
+
}, ...{
|
|
32
|
+
readonly locale: "en" | "ja" | "ko";
|
|
33
|
+
readonly message: string;
|
|
34
|
+
}[]], Schema.suspend<readonly [{
|
|
35
|
+
readonly locale: "zh";
|
|
36
|
+
readonly message: string;
|
|
37
|
+
}, ...{
|
|
38
|
+
readonly locale: "en" | "ja" | "ko";
|
|
39
|
+
readonly message: string;
|
|
40
|
+
}[]], string | readonly [{
|
|
41
|
+
readonly locale: "zh";
|
|
42
|
+
readonly message: string;
|
|
43
|
+
}, ...{
|
|
44
|
+
readonly locale: "en" | "ja" | "ko";
|
|
45
|
+
readonly message: string;
|
|
46
|
+
}[]], never>>;
|
|
47
|
+
nodeIcon: Schema.optional<Schema.Schema<string, string, never>>;
|
|
48
|
+
nodeRoute: Schema.optional<Schema.Schema<string, string, never>>;
|
|
49
|
+
nodeVisible: Schema.optional<Schema.Schema<string, string, never>>;
|
|
50
|
+
style: Schema.optional<Schema.Schema<string, string, never>>;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Creation-time defaults for a fresh sidebar. Seeds working expressions (like
|
|
54
|
+
* the tree block's `defaultBody`) so a freshly-added sidebar renders a sane
|
|
55
|
+
* skeleton the designer can immediately edit against real data shapes.
|
|
56
|
+
*/
|
|
57
|
+
export declare function defaults(): Omit<SidebarConfigValue, 'kind'>;
|
|
58
|
+
export declare function createSidebarConfig(body?: Partial<Omit<SidebarConfigValue, 'kind'>>): SidebarConfigValue;
|
|
59
|
+
export type SidebarConfigValue = Readonly<{
|
|
60
|
+
kind: typeof KIND;
|
|
61
|
+
dataSource: Readonly<{
|
|
62
|
+
request?: string;
|
|
63
|
+
data: string;
|
|
64
|
+
}>;
|
|
65
|
+
nodeKey: string;
|
|
66
|
+
nodeChildren: string;
|
|
67
|
+
nodeLabel: LocaleValue;
|
|
68
|
+
nodeIcon?: string;
|
|
69
|
+
nodeRoute?: string;
|
|
70
|
+
nodeVisible?: string;
|
|
71
|
+
style?: string;
|
|
72
|
+
}>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Expression, LocaleMarkdown } from "../../share/expression.js";
|
|
3
|
+
import { registerNodeVariableIfAbsent } from "../../share/tree-node-var.js";
|
|
4
|
+
import { DataSource } from "../table/schema.js";
|
|
5
|
+
export { getStructFieldDescription, getStructFieldTitle } from "../table/utils/schema-meta.js";
|
|
6
|
+
const KIND = "shwfed.component.sidebar";
|
|
7
|
+
export function SidebarConfig(configure) {
|
|
8
|
+
const nodeConfigure = (env) => {
|
|
9
|
+
configure(env);
|
|
10
|
+
registerNodeVariableIfAbsent(env);
|
|
11
|
+
};
|
|
12
|
+
const CelNodeKey = Expression({
|
|
13
|
+
configure: nodeConfigure,
|
|
14
|
+
resultType: (t) => t === "string" || t === "number" || t === "dyn"
|
|
15
|
+
});
|
|
16
|
+
const CelNodeChildren = Expression({
|
|
17
|
+
configure: nodeConfigure,
|
|
18
|
+
// Accept lists, optional lists, and `dyn` — CEL infers an empty literal
|
|
19
|
+
// such as `[]` as `list<dyn>`; a configured expression like
|
|
20
|
+
// `has(node.kids) ? node.kids : null` lands on `dyn`. Mirrors the table's
|
|
21
|
+
// `data` expression.
|
|
22
|
+
resultType: (t) => t === "dyn" || t.startsWith("list") || t.startsWith("optional")
|
|
23
|
+
});
|
|
24
|
+
const NodeLocaleMd = LocaleMarkdown({ configure: nodeConfigure });
|
|
25
|
+
const CelNodeString = Expression({
|
|
26
|
+
configure: nodeConfigure,
|
|
27
|
+
resultType: (t) => t === "string" || t === "dyn" || t.startsWith("optional")
|
|
28
|
+
});
|
|
29
|
+
const CelNodeBool = Expression({ configure: nodeConfigure, resultType: "bool" });
|
|
30
|
+
const CelStyle = Expression({
|
|
31
|
+
configure,
|
|
32
|
+
resultType: (t) => t === "string" || t === "dyn" || t.startsWith("map") || t.startsWith("optional")
|
|
33
|
+
});
|
|
34
|
+
return Schema.Struct({
|
|
35
|
+
kind: Schema.tag(KIND),
|
|
36
|
+
dataSource: DataSource(configure).annotations({
|
|
37
|
+
title: "\u6570\u636E\u6E90",
|
|
38
|
+
description: "\u8FD4\u56DE\u5B8C\u6574\u5BFC\u822A\u6811\uFF08\u6839\u8282\u70B9\u5217\u8868\uFF09\u7684\u6570\u636E\u6E90\uFF1B\u4FA7\u680F\u901A\u8FC7 `\u8282\u70B9\u5B50\u7EA7` \u8868\u8FBE\u5F0F\u9010\u5C42\u5411\u4E0B\u8BFB\u53D6\u5B50\u7EA7"
|
|
39
|
+
}),
|
|
40
|
+
nodeKey: CelNodeKey.annotations({
|
|
41
|
+
title: "\u8282\u70B9 ID",
|
|
42
|
+
description: "\u4E3A\u6BCF\u4E2A\u8282\u70B9\u8BA1\u7B97\u7A33\u5B9A\u552F\u4E00 ID \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u8FD0\u884C\u65F6\u4EE5\u5B57\u7B26\u4E32\u5F62\u5F0F\u5B58\u50A8\uFF0C\u4E5F\u7528\u4F5C\u6536\u85CF\u6807\u8BC6"
|
|
43
|
+
}),
|
|
44
|
+
nodeChildren: CelNodeChildren.annotations({
|
|
45
|
+
title: "\u8282\u70B9\u5B50\u7EA7",
|
|
46
|
+
description: "\u8FD4\u56DE\u5F53\u524D\u8282\u70B9\u7684\u5B50\u7EA7\u5217\u8868\u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u8FD4\u56DE `none` / `null` \u89C6\u4E3A\u53F6\u5B50\u8282\u70B9"
|
|
47
|
+
}),
|
|
48
|
+
nodeLabel: NodeLocaleMd.annotations({
|
|
49
|
+
title: "\u8282\u70B9\u6807\u7B7E",
|
|
50
|
+
description: "\u8282\u70B9\u5C55\u793A\u7684\u672C\u5730\u5316\u6587\u672C\uFF0C\u652F\u6301 Markdown \u4E0E `{{ node.foo }}` \u63D2\u503C"
|
|
51
|
+
}),
|
|
52
|
+
nodeIcon: Schema.optional(CelNodeString.annotations({
|
|
53
|
+
title: "\u8282\u70B9\u56FE\u6807",
|
|
54
|
+
description: "\u8FD4\u56DE Iconify \u56FE\u6807 ID\uFF08\u5982 `'fluent:home-20-regular'`\uFF09\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u7559\u7A7A\u5219\u4E0D\u663E\u793A\u56FE\u6807"
|
|
55
|
+
})),
|
|
56
|
+
nodeRoute: Schema.optional(CelNodeString.annotations({
|
|
57
|
+
title: "\u8282\u70B9\u94FE\u63A5",
|
|
58
|
+
description: "\u8FD4\u56DE\u76EE\u6807\u8DEF\u7531\uFF08`string`\uFF09\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\uFF1B\u89E3\u6790\u4E3A\u975E\u7A7A\u65F6\u8BE5\u8282\u70B9\u6210\u4E3A\u53EF\u70B9\u51FB\u7684\u5BFC\u822A\u94FE\u63A5\uFF08\u5E76\u53EF\u88AB\u6536\u85CF\uFF09\uFF0C\u5426\u5219\u4EC5\u4F5C\u4E3A\u5206\u7EC4\u6807\u9898"
|
|
59
|
+
})),
|
|
60
|
+
nodeVisible: Schema.optional(CelNodeBool.annotations({
|
|
61
|
+
title: "\u8282\u70B9\u53EF\u89C1\u6761\u4EF6",
|
|
62
|
+
description: "\u8FD4\u56DE `true` \u8868\u793A\u8BE5\u8282\u70B9\uFF08\u53CA\u5176\u5B50\u6811\uFF09\u53EF\u89C1\uFF1B\u7559\u7A7A\u5219\u59CB\u7EC8\u53EF\u89C1\u3002\u5E38\u7528\u4E8E\u6309\u6743\u9650\u63A7\u5236\u5BFC\u822A\u9879"
|
|
63
|
+
})),
|
|
64
|
+
style: Schema.optional(CelStyle).annotations({
|
|
65
|
+
title: "\u6837\u5F0F",
|
|
66
|
+
description: "\u8FD4\u56DE\u5E94\u7528\u5728\u4FA7\u680F\u6839\u5143\u7D20\u4E0A\u7684\u6837\u5F0F\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8FD4\u56DE CSS \u5B57\u7B26\u4E32\u6216\u6837\u5F0F\u5BF9\u8C61\uFF1B\u7559\u7A7A\u5219\u4E0D\u8BBE\u989D\u5916\u6837\u5F0F"
|
|
67
|
+
})
|
|
68
|
+
}).annotations({
|
|
69
|
+
title: "SidebarConfig",
|
|
70
|
+
description: "\u6570\u636E\u9A71\u52A8\u7684\u5BFC\u822A\u4FA7\u680F\u5B8C\u6574\u914D\u7F6E"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function defaults() {
|
|
74
|
+
return {
|
|
75
|
+
dataSource: { data: "[]" },
|
|
76
|
+
nodeKey: "string(node.id)",
|
|
77
|
+
nodeChildren: "has(node.children) ? node.children : null",
|
|
78
|
+
nodeLabel: [{ locale: "zh", message: "{{ node.label }}" }],
|
|
79
|
+
nodeRoute: "node.route"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function createSidebarConfig(body) {
|
|
83
|
+
return { kind: KIND, ...defaults(), ...body };
|
|
84
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
2
|
+
import type { SidebarConfigValue } from './schema.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
config: SidebarConfigValue;
|
|
5
|
+
configure?: (env: Environment) => void;
|
|
6
|
+
storageKey: string;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Icon } from "@iconify/vue";
|
|
3
|
+
import { Effect, Fiber, Option } from "effect";
|
|
4
|
+
import { Fetch } from "fx-fetch";
|
|
5
|
+
import { computed, ref, watch } from "vue";
|
|
6
|
+
import { useI18n } from "vue-i18n";
|
|
7
|
+
import { fetchJsonOption } from "../../share/request";
|
|
8
|
+
import { cel as _rawCel } from "../../utils/cel";
|
|
9
|
+
import { celBindings, injectCELContext } from "../../utils/cel-context";
|
|
10
|
+
import { getLocalizedText } from "../../share/locale";
|
|
11
|
+
import { interpolateMarkdown } from "../table/utils/runtime";
|
|
12
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible";
|
|
13
|
+
import { SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu } from "../ui/sidebar";
|
|
14
|
+
import { Skeleton } from "../ui/skeleton";
|
|
15
|
+
import SidebarNode from "./node.vue";
|
|
16
|
+
import { useSidebarFavorites } from "./favorites";
|
|
17
|
+
defineOptions({ name: "ShwfedSidebarTree" });
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
config: { type: Object, required: true },
|
|
20
|
+
configure: { type: Function, required: false },
|
|
21
|
+
storageKey: { type: String, required: true }
|
|
22
|
+
});
|
|
23
|
+
const { locale, t } = useI18n({
|
|
24
|
+
inheritLocale: true,
|
|
25
|
+
useScope: "global",
|
|
26
|
+
messages: {
|
|
27
|
+
zh: { "sidebar-favorites": "\u6536\u85CF" },
|
|
28
|
+
en: { "sidebar-favorites": "Favorites" },
|
|
29
|
+
ja: { "sidebar-favorites": "\u304A\u6C17\u306B\u5165\u308A" },
|
|
30
|
+
ko: { "sidebar-favorites": "\uC990\uACA8\uCC3E\uAE30" }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const favorites = useSidebarFavorites(props.storageKey);
|
|
34
|
+
const inherited = injectCELContext();
|
|
35
|
+
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
|
|
36
|
+
const styleValue = computed(() => {
|
|
37
|
+
const expr = props.config.style;
|
|
38
|
+
if (!expr) return void 0;
|
|
39
|
+
try {
|
|
40
|
+
const r = Effect.runSync($cel(expr, {}));
|
|
41
|
+
if (r == null) return void 0;
|
|
42
|
+
if (typeof r === "string") return r;
|
|
43
|
+
if (typeof r === "object") return r;
|
|
44
|
+
return String(r);
|
|
45
|
+
} catch {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const roots = ref([]);
|
|
50
|
+
const loading = ref(false);
|
|
51
|
+
async function fetchTree() {
|
|
52
|
+
const dataSource = props.config.dataSource;
|
|
53
|
+
if (!dataSource) {
|
|
54
|
+
roots.value = [];
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const effect = Effect.gen(function* () {
|
|
58
|
+
let jsonOpt = Option.none();
|
|
59
|
+
if (dataSource.request) {
|
|
60
|
+
jsonOpt = yield* fetchJsonOption(yield* $cel(dataSource.request, {}));
|
|
61
|
+
}
|
|
62
|
+
const dataRaw = yield* $cel(dataSource.data, { json: jsonOpt });
|
|
63
|
+
return Array.isArray(dataRaw) ? dataRaw : [];
|
|
64
|
+
});
|
|
65
|
+
const program = Effect.gen(function* () {
|
|
66
|
+
const showMaskFiber = yield* Effect.fork(
|
|
67
|
+
Effect.sync(() => {
|
|
68
|
+
loading.value = true;
|
|
69
|
+
}).pipe(Effect.delay("30 millis"))
|
|
70
|
+
);
|
|
71
|
+
return yield* Effect.ensuring(effect, Fiber.interrupt(showMaskFiber));
|
|
72
|
+
}).pipe(
|
|
73
|
+
Effect.ensuring(Effect.sync(() => {
|
|
74
|
+
loading.value = false;
|
|
75
|
+
}))
|
|
76
|
+
);
|
|
77
|
+
try {
|
|
78
|
+
roots.value = await Effect.runPromise(Effect.provide(program, Fetch.layer));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.warn("[shwfed-sidebar] dataSource fetch failed", e);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
watch(
|
|
84
|
+
() => [props.config.dataSource?.request, props.config.dataSource?.data],
|
|
85
|
+
() => {
|
|
86
|
+
void fetchTree();
|
|
87
|
+
},
|
|
88
|
+
{ immediate: true }
|
|
89
|
+
);
|
|
90
|
+
function nodeCtx(node) {
|
|
91
|
+
return { node };
|
|
92
|
+
}
|
|
93
|
+
function getKey(node) {
|
|
94
|
+
try {
|
|
95
|
+
return String(Effect.runSync($cel(props.config.nodeKey, nodeCtx(node))));
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error("[shwfed-sidebar] failed to evaluate nodeKey:", err);
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function getChildren(node) {
|
|
102
|
+
let raw;
|
|
103
|
+
try {
|
|
104
|
+
raw = Effect.runSync($cel(props.config.nodeChildren, nodeCtx(node)));
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error("[shwfed-sidebar] failed to evaluate nodeChildren:", err);
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
return Array.isArray(raw) ? raw : [];
|
|
110
|
+
}
|
|
111
|
+
function getOptionalString(expr, node) {
|
|
112
|
+
if (!expr) return void 0;
|
|
113
|
+
try {
|
|
114
|
+
const r = Effect.runSync($cel(expr, nodeCtx(node)));
|
|
115
|
+
if (r == null) return void 0;
|
|
116
|
+
const s = String(r);
|
|
117
|
+
return s.length > 0 ? s : void 0;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error("[shwfed-sidebar] failed to evaluate node string expr:", err);
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function nodeVisible(node) {
|
|
124
|
+
const expr = props.config.nodeVisible;
|
|
125
|
+
if (!expr) return true;
|
|
126
|
+
try {
|
|
127
|
+
return Effect.runSync($cel(expr, nodeCtx(node)));
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error("[shwfed-sidebar] failed to evaluate nodeVisible:", err);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function nodeLabelText(node) {
|
|
134
|
+
const tpl = getLocalizedText(props.config.nodeLabel, locale.value) ?? "";
|
|
135
|
+
if (!tpl) return "";
|
|
136
|
+
try {
|
|
137
|
+
return interpolateMarkdown(tpl, $cel, { node });
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error("[shwfed-sidebar] failed to interpolate node label:", err);
|
|
140
|
+
return tpl;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function buildNode(raw) {
|
|
144
|
+
if (!nodeVisible(raw)) return null;
|
|
145
|
+
const children = getChildren(raw).map(buildNode).filter((n) => n !== null);
|
|
146
|
+
const route = getOptionalString(props.config.nodeRoute, raw);
|
|
147
|
+
return {
|
|
148
|
+
key: getKey(raw),
|
|
149
|
+
label: nodeLabelText(raw),
|
|
150
|
+
icon: getOptionalString(props.config.nodeIcon, raw),
|
|
151
|
+
route,
|
|
152
|
+
favoritable: children.length === 0 && !!route,
|
|
153
|
+
children
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const tree = computed(
|
|
157
|
+
() => roots.value.map(buildNode).filter((n) => n !== null)
|
|
158
|
+
);
|
|
159
|
+
const favoriteNodes = computed(() => {
|
|
160
|
+
const out = [];
|
|
161
|
+
const walk = (nodes) => {
|
|
162
|
+
for (const n of nodes) {
|
|
163
|
+
if (n.favoritable && favorites.isFavorite(n.key)) out.push(n);
|
|
164
|
+
if (n.children.length) walk(n.children);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
walk(tree.value);
|
|
168
|
+
return out;
|
|
169
|
+
});
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<template>
|
|
173
|
+
<SidebarContent
|
|
174
|
+
class="gap-2 py-1"
|
|
175
|
+
:style="styleValue"
|
|
176
|
+
>
|
|
177
|
+
<div
|
|
178
|
+
v-if="loading && roots.length === 0"
|
|
179
|
+
class="flex flex-col gap-1.5 px-2 py-1"
|
|
180
|
+
aria-busy="true"
|
|
181
|
+
>
|
|
182
|
+
<Skeleton class="h-6 w-2/3" />
|
|
183
|
+
<Skeleton class="h-6 w-1/2" />
|
|
184
|
+
<Skeleton class="h-6 w-3/5" />
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<template v-else>
|
|
188
|
+
<SidebarGroup v-if="favoriteNodes.length">
|
|
189
|
+
<SidebarGroupLabel class="text-sm text-zinc-700">
|
|
190
|
+
{{ t("sidebar-favorites") }}
|
|
191
|
+
</SidebarGroupLabel>
|
|
192
|
+
<SidebarGroupContent>
|
|
193
|
+
<SidebarMenu>
|
|
194
|
+
<SidebarNode
|
|
195
|
+
v-for="n in favoriteNodes"
|
|
196
|
+
:key="`fav-${n.key}`"
|
|
197
|
+
:node="n"
|
|
198
|
+
:favorites="favorites"
|
|
199
|
+
/>
|
|
200
|
+
</SidebarMenu>
|
|
201
|
+
</SidebarGroupContent>
|
|
202
|
+
</SidebarGroup>
|
|
203
|
+
|
|
204
|
+
<template
|
|
205
|
+
v-for="n in tree"
|
|
206
|
+
:key="n.key"
|
|
207
|
+
>
|
|
208
|
+
<!-- Top-level group: the group label IS the collapsible trigger -->
|
|
209
|
+
<SidebarGroup v-if="n.children.length">
|
|
210
|
+
<Collapsible
|
|
211
|
+
class="group/collapsible"
|
|
212
|
+
:open="favorites.isOpen(n.key, true)"
|
|
213
|
+
@update:open="(v) => favorites.setOpen(n.key, v)"
|
|
214
|
+
>
|
|
215
|
+
<SidebarGroupLabel
|
|
216
|
+
as-child
|
|
217
|
+
class="group/label w-full cursor-pointer text-sm text-zinc-700 hover:bg-zinc-100 hover:text-zinc-900"
|
|
218
|
+
>
|
|
219
|
+
<CollapsibleTrigger class="w-full">
|
|
220
|
+
<Icon
|
|
221
|
+
v-if="n.icon"
|
|
222
|
+
:icon="n.icon"
|
|
223
|
+
/>
|
|
224
|
+
{{ n.label }}
|
|
225
|
+
<Icon
|
|
226
|
+
icon="fluent:chevron-right-20-regular"
|
|
227
|
+
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
|
|
228
|
+
/>
|
|
229
|
+
</CollapsibleTrigger>
|
|
230
|
+
</SidebarGroupLabel>
|
|
231
|
+
<CollapsibleContent class="overflow-hidden data-[state=open]:overflow-visible">
|
|
232
|
+
<SidebarGroupContent>
|
|
233
|
+
<SidebarMenu class="pl-4">
|
|
234
|
+
<SidebarNode
|
|
235
|
+
v-for="child in n.children"
|
|
236
|
+
:key="child.key"
|
|
237
|
+
:node="child"
|
|
238
|
+
:favorites="favorites"
|
|
239
|
+
/>
|
|
240
|
+
</SidebarMenu>
|
|
241
|
+
</SidebarGroupContent>
|
|
242
|
+
</CollapsibleContent>
|
|
243
|
+
</Collapsible>
|
|
244
|
+
</SidebarGroup>
|
|
245
|
+
|
|
246
|
+
<!-- Top-level direct link -->
|
|
247
|
+
<SidebarGroup v-else>
|
|
248
|
+
<SidebarGroupContent>
|
|
249
|
+
<SidebarMenu>
|
|
250
|
+
<SidebarNode
|
|
251
|
+
:node="n"
|
|
252
|
+
:favorites="favorites"
|
|
253
|
+
/>
|
|
254
|
+
</SidebarMenu>
|
|
255
|
+
</SidebarGroupContent>
|
|
256
|
+
</SidebarGroup>
|
|
257
|
+
</template>
|
|
258
|
+
</template>
|
|
259
|
+
</SidebarContent>
|
|
260
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
2
|
+
import type { SidebarConfigValue } from './schema.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
config: SidebarConfigValue;
|
|
5
|
+
configure?: (env: Environment) => void;
|
|
6
|
+
storageKey: string;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A normalized navigation node — the product of walking the raw `DataSource`
|
|
3
|
+
* payload and evaluating every per-node CEL expression once. The recursive
|
|
4
|
+
* renderer (`node.vue`) consumes this plain structure and never touches CEL, so
|
|
5
|
+
* evaluation happens a single time per node in `tree.vue` rather than once per
|
|
6
|
+
* recursive component instance.
|
|
7
|
+
*
|
|
8
|
+
* Group-vs-link is decided here: a node with `children` renders collapsible; a
|
|
9
|
+
* childless node whose `route` is non-empty renders as a navigable link (and is
|
|
10
|
+
* `favoritable`).
|
|
11
|
+
*/
|
|
12
|
+
export interface NavNode {
|
|
13
|
+
key: string;
|
|
14
|
+
label: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
route?: string;
|
|
17
|
+
favoritable: boolean;
|
|
18
|
+
children: NavNode[];
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref } from 'vue';
|
|
2
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
3
|
+
import { type SidebarConfigValue } from './schema.js';
|
|
4
|
+
export interface SidebarEditorState {
|
|
5
|
+
draft: Ref<SidebarConfigValue>;
|
|
6
|
+
validationError: Ref<string | null>;
|
|
7
|
+
isDirty: ComputedRef<boolean>;
|
|
8
|
+
configure: (env: Environment) => void;
|
|
9
|
+
confirm: () => void;
|
|
10
|
+
dismiss: () => void;
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface UseSidebarEditorOptions {
|
|
14
|
+
configure?: (env: Environment) => void;
|
|
15
|
+
/** Called after a successful 确认 commits the draft to the outer model. */
|
|
16
|
+
onClose?: () => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function useSidebarEditor(model: Ref<SidebarConfigValue>, options?: UseSidebarEditorOptions): SidebarEditorState;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
import { formatValidationError } from "../config/utils/validation-error.js";
|
|
4
|
+
import { SidebarConfig } from "./schema.js";
|
|
5
|
+
function deepClone(value) {
|
|
6
|
+
return JSON.parse(JSON.stringify(value));
|
|
7
|
+
}
|
|
8
|
+
export function useSidebarEditor(model, options = {}) {
|
|
9
|
+
const configure = options.configure ?? (() => {
|
|
10
|
+
});
|
|
11
|
+
const baseline = ref(deepClone(model.value));
|
|
12
|
+
const draft = ref(deepClone(baseline.value));
|
|
13
|
+
const isDirty = computed(
|
|
14
|
+
() => JSON.stringify(draft.value) !== JSON.stringify(baseline.value)
|
|
15
|
+
);
|
|
16
|
+
const validationError = ref(null);
|
|
17
|
+
function confirm() {
|
|
18
|
+
const schema = SidebarConfig(configure);
|
|
19
|
+
try {
|
|
20
|
+
const decoded = Schema.decodeUnknownSync(schema)(draft.value);
|
|
21
|
+
validationError.value = null;
|
|
22
|
+
model.value = decoded;
|
|
23
|
+
baseline.value = deepClone(decoded);
|
|
24
|
+
draft.value = deepClone(decoded);
|
|
25
|
+
options.onClose?.();
|
|
26
|
+
} catch (err) {
|
|
27
|
+
validationError.value = formatValidationError(err, () => void 0, draft.value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function reset() {
|
|
31
|
+
baseline.value = deepClone(model.value);
|
|
32
|
+
draft.value = deepClone(model.value);
|
|
33
|
+
validationError.value = null;
|
|
34
|
+
}
|
|
35
|
+
function dismiss() {
|
|
36
|
+
reset();
|
|
37
|
+
options.onClose?.();
|
|
38
|
+
}
|
|
39
|
+
watch(draft, () => {
|
|
40
|
+
if (validationError.value) validationError.value = null;
|
|
41
|
+
}, { deep: true });
|
|
42
|
+
return { draft, validationError, isDirty, configure, confirm, dismiss, reset };
|
|
43
|
+
}
|
|
@@ -24,6 +24,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
24
24
|
readonly locale: "en" | "ja" | "ko";
|
|
25
25
|
readonly message: string;
|
|
26
26
|
}[]] | undefined;
|
|
27
|
+
readonly options: string;
|
|
27
28
|
readonly compatibilityDate: "2026-05-24";
|
|
28
29
|
readonly grow?: boolean | undefined;
|
|
29
30
|
readonly triggers?: readonly {
|
|
@@ -44,7 +45,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
44
45
|
readonly readonly?: string | undefined;
|
|
45
46
|
readonly request: string;
|
|
46
47
|
readonly enableSorting?: boolean | undefined;
|
|
47
|
-
readonly options: string;
|
|
48
48
|
readonly optionValue: string;
|
|
49
49
|
readonly optionLabel: readonly [{
|
|
50
50
|
readonly locale: "zh";
|
|
@@ -79,6 +79,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
79
79
|
readonly locale: "en" | "ja" | "ko";
|
|
80
80
|
readonly message: string;
|
|
81
81
|
}[]] | undefined;
|
|
82
|
+
readonly options: string;
|
|
82
83
|
readonly compatibilityDate: "2026-05-24";
|
|
83
84
|
readonly grow?: boolean | undefined;
|
|
84
85
|
readonly triggers?: readonly {
|
|
@@ -99,7 +100,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
99
100
|
readonly readonly?: string | undefined;
|
|
100
101
|
readonly request: string;
|
|
101
102
|
readonly enableSorting?: boolean | undefined;
|
|
102
|
-
readonly options: string;
|
|
103
103
|
readonly optionValue: string;
|
|
104
104
|
readonly optionLabel: readonly [{
|
|
105
105
|
readonly locale: "zh";
|