@supersoniks/concorde 3.2.5 → 3.2.8

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 (150) hide show
  1. package/README.md +163 -0
  2. package/build-infos.json +1 -1
  3. package/concorde-core.bundle.js +131 -124
  4. package/concorde-core.es.js +808 -716
  5. package/dist/concorde-core.bundle.js +131 -124
  6. package/dist/concorde-core.es.js +808 -716
  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/functional/queue/queue.demo.ts +0 -0
  126. package/src/core/components/functional/sdui/sdui.ts +47 -22
  127. package/src/core/components/ui/form/checkbox/checkbox.ts +6 -0
  128. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.ts +9 -1
  129. package/src/core/components/ui/form/radio/radio.ts +11 -1
  130. package/src/core/components/ui/icon/icons.ts +0 -0
  131. package/src/core/components/ui/menu/menu.md +0 -0
  132. package/src/core/components/ui/modal/modal-close.ts +0 -0
  133. package/src/core/components/ui/modal/modal.ts +0 -0
  134. package/src/core/components/ui/toast/toast-item.ts +68 -2
  135. package/src/core/components/ui/toast/toast.md +166 -0
  136. package/src/core/components/ui/toast/toast.ts +1 -1
  137. package/src/core/components/ui/tooltip/tooltip.ts +67 -2
  138. package/src/core/decorators/Subscriber.ts +0 -0
  139. package/src/core/mixins/Fetcher.ts +40 -4
  140. package/src/core/utils/HTML.ts +2 -0
  141. package/src/core/utils/PublisherProxy.ts +0 -0
  142. package/src/core/utils/api.ts +7 -0
  143. package/src/decorators.ts +0 -0
  144. package/src/docs/navigation/navigation.ts +1 -1
  145. package/src/docs/search/docs-search.json +135 -0
  146. package/src/docs.ts +0 -0
  147. package/src/tsconfig-model.json +0 -0
  148. package/src/tsconfig.json +0 -0
  149. package/src/tsconfig.tsbuildinfo +0 -0
  150. package/templates-test.html +0 -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.8",
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>
@@ -5,11 +5,11 @@ import {
5
5
  SDUINode,
6
6
  SDUITransformDescription,
7
7
  } from "@supersoniks/concorde/core/components/functional/sdui/types";
8
- import {Fetcher, Subscriber} from "@supersoniks/concorde/mixins";
8
+ import { Fetcher, Subscriber } from "@supersoniks/concorde/mixins";
9
9
 
10
- import {HTML, Objects} from "@supersoniks/concorde/utils";
11
- import {LitElement, PropertyValues} from "lit";
12
- import {customElement, property} from "lit/decorators.js";
10
+ import { HTML, Objects } from "@supersoniks/concorde/utils";
11
+ import { LitElement, PropertyValues } from "lit";
12
+ import { customElement, property } from "lit/decorators.js";
13
13
  const tagName = "sonic-sdui"; // For Astro.build
14
14
  /**
15
15
  * ### sonic-sdui (Server Driven User Interface) est un fetcher chargant un JSON décrivant une interface utilisateur
@@ -75,7 +75,9 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
75
75
  * Suppressiond du contenu du composant avant le génération de la nouvelle ui
76
76
  */
