@abyss-project/commons-front-core 9.1.1 → 9.2.0
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 +35 -0
- package/catalog/catalog-entries.ts +7 -0
- package/catalog/pages/AvatarGroup.tsx +45 -0
- package/catalog/pages/CompanySearchField.tsx +113 -0
- package/catalog/pages/ConfirmDialog.tsx +43 -1
- package/catalog/pages/CopyField.tsx +24 -1
- package/catalog/pages/EntityImageField.tsx +124 -0
- package/catalog/pages/LineChart.tsx +118 -0
- package/catalog/pages/Pagination.tsx +52 -0
- package/catalog/pages/Section.tsx +16 -2
- package/catalog/pages/TeamAvatar.tsx +40 -0
- package/catalog/pages/UserPicker.tsx +124 -0
- package/catalog/pages/index.ts +14 -0
- package/dist/{AbyssCookieConsentProvider-B7-F6osY.js → AbyssCookieConsentProvider-DUYUEZaO.js} +94 -36
- package/dist/{CookieConsentSettings.component-DxQL4ULz.js → CookieConsentSettings.component-aVDMZLIs.js} +1 -1
- package/dist/components/data/LineChart/LineChart.d.ts +32 -0
- package/dist/components/data/LineChart/LineChart.spec.d.ts +1 -0
- package/dist/components/data/LineChart/line-chart-ticks.d.ts +31 -0
- package/dist/components/data/LineChart/line-chart-ticks.spec.d.ts +1 -0
- package/dist/components/data/Pagination/Pagination.d.ts +16 -0
- package/dist/components/data/Pagination/Pagination.spec.d.ts +1 -0
- package/dist/components/data/index.d.ts +2 -0
- package/dist/components/inputs/CompanySearchField/CompanySearchField.d.ts +24 -0
- package/dist/components/inputs/CompanySearchField/CompanySearchField.spec.d.ts +1 -0
- package/dist/components/inputs/CompanySearchField/pappers-company.d.ts +18 -0
- package/dist/components/inputs/CopyField/CopyField.d.ts +22 -6
- package/dist/components/inputs/EntityImageField/EntityImageField.d.ts +47 -0
- package/dist/components/inputs/EntityImageField/EntityImageField.spec.d.ts +1 -0
- package/dist/components/inputs/UserPicker/UserPicker.d.ts +50 -0
- package/dist/components/inputs/UserPicker/UserPicker.spec.d.ts +1 -0
- package/dist/components/inputs/index.d.ts +3 -0
- package/dist/components/layers/ConfirmDialog/useConfirmDialog.d.ts +21 -0
- package/dist/components/layers/ConfirmDialog/useConfirmDialog.spec.d.ts +1 -0
- package/dist/components/layers/index.d.ts +1 -0
- package/dist/components/meaning/AvatarGroup/AvatarGroup.d.ts +22 -0
- package/dist/components/meaning/AvatarGroup/AvatarGroup.spec.d.ts +1 -0
- package/dist/components/meaning/TeamAvatar/TeamAvatar.d.ts +14 -0
- package/dist/components/meaning/TeamAvatar/TeamAvatar.spec.d.ts +1 -0
- package/dist/components/meaning/index.d.ts +2 -0
- package/dist/components/structure/Section/Section.d.ts +10 -2
- package/dist/cookie-consent.js +2 -2
- package/dist/design-theme-DyWPbqXp.js +26515 -0
- package/dist/hooks/use-rich-intl.hook.d.ts +58 -0
- package/dist/{index-DflXNluH.js → index-77h-J0x1.js} +2 -2
- package/dist/{index-Bhhwuj39.js → index-BGIR-HuR.js} +2 -2
- package/dist/{index-DkcMwm_T.js → index-BM8u6b8w.js} +3 -3
- package/dist/{index-2zbZ2URx.js → index-BMgcTA0L.js} +3 -3
- package/dist/{index-C5OyIHCq.js → index-Bdv62RC2.js} +2 -2
- package/dist/{index-uGXjK3xv.js → index-BykEZXU6.js} +3 -3
- package/dist/{index-CsyAu658.js → index-C4plH_av.js} +2 -2
- package/dist/{index-uLh81uFU.js → index-C6YE_5pO.js} +2 -2
- package/dist/{index-BloHDADq.js → index-C6rmH0hM.js} +35694 -46623
- package/dist/{index-BuQlif6i.js → index-C9Vm0yCV.js} +2 -2
- package/dist/{index-BtzB7aR_.js → index-CAWnWyTQ.js} +2 -2
- package/dist/{index-CTP0QoCT.js → index-CX-2IC2j.js} +3 -3
- package/dist/{index-jhW25X__.js → index-CZj1-4RN.js} +2 -2
- package/dist/{index-RkXYGPSf.js → index-ChBWwrvm.js} +4 -4
- package/dist/{index-BMM6rvML.js → index-DKF4BSrW.js} +2 -2
- package/dist/{index-iQhqBjKY.js → index-DPsPJ7S5.js} +2 -2
- package/dist/{index-IooWxH41.js → index-LNg1bMNB.js} +2 -2
- package/dist/{index-Dew1ymFy.js → index-URaKKzT4.js} +2 -2
- package/dist/{index-BAcf97X_.js → index-UXYdJQT_.js} +2 -2
- package/dist/{index-BXtPl6pH.js → index-WINZVUn3.js} +1 -1
- package/dist/{index-CspGahq0.js → index-_kMEUAM7.js} +4 -4
- package/dist/{index-Waz6-pQr.js → index-elrbx7C5.js} +4 -4
- package/dist/index.js +5483 -4346
- package/dist/legacy/components/CompanySearchAutocomplete/CompanySearchAutocomplete.d.ts +2 -14
- package/dist/legacy/components/Layouts/AbyssFamilyBanner/AbyssFamilyBanner.component.d.ts +3 -7
- package/dist/legacy.js +61 -61
- package/dist/theme/tokens/semantic.d.ts +4 -1
- package/dist/translations/en.d.ts +29 -0
- package/dist/translations/fr.d.ts +29 -0
- package/dist/utils/family.d.ts +12 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/test-results/catalog/.last-run.json +1 -1
- package/dist/design-theme-BztWe75w.js +0 -15490
package/README.md
CHANGED
|
@@ -90,6 +90,41 @@ export const buildRumOptions = () =>
|
|
|
90
90
|
|
|
91
91
|
## Changements visibles
|
|
92
92
|
|
|
93
|
+
### 9.2.0
|
|
94
|
+
|
|
95
|
+
Organisation et projet du front principal sur le design system (carte #286, zone 3). Pièces nouvelles et évolutions,
|
|
96
|
+
chacune documentée dans le `README.md` de son dossier et montrée dans le catalogue.
|
|
97
|
+
|
|
98
|
+
- **`AvatarGroup`** (`meaning`) : pile d'avatars nommés, `max` au-delà duquel « +N » est nommé par les noms restants (`intl.formatList`) et les liste en infobulle ; aucun élément interactif. Remplace `UserAvatarList`.
|
|
99
|
+
- **`TeamAvatar`** (`meaning`) : initiale d'équipe sur sa couleur, encre `paper` ou `paper.on` la plus contrastée ; une couleur absente ou illisible (`CSS.supports`) donne la teinte neutre au lieu d'une exception (dette `DS-TEAM-COLOR`).
|
|
100
|
+
- **`Pagination`** (`data`) : précédent, numéros de page avec ellipses (sept places fixes), suivant ; page courante
|
|
101
|
+
en `aria-current="page"`, bornes désactivées, rien sous deux pages ; sous 768 px, précédent, « Page x sur y »,
|
|
102
|
+
suivant, cibles de 44 px.
|
|
103
|
+
- **`LineChart`** (`data`) : courbes Nivo sous `abyssNivoTheme`, échelle de temps ou linéaire, aire facultative ;
|
|
104
|
+
couleurs prises dans l'ordre de la palette de séries, jamais passées par l'hôte ; légende au-delà d'une série ;
|
|
105
|
+
figure nommée et tableau des valeurs pour les lecteurs d'écran ; « Aucune donnée à afficher » sans point.
|
|
106
|
+
Graduations de l'axe x selon la largeur mesurée du graphique (et non celle de l'écran) : pas le plus fin, jamais plus
|
|
107
|
+
fin que les données, dont les libellés mono gardent `space[8]` entre eux ; un libellé qui déborderait des marges
|
|
108
|
+
n'est pas affiché.
|
|
109
|
+
- **Palette de séries** (`theme`) : jetons `data.series.1` à `data.series.6` (et `--abyss-data-series-*`), ordre
|
|
110
|
+
exporté par `DATA_SERIES_TOKENS` ; luminosité et chroma égales, 3:1 au moins sur les fonds (ADR-0174, avenant).
|
|
111
|
+
- **`UserPicker`** (`inputs`) : choix simple ou multiple d'utilisateurs (`{ id, name, email, pictureUrl }`), avatar,
|
|
112
|
+
nom et e-mail par option ; filtre local sur le nom et l'e-mail, ou recherche serveur par `onSearch` avec `loading` ;
|
|
113
|
+
`disabledIds` ; en multiple, puces parcourues à la flèche gauche et retirées par Retour arrière ou Suppr ; sous
|
|
114
|
+
768 px, 44 px par option, puces de 44 px de haut dont la croix de retrait est un carré tactile, et texte du champ
|
|
115
|
+
jamais plus étroit que 44 px quand les puces remplissent la ligne.
|
|
116
|
+
- **`CompanySearchField`** (`inputs`) : contrat de `CompanySearchAutocomplete` (Pappers, `onSelect(PappersCompany)`,
|
|
117
|
+
erreur distincte d'une réponse vide, date de création dans la langue de l'utilisateur) sur `FormField` et les jetons
|
|
118
|
+
du core ; temporisation annulée au démontage, réponse dépassée ignorée.
|
|
119
|
+
- **Types sortis de `legacy/`** : `PappersCompany` et `CurrentFamilyInfo` (avec `CurrentFamilyStatus`) sont exportés de
|
|
120
|
+
la racine ; `/legacy` les réexporte.
|
|
121
|
+
- **Traductions** : clés `ds.inputs.user-picker.*` et `ds.inputs.company-search-field.*` (FR et EN).
|
|
122
|
+
- **`EntityImageField`** (`inputs`) : image d'entité changée sur place, `EntityImageVariant.Avatar` (pastille d'appareil photo, `EntityImageShape.Round` ou `Square`) ou `Banner` (bouton en surimpression) ; sélecteur de l'appareil, ou menu d'`ImportButton` avec `cloudImport` ; envoi indiqué, annoncé, et bloquant.
|
|
123
|
+
- **`useConfirmDialog`** et **`ConfirmDialogProvider`** (`layers`) : `confirm(options)` renvoie `Promise<boolean>` et ouvre le `ConfirmDialog` ; une demande à la fois, les suivantes attendent ; hors du fournisseur, une erreur qui le nomme.
|
|
124
|
+
- **`CopyField.loadValue`** : la valeur n'est demandée qu'au premier « Afficher » ou « Copier », puis gardée ; chargement sur le bouton, échec annoncé et rejouable. `value` et `loadValue` sont exclusifs.
|
|
125
|
+
- **`Section tone`** : `SectionTone.Danger` met le titre et un filet en `status.danger` (zones de danger).
|
|
126
|
+
- **`DataTable`, liste mobile** : sous 768 px, chaque contrôle de la colonne `trailing` (lien, bouton, `role="button"`, `role="link"`, `role="menuitem"`) offre une cible de 44 × 44 (`densityValues.touchTarget`), même rendu en `size="small"` par l'hôte ; rien ne change à partir de 768 px.
|
|
127
|
+
|
|
93
128
|
### 9.1.1
|
|
94
129
|
|
|
95
130
|
- **`useServiceTour`** : sous un `AbyssCookieConsentProvider`, une première visite attend que le bandeau de cookies
|
|
@@ -66,6 +66,8 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
|
|
|
66
66
|
entry('KeyValueList', 'data', 'key-value-list'),
|
|
67
67
|
entry('StatTile', 'data', 'stat-tile'),
|
|
68
68
|
entry('Sparkline', 'data', 'sparkline'),
|
|
69
|
+
entry('Pagination', 'data', 'pagination', RESPONSIVE),
|
|
70
|
+
entry('LineChart', 'data', 'line-chart'),
|
|
69
71
|
entry('ChartTheme', 'data', 'chart-theme'),
|
|
70
72
|
entry('UsageMeter', 'data', 'usage-meter'),
|
|
71
73
|
entry('StatusBadge', 'meaning', 'status-badge'),
|
|
@@ -75,6 +77,8 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
|
|
|
75
77
|
entry('RoleBadge', 'meaning', 'role-badge'),
|
|
76
78
|
entry('ToolIcon', 'meaning', 'tool-icon'),
|
|
77
79
|
entry('Avatar', 'meaning', 'avatar'),
|
|
80
|
+
entry('AvatarGroup', 'meaning', 'avatar-group'),
|
|
81
|
+
entry('TeamAvatar', 'meaning', 'team-avatar'),
|
|
78
82
|
entry('InfectedDownloadModal', 'business', 'infected-download-modal'),
|
|
79
83
|
entry('CloudImport', 'business', 'cloud-import', RESPONSIVE),
|
|
80
84
|
entry('ImportFromCloudModal', 'business', 'import-from-cloud-modal', {
|
|
@@ -97,6 +101,9 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
|
|
|
97
101
|
entry('NumberField', 'inputs', 'number-field'),
|
|
98
102
|
entry('SettingsRow', 'inputs', 'settings-row', RESPONSIVE),
|
|
99
103
|
entry('CopyField', 'inputs', 'copy-field', RESPONSIVE),
|
|
104
|
+
entry('UserPicker', 'inputs', 'user-picker', RESPONSIVE),
|
|
105
|
+
entry('CompanySearchField', 'inputs', 'company-search-field', RESPONSIVE),
|
|
106
|
+
entry('EntityImageField', 'inputs', 'entity-image-field', RESPONSIVE),
|
|
100
107
|
entry('ContextSelector', 'inputs', 'context-selector', RESPONSIVE),
|
|
101
108
|
entry('DropZone', 'inputs', 'drop-zone', RESPONSIVE),
|
|
102
109
|
entry('ImportButton', 'inputs', 'import-button'),
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AvatarGroup } from '../../src/components/meaning/AvatarGroup/AvatarGroup';
|
|
2
|
+
import type { AvatarGroupPerson } from '../../src/components/meaning/AvatarGroup/AvatarGroup';
|
|
3
|
+
import { LogoAbyssSmall } from '../../src/assets';
|
|
4
|
+
import { Example, Showcase } from '../frame';
|
|
5
|
+
|
|
6
|
+
const PEOPLE: AvatarGroupPerson[] = [
|
|
7
|
+
{ id: 'u1', name: 'Mehdi', pictureUrl: LogoAbyssSmall },
|
|
8
|
+
{ id: 'u2', name: 'Camille', pictureUrl: null },
|
|
9
|
+
{ id: 'u3', name: 'Lina', pictureUrl: null },
|
|
10
|
+
// A picture that cannot load: the initial stays.
|
|
11
|
+
{ id: 'u4', name: 'Paul', pictureUrl: '/catalogue-introuvable/u4.png' },
|
|
12
|
+
{ id: 'u5', name: 'Zoé', pictureUrl: null },
|
|
13
|
+
{ id: 'u6', name: 'Hugo', pictureUrl: null },
|
|
14
|
+
{ id: 'u7', name: 'Inès', pictureUrl: null },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export const AvatarGroupPage = () => (
|
|
18
|
+
<Showcase
|
|
19
|
+
family="Sens"
|
|
20
|
+
name="AvatarGroup"
|
|
21
|
+
description="Personnes en pile : la photo ou l'initiale, chaque avatar nommé. Au-delà de max, « +N » compte les autres et les nomme dans une infobulle. Rien n'est interactif."
|
|
22
|
+
>
|
|
23
|
+
<Example title="Tout le monde (sans max)" framed>
|
|
24
|
+
<AvatarGroup people={PEOPLE.slice(0, 4)} label="Membres de Design" />
|
|
25
|
+
</Example>
|
|
26
|
+
<Example title="max = 3 : « +4 », noms au survol" framed>
|
|
27
|
+
<AvatarGroup people={PEOPLE} max={3} label="Membres de Ops" />
|
|
28
|
+
</Example>
|
|
29
|
+
<Example title="Tailles : 22, 32 (défaut), 40" framed>
|
|
30
|
+
<AvatarGroup
|
|
31
|
+
people={PEOPLE}
|
|
32
|
+
max={4}
|
|
33
|
+
size={22}
|
|
34
|
+
label="Membres de Support"
|
|
35
|
+
/>
|
|
36
|
+
<AvatarGroup people={PEOPLE} max={4} label="Membres de Finance" />
|
|
37
|
+
<AvatarGroup
|
|
38
|
+
people={PEOPLE}
|
|
39
|
+
max={4}
|
|
40
|
+
size={40}
|
|
41
|
+
label="Membres de Ventes"
|
|
42
|
+
/>
|
|
43
|
+
</Example>
|
|
44
|
+
</Showcase>
|
|
45
|
+
);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { CompanySearchField } from '../../src/components/inputs/CompanySearchField/CompanySearchField';
|
|
3
|
+
import type { PappersCompany } from '../../src/components/inputs/CompanySearchField/pappers-company';
|
|
4
|
+
import { Example, LastEvent, Showcase, useEventLog } from '../frame';
|
|
5
|
+
|
|
6
|
+
const COMPANIES: PappersCompany[] = [
|
|
7
|
+
{
|
|
8
|
+
siren: '901234567',
|
|
9
|
+
nom_entreprise: 'ABYSS PROJECT',
|
|
10
|
+
date_creation: '2021-09-14',
|
|
11
|
+
forme_juridique: 'SAS',
|
|
12
|
+
domaine_activite: 'Édition de logiciels applicatifs',
|
|
13
|
+
siege: {
|
|
14
|
+
adresse: '12 rue de la Paix',
|
|
15
|
+
code_postal: '75002',
|
|
16
|
+
ville: 'Paris',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
siren: '812345678',
|
|
21
|
+
nom_entreprise:
|
|
22
|
+
'ABYSSAL CONSEIL ET ACCOMPAGNEMENT DES ENTREPRISES DE LA RÉGION AUVERGNE-RHÔNE-ALPES',
|
|
23
|
+
date_creation: '2016-02-03',
|
|
24
|
+
forme_juridique: 'SARL',
|
|
25
|
+
siege: { ville: 'Lyon' },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
siren: '723456789',
|
|
29
|
+
nom_entreprise: 'KARBON STUDIO',
|
|
30
|
+
date_creation: '2012-06-30',
|
|
31
|
+
forme_juridique: 'EURL',
|
|
32
|
+
domaine_activite: 'Activités photographiques',
|
|
33
|
+
siege: {
|
|
34
|
+
adresse: '4 quai des Chartrons',
|
|
35
|
+
code_postal: '33000',
|
|
36
|
+
ville: 'Bordeaux',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/** The text that makes the stand-in for Pappers fail, to show the error state. */
|
|
42
|
+
const FAILING_QUERY = 'panne';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pappers answered from memory while the page is shown: the catalog has no network. Typing
|
|
46
|
+
* « panne » shows the error state.
|
|
47
|
+
*/
|
|
48
|
+
const usePappersFromMemory = () => {
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const realFetch = window.fetch;
|
|
51
|
+
window.fetch = async (input, init) => {
|
|
52
|
+
const url = new URL(
|
|
53
|
+
typeof input === 'string' || input instanceof URL ? input : input.url,
|
|
54
|
+
);
|
|
55
|
+
if (url.hostname !== 'suggestions.pappers.fr') {
|
|
56
|
+
return realFetch(input, init);
|
|
57
|
+
}
|
|
58
|
+
const query = (url.searchParams.get('q') ?? '').toLowerCase();
|
|
59
|
+
if (query.includes(FAILING_QUERY)) {
|
|
60
|
+
return new Response(null, { status: 503 });
|
|
61
|
+
}
|
|
62
|
+
const found = COMPANIES.filter((company) =>
|
|
63
|
+
company.nom_entreprise.toLowerCase().includes(query),
|
|
64
|
+
);
|
|
65
|
+
return new Response(JSON.stringify({ resultats_nom_entreprise: found }), {
|
|
66
|
+
status: 200,
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
return () => {
|
|
71
|
+
window.fetch = realFetch;
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const CompanySearchFieldPage = () => {
|
|
77
|
+
const { log } = useEventLog();
|
|
78
|
+
const [name, setName] = useState('');
|
|
79
|
+
const [filled, setFilled] = useState('ABYSS PROJECT');
|
|
80
|
+
usePappersFromMemory();
|
|
81
|
+
return (
|
|
82
|
+
<Showcase
|
|
83
|
+
family="Saisie"
|
|
84
|
+
name="CompanySearchField"
|
|
85
|
+
description="Nom d'entreprise avec les suggestions de Pappers : forme juridique, SIREN, date de création, siège et activité. Une requête en échec met le champ en erreur, distincte d'une recherche sans résultat. Ici, Pappers répond de mémoire (« abyss », « karbon » ; « panne » simule une panne)."
|
|
86
|
+
>
|
|
87
|
+
<Example title="Recherche" maxWidth={640}>
|
|
88
|
+
<CompanySearchField
|
|
89
|
+
value={name}
|
|
90
|
+
onInputChange={setName}
|
|
91
|
+
onSelect={(company) => log(`entreprise ${company.siren}`)}
|
|
92
|
+
/>
|
|
93
|
+
</Example>
|
|
94
|
+
<Example title="Nom déjà saisi" maxWidth={640}>
|
|
95
|
+
<CompanySearchField
|
|
96
|
+
value={filled}
|
|
97
|
+
onInputChange={setFilled}
|
|
98
|
+
onSelect={(company) => log(`entreprise ${company.siren}`)}
|
|
99
|
+
label="Raison sociale"
|
|
100
|
+
/>
|
|
101
|
+
</Example>
|
|
102
|
+
<Example title="Inactif" maxWidth={640}>
|
|
103
|
+
<CompanySearchField
|
|
104
|
+
value="ABYSS PROJECT"
|
|
105
|
+
onInputChange={() => undefined}
|
|
106
|
+
onSelect={() => undefined}
|
|
107
|
+
disabled
|
|
108
|
+
/>
|
|
109
|
+
</Example>
|
|
110
|
+
<LastEvent />
|
|
111
|
+
</Showcase>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
@@ -4,7 +4,44 @@ import {
|
|
|
4
4
|
ConfirmDialog,
|
|
5
5
|
ConfirmDialogTone,
|
|
6
6
|
} from '../../src/components/layers/ConfirmDialog/ConfirmDialog';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ConfirmDialogProvider,
|
|
9
|
+
useConfirmDialog,
|
|
10
|
+
} from '../../src/components/layers/ConfirmDialog/useConfirmDialog';
|
|
11
|
+
import { Example, LastEvent, Row, Showcase, useEventLog } from '../frame';
|
|
12
|
+
|
|
13
|
+
/** Asks through `useConfirmDialog`, twice in a row: the second request waits for the first. */
|
|
14
|
+
const ImperativeConfirm = () => {
|
|
15
|
+
const { log } = useEventLog();
|
|
16
|
+
const { confirm } = useConfirmDialog();
|
|
17
|
+
const ask = async () => {
|
|
18
|
+
const removed = await confirm({
|
|
19
|
+
title: 'Supprimer l’équipe Design ?',
|
|
20
|
+
body: 'Ses 4 membres perdent les accès donnés par l’équipe.',
|
|
21
|
+
confirmLabel: 'Supprimer l’équipe',
|
|
22
|
+
tone: ConfirmDialogTone.Danger,
|
|
23
|
+
});
|
|
24
|
+
log(removed ? 'équipe supprimée' : 'suppression annulée');
|
|
25
|
+
};
|
|
26
|
+
const askTwice = () => {
|
|
27
|
+
void ask();
|
|
28
|
+
void confirm({
|
|
29
|
+
title: 'Quitter l’organisation ?',
|
|
30
|
+
body: 'Vous perdez l’accès à ses projets.',
|
|
31
|
+
confirmLabel: 'Quitter',
|
|
32
|
+
}).then((left) => log(left ? 'quitter' : 'rester'));
|
|
33
|
+
};
|
|
34
|
+
return (
|
|
35
|
+
<Row>
|
|
36
|
+
<Button variant="outlined" onClick={() => void ask()}>
|
|
37
|
+
Supprimer l’équipe
|
|
38
|
+
</Button>
|
|
39
|
+
<Button variant="outlined" onClick={askTwice}>
|
|
40
|
+
Deux demandes à la suite
|
|
41
|
+
</Button>
|
|
42
|
+
</Row>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
8
45
|
|
|
9
46
|
export const ConfirmDialogPage = () => {
|
|
10
47
|
const { log } = useEventLog();
|
|
@@ -20,6 +57,11 @@ export const ConfirmDialogPage = () => {
|
|
|
20
57
|
Supprimer Impôts 2026
|
|
21
58
|
</Button>
|
|
22
59
|
</Example>
|
|
60
|
+
<Example title="useConfirmDialog : confirm() renvoie une promesse">
|
|
61
|
+
<ConfirmDialogProvider>
|
|
62
|
+
<ImperativeConfirm />
|
|
63
|
+
</ConfirmDialogProvider>
|
|
64
|
+
</Example>
|
|
23
65
|
<LastEvent />
|
|
24
66
|
<ConfirmDialog
|
|
25
67
|
open={open}
|
|
@@ -7,7 +7,7 @@ export const CopyFieldPage = () => {
|
|
|
7
7
|
<Showcase
|
|
8
8
|
family="Saisie"
|
|
9
9
|
name="CopyField"
|
|
10
|
-
description="Valeur à copier, masquable : bouton de copie, annonce du résultat dans une région vivante, bouton pour afficher une valeur masquée."
|
|
10
|
+
description="Valeur à copier, masquable : bouton de copie, annonce du résultat dans une région vivante, bouton pour afficher une valeur masquée. Avec loadValue, la valeur n'est demandée qu'au premier « Afficher » ou « Copier »."
|
|
11
11
|
>
|
|
12
12
|
<Example title="Clé d'API, masquée" maxWidth={640}>
|
|
13
13
|
<CopyField
|
|
@@ -17,6 +17,29 @@ export const CopyFieldPage = () => {
|
|
|
17
17
|
copyLabel="Copier la clé"
|
|
18
18
|
/>
|
|
19
19
|
</Example>
|
|
20
|
+
<Example title="Jeton chargé au premier geste" maxWidth={640}>
|
|
21
|
+
<CopyField
|
|
22
|
+
loadValue={() =>
|
|
23
|
+
new Promise<string>((resolve) => {
|
|
24
|
+
log('jeton demandé');
|
|
25
|
+
setTimeout(() => resolve('abyss_env_tok_9c1e4b7a2f6d8e30'), 800);
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
masked
|
|
29
|
+
onReveal={() => log('afficher le jeton')}
|
|
30
|
+
copyLabel="Copier le jeton"
|
|
31
|
+
/>
|
|
32
|
+
</Example>
|
|
33
|
+
<Example title="Jeton dont le chargement échoue" maxWidth={640}>
|
|
34
|
+
<CopyField
|
|
35
|
+
loadValue={() => {
|
|
36
|
+
log('jeton refusé');
|
|
37
|
+
return Promise.reject(new Error('forbidden'));
|
|
38
|
+
}}
|
|
39
|
+
masked
|
|
40
|
+
copyLabel="Copier le jeton"
|
|
41
|
+
/>
|
|
42
|
+
</Example>
|
|
20
43
|
<Example title="Identifiant de connexion IA" maxWidth={640}>
|
|
21
44
|
<CopyField
|
|
22
45
|
value="https://mcp.abyss-project.fr/u/7f3a9c2e"
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { styled } from '@mui/material/styles';
|
|
2
|
+
import {
|
|
3
|
+
EntityImageField,
|
|
4
|
+
EntityImageShape,
|
|
5
|
+
EntityImageVariant,
|
|
6
|
+
} from '../../src/components/inputs/EntityImageField/EntityImageField';
|
|
7
|
+
import {
|
|
8
|
+
OrganizationAvatar,
|
|
9
|
+
UserAvatar,
|
|
10
|
+
} from '../../src/components/meaning/Avatar/Avatar';
|
|
11
|
+
import { LogoAbyssSmall } from '../../src/assets';
|
|
12
|
+
import { px } from '../../src/theme/components/shared';
|
|
13
|
+
import { Example, LastEvent, Row, Showcase, useEventLog } from '../frame';
|
|
14
|
+
import { CATALOG_CLOUD_PICKER } from '../cloud-picker-fixture';
|
|
15
|
+
|
|
16
|
+
const AVATAR_SIZE = 72;
|
|
17
|
+
|
|
18
|
+
const picture = () => LogoAbyssSmall;
|
|
19
|
+
|
|
20
|
+
/** A stand-in banner image: the brand gradient over the width. */
|
|
21
|
+
const BannerPicture = styled('div')(({ theme }) => {
|
|
22
|
+
const { tokens, space } = theme.design;
|
|
23
|
+
return {
|
|
24
|
+
height: px(space[10] * 2),
|
|
25
|
+
backgroundImage: `linear-gradient(120deg, ${tokens['brand.gradientFrom']}, ${tokens['brand.gradientTo']})`,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const EntityImageFieldPage = () => {
|
|
30
|
+
const { log } = useEventLog();
|
|
31
|
+
return (
|
|
32
|
+
<Showcase
|
|
33
|
+
family="Saisie"
|
|
34
|
+
name="EntityImageField"
|
|
35
|
+
description="L'image d'une entité, changée sur place : l'avatar est lui-même le bouton (pastille d'appareil photo), la bannière porte un bouton en surimpression. Sans AbyssCloud, le bouton ouvre le sélecteur de fichiers ; avec, le menu des sources."
|
|
36
|
+
>
|
|
37
|
+
<Example title="Avatars : photo ronde avec AbyssCloud, logo carré sans">
|
|
38
|
+
<Row>
|
|
39
|
+
<EntityImageField
|
|
40
|
+
variant={EntityImageVariant.Avatar}
|
|
41
|
+
picture={
|
|
42
|
+
<UserAvatar
|
|
43
|
+
userId="u1"
|
|
44
|
+
alias="Mehdi"
|
|
45
|
+
hasThumbnail
|
|
46
|
+
size={AVATAR_SIZE}
|
|
47
|
+
getProfilePictureUrl={picture}
|
|
48
|
+
/>
|
|
49
|
+
}
|
|
50
|
+
label="Changer la photo"
|
|
51
|
+
accept={['image/*']}
|
|
52
|
+
onFile={(file) => log(`fichier ${file.name}`)}
|
|
53
|
+
uploading={false}
|
|
54
|
+
cloudImport={{
|
|
55
|
+
...CATALOG_CLOUD_PICKER,
|
|
56
|
+
onImport: (_space, files) =>
|
|
57
|
+
log(`importé ${files.map((file) => file.name).join(', ')}`),
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
<EntityImageField
|
|
61
|
+
variant={EntityImageVariant.Avatar}
|
|
62
|
+
shape={EntityImageShape.Square}
|
|
63
|
+
picture={
|
|
64
|
+
<OrganizationAvatar
|
|
65
|
+
orgId="o1"
|
|
66
|
+
name="Abyss"
|
|
67
|
+
hasThumbnail={false}
|
|
68
|
+
size={AVATAR_SIZE}
|
|
69
|
+
getLogoUrl={picture}
|
|
70
|
+
/>
|
|
71
|
+
}
|
|
72
|
+
label="Changer le logo"
|
|
73
|
+
accept={['image/*']}
|
|
74
|
+
onFile={(file) => log(`fichier ${file.name}`)}
|
|
75
|
+
uploading={false}
|
|
76
|
+
/>
|
|
77
|
+
</Row>
|
|
78
|
+
</Example>
|
|
79
|
+
<Example title="Envoi en cours">
|
|
80
|
+
<EntityImageField
|
|
81
|
+
variant={EntityImageVariant.Avatar}
|
|
82
|
+
shape={EntityImageShape.Square}
|
|
83
|
+
picture={
|
|
84
|
+
<OrganizationAvatar
|
|
85
|
+
orgId="o2"
|
|
86
|
+
name="Atlas"
|
|
87
|
+
hasThumbnail={false}
|
|
88
|
+
size={AVATAR_SIZE}
|
|
89
|
+
getLogoUrl={picture}
|
|
90
|
+
/>
|
|
91
|
+
}
|
|
92
|
+
label="Changer le logo"
|
|
93
|
+
onFile={(file) => log(`fichier ${file.name}`)}
|
|
94
|
+
uploading
|
|
95
|
+
/>
|
|
96
|
+
</Example>
|
|
97
|
+
<Example title="Bannière d'organisation" maxWidth={720}>
|
|
98
|
+
<EntityImageField
|
|
99
|
+
variant={EntityImageVariant.Banner}
|
|
100
|
+
picture={<BannerPicture />}
|
|
101
|
+
label="Changer la bannière"
|
|
102
|
+
accept={['image/*']}
|
|
103
|
+
onFile={(file) => log(`bannière ${file.name}`)}
|
|
104
|
+
uploading={false}
|
|
105
|
+
cloudImport={{
|
|
106
|
+
...CATALOG_CLOUD_PICKER,
|
|
107
|
+
onImport: (_space, files) =>
|
|
108
|
+
log(`bannière ${files.map((file) => file.name).join(', ')}`),
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
</Example>
|
|
112
|
+
<Example title="Bannière, envoi en cours" maxWidth={720}>
|
|
113
|
+
<EntityImageField
|
|
114
|
+
variant={EntityImageVariant.Banner}
|
|
115
|
+
picture={<BannerPicture />}
|
|
116
|
+
label="Changer la bannière"
|
|
117
|
+
onFile={(file) => log(`bannière ${file.name}`)}
|
|
118
|
+
uploading
|
|
119
|
+
/>
|
|
120
|
+
</Example>
|
|
121
|
+
<LastEvent />
|
|
122
|
+
</Showcase>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { LineChart } from '../../src/components/data/LineChart/LineChart';
|
|
2
|
+
import type {
|
|
3
|
+
LineChartSeries,
|
|
4
|
+
LineChartX,
|
|
5
|
+
} from '../../src/components/data/LineChart/LineChart';
|
|
6
|
+
import { Surface } from '../../src/components/structure/Surface/Surface';
|
|
7
|
+
import { Example, Showcase } from '../frame';
|
|
8
|
+
|
|
9
|
+
const START = new Date(2026, 9, 13, 0);
|
|
10
|
+
|
|
11
|
+
const atHour = (hour: number) =>
|
|
12
|
+
new Date(START.getTime() + hour * 60 * 60 * 1000);
|
|
13
|
+
|
|
14
|
+
const REQUESTS = [
|
|
15
|
+
42, 31, 24, 19, 17, 22, 48, 96, 184, 236, 262, 248, 231, 244, 268, 281, 254,
|
|
16
|
+
212, 168, 131, 104, 86, 67, 51,
|
|
17
|
+
];
|
|
18
|
+
const ERRORS = [
|
|
19
|
+
1, 0, 0, 0, 1, 0, 2, 4, 9, 6, 5, 4, 12, 18, 7, 5, 4, 3, 3, 2, 1, 1, 0, 1,
|
|
20
|
+
];
|
|
21
|
+
const LATENCY = [
|
|
22
|
+
88, 84, 81, 80, 79, 83, 94, 118, 141, 152, 158, 155, 149, 162, 171, 166, 150,
|
|
23
|
+
133, 117, 104, 97, 93, 90, 89,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const hourly = (
|
|
27
|
+
id: string,
|
|
28
|
+
label: string,
|
|
29
|
+
values: number[],
|
|
30
|
+
): LineChartSeries => ({
|
|
31
|
+
id,
|
|
32
|
+
label,
|
|
33
|
+
points: values.map((y, hour) => ({ x: atHour(hour), y })),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const SIX_SERIES: LineChartSeries[] = [
|
|
37
|
+
'Atlas',
|
|
38
|
+
'Board',
|
|
39
|
+
'Draw',
|
|
40
|
+
'Beam',
|
|
41
|
+
'Extract',
|
|
42
|
+
'ShortLink',
|
|
43
|
+
].map((label, index) => ({
|
|
44
|
+
id: label.toLowerCase(),
|
|
45
|
+
label,
|
|
46
|
+
points: Array.from({ length: 12 }, (_, month) => ({
|
|
47
|
+
x: month + 1,
|
|
48
|
+
y: 20 + index * 12 + Math.round(10 * Math.sin((month + index) / 2)),
|
|
49
|
+
})),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const time = new Intl.DateTimeFormat('fr-FR', {
|
|
53
|
+
hour: '2-digit',
|
|
54
|
+
minute: '2-digit',
|
|
55
|
+
});
|
|
56
|
+
const count = new Intl.NumberFormat('fr-FR');
|
|
57
|
+
|
|
58
|
+
const formatHour = (x: LineChartX) =>
|
|
59
|
+
time.format(x instanceof Date ? x : new Date(x));
|
|
60
|
+
const formatMonth = (x: LineChartX) =>
|
|
61
|
+
`M${count.format(x instanceof Date ? x.getMonth() + 1 : x)}`;
|
|
62
|
+
|
|
63
|
+
export const LineChartPage = () => (
|
|
64
|
+
<Showcase
|
|
65
|
+
family="Données"
|
|
66
|
+
name="LineChart"
|
|
67
|
+
description="Courbes Nivo sous abyssNivoTheme : couleurs prises dans l'ordre de la palette data.series, légende au-delà d'une série, figure nommée et tableau des valeurs pour les lecteurs d'écran."
|
|
68
|
+
>
|
|
69
|
+
<Example title="Une série, aire">
|
|
70
|
+
<Surface padding={4}>
|
|
71
|
+
<LineChart
|
|
72
|
+
series={[hourly('requests', 'Requêtes', REQUESTS)]}
|
|
73
|
+
height={240}
|
|
74
|
+
area
|
|
75
|
+
formatX={formatHour}
|
|
76
|
+
formatY={(y) => count.format(y)}
|
|
77
|
+
label="Requêtes par heure, sur 24 heures"
|
|
78
|
+
/>
|
|
79
|
+
</Surface>
|
|
80
|
+
</Example>
|
|
81
|
+
<Example title="Deux séries">
|
|
82
|
+
<Surface padding={4}>
|
|
83
|
+
<LineChart
|
|
84
|
+
series={[
|
|
85
|
+
hourly('latency', 'Latence p95', LATENCY),
|
|
86
|
+
hourly('errors', 'Erreurs', ERRORS),
|
|
87
|
+
]}
|
|
88
|
+
height={240}
|
|
89
|
+
formatX={formatHour}
|
|
90
|
+
formatY={(y) => count.format(y)}
|
|
91
|
+
label="Latence et erreurs par heure"
|
|
92
|
+
/>
|
|
93
|
+
</Surface>
|
|
94
|
+
</Example>
|
|
95
|
+
<Example title="Palette des six séries">
|
|
96
|
+
<Surface padding={4}>
|
|
97
|
+
<LineChart
|
|
98
|
+
series={SIX_SERIES}
|
|
99
|
+
height={240}
|
|
100
|
+
formatX={formatMonth}
|
|
101
|
+
formatY={(y) => count.format(y)}
|
|
102
|
+
label="Contenus créés par outil et par mois"
|
|
103
|
+
/>
|
|
104
|
+
</Surface>
|
|
105
|
+
</Example>
|
|
106
|
+
<Example title="Aucune donnée">
|
|
107
|
+
<Surface padding={4}>
|
|
108
|
+
<LineChart
|
|
109
|
+
series={[{ id: 'requests', label: 'Requêtes', points: [] }]}
|
|
110
|
+
height={160}
|
|
111
|
+
formatX={formatHour}
|
|
112
|
+
formatY={(y) => count.format(y)}
|
|
113
|
+
label="Requêtes par heure"
|
|
114
|
+
/>
|
|
115
|
+
</Surface>
|
|
116
|
+
</Example>
|
|
117
|
+
</Showcase>
|
|
118
|
+
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Pagination } from '../../src/components/data/Pagination/Pagination';
|
|
4
|
+
import { Example, LastEvent, Showcase, useEventLog } from '../frame';
|
|
5
|
+
|
|
6
|
+
/** A paged list the reader can walk through: the page is held here, as a host would. */
|
|
7
|
+
const LivePagination = ({
|
|
8
|
+
initialPage,
|
|
9
|
+
pageCount,
|
|
10
|
+
label,
|
|
11
|
+
}: {
|
|
12
|
+
initialPage: number;
|
|
13
|
+
pageCount: number;
|
|
14
|
+
label: string;
|
|
15
|
+
}) => {
|
|
16
|
+
const { log } = useEventLog();
|
|
17
|
+
const [page, setPage] = useState(initialPage);
|
|
18
|
+
return (
|
|
19
|
+
<Pagination
|
|
20
|
+
page={page}
|
|
21
|
+
pageCount={pageCount}
|
|
22
|
+
label={label}
|
|
23
|
+
onChange={(next) => {
|
|
24
|
+
setPage(next);
|
|
25
|
+
log(`page ${next}`);
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const PaginationPage = () => (
|
|
32
|
+
<Showcase
|
|
33
|
+
family="Données"
|
|
34
|
+
name="Pagination"
|
|
35
|
+
description="Navigation entre les pages d'une liste : précédent, numéros avec ellipses, suivant ; sous 768 px, précédent, « Page x sur y », suivant."
|
|
36
|
+
>
|
|
37
|
+
<Example title="Cinq pages">
|
|
38
|
+
<LivePagination initialPage={2} pageCount={5} label="Pages des membres" />
|
|
39
|
+
</Example>
|
|
40
|
+
<Example title="Quarante pages, au milieu">
|
|
41
|
+
<LivePagination
|
|
42
|
+
initialPage={18}
|
|
43
|
+
pageCount={40}
|
|
44
|
+
label="Pages des permissions"
|
|
45
|
+
/>
|
|
46
|
+
</Example>
|
|
47
|
+
<Example title="Quarante pages, près du début">
|
|
48
|
+
<LivePagination initialPage={3} pageCount={40} label="Pages du journal" />
|
|
49
|
+
</Example>
|
|
50
|
+
<LastEvent />
|
|
51
|
+
</Showcase>
|
|
52
|
+
);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import Button from '@mui/material/Button';
|
|
2
2
|
import ArrowForwardRounded from '@mui/icons-material/ArrowForwardRounded';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Section,
|
|
5
|
+
SectionTone,
|
|
6
|
+
} from '../../src/components/structure/Section/Section';
|
|
4
7
|
import { KeyValueList } from '../../src/components/data/KeyValueList/KeyValueList';
|
|
5
8
|
import { Columns, Example, LastEvent, Showcase, useEventLog } from '../frame';
|
|
6
9
|
import { ACCOUNTS, LONG_NAV_LABEL, formatEuro } from '../fixtures';
|
|
@@ -12,7 +15,7 @@ export const SectionPage = () => {
|
|
|
12
15
|
<Showcase
|
|
13
16
|
family="Structure"
|
|
14
17
|
name="Section"
|
|
15
|
-
description="Bloc titré d'une page : titre de 16 px, compteur en mono, actions alignées à droite."
|
|
18
|
+
description="Bloc titré d'une page : titre de 16 px, compteur en mono, actions alignées à droite. En ton danger, titre et filet en rouge."
|
|
16
19
|
>
|
|
17
20
|
<Example title="Avec compteur et action">
|
|
18
21
|
<Section
|
|
@@ -67,6 +70,17 @@ export const SectionPage = () => {
|
|
|
67
70
|
/>
|
|
68
71
|
</Section>
|
|
69
72
|
</Example>
|
|
73
|
+
<Example title="Zone de danger">
|
|
74
|
+
<Section title="Zone de danger" tone={SectionTone.Danger}>
|
|
75
|
+
<Button
|
|
76
|
+
variant="outlined"
|
|
77
|
+
color="error"
|
|
78
|
+
onClick={() => log('supprimer l’organisation')}
|
|
79
|
+
>
|
|
80
|
+
Supprimer l’organisation
|
|
81
|
+
</Button>
|
|
82
|
+
</Section>
|
|
83
|
+
</Example>
|
|
70
84
|
<Example title="Titre de 200 caractères sans espace">
|
|
71
85
|
<Section title={LONG_NAV_LABEL} count={12}>
|
|
72
86
|
<LastEvent />
|