@supersoniks/concorde 3.2.8 → 3.3.2
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/build-infos.json +1 -1
- package/concorde-core.bundle.js +229 -229
- package/concorde-core.es.js +2166 -1831
- package/dist/concorde-core.bundle.js +229 -229
- package/dist/concorde-core.es.js +2166 -1831
- package/docs/assets/{index-C0K6xugr.css → index-B669R8JF.css} +1 -1
- package/docs/assets/index-BTo6ly4d.js +4820 -0
- package/docs/index.html +2 -2
- package/docs/src/core/components/functional/fetch/fetch.md +6 -0
- package/docs/src/core/components/ui/menu/menu.md +46 -5
- package/docs/src/core/components/ui/modal/modal.md +0 -4
- package/docs/src/core/components/ui/toast/toast.md +166 -0
- package/docs/src/docs/_misc/ancestor-attribute.md +94 -0
- package/docs/src/docs/_misc/auto-subscribe.md +199 -0
- package/docs/src/docs/_misc/bind.md +362 -0
- package/docs/src/docs/_misc/on-assign.md +336 -0
- package/docs/src/docs/_misc/templates-demo.md +19 -0
- package/docs/src/docs/search/docs-search.json +550 -0
- package/docs/src/tsconfig-model.json +1 -1
- package/docs/src/tsconfig.json +28 -8
- package/package.json +8 -1
- package/src/core/components/functional/queue/queue.demo.ts +8 -11
- package/src/core/components/functional/sdui/sdui.ts +0 -0
- package/src/core/decorators/Subscriber.ts +5 -187
- package/src/core/decorators/subscriber/ancestorAttribute.ts +17 -0
- package/src/core/decorators/subscriber/autoFill.ts +28 -0
- package/src/core/decorators/subscriber/autoSubscribe.ts +54 -0
- package/src/core/decorators/subscriber/bind.ts +305 -0
- package/src/core/decorators/subscriber/common.ts +50 -0
- package/src/core/decorators/subscriber/onAssign.ts +318 -0
- package/src/core/mixins/Fetcher.ts +0 -0
- package/src/core/utils/HTML.ts +0 -0
- package/src/core/utils/PublisherProxy.ts +1 -1
- package/src/core/utils/api.ts +0 -0
- package/src/decorators.ts +9 -2
- package/src/docs/_misc/ancestor-attribute.md +94 -0
- package/src/docs/_misc/auto-subscribe.md +199 -0
- package/src/docs/_misc/bind.md +362 -0
- package/src/docs/_misc/on-assign.md +336 -0
- package/src/docs/_misc/templates-demo.md +19 -0
- package/src/docs/example/decorators-demo.ts +658 -0
- package/src/docs/navigation/navigation.ts +22 -3
- package/src/docs/search/docs-search.json +415 -0
- package/src/docs.ts +4 -0
- package/src/tsconfig-model.json +1 -1
- package/src/tsconfig.json +22 -2
- package/src/tsconfig.tsbuildinfo +1 -1
- package/vite.config.mts +0 -2
- package/docs/assets/index-Dgl1lJQo.js +0 -4861
- package/templates-test.html +0 -32
package/docs/index.html
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<!-- <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
11
11
|
-->
|
|
12
12
|
<script src="https://cdn.jsdelivr.net/npm/marked@13.0.3"></script>
|
|
13
|
-
<script type="module" crossorigin src="./assets/index-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
13
|
+
<script type="module" crossorigin src="./assets/index-BTo6ly4d.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B669R8JF.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body class="px-6 lg:px-20">
|
|
17
17
|
<sonic-theme
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
The **sonic-fetch** component is used to request and store data from an API.
|
|
3
3
|
Fetch extends the mixins Fetcher and [Subscriber](#docs/_core-concept/subscriber.md/subscriber)
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
## Basic usage
|
|
6
10
|
In order to work properly the <b>sonic-fetch</b> component needs at least the following attributes.
|
|
7
11
|
- **serviceURL** : A base service url. This attribute can be inherited from an ancestor.
|
|
@@ -11,6 +15,8 @@ In order to work properly the <b>sonic-fetch</b> component needs at least the fo
|
|
|
11
15
|
- **dataProvider *(Required)*** : An ID that is used as a reference to the object storing the data returned by the API.
|
|
12
16
|
This attribute can be inherited from an ancestor.
|
|
13
17
|
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
<sonic-code>
|
|
15
21
|
<template>
|
|
16
22
|
<sonic-fetch serviceURL="https://reqres.in" endPoint="api/users?page=2" dataProvider="myDataObj"></sonic-fetch>
|
|
@@ -264,6 +264,36 @@
|
|
|
264
264
|
<sonic-icon library="iconoir" name="clock-outline" slot="prefix"></sonic-icon>
|
|
265
265
|
History
|
|
266
266
|
</sonic-menu-item>
|
|
267
|
+
<sonic-menu-item >
|
|
268
|
+
<sonic-icon library="iconoir" name="bag" slot="prefix"></sonic-icon>
|
|
269
|
+
Orders
|
|
270
|
+
</sonic-menu-item> <sonic-menu-item variant="ghost">
|
|
271
|
+
<sonic-icon library="iconoir" name="home" slot="prefix"></sonic-icon>
|
|
272
|
+
Home
|
|
273
|
+
</sonic-menu-item>
|
|
274
|
+
<sonic-menu-item>
|
|
275
|
+
<sonic-icon library="iconoir" name="user" slot="prefix"></sonic-icon>
|
|
276
|
+
Profile
|
|
277
|
+
</sonic-menu-item>
|
|
278
|
+
<sonic-menu-item >
|
|
279
|
+
<sonic-icon library="iconoir" name="clock-outline" slot="prefix"></sonic-icon>
|
|
280
|
+
History
|
|
281
|
+
</sonic-menu-item>
|
|
282
|
+
<sonic-menu-item >
|
|
283
|
+
<sonic-icon library="iconoir" name="bag" slot="prefix"></sonic-icon>
|
|
284
|
+
Orders
|
|
285
|
+
</sonic-menu-item> <sonic-menu-item variant="ghost">
|
|
286
|
+
<sonic-icon library="iconoir" name="home" slot="prefix"></sonic-icon>
|
|
287
|
+
Home
|
|
288
|
+
</sonic-menu-item>
|
|
289
|
+
<sonic-menu-item>
|
|
290
|
+
<sonic-icon library="iconoir" name="user" slot="prefix"></sonic-icon>
|
|
291
|
+
Profile
|
|
292
|
+
</sonic-menu-item>
|
|
293
|
+
<sonic-menu-item >
|
|
294
|
+
<sonic-icon library="iconoir" name="clock-outline" slot="prefix"></sonic-icon>
|
|
295
|
+
History
|
|
296
|
+
</sonic-menu-item>
|
|
267
297
|
<sonic-menu-item >
|
|
268
298
|
<sonic-icon library="iconoir" name="bag" slot="prefix"></sonic-icon>
|
|
269
299
|
Orders
|
|
@@ -276,11 +306,22 @@
|
|
|
276
306
|
<sonic-icon library="iconoir" name="chat-bubble" slot="prefix"></sonic-icon>
|
|
277
307
|
Messages
|
|
278
308
|
</sonic-menu-item>
|
|
279
|
-
<sonic-menu
|
|
280
|
-
<sonic-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
309
|
+
<sonic-menu slot="more">
|
|
310
|
+
<sonic-menu-item >
|
|
311
|
+
<sonic-icon library="iconoir" name="settings" slot="prefix"></sonic-icon>
|
|
312
|
+
Settings
|
|
313
|
+
</sonic-menu-item>
|
|
314
|
+
<sonic-menu-item >
|
|
315
|
+
<sonic-icon library="iconoir" name="chat-bubble" slot="prefix"></sonic-icon>
|
|
316
|
+
Messages
|
|
317
|
+
</sonic-menu-item>
|
|
318
|
+
<sonic-divider></sonic-divider>
|
|
319
|
+
<sonic-menu-item type="danger">
|
|
320
|
+
<sonic-icon library="iconoir" name="log-out" slot="prefix"></sonic-icon>
|
|
321
|
+
Log out
|
|
322
|
+
</sonic-menu-item>
|
|
323
|
+
</sonic-menu>
|
|
324
|
+
|
|
284
325
|
</template>
|
|
285
326
|
</sonic-code>
|
|
286
327
|
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
Simple modal
|
|
7
7
|
</sonic-button>
|
|
8
8
|
<sonic-modal id="modalExample">
|
|
9
|
-
<sonic-modal-close></sonic-modal-close>
|
|
10
9
|
<sonic-modal-title
|
|
11
10
|
>Lorem ipsum dolor sit amet <sonic-badge type="danger" size="sm">+33</sonic-badge></sonic-modal-title
|
|
12
11
|
>
|
|
@@ -31,7 +30,6 @@
|
|
|
31
30
|
<template>
|
|
32
31
|
<sonic-button onclick="document.getElementById('modalText').show()"> Long text </sonic-button>
|
|
33
32
|
<sonic-modal align="left" id="modalText">
|
|
34
|
-
<sonic-modal-close></sonic-modal-close>
|
|
35
33
|
<sonic-modal-title>Infos et tarifs </sonic-modal-title>
|
|
36
34
|
<sonic-modal-content>
|
|
37
35
|
<div class="prose">
|
|
@@ -97,7 +95,6 @@
|
|
|
97
95
|
<template>
|
|
98
96
|
<sonic-button onclick="document.getElementById('CustomWidth').show()"> Custom width </sonic-button>
|
|
99
97
|
<sonic-modal maxHeight="50vh" maxWidth="90vw" width="100%" height="100%" id="CustomWidth">
|
|
100
|
-
<sonic-modal-close></sonic-modal-close>
|
|
101
98
|
<sonic-image
|
|
102
99
|
cover
|
|
103
100
|
objectPosition="center 20%"
|
|
@@ -113,7 +110,6 @@
|
|
|
113
110
|
<template>
|
|
114
111
|
<sonic-button onclick="document.getElementById('Fullscreen').show()"> Fullscreen </sonic-button>
|
|
115
112
|
<sonic-modal fullscreen id="Fullscreen">
|
|
116
|
-
<sonic-modal-close></sonic-modal-close>
|
|
117
113
|
<sonic-image
|
|
118
114
|
cover
|
|
119
115
|
src="https://thegoodlife.fr/wp-content/uploads/sites/2/2022/03/compagnies-aeriennes-nostalgie-coeur-insert-03-dr.jpg"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Toast
|
|
2
|
+
|
|
3
|
+
## Utilisation de base
|
|
4
|
+
|
|
5
|
+
<sonic-code>
|
|
6
|
+
<template>
|
|
7
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Message de notification simple' })">
|
|
8
|
+
Afficher un toast
|
|
9
|
+
</sonic-button>
|
|
10
|
+
</template>
|
|
11
|
+
</sonic-code>
|
|
12
|
+
|
|
13
|
+
## Statut
|
|
14
|
+
|
|
15
|
+
Les statuts disponibles sont : `success`, `error`, `warning`, `info` ou vide (par défaut).
|
|
16
|
+
|
|
17
|
+
<sonic-code>
|
|
18
|
+
<template>
|
|
19
|
+
<div class="flex flex-wrap gap-2">
|
|
20
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Message par défaut', status: '' })">
|
|
21
|
+
Default
|
|
22
|
+
</sonic-button>
|
|
23
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Opération réussie !', status: 'success' })">
|
|
24
|
+
Success
|
|
25
|
+
</sonic-button>
|
|
26
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Une erreur est survenue', status: 'error' })">
|
|
27
|
+
Error
|
|
28
|
+
</sonic-button>
|
|
29
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Attention à ce point', status: 'warning' })">
|
|
30
|
+
Warning
|
|
31
|
+
</sonic-button>
|
|
32
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Information importante', status: 'info' })">
|
|
33
|
+
Info
|
|
34
|
+
</sonic-button>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
</sonic-code>
|
|
38
|
+
|
|
39
|
+
## Avec titre
|
|
40
|
+
|
|
41
|
+
<sonic-code>
|
|
42
|
+
<template>
|
|
43
|
+
<div class="flex flex-wrap gap-2">
|
|
44
|
+
<sonic-button onclick="window.SonicToast.add({ title: 'Succès', text: 'Votre demande a été traitée avec succès.', status: 'success' })">
|
|
45
|
+
Toast avec titre
|
|
46
|
+
</sonic-button>
|
|
47
|
+
<sonic-button onclick="window.SonicToast.add({ title: 'Test', text: 'Une erreur est survenue lors du traitement.', status: 'error' })">
|
|
48
|
+
Toast d'erreur avec titre
|
|
49
|
+
</sonic-button>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
</sonic-code>
|
|
53
|
+
|
|
54
|
+
## Avec contenu HTML
|
|
55
|
+
|
|
56
|
+
Le contenu du toast peut contenir du HTML.
|
|
57
|
+
|
|
58
|
+
<sonic-code>
|
|
59
|
+
<template>
|
|
60
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Message avec <strong>HTML</strong> et un <a href="#">lien cliquable</a>', status: 'info' })">
|
|
61
|
+
Toast avec HTML
|
|
62
|
+
</sonic-button>
|
|
63
|
+
</template>
|
|
64
|
+
</sonic-code>
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Persistance
|
|
68
|
+
|
|
69
|
+
Par défaut, les toasts disparaissent automatiquement. Avec `preserve: true`, le toast reste affiché jusqu'à ce qu'il soit fermé manuellement.
|
|
70
|
+
|
|
71
|
+
<sonic-code>
|
|
72
|
+
<template>
|
|
73
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Ce toast ne disparaîtra pas automatiquement', status: 'info', preserve: true })">
|
|
74
|
+
Toast persistant
|
|
75
|
+
</sonic-button>
|
|
76
|
+
</template>
|
|
77
|
+
</sonic-code>
|
|
78
|
+
|
|
79
|
+
## Masquer définitivement
|
|
80
|
+
|
|
81
|
+
Avec `dismissForever: true` et un `id`, le toast peut être masqué définitivement. Une fois fermé, il ne réapparaîtra plus même après rechargement de la page.
|
|
82
|
+
|
|
83
|
+
<sonic-code>
|
|
84
|
+
<template>
|
|
85
|
+
<sonic-button onclick="window.SonicToast.add({ id: 'unique-toast-id', text: 'Ce toast peut être masqué définitivement', status: 'info', dismissForever: true })">
|
|
86
|
+
Toast avec dismiss forever
|
|
87
|
+
</sonic-button>
|
|
88
|
+
</template>
|
|
89
|
+
</sonic-code>
|
|
90
|
+
|
|
91
|
+
## Fantôme
|
|
92
|
+
|
|
93
|
+
Avec `ghost: true`, le toast devient semi-transparent et non-interactif.
|
|
94
|
+
|
|
95
|
+
<sonic-code>
|
|
96
|
+
<template>
|
|
97
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast fantôme (semi-transparent)', status: 'info', ghost: true })">
|
|
98
|
+
Toast ghost
|
|
99
|
+
</sonic-button>
|
|
100
|
+
</template>
|
|
101
|
+
</sonic-code>
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Méthodes de suppression
|
|
105
|
+
|
|
106
|
+
### Tout supprimer
|
|
107
|
+
|
|
108
|
+
Supprime tous les toasts sauf ceux marqués comme `ghost`.
|
|
109
|
+
|
|
110
|
+
<sonic-code>
|
|
111
|
+
<template>
|
|
112
|
+
<div class="flex flex-wrap gap-2">
|
|
113
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast 1', status: 'info' })">Ajouter toast 1</sonic-button>
|
|
114
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast 2', status: 'success' })">Ajouter toast 2</sonic-button>
|
|
115
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast 3', status: 'warning' })">Ajouter toast 3</sonic-button>
|
|
116
|
+
<sonic-button onclick="window.SonicToast.removeAll()">Supprimer tous</sonic-button>
|
|
117
|
+
</div>
|
|
118
|
+
</template>
|
|
119
|
+
</sonic-code>
|
|
120
|
+
|
|
121
|
+
### Supprimer par statut
|
|
122
|
+
|
|
123
|
+
Supprime tous les toasts d'un statut spécifique.
|
|
124
|
+
|
|
125
|
+
<sonic-code>
|
|
126
|
+
<template>
|
|
127
|
+
<div class="flex flex-wrap gap-2">
|
|
128
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast success', status: 'success' })">Success</sonic-button>
|
|
129
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast error', status: 'error' })">Error</sonic-button>
|
|
130
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast warning', status: 'warning' })">Warning</sonic-button>
|
|
131
|
+
<sonic-button onclick="window.SonicToast.removeItemsByStatus('success')">Supprimer success</sonic-button>
|
|
132
|
+
<sonic-button onclick="window.SonicToast.removeItemsByStatus('error')">Supprimer error</sonic-button>
|
|
133
|
+
</div>
|
|
134
|
+
</template>
|
|
135
|
+
</sonic-code>
|
|
136
|
+
|
|
137
|
+
### Supprimer les éléments temporaires
|
|
138
|
+
|
|
139
|
+
Supprime tous les toasts qui ne sont pas marqués comme `preserve`.
|
|
140
|
+
|
|
141
|
+
<sonic-code>
|
|
142
|
+
<template>
|
|
143
|
+
<div class="flex flex-wrap gap-2">
|
|
144
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast temporaire', status: 'info' })">Temporaire</sonic-button>
|
|
145
|
+
<sonic-button onclick="window.SonicToast.add({ text: 'Toast persistant', status: 'success', preserve: true })">Persistant</sonic-button>
|
|
146
|
+
<sonic-button onclick="window.SonicToast.removeTemporaryItems()">Supprimer temporaires</sonic-button>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
</sonic-code>
|
|
150
|
+
|
|
151
|
+
## Exemple complet
|
|
152
|
+
|
|
153
|
+
<sonic-code>
|
|
154
|
+
<template>
|
|
155
|
+
<sonic-button onclick="window.SonicToast.add({
|
|
156
|
+
id: 'welcome-toast',
|
|
157
|
+
title: 'Bienvenue',
|
|
158
|
+
text: 'Bienvenue sur notre plateforme ! Vous pouvez commencer à explorer les fonctionnalités.',
|
|
159
|
+
status: 'success',
|
|
160
|
+
dismissForever: true
|
|
161
|
+
})">
|
|
162
|
+
Toast complet
|
|
163
|
+
</sonic-button>
|
|
164
|
+
</template>
|
|
165
|
+
</sonic-code>
|
|
166
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @ancestorAttribute
|
|
2
|
+
|
|
3
|
+
The `@ancestorAttribute` decorator automatically injects the value of an ancestor's attribute into a class property at the time of `connectedCallback`.
|
|
4
|
+
|
|
5
|
+
## Principle
|
|
6
|
+
|
|
7
|
+
This decorator uses `HTML.getAncestorAttributeValue` to traverse up the DOM tree from the current element and find the first ancestor that has the specified attribute. The value of this attribute is then assigned to the decorated property.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
### Import
|
|
12
|
+
|
|
13
|
+
<sonic-code language="typescript">
|
|
14
|
+
<template>
|
|
15
|
+
import { ancestorAttribute } from "@supersoniks/concorde/decorators";
|
|
16
|
+
</template>
|
|
17
|
+
</sonic-code>
|
|
18
|
+
|
|
19
|
+
### Basic example
|
|
20
|
+
Le composant lit les attributs `dataProvider` et `testAttribute` exposés par son conteneur ancêtre.
|
|
21
|
+
|
|
22
|
+
<sonic-code language="typescript">
|
|
23
|
+
<template>
|
|
24
|
+
//...
|
|
25
|
+
@customElement("demo-bind-reflect")
|
|
26
|
+
export class DemoBindReflect extends LitElement {
|
|
27
|
+
static styles = [tailwind];
|
|
28
|
+
//
|
|
29
|
+
@bind("bindReflectDemo.count", { reflect: true })
|
|
30
|
+
@state()
|
|
31
|
+
withReflect: number = 0;
|
|
32
|
+
//
|
|
33
|
+
@bind("bindReflectDemo.count")
|
|
34
|
+
@state()
|
|
35
|
+
withoutReflect: number = 0;
|
|
36
|
+
// initialize the publisher data
|
|
37
|
+
connectedCallback() {
|
|
38
|
+
super.connectedCallback();
|
|
39
|
+
this.resetData();
|
|
40
|
+
}
|
|
41
|
+
//
|
|
42
|
+
resetData() {
|
|
43
|
+
PublisherManager.get("bindReflectDemo").set({ count: 0 });
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
return html`
|
|
47
|
+
<div class="mb-3">
|
|
48
|
+
from publisher : ${sub("bindReflectDemo.count")} <br />
|
|
49
|
+
from component with reflect : ${this.withReflect} <br />
|
|
50
|
+
from component without reflect : ${this.withoutReflect}
|
|
51
|
+
</div>
|
|
52
|
+
<sonic-button @click=${() => this.withReflect++}
|
|
53
|
+
>Increment with reflect</sonic-button
|
|
54
|
+
>
|
|
55
|
+
<sonic-button @click=${() => this.withoutReflect++}
|
|
56
|
+
>Increment without reflect</sonic-button
|
|
57
|
+
>
|
|
58
|
+
<sonic-button @click=${this.resetData}>Reset publisher data</sonic-button>
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</template>
|
|
63
|
+
</sonic-code>
|
|
64
|
+
|
|
65
|
+
<sonic-code>
|
|
66
|
+
<template>
|
|
67
|
+
<div dataProvider="demoDataProvider" testAttribute="test-value-123">
|
|
68
|
+
<demo-ancestor-attribute></demo-ancestor-attribute>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
71
|
+
</sonic-code>
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Use cases
|
|
75
|
+
|
|
76
|
+
This decorator is particularly useful for:
|
|
77
|
+
|
|
78
|
+
- **Retrieving the `dataProvider`** from an ancestor without having to pass it explicitly
|
|
79
|
+
- **Retrieving the `formDataProvider`** in form components
|
|
80
|
+
- **Retrieving the `wordingProvider`** for translation
|
|
81
|
+
- **Retrieving any other attribute** defined on an ancestor
|
|
82
|
+
|
|
83
|
+
## Behavior
|
|
84
|
+
|
|
85
|
+
- The search starts from the current element and traverses up the DOM tree
|
|
86
|
+
- If the attribute is not found, the property will be assigned `null`
|
|
87
|
+
- The injection happens automatically at the time of `connectedCallback`
|
|
88
|
+
- The value is not reactive: it is only updated once when the element is connected to the DOM
|
|
89
|
+
|
|
90
|
+
## Notes
|
|
91
|
+
|
|
92
|
+
- This decorator works with any component that has a `connectedCallback` method (such as `LitElement` or components extending `Subscriber`)
|
|
93
|
+
- The search also traverses Shadow DOM if necessary
|
|
94
|
+
- If multiple ancestors have the attribute, the closest one will be used
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# @autoSubscribe
|
|
2
|
+
|
|
3
|
+
The `@autoSubscribe` decorator automatically detects which publishers are accessed within a method and subscribes to them. When any of these publishers change, the method is automatically re-executed.
|
|
4
|
+
|
|
5
|
+
## Principle
|
|
6
|
+
|
|
7
|
+
This decorator wraps a method to track which publishers are accessed during its execution. It then subscribes to all accessed publishers, and when any of them change, the method is re-executed. This provides automatic reactivity without manually managing subscriptions.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
### Import
|
|
12
|
+
|
|
13
|
+
<sonic-code language="typescript">
|
|
14
|
+
<template>
|
|
15
|
+
import { autoSubscribe } from "@supersoniks/concorde/decorators";
|
|
16
|
+
</template>
|
|
17
|
+
</sonic-code>
|
|
18
|
+
|
|
19
|
+
### Basic example
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
<sonic-code language="typescript">
|
|
23
|
+
<template>
|
|
24
|
+
@customElement("demo-auto-subscribe")
|
|
25
|
+
export class DemoAutoSubscribe extends LitElement {
|
|
26
|
+
static styles = [tailwind];
|
|
27
|
+
//
|
|
28
|
+
@state() displayText: string = "";
|
|
29
|
+
@state() computedValue: number = 0;
|
|
30
|
+
//
|
|
31
|
+
@autoSubscribe()
|
|
32
|
+
updateDisplay() {
|
|
33
|
+
const value1 = PublisherManager.get("autoValue1").get() || 0;
|
|
34
|
+
const value2 = PublisherManager.get("autoValue2").get() || 0;
|
|
35
|
+
this.computedValue = value1 + value2;
|
|
36
|
+
this.displayText = `${value1} + ${value2} = ${this.computedValue}`;
|
|
37
|
+
}
|
|
38
|
+
//
|
|
39
|
+
render() {
|
|
40
|
+
return html`
|
|
41
|
+
<p><strong>${this.displayText}</strong></p>
|
|
42
|
+
<div>
|
|
43
|
+
<sonic-button @click=${() => this.randomizeValue("autoValue1")}>
|
|
44
|
+
Randomize Value 1
|
|
45
|
+
</sonic-button>
|
|
46
|
+
<sonic-button @click=${() => this.randomizeValue("autoValue2")}>
|
|
47
|
+
Randomize Value 2
|
|
48
|
+
</sonic-button>
|
|
49
|
+
</div>
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
//
|
|
53
|
+
randomizeValue(publisherId: string) {
|
|
54
|
+
const value = PublisherManager.get(publisherId);
|
|
55
|
+
value.set(Math.floor(Math.random() * 100));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</template>
|
|
59
|
+
</sonic-code>
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
<sonic-code >
|
|
63
|
+
<template>
|
|
64
|
+
<demo-auto-subscribe></demo-auto-subscribe>
|
|
65
|
+
</template>
|
|
66
|
+
</sonic-code>
|
|
67
|
+
|
|
68
|
+
### Example with render method
|
|
69
|
+
|
|
70
|
+
<sonic-code language="typescript">
|
|
71
|
+
<template>
|
|
72
|
+
@customElement("reactive-view")
|
|
73
|
+
export class ReactiveView extends LitElement {
|
|
74
|
+
@autoSubscribe()
|
|
75
|
+
render() {
|
|
76
|
+
const data = PublisherManager.get("myData");
|
|
77
|
+
const config = PublisherManager.get("config");
|
|
78
|
+
//
|
|
79
|
+
// This render method will be automatically re-executed
|
|
80
|
+
// when myData or config change
|
|
81
|
+
const value = data.get()?.value || 0;
|
|
82
|
+
const multiplier = config.get()?.multiplier || 1;
|
|
83
|
+
//
|
|
84
|
+
return html`
|
|
85
|
+
<div>
|
|
86
|
+
<h1>Result: ${value * multiplier}</h1>
|
|
87
|
+
<p>Value: ${value}</p>
|
|
88
|
+
<p>Multiplier: ${multiplier}</p>
|
|
89
|
+
</div>
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
</template>
|
|
94
|
+
</sonic-code>
|
|
95
|
+
|
|
96
|
+
## How it works
|
|
97
|
+
|
|
98
|
+
1. **First execution**: When the method is called, `PublisherManager.collectModifiedPublisher()` is used to track which publishers are accessed
|
|
99
|
+
2. **Subscription**: After execution, the decorator subscribes to all detected publishers
|
|
100
|
+
3. **Re-execution**: When any subscribed publisher changes, the method is automatically called again
|
|
101
|
+
4. **Cleanup**: On `disconnectedCallback`, all subscriptions are automatically removed
|
|
102
|
+
|
|
103
|
+
## Behavior
|
|
104
|
+
|
|
105
|
+
- The method is automatically called on `connectedCallback`
|
|
106
|
+
- The method is re-executed whenever any accessed publisher changes
|
|
107
|
+
- Subscriptions are managed automatically (no manual cleanup needed)
|
|
108
|
+
- Only publishers accessed during method execution are subscribed to
|
|
109
|
+
- The decorator uses `queueMicrotask` to batch multiple updates and avoid unnecessary re-renders
|
|
110
|
+
|
|
111
|
+
## Use cases
|
|
112
|
+
|
|
113
|
+
This decorator is particularly useful for:
|
|
114
|
+
|
|
115
|
+
- **Reactive rendering** where the render method depends on multiple publishers
|
|
116
|
+
- **Data transformation** that needs to update when source data changes
|
|
117
|
+
- **Computed properties** that depend on multiple data sources
|
|
118
|
+
- **Automatic synchronization** between publishers and component state
|
|
119
|
+
|
|
120
|
+
## Complete example
|
|
121
|
+
|
|
122
|
+
<sonic-code language="typescript">
|
|
123
|
+
<template>
|
|
124
|
+
import { html, LitElement } from "lit";
|
|
125
|
+
import { customElement } from "lit/decorators.js";
|
|
126
|
+
import { autoSubscribe } from "@supersoniks/concorde/decorators";
|
|
127
|
+
import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
|
|
128
|
+
//
|
|
129
|
+
@customElement("shopping-cart")
|
|
130
|
+
export class ShoppingCart extends LitElement {
|
|
131
|
+
items: any[] = [];
|
|
132
|
+
total: number = 0;
|
|
133
|
+
discount: number = 0;
|
|
134
|
+
//
|
|
135
|
+
@autoSubscribe()
|
|
136
|
+
calculateTotal() {
|
|
137
|
+
const cart = PublisherManager.get("cart");
|
|
138
|
+
const promo = PublisherManager.get("promo");
|
|
139
|
+
//
|
|
140
|
+
// Access cart items
|
|
141
|
+
this.items = cart.items.get() || [];
|
|
142
|
+
//
|
|
143
|
+
// Access promo code
|
|
144
|
+
const promoCode = promo.code.get() || "";
|
|
145
|
+
const discountPercent = promoCode === "SAVE10" ? 0.1 : 0;
|
|
146
|
+
//
|
|
147
|
+
// Calculate totals
|
|
148
|
+
const subtotal = this.items.reduce((sum, item) =>
|
|
149
|
+
sum + (item.price * item.quantity), 0
|
|
150
|
+
);
|
|
151
|
+
this.discount = subtotal * discountPercent;
|
|
152
|
+
this.total = subtotal - this.discount;
|
|
153
|
+
//
|
|
154
|
+
this.requestUpdate();
|
|
155
|
+
}
|
|
156
|
+
//
|
|
157
|
+
connectedCallback() {
|
|
158
|
+
super.connectedCallback();
|
|
159
|
+
this.calculateTotal();
|
|
160
|
+
}
|
|
161
|
+
//
|
|
162
|
+
render() {
|
|
163
|
+
return html`
|
|
164
|
+
<div class="cart">
|
|
165
|
+
<h2>Shopping Cart</h2>
|
|
166
|
+
${this.items.map(item => html`
|
|
167
|
+
<div>${item.name} x${item.quantity} - ${item.price}€</div>
|
|
168
|
+
`)}
|
|
169
|
+
<div class="total">
|
|
170
|
+
<p>Subtotal: ${this.total + this.discount}€</p>
|
|
171
|
+
<p>Discount: -${this.discount}€</p>
|
|
172
|
+
<p><strong>Total: ${this.total}€</strong></p>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//
|
|
179
|
+
// When you update the publishers, calculateTotal is automatically called:
|
|
180
|
+
const cart = PublisherManager.get("cart");
|
|
181
|
+
cart.items.set([
|
|
182
|
+
{ name: "Product 1", price: 10, quantity: 2 },
|
|
183
|
+
{ name: "Product 2", price: 15, quantity: 1 }
|
|
184
|
+
]);
|
|
185
|
+
//
|
|
186
|
+
const promo = PublisherManager.get("promo");
|
|
187
|
+
promo.code.set("SAVE10");
|
|
188
|
+
// calculateTotal will be automatically called and the UI will update
|
|
189
|
+
</template>
|
|
190
|
+
</sonic-code>
|
|
191
|
+
|
|
192
|
+
## Notes
|
|
193
|
+
|
|
194
|
+
- This decorator works with any component that has `connectedCallback` and `disconnectedCallback` methods (such as `LitElement` or components extending `Subscriber`)
|
|
195
|
+
- The method is called automatically on `connectedCallback`
|
|
196
|
+
- Remember to call `this.requestUpdate()` if you're updating component properties
|
|
197
|
+
- The decorator uses debouncing via `queueMicrotask` to prevent excessive re-executions
|
|
198
|
+
- For more information about publishers, see the documentation on [Sharing data](#docs/_getting-started/pubsub.md/pubsub)
|
|
199
|
+
|