@supersoniks/concorde 4.4.2 → 4.5.1

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.
Files changed (137) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-core.bundle.js +670 -584
  3. package/concorde-core.es.js +9729 -7104
  4. package/dist/concorde-core.bundle.js +670 -584
  5. package/dist/concorde-core.es.js +9729 -7104
  6. package/package.json +7 -4
  7. package/src/core/components/functional/fetch/fetch.md +0 -0
  8. package/src/core/components/ui/_css/scroll.ts +0 -0
  9. package/src/core/components/ui/_css/size.ts +0 -0
  10. package/src/core/components/ui/alert/alert.ts +0 -0
  11. package/src/core/components/ui/button/button.ts +0 -0
  12. package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
  13. package/src/core/components/ui/divider/divider.ts +0 -0
  14. package/src/core/components/ui/menu/menu.md +0 -0
  15. package/src/core/components/ui/modal/modal-close.ts +0 -0
  16. package/src/core/components/ui/modal/modal-utils.ts +46 -0
  17. package/src/core/components/ui/modal/modal.md +0 -0
  18. package/src/core/components/ui/modal/modal.ts +33 -8
  19. package/src/core/components/ui/table/table-caption.ts +0 -0
  20. package/src/core/decorators/subscriber/onAssign.ts +4 -4
  21. package/src/core/utils/dataProviderKey.spec.ts +8 -0
  22. package/src/core/utils/dataProviderKey.ts +31 -12
  23. package/src/core/utils/route.ts +0 -0
  24. package/src/docs/code.ts +0 -0
  25. package/src/tsconfig.json +9 -0
  26. package/src/tsconfig.tsbuildinfo +1 -1
  27. package/vite/config.js +52 -34
  28. package/vite.config.mts +14 -12
  29. package/docs/assets/index-CW8cIYT9.js +0 -4949
  30. package/docs/assets/index-DZtxIZCW.css +0 -1
  31. package/docs/css/docs.css +0 -0
  32. package/docs/fonts/ClashGrotesk-Bold.eot +0 -0
  33. package/docs/fonts/ClashGrotesk-Bold.ttf +0 -0
  34. package/docs/fonts/ClashGrotesk-Bold.woff +0 -0
  35. package/docs/fonts/ClashGrotesk-Bold.woff2 +0 -0
  36. package/docs/fonts/ClashGrotesk-Extralight.eot +0 -0
  37. package/docs/fonts/ClashGrotesk-Extralight.ttf +0 -0
  38. package/docs/fonts/ClashGrotesk-Extralight.woff +0 -0
  39. package/docs/fonts/ClashGrotesk-Extralight.woff2 +0 -0
  40. package/docs/fonts/ClashGrotesk-Light.eot +0 -0
  41. package/docs/fonts/ClashGrotesk-Light.ttf +0 -0
  42. package/docs/fonts/ClashGrotesk-Light.woff +0 -0
  43. package/docs/fonts/ClashGrotesk-Light.woff2 +0 -0
  44. package/docs/fonts/ClashGrotesk-Medium.eot +0 -0
  45. package/docs/fonts/ClashGrotesk-Medium.ttf +0 -0
  46. package/docs/fonts/ClashGrotesk-Medium.woff +0 -0
  47. package/docs/fonts/ClashGrotesk-Medium.woff2 +0 -0
  48. package/docs/fonts/ClashGrotesk-Regular.eot +0 -0
  49. package/docs/fonts/ClashGrotesk-Regular.ttf +0 -0
  50. package/docs/fonts/ClashGrotesk-Regular.woff +0 -0
  51. package/docs/fonts/ClashGrotesk-Regular.woff2 +0 -0
  52. package/docs/fonts/ClashGrotesk-Semibold.eot +0 -0
  53. package/docs/fonts/ClashGrotesk-Semibold.ttf +0 -0
  54. package/docs/fonts/ClashGrotesk-Semibold.woff +0 -0
  55. package/docs/fonts/ClashGrotesk-Semibold.woff2 +0 -0
  56. package/docs/fonts/ClashGrotesk-Variable.eot +0 -0
  57. package/docs/fonts/ClashGrotesk-Variable.ttf +0 -0
  58. package/docs/fonts/ClashGrotesk-Variable.woff +0 -0
  59. package/docs/fonts/ClashGrotesk-Variable.woff2 +0 -0
  60. package/docs/img/concorde-icon.svg +0 -5
  61. package/docs/img/concorde-logo.svg +0 -1
  62. package/docs/img/concorde.png +0 -0
  63. package/docs/img/concorde_def.png +0 -0
  64. package/docs/img/concorde_seuil.png.webp +0 -0
  65. package/docs/img/concorde_seuil_invert.png +0 -0
  66. package/docs/img/paul_metrand.jpg +0 -0
  67. package/docs/img/paul_metrand_xs.jpg +0 -0
  68. package/docs/index.html +0 -93
  69. package/docs/src/core/components/functional/date/date.md +0 -290
  70. package/docs/src/core/components/functional/fetch/fetch.md +0 -123
  71. package/docs/src/core/components/functional/if/if.md +0 -16
  72. package/docs/src/core/components/functional/list/list.md +0 -199
  73. package/docs/src/core/components/functional/mix/mix.md +0 -41
  74. package/docs/src/core/components/functional/queue/queue.md +0 -87
  75. package/docs/src/core/components/functional/router/router.md +0 -129
  76. package/docs/src/core/components/functional/sdui/default-library.json +0 -108
  77. package/docs/src/core/components/functional/sdui/example.json +0 -99
  78. package/docs/src/core/components/functional/sdui/sdui.md +0 -356
  79. package/docs/src/core/components/functional/states/states.md +0 -87
  80. package/docs/src/core/components/functional/submit/submit.md +0 -83
  81. package/docs/src/core/components/functional/subscriber/subscriber.md +0 -91
  82. package/docs/src/core/components/functional/value/value.md +0 -35
  83. package/docs/src/core/components/ui/alert/alert.md +0 -121
  84. package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -0
  85. package/docs/src/core/components/ui/badge/badge.md +0 -127
  86. package/docs/src/core/components/ui/button/button.md +0 -182
  87. package/docs/src/core/components/ui/captcha/captcha.md +0 -24
  88. package/docs/src/core/components/ui/card/card.md +0 -97
  89. package/docs/src/core/components/ui/divider/divider.md +0 -35
  90. package/docs/src/core/components/ui/form/checkbox/checkbox.md +0 -104
  91. package/docs/src/core/components/ui/form/fieldset/fieldset.md +0 -129
  92. package/docs/src/core/components/ui/form/form-actions/form-actions.md +0 -77
  93. package/docs/src/core/components/ui/form/form-layout/form-layout.md +0 -44
  94. package/docs/src/core/components/ui/form/input/input.md +0 -167
  95. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +0 -131
  96. package/docs/src/core/components/ui/form/radio/radio.md +0 -84
  97. package/docs/src/core/components/ui/form/select/select.md +0 -97
  98. package/docs/src/core/components/ui/form/switch/switch.md +0 -84
  99. package/docs/src/core/components/ui/form/textarea/textarea.md +0 -65
  100. package/docs/src/core/components/ui/group/group.md +0 -75
  101. package/docs/src/core/components/ui/icon/icon.md +0 -125
  102. package/docs/src/core/components/ui/icon/icons.json +0 -1
  103. package/docs/src/core/components/ui/image/image.md +0 -107
  104. package/docs/src/core/components/ui/link/link.md +0 -43
  105. package/docs/src/core/components/ui/loader/loader.md +0 -67
  106. package/docs/src/core/components/ui/menu/menu.md +0 -329
  107. package/docs/src/core/components/ui/modal/modal.md +0 -119
  108. package/docs/src/core/components/ui/pop/pop.md +0 -96
  109. package/docs/src/core/components/ui/progress/progress.md +0 -63
  110. package/docs/src/core/components/ui/table/table.md +0 -455
  111. package/docs/src/core/components/ui/toast/toast.md +0 -166
  112. package/docs/src/core/components/ui/tooltip/tooltip.md +0 -82
  113. package/docs/src/docs/_core-concept/overview.md +0 -57
  114. package/docs/src/docs/_core-concept/subscriber.md +0 -76
  115. package/docs/src/docs/_decorators/ancestor-attribute.md +0 -78
  116. package/docs/src/docs/_decorators/auto-subscribe.md +0 -199
  117. package/docs/src/docs/_decorators/bind.md +0 -164
  118. package/docs/src/docs/_decorators/get.md +0 -65
  119. package/docs/src/docs/_decorators/on-assign.md +0 -336
  120. package/docs/src/docs/_decorators/publish.md +0 -54
  121. package/docs/src/docs/_decorators/subscribe.md +0 -36
  122. package/docs/src/docs/_decorators/wait-for-ancestors.md +0 -160
  123. package/docs/src/docs/_getting-started/concorde-outside.md +0 -143
  124. package/docs/src/docs/_getting-started/create-a-component.md +0 -137
  125. package/docs/src/docs/_getting-started/my-first-subscriber.md +0 -174
  126. package/docs/src/docs/_getting-started/pubsub.md +0 -150
  127. package/docs/src/docs/_getting-started/start.md +0 -39
  128. package/docs/src/docs/_getting-started/theming.md +0 -91
  129. package/docs/src/docs/_misc/dataProviderKey.md +0 -135
  130. package/docs/src/docs/_misc/endpoint.md +0 -42
  131. package/docs/src/docs/_misc/templates-demo.md +0 -19
  132. package/docs/src/docs/search/docs-search.json +0 -5197
  133. package/docs/src/tag-list.json +0 -1
  134. package/docs/src/tsconfig-model.json +0 -23
  135. package/docs/src/tsconfig.json +0 -987
  136. package/docs/svg/regular/plane.svg +0 -1
  137. package/docs/svg/solid/plane.svg +0 -1
