@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,157 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { mount, createLocalVue } from '@vue/test-utils';
3
+ import FToast from './FToast.vue';
4
+ import FIcon from '../../atoms/FIcon/FIcon.vue';
5
+ import FTypography from '../../atoms/FTypography/FTypography.vue';
6
+ import FButton from '../../atoms/FButton/FButton.vue';
7
+
8
+ const localVue = createLocalVue();
9
+ localVue.component('FIcon', FIcon);
10
+ localVue.component('FTypography', FTypography);
11
+ localVue.component('FButton', FButton);
12
+
13
+ describe('FToast', () => {
14
+ beforeEach(() => {
15
+ vi.useFakeTimers();
16
+ });
17
+
18
+ afterEach(() => {
19
+ vi.restoreAllMocks();
20
+ });
21
+
22
+ it('renders correctly with default props', () => {
23
+ const wrapper = mount(FToast, { localVue });
24
+ expect(wrapper.find('[role="alert"]').exists()).toBe(true);
25
+ });
26
+
27
+ it('displays title when provided', () => {
28
+ const wrapper = mount(FToast, {
29
+ localVue,
30
+ propsData: { title: 'Toast Title' }
31
+ });
32
+ expect(wrapper.text()).toContain('Toast Title');
33
+ });
34
+
35
+ it('displays message when provided', () => {
36
+ const wrapper = mount(FToast, {
37
+ localVue,
38
+ propsData: { message: 'Toast message content' }
39
+ });
40
+ expect(wrapper.text()).toContain('Toast message content');
41
+ });
42
+
43
+ it('applies correct variant classes', () => {
44
+ const variants = ['success', 'error', 'info', 'warning'] as const;
45
+ variants.forEach((variant) => {
46
+ const wrapper = mount(FToast, {
47
+ localVue,
48
+ propsData: { variant }
49
+ });
50
+ expect(wrapper.find('[role="alert"]').exists()).toBe(true);
51
+ });
52
+ });
53
+
54
+ it('shows close button by default', () => {
55
+ const wrapper = mount(FToast, { localVue });
56
+ expect(wrapper.findComponent({ name: 'FButton' }).exists()).toBe(true);
57
+ });
58
+
59
+ it('hides close button when closable is false', () => {
60
+ const wrapper = mount(FToast, {
61
+ localVue,
62
+ propsData: { closable: false }
63
+ });
64
+ expect(wrapper.findComponent({ name: 'FButton' }).exists()).toBe(false);
65
+ });
66
+
67
+ it('emits close event when close button is clicked', async () => {
68
+ const wrapper = mount(FToast, { localVue });
69
+ await wrapper.findComponent({ name: 'FButton' }).trigger('click');
70
+ expect(wrapper.emitted('close')).toBeTruthy();
71
+ });
72
+
73
+ it('displays slot content', () => {
74
+ const wrapper = mount(FToast, {
75
+ localVue,
76
+ slots: { default: '<p>Custom content</p>' }
77
+ });
78
+ expect(wrapper.html()).toContain('Custom content');
79
+ });
80
+
81
+ it('auto-closes after duration', async () => {
82
+ const wrapper = mount(FToast, {
83
+ localVue,
84
+ propsData: { duration: 3000 }
85
+ });
86
+
87
+ expect(wrapper.emitted('close')).toBeFalsy();
88
+
89
+ vi.advanceTimersByTime(3000);
90
+
91
+ await wrapper.vm.$nextTick();
92
+ expect(wrapper.emitted('close')).toBeTruthy();
93
+ });
94
+
95
+ it('does not auto-close when duration is 0', async () => {
96
+ const wrapper = mount(FToast, {
97
+ localVue,
98
+ propsData: { duration: 0 }
99
+ });
100
+
101
+ vi.advanceTimersByTime(10000);
102
+
103
+ await wrapper.vm.$nextTick();
104
+ expect(wrapper.emitted('close')).toBeFalsy();
105
+ });
106
+
107
+ it('has pause and resume timer methods', () => {
108
+ const wrapper = mount(FToast, {
109
+ localVue,
110
+ propsData: { duration: 3000 }
111
+ });
112
+
113
+ // Verify the methods exist
114
+ expect(typeof wrapper.vm.pauseTimer).toBe('function');
115
+ expect(typeof wrapper.vm.resumeTimer).toBe('function');
116
+
117
+ // Call the methods to ensure they don't throw
118
+ wrapper.vm.pauseTimer();
119
+ wrapper.vm.resumeTimer();
120
+ });
121
+
122
+ it('uses correct icon for each variant', () => {
123
+ const variants = {
124
+ success: 'success',
125
+ error: 'error',
126
+ info: 'info',
127
+ warning: 'warning'
128
+ } as const;
129
+
130
+ Object.entries(variants).forEach(([variant, expectedIcon]) => {
131
+ const wrapper = mount(FToast, {
132
+ localVue,
133
+ propsData: { variant }
134
+ });
135
+ expect(wrapper.vm.iconName).toBe(expectedIcon);
136
+ });
137
+ });
138
+
139
+ it('applies correct transition based on position', () => {
140
+ const positions = [
141
+ { position: 'top-left', transition: 'toast-slide-left' },
142
+ { position: 'top-right', transition: 'toast-slide-right' },
143
+ { position: 'top-center', transition: 'toast-slide-down' },
144
+ { position: 'bottom-left', transition: 'toast-slide-left' },
145
+ { position: 'bottom-right', transition: 'toast-slide-right' },
146
+ { position: 'bottom-center', transition: 'toast-slide-down' }
147
+ ] as const;
148
+
149
+ positions.forEach(({ position, transition }) => {
150
+ const wrapper = mount(FToast, {
151
+ localVue,
152
+ propsData: { position }
153
+ });
154
+ expect(wrapper.vm.transitionName).toBe(transition);
155
+ });
156
+ });
157
+ });
@@ -0,0 +1,283 @@
1
+ <template>
2
+ <transition
3
+ :name="transitionName"
4
+ @before-enter="onBeforeEnter"
5
+ @enter="onEnter"
6
+ @leave="onLeave"
7
+ >
8
+ <div
9
+ v-if="isVisible"
10
+ :class="toastClasses"
11
+ role="alert"
12
+ aria-live="polite"
13
+ @mouseenter="pauseTimer"
14
+ @mouseleave="resumeTimer"
15
+ >
16
+ <f-icon :name="iconName" size="md" />
17
+ <div class="flex-1 min-w-0">
18
+ <f-typography v-if="title" variant="h6" :class="titleClasses">
19
+ {{ title }}
20
+ </f-typography>
21
+ <f-typography v-if="message" variant="body" :class="messageClasses">
22
+ {{ message }}
23
+ </f-typography>
24
+ <slot />
25
+ </div>
26
+ <f-button
27
+ v-if="closable"
28
+ variant="text"
29
+ size="small"
30
+ :class="closeButtonClasses"
31
+ @click="handleClose"
32
+ >
33
+ <f-icon name="close" size="sm" />
34
+ <span class="sr-only">Fermer la notification</span>
35
+ </f-button>
36
+ </div>
37
+ </transition>
38
+ </template>
39
+
40
+ <script>
41
+ import FIcon from '../../atoms/FIcon/FIcon.vue';
42
+ import FTypography from '../../atoms/FTypography/FTypography.vue';
43
+ import FButton from '../../atoms/FButton/FButton.vue';
44
+
45
+ const VARIANT_STYLES = {
46
+ success: {
47
+ container: 'bg-success-50 border-success-200 text-success-800',
48
+ title: 'text-success-800',
49
+ message: 'text-success-700',
50
+ closeButton: 'text-success-600 hover:text-success-800',
51
+ icon: 'success'
52
+ },
53
+ error: {
54
+ container: 'bg-danger-50 border-danger-200 text-danger-800',
55
+ title: 'text-danger-800',
56
+ message: 'text-danger-700',
57
+ closeButton: 'text-danger-600 hover:text-danger-800',
58
+ icon: 'error'
59
+ },
60
+ info: {
61
+ container: 'bg-primary-50 border-primary-200 text-primary-800',
62
+ title: 'text-primary-800',
63
+ message: 'text-primary-700',
64
+ closeButton: 'text-primary-600 hover:text-primary-800',
65
+ icon: 'info'
66
+ },
67
+ warning: {
68
+ container: 'bg-warning-50 border-warning-200 text-warning-800',
69
+ title: 'text-warning-800',
70
+ message: 'text-warning-700',
71
+ closeButton: 'text-warning-600 hover:text-warning-800',
72
+ icon: 'warning'
73
+ }
74
+ };
75
+
76
+ export default {
77
+ name: 'FToast',
78
+ components: {
79
+ FIcon,
80
+ FTypography,
81
+ FButton
82
+ },
83
+ props: {
84
+ /**
85
+ * Type de toast (success, error, info, warning)
86
+ */
87
+ variant: {
88
+ type: String,
89
+ default: 'info',
90
+ validator: (value) =>
91
+ ['success', 'error', 'info', 'warning'].includes(value)
92
+ },
93
+ /**
94
+ * Titre du toast
95
+ */
96
+ title: {
97
+ type: String,
98
+ default: ''
99
+ },
100
+ /**
101
+ * Message du toast
102
+ */
103
+ message: {
104
+ type: String,
105
+ default: ''
106
+ },
107
+ /**
108
+ * Afficher le bouton de fermeture
109
+ */
110
+ closable: {
111
+ type: Boolean,
112
+ default: true
113
+ },
114
+ /**
115
+ * Durée d'affichage en millisecondes (0 = pas de fermeture automatique)
116
+ */
117
+ duration: {
118
+ type: Number,
119
+ default: 5000
120
+ },
121
+ /**
122
+ * Position du toast (utilisé pour l'animation)
123
+ */
124
+ position: {
125
+ type: String,
126
+ default: 'top-right',
127
+ validator: (value) =>
128
+ [
129
+ 'top-left',
130
+ 'top-center',
131
+ 'top-right',
132
+ 'bottom-left',
133
+ 'bottom-center',
134
+ 'bottom-right'
135
+ ].includes(value)
136
+ }
137
+ },
138
+ data() {
139
+ return {
140
+ isVisible: true,
141
+ timer: null,
142
+ remainingTime: this.duration,
143
+ startTime: null,
144
+ pausedAt: null
145
+ };
146
+ },
147
+ computed: {
148
+ variantStyles() {
149
+ return VARIANT_STYLES[this.variant];
150
+ },
151
+ toastClasses() {
152
+ const baseClasses =
153
+ 'flex items-start gap-3 p-4 rounded-lg border shadow-lg min-w-[320px] max-w-md';
154
+ return `${baseClasses} ${this.variantStyles.container}`;
155
+ },
156
+ titleClasses() {
157
+ return this.variantStyles.title;
158
+ },
159
+ messageClasses() {
160
+ return this.variantStyles.message;
161
+ },
162
+ closeButtonClasses() {
163
+ return `flex-shrink-0 ${this.variantStyles.closeButton}`;
164
+ },
165
+ iconName() {
166
+ return this.variantStyles.icon;
167
+ },
168
+ transitionName() {
169
+ if (this.position.includes('left')) {
170
+ return 'toast-slide-left';
171
+ } else if (this.position.includes('right')) {
172
+ return 'toast-slide-right';
173
+ }
174
+ return 'toast-slide-down';
175
+ }
176
+ },
177
+ mounted() {
178
+ if (this.duration > 0) {
179
+ this.startTimer();
180
+ }
181
+ },
182
+ beforeDestroy() {
183
+ this.clearTimer();
184
+ },
185
+ methods: {
186
+ startTimer() {
187
+ this.clearTimer();
188
+ this.startTime = Date.now();
189
+ this.timer = setTimeout(() => {
190
+ this.handleClose();
191
+ }, this.remainingTime);
192
+ },
193
+ clearTimer() {
194
+ if (this.timer) {
195
+ clearTimeout(this.timer);
196
+ this.timer = null;
197
+ }
198
+ },
199
+ pauseTimer() {
200
+ if (this.timer && this.duration > 0 && this.startTime) {
201
+ const elapsed = Date.now() - this.startTime;
202
+ this.remainingTime = Math.max(0, this.remainingTime - elapsed);
203
+ this.clearTimer();
204
+ this.pausedAt = Date.now();
205
+ }
206
+ },
207
+ resumeTimer() {
208
+ if (this.pausedAt && this.duration > 0 && this.remainingTime > 0) {
209
+ this.pausedAt = null;
210
+ this.startTimer();
211
+ } else if (this.pausedAt && this.remainingTime <= 0) {
212
+ this.handleClose();
213
+ }
214
+ },
215
+ handleClose() {
216
+ this.isVisible = false;
217
+ this.$emit('close');
218
+ },
219
+ onBeforeEnter(el) {
220
+ el.style.opacity = '0';
221
+ },
222
+ onEnter(el, done) {
223
+ // Force reflow
224
+ void el.offsetHeight;
225
+ el.style.transition = 'all 0.3s ease-out';
226
+ el.style.opacity = '1';
227
+ done();
228
+ },
229
+ onLeave(el, done) {
230
+ el.style.transition = 'all 0.3s ease-in';
231
+ el.style.opacity = '0';
232
+ setTimeout(done, 300);
233
+ }
234
+ }
235
+ };
236
+ </script>
237
+
238
+ <style scoped>
239
+ .toast-slide-left-enter-active,
240
+ .toast-slide-left-leave-active {
241
+ transition: all 0.3s ease-out;
242
+ }
243
+
244
+ .toast-slide-left-enter {
245
+ transform: translateX(-100%);
246
+ opacity: 0;
247
+ }
248
+
249
+ .toast-slide-left-leave-to {
250
+ transform: translateX(-100%);
251
+ opacity: 0;
252
+ }
253
+
254
+ .toast-slide-right-enter-active,
255
+ .toast-slide-right-leave-active {
256
+ transition: all 0.3s ease-out;
257
+ }
258
+
259
+ .toast-slide-right-enter {
260
+ transform: translateX(100%);
261
+ opacity: 0;
262
+ }
263
+
264
+ .toast-slide-right-leave-to {
265
+ transform: translateX(100%);
266
+ opacity: 0;
267
+ }
268
+
269
+ .toast-slide-down-enter-active,
270
+ .toast-slide-down-leave-active {
271
+ transition: all 0.3s ease-out;
272
+ }
273
+
274
+ .toast-slide-down-enter {
275
+ transform: translateY(-100%);
276
+ opacity: 0;
277
+ }
278
+
279
+ .toast-slide-down-leave-to {
280
+ transform: translateY(-100%);
281
+ opacity: 0;
282
+ }
283
+ </style>
@@ -0,0 +1,37 @@
1
+ import FAccordionItem from './FAccordionItem/FAccordionItem.vue';
2
+ import FAlert from './FAlert/FAlert.vue';
3
+ import FBreadcrumb from './FBreadcrumb/FBreadcrumb.vue';
4
+ import FButtonGroup from './FButtonGroup/FButtonGroup.vue';
5
+ import FDatePicker from './FDatePicker/FDatePicker.vue';
6
+ import FEmptyState from './FEmptyState/FEmptyState.vue';
7
+ import FFilePreview from './FFilePreview/FFilePreview.vue';
8
+ import FFormField from './FFormField/FFormField.vue';
9
+ import FCard from './FCard/FCard.vue';
10
+ import FSearchBar from './FSearchBar/FSearchBar.vue';
11
+ import FListItem from './FListItem/FListItem.vue';
12
+ import FPagination from './FPagination/FPagination.vue';
13
+ import FStatCard from './FStatCard/FStatCard.vue';
14
+ import FSelect from './FSelect/FSelect.vue';
15
+ import FTabs from './FTabs/FTabs.vue';
16
+ import FTab from './FTabs/FTab.vue';
17
+ import FToast from './FToast/FToast.vue';
18
+
19
+ export {
20
+ FAccordionItem,
21
+ FAlert,
22
+ FBreadcrumb,
23
+ FButtonGroup,
24
+ FDatePicker,
25
+ FEmptyState,
26
+ FFilePreview,
27
+ FFormField,
28
+ FCard,
29
+ FSearchBar,
30
+ FListItem,
31
+ FPagination,
32
+ FStatCard,
33
+ FSelect,
34
+ FTabs,
35
+ FTab,
36
+ FToast
37
+ };
@@ -0,0 +1,217 @@
1
+ import FActivityFeed from './FActivityFeed.vue';
2
+
3
+ export default {
4
+ title: 'Organisms/FActivityFeed',
5
+ component: FActivityFeed,
6
+ tags: ['autodocs'],
7
+ argTypes: {
8
+ events: {
9
+ control: 'object',
10
+ description: 'Liste des événements'
11
+ },
12
+ loading: {
13
+ control: 'boolean',
14
+ description: 'État de chargement'
15
+ },
16
+ loadingNew: {
17
+ control: 'boolean',
18
+ description: 'Chargement de nouveaux événements'
19
+ },
20
+ hasMore: {
21
+ control: 'boolean',
22
+ description: "Plus d'événements disponibles"
23
+ },
24
+ clickable: {
25
+ control: 'boolean',
26
+ description: 'Événements cliquables'
27
+ },
28
+ showTimeline: {
29
+ control: 'boolean',
30
+ description: 'Afficher la timeline'
31
+ },
32
+ truncateContent: {
33
+ control: 'boolean',
34
+ description: 'Tronquer le contenu'
35
+ }
36
+ }
37
+ };
38
+
39
+ const sampleEvents = [
40
+ {
41
+ id: 1,
42
+ type: 'create',
43
+ title: 'Nouveau projet créé',
44
+ description: 'Le projet "Application Mobile" a été créé.',
45
+ actor: 'Jean Dupont',
46
+ timestamp: new Date(Date.now() - 1000 * 60 * 5).toISOString()
47
+ },
48
+ {
49
+ id: 2,
50
+ type: 'comment',
51
+ title: 'Nouveau commentaire',
52
+ description: 'Un commentaire a été ajouté sur la tâche #42.',
53
+ actor: 'Marie Martin',
54
+ timestamp: new Date(Date.now() - 1000 * 60 * 30).toISOString()
55
+ },
56
+ {
57
+ id: 3,
58
+ type: 'update',
59
+ title: 'Document mis à jour',
60
+ description: 'Le fichier "Spécifications.pdf" a été modifié.',
61
+ actor: 'Pierre Durand',
62
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 2).toISOString()
63
+ },
64
+ {
65
+ id: 4,
66
+ type: 'status',
67
+ title: 'Statut changé',
68
+ description: 'La tâche "Design homepage" est passée à "En cours".',
69
+ actor: 'Sophie Petit',
70
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 5).toISOString()
71
+ },
72
+ {
73
+ id: 5,
74
+ type: 'delete',
75
+ title: 'Élément supprimé',
76
+ description: 'Le fichier temporaire a été supprimé.',
77
+ actor: 'Admin',
78
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString()
79
+ }
80
+ ];
81
+
82
+ const Template = (args, { argTypes }) => ({
83
+ components: { FActivityFeed },
84
+ props: Object.keys(argTypes),
85
+ template: '<FActivityFeed v-bind="$props" />'
86
+ });
87
+
88
+ export const Default = Template.bind({});
89
+ Default.args = {
90
+ events: sampleEvents
91
+ };
92
+
93
+ export const WithTimeline = Template.bind({});
94
+ WithTimeline.args = {
95
+ events: sampleEvents,
96
+ showTimeline: true
97
+ };
98
+
99
+ export const Clickable = Template.bind({});
100
+ Clickable.args = {
101
+ events: sampleEvents,
102
+ clickable: true
103
+ };
104
+
105
+ export const Loading = Template.bind({});
106
+ Loading.args = {
107
+ events: sampleEvents,
108
+ loading: true
109
+ };
110
+
111
+ export const LoadingNew = Template.bind({});
112
+ LoadingNew.args = {
113
+ events: sampleEvents,
114
+ loadingNew: true
115
+ };
116
+
117
+ export const WithLoadMore = Template.bind({});
118
+ WithLoadMore.args = {
119
+ events: sampleEvents,
120
+ hasMore: true
121
+ };
122
+
123
+ export const Empty = Template.bind({});
124
+ Empty.args = {
125
+ events: []
126
+ };
127
+
128
+ export const CustomEmptyState = Template.bind({});
129
+ CustomEmptyState.args = {
130
+ events: [],
131
+ emptyTitle: 'Aucune activité récente',
132
+ emptyDescription: "Il n'y a eu aucune activité dans les dernières 24 heures.",
133
+ emptyActionLabel: 'Rafraîchir'
134
+ };
135
+
136
+ export const Interactive = () => ({
137
+ components: { FActivityFeed },
138
+ data() {
139
+ return {
140
+ events: sampleEvents,
141
+ loading: false
142
+ };
143
+ },
144
+ methods: {
145
+ handleEventClick(event) {
146
+ alert(`Événement cliqué: ${event.title}`);
147
+ },
148
+ handleLoadMore() {
149
+ this.loading = true;
150
+ setTimeout(() => {
151
+ this.events = [
152
+ ...this.events,
153
+ {
154
+ id: Date.now(),
155
+ type: 'create',
156
+ title: 'Nouvel événement chargé',
157
+ description: "Cet événement vient d'être chargé.",
158
+ actor: 'Système',
159
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 48).toISOString()
160
+ }
161
+ ];
162
+ this.loading = false;
163
+ }, 1000);
164
+ }
165
+ },
166
+ template: `
167
+ <FActivityFeed
168
+ :events="events"
169
+ :loading="loading"
170
+ :hasMore="true"
171
+ clickable
172
+ showTimeline
173
+ @event-click="handleEventClick"
174
+ @load-more="handleLoadMore"
175
+ />
176
+ `
177
+ });
178
+
179
+ export const VirtualizedLargeDataset = () => ({
180
+ components: { FActivityFeed },
181
+ data() {
182
+ return {
183
+ events: Array.from({ length: 5000 }, (_, i) => ({
184
+ id: i + 1,
185
+ type: ['create', 'comment', 'update', 'status', 'delete'][i % 5],
186
+ title: `Événement ${i + 1}`,
187
+ description: `Description de l'événement numéro ${
188
+ i + 1
189
+ }. Ceci est un événement de test généré automatiquement.`,
190
+ actor: `Utilisateur ${(i % 10) + 1}`,
191
+ timestamp: new Date(Date.now() - i * 60 * 1000).toISOString()
192
+ }))
193
+ };
194
+ },
195
+ template: `
196
+ <div>
197
+ <p class="text-sm text-neutral-600 mb-4">
198
+ Ce fil d'activité utilise la virtualisation pour afficher 5 000 événements de manière fluide.
199
+ Seuls les éléments visibles sont rendus dans le DOM, ce qui améliore drastiquement les performances.
200
+ </p>
201
+ <FActivityFeed
202
+ :events="events"
203
+ virtual
204
+ showTimeline
205
+ clickable
206
+ />
207
+ </div>
208
+ `
209
+ });
210
+ VirtualizedLargeDataset.parameters = {
211
+ docs: {
212
+ description: {
213
+ story:
214
+ "La virtualisation permet d'afficher des milliers d'événements sans ralentissement. Le fil d'activité ne rend que les événements visibles à l'écran, ce qui réduit la charge du DOM et améliore les performances de défilement."
215
+ }
216
+ }
217
+ };