@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,83 @@
1
+ <template>
2
+ <div class="w-full hidden lg:block">
3
+ <DropdownMenu
4
+ class="border border-border-default"
5
+ positionClass="absolute bottom-[64px]"
6
+ >
7
+ <template #activator="{ onClick }">
8
+
9
+ <!-- User Wrapper -->
10
+ <div
11
+ :class="[
12
+ 'w-full min-h-[64px]',
13
+ 'py-3',
14
+ 'px-6',
15
+ 'hover:cursor-pointer',
16
+ 'group',
17
+ 'flex',
18
+ 'items-center',
19
+ 'bg-background-surface',
20
+ ]"
21
+ @click="() => onClick()"
22
+ >
23
+ <User
24
+ :displayName="userDisplayName"
25
+ isInteractive
26
+ :imgUrl= "userAvatarUrl"
27
+ />
28
+ </div>
29
+ </template>
30
+ <template #items="{ onClose }">
31
+ <DropdownMenuItem
32
+ v-for="item in menuItems" :key="item.text"
33
+ :text="item.text"
34
+ :actionType="item.actionType"
35
+ :to="item.to"
36
+ :type="item.type"
37
+ :icon="item.icon"
38
+ :imgUrl="item.imgUrl"
39
+ :alt="item.alt"
40
+ class="px-6 w-full"
41
+ @click="() => {
42
+ if(item.callback) {
43
+ item.callback()
44
+ }
45
+ onClose()
46
+ }"
47
+ />
48
+ </template>
49
+ </DropdownMenu>
50
+ </div>
51
+ </template>
52
+
53
+ <script setup lang="ts">
54
+ // Props
55
+ defineProps({
56
+ userDisplayName: {
57
+ type: String as PropType<string>,
58
+ default: 'Test user'
59
+ },
60
+ userAvatarUrl: String as PropType<string>,
61
+ menuItems: {
62
+ type: Array as PropType<DropdownMenuItem[]>,
63
+ default: () => [
64
+ {
65
+ text: 'Account settings',
66
+ icon: 'mdiAccountCogOutline',
67
+ },
68
+ {
69
+ text: 'Support',
70
+ icon: 'mdiLifebuoy',
71
+ to: '/',
72
+ },
73
+ {
74
+ text: 'Logout',
75
+ icon: 'mdiLogout',
76
+ actionType: DropdownActionType.ACTION,
77
+ type: DropdownItemType.DANGER_ICON,
78
+ callback: () => {},
79
+ },
80
+ ],
81
+ },
82
+ })
83
+ </script>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <!-- Nav Sidebar -->
3
+ <aside
4
+ ref="sidebarRef"
5
+ :style="stickOnScroll && { top: isSticky ? '0px' : `${stickyScrollHeight}px` }"
6
+ :class="[
7
+ isFixed && 'fixed',
8
+ 'w-[240px]',
9
+ 'h-screen',
10
+ 'bg-background-surface',
11
+ 'flex flex-col items-center gap-6',
12
+ 'py-4',
13
+ 'border-r border-border-default',
14
+ 'transition-transform duration-300',
15
+ isMobileSidebarOpen ? 'translate-x-0' : '-translate-x-full',
16
+ 'lg:translate-x-0' // Always visible on large screens
17
+ ]"
18
+ >
19
+ <!-- Only for small screen width -->
20
+ <ActionIconButton
21
+ v-if="hasCloseButton"
22
+ icon="mdiClose"
23
+ class="absolute top-3 right-3 sm:hidden"
24
+ :size="ButtonSize.SM"
25
+ @click="toggleMobileSidebar()"
26
+ />
27
+
28
+ <!-- Header -->
29
+ <slot name="sidebar-header" />
30
+
31
+ <!-- Menu -->
32
+
33
+ <NavSidebarMenu
34
+ :class="[!$slots['sidebar-footer'] && '80% lg:90%']"
35
+ :style="{
36
+ height: computedMenuHeight
37
+ }"
38
+ >
39
+ <slot name="sidebar-menu-prefix-content" />
40
+ <template
41
+ v-for="(item, index) in menuItems"
42
+ :key="index"
43
+ >
44
+ <NavSidebarMenuSectionTitle
45
+ v-if="item.isSectionTitle"
46
+ :text="item.text"
47
+ :icon="item.icon"
48
+ :styleType="itemsStyleType"
49
+ />
50
+ <NavSidebarMenuItem
51
+ v-else
52
+ :text="item.text"
53
+ :icon="item.icon"
54
+ :to="item.to"
55
+ :styleType="itemsStyleType"
56
+ :class="itemsCustomClass"
57
+ />
58
+ </template>
59
+ <slot name="sidebar-menu-suffix-content" />
60
+ </NavSidebarMenu>
61
+
62
+ <!-- Footer -->
63
+ <div
64
+ v-if="$slots['sidebar-footer']"
65
+ :class="[
66
+ 'w-full',
67
+ 'flex flex-col',
68
+ 'absolute',
69
+ ]"
70
+ :style="{bottom: `${footerHeight}px`}"
71
+ >
72
+ <slot name="sidebar-footer" />
73
+ </div>
74
+ </aside>
75
+ </template>
76
+ <script setup lang="ts">
77
+ // Props
78
+ const props = defineProps({
79
+ menuItems: {
80
+ type: Array as PropType<SidebarMenuItem[]>,
81
+ default: () => [
82
+ {
83
+ isSectionTitle: true,
84
+ text: 'Section title',
85
+ icon: 'mdiBullseye',
86
+ },
87
+ {
88
+ text: 'Item 1',
89
+ icon: 'mdiHelp',
90
+ to: '/',
91
+ },
92
+ {
93
+ text: 'Item 2',
94
+ icon: 'mdiHelp',
95
+ to: '/',
96
+ },
97
+ {
98
+ text: 'Item 3',
99
+ icon: 'mdiHelp',
100
+ to: '/',
101
+ },
102
+ ],
103
+
104
+ },
105
+ hasCloseButton: {
106
+ type: Boolean as PropType<boolean>,
107
+ default: false,
108
+ },
109
+ isFixed: {
110
+ type: Boolean as PropType<boolean>,
111
+ default: true,
112
+ },
113
+ stickOnScroll: {
114
+ type: Boolean as PropType<boolean>,
115
+ default: false,
116
+ },
117
+ stickyScrollHeight: {
118
+ type: Number as PropType<number>,
119
+ default: 0,
120
+ },
121
+ headerHeight: {
122
+ type: Number as PropType<number>,
123
+ default: 0,
124
+ },
125
+ footerHeight: Number as PropType<number>,
126
+ footerSafeAreaHeight: {
127
+ type: Number as PropType<number>,
128
+ default: 180,
129
+ },
130
+ itemsStyleType: String as PropType<SidebarNavMenuItemStyleType>,
131
+ itemsCustomClass: String as PropType<string>
132
+ })
133
+
134
+ // States
135
+ const sidebarRef = ref<HTMLElement | null>(null)
136
+ const isSticky = ref(false)
137
+
138
+ // Slots
139
+ const slots = defineSlots()
140
+
141
+ // Composables
142
+ const { isMobileSidebarOpen, toggleMobileSidebar } = useMobileSidebar()
143
+
144
+ // Methods
145
+ const handleScroll = () => {
146
+ if(props.stickOnScroll) {
147
+ isSticky.value = window.scrollY > props.stickyScrollHeight
148
+ }
149
+ }
150
+
151
+ // Dynamic height computation
152
+ const computedMenuHeight = computed(() => {
153
+ if (slots['sidebar-footer']) {
154
+ return `calc(100% - ${props.footerSafeAreaHeight + props.headerHeight}px)`
155
+ } else {
156
+ return `calc(100% - ${props.headerHeight}px)`
157
+ }
158
+ })
159
+
160
+ onMounted(() => {
161
+ if(props.stickOnScroll) {
162
+ window.addEventListener('scroll', handleScroll)
163
+ handleScroll()
164
+ }
165
+ })
166
+
167
+ onUnmounted(() => {
168
+ if(props.stickOnScroll) {
169
+ window.removeEventListener('scroll', handleScroll)
170
+ }
171
+ })
172
+ </script>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <nav
3
+ :class="[
4
+ 'flex',
5
+ 'flex-col',
6
+ 'gap-1',
7
+ 'w-full',
8
+ 'px-6',
9
+ 'overflow-y-auto',
10
+ ]"
11
+ >
12
+ <slot />
13
+ </nav>
14
+ </template>
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <NuxtLink
3
+ :to="to"
4
+ :class="[
5
+ 'flex',
6
+ 'items-center',
7
+ 'text-sm',
8
+ 'font-semibold',
9
+ 'rounded-lg',
10
+ 'hover:bg-background-neutral-hover',
11
+ spacingClass,
12
+ isActive && 'text-text-primary-brand-on-neutral-hover-bg bg-background-neutral-hover',
13
+ ]"
14
+ >
15
+ <MdiIcon
16
+ v-if="icon"
17
+ :icon
18
+ preserveAspectRatio="xMidYMid meet"
19
+ :class="['text-icon-neutral-subtler', iconSizeClass]"
20
+ />
21
+ <span :class="[!isActive && 'text-text-default']">
22
+ {{ text }}
23
+ </span>
24
+ </NuxtLink>
25
+ </template>
26
+ <script setup lang="ts">
27
+ // Props
28
+ const props = defineProps({
29
+ text: {
30
+ type: String as PropType<string>,
31
+ default: 'Item text'
32
+ },
33
+ icon: String as PropType<any>,
34
+ to: {
35
+ type: String as PropType<string>,
36
+ default: null
37
+ },
38
+ isActive: {
39
+ type: Boolean as PropType<boolean>,
40
+ default: false
41
+ },
42
+ styleType: {
43
+ type: String as PropType<SidebarNavMenuItemStyleType>,
44
+ default: SidebarNavMenuItemStyleType.COMPACT,
45
+ validator: (value: SidebarNavMenuItemStyleType) => Object.values(SidebarNavMenuItemStyleType).includes(value),
46
+ },
47
+ detectActive: {
48
+ type: Boolean,
49
+ default: true,
50
+ },
51
+ })
52
+
53
+ // Composables
54
+ const route = useRoute()
55
+
56
+ // Computed classes
57
+ const spacingClass = computed(() => {
58
+ const variant = {
59
+ [SidebarNavMenuItemStyleType.SPACED]: 'min-h-[40px] gap-3 py-2 pl-3 pr-2',
60
+ [SidebarNavMenuItemStyleType.COMPACT]: 'gap-2 py-1 pl-2 pr-1',
61
+ }
62
+ return variant[props.styleType as SidebarNavMenuItemStyleType] || 'min-h-[40px] gap-2 py-2 pl-3 pr-2'
63
+ })
64
+
65
+ const iconSizeClass = computed(() => {
66
+ const variant = {
67
+ [SidebarNavMenuItemStyleType.SPACED]: 'w-[20px] h-[20px] min-w-[20px] min-h-[20px]',
68
+ [SidebarNavMenuItemStyleType.COMPACT]: 'w-[16px] h-[16px] min-w-[16px] min-h-[16px]',
69
+ }
70
+ return variant[props.styleType as SidebarNavMenuItemStyleType] || 'w-[20px] h-[20px] min-w-[20px] min-h-[20px]'
71
+ })
72
+
73
+ const isActive = computed(() => {
74
+ return props.detectActive && route.path === props.to
75
+ })
76
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'flex',
5
+ 'items-center',
6
+ 'text-sm',
7
+ 'font-semibold',
8
+ 'text-text-neutral-subtle',
9
+ 'border-b',
10
+ 'border-border-neutral-subtle',
11
+ spacingClass,
12
+ ]"
13
+ >
14
+ <MdiIcon
15
+ v-if="icon"
16
+ :icon
17
+ preserveAspectRatio="xMidYMid meet"
18
+ :class="['text-icon-neutral-subtle', iconSizeClass]"
19
+ />
20
+ {{ text }}
21
+ </div>
22
+ </template>
23
+ <script setup lang="ts">
24
+ // Props
25
+ const props = defineProps({
26
+ text: {
27
+ type: String as PropType<string>,
28
+ default: 'Item text'
29
+ },
30
+ icon: String as PropType<any>,
31
+ styleType: {
32
+ type: String as PropType<SidebarNavMenuItemStyleType>,
33
+ default: SidebarNavMenuItemStyleType.COMPACT,
34
+ validator: (value: SidebarNavMenuItemStyleType) => Object.values(SidebarNavMenuItemStyleType).includes(value),
35
+ },
36
+ })
37
+
38
+ // Computed classes
39
+ const spacingClass = computed(() => {
40
+ const variant = {
41
+ [SidebarNavMenuItemStyleType.SPACED]: 'min-h-[40px] gap-3 pt-4 pb-2 px-3',
42
+ [SidebarNavMenuItemStyleType.COMPACT]: 'gap-2 pt-3 pb-1 px-2',
43
+ }
44
+ return variant[props.styleType as SidebarNavMenuItemStyleType] || 'min-h-[40px] gap-2 pt-4 pb-2 px-3'
45
+ })
46
+
47
+ const iconSizeClass = computed(() => {
48
+ const variant = {
49
+ [SidebarNavMenuItemStyleType.SPACED]: 'w-[20px] h-[20px] min-w-[20px] min-h-[20px]',
50
+ [SidebarNavMenuItemStyleType.COMPACT]: 'w-[16px] h-[16px] min-w-[16px] min-h-[16px]',
51
+ }
52
+ return variant[props.styleType as SidebarNavMenuItemStyleType] || 'w-[20px] h-[20px] min-w-[20px] min-h-[20px]'
53
+ })
54
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <nav
3
+ class="w-full flex flex-col gap-2 text-sm"
4
+ aria-label="Table of contents"
5
+ >
6
+ <span
7
+ v-if="title"
8
+ class="font-semibold text-text-default mb-4"
9
+ >
10
+ {{ title }}
11
+ </span>
12
+ <ul class="w-full flex flex-col gap-2">
13
+ <TableOfContentsItem
14
+ v-for="(link, index) in links"
15
+ :key="index"
16
+ :link="link"
17
+ :activeId
18
+ />
19
+ </ul>
20
+ </nav>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ // Props
25
+ defineProps({
26
+ title: String as PropType<string>,
27
+ links: {
28
+ type: Array as PropType<TOCLink[]>,
29
+ default: () => [],
30
+ },
31
+ })
32
+
33
+ // Composable
34
+ const { activeId } = useTableOfContents()
35
+ </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <li>
3
+ <a
4
+ :href="`#${link.id}`"
5
+ :class="[
6
+ 'hover:text-text-primary-brand-hover',
7
+ 'font-medium text-text-neutral-subtle',
8
+ link.depth !== 2 && 'pl-4',
9
+ activeId === link.id && 'text-text-primary-brand-active font-semibold',
10
+ ]"
11
+ >
12
+ {{ link.text }}
13
+ </a>
14
+ <ul
15
+ v-if="link.children?.length"
16
+ class="mt-1 space-y-1"
17
+ >
18
+ <TableOfContentsItem
19
+ v-for="(child, index) in link.children"
20
+ :key="index"
21
+ :link="child"
22
+ :activeId
23
+ />
24
+ </ul>
25
+ </li>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ // Props
30
+ defineProps({
31
+ link: {
32
+ type: Object as PropType<TOCLink>,
33
+ required: true,
34
+ },
35
+ activeId: {
36
+ type: String as PropType<string | null>,
37
+ default: null,
38
+ },
39
+ })
40
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <aside
3
+ :class="[
4
+ 'flex',
5
+ 'flex-col',
6
+ 'gap-3',
7
+ 'w-full',
8
+ 'max-w-[240px]',
9
+ 'pt-12',
10
+ 'pb-8',
11
+ 'px-content-side-padding-mobile md:px-content-side-padding',
12
+ 'bg-background-container-surface',
13
+ 'h-screen',
14
+ 'fixed right-0'
15
+ ]"
16
+ >
17
+ <TableOfContents
18
+ :title
19
+ :links
20
+ />
21
+ </aside>
22
+
23
+ </template>
24
+ <script setup lang="ts">
25
+ defineProps({
26
+ title: String as PropType<string>,
27
+ links: Array as PropType<TOCLink[]>,
28
+ })
29
+ </script>