@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
@@ -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,138 @@
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 { ChartSeriesTone } from '../../src/components/data/chart-series-tone';
7
+ import { Surface } from '../../src/components/structure/Surface/Surface';
8
+ import { Example, Showcase } from '../frame';
9
+
10
+ const START = new Date(2026, 9, 13, 0);
11
+
12
+ const atHour = (hour: number) =>
13
+ new Date(START.getTime() + hour * 60 * 60 * 1000);
14
+
15
+ const REQUESTS = [
16
+ 42, 31, 24, 19, 17, 22, 48, 96, 184, 236, 262, 248, 231, 244, 268, 281, 254,
17
+ 212, 168, 131, 104, 86, 67, 51,
18
+ ];
19
+ const ERRORS = [
20
+ 1, 0, 0, 0, 1, 0, 2, 4, 9, 6, 5, 4, 12, 18, 7, 5, 4, 3, 3, 2, 1, 1, 0, 1,
21
+ ];
22
+ const LATENCY = [
23
+ 88, 84, 81, 80, 79, 83, 94, 118, 141, 152, 158, 155, 149, 162, 171, 166, 150,
24
+ 133, 117, 104, 97, 93, 90, 89,
25
+ ];
26
+
27
+ const hourly = (
28
+ id: string,
29
+ label: string,
30
+ values: number[],
31
+ tone?: ChartSeriesTone,
32
+ ): LineChartSeries => ({
33
+ id,
34
+ label,
35
+ points: values.map((y, hour) => ({ x: atHour(hour), y })),
36
+ tone,
37
+ });
38
+
39
+ const DELIVERED = REQUESTS.map((value, hour) => value - ERRORS[hour]);
40
+
41
+ const SIX_SERIES: LineChartSeries[] = [
42
+ 'Atlas',
43
+ 'Board',
44
+ 'Draw',
45
+ 'Beam',
46
+ 'Extract',
47
+ 'ShortLink',
48
+ ].map((label, index) => ({
49
+ id: label.toLowerCase(),
50
+ label,
51
+ points: Array.from({ length: 12 }, (_, month) => ({
52
+ x: month + 1,
53
+ y: 20 + index * 12 + Math.round(10 * Math.sin((month + index) / 2)),
54
+ })),
55
+ }));
56
+
57
+ const time = new Intl.DateTimeFormat('fr-FR', {
58
+ hour: '2-digit',
59
+ minute: '2-digit',
60
+ });
61
+ const count = new Intl.NumberFormat('fr-FR');
62
+
63
+ const formatHour = (x: LineChartX) =>
64
+ time.format(x instanceof Date ? x : new Date(x));
65
+ const formatMonth = (x: LineChartX) =>
66
+ `M${count.format(x instanceof Date ? x.getMonth() + 1 : x)}`;
67
+
68
+ export const LineChartPage = () => (
69
+ <Showcase
70
+ family="Données"
71
+ name="LineChart"
72
+ description="Courbes Nivo sous abyssNivoTheme : couleurs prises dans l'ordre de la palette data.series, ou dans status.* pour une série à ton ; aires empilées avec stacked ; légende au-delà d'une série, figure nommée et tableau des valeurs pour les lecteurs d'écran."
73
+ >
74
+ <Example title="Une série, aire">
75
+ <Surface padding={4}>
76
+ <LineChart
77
+ series={[hourly('requests', 'Requêtes', REQUESTS)]}
78
+ height={240}
79
+ area
80
+ formatX={formatHour}
81
+ formatY={(y) => count.format(y)}
82
+ label="Requêtes par heure, sur 24 heures"
83
+ />
84
+ </Surface>
85
+ </Example>
86
+ <Example title="Deux séries">
87
+ <Surface padding={4}>
88
+ <LineChart
89
+ series={[
90
+ hourly('latency', 'Latence p95', LATENCY),
91
+ hourly('errors', 'Erreurs', ERRORS),
92
+ ]}
93
+ height={240}
94
+ formatX={formatHour}
95
+ formatY={(y) => count.format(y)}
96
+ label="Latence et erreurs par heure"
97
+ />
98
+ </Surface>
99
+ </Example>
100
+ <Example title="Empilées, séries à ton (délivrés et échecs)">
101
+ <Surface padding={4}>
102
+ <LineChart
103
+ series={[
104
+ hourly('delivered', 'Délivrés', DELIVERED, ChartSeriesTone.Success),
105
+ hourly('failed', 'Échecs', ERRORS, ChartSeriesTone.Danger),
106
+ ]}
107
+ height={240}
108
+ stacked
109
+ formatX={formatHour}
110
+ formatY={(y) => count.format(y)}
111
+ label="Webhooks délivrés et en échec par heure"
112
+ />
113
+ </Surface>
114
+ </Example>
115
+ <Example title="Palette des six séries">
116
+ <Surface padding={4}>
117
+ <LineChart
118
+ series={SIX_SERIES}
119
+ height={240}
120
+ formatX={formatMonth}
121
+ formatY={(y) => count.format(y)}
122
+ label="Contenus créés par outil et par mois"
123
+ />
124
+ </Surface>
125
+ </Example>
126
+ <Example title="Aucune donnée">
127
+ <Surface padding={4}>
128
+ <LineChart
129
+ series={[{ id: 'requests', label: 'Requêtes', points: [] }]}
130
+ height={160}
131
+ formatX={formatHour}
132
+ formatY={(y) => count.format(y)}
133
+ label="Requêtes par heure"
134
+ />
135
+ </Surface>
136
+ </Example>
137
+ </Showcase>
138
+ );
@@ -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 { Section } from '../../src/components/structure/Section/Section';
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 />
@@ -0,0 +1,40 @@
1
+ import { useTheme } from '@mui/material/styles';
2
+
3
+ import { TeamAvatar } from '../../src/components/meaning/TeamAvatar/TeamAvatar';
4
+ import { Example, Row, Showcase } from '../frame';
5
+
6
+ export const TeamAvatarPage = () => {
7
+ // Stored team colors are user data; the catalog borrows token values so as not to hard-code any.
8
+ const { tokens } = useTheme().design;
9
+ return (
10
+ <Showcase
11
+ family="Sens"
12
+ name="TeamAvatar"
13
+ description="Équipe : l'initiale de son nom sur sa couleur, dans un carré arrondi, à l'encre claire ou sombre qui contraste le mieux. Sans couleur, ou avec une couleur que le navigateur ne sait pas lire, la teinte neutre des avatars."
14
+ >
15
+ <Example title="Couleurs d'équipe, foncées et claires" framed>
16
+ <Row>
17
+ <TeamAvatar name="Design" color={tokens['status.info']} />
18
+ <TeamAvatar name="Ops" color={tokens['status.success']} />
19
+ <TeamAvatar name="Support" color={tokens['status.warning']} />
20
+ <TeamAvatar name="Finance" color={tokens['status.highlight']} />
21
+ <TeamAvatar name="Sécurité" color={tokens['status.danger']} />
22
+ </Row>
23
+ </Example>
24
+ <Example title="Sans couleur, couleur illisible, sans nom" framed>
25
+ <Row>
26
+ <TeamAvatar name="Marketing" color={null} />
27
+ <TeamAvatar name="Ventes" color="pas-une-couleur" />
28
+ <TeamAvatar name="" color={tokens['status.info']} />
29
+ </Row>
30
+ </Example>
31
+ <Example title="Tailles : 22, 32 (défaut), 64" framed>
32
+ <Row>
33
+ <TeamAvatar name="Ops" color={tokens['status.info']} size={22} />
34
+ <TeamAvatar name="Ops" color={tokens['status.info']} />
35
+ <TeamAvatar name="Ops" color={tokens['status.info']} size={64} />
36
+ </Row>
37
+ </Example>
38
+ </Showcase>
39
+ );
40
+ };
@@ -0,0 +1,124 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { UserPicker } from '../../src/components/inputs/UserPicker/UserPicker';
3
+ import type { UserPickerOption } from '../../src/components/inputs/UserPicker/UserPicker';
4
+ import { LogoAbyssSmall } from '../../src/assets';
5
+ import { Example, LastEvent, Showcase, useEventLog } from '../frame';
6
+
7
+ const MEMBERS: UserPickerOption[] = [
8
+ {
9
+ id: 'u1',
10
+ name: 'Mehdi Benali',
11
+ email: 'mehdi@abyss.fr',
12
+ pictureUrl: LogoAbyssSmall,
13
+ },
14
+ {
15
+ id: 'u2',
16
+ name: 'Alice Martin',
17
+ email: 'alice.martin@karbon-studio.fr',
18
+ pictureUrl: null,
19
+ },
20
+ { id: 'u3', name: 'Bruno Petit', email: 'bruno@abyss.fr', pictureUrl: null },
21
+ { id: 'u4', name: 'Clara Durand', email: null, pictureUrl: null },
22
+ {
23
+ id: 'u5',
24
+ name: 'Maximilien-Alexandre de La Tour d’Auvergne',
25
+ email: 'maximilien-alexandre.delatourdauvergne@exemple-tres-long.fr',
26
+ pictureUrl: null,
27
+ },
28
+ ];
29
+
30
+ /** Delay of the simulated server search, in ms. */
31
+ const SEARCH_DELAY = 600;
32
+
33
+ /** A server search answered from memory, after a delay: the catalog has no network. */
34
+ const useSimulatedSearch = () => {
35
+ const [query, setQuery] = useState('');
36
+ const [results, setResults] = useState<UserPickerOption[]>(MEMBERS);
37
+ const [loading, setLoading] = useState(false);
38
+ useEffect(() => {
39
+ setLoading(true);
40
+ const timer = setTimeout(() => {
41
+ const text = query.trim().toLowerCase();
42
+ setResults(
43
+ MEMBERS.filter((member) =>
44
+ [member.name, member.email ?? ''].some((field) =>
45
+ field.toLowerCase().includes(text),
46
+ ),
47
+ ),
48
+ );
49
+ setLoading(false);
50
+ }, SEARCH_DELAY);
51
+ return () => clearTimeout(timer);
52
+ }, [query]);
53
+ return { results, loading, search: setQuery };
54
+ };
55
+
56
+ export const UserPickerPage = () => {
57
+ const { log } = useEventLog();
58
+ const [owner, setOwner] = useState<string | null>('u2');
59
+ const [recipients, setRecipients] = useState<string[]>(['u1', 'u3']);
60
+ const [target, setTarget] = useState<string | null>(null);
61
+ const remote = useSimulatedSearch();
62
+ return (
63
+ <Showcase
64
+ family="Saisie"
65
+ name="UserPicker"
66
+ description="Choix d'un utilisateur ou de plusieurs : avatar, nom et e-mail dans chaque option, recherche sur le nom et l'e-mail (locale, ou confiée au serveur par onSearch). En multiple, les choisis sont des puces que le clavier retire."
67
+ >
68
+ <Example title="Choix simple" maxWidth={480}>
69
+ <UserPicker
70
+ options={MEMBERS}
71
+ value={owner}
72
+ onChange={(next) => {
73
+ setOwner(next);
74
+ log(`responsable ${next ?? 'aucun'}`);
75
+ }}
76
+ label="Responsable de l’équipe"
77
+ placeholder="Choisir un membre"
78
+ noOptionsText="Aucun membre ne correspond"
79
+ />
80
+ </Example>
81
+ <Example title="Choix multiple" maxWidth={480}>
82
+ <UserPicker
83
+ multiple
84
+ options={MEMBERS}
85
+ value={recipients}
86
+ onChange={(next) => {
87
+ setRecipients(next);
88
+ log(`destinataires ${next.join(', ')}`);
89
+ }}
90
+ label="Destinataires du rapport"
91
+ placeholder="Ajouter un membre"
92
+ noOptionsText="Aucun membre ne correspond"
93
+ />
94
+ </Example>
95
+ <Example title="Recherche serveur, propriétaire exclu" maxWidth={480}>
96
+ <UserPicker
97
+ options={remote.results}
98
+ onSearch={remote.search}
99
+ loading={remote.loading}
100
+ value={target}
101
+ onChange={(next) => {
102
+ setTarget(next);
103
+ log(`nouveau propriétaire ${next ?? 'aucun'}`);
104
+ }}
105
+ disabledIds={['u1']}
106
+ label="Nouveau propriétaire"
107
+ placeholder="Rechercher un membre"
108
+ noOptionsText="Aucun membre ne correspond"
109
+ />
110
+ </Example>
111
+ <Example title="Inactif" maxWidth={480}>
112
+ <UserPicker
113
+ options={MEMBERS}
114
+ value="u1"
115
+ onChange={() => undefined}
116
+ label="Responsable de l’équipe"
117
+ noOptionsText="Aucun membre ne correspond"
118
+ disabled
119
+ />
120
+ </Example>
121
+ <LastEvent />
122
+ </Showcase>
123
+ );
124
+ };
@@ -1,8 +1,10 @@
1
1
  import type { ComponentType } from 'react';
