@abyss-project/commons-front-core 9.1.1 → 9.3.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.
Files changed (97) hide show
  1. package/README.md +63 -0
  2. package/catalog/catalog-entries.ts +12 -0
  3. package/catalog/pages/AmbientBackground.tsx +83 -0
  4. package/catalog/pages/AvatarGroup.tsx +45 -0
  5. package/catalog/pages/BarChart.tsx +127 -0
  6. package/catalog/pages/CompanySearchField.tsx +113 -0
  7. package/catalog/pages/ConfirmDialog.tsx +43 -1
  8. package/catalog/pages/ConfirmDialogWarning.tsx +45 -0
  9. package/catalog/pages/CopyField.tsx +24 -1
  10. package/catalog/pages/EntityImageField.tsx +124 -0
  11. package/catalog/pages/LineChart.tsx +138 -0
  12. package/catalog/pages/Pagination.tsx +52 -0
  13. package/catalog/pages/Section.tsx +16 -2
  14. package/catalog/pages/TeamAvatar.tsx +40 -0
  15. package/catalog/pages/UserPicker.tsx +124 -0
  16. package/catalog/pages/index.ts +20 -0
  17. package/dist/{AbyssCookieConsentProvider-B7-F6osY.js → AbyssCookieConsentProvider-CeDdkoPq.js} +104 -36
  18. package/dist/{CookieConsentSettings.component-DxQL4ULz.js → CookieConsentSettings.component-tJE0g-SS.js} +1 -1
  19. package/dist/components/data/BarChart/BarChart.d.ts +47 -0
  20. package/dist/components/data/BarChart/BarChart.spec.d.ts +1 -0
  21. package/dist/components/data/DataTable/DataTableDesktop.d.ts +1 -1
  22. package/dist/components/data/DataTable/DataTableMobile.d.ts +1 -1
  23. package/dist/components/data/DataTable/data-table.types.d.ts +6 -0
  24. package/dist/components/data/LineChart/LineChart.d.ts +44 -0
  25. package/dist/components/data/LineChart/LineChart.spec.d.ts +1 -0
  26. package/dist/components/data/LineChart/line-chart-ticks.d.ts +31 -0
  27. package/dist/components/data/LineChart/line-chart-ticks.spec.d.ts +1 -0
  28. package/dist/components/data/Pagination/Pagination.d.ts +16 -0
  29. package/dist/components/data/Pagination/Pagination.spec.d.ts +1 -0
  30. package/dist/components/data/chart-series-tone.d.ts +21 -0
  31. package/dist/components/data/chart-series-tone.spec.d.ts +1 -0
  32. package/dist/components/data/index.d.ts +4 -0
  33. package/dist/components/inputs/ColorField/ColorField.d.ts +8 -5
  34. package/dist/components/inputs/CompanySearchField/CompanySearchField.d.ts +24 -0
  35. package/dist/components/inputs/CompanySearchField/CompanySearchField.spec.d.ts +1 -0
  36. package/dist/components/inputs/CompanySearchField/pappers-company.d.ts +18 -0
  37. package/dist/components/inputs/CopyField/CopyField.d.ts +22 -6
  38. package/dist/components/inputs/EntityImageField/EntityImageField.d.ts +47 -0
  39. package/dist/components/inputs/EntityImageField/EntityImageField.spec.d.ts +1 -0
  40. package/dist/components/inputs/UserPicker/UserPicker.d.ts +50 -0
  41. package/dist/components/inputs/UserPicker/UserPicker.spec.d.ts +1 -0
  42. package/dist/components/inputs/index.d.ts +3 -0
  43. package/dist/components/layers/ConfirmDialog/ConfirmDialog.d.ts +6 -2
  44. package/dist/components/layers/ConfirmDialog/useConfirmDialog.d.ts +21 -0
  45. package/dist/components/layers/ConfirmDialog/useConfirmDialog.spec.d.ts +1 -0
  46. package/dist/components/layers/index.d.ts +1 -0
  47. package/dist/components/meaning/AvatarGroup/AvatarGroup.d.ts +22 -0
  48. package/dist/components/meaning/AvatarGroup/AvatarGroup.spec.d.ts +1 -0
  49. package/dist/components/meaning/TeamAvatar/TeamAvatar.d.ts +14 -0
  50. package/dist/components/meaning/TeamAvatar/TeamAvatar.spec.d.ts +1 -0
  51. package/dist/components/meaning/index.d.ts +2 -0
  52. package/dist/components/structure/AmbientBackground/AmbientBackground.d.ts +24 -0
  53. package/dist/components/structure/AmbientBackground/AmbientBackground.spec.d.ts +1 -0
  54. package/dist/components/structure/Section/Section.d.ts +10 -2
  55. package/dist/components/structure/index.d.ts +1 -0
  56. package/dist/cookie-consent.js +2 -2
  57. package/dist/design-theme-D3DQFMu-.js +27614 -0
  58. package/dist/hooks/use-rich-intl.hook.d.ts +68 -0
  59. package/dist/{index-uLh81uFU.js → index-3wZ_n-X5.js} +2 -2
  60. package/dist/{index-BuQlif6i.js → index-7E3aXufv.js} +2 -2
  61. package/dist/{index-iQhqBjKY.js → index-9E4QhCRp.js} +2 -2
  62. package/dist/{index-BtzB7aR_.js → index-B7yu6Zm9.js} +2 -2
  63. package/dist/{index-DkcMwm_T.js → index-BK4lWFRR.js} +3 -3
  64. package/dist/{index-2zbZ2URx.js → index-BTfe4Sz-.js} +3 -3
  65. package/dist/{index-CspGahq0.js → index-BbwAoYtC.js} +4 -4
  66. package/dist/{index-Waz6-pQr.js → index-BcUA-oDy.js} +4 -4
  67. package/dist/{index-CTP0QoCT.js → index-BfICcXrk.js} +3 -3
  68. package/dist/{index-DflXNluH.js → index-By24n0PI.js} +2 -2
  69. package/dist/{index-C5OyIHCq.js → index-CMyNL_Kk.js} +2 -2
  70. package/dist/{index-BloHDADq.js → index-CNMUrFys.js} +35281 -47311
  71. package/dist/{index-BMM6rvML.js → index-CTkBexi5.js} +2 -2
  72. package/dist/{index-Bhhwuj39.js → index-CUoOFhRH.js} +2 -2
  73. package/dist/{index-RkXYGPSf.js → index-CZvosKer.js} +4 -4
  74. package/dist/{index-uGXjK3xv.js → index-CzkoqdQK.js} +3 -3
  75. package/dist/{index-CsyAu658.js → index-D3QW807K.js} +2 -2
  76. package/dist/{index-IooWxH41.js → index-D6Gkj-V6.js} +2 -2
  77. package/dist/{index-Dew1ymFy.js → index-DYUdQPwj.js} +2 -2
  78. package/dist/{index-jhW25X__.js → index-DeoE1f9Z.js} +2 -2
  79. package/dist/{index-BXtPl6pH.js → index-F67JSDx7.js} +1 -1
  80. package/dist/{index-BAcf97X_.js → index-rpW-_Gh4.js} +2 -2
  81. package/dist/index.js +5979 -4545
  82. package/dist/legacy/components/CompanySearchAutocomplete/CompanySearchAutocomplete.d.ts +2 -14
  83. package/dist/legacy/components/Layouts/AbyssFamilyBanner/AbyssFamilyBanner.component.d.ts +3 -7
  84. package/dist/legacy/utils/file.utils.d.ts +1 -2
  85. package/dist/legacy.js +107 -107
  86. package/dist/theme/tokens/choice-swatches.d.ts +12 -0
  87. package/dist/theme/tokens/choice-swatches.spec.d.ts +1 -0
  88. package/dist/theme/tokens/index.d.ts +1 -0
  89. package/dist/theme/tokens/semantic.d.ts +4 -1
  90. package/dist/translations/en.d.ts +34 -0
  91. package/dist/translations/fr.d.ts +34 -0
  92. package/dist/utils/family.d.ts +12 -0
  93. package/dist/utils/file.utils.d.ts +10 -0
  94. package/dist/utils/file.utils.spec.d.ts +1 -0
  95. package/dist/utils/index.d.ts +2 -0
  96. package/package.json +1 -1
  97. package/dist/design-theme-BztWe75w.js +0 -15490
