@supersoniks/concorde 3.1.61 → 3.1.62

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 (217) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-core.bundle.js +187 -222
  3. package/concorde-core.es.js +1449 -1561
  4. package/dist/concorde-core.bundle.js +187 -222
  5. package/dist/concorde-core.es.js +1449 -1561
  6. package/package.json +24 -3
  7. package/scripts/generate-routes.js +206 -0
  8. package/scripts/post-build-docs.js +0 -0
  9. package/scripts/pre-publish.mjs +0 -0
  10. package/src/core/components/functional/date/date.ts +0 -0
  11. package/src/core/components/functional/functional.ts +0 -0
  12. package/src/core/components/functional/if/if.md +0 -0
  13. package/src/core/components/functional/if/if.test.ts +0 -0
  14. package/src/core/components/functional/list/list.demo.ts +58 -0
  15. package/src/core/components/functional/list/list.spec.ts +215 -0
  16. package/src/core/components/functional/list/list.ts +83 -32
  17. package/src/core/components/functional/queue/queue.demo.ts +38 -0
  18. package/src/core/components/functional/queue/queue.ts +7 -0
  19. package/src/core/components/functional/router/router.demo.ts +62 -0
  20. package/src/core/components/functional/router/router.md +22 -1
  21. package/src/core/components/functional/router/router.spec.ts +214 -0
  22. package/src/core/components/functional/router/router.ts +102 -8
  23. package/src/core/components/functional/sonic-scope/sonic-scope.ts +0 -0
  24. package/src/core/components/functional/states/states.demo.ts +83 -0
  25. package/src/core/components/functional/states/states.md +0 -0
  26. package/src/core/components/functional/states/states.spec.ts +141 -0
  27. package/src/core/components/functional/states/states.ts +74 -9
  28. package/src/core/components/functional/translation/translation.ts +0 -0
  29. package/src/core/components/ui/_css/scroll.ts +0 -0
  30. package/src/core/components/ui/_css/shadow.ts +0 -0
  31. package/src/core/components/ui/_css/size.ts +1 -1
  32. package/src/core/components/ui/_css/type.ts +0 -0
  33. package/src/core/components/ui/alert/alert.md +0 -0
  34. package/src/core/components/ui/alert/alert.ts +0 -0
  35. package/src/core/components/ui/alert-messages/alert-messages.md +0 -0
  36. package/src/core/components/ui/alert-messages/alert-messages.ts +0 -0
  37. package/src/core/components/ui/badge/badge.md +0 -0
  38. package/src/core/components/ui/button/button.md +0 -0
  39. package/src/core/components/ui/button/button.ts +5 -1
  40. package/src/core/components/ui/card/card-footer.ts +0 -0
  41. package/src/core/components/ui/card/card-header-descripton.ts +0 -0
  42. package/src/core/components/ui/card/card-header.ts +0 -0
  43. package/src/core/components/ui/card/card-main.ts +0 -0
  44. package/src/core/components/ui/card/card.md +0 -0
  45. package/src/core/components/ui/card/card.ts +0 -0
  46. package/src/core/components/ui/divider/divider.ts +0 -0
  47. package/src/core/components/ui/form/checkbox/checkbox.md +0 -0
  48. package/src/core/components/ui/form/checkbox/checkbox.ts +0 -0
  49. package/src/core/components/ui/form/css/form-control.ts +4 -1
  50. package/src/core/components/ui/form/fieldset/legend-description.ts +0 -0
  51. package/src/core/components/ui/form/fieldset/legend.ts +0 -0
  52. package/src/core/components/ui/form/form-layout/form-layout.md +0 -0
  53. package/src/core/components/ui/form/input/input.md +0 -0
  54. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.ts +0 -0
  55. package/src/core/components/ui/form/radio/radio.md +0 -0
  56. package/src/core/components/ui/form/radio/radio.ts +0 -0
  57. package/src/core/components/ui/form/select/select.md +0 -0
  58. package/src/core/components/ui/form/select/select.ts +0 -0
  59. package/src/core/components/ui/form/switch/switch.md +0 -0
  60. package/src/core/components/ui/form/textarea/textarea.ts +0 -0
  61. package/src/core/components/ui/group/group.ts +0 -0
  62. package/src/core/components/ui/icon/icon.ts +4 -0
  63. package/src/core/components/ui/icon/icons.ts +0 -0
  64. package/src/core/components/ui/image/image.md +0 -0
  65. package/src/core/components/ui/image/image.ts +0 -0
  66. package/src/core/components/ui/loader/loader.md +0 -0
  67. package/src/core/components/ui/loader/loader.ts +0 -0
  68. package/src/core/components/ui/loader/styles/fixed.ts +0 -0
  69. package/src/core/components/ui/loader/styles/inline.ts +0 -0
  70. package/src/core/components/ui/menu/menu.ts +0 -0
  71. package/src/core/components/ui/modal/modal-actions.ts +0 -0
  72. package/src/core/components/ui/modal/modal-subtitle.ts +0 -0
  73. package/src/core/components/ui/modal/modal-title.ts +0 -0
  74. package/src/core/components/ui/modal/modal.md +0 -0
  75. package/src/core/components/ui/modal/modal.ts +0 -0
  76. package/src/core/components/ui/pop/pop.ts +21 -0
  77. package/src/core/components/ui/progress/progress.ts +0 -0
  78. package/src/core/components/ui/table/table-tbody.ts +0 -0
  79. package/src/core/components/ui/table/table-th.ts +0 -0
  80. package/src/core/components/ui/table/table.ts +0 -0
  81. package/src/core/components/ui/theme/theme-collection/core-variables.ts +0 -0
  82. package/src/core/components/ui/theme/theme-collection/dark.ts +0 -0
  83. package/src/core/components/ui/theme/theme-collection/light.ts +0 -0
  84. package/src/core/components/ui/theme/theme.ts +0 -0
  85. package/src/core/components/ui/toast/message-subscriber.ts +0 -0
  86. package/src/core/components/ui/toast/toast-item.ts +0 -0
  87. package/src/core/components/ui/toast/toast.ts +0 -0
  88. package/src/core/components/ui/tooltip/tooltip.ts +0 -0
  89. package/src/core/components/ui/ui.ts +0 -0
  90. package/src/core/directives/DataProvider.ts +0 -0
  91. package/src/core/directives/Wording.ts +0 -0
  92. package/src/core/mixins/FormCheckable.ts +0 -0
  93. package/src/core/mixins/FormElement.ts +1 -1
  94. package/src/core/mixins/Subscriber.ts +0 -0
  95. package/src/core/mixins/TemplatesContainer.ts +19 -10
  96. package/src/core/utils/HTML.ts +0 -0
  97. package/src/core/utils/LocationHandler.ts +3 -1
  98. package/src/core/utils/Utils.ts +0 -0
  99. package/src/core/utils/route.ts +0 -0
  100. package/src/docs/code.ts +0 -0
  101. package/src/docs/docs.ts +0 -0
  102. package/src/docs/header/header.ts +0 -0
  103. package/src/docs/layout.ts +0 -0
  104. package/src/docs/navigation/navigation.ts +0 -0
  105. package/src/docs/search/docs-search.json +15 -0
  106. package/src/docs/search/markdown-renderer.ts +0 -0
  107. package/src/docs/search/page.ts +1 -0
  108. package/src/docs/search/search.ts +0 -0
  109. package/src/docs/tailwind/css/tailwind.css +0 -0
  110. package/src/docs/tailwind/css.d.ts +4 -0
  111. package/src/docs/tailwind/index.ts +2 -1
  112. package/src/test-utils/TestUtils.ts +2 -2
  113. package/src/tsconfig-model.json +1 -1
  114. package/src/tsconfig.json +69 -1
  115. package/src/tsconfig.tsbuildinfo +1 -1
  116. package/tailwind.config.js +0 -0
  117. package/templates-test.html +32 -0
  118. package/vite/config.js +30 -0
  119. package/docs/assets/index-D9bBwsCn.js +0 -4537
  120. package/docs/assets/index-DCRPZO3x.css +0 -1
  121. package/docs/css/docs.css +0 -0
  122. package/docs/fonts/ClashGrotesk-Bold.eot +0 -0
  123. package/docs/fonts/ClashGrotesk-Bold.ttf +0 -0
  124. package/docs/fonts/ClashGrotesk-Bold.woff +0 -0
  125. package/docs/fonts/ClashGrotesk-Bold.woff2 +0 -0
  126. package/docs/fonts/ClashGrotesk-Extralight.eot +0 -0
  127. package/docs/fonts/ClashGrotesk-Extralight.ttf +0 -0
  128. package/docs/fonts/ClashGrotesk-Extralight.woff +0 -0
  129. package/docs/fonts/ClashGrotesk-Extralight.woff2 +0 -0
  130. package/docs/fonts/ClashGrotesk-Light.eot +0 -0
  131. package/docs/fonts/ClashGrotesk-Light.ttf +0 -0
  132. package/docs/fonts/ClashGrotesk-Light.woff +0 -0
  133. package/docs/fonts/ClashGrotesk-Light.woff2 +0 -0
  134. package/docs/fonts/ClashGrotesk-Medium.eot +0 -0
  135. package/docs/fonts/ClashGrotesk-Medium.ttf +0 -0
  136. package/docs/fonts/ClashGrotesk-Medium.woff +0 -0
  137. package/docs/fonts/ClashGrotesk-Medium.woff2 +0 -0
  138. package/docs/fonts/ClashGrotesk-Regular.eot +0 -0
  139. package/docs/fonts/ClashGrotesk-Regular.ttf +0 -0
  140. package/docs/fonts/ClashGrotesk-Regular.woff +0 -0
  141. package/docs/fonts/ClashGrotesk-Regular.woff2 +0 -0
  142. package/docs/fonts/ClashGrotesk-Semibold.eot +0 -0
  143. package/docs/fonts/ClashGrotesk-Semibold.ttf +0 -0
  144. package/docs/fonts/ClashGrotesk-Semibold.woff +0 -0
  145. package/docs/fonts/ClashGrotesk-Semibold.woff2 +0 -0
  146. package/docs/fonts/ClashGrotesk-Variable.eot +0 -0
  147. package/docs/fonts/ClashGrotesk-Variable.ttf +0 -0
  148. package/docs/fonts/ClashGrotesk-Variable.woff +0 -0
  149. package/docs/fonts/ClashGrotesk-Variable.woff2 +0 -0
  150. package/docs/img/concorde-icon.svg +0 -5
  151. package/docs/img/concorde-logo.svg +0 -1
  152. package/docs/img/concorde.png +0 -0
  153. package/docs/img/concorde_def.png +0 -0
  154. package/docs/img/concorde_seuil.png.webp +0 -0
  155. package/docs/img/concorde_seuil_invert.png +0 -0
  156. package/docs/img/paul_metrand.jpg +0 -0
  157. package/docs/img/paul_metrand_xs.jpg +0 -0
  158. package/docs/index.html +0 -93
  159. package/docs/src/core/components/functional/date/date.md +0 -290
  160. package/docs/src/core/components/functional/fetch/fetch.md +0 -117
  161. package/docs/src/core/components/functional/if/if.md +0 -16
  162. package/docs/src/core/components/functional/list/list.md +0 -199
  163. package/docs/src/core/components/functional/mix/mix.md +0 -41
  164. package/docs/src/core/components/functional/queue/queue.md +0 -87
  165. package/docs/src/core/components/functional/router/router.md +0 -108
  166. package/docs/src/core/components/functional/sdui/default-library.json +0 -108
  167. package/docs/src/core/components/functional/sdui/example.json +0 -99
  168. package/docs/src/core/components/functional/sdui/sdui.md +0 -356
  169. package/docs/src/core/components/functional/states/states.md +0 -87
  170. package/docs/src/core/components/functional/submit/submit.md +0 -83
  171. package/docs/src/core/components/functional/subscriber/subscriber.md +0 -91
  172. package/docs/src/core/components/functional/value/value.md +0 -35
  173. package/docs/src/core/components/ui/alert/alert.md +0 -121
  174. package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -0
  175. package/docs/src/core/components/ui/badge/badge.md +0 -127
  176. package/docs/src/core/components/ui/button/button.md +0 -182
  177. package/docs/src/core/components/ui/captcha/captcha.md +0 -12
  178. package/docs/src/core/components/ui/card/card.md +0 -97
  179. package/docs/src/core/components/ui/divider/divider.md +0 -35
  180. package/docs/src/core/components/ui/form/checkbox/checkbox.md +0 -94
  181. package/docs/src/core/components/ui/form/fieldset/fieldset.md +0 -129
  182. package/docs/src/core/components/ui/form/form-actions/form-actions.md +0 -77
  183. package/docs/src/core/components/ui/form/form-layout/form-layout.md +0 -44
  184. package/docs/src/core/components/ui/form/input/input.md +0 -167
  185. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +0 -130
  186. package/docs/src/core/components/ui/form/radio/radio.md +0 -84
  187. package/docs/src/core/components/ui/form/select/select.md +0 -97
  188. package/docs/src/core/components/ui/form/switch/switch.md +0 -84
  189. package/docs/src/core/components/ui/form/textarea/textarea.md +0 -65
  190. package/docs/src/core/components/ui/group/group.md +0 -75
  191. package/docs/src/core/components/ui/icon/icon.md +0 -125
  192. package/docs/src/core/components/ui/icon/icons.json +0 -1
  193. package/docs/src/core/components/ui/image/image.md +0 -107
  194. package/docs/src/core/components/ui/link/link.md +0 -43
  195. package/docs/src/core/components/ui/loader/loader.md +0 -67
  196. package/docs/src/core/components/ui/menu/menu.md +0 -288
  197. package/docs/src/core/components/ui/modal/modal.md +0 -123
  198. package/docs/src/core/components/ui/pop/pop.md +0 -79
  199. package/docs/src/core/components/ui/progress/progress.md +0 -63
  200. package/docs/src/core/components/ui/table/table.md +0 -455
  201. package/docs/src/core/components/ui/tooltip/tooltip.md +0 -82
  202. package/docs/src/docs/_core-concept/overview.md +0 -57
  203. package/docs/src/docs/_core-concept/subscriber.md +0 -76
  204. package/docs/src/docs/_getting-started/concorde-outside.md +0 -143
  205. package/docs/src/docs/_getting-started/create-a-component.md +0 -137
  206. package/docs/src/docs/_getting-started/my-first-subscriber.md +0 -174
  207. package/docs/src/docs/_getting-started/pubsub.md +0 -150
  208. package/docs/src/docs/_getting-started/start.md +0 -39
  209. package/docs/src/docs/_getting-started/theming.md +0 -91
  210. package/docs/src/docs/search/docs-search.json +0 -3902
  211. package/docs/src/tag-list.json +0 -1
  212. package/docs/src/tsconfig-model.json +0 -23
  213. package/docs/src/tsconfig.json +0 -835
  214. package/docs/svg/regular/plane.svg +0 -1
  215. package/docs/svg/solid/plane.svg +0 -1
  216. package/php/get-challenge.php +0 -34
  217. package/php/some-service.php +0 -42