2
2
  import { AccountMenuPage } from './AccountMenu';
3
3
  import { ActionBarPage } from './ActionBar';
4
+ import { AmbientBackgroundPage } from './AmbientBackground';
4
5
  import { AppShellPage } from './AppShell';
5
6
  import { AvatarPage } from './Avatar';
7
+ import { BarChartPage } from './BarChart';
6
8
  import { BottomSheetPage } from './BottomSheet';
7
9
  import { CalloutPage } from './Callout';
8
10
  import { ChartThemePage } from './ChartTheme';
@@ -10,8 +12,16 @@ import { CloudImportPage } from './CloudImport';
10
12
  import { ColorFieldPage } from './ColorField';
11
13
  import { CommandPalettePage } from './CommandPalette';
12
14
  import { ConfirmDialogPage } from './ConfirmDialog';
15
+ import { ConfirmDialogWarningPage } from './ConfirmDialogWarning';
13
16
  import { ContextSelectorPage } from './ContextSelector';
14
17
  import { CopyFieldPage } from './CopyField';
18
+ import { LineChartPage } from './LineChart';
19
+ import { PaginationPage } from './Pagination';
20
+ import { AvatarGroupPage } from './AvatarGroup';
21
+ import { TeamAvatarPage } from './TeamAvatar';
22
+ import { UserPickerPage } from './UserPicker';
23
+ import { CompanySearchFieldPage } from './CompanySearchField';
24
+ import { EntityImageFieldPage } from './EntityImageField';
15
25
  import { DataTablePage } from './DataTable';