package/README.md CHANGED
@@ -90,6 +90,69 @@ export const buildRumOptions = () =>
90
90
 
91
91
  ## Changements visibles
92
92
 
93
+ ### 9.3.0
94
+
95
+ Pages des outils du front principal sur le design system (carte #286, zone 4a). Pièces nouvelles et évolutions,
96
+ chacune documentée dans le `README.md` de son dossier et montrée dans le catalogue.
97
+
98
+ - **`BarChart`** (`data`) : barres Nivo sous `abyssNivoTheme`, groupées ou empilées (`stacked`), verticales ou
99
+ horizontales (`BarChartLayout`) ; une série par `id`, une catégorie par groupe, une clé absente n'est pas une barre ;
100
+ infobulle à la valeur propre de la série ; légende au-delà d'une série ; figure nommée et tableau des valeurs pour
101
+ les lecteurs d'écran ; « Aucune donnée à afficher » sans valeur. Graduations de l'axe des catégories selon la
102
+ largeur mesurée. Remplace `AbyssBarChart`.
103
+ - **Ton sémantique des séries** (`data`) : `ChartSeriesTone` (`Success`, `Warning`, `Danger`, `Info`, `Neutral`) et
104
+ `seriesColorTokens`, partagés par `LineChart` et `BarChart`. Une série à ton prend le jeton `status.*` de son ton ;
105
+ les séries sans ton gardent la palette `data.series.*` dans leur ordre (ADR-0174, avenant 9.3.0).
106
+ - **`LineChart`** (`data`) : `tone` par série et `stacked` (aires empilées depuis zéro, une abscisse absente d'une
107
+ série comptée à zéro ; infobulle et tableau gardent les valeurs propres). Le reste du contrat 9.2.0 est inchangé.
108
+ - **`AmbientBackground`** (`structure`) : fond de décor animé et sobre (lueurs d'`accent` et de `data.series.5` en
109
+ `color-mix` sur les variables `--abyss-*`), `aria-hidden`, hors du flux, `Viewport` ou `Container` ; figé sous
110
+ `prefers-reduced-motion`, sous 768 px et avec `paused`, état exposé par `data-motion`. Remplace `WaterParticles`.
111
+ - **`ConfirmDialogTone.Warning`** (`layers`) : bouton en `status.warning`, texte dans l'encre `paper`/`paper.on` la
112
+ plus contrastée, focus initial sur « Annuler » ; accepté par `useConfirmDialog`.
113
+ - **`DataTable`** (`data`) : `label` facultatif, nom accessible de la table et de la liste mobile.
114
+ - **Palette de choix** (`theme`) : `CHOICE_SWATCHES`, douze teintes catégorielles (une tous les 30° depuis la teinte
115
+ de marque, `0.72 0.12` en OKLCH), valeurs hexadécimales stables, lisibles sous l'encre `paper.on` à 4,5:1 au moins
116
+ dans les deux thèmes. **`ColorField`** les propose quand l'hôte ne passe pas `swatches` (devenu facultatif).
117
+ - **Utilitaires sortis de `legacy/`** : `getFileIcon` et `getFileColor` sont exportés de la racine ; `getFileColor`
118
+ y rend une couleur des jetons, `var(--abyss-data-series-n)` ou `var(--abyss-text-muted)`.
119
+ - **Traductions** : clés `ds.data.bar-chart.*` (FR et EN).
120
+
121
+ ### 9.2.0
122
+
123
+ Organisation et projet du front principal sur le design system (carte #286, zone 3). Pièces nouvelles et évolutions,
124
+ chacune documentée dans le `README.md` de son dossier et montrée dans le catalogue.
125
+
126
+ - **`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`.
127
+ - **`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`).
128
+ - **`Pagination`** (`data`) : précédent, numéros de page avec ellipses (sept places fixes), suivant ; page courante
129
+ en `aria-current="page"`, bornes désactivées, rien sous deux pages ; sous 768 px, précédent, « Page x sur y »,
130
+ suivant, cibles de 44 px.
131
+ - **`LineChart`** (`data`) : courbes Nivo sous `abyssNivoTheme`, échelle de temps ou linéaire, aire facultative ;
132
+ 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 ;
133
+ figure nommée et tableau des valeurs pour les lecteurs d'écran ; « Aucune donnée à afficher » sans point.
134
+ Graduations de l'axe x selon la largeur mesurée du graphique (et non celle de l'écran) : pas le plus fin, jamais plus
135
+ fin que les données, dont les libellés mono gardent `space[8]` entre eux ; un libellé qui déborderait des marges
136
+ n'est pas affiché.
137
+ - **Palette de séries** (`theme`) : jetons `data.series.1` à `data.series.6` (et `--abyss-data-series-*`), ordre
138
+ exporté par `DATA_SERIES_TOKENS` ; luminosité et chroma égales, 3:1 au moins sur les fonds (ADR-0174, avenant).
139
+ - **`UserPicker`** (`inputs`) : choix simple ou multiple d'utilisateurs (`{ id, name, email, pictureUrl }`), avatar,
140
+ nom et e-mail par option ; filtre local sur le nom et l'e-mail, ou recherche serveur par `onSearch` avec `loading` ;
141
+ `disabledIds` ; en multiple, puces parcourues à la flèche gauche et retirées par Retour arrière ou Suppr ; sous
142
+ 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
143
+ jamais plus étroit que 44 px quand les puces remplissent la ligne.
144
+ - **`CompanySearchField`** (`inputs`) : contrat de `CompanySearchAutocomplete` (Pappers, `onSelect(PappersCompany)`,
145
+ erreur distincte d'une réponse vide, date de création dans la langue de l'utilisateur) sur `FormField` et les jetons
146
+ du core ; temporisation annulée au démontage, réponse dépassée ignorée.
147
+ - **Types sortis de `legacy/`** : `PappersCompany` et `CurrentFamilyInfo` (avec `CurrentFamilyStatus`) sont exportés de
148
+ la racine ; `/legacy` les réexporte.
149
+ - **Traductions** : clés `ds.inputs.user-picker.*` et `ds.inputs.company-search-field.*` (FR et EN).
150
+ - **`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.
151
+ - **`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.
152
+ - **`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.
153
+ - **`Section tone`** : `SectionTone.Danger` met le titre et un filet en `status.danger` (zones de danger).
154
+ - **`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.
155
+
93
156
  ### 9.1.1
