@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,128 @@
1
+ <template>
2
+ <div
3
+ ref="popoverContainer"
4
+ class="relative popover-container"
5
+ >
6
+ <Transition
7
+ :enter-from-class="transitionEnterFromClass"
8
+ :leave-to-class="transitionLeaveToClass"
9
+ enter-active-class="transition ease-out duration-200"
10
+ leave-active-class="transition ease-in duration-150"
11
+ enter-to-class="opacity-100 translate-y-0"
12
+ leave-from-class="opacity-100 translate-y-0"
13
+ >
14
+ <Card
15
+ v-if="isOpen"
16
+ :class="[
17
+ 'absolute',
18
+ 'z-10',
19
+ position === Position.TOP ? 'bottom-full mb-2' : 'top-full mt-2',
20
+ alignClass,
21
+ popoverClass,
22
+ '!shadow-md'
23
+ ]"
24
+ >
25
+ <CardBody>
26
+ <slot
27
+ name="content"
28
+ :onClose="toggle"
29
+ />
30
+ </CardBody>
31
+ </Card>
32
+ </Transition>
33
+
34
+ <!-- Activator -->
35
+ <div
36
+ class="popover-activator"
37
+ @click="handleClick"
38
+ @mouseenter="handleMouseEnter"
39
+ @mouseleave="handleMouseLeave"
40
+ >
41
+ <slot
42
+ name="activator"
43
+ :onClick="toggle"
44
+ />
45
+ </div>
46
+ </div>
47
+ </template>
48
+
49
+ <script setup lang="ts">
50
+ // Props
51
+ const props = defineProps({
52
+ position: {
53
+ type: String as PropType<Position>,
54
+ default: Position.TOP,
55
+ validator: (value: Position) => Object.values(Position).includes(value),
56
+ },
57
+ align: {
58
+ type: String as PropType<Align>,
59
+ default: Align.CENTER,
60
+ validator: (value: Align) => Object.values(Align).includes(value),
61
+ },
62
+ trigger: {
63
+ type: String as PropType<Trigger>,
64
+ default: Trigger.HOVER,
65
+ validator: (value: Trigger) => Object.values(Trigger).includes(value),
66
+ },
67
+ popoverClass: {
68
+ type: String as PropType<string>,
69
+ default: 'min-w-[300px]'
70
+ }
71
+ })
72
+
73
+ // Computed classes
74
+ const alignClass = computed(() => {
75
+ const alignVariant: Record<Align, string> = {
76
+ [Align.LEFT]: "left-0",
77
+ [Align.CENTER]: "left-1/2 -translate-x-1/2",
78
+ [Align.RIGHT]: "right-0",
79
+ }
80
+
81
+ return alignVariant[props.align as Align] || "left-1/2 -translate-x-1/2"
82
+ })
83
+
84
+ const transitionEnterFromClass = computed(() => {
85
+ return props.position === Position.TOP
86
+ ? 'opacity-0 translate-y-2'
87
+ : 'opacity-0 -translate-y-2'
88
+ })
89
+
90
+ const transitionLeaveToClass = computed(() => {
91
+ return props.position === Position.TOP
92
+ ? 'opacity-0 translate-y-2'
93
+ : 'opacity-0 -translate-y-2'
94
+ })
95
+
96
+ // Refs
97
+ const popoverContainer = ref(null)
98
+
99
+ // Composables
100
+ const [isOpen, toggle] = useToggle(false)
101
+ let hoverTimeout: ReturnType<typeof setTimeout>
102
+
103
+ onClickOutside(popoverContainer, () => {
104
+ isOpen.value = false
105
+ })
106
+
107
+ // Handlers
108
+ const handleClick = () => {
109
+ if (props.trigger === Trigger.CLICK) {
110
+ toggle()
111
+ }
112
+ }
113
+
114
+ const handleMouseEnter = () => {
115
+ if (props.trigger === Trigger.HOVER) {
116
+ clearTimeout(hoverTimeout)
117
+ isOpen.value = true
118
+ }
119
+ }
120
+
121
+ const handleMouseLeave = () => {
122
+ if (props.trigger === Trigger.HOVER) {
123
+ hoverTimeout = setTimeout(() => {
124
+ isOpen.value = false
125
+ }, 200)
126
+ }
127
+ }
128
+ </script>
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <div
3
+ class="flex gap-1"
4
+ @mouseleave="hoverIndex = null"
5
+ >
6
+ <RatingItem
7
+ v-for="(icon, index) in items"
8
+ :key="index"
9
+ :icon="icon"
10
+ :size="size"
11
+ :color="color"
12
+ isInteractive
13
+ @click="handleClick(index)"
14
+ @mouseenter="onMouseEnter(index)"
15
+ />
16
+ </div>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ const props = defineProps({
21
+ modelValue: {
22
+ type: Number,
23
+ required: true
24
+ },
25
+ size: {
26
+ type: String as PropType<RatingItemSize>,
27
+ default: RatingItemSize.SM,
28
+ validator: (value: RatingItemSize) => Object.values(RatingItemSize).includes(value),
29
+ },
30
+ color: {
31
+ type: String as PropType<RatingItemColor>,
32
+ default: RatingItemColor.GOLD,
33
+ validator: (value: RatingItemColor) => Object.values(RatingItemColor).includes(value),
34
+ },
35
+ emptyIndicatorIcon: {
36
+ type: String as PropType<any>,
37
+ default: 'mdiStarOutline'
38
+ },
39
+ halfIndicatorIcon: {
40
+ type: String as PropType<any>,
41
+ default: 'mdiStarHalfFull'
42
+ },
43
+ fullIndicatorIcon: {
44
+ type: String as PropType<any>,
45
+ default: 'mdiStar'
46
+ },
47
+ hoverPreview: {
48
+ type: Boolean,
49
+ default: true
50
+ }
51
+ })
52
+
53
+ // States
54
+ const hoverIndex = ref<number | null>(null)
55
+
56
+ // Emits
57
+ const emit = defineEmits(['update:modelValue'])
58
+
59
+ // Handlers
60
+ const onMouseEnter = (index: number) => {
61
+ if (props.hoverPreview) {
62
+ hoverIndex.value = index
63
+ }
64
+ }
65
+
66
+ const handleClick = (index: number) => {
67
+ const clickedValue = index + 1
68
+
69
+ if (clickedValue === props.modelValue) {
70
+ emit('update:modelValue', 0)
71
+ } else {
72
+ emit('update:modelValue', clickedValue)
73
+ }
74
+ }
75
+
76
+ // Computed
77
+ // Get the value to render: either hover preview or actual value
78
+ const displayValue = computed(() => {
79
+ if (props.hoverPreview && hoverIndex.value !== null)
80
+ return hoverIndex.value + 1
81
+
82
+ if (!isFinite(props.modelValue) || isNaN(props.modelValue))
83
+ return 0
84
+
85
+ return Math.min(5, Math.max(0, Math.round(props.modelValue * 2) / 2))
86
+ })
87
+
88
+ const items = computed(() => getRatingIndicator(
89
+ displayValue.value,
90
+ props.emptyIndicatorIcon,
91
+ props.halfIndicatorIcon,
92
+ props.fullIndicatorIcon
93
+ ))
94
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div class="flex gap-1">
3
+ <RatingItem
4
+ v-for="(icon, index) in items"
5
+ :key="index"
6
+ :icon
7
+ :size
8
+ :color
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ // Props
15
+ const props = defineProps({
16
+ value: {
17
+ type: Number,
18
+ default: 0,
19
+ },
20
+ size: {
21
+ type: String as PropType<RatingItemSize>,
22
+ default: RatingItemSize.SM,
23
+ validator: (value: RatingItemSize) => Object.values(RatingItemSize).includes(value),
24
+ },
25
+ color: {
26
+ type: String as PropType<RatingItemColor>,
27
+ default: RatingItemColor.GOLD,
28
+ validator: (value: RatingItemColor) => Object.values(RatingItemColor).includes(value),
29
+ },
30
+ emptyIndicatorIcon: {
31
+ type: String as PropType<any>,
32
+ default: 'mdiStarOutline'
33
+ },
34
+ halfIndicatorIcon: {
35
+ type: String as PropType<any>,
36
+ default: 'mdiStarHalfFull'
37
+ },
38
+ fullIndicatorIcon: {
39
+ type: String as PropType<any>,
40
+ default: 'mdiStar'
41
+ }
42
+ })
43
+
44
+
45
+ // Computed functions
46
+ // Normalize value: clamp between 0 and 5, round to nearest 0.5
47
+ const safeValue = computed(() => {
48
+ if (!isFinite(props.value) || isNaN(props.value))
49
+ return 0
50
+
51
+ return Math.min(5, Math.max(0, Math.round(props.value * 2) / 2))
52
+ })
53
+
54
+ const items = computed(() => getRatingIndicator(
55
+ safeValue.value,
56
+ props.emptyIndicatorIcon,
57
+ props.halfIndicatorIcon,
58
+ props.fullIndicatorIcon
59
+ ))
60
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <MdiIcon
3
+ :icon
4
+ :size="iconSize"
5
+ preserveAspectRatio="xMidYMid meet"
6
+ :class="[
7
+ colorClass,
8
+ isInteractive && 'hover:cursor-pointer'
9
+ ]"
10
+ />
11
+ </template>
12
+ <script setup lang="ts">
13
+ // Props
14
+ const props = defineProps({
15
+ icon: {
16
+ type: String as PropType<any>,
17
+ default: 'mdiStarOutline'
18
+ },
19
+ size: {
20
+ type: String as PropType<RatingItemSize>,
21
+ default: RatingItemSize.SM,
22
+ validator: (value: RatingItemSize) => Object.values(RatingItemSize).includes(value),
23
+ },
24
+ color: {
25
+ type: String as PropType<RatingItemColor>,
26
+ default: RatingItemColor.GOLD,
27
+ validator: (value: RatingItemColor) => Object.values(RatingItemColor).includes(value),
28
+ },
29
+ isInteractive: {
30
+ type: Boolean as PropType<boolean>,
31
+ default: false
32
+ }
33
+ })
34
+
35
+ // Computed
36
+ const iconSize = computed(() => {
37
+ const sizeVariant = {
38
+ [RatingItemSize.SM]: '20',
39
+ [RatingItemSize.MD]: '24',
40
+ [RatingItemSize.LG]: '32',
41
+ [RatingItemSize.XL]: '40',
42
+ [RatingItemSize.XXL]: '48',
43
+ }
44
+ return sizeVariant[props.size as RatingItemSize] || '20'
45
+ })
46
+
47
+ const colorClass = computed(() => {
48
+ const colorVariants = {
49
+ [RatingItemColor.GOLD]: 'text-icon-rating',
50
+ [RatingItemColor.PRIMARY_BRAND]: 'text-icon-primary-brand-rating',
51
+ }
52
+ return colorVariants[props.color as RatingItemColor] || 'text-icon-rating'
53
+ })
54
+ </script>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'w-20',
5
+ 'h-10',
6
+ 'bg-gray-200 dark:bg-gray-800',
7
+ 'rounded',
8
+ 'animate-pulse',
9
+ ]"
10
+ />
11
+ </template>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'animate-spin',
5
+ 'rounded-full',
6
+ 'h-4',
7
+ 'w-4',
8
+ 'border-2',
9
+ 'border-border-primary-brand-default',
10
+ 'border-t-transparent',
11
+ ]"
12
+ />
13
+ </template>
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex items-center',
5
+ isFullWidth ? 'w-full' : 'w-fit',
6
+ ]"
7
+ >
8
+ <template v-for="(item, index) in stepItems" :key="index">
9
+ <StepIndicator
10
+ :type
11
+ :status="item.status"
12
+ :size="size"
13
+ :step="index + 1"
14
+ :stepIcon="item.icon"
15
+ :completedIcon
16
+ :isHovered="isInteractive && hoveredIndex === index"
17
+ @click="handleStepClick(index)"
18
+ @mouseenter="handleMouseEnter(index)"
19
+ @mouseleave="handleMouseLeave"
20
+ />
21
+
22
+ <!-- Line divider -->
23
+ <Divider
24
+ v-if="index < stepItems.length - 1"
25
+ :class="[
26
+ 'min-w-[20px] sm:min-w-[40px]',
27
+ 'border-2',
28
+ stepItems[index].status === StepStatus.COMPLETED && '!border-border-primary-brand-active',
29
+ dividerClass
30
+ ]"
31
+ />
32
+ </template>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ // Imports
38
+ const emit = defineEmits(['update:modelValue'])
39
+
40
+ // Props
41
+ const props = defineProps({
42
+ modelValue: {
43
+ type: Number as PropType<number>,
44
+ default: 1,
45
+ },
46
+ steps: {
47
+ type: [Array, Number] as PropType<number | CircleStep[]>,
48
+ default: () => [
49
+ {
50
+ icon: 'mdiHomeOutline',
51
+ },
52
+ {
53
+ icon: 'mdiAccountOutline',
54
+ },
55
+ {
56
+ icon: 'mdiMapMarkerStarOutline',
57
+ },
58
+ ],
59
+ },
60
+ type: String as PropType<StepIndicatorType>,
61
+ size: String as PropType<StepIndicatorSize>,
62
+ completedIcon: String as PropType<any>,
63
+ isInteractive: {
64
+ type: Boolean as PropType<boolean>,
65
+ default: false,
66
+ },
67
+ isFullWidth: {
68
+ type: Boolean as PropType<boolean>,
69
+ default: false,
70
+ },
71
+ dividerClass: String as PropType<string>,
72
+ })
73
+
74
+ // States
75
+ const hoveredIndex = ref<number | null>(null)
76
+
77
+ // Computed step status
78
+ const stepItems = computed(() => {
79
+ const activeIndex = props.modelValue > 0 ? props.modelValue - 1 : 0
80
+
81
+ const stepsArray = Array.isArray(props.steps)
82
+ ? props.steps
83
+ : Array.from({ length: props.steps }, () => ({}))
84
+
85
+ return stepsArray.map((step, index) => {
86
+ let status: StepStatus
87
+
88
+ if (index < activeIndex) {
89
+ status = StepStatus.COMPLETED
90
+ } else if (index === activeIndex) {
91
+ status = StepStatus.CURRENT
92
+ } else {
93
+ status = StepStatus.INACTIVE
94
+ }
95
+
96
+ const icon = typeof step === 'object' && 'icon' in step ? step.icon : undefined
97
+
98
+ return {
99
+ icon,
100
+ status,
101
+ }
102
+ })
103
+ })
104
+
105
+
106
+ // Handlers
107
+ const handleStepClick = (index: number) => {
108
+ if (props.isInteractive) {
109
+ emit('update:modelValue', index + 1)
110
+ }
111
+ }
112
+
113
+ const handleMouseEnter = (index: number) => {
114
+ if (props.isInteractive) {
115
+ hoveredIndex.value = index
116
+ }
117
+ }
118
+
119
+ const handleMouseLeave = () => {
120
+ hoveredIndex.value = null
121
+ }
122
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex gap-5',
5
+ 'select-none',
6
+ !description && 'items-center',
7
+ isInteractive && 'cursor-pointer group',
8
+ ]"
9
+ @mouseenter="isInteractive && (isHovered = true)"
10
+ @mouseleave="isInteractive && (isHovered = false)"
11
+ >
12
+ <StepIndicator
13
+ :type
14
+ :status
15
+ :size
16
+ :step
17
+ :stepIcon
18
+ :completedIcon
19
+ :isHovered
20
+ class="!group-hover:border-red-500"
21
+ />
22
+ <div class="flex flex-col w-full text-sm">
23
+ <span
24
+ :class="[
25
+ 'font-semibold',
26
+ titleClass,
27
+ ]"
28
+ >
29
+ {{ title }}
30
+ </span>
31
+ <p
32
+ v-if="description"
33
+ class="text-text-neutral-inactive"
34
+ >
35
+ {{ description }}
36
+ </p>
37
+ </div>
38
+ </div>
39
+ </template>
40
+ <script setup lang="ts">
41
+ // Props
42
+ const props = defineProps({
43
+ title: {
44
+ type: String as PropType<string>,
45
+ default: 'Title',
46
+ },
47
+ description: String as PropType<string>,
48
+ type: String as PropType<StepIndicatorType>,
49
+ status: String as PropType<StepStatus>,
50
+ size: String as PropType<StepIndicatorSize>,
51
+ step: Number as PropType<number>,
52
+ stepIcon: String as PropType<any>,
53
+ completedIcon: String as PropType<any>,
54
+ isInteractive: {
55
+ type: Boolean as PropType<boolean>,
56
+ default: false,
57
+ },
58
+ })
59
+
60
+ // States
61
+ const isHovered = ref(false)
62
+
63
+ // Computed classes
64
+ const titleClass = computed(() => {
65
+ const variant = {
66
+ [StepStatus.INACTIVE]: 'text-text-neutral-inactive group-hover:text-text-primary-brand-hover',
67
+ [StepStatus.CURRENT]: 'text-text-primary-brand-active group-hover:text-text-primary-brand-hover',
68
+ [StepStatus.COMPLETED]: 'text-text-default group-hover:text-text-primary-brand-hover',
69
+ }
70
+ return variant[props.status as StepStatus] || 'text-text-neutral-inactive'
71
+ })
72
+ </script>