@supersoniks/concorde 3.2.5 → 3.2.6

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 (136) hide show
  1. package/README.md +163 -0
  2. package/build-infos.json +1 -1
  3. package/concorde-core.bundle.js +86 -79
  4. package/concorde-core.es.js +424 -362
  5. package/dist/concorde-core.bundle.js +86 -79
  6. package/dist/concorde-core.es.js +424 -362
  7. package/docs/assets/index-C0K6xugr.css +1 -0
  8. package/docs/assets/index-Dgl1lJQo.js +4861 -0
  9. package/docs/css/docs.css +0 -0
  10. package/docs/fonts/ClashGrotesk-Bold.eot +0 -0
  11. package/docs/fonts/ClashGrotesk-Bold.ttf +0 -0
  12. package/docs/fonts/ClashGrotesk-Bold.woff +0 -0
  13. package/docs/fonts/ClashGrotesk-Bold.woff2 +0 -0
  14. package/docs/fonts/ClashGrotesk-Extralight.eot +0 -0
  15. package/docs/fonts/ClashGrotesk-Extralight.ttf +0 -0
  16. package/docs/fonts/ClashGrotesk-Extralight.woff +0 -0
  17. package/docs/fonts/ClashGrotesk-Extralight.woff2 +0 -0
  18. package/docs/fonts/ClashGrotesk-Light.eot +0 -0
  19. package/docs/fonts/ClashGrotesk-Light.ttf +0 -0
  20. package/docs/fonts/ClashGrotesk-Light.woff +0 -0
  21. package/docs/fonts/ClashGrotesk-Light.woff2 +0 -0
  22. package/docs/fonts/ClashGrotesk-Medium.eot +0 -0
  23. package/docs/fonts/ClashGrotesk-Medium.ttf +0 -0
  24. package/docs/fonts/ClashGrotesk-Medium.woff +0 -0
  25. package/docs/fonts/ClashGrotesk-Medium.woff2 +0 -0
  26. package/docs/fonts/ClashGrotesk-Regular.eot +0 -0
  27. package/docs/fonts/ClashGrotesk-Regular.ttf +0 -0
  28. package/docs/fonts/ClashGrotesk-Regular.woff +0 -0
  29. package/docs/fonts/ClashGrotesk-Regular.woff2 +0 -0
  30. package/docs/fonts/ClashGrotesk-Semibold.eot +0 -0
  31. package/docs/fonts/ClashGrotesk-Semibold.ttf +0 -0
  32. package/docs/fonts/ClashGrotesk-Semibold.woff +0 -0
  33. package/docs/fonts/ClashGrotesk-Semibold.woff2 +0 -0
  34. package/docs/fonts/ClashGrotesk-Variable.eot +0 -0
  35. package/docs/fonts/ClashGrotesk-Variable.ttf +0 -0
  36. package/docs/fonts/ClashGrotesk-Variable.woff +0 -0
  37. package/docs/fonts/ClashGrotesk-Variable.woff2 +0 -0
  38. package/docs/img/concorde-icon.svg +5 -0
  39. package/docs/img/concorde-logo.svg +1 -0
  40. package/docs/img/concorde.png +0 -0
  41. package/docs/img/concorde_def.png +0 -0
  42. package/docs/img/concorde_seuil.png.webp +0 -0
  43. package/docs/img/concorde_seuil_invert.png +0 -0
  44. package/docs/img/paul_metrand.jpg +0 -0
  45. package/docs/img/paul_metrand_xs.jpg +0 -0
  46. package/docs/index.html +93 -0
  47. package/docs/src/core/components/functional/date/date.md +290 -0
  48. package/docs/src/core/components/functional/fetch/fetch.md +117 -0
  49. package/docs/src/core/components/functional/if/if.md +16 -0
  50. package/docs/src/core/components/functional/list/list.md +199 -0
  51. package/docs/src/core/components/functional/mix/mix.md +41 -0
  52. package/docs/src/core/components/functional/queue/queue.md +87 -0
  53. package/docs/src/core/components/functional/router/router.md +129 -0
  54. package/docs/src/core/components/functional/sdui/default-library.json +108 -0
  55. package/docs/src/core/components/functional/sdui/example.json +99 -0
  56. package/docs/src/core/components/functional/sdui/sdui.md +356 -0
  57. package/docs/src/core/components/functional/states/states.md +87 -0
  58. package/docs/src/core/components/functional/submit/submit.md +83 -0
  59. package/docs/src/core/components/functional/subscriber/subscriber.md +91 -0
  60. package/docs/src/core/components/functional/value/value.md +35 -0
  61. package/docs/src/core/components/ui/alert/alert.md +121 -0
  62. package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -0
  63. package/docs/src/core/components/ui/badge/badge.md +127 -0
  64. package/docs/src/core/components/ui/button/button.md +182 -0
  65. package/docs/src/core/components/ui/captcha/captcha.md +24 -0
  66. package/docs/src/core/components/ui/card/card.md +97 -0
  67. package/docs/src/core/components/ui/divider/divider.md +35 -0
  68. package/docs/src/core/components/ui/form/checkbox/checkbox.md +104 -0
  69. package/docs/src/core/components/ui/form/fieldset/fieldset.md +129 -0
  70. package/docs/src/core/components/ui/form/form-actions/form-actions.md +77 -0
  71. package/docs/src/core/components/ui/form/form-layout/form-layout.md +44 -0
  72. package/docs/src/core/components/ui/form/input/input.md +167 -0
  73. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +131 -0
  74. package/docs/src/core/components/ui/form/radio/radio.md +84 -0
  75. package/docs/src/core/components/ui/form/select/select.md +97 -0
  76. package/docs/src/core/components/ui/form/switch/switch.md +84 -0
  77. package/docs/src/core/components/ui/form/textarea/textarea.md +65 -0
  78. package/docs/src/core/components/ui/group/group.md +75 -0
  79. package/docs/src/core/components/ui/icon/icon.md +125 -0
  80. package/docs/src/core/components/ui/icon/icons.json +1 -0
  81. package/docs/src/core/components/ui/image/image.md +107 -0
  82. package/docs/src/core/components/ui/link/link.md +43 -0
  83. package/docs/src/core/components/ui/loader/loader.md +67 -0
  84. package/docs/src/core/components/ui/menu/menu.md +288 -0
  85. package/docs/src/core/components/ui/modal/modal.md +123 -0
  86. package/docs/src/core/components/ui/pop/pop.md +96 -0
  87. package/docs/src/core/components/ui/progress/progress.md +63 -0
  88. package/docs/src/core/components/ui/table/table.md +455 -0
  89. package/docs/src/core/components/ui/tooltip/tooltip.md +82 -0
  90. package/docs/src/docs/_core-concept/overview.md +57 -0
  91. package/docs/src/docs/_core-concept/subscriber.md +76 -0
  92. package/docs/src/docs/_getting-started/concorde-outside.md +143 -0
  93. package/docs/src/docs/_getting-started/create-a-component.md +137 -0
  94. package/docs/src/docs/_getting-started/my-first-subscriber.md +174 -0
  95. package/docs/src/docs/_getting-started/pubsub.md +150 -0
  96. package/docs/src/docs/_getting-started/start.md +39 -0
  97. package/docs/src/docs/_getting-started/theming.md +91 -0
  98. package/docs/src/docs/search/docs-search.json +3917 -0
  99. package/docs/src/tag-list.json +1 -0
  100. package/docs/src/tsconfig-model.json +23 -0
  101. package/docs/src/tsconfig.json +918 -0
  102. package/docs/svg/regular/plane.svg +1 -0
  103. package/docs/svg/solid/plane.svg +1 -0
  104. package/mcp-server/COMPARISON-MCP.md +176 -0
  105. package/mcp-server/README-MCP-NODEJS.md +284 -0
  106. package/mcp-server/README-MCP.md +114 -0
  107. package/mcp-server/README.md +127 -0
  108. package/mcp-server/TECHNICAL-DOCS.md +269 -0
  109. package/mcp-server/concorde-mcp-server.js +859 -0
  110. package/mcp-server/concorde-mcp-server.py +801 -0
  111. package/mcp-server/cursor-mcp-config-advanced.json +68 -0
  112. package/mcp-server/cursor-mcp-config-nodejs.json +74 -0
  113. package/mcp-server/cursor-mcp-config.json +11 -0
  114. package/mcp-server/install-mcp-nodejs.sh +104 -0
  115. package/mcp-server/install-mcp.sh +62 -0
  116. package/mcp-server/package-lock.json +147 -0
  117. package/mcp-server/package-mcp.json +40 -0
  118. package/mcp-server/package.json +40 -0
  119. package/mcp-server/test-mcp.js +107 -0
  120. package/mcp-server/test-mcp.py +73 -0
  121. package/package.json +2 -1
  122. package/php/get-challenge.php +34 -0
  123. package/php/some-service.php +42 -0
  124. package/src/core/components/functional/fetch/fetch.md +6 -0
  125. package/src/core/components/ui/form/checkbox/checkbox.ts +6 -0
  126. package/src/core/components/ui/form/radio/radio.ts +11 -1
  127. package/src/core/components/ui/icon/icons.ts +0 -0
  128. package/src/core/components/ui/menu/menu.md +0 -0
  129. package/src/core/components/ui/modal/modal-close.ts +0 -0
  130. package/src/core/components/ui/modal/modal.ts +0 -0
  131. package/src/core/components/ui/toast/toast-item.ts +68 -2
  132. package/src/core/components/ui/toast/toast.md +166 -0
  133. package/src/core/components/ui/toast/toast.ts +1 -1
  134. package/src/core/mixins/Fetcher.ts +31 -4
  135. package/src/docs/navigation/navigation.ts +1 -1
  136. package/src/docs/search/docs-search.json +135 -0
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script de test pour le serveur MCP Concorde
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ # Ajouter le répertoire courant au path
12
+ sys.path.insert(0, str(Path(__file__).parent))
13
+
14
+ from concorde_mcp_server import ConcordeMCPServer
15
+
16
+ async def test_server():
17
+ """Teste le serveur MCP"""
18
+ print("🧪 Test du serveur MCP Concorde...")
19
+
20
+ server = ConcordeMCPServer()
21
+
22
+ # Test 1: Vérifier que les composants sont chargés
23
+ print(f"\n📦 Composants chargés: {len(server.components)}")
24
+ for name, comp in list(server.components.items())[:5]:
25
+ print(f" - {comp.tag_name} ({comp.category})")
26
+
27
+ # Test 2: Recherche de composant
28
+ print("\n🔍 Test de recherche...")
29
+ result = await server._search_component("button")
30
+ print(f"Résultat recherche 'button': {len(result.content[0].text)} caractères")
31
+
32
+ # Test 3: Informations sur un composant
33
+ print("\n📋 Test d'informations composant...")
34
+ if "button" in server.components:
35
+ result = await server._get_component_info("button")
36
+ print(f"Info composant 'button': {len(result.content[0].text)} caractères")
37
+
38
+ # Test 4: Génération de code
39
+ print("\n💻 Test de génération de code...")
40
+ if "button" in server.components:
41
+ result = await server._generate_component_code(
42
+ "button",
43
+ {"type": "primary", "size": "lg"},
44
+ "Test"
45
+ )
46
+ print(f"Code généré: {result.content[0].text}")
47
+
48
+ # Test 5: Exemple de formulaire
49
+ print("\n📝 Test d'exemple de formulaire...")
50
+ result = await server._get_form_example([
51
+ {"name": "email", "type": "email", "label": "Email", "required": True},
52
+ {"name": "password", "type": "password", "label": "Mot de passe", "required": True}
53
+ ])
54
+ print(f"Formulaire généré: {len(result.content[0].text)} caractères")
55
+
56
+ # Test 6: Informations sur le thème
57
+ print("\n🎨 Test d'informations thème...")
58
+ result = await server._get_theme_info("light")
59
+ print(f"Info thème: {len(result.content[0].text)} caractères")
60
+
61
+ # Test 7: Liste des icônes
62
+ print("\n🔤 Test de liste d'icônes...")
63
+ result = await server._get_icons_list("core")
64
+ print(f"Liste icônes: {len(result.content[0].text)} caractères")
65
+
66
+ print("\n✅ Tous les tests sont passés avec succès !")
67
+ print(f"\n📊 Statistiques:")
68
+ print(f" - Composants UI: {len([c for c in server.components.values() if c.category == 'ui'])}")
69
+ print(f" - Composants fonctionnels: {len([c for c in server.components.values() if c.category == 'functional'])}")
70
+ print(f" - Documentation chargée: {len(server.documentation)} sections")
71
+
72
+ if __name__ == "__main__":
73
+ asyncio.run(test_server())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersoniks/concorde",
3
- "version": "3.2.5",
3
+ "version": "3.2.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "",
@@ -321,6 +321,7 @@
321
321
  "@vitejs/plugin-basic-ssl": "^2.1.0",