94
157
 
95
158
  - **`useServiceTour`** : sous un `AbyssCookieConsentProvider`, une première visite attend que le bandeau de cookies
@@ -52,6 +52,9 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
52
52
  entry('FilterBar', 'structure', 'filter-bar', RESPONSIVE),
53
53
  entry('SplitView', 'structure', 'split-view', RESPONSIVE),
54
54
  entry('Tabs', 'structure', 'tabs', RESPONSIVE),
55
+ entry('AmbientBackground', 'structure', 'ambient-background', {
56
+ mobileCapture: true,
57
+ }),
55
58
  entry('NotificationsMenu', 'structure', 'notifications-menu', {
56
59
  mobileCapture: true,
57
60
  }),
@@ -66,6 +69,9 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
66
69
  entry('KeyValueList', 'data', 'key-value-list'),
67
70
  entry('StatTile', 'data', 'stat-tile'),
68
71
  entry('Sparkline', 'data', 'sparkline'),
72
+ entry('Pagination', 'data', 'pagination', RESPONSIVE),
73
+ entry('LineChart', 'data', 'line-chart'),
74
+ entry('BarChart', 'data', 'bar-chart'),
69
75
  entry('ChartTheme', 'data', 'chart-theme'),
70
76
  entry('UsageMeter', 'data', 'usage-meter'),
