@pyreweb/fabric 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +119 -0
  2. package/dist/fabric.cjs.js +18109 -0
  3. package/dist/fabric.css +2180 -0
  4. package/dist/fabric.esm.js +18062 -0
  5. package/dist/fabric.min.js +18112 -0
  6. package/dist/types/components/atoms/FAvatar/FAvatar.test.d.ts +1 -0
  7. package/dist/types/components/atoms/FBadge/FBadge.test.d.ts +1 -0
  8. package/dist/types/components/atoms/FButton/FButton.test.d.ts +1 -0
  9. package/dist/types/components/atoms/FCheckbox/FCheckbox.test.d.ts +1 -0
  10. package/dist/types/components/atoms/FDivider/FDivider.test.d.ts +1 -0
  11. package/dist/types/components/atoms/FIcon/FIcon.test.d.ts +1 -0
  12. package/dist/types/components/atoms/FInput/FInput.test.d.ts +1 -0
  13. package/dist/types/components/atoms/FLoader/FLoader.test.d.ts +1 -0
  14. package/dist/types/components/atoms/FRadio/FRadio.test.d.ts +1 -0
  15. package/dist/types/components/atoms/FTextarea/FTextarea.test.d.ts +1 -0
  16. package/dist/types/components/atoms/FToggle/FToggle.test.d.ts +1 -0
  17. package/dist/types/components/atoms/FTypography/FTypography.test.d.ts +1 -0
  18. package/dist/types/components/atoms/index.d.ts +13 -0
  19. package/dist/types/components/molecules/FAccordionItem/FAccordionItem.test.d.ts +1 -0
  20. package/dist/types/components/molecules/FAlert/FAlert.test.d.ts +1 -0
  21. package/dist/types/components/molecules/FBreadcrumb/FBreadcrumb.test.d.ts +1 -0
  22. package/dist/types/components/molecules/FButtonGroup/FButtonGroup.test.d.ts +1 -0
  23. package/dist/types/components/molecules/FCard/FCard.test.d.ts +1 -0
  24. package/dist/types/components/molecules/FDatePicker/FDatePicker.test.d.ts +1 -0
  25. package/dist/types/components/molecules/FEmptyState/FEmptyState.test.d.ts +1 -0
  26. package/dist/types/components/molecules/FFilePreview/FFilePreview.test.d.ts +1 -0
  27. package/dist/types/components/molecules/FFormField/FFormField.test.d.ts +1 -0
  28. package/dist/types/components/molecules/FListItem/FListItem.test.d.ts +1 -0
  29. package/dist/types/components/molecules/FPagination/FPagination.test.d.ts +1 -0
  30. package/dist/types/components/molecules/FSearchBar/FSearchBar.test.d.ts +1 -0
  31. package/dist/types/components/molecules/FSelect/FSelect.test.d.ts +1 -0
  32. package/dist/types/components/molecules/FStatCard/FStatCard.test.d.ts +1 -0
  33. package/dist/types/components/molecules/FTabs/FTabs.test.d.ts +1 -0
  34. package/dist/types/components/molecules/FToast/FToast.test.d.ts +1 -0
  35. package/dist/types/components/molecules/index.d.ts +18 -0
  36. package/dist/types/components/organisms/FActivityFeed/FActivityFeed.test.d.ts +1 -0
  37. package/dist/types/components/organisms/FDataTable/FDataTable.test.d.ts +1 -0
  38. package/dist/types/components/organisms/FDrawer/FDrawer.test.d.ts +1 -0
  39. package/dist/types/components/organisms/FFileUpload/FFileUpload.test.d.ts +1 -0
  40. package/dist/types/components/organisms/FFilterSidebar/FFilterSidebar.test.d.ts +1 -0
  41. package/dist/types/components/organisms/FForm/FForm.test.d.ts +1 -0
  42. package/dist/types/components/organisms/FModal/FModal.test.d.ts +1 -0
  43. package/dist/types/components/organisms/FNavigationSidebar/FNavigationSidebar.test.d.ts +1 -0
  44. package/dist/types/components/organisms/FOnboardingStepper/FOnboardingStepper.test.d.ts +1 -0
  45. package/dist/types/components/organisms/FOnboardingStepper/FStepperProgress.test.d.ts +1 -0
  46. package/dist/types/components/organisms/FPageHeader/FPageHeader.test.d.ts +1 -0
  47. package/dist/types/components/organisms/FProfileSection/FProfileSection.test.d.ts +1 -0
  48. package/dist/types/components/organisms/FToastProvider/FToastProvider.test.d.ts +1 -0
  49. package/dist/types/components/organisms/FUserMenu/FUserMenu.test.d.ts +1 -0
  50. package/dist/types/components/organisms/index.d.ts +14 -0
  51. package/dist/types/components/utils/FThemeProvider.test.d.ts +1 -0
  52. package/dist/types/components/utils/index.d.ts +2 -0
  53. package/dist/types/components.d.ts +602 -0
  54. package/dist/types/composables/index.d.ts +12 -0
  55. package/dist/types/composables/useDataTableState.d.ts +106 -0
  56. package/dist/types/composables/useDataTableState.test.d.ts +1 -0
  57. package/dist/types/composables/useFormValidation.d.ts +49 -0
  58. package/dist/types/composables/useFormValidation.test.d.ts +1 -0
  59. package/dist/types/composables/useSidebarState.d.ts +65 -0
  60. package/dist/types/composables/useSidebarState.test.d.ts +1 -0
  61. package/dist/types/index.d.ts +19 -0
  62. package/dist/types/types.d.ts +529 -0
  63. package/package.json +100 -0
  64. package/src/components/atoms/FAvatar/FAvatar.stories.js +100 -0
  65. package/src/components/atoms/FAvatar/FAvatar.test.ts +95 -0
  66. package/src/components/atoms/FAvatar/FAvatar.vue +190 -0
  67. package/src/components/atoms/FBadge/FBadge.stories.js +129 -0
  68. package/src/components/atoms/FBadge/FBadge.test.ts +93 -0
  69. package/src/components/atoms/FBadge/FBadge.vue +103 -0
  70. package/src/components/atoms/FButton/FButton.stories.js +122 -0
  71. package/src/components/atoms/FButton/FButton.test.ts +98 -0
  72. package/src/components/atoms/FButton/FButton.vue +147 -0
  73. package/src/components/atoms/FCheckbox/FCheckbox.stories.js +96 -0
  74. package/src/components/atoms/FCheckbox/FCheckbox.test.ts +64 -0
  75. package/src/components/atoms/FCheckbox/FCheckbox.vue +76 -0
  76. package/src/components/atoms/FDivider/FDivider.stories.js +104 -0
  77. package/src/components/atoms/FDivider/FDivider.test.ts +80 -0
  78. package/src/components/atoms/FDivider/FDivider.vue +117 -0
  79. package/src/components/atoms/FIcon/FIcon.stories.js +189 -0
  80. package/src/components/atoms/FIcon/FIcon.test.ts +99 -0
  81. package/src/components/atoms/FIcon/FIcon.vue +192 -0
  82. package/src/components/atoms/FInput/FInput.stories.js +119 -0
  83. package/src/components/atoms/FInput/FInput.test.ts +79 -0
  84. package/src/components/atoms/FInput/FInput.vue +88 -0
  85. package/src/components/atoms/FLoader/FLoader.stories.js +109 -0
  86. package/src/components/atoms/FLoader/FLoader.test.ts +66 -0
  87. package/src/components/atoms/FLoader/FLoader.vue +97 -0
  88. package/src/components/atoms/FRadio/FRadio.stories.js +105 -0
  89. package/src/components/atoms/FRadio/FRadio.test.ts +75 -0
  90. package/src/components/atoms/FRadio/FRadio.vue +119 -0
  91. package/src/components/atoms/FTextarea/FTextarea.stories.js +126 -0
  92. package/src/components/atoms/FTextarea/FTextarea.test.ts +94 -0
  93. package/src/components/atoms/FTextarea/FTextarea.vue +156 -0
  94. package/src/components/atoms/FToggle/FToggle.stories.js +108 -0
  95. package/src/components/atoms/FToggle/FToggle.test.ts +96 -0
  96. package/src/components/atoms/FToggle/FToggle.vue +123 -0
  97. package/src/components/atoms/FTypography/FTypography.stories.js +127 -0
  98. package/src/components/atoms/FTypography/FTypography.test.ts +93 -0
  99. package/src/components/atoms/FTypography/FTypography.vue +78 -0
  100. package/src/components/atoms/index.ts +27 -0
  101. package/src/components/molecules/FAccordionItem/FAccordionItem.stories.js +71 -0
  102. package/src/components/molecules/FAccordionItem/FAccordionItem.test.ts +61 -0
  103. package/src/components/molecules/FAccordionItem/FAccordionItem.vue +105 -0
  104. package/src/components/molecules/FAlert/FAlert.stories.js +87 -0
  105. package/src/components/molecules/FAlert/FAlert.test.ts +59 -0
  106. package/src/components/molecules/FAlert/FAlert.vue +108 -0
  107. package/src/components/molecules/FBreadcrumb/FBreadcrumb.stories.js +90 -0
  108. package/src/components/molecules/FBreadcrumb/FBreadcrumb.test.ts +76 -0
  109. package/src/components/molecules/FBreadcrumb/FBreadcrumb.vue +117 -0
  110. package/src/components/molecules/FButtonGroup/FButtonGroup.stories.js +82 -0
  111. package/src/components/molecules/FButtonGroup/FButtonGroup.test.ts +44 -0
  112. package/src/components/molecules/FButtonGroup/FButtonGroup.vue +31 -0
  113. package/src/components/molecules/FCard/FCard.stories.js +136 -0
  114. package/src/components/molecules/FCard/FCard.test.ts +87 -0
  115. package/src/components/molecules/FCard/FCard.vue +75 -0
  116. package/src/components/molecules/FDatePicker/FDatePicker.stories.js +305 -0
  117. package/src/components/molecules/FDatePicker/FDatePicker.test.ts +282 -0
  118. package/src/components/molecules/FDatePicker/FDatePicker.vue +750 -0
  119. package/src/components/molecules/FEmptyState/FEmptyState.stories.js +98 -0
  120. package/src/components/molecules/FEmptyState/FEmptyState.test.ts +82 -0
  121. package/src/components/molecules/FEmptyState/FEmptyState.vue +89 -0
  122. package/src/components/molecules/FFilePreview/FFilePreview.stories.js +130 -0
  123. package/src/components/molecules/FFilePreview/FFilePreview.test.ts +70 -0
  124. package/src/components/molecules/FFilePreview/FFilePreview.vue +125 -0
  125. package/src/components/molecules/FFormField/FFormField.stories.js +149 -0
  126. package/src/components/molecules/FFormField/FFormField.test.ts +85 -0
  127. package/src/components/molecules/FFormField/FFormField.vue +107 -0
  128. package/src/components/molecules/FListItem/FListItem.stories.js +158 -0
  129. package/src/components/molecules/FListItem/FListItem.test.ts +93 -0
  130. package/src/components/molecules/FListItem/FListItem.vue +113 -0
  131. package/src/components/molecules/FPagination/FPagination.stories.js +132 -0
  132. package/src/components/molecules/FPagination/FPagination.test.ts +79 -0
  133. package/src/components/molecules/FPagination/FPagination.vue +206 -0
  134. package/src/components/molecules/FSearchBar/FSearchBar.stories.js +129 -0
  135. package/src/components/molecules/FSearchBar/FSearchBar.test.ts +81 -0
  136. package/src/components/molecules/FSearchBar/FSearchBar.vue +180 -0
  137. package/src/components/molecules/FSelect/FSelect.stories.js +333 -0
  138. package/src/components/molecules/FSelect/FSelect.test.ts +478 -0
  139. package/src/components/molecules/FSelect/FSelect.vue +551 -0
  140. package/src/components/molecules/FStatCard/FStatCard.stories.js +144 -0
  141. package/src/components/molecules/FStatCard/FStatCard.test.ts +78 -0
  142. package/src/components/molecules/FStatCard/FStatCard.vue +106 -0
  143. package/src/components/molecules/FTabs/FTab.vue +63 -0
  144. package/src/components/molecules/FTabs/FTabs.stories.js +277 -0
  145. package/src/components/molecules/FTabs/FTabs.test.ts +264 -0
  146. package/src/components/molecules/FTabs/FTabs.vue +273 -0
  147. package/src/components/molecules/FToast/FToast.stories.js +150 -0
  148. package/src/components/molecules/FToast/FToast.test.ts +157 -0
  149. package/src/components/molecules/FToast/FToast.vue +283 -0
  150. package/src/components/molecules/index.ts +37 -0
  151. package/src/components/organisms/FActivityFeed/FActivityFeed.stories.js +217 -0
  152. package/src/components/organisms/FActivityFeed/FActivityFeed.test.ts +134 -0
  153. package/src/components/organisms/FActivityFeed/FActivityFeed.vue +589 -0
  154. package/src/components/organisms/FDataTable/FDataTable.stories.js +370 -0
  155. package/src/components/organisms/FDataTable/FDataTable.test.ts +248 -0
  156. package/src/components/organisms/FDataTable/FDataTable.vue +808 -0
  157. package/src/components/organisms/FDrawer/FDrawer.stories.js +296 -0
  158. package/src/components/organisms/FDrawer/FDrawer.test.ts +142 -0
  159. package/src/components/organisms/FDrawer/FDrawer.vue +303 -0
  160. package/src/components/organisms/FFileUpload/FFileUpload.stories.js +162 -0
  161. package/src/components/organisms/FFileUpload/FFileUpload.test.ts +103 -0
  162. package/src/components/organisms/FFileUpload/FFileUpload.vue +616 -0
  163. package/src/components/organisms/FFilterSidebar/FFilterSidebar.stories.js +161 -0
  164. package/src/components/organisms/FFilterSidebar/FFilterSidebar.test.ts +92 -0
  165. package/src/components/organisms/FFilterSidebar/FFilterSidebar.vue +458 -0
  166. package/src/components/organisms/FForm/FForm.stories.js +270 -0
  167. package/src/components/organisms/FForm/FForm.test.ts +63 -0
  168. package/src/components/organisms/FForm/FForm.vue +19 -0
  169. package/src/components/organisms/FModal/FModal.stories.js +227 -0
  170. package/src/components/organisms/FModal/FModal.test.ts +181 -0
  171. package/src/components/organisms/FModal/FModal.vue +319 -0
  172. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.stories.js +176 -0
  173. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.test.ts +95 -0
  174. package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.vue +577 -0
  175. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.stories.js +197 -0
  176. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.test.ts +114 -0
  177. package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.vue +212 -0
  178. package/src/components/organisms/FOnboardingStepper/FStepperProgress.stories.js +122 -0
  179. package/src/components/organisms/FOnboardingStepper/FStepperProgress.test.ts +130 -0
  180. package/src/components/organisms/FOnboardingStepper/FStepperProgress.vue +146 -0
  181. package/src/components/organisms/FPageHeader/FPageHeader.stories.js +142 -0
  182. package/src/components/organisms/FPageHeader/FPageHeader.test.ts +83 -0
  183. package/src/components/organisms/FPageHeader/FPageHeader.vue +241 -0
  184. package/src/components/organisms/FProfileSection/FProfileSection.stories.js +190 -0
  185. package/src/components/organisms/FProfileSection/FProfileSection.test.ts +85 -0
  186. package/src/components/organisms/FProfileSection/FProfileSection.vue +562 -0
  187. package/src/components/organisms/FToastProvider/FToastProvider.stories.js +290 -0
  188. package/src/components/organisms/FToastProvider/FToastProvider.test.ts +215 -0
  189. package/src/components/organisms/FToastProvider/FToastProvider.vue +214 -0
  190. package/src/components/organisms/FUserMenu/FUserMenu.stories.js +170 -0
  191. package/src/components/organisms/FUserMenu/FUserMenu.test.ts +102 -0
  192. package/src/components/organisms/FUserMenu/FUserMenu.vue +407 -0
  193. package/src/components/organisms/index.ts +29 -0
  194. package/src/components/utils/FThemeProvider.stories.js +236 -0
  195. package/src/components/utils/FThemeProvider.test.ts +244 -0
  196. package/src/components/utils/FThemeProvider.vue +191 -0
  197. package/src/components/utils/index.ts +3 -0
  198. package/src/components.d.ts +602 -0
  199. package/src/composables/README.md +233 -0
  200. package/src/composables/index.ts +25 -0
  201. package/src/composables/useDataTableState.test.ts +378 -0
  202. package/src/composables/useDataTableState.ts +361 -0
  203. package/src/composables/useFormValidation.test.ts +198 -0
  204. package/src/composables/useFormValidation.ts +178 -0
  205. package/src/composables/useSidebarState.test.ts +307 -0
  206. package/src/composables/useSidebarState.ts +201 -0
  207. package/src/env.d.ts +14 -0
  208. package/src/index.ts +167 -0
  209. package/src/styles/tailwind.css +173 -0
  210. package/src/types.ts +740 -0
