@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,228 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'aspect-square',
5
+ 'border-2',
6
+ 'flex items-center justify-center',
7
+ 'rounded-full',
8
+ 'select-none',
9
+ 'text-center',
10
+ containerBackgroundClass,
11
+ containerSizeClass,
12
+ containerBorderClass,
13
+ ]"
14
+ >
15
+ <!-- NONE -->
16
+ <span
17
+ v-if="status === StepStatus.NONE"
18
+ :class="[
19
+ 'font-bold',
20
+ stepNumberSizeClass,
21
+ textColorClass,
22
+ ]"
23
+ >
24
+ <template v-if="type === StepIndicatorType.NUMBER">
25
+ {{ step }}
26
+ </template>
27
+ <template v-else-if="type === StepIndicatorType.ICON && stepIcon">
28
+ <MdiIcon
29
+ :icon="stepIcon"
30
+ :size="iconSize"
31
+ preserveAspectRatio="xMidYMid meet"
32
+ />
33
+ </template>
34
+ </span>
35
+
36
+ <!-- INACTIVE -->
37
+ <span
38
+ v-if="status === StepStatus.INACTIVE"
39
+ :class="[
40
+ 'font-bold',
41
+ stepNumberSizeClass,
42
+ textColorClass,
43
+ ]"
44
+ >
45
+ <template v-if="type === StepIndicatorType.NUMBER">
46
+ {{ step }}
47
+ </template>
48
+ <template v-else-if="type === StepIndicatorType.ICON && stepIcon">
49
+ <MdiIcon
50
+ :icon="stepIcon"
51
+ :size="iconSize"
52
+ preserveAspectRatio="xMidYMid meet"
53
+ />
54
+ </template>
55
+ </span>
56
+
57
+ <!-- CURRENT -->
58
+ <span
59
+ v-else-if="status === StepStatus.CURRENT"
60
+ :class="[
61
+ 'font-bold text-text-primary-brand-default',
62
+ stepNumberSizeClass,
63
+ textColorClass,
64
+ ]"
65
+ >
66
+ <template v-if="type === StepIndicatorType.NUMBER">
67
+ {{ step }}
68
+ </template>
69
+ <template v-else-if="type === StepIndicatorType.ICON && stepIcon">
70
+ <MdiIcon
71
+ :icon="stepIcon"
72
+ :size="iconSize"
73
+ preserveAspectRatio="xMidYMid meet"
74
+ />
75
+ </template>
76
+ <template v-else-if="type === StepIndicatorType.EMPTY">
77
+ <!-- Circle -->
78
+ <div
79
+ :class="[
80
+ 'w-[10px] h-[10px] rounded-full bg-icon-primary-brand-default',
81
+ circleColorClass,
82
+ ]"
83
+ />
84
+ </template>
85
+ </span>
86
+
87
+ <!-- COMPLETED -->
88
+ <span
89
+ v-else-if="status === StepStatus.COMPLETED"
90
+ class="text-sm font-semibold text-text-primary-brand-default"
91
+ >
92
+ <MdiIcon
93
+ :icon="completedIcon"
94
+ :size="iconSize"
95
+ preserveAspectRatio="xMidYMid meet"
96
+ class="text-icon-neutral-on-filled-bg"
97
+ />
98
+ </span>
99
+ </div>
100
+ </template>
101
+ <script setup lang="ts">
102
+ // Props
103
+ const props = defineProps({
104
+ type: {
105
+ type: String as PropType<StepIndicatorType>,
106
+ default: StepIndicatorType.NUMBER,
107
+ validator: (value: StepIndicatorType) => Object.values(StepIndicatorType).includes(value),
108
+ },
109
+ status: {
110
+ type: String as PropType<StepStatus>,
111
+ default: StepStatus.INACTIVE,
112
+ validator: (value: StepStatus) => Object.values(StepStatus).includes(value),
113
+ },
114
+ size: {
115
+ type: String as PropType<StepIndicatorSize>,
116
+ default: StepIndicatorSize.XL,
117
+ validator: (value: StepIndicatorSize) => Object.values(StepIndicatorSize).includes(value),
118
+ },
119
+ step: {
120
+ type: Number as PropType<number>,
121
+ default: 1,
122
+ },
123
+ stepIcon: String as PropType<any>,
124
+ completedIcon: {
125
+ type: String as PropType<any>,
126
+ default: 'mdiCheck',
127
+ },
128
+ isHovered: {
129
+ type: Boolean as PropType<boolean>,
130
+ default: false,
131
+ },
132
+ })
133
+
134
+ // Computed classes
135
+ const containerSizeClass = computed(() => {
136
+ const variant = {
137
+ [StepIndicatorSize.XL]: 'w-[40px] h-[40px]',
138
+ [StepIndicatorSize.LG]: 'w-[36px] h-[36px]',
139
+ [StepIndicatorSize.MD]: 'w-[32px] h-[32px]',
140
+ [StepIndicatorSize.SM]: 'w-[28px] h-[28px]',
141
+ [StepIndicatorSize.XS]: 'w-[24px] h-[24px]',
142
+ }
143
+ return variant[props.size as StepIndicatorSize] || 'w-[40px] h-[40px]'
144
+ })
145
+
146
+ const containerBorderClass = computed(() => {
147
+ const variant = {
148
+ [StepStatus.NONE]: 'border-none',
149
+ [StepStatus.INACTIVE]: 'border-border-default',
150
+ [StepStatus.CURRENT]: 'border-border-primary-brand-active',
151
+ [StepStatus.COMPLETED]: 'border-border-primary-brand-active',
152
+ }
153
+
154
+ let base = variant[props.status as StepStatus] || 'border-border-default'
155
+
156
+ if (props.isHovered && props.status !== StepStatus.COMPLETED) {
157
+ base = 'border-border-primary-brand-hover'
158
+ }
159
+
160
+ return base
161
+ })
162
+
163
+ const iconSize = computed(() => {
164
+ const variant = {
165
+ [StepIndicatorSize.XL]: '24',
166
+ [StepIndicatorSize.LG]: '20',
167
+ [StepIndicatorSize.MD]: '20',
168
+ [StepIndicatorSize.SM]: '16',
169
+ [StepIndicatorSize.XS]: '16',
170
+ }
171
+ return variant[props.size as StepIndicatorSize] || '24'
172
+ })
173
+
174
+ const stepNumberSizeClass = computed(() => {
175
+ const variant = {
176
+ [StepIndicatorSize.XL]: 'text-sm',
177
+ [StepIndicatorSize.LG]: 'text-xs',
178
+ [StepIndicatorSize.MD]: 'text-xs',
179
+ [StepIndicatorSize.SM]: 'text-xs',
180
+ [StepIndicatorSize.XS]: 'text-xs',
181
+ }
182
+ return variant[props.size as StepIndicatorSize] || 'text-sm'
183
+ })
184
+
185
+ const textColorClass = computed(() => {
186
+ const variant = {
187
+ [StepStatus.NONE]: 'text-text-default',
188
+ [StepStatus.INACTIVE]: 'text-text-neutral-inactive',
189
+ [StepStatus.CURRENT]: 'text-text-primary-brand-active',
190
+ [StepStatus.COMPLETED]: 'text-text-default',
191
+ }
192
+
193
+ let base = variant[props.status as StepStatus] || 'text-text-neutral-inactive'
194
+
195
+ if (props.isHovered && props.status !== StepStatus.COMPLETED) {
196
+ base = 'text-text-primary-brand-hover'
197
+ }
198
+
199
+ return base
200
+ })
201
+
202
+ const circleColorClass = computed(() => {
203
+ const variant = {
204
+ [StepStatus.NONE]: 'bg-background-neutral-bold',
205
+ [StepStatus.INACTIVE]: 'bg-transparent',
206
+ [StepStatus.CURRENT]: 'bg-primary-brand-active',
207
+ [StepStatus.COMPLETED]: 'bg-transparent',
208
+ }
209
+
210
+ let base = variant[props.status as StepStatus] || 'bg-transparent'
211
+
212
+ if (props.isHovered && props.status !== StepStatus.COMPLETED) {
213
+ base = 'bg-text-primary-brand-hover'
214
+ }
215
+
216
+ return base
217
+ })
218
+
219
+ const containerBackgroundClass = computed(() => {
220
+ const variant = {
221
+ [StepStatus.NONE]: 'bg-background-neutral-subtle',
222
+ [StepStatus.INACTIVE]: undefined,
223
+ [StepStatus.CURRENT]: undefined,
224
+ [StepStatus.COMPLETED]: 'bg-background-primary-brand-default',
225
+ }
226
+ return variant[props.status as StepStatus] || undefined
227
+ })
228
+ </script>
@@ -0,0 +1,126 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex gap-4 flex-col sm:flex-row',
5
+ 'border border-border-default',
6
+ 'rounded',
7
+ 'py-4 sm:py-0',
8
+ isFullWidth ? 'w-full' : 'w-fit',
9
+ hasDivider && justifySteps && 'justify-between'
10
+ ]"
11
+ >
12
+ <template v-for="(item, index) in stepItems" :key="index">
13
+ <Step
14
+ :title="item.title"
15
+ :description="item.description"
16
+ :type
17
+ :status="item.status"
18
+ :size="size"
19
+ :step="index + 1"
20
+ :stepIcon="item.icon"
21
+ :completedIcon
22
+ :isInteractive
23
+ :class="[
24
+ '!py-0 sm:!py-4 px-6',
25
+ justifySteps && 'grow',
26
+ ]"
27
+ @click="handleStepClick(index)"
28
+ />
29
+
30
+ <!-- Chevron divider -->
31
+ <div
32
+ v-if="hasDivider && index < stepItems.length - 1"
33
+ class="lg:flex justify-center items-center hidden"
34
+ >
35
+ <svg
36
+ width="22"
37
+ height="74"
38
+ viewBox="0 0 22 74"
39
+ fill="none"
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ class="text-border-default"
42
+ >
43
+ <path stroke="currentColor" d="M1 1L21 37L1 73" />
44
+ </svg>
45
+ </div>
46
+ </template>
47
+ </div>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ // Imports
52
+ const emit = defineEmits(['update:modelValue'])
53
+
54
+ // Props
55
+ const props = defineProps({
56
+ modelValue: {
57
+ type: Number as PropType<number>,
58
+ default: 1,
59
+ },
60
+ steps: {
61
+ type: Array as PropType<TabStep[]>,
62
+ default: () => [
63
+ {
64
+ title: 'Step 1',
65
+ description: 'Description one',
66
+ },
67
+ {
68
+ title: 'Step 2',
69
+ description: 'Description two',
70
+ },
71
+ {
72
+ title: 'Step 3',
73
+ description: 'Description three',
74
+ },
75
+ ],
76
+ },
77
+ type: String as PropType<StepIndicatorType>,
78
+ size: String as PropType<StepIndicatorSize>,
79
+ completedIcon: String as PropType<any>,
80
+ isInteractive: {
81
+ type: Boolean as PropType<boolean>,
82
+ default: false,
83
+ },
84
+ hasDivider: {
85
+ type: Boolean as PropType<boolean>,
86
+ default: true,
87
+ },
88
+ justifySteps: {
89
+ type: Boolean as PropType<boolean>,
90
+ default: false,
91
+ },
92
+ isFullWidth: {
93
+ type: Boolean as PropType<boolean>,
94
+ default: true,
95
+ }
96
+ })
97
+
98
+ // Computed step status
99
+ const stepItems = computed(() => {
100
+ const activeIndex = props.modelValue > 0 ? props.modelValue - 1 : 0
101
+
102
+ return props.steps.map((step, index) => {
103
+ let status: StepStatus
104
+
105
+ if (index < activeIndex) {
106
+ status = StepStatus.COMPLETED
107
+ } else if (index === activeIndex) {
108
+ status = StepStatus.CURRENT
109
+ } else {
110
+ status = StepStatus.INACTIVE
111
+ }
112
+
113
+ return {
114
+ ...step,
115
+ status,
116
+ }
117
+ })
118
+ })
119
+
120
+ // Handlers
121
+ const handleStepClick = (index: number) => {
122
+ if (props.isInteractive) {
123
+ emit('update:modelValue', index + 1)
124
+ }
125
+ }
126
+ </script>
@@ -0,0 +1,223 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'relative',
5
+ 'flex',
6
+ leftSpacingClass
7
+ ]"
8
+ >
9
+ <!-- Circle + vertical line -->
10
+ <div
11
+ v-if="hasStepper"
12
+ :class="[
13
+ 'absolute',
14
+ 'left-0',
15
+ 'top-0',
16
+ 'h-full',
17
+ 'flex',
18
+ 'flex-col',
19
+ 'items-center',
20
+ ]"
21
+ >
22
+ <!-- Step circle -->
23
+ <StepIndicator
24
+ :type="stepType"
25
+ :size="stepSize"
26
+ :status="stepStatus"
27
+ :stepIcon
28
+ :completedIcon="stepCompletedIcon"
29
+ :step
30
+ :isHovered
31
+ class="z-10"
32
+ />
33
+
34
+ <!-- Vertical line -->
35
+ <div
36
+ v-if="showLine && !isLast"
37
+ :class="[
38
+ stepStatus === StepStatus.COMPLETED ? 'w-[2px] bg-background-primary-brand-default' : 'w-[1px] bg-border-default',
39
+ 'flex-1',
40
+ 'rounded-md',
41
+ stepIndicatorGap,
42
+ ]"
43
+ />
44
+ </div>
45
+
46
+ <!-- Step content -->
47
+ <div
48
+ :class="[
49
+ 'ml-2',
50
+ 'w-full',
51
+ 'flex',
52
+ 'flex-col',
53
+ 'gap-3',
54
+ !isLast && 'mb-7',
55
+ ]"
56
+ >
57
+ <!-- Step header -->
58
+ <div
59
+ :class="[
60
+ 'w-full',
61
+ 'flex',
62
+ 'flex-col',
63
+ titleHeaderClass,
64
+ ]"
65
+ >
66
+ <div
67
+ v-if="metaTitle"
68
+ :class="[
69
+ metaTitleSizeClass,
70
+ 'text-text-neutral-subtle',
71
+ ]"
72
+ >
73
+ {{ metaTitle }}
74
+ </div>
75
+ <h3
76
+ :class="[
77
+ titleSizeClass,
78
+ 'font-bold',
79
+ 'text-text-default',
80
+ ]"
81
+ >
82
+ {{ title }}
83
+ </h3>
84
+ </div>
85
+
86
+ <!-- Can use description or slot as content -->
87
+ <p
88
+ v-if="description"
89
+ :class="contentTextSizeClass"
90
+ >
91
+ {{ description }}
92
+ </p>
93
+
94
+ <div
95
+ v-else
96
+ :class="contentTextSizeClass"
97
+ >
98
+ <slot />
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </template>
103
+
104
+ <script setup lang="ts">
105
+ // Props
106
+ const props = defineProps({
107
+ title: {
108
+ type: String as PropType<string>,
109
+ default: 'Step title',
110
+ },
111
+ metaTitle: String as PropType<string>,
112
+ description: String as PropType<string>,
113
+ step: {
114
+ type: Number as PropType<number>,
115
+ default: 1,
116
+ },
117
+ stepType: String as PropType<StepIndicatorType>,
118
+ stepStatus: {
119
+ type: String as PropType<StepStatus>,
120
+ default: StepStatus.NONE,
121
+ validator: (value: StepStatus) => Object.values(StepStatus).includes(value),
122
+ },
123
+ stepSize: {
124
+ type: String as PropType<StepIndicatorSize>,
125
+ default: StepIndicatorSize.SM,
126
+ validator: (value: StepIndicatorSize) => Object.values(StepIndicatorSize).includes(value),
127
+ },
128
+ stepIcon: String as PropType<any>,
129
+ stepCompletedIcon: String as PropType<any>,
130
+ isLast: {
131
+ type: Boolean as PropType<boolean>,
132
+ default: false,
133
+ },
134
+ hasStepper: {
135
+ type: Boolean as PropType<boolean>,
136
+ default: true,
137
+ },
138
+ hasStepperGap: {
139
+ type: Boolean as PropType<boolean>,
140
+ default: true,
141
+ },
142
+ showLine: {
143
+ type: Boolean as PropType<boolean>,
144
+ default: true,
145
+ },
146
+ isHovered: Boolean as PropType<boolean>,
147
+ })
148
+
149
+ // Computed classes
150
+ const titleHeaderClass = computed(() => {
151
+ // If using metaTitle, the fixing top margin is not necessary
152
+ if(props.metaTitle) return undefined
153
+
154
+ const variant = {
155
+ [StepIndicatorSize.XL]: 'mt-1.5',
156
+ [StepIndicatorSize.LG]: 'mt-1',
157
+ [StepIndicatorSize.MD]: undefined,
158
+ [StepIndicatorSize.SM]: undefined,
159
+ [StepIndicatorSize.XS]: undefined,
160
+ }
161
+ return variant[props.stepSize as StepIndicatorSize] || undefined
162
+ })
163
+
164
+ const titleSizeClass = computed(() => {
165
+ const variant = {
166
+ [StepIndicatorSize.XL]: 'text-lg',
167
+ [StepIndicatorSize.LG]: 'text-lg',
168
+ [StepIndicatorSize.MD]: 'text-lg',
169
+ [StepIndicatorSize.SM]: 'text-lg',
170
+ [StepIndicatorSize.XS]: 'text-base',
171
+ }
172
+ return variant[props.stepSize as StepIndicatorSize] || 'text-lg'
173
+ })
174
+
175
+ const metaTitleSizeClass = computed(() => {
176
+ const variant = {
177
+ [StepIndicatorSize.XL]: 'text-sm',
178
+ [StepIndicatorSize.LG]: 'text-sm',
179
+ [StepIndicatorSize.MD]: 'text-sm',
180
+ [StepIndicatorSize.SM]: 'text-sm',
181
+ [StepIndicatorSize.XS]: 'text-xs',
182
+ }
183
+ return variant[props.stepSize as StepIndicatorSize] || 'text-sm'
184
+ })
185
+
186
+ const contentTextSizeClass = computed(() => {
187
+ const variant = {
188
+ [StepIndicatorSize.XL]: 'text-base',
189
+ [StepIndicatorSize.LG]: 'text-base',
190
+ [StepIndicatorSize.MD]: 'text-sm',
191
+ [StepIndicatorSize.SM]: 'text-sm',
192
+ [StepIndicatorSize.XS]: 'text-sm',
193
+ }
194
+ return variant[props.stepSize as StepIndicatorSize] || 'text-sm'
195
+ })
196
+
197
+ const leftSpacingClass = computed(() => {
198
+ if(!props.hasStepper) return undefined
199
+
200
+ const variant = {
201
+ [StepIndicatorSize.XL]: 'pl-14',
202
+ [StepIndicatorSize.LG]: 'pl-12',
203
+ [StepIndicatorSize.MD]: 'pl-10',
204
+ [StepIndicatorSize.SM]: 'pl-8',
205
+ [StepIndicatorSize.XS]: 'pl-6',
206
+ }
207
+ return variant[props.stepSize as StepIndicatorSize] || 'pl-8'
208
+ })
209
+
210
+ const stepIndicatorGap = computed(() => {
211
+ // Only apply gap if it has to
212
+ if(!props.hasStepperGap) return undefined
213
+
214
+ const variant = {
215
+ [StepIndicatorSize.XL]: 'my-2',
216
+ [StepIndicatorSize.LG]: 'my-1.5',
217
+ [StepIndicatorSize.MD]: 'my-1',
218
+ [StepIndicatorSize.SM]: 'my-0.5',
219
+ [StepIndicatorSize.XS]: 'my-0.5',
220
+ }
221
+ return variant[props.stepSize as StepIndicatorSize] || 'my-0.5'
222
+ })
223
+ </script>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'relative',
5
+ 'flex',
6
+ 'flex-col',
7
+ ]"
8
+ >
9
+ <template v-if="items && items.length && !$slots.default">
10
+ <VerticalStep
11
+ v-for="(step, index) in items"
12
+ :key="`vertical-stepper-item-${index + 1}`"
13
+ :title="step.title"
14
+ :metaTitle="step.metaTitle"
15
+ :description="step.description"
16
+ :stepSize
17
+ :step="index + 1"
18
+ :stepIcon="step.stepIcon"
19
+ :stepType
20
+ :stepCompletedIcon
21
+ :stepStatus="step.stepStatus"
22
+ :isLast="index === items.length - 1"
23
+ :isHovered="isStepInteractive && hoveredIndex === index"
24
+ :hasStepper
25
+ :hasStepperGap
26
+ :showLine
27
+ @mouseenter="() => isStepInteractive && (hoveredIndex = index)"
28
+ @mouseleave="() => isStepInteractive && (hoveredIndex = null)"
29
+ />
30
+ </template>
31
+
32
+ <slot />
33
+ </div>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ // Props
38
+ defineProps({
39
+ items: Array as PropType<VerticalStepperItem[]>,
40
+ stepType: String as PropType<StepIndicatorType>,
41
+ stepSize: String as PropType<StepIndicatorSize>,
42
+ stepCompletedIcon: String as PropType<any>,
43
+ hasStepper: {
44
+ type: Boolean as PropType<boolean>,
45
+ default: true,
46
+ },
47
+ hasStepperGap: {
48
+ type: Boolean as PropType<boolean>,
49
+ default: true,
50
+ },
51
+ showLine: {
52
+ type: Boolean as PropType<boolean>,
53
+ default: true,
54
+ },
55
+ isStepInteractive: {
56
+ type: Boolean as PropType<boolean>,
57
+ default: false,
58
+ },
59
+ })
60
+
61
+ // Status
62
+ const hoveredIndex = ref<number | null>(null)
63
+ </script>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'flex-col',
6
+ 'gap-4',
7
+ 'w-full',
8
+ hasOverflow && 'overflow-x-auto',
9
+ 'pb-3',
10
+ ]"
11
+ >
12
+ <table class="table-auto w-full border-collapse">
13
+ <slot />
14
+ </table>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup>
19
+ // Props
20
+ defineProps({
21
+ hasOverflow: {
22
+ type: Boolean,
23
+ default: true,
24
+ },
25
+ })
26
+ </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <tbody>
3
+ <slot />
4
+ </tbody>
5
+ </template>