77
77
  private removeChildren() {
78
- while ([...this.children].filter((elt) => elt.nodeName != "SLOT").length > 0) {
78
+ while (
79
+ [...this.children].filter((elt) => elt.nodeName != "SLOT").length > 0
80
+ ) {
79
81
  this.removeChild(this.children[0]);
80
82
  }
81
83
  }
@@ -118,9 +120,12 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
118
120
  if (!this.sduiDescriptor) return;
119
121
  let nodes = this.sduiDescriptor.nodes;
120
122
  if (!nodes) nodes = [];
121
- const messageProvider = {tagName: "sonic-toast-message-subscriber", attributes: {}};
123
+ const messageProvider = {
124
+ tagName: "sonic-toast-message-subscriber",
125
+ attributes: {},
126
+ };
122
127
  if (this.messagesKey) {
123
- messageProvider.attributes = {subDataProvider: this.messagesKey};
128
+ messageProvider.attributes = { subDataProvider: this.messagesKey };
124
129
  }
125
130
  nodes.push(messageProvider);
126
131
  nodes.forEach((node) => this.appendChild(this.parseChild(node)));
@@ -130,7 +135,7 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
130
135
  */
131
136
  private parseChild(node: SDUINode) {
132
137
  const tagName = node.tagName || "div";
133
- let {element, contentElement} = this.handleLibrary(node, tagName);
138
+ let { element, contentElement } = this.handleLibrary(node, tagName);
134
139
  this.handleAttributes(node, element);
135
140
  element = this.handleMarkup(node, element);
136
141
  if (!contentElement) contentElement = element;
@@ -148,7 +153,8 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
148
153
  */
149
154
  private handlePrefixSuffix(node: SDUINode, element: HTMLElement) {
150
155
  const container = document.createElement("div");
151
- container.innerHTML = (node.prefix || "") + element.outerHTML + (node.suffix || "");
156
+ container.innerHTML =
157
+ (node.prefix || "") + element.outerHTML + (node.suffix || "");
152
158
  container.style.display = "contents";
153
159
  return container;
154
160
  }
@@ -156,18 +162,26 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
156
162
  * Création des enfants du noeud courant
157
163
  * 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.
158
164
  */
159
- private handleChildNodes(node: SDUINode, contentElement: HTMLElement, element: HTMLElement) {
165
+ private handleChildNodes(
166
+ node: SDUINode,
167
+ contentElement: HTMLElement,
168
+ element: HTMLElement
169
+ ) {
160
170
  if (node.nodes) {
161
171
  const children: Array<SDUINode> = node.nodes;
162
172
  for (const child of children) {
163
173
  const childElement = this.parseChild(child);
164
174
  let nodeToAppendOn = contentElement;
165
175
  if (child.parentElementSelector) {
166
- nodeToAppendOn = element.querySelector(child.parentElementSelector) || contentElement;
176
+ nodeToAppendOn =
177
+ element.querySelector(child.parentElementSelector) ||
178
+ contentElement;
167
179
  }
168
- if (nodeToAppendOn.shadowRoot) nodeToAppendOn.shadowRoot.appendChild(childElement);
180
+ if (nodeToAppendOn.shadowRoot)
181
+ nodeToAppendOn.shadowRoot.appendChild(childElement);
169
182
  else if (nodeToAppendOn.tagName.toLocaleLowerCase() == "template") {
170
- const template: HTMLTemplateElement = nodeToAppendOn as HTMLTemplateElement;
183
+ const template: HTMLTemplateElement =
184
+ nodeToAppendOn as HTMLTemplateElement;
171
185
  template.content.appendChild(childElement);
172
186
  } else nodeToAppendOn.appendChild(childElement);
173
187
  }
@@ -184,11 +198,15 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
184
198
  let element: HTMLElement;
185
199
  let contentElement: HTMLElement | undefined;
186
200
  if (node.libraryKey && this.sduiDescriptor.library) {
187
- element = this.parseChild(this.sduiDescriptor.library[node.libraryKey] || {tagName: "div"});
188
- const selector = (this.sduiDescriptor.library[node.libraryKey] || {}).contentElementSelector;
189
- if (selector) contentElement = element.querySelector(selector) as HTMLElement;
201
+ element = this.parseChild(
202
+ this.sduiDescriptor.library[node.libraryKey] || { tagName: "div" }
203
+ );
204
+ const selector = (this.sduiDescriptor.library[node.libraryKey] || {})
205
+ .contentElementSelector;
206
+ if (selector)
207
+ contentElement = element.querySelector(selector) as HTMLElement;
190
208
  } else element = document.createElement(tagName) as HTMLElement;
191
- return {element, contentElement};
209
+ return { element, contentElement };
192
210
  }
193
211
  /**
194
212
  * Remplissage des attributs html avec les attributs fournis dans le noeud
@@ -197,7 +215,9 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
197
215
  const attributes = node.attributes;
198
216
  for (const k in attributes) {
199
217
  const attrData: object | string = attributes[k];
200
- const attr: string = Objects.isObject(attrData) ? JSON.stringify(attrData) : attrData;
218
+ const attr: string = Objects.isObject(attrData)
219
+ ? JSON.stringify(attrData)
220
+ : attrData;
201
221
  element.setAttribute(k, attr);
202
222
  }
203
223
  }
@@ -216,13 +236,18 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
216
236
  /**
217
237
  * si le noeud à une propriété innerHTML, on l'ajout ay innerHTML de l'élément html en cours de création
218
238
  */
219
- private handleInnerHTML(node: SDUINode, contentElement: HTMLElement | undefined) {
239
+ private handleInnerHTML(
240
+ node: SDUINode,
241
+ contentElement: HTMLElement | undefined
242
+ ) {
220
243
  if (!node.innerHTML) return;
221
244
  if (node.innerHTML.indexOf("wording_") != -1) {
222
245
  const wordingProvider = this.getAncestorAttributeValue("wordingProvider");
223
- this.api?.post(wordingProvider, {labels: [node.innerHTML.substring(8)]}).then((value) => {
224
- if (contentElement) contentElement.innerHTML += value;
225
- });
246
+ this.api
247
+ ?.post(wordingProvider, { labels: [node.innerHTML.substring(8)] })
248
+ .then((value) => {
249
+ if (contentElement) contentElement.innerHTML += value;
250
+ });
226
251
  } else if (contentElement) {
227
252
  contentElement.innerHTML += node.innerHTML;
228
253
  }
@@ -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() {
@@ -4,6 +4,7 @@ import {
4
4
  property,
5
5
  queryAssignedNodes,
6
6
  state,
7
+ query,
7
8
  } from "lit/decorators.js";
8
9
  import {
9
10
  FormInput,
@@ -23,6 +24,7 @@ import { Input } from "@supersoniks/concorde/core/components/ui/form/input/input
23
24
  import { customScroll } from "@supersoniks/concorde/core/components/ui/_css/scroll";
24
25
  import { ResizeController } from "@lit-labs/observers/resize-controller.js";
25
26
  import { traverseDotNotation } from "@supersoniks/concorde/core/utils/Objects";
27
+ import { Pop } from "../../pop/pop";
26
28
 
27
29
  type ListItem = Record<string, string>;
28
30
 
@@ -82,6 +84,7 @@ export class InputAutocomplete extends TemplatesContainer(
82
84
  slotInputPrefixNodes!: Array<Node>;
83
85
 
84
86
  @state() hasInputPrefix = false;
87
+ @query("sonic-pop") popElement!: Pop;
85
88
  _resizeController = new ResizeController(this, {});
86
89
 
87
90
  @state() isPopVisible = false;
@@ -324,9 +327,13 @@ export class InputAutocomplete extends TemplatesContainer(
324
327
  return this.searchPublisher?.get();
325
328
  }
326
329
 
330
+ handleFocus(): void {
331
+ this.popElement.show();
332
+ }
333
+
327
334
  render() {
328
335
  return html`
329
- <sonic-pop noToggle style="display:block;" @hide=${this.handleHide}>
336
+ <sonic-pop manual style="display:block;" @hide=${this.handleHide}>
330
337
  <sonic-input
331
338
  class="form-element"
332
339
  dataProvider="${this.initSearchDataProvider + Math.random()}"
@@ -344,6 +351,7 @@ export class InputAutocomplete extends TemplatesContainer(
344
351
  clearable
345
352
  inlineContent
346
353
  size=${this.size}
354
+ @focus=${this.handleFocus}
347
355
  value=${ifDefined(this.getInputValue())}
348
356
  >
349
357
  <slot
@@ -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>