@@ -0,0 +1,290 @@
1
+ import FToastProvider from './FToastProvider.vue';
2
+ import FButton from '../../atoms/FButton/FButton.vue';
3
+
4
+ export default {
5
+ title: 'Organisms/FToastProvider',
6
+ component: FToastProvider,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ position: {
10
+ control: { type: 'select' },
11
+ options: [
12
+ 'top-left',
13
+ 'top-center',
14
+ 'top-right',
15
+ 'bottom-left',
16
+ 'bottom-center',
17
+ 'bottom-right'
18
+ ],
19
+ description: 'Position par défaut des toasts'
20
+ },
21
+ maxToasts: {
22
+ control: 'number',
23
+ description: 'Nombre maximum de toasts affichés simultanément'
24
+ }
25
+ }
26
+ };
27
+
28
+ export const Default = () => ({
29
+ components: { FToastProvider, FButton },
30
+ template: `
31
+ <FToastProvider ref="provider">
32
+ <div class="p-8 space-y-4">
33
+ <h2 class="text-2xl font-bold mb-4">Système de Notifications Toast</h2>
34
+ <p class="mb-4">Cliquez sur les boutons ci-dessous pour afficher différents types de notifications.</p>
35
+
36
+ <div class="flex flex-wrap gap-3">
37
+ <FButton variant="success" @click="showSuccess">
38
+ Toast Succès
39
+ </FButton>
40
+ <FButton variant="danger" @click="showError">
41
+ Toast Erreur
42
+ </FButton>
43
+ <FButton variant="primary" @click="showInfo">
44
+ Toast Info
45
+ </FButton>
46
+ <FButton variant="warning" @click="showWarning">
47
+ Toast Avertissement
48
+ </FButton>
49
+ <FButton variant="outline" @click="showCustom">
50
+ Toast Personnalisé
51
+ </FButton>
52
+ <FButton variant="ghost" @click="clearAll">
53
+ Tout Effacer
54
+ </FButton>
55
+ </div>
56
+ </div>
57
+ </FToastProvider>
58
+ `,
59
+ methods: {
60
+ showSuccess() {
61
+ this.$refs.provider.success('Votre action a été effectuée avec succès.');
62
+ },
63
+ showError() {
64
+ this.$refs.provider.error(
65
+ "Une erreur s'est produite. Veuillez réessayer."
66
+ );
67
+ },
68
+ showInfo() {
69
+ this.$refs.provider.info(
70
+ 'Voici une information importante à prendre en compte.'
71
+ );
72
+ },
73
+ showWarning() {
74
+ this.$refs.provider.warning(
75
+ 'Veuillez vérifier vos informations avant de continuer.'
76
+ );
77
+ },
78
+ showCustom() {
79
+ this.$refs.provider.show({
80
+ variant: 'info',
81
+ title: 'Notification Personnalisée',
82
+ message: 'Ce toast a une durée personnalisée de 10 secondes.',
83
+ duration: 10000,
84
+ closable: true
85
+ });
86
+ },
87
+ clearAll() {
88
+ this.$refs.provider.clear();
89
+ }
90
+ }
91
+ });
92
+
93
+ export const DifferentPositions = () => ({
94
+ components: { FToastProvider, FButton },
95
+ template: `
96
+ <FToastProvider ref="provider">
97
+ <div class="p-8 space-y-4">
98
+ <h2 class="text-2xl font-bold mb-4">Positions des Toasts</h2>
99
+ <p class="mb-4">Cliquez sur les boutons pour afficher des toasts à différentes positions.</p>
100
+
101
+ <div class="grid grid-cols-3 gap-3">
102
+ <FButton variant="primary" @click="showTopLeft">
103
+ Haut Gauche
104
+ </FButton>
105
+ <FButton variant="primary" @click="showTopCenter">
106
+ Haut Centre
107
+ </FButton>
108
+ <FButton variant="primary" @click="showTopRight">
109
+ Haut Droite
110
+ </FButton>
111
+ <FButton variant="primary" @click="showBottomLeft">
112
+ Bas Gauche
113
+ </FButton>
114
+ <FButton variant="primary" @click="showBottomCenter">
115
+ Bas Centre
116
+ </FButton>
117
+ <FButton variant="primary" @click="showBottomRight">
118
+ Bas Droite
119
+ </FButton>
120
+ </div>
121
+ </div>
122
+ </FToastProvider>
123
+ `,
124
+ methods: {
125
+ showTopLeft() {
126
+ this.$refs.provider.show({
127
+ variant: 'info',
128
+ message: 'Toast en haut à gauche',
129
+ position: 'top-left'
130
+ });
131
+ },
132
+ showTopCenter() {
133
+ this.$refs.provider.show({
134
+ variant: 'info',
135
+ message: 'Toast en haut au centre',
136
+ position: 'top-center'
137
+ });
138
+ },
139
+ showTopRight() {
140
+ this.$refs.provider.show({
141
+ variant: 'info',
142
+ message: 'Toast en haut à droite',
143
+ position: 'top-right'
144
+ });
145
+ },
146
+ showBottomLeft() {
147
+ this.$refs.provider.show({
148
+ variant: 'success',
149
+ message: 'Toast en bas à gauche',
150
+ position: 'bottom-left'
151
+ });
152
+ },
153
+ showBottomCenter() {
154
+ this.$refs.provider.show({
155
+ variant: 'success',
156
+ message: 'Toast en bas au centre',
157
+ position: 'bottom-center'
158
+ });
159
+ },
160
+ showBottomRight() {
161
+ this.$refs.provider.show({
162
+ variant: 'success',
163
+ message: 'Toast en bas à droite',
164
+ position: 'bottom-right'
165
+ });
166
+ }
167
+ }
168
+ });
169
+
170
+ export const GlobalAPI = () => ({
171
+ components: { FToastProvider, FButton },
172
+ template: `
173
+ <FToastProvider>
174
+ <div class="p-8 space-y-4">
175
+ <h2 class="text-2xl font-bold mb-4">API Globale via $root.$toast</h2>
176
+ <p class="mb-4">Une fois le FToastProvider monté, vous pouvez accéder aux toasts via <code>this.$root.$toast</code>.</p>
177
+
178
+ <div class="flex flex-wrap gap-3">
179
+ <FButton variant="success" @click="$root.$toast.success('Opération réussie!')">
180
+ $root.$toast.success()
181
+ </FButton>
182
+ <FButton variant="danger" @click="$root.$toast.error('Erreur détectée!')">
183
+ $root.$toast.error()
184
+ </FButton>
185
+ <FButton variant="primary" @click="$root.$toast.info('Nouvelle information')">
186
+ $root.$toast.info()
187
+ </FButton>
188
+ <FButton variant="warning" @click="$root.$toast.warning('Attention requise')">
189
+ $root.$toast.warning()
190
+ </FButton>
191
+ </div>
192
+
193
+ <div class="mt-6 p-4 bg-neutral-100 rounded">
194
+ <h3 class="font-bold mb-2">Exemple d'utilisation dans vos composants:</h3>
195
+ <pre class="text-sm"><code>// Dans n'importe quel composant
196
+ this.$root.$toast.success('Message de succès');
197
+ this.$root.$toast.error('Message d\'erreur');
198
+ this.$root.$toast.info('Message d\'information');
199
+ this.$root.$toast.warning('Message d\'avertissement');
200
+
201
+ // Avec options personnalisées
202
+ this.$root.$toast.show({
203
+ variant: 'success',
204
+ title: 'Titre personnalisé',
205
+ message: 'Message personnalisé',
206
+ duration: 3000,
207
+ position: 'bottom-right'
208
+ });</code></pre>
209
+ </div>
210
+ </div>
211
+ </FToastProvider>
212
+ `
213
+ });
214
+
215
+ export const MaxToastsLimit = () => ({
216
+ components: { FToastProvider, FButton },
217
+ data() {
218
+ return {
219
+ counter: 0
220
+ };
221
+ },
222
+ template: `
223
+ <FToastProvider ref="provider" :max-toasts="3">
224
+ <div class="p-8 space-y-4">
225
+ <h2 class="text-2xl font-bold mb-4">Limite de Toasts (max: 3)</h2>
226
+ <p class="mb-4">Le provider est configuré pour afficher un maximum de 3 toasts. Les anciens seront automatiquement supprimés.</p>
227
+
228
+ <FButton variant="primary" @click="addToast">
229
+ Ajouter un Toast ({{ counter }})
230
+ </FButton>
231
+ </div>
232
+ </FToastProvider>
233
+ `,
234
+ methods: {
235
+ addToast() {
236
+ this.counter++;
237
+ this.$refs.provider.show({
238
+ variant: 'info',
239
+ title: `Toast #${this.counter}`,
240
+ message: `Ceci est le toast numéro ${this.counter}`,
241
+ duration: 0
242
+ });
243
+ }
244
+ }
245
+ });
246
+
247
+ export const PersistentToasts = () => ({
248
+ components: { FToastProvider, FButton },
249
+ template: `
250
+ <FToastProvider ref="provider">
251
+ <div class="p-8 space-y-4">
252
+ <h2 class="text-2xl font-bold mb-4">Toasts Persistants</h2>
253
+ <p class="mb-4">Ces toasts ne se ferment pas automatiquement (duration: 0).</p>
254
+
255
+ <div class="flex flex-wrap gap-3">
256
+ <FButton variant="primary" @click="showPersistent">
257
+ Toast Persistant
258
+ </FButton>
259
+ <FButton variant="outline" @click="showNonClosable">
260
+ Toast Non Fermable
261
+ </FButton>
262
+ <FButton variant="ghost" @click="$refs.provider.clear()">
263
+ Tout Effacer
264
+ </FButton>
265
+ </div>
266
+ </div>
267
+ </FToastProvider>
268
+ `,
269
+ methods: {
270
+ showPersistent() {
271
+ this.$refs.provider.show({
272
+ variant: 'info',
273
+ title: 'Toast Persistant',
274
+ message:
275
+ 'Ce toast ne se fermera pas automatiquement, mais vous pouvez le fermer manuellement.',
276
+ duration: 0
277
+ });
278
+ },
279
+ showNonClosable() {
280
+ this.$refs.provider.show({
281
+ variant: 'warning',
282
+ title: 'Toast Non Fermable',
283
+ message:
284
+ 'Ce toast ne peut être fermé ni automatiquement ni manuellement.',
285
+ duration: 0,
286
+ closable: false
287
+ });
288
+ }
289
+ }
290
+ });
@@ -0,0 +1,215 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { mount, createLocalVue } from '@vue/test-utils';
3
+ import FToastProvider from './FToastProvider.vue';
4
+ import FToast from '../../molecules/FToast/FToast.vue';
5
+
6
+ const localVue = createLocalVue();
7
+ localVue.component('FToast', FToast);
8
+
9
+ describe('FToastProvider', () => {
10
+ beforeEach(() => {
11
+ vi.useFakeTimers();
12
+ });
13
+
14
+ it('renders correctly with default slot', () => {
15
+ const wrapper = mount(FToastProvider, {
16
+ localVue,
17
+ slots: { default: '<div>App Content</div>' }
18
+ });
19
+ expect(wrapper.text()).toContain('App Content');
20
+ });
21
+
22
+ it('shows toast when show method is called', async () => {
23
+ const wrapper = mount(FToastProvider, { localVue });
24
+ const vm = wrapper.vm;
25
+
26
+ vm.show({
27
+ variant: 'success',
28
+ title: 'Test',
29
+ message: 'Test message'
30
+ });
31
+
32
+ await wrapper.vm.$nextTick();
33
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(1);
34
+ });
35
+
36
+ it('shows success toast using success method', async () => {
37
+ const wrapper = mount(FToastProvider, { localVue });
38
+ const vm = wrapper.vm;
39
+
40
+ vm.success('Success message');
41
+
42
+ await wrapper.vm.$nextTick();
43
+ const toasts = wrapper.findAllComponents({ name: 'FToast' });
44
+ expect(toasts.length).toBe(1);
45
+ });
46
+
47
+ it('shows error toast using error method', async () => {
48
+ const wrapper = mount(FToastProvider, { localVue });
49
+ const vm = wrapper.vm;
50
+
51
+ vm.error('Error message');
52
+
53
+ await wrapper.vm.$nextTick();
54
+ const toasts = wrapper.findAllComponents({ name: 'FToast' });
55
+ expect(toasts.length).toBe(1);
56
+ });
57
+
58
+ it('shows info toast using info method', async () => {
59
+ const wrapper = mount(FToastProvider, { localVue });
60
+ const vm = wrapper.vm;
61
+
62
+ vm.info('Info message');
63
+
64
+ await wrapper.vm.$nextTick();
65
+ const toasts = wrapper.findAllComponents({ name: 'FToast' });
66
+ expect(toasts.length).toBe(1);
67
+ });
68
+
69
+ it('shows warning toast using warning method', async () => {
70
+ const wrapper = mount(FToastProvider, { localVue });
71
+ const vm = wrapper.vm;
72
+
73
+ vm.warning('Warning message');
74
+
75
+ await wrapper.vm.$nextTick();
76
+ const toasts = wrapper.findAllComponents({ name: 'FToast' });
77
+ expect(toasts.length).toBe(1);
78
+ });
79
+
80
+ it('removes toast when removeToast is called', async () => {
81
+ const wrapper = mount(FToastProvider, { localVue });
82
+ const vm = wrapper.vm;
83
+
84
+ const id = vm.show({
85
+ variant: 'info',
86
+ message: 'Test'
87
+ });
88
+
89
+ await wrapper.vm.$nextTick();
90
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(1);
91
+
92
+ vm.removeToast(id);
93
+ await wrapper.vm.$nextTick();
94
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(0);
95
+ });
96
+
97
+ it('clears all toasts when clear is called', async () => {
98
+ const wrapper = mount(FToastProvider, { localVue });
99
+ const vm = wrapper.vm;
100
+
101
+ vm.show({ message: 'Toast 1' });
102
+ vm.show({ message: 'Toast 2' });
103
+ vm.show({ message: 'Toast 3' });
104
+
105
+ await wrapper.vm.$nextTick();
106
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(3);
107
+
108
+ vm.clear();
109
+ await wrapper.vm.$nextTick();
110
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(0);
111
+ });
112
+
113
+ it('respects maxToasts limit', async () => {
114
+ const wrapper = mount(FToastProvider, {
115
+ localVue,
116
+ propsData: { maxToasts: 3 }
117
+ });
118
+ const vm = wrapper.vm;
119
+
120
+ vm.show({ message: 'Toast 1' });
121
+ vm.show({ message: 'Toast 2' });
122
+ vm.show({ message: 'Toast 3' });
123
+ vm.show({ message: 'Toast 4' });
124
+
125
+ await wrapper.vm.$nextTick();
126
+ expect(wrapper.findAllComponents({ name: 'FToast' }).length).toBe(3);
127
+ });
128
+
129
+ it('uses default position when not specified', async () => {
130
+ const wrapper = mount(FToastProvider, {
131
+ localVue,
132
+ propsData: { position: 'bottom-left' }
133
+ });
134
+ const vm = wrapper.vm;
135
+
136
+ vm.show({ message: 'Test' });
137
+
138
+ await wrapper.vm.$nextTick();
139
+ expect(vm.toasts[0].position).toBe('bottom-left');
140
+ });
141
+
142
+ it('uses custom position when specified', async () => {
143
+ const wrapper = mount(FToastProvider, { localVue });
144
+ const vm = wrapper.vm;
145
+
146
+ vm.show({
147
+ message: 'Test',
148
+ position: 'top-center'
149
+ });
150
+
151
+ await wrapper.vm.$nextTick();
152
+ expect(vm.toasts[0].position).toBe('top-center');
153
+ });
154
+
155
+ it('emits show event when toast is shown', async () => {
156
+ const wrapper = mount(FToastProvider, { localVue });
157
+ const vm = wrapper.vm;
158
+
159
+ vm.show({ message: 'Test' });
160
+
161
+ await wrapper.vm.$nextTick();
162
+ expect(wrapper.emitted('show')).toBeTruthy();
163
+ });
164
+
165
+ it('emits remove event when toast is removed', async () => {
166
+ const wrapper = mount(FToastProvider, { localVue });
167
+ const vm = wrapper.vm;
168
+
169
+ const id = vm.show({ message: 'Test' });
170
+ await wrapper.vm.$nextTick();
171
+
172
+ vm.removeToast(id);
173
+ await wrapper.vm.$nextTick();
174
+ expect(wrapper.emitted('remove')).toBeTruthy();
175
+ });
176
+
177
+ it('emits clear event when all toasts are cleared', async () => {
178
+ const wrapper = mount(FToastProvider, { localVue });
179
+ const vm = wrapper.vm;
180
+
181
+ vm.show({ message: 'Test' });
182
+ await wrapper.vm.$nextTick();
183
+
184
+ vm.clear();
185
+ await wrapper.vm.$nextTick();
186
+ expect(wrapper.emitted('clear')).toBeTruthy();
187
+ });
188
+
189
+ it('groups toasts by position', async () => {
190
+ const wrapper = mount(FToastProvider, { localVue });
191
+ const vm = wrapper.vm;
192
+
193
+ vm.show({ message: 'Top Left', position: 'top-left' });
194
+ vm.show({ message: 'Top Right', position: 'top-right' });
195
+ vm.show({ message: 'Bottom Center', position: 'bottom-center' });
196
+
197
+ await wrapper.vm.$nextTick();
198
+
199
+ expect(vm.toastsByPosition['top-left'].length).toBe(1);
200
+ expect(vm.toastsByPosition['top-right'].length).toBe(1);
201
+ expect(vm.toastsByPosition['bottom-center'].length).toBe(1);
202
+ });
203
+
204
+ it('exposes global API on root instance', () => {
205
+ const wrapper = mount(FToastProvider, { localVue });
206
+
207
+ expect(wrapper.vm.$root.$toast).toBeDefined();
208
+ expect(typeof wrapper.vm.$root.$toast.show).toBe('function');
209
+ expect(typeof wrapper.vm.$root.$toast.success).toBe('function');
210
+ expect(typeof wrapper.vm.$root.$toast.error).toBe('function');
211
+ expect(typeof wrapper.vm.$root.$toast.info).toBe('function');
212
+ expect(typeof wrapper.vm.$root.$toast.warning).toBe('function');
213
+ expect(typeof wrapper.vm.$root.$toast.clear).toBe('function');
214
+ });
215
+ });
@@ -0,0 +1,214 @@
1
+ <template>
2
+ <div>
3
+ <slot />
4
+ <div v-for="pos in positions" :key="pos" :class="containerClasses(pos)">
5
+ <f-toast
6
+ v-for="toast in toastsByPosition[pos]"
7
+ :key="toast.id"
8
+ :variant="toast.variant"
9
+ :title="toast.title"
10
+ :message="toast.message"
11
+ :closable="toast.closable"
12
+ :duration="toast.duration"
13
+ :position="pos"
14
+ @close="removeToast(toast.id)"
15
+ />
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import FToast from '../../molecules/FToast/FToast.vue';
22
+
23
+ let toastId = 0;
24
+
25
+ export default {
26
+ name: 'FToastProvider',
27
+ components: {
28
+ FToast
29
+ },
30
+ props: {
31
+ /**
32
+ * Position par défaut des toasts
33
+ */
34
+ position: {
35
+ type: String,
36
+ default: 'top-right',
37
+ validator: (value) =>
38
+ [
39
+ 'top-left',
40
+ 'top-center',
41
+ 'top-right',
42
+ 'bottom-left',
43
+ 'bottom-center',
44
+ 'bottom-right'
45
+ ].includes(value)
46
+ },
47
+ /**
48
+ * Nombre maximum de toasts affichés simultanément
49
+ */
50
+ maxToasts: {
51
+ type: Number,
52
+ default: 5
53
+ }
54
+ },
55
+ data() {
56
+ return {
57
+ toasts: [],
58
+ positions: [
59
+ 'top-left',
60
+ 'top-center',
61
+ 'top-right',
62
+ 'bottom-left',
63
+ 'bottom-center',
64
+ 'bottom-right'
65
+ ]
66
+ };
67
+ },
68
+ computed: {
69
+ toastsByPosition() {
70
+ return this.positions.reduce((acc, position) => {
71
+ acc[position] = this.toasts.filter(
72
+ (toast) => toast.position === position
73
+ );
74
+ return acc;
75
+ }, {});
76
+ }
77
+ },
78
+ created() {
79
+ // Expose API globally via $root for Vue 2 compatibility
80
+ // Note: In Vue 3, this should be replaced with provide/inject or a plugin
81
+ // This approach is acceptable for Vue 2 applications for convenience
82
+ if (this.$root && !this.$root.$toast) {
83
+ this.$root.$toast = {
84
+ show: this.show,
85
+ success: this.success,
86
+ error: this.error,
87
+ info: this.info,
88
+ warning: this.warning,
89
+ clear: this.clear
90
+ };
91
+ } else if (this.$root && this.$root.$toast) {
92
+ // Warn if another FToastProvider is already mounted
93
+ console.warn(
94
+ 'FToastProvider: Multiple instances detected. Only one FToastProvider should be mounted at a time. The global API will use the first mounted instance.'
95
+ );
96
+ }
97
+ },
98
+ beforeDestroy() {
99
+ // Clean up global API
100
+ if (this.$root && this.$root.$toast) {
101
+ delete this.$root.$toast;
102
+ }
103
+ },
104
+ methods: {
105
+ containerClasses(position) {
106
+ const baseClasses =
107
+ 'fixed z-50 flex flex-col gap-3 p-4 pointer-events-none';
108
+ const positionClasses = {
109
+ 'top-left': 'top-0 left-0',
110
+ 'top-center': 'top-0 left-1/2 -translate-x-1/2',
111
+ 'top-right': 'top-0 right-0',
112
+ 'bottom-left': 'bottom-0 left-0',
113
+ 'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2',
114
+ 'bottom-right': 'bottom-0 right-0'
115
+ };
116
+
117
+ return `${baseClasses} ${positionClasses[position]}`;
118
+ },
119
+ /**
120
+ * Affiche un toast avec des options personnalisées
121
+ */
122
+ show(options) {
123
+ const toast = {
124
+ id: ++toastId,
125
+ variant: options.variant || 'info',
126
+ title: options.title || '',
127
+ message: options.message || '',
128
+ closable: options.closable !== undefined ? options.closable : true,
129
+ duration: options.duration !== undefined ? options.duration : 5000,
130
+ position: options.position || this.position
131
+ };
132
+
133
+ // Limit the number of toasts
134
+ if (this.toasts.length >= this.maxToasts) {
135
+ this.toasts.shift();
136
+ }
137
+
138
+ this.toasts.push(toast);
139
+ this.$emit('show', toast);
140
+
141
+ return toast.id;
142
+ },
143
+ /**
144
+ * Affiche un toast de succès
145
+ */
146
+ success(message, title = 'Succès', options = {}) {
147
+ return this.show({
148
+ variant: 'success',
149
+ title,
150
+ message,
151
+ ...options
152
+ });
153
+ },
154
+ /**
155
+ * Affiche un toast d'erreur
156
+ */
157
+ error(message, title = 'Erreur', options = {}) {
158
+ return this.show({
159
+ variant: 'error',
160
+ title,
161
+ message,
162
+ ...options
163
+ });
164
+ },
165
+ /**
166
+ * Affiche un toast d'information
167
+ */
168
+ info(message, title = 'Information', options = {}) {
169
+ return this.show({
170
+ variant: 'info',
171
+ title,
172
+ message,
173
+ ...options
174
+ });
175
+ },
176
+ /**
177
+ * Affiche un toast d'avertissement
178
+ */
179
+ warning(message, title = 'Avertissement', options = {}) {
180
+ return this.show({
181
+ variant: 'warning',
182
+ title,
183
+ message,
184
+ ...options
185
+ });
186
+ },
187
+ /**
188
+ * Supprime un toast spécifique
189
+ */
190
+ removeToast(id) {
191
+ const index = this.toasts.findIndex((t) => t.id === id);
192
+ if (index !== -1) {
193
+ const toast = this.toasts[index];
194
+ this.toasts.splice(index, 1);
195
+ this.$emit('remove', toast);
196
+ }
197
+ },
198
+ /**
199
+ * Supprime tous les toasts
200
+ */
201
+ clear() {
202
+ this.toasts = [];
203
+ this.$emit('clear');
204
+ }
205
+ }
206
+ };
207
+ </script>
208
+
209
+ <style scoped>
210
+ /* Ensure toasts are clickable */
211
+ .fixed > * {
212
+ pointer-events: auto;
213
+ }
214
+ </style>