@imaginario27/air-ui-ds 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/assets/css/defaults.css +55 -0
  2. package/assets/css/main.css +238 -0
  3. package/assets/css/theme/colors.css +106 -0
  4. package/assets/css/theme/primitives.css +105 -0
  5. package/assets/css/theme/ui-theme.css +454 -0
  6. package/assets/images/placeholders/missing-image-placeholder.png +0 -0
  7. package/components/accordions/Accordion.vue +31 -0
  8. package/components/accordions/AccordionGroup.vue +78 -0
  9. package/components/accordions/AccordionItem.vue +39 -0
  10. package/components/action-panels/ActionPanel.vue +49 -0
  11. package/components/alerts/Alert.vue +159 -0
  12. package/components/avatars/Avatar.vue +152 -0
  13. package/components/avatars/AvatarStack.vue +97 -0
  14. package/components/avatars/AvatarStackCounter.vue +74 -0
  15. package/components/badges/Badge.vue +221 -0
  16. package/components/badges/BadgeStack.vue +110 -0
  17. package/components/badges/IconBadge.vue +57 -0
  18. package/components/badges/IconTextBadge.vue +50 -0
  19. package/components/breadcrumbs/Breadcrumbs.vue +54 -0
  20. package/components/buttons/ActionButton.vue +395 -0
  21. package/components/buttons/ActionIconButton.vue +283 -0
  22. package/components/buttons/AlertButton.vue +125 -0
  23. package/components/buttons/AlertIconButton.vue +105 -0
  24. package/components/buttons/PaginationButton.vue +45 -0
  25. package/components/buttons/options/OptionButton.vue +61 -0
  26. package/components/buttons/options/OptionButtonGroup.vue +155 -0
  27. package/components/buttons/options/OptionButtonSlider.vue +154 -0
  28. package/components/buttons/toggle/ToggleButton.vue +142 -0
  29. package/components/buttons/toggle/ToggleButtonGroup.vue +73 -0
  30. package/components/cards/Card.vue +33 -0
  31. package/components/cards/CardActions.vue +5 -0
  32. package/components/cards/CardBody.vue +5 -0
  33. package/components/cards/CardFooter.vue +20 -0
  34. package/components/cards/CardHeader.vue +5 -0
  35. package/components/cards/CardTitle.vue +13 -0
  36. package/components/cards/specific/ContactDetailsCard.vue +47 -0
  37. package/components/cards/specific/FeatureCard.vue +59 -0
  38. package/components/cards/specific/HelpTopicCard.vue +62 -0
  39. package/components/cards/specific/MetricCard.vue +42 -0
  40. package/components/cards/specific/TestimonialCard.vue +57 -0
  41. package/components/cards/specific/subscription/CurrentActiveSubscriptionCard.vue +105 -0
  42. package/components/cards/specific/subscription/SubscriptionPlanCard.vue +178 -0
  43. package/components/cards/specific/subscription/UniqueSubscriptionPlanCard.vue +106 -0
  44. package/components/collapsibles/Collapsible.vue +33 -0
  45. package/components/content/ContentItem.vue +144 -0
  46. package/components/content/ContentItemImage.vue +125 -0
  47. package/components/dividers/Divider.vue +35 -0
  48. package/components/dividers/TextLineDivider.vue +58 -0
  49. package/components/dropdowns/DropdownMenu.vue +207 -0
  50. package/components/dropdowns/DropdownMenuActions.vue +11 -0
  51. package/components/dropdowns/DropdownMenuItem.vue +240 -0
  52. package/components/dropdowns/DropdownSelect.vue +469 -0
  53. package/components/dropdowns/DropdownSelectItem.vue +182 -0
  54. package/components/empty-states/EmptyState.vue +170 -0
  55. package/components/features/Feature.vue +77 -0
  56. package/components/forms/DataDetails.vue +7 -0
  57. package/components/forms/DataDetailsActions.vue +23 -0
  58. package/components/forms/DataDetailsFieldGroup.vue +35 -0
  59. package/components/forms/DataDetailsRow.vue +22 -0
  60. package/components/forms/Form.vue +25 -0
  61. package/components/forms/FormActions.vue +23 -0
  62. package/components/forms/FormFieldGroup.vue +35 -0
  63. package/components/forms/FormRow.vue +22 -0
  64. package/components/forms/fields/ButtonField.vue +119 -0
  65. package/components/forms/fields/CheckboxField.vue +205 -0
  66. package/components/forms/fields/DataField.vue +99 -0
  67. package/components/forms/fields/FileUploadField.vue +326 -0
  68. package/components/forms/fields/InputField.vue +371 -0
  69. package/components/forms/fields/OptionButtonsGroupField.vue +120 -0
  70. package/components/forms/fields/RepeaterField.vue +109 -0
  71. package/components/forms/fields/SearchField.vue +184 -0
  72. package/components/forms/fields/SelectField.vue +233 -0
  73. package/components/forms/fields/SliderField.vue +759 -0
  74. package/components/forms/fields/SwitchField.vue +257 -0
  75. package/components/forms/fields/TextareaField.vue +205 -0
  76. package/components/forms/fields/ToggleButtonsGroupField.vue +65 -0
  77. package/components/forms/fields/radio/RadioButtonField.vue +238 -0
  78. package/components/forms/fields/radio/RadioField.vue +157 -0
  79. package/components/forms/fields/radio/RadioGroupField.vue +156 -0
  80. package/components/icons/ContainedIcon.vue +130 -0
  81. package/components/images/QRCode.vue +124 -0
  82. package/components/layouts/ContainerWrapper.vue +13 -0
  83. package/components/layouts/ContentBody.vue +30 -0
  84. package/components/layouts/Grid.vue +25 -0
  85. package/components/layouts/Heading.vue +159 -0
  86. package/components/layouts/MainContent.vue +26 -0
  87. package/components/layouts/MaxWidthContainer.vue +15 -0
  88. package/components/layouts/Overtitle.vue +25 -0
  89. package/components/layouts/headers/CompactHeader.vue +181 -0
  90. package/components/layouts/headers/PageHeader.vue +102 -0
  91. package/components/layouts/headers/WebAppHeader.vue +54 -0
  92. package/components/layouts/section/Section.vue +90 -0
  93. package/components/layouts/section/SectionBody.vue +12 -0
  94. package/components/layouts/section/SectionHeader.vue +12 -0
  95. package/components/layouts/section/SectionTitle.vue +13 -0
  96. package/components/lists/List.vue +69 -0
  97. package/components/lists/ListItem.vue +58 -0
  98. package/components/loaders/Loading.vue +83 -0
  99. package/components/loaders/LoadingScreen.vue +285 -0
  100. package/components/modals/DangerModalDialog.vue +149 -0
  101. package/components/modals/InfoModalDialog.vue +143 -0
  102. package/components/modals/ModalActions.vue +22 -0
  103. package/components/modals/ModalContent.vue +5 -0
  104. package/components/modals/ModalDescription.vue +5 -0
  105. package/components/modals/ModalDialog.vue +122 -0
  106. package/components/modals/ModalHeaderGroup.vue +19 -0
  107. package/components/modals/ModalHeadings.vue +5 -0
  108. package/components/modals/ModalSubtitle.vue +14 -0
  109. package/components/modals/ModalTitle.vue +14 -0
  110. package/components/modals/SuccessModalDialog.vue +90 -0
  111. package/components/modules/AppLogo.vue +46 -0
  112. package/components/modules/SVGImage.vue +44 -0
  113. package/components/navigation/links/NavLink.vue +112 -0
  114. package/components/navigation/nav-menu/NavFooterMenu.vue +91 -0
  115. package/components/navigation/nav-menu/NavMenu.vue +36 -0
  116. package/components/navigation/nav-menu/NavMenuItem.vue +44 -0
  117. package/components/navigation/nav-sidebar/BottomUserNavBar.vue +83 -0
  118. package/components/navigation/nav-sidebar/NavSidebar.vue +172 -0
  119. package/components/navigation/nav-sidebar/NavSidebarMenu.vue +14 -0
  120. package/components/navigation/nav-sidebar/NavSidebarMenuItem.vue +76 -0
  121. package/components/navigation/nav-sidebar/NavSidebarMenuSectionTitle.vue +54 -0
  122. package/components/navigation/table-of-contents/TableOfContents.vue +35 -0
  123. package/components/navigation/table-of-contents/TableOfContentsItem.vue +40 -0
  124. package/components/navigation/table-of-contents/TableOfContentsSidebar.vue +29 -0
  125. package/components/pagination/ButtonPagination.vue +274 -0
  126. package/components/pagination/RowsPerPage.vue +60 -0
  127. package/components/pagination/SimplePagination.vue +97 -0
  128. package/components/password/SecurePasswordCondition.vue +41 -0
  129. package/components/password/SecurePasswordConditions.vue +83 -0
  130. package/components/placeholders/ContentPlaceholder.vue +41 -0
  131. package/components/popovers/Popover.vue +128 -0
  132. package/components/rating/InteractiveRating.vue +94 -0
  133. package/components/rating/Rating.vue +60 -0
  134. package/components/rating/RatingItem.vue +54 -0
  135. package/components/skeletons/Skeleton.vue +11 -0
  136. package/components/spinners/Spinner.vue +13 -0
  137. package/components/steppers/CircleStepper.vue +122 -0
  138. package/components/steppers/Step.vue +72 -0
  139. package/components/steppers/StepIndicator.vue +228 -0
  140. package/components/steppers/TabStepper.vue +126 -0
  141. package/components/steppers/vertical-stepper/VerticalStep.vue +223 -0
  142. package/components/steppers/vertical-stepper/VerticalStepper.vue +63 -0
  143. package/components/tables/Table.vue +26 -0
  144. package/components/tables/TableBody.vue +5 -0
  145. package/components/tables/TableCell.vue +34 -0
  146. package/components/tables/TableCellActions.vue +7 -0
  147. package/components/tables/TableHeader.vue +5 -0
  148. package/components/tables/TableHeaderCell.vue +15 -0
  149. package/components/tables/TableRow.vue +14 -0
  150. package/components/tables/TableWrapper.vue +12 -0
  151. package/components/tabs/Tab.vue +145 -0
  152. package/components/tabs/TabBar.vue +64 -0
  153. package/components/tabs/TabContent.vue +5 -0
  154. package/components/tabs/TabsContainer.vue +5 -0
  155. package/components/transitions/HorizontalExpansionTransition.vue +12 -0
  156. package/components/transitions/VerticalExpansionTransition.vue +14 -0
  157. package/components/users/Author.vue +113 -0
  158. package/components/users/User.vue +53 -0
  159. package/composables/useAccordion.ts +12 -0
  160. package/composables/useDarkMode.ts +9 -0
  161. package/composables/useDropdownMenu.ts +25 -0
  162. package/composables/useForm.ts +134 -0
  163. package/composables/useFormValidationMode.ts +11 -0
  164. package/composables/useIsMobile.ts +27 -0
  165. package/composables/useMobileSidebar.ts +32 -0
  166. package/composables/useShiki.ts +12 -0
  167. package/composables/useTableOfContents.ts +50 -0
  168. package/composables/useToastifyConfig.ts +7 -0
  169. package/eslint.config.mjs +14 -0
  170. package/models/constants/app.ts +8 -0
  171. package/models/constants/form.ts +22 -0
  172. package/models/enums/alerts.ts +6 -0
  173. package/models/enums/aspect-ratios.ts +9 -0
  174. package/models/enums/avatars.ts +21 -0
  175. package/models/enums/badges.ts +10 -0
  176. package/models/enums/buttons.ts +38 -0
  177. package/models/enums/colors.ts +9 -0
  178. package/models/enums/content.ts +4 -0
  179. package/models/enums/counters.ts +4 -0
  180. package/models/enums/dividers.ts +9 -0
  181. package/models/enums/dropdowns.ts +18 -0
  182. package/models/enums/effects.ts +6 -0
  183. package/models/enums/emptyPlaceholders.ts +5 -0
  184. package/models/enums/formFields.ts +19 -0
  185. package/models/enums/formValidations.ts +4 -0
  186. package/models/enums/headings.ts +11 -0
  187. package/models/enums/icons.ts +22 -0
  188. package/models/enums/images.ts +16 -0
  189. package/models/enums/lists.ts +10 -0
  190. package/models/enums/loaders.ts +15 -0
  191. package/models/enums/navigation.ts +18 -0
  192. package/models/enums/order.ts +10 -0
  193. package/models/enums/orientations.ts +4 -0
  194. package/models/enums/pages.ts +10 -0
  195. package/models/enums/positions.ts +21 -0
  196. package/models/enums/rating.ts +12 -0
  197. package/models/enums/sections.ts +8 -0
  198. package/models/enums/selects.ts +16 -0
  199. package/models/enums/sliders.ts +4 -0
  200. package/models/enums/steppers.ts +20 -0
  201. package/models/enums/tabs.ts +11 -0
  202. package/models/enums/triggers.ts +4 -0
  203. package/models/types/accordions.ts +6 -0
  204. package/models/types/avatars.ts +4 -0
  205. package/models/types/badges.ts +4 -0
  206. package/models/types/buttons.ts +26 -0
  207. package/models/types/dropdowns.ts +20 -0
  208. package/models/types/forms.ts +14 -0
  209. package/models/types/navigation.ts +11 -0
  210. package/models/types/pagination.ts +4 -0
  211. package/models/types/pdfExportTable.ts +6 -0
  212. package/models/types/radio.ts +9 -0
  213. package/models/types/selects.ts +14 -0
  214. package/models/types/steppers.ts +17 -0
  215. package/models/types/tableOfContent.ts +6 -0
  216. package/models/types/tabs.ts +7 -0
  217. package/nuxt.config.ts +40 -0
  218. package/package.json +57 -0
  219. package/plugins/vue3-toastify.ts +14 -0
  220. package/tsconfig.json +7 -0
