@gradio/core 1.1.3 → 1.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/CHANGELOG.md +51 -0
- package/dist/src/Blocks.svelte +33 -2
- package/dist/src/_init.js +2 -2
- package/dist/src/api_docs/ApiBanner.svelte +6 -2
- package/dist/src/api_docs/ApiBanner.svelte.d.ts +1 -1
- package/dist/src/api_docs/ApiDocs.svelte +41 -25
- package/dist/src/api_docs/CodeSnippet.svelte +67 -170
- package/dist/src/api_docs/CodeSnippet.svelte.d.ts +2 -6
- package/dist/src/api_docs/CopyMarkdown.svelte +7 -2
- package/dist/src/api_docs/CopyMarkdown.svelte.d.ts +1 -1
- package/dist/src/api_docs/InstallSnippet.svelte +6 -1
- package/dist/src/api_docs/InstallSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/ParametersSnippet.svelte +6 -1
- package/dist/src/api_docs/ParametersSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/RecordingSnippet.svelte +6 -1
- package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/ResponseSnippet.svelte +6 -1
- package/dist/src/api_docs/ResponseSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/Settings.svelte +2 -7
- package/dist/src/api_docs/SettingsBanner.svelte +0 -3
- package/dist/src/api_docs/SkillSnippet.svelte +125 -0
- package/dist/src/api_docs/SkillSnippet.svelte.d.ts +20 -0
- package/dist/src/api_docs/img/skill.svg +10 -0
- package/dist/src/api_docs/utils.d.ts +0 -1
- package/dist/src/api_docs/utils.js +0 -22
- package/dist/src/dependency.d.ts +3 -1
- package/dist/src/dependency.js +39 -1
- package/dist/src/gradio_helper.js +5 -13
- package/dist/src/i18n.d.ts +1 -3
- package/dist/src/i18n.js +6 -53
- package/dist/src/init.svelte.js +81 -82
- package/package.json +49 -49
- package/src/Blocks.svelte +33 -2
- package/src/_init.ts +2 -2
- package/src/api_docs/ApiBanner.svelte +6 -2
- package/src/api_docs/ApiDocs.svelte +41 -25
- package/src/api_docs/CodeSnippet.svelte +67 -170
- package/src/api_docs/CopyMarkdown.svelte +7 -2
- package/src/api_docs/InstallSnippet.svelte +6 -1
- package/src/api_docs/ParametersSnippet.svelte +6 -1
- package/src/api_docs/RecordingSnippet.svelte +6 -1
- package/src/api_docs/ResponseSnippet.svelte +6 -1
- package/src/api_docs/Settings.svelte +2 -7
- package/src/api_docs/SettingsBanner.svelte +0 -3
- package/src/api_docs/SkillSnippet.svelte +125 -0
- package/src/api_docs/img/skill.svg +10 -0
- package/src/api_docs/utils.ts +0 -25
- package/src/dependency.ts +39 -1
- package/src/gradio_helper.ts +5 -17
- package/src/i18n.test.ts +41 -9
- package/src/i18n.ts +9 -62
- package/src/init.svelte.ts +95 -99
package/src/gradio_helper.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { all_common_keys } from "./i18n";
|
|
2
1
|
import { _ } from "svelte-i18n";
|
|
3
2
|
import { get, derived } from "svelte/store";
|
|
4
3
|
export { Gradio } from "@gradio/utils";
|
|
4
|
+
import { I18N_MARKER, translate_i18n_marker } from "@gradio/utils";
|
|
5
5
|
|
|
6
6
|
export type I18nFormatter = typeof formatter;
|
|
7
7
|
|
|
@@ -12,27 +12,15 @@ export function formatter(value: string | null | undefined): string {
|
|
|
12
12
|
const string_value = String(value);
|
|
13
13
|
const translate = get(_);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
if (string_value.includes(I18N_MARKER)) {
|
|
16
|
+
return translate_i18n_marker(string_value, translate);
|
|
17
|
+
}
|
|
16
18
|
|
|
19
|
+
const direct_translation = translate(string_value);
|
|
17
20
|
if (direct_translation !== string_value) {
|
|
18
21
|
return direct_translation;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
const lower_value = string_value.toLowerCase();
|
|
22
|
-
|
|
23
|
-
for (const common_key of all_common_keys) {
|
|
24
|
-
const key_name = common_key.substring(common_key.indexOf(".") + 1);
|
|
25
|
-
|
|
26
|
-
if (lower_value === key_name) {
|
|
27
|
-
const translation = translate(common_key);
|
|
28
|
-
|
|
29
|
-
if (translation !== common_key) {
|
|
30
|
-
return translation;
|
|
31
|
-
}
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
24
|
return string_value;
|
|
37
25
|
}
|
|
38
26
|
|
package/src/i18n.test.ts
CHANGED
|
@@ -11,12 +11,12 @@ import { Lang, process_langs } from "./i18n";
|
|
|
11
11
|
import languagesByAnyCode from "wikidata-lang/indexes/by_any_code";
|
|
12
12
|
import BCP47 from "./lang/BCP47_codes";
|
|
13
13
|
import {
|
|
14
|
-
translate_if_needed,
|
|
15
14
|
get_initial_locale,
|
|
16
15
|
load_translations,
|
|
17
16
|
changeLocale,
|
|
18
17
|
is_translation_metadata
|
|
19
18
|
} from "./i18n";
|
|
19
|
+
import { formatter } from "./gradio_helper";
|
|
20
20
|
import { loading } from "./lang/loading";
|
|
21
21
|
|
|
22
22
|
const loading_count = Object.keys(loading).length;
|
|
@@ -28,8 +28,16 @@ vi.mock("svelte-i18n", () => ({
|
|
|
28
28
|
init: vi.fn().mockResolvedValue(undefined)
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
+
const mock_translations: Record<string, string> = {
|
|
32
|
+
"common.submit": "Submit",
|
|
33
|
+
"common.name": "Name",
|
|
34
|
+
"common.greeting": "Hello",
|
|
35
|
+
"common.submit_es": "Enviar",
|
|
36
|
+
"common.name_es": "Nombre"
|
|
37
|
+
};
|
|
38
|
+
|
|
31
39
|
vi.mock("svelte/store", () => ({
|
|
32
|
-
get: vi.fn((
|
|
40
|
+
get: vi.fn(() => (key: string) => mock_translations[key] ?? key),
|
|
33
41
|
derived: vi.fn()
|
|
34
42
|
}));
|
|
35
43
|
|
|
@@ -66,13 +74,6 @@ describe("i18n", () => {
|
|
|
66
74
|
);
|
|
67
75
|
});
|
|
68
76
|
|
|
69
|
-
describe("basic functions", () => {
|
|
70
|
-
test("translate_if_needed handles regular strings", () => {
|
|
71
|
-
const regularString = "hello world";
|
|
72
|
-
expect(translate_if_needed(regularString)).toBe(regularString);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
77
|
describe("locale management", () => {
|
|
77
78
|
test("get_initial_locale returns browser locale when available", () => {
|
|
78
79
|
const result = get_initial_locale("fr", ["en", "fr", "de"]);
|
|
@@ -155,4 +156,35 @@ describe("i18n", () => {
|
|
|
155
156
|
expect(Boolean(is_translation_metadata("not an object"))).toBe(false);
|
|
156
157
|
});
|
|
157
158
|
});
|
|
159
|
+
|
|
160
|
+
describe("formatter", () => {
|
|
161
|
+
test("translates i18n markers", () => {
|
|
162
|
+
expect(formatter('__i18n__{"key":"common.submit"}')).toBe("Submit");
|
|
163
|
+
expect(formatter('Click: __i18n__{"key":"common.submit"}')).toBe(
|
|
164
|
+
"Click: Submit"
|
|
165
|
+
);
|
|
166
|
+
expect(formatter('__i18n__{"key":"common.name"} field')).toBe(
|
|
167
|
+
"Name field"
|
|
168
|
+
);
|
|
169
|
+
expect(formatter('__i18n__{"key":"common.submit_es"}')).toBe("Enviar");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("returns key when no translation exists", () => {
|
|
173
|
+
expect(formatter('__i18n__{"key":"unknown.key"}')).toBe("unknown.key");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("handles null, undefined, and plain text", () => {
|
|
177
|
+
expect(formatter(null)).toBe("");
|
|
178
|
+
expect(formatter(undefined)).toBe("");
|
|
179
|
+
expect(formatter("Hello world")).toBe("Hello world");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("handles malformed markers", () => {
|
|
183
|
+
expect(formatter("__i18n__")).toBe("__i18n__");
|
|
184
|
+
expect(formatter('__i18n__{"key":"test.key"')).toBe(
|
|
185
|
+
'__i18n__{"key":"test.key"'
|
|
186
|
+
);
|
|
187
|
+
expect(formatter("__i18n__{invalid}")).toBe("__i18n__{invalid}");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
158
190
|
});
|
package/src/i18n.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
register,
|
|
7
7
|
waitLocale
|
|
8
8
|
} from "svelte-i18n";
|
|
9
|
-
import { formatter } from "./gradio_helper";
|
|
10
9
|
import { loading } from "./lang/loading";
|
|
11
10
|
|
|
12
11
|
const lang_map = {
|
|
@@ -71,63 +70,7 @@ export function is_translation_metadata(obj: any): obj is I18nData {
|
|
|
71
70
|
return result;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
export
|
|
75
|
-
|
|
76
|
-
// handles strings with embedded JSON metadata of shape "__i18n__{"key": "some.key"}"
|
|
77
|
-
export function translate_if_needed(value: any): string {
|
|
78
|
-
if (typeof value !== "string") {
|
|
79
|
-
return value;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const marker_index = value.indexOf(i18n_marker);
|
|
83
|
-
|
|
84
|
-
if (marker_index === -1) {
|
|
85
|
-
return value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const before_marker =
|
|
90
|
-
marker_index > 0 ? value.substring(0, marker_index) : "";
|
|
91
|
-
|
|
92
|
-
const after_marker_index = marker_index + i18n_marker.length;
|
|
93
|
-
const json_start = value.indexOf("{", after_marker_index);
|
|
94
|
-
let json_end = -1;
|
|
95
|
-
let bracket_count = 0;
|
|
96
|
-
|
|
97
|
-
for (let i = json_start; i < value.length; i++) {
|
|
98
|
-
if (value[i] === "{") bracket_count++;
|
|
99
|
-
if (value[i] === "}") bracket_count--;
|
|
100
|
-
if (bracket_count === 0) {
|
|
101
|
-
json_end = i + 1;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (json_end === -1) {
|
|
107
|
-
console.error("Could not find end of JSON in i18n string");
|
|
108
|
-
return value;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const metadata_json = value.substring(json_start, json_end);
|
|
112
|
-
const after_json = json_end < value.length ? value.substring(json_end) : "";
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const metadata = JSON.parse(metadata_json);
|
|
116
|
-
|
|
117
|
-
if (metadata && metadata.key) {
|
|
118
|
-
const translated = formatter(metadata.key);
|
|
119
|
-
return before_marker + translated + after_json;
|
|
120
|
-
}
|
|
121
|
-
} catch (jsonError) {
|
|
122
|
-
console.error("Error parsing i18n JSON:", jsonError);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return value;
|
|
126
|
-
} catch (e) {
|
|
127
|
-
console.error("Error processing translation:", e);
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
73
|
+
export { I18N_MARKER as i18n_marker } from "@gradio/utils";
|
|
131
74
|
|
|
132
75
|
export function process_langs(): LangsRecord {
|
|
133
76
|
const lazy_langs = Object.fromEntries(
|
|
@@ -150,8 +93,6 @@ export const language_choices: [string, string][] = Object.entries(
|
|
|
150
93
|
processed_langs
|
|
151
94
|
).map(([code]) => [lang_map[code as keyof typeof lang_map] || code, code]);
|
|
152
95
|
|
|
153
|
-
export let all_common_keys: Set<string> = new Set();
|
|
154
|
-
|
|
155
96
|
let i18n_initialized = false;
|
|
156
97
|
let previous_translations: Record<string, Record<string, string>> | undefined;
|
|
157
98
|
|
|
@@ -183,15 +124,21 @@ export async function setupi18n(
|
|
|
183
124
|
return;
|
|
184
125
|
}
|
|
185
126
|
|
|
186
|
-
|
|
127
|
+
const translations_to_use =
|
|
128
|
+
custom_translations ?? previous_translations ?? {};
|
|
129
|
+
|
|
130
|
+
if (custom_translations !== undefined) {
|
|
131
|
+
previous_translations = custom_translations;
|
|
132
|
+
}
|
|
187
133
|
|
|
188
134
|
load_translations({
|
|
189
135
|
processed_langs,
|
|
190
|
-
custom_translations:
|
|
136
|
+
custom_translations: translations_to_use
|
|
191
137
|
});
|
|
192
138
|
|
|
193
139
|
let initial_locale: string | null = null;
|
|
194
140
|
const browser_locale = getLocaleFromNavigator();
|
|
141
|
+
|
|
195
142
|
if (preferred_locale) {
|
|
196
143
|
initial_locale = get_lang_from_preferred_locale(preferred_locale);
|
|
197
144
|
} else {
|
package/src/init.svelte.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
get_component,
|
|
4
4
|
get_inputs_outputs
|
|
5
5
|
} from "./init_utils";
|
|
6
|
-
import { translate_if_needed } from "./i18n";
|
|
7
6
|
import { tick } from "svelte";
|
|
8
7
|
import { dequal } from "dequal";
|
|
9
8
|
|
|
@@ -68,6 +67,7 @@ export class AppTree {
|
|
|
68
67
|
|
|
69
68
|
#get_callbacks = new Map<number, get_data_type>();
|
|
70
69
|
#set_callbacks = new Map<number, set_data_type>();
|
|
70
|
+
#pending_updates = new Map<number, Record<string, unknown>>();
|
|
71
71
|
#event_dispatcher: (id: number, event: string, data: unknown) => void;
|
|
72
72
|
component_ids: number[];
|
|
73
73
|
initial_tabs: Record<number, Tab[]> = {};
|
|
@@ -196,6 +196,21 @@ export class AppTree {
|
|
|
196
196
|
this.#set_callbacks.set(id, _set_data);
|
|
197
197
|
this.#get_callbacks.set(id, _get_data);
|
|
198
198
|
this.components_to_register.delete(id);
|
|
199
|
+
|
|
200
|
+
// Apply any pending updates that were stored while the component
|
|
201
|
+
// was not yet mounted (e.g. hidden in an inactive tab).
|
|
202
|
+
// We must apply AFTER tick() so that the Gradio class's $effect
|
|
203
|
+
// (which syncs from node props) has already run. Otherwise the
|
|
204
|
+
// $effect would overwrite the values we set here.
|
|
205
|
+
const pending = this.#pending_updates.get(id);
|
|
206
|
+
if (pending) {
|
|
207
|
+
this.#pending_updates.delete(id);
|
|
208
|
+
tick().then(() => {
|
|
209
|
+
const _set = this.#set_callbacks.get(id);
|
|
210
|
+
if (_set) _set(pending);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
199
214
|
if (this.components_to_register.size === 0 && !this.resolved) {
|
|
200
215
|
this.resolved = true;
|
|
201
216
|
this.ready_resolve();
|
|
@@ -222,8 +237,6 @@ export class AppTree {
|
|
|
222
237
|
node,
|
|
223
238
|
this.components_to_register
|
|
224
239
|
),
|
|
225
|
-
(node) => handle_empty_forms(node, this.components_to_register),
|
|
226
|
-
(node) => translate_props(node),
|
|
227
240
|
(node) => apply_initial_tabs(node, this.initial_tabs),
|
|
228
241
|
(node) => this.find_attached_events(node, this.#dependency_payload),
|
|
229
242
|
(node) =>
|
|
@@ -422,7 +435,6 @@ export class AppTree {
|
|
|
422
435
|
//@ts-ignore
|
|
423
436
|
(n) => set_visibility_for_updated_node(n, id, new_state.visible),
|
|
424
437
|
//@ts-ignore
|
|
425
|
-
(n) => update_parent_visibility(n, id, new_state.visible),
|
|
426
438
|
(n) => handle_visibility(n, this.#config.api_url)
|
|
427
439
|
]);
|
|
428
440
|
await tick();
|
|
@@ -433,11 +445,24 @@ export class AppTree {
|
|
|
433
445
|
const old_value = node?.props.props.value;
|
|
434
446
|
// @ts-ignore
|
|
435
447
|
const new_props = create_props_shared_props(new_state);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
448
|
+
// Modify props in-place instead of replacing the entire object.
|
|
449
|
+
// Replacing with a new object via spread can cause Svelte 5's
|
|
450
|
+
// deep $state proxy to lose track of the values during async
|
|
451
|
+
// component mounting/revival.
|
|
452
|
+
for (const key in new_props.shared_props) {
|
|
453
|
+
// @ts-ignore
|
|
454
|
+
node!.props.shared_props[key] = new_props.shared_props[key];
|
|
455
|
+
}
|
|
456
|
+
for (const key in new_props.props) {
|
|
457
|
+
// @ts-ignore
|
|
458
|
+
node!.props.props[key] = new_props.props[key];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Also store as pending so the value can be applied via _set_data
|
|
462
|
+
// when the component eventually mounts and registers
|
|
463
|
+
const existing = this.#pending_updates.get(id) || {};
|
|
464
|
+
this.#pending_updates.set(id, { ...existing, ...new_state });
|
|
465
|
+
|
|
441
466
|
if ("value" in new_state && !dequal(old_value, new_state.value)) {
|
|
442
467
|
this.#event_dispatcher(id, "change", null);
|
|
443
468
|
}
|
|
@@ -453,10 +478,6 @@ export class AppTree {
|
|
|
453
478
|
// any values currently in the UI.
|
|
454
479
|
// @ts-ignore
|
|
455
480
|
await this.update_visibility(node, new_state);
|
|
456
|
-
const parent_node = find_parent(this.root!, id);
|
|
457
|
-
if (parent_node)
|
|
458
|
-
// @ts-ignore
|
|
459
|
-
update_parent_visibility(parent_node, id, new_state.visible);
|
|
460
481
|
}
|
|
461
482
|
|
|
462
483
|
/**
|
|
@@ -477,28 +498,73 @@ export class AppTree {
|
|
|
477
498
|
}
|
|
478
499
|
|
|
479
500
|
async render_previously_invisible_children(id: number) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
(node)
|
|
488
|
-
|
|
501
|
+
const node = find_node_by_id(this.root!, id);
|
|
502
|
+
if (!node) return;
|
|
503
|
+
|
|
504
|
+
// Check if this node or any of its descendants need to be made visible.
|
|
505
|
+
// If not, skip entirely to avoid unnecessary reactive updates
|
|
506
|
+
// from mutating the tree through the $state proxy.
|
|
507
|
+
if (
|
|
508
|
+
!this.#hidden_on_startup.has(node.id) &&
|
|
509
|
+
!has_hidden_descendants(node, this.#hidden_on_startup)
|
|
510
|
+
) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
make_visible_if_not_rendered(node, this.#hidden_on_startup, true);
|
|
515
|
+
load_components(node, this.#config.api_url);
|
|
489
516
|
}
|
|
490
517
|
}
|
|
491
518
|
|
|
492
519
|
function make_visible_if_not_rendered(
|
|
493
520
|
node: ProcessedComponentMeta,
|
|
494
|
-
hidden_on_startup: Set<number
|
|
521
|
+
hidden_on_startup: Set<number>,
|
|
522
|
+
is_target_node = false
|
|
495
523
|
): void {
|
|
496
524
|
node.props.shared_props.visible = hidden_on_startup.has(node.id)
|
|
497
525
|
? true
|
|
498
526
|
: node.props.shared_props.visible;
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
527
|
+
|
|
528
|
+
if (node.type === "tabs") {
|
|
529
|
+
const selectedId =
|
|
530
|
+
node.props.props.selected ?? node.props.props.initial_tabs?.[0]?.id;
|
|
531
|
+
node.children.forEach((child) => {
|
|
532
|
+
if (
|
|
533
|
+
child.type === "tabitem" &&
|
|
534
|
+
(child.props.props.id === selectedId || child.id === selectedId)
|
|
535
|
+
) {
|
|
536
|
+
make_visible_if_not_rendered(child, hidden_on_startup, false);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
} else if (
|
|
540
|
+
node.type === "accordion" &&
|
|
541
|
+
node.props.props.open === false &&
|
|
542
|
+
!is_target_node
|
|
543
|
+
) {
|
|
544
|
+
// Don't recurse into closed accordion content
|
|
545
|
+
} else {
|
|
546
|
+
node.children.forEach((child) => {
|
|
547
|
+
make_visible_if_not_rendered(child, hidden_on_startup, false);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function has_hidden_descendants(
|
|
553
|
+
node: ProcessedComponentMeta,
|
|
554
|
+
hidden_on_startup: Set<number>
|
|
555
|
+
): boolean {
|
|
556
|
+
for (const child of node.children) {
|
|
557
|
+
if (hidden_on_startup.has(child.id)) return true;
|
|
558
|
+
if (has_hidden_descendants(child, hidden_on_startup)) return true;
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function load_components(node: ProcessedComponentMeta, api_url: string): void {
|
|
564
|
+
if (node.props.shared_props.visible && !node.component) {
|
|
565
|
+
node.component = get_component(node.type, node.component_class_id, api_url);
|
|
566
|
+
}
|
|
567
|
+
node.children.forEach((child) => load_components(child, api_url));
|
|
502
568
|
}
|
|
503
569
|
|
|
504
570
|
/**
|
|
@@ -701,78 +767,6 @@ function untrack_children_of_closed_accordions_or_inactive_tabs(
|
|
|
701
767
|
return node;
|
|
702
768
|
}
|
|
703
769
|
|
|
704
|
-
function handle_empty_forms(
|
|
705
|
-
node: ProcessedComponentMeta,
|
|
706
|
-
components_to_register: Set<number>
|
|
707
|
-
): ProcessedComponentMeta {
|
|
708
|
-
// Check if the node is visible
|
|
709
|
-
if (node.type === "form") {
|
|
710
|
-
const all_children_invisible = node.children.every(
|
|
711
|
-
(child) => child.props.shared_props.visible === false
|
|
712
|
-
);
|
|
713
|
-
|
|
714
|
-
if (all_children_invisible) {
|
|
715
|
-
node.props.shared_props.visible = false;
|
|
716
|
-
components_to_register.delete(node.id);
|
|
717
|
-
return node;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
return node;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
function update_parent_visibility(
|
|
725
|
-
node: ProcessedComponentMeta,
|
|
726
|
-
child_made_visible: number,
|
|
727
|
-
visibility_state: boolean | "hidden"
|
|
728
|
-
): ProcessedComponentMeta {
|
|
729
|
-
// This function was added to address a tricky situation:
|
|
730
|
-
// Form components are wrapped in a Form component automatically.
|
|
731
|
-
// If all the children of the Form are invisible, the Form itself is marked invisible.
|
|
732
|
-
// in AppTree.postprocess -> handle_empty_forms
|
|
733
|
-
// This is to avoid rendering empty forms in the UI. They look ugly.
|
|
734
|
-
// So what happens when a child inside the Form is made visible again?
|
|
735
|
-
// The Form needs to become visible again too.
|
|
736
|
-
// If the child is made invisible, the form should be too if all other children are invisible.
|
|
737
|
-
// However, we are not doing this now since what we want to do is fetch the latest visibility of all
|
|
738
|
-
// the children from the UI. However, get_data only returns the props, not the shared props.
|
|
739
|
-
if (
|
|
740
|
-
node.type === "form" &&
|
|
741
|
-
node.children.length &&
|
|
742
|
-
node.children.some((child) => child.id === child_made_visible)
|
|
743
|
-
) {
|
|
744
|
-
if (visibility_state === true) node.props.shared_props.visible = true;
|
|
745
|
-
else if (!visibility_state && node.children.length === 1)
|
|
746
|
-
node.props.shared_props.visible = "hidden";
|
|
747
|
-
}
|
|
748
|
-
return node;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function translate_props(node: ProcessedComponentMeta): ProcessedComponentMeta {
|
|
752
|
-
const supported_props = [
|
|
753
|
-
"description",
|
|
754
|
-
"info",
|
|
755
|
-
"title",
|
|
756
|
-
"placeholder",
|
|
757
|
-
"value",
|
|
758
|
-
"label"
|
|
759
|
-
];
|
|
760
|
-
for (const attr of Object.keys(node.props.shared_props)) {
|
|
761
|
-
if (supported_props.includes(attr as string)) {
|
|
762
|
-
// @ts-ignore
|
|
763
|
-
node.props.shared_props[attr] = translate_if_needed(
|
|
764
|
-
node.props.shared_props[attr as keyof SharedProps]
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
for (const attr of Object.keys(node.props.props)) {
|
|
769
|
-
if (supported_props.includes(attr as string)) {
|
|
770
|
-
node.props.props[attr] = translate_if_needed(node.props.props[attr]);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
return node;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
770
|
function apply_initial_tabs(
|
|
777
771
|
node: ProcessedComponentMeta,
|
|
778
772
|
initial_tabs: Record<number, Tab[]>
|
|
@@ -799,8 +793,10 @@ function _gather_initial_tabs(
|
|
|
799
793
|
if (!("id" in node.props.props)) {
|
|
800
794
|
node.props.props.id = node.id;
|
|
801
795
|
}
|
|
796
|
+
const i18n = node.props.props.i18n as ((str: string) => string) | undefined;
|
|
797
|
+
const raw_label = node.props.shared_props.label as string;
|
|
802
798
|
initial_tabs[parent_tab_id].push({
|
|
803
|
-
label:
|
|
799
|
+
label: i18n ? i18n(raw_label) : raw_label,
|
|
804
800
|
id: node.props.props.id as string,
|
|
805
801
|
elem_id: node.props.shared_props.elem_id,
|
|
806
802
|
visible: node.props.shared_props.visible as boolean,
|