71
77
  entry('StatusBadge', 'meaning', 'status-badge'),
@@ -75,6 +81,8 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
75
81
  entry('RoleBadge', 'meaning', 'role-badge'),
76
82
  entry('ToolIcon', 'meaning', 'tool-icon'),
77
83
  entry('Avatar', 'meaning', 'avatar'),
84
+ entry('AvatarGroup', 'meaning', 'avatar-group'),
85
+ entry('TeamAvatar', 'meaning', 'team-avatar'),
78
86
  entry('InfectedDownloadModal', 'business', 'infected-download-modal'),
79
87
  entry('CloudImport', 'business', 'cloud-import', RESPONSIVE),
80
88
  entry('ImportFromCloudModal', 'business', 'import-from-cloud-modal', {
@@ -83,6 +91,7 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
83
91
  entry('QRCodeModal', 'business', 'qr-code-modal', { mobileCapture: true }),
84
92
  entry('Modal', 'layers', 'modal', { mobileCapture: true }),
85
93
  entry('ConfirmDialog', 'layers', 'confirm-dialog'),
94
+ entry('ConfirmDialogWarning', 'layers', 'confirm-dialog-warning'),
86
95
  entry('Drawer', 'layers', 'drawer'),
87
96
  entry('Menu', 'layers', 'menu'),
88
97
  entry('Toast', 'layers', 'toast'),
@@ -97,6 +106,9 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
97
106
  entry('NumberField', 'inputs', 'number-field'),
98
107
  entry('SettingsRow', 'inputs', 'settings-row', RESPONSIVE),
99
108
  entry('CopyField', 'inputs', 'copy-field', RESPONSIVE),
109
+ entry('UserPicker', 'inputs', 'user-picker', RESPONSIVE),
110
+ entry('CompanySearchField', 'inputs', 'company-search-field', RESPONSIVE),
111
+ entry('EntityImageField', 'inputs', 'entity-image-field', RESPONSIVE),
100
112
  entry('ContextSelector', 'inputs', 'context-selector', RESPONSIVE),
101
113
  entry('DropZone', 'inputs', 'drop-zone', RESPONSIVE),
102
114
  entry('ImportButton', 'inputs', 'import-button'),
@@ -0,0 +1,83 @@
1
+ import { useState } from 'react';
2
+ import Button from '@mui/material/Button';
3
+ import { styled } from '@mui/material/styles';
4
+
5
+ import {
6
+ AmbientBackground,
7
+ AmbientBackgroundPlacement,
8
+ } from '../../src/components/structure/AmbientBackground/AmbientBackground';
9
+ import { hairline, px } from '../../src/theme/components/shared';
10
+ import { Example, Showcase } from '../frame';
11
+
12
+ /** A panel the background stops at: positioned, clipped, as a host lays it out. */
13
+ const Panel = styled('div')(({ theme }) => {
14
+ const { tokens, space, radii, typeScale } = theme.design;
15
+ return {
16
+ position: 'relative',
17
+ overflow: 'hidden',
18
+ display: 'flex',
19
+ flexDirection: 'column',
20
+ alignItems: 'flex-start',
21
+ justifyContent: 'center',
22
+ gap: px(space[3]),
23
+ minHeight: px(space[10] * 4),
24
+ padding: px(space[6]),
25
+ backgroundColor: tokens['bg.canvas'],
26
+ border: hairline(tokens['border.subtle']),
27
+ borderRadius: px(radii.md),
28
+ color: tokens['text.primary'],
29
+ fontSize: px(typeScale.body),
30
+ };
31
+ });
32
+
33
+ /** The content of a panel, laid above its background. */
34
+ const Content = styled('div')(({ theme }) => ({
35
+ position: 'relative',
36
+ display: 'flex',
37
+ flexDirection: 'column',
38
+ alignItems: 'flex-start',
39
+ gap: px(theme.design.space[2]),
40
+ }));
41
+
42
+ const Title = styled('strong')(({ theme }) => ({
43
+ fontSize: px(theme.design.typeScale.sectionTitle),
44
+ }));
45
+
46
+ const PausableScanner = () => {
47
+ const [scanning, setScanning] = useState(true);
48
+ return (
49
+ <Panel>
50
+ <AmbientBackground
51
+ placement={AmbientBackgroundPlacement.Container}
52
+ paused={!scanning}
53
+ />
54
+ <Content>
55
+ <Title>{scanning ? 'Analyse en cours…' : 'Analyse terminée'}</Title>
56
+ <Button variant="outlined" onClick={() => setScanning(!scanning)}>
57
+ {scanning ? 'Figer le fond' : 'Relancer le fond'}
58
+ </Button>
59
+ </Content>
60
+ </Panel>
61
+ );
62
+ };
63
+
64
+ export const AmbientBackgroundPage = () => (
65
+ <Showcase
66
+ family="Structure"
67
+ name="AmbientBackground"
68
+ description="Fond de décor : lueurs d'accent et de data.series.5 qui dérivent lentement sous le contenu, masquées aux lecteurs d'écran, figées sous prefers-reduced-motion, sous 768 px et en pause."
69
+ >
70
+ <Example title="Dans un panneau (placement Container)">
71
+ <Panel>
72
+ <AmbientBackground placement={AmbientBackgroundPlacement.Container} />
73
+ <Content>
74
+ <Title>Envoyez vos fichiers avec Beam</Title>
75
+ <span>Le fond reste sous le contenu et ne prend aucun clic.</span>
76
+ </Content>
77
+ </Panel>
78
+ </Example>
79
+ <Example title="En pause : le scanner de Probe entre deux analyses">
80
+ <PausableScanner />
81
+ </Example>
82
+ </Showcase>
83
+ );
@@ -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,127 @@
1
+ import {
2
+ BarChart,
3
+ BarChartLayout,
4
+ } from '../../src/components/data/BarChart/BarChart';
5
+ import type {
6
+ BarChartCategory,
7
+ BarChartSeries,
8
+ } from '../../src/components/data/BarChart/BarChart';
9
+ import { ChartSeriesTone } from '../../src/components/data/chart-series-tone';
10
+ import { Surface } from '../../src/components/structure/Surface/Surface';
11
+ import { Example, Showcase } from '../frame';
12
+
13
+ const count = new Intl.NumberFormat('fr-FR');
14
+ const seconds = new Intl.NumberFormat('fr-FR', {
15
+ style: 'unit',
16
+ unit: 'second',
17
+ maximumFractionDigits: 1,
18
+ });
19
+
20
+ const DURATION: BarChartSeries[] = [{ id: 'duration', label: 'Durée moyenne' }];
21
+
22
+ const CONFIGS: BarChartCategory[] = [
23
+ { id: 'invoice', label: 'Factures', values: { duration: 4.2 } },
24
+ { id: 'receipt', label: 'Reçus', values: { duration: 2.8 } },
25
+ { id: 'payslip', label: 'Bulletins de paie', values: { duration: 6.1 } },
26
+ { id: 'id-card', label: 'Pièces d’identité', values: { duration: 3.4 } },
27
+ { id: 'bank', label: 'Relevés bancaires', values: { duration: 7.9 } },
28
+ ];
29
+
30
+ const OUTCOMES: BarChartSeries[] = [
31
+ { id: 'succeeded', label: 'Réussies', tone: ChartSeriesTone.Success },
32
+ { id: 'failed', label: 'Échouées', tone: ChartSeriesTone.Danger },
33
+ ];
34
+
35
+ const OUTCOMES_BY_CONFIG: BarChartCategory[] = [
36
+ { id: 'invoice', label: 'Factures', values: { succeeded: 412, failed: 9 } },
37
+ { id: 'receipt', label: 'Reçus', values: { succeeded: 268, failed: 21 } },
38
+ { id: 'payslip', label: 'Bulletins', values: { succeeded: 133 } },
39
+ { id: 'id-card', label: 'Identité', values: { succeeded: 96, failed: 14 } },
40
+ { id: 'bank', label: 'Relevés', values: { succeeded: 57, failed: 3 } },
41
+ ];
42
+
43
+ const TOOLS: BarChartSeries[] = ['Atlas', 'Board', 'Draw'].map((label) => ({
44
+ id: label.toLowerCase(),
45
+ label,
46
+ }));
47
+
48
+ const MONTHS = [
49
+ 'Jan.',
50
+ 'Fév.',
51
+ 'Mars',
52
+ 'Avr.',
53
+ 'Mai',
54
+ 'Juin',
55
+ 'Juil.',
56
+ 'Août',
57
+ 'Sept.',
58
+ 'Oct.',
59
+ 'Nov.',
60
+ 'Déc.',
61
+ ];
62
+
63
+ const BY_MONTH: BarChartCategory[] = MONTHS.map((label, month) => ({
64
+ id: `m${month + 1}`,
65
+ label,
66
+ values: Object.fromEntries(
67
+ TOOLS.map(({ id }, index) => [
68
+ id,
69
+ 12 + index * 6 + Math.round(8 * Math.sin((month + index) / 2)),
70
+ ]),
71
+ ),
72
+ }));
73
+
74
+ export const BarChartPage = () => (
75
+ <Showcase
76
+ family="Données"
77
+ name="BarChart"
78
+ description="Barres Nivo sous abyssNivoTheme, groupées ou empilées, verticales ou horizontales : palette data.series ou ton status.* quand la série porte un sens, légende au-delà d'une série, figure nommée et tableau des valeurs pour les lecteurs d'écran."
79
+ >
80
+ <Example title="Une série, horizontale">
81
+ <Surface padding={4}>
82
+ <BarChart
83
+ series={DURATION}
84
+ categories={CONFIGS}
85
+ height={220}
86
+ layout={BarChartLayout.Horizontal}
87
+ formatValue={(value) => seconds.format(value)}
88
+ label="Durée moyenne d’une extraction par configuration"
89
+ />
90
+ </Surface>
91
+ </Example>
92
+ <Example title="Empilées, séries à ton (réussites et échecs)">
93
+ <Surface padding={4}>
94
+ <BarChart
95
+ series={OUTCOMES}
96
+ categories={OUTCOMES_BY_CONFIG}
97
+ height={240}
98
+ stacked
99
+ formatValue={(value) => count.format(value)}
100
+ label="Extractions réussies et échouées par configuration"
101
+ />
102
+ </Surface>
103
+ </Example>
104
+ <Example title="Groupées, palette de séries, douze catégories">
105
+ <Surface padding={4}>
106
+ <BarChart
107
+ series={TOOLS}
108
+ categories={BY_MONTH}
109
+ height={240}
110
+ formatValue={(value) => count.format(value)}
111
+ label="Contenus créés par outil et par mois"
112
+ />
113
+ </Surface>
114
+ </Example>
115
+ <Example title="Aucune donnée">
116
+ <Surface padding={4}>
117
+ <BarChart
118
+ series={DURATION}
119
+ categories={[]}
120
+ height={160}
121
+ formatValue={(value) => seconds.format(value)}
122
+ label="Durée moyenne d’une extraction par configuration"
123
+ />
124
+ </Surface>
125
+ </Example>
126
+ </Showcase>
127
+ );
@@ -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 { Example, LastEvent, Showcase, useEventLog } from '../frame';
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}
@@ -0,0 +1,45 @@
1
+ import { useState } from 'react';
2
+ import Button from '@mui/material/Button';
3
+ import {
4
+ ConfirmDialog,
5
+ ConfirmDialogTone,
6
+ } from '../../src/components/layers/ConfirmDialog/ConfirmDialog';
7
+ import { Example, LastEvent, Showcase, useEventLog } from '../frame';
8
+
9
+ export const ConfirmDialogWarningPage = () => {
10
+ const { log } = useEventLog();
11
+ const [open, setOpen] = useState(true);
12
+ return (
13
+ <Showcase
14
+ family="Couches"
15
+ name="ConfirmDialog — ton Warning"
16
+ description="Confirmation d'une action permise mais risquée : bouton en status.warning, texte dans l'encre paper la plus contrastée, focus initial sur « Annuler »."
17
+ >
18
+ <Example title="Télécharger un fichier encore en analyse">
19
+ <Button
20
+ variant="outlined"
21
+ color="warning"
22
+ onClick={() => setOpen(true)}
23
+ >
24
+ Télécharger contrat-2026.pdf
25
+ </Button>
26
+ </Example>
27
+ <LastEvent />
28
+ <ConfirmDialog
29
+ open={open}
30
+ tone={ConfirmDialogTone.Warning}
31
+ title="Télécharger un fichier en cours d’analyse ?"
32
+ body="L’analyse antivirus de contrat-2026.pdf n’est pas terminée : rien ne garantit encore que le fichier est sain."
33
+ confirmLabel="Télécharger quand même"
34
+ onConfirm={() => {
35
+ setOpen(false);
36
+ log('télécharger');
37
+ }}
38
+ onCancel={() => {
39
+ setOpen(false);
40
+ log('annuler');
41
+ }}
42
+ />
43
+ </Showcase>
44
+ );
45
+ };
@@ -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"