@selvajs/ui 6.4.0 → 6.5.0
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/components/compute/AppLayout.svelte +30 -5
- package/dist/components/preview/TabLayout.svelte +3 -24
- package/dist/schema/visibility-rules.d.ts +2 -1
- package/dist/schema/visibility-rules.js +3 -0
- package/package.json +9 -9
- package/src/lib/components/compute/AppLayout.svelte +30 -5
- package/src/lib/components/preview/TabLayout.svelte +3 -24
- package/src/lib/schema/visibility-rules.ts +8 -1
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { Button } from '../..';
|
|
14
14
|
import ParameterPresetManager from './ParameterPresetManager.svelte';
|
|
15
15
|
import { getLocaleContext } from '../../i18n/localeContext.svelte';
|
|
16
|
+
import { buildVisibilityMap, isTabVisible, itemKey } from '../../schema/visibility-rules';
|
|
16
17
|
|
|
17
18
|
const locale = getLocaleContext();
|
|
18
19
|
const t = $derived(locale.messages);
|
|
@@ -68,12 +69,36 @@
|
|
|
68
69
|
const hasViewer = $derived(
|
|
69
70
|
!!(schema?.viewerOptions?.enableLocal || schema?.viewerOptions?.enableRemote)
|
|
70
71
|
);
|
|
71
|
-
const
|
|
72
|
-
schema.layout.type === 'tabbed'
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
schema.layout.type === 'tabbed' ? schema.layout.tabs.filter((t) => t.position === 'right') : []
|
|
72
|
+
const shownTabs = $derived(
|
|
73
|
+
schema.layout.type === 'tabbed'
|
|
74
|
+
? schema.layout.tabs.filter((tab) => isTabVisible(tab, values))
|
|
75
|
+
: []
|
|
76
76
|
);
|
|
77
|
+
// A hidden or disabled input still solves, so reset it to its default — otherwise the
|
|
78
|
+
// value the user last set while it was visible keeps feeding the definition. Lives here,
|
|
79
|
+
// not in TabLayout: a panel whose tabs all hide unmounts and would miss the reset.
|
|
80
|
+
$effect(() => {
|
|
81
|
+
if (schema.layout.type !== 'tabbed') return;
|
|
82
|
+
const updates: Record<string, unknown> = {};
|
|
83
|
+
schema.layout.tabs.forEach((tab) =>
|
|
84
|
+
tab.groups.forEach((group) => {
|
|
85
|
+
const visibilityMap = buildVisibilityMap(group.items, values);
|
|
86
|
+
group.items.forEach((layoutItem) => {
|
|
87
|
+
if (layoutItem.type === 'linebreak') return;
|
|
88
|
+
const { visible, disabled, defaultValue } = visibilityMap[itemKey(layoutItem)];
|
|
89
|
+
const input = schema.inputs.find((i) => i.id === layoutItem.paramId);
|
|
90
|
+
if (!input || defaultValue === undefined) return;
|
|
91
|
+
if ((!visible || disabled) && values[input.id] !== defaultValue) {
|
|
92
|
+
updates[input.id] = defaultValue;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
if (Object.keys(updates).length > 0) Object.assign(values, updates);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const leftTabs = $derived(shownTabs.filter((t) => t.position !== 'right'));
|
|
101
|
+
const rightTabs = $derived(shownTabs.filter((t) => t.position === 'right'));
|
|
77
102
|
const hasLeftPanel = $derived(leftTabs.length > 0);
|
|
78
103
|
const hasRightPanel = $derived(rightTabs.length > 0);
|
|
79
104
|
const hasSidebar = $derived(hasViewer || hasRightPanel);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as Tabs from '../primitives/tabs';
|
|
5
5
|
import TabBar from './TabBar.svelte';
|
|
6
6
|
import TabContent from './TabContent.svelte';
|
|
7
|
-
import {
|
|
7
|
+
import { isTabVisible } from '../../schema/visibility-rules';
|
|
8
8
|
import { buildDynamicValueListOptions } from '../../schema/dynamic-value-list';
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
const visibleTabs = $derived(
|
|
26
26
|
schema.layout.type === 'tabbed'
|
|
27
27
|
? schema.layout.tabs.filter((tab) => {
|
|
28
|
+
if (!isTabVisible(tab, values)) return false;
|
|
28
29
|
if (!panelFilter) return true;
|
|
29
30
|
return panelFilter === 'right' ? tab.position === 'right' : tab.position !== 'right';
|
|
30
31
|
})
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
$effect(() => {
|
|
40
41
|
if (requestedTabId && visibleTabs.some((t) => t.id === requestedTabId)) {
|
|
41
42
|
activeTabId = requestedTabId;
|
|
42
|
-
} else if (visibleTabs.length > 0 && !activeTabId) {
|
|
43
|
+
} else if (visibleTabs.length > 0 && !visibleTabs.some((t) => t.id === activeTabId)) {
|
|
43
44
|
activeTabId = visibleTabs[0].id;
|
|
44
45
|
}
|
|
45
46
|
});
|
|
@@ -56,28 +57,6 @@
|
|
|
56
57
|
if (Object.keys(initial).length > 0) Object.assign(collapsedGroups, initial);
|
|
57
58
|
});
|
|
58
59
|
|
|
59
|
-
// A hidden or disabled input still solves, so reset it to its default — otherwise the
|
|
60
|
-
// value the user last set while it was visible keeps feeding the definition.
|
|
61
|
-
$effect(() => {
|
|
62
|
-
if (schema.layout.type !== 'tabbed') return;
|
|
63
|
-
const updates: Record<string, unknown> = {};
|
|
64
|
-
visibleTabs.forEach((tab) =>
|
|
65
|
-
tab.groups.forEach((group) => {
|
|
66
|
-
const visibilityMap = buildVisibilityMap(group.items, values);
|
|
67
|
-
group.items.forEach((layoutItem) => {
|
|
68
|
-
if (layoutItem.type === 'linebreak') return;
|
|
69
|
-
const { visible, disabled, defaultValue } = visibilityMap[itemKey(layoutItem)];
|
|
70
|
-
const input = schema.inputs.find((i) => i.id === layoutItem.paramId);
|
|
71
|
-
if (!input || defaultValue === undefined) return;
|
|
72
|
-
if ((!visible || disabled) && values[input.id] !== defaultValue) {
|
|
73
|
-
updates[input.id] = defaultValue;
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
})
|
|
77
|
-
);
|
|
78
|
-
if (Object.keys(updates).length > 0) Object.assign(values, updates);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
60
|
function toggleGroup(groupId: string) {
|
|
82
61
|
collapsedGroups[groupId] = !collapsedGroups[groupId];
|
|
83
62
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LayoutItem, VisibilityRule, VisibilityCondition, GroupVisibilityCondition } from '@selvajs/schemas';
|
|
1
|
+
import type { LayoutItem, VisibilityRule, VisibilityCondition, GroupVisibilityCondition, TabConfig } from '@selvajs/schemas';
|
|
2
2
|
export interface VisibilityResult {
|
|
3
3
|
visible: boolean;
|
|
4
4
|
disabled: boolean;
|
|
@@ -19,6 +19,7 @@ export declare function evaluateGroupVisibility(group: {
|
|
|
19
19
|
visibilityCondition?: GroupVisibilityCondition;
|
|
20
20
|
items?: LayoutItem[];
|
|
21
21
|
}, values: Record<string, unknown>): boolean;
|
|
22
|
+
export declare function isTabVisible(tab: TabConfig, values: Record<string, unknown>): boolean;
|
|
22
23
|
/**
|
|
23
24
|
* A linebreak is always "visible" but renders as a divider only, so a group left with
|
|
24
25
|
* nothing but separators counts as empty.
|
|
@@ -82,6 +82,9 @@ export function evaluateGroupVisibility(group, values) {
|
|
|
82
82
|
const met = evaluateCondition(group.visibilityCondition, values);
|
|
83
83
|
return action === 'hide' ? !met : met;
|
|
84
84
|
}
|
|
85
|
+
export function isTabVisible(tab, values) {
|
|
86
|
+
return (tab.visible !== false && tab.groups.some((group) => evaluateGroupVisibility(group, values)));
|
|
87
|
+
}
|
|
85
88
|
/**
|
|
86
89
|
* A linebreak is always "visible" but renders as a divider only, so a group left with
|
|
87
90
|
* nothing but separators counts as empty.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/ui",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "Shared UI components and utilities for Selva applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"svelte": "^5",
|
|
60
60
|
"tailwind-variants": "^3.3.1",
|
|
61
61
|
"three": "^0.186.1",
|
|
62
|
-
"@selvajs/compute": "^4.2.
|
|
63
|
-
"@selvajs/schemas": "^5.
|
|
64
|
-
"@selvajs/solve": "^1.2.
|
|
65
|
-
"@selvajs/visualization": "^1.
|
|
62
|
+
"@selvajs/compute": "^4.2.1",
|
|
63
|
+
"@selvajs/schemas": "^5.2.0",
|
|
64
|
+
"@selvajs/solve": "^1.2.2",
|
|
65
|
+
"@selvajs/visualization": "^1.5.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"three": {
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"svelte": "5.57.1",
|
|
91
91
|
"tailwind-variants": "^3.3.1",
|
|
92
92
|
"vitest": "^5.0.2",
|
|
93
|
-
"@selvajs/compute": "4.2.
|
|
93
|
+
"@selvajs/compute": "4.2.1",
|
|
94
94
|
"@selvajs/config": "0.0.4",
|
|
95
|
-
"@selvajs/
|
|
96
|
-
"@selvajs/
|
|
97
|
-
"@selvajs/
|
|
95
|
+
"@selvajs/schemas": "5.2.0",
|
|
96
|
+
"@selvajs/visualization": "1.5.0",
|
|
97
|
+
"@selvajs/solve": "1.2.2"
|
|
98
98
|
},
|
|
99
99
|
"scripts": {
|
|
100
100
|
"predev": "node ../../scripts/sync-shared-assets.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { Button } from '$lib';
|
|
14
14
|
import ParameterPresetManager from './ParameterPresetManager.svelte';
|
|
15
15
|
import { getLocaleContext } from '../../i18n/localeContext.svelte';
|
|
16
|
+
import { buildVisibilityMap, isTabVisible, itemKey } from '../../schema/visibility-rules';
|
|
16
17
|
|
|
17
18
|
const locale = getLocaleContext();
|
|
18
19
|
const t = $derived(locale.messages);
|
|
@@ -68,12 +69,36 @@
|
|
|
68
69
|
const hasViewer = $derived(
|
|
69
70
|
!!(schema?.viewerOptions?.enableLocal || schema?.viewerOptions?.enableRemote)
|
|
70
71
|
);
|
|
71
|
-
const
|
|
72
|
-
schema.layout.type === 'tabbed'
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
schema.layout.type === 'tabbed' ? schema.layout.tabs.filter((t) => t.position === 'right') : []
|
|
72
|
+
const shownTabs = $derived(
|
|
73
|
+
schema.layout.type === 'tabbed'
|
|
74
|
+
? schema.layout.tabs.filter((tab) => isTabVisible(tab, values))
|
|
75
|
+
: []
|
|
76
76
|
);
|
|
77
|
+
// A hidden or disabled input still solves, so reset it to its default — otherwise the
|
|
78
|
+
// value the user last set while it was visible keeps feeding the definition. Lives here,
|
|
79
|
+
// not in TabLayout: a panel whose tabs all hide unmounts and would miss the reset.
|
|
80
|
+
$effect(() => {
|
|
81
|
+
if (schema.layout.type !== 'tabbed') return;
|
|
82
|
+
const updates: Record<string, unknown> = {};
|
|
83
|
+
schema.layout.tabs.forEach((tab) =>
|
|
84
|
+
tab.groups.forEach((group) => {
|
|
85
|
+
const visibilityMap = buildVisibilityMap(group.items, values);
|
|
86
|
+
group.items.forEach((layoutItem) => {
|
|
87
|
+
if (layoutItem.type === 'linebreak') return;
|
|
88
|
+
const { visible, disabled, defaultValue } = visibilityMap[itemKey(layoutItem)];
|
|
89
|
+
const input = schema.inputs.find((i) => i.id === layoutItem.paramId);
|
|
90
|
+
if (!input || defaultValue === undefined) return;
|
|
91
|
+
if ((!visible || disabled) && values[input.id] !== defaultValue) {
|
|
92
|
+
updates[input.id] = defaultValue;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
if (Object.keys(updates).length > 0) Object.assign(values, updates);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const leftTabs = $derived(shownTabs.filter((t) => t.position !== 'right'));
|
|
101
|
+
const rightTabs = $derived(shownTabs.filter((t) => t.position === 'right'));
|
|
77
102
|
const hasLeftPanel = $derived(leftTabs.length > 0);
|
|
78
103
|
const hasRightPanel = $derived(rightTabs.length > 0);
|
|
79
104
|
const hasSidebar = $derived(hasViewer || hasRightPanel);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as Tabs from '$lib/components/primitives/tabs';
|
|
5
5
|
import TabBar from './TabBar.svelte';
|
|
6
6
|
import TabContent from './TabContent.svelte';
|
|
7
|
-
import {
|
|
7
|
+
import { isTabVisible } from '$lib/schema/visibility-rules';
|
|
8
8
|
import { buildDynamicValueListOptions } from '$lib/schema/dynamic-value-list';
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
const visibleTabs = $derived(
|
|
26
26
|
schema.layout.type === 'tabbed'
|
|
27
27
|
? schema.layout.tabs.filter((tab) => {
|
|
28
|
+
if (!isTabVisible(tab, values)) return false;
|
|
28
29
|
if (!panelFilter) return true;
|
|
29
30
|
return panelFilter === 'right' ? tab.position === 'right' : tab.position !== 'right';
|
|
30
31
|
})
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
$effect(() => {
|
|
40
41
|
if (requestedTabId && visibleTabs.some((t) => t.id === requestedTabId)) {
|
|
41
42
|
activeTabId = requestedTabId;
|
|
42
|
-
} else if (visibleTabs.length > 0 && !activeTabId) {
|
|
43
|
+
} else if (visibleTabs.length > 0 && !visibleTabs.some((t) => t.id === activeTabId)) {
|
|
43
44
|
activeTabId = visibleTabs[0].id;
|
|
44
45
|
}
|
|
45
46
|
});
|
|
@@ -56,28 +57,6 @@
|
|
|
56
57
|
if (Object.keys(initial).length > 0) Object.assign(collapsedGroups, initial);
|
|
57
58
|
});
|
|
58
59
|
|
|
59
|
-
// A hidden or disabled input still solves, so reset it to its default — otherwise the
|
|
60
|
-
// value the user last set while it was visible keeps feeding the definition.
|
|
61
|
-
$effect(() => {
|
|
62
|
-
if (schema.layout.type !== 'tabbed') return;
|
|
63
|
-
const updates: Record<string, unknown> = {};
|
|
64
|
-
visibleTabs.forEach((tab) =>
|
|
65
|
-
tab.groups.forEach((group) => {
|
|
66
|
-
const visibilityMap = buildVisibilityMap(group.items, values);
|
|
67
|
-
group.items.forEach((layoutItem) => {
|
|
68
|
-
if (layoutItem.type === 'linebreak') return;
|
|
69
|
-
const { visible, disabled, defaultValue } = visibilityMap[itemKey(layoutItem)];
|
|
70
|
-
const input = schema.inputs.find((i) => i.id === layoutItem.paramId);
|
|
71
|
-
if (!input || defaultValue === undefined) return;
|
|
72
|
-
if ((!visible || disabled) && values[input.id] !== defaultValue) {
|
|
73
|
-
updates[input.id] = defaultValue;
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
})
|
|
77
|
-
);
|
|
78
|
-
if (Object.keys(updates).length > 0) Object.assign(values, updates);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
60
|
function toggleGroup(groupId: string) {
|
|
82
61
|
collapsedGroups[groupId] = !collapsedGroups[groupId];
|
|
83
62
|
}
|
|
@@ -2,7 +2,8 @@ import type {
|
|
|
2
2
|
LayoutItem,
|
|
3
3
|
VisibilityRule,
|
|
4
4
|
VisibilityCondition,
|
|
5
|
-
GroupVisibilityCondition
|
|
5
|
+
GroupVisibilityCondition,
|
|
6
|
+
TabConfig
|
|
6
7
|
} from '@selvajs/schemas';
|
|
7
8
|
|
|
8
9
|
export interface VisibilityResult {
|
|
@@ -118,6 +119,12 @@ export function evaluateGroupVisibility(
|
|
|
118
119
|
return action === 'hide' ? !met : met;
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
export function isTabVisible(tab: TabConfig, values: Record<string, unknown>): boolean {
|
|
123
|
+
return (
|
|
124
|
+
tab.visible !== false && tab.groups.some((group) => evaluateGroupVisibility(group, values))
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
121
128
|
/**
|
|
122
129
|
* A linebreak is always "visible" but renders as a divider only, so a group left with
|
|
123
130
|
* nothing but separators counts as empty.
|