@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,562 @@
1
+ <template>
2
+ <f-card :bordered="bordered">
3
+ <!-- Header with title and edit button -->
4
+ <template #header>
5
+ <div class="flex items-center justify-between w-full">
6
+ <div class="flex-1 min-w-0">
7
+ <f-typography v-if="title" variant="h5">{{ title }}</f-typography>
8
+ <f-typography v-if="subtitle" variant="caption">{{
9
+ subtitle
10
+ }}</f-typography>
11
+ </div>
12
+ <f-button
13
+ v-if="!isEditing && editable"
14
+ variant="ghost"
15
+ size="small"
16
+ @click="startEditing"
17
+ >
18
+ <slot name="edit-button-content">
19
+ {{ editButtonLabel }}
20
+ </slot>
21
+ </f-button>
22
+ </div>
23
+ </template>
24
+
25
+ <!-- Loading state overlay -->
26
+ <div v-if="isLoading" class="flex items-center justify-center py-8">
27
+ <f-loader :size="loaderSize" :label="loadingLabel" />
28
+ </div>
29
+
30
+ <!-- Alert for success/error messages -->
31
+ <f-alert
32
+ v-if="alertMessage && !isLoading"
33
+ :variant="alertVariant"
34
+ :message="alertMessage"
35
+ :closable="true"
36
+ class="mb-4"
37
+ @close="clearAlert"
38
+ />
39
+
40
+ <!-- Content area -->
41
+ <div v-if="!isLoading">
42
+ <!-- Edit Mode -->
43
+ <f-form v-if="isEditing" @submit="handleSubmit">
44
+ <!-- Avatar section with edit capability -->
45
+ <div v-if="showAvatar" class="flex items-center gap-4 mb-4">
46
+ <div class="relative">
47
+ <f-avatar
48
+ :src="avatarSrc"
49
+ :alt="avatarAlt"
50
+ :initials="avatarInitials"
51
+ :name="avatarName"
52
+ :size="avatarSize"
53
+ :shape="avatarShape"
54
+ />
55
+ <slot name="avatar-edit">
56
+ <button
57
+ v-if="avatarEditable"
58
+ type="button"
59
+ class="absolute bottom-0 right-0 bg-primary-600 text-white rounded-full p-1.5 hover:bg-primary-700 transition-colors duration-[var(--transition-duration-base)] ease-[var(--transition-easing-standard)]"
60
+ @click="handleAvatarEdit"
61
+ >
62
+ <svg
63
+ class="w-3 h-3"
64
+ fill="none"
65
+ stroke="currentColor"
66
+ viewBox="0 0 24 24"
67
+ >
68
+ <path
69
+ stroke-linecap="round"
70
+ stroke-linejoin="round"
71
+ stroke-width="2"
72
+ d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
73
+ />
74
+ </svg>
75
+ </button>
76
+ </slot>
77
+ </div>
78
+ <div class="flex-1">
79
+ <slot name="avatar-info" />
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Form fields slot -->
84
+ <slot
85
+ name="edit-fields"
86
+ :form-data="localFormData"
87
+ :errors="validationErrors"
88
+ >
89
+ <!-- Default field rendering if fields prop is provided -->
90
+ <div class="flex flex-col gap-4">
91
+ <template v-for="field in fields">
92
+ <div
93
+ v-if="field.type === 'textarea'"
94
+ :key="field.name"
95
+ class="flex flex-col gap-1.5"
96
+ >
97
+ <label
98
+ v-if="field.label"
99
+ :for="`field-${field.name}`"
100
+ :class="[
101
+ 'text-sm font-medium text-neutral-700',
102
+ {
103
+ 'after:content-[\'_*\'] after:text-danger-500':
104
+ field.required
105
+ }
106
+ ]"
107
+ >
108
+ {{ field.label }}
109
+ </label>
110
+ <f-textarea
111
+ :id="`field-${field.name}`"
112
+ :value="localFormData[field.name]"
113
+ :placeholder="field.placeholder"
114
+ :disabled="field.disabled"
115
+ :rows="field.rows || 3"
116
+ :error-message="validationErrors[field.name]"
117
+ @input="updateField(field.name, $event)"
118
+ />
119
+ </div>
120
+ <f-form-field
121
+ v-else
122
+ :key="field.name"
123
+ :value="localFormData[field.name]"
124
+ :label="field.label"
125
+ :type="field.type || 'text'"
126
+ :placeholder="field.placeholder"
127
+ :required="field.required"
128
+ :disabled="field.disabled"
129
+ :error-message="validationErrors[field.name]"
130
+ @input="updateField(field.name, $event)"
131
+ />
132
+ </template>
133
+ </div>
134
+ </slot>
135
+
136
+ <!-- Form actions -->
137
+ <template #actions>
138
+ <f-button
139
+ variant="outline"
140
+ type="button"
141
+ :disabled="isSubmitting"
142
+ @click="cancelEditing"
143
+ >
144
+ {{ cancelButtonLabel }}
145
+ </f-button>
146
+ <f-button
147
+ variant="primary"
148
+ type="submit"
149
+ :loading="isSubmitting"
150
+ :disabled="isSubmitting"
151
+ >
152
+ {{ saveButtonLabel }}
153
+ </f-button>
154
+ </template>
155
+ </f-form>
156
+
157
+ <!-- Read Mode -->
158
+ <div v-else>
159
+ <!-- Avatar section in read mode -->
160
+ <div v-if="showAvatar" class="flex items-center gap-4 mb-4">
161
+ <f-avatar
162
+ :src="avatarSrc"
163
+ :alt="avatarAlt"
164
+ :initials="avatarInitials"
165
+ :name="avatarName"
166
+ :size="avatarSize"
167
+ :shape="avatarShape"
168
+ />
169
+ <div class="flex-1">
170
+ <slot name="avatar-info" />
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Read mode content slot -->
175
+ <slot name="read-fields" :data="value">
176
+ <!-- Default read mode rendering if fields prop is provided -->
177
+ <div class="flex flex-col gap-3">
178
+ <div
179
+ v-for="field in fields"
180
+ :key="field.name"
181
+ class="flex flex-col"
182
+ >
183
+ <f-typography variant="caption" class="text-neutral-500">
184
+ {{ field.label }}
185
+ </f-typography>
186
+ <f-typography variant="body">
187
+ {{ value[field.name] || '-' }}
188
+ </f-typography>
189
+ </div>
190
+ </div>
191
+ </slot>
192
+ </div>
193
+ </div>
194
+ </f-card>
195
+ </template>
196
+
197
+ <script>
198
+ import FCard from '../../molecules/FCard/FCard.vue';
199
+ import FTypography from '../../atoms/FTypography/FTypography.vue';
200
+ import FButton from '../../atoms/FButton/FButton.vue';
201
+ import FLoader from '../../atoms/FLoader/FLoader.vue';
202
+ import FAlert from '../../molecules/FAlert/FAlert.vue';
203
+ import FForm from '../FForm/FForm.vue';
204
+ import FFormField from '../../molecules/FFormField/FFormField.vue';
205
+ import FTextarea from '../../atoms/FTextarea/FTextarea.vue';
206
+ import FAvatar from '../../atoms/FAvatar/FAvatar.vue';
207
+
208
+ export default {
209
+ name: 'FProfileSection',
210
+ components: {
211
+ FCard,
212
+ FTypography,
213
+ FButton,
214
+ FLoader,
215
+ FAlert,
216
+ FForm,
217
+ FFormField,
218
+ FTextarea,
219
+ FAvatar
220
+ },
221
+ props: {
222
+ /**
223
+ * Section title
224
+ */
225
+ title: {
226
+ type: String,
227
+ default: ''
228
+ },
229
+ /**
230
+ * Section subtitle
231
+ */
232
+ subtitle: {
233
+ type: String,
234
+ default: ''
235
+ },
236
+ /**
237
+ * Data object containing the profile/entity information
238
+ * Used for v-model support
239
+ */
240
+ value: {
241
+ type: Object,
242
+ default: () => ({})
243
+ },
244
+ /**
245
+ * Field definitions for automatic form generation
246
+ * Each field: { name, label, type, placeholder, required, disabled, rows (for textarea) }
247
+ */
248
+ fields: {
249
+ type: Array,
250
+ default: () => []
251
+ },
252
+ /**
253
+ * Whether the section is currently in editing mode
254
+ */
255
+ editing: {
256
+ type: Boolean,
257
+ default: false
258
+ },
259
+ /**
260
+ * Whether the section is editable (shows edit button)
261
+ */
262
+ editable: {
263
+ type: Boolean,
264
+ default: true
265
+ },
266
+ /**
267
+ * Whether the section is currently loading data
268
+ */
269
+ loading: {
270
+ type: Boolean,
271
+ default: false
272
+ },
273
+ /**
274
+ * Whether the form is currently submitting
275
+ */
276
+ submitting: {
277
+ type: Boolean,
278
+ default: false
279
+ },
280
+ /**
281
+ * Custom validation function
282
+ * Should return an object with field names as keys and error messages as values
283
+ * Return empty object if validation passes
284
+ */
285
+ validate: {
286
+ type: Function,
287
+ default: null
288
+ },
289
+ /**
290
+ * Avatar image source URL
291
+ */
292
+ avatarSrc: {
293
+ type: String,
294
+ default: ''
295
+ },
296
+ /**
297
+ * Avatar alt text
298
+ */
299
+ avatarAlt: {
300
+ type: String,
301
+ default: ''
302
+ },
303
+ /**
304
+ * Avatar initials
305
+ */
306
+ avatarInitials: {
307
+ type: String,
308
+ default: ''
309
+ },
310
+ /**
311
+ * Avatar name (for computing initials)
312
+ */
313
+ avatarName: {
314
+ type: String,
315
+ default: ''
316
+ },
317
+ /**
318
+ * Avatar size
319
+ */
320
+ avatarSize: {
321
+ type: String,
322
+ default: 'lg',
323
+ validator: (value) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value)
324
+ },
325
+ /**
326
+ * Avatar shape
327
+ */
328
+ avatarShape: {
329
+ type: String,
330
+ default: 'circle',
331
+ validator: (value) => ['circle', 'square'].includes(value)
332
+ },
333
+ /**
334
+ * Whether the avatar can be edited
335
+ */
336
+ avatarEditable: {
337
+ type: Boolean,
338
+ default: false
339
+ },
340
+ /**
341
+ * Whether the card has a border
342
+ */
343
+ bordered: {
344
+ type: Boolean,
345
+ default: true
346
+ },
347
+ /**
348
+ * Loader size
349
+ */
350
+ loaderSize: {
351
+ type: String,
352
+ default: 'md',
353
+ validator: (value) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value)
354
+ },
355
+ /**
356
+ * Loading state label for accessibility
357
+ */
358
+ loadingLabel: {
359
+ type: String,
360
+ default: 'Chargement en cours'
361
+ },
362
+ /**
363
+ * Edit button label
364
+ */
365
+ editButtonLabel: {
366
+ type: String,
367
+ default: 'Modifier'
368
+ },
369
+ /**
370
+ * Save button label
371
+ */
372
+ saveButtonLabel: {
373
+ type: String,
374
+ default: 'Enregistrer'
375
+ },
376
+ /**
377
+ * Cancel button label
378
+ */
379
+ cancelButtonLabel: {
380
+ type: String,
381
+ default: 'Annuler'
382
+ }
383
+ },
384
+ data() {
385
+ return {
386
+ internalEditing: false,
387
+ localFormData: {},
388
+ validationErrors: {},
389
+ alertMessage: '',
390
+ alertVariant: 'info'
391
+ };
392
+ },
393
+ computed: {
394
+ /**
395
+ * Computed property for editing mode with v-model support
396
+ */
397
+ isEditing: {
398
+ get() {
399
+ return this.editing || this.internalEditing;
400
+ },
401
+ set(value) {
402
+ this.internalEditing = value;
403
+ this.$emit('update:editing', value);
404
+ }
405
+ },
406
+ /**
407
+ * Computed property for loading state
408
+ */
409
+ isLoading() {
410
+ return this.loading;
411
+ },
412
+ /**
413
+ * Computed property for submitting state
414
+ */
415
+ isSubmitting() {
416
+ return this.submitting;
417
+ },
418
+ /**
419
+ * Check if avatar should be displayed
420
+ */
421
+ showAvatar() {
422
+ return this.avatarSrc || this.avatarInitials || this.avatarName;
423
+ }
424
+ },
425
+ watch: {
426
+ /**
427
+ * Watch for changes in value prop to update local form data
428
+ */
429
+ value: {
430
+ immediate: true,
431
+ deep: true,
432
+ handler(newValue) {
433
+ this.localFormData = { ...newValue };
434
+ }
435
+ },
436
+ /**
437
+ * Watch editing prop changes
438
+ */
439
+ editing: {
440
+ immediate: true,
441
+ handler(newValue) {
442
+ if (newValue) {
443
+ this.localFormData = { ...this.value };
444
+ this.validationErrors = {};
445
+ }
446
+ }
447
+ }
448
+ },
449
+ methods: {
450
+ /**
451
+ * Start editing mode
452
+ */
453
+ startEditing() {
454
+ this.localFormData = { ...this.value };
455
+ this.validationErrors = {};
456
+ this.clearAlert();
457
+ this.isEditing = true;
458
+ this.$emit('edit-start');
459
+ },
460
+ /**
461
+ * Cancel editing and return to read mode
462
+ */
463
+ cancelEditing() {
464
+ this.localFormData = { ...this.value };
465
+ this.validationErrors = {};
466
+ this.isEditing = false;
467
+ this.$emit('edit-cancel');
468
+ },
469
+ /**
470
+ * Update a specific field in the form data
471
+ */
472
+ updateField(fieldName, value) {
473
+ this.localFormData = {
474
+ ...this.localFormData,
475
+ [fieldName]: value
476
+ };
477
+ // Clear validation error for this field when it's updated
478
+ if (this.validationErrors[fieldName]) {
479
+ const { [fieldName]: removed, ...rest } = this.validationErrors;
480
+ this.validationErrors = rest;
481
+ }
482
+ this.$emit('field-change', {
483
+ field: fieldName,
484
+ value,
485
+ formData: this.localFormData
486
+ });
487
+ },
488
+ /**
489
+ * Validate the form data
490
+ * Returns true if valid, false otherwise
491
+ */
492
+ validateForm() {
493
+ // Run custom validation if provided
494
+ if (this.validate) {
495
+ this.validationErrors = this.validate(this.localFormData) || {};
496
+ return Object.keys(this.validationErrors).length === 0;
497
+ }
498
+
499
+ // Default required field validation
500
+ const errors = {};
501
+ for (const field of this.fields) {
502
+ if (field.required && !this.localFormData[field.name]) {
503
+ errors[field.name] = `${field.label} est requis`;
504
+ }
505
+ }
506
+ this.validationErrors = errors;
507
+ return Object.keys(errors).length === 0;
508
+ },
509
+ /**
510
+ * Handle form submission
511
+ */
512
+ handleSubmit() {
513
+ if (!this.validateForm()) {
514
+ return;
515
+ }
516
+
517
+ this.$emit('submit', {
518
+ data: { ...this.localFormData },
519
+ done: this.handleSubmitSuccess,
520
+ fail: this.handleSubmitError
521
+ });
522
+ },
523
+ /**
524
+ * Handle successful submission
525
+ */
526
+ handleSubmitSuccess(message = 'Modifications enregistrées avec succès') {
527
+ this.$emit('input', { ...this.localFormData });
528
+ this.isEditing = false;
529
+ this.showAlert('success', message);
530
+ this.$emit('save-success', { data: this.localFormData, message });
531
+ },
532
+ /**
533
+ * Handle submission error
534
+ */
535
+ handleSubmitError(
536
+ message = "Une erreur est survenue lors de l'enregistrement"
537
+ ) {
538
+ this.showAlert('error', message);
539
+ this.$emit('save-error', { data: this.localFormData, message });
540
+ },
541
+ /**
542
+ * Handle avatar edit button click
543
+ */
544
+ handleAvatarEdit() {
545
+ this.$emit('avatar-edit');
546
+ },
547
+ /**
548
+ * Show alert message
549
+ */
550
+ showAlert(variant, message) {
551
+ this.alertVariant = variant;
552
+ this.alertMessage = message;
553
+ },
554
+ /**
555
+ * Clear alert message
556
+ */
557
+ clearAlert() {
558
+ this.alertMessage = '';
559
+ }
560
+ }
561
+ };
562
+ </script>