@vobs/ui 0.1.0 → 0.3.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/app-shell-dom.d.ts +6 -0
- package/dist/app-shell-dom.js +373 -33
- package/dist/app-shell.d.ts +55 -19
- package/dist/app-shell.js +63 -18
- package/dist/base.css +1 -2
- package/dist/complex-inputs.js +7 -3
- package/dist/components.css +235 -47
- package/dist/data-display.d.ts +31 -0
- package/dist/data-display.js +72 -29
- package/dist/dialog.d.ts +26 -0
- package/dist/dialog.js +94 -0
- package/dist/forms.js +10 -6
- package/dist/index.d.ts +11 -2
- package/dist/index.js +16 -8
- package/dist/overlay.d.ts +4 -0
- package/dist/overlay.js +10 -5
- package/dist/page-state.d.ts +23 -0
- package/dist/page-state.js +96 -0
- package/dist/theme-data.d.ts +31 -0
- package/dist/theme-data.js +43 -0
- package/dist/theme.d.ts +19 -1
- package/dist/theme.js +72 -33
- package/dist/tokens.css +121 -0
- package/dist/tree.js +11 -5
- package/dist/virtual-list.js +28 -11
- package/package.json +17 -9
package/dist/app-shell.d.ts
CHANGED
|
@@ -4,16 +4,23 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { type UiAttributes, type UiDensity, type UiDomPlan } from './primitives.js';
|
|
6
6
|
import { type ReadonlySignal } from '@vobs/reactivity';
|
|
7
|
+
import { type I18nInstance } from '@vobs/i18n';
|
|
7
8
|
import { type RouteLocation } from '@vobs/router';
|
|
9
|
+
import type { TranslationAdapter } from '@vobs/runtime-core';
|
|
8
10
|
import { type ComponentProps, type InjectionKey } from '@vobs/runtime-dom';
|
|
9
11
|
import { type UiEnvironment } from './icons.js';
|
|
10
|
-
import type { BrandInput,
|
|
12
|
+
import type { BrandInput, ThemePreference } from './theme.js';
|
|
11
13
|
export type AppShellContentMode = 'contained' | 'scroll';
|
|
12
14
|
export type AppShellSidebarMode = 'collapsed' | 'expanded' | 'hidden';
|
|
13
15
|
export type AppShellNavItemTone = 'danger' | 'default' | 'success' | 'warning';
|
|
16
|
+
export interface AppShellTextConfig {
|
|
17
|
+
readonly key: string;
|
|
18
|
+
readonly fallback?: string;
|
|
19
|
+
}
|
|
20
|
+
export type AppShellText = string | AppShellTextConfig;
|
|
14
21
|
export interface AppShellNavItem {
|
|
15
22
|
readonly id: string;
|
|
16
|
-
readonly label:
|
|
23
|
+
readonly label: AppShellText;
|
|
17
24
|
readonly route?: string;
|
|
18
25
|
readonly href?: string;
|
|
19
26
|
readonly icon?: string;
|
|
@@ -26,7 +33,7 @@ export interface AppShellNavItem {
|
|
|
26
33
|
}
|
|
27
34
|
export interface AppShellAction {
|
|
28
35
|
readonly id: string;
|
|
29
|
-
readonly label:
|
|
36
|
+
readonly label: AppShellText;
|
|
30
37
|
readonly icon?: string;
|
|
31
38
|
readonly badge?: string | number;
|
|
32
39
|
readonly intent?: string;
|
|
@@ -37,7 +44,7 @@ export type AppShellAccountMenuItemTone = 'danger' | 'default';
|
|
|
37
44
|
export type AppShellAccountMenuItemKind = 'item' | 'separator';
|
|
38
45
|
export interface AppShellAccountMenuItem {
|
|
39
46
|
readonly id: string;
|
|
40
|
-
readonly label?:
|
|
47
|
+
readonly label?: AppShellText;
|
|
41
48
|
readonly kind?: AppShellAccountMenuItemKind;
|
|
42
49
|
readonly route?: string;
|
|
43
50
|
readonly href?: string;
|
|
@@ -46,7 +53,7 @@ export interface AppShellAccountMenuItem {
|
|
|
46
53
|
readonly tone?: AppShellAccountMenuItemTone;
|
|
47
54
|
}
|
|
48
55
|
export interface AppShellBrandConfig {
|
|
49
|
-
readonly label:
|
|
56
|
+
readonly label: AppShellText;
|
|
50
57
|
readonly href?: string;
|
|
51
58
|
readonly mark?: string;
|
|
52
59
|
}
|
|
@@ -55,26 +62,43 @@ export interface AppShellNavigationConfig {
|
|
|
55
62
|
readonly utility?: readonly AppShellNavItem[];
|
|
56
63
|
}
|
|
57
64
|
export interface AppShellAccountConfig {
|
|
58
|
-
readonly label:
|
|
65
|
+
readonly label: AppShellText;
|
|
59
66
|
readonly initials?: string;
|
|
60
67
|
readonly icon?: string;
|
|
61
68
|
readonly menu?: readonly AppShellAccountMenuItem[];
|
|
62
69
|
readonly menuExpanded?: boolean;
|
|
63
70
|
}
|
|
71
|
+
export interface AppShellLanguageOption {
|
|
72
|
+
readonly locale: string;
|
|
73
|
+
readonly label: AppShellText;
|
|
74
|
+
}
|
|
75
|
+
export interface AppShellLanguageConfig {
|
|
76
|
+
readonly label?: AppShellText;
|
|
77
|
+
readonly icon?: string;
|
|
78
|
+
readonly options: readonly AppShellLanguageOption[];
|
|
79
|
+
}
|
|
64
80
|
export interface AppShellTopbarConfig {
|
|
65
81
|
readonly actions?: readonly AppShellAction[];
|
|
82
|
+
readonly language?: AppShellLanguageConfig;
|
|
66
83
|
readonly account?: AppShellAccountConfig;
|
|
67
84
|
}
|
|
68
85
|
export interface AppShellPageHeaderConfig {
|
|
69
|
-
readonly
|
|
70
|
-
readonly
|
|
71
|
-
readonly
|
|
86
|
+
readonly visible?: boolean;
|
|
87
|
+
readonly kicker?: AppShellText;
|
|
88
|
+
readonly titleFallback?: AppShellText;
|
|
89
|
+
readonly descriptionFallback?: AppShellText;
|
|
90
|
+
}
|
|
91
|
+
export interface AppShellRoutePageHeaderConfig {
|
|
92
|
+
readonly visible?: boolean;
|
|
93
|
+
readonly kicker?: AppShellText;
|
|
94
|
+
readonly title?: AppShellText;
|
|
95
|
+
readonly description?: AppShellText;
|
|
72
96
|
}
|
|
73
97
|
export interface AppShellStatusbarConfig {
|
|
74
|
-
readonly text?:
|
|
98
|
+
readonly text?: AppShellText;
|
|
75
99
|
readonly icon?: string;
|
|
76
|
-
readonly version?:
|
|
77
|
-
readonly pendingText?:
|
|
100
|
+
readonly version?: AppShellText;
|
|
101
|
+
readonly pendingText?: AppShellText;
|
|
78
102
|
readonly pendingIcon?: string;
|
|
79
103
|
}
|
|
80
104
|
export interface AppShellThemeIconConfig {
|
|
@@ -82,7 +106,7 @@ export interface AppShellThemeIconConfig {
|
|
|
82
106
|
readonly light?: string;
|
|
83
107
|
}
|
|
84
108
|
export interface AppShellThemeConfig {
|
|
85
|
-
readonly mode?:
|
|
109
|
+
readonly mode?: ThemePreference;
|
|
86
110
|
readonly brand?: BrandInput;
|
|
87
111
|
readonly brands?: readonly BrandInput[];
|
|
88
112
|
readonly icons?: AppShellThemeIconConfig;
|
|
@@ -92,14 +116,15 @@ export interface AppShellThemeConfig {
|
|
|
92
116
|
export interface AppShellRouteConfig {
|
|
93
117
|
readonly path?: string;
|
|
94
118
|
readonly route?: string;
|
|
95
|
-
readonly title?:
|
|
96
|
-
readonly description?:
|
|
97
|
-
readonly status?:
|
|
119
|
+
readonly title?: AppShellText;
|
|
120
|
+
readonly description?: AppShellText;
|
|
121
|
+
readonly status?: AppShellText;
|
|
98
122
|
readonly activeNav?: string;
|
|
123
|
+
readonly pageHeader?: false | AppShellRoutePageHeaderConfig;
|
|
99
124
|
}
|
|
100
125
|
export interface AppShellDefinition {
|
|
101
126
|
readonly id: string;
|
|
102
|
-
readonly title?:
|
|
127
|
+
readonly title?: AppShellText;
|
|
103
128
|
readonly brand: AppShellBrandConfig;
|
|
104
129
|
readonly navigation?: AppShellNavigationConfig;
|
|
105
130
|
readonly routes?: readonly AppShellRouteConfig[];
|
|
@@ -134,6 +159,13 @@ export interface ResolvedAppShellState {
|
|
|
134
159
|
readonly description: string;
|
|
135
160
|
readonly status: string;
|
|
136
161
|
readonly page: AppShellRouteConfig | undefined;
|
|
162
|
+
readonly pageHeader: ResolvedAppShellPageHeader;
|
|
163
|
+
}
|
|
164
|
+
export interface ResolvedAppShellPageHeader {
|
|
165
|
+
readonly visible: boolean;
|
|
166
|
+
readonly kicker: string;
|
|
167
|
+
readonly title: string;
|
|
168
|
+
readonly description: string;
|
|
137
169
|
}
|
|
138
170
|
export interface ResolvedAppShellStatus {
|
|
139
171
|
readonly state: 'idle' | 'pending';
|
|
@@ -145,14 +177,17 @@ export declare function defineAppShell<const Shell extends AppShellDefinition>(s
|
|
|
145
177
|
export declare function defineAppShellConfig<const Config extends AppShellConfigInput>(config: Config): Config;
|
|
146
178
|
export declare function createAppShellRegistry(config: AppShellConfigInput): AppShellRegistry;
|
|
147
179
|
export declare function provideUiAppShells(context: UiAppShellProviderContext, config: AppShellConfigInput): () => void;
|
|
148
|
-
export declare function resolveAppShellState(shell: AppShellDefinition, route: RouteLocation | string | null | undefined): ResolvedAppShellState;
|
|
149
|
-
export declare function resolveAppShellStatus(shell: AppShellDefinition, route: RouteLocation | string | null | undefined, pendingRoute: RouteLocation | string | null | undefined): ResolvedAppShellStatus;
|
|
180
|
+
export declare function resolveAppShellState(shell: AppShellDefinition, route: RouteLocation | string | null | undefined, translate?: (key: string) => string): ResolvedAppShellState;
|
|
181
|
+
export declare function resolveAppShellStatus(shell: AppShellDefinition, route: RouteLocation | string | null | undefined, pendingRoute: RouteLocation | string | null | undefined, translate?: (key: string) => string): ResolvedAppShellStatus;
|
|
182
|
+
export declare function resolveAppShellText(value: AppShellText | undefined, translate?: (key: string) => string): string | undefined;
|
|
150
183
|
export declare function resolveActiveAppShellNavItemId(routePath: string | undefined, items: readonly AppShellNavItem[], activeNav: string | undefined): string | undefined;
|
|
151
184
|
export declare const AppShell: import("@vobs/runtime-core").ComponentDefinition<AppShellProps, Record<string, never>, AppShellComponentScope, Readonly<Record<string, Readonly<Record<string, unknown>>>>>;
|
|
152
185
|
export interface AppShellComponentScope {
|
|
153
186
|
readonly props: ComponentProps<AppShellProps>;
|
|
154
187
|
readonly appShells: AppShellRegistry;
|
|
155
188
|
readonly icons: UiEnvironment;
|
|
189
|
+
readonly i18n: I18nInstance | undefined;
|
|
190
|
+
readonly translation: TranslationAdapter | undefined;
|
|
156
191
|
readonly currentRoute: ReadonlySignal<RouteLocation | null>;
|
|
157
192
|
readonly pendingRoute: ReadonlySignal<RouteLocation | null>;
|
|
158
193
|
}
|
|
@@ -166,6 +201,7 @@ export interface AppShellPlanOptions {
|
|
|
166
201
|
readonly activeId?: string;
|
|
167
202
|
readonly sidebarMode?: AppShellSidebarMode;
|
|
168
203
|
readonly contentMode?: AppShellContentMode;
|
|
204
|
+
readonly pageHeaderVisible?: boolean;
|
|
169
205
|
readonly title?: string;
|
|
170
206
|
readonly subtitle?: string;
|
|
171
207
|
readonly commandLabel?: string;
|
package/dist/app-shell.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createUiDomPlan, } from './primitives.js';
|
|
6
6
|
import { effect, signal } from '@vobs/reactivity';
|
|
7
|
+
import { I18nKey } from '@vobs/i18n';
|
|
7
8
|
import { CurrentRouteKey, PendingRouteKey } from '@vobs/router';
|
|
8
|
-
import { createCompiledTemplate, createInjectionKey, createRuntimeError, defineComponent, mountSlot, } from '@vobs/runtime-dom';
|
|
9
|
-
import { bindAppShellAccountMenu, bindAppShellThemeControls, createAppShellFrame, syncAppShellFrame, } from './app-shell-dom.js';
|
|
9
|
+
import { createCompiledTemplate, createInjectionKey, createRuntimeError, defineComponent, mountSlot, TranslationAdapterKey, } from '@vobs/runtime-dom';
|
|
10
|
+
import { bindAppShellAccountMenu, bindAppShellActions, bindAppShellLanguageMenu, bindAppShellThemeControls, createAppShellFrame, syncAppShellFrame, } from './app-shell-dom.js';
|
|
10
11
|
import { createUiEnvironment, UiIconEnvironmentKey } from './icons.js';
|
|
11
12
|
export const UiAppShellEnvironmentKey = createInjectionKey('vobs.ui.appShells');
|
|
12
13
|
export function defineAppShell(shell) {
|
|
@@ -30,23 +31,25 @@ export function provideUiAppShells(context, config) {
|
|
|
30
31
|
appShells: createAppShellRegistry(config),
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
|
-
export function resolveAppShellState(shell, route) {
|
|
34
|
+
export function resolveAppShellState(shell, route, translate) {
|
|
34
35
|
const routePath = typeof route === 'string' ? route : route?.path;
|
|
35
36
|
const page = resolveAppShellPage(shell, routePath);
|
|
36
37
|
const navItems = collectAppShellNavItems(shell);
|
|
37
38
|
const activeId = resolveActiveAppShellNavItemId(routePath, navItems, page?.activeNav);
|
|
39
|
+
const pageHeader = resolveAppShellPageHeader(shell, page, translate);
|
|
38
40
|
return {
|
|
39
41
|
activeId,
|
|
40
|
-
title:
|
|
41
|
-
description:
|
|
42
|
-
status: page?.status ?? shell.statusbar?.text ?? '',
|
|
42
|
+
title: pageHeader.title,
|
|
43
|
+
description: pageHeader.description,
|
|
44
|
+
status: resolveAppShellText(page?.status ?? shell.statusbar?.text, translate) ?? '',
|
|
43
45
|
page,
|
|
46
|
+
pageHeader,
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
|
-
export function resolveAppShellStatus(shell, route, pendingRoute) {
|
|
49
|
+
export function resolveAppShellStatus(shell, route, pendingRoute, translate) {
|
|
47
50
|
if (pendingRoute !== null && pendingRoute !== undefined) {
|
|
48
|
-
const pending = resolveAppShellState(shell, pendingRoute);
|
|
49
|
-
const textBase = shell.statusbar?.pendingText ?? 'Loading';
|
|
51
|
+
const pending = resolveAppShellState(shell, pendingRoute, translate);
|
|
52
|
+
const textBase = resolveAppShellText(shell.statusbar?.pendingText, translate) ?? 'Loading';
|
|
50
53
|
return {
|
|
51
54
|
state: 'pending',
|
|
52
55
|
text: pending.title.length === 0 ? textBase : `${textBase} ${pending.title}`,
|
|
@@ -55,13 +58,20 @@ export function resolveAppShellStatus(shell, route, pendingRoute) {
|
|
|
55
58
|
: { icon: shell.statusbar?.pendingIcon ?? shell.statusbar?.icon }),
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
|
-
const current = resolveAppShellState(shell, route);
|
|
61
|
+
const current = resolveAppShellState(shell, route, translate);
|
|
59
62
|
return {
|
|
60
63
|
state: 'idle',
|
|
61
|
-
text: current.status || shell.statusbar?.text || '',
|
|
64
|
+
text: current.status || resolveAppShellText(shell.statusbar?.text, translate) || '',
|
|
62
65
|
...(shell.statusbar?.icon === undefined ? {} : { icon: shell.statusbar.icon }),
|
|
63
66
|
};
|
|
64
67
|
}
|
|
68
|
+
export function resolveAppShellText(value, translate) {
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
return undefined;
|
|
71
|
+
if (typeof value === 'string')
|
|
72
|
+
return value;
|
|
73
|
+
return translate?.(value.key) ?? value.fallback ?? value.key;
|
|
74
|
+
}
|
|
65
75
|
export function resolveActiveAppShellNavItemId(routePath, items, activeNav) {
|
|
66
76
|
if (activeNav !== undefined && items.some((item) => item.id === activeNav))
|
|
67
77
|
return activeNav;
|
|
@@ -108,6 +118,8 @@ export const AppShell = defineComponent({
|
|
|
108
118
|
props: context.props,
|
|
109
119
|
appShells: appShellEnvironment.appShells,
|
|
110
120
|
icons: context.maybeInject(UiIconEnvironmentKey) ?? createUiEnvironment(),
|
|
121
|
+
i18n: context.maybeInject(I18nKey),
|
|
122
|
+
translation: context.maybeInject(TranslationAdapterKey),
|
|
111
123
|
currentRoute,
|
|
112
124
|
pendingRoute,
|
|
113
125
|
};
|
|
@@ -182,10 +194,10 @@ export function createAppShellPlans(options) {
|
|
|
182
194
|
state: action.disabled === true ? 'disabled' : action.pressed === true ? 'pressed' : 'idle',
|
|
183
195
|
attrs: {
|
|
184
196
|
type: 'button',
|
|
185
|
-
'aria-label': action.label,
|
|
197
|
+
'aria-label': resolveAppShellText(action.label),
|
|
186
198
|
'aria-pressed': action.pressed === undefined ? undefined : action.pressed ? 'true' : 'false',
|
|
187
199
|
'data-action-id': action.id,
|
|
188
|
-
'data-label': action.label,
|
|
200
|
+
'data-label': resolveAppShellText(action.label),
|
|
189
201
|
},
|
|
190
202
|
})),
|
|
191
203
|
main: createUiDomPlan('main', 'app-shell-main', {
|
|
@@ -194,7 +206,13 @@ export function createAppShellPlans(options) {
|
|
|
194
206
|
},
|
|
195
207
|
}),
|
|
196
208
|
pageFrame: createUiDomPlan('section', 'app-shell-page-frame'),
|
|
197
|
-
pageHeader: createUiDomPlan('header', 'app-shell-page-header'
|
|
209
|
+
pageHeader: createUiDomPlan('header', 'app-shell-page-header', {
|
|
210
|
+
state: options.pageHeaderVisible === false ? 'hidden' : 'visible',
|
|
211
|
+
attrs: {
|
|
212
|
+
hidden: options.pageHeaderVisible === false,
|
|
213
|
+
'aria-hidden': options.pageHeaderVisible === false ? 'true' : undefined,
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
198
216
|
pageTitle: options.title === undefined
|
|
199
217
|
? undefined
|
|
200
218
|
: createUiDomPlan('h1', 'app-shell-page-title', {
|
|
@@ -249,7 +267,7 @@ function createNavItemPlan(options, item, activeId) {
|
|
|
249
267
|
'aria-current': active ? 'page' : undefined,
|
|
250
268
|
tabindex: active && item.disabled !== true ? 0 : -1,
|
|
251
269
|
'data-nav-id': item.id,
|
|
252
|
-
'data-label': item.label,
|
|
270
|
+
'data-label': resolveAppShellText(item.label),
|
|
253
271
|
'data-tone': item.tone ?? 'default',
|
|
254
272
|
'data-has-badge': item.badge === undefined ? undefined : 'true',
|
|
255
273
|
},
|
|
@@ -271,18 +289,31 @@ function mountAppShell(owner, scope, environment) {
|
|
|
271
289
|
mountSlot(owner, pageBody, 'default', environment.slots?.default, undefined);
|
|
272
290
|
}
|
|
273
291
|
bindAppShellThemeControls(owner, frame, shell, scope, environment);
|
|
292
|
+
bindAppShellActions(owner, frame, shell);
|
|
274
293
|
bindAppShellAccountMenu(owner, frame, shell);
|
|
275
|
-
|
|
294
|
+
bindAppShellLanguageMenu(owner, frame, shell, scope, environment);
|
|
295
|
+
syncAppShellFrame(frame, shell, resolveShellState(shell, scope), resolveShellStatus(shell, scope), scope, environment);
|
|
276
296
|
owner.run(() => {
|
|
277
297
|
const stop = effect(() => {
|
|
278
|
-
|
|
279
|
-
const
|
|
298
|
+
void scope.translation?.locale?.value;
|
|
299
|
+
const state = resolveShellState(shell, scope);
|
|
300
|
+
const status = resolveShellStatus(shell, scope);
|
|
280
301
|
syncAppShellFrame(frame, shell, state, status, scope, environment);
|
|
281
302
|
}, { scheduler: environment.scheduler });
|
|
282
303
|
owner.own(stop);
|
|
283
304
|
});
|
|
284
305
|
return { root: frame.root, refs: {} };
|
|
285
306
|
}
|
|
307
|
+
function resolveShellState(shell, scope) {
|
|
308
|
+
return resolveAppShellState(shell, scope.currentRoute.value, appShellTranslate(scope));
|
|
309
|
+
}
|
|
310
|
+
function resolveShellStatus(shell, scope) {
|
|
311
|
+
return resolveAppShellStatus(shell, scope.currentRoute.value, scope.pendingRoute.value, appShellTranslate(scope));
|
|
312
|
+
}
|
|
313
|
+
function appShellTranslate(scope) {
|
|
314
|
+
const translation = scope.translation;
|
|
315
|
+
return translation === undefined ? undefined : (key) => translation.t(key);
|
|
316
|
+
}
|
|
286
317
|
function readAppShell(scope) {
|
|
287
318
|
const id = readOptionalString(scope.props.shell?.value);
|
|
288
319
|
if (id === undefined)
|
|
@@ -323,6 +354,20 @@ function resolveAppShellPage(shell, routePath) {
|
|
|
323
354
|
.filter((candidate) => candidate.score >= 0)
|
|
324
355
|
.sort((left, right) => right.score - left.score)[0]?.page;
|
|
325
356
|
}
|
|
357
|
+
function resolveAppShellPageHeader(shell, page, translate) {
|
|
358
|
+
const routeHeader = page?.pageHeader;
|
|
359
|
+
const header = routeHeader === false ? undefined : routeHeader;
|
|
360
|
+
const title = resolveAppShellText(header?.title ?? page?.title ?? shell.pageHeader?.titleFallback ?? shell.title, translate) ?? '';
|
|
361
|
+
const description = resolveAppShellText(header?.description ?? page?.description ?? shell.pageHeader?.descriptionFallback, translate) ?? '';
|
|
362
|
+
return {
|
|
363
|
+
visible: routeHeader === false ? false : (header?.visible ?? shell.pageHeader?.visible ?? true),
|
|
364
|
+
kicker: resolveAppShellText(header?.kicker ?? shell.pageHeader?.kicker ?? shell.title, translate) ??
|
|
365
|
+
resolveAppShellText(shell.brand.label, translate) ??
|
|
366
|
+
'',
|
|
367
|
+
title,
|
|
368
|
+
description,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
326
371
|
function scoreRoutePattern(pattern, path) {
|
|
327
372
|
if (pattern === undefined)
|
|
328
373
|
return -1;
|
package/dist/base.css
CHANGED
package/dist/complex-inputs.js
CHANGED
|
@@ -137,10 +137,11 @@ export function createUploadTransportStateContract(options = {}) {
|
|
|
137
137
|
const disabled = options.disabled === true;
|
|
138
138
|
const maxRetries = normalizeRetryCount(options.maxRetries);
|
|
139
139
|
const retryingIds = normalizeIds(options.retryingIds);
|
|
140
|
+
const retryingSet = new Set(retryingIds);
|
|
140
141
|
const files = (options.files ?? []).map((file) => uploadTransportFileContract(file, {
|
|
141
142
|
disabled,
|
|
142
143
|
maxRetries,
|
|
143
|
-
retrying:
|
|
144
|
+
retrying: retryingSet.has(file.id),
|
|
144
145
|
serverErrors: normalizeServerErrors(options.serverErrors?.[file.id], file.serverError),
|
|
145
146
|
}));
|
|
146
147
|
const fileCount = files.length;
|
|
@@ -291,13 +292,14 @@ export function createCascaderAdvancedStateContract(options) {
|
|
|
291
292
|
const visibleIds = options.visibleIds === undefined
|
|
292
293
|
? flattenedOptions.map((option) => option.id)
|
|
293
294
|
: normalizeCascaderIds(options.visibleIds, new Set(flattenedOptions.map((option) => option.id)));
|
|
295
|
+
const visibleSet = new Set(visibleIds);
|
|
294
296
|
const allowedSet = options.allowedIds === undefined
|
|
295
297
|
? undefined
|
|
296
298
|
: new Set(normalizeCascaderIds(options.allowedIds, new Set(flattenedOptions.map((option) => option.id))));
|
|
297
299
|
const deniedIds = normalizeCascaderIds(options.deniedIds, new Set(flattenedOptions.map((option) => option.id)));
|
|
298
300
|
const deniedSet = new Set(deniedIds);
|
|
299
301
|
const optionContracts = flattenedOptions.map((option) => {
|
|
300
|
-
const visible =
|
|
302
|
+
const visible = visibleSet.has(option.id);
|
|
301
303
|
const allowed = !deniedSet.has(option.id) && (allowedSet === undefined || allowedSet.has(option.id));
|
|
302
304
|
const reason = allowed ? '' : (options.disabledReasons?.[option.id] ?? 'Permission denied');
|
|
303
305
|
return {
|
|
@@ -658,9 +660,11 @@ function normalizeCascaderIds(ids, knownIds) {
|
|
|
658
660
|
if (ids === undefined)
|
|
659
661
|
return [];
|
|
660
662
|
const result = [];
|
|
663
|
+
const seen = new Set();
|
|
661
664
|
for (const id of ids) {
|
|
662
|
-
if (!knownIds.has(id) ||
|
|
665
|
+
if (!knownIds.has(id) || seen.has(id))
|
|
663
666
|
continue;
|
|
667
|
+
seen.add(id);
|
|
664
668
|
result.push(id);
|
|
665
669
|
}
|
|
666
670
|
return result;
|