322
322
  "altcha": "^1.0.7",
323
323
  "autoprefixer": "^10.4.19",
324
+ "baseline-browser-mapping": "^2.8.31",
324
325
  "jsdom": "^26.0.0",
325
326
  "lit": "^3.1.3",
326
327
  "marked": "^12.0.1",
@@ -0,0 +1,34 @@
1
+ <?php
2
+ /* *
3
+ * Call get-challenge on auto-hosted latcha service at https://altcha.supersoniks.org
4
+ * */
5
+
6
+ // Autoriser toutes les origines
7
+ header("Access-Control-Allow-Origin: *");
8
+
9
+ // Autoriser les méthodes HTTP spécifiques
10
+ header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
11
+
12
+ // Autoriser certains en-têtes spécifiques
13
+ header("Access-Control-Allow-Headers: Content-Type, Authorization");
14
+
15
+ // Si la méthode est OPTIONS, terminer la requête ici
16
+ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
17
+ http_response_code(200);
18
+ exit();
19
+ }
20
+
21
+ function getChallenge($key){
22
+ $maxNumber=20000;
23
+ $queryString = $params = [
24
+ 'key' => $key,
25
+ 'maxNumber' => $maxNumber
26
+ ];
27
+ // Générer la chaîne de requête
28
+ $queryString = http_build_query($params);
29
+ $url = "https://altcha.supersoniks.org/get-challenge?key=".$queryString;
30
+ $response = file_get_contents($url);
31
+ return $response;
32
+ }
33
+
34
+ echo getChallenge($_GET['key']);
@@ -0,0 +1,42 @@
1
+ <?php
2
+ /* *
3
+ * Call verify-solution on auto-hosted latcha service at https://altcha.supersoniks.org
4
+ * */
5
+
6
+
7
+ // Autoriser toutes les origines
8
+ header("Access-Control-Allow-Origin: *");
9
+
10
+ // Autoriser les méthodes HTTP spécifiques
11
+ header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
12
+
13
+ // Autoriser certains en-têtes spécifiques
14
+ header("Access-Control-Allow-Headers: Content-Type, Authorization, x-altcha-spam-filter");
15
+
16
+ // Si la méthode est OPTIONS, terminer la requête ici
17
+ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
18
+ http_response_code(200);
19
+ exit();
20
+ }
21
+
22
+ function verifySolution($key, $solution){
23
+ $queryString = $params = [
24
+ 'key' => $key,
25
+ 'altcha' => $solution
26
+ ];
27
+ // Générer la chaîne de requête
28
+ $queryString = http_build_query($params);
29
+ $url = "https://altcha.supersoniks.org/verify-solution?".$queryString;
30
+ $response = file_get_contents($url);
31
+ return $response;
32
+ }
33
+
34
+ /**
35
+ * Get json posted data
36
+ */
37
+
38
+ // Get the posted data
39
+
40
+ $data = json_decode(file_get_contents("php://input"));
41
+
42
+ echo verifySolution($data->captchakey, $data->captchatoken);
@@ -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>
@@ -184,6 +184,12 @@ export class Checkbox extends FormCheckable(
184
184
  willUpdate(changedProperties: PropertyValues) {
185
185
  this.hasSlotOrProps();
186
186
  super.willUpdate(changedProperties);
187
+ /**
188
+ * On force le type à checkbox pour les checkbox
189
+ */
190
+ if (changedProperties.has("type")) {
191
+ this.type = "checkbox";
192
+ }
187
193
  }
188
194
 
189
195
  hasSlotOrProps() {
@@ -1,4 +1,4 @@
1
- import { css } from "lit";
1
+ import { css, PropertyValues } from "lit";
2
2
  import { customElement } from "lit/decorators.js";
3
3
  import { Checkbox } from "@supersoniks/concorde/core/components/ui/form/checkbox/checkbox";
4
4
 
@@ -37,6 +37,16 @@ export class Radio extends Checkbox {
37
37
  super();
38
38
  this.radio = true;
39
39
  }
40
+ willUpdate(changedProperties: PropertyValues) {
41
+ const typeChanged = changedProperties.has("type");
42
+ super.willUpdate(changedProperties);
43
+ /**
44
+ * On force le type a radio pour tous les boutons radio
45
+ */
46
+ if (typeChanged) {
47
+ this.type = "radio";
48
+ }
49
+ }
40
50
  connectedCallback() {
41
51
  // this.iconName = "circle-small";
42
52
  // this.iconPrefix = "solid";
File without changes
File without changes
File without changes
File without changes
@@ -210,10 +210,58 @@ export class SonicToastItem extends LitElement {
210
210
  @property({ type: Boolean }) dismissForever = false;
211
211
  @property({ type: String }) maxHeight = "10rem";
212
212
  @state() visible = true;
213
+ @state() private _titleId: string = "";
213
214
 
214
215
  // @queryAssignedElements({flatten: true, slot: main"})
215
216
  // textSlot!: HTMLElement[];
216
217
 
218
+ /**
219
+ * Retourne le rôle ARIA approprié selon le statut du toast
220
+ * - "alert" pour les erreurs (messages critiques) - implique déjà aria-live="assertive"
221
+ * - "status" pour les autres (messages informatifs)
222
+ */
223
+ getAriaRole(): "alert" | "status" {
224
+ return this.status === "error" ? "alert" : "status";
225
+ }
226
+
227
+ /**
228
+ * Retourne la valeur aria-live appropriée selon le statut
229
+ * - null pour les erreurs car role="alert" implique déjà aria-live="assertive"
230
+ * - "polite" pour les autres (peut attendre)
231
+ */
232
+ getAriaLive(): "polite" | null {
233
+ return this.status === "error" ? null : "polite";
234
+ }
235
+
236
+ /**
237
+ * Génère et mémorise un ID unique pour le titre du toast
238
+ */
239
+ getTitleId(): string {
240
+ if (this._titleId) {
241
+ return this._titleId;
242
+ }
243
+ this._titleId = this.id
244
+ ? `toast-title-${this.id}`
245
+ : `toast-title-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
246
+ return this._titleId;
247
+ }
248
+
249
+ /**
250
+ * Génère un label ARIA descriptif pour le bouton de fermeture
251
+ */
252
+ getCloseButtonLabel(): string {
253
+ const statusLabels: Record<string, string> = {
254
+ error: "Fermer le message d'erreur",
255
+ warning: "Fermer l'avertissement",
256
+ success: "Fermer le message de succès",
257
+ info: "Fermer l'information",
258
+ };
259
+ const statusLabel = this.status
260
+ ? statusLabels[this.status] || "Fermer la notification"
261
+ : "Fermer la notification";
262
+ return statusLabel;
263
+ }
264
+
217
265
  render() {
218
266
  // check if the toast is dismissed
219
267
  if (this.dismissForever) {
@@ -228,11 +276,26 @@ export class SonicToastItem extends LitElement {
228
276
  return nothing;
229
277
  }
230
278
 
279
+ const titleId = this.title ? this.getTitleId() : undefined;
280
+ // Extrait un label pour aria-label si pas de titre, en essayant de couper proprement
281
+ const ariaLabel =
282
+ !this.title && this.text
283
+ ? this.text.length > 100
284
+ ? this.text.substring(0, 100).replace(/\s+\S*$/, "") + "..."
285
+ : this.text
286
+ : undefined;
287
+
288
+ const ariaLive = this.getAriaLive();
289
+
231
290
  return html`<div
232
291
  class="sonic-toast ${this.status} ${this.ghost ? "ghost" : ""}"
292
+ role=${this.getAriaRole()}
293
+ aria-live=${ariaLive || nothing}
294
+ aria-atomic="true"
295
+ aria-label=${ariaLabel || nothing}
233
296
  >
234
297
  <button
235
- aria-label="Close"
298
+ aria-label=${this.getCloseButtonLabel()}
236
299
  class="sonic-toast-close"
237
300
  @click=${() => this.hide()}
238
301
  >
@@ -247,11 +310,14 @@ export class SonicToastItem extends LitElement {
247
310
  name=${icon[this.status]}
248
311
  class="sonic-toast-icon"
249
312
  size="2xl"
313
+ aria-hidden="true"
250
314
  ></sonic-icon>`}
251
315
 
252
316
  <div class="sonic-toast-text">
253
317
  ${this.title
254
- ? html`<div class="sonic-toast-title">${this.title}</div>`
318
+ ? html`<div class="sonic-toast-title" id=${titleId}>
319
+ ${this.title}
320
+ </div>`
255
321
  : ""}
256
322
  ${this.text ? unsafeHTML(this.text) : ""}
257
323
  <slot></slot>
@@ -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=&quot;#&quot;>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
+
@@ -56,7 +56,7 @@ export class SonicToast extends LitElement {
56
56
  SonicToast.handleExistingToastDelegation();
57
57
  if (!this.toasts) return nothing;
58
58
 
59
- return html`<div aria-live="polite" style=${styleMap(styles)}>
59
+ return html`<div style=${styleMap(styles)}>
60
60
  ${repeat(
61
61
  this.toasts,
62
62
  (item) => item.id,
@@ -121,6 +121,24 @@ const Fetcher = <
121
121
  @property({ type: Number }) refetchEveryMs = 0;
122
122
  refetchTimeOutId?: ReturnType<typeof setTimeout>;
123
123
 
124
+ private isFetching = false;
125
+ private mustRefetch = false;
126
+ handleStartFetching(): "fetching" | "okToFetch" {
127
+ if (this.isFetching) {
128
+ this.mustRefetch = true;
129
+ return "fetching";
130
+ }
131
+ this.isFetching = true;
132
+ return "okToFetch";
133
+ }
134
+
135
+ handleEndFetching() {
136
+ this.isFetching = false;
137
+ if (this.mustRefetch) {
138
+ this.mustRefetch = false;
139
+ this._fetchData();
140
+ }
141
+ }
124
142
  /**
125
143
  *
126
144
  * C'est ici que les données sont chargées via l'utilitaire API
@@ -156,10 +174,19 @@ const Fetcher = <
156
174
  });
157
175
  }
158
176
 
159
- let data = await this.api.get<PropsType>(
160
- this.endPoint || this.dataProvider || "",
161
- headerData
162
- );
177
+ const fetchStatus = this.handleStartFetching();
178
+ if (fetchStatus === "fetching") {
179
+ return;
180
+ }
181
+ let data = null;
182
+ try {
183
+ data = await this.api.get<PropsType>(
184
+ this.endPoint || this.dataProvider || "",
185
+ headerData
186
+ );
187
+ } catch (_error) {}
188
+
189
+ this.handleEndFetching();
163
190
 
164
191
  this.fetchedData = data;
165
192
  if (this.api.lastResult && !this.api.lastResult.ok) {
@@ -127,7 +127,7 @@ export class DocsNavigation extends LitElement {
127
127
  href: "#core/components/ui/form/textarea/textarea.md/textarea",
128
128
  },
129
129
  { label: "Theme", href: "#" },
130
- { label: "Toast", href: "#" },
130
+ { label: "Toast", href: "#core/components/ui/toast/toast.md/toast" },
131
131
  {
132
132
  label: "Tooltip",
133
133
  href: "#core/components/ui/tooltip/tooltip.md/tooltip",