@@ -0,0 +1,102 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'flex-col',
6
+ 'w-full',
7
+ 'gap-3',
8
+ hasSidePadding && 'px-content-side-padding-mobile md:px-content-side-padding',
9
+ 'pt-12',
10
+ 'pb-8',
11
+ 'border-b border-border-default'
12
+ ]"
13
+ >
14
+ <NavLink
15
+ v-if="hasGoBackLink"
16
+ :text="goBackText"
17
+ :iconPosition="IconPosition.LEFT"
18
+ :to="goBackLink"
19
+ icon="mdiKeyboardBackspace"
20
+ class="mb-2"
21
+ />
22
+
23
+ <Breadcrumbs
24
+ v-if="type === PageTitleType.WITH_BREADCRUMBS"
25
+ />
26
+
27
+ <Overtitle
28
+ v-if="type === PageTitleType.WITH_OVERTITLE"
29
+ :title="overtitle ? overtitle : pageOvertitle.toString()"
30
+ :class="overtitleClass"
31
+ :isUppercase="isOverTitleUppercase"
32
+ />
33
+
34
+ <h1
35
+ :class="[
36
+ 'text-3xl md:text-4xl',
37
+ 'font-semibold',
38
+ 'leading-tight',
39
+ 'text-text-default',
40
+ ]"
41
+ >
42
+ {{ title ? title : pageTitle }}
43
+ </h1>
44
+
45
+ <p
46
+ v-if="pageDescription || description"
47
+ class="text-text-neutral-subtle text-lg max-w-[800px]"
48
+ >
49
+ {{ description ? description : pageDescription }}
50
+ </p>
51
+ </div>
52
+ </template>
53
+ <script setup lang="ts">
54
+ // Props
55
+ defineProps({
56
+ type: {
57
+ type: String as PropType<PageTitleType>,
58
+ default: PageTitleType.SIMPLE,
59
+ validator: (value: PageTitleType) => Object.values(PageTitleType).includes(value),
60
+ },
61
+ title: String as PropType<string>,
62
+ overtitle: String as PropType<string>,
63
+ overtitleClass: String as PropType<string>,
64
+ description: String as PropType<string>,
65
+ isOverTitleUppercase: Boolean as PropType<boolean>,
66
+ hasSidePadding: {
67
+ type: Boolean as PropType<boolean>,
68
+ default: true,
69
+ },
70
+ hasGoBackLink: {
71
+ type: Boolean as PropType<boolean>,
72
+ default: false,
73
+ },
74
+ goBackText: {
75
+ type: String as PropType<string>,
76
+ default: 'Go back',
77
+ },
78
+ goBackLink: {
79
+ type: String as PropType<string>,
80
+ default: '',
81
+ },
82
+ })
83
+
84
+ // Page title
85
+ const route = useRoute()
86
+ const pageTitle = ref(route.meta.title || 'Page title')
87
+ const pageOvertitle = ref(route.meta.overtitle || 'Overtitle')
88
+ const pageDescription = ref(route.meta.description)
89
+
90
+ // Watchers
91
+ watch(() => route.meta.title, (newTitle) => {
92
+ pageTitle.value = newTitle || 'Page title'
93
+ })
94
+
95
+ watch(() => route.meta.overtitle, (newOvertitle) => {
96
+ pageOvertitle.value = newOvertitle || 'Overtitle'
97
+ })
98
+
99
+ watch(() => route.meta.description, (newDescription) => {
100
+ pageDescription.value = newDescription || 'Page description'
101
+ })
102
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <header
3
+ :class="[
4
+ 'flex',
5
+ 'items-center',
6
+ 'justify-between',
7
+ 'pt-12',
8
+ 'pb-8',
9
+ 'gap-3',
10
+ 'px-content-side-padding-mobile md:px-content-side-padding',
11
+ ]"
12
+ >
13
+ <div class="flex gap-4">
14
+ <ActionIconButton
15
+ :icon="isMobileSidebarOpen ? 'mdiMenuOpen' : 'mdiMenuClose'"
16
+ class="lg:hidden shadow-sm"
17
+ @click="toggleMobileSidebar"
18
+ />
19
+ <PageTitle class="hidden lg:flex" :type="pageTitleType"/>
20
+ </div>
21
+ <div class="flex gap-3 items-center xs:w-auto">
22
+ <slot name="header-actions" />
23
+ </div>
24
+ </header>
25
+
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ // Imports
30
+ import { useRoute } from 'vue-router'
31
+
32
+ // Props
33
+ defineProps({
34
+ pageTitleType: {
35
+ type: String as PropType<PageTitleType>,
36
+ default: PageTitleType.SIMPLE
37
+ }
38
+ })
39
+
40
+ // Page title
41
+ const route = useRoute()
42
+ const currentPageTitle = computed<string>(() =>
43
+ (route.meta.title as string) || 'Page title'
44
+ )
45
+
46
+ // Dynamically set the page title
47
+ useHead(() => ({
48
+ title: pageTitle(currentPageTitle.value, App.NAME),
49
+ }))
50
+
51
+ // Composables
52
+ const { isMobileSidebarOpen, toggleMobileSidebar } = useMobileSidebar()
53
+
54
+ </script>
@@ -0,0 +1,90 @@
1
+ <template>
2
+ <section
3
+ :id="id || undefined"
4
+ :class="[
5
+ 'flex',
6
+ 'flex-col',
7
+ 'gap-3',
8
+ 'items-center',
9
+ 'justify-center',
10
+ 'w-full',
11
+ hasSidePadding && 'px-content-side-padding-mobile md:px-content-side-padding',
12
+ spacingClass,
13
+ topSpacing && topSpacingClass,
14
+ bottomSpacing && bottomSpacingClass,
15
+ isDark && 'dark'
16
+ ]"
17
+ >
18
+ <template v-if="hasContentMaxWidth">
19
+ <MaxWidthContainer>
20
+ <slot />
21
+ </MaxWidthContainer>
22
+ </template>
23
+ <template v-else>
24
+ <slot />
25
+ </template>
26
+ </section>
27
+ </template>
28
+ <script setup lang="ts">
29
+ // Props
30
+ const props = defineProps({
31
+ id: String as PropType<string | undefined>,
32
+ hasSidePadding: {
33
+ type: Boolean as PropType<boolean>,
34
+ default: true,
35
+ },
36
+ hasContentMaxWidth: {
37
+ type: Boolean as PropType<boolean>,
38
+ default: false,
39
+ },
40
+ spacing: {
41
+ type: String as PropType<SectionSpacing>,
42
+ default: SectionSpacing.XS,
43
+ validator: (value: SectionSpacing) => Object.values(SectionSpacing).includes(value),
44
+ },
45
+ topSpacing: String as PropType<SectionSpacing>,
46
+ bottomSpacing: String as PropType<SectionSpacing>,
47
+ isDark: {
48
+ type: Boolean as PropType<boolean>,
49
+ default: false,
50
+ },
51
+ })
52
+
53
+ // Computed
54
+ const spacingClass = computed(() => {
55
+ const variant = {
56
+ [SectionSpacing.NONE]: 'py-0',
57
+ [SectionSpacing.XS]: 'py-section-xs',
58
+ [SectionSpacing.SM]: 'py-section-sm',
59
+ [SectionSpacing.MD]: 'py-section-md',
60
+ [SectionSpacing.LG]: 'py-section-lg',
61
+ [SectionSpacing.XL]: 'py-section-xl',
62
+ }
63
+ return variant[props.spacing as SectionSpacing] || 'py-section-xs'
64
+ })
65
+
66
+ const topSpacingClass = computed(() => {
67
+ const variant = {
68
+ [SectionSpacing.NONE]: 'py-0',
69
+ [SectionSpacing.XS]: 'pt-section-xs',
70
+ [SectionSpacing.SM]: 'pt-section-sm',
71
+ [SectionSpacing.MD]: 'pt-section-md',
72
+ [SectionSpacing.LG]: 'pt-section-lg',
73
+ [SectionSpacing.XL]: 'pt-section-xl',
74
+ }
75
+ return variant[props.topSpacing as SectionSpacing]
76
+ })
77
+
78
+ const bottomSpacingClass = computed(() => {
79
+ const variant = {
80
+ [SectionSpacing.NONE]: '!pb-0',
81
+ [SectionSpacing.XS]: '!pb-section-xs',
82
+ [SectionSpacing.SM]: '!pb-section-sm',
83
+ [SectionSpacing.MD]: '!pb-section-md',
84
+ [SectionSpacing.LG]: 'pb-section-lg',
85
+ [SectionSpacing.XL]: 'pb-section-xl',
86
+ }
87
+ return variant[props.bottomSpacing as SectionSpacing]
88
+ })
89
+
90
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'flex-col',
6
+ 'w-full',
7
+ 'gap-6',
8
+ ]"
9
+ >
10
+ <slot />
11
+ </div>
12
+ </template>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'w-full',
6
+ 'justify-between',
7
+ 'gap-4'
8
+ ]"
9
+ >
10
+ <slot />
11
+ </div>
12
+ </template>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <h2 class="text-xl font-semibold">
3
+ {{ title }}
4
+ </h2>
5
+ </template>
6
+ <script setup lang="ts">
7
+ defineProps({
8
+ title: {
9
+ type: String as PropType<string>,
10
+ default: 'Title',
11
+ },
12
+ })
13
+ </script>
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <ul
3
+ :class="[
4
+ 'w-full flex flex-col gap-2',
5
+ hasSeparator && 'gap-0 divide-y divide-border-neutral-subtle border-y border-border-neutral-subtle',
6
+ layout === ListLayout.GRID && gridClasses,
7
+ ]"
8
+ >
9
+ <template v-if="items.length && !$slots.default">
10
+ <ListItem
11
+ v-for="(item, index) in items" :key="index"
12
+ :icon="listItemIcon"
13
+ :iconClass="listItemIconClass"
14
+ :size="listItemSize"
15
+ :spaced="spaced"
16
+ >
17
+ {{ item }}
18
+ </ListItem>
19
+ </template>
20
+ <slot v-else-if="$slots.default" />
21
+ </ul>
22
+ </template>
23
+ <script setup lang="ts">
24
+ // Props
25
+ const props = defineProps({
26
+ items: {
27
+ type: Array as PropType<any>,
28
+ default: () => ["Item 1", "Item 2", "Item 3"],
29
+ },
30
+ hasSeparator: {
31
+ type: Boolean as PropType<boolean>,
32
+ default: false,
33
+ },
34
+ layout: {
35
+ type: String as PropType<ListLayout>,
36
+ default: ListLayout.LIST,
37
+ validator: (value: ListLayout) => Object.values(ListLayout).includes(value),
38
+ },
39
+ cols: { // Desktop
40
+ type: Number as PropType<number>,
41
+ default: 2,
42
+ },
43
+ tabletCols: {
44
+ type: Number as PropType<number>,
45
+ default: 2,
46
+ },
47
+ mobileCols: {
48
+ type: Number as PropType<number>,
49
+ default: 1,
50
+ },
51
+ listItemIcon: String as PropType<any>,
52
+ listItemIconClass: {
53
+ type: String as PropType<string>,
54
+ default: 'text-icon-secondary-brand-default'
55
+ },
56
+ listItemSize: {
57
+ type: String as PropType<ListItemSize>,
58
+ default: ListItemSize.SM,
59
+ validator: (value: ListItemSize) => Object.values(ListItemSize).includes(value),
60
+ },
61
+ spaced: {
62
+ type: Boolean as PropType<boolean>,
63
+ default: false
64
+ },
65
+ })
66
+
67
+ // Computed classes
68
+ const gridClasses = computed(() => getGridClasses(props.cols, props.tabletCols, props.mobileCols))
69
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <li
3
+ :class="[
4
+ 'flex gap-2',
5
+ spaced && 'py-4'
6
+ ]"
7
+ >
8
+ <span v-if="icon">
9
+ <MdiIcon
10
+ :class="iconClass"
11
+ :icon="icon"
12
+ preserveAspectRatio="xMidYMid meet"
13
+ :size="iconSize"
14
+ />
15
+ </span>
16
+ <span :class="[contentSizeClass, 'w-full']">
17
+ <slot />
18
+ </span>
19
+ </li>
20
+ </template>
21
+ <script setup lang="ts">
22
+ // Props
23
+ const props = defineProps({
24
+ icon: String as PropType<any>,
25
+ iconClass: {
26
+ type: String as PropType<string>,
27
+ default: 'text-icon-secondary-brand-default'
28
+ },
29
+ spaced: {
30
+ type: Boolean as PropType<boolean>,
31
+ default: false
32
+ },
33
+ size: {
34
+ type: String as PropType<ListItemSize>,
35
+ default: ListItemSize.SM,
36
+ validator: (value: ListItemSize) => Object.values(ListItemSize).includes(value),
37
+ },
38
+ })
39
+
40
+ // Computed
41
+ const iconSize = computed(() => {
42
+ const sizeVariant = {
43
+ [ListItemSize.XS]: '16',
44
+ [ListItemSize.SM]: '20',
45
+ [ListItemSize.MD]: '24',
46
+ }
47
+ return sizeVariant[props.size as ListItemSize] || '20'
48
+ })
49
+
50
+ const contentSizeClass = computed(() => {
51
+ const sizeVariant = {
52
+ [ListItemSize.XS]: 'text-xs',
53
+ [ListItemSize.SM]: 'text-sm',
54
+ [ListItemSize.MD]: 'text-md',
55
+ }
56
+ return sizeVariant[props.size as ListItemSize] || 'text-sm'
57
+ })
58
+ </script>
@@ -0,0 +1,83 @@
1
+ <template>
2
+ <div
3
+ v-if="isLoading"
4
+ class="flex flex-col items-center gap-4"
5
+ >
6
+ <Spinner :class="[spinnerSizeClass, spinnerClass]" />
7
+ <span>{{ animatedText }}</span>
8
+ </div>
9
+ </template>
10
+ <script setup lang="ts">
11
+
12
+ // Props
13
+ const props = defineProps({
14
+ isLoading: {
15
+ type: Boolean as PropType<boolean>,
16
+ default: true,
17
+ },
18
+ text: {
19
+ type: String as PropType<string>,
20
+ default: "Loading",
21
+ },
22
+ spinnerSize: {
23
+ type: String as PropType<LoadingSpinnerSize>,
24
+ default: LoadingSpinnerSize.LG,
25
+ validator: (value: LoadingSpinnerSize) => Object.values(LoadingSpinnerSize).includes(value),
26
+ },
27
+ spinnerClass: String as PropType<string>,
28
+ })
29
+
30
+ // Computed classes
31
+ const spinnerSizeClass = computed(() => {
32
+ const variants = {
33
+ [LoadingSpinnerSize.XS]: 'w-[24px] h-[24px] border-2',
34
+ [LoadingSpinnerSize.SM]: 'w-[28px] h-[28px] border-2',
35
+ [LoadingSpinnerSize.MD]: 'w-[32px] h-[32px] border-3',
36
+ [LoadingSpinnerSize.LG]: 'w-[36px] h-[36px] border-3',
37
+ [LoadingSpinnerSize.XL]: 'w-[40px] h-[40px] border-3',
38
+ [LoadingSpinnerSize.XXL]: 'w-[44px] h-[44px] border-4',
39
+ [LoadingSpinnerSize.XXXL]: 'w-[48px] h-[48px] border-4',
40
+ }
41
+ return variants[props.spinnerSize as LoadingSpinnerSize] || 'w-[36px] h-[36px] border-2'
42
+ })
43
+
44
+
45
+ // Reactive state for animated ellipsis
46
+ const animatedText = ref(props.text)
47
+ let intervalId: ReturnType<typeof setInterval> | null = null
48
+
49
+ // Methods
50
+ const startAnimation = () => {
51
+ if (intervalId !== null) return // Prevent multiple intervals
52
+
53
+ let dotCount = 0
54
+ intervalId = setInterval(() => {
55
+ dotCount = (dotCount + 1) % 4 // Cycles through 0, 1, 2, 3
56
+ animatedText.value = props.text + '.'.repeat(dotCount)
57
+ }, 500) // Update every 500ms
58
+ }
59
+
60
+ const stopAnimation = () => {
61
+ if (intervalId !== null) {
62
+ clearInterval(intervalId)
63
+ intervalId = null
64
+ animatedText.value = props.text // Reset to base text
65
+ }
66
+ }
67
+
68
+ // Watch
69
+ watch(
70
+ () => props.isLoading,
71
+ (newLoading) => {
72
+ if (newLoading) {
73
+ startAnimation()
74
+ } else {
75
+ stopAnimation()
76
+ }
77
+ },
78
+ { immediate: true }
79
+ )
80
+
81
+ // Clear interval on unmount
82
+ onUnmounted(() => stopAnimation())
83
+ </script>