@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,155 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'flex-wrap gap-2',
6
+ ]"
7
+ >
8
+ <OptionButton
9
+ v-for="(button, index) in displayButtons"
10
+ :key="index"
11
+ :active="isButtonActive(button)"
12
+ :text="button.text"
13
+ :size
14
+ :isRounded
15
+ :icon="button.icon"
16
+ :iconPosition="button.iconPosition"
17
+ :disabled="button.disabled"
18
+ :styleType="getButtonStyleType(button)"
19
+ :class="[isButtonActive(button) && 'border border-border-primary-brand-active']"
20
+ @click="handleButtonClick(button)"
21
+ />
22
+ </div>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ const props = defineProps({
27
+ modelValue: {
28
+ type: [String, Number, Array] as PropType<string | number | string[]>,
29
+ default: 'button-1',
30
+ },
31
+ buttons: {
32
+ type: Array as PropType<OptionButton[]>,
33
+ default: () => [
34
+ {
35
+ text: 'Button 1',
36
+ value: 'button-1',
37
+ action: () => {},
38
+ },
39
+ {
40
+ text: 'Button 2',
41
+ value: 'button-2',
42
+ action: () => {},
43
+ },
44
+ ],
45
+ },
46
+ styleType: {
47
+ type: String as PropType<
48
+ ButtonStyleType.NEUTRAL_OUTLINED | ButtonStyleType.PRIMARY_BRAND_SOFT
49
+ >,
50
+ default: ButtonStyleType.NEUTRAL_OUTLINED,
51
+ validator: (value: ButtonStyleType.NEUTRAL_OUTLINED | ButtonStyleType.PRIMARY_BRAND_SOFT) =>
52
+ [ButtonStyleType.NEUTRAL_OUTLINED, ButtonStyleType.PRIMARY_BRAND_SOFT].includes(value),
53
+ },
54
+ size: String as PropType<ButtonSize>,
55
+ isRounded: Boolean as PropType<boolean>,
56
+ isMultiple: {
57
+ type: Boolean as PropType<boolean>,
58
+ default: false,
59
+ },
60
+ hasAllButton: {
61
+ type: Boolean as PropType<boolean>,
62
+ default: false,
63
+ },
64
+ allButtonText: {
65
+ type: String as PropType<string>,
66
+ default: 'All',
67
+ },
68
+ allButtonValue: {
69
+ type: String as PropType<string>,
70
+ default: 'all',
71
+ },
72
+ })
73
+
74
+ // Emits
75
+ const emit = defineEmits(['update:modelValue'])
76
+
77
+ // Create the dynamic "All" button using user props
78
+ const allButton = computed<OptionButton>(() => ({
79
+ text: props.allButtonText,
80
+ value: props.allButtonValue,
81
+ size: props.size,
82
+ action: () => {},
83
+ }))
84
+
85
+ // Computed list of buttons
86
+ const displayButtons = computed(() => {
87
+ return props.hasAllButton
88
+ ? [allButton.value, ...props.buttons]
89
+ : props.buttons
90
+ })
91
+
92
+ // Active state logic
93
+ const isButtonActive = (button: OptionButton) => {
94
+ if (props.isMultiple) {
95
+ if (button.value === props.allButtonValue) {
96
+ if (!Array.isArray(props.modelValue)) return false
97
+ const selected = props.modelValue
98
+ return selected.length === 0 || selected.length === props.buttons.length
99
+ }
100
+
101
+ if (Array.isArray(props.modelValue)) {
102
+ return props.modelValue.includes(button.value)
103
+ }
104
+
105
+ return false
106
+ }
107
+
108
+ return button.value === props.modelValue
109
+ }
110
+
111
+
112
+ // Handlers
113
+ const getButtonStyleType = (button: OptionButton) => {
114
+ if(props.styleType === ButtonStyleType.PRIMARY_BRAND_SOFT) {
115
+ return isButtonActive(button)
116
+ ? ButtonStyleType.PRIMARY_BRAND_FILLED
117
+ : ButtonStyleType.PRIMARY_BRAND_SOFT
118
+ }
119
+
120
+ // Default style for outlined
121
+ return isButtonActive(button)
122
+ ? ButtonStyleType.PRIMARY_BRAND_SOFT
123
+ : ButtonStyleType.NEUTRAL_OUTLINED
124
+ }
125
+
126
+ const handleButtonClick = (button: OptionButton) => {
127
+ if (button?.action) {
128
+ button.action()
129
+ }
130
+
131
+ const isAllButton = button.value === props.allButtonValue
132
+
133
+ // Multiple select with "All" logic
134
+ if (props.isMultiple) {
135
+ if (isAllButton) {
136
+ const allValues = props.buttons.map(b => b.value)
137
+ const selected = props.modelValue as string[]
138
+ const isAllSelected = selected.length === allValues.length
139
+ emit('update:modelValue', isAllSelected ? [] : allValues)
140
+ return
141
+ }
142
+
143
+ if (Array.isArray(props.modelValue)) {
144
+ const isActive = props.modelValue.includes(button.value)
145
+ const updated = isActive
146
+ ? props.modelValue.filter(v => v !== button.value)
147
+ : [...props.modelValue, button.value]
148
+ emit('update:modelValue', updated)
149
+ }
150
+ } else {
151
+ // Single-select: treat all buttons the same
152
+ emit('update:modelValue', button.value)
153
+ }
154
+ }
155
+ </script>
@@ -0,0 +1,154 @@
1
+ <template>
2
+ <div class="relative overflow-hidden">
3
+ <!-- Sombras difuminadas laterales -->
4
+ <div class="pointer-events-none absolute inset-y-0 left-0 w-8 bg-gradient-to-r from-white/90 to-transparent z-10"></div>
5
+ <div class="pointer-events-none absolute inset-y-0 right-0 w-8 bg-gradient-to-l from-white/90 to-transparent z-10"></div>
6
+
7
+ <!-- Carrusel de botones -->
8
+ <div
9
+ ref="carousel"
10
+ class="flex gap-3 overflow-x-auto no-scrollbar px-2 py-1 cursor-grab active:cursor-grabbing select-none"
11
+ @mousedown="startDrag"
12
+ @mousemove="onDrag"
13
+ @mouseup="stopDrag"
14
+ @mouseleave="stopDrag"
15
+ >
16
+ <OptionButton
17
+ v-for="(button, index) in displayButtons"
18
+ :key="index"
19
+ :active="isButtonActive(button)"
20
+ :text="button.text"
21
+ :size="button.size"
22
+ :icon="button.icon"
23
+ :iconPosition="button.iconPosition"
24
+ :disabled="button.disabled"
25
+ :styleType="getButtonStyleType(button)"
26
+ @click="handleButtonClick(button)"
27
+ />
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { ref, computed } from 'vue'
34
+
35
+ const props = defineProps({
36
+ modelValue: {
37
+ type: [String, Array] as PropType<string | string[]>,
38
+ default: 'button-1',
39
+ },
40
+ buttons: {
41
+ type: Array as PropType<OptionButton[]>,
42
+ default: () => [],
43
+ },
44
+ styleType: String as PropType<ButtonStyleType>,
45
+ size: String as PropType<ButtonSize>,
46
+ isMultiple: {
47
+ type: Boolean,
48
+ default: false,
49
+ },
50
+ hasAllButton: {
51
+ type: Boolean,
52
+ default: false,
53
+ },
54
+ allButtonText: {
55
+ type: String,
56
+ default: 'All',
57
+ },
58
+ allButtonValue: {
59
+ type: String,
60
+ default: 'all',
61
+ },
62
+ })
63
+
64
+ const emit = defineEmits(['update:modelValue'])
65
+
66
+ const allButton = computed<OptionButton>(() => ({
67
+ text: props.allButtonText,
68
+ value: props.allButtonValue,
69
+ size: props.size,
70
+ action: () => {},
71
+ }))
72
+
73
+ const displayButtons = computed(() => {
74
+ return props.hasAllButton ? [allButton.value, ...props.buttons] : props.buttons
75
+ })
76
+
77
+ const isButtonActive = (button: OptionButton) => {
78
+ if (props.isMultiple) {
79
+ if (button.value === props.allButtonValue) {
80
+ if (!Array.isArray(props.modelValue)) return false
81
+ return (
82
+ props.modelValue.length === 0 ||
83
+ props.modelValue.length === props.buttons.length
84
+ )
85
+ }
86
+
87
+ return Array.isArray(props.modelValue) && props.modelValue.includes(button.value)
88
+ }
89
+
90
+ return button.value === props.modelValue
91
+ }
92
+
93
+ // NUEVO: Determinar el estilo del botón según si está activo
94
+ const getButtonStyleType = (button: OptionButton) => {
95
+ return isButtonActive(button)
96
+ ? ButtonStyleType.PRIMARY_BRAND_FILLED
97
+ : ButtonStyleType.PRIMARY_BRAND_SOFT
98
+ }
99
+
100
+ const handleButtonClick = (button: OptionButton) => {
101
+ button?.action?.()
102
+
103
+ if (props.isMultiple) {
104
+ if (button.value === props.allButtonValue) {
105
+ const allValues = props.buttons.map(b => b.value)
106
+ const selected = props.modelValue as string[]
107
+ const isAllSelected = selected.length === allValues.length
108
+ emit('update:modelValue', isAllSelected ? [] : allValues)
109
+ } else if (Array.isArray(props.modelValue)) {
110
+ const isActive = props.modelValue.includes(button.value)
111
+ const updated = isActive
112
+ ? props.modelValue.filter(v => v !== button.value)
113
+ : [...props.modelValue, button.value]
114
+ emit('update:modelValue', updated)
115
+ }
116
+ } else {
117
+ emit('update:modelValue', button.value)
118
+ }
119
+ }
120
+
121
+ const carousel = ref<HTMLElement | null>(null)
122
+ let isDragging = false
123
+ let startX = 0
124
+ let scrollLeft = 0
125
+
126
+ const startDrag = (e: MouseEvent) => {
127
+ if (!carousel.value) return
128
+ isDragging = true
129
+ startX = e.pageX - carousel.value.offsetLeft
130
+ scrollLeft = carousel.value.scrollLeft
131
+ }
132
+
133
+ const onDrag = (e: MouseEvent) => {
134
+ if (!isDragging || !carousel.value) return
135
+ const x = e.pageX - carousel.value.offsetLeft
136
+ const walk = (x - startX) * 1.5
137
+ carousel.value.scrollLeft = scrollLeft - walk
138
+ }
139
+
140
+ const stopDrag = () => {
141
+ isDragging = false
142
+ }
143
+ </script>
144
+
145
+ <style scoped>
146
+ /* Ocultar scroll nativo */
147
+ .no-scrollbar::-webkit-scrollbar {
148
+ display: none;
149
+ }
150
+ .no-scrollbar {
151
+ -ms-overflow-style: none;
152
+ scrollbar-width: none;
153
+ }
154
+ </style>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :disabled="disabled"
5
+ :class="[
6
+ 'flex items-center justify-center',
7
+ 'aspect-square',
8
+ 'text-nowrap',
9
+ ...buttonStyleClass,
10
+ buttonSizeClass,
11
+ horizontalPaddingClass,
12
+ gapClass,
13
+ disabled ? 'opacity-disabled cursor-not-allowed' : '',
14
+ 'self-start',
15
+ ]"
16
+ @click="emitClick"
17
+ >
18
+ <!-- Left icon -->
19
+ <MdiIcon
20
+ v-if="iconPosition === IconPosition.LEFT"
21
+ :icon="icon"
22
+ preserveAspectRatio="xMidYMid meet"
23
+ :size="iconSizeClass"
24
+ />
25
+ <span :class="['font-semibold', textSizeClass]">
26
+ {{ text }}
27
+ </span>
28
+ <!-- Right icon -->
29
+ <MdiIcon
30
+ v-if="iconPosition === IconPosition.RIGHT"
31
+ :icon="icon"
32
+ preserveAspectRatio="xMidYMid meet"
33
+ :size="iconSizeClass"
34
+ />
35
+ </button>
36
+ </template>
37
+ <script setup lang="ts">
38
+ // Props
39
+ const props = defineProps({
40
+ text: {
41
+ type: String as PropType<string>,
42
+ default: 'Button text',
43
+ },
44
+ size: {
45
+ type: String as PropType<ButtonSize>,
46
+ default: ButtonSize.LG,
47
+ validator: (value: ButtonSize) => Object.values(ButtonSize).includes(value),
48
+ },
49
+ icon: {
50
+ type: String as PropType<any>,
51
+ default: 'mdiHelp',
52
+ },
53
+ iconPosition: {
54
+ type: String as PropType<IconPosition>,
55
+ default: IconPosition.NONE,
56
+ validator: (value: IconPosition) => Object.values(IconPosition).includes(value),
57
+ },
58
+ disabled: {
59
+ type: Boolean as PropType<boolean>,
60
+ default: false,
61
+ },
62
+ active: {
63
+ type: Boolean as PropType<boolean>,
64
+ default: false,
65
+ },
66
+ })
67
+
68
+ // Emits
69
+ const emit = defineEmits(['click'])
70
+ const emitClick = () => {
71
+ if (!props.disabled) {
72
+ emit('click')
73
+ }
74
+ }
75
+
76
+ // Computed classes
77
+ const buttonStyleClass = computed(() =>
78
+ props.active
79
+ ? ['bg-background-primary-brand-subtle-active', 'text-text-primary-brand-on-soft-bg']
80
+ : ['text-text-neutral-inactive', 'hover:text-text-primary-brand-hover']
81
+ )
82
+
83
+ const buttonSizeClass = computed(() => {
84
+ const sizeVariant = {
85
+ [ButtonSize.XS]: 'h-[24px]',
86
+ [ButtonSize.SM]: 'h-[28px]',
87
+ [ButtonSize.MD]: 'h-[32px]',
88
+ [ButtonSize.LG]: 'h-[36px]',
89
+ [ButtonSize.XL]: 'h-[40px]',
90
+ [ButtonSize.XXL]: 'h-[48px]',
91
+ }
92
+ return sizeVariant[props.size as ButtonSize] || 'h-[36px]'
93
+ })
94
+
95
+ const iconSizeClass = computed(() => {
96
+ const sizeVariant = {
97
+ [ButtonSize.XS]: '16px',
98
+ [ButtonSize.SM]: '16px',
99
+ [ButtonSize.MD]: '16px',
100
+ [ButtonSize.LG]: '20px',
101
+ [ButtonSize.XL]: '20px',
102
+ [ButtonSize.XXL]: '24px',
103
+ }
104
+ return sizeVariant[props.size as ButtonSize] || '20px'
105
+ })
106
+
107
+ const textSizeClass = computed(() => {
108
+ const sizeVariant = {
109
+ [ButtonSize.XS]: 'text-xs',
110
+ [ButtonSize.SM]: 'text-sm',
111
+ [ButtonSize.MD]: 'text-sm',
112
+ [ButtonSize.LG]: 'text-sm',
113
+ [ButtonSize.XL]: 'text-sm',
114
+ [ButtonSize.XXL]: 'text-base',
115
+ }
116
+ return sizeVariant[props.size as ButtonSize] || 'text-sm'
117
+ })
118
+
119
+ const horizontalPaddingClass = computed(() => {
120
+ const sizeVariant = {
121
+ [ButtonSize.XS]: 'px-2',
122
+ [ButtonSize.SM]: 'px-2',
123
+ [ButtonSize.MD]: 'px-2.5',
124
+ [ButtonSize.LG]: 'px-3',
125
+ [ButtonSize.XL]: 'px-3.5',
126
+ [ButtonSize.XXL]: 'px-4',
127
+ }
128
+ return sizeVariant[props.size as ButtonSize] || 'px-3'
129
+ })
130
+
131
+ const gapClass = computed(() => {
132
+ const sizeVariant = {
133
+ [ButtonSize.XS]: 'gap-1',
134
+ [ButtonSize.SM]: 'gap-1.5',
135
+ [ButtonSize.MD]: 'gap-2',
136
+ [ButtonSize.LG]: 'gap-2',
137
+ [ButtonSize.XL]: 'gap-2',
138
+ [ButtonSize.XXL]: 'gap-2',
139
+ }
140
+ return sizeVariant[props.size as ButtonSize] || 'gap-2'
141
+ })
142
+ </script>
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ groupStyle === ToggleButtonGroupStyle.GROUPED ? 'border border-border-default overflow-hidden' : 'flex-wrap gap-3',
6
+ 'w-fit',
7
+ 'rounded-button'
8
+ ]"
9
+ >
10
+ <ToggleButton
11
+ v-for="(button, index) in buttons"
12
+ :key="index"
13
+ :active="button.value === modelValue"
14
+ :text="button.text"
15
+ :size="button.size"
16
+ :icon="button.icon"
17
+ :iconPosition="button.iconPosition"
18
+ :disabled="button.disabled"
19
+ :class="[
20
+ groupStyle === ToggleButtonGroupStyle.SEGMENTED && hasButtonBorder && 'border border-border-default rounded',
21
+ groupStyle === ToggleButtonGroupStyle.SEGMENTED && button.active && '!border-border-primary-brand-active',
22
+ !hasButtonBorder && 'rounded-button',
23
+ ]"
24
+ @click="handleButtonClick(button)"
25
+ />
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ // Props
31
+ defineProps({
32
+ modelValue: {
33
+ type: String as PropType<string>,
34
+ default: 'button-1',
35
+ },
36
+ buttons: {
37
+ type: Array as PropType<ToggleButton[]>,
38
+ default: () => [
39
+ {
40
+ text: 'Button 1',
41
+ value: 'button-1',
42
+ action: () => {},
43
+ },
44
+ {
45
+ text: 'Button 2',
46
+ value: 'button-2',
47
+ action: () => {},
48
+ },
49
+ ],
50
+ },
51
+ groupStyle: {
52
+ type: String as PropType<ToggleButtonGroupStyle>,
53
+ default: ToggleButtonGroupStyle.GROUPED,
54
+ validator: (value: ToggleButtonGroupStyle) => Object.values(ToggleButtonGroupStyle).includes(value),
55
+ },
56
+ hasButtonBorder: {
57
+ type: Boolean as PropType<boolean>,
58
+ default: true,
59
+ },
60
+ })
61
+
62
+ // Emits
63
+ const emit = defineEmits(['update:modelValue'])
64
+
65
+ // Handlers
66
+ const handleButtonClick = (button: ToggleButton) => {
67
+ if (button?.action) {
68
+ button.action()
69
+ }
70
+
71
+ emit('update:modelValue', button.value)
72
+ }
73
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex flex-col gap-5',
5
+ 'bg-background-container-surface',
6
+ 'p-4',
7
+ hasBorder ? 'border border-border-default' : undefined,
8
+ 'rounded-lg',
9
+ hasShadow ? 'shadow-sm' : undefined,
10
+ 'w-full',
11
+ hasBackgroundHover && 'hover:bg-background-neutral-subtlest/40',
12
+ ]"
13
+ >
14
+ <slot />
15
+ </div>
16
+ </template>
17
+ <script setup lang="ts">
18
+ // Props
19
+ defineProps({
20
+ hasShadow: {
21
+ type: Boolean as PropType<boolean>,
22
+ default: true,
23
+ },
24
+ hasBorder: {
25
+ type: Boolean as PropType<boolean>,
26
+ default: true,
27
+ },
28
+ hasBackgroundHover: {
29
+ type: Boolean as PropType<boolean>,
30
+ default: false,
31
+ },
32
+ })
33
+ </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="flex gap-2 flex-col md:flex-row">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="flex flex-col gap-4 w-full">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex flex-col gap-4 w-full pt-5',
5
+ hasBorder && 'border-t border-border-neutral-subtle'
6
+ ]"
7
+ >
8
+ <slot />
9
+ </div>
10
+ </template>
11
+ <script setup lang="ts">
12
+ // Props
13
+ defineProps({
14
+ hasBorder: {
15
+ type: Boolean as PropType<boolean>,
16
+ default: true
17
+ }
18
+ })
19
+
20
+ </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="flex justify-between gap-3 w-full">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <h3 class="font-semibold w-full">
3
+ {{ title }}
4
+ </h3>
5
+ </template>
6
+ <script setup lang="ts">
7
+ defineProps({
8
+ title: {
9
+ type: String as PropType<string>,
10
+ default: 'Card title'
11
+ }
12
+ })
13
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <Card class="lg:py-7 lg:px-8 bg-background-neutral-subtlest border-0">
3
+ <CardHeader>
4
+ <CardTitle :title="name"/>
5
+ </CardHeader>
6
+ <CardBody>
7
+ <div
8
+ v-if="email"
9
+ class="flex gap-2 items-center text-text-primary-brand-default"
10
+ >
11
+ <MdiIcon
12
+ icon="mdiAt"
13
+ size="20"
14
+ preserveAspectRatio="xMidYMid meet"
15
+ />
16
+ <span class="font-semibold text-sm">
17
+ {{ email }}
18
+ </span>
19
+ </div>
20
+
21
+ <div
22
+ v-if="phone"
23
+ class="flex gap-2 items-center text-text-neutral-subtle"
24
+ >
25
+ <MdiIcon
26
+ icon="mdiPhoneOutline"
27
+ size="20"
28
+ preserveAspectRatio="xMidYMid meet"
29
+ />
30
+ <span class="text-sm">
31
+ {{ phone }}
32
+ </span>
33
+ </div>
34
+ </CardBody>
35
+ </Card>
36
+ </template>
37
+ <script setup lang="ts">
38
+ // Props
39
+ defineProps({
40
+ name: {
41
+ type: String as PropType<string>,
42
+ default: 'John Doe'
43
+ },
44
+ email: String as PropType<string>,
45
+ phone: String as PropType<string>,
46
+ })
47
+ </script>