sprintify-ui 0.10.64 → 0.10.65
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.
- package/README.md +266 -266
- package/dist/{BaseCkeditor-B4PbYw6a.js → BaseCkeditor-D6D4FPEb.js} +2 -2
- package/dist/sprintify-ui.es.js +3732 -3594
- package/dist/style.css +3 -3
- package/dist/tailwindcss/button.js +260 -260
- package/dist/tailwindcss/index.js +21 -21
- package/dist/tailwindcss/input.js +22 -22
- package/dist/tailwindcss/overlay.js +12 -12
- package/dist/tailwindcss/table.js +91 -91
- package/dist/tailwindcss/theme.js +52 -52
- package/dist/types/components/BaseActionButtons.vue.d.ts +1 -1
- package/dist/types/components/BaseActionItem.vue.d.ts +10 -7
- package/dist/types/components/BaseAddressForm.vue.d.ts +1 -1
- package/dist/types/components/BaseAlert.vue.d.ts +11 -24
- package/dist/types/components/BaseApp.vue.d.ts +12 -14
- package/dist/types/components/BaseAssign.vue.d.ts +1 -1
- package/dist/types/components/BaseAutocomplete.vue.d.ts +455 -665
- package/dist/types/components/BaseAutocompleteDrawer.vue.d.ts +24 -119
- package/dist/types/components/BaseAutocompleteFetch.vue.d.ts +412 -1033
- package/dist/types/components/BaseAvatarGroup.vue.d.ts +1 -1
- package/dist/types/components/BaseBadge.vue.d.ts +11 -23
- package/dist/types/components/BaseBelongsTo.vue.d.ts +411 -998
- package/dist/types/components/BaseBelongsToFetch.vue.d.ts +371 -754
- package/dist/types/components/BaseBoolean.vue.d.ts +1 -1
- package/dist/types/components/BaseBreadcrumbs.vue.d.ts +1 -1
- package/dist/types/components/BaseButton.vue.d.ts +16 -78
- package/dist/types/components/BaseButtonGroup.vue.d.ts +15 -166
- package/dist/types/components/BaseCard.vue.d.ts +12 -26
- package/dist/types/components/BaseCardRow.vue.d.ts +11 -20
- package/dist/types/components/BaseCharacterCounter.vue.d.ts +1 -1
- package/dist/types/components/BaseClipboard.vue.d.ts +13 -45
- package/dist/types/components/BaseCollapse.vue.d.ts +20 -41
- package/dist/types/components/BaseContainer.vue.d.ts +11 -16
- package/dist/types/components/BaseCounter.vue.d.ts +1 -1
- package/dist/types/components/BaseCropper.vue.d.ts +30 -55
- package/dist/types/components/BaseDataIterator.vue.d.ts +42 -43
- package/dist/types/components/BaseDataIteratorSectionBox.vue.d.ts +12 -15
- package/dist/types/components/BaseDataIteratorSectionColumns.vue.d.ts +1 -1
- package/dist/types/components/BaseDataIteratorSectionModal.vue.d.ts +10 -19
- package/dist/types/components/BaseDataTable.vue.d.ts +736 -2088
- package/dist/types/components/BaseDataTableTemplate.vue.d.ts +100 -576
- package/dist/types/components/BaseDatePicker.vue.d.ts +1 -1
- package/dist/types/components/BaseDateSelect.vue.d.ts +1 -1
- package/dist/types/components/BaseDescriptionList.vue.d.ts +12 -12
- package/dist/types/components/BaseDescriptionListItem.vue.d.ts +15 -16
- package/dist/types/components/BaseDialog.vue.d.ts +413 -114
- package/dist/types/components/BaseDisplayRelativeTime.vue.d.ts +13 -55
- package/dist/types/components/BaseDraggable.vue.d.ts +16 -25
- package/dist/types/components/BaseDropdown.vue.d.ts +17 -155
- package/dist/types/components/BaseDropdownAutocomplete.vue.d.ts +16 -131
- package/dist/types/components/BaseField.vue.d.ts +12 -103
- package/dist/types/components/BaseFieldI18n.vue.d.ts +1 -1
- package/dist/types/components/BaseFilePicker.vue.d.ts +16 -35
- package/dist/types/components/BaseFilePickerCrop.vue.d.ts +42 -87
- package/dist/types/components/BaseFileUploader.vue.d.ts +29 -195
- package/dist/types/components/BaseForm.vue.d.ts +20 -161
- package/dist/types/components/BaseGantt.vue.d.ts +409 -1130
- package/dist/types/components/BaseHasMany.vue.d.ts +347 -590
- package/dist/types/components/BaseHasManyFetch.vue.d.ts +251 -602
- package/dist/types/components/BaseHeader.vue.d.ts +1 -1
- package/dist/types/components/BaseIconPicker.vue.d.ts +1 -1
- package/dist/types/components/BaseInputError.vue.d.ts +11 -23
- package/dist/types/components/BaseJsonReaderItem.vue.d.ts +1 -1
- package/dist/types/components/BaseLayoutNotificationItemContent.vue.d.ts +1 -1
- package/dist/types/components/BaseLayoutSidebar.vue.d.ts +16 -124
- package/dist/types/components/BaseLayoutSidebarConfigurable.vue.d.ts +13 -115
- package/dist/types/components/BaseLayoutStacked.vue.d.ts +22 -69
- package/dist/types/components/BaseLayoutStackedConfigurable.vue.d.ts +10 -120
- package/dist/types/components/BaseLazy.vue.d.ts +15 -20
- package/dist/types/components/BaseMediaGallery.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaGalleryItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaLibrary.vue.d.ts +34 -234
- package/dist/types/components/BaseMediaListItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaPicturesItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMenu.vue.d.ts +30 -233
- package/dist/types/components/BaseMenuItem.vue.d.ts +1 -1
- package/dist/types/components/BaseModalCenter.vue.d.ts +12 -88
- package/dist/types/components/BaseModalSide.vue.d.ts +20 -71
- package/dist/types/components/BaseNavbar.vue.d.ts +22 -88
- package/dist/types/components/BaseNavbarItem.vue.d.ts +1 -1
- package/dist/types/components/BaseNavbarItemContent.vue.d.ts +1 -1
- package/dist/types/components/BaseNavbarSideItem.vue.d.ts +1 -1
- package/dist/types/components/BaseNavbarSideItemContent.vue.d.ts +1 -1
- package/dist/types/components/BasePagination.vue.d.ts +1 -1
- package/dist/types/components/BaseRadioGroup.vue.d.ts +13 -113
- package/dist/types/components/BaseReadMore.vue.d.ts +12 -31
- package/dist/types/components/BaseRichText.vue.d.ts +1 -1
- package/dist/types/components/BaseSelect.vue.d.ts +12 -149
- package/dist/types/components/BaseShortcut.vue.d.ts +1 -1
- package/dist/types/components/BaseSideNavigation.vue.d.ts +12 -11
- package/dist/types/components/BaseSideNavigationItem.vue.d.ts +13 -22
- package/dist/types/components/BaseSkeleton.vue.d.ts +3 -3
- package/dist/types/components/BaseStepper.vue.d.ts +1 -1
- package/dist/types/components/BaseSwitch.vue.d.ts +10 -118
- package/dist/types/components/BaseSystemAlert.vue.d.ts +11 -58
- package/dist/types/components/BaseTabItem.vue.d.ts +18 -35
- package/dist/types/components/BaseTable.vue.d.ts +14 -29
- package/dist/types/components/BaseTableBody.vue.d.ts +12 -9
- package/dist/types/components/BaseTableCell.vue.d.ts +15 -40
- package/dist/types/components/BaseTableColumn.vue.d.ts +3 -3
- package/dist/types/components/BaseTableHead.vue.d.ts +12 -14
- package/dist/types/components/BaseTableHeader.vue.d.ts +17 -46
- package/dist/types/components/BaseTableRow.vue.d.ts +13 -40
- package/dist/types/components/BaseTabs.vue.d.ts +15 -21
- package/dist/types/components/BaseTagAutocomplete.vue.d.ts +203 -602
- package/dist/types/components/BaseTagAutocompleteFetch.vue.d.ts +348 -807
- package/dist/types/components/BaseTextareaAutoresize.vue.d.ts +1 -1
- package/dist/types/components/BaseTimeline.vue.d.ts +1 -1
- package/dist/types/components/BaseTimelineItem.vue.d.ts +1 -1
- package/dist/types/components/BaseToast.vue.d.ts +1 -1
- package/dist/types/components/BaseTooltip.vue.d.ts +15 -41
- package/dist/types/components/BaseUniqueCode.vue.d.ts +1 -1
- package/dist/types/stories/PageInputSizes.vue.d.ts +1 -1
- package/dist/types/stories/PageShow.vue.d.ts +1 -1
- package/dist/types/svg/BaseEmptyState.vue.d.ts +1 -1
- package/dist/types/svg/BaseSpinnerSmall.vue.d.ts +1 -1
- package/package.json +135 -135
- package/src/assets/base-cropper.css +61 -61
- package/src/assets/base-date-picker.css +4 -4
- package/src/assets/base-rich-text.css +270 -270
- package/src/assets/base-spinner.css +18 -18
- package/src/assets/base-tabs.css +4 -4
- package/src/assets/base-time-picker.css +9 -9
- package/src/assets/flatpickr.css +247 -247
- package/src/assets/form.css +6 -6
- package/src/assets/google-pac.css +25 -25
- package/src/assets/main.css +56 -56
- package/src/assets/tailwind.css +2 -2
- package/src/changelog.mdx +6 -6
- package/src/components/BaseActionButtons.vue +76 -76
- package/src/components/BaseActionItem.vue +80 -80
- package/src/components/BaseActionItemButton.vue +75 -75
- package/src/components/BaseAddressForm.stories.js +114 -114
- package/src/components/BaseAddressForm.vue +382 -382
- package/src/components/BaseAlert.stories.js +75 -75
- package/src/components/BaseAlert.vue +101 -101
- package/src/components/BaseApp.vue +16 -16
- package/src/components/BaseAppDialogs.vue +126 -126
- package/src/components/BaseAppSnackbars.vue +40 -40
- package/src/components/BaseAssign.mdx +98 -98
- package/src/components/BaseAssign.stories.js +78 -78
- package/src/components/BaseAssign.vue +366 -366
- package/src/components/BaseAutocomplete.stories.js +243 -243
- package/src/components/BaseAutocomplete.vue +603 -603
- package/src/components/BaseAutocompleteDrawer.vue +386 -386
- package/src/components/BaseAutocompleteFetch.stories.js +224 -224
- package/src/components/BaseAutocompleteFetch.vue +314 -314
- package/src/components/BaseAvatar.stories.js +39 -39
- package/src/components/BaseAvatar.vue +164 -164
- package/src/components/BaseAvatarGroup.stories.js +71 -71
- package/src/components/BaseAvatarGroup.vue +148 -148
- package/src/components/BaseBadge.stories.js +130 -130
- package/src/components/BaseBadge.vue +97 -97
- package/src/components/BaseBelongsTo.stories.js +220 -220
- package/src/components/BaseBelongsTo.vue +164 -164
- package/src/components/BaseBelongsToFetch.stories.js +223 -223
- package/src/components/BaseBelongsToFetch.vue +213 -213
- package/src/components/BaseBoolean.stories.js +35 -35
- package/src/components/BaseBoolean.vue +26 -26
- package/src/components/BaseBreadcrumbs.stories.js +50 -50
- package/src/components/BaseBreadcrumbs.vue +109 -109
- package/src/components/BaseButton.stories.js +126 -126
- package/src/components/BaseButton.vue +279 -279
- package/src/components/BaseButtonGroup.stories.js +114 -114
- package/src/components/BaseButtonGroup.vue +193 -193
- package/src/components/BaseCard.stories.js +63 -63
- package/src/components/BaseCard.vue +49 -49
- package/src/components/BaseCardRow.vue +53 -53
- package/src/components/BaseCharacterCounter.stories.js +30 -30
- package/src/components/BaseCharacterCounter.vue +64 -64
- package/src/components/BaseCkeditor.vue +154 -154
- package/src/components/BaseClipboard.stories.js +55 -55
- package/src/components/BaseClipboard.vue +105 -105
- package/src/components/BaseCollapse.stories.js +168 -168
- package/src/components/BaseCollapse.vue +98 -98
- package/src/components/BaseColor.stories.js +66 -66
- package/src/components/BaseColor.vue +155 -155
- package/src/components/BaseContainer.stories.js +34 -34
- package/src/components/BaseContainer.vue +64 -64
- package/src/components/BaseCounter.stories.js +47 -47
- package/src/components/BaseCounter.vue +83 -83
- package/src/components/BaseCropper.stories.js +113 -113
- package/src/components/BaseCropper.vue +390 -390
- package/src/components/BaseCropperModal.stories.js +54 -54
- package/src/components/BaseCropperModal.vue +143 -143
- package/src/components/BaseDataIterator.stories.js +292 -292
- package/src/components/BaseDataIterator.vue +986 -986
- package/src/components/BaseDataIteratorSectionBox.vue +36 -36
- package/src/components/BaseDataIteratorSectionButton.vue +42 -42
- package/src/components/BaseDataIteratorSectionColumns.vue +150 -150
- package/src/components/BaseDataIteratorSectionModal.vue +41 -41
- package/src/components/BaseDataTable.stories.js +393 -393
- package/src/components/BaseDataTable.vue +966 -966
- package/src/components/BaseDataTableRowAction.vue +70 -70
- package/src/components/BaseDataTableTemplate.vue +838 -838
- package/src/components/BaseDatePicker.stories.js +166 -166
- package/src/components/BaseDatePicker.vue +372 -372
- package/src/components/BaseDateSelect.stories.js +68 -68
- package/src/components/BaseDateSelect.vue +222 -222
- package/src/components/BaseDescriptionList.stories.js +35 -35
- package/src/components/BaseDescriptionList.vue +13 -13
- package/src/components/BaseDescriptionListItem.vue +47 -47
- package/src/components/BaseDialog.stories.js +95 -95
- package/src/components/BaseDialog.vue +221 -221
- package/src/components/BaseDisplayRelativeTime.stories.js +47 -47
- package/src/components/BaseDisplayRelativeTime.vue +126 -126
- package/src/components/BaseDraggable.stories.js +34 -34
- package/src/components/BaseDraggable.vue +111 -111
- package/src/components/BaseDropdown.stories.js +164 -164
- package/src/components/BaseDropdown.vue +74 -74
- package/src/components/BaseDropdownAutocomplete.stories.js +208 -208
- package/src/components/BaseDropdownAutocomplete.vue +203 -203
- package/src/components/BaseField.vue +151 -151
- package/src/components/BaseFieldI18n.stories.js +37 -37
- package/src/components/BaseFieldI18n.vue +170 -170
- package/src/components/BaseFilePicker.stories.js +84 -84
- package/src/components/BaseFilePicker.vue +163 -163
- package/src/components/BaseFilePickerCrop.stories.js +135 -135
- package/src/components/BaseFilePickerCrop.vue +130 -130
- package/src/components/BaseFileUploader.stories.js +101 -101
- package/src/components/BaseFileUploader.vue +185 -185
- package/src/components/BaseForm.mdx +87 -87
- package/src/components/BaseForm.stories.js +133 -133
- package/src/components/BaseForm.vue +372 -372
- package/src/components/BaseGantt.stories.js +145 -145
- package/src/components/BaseGantt.vue +384 -384
- package/src/components/BaseHasMany.stories.js +211 -211
- package/src/components/BaseHasMany.vue +135 -135
- package/src/components/BaseHasManyFetch.stories.js +278 -278
- package/src/components/BaseHasManyFetch.vue +222 -222
- package/src/components/BaseHeader.stories.js +137 -137
- package/src/components/BaseHeader.vue +141 -141
- package/src/components/BaseIconPicker.stories.js +22 -22
- package/src/components/BaseIconPicker.vue +225 -225
- package/src/components/BaseInput.stories.js +202 -202
- package/src/components/BaseInput.vue +402 -402
- package/src/components/BaseInputError.vue +39 -39
- package/src/components/BaseInputLabel.stories.js +36 -36
- package/src/components/BaseInputLabel.vue +83 -83
- package/src/components/BaseInputPercent.stories.js +66 -66
- package/src/components/BaseInputPercent.vue +139 -139
- package/src/components/BaseJsonReader.stories.js +120 -120
- package/src/components/BaseJsonReader.vue +51 -51
- package/src/components/BaseJsonReaderItem.vue +119 -119
- package/src/components/BaseLayoutNotificationDropdown.vue +153 -153
- package/src/components/BaseLayoutNotificationItem.vue +53 -53
- package/src/components/BaseLayoutNotificationItemContent.vue +41 -41
- package/src/components/BaseLayoutSidebar.vue +300 -300
- package/src/components/BaseLayoutSidebarConfigurable.stories.js +217 -217
- package/src/components/BaseLayoutSidebarConfigurable.vue +202 -202
- package/src/components/BaseLayoutStacked.vue +78 -78
- package/src/components/BaseLayoutStackedConfigurable.stories.js +181 -181
- package/src/components/BaseLayoutStackedConfigurable.vue +196 -196
- package/src/components/BaseLazy.stories.js +59 -59
- package/src/components/BaseLazy.vue +80 -80
- package/src/components/BaseLoadingCover.stories.js +55 -55
- package/src/components/BaseLoadingCover.vue +101 -101
- package/src/components/BaseMediaGallery.vue +96 -96
- package/src/components/BaseMediaGalleryItem.vue +101 -101
- package/src/components/BaseMediaItem.stories.js +41 -41
- package/src/components/BaseMediaItem.vue +80 -80
- package/src/components/BaseMediaLibrary.stories.js +267 -267
- package/src/components/BaseMediaLibrary.vue +357 -357
- package/src/components/BaseMediaList.vue +67 -67
- package/src/components/BaseMediaListItem.vue +213 -213
- package/src/components/BaseMediaPictures.vue +64 -64
- package/src/components/BaseMediaPicturesItem.vue +100 -100
- package/src/components/BaseMediaPreview.stories.js +72 -72
- package/src/components/BaseMediaPreview.vue +106 -106
- package/src/components/BaseMenu.stories.js +134 -134
- package/src/components/BaseMenu.vue +187 -187
- package/src/components/BaseMenuItem.vue +177 -177
- package/src/components/BaseModalCenter.stories.js +68 -68
- package/src/components/BaseModalCenter.vue +128 -128
- package/src/components/BaseModalSide.stories.js +61 -55
- package/src/components/BaseModalSide.vue +130 -116
- package/src/components/BaseNavbar.stories.js +152 -152
- package/src/components/BaseNavbar.vue +191 -191
- package/src/components/BaseNavbarItem.vue +108 -108
- package/src/components/BaseNavbarItemContent.vue +124 -124
- package/src/components/BaseNavbarSideItem.vue +187 -187
- package/src/components/BaseNavbarSideItemContent.vue +126 -126
- package/src/components/BasePagination.stories.js +35 -35
- package/src/components/BasePagination.vue +266 -266
- package/src/components/BasePanel.stories.js +56 -56
- package/src/components/BasePanel.vue +42 -42
- package/src/components/BasePassword.stories.js +80 -80
- package/src/components/BasePassword.vue +87 -87
- package/src/components/BaseProgressCircle.stories.js +27 -27
- package/src/components/BaseProgressCircle.vue +80 -80
- package/src/components/BaseRadioGroup.stories.js +87 -87
- package/src/components/BaseRadioGroup.vue +124 -124
- package/src/components/BaseReadMore.stories.js +30 -30
- package/src/components/BaseReadMore.vue +73 -73
- package/src/components/BaseRichText.stories.js +90 -90
- package/src/components/BaseRichText.vue +87 -87
- package/src/components/BaseScrollColumn.vue +128 -128
- package/src/components/BaseSelect.stories.js +151 -151
- package/src/components/BaseSelect.vue +241 -241
- package/src/components/BaseShortcut.stories.js +100 -100
- package/src/components/BaseShortcut.vue +114 -114
- package/src/components/BaseSideNavigation.stories.js +85 -85
- package/src/components/BaseSideNavigation.vue +32 -32
- package/src/components/BaseSideNavigationItem.vue +99 -99
- package/src/components/BaseSkeleton.stories.js +36 -36
- package/src/components/BaseSkeleton.vue +40 -40
- package/src/components/BaseStatistic.stories.js +51 -51
- package/src/components/BaseStatistic.vue +98 -98
- package/src/components/BaseStepper.stories.js +94 -94
- package/src/components/BaseStepper.vue +72 -72
- package/src/components/BaseStepperItem.stories.js +65 -65
- package/src/components/BaseStepperItem.vue +149 -149
- package/src/components/BaseSwitch.stories.js +133 -133
- package/src/components/BaseSwitch.vue +226 -226
- package/src/components/BaseSystemAlert.stories.js +63 -63
- package/src/components/BaseSystemAlert.vue +89 -89
- package/src/components/BaseTabItem.vue +192 -192
- package/src/components/BaseTable.stories.js +214 -214
- package/src/components/BaseTable.vue +111 -111
- package/src/components/BaseTableBody.vue +14 -14
- package/src/components/BaseTableCell.vue +204 -204
- package/src/components/BaseTableColumn.vue +140 -140
- package/src/components/BaseTableHead.vue +38 -38
- package/src/components/BaseTableHeader.vue +139 -139
- package/src/components/BaseTableRow.vue +197 -197
- package/src/components/BaseTabs.stories.js +165 -165
- package/src/components/BaseTabs.vue +203 -203
- package/src/components/BaseTagAutocomplete.stories.js +271 -271
- package/src/components/BaseTagAutocomplete.vue +565 -565
- package/src/components/BaseTagAutocompleteFetch.stories.js +211 -211
- package/src/components/BaseTagAutocompleteFetch.vue +237 -237
- package/src/components/BaseTextarea.stories.js +81 -81
- package/src/components/BaseTextarea.vue +138 -138
- package/src/components/BaseTextareaAutoresize.stories.js +125 -125
- package/src/components/BaseTextareaAutoresize.vue +187 -187
- package/src/components/BaseTimePicker.stories.js +68 -68
- package/src/components/BaseTimePicker.vue +379 -379
- package/src/components/BaseTimeline.stories.js +55 -55
- package/src/components/BaseTimeline.vue +38 -38
- package/src/components/BaseTimelineItem.stories.js +77 -77
- package/src/components/BaseTimelineItem.vue +90 -90
- package/src/components/BaseToast.stories.js +50 -50
- package/src/components/BaseToast.vue +43 -43
- package/src/components/BaseTooltip.stories.js +65 -65
- package/src/components/BaseTooltip.vue +93 -93
- package/src/components/BaseUniqueCode.stories.js +36 -36
- package/src/components/BaseUniqueCode.vue +183 -183
- package/src/components/SlotComponent.ts +37 -37
- package/src/components/index.ts +222 -222
- package/src/composables/breakpoints.ts +94 -94
- package/src/composables/clickOutside.ts +80 -80
- package/src/composables/field.ts +156 -156
- package/src/composables/hasOptions.ts +86 -86
- package/src/composables/inputSize.ts +35 -35
- package/src/composables/isLastColumn.ts +30 -30
- package/src/composables/mediaQuery.ts +42 -42
- package/src/composables/modal.ts +73 -73
- package/src/composables/paginatedData.ts +76 -76
- package/src/composables/tooltip.ts +84 -84
- package/src/constants/MyConstants.ts +1 -1
- package/src/constants/index.ts +5 -5
- package/src/env.d.ts +15 -15
- package/src/i18n/index.ts +60 -60
- package/src/index.ts +138 -138
- package/src/lang/en.json +101 -101
- package/src/lang/fr.json +101 -101
- package/src/services/gantt/format.ts +133 -133
- package/src/services/gantt/timescale.ts +250 -250
- package/src/services/gantt/types.ts +81 -81
- package/src/services/table/classes.ts +37 -37
- package/src/services/table/customKeyActions.ts +2 -2
- package/src/services/table/types.ts +27 -27
- package/src/stores/dialogs.ts +48 -48
- package/src/stores/i18n.ts +14 -14
- package/src/stores/snackbars.ts +47 -47
- package/src/stores/systemAlerts.ts +32 -32
- package/src/stories/InputSizes.stories.js +21 -21
- package/src/stories/List.stories.js +136 -136
- package/src/stories/PageInputSizes.vue +228 -228
- package/src/stories/PageShow.vue +423 -423
- package/src/stories/Show.stories.js +21 -21
- package/src/svg/BaseEmptyState.vue +38 -38
- package/src/svg/BaseSpinnerLarge.vue +40 -40
- package/src/svg/BaseSpinnerSmall.vue +13 -13
- package/src/types/Color.ts +9 -9
- package/src/types/Country.ts +4 -4
- package/src/types/ImagePickerResult.ts +5 -5
- package/src/types/Media.ts +10 -10
- package/src/types/Notification.ts +11 -11
- package/src/types/Region.ts +5 -5
- package/src/types/Status.ts +5 -5
- package/src/types/StepperItem.ts +8 -8
- package/src/types/ToolbarOption.ts +1 -1
- package/src/types/UploadedFile.ts +11 -11
- package/src/types/User.ts +7 -7
- package/src/types/index.ts +302 -302
- package/src/utils/blob.ts +30 -30
- package/src/utils/colors.ts +239 -239
- package/src/utils/cropper/avatar.ts +33 -33
- package/src/utils/cropper/cover.ts +41 -41
- package/src/utils/cropper/presetInterface.ts +16 -16
- package/src/utils/cropper/presets.ts +7 -7
- package/src/utils/deepIncludes.ts +76 -76
- package/src/utils/fileSizeFormat.ts +15 -15
- package/src/utils/fileValidations.ts +26 -26
- package/src/utils/getApiData.ts +11 -11
- package/src/utils/index.ts +18 -18
- package/src/utils/resizeImageFromURI.ts +118 -118
- package/src/utils/scrollPreventer.ts +11 -11
- package/src/utils/sizeBehaviors.ts +3 -3
- package/src/utils/sizes.ts +38 -38
- package/src/utils/slots.ts +12 -12
- package/src/utils/storage.ts +36 -36
- package/src/utils/toHumanList.ts +20 -20
- package/src/utils/uuid.ts +7 -7
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="relative">
|
|
3
|
-
<component
|
|
4
|
-
:is="componentInternal"
|
|
5
|
-
v-bind="pickerProps"
|
|
6
|
-
@select="onFileSelect"
|
|
7
|
-
>
|
|
8
|
-
<template #default="slotProps">
|
|
9
|
-
<slot
|
|
10
|
-
name="default"
|
|
11
|
-
:uploading="uploading"
|
|
12
|
-
:loading="loading"
|
|
13
|
-
v-bind="slotProps"
|
|
14
|
-
/>
|
|
15
|
-
<slot
|
|
16
|
-
name="loading"
|
|
17
|
-
:uploading="uploading"
|
|
18
|
-
:loading="loading"
|
|
19
|
-
v-bind="slotProps"
|
|
20
|
-
>
|
|
21
|
-
<BaseLoadingCover
|
|
22
|
-
:delay="0"
|
|
23
|
-
tw-icon="w-6 h-6 text-primary-600"
|
|
24
|
-
:model-value="loading || uploading || slotProps.selecting"
|
|
25
|
-
/>
|
|
26
|
-
</slot>
|
|
27
|
-
</template>
|
|
28
|
-
</component>
|
|
29
|
-
</div>
|
|
30
|
-
</template>
|
|
31
|
-
|
|
32
|
-
<script lang="ts" setup>
|
|
33
|
-
import { config } from '@/index';
|
|
34
|
-
import { UploadedFile } from '@/types/UploadedFile';
|
|
35
|
-
import { useSnackbarsStore } from '@/stores/snackbars';
|
|
36
|
-
import BaseLoadingCover from '@/components/BaseLoadingCover.vue';
|
|
37
|
-
import { BaseCropperConfig } from '@/types';
|
|
38
|
-
import BaseFilePicker from './BaseFilePicker.vue';
|
|
39
|
-
import BaseFilePickerCrop from './BaseFilePickerCrop.vue';
|
|
40
|
-
import { t } from '@/i18n';
|
|
41
|
-
|
|
42
|
-
const http = config.http;
|
|
43
|
-
const snackbarsStore = useSnackbarsStore();
|
|
44
|
-
|
|
45
|
-
const props = withDefaults(
|
|
46
|
-
defineProps<{
|
|
47
|
-
component?: 'BaseFilePicker' | 'BaseFilePickerCrop';
|
|
48
|
-
url?: string;
|
|
49
|
-
disabled?: boolean;
|
|
50
|
-
loading?: boolean;
|
|
51
|
-
beforeUpload?: () => boolean;
|
|
52
|
-
twButton?: string;
|
|
53
|
-
maxSize?: number;
|
|
54
|
-
accept?: string;
|
|
55
|
-
acceptedExtensions?: string[];
|
|
56
|
-
cropper?: BaseCropperConfig | Record<string, any> | boolean | null;
|
|
57
|
-
multiple?: boolean;
|
|
58
|
-
}>(),
|
|
59
|
-
{
|
|
60
|
-
component: 'BaseFilePicker',
|
|
61
|
-
url: undefined,
|
|
62
|
-
disabled: false,
|
|
63
|
-
loading: false,
|
|
64
|
-
beforeUpload: (): boolean => {
|
|
65
|
-
return true;
|
|
66
|
-
},
|
|
67
|
-
twButton: '',
|
|
68
|
-
maxSize: undefined,
|
|
69
|
-
accept: undefined,
|
|
70
|
-
acceptedExtensions: undefined,
|
|
71
|
-
cropper: true,
|
|
72
|
-
multiple: false
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const emit = defineEmits([
|
|
77
|
-
'start',
|
|
78
|
-
'success',
|
|
79
|
-
'fail',
|
|
80
|
-
'end',
|
|
81
|
-
]);
|
|
82
|
-
|
|
83
|
-
const componentInternal = computed(() => {
|
|
84
|
-
if (props.component == 'BaseFilePickerCrop') {
|
|
85
|
-
return BaseFilePickerCrop;
|
|
86
|
-
}
|
|
87
|
-
return BaseFilePicker;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const pickerProps = computed(() => {
|
|
91
|
-
const pickerProps = {
|
|
92
|
-
disabled: props.disabled || props.loading || uploading.value,
|
|
93
|
-
twButton: props.twButton,
|
|
94
|
-
maxSize: props.maxSize,
|
|
95
|
-
accept: props.component == 'BaseFilePickerCrop' ? undefined : props.accept,
|
|
96
|
-
acceptedExtensions: props.acceptedExtensions,
|
|
97
|
-
multiple: props.component == 'BaseFilePickerCrop' ? undefined : props.multiple,
|
|
98
|
-
} as Record<string, any>;
|
|
99
|
-
|
|
100
|
-
if (props.component == 'BaseFilePickerCrop') {
|
|
101
|
-
pickerProps.cropper = props.cropper;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return pickerProps;
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const uploading = ref(false);
|
|
108
|
-
|
|
109
|
-
async function onFileSelect(files: File | File[]) {
|
|
110
|
-
if (uploading.value) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
uploading.value = true;
|
|
115
|
-
|
|
116
|
-
if (!(await props.beforeUpload())) {
|
|
117
|
-
uploading.value = false;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
emit('start');
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let payloads = [] as UploadedFile[];
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
|
|
128
|
-
if (Array.isArray(files)) {
|
|
129
|
-
payloads = await Promise.all(files.map(f => processFileUpload(f)));
|
|
130
|
-
} else {
|
|
131
|
-
const payload = await processFileUpload(files);
|
|
132
|
-
payloads.push(payload);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
emit('success', payloads)
|
|
136
|
-
|
|
137
|
-
} catch (e: unknown) {
|
|
138
|
-
console.error(e);
|
|
139
|
-
emit('fail');
|
|
140
|
-
snackbarsStore.push({
|
|
141
|
-
color: 'danger',
|
|
142
|
-
title: t('sui.error'),
|
|
143
|
-
text: t('sui.upload_failed'),
|
|
144
|
-
});
|
|
145
|
-
} finally {
|
|
146
|
-
emit('end');
|
|
147
|
-
uploading.value = false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function processFileUpload(file: File): Promise<UploadedFile> {
|
|
152
|
-
|
|
153
|
-
const formData = new FormData();
|
|
154
|
-
|
|
155
|
-
formData.append('file', file);
|
|
156
|
-
|
|
157
|
-
const response = await http.post(props.url ?? config.upload_url, formData);
|
|
158
|
-
|
|
159
|
-
const payload = response.data as UploadedFile;
|
|
160
|
-
payload.original_file = file;
|
|
161
|
-
|
|
162
|
-
// Read file if image, add add data_url to payload
|
|
163
|
-
|
|
164
|
-
return new Promise(resolve => {
|
|
165
|
-
|
|
166
|
-
const reader = new FileReader();
|
|
167
|
-
|
|
168
|
-
reader.onload = (e: any) => {
|
|
169
|
-
payload.data_url = e.target.result;
|
|
170
|
-
resolve(payload);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
reader.onerror = () => {
|
|
174
|
-
resolve(payload);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
if (payload.mime_type.includes('image')) {
|
|
178
|
-
reader.readAsDataURL(file);
|
|
179
|
-
} else {
|
|
180
|
-
resolve(payload);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative">
|
|
3
|
+
<component
|
|
4
|
+
:is="componentInternal"
|
|
5
|
+
v-bind="pickerProps"
|
|
6
|
+
@select="onFileSelect"
|
|
7
|
+
>
|
|
8
|
+
<template #default="slotProps">
|
|
9
|
+
<slot
|
|
10
|
+
name="default"
|
|
11
|
+
:uploading="uploading"
|
|
12
|
+
:loading="loading"
|
|
13
|
+
v-bind="slotProps"
|
|
14
|
+
/>
|
|
15
|
+
<slot
|
|
16
|
+
name="loading"
|
|
17
|
+
:uploading="uploading"
|
|
18
|
+
:loading="loading"
|
|
19
|
+
v-bind="slotProps"
|
|
20
|
+
>
|
|
21
|
+
<BaseLoadingCover
|
|
22
|
+
:delay="0"
|
|
23
|
+
tw-icon="w-6 h-6 text-primary-600"
|
|
24
|
+
:model-value="loading || uploading || slotProps.selecting"
|
|
25
|
+
/>
|
|
26
|
+
</slot>
|
|
27
|
+
</template>
|
|
28
|
+
</component>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script lang="ts" setup>
|
|
33
|
+
import { config } from '@/index';
|
|
34
|
+
import { UploadedFile } from '@/types/UploadedFile';
|
|
35
|
+
import { useSnackbarsStore } from '@/stores/snackbars';
|
|
36
|
+
import BaseLoadingCover from '@/components/BaseLoadingCover.vue';
|
|
37
|
+
import { BaseCropperConfig } from '@/types';
|
|
38
|
+
import BaseFilePicker from './BaseFilePicker.vue';
|
|
39
|
+
import BaseFilePickerCrop from './BaseFilePickerCrop.vue';
|
|
40
|
+
import { t } from '@/i18n';
|
|
41
|
+
|
|
42
|
+
const http = config.http;
|
|
43
|
+
const snackbarsStore = useSnackbarsStore();
|
|
44
|
+
|
|
45
|
+
const props = withDefaults(
|
|
46
|
+
defineProps<{
|
|
47
|
+
component?: 'BaseFilePicker' | 'BaseFilePickerCrop';
|
|
48
|
+
url?: string;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
loading?: boolean;
|
|
51
|
+
beforeUpload?: () => boolean;
|
|
52
|
+
twButton?: string;
|
|
53
|
+
maxSize?: number;
|
|
54
|
+
accept?: string;
|
|
55
|
+
acceptedExtensions?: string[];
|
|
56
|
+
cropper?: BaseCropperConfig | Record<string, any> | boolean | null;
|
|
57
|
+
multiple?: boolean;
|
|
58
|
+
}>(),
|
|
59
|
+
{
|
|
60
|
+
component: 'BaseFilePicker',
|
|
61
|
+
url: undefined,
|
|
62
|
+
disabled: false,
|
|
63
|
+
loading: false,
|
|
64
|
+
beforeUpload: (): boolean => {
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
twButton: '',
|
|
68
|
+
maxSize: undefined,
|
|
69
|
+
accept: undefined,
|
|
70
|
+
acceptedExtensions: undefined,
|
|
71
|
+
cropper: true,
|
|
72
|
+
multiple: false
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const emit = defineEmits([
|
|
77
|
+
'start',
|
|
78
|
+
'success',
|
|
79
|
+
'fail',
|
|
80
|
+
'end',
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const componentInternal = computed(() => {
|
|
84
|
+
if (props.component == 'BaseFilePickerCrop') {
|
|
85
|
+
return BaseFilePickerCrop;
|
|
86
|
+
}
|
|
87
|
+
return BaseFilePicker;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const pickerProps = computed(() => {
|
|
91
|
+
const pickerProps = {
|
|
92
|
+
disabled: props.disabled || props.loading || uploading.value,
|
|
93
|
+
twButton: props.twButton,
|
|
94
|
+
maxSize: props.maxSize,
|
|
95
|
+
accept: props.component == 'BaseFilePickerCrop' ? undefined : props.accept,
|
|
96
|
+
acceptedExtensions: props.acceptedExtensions,
|
|
97
|
+
multiple: props.component == 'BaseFilePickerCrop' ? undefined : props.multiple,
|
|
98
|
+
} as Record<string, any>;
|
|
99
|
+
|
|
100
|
+
if (props.component == 'BaseFilePickerCrop') {
|
|
101
|
+
pickerProps.cropper = props.cropper;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return pickerProps;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const uploading = ref(false);
|
|
108
|
+
|
|
109
|
+
async function onFileSelect(files: File | File[]) {
|
|
110
|
+
if (uploading.value) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
uploading.value = true;
|
|
115
|
+
|
|
116
|
+
if (!(await props.beforeUpload())) {
|
|
117
|
+
uploading.value = false;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
emit('start');
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
let payloads = [] as UploadedFile[];
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
|
|
128
|
+
if (Array.isArray(files)) {
|
|
129
|
+
payloads = await Promise.all(files.map(f => processFileUpload(f)));
|
|
130
|
+
} else {
|
|
131
|
+
const payload = await processFileUpload(files);
|
|
132
|
+
payloads.push(payload);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
emit('success', payloads)
|
|
136
|
+
|
|
137
|
+
} catch (e: unknown) {
|
|
138
|
+
console.error(e);
|
|
139
|
+
emit('fail');
|
|
140
|
+
snackbarsStore.push({
|
|
141
|
+
color: 'danger',
|
|
142
|
+
title: t('sui.error'),
|
|
143
|
+
text: t('sui.upload_failed'),
|
|
144
|
+
});
|
|
145
|
+
} finally {
|
|
146
|
+
emit('end');
|
|
147
|
+
uploading.value = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function processFileUpload(file: File): Promise<UploadedFile> {
|
|
152
|
+
|
|
153
|
+
const formData = new FormData();
|
|
154
|
+
|
|
155
|
+
formData.append('file', file);
|
|
156
|
+
|
|
157
|
+
const response = await http.post(props.url ?? config.upload_url, formData);
|
|
158
|
+
|
|
159
|
+
const payload = response.data as UploadedFile;
|
|
160
|
+
payload.original_file = file;
|
|
161
|
+
|
|
162
|
+
// Read file if image, add add data_url to payload
|
|
163
|
+
|
|
164
|
+
return new Promise(resolve => {
|
|
165
|
+
|
|
166
|
+
const reader = new FileReader();
|
|
167
|
+
|
|
168
|
+
reader.onload = (e: any) => {
|
|
169
|
+
payload.data_url = e.target.result;
|
|
170
|
+
resolve(payload);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
reader.onerror = () => {
|
|
174
|
+
resolve(payload);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (payload.mime_type.includes('image')) {
|
|
178
|
+
reader.readAsDataURL(file);
|
|
179
|
+
} else {
|
|
180
|
+
resolve(payload);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
</script>
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import { Canvas, Meta, Primary, Controls, Story } from '@storybook/blocks';
|
|
2
|
-
|
|
3
|
-
import * as BaseFormStories from './BaseForm.stories';
|
|
4
|
-
|
|
5
|
-
<Meta of={BaseFormStories} />
|
|
6
|
-
|
|
7
|
-
# BaseForm
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
The **BaseForm** Vue component is a versatile form wrapper designed to handle common tasks such as sending HTTP requests, formatting request data, displaying loading states, showing notifications, and managing form errors. By default, the payload is sent as JSON, but you can switch it to `formData` if needed.
|
|
12
|
-
|
|
13
|
-
It also handles errors gracefully—if you're using **BaseFields** (or components that wrap them) to display inputs, any validation errors received from the server can be shown directly under those fields. You can additionally show a global success or error notification for a better user experience. All of this can be toggled as you see fit.
|
|
14
|
-
|
|
15
|
-
<Primary />
|
|
16
|
-
|
|
17
|
-
## Component Responsibilities
|
|
18
|
-
|
|
19
|
-
- **Sending Requests:** The component can be bound to an `axiosInstance`, which it uses to send POST, PUT, PATCH, or any other method requests to a specified `url`.
|
|
20
|
-
- **Formatting JSON Payload:** By default, the form data is sent as JSON, but you can switch to `formData` by adjusting the `format` prop.
|
|
21
|
-
- **Loading State:** A loading spinner can be displayed while the form is submitting. This spinner can have custom Tailwind CSS classes via the `twLoadingMask` prop, and it can be toggled on/off using `showLoadingMask`.
|
|
22
|
-
- **Error and Success Handling:**
|
|
23
|
-
- The component can display error messages next to fields (if those fields are wrapped in a **BaseField** and `name` prop is set and equivalent to the server response).
|
|
24
|
-
- It can also show a global success or error notification, which is controlled by `showNotificationOnSuccess` and `showNotificationOnError`.
|
|
25
|
-
- **Lifecycle Hooks:** The form provides hooks for `beforeSubmit`, `successHandler`, and `errorHandler` that allow you to intercept the submission lifecycle at various points.
|
|
26
|
-
|
|
27
|
-
## Default Slot
|
|
28
|
-
|
|
29
|
-
The default slot is where you place your form elements and the submit buttons. For example:
|
|
30
|
-
|
|
31
|
-
```html
|
|
32
|
-
<BaseForm url="/api/save" :data="formData" method="post">
|
|
33
|
-
<BaseField label="Name" name="name">
|
|
34
|
-
<BaseInput v-model="formData.name" />
|
|
35
|
-
</BaseField>
|
|
36
|
-
<BaseField label="Email" name="email">
|
|
37
|
-
<BaseInput v-model="formData.email" />
|
|
38
|
-
</BaseField>
|
|
39
|
-
|
|
40
|
-
<button type="submit">Submit</button>
|
|
41
|
-
</BaseForm>
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
- All standard HTML and Vue event handling still applies.
|
|
45
|
-
- If you’re using custom input components, make sure they handle `v-model` correctly so that the internal `data` is properly updated.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
## Autosave
|
|
49
|
-
|
|
50
|
-
The **autosave** feature is a powerful enhancement that automatically saves the form whenever any field changes. It waits for a brief interval—by default, 400ms—to collect and debounce multiple changes, then sends a request.
|
|
51
|
-
|
|
52
|
-
### How it Works
|
|
53
|
-
1. **Change Detection:** Whenever an input updates the `data` object, the component sets a timer for 400ms.
|
|
54
|
-
2. **Debouncing:** If another change is detected before 400ms has passed, it resets the timer.
|
|
55
|
-
3. **Auto Submission:** Once 400ms have elapsed with no further changes, the form is automatically submitted to the given `url` using the specified `method` and `format`.
|
|
56
|
-
4. **Notifications and Hooks:** The usual loading state, success notifications, and error handling still apply. The `beforeSubmit`, `successHandler`, and `errorHandler` hooks are also triggered for these autosave submissions.
|
|
57
|
-
|
|
58
|
-
### Why Use Autosave?
|
|
59
|
-
- **Reduced Friction:** Users don’t need to remember to click “Save” or “Submit.”
|
|
60
|
-
- **Immediate Updates:** Ideal for scenarios like live collaboration or frequent data entry.
|
|
61
|
-
- **Fewer Data Loss Risks:** Changes are saved quickly, so fewer chances of losing progress.
|
|
62
|
-
- **Inline edit in tables:** Great for inline editing in tables or other scenarios where a user might make many small changes.
|
|
63
|
-
|
|
64
|
-
### Example
|
|
65
|
-
|
|
66
|
-
In the example below, the form will automatically save after a 400ms delay whenever a field changes. The server also returns a 422 status code with validation errors, highlighting the field with the error.
|
|
67
|
-
|
|
68
|
-
<Canvas of={BaseFormStories.Autosave} />
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Lifecycle Hooks
|
|
72
|
-
|
|
73
|
-
**beforeSubmit(formData)**
|
|
74
|
-
- Called right before the request is sent.
|
|
75
|
-
- A good place to do last-minute transformations on `formData` or to trigger validations.
|
|
76
|
-
|
|
77
|
-
**successHandler(response)**
|
|
78
|
-
- Invoked when the request is successful (2xx status).
|
|
79
|
-
- Use it to update your UI further or show additional success messages.
|
|
80
|
-
|
|
81
|
-
**errorHandler(error)**
|
|
82
|
-
- Runs when the request fails (4xx, 5xx, or network error).
|
|
83
|
-
- Useful for custom error logging or handling specialized error formats.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## API
|
|
87
|
-
|
|
1
|
+
import { Canvas, Meta, Primary, Controls, Story } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
import * as BaseFormStories from './BaseForm.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={BaseFormStories} />
|
|
6
|
+
|
|
7
|
+
# BaseForm
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The **BaseForm** Vue component is a versatile form wrapper designed to handle common tasks such as sending HTTP requests, formatting request data, displaying loading states, showing notifications, and managing form errors. By default, the payload is sent as JSON, but you can switch it to `formData` if needed.
|
|
12
|
+
|
|
13
|
+
It also handles errors gracefully—if you're using **BaseFields** (or components that wrap them) to display inputs, any validation errors received from the server can be shown directly under those fields. You can additionally show a global success or error notification for a better user experience. All of this can be toggled as you see fit.
|
|
14
|
+
|
|
15
|
+
<Primary />
|
|
16
|
+
|
|
17
|
+
## Component Responsibilities
|
|
18
|
+
|
|
19
|
+
- **Sending Requests:** The component can be bound to an `axiosInstance`, which it uses to send POST, PUT, PATCH, or any other method requests to a specified `url`.
|
|
20
|
+
- **Formatting JSON Payload:** By default, the form data is sent as JSON, but you can switch to `formData` by adjusting the `format` prop.
|
|
21
|
+
- **Loading State:** A loading spinner can be displayed while the form is submitting. This spinner can have custom Tailwind CSS classes via the `twLoadingMask` prop, and it can be toggled on/off using `showLoadingMask`.
|
|
22
|
+
- **Error and Success Handling:**
|
|
23
|
+
- The component can display error messages next to fields (if those fields are wrapped in a **BaseField** and `name` prop is set and equivalent to the server response).
|
|
24
|
+
- It can also show a global success or error notification, which is controlled by `showNotificationOnSuccess` and `showNotificationOnError`.
|
|
25
|
+
- **Lifecycle Hooks:** The form provides hooks for `beforeSubmit`, `successHandler`, and `errorHandler` that allow you to intercept the submission lifecycle at various points.
|
|
26
|
+
|
|
27
|
+
## Default Slot
|
|
28
|
+
|
|
29
|
+
The default slot is where you place your form elements and the submit buttons. For example:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<BaseForm url="/api/save" :data="formData" method="post">
|
|
33
|
+
<BaseField label="Name" name="name">
|
|
34
|
+
<BaseInput v-model="formData.name" />
|
|
35
|
+
</BaseField>
|
|
36
|
+
<BaseField label="Email" name="email">
|
|
37
|
+
<BaseInput v-model="formData.email" />
|
|
38
|
+
</BaseField>
|
|
39
|
+
|
|
40
|
+
<button type="submit">Submit</button>
|
|
41
|
+
</BaseForm>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- All standard HTML and Vue event handling still applies.
|
|
45
|
+
- If you’re using custom input components, make sure they handle `v-model` correctly so that the internal `data` is properly updated.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Autosave
|
|
49
|
+
|
|
50
|
+
The **autosave** feature is a powerful enhancement that automatically saves the form whenever any field changes. It waits for a brief interval—by default, 400ms—to collect and debounce multiple changes, then sends a request.
|
|
51
|
+
|
|
52
|
+
### How it Works
|
|
53
|
+
1. **Change Detection:** Whenever an input updates the `data` object, the component sets a timer for 400ms.
|
|
54
|
+
2. **Debouncing:** If another change is detected before 400ms has passed, it resets the timer.
|
|
55
|
+
3. **Auto Submission:** Once 400ms have elapsed with no further changes, the form is automatically submitted to the given `url` using the specified `method` and `format`.
|
|
56
|
+
4. **Notifications and Hooks:** The usual loading state, success notifications, and error handling still apply. The `beforeSubmit`, `successHandler`, and `errorHandler` hooks are also triggered for these autosave submissions.
|
|
57
|
+
|
|
58
|
+
### Why Use Autosave?
|
|
59
|
+
- **Reduced Friction:** Users don’t need to remember to click “Save” or “Submit.”
|
|
60
|
+
- **Immediate Updates:** Ideal for scenarios like live collaboration or frequent data entry.
|
|
61
|
+
- **Fewer Data Loss Risks:** Changes are saved quickly, so fewer chances of losing progress.
|
|
62
|
+
- **Inline edit in tables:** Great for inline editing in tables or other scenarios where a user might make many small changes.
|
|
63
|
+
|
|
64
|
+
### Example
|
|
65
|
+
|
|
66
|
+
In the example below, the form will automatically save after a 400ms delay whenever a field changes. The server also returns a 422 status code with validation errors, highlighting the field with the error.
|
|
67
|
+
|
|
68
|
+
<Canvas of={BaseFormStories.Autosave} />
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Lifecycle Hooks
|
|
72
|
+
|
|
73
|
+
**beforeSubmit(formData)**
|
|
74
|
+
- Called right before the request is sent.
|
|
75
|
+
- A good place to do last-minute transformations on `formData` or to trigger validations.
|
|
76
|
+
|
|
77
|
+
**successHandler(response)**
|
|
78
|
+
- Invoked when the request is successful (2xx status).
|
|
79
|
+
- Use it to update your UI further or show additional success messages.
|
|
80
|
+
|
|
81
|
+
**errorHandler(error)**
|
|
82
|
+
- Runs when the request fails (4xx, 5xx, or network error).
|
|
83
|
+
- Useful for custom error logging or handling specialized error formats.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## API
|
|
87
|
+
|
|
88
88
|
<Controls />
|