@@ -1,65 +0,0 @@
1
- # @get
2
-
3
- Loads data through [`API.getDetailed`](../../core/utils/api.ts). The decorated property is **`ApiGetResult<T> | null`**: `request`, `response` (or `null` for `dataProvider(...)` resolution without HTTP), and typed `result`.
4
-
5
- Pass an [`Endpoint<T>`](#docs/_misc/endpoint.md/endpoint) as the first argument. Import `get` and `ApiGetResult` from `@supersoniks/concorde/decorators`, and `Endpoint` from `@supersoniks/concorde/utils/endpoint`.
6
-
7
- ## Configuration
8
-
9
- - **Default:** `HTML.getApiConfiguration(host)` (ancestor `serviceURL`, etc.).
10
- - **Second argument:** `DataProviderKey<APIConfiguration>` — config is read from the publisher at the resolved path; internal mutations trigger another GET.
11
-
12
- ## When the GET runs again
13
-
14
- - A referenced Lit property changes (endpoint path and/or config key contains `${...}`).
15
- - `set` on the active configuration publisher (`onInternalMutation`).
16
-
17
- ## Import
18
-
19
- <sonic-code language="typescript">
20
- <template>
21
- import { get, type ApiGetResult } from "@supersoniks/concorde/decorators";
22
- import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
23
- import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
24
- </template>
25
- </sonic-code>
26
-
27
- ## Minimal example
28
-
29
- Same demo service as [`sonic-queue`](../../core/components/functional/queue/queue.demo.ts) (`geo.api.gouv.fr`). Publisher setup lives in `decorators-demo-geo.ts` and `decorators-demo-subscribe-publish-get-demos.ts`.
30
-
31
- <sonic-code language="typescript">
32
- <template>
33
- @get(new Endpoint<User>("users/${userId}"))
34
- @state()
35
- payload: ApiGetResult<User> | null = null;
36
- </template>
37
- </sonic-code>
38
-
39
- ## Live demos
40
-
41
- <sonic-code>
42
- <template>
43
- <demo-api-get></demo-api-get>
44
- </template>
45
- </sonic-code>
46
-
47
- Dynamic config and endpoint path (`demo-api-get-configuration-key` in doc sources):
48
-
49
- <sonic-code>
50
- <template>
51
- <demo-api-get-configuration-key></demo-api-get-configuration-key>
52
- </template>
53
- </sonic-code>
54
-
55
- Scoped `@get` with `@publish` / `@subscribe` on the payload (see [@publish](#docs/_decorators/publish.md/publish) and [@subscribe](#docs/_decorators/subscribe.md/subscribe)) — wrap under an ancestor with `serviceURL="https://geo.api.gouv.fr/"`:
56
-
57
- <sonic-code>
58
- <template>
59
- <div serviceURL="https://geo.api.gouv.fr/">
60
- <demo-api-get-publish-subscribe></demo-api-get-publish-subscribe>
61
- </div>
62
- </template>
63
- </sonic-code>
64
-
65
- Stale responses are ignored if the path or generation changed before the request finished.
@@ -1,336 +0,0 @@
1
- # @onAssign
2
-
3
- The `@onAssign` decorator allows you to execute a method when one or more publishers are updated. The method is called only when all specified publishers have been assigned values.
4
-
5
- ## Principle
6
-
7
- This decorator subscribes to one or more publishers via the `PublisherManager`. When all specified publishers have been assigned values (via `set`), the decorated method is called with all the values as arguments.
8
-
9
- This is particularly useful when you need to wait for multiple data sources to be ready before executing logic.
10
-
11
- ## Usage
12
-
13
- ### Import
14
-
15
- <sonic-code language="typescript">
16
- <template>
17
- import { onAssign } from "@supersoniks/concorde/decorators";
18
- </template>
19
- </sonic-code>
20
-
21
- ### Basic example
22
-
23
-
24
- <sonic-code language="typescript">
25
- <template>
26
- //...
27
- @customElement("demo-on-assign")
28
- export class DemoOnAssign extends LitElement {
29
- static styles = [tailwind];
30
- //
31
- @state() userWithSettings: any = null;
32
- @state() isReady: boolean = false;
33
- @state() lastUpdate: string = "";
34
- //
35
- @onAssign("demoUser", "demoUserSettings")
36
- handleDataReady(user: any, settings: any) {
37
- this.isReady = Object.keys(user).length > 0 && Object.keys(settings).length > 0;
38
- this.userWithSettings = { ...user, ...settings };
39
- this.lastUpdate = new Date().toLocaleTimeString();
40
- this.requestUpdate();
41
- }
42
- //
43
- render() {
44
- const { name, email, theme, language } = this.userWithSettings;
45
- return //...
46
- }
47
- //
48
- updateData() {
49
- const user = PublisherManager.get("demoUser");
50
- const userSettings = PublisherManager.get("demoUserSettings");
51
- const userNumber = Math.floor(Math.random() * 100);
52
- user.set({
53
- name: `User n°${userNumber}`,
54
- email: `user-${userNumber}@example.com`,
55
- });
56
- //
57
- userSettings.set({
58
- theme: ["light", "dark", "auto"][Math.floor(Math.random() * 3)],
59
- language: ["en", "fr", "es"][Math.floor(Math.random() * 3)],
60
- });
61
- }
62
- }
63
- </template>
64
- </sonic-code>
65
-
66
- <sonic-code>
67
- <template>
68
- <demo-on-assign></demo-on-assign>
69
- </template>
70
- </sonic-code>
71
-
72
-
73
- ### Example with nested paths
74
-
75
- <sonic-code language="typescript">
76
- <template>
77
- @customElement("product-view")
78
- export class ProductView extends LitElement {
79
- product: any = null;
80
- inventory: any = null;
81
- //
82
- @onAssign("store.product", "store.inventory")
83
- handleProductData(product: any, inventory: any) {
84
- this.product = product;
85
- this.inventory = inventory;
86
- this.requestUpdate();
87
- }
88
- //
89
- render() {
90
- if (!this.product) return html`<div>Loading...</div>`;
91
- //
92
- const stock = this.inventory[this.product.id] || 0;
93
- return html`
94
- <div>
95
- <h2>${this.product.name}</h2>
96
- <p>Price: ${this.product.price}€</p>
97
- <p>Stock: ${stock}</p>
98
- </div>
99
- `;
100
- }
101
- }
102
- </template>
103
- </sonic-code>
104
-
105
- ## Path syntax
106
-
107
- The path uses dot notation to navigate through the publisher structure:
108
-
109
- - **First segment**: dataProvider identifier (e.g., `"userData"`)
110
- - **Following segments**: nested properties (e.g., `"store.product"`)
111
- - **Array access**: use numeric index (e.g., `"data.items.0"`)
112
-
113
- ### Dynamic path driven by class properties
114
-
115
- You can now build the paths dynamically by referencing the host class properties inside the strings passed to `@onAssign`. Two placeholder syntaxes are supported:
116
-
117
- - `` ${myProperty} `` or `` ${this.myProperty} ``
118
- - `` {$myProperty} ``
119
-
120
- Each placeholder is replaced at runtime with the current value of the corresponding property. `@onAssign` automatically watches those properties and:
121
-
122
- - re-evaluates the final paths when one of them changes,
123
- - removes the previous subscriptions before attaching the new ones,
124
- - observe the changes inside `willUpdate(changedProperties)` so nothing touches the getters/setters.
125
-
126
-
127
- <sonic-code language="typescript">
128
- <template>
129
- @customElement("demo-on-assign-dynamic")
130
- export class DemoOnAssignDynamic extends LitElement {
131
- static styles = [tailwind];
132
- //
133
- @property({ type: String })
134
- dataProvider: "demoUsers" | "demoUsersAlt" = "demoUsers";
135
- //
136
- @property({ type: Number })
137
- userIndex: number = 0;
138
- //
139
- @state() user: any = null;
140
- @state() userSettings: any = null;
141
- //
142
- @onAssign("${dataProvider}.${userIndex}", "${dataProvider}Settings.${userIndex}")
143
- handleUserDataReady(user: any, settings: any) {
144
- this.user = user;
145
- this.userSettings = settings;
146
- }
147
- //
148
- updateUserIndex(e: Event) {
149
- this.userIndex = parseInt((e.target as HTMLInputElement).value);
150
- }
151
- //
152
- updateDataProvider(e: Event) {
153
- this.dataProvider = (e.target as HTMLSelectElement).value as
154
- | "demoUsers"
155
- | "demoUsersAlt";
156
- }
157
- //
158
- updateCurrentUserData() {
159
- const usersPublisher = PublisherManager.get(this.dataProvider);
160
- const settingsPublisher = PublisherManager.get(
161
- `${this.dataProvider}Settings`
162
- );
163
- const userPublisher = Objects.traverse(
164
- usersPublisher,
165
- [String(this.userIndex)]
166
- ) as PublisherProxy;
167
- const settingPublisher = Objects.traverse(
168
- settingsPublisher,
169
- [String(this.userIndex)]
170
- ) as PublisherProxy;
171
- //
172
- if (userPublisher && settingPublisher) {
173
- // Générer de nouvelles données aléatoires
174
- const randomNames = [
175
- { firstName: "Alice", lastName: "Wonder" },
176
- { firstName: "Bob", lastName: "Builder" },
177
- { firstName: "Charlie", lastName: "Chaplin" },
178
- ];
179
- const randomThemes = ["light", "dark", "auto"];
180
- const randomLanguages = ["en", "fr", "es"];
181
- //
182
- const randomName =
183
- randomNames[Math.floor(Math.random() * randomNames.length)];
184
- const randomEmail = `${randomName.firstName.toLowerCase()}.${randomName.lastName.toLowerCase()}@example.com`;
185
- const randomTheme =
186
- randomThemes[Math.floor(Math.random() * randomThemes.length)];
187
- const randomLanguage =
188
- randomLanguages[Math.floor(Math.random() * randomLanguages.length)];
189
- //
190
- // Mettre à jour l'utilisateur directement
191
- const currentUser = userPublisher.get() || {};
192
- userPublisher.set({
193
- ...currentUser,
194
- firstName: randomName.firstName,
195
- lastName: randomName.lastName,
196
- email: randomEmail,
197
- });
198
- //
199
- // Mettre à jour les settings directement
200
- settingPublisher.set({
201
- theme: randomTheme,
202
- language: randomLanguage,
203
- });
204
- }
205
- }
206
- //
207
- render() {
208
- return html`
209
- <div class="flex flex-col gap-2">
210
- <sonic-select label="Users set" @change=${this.updateDataProvider}>
211
- <option value="demoUsers">First set of users</option>
212
- <option value="demoUsersAlt">Second set of users</option>
213
- </sonic-select>
214
- <sonic-input
215
- type="number"
216
- .value=${this.userIndex}
217
- @input=${this.updateUserIndex}
218
- min="0"
219
- max="9"
220
- label="Index"
221
- class="block"
222
- >
223
- </sonic-input>
224
- <sonic-button @click=${this.updateCurrentUserData}
225
- >Update current user data</sonic-button
226
- >
227
- <div class="flex flex-col gap-2 border p-2">
228
- <div>
229
- <sonic-icon name="user" library="heroicons"></sonic-icon>
230
- ${this.user?.firstName} ${this.user?.lastName}
231
- </div>
232
- <div>
233
- <sonic-icon name="envelope" library="heroicons"></sonic-icon>
234
- ${this.user?.email}
235
- </div>
236
- <div>
237
- Theme: ${this.userSettings?.theme} | Language:
238
- ${this.userSettings?.language}
239
- </div>
240
- </div>
241
- </div>
242
- `;
243
- }
244
- }
245
- </template>
246
- </sonic-code>
247
-
248
- <sonic-code>
249
- <template>
250
- <demo-on-assign-dynamic></demo-on-assign-dynamic>
251
- </template>
252
- </sonic-code>
253
-
254
- > ⚠️ Use classic string literals: `@onAssign("${dataProvider}.${profileId}", "settings.${profileId}")`. Do **not** use template literals (backticks), otherwise JavaScript would try to interpolate the value immediately.
255
-
256
- Additional constraints:
257
-
258
- - The hosting class must expose a `willUpdate(changedProperties?: Map<PropertyKey, unknown>)` method (LitElement already provides it) so that `@onAssign` can listen to dependency changes.
259
- - Dependencies need to be reactive (e.g. `@property()` on LitElement) or you must call `this.requestUpdate("myProp")` manually after changing them, otherwise `willUpdate` will never be notified.
260
- - If you use nested expressions like `${user.id}`, the first segment (`user`) is the one being observed: you need to reassign `this.user` (e.g. with a new object) so that the binding can detect the change.
261
-
262
- ## Behavior
263
-
264
- - The method is called only when **all** specified publishers have been assigned values
265
- - The method receives the values of all publishers as arguments, in the same order as specified
266
- - Subscription happens at the time of `connectedCallback`
267
- - Unsubscription happens automatically at the time of `disconnectedCallback`
268
- - If a publisher doesn't exist yet, it will be created with the value `null`
269
- - The method is called with `this` bound to the component instance
270
-
271
- ## Use cases
272
-
273
- This decorator is particularly useful for:
274
-
275
- - **Waiting for multiple data sources** before rendering or executing logic
276
- - **Synchronizing data** from different publishers
277
- - **Initializing components** that depend on multiple data providers
278
- - **Handling complex data dependencies** where you need all data to be ready
279
-
280
- ## Complete example
281
-
282
- <sonic-code language="typescript">
283
- <template>
284
- import { html, LitElement } from "lit";
285
- import { customElement } from "lit/decorators.js";
286
- import { onAssign } from "@supersoniks/concorde/decorators";
287
- import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
288
- //
289
- @customElement("order-summary")
290
- export class OrderSummary extends LitElement {
291
- order: any = null;
292
- customer: any = null;
293
- shipping: any = null;
294
- //
295
- @onAssign("orderData", "customerData", "shippingData")
296
- handleOrderReady(order: any, customer: any, shipping: any) {
297
- this.order = order;
298
- this.customer = customer;
299
- this.shipping = shipping;
300
- this.requestUpdate();
301
- }
302
- //
303
- render() {
304
- if (!this.order || !this.customer || !this.shipping) {
305
- return html`<div>Loading order details...</div>`;
306
- }
307
- //
308
- return html`
309
- <div class="order-summary">
310
- <h2>Order #${this.order.id}</h2>
311
- <p>Customer: ${this.customer.name}</p>
312
- <p>Shipping to: ${this.shipping.address}</p>
313
- <p>Total: ${this.order.total}€</p>
314
- </div>
315
- `;
316
- }
317
- }
318
- // Somewhere in your code, update the publishers:
319
- const orderPub = PublisherManager.get("orderData");
320
- const customerPub = PublisherManager.get("customerData");
321
- const shippingPub = PublisherManager.get("shippingData");
322
- // The method will be called only when all three are set:
323
- orderPub.set({ id: "123", total: 99.99 });
324
- customerPub.set({ name: "John Doe", email: "john@example.com" });
325
- shippingPub.set({ address: "123 Main St" });
326
- // handleOrderReady will be called with all three values
327
- </template>
328
- </sonic-code>
329
-
330
- ## Notes
331
-
332
- - This decorator works with any component that has `connectedCallback` and `disconnectedCallback` methods (such as `LitElement` or components extending `Subscriber`)
333
- - The method is called synchronously when all publishers are ready
334
- - If you need to update the UI, remember to call `this.requestUpdate()` inside the method
335
- - For more information about publishers, see the documentation on [Sharing data](#docs/_getting-started/pubsub.md/pubsub)
336
-
@@ -1,54 +0,0 @@
1
- # @publish
2
-
3
- Write-only binding: assigning to the property publishes to the `DataProviderKey` path. No read subscription (inverse of [@subscribe](#docs/_decorators/subscribe.md/subscribe)).
4
-
5
- Similar to the “reflect” half of [@bind](#docs/_decorators/bind.md/bind) without listening to the publisher.
6
-
7
- ## Import
8
-
9
- <sonic-code language="typescript">
10
- <template>
11
- import { publish } from "@supersoniks/concorde/decorators";
12
- import { sub } from "@supersoniks/concorde/directives";
13
- import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
14
- </template>
15
- </sonic-code>
16
-
17
- ## Example
18
-
19
- <sonic-code language="typescript">
20
- <template>
21
- type PublishDemoData = { email: string; message: string };
22
- const publishDemoKey = new DataProviderKey<PublishDemoData>("publishDemo");
23
- //
24
- @customElement("demo-publish")
25
- export class DemoPublish extends LitElement {
26
- @publish(publishDemoKey.email)
27
- @state()
28
- email = "";
29
- //
30
- @publish(publishDemoKey.message)
31
- @state()
32
- message = "";
33
- //
34
- render() {
35
- return html`
36
- <sonic-input
37
- .value=${this.email}
38
- @input=${(e) => (this.email = (e.target as HTMLInputElement).value)}
39
- label="Email"
40
- ></sonic-input>
41
- <p>${sub("publishDemo.email")}</p>
42
- `;
43
- }
44
- }
45
- </template>
46
- </sonic-code>
47
-
48
- <sonic-code>
49
- <template>
50
- <demo-publish></demo-publish>
51
- </template>
52
- </sonic-code>
53
-
54
- Dynamic paths use the same placeholder rules as `@bind` / `@subscribe`.
@@ -1,36 +0,0 @@
1
- # @subscribe
2
-
3
- Read-only binding: **only** `DataProviderKey<T>` (no legacy string path). No `reflect` option — the publisher updates the property, not the other way around.
4
-
5
- For bidirectional binding or string paths, use [@bind](#docs/_decorators/bind.md/bind).
6
-
7
- ## Import
8
-
9
- <sonic-code language="typescript">
10
- <template>
11
- import { subscribe } from "@supersoniks/concorde/decorators";
12
- import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
13
- //
14
- type Data = { count: number };
15
- const dataKey = new DataProviderKey<Data>("data");
16
- //
17
- @subscribe(dataKey.count)
18
- @state()
19
- count = 0;
20
- </template>
21
- </sonic-code>
22
-
23
- ## Highlights
24
-
25
- - Strict typing: the property type must match `T`.
26
- - Dynamic paths: placeholders in `DataProviderKey`, resolved from the host component’s properties.
27
-
28
- ## Demo
29
-
30
- <sonic-code>
31
- <template>
32
- <demo-subscribe-dynamic></demo-subscribe-dynamic>
33
- </template>
34
- </sonic-code>
35
-
36
- See also [DataProviderKey](#docs/_misc/dataProviderKey.md/dataProviderKey).
@@ -1,160 +0,0 @@
1
- # @awaitConnectedAncestors and @dispatchConnectedEvent
2
-
3
- The `@awaitConnectedAncestors` and `@dispatchConnectedEvent` decorators delay a web component's initialization until its matching ancestors have executed their `connectedCallback`. This is when contextual elements (publisher, dataProvider, etc.) are configured.
4
-
5
- ## Principle
6
-
7
- When a child component attaches to the DOM, its ancestors may not yet be initialized (especially if custom element definitions are loaded asynchronously). The `@awaitConnectedAncestors` decorator delays the component's `connectedCallback` until all ancestors matching the provided CSS selectors have executed their `connectedCallback`.
8
-
9
- The `@dispatchConnectedEvent` decorator allows ancestors to signal they are ready by dispatching the `sonic-connected` event at the end of their `connectedCallback`. The event bubbles, so it can be listened to from anywhere (e.g. `document.addEventListener(CONNECTED, handler)`).
10
-
11
- Ancestors that are not web components (no hyphen in tag name) are considered connected by default and do not need to emit the event.
12
-
13
- ## Usage
14
-
15
- ### Import
16
-
17
- <sonic-code language="typescript">
18
- <template>
19
- import { awaitConnectedAncestors, dispatchConnectedEvent, ancestorAttribute } from "@supersoniks/concorde/decorators";
20
- </template>
21
- </sonic-code>
22
-
23
- ### Basic example
24
-
25
- An ancestor container decorated with `@dispatchConnectedEvent()` signals when it is ready. A child component decorated with `@awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]")` waits for this container to be initialized before initializing itself. Parameters are CSS selectors (`element.matches()`).
26
-
27
- The parent is registered via `customElements.define()` (vanilla JS) rather than `@customElement`, so it can be defined later—e.g. when the user clicks a button. This demonstrates the child waiting until the parent exists.
28
-
29
- <sonic-code language="typescript">
30
- <template>
31
- import { html, LitElement } from "lit";
32
- import { customElement, state } from "lit/decorators.js";
33
- //
34
- // Parent: registered later via customElements.define(), not @customElement
35
- @dispatchConnectedEvent()
36
- export class DemoWaitAncestorContainer extends LitElement {
37
- render() {
38
- return html`<slot></slot>`;
39
- }
40
- }
41
- //
42
- // Child: waits for parent before initializing
43
- @customElement("demo-wait-ancestor-value")
44
- @awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]")
45
- export class DemoWaitAncestorValue extends LitElement {
46
- @ancestorAttribute("dataProvider")
47
- dataProvider: string | null = null;
48
- //
49
- @state() initializedAt: string = "";
50
- //
51
- connectedCallback() {
52
- super.connectedCallback();
53
- this.initializedAt = new Date().toISOString();
54
- }
55
- //
56
- render() {
57
- return html`
58
- <p>DataProvider from ancestor: <strong>${this.dataProvider || "—"}</strong></p>
59
- <p>Initialized at: ${this.initializedAt || "(waiting for parent…)"}</p>
60
- `;
61
- }
62
- }
63
- //
64
- // Demo section: register parent via customElements.define() when user clicks
65
- @customElement("demo-wait-ancestors-section")
66
- export class DemoWaitAncestorsSection extends LitElement {
67
- registerParent() {
68
- if (!customElements.get("demo-wait-ancestor-container")) {
69
- customElements.define("demo-wait-ancestor-container", DemoWaitAncestorContainer);
70
- }
71
- }
72
- render() {
73
- return html`
74
- <sonic-button @click=${this.registerParent}>Register parent component</sonic-button>
75
- <demo-wait-ancestor-container dataProvider="waitAncestorDemo">
76
- <demo-wait-ancestor-value></demo-wait-ancestor-value>
77
- </demo-wait-ancestor-container>
78
- `;
79
- }
80
- }
81
- </template>
82
- </sonic-code>
83
-
84
- <sonic-code>
85
- <template>
86
- <demo-wait-ancestors-section></demo-wait-ancestors-section>
87
- </template>
88
- </sonic-code>
89
-
90
- ### Multiple ancestors
91
-
92
- The child waits for all specified ancestors. Register outer first, then inner — the child initializes only when both are ready.
93
-
94
- <sonic-code>
95
- <template>
96
- <demo-wait-ancestors-multi-section></demo-wait-ancestors-multi-section>
97
- </template>
98
- </sonic-code>
99
-
100
- ### Ancestors already connected
101
-
102
- When the parent is defined at load and already in the DOM, the child initializes immediately (no delay).
103
-
104
- **Static (both in DOM from start):**
105
-
106
- <sonic-code>
107
- <template>
108
- <demo-wait-ancestors-static-section></demo-wait-ancestors-static-section>
109
- </template>
110
- </sonic-code>
111
-
112
- **Dynamic (child added on button click):**
113
-
114
- <sonic-code>
115
- <template>
116
- <demo-wait-ancestors-ready-section></demo-wait-ancestors-ready-section>
117
- </template>
118
- </sonic-code>
119
-
120
- ## CSS selector support
121
-
122
- Parameters are CSS selectors matched via `element.matches()` — e.g. `"sonic-subscriber"`, `"sonic-subscriber[dataProvider]"`, `".my-container"`, or multiple: `"sonic-subscriber", "sonic-sdui"`.
123
-
124
- ## Behavior
125
-
126
- - Ancestor search uses CSS selectors (`element.matches(selector)`) — supports tag names, classes, attributes, combinators, etc.
127
- - Traversal includes shadow roots (parentNode / host)
128
- - Non-web components (no hyphen in tag name) are considered connected by default
129
- - For web component ancestors, it waits for `customElements.whenDefined(tagName)` and the `sonic-connected` event (or a timeout as fallback)
130
- - If no matching ancestor is found, the original `connectedCallback` is called immediately
131
- - Ancestors that do not emit `sonic-connected` trigger the fallback after the timeout (compatibility with existing components)
132
-
133
- ## Use cases
134
-
135
- These decorators are particularly useful for:
136
-
137
- - **sonic-value** inside a **sonic-subscriber**: the value waits for the subscriber to configure its publisher
138
- - **Components inside sonic-sdui**: wait for the SDUI to load and configure its context
139
- - **Any component** depending on context provided by an ancestor custom element
140
-
141
- ## Listening to the connected event
142
-
143
- The `sonic-connected` event bubbles, so you can listen to it from anywhere:
144
-
145
- <sonic-code language="typescript">
146
- <template>
147
- import { CONNECTED } from "@supersoniks/concorde/decorators";
148
- //
149
- someConnectable.addEventListener(CONNECTED, (e) => {
150
- console.log("Component connected:", e.target);
151
- });
152
- </template>
153
- </sonic-code>
154
-
155
- ## Notes
156
-
157
- - These decorators apply only to web components (classes extending `HTMLElement`)
158
- - The fallback timeout ensures compatibility with components that do not yet use `@dispatchConnectedEvent`
159
- - Traversal includes shadow roots
160
- - For a guarantee without relying on the timeout, decorate ancestors (Subscriber, Fetcher, etc.) with `@dispatchConnectedEvent`