@@ -37,7 +37,7 @@ const tagName = "sonic-list";
37
37
  * *
38
38
  */
39
39
  export type ListItemMetadata = {
40
- key?: string;
40
+ key?: string | number;
41
41
  even?: boolean;
42
42
  odd?: boolean;
43
43
  onlyChild?: boolean;
@@ -45,6 +45,12 @@ export type ListItemMetadata = {
45
45
  lastChild?: boolean;
46
46
  };
47
47
 
48
+ // Simplifier le type ListItems pour n'être qu'une fonction
49
+ export type ListItems = (
50
+ item: any,
51
+ metadata: ListItemMetadata
52
+ ) => DirectiveResult;
53
+
48
54
  @customElement(tagName)
49
55
  export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
50
56
  @property({ type: Object }) itemPropertyMap?: object;
@@ -63,6 +69,10 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
63
69
  @property() limit = Number.POSITIVE_INFINITY;
64
70
  @property() offset = 0;
65
71
 
72
+ @property({ type: Object }) items?: ListItems;
73
+ @property({ type: Object }) noItems?: () => DirectiveResult;
74
+ @property({ type: Object }) skeleton?: () => DirectiveResult;
75
+
66
76
  connectedCallback() {
67
77
  this.noShadowDom = "";
68
78
  this.defferedDebug = this.hasAttribute("debug") || null;
@@ -84,6 +94,9 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
84
94
  return html`<sonic-loader mode=${loader}></sonic-loader>`;
85
95
  }
86
96
  renderSkeleton() {
97
+ if (this.skeleton) {
98
+ return this.skeleton();
99
+ }
87
100
  const template = this.templateParts["skeleton"];
88
101
  if (!(this.isLoading && template)) return nothing;
89
102
  return templateContent(template) as DirectiveResult;
@@ -207,6 +220,23 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
207
220
  `;
208
221
  }
209
222
 
223
+ private handleProgrammaticTemplates(
224
+ item: any,
225
+ metadata: ListItemMetadata,
226
+ key: string | number
227
+ ): DirectiveResult | null {
228
+ if (!this.items) return null;
229
+
230
+ return html`<sonic-subscriber
231
+ ?debug=${this.defferedDebug === true}
232
+ .bindPublisher=${() => this.publisher[key]}
233
+ .propertyMap?=${this.itemPropertyMap}
234
+ dataProvider="${this.dataProvider}/list-item/${key}"
235
+ >
236
+ ${this.items(item, metadata)}
237
+ </sonic-subscriber>`;
238
+ }
239
+
210
240
  renderContent() {
211
241
  /**
212
242
  * Loading
@@ -231,11 +261,19 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
231
261
 
232
262
  const props = this.formatProps();
233
263
  /**
234
- * On peut définir un template spécifique si le résultat est un tableau vide
264
+ * On peut définir un template spécifique si le résultat est un tableau vide
235
265
  */
236
- if ((props?.length || 0) == 0 && this.templateParts["no-item"]) {
237
- return templateContent(this.templateParts["no-item"]);
266
+ if ((props?.length || 0) == 0) {
267
+ if (this.noItems) {
268
+ return this.noItems();
269
+ }
270
+ this.templateParts["no-items"] =
271
+ this.templateParts["no-items"] || this.templateParts["no-item"];
272
+ if (this.templateParts["no-items"]) {
273
+ return templateContent(this.templateParts["no-items"]);
274
+ }
238
275
  }
276
+
239
277
  /**
240
278
  * Rendu des lignes
241
279
  * */
@@ -250,29 +288,25 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
250
288
  return html`
251
289
  ${items?.map((item, index: number) => {
252
290
  if (item == null) return nothing;
253
- let templatePart: HTMLTemplateElement | null = null;
254
291
  let key: number | string = index;
292
+ let templatePart: HTMLTemplateElement | null = null;
255
293
 
256
294
  if (typeof item == "object" && !Array.isArray(item)) {
295
+ if (extractValues) key = item?.["key"] as number | string;
296
+ // Récupérer le template par son nom si présent
257
297
  const templatePartName = item[this.templateKey];
258
298
  if (templatePartName && typeof templatePartName == "string") {
259
299
  templatePart = this.templateParts[templatePartName];
260
300
  }
261
- if (extractValues) key = item?.["key"] as number | string;
262
301
  }
263
302
  if (key == "_sonic_http_response_") return nothing;
264
303
  if (typeof key != "string" && typeof key != "number") return nothing;
304
+
265
305
  const isLastChild = index >= length - 1;
266
306
  const indexMod2 = index % 2;
267
307
  const childPublisher = this.publisher[key];
268
- /**
269
- * Ajout de metatdonnées au publisher de l'enfant
270
- */
271
- //La prop key est gardée pour le moment pour compatibilité
272
- //TODO : supprimer la prop key cquand c'est ok côté covoit.
273
- childPublisher._key_ = key + "";
274
- childPublisher._metadata_ = {
275
- ...childPublisher._metadata_.get(), //Si il y a d'autres données.
308
+
309
+ const metadata: ListItemMetadata = {
276
310
  key: key,
277
311
  even: indexMod2 == 0,
278
312
  odd: indexMod2 == 1,
@@ -280,26 +314,43 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
280
314
  firstChild: index == 0,
281
315
  lastChild: isLastChild,
282
316
  };
317
+
318
+ childPublisher._key_ = key + "";
319
+ childPublisher._metadata_ = {
320
+ ...childPublisher._metadata_.get(),
321
+ ...metadata,
322
+ };
323
+
324
+ // Essayer d'utiliser les templates programmatiques
325
+ const programmaticTemplate = this.handleProgrammaticTemplates(
326
+ item,
327
+ metadata,
328
+ key
329
+ );
330
+ if (programmaticTemplate) {
331
+ return html`${programmaticTemplate}${separator && !isLastChild
332
+ ? templateContent(separator)
333
+ : nothing}`;
334
+ }
335
+
336
+ // Sinon, utiliser le système de template existant
283
337
  counter++;
284
338
  if (templatePart) counter = -1;
285
- return (
286
- item &&
287
- html`
288
- <sonic-subscriber
289
- ?debug=${this.defferedDebug === true}
290
- .bindPublisher=${function () {
291
- return childPublisher;
292
- }}
293
- .propertyMap?=${this.itemPropertyMap}
294
- dataProvider="${this.dataProvider}/list-item/${key}"
295
- >
296
- ${templatePart
297
- ? templateContent(templatePart)
298
- : templateContent(this.templateList[counter % templateCount])}
299
- </sonic-subscriber>
300
- ${separator && !isLastChild ? templateContent(separator) : nothing}
301
- `
302
- );
339
+ return html`
340
+ <sonic-subscriber
341
+ ?debug=${this.defferedDebug === true}
342
+ .bindPublisher=${function () {
343
+ return childPublisher;
344
+ }}
345
+ .propertyMap?=${this.itemPropertyMap}
346
+ dataProvider="${this.dataProvider}/list-item/${key}"
347
+ >
348
+ ${templatePart
349
+ ? templateContent(templatePart)
350
+ : templateContent(this.templateList[counter % templateCount])}
351
+ </sonic-subscriber>
352
+ ${separator && !isLastChild ? templateContent(separator) : nothing}
353
+ `;
303
354
  })}
304
355
  `;
305
356
  }
@@ -0,0 +1,38 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit";
3
+ import { customElement } from "lit/decorators.js";
4
+ import "./queue";
5
+ import { tailwind } from "../../../../docs/tailwind";
6
+
7
+ @customElement("sonic-queue-demo")
8
+ export class QueueDemo extends LitElement {
9
+ static styles = [tailwind];
10
+
11
+ private items = (item: any) => html`
12
+ <div class="bg-neutral-100 p-2 rounded-md">
13
+ <div class="font-medium">${item.nom}</div>
14
+ <div class="text-sm text-gray-600">Code: ${item.code}</div>
15
+ </div>
16
+ `;
17
+ private noItems = () => html`
18
+ <div class="flex flex-col items-center justify-center p-4 text-gray-500">
19
+ <p>Aucune commune trouvée</p>
20
+ </div>
21
+ `;
22
+
23
+ render() {
24
+ return html`
25
+ <div class="p-4">
26
+ <h2 class="text-xl font-medium mb-4">Liste des communes</h2>
27
+ <sonic-queue
28
+ class="grid grid-cols-3 gap-3"
29
+ serviceurl="https://geo.api.gouv.fr/"
30
+ dataproviderexpression="communes?limit=$limit"
31
+ limit="30"
32
+ .items=${this.items}
33
+ .noItems=${this.noItems}
34
+ ></sonic-queue>
35
+ </div>
36
+ `;
37
+ }
38
+ }
@@ -6,6 +6,7 @@ import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProx
6
6
  import "@supersoniks/concorde/core/components/functional/list/list";
7
7
  import { PublisherProxy } from "@supersoniks/concorde/core/utils/PublisherProxy";
8
8
  import { HTML } from "@supersoniks/concorde/utils";
9
+ import { ListItems } from "@supersoniks/concorde/core/components/functional/list/list";
9
10
 
10
11
  type QueueItem = {
11
12
  id: string;
@@ -32,6 +33,9 @@ const tagName = "sonic-queue";
32
33
  export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
33
34
  @property({ type: Array }) templates: Array<HTMLTemplateElement> | null =
34
35
  null;
36
+ @property({ type: Object }) items: ListItems | null = null;
37
+ @property({ type: Object }) noItems: ListItems | null = null;
38
+ @property({ type: Object }) skeleton: ListItems | null = null;
35
39
  lastRequestTime = 0;
36
40
  key = "";
37
41
 
@@ -363,6 +367,9 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
363
367
  endPoint="${item.endPoint}"
364
368
  idKey=${this.idKey}
365
369
  .templates=${templates}
370
+ .items=${this.items}
371
+ .noItems=${this.noItems}
372
+ .skeleton=${this.skeleton}
366
373
  >
367
374
  </sonic-list>
368
375
  `;
@@ -0,0 +1,62 @@
1
+ import { html, LitElement } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import "./router";
4
+ import { Routes } from "@supersoniks/concorde/core/utils/route";
5
+
6
+ import { tailwind } from "../../../../docs/tailwind";
7
+ const defaultRoute = {
8
+ user: "#user/:id/:slug",
9
+ };
10
+ export type AppRoutes = typeof defaultRoute;
11
+
12
+ Routes.register<AppRoutes>(defaultRoute);
13
+
14
+ @customElement("sonic-router-demo")
15
+ export class RouterDemo extends LitElement {
16
+ static styles = [tailwind];
17
+
18
+ @state()
19
+ private routes = {
20
+ "#home": () => html`
21
+ <div class="p-4 border border-gray-200 rounded-md">
22
+ <h1 class="text-xl font-medium mb-2">Accueil</h1>
23
+ <p class="text-gray-600">Bienvenue sur la démo du router</p>
24
+ </div>
25
+ `,
26
+ [defaultRoute.user]: ({ slug, id }: { id: string; slug: string }) => html`
27
+ <div class="p-4 border border-gray-200 rounded-md">
28
+ <h1 class="text-xl font-medium mb-2">Profil Utilisateur ${id}</h1>
29
+ <p class="text-gray-600">Slug: ${slug}</p>
30
+ </div>
31
+ `,
32
+ "#products/(\\d+)/(\\w+)": ([id, slug]: [string, string]) => html`
33
+ <div class="p-4 border border-gray-200 rounded-md">
34
+ <h1 class="text-xl font-medium mb-2">Produit ${id}</h1>
35
+ <p class="text-gray-600">Slug: ${slug}</p>
36
+ </div>
37
+ `,
38
+ fallback: () => html`
39
+ <div class="p-4 border border-red-200 bg-red-50 rounded-md">
40
+ <h1 class="text-xl font-medium text-red-800 mb-2">404</h1>
41
+ <p class="text-red-600">Page non trouvée</p>
42
+ </div>
43
+ `,
44
+ };
45
+
46
+ render() {
47
+ return html`
48
+ <div class="p-4">
49
+ <h2 class="text-2xl font-medium mb-6">Démo du router</h2>
50
+
51
+ <div class="flex gap-4 mb-6">
52
+ <sonic-button href="#home"> Accueil </sonic-button>
53
+ <sonic-button href="#user/123/mySlug"> Utilisateur 123 </sonic-button>
54
+ <sonic-button href="#products/456/mySlug"> Produit 456 </sonic-button>
55
+ <sonic-button href="#invalid"> 404 </sonic-button>
56
+ </div>
57
+
58
+ <sonic-router .routes=${this.routes}></sonic-router>
59
+ </div>
60
+ `;
61
+ }
62
+ }
@@ -105,4 +105,25 @@ We are then redirected to the url *#data-is-set* which does nothing in itself.
105
105
  </template>
106
106
  </sonic-code>
107
107
 
108
- Example of use : use with a router and a submit to manage the steps of login/logout, display of user info.
108
+ Example of use : use with a router and a submit to manage the steps of login/logout, display of user info.
109
+
110
+ ## Fallback
111
+
112
+ The fallback route is rendered when no other route matches the current location.
113
+
114
+ <sonic-code>
115
+ <sonic-router>
116
+ <template data-route="#home">
117
+ <div>Home</div>
118
+ </template>
119
+ <template data-route="#fallback">
120
+ <div>Fallback</div>
121
+ </template>
122
+ </sonic-router>
123
+ </sonic-code>
124
+
125
+ ## Programmatic routes
126
+
127
+
128
+
129
+
@@ -0,0 +1,214 @@
1
+ import { expect } from "vitest";
2
+ import TestUtils from "@supersoniks/concorde/test-utils/TestUtils";
3
+ import "./router";
4
+ import { html } from "lit";
5
+ import { Routes } from "@supersoniks/concorde/core/utils/route";
6
+
7
+ function create(template = "", addToDocument = true) {
8
+ return TestUtils.bootstrap(
9
+ `
10
+ <sonic-router>
11
+ ${template}
12
+ </sonic-router>
13
+ `,
14
+ addToDocument
15
+ )[0];
16
+ }
17
+
18
+ describe("SonicRouter", () => {
19
+ it("devrait afficher le contenu du template avec une route regexp", async () => {
20
+ const elt: any = create(`
21
+ <template data-route="/utilisateur/\\d+">
22
+ <div>Page Utilisateur</div>
23
+ </template>
24
+ `);
25
+ elt.location = "/utilisateur/123";
26
+ await elt.updated();
27
+ expect(elt.textContent.trim()).toBe("Page Utilisateur");
28
+ });
29
+
30
+ it("devrait afficher le contenu avec dataProvider quand spécifié", async () => {
31
+ const elt: any = create(`
32
+ <template data-route="/utilisateur/(\\d+)" dataProviderExpression="/api/user/$1">
33
+ <div>Page Utilisateur</div>
34
+ </template>
35
+ `);
36
+ elt.location = "/utilisateur/123";
37
+ await elt.updated();
38
+ const divWrapper = elt.querySelector("div[dataProvider]");
39
+ expect(divWrapper.getAttribute("dataProvider")).toBe("/api/user/123");
40
+ });
41
+
42
+ it("devrait afficher le contenu du template avec url-pattern", async () => {
43
+ const elt: any = create(`
44
+ <template data-route="utilisateur/:id">
45
+ <div>Page Utilisateur Pattern</div>
46
+ </template>
47
+ `);
48
+ elt.location = "/utilisateur/123";
49
+ await elt.updated();
50
+ expect(elt.textContent.trim()).toBe("Page Utilisateur Pattern");
51
+ });
52
+
53
+ it("devrait afficher le fallback quand aucune route ne correspond", async () => {
54
+ const elt: any = create(`
55
+ <template data-route="/utilisateur/\\d+">
56
+ <div>Page Utilisateur</div>
57
+ </template>
58
+ <template data-fallback>
59
+ <div>Page 404</div>
60
+ </template>
61
+ `);
62
+ elt.location = "/page-inexistante";
63
+ await elt.updated();
64
+ expect(elt.textContent.trim()).toBe("Page 404");
65
+ });
66
+
67
+ it("devrait mettre à jour le titre de la page quand spécifié", async () => {
68
+ const elt: any = create(`
69
+ <template data-route="/accueil" title="Page d'accueil">
70
+ <div>Accueil</div>
71
+ </template>
72
+ `);
73
+ elt.location = "/accueil";
74
+ await elt.updated();
75
+ expect(document.title).toBe("Page d'accueil");
76
+ });
77
+
78
+ it("devrait gérer plusieurs templates correspondants", async () => {
79
+ const elt: any = create(`
80
+ <template data-route="/utilisateur">
81
+ <div>Base Utilisateur</div>
82
+ </template>
83
+ <template data-route="/utilisateur/(\\d+)">
84
+ <div>Détail Utilisateur</div>
85
+ </template>
86
+ `);
87
+ elt.location = "/utilisateur/123";
88
+ await elt.updated();
89
+ expect(elt.textContent.trim().replace(/\s+/gm, " ")).toBe(
90
+ "Base Utilisateur Détail Utilisateur"
91
+ );
92
+ });
93
+
94
+ it("devrait rediriger vers fallBackRoute si défini et aucune route ne correspond", async () => {
95
+ const elt: any = create(`
96
+ <template data-route="/utilisateur">
97
+ <div>Page Utilisateur</div>
98
+ </template>
99
+ `);
100
+ elt.fallBackRoute = "#/404";
101
+ document.location.href = "#/page-inexistante";
102
+ await elt.updated();
103
+ // On ne peut pas tester document.location.href directement à cause de jsdom
104
+ // Mais on peut vérifier que la propriété est bien définie
105
+ expect(document.location.href.replace(document.location.origin, "")).toBe(
106
+ "/#/404"
107
+ );
108
+ });
109
+
110
+ it("devrait gérer les templates fournis via l'attribut templates", async () => {
111
+ const elt: any = create("", false);
112
+ const template = document.createElement("template");
113
+ template.setAttribute("data-route", "/test");
114
+ template.innerHTML = "<div>Test Template</div>";
115
+ elt.templates = [template];
116
+ document.body.appendChild(elt);
117
+ elt.location = "/test";
118
+ await elt.updated();
119
+ expect(elt.textContent.trim()).toBe("Test Template");
120
+ });
121
+
122
+ it("devrait gérer le dataProvider avec url-pattern", async () => {
123
+ const elt: any = create(`
124
+ <template data-route="utilisateur/:id/profil/:section" dataProviderExpression="/api/user/:id/:section">
125
+ <div>Profil Utilisateur</div>
126
+ </template>
127
+ `);
128
+ elt.location = "/utilisateur/123/profil/preferences";
129
+ await elt.updated();
130
+ const divWrapper = elt.querySelector("div[dataProvider]");
131
+ expect(divWrapper.getAttribute("dataProvider")).toBe(
132
+ "/api/user/123/preferences"
133
+ );
134
+ });
135
+ });
136
+
137
+ describe("SonicRouter Programmatic", () => {
138
+ it("devrait supporter les routes programmées simples", async () => {
139
+ const elt: any = create();
140
+ const routes = {
141
+ home: () => html`<div>Accueil</div>`,
142
+ about: () => html`<div>À propos</div>`,
143
+ fallback: () => html`<div>404</div>`,
144
+ } satisfies Routes;
145
+
146
+ elt.routes = routes;
147
+ elt.location = "/home";
148
+ await elt.updated();
149
+ expect(elt.textContent.trim()).toBe("Accueil");
150
+ });
151
+
152
+ it("devrait supporter les paramètres avec url-pattern", async () => {
153
+ const elt: any = create("", false);
154
+ const routes = {
155
+ "user/:id/profile/:section": ({
156
+ id,
157
+ section,
158
+ }: {
159
+ id: number;
160
+ section: string;
161
+ }) => html` <div>Utilisateur ${id} - Section ${section}</div> `,
162
+ } satisfies Routes;
163
+
164
+ elt.routes = routes;
165
+ document.body.appendChild(elt);
166
+ elt.location = "/user/123/profile/settings";
167
+
168
+ await elt.updated();
169
+ expect(elt.textContent.trim()).toBe("Utilisateur 123 - Section settings");
170
+ });
171
+
172
+ it("devrait supporter les expressions régulières", async () => {
173
+ const elt: any = create();
174
+ const routes = {
175
+ "user/(\\d+)/post/(\\d+)": ([userId, postId]: number[]) =>
176
+ html` <div>User ${userId} Post ${postId}</div> `,
177
+ } satisfies Routes;
178
+
179
+ elt.routes = routes;
180
+ elt.location = "/user/123/post/456";
181
+ await elt.updated();
182
+ expect(elt.textContent.trim()).toBe("User 123 Post 456");
183
+ });
184
+
185
+ it("devrait utiliser le fallback quand aucune route ne correspond", async () => {
186
+ const elt: any = create();
187
+ const routes = {
188
+ home: () => html`<div>Accueil</div>`,
189
+ fallback: () => html`<div>Page Non Trouvée</div>`,
190
+ } satisfies Routes;
191
+
192
+ elt.routes = routes;
193
+ elt.location = "/invalid";
194
+ await elt.updated();
195
+ expect(elt.textContent.trim()).toBe("Page Non Trouvée");
196
+ });
197
+
198
+ it("devrait prioriser les routes programmées sur les templates HTML", async () => {
199
+ const elt: any = create(`
200
+ <template data-route="home">
201
+ <div>Accueil HTML</div>
202
+ </template>
203
+ `);
204
+
205
+ const routes = {
206
+ home: () => html`<div>Accueil Programmé</div>`,
207
+ } satisfies Routes;
208
+
209
+ elt.routes = routes;
210
+ elt.location = "/home";
211
+ await elt.updated();
212
+ expect(elt.textContent.trim()).toBe("Accueil Programmé");
213
+ });
214
+ });