@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.
- package/README.md +163 -0
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +86 -79
- package/concorde-core.es.js +424 -362
- package/dist/concorde-core.bundle.js +86 -79
- package/dist/concorde-core.es.js +424 -362
- package/docs/assets/index-C0K6xugr.css +1 -0
- package/docs/assets/index-Dgl1lJQo.js +4861 -0
- package/docs/css/docs.css +0 -0
- package/docs/fonts/ClashGrotesk-Bold.eot +0 -0
- package/docs/fonts/ClashGrotesk-Bold.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Bold.woff +0 -0
- package/docs/fonts/ClashGrotesk-Bold.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Extralight.eot +0 -0
- package/docs/fonts/ClashGrotesk-Extralight.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Extralight.woff +0 -0
- package/docs/fonts/ClashGrotesk-Extralight.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Light.eot +0 -0
- package/docs/fonts/ClashGrotesk-Light.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Light.woff +0 -0
- package/docs/fonts/ClashGrotesk-Light.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Medium.eot +0 -0
- package/docs/fonts/ClashGrotesk-Medium.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Medium.woff +0 -0
- package/docs/fonts/ClashGrotesk-Medium.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Regular.eot +0 -0
- package/docs/fonts/ClashGrotesk-Regular.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Regular.woff +0 -0
- package/docs/fonts/ClashGrotesk-Regular.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Semibold.eot +0 -0
- package/docs/fonts/ClashGrotesk-Semibold.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Semibold.woff +0 -0
- package/docs/fonts/ClashGrotesk-Semibold.woff2 +0 -0
- package/docs/fonts/ClashGrotesk-Variable.eot +0 -0
- package/docs/fonts/ClashGrotesk-Variable.ttf +0 -0
- package/docs/fonts/ClashGrotesk-Variable.woff +0 -0
- package/docs/fonts/ClashGrotesk-Variable.woff2 +0 -0
- package/docs/img/concorde-icon.svg +5 -0
- package/docs/img/concorde-logo.svg +1 -0
- package/docs/img/concorde.png +0 -0
- package/docs/img/concorde_def.png +0 -0
- package/docs/img/concorde_seuil.png.webp +0 -0
- package/docs/img/concorde_seuil_invert.png +0 -0
- package/docs/img/paul_metrand.jpg +0 -0
- package/docs/img/paul_metrand_xs.jpg +0 -0
- package/docs/index.html +93 -0
- package/docs/src/core/components/functional/date/date.md +290 -0
- package/docs/src/core/components/functional/fetch/fetch.md +117 -0
- package/docs/src/core/components/functional/if/if.md +16 -0
- package/docs/src/core/components/functional/list/list.md +199 -0
- package/docs/src/core/components/functional/mix/mix.md +41 -0
- package/docs/src/core/components/functional/queue/queue.md +87 -0
- package/docs/src/core/components/functional/router/router.md +129 -0
- package/docs/src/core/components/functional/sdui/default-library.json +108 -0
- package/docs/src/core/components/functional/sdui/example.json +99 -0
- package/docs/src/core/components/functional/sdui/sdui.md +356 -0
- package/docs/src/core/components/functional/states/states.md +87 -0
- package/docs/src/core/components/functional/submit/submit.md +83 -0
- package/docs/src/core/components/functional/subscriber/subscriber.md +91 -0
- package/docs/src/core/components/functional/value/value.md +35 -0
- package/docs/src/core/components/ui/alert/alert.md +121 -0
- package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -0
- package/docs/src/core/components/ui/badge/badge.md +127 -0
- package/docs/src/core/components/ui/button/button.md +182 -0
- package/docs/src/core/components/ui/captcha/captcha.md +24 -0
- package/docs/src/core/components/ui/card/card.md +97 -0
- package/docs/src/core/components/ui/divider/divider.md +35 -0
- package/docs/src/core/components/ui/form/checkbox/checkbox.md +104 -0
- package/docs/src/core/components/ui/form/fieldset/fieldset.md +129 -0
- package/docs/src/core/components/ui/form/form-actions/form-actions.md +77 -0
- package/docs/src/core/components/ui/form/form-layout/form-layout.md +44 -0
- package/docs/src/core/components/ui/form/input/input.md +167 -0
- package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +131 -0
- package/docs/src/core/components/ui/form/radio/radio.md +84 -0
- package/docs/src/core/components/ui/form/select/select.md +97 -0
- package/docs/src/core/components/ui/form/switch/switch.md +84 -0
- package/docs/src/core/components/ui/form/textarea/textarea.md +65 -0
- package/docs/src/core/components/ui/group/group.md +75 -0
- package/docs/src/core/components/ui/icon/icon.md +125 -0
- package/docs/src/core/components/ui/icon/icons.json +1 -0
- package/docs/src/core/components/ui/image/image.md +107 -0
- package/docs/src/core/components/ui/link/link.md +43 -0
- package/docs/src/core/components/ui/loader/loader.md +67 -0
- package/docs/src/core/components/ui/menu/menu.md +288 -0
- package/docs/src/core/components/ui/modal/modal.md +123 -0
- package/docs/src/core/components/ui/pop/pop.md +96 -0
- package/docs/src/core/components/ui/progress/progress.md +63 -0
- package/docs/src/core/components/ui/table/table.md +455 -0
- package/docs/src/core/components/ui/tooltip/tooltip.md +82 -0
- package/docs/src/docs/_core-concept/overview.md +57 -0
- package/docs/src/docs/_core-concept/subscriber.md +76 -0
- package/docs/src/docs/_getting-started/concorde-outside.md +143 -0
- package/docs/src/docs/_getting-started/create-a-component.md +137 -0
- package/docs/src/docs/_getting-started/my-first-subscriber.md +174 -0
- package/docs/src/docs/_getting-started/pubsub.md +150 -0
- package/docs/src/docs/_getting-started/start.md +39 -0
- package/docs/src/docs/_getting-started/theming.md +91 -0
- package/docs/src/docs/search/docs-search.json +3917 -0
- package/docs/src/tag-list.json +1 -0
- package/docs/src/tsconfig-model.json +23 -0
- package/docs/src/tsconfig.json +918 -0
- package/docs/svg/regular/plane.svg +1 -0
- package/docs/svg/solid/plane.svg +1 -0
- package/mcp-server/COMPARISON-MCP.md +176 -0
- package/mcp-server/README-MCP-NODEJS.md +284 -0
- package/mcp-server/README-MCP.md +114 -0
- package/mcp-server/README.md +127 -0
- package/mcp-server/TECHNICAL-DOCS.md +269 -0
- package/mcp-server/concorde-mcp-server.js +859 -0
- package/mcp-server/concorde-mcp-server.py +801 -0
- package/mcp-server/cursor-mcp-config-advanced.json +68 -0
- package/mcp-server/cursor-mcp-config-nodejs.json +74 -0
- package/mcp-server/cursor-mcp-config.json +11 -0
- package/mcp-server/install-mcp-nodejs.sh +104 -0
- package/mcp-server/install-mcp.sh +62 -0
- package/mcp-server/package-lock.json +147 -0
- package/mcp-server/package-mcp.json +40 -0
- package/mcp-server/package.json +40 -0
- package/mcp-server/test-mcp.js +107 -0
- package/mcp-server/test-mcp.py +73 -0
- package/package.json +2 -1
- package/php/get-challenge.php +34 -0
- package/php/some-service.php +42 -0
- package/src/core/components/functional/fetch/fetch.md +6 -0
- package/src/core/components/ui/form/checkbox/checkbox.ts +6 -0
- package/src/core/components/ui/form/radio/radio.ts +11 -1
- package/src/core/components/ui/icon/icons.ts +0 -0
- package/src/core/components/ui/menu/menu.md +0 -0
- package/src/core/components/ui/modal/modal-close.ts +0 -0
- package/src/core/components/ui/modal/modal.ts +0 -0
- package/src/core/components/ui/toast/toast-item.ts +68 -2
- package/src/core/components/ui/toast/toast.md +166 -0
- package/src/core/components/ui/toast/toast.ts +1 -1
- package/src/core/mixins/Fetcher.ts +31 -4
- package/src/docs/navigation/navigation.ts +1 -1
- 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.
|
|
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
|
|
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"
|
|
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="#">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
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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",
|