@supersoniks/concorde 5.0.5 → 5.1.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/.github/workflows/ci.yml +21 -0
- package/.github-export-ignore +30 -0
- package/.tmp-cem/custom-elements.json +13705 -0
- package/CONTRIBUTING.md +59 -0
- package/DEV.md +54 -0
- package/LICENSE +21 -0
- package/OPEN_SOURCE_CHECKPOINT.md +133 -0
- package/build-infos.json +1 -1
- package/concorde-3d.bundle.js +3960 -0
- package/concorde-3d.es.js +33433 -0
- package/concorde-core.bundle.js +196 -196
- package/concorde-core.es.js +3685 -3405
- package/concorde-hugging-face-infer.bundle.js +32 -0
- package/concorde-hugging-face-infer.es.js +2952 -0
- package/concorde-interactive.bundle.js +1157 -0
- package/concorde-interactive.es.js +10734 -0
- package/concorde-shader.bundle.js +116 -0
- package/concorde-shader.es.js +8353 -0
- package/concorde-webgpu.bundle.js +307 -0
- package/concorde-webgpu.es.js +8627 -0
- package/custom-elements-manifest.config.mjs +14 -0
- package/dist/concorde-core.bundle.js +196 -196
- package/dist/concorde-core.es.js +3685 -3405
- package/docs/assets/{index-wVZpLVYW.js → index-D8MVWqY6.js} +1351 -1222
- package/docs/assets/index-DiD0qYvE.css +1 -0
- package/docs/crawl/core/components/ui/alert/alert.html +71 -172
- package/docs/crawl/core/components/ui/alert-messages/alert-messages.html +5 -113
- package/docs/crawl/core/components/ui/badge/badge.html +79 -157
- package/docs/crawl/core/components/ui/button/button.html +0 -1
- package/docs/crawl/core/components/ui/captcha/captcha.html +8 -90
- package/docs/crawl/core/components/ui/card/card.html +68 -222
- package/docs/crawl/core/components/ui/divider/divider.html +31 -117
- package/docs/crawl/core/components/ui/form/fieldset/fieldset.html +101 -232
- package/docs/crawl/core/components/ui/form/form-actions/form-actions.html +52 -95
- package/docs/crawl/core/components/ui/form/form-layout/form-layout.html +38 -97
- package/docs/crawl/core/components/ui/form/input/input.html +3 -3
- package/docs/crawl/core/components/ui/form/textarea/textarea.html +3 -3
- package/docs/crawl/core/components/ui/group/group.html +38 -60
- package/docs/crawl/core/components/ui/icon/icon.html +47 -136
- package/docs/crawl/core/components/ui/image/image.html +56 -244
- package/docs/crawl/core/components/ui/link/link.html +9 -67
- package/docs/crawl/core/components/ui/loader/loader.html +23 -97
- package/docs/crawl/core/components/ui/menu/menu.html +90 -162
- package/docs/crawl/core/components/ui/modal/modal.html +50 -275
- package/docs/crawl/core/components/ui/pop/pop.html +66 -260
- package/docs/crawl/core/components/ui/progress/progress.html +30 -103
- package/docs/crawl/core/components/ui/table/table.html +171 -303
- package/docs/crawl/core/components/ui/toast/toast.html +59 -170
- package/docs/crawl/core/components/ui/tooltip/tooltip.html +29 -90
- package/docs/crawl/docs/_decorators/ancestor-attribute.html +2 -3
- package/docs/crawl/docs/_getting-started/concorde-outside.html +3 -3
- package/docs/crawl/docs/_getting-started/start.html +1 -1
- package/docs/crawl/docs/_getting-started/theming.html +63 -311
- package/docs/crawl/hubs/form-components.html +53 -75
- package/docs/crawl/index.html +6 -7
- package/docs/docs-mock-api-sw.js +0 -2
- package/docs/docs-mock-api-sw.js.map +2 -2
- package/docs/index.html +7 -9
- package/docs/llms-full.txt +456 -1703
- package/docs/llms.txt +32 -25
- package/docs/sitemap.xml +0 -3
- package/docs/src/core/components/functional/fetch/fetch.md +0 -0
- package/docs/src/core/components/functional/if/if.md +0 -0
- package/docs/src/core/components/functional/list/list.md +0 -0
- package/docs/src/core/components/functional/router/router.md +0 -0
- package/docs/src/core/components/functional/sdui/sdui.md +0 -0
- package/docs/src/core/components/functional/states/states.md +0 -0
- package/docs/src/core/components/functional/submit/submit.md +0 -0
- package/docs/src/core/components/ui/alert/alert.md +72 -147
- package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -85
- package/docs/src/core/components/ui/badge/badge.md +80 -141
- package/docs/src/core/components/ui/button/button.md +0 -1
- package/docs/src/core/components/ui/captcha/captcha.md +6 -65
- package/docs/src/core/components/ui/card/card.md +65 -183
- package/docs/src/core/components/ui/divider/divider.md +13 -107
- package/docs/src/core/components/ui/form/fieldset/fieldset.md +99 -175
- package/docs/src/core/components/ui/form/form-actions/form-actions.md +52 -75
- package/docs/src/core/components/ui/form/form-layout/form-layout.md +34 -109
- package/docs/src/core/components/ui/form/input/input.md +1 -1
- package/docs/src/core/components/ui/form/radio/radio.md +0 -0
- package/docs/src/core/components/ui/form/select/select.md +0 -0
- package/docs/src/core/components/ui/form/switch/switch.md +0 -0
- package/docs/src/core/components/ui/form/textarea/textarea.md +1 -1
- package/docs/src/core/components/ui/group/group.md +36 -36
- package/docs/src/core/components/ui/icon/icon.md +43 -132
- package/docs/src/core/components/ui/image/image.md +53 -179
- package/docs/src/core/components/ui/link/link.md +7 -45
- package/docs/src/core/components/ui/loader/loader.md +22 -79
- package/docs/src/core/components/ui/menu/menu.md +92 -133
- package/docs/src/core/components/ui/modal/modal.md +47 -163
- package/docs/src/core/components/ui/pop/pop.md +64 -262
- package/docs/src/core/components/ui/progress/progress.md +30 -76
- package/docs/src/core/components/ui/table/table.md +171 -147
- package/docs/src/core/components/ui/toast/toast.md +58 -108
- package/docs/src/core/components/ui/tooltip/tooltip.md +26 -68
- package/docs/src/docs/_core-concept/subscriber.md +0 -0
- package/docs/src/docs/_decorators/ancestor-attribute.md +2 -3
- package/docs/src/docs/_getting-started/concorde-outside.md +3 -3
- package/docs/src/docs/_getting-started/my-first-subscriber.md +0 -0
- package/docs/src/docs/_getting-started/pubsub.md +0 -0
- package/docs/src/docs/_getting-started/start.md +1 -1
- package/docs/src/docs/_getting-started/theming.md +61 -223
- package/docs/src/docs/search/docs-search.json +348 -3083
- package/docs/src/tsconfig.declarations.json +22 -0
- package/docs/src/tsconfig.emit.json +23 -0
- package/docs/src/tsconfig.json +3 -63
- package/gitlab/job_github_mirror.sh +39 -0
- package/package.json +36 -2
- package/scripts/generate-sdui-catalog.mjs +250 -0
- package/scripts/generate-sdui-catalog.spec.ts +91 -0
- package/scripts/github-export.mjs +150 -0
- package/scripts/post-build-docs.js +0 -0
- package/scripts/post-build.mjs +0 -0
- package/scripts/pre-build.mjs +15 -0
- package/src/core/components/functional/fetch/fetch.md +0 -0
- package/src/core/components/functional/if/if.md +0 -0
- package/src/core/components/functional/if/if.ts +0 -0
- package/src/core/components/functional/list/list.demo.ts +0 -0
- package/src/core/components/functional/list/list.md +0 -0
- package/src/core/components/functional/router/router.md +0 -0
- package/src/core/components/functional/router/router.ts +0 -0
- package/src/core/components/functional/sdui/SDUIDescriptorTransformer.ts +5 -5
- package/src/core/components/functional/sdui/__fixtures__/descriptors.ts +251 -0
- package/src/core/components/functional/sdui/__snapshots__/sdui-action.spec.ts.snap +73 -0
- package/src/core/components/functional/sdui/__snapshots__/sdui-incremental.spec.ts.snap +50 -0
- package/src/core/components/functional/sdui/__snapshots__/sdui-partial.spec.ts.snap +50 -0
- package/src/core/components/functional/sdui/__snapshots__/sdui.regression.spec.ts.snap +461 -0
- package/src/core/components/functional/sdui/component-catalog.gaps.json +511 -0
- package/src/core/components/functional/sdui/component-catalog.json +3103 -0
- package/src/core/components/functional/sdui/component-catalog.spec.ts +23 -0
- package/src/core/components/functional/sdui/sdui-action.spec.ts +99 -0
- package/src/core/components/functional/sdui/sdui-dom-snapshot.ts +61 -0
- package/src/core/components/functional/sdui/sdui-extension-points.md +94 -0
- package/src/core/components/functional/sdui/sdui-incremental.spec.ts +137 -0
- package/src/core/components/functional/sdui/sdui-partial.spec.ts +83 -0
- package/src/core/components/functional/sdui/sdui-partial.ts +19 -0
- package/src/core/components/functional/sdui/sdui-safe.spec.ts +188 -0
- package/src/core/components/functional/sdui/sdui-safe.ts +100 -0
- package/src/core/components/functional/sdui/sdui.md +122 -0
- package/src/core/components/functional/sdui/sdui.regression.spec.ts +64 -0
- package/src/core/components/functional/sdui/sdui.ts +442 -34
- package/src/core/components/functional/sdui/types.ts +49 -0
- package/src/core/components/functional/states/states.md +0 -0
- package/src/core/components/functional/submit/submit.md +0 -0
- package/src/core/components/functional/subscriber/subscriber.ts +0 -0
- package/src/core/components/functional/value/value.ts +0 -0
- package/src/core/components/ui/_css/size.ts +0 -0
- package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
- package/src/core/components/ui/form/radio/radio.md +0 -0
- package/src/core/components/ui/form/select/select.md +0 -0
- package/src/core/components/ui/form/switch/switch.md +0 -0
- package/src/core/mixins/Subscriber.ts +0 -0
- package/src/core/utils/DataBindObserver.ts +0 -0
- package/src/core/utils/Objects.ts +0 -0
- package/src/core/utils/route.ts +0 -0
- package/src/css.d.ts +19 -0
- package/src/docs/_core-concept/subscriber.md +0 -0
- package/src/docs/_getting-started/my-first-subscriber.md +0 -0
- package/src/docs/_getting-started/pubsub.md +0 -0
- package/src/docs/example/users.ts +0 -0
- package/src/docs/search/page.ts +0 -0
- package/src/tsconfig.declarations.json +22 -0
- package/src/tsconfig.emit.json +23 -0
- package/src/tsconfig.json +90 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/docs/assets/index-CD4WyL6o.css +0 -1
- package/docs/crawl/core/components/ui/theme/theme.html +0 -131
- package/docs/src/core/components/ui/theme/theme.md +0 -122
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import default_library from "@supersoniks/concorde/core/components/functional/sdui/default-library.json";
|
|
2
2
|
import SDUIDescriptorTransformer from "@supersoniks/concorde/core/components/functional/sdui/SDUIDescriptorTransformer";
|
|
3
|
+
import { getPublisherFromPath } from "@supersoniks/concorde/core/decorators/subscriber/publisherPath";
|
|
3
4
|
import {
|
|
5
|
+
createSduiPlaceholder,
|
|
6
|
+
SDUI_NODE_ID_ATTR,
|
|
7
|
+
} from "@supersoniks/concorde/core/components/functional/sdui/sdui-partial";
|
|
8
|
+
import {
|
|
9
|
+
formatSafeJsWarning,
|
|
10
|
+
formatSafeNodeWarning,
|
|
11
|
+
isUnsafeSduiNode,
|
|
12
|
+
} from "@supersoniks/concorde/core/components/functional/sdui/sdui-safe";
|
|
13
|
+
import {
|
|
14
|
+
SDUIActionDetail,
|
|
4
15
|
SDUIDescriptor,
|
|
5
16
|
SDUINode,
|
|
17
|
+
SDUIOp,
|
|
6
18
|
SDUITransformDescription,
|
|
7
19
|
} from "@supersoniks/concorde/core/components/functional/sdui/types";
|
|
8
20
|
import { Fetcher, Subscriber } from "@supersoniks/concorde/mixins";
|
|
@@ -21,6 +33,10 @@ const tagName = "sonic-sdui"; // For Astro.build
|
|
|
21
33
|
* * Le format du JSON est décrit par le type SDUIDescriptor
|
|
22
34
|
* * Un attribut supplémentaire `transformation` permet de transformer le json reçu avant la génération de l'interface utilisateur<br>
|
|
23
35
|
* Son format est décrit par le type SDUITransformDescription
|
|
36
|
+
* * Attribut opt-in `profile="safe"` : refuse js, markup, innerHTML, prefix et suffix (nœud ignoré + warning).
|
|
37
|
+
* * Champ de nœud opt-in `action: { name, context }` : au clic, émet `sdui-action` (bubbles + composed).
|
|
38
|
+
* * Attribut opt-in `partial` : enfants `childNodeIds` absents → placeholder léger (sinon warning + skip).
|
|
39
|
+
* * Ops incrémentales opt-in : `upsertNode` / `removeNode` / `setData` (chemin à côté du descripteur entier).
|
|
24
40
|
*
|
|
25
41
|
* * Si le résultat de la requête est un objet, il est imbriqué dans un tableau pour garantir le fonctionnement.<br>
|
|
26
42
|
* *
|
|
@@ -32,17 +48,57 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
32
48
|
this.displayContents = true;
|
|
33
49
|
this.isFetchEnabled = this.hasAttribute("fetch");
|
|
34
50
|
super.connectedCallback();
|
|
51
|
+
if (this.opsDataProvider) this.bindOpsDataProvider();
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
@property() sduiKey?: string;
|
|
38
55
|
@property() messagesKey?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Profil de rendu opt-in. `safe` refuse js / markup / innerHTML / prefix / suffix.
|
|
58
|
+
* Sans attribut, comportement historique inchangé.
|
|
59
|
+
*/
|
|
60
|
+
@property({ type: String, reflect: true }) profile?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Rendu partiel opt-in : les `childNodeIds` introuvables deviennent des placeholders.
|
|
63
|
+
* Activé aussi en mode incrémental (ops étape 6).
|
|
64
|
+
*/
|
|
65
|
+
@property({ type: Boolean, reflect: true }) partial = false;
|
|
66
|
+
/**
|
|
67
|
+
* DataProvider optionnel alimenté par un flux d'ops (`SDUIOp` ou `SDUIOp[]`).
|
|
68
|
+
* Chaque assignation est consommée via `applyOp`.
|
|
69
|
+
*/
|
|
70
|
+
@property({ type: String }) opsDataProvider?: string;
|
|
39
71
|
private sduiDescriptor: SDUIDescriptor = {};
|
|
72
|
+
private nodeIndex = new Map<string, SDUINode>();
|
|
73
|
+
/** Garde-fou cycles childNodeIds. */
|
|
74
|
+
private renderingStack = new Set<string>();
|
|
75
|
+
/** Forcé à true par les ops incrémentales. */
|
|
76
|
+
private incrementalPartial = false;
|
|
77
|
+
/** Mode ops : descripteur local cloné ; ignore les props publisher. */
|
|
78
|
+
private incrementalActive = false;
|
|
79
|
+
private opsUnsub?: () => void;
|
|
80
|
+
|
|
81
|
+
private get isSafeProfile(): boolean {
|
|
82
|
+
return this.profile === "safe";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private get isPartialMode(): boolean {
|
|
86
|
+
return this.partial || this.incrementalPartial;
|
|
87
|
+
}
|
|
40
88
|
|
|
41
89
|
/**
|
|
42
90
|
* On peut passer la description sous form de props, sinon il faut utiliser l'attribut fetch
|
|
43
91
|
*/
|
|
44
92
|
|
|
45
93
|
willUpdate(changedProperties: PropertyValues): void {
|
|
94
|
+
if (changedProperties.has("opsDataProvider")) {
|
|
95
|
+
this.bindOpsDataProvider();
|
|
96
|
+
}
|
|
97
|
+
// Chemin incrémental : ne pas reconstruire depuis props (descripteur local).
|
|
98
|
+
if (this.incrementalActive) {
|
|
99
|
+
super.willUpdate(changedProperties);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
46
102
|
if (this.props == null) {
|
|
47
103
|
this.sduiDescriptor = {};
|
|
48
104
|
}
|
|
@@ -50,13 +106,26 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
50
106
|
const newSduiDescriptor = this.sduiKey
|
|
51
107
|
? (this.props as Record<string, SDUIDescriptor>)[this.sduiKey]
|
|
52
108
|
: (this.props as SDUIDescriptor);
|
|
53
|
-
|
|
109
|
+
const profileChanged = changedProperties.has("profile");
|
|
110
|
+
const partialChanged = changedProperties.has("partial");
|
|
111
|
+
if (
|
|
112
|
+
this.sduiDescriptor == newSduiDescriptor &&
|
|
113
|
+
!profileChanged &&
|
|
114
|
+
!partialChanged
|
|
115
|
+
)
|
|
116
|
+
return;
|
|
54
117
|
this.sduiDescriptor = newSduiDescriptor;
|
|
55
118
|
this.updateContents();
|
|
56
119
|
}
|
|
57
120
|
super.willUpdate(changedProperties);
|
|
58
121
|
}
|
|
59
122
|
|
|
123
|
+
disconnectedCallback(): void {
|
|
124
|
+
this.opsUnsub?.();
|
|
125
|
+
this.opsUnsub = undefined;
|
|
126
|
+
super.disconnectedCallback();
|
|
127
|
+
}
|
|
128
|
+
|
|
60
129
|
/**
|
|
61
130
|
* updateContents est déclenché quand les sduiDescriptor sont renseignées
|
|
62
131
|
* Le contenu du composant est regénéré en fonction du descripteur fourni
|
|
@@ -69,8 +138,32 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
69
138
|
this.loadAssets();
|
|
70
139
|
await this.loadLibrary();
|
|
71
140
|
await this.transformSDUIDescriptor();
|
|
141
|
+
this.rebuildNodeIndex();
|
|
72
142
|
this.parseRootNodes();
|
|
73
143
|
}
|
|
144
|
+
|
|
145
|
+
/** Index `nodeId` → nœud (nodeById + parcours du tree / library). */
|
|
146
|
+
private rebuildNodeIndex() {
|
|
147
|
+
this.nodeIndex.clear();
|
|
148
|
+
const visit = (node: SDUINode) => {
|
|
149
|
+
if (node.nodeId) this.nodeIndex.set(node.nodeId, node);
|
|
150
|
+
node.nodes?.forEach(visit);
|
|
151
|
+
};
|
|
152
|
+
if (this.sduiDescriptor.nodeById) {
|
|
153
|
+
for (const id of Object.keys(this.sduiDescriptor.nodeById)) {
|
|
154
|
+
const n = this.sduiDescriptor.nodeById[id];
|
|
155
|
+
if (!n.nodeId) n.nodeId = id;
|
|
156
|
+
this.nodeIndex.set(id, n);
|
|
157
|
+
visit(n);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.sduiDescriptor.nodes?.forEach(visit);
|
|
161
|
+
if (this.sduiDescriptor.library) {
|
|
162
|
+
for (const key of Object.keys(this.sduiDescriptor.library)) {
|
|
163
|
+
visit(this.sduiDescriptor.library[key]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
74
167
|
/**
|
|
75
168
|
* Suppressiond du contenu du composant avant le génération de la nouvelle ui
|
|
76
169
|
*/
|
|
@@ -86,8 +179,12 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
86
179
|
**/
|
|
87
180
|
private loadAssets() {
|
|
88
181
|
if (!this.sduiDescriptor) return;
|
|
89
|
-
if (this.sduiDescriptor.js) {
|
|
90
|
-
|
|
182
|
+
if (this.sduiDescriptor.js?.length) {
|
|
183
|
+
if (this.isSafeProfile) {
|
|
184
|
+
console.warn(formatSafeJsWarning(this.sduiDescriptor.js));
|
|
185
|
+
} else {
|
|
186
|
+
for (const src of this.sduiDescriptor.js) HTML.loadJS(src);
|
|
187
|
+
}
|
|
91
188
|
}
|
|
92
189
|
if (this.sduiDescriptor.css) {
|
|
93
190
|
for (const src of this.sduiDescriptor.css) HTML.loadCSS(src);
|
|
@@ -128,24 +225,48 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
128
225
|
messageProvider.attributes = { subDataProvider: this.messagesKey };
|
|
129
226
|
}
|
|
130
227
|
nodes.push(messageProvider);
|
|
131
|
-
nodes.forEach((node) =>
|
|
228
|
+
nodes.forEach((node, i) => {
|
|
229
|
+
const el = this.parseChild(node, `nodes[${i}]`);
|
|
230
|
+
if (el) this.appendChild(el);
|
|
231
|
+
});
|
|
132
232
|
}
|
|
133
233
|
/**
|
|
134
234
|
* On parse un noeud ce qui crée un éléments graphique et ses enfants par recursivité via `handleChildNodes`
|
|
235
|
+
* En profil safe, un nœud avec markup/innerHTML/prefix/suffix est ignoré (null).
|
|
135
236
|
*/
|
|
136
|
-
private parseChild(node: SDUINode) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
237
|
+
private parseChild(node: SDUINode, path = "node"): HTMLElement | null {
|
|
238
|
+
if (this.isSafeProfile && isUnsafeSduiNode(node)) {
|
|
239
|
+
console.warn(formatSafeNodeWarning(path, node));
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
if (node.nodeId && this.renderingStack.has(node.nodeId)) {
|
|
243
|
+
console.warn(
|
|
244
|
+
`sonic-sdui : cycle détecté sur nodeId="${node.nodeId}" (${path})`
|
|
245
|
+
);
|
|
246
|
+
return this.isPartialMode ? createSduiPlaceholder(node.nodeId) : null;
|
|
247
|
+
}
|
|
248
|
+
if (node.nodeId) this.renderingStack.add(node.nodeId);
|
|
249
|
+
try {
|
|
250
|
+
const tagName = node.tagName || "div";
|
|
251
|
+
let { element, contentElement } = this.handleLibrary(node, tagName, path);
|
|
252
|
+
if (!element) return null;
|
|
253
|
+
this.handleAttributes(node, element);
|
|
254
|
+
element = this.handleMarkup(node, element);
|
|
255
|
+
if (!contentElement) contentElement = element;
|
|
256
|
+
this.handleChildNodes(node, contentElement, element, path);
|
|
257
|
+
this.handleTextContent(node, contentElement);
|
|
258
|
+
this.handleInnerHTML(node, contentElement);
|
|
259
|
+
let host = element;
|
|
260
|
+
if (node.prefix || node.suffix) {
|
|
261
|
+
host = this.handlePrefixSuffix(node, element);
|
|
262
|
+
}
|
|
263
|
+
// Après prefix/suffix : innerHTML recrée le DOM, donc binder l'action sur l'hôte final.
|
|
264
|
+
this.bindAction(node, host);
|
|
265
|
+
if (node.nodeId) host.setAttribute(SDUI_NODE_ID_ATTR, node.nodeId);
|
|
266
|
+
return host;
|
|
267
|
+
} finally {
|
|
268
|
+
if (node.nodeId) this.renderingStack.delete(node.nodeId);
|
|
147
269
|
}
|
|
148
|
-
return element;
|
|
149
270
|
}
|
|
150
271
|
/**
|
|
151
272
|
* Si le noeud courant a des propriétés prefix et ou suffix il est entouré des markups fournis dans ces sduiDescriptor.
|
|
@@ -158,35 +279,100 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
158
279
|
container.style.display = "contents";
|
|
159
280
|
return container;
|
|
160
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Branche un clic → CustomEvent `sdui-action` si le nœud déclare `action.name`.
|
|
284
|
+
* bubbles + composed : un ancêtre (shell, page) peut écouter sans se coller au nœud.
|
|
285
|
+
*/
|
|
286
|
+
private bindAction(node: SDUINode, element: HTMLElement) {
|
|
287
|
+
const action = node.action;
|
|
288
|
+
if (!action?.name) return;
|
|
289
|
+
element.addEventListener("click", () => {
|
|
290
|
+
const detail: SDUIActionDetail = {
|
|
291
|
+
name: action.name,
|
|
292
|
+
context: action.context ?? {},
|
|
293
|
+
sourceNodeId: node.nodeId,
|
|
294
|
+
};
|
|
295
|
+
this.dispatchEvent(
|
|
296
|
+
new CustomEvent<SDUIActionDetail>("sdui-action", {
|
|
297
|
+
detail,
|
|
298
|
+
bubbles: true,
|
|
299
|
+
composed: true,
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
161
304
|
/**
|
|
162
305
|
* Création des enfants du noeud courant
|
|
163
306
|
* Si l'enfant à un attribut parentElementSelector, il est ajouté dans le noeud correspondant au sélecteur css associé et non pas dans l'élément directement.
|
|
307
|
+
* `childNodeIds` résout des nœuds du registre ; absents → placeholder (partial) ou skip.
|
|
164
308
|
*/
|
|
165
309
|
private handleChildNodes(
|
|
166
310
|
node: SDUINode,
|
|
167
311
|
contentElement: HTMLElement,
|
|
168
|
-
element: HTMLElement
|
|
312
|
+
element: HTMLElement,
|
|
313
|
+
path: string
|
|
169
314
|
) {
|
|
170
315
|
if (node.nodes) {
|
|
171
316
|
const children: Array<SDUINode> = node.nodes;
|
|
172
|
-
for (
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
317
|
+
for (let i = 0; i < children.length; i++) {
|
|
318
|
+
const child = children[i];
|
|
319
|
+
const childElement = this.parseChild(child, `${path}.nodes[${i}]`);
|
|
320
|
+
if (!childElement) continue;
|
|
321
|
+
this.appendChildElement(child, childElement, contentElement, element);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (node.childNodeIds?.length) {
|
|
325
|
+
for (const childId of node.childNodeIds) {
|
|
326
|
+
const child = this.nodeIndex.get(childId);
|
|
327
|
+
if (child) {
|
|
328
|
+
const childElement = this.parseChild(
|
|
329
|
+
child,
|
|
330
|
+
`${path}.childNodeIds[${childId}]`
|
|
331
|
+
);
|
|
332
|
+
if (childElement) {
|
|
333
|
+
this.appendChildElement(
|
|
334
|
+
child,
|
|
335
|
+
childElement,
|
|
336
|
+
contentElement,
|
|
337
|
+
element
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (this.isPartialMode) {
|
|
343
|
+
this.appendToTarget(
|
|
344
|
+
contentElement,
|
|
345
|
+
createSduiPlaceholder(childId)
|
|
346
|
+
);
|
|
347
|
+
} else {
|
|
348
|
+
console.warn(
|
|
349
|
+
`sonic-sdui : childNodeId "${childId}" introuvable (${path}) — ignoré (activez partial pour un placeholder)`
|
|
350
|
+
);
|
|
179
351
|
}
|
|
180
|
-
if (nodeToAppendOn.shadowRoot)
|
|
181
|
-
nodeToAppendOn.shadowRoot.appendChild(childElement);
|
|
182
|
-
else if (nodeToAppendOn.tagName.toLocaleLowerCase() == "template") {
|
|
183
|
-
const template: HTMLTemplateElement =
|
|
184
|
-
nodeToAppendOn as HTMLTemplateElement;
|
|
185
|
-
template.content.appendChild(childElement);
|
|
186
|
-
} else nodeToAppendOn.appendChild(childElement);
|
|
187
352
|
}
|
|
188
353
|
}
|
|
189
354
|
}
|
|
355
|
+
|
|
356
|
+
private appendChildElement(
|
|
357
|
+
child: SDUINode,
|
|
358
|
+
childElement: HTMLElement,
|
|
359
|
+
contentElement: HTMLElement,
|
|
360
|
+
element: HTMLElement
|
|
361
|
+
) {
|
|
362
|
+
let nodeToAppendOn = contentElement;
|
|
363
|
+
if (child.parentElementSelector) {
|
|
364
|
+
nodeToAppendOn =
|
|
365
|
+
element.querySelector(child.parentElementSelector) || contentElement;
|
|
366
|
+
}
|
|
367
|
+
this.appendToTarget(nodeToAppendOn, childElement);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private appendToTarget(target: HTMLElement, childElement: HTMLElement) {
|
|
371
|
+
if (target.shadowRoot) target.shadowRoot.appendChild(childElement);
|
|
372
|
+
else if (target.tagName.toLocaleLowerCase() == "template") {
|
|
373
|
+
(target as HTMLTemplateElement).content.appendChild(childElement);
|
|
374
|
+
} else target.appendChild(childElement);
|
|
375
|
+
}
|
|
190
376
|
/**
|
|
191
377
|
* Gestion de librarie :
|
|
192
378
|
* * Si l'élément référence un élément de la librairie, on se sert de cet élément comme model pour créer le composant graphique.
|
|
@@ -194,13 +380,25 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
194
380
|
* * Si contentElementSelector est definit, alors contentElement correspond à l'élément obtenu par selection css d'après la valeurs de contentElementSelector
|
|
195
381
|
* * Les éléments enfants seront ensuite ajoutés dans contentElement
|
|
196
382
|
*/
|
|
197
|
-
private handleLibrary(
|
|
198
|
-
|
|
383
|
+
private handleLibrary(
|
|
384
|
+
node: SDUINode,
|
|
385
|
+
tagName: string,
|
|
386
|
+
path: string
|
|
387
|
+
): { element: HTMLElement | null; contentElement: HTMLElement | undefined } {
|
|
388
|
+
let element: HTMLElement | null;
|
|
199
389
|
let contentElement: HTMLElement | undefined;
|
|
200
390
|
if (node.libraryKey && this.sduiDescriptor.library) {
|
|
391
|
+
const libPath = `library.${node.libraryKey}`;
|
|
201
392
|
element = this.parseChild(
|
|
202
|
-
this.sduiDescriptor.library[node.libraryKey] || { tagName: "div" }
|
|
393
|
+
this.sduiDescriptor.library[node.libraryKey] || { tagName: "div" },
|
|
394
|
+
libPath
|
|
203
395
|
);
|
|
396
|
+
if (!element) {
|
|
397
|
+
console.warn(
|
|
398
|
+
`sonic-sdui (profile="safe") : nœud ignoré (${path}) — modèle libraryKey="${node.libraryKey}" refusé`
|
|
399
|
+
);
|
|
400
|
+
return { element: null, contentElement: undefined };
|
|
401
|
+
}
|
|
204
402
|
const selector = (this.sduiDescriptor.library[node.libraryKey] || {})
|
|
205
403
|
.contentElementSelector;
|
|
206
404
|
if (selector)
|
|
@@ -233,6 +431,19 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
233
431
|
return element;
|
|
234
432
|
}
|
|
235
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Texte plain via textContent (pas de parse HTML). Autorisé en profile="safe".
|
|
436
|
+
* Ignoré si le nœud a déjà des enfants déclarés dans `nodes` (évite d'effacer le sous-arbre).
|
|
437
|
+
*/
|
|
438
|
+
private handleTextContent(
|
|
439
|
+
node: SDUINode,
|
|
440
|
+
contentElement: HTMLElement | undefined
|
|
441
|
+
) {
|
|
442
|
+
if (node.textContent == null || !contentElement) return;
|
|
443
|
+
if (node.nodes?.length || node.childNodeIds?.length) return;
|
|
444
|
+
contentElement.textContent = node.textContent;
|
|
445
|
+
}
|
|
446
|
+
|
|
236
447
|
/**
|
|
237
448
|
* si le noeud à une propriété innerHTML, on l'ajout ay innerHTML de l'élément html en cours de création
|
|
238
449
|
*/
|
|
@@ -252,4 +463,201 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
252
463
|
contentElement.innerHTML += node.innerHTML;
|
|
253
464
|
}
|
|
254
465
|
}
|
|
466
|
+
|
|
467
|
+
// ——— Ops incrémentales (chemin à côté du descripteur entier) ———
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Ajoute ou remplace un nœud par `nodeId` (requis).
|
|
471
|
+
* Rendu chirurgical : les frères DOM sont préservés.
|
|
472
|
+
*/
|
|
473
|
+
upsertNode(node: SDUINode, parentId?: string, index?: number): void {
|
|
474
|
+
if (!node.nodeId) {
|
|
475
|
+
console.warn('sonic-sdui.upsertNode : "nodeId" est requis');
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
this.beginIncremental();
|
|
479
|
+
const id = node.nodeId;
|
|
480
|
+
if (!this.sduiDescriptor.nodeById) this.sduiDescriptor.nodeById = {};
|
|
481
|
+
this.sduiDescriptor.nodeById[id] = node;
|
|
482
|
+
this.nodeIndex.set(id, node);
|
|
483
|
+
|
|
484
|
+
if (parentId) {
|
|
485
|
+
let parent =
|
|
486
|
+
this.nodeIndex.get(parentId) || this.sduiDescriptor.nodeById[parentId];
|
|
487
|
+
if (!parent) {
|
|
488
|
+
parent = {
|
|
489
|
+
nodeId: parentId,
|
|
490
|
+
tagName: "div",
|
|
491
|
+
childNodeIds: [],
|
|
492
|
+
};
|
|
493
|
+
this.sduiDescriptor.nodeById[parentId] = parent;
|
|
494
|
+
this.nodeIndex.set(parentId, parent);
|
|
495
|
+
if (!this.sduiDescriptor.nodes) this.sduiDescriptor.nodes = [];
|
|
496
|
+
if (!this.sduiDescriptor.nodes.some((n) => n.nodeId === parentId)) {
|
|
497
|
+
this.sduiDescriptor.nodes.push(parent);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (!parent.childNodeIds) parent.childNodeIds = [];
|
|
501
|
+
const prev = parent.childNodeIds.indexOf(id);
|
|
502
|
+
if (prev >= 0) parent.childNodeIds.splice(prev, 1);
|
|
503
|
+
const at =
|
|
504
|
+
index != null
|
|
505
|
+
? Math.max(0, Math.min(index, parent.childNodeIds.length))
|
|
506
|
+
: parent.childNodeIds.length;
|
|
507
|
+
parent.childNodeIds.splice(at, 0, id);
|
|
508
|
+
} else {
|
|
509
|
+
if (!this.sduiDescriptor.nodes) this.sduiDescriptor.nodes = [];
|
|
510
|
+
const roots = this.sduiDescriptor.nodes;
|
|
511
|
+
const ri = roots.findIndex((n) => n.nodeId === id);
|
|
512
|
+
if (ri >= 0) roots[ri] = node;
|
|
513
|
+
else if (index != null) {
|
|
514
|
+
roots.splice(Math.max(0, Math.min(index, roots.length)), 0, node);
|
|
515
|
+
} else roots.push(node);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
this.rebuildNodeIndex();
|
|
519
|
+
this.domUpsertNode(node, parentId, index);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Supprime un nœud du registre et du DOM (frères préservés). */
|
|
523
|
+
removeNode(nodeId: string): void {
|
|
524
|
+
this.beginIncremental();
|
|
525
|
+
delete this.sduiDescriptor.nodeById?.[nodeId];
|
|
526
|
+
this.nodeIndex.delete(nodeId);
|
|
527
|
+
if (this.sduiDescriptor.nodes) {
|
|
528
|
+
this.sduiDescriptor.nodes = this.sduiDescriptor.nodes.filter(
|
|
529
|
+
(n) => n.nodeId !== nodeId
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
if (this.sduiDescriptor.nodeById) {
|
|
533
|
+
for (const n of Object.values(this.sduiDescriptor.nodeById)) {
|
|
534
|
+
if (n.childNodeIds) {
|
|
535
|
+
n.childNodeIds = n.childNodeIds.filter((id) => id !== nodeId);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
this.findDomNode(nodeId)?.remove();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Met à jour une donnée publisher par chemin (`provider.cle.imbrique` ou relatif
|
|
544
|
+
* au `dataProvider` du composant si le premier segment ne matche pas).
|
|
545
|
+
*/
|
|
546
|
+
setData(path: string, value: unknown): void {
|
|
547
|
+
const direct = getPublisherFromPath(path);
|
|
548
|
+
if (direct) {
|
|
549
|
+
direct.set(value);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (this.dataProvider) {
|
|
553
|
+
const nested = getPublisherFromPath(`${this.dataProvider}.${path}`);
|
|
554
|
+
if (nested) {
|
|
555
|
+
nested.set(value);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
console.warn(`sonic-sdui.setData : chemin introuvable "${path}"`);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/** Applique une op (méthode ou entrée de flux `opsDataProvider`). */
|
|
563
|
+
applyOp(op: SDUIOp): void {
|
|
564
|
+
switch (op.op) {
|
|
565
|
+
case "upsertNode":
|
|
566
|
+
this.upsertNode(op.node, op.parentId, op.index);
|
|
567
|
+
break;
|
|
568
|
+
case "removeNode":
|
|
569
|
+
this.removeNode(op.nodeId);
|
|
570
|
+
break;
|
|
571
|
+
case "setData":
|
|
572
|
+
this.setData(op.path, op.value);
|
|
573
|
+
break;
|
|
574
|
+
default:
|
|
575
|
+
console.warn("sonic-sdui.applyOp : op inconnue", op);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** Quitte le mode incrémental ; le prochain `props` refait un rendu complet. */
|
|
580
|
+
endIncremental(): void {
|
|
581
|
+
this.incrementalActive = false;
|
|
582
|
+
this.incrementalPartial = false;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private beginIncremental() {
|
|
586
|
+
if (this.incrementalActive) return;
|
|
587
|
+
this.sduiDescriptor = structuredClone(this.sduiDescriptor || {});
|
|
588
|
+
if (!this.sduiDescriptor.nodeById) this.sduiDescriptor.nodeById = {};
|
|
589
|
+
if (!this.sduiDescriptor.nodes) this.sduiDescriptor.nodes = [];
|
|
590
|
+
// Fusion library défaut si absente (évite un rebuild complet)
|
|
591
|
+
if (!this.sduiDescriptor.library) {
|
|
592
|
+
const library: Record<string, SDUINode> = {};
|
|
593
|
+
Object.assign(library, default_library);
|
|
594
|
+
this.sduiDescriptor.library = library;
|
|
595
|
+
}
|
|
596
|
+
this.incrementalActive = true;
|
|
597
|
+
this.incrementalPartial = true;
|
|
598
|
+
this.rebuildNodeIndex();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private findDomNode(nodeId: string): HTMLElement | null {
|
|
602
|
+
const escaped =
|
|
603
|
+
typeof CSS !== "undefined" && CSS.escape
|
|
604
|
+
? CSS.escape(nodeId)
|
|
605
|
+
: nodeId.replace(/"/g, '\\"');
|
|
606
|
+
// Placeholders portent aussi SDUI_NODE_ID_ATTR
|
|
607
|
+
return this.querySelector(
|
|
608
|
+
`[${SDUI_NODE_ID_ATTR}="${escaped}"]`
|
|
609
|
+
) as HTMLElement | null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private domUpsertNode(
|
|
613
|
+
node: SDUINode,
|
|
614
|
+
parentId?: string,
|
|
615
|
+
index?: number
|
|
616
|
+
) {
|
|
617
|
+
const rendered = this.parseChild(node, `upsert:${node.nodeId}`);
|
|
618
|
+
if (!rendered) return;
|
|
619
|
+
const existing = this.findDomNode(node.nodeId!);
|
|
620
|
+
if (existing && existing !== rendered) {
|
|
621
|
+
existing.replaceWith(rendered);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
if (existing === rendered) return;
|
|
625
|
+
|
|
626
|
+
if (parentId) {
|
|
627
|
+
const parentEl = this.findDomNode(parentId);
|
|
628
|
+
if (parentEl) {
|
|
629
|
+
const kids = Array.from(parentEl.children);
|
|
630
|
+
if (index != null && index < kids.length) {
|
|
631
|
+
parentEl.insertBefore(rendered, kids[index]);
|
|
632
|
+
} else parentEl.appendChild(rendered);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const toast = this.querySelector("sonic-toast-message-subscriber");
|
|
638
|
+
if (toast) this.insertBefore(rendered, toast);
|
|
639
|
+
else this.appendChild(rendered);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
private bindOpsDataProvider() {
|
|
643
|
+
this.opsUnsub?.();
|
|
644
|
+
this.opsUnsub = undefined;
|
|
645
|
+
const id = this.opsDataProvider;
|
|
646
|
+
if (!id) return;
|
|
647
|
+
const pub = getPublisherFromPath(id);
|
|
648
|
+
if (!pub) return;
|
|
649
|
+
const handler = (payload: unknown) => {
|
|
650
|
+
if (payload == null) return;
|
|
651
|
+
const list = Array.isArray(payload) ? payload : [payload];
|
|
652
|
+
for (const item of list) {
|
|
653
|
+
if (item && typeof item === "object" && "op" in (item as object)) {
|
|
654
|
+
this.applyOp(item as SDUIOp);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
pub.onAssign(handler);
|
|
659
|
+
this.opsUnsub = () => pub.offAssign(handler);
|
|
660
|
+
const current = pub.get();
|
|
661
|
+
if (current != null) handler(current);
|
|
662
|
+
}
|
|
255
663
|
}
|
|
@@ -3,18 +3,56 @@ export type SDUIDescriptor = {
|
|
|
3
3
|
css?: Array<string>;
|
|
4
4
|
library?: Record<string, SDUINode>;
|
|
5
5
|
nodes?: Array<SDUINode>;
|
|
6
|
+
/**
|
|
7
|
+
* Registre plat de nœuds indexés par `nodeId` (opt-in).
|
|
8
|
+
* Permet de référencer des enfants via `childNodeIds` avant qu'ils n'arrivent
|
|
9
|
+
* (rendu partiel / ops incrémentales).
|
|
10
|
+
*/
|
|
11
|
+
nodeById?: Record<string, SDUINode>;
|
|
6
12
|
};
|
|
13
|
+
/** Action déclarative sur un nœud — émet l'événement `sdui-action` au clic. */
|
|
14
|
+
export type SDUINodeAction = {
|
|
15
|
+
name: string;
|
|
16
|
+
context?: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Detail de l'événement CustomEvent `sdui-action`. */
|
|
20
|
+
export type SDUIActionDetail = {
|
|
21
|
+
name: string;
|
|
22
|
+
context: Record<string, unknown>;
|
|
23
|
+
/** Renseigné plus tard si le nœud porte un `nodeId` (ops incrémentales). */
|
|
24
|
+
sourceNodeId?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
7
27
|
export type SDUINode = {
|
|
8
28
|
libraryKey?: string;
|
|
9
29
|
markup?: string;
|
|
10
30
|
tagName?: string;
|
|
11
31
|
attributes?: Record<string, string>;
|
|
12
32
|
nodes?: Array<SDUINode>;
|
|
33
|
+
/** Texte plain (pas de parse HTML). Autorisé en profile="safe". Ignoré si `nodes` est non vide. */
|
|
34
|
+
textContent?: string;
|
|
13
35
|
innerHTML?: string;
|
|
14
36
|
prefix?: string;
|
|
15
37
|
suffix?: string;
|
|
16
38
|
contentElementSelector?: string;
|
|
17
39
|
parentElementSelector?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Action déclarative opt-in. Au clic, émet `sdui-action`
|
|
42
|
+
* (bubbles + composed) avec `{ name, context, sourceNodeId? }`.
|
|
43
|
+
* N'interfère pas avec sonic-submit / POST formulaire.
|
|
44
|
+
*/
|
|
45
|
+
action?: SDUINodeAction;
|
|
46
|
+
/**
|
|
47
|
+
* Identifiant logique de nœud (pas l'attribut DOM `id`).
|
|
48
|
+
* Utilisé dans `sdui-action.detail.sourceNodeId` ; ops incrémentales plus tard.
|
|
49
|
+
*/
|
|
50
|
+
nodeId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Enfants référencés par `nodeId` (registre `descriptor.nodeById` + index du tree).
|
|
53
|
+
* Si un id est absent : placeholder en mode partial, sinon warning + skip.
|
|
54
|
+
*/
|
|
55
|
+
childNodeIds?: string[];
|
|
18
56
|
};
|
|
19
57
|
|
|
20
58
|
export type SDUITransformDescription = {
|
|
@@ -32,3 +70,14 @@ export type SDUITransformAction = {
|
|
|
32
70
|
ui?: SDUINode;
|
|
33
71
|
};
|
|
34
72
|
export type SDUITransformList = Array<{parent: SDUINode; child: SDUINode; index: number}>;
|
|
73
|
+
|
|
74
|
+
/** Opération incrémentale (méthodes publiques ou flux opsDataProvider). */
|
|
75
|
+
export type SDUIOp =
|
|
76
|
+
| {
|
|
77
|
+
op: "upsertNode";
|
|
78
|
+
node: SDUINode;
|
|
79
|
+
parentId?: string;
|
|
80
|
+
index?: number;
|
|
81
|
+
}
|
|
82
|
+
| { op: "removeNode"; nodeId: string }
|
|
83
|
+
| { op: "setData"; path: string; value: unknown };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/core/utils/route.ts
CHANGED
|
File without changes
|