16
26
  import { DateFieldPage } from './DateField';
17
27
  import { DateRangeFieldPage } from './DateRangeField';
@@ -58,8 +68,10 @@ import { UsageMeterPage } from './UsageMeter';
58
68
  export const CATALOG_PAGES: Record<string, ComponentType> = {
59
69
  AccountMenu: AccountMenuPage,
60
70
  ActionBar: ActionBarPage,
71
+ AmbientBackground: AmbientBackgroundPage,
61
72
  AppShell: AppShellPage,
62
73
  Avatar: AvatarPage,
74
+ BarChart: BarChartPage,
63
75
  BottomSheet: BottomSheetPage,
64
76
  Callout: CalloutPage,
65
77
  ChartTheme: ChartThemePage,
@@ -67,8 +79,16 @@ export const CATALOG_PAGES: Record<string, ComponentType> = {
67
79
  ColorField: ColorFieldPage,
68
80
  CommandPalette: CommandPalettePage,
69
81
  ConfirmDialog: ConfirmDialogPage,
82
+ ConfirmDialogWarning: ConfirmDialogWarningPage,
70
83
  ContextSelector: ContextSelectorPage,
71
84
  CopyField: CopyFieldPage,
85
+ LineChart: LineChartPage,
86
+ Pagination: PaginationPage,
87
+ AvatarGroup: AvatarGroupPage,
88
+ TeamAvatar: TeamAvatarPage,
89
+ UserPicker: UserPickerPage,
90
+ CompanySearchField: CompanySearchFieldPage,
91
+ EntityImageField: EntityImageFieldPage,
72
92
  DataTable: DataTablePage,
73
93
  DateField: DateFieldPage,
74
94
  DateRangeField: DateRangeFieldPage,