@myelophone/nuxt 0.18.2

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 (259) hide show
  1. package/.devcontainer/Dockerfile +7 -0
  2. package/.devcontainer/devcontainer.json +21 -0
  3. package/.editorconfig +43 -0
  4. package/.github/workflows/package.yml +73 -0
  5. package/.nuxtignore +6 -0
  6. package/.releaserc.js +132 -0
  7. package/.yarn/releases/yarn-4.18.0.cjs +1000 -0
  8. package/.yarnrc.yml +8 -0
  9. package/CHANGELOG.md +141 -0
  10. package/LICENSE +77 -0
  11. package/README.md +3004 -0
  12. package/app/app.vue +455 -0
  13. package/app/assets/css/animation.css +81 -0
  14. package/app/assets/css/backgrounds.css +57 -0
  15. package/app/assets/css/base.css +182 -0
  16. package/app/assets/css/index.css +9 -0
  17. package/app/assets/css/links.css +12 -0
  18. package/app/assets/css/multimedia.css +102 -0
  19. package/app/assets/css/preloader.css +68 -0
  20. package/app/assets/css/print.css +40 -0
  21. package/app/assets/css/tailwind.css +20 -0
  22. package/app/assets/css/theme.css +29 -0
  23. package/app/components/consent/GoogleMap.vue +59 -0
  24. package/app/components/consent/Youtube.vue +27 -0
  25. package/app/components/cookie/Banner.vue +70 -0
  26. package/app/components/cookie/ConsentWrapper.vue +71 -0
  27. package/app/components/cookie/PrivacyPolicy.vue +179 -0
  28. package/app/components/cookie/SettingsModal.vue +387 -0
  29. package/app/components/grid/Col.vue +262 -0
  30. package/app/components/grid/Container.vue +236 -0
  31. package/app/components/grid/Row.vue +248 -0
  32. package/app/components/grid/Stack.vue +233 -0
  33. package/app/components/myelophone/Copyright.vue +5 -0
  34. package/app/components/myelophone/Welcome.vue +107 -0
  35. package/app/components/page/FullscreenPreloader.vue +177 -0
  36. package/app/components/page/Preloader.vue +18 -0
  37. package/app/components/page/ReadyLocalized.vue +19 -0
  38. package/app/components/ready/.gitkeep +0 -0
  39. package/app/components/safe/Email.vue +52 -0
  40. package/app/components/safe/ImgWithLoader.vue +180 -0
  41. package/app/components/safe/NuxtImg.vue +25 -0
  42. package/app/components/safe/NuxtPicture.vue +29 -0
  43. package/app/components/section/.gitkeep +0 -0
  44. package/app/components/seo/BrandInfo.vue +76 -0
  45. package/app/components/seo/ContentNoIndex.vue +35 -0
  46. package/app/components/seo/NoIndex.vue +22 -0
  47. package/app/components/seo/OrgInfo.vue +130 -0
  48. package/app/components/seo/ProfileInfo.vue +137 -0
  49. package/app/components/ui/Accordion.vue +163 -0
  50. package/app/components/ui/Alert.vue +179 -0
  51. package/app/components/ui/Avatar.vue +88 -0
  52. package/app/components/ui/Badge.vue +150 -0
  53. package/app/components/ui/Banner.vue +133 -0
  54. package/app/components/ui/Button.vue +175 -0
  55. package/app/components/ui/Card.vue +66 -0
  56. package/app/components/ui/Checkbox.vue +134 -0
  57. package/app/components/ui/Chip.vue +136 -0
  58. package/app/components/ui/CommandPalette.vue +434 -0
  59. package/app/components/ui/Console.vue +583 -0
  60. package/app/components/ui/CursorCreative.vue +148 -0
  61. package/app/components/ui/FeatureAccordion.vue +165 -0
  62. package/app/components/ui/FileUpload.vue +705 -0
  63. package/app/components/ui/GeoDependent.vue +72 -0
  64. package/app/components/ui/Heading.vue +51 -0
  65. package/app/components/ui/Icon.vue +42 -0
  66. package/app/components/ui/Icon8.vue +39 -0
  67. package/app/components/ui/Input.vue +88 -0
  68. package/app/components/ui/LangLink.vue +34 -0
  69. package/app/components/ui/LangVisible.vue +33 -0
  70. package/app/components/ui/LanguageSelect.vue +202 -0
  71. package/app/components/ui/LightBox.vue +155 -0
  72. package/app/components/ui/LocalTime.vue +170 -0
  73. package/app/components/ui/Modal.vue +161 -0
  74. package/app/components/ui/Popup.vue +200 -0
  75. package/app/components/ui/PostContent.server.vue +23 -0
  76. package/app/components/ui/ProtectedEmail.vue +52 -0
  77. package/app/components/ui/RelativeTime.vue +141 -0
  78. package/app/components/ui/ScheduledContent.vue +150 -0
  79. package/app/components/ui/ScrollToTop.client.vue +48 -0
  80. package/app/components/ui/SegmentedControl.vue +148 -0
  81. package/app/components/ui/SiteSearch.vue +1191 -0
  82. package/app/components/ui/Skeleton.vue +91 -0
  83. package/app/components/ui/SmartContrast.vue +74 -0
  84. package/app/components/ui/SnapContainer.vue +222 -0
  85. package/app/components/ui/SnapSection.vue +294 -0
  86. package/app/components/ui/SonnerStyle.vue +7 -0
  87. package/app/components/ui/SplitSection.vue +99 -0
  88. package/app/components/ui/Steps.vue +858 -0
  89. package/app/components/ui/StickyWrapper.vue +46 -0
  90. package/app/components/ui/Table.vue +251 -0
  91. package/app/components/ui/Tabs.vue +451 -0
  92. package/app/components/ui/TextColumn.vue +36 -0
  93. package/app/components/ui/Textarea.vue +251 -0
  94. package/app/components/ui/Toggler.vue +40 -0
  95. package/app/components/ui/Tooltip.vue +524 -0
  96. package/app/components/ui/TruncateText.vue +105 -0
  97. package/app/components/ui/ViewportSpacer.vue +72 -0
  98. package/app/components/view/AnimatedCounter.vue +121 -0
  99. package/app/components/view/AnnouncementBar.vue +104 -0
  100. package/app/components/view/AvatarGroup.vue +104 -0
  101. package/app/components/view/Breadcrumbs.vue +97 -0
  102. package/app/components/view/ContentWithToc.vue +152 -0
  103. package/app/components/view/Copyright.vue +68 -0
  104. package/app/components/view/CountdownTimer.vue +184 -0
  105. package/app/components/view/CreativeCTA.vue +143 -0
  106. package/app/components/view/CurrencySelect.vue +254 -0
  107. package/app/components/view/FeatureBoxGrid.vue +142 -0
  108. package/app/components/view/HorizontalMenu.vue +97 -0
  109. package/app/components/view/ImageCompare.vue +141 -0
  110. package/app/components/view/ImgSlider.vue +333 -0
  111. package/app/components/view/InfiniteMarquee.vue +147 -0
  112. package/app/components/view/InfoBar.vue +86 -0
  113. package/app/components/view/LogoGrid.vue +106 -0
  114. package/app/components/view/LogoSlider.vue +83 -0
  115. package/app/components/view/NotFound.vue +81 -0
  116. package/app/components/view/PartialBackground.vue +313 -0
  117. package/app/components/view/QuoteBig.vue +111 -0
  118. package/app/components/view/ResponsiveMenu.vue +219 -0
  119. package/app/components/view/ReviewsSlider.vue +230 -0
  120. package/app/components/view/SocialBar.vue +78 -0
  121. package/app/components/view/StickyHeadingContent.vue +158 -0
  122. package/app/components/view/TallyForm.vue +295 -0
  123. package/app/components/view/WarningOutdated.vue +113 -0
  124. package/app/composables/useApi.ts +88 -0
  125. package/app/composables/useAppSeo.ts +78 -0
  126. package/app/composables/useBreadcrumbs.ts +106 -0
  127. package/app/composables/useChangeUrl.ts +73 -0
  128. package/app/composables/useCommand.ts +28 -0
  129. package/app/composables/useCookieControl.ts +111 -0
  130. package/app/composables/useCookieModal.ts +9 -0
  131. package/app/composables/useDevice.ts +109 -0
  132. package/app/composables/useGeoFetch.ts +139 -0
  133. package/app/composables/useLoadScript.ts +36 -0
  134. package/app/composables/useModalState.ts +37 -0
  135. package/app/composables/useMultiLang.ts +278 -0
  136. package/app/composables/useNetwork.ts +49 -0
  137. package/app/composables/useNuxtVersion.ts +5 -0
  138. package/app/composables/usePiniaStore.ts +5 -0
  139. package/app/composables/usePopupControl.ts +57 -0
  140. package/app/composables/useQuery.ts +307 -0
  141. package/app/composables/useReadyPage.ts +182 -0
  142. package/app/composables/useSafeList.ts +90 -0
  143. package/app/composables/useScriptLoader.ts +221 -0
  144. package/app/composables/useSiteSearch.ts +32 -0
  145. package/app/composables/useSnapScroll.ts +24 -0
  146. package/app/composables/useStatic.ts +5 -0
  147. package/app/composables/useStorage.ts +252 -0
  148. package/app/composables/useStoreBroadcast.ts +146 -0
  149. package/app/composables/useThemeSync.ts +81 -0
  150. package/app/constants/layout.ts +109 -0
  151. package/app/constants/predefinedCookieScripts.ts +1222 -0
  152. package/app/constants/ui-events.ts +1 -0
  153. package/app/error.vue +169 -0
  154. package/app/layouts/default-blog.vue +5 -0
  155. package/app/layouts/default.vue +3 -0
  156. package/app/locales/en/commands.json +5 -0
  157. package/app/locales/en/cookies.json +182 -0
  158. package/app/locales/en/error.json +4 -0
  159. package/app/locales/en/interface.json +33 -0
  160. package/app/locales/en/myelophone.json +8 -0
  161. package/app/locales/en/outdated.json +5 -0
  162. package/app/locales/en/times.json +18 -0
  163. package/app/locales/pl/commands.json +5 -0
  164. package/app/locales/pl/cookies.json +182 -0
  165. package/app/locales/pl/error.json +4 -0
  166. package/app/locales/pl/interface.json +33 -0
  167. package/app/locales/pl/myelophone.json +8 -0
  168. package/app/locales/pl/outdated.json +5 -0
  169. package/app/locales/pl/times.json +18 -0
  170. package/app/locales/ru/commands.json +5 -0
  171. package/app/locales/ru/cookies.json +182 -0
  172. package/app/locales/ru/error.json +4 -0
  173. package/app/locales/ru/interface.json +33 -0
  174. package/app/locales/ru/myelophone.json +8 -0
  175. package/app/locales/ru/outdated.json +5 -0
  176. package/app/locales/ru/times.json +18 -0
  177. package/app/middleware/blog-layout.ts +28 -0
  178. package/app/middleware/i18n.global.ts +17 -0
  179. package/app/middleware/not-found-status.ts +11 -0
  180. package/app/modules/default-blog/index.ts +39 -0
  181. package/app/modules/default-blog/posts.ts +80 -0
  182. package/app/modules/default-sitemap/index.ts +116 -0
  183. package/app/modules/default-sitemap/runtime/sitemap.get.ts +42 -0
  184. package/app/modules/fullscreen-preloader/module.ts +43 -0
  185. package/app/modules/htaccess/.htaccess +38 -0
  186. package/app/modules/htaccess/index.ts +23 -0
  187. package/app/modules/i18n-shaker.mjs +221 -0
  188. package/app/modules/multi18n/module.ts +71 -0
  189. package/app/modules/myelophone-config/module.ts +241 -0
  190. package/app/pages/[...all].vue +9 -0
  191. package/app/pages/index.vue +3 -0
  192. package/app/pages/post/[...slug].vue +30 -0
  193. package/app/plugins/bfcache-refresh.client.ts +9 -0
  194. package/app/plugins/canonical.ts +30 -0
  195. package/app/plugins/csp-nonce.server.ts +41 -0
  196. package/app/plugins/multi18n.client.ts +21 -0
  197. package/app/plugins/multi18n.server.ts +15 -0
  198. package/app/plugins/nuxt-version.client.ts +4 -0
  199. package/app/plugins/restore.client.ts +21 -0
  200. package/app/plugins/reveal.ts +89 -0
  201. package/app/plugins/url-helper.client.ts +66 -0
  202. package/app/server/api/exchange-rates.get.ts +216 -0
  203. package/app/server/api/healthz.ts +1 -0
  204. package/app/server/api/logout.post.ts +6 -0
  205. package/app/server/middleware/normalize-url.ts +14 -0
  206. package/app/server/middleware/security-headers.ts +147 -0
  207. package/app/server/middleware/url-helper.ts +43 -0
  208. package/app/server/plugins/clean-html-spaces.ts +15 -0
  209. package/app/server/plugins/clean-styles.ts +94 -0
  210. package/app/server/plugins/compression.ts +122 -0
  211. package/app/server/plugins/css-preload.ts +26 -0
  212. package/app/server/plugins/early-hints.ts +31 -0
  213. package/app/server/plugins/force-noindex.ts +43 -0
  214. package/app/server/plugins/html-one-line.ts +17 -0
  215. package/app/server/plugins/inline-style.ts +7 -0
  216. package/app/server/routes/healthz.get.ts +1 -0
  217. package/app/server/tsconfig.json +3 -0
  218. package/app/stores/cart.ts +945 -0
  219. package/app/stores/commands.ts +43 -0
  220. package/app/stores/device.ts +156 -0
  221. package/app/stores/querySync.ts +5 -0
  222. package/app/stores/settings.ts +145 -0
  223. package/app/stores/user.ts +607 -0
  224. package/app/types/cookie.ts +89 -0
  225. package/app/types/myelophone.d.ts +87 -0
  226. package/app/types/site-search.ts +110 -0
  227. package/app/utils/asyncRefresh.ts +4 -0
  228. package/app/utils/blog.server.ts +142 -0
  229. package/app/utils/jsonLd.ts +56 -0
  230. package/app/utils/myelophoneConfig.ts +4 -0
  231. package/app/utils/sortBy.ts +18 -0
  232. package/app/utils/transliterate.ts +75 -0
  233. package/app/utils/ytVideo.ts +37 -0
  234. package/app/workers/site-search.worker.ts +661 -0
  235. package/biome.json +94 -0
  236. package/commitlint.config.js +3 -0
  237. package/nuxt.config.ts +539 -0
  238. package/package.json +101 -0
  239. package/playground/content/.gitkeep +0 -0
  240. package/playground/locales/.gitkeep +0 -0
  241. package/playground/myelophone.dev.ts +45 -0
  242. package/playground/nuxt.config.ts +105 -0
  243. package/playground/package.json +5 -0
  244. package/playground/pages/.gitkeep +0 -0
  245. package/playground/tsconfig.json +3 -0
  246. package/playwright.config.ts +23 -0
  247. package/public/browsers/chrome.svg +77 -0
  248. package/public/browsers/edge.svg +98 -0
  249. package/public/browsers/firefox.svg +300 -0
  250. package/public/favicon.ico +0 -0
  251. package/public/myelophone_eng.png +0 -0
  252. package/public/myelophone_eng_white.png +0 -0
  253. package/spa-loading-template.html +72 -0
  254. package/tests/home.spec.ts +9 -0
  255. package/tests/utils.ts +23 -0
  256. package/tsconfig.json +16 -0
  257. package/vite/plugins/clean-css.ts +190 -0
  258. package/vite/plugins/postcss-add-viewportunits.ts +33 -0
  259. package/vite/plugins/postcss-viewport-fallback.ts +49 -0
@@ -0,0 +1,451 @@
1
+ <script setup lang="ts">
2
+ import type { CSSProperties } from "vue";
3
+
4
+ type TabValue = string | number;
5
+ type TabsOrientation = "vertical" | "horizontal";
6
+ type TabsSide = "left" | "right" | "top" | "bottom";
7
+ type TabsAlign = "start" | "center" | "end";
8
+ type TabsWidth = "full" | "auto";
9
+
10
+ interface TabItem {
11
+ label: string;
12
+ value?: TabValue;
13
+ content?: string;
14
+ disabled?: boolean;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ interface TabsClasses {
19
+ root?: string;
20
+ list?: string;
21
+ tab?: string;
22
+ activeTab?: string;
23
+ panel?: string;
24
+ }
25
+
26
+ interface Props {
27
+ items: TabItem[];
28
+ modelValue?: TabValue;
29
+ defaultValue?: TabValue;
30
+ orientation?: TabsOrientation;
31
+ side?: TabsSide;
32
+ tabWidth?: TabsWidth;
33
+ align?: TabsAlign;
34
+ tabsWidth?: string;
35
+ loop?: boolean;
36
+ stackOnMobile?: boolean;
37
+ ariaLabel?: string;
38
+ ui?: TabsClasses;
39
+ }
40
+
41
+ const props = withDefaults(defineProps<Props>(), {
42
+ orientation: "vertical",
43
+ tabWidth: "full",
44
+ align: "start",
45
+ tabsWidth: "16rem",
46
+ loop: true,
47
+ stackOnMobile: true,
48
+ ariaLabel: "Tabs",
49
+ ui: () => ({}),
50
+ });
51
+
52
+ const emit = defineEmits<{
53
+ "update:modelValue": [value: TabValue];
54
+ change: [value: TabValue, item: TabItem, index: number];
55
+ }>();
56
+
57
+ const componentId = useId();
58
+ const tabButtons = ref<Array<HTMLButtonElement | null>>([]);
59
+
60
+ const getTabValue = (item: TabItem, index: number): TabValue =>
61
+ item.value ?? index;
62
+ const firstEnabledValue = () => {
63
+ const index = props.items.findIndex((item) => !item.disabled);
64
+ return index >= 0
65
+ ? getTabValue(props.items[index] as TabItem, index)
66
+ : undefined;
67
+ };
68
+
69
+ const activeValue = ref<TabValue | undefined>(
70
+ props.modelValue ?? props.defaultValue ?? firstEnabledValue(),
71
+ );
72
+
73
+ const activeIndex = computed(() =>
74
+ props.items.findIndex(
75
+ (item, index) => getTabValue(item, index) === activeValue.value,
76
+ ),
77
+ );
78
+ const activeItem = computed(() => props.items[activeIndex.value]);
79
+
80
+ const resolvedSide = computed<TabsSide>(() => {
81
+ if (props.orientation === "vertical") {
82
+ return props.side === "right" ? "right" : "left";
83
+ }
84
+
85
+ return props.side === "bottom" ? "bottom" : "top";
86
+ });
87
+
88
+ type TabsStyles = CSSProperties & Record<`--ui-tabs-${string}`, string>;
89
+
90
+ const rootStyles = computed<TabsStyles>(() => ({
91
+ "--ui-tabs-list-width": props.tabsWidth,
92
+ "--ui-tabs-panel-enter-x":
93
+ props.orientation === "vertical"
94
+ ? resolvedSide.value === "right"
95
+ ? "-0.5rem"
96
+ : "0.5rem"
97
+ : "0",
98
+ "--ui-tabs-panel-enter-y":
99
+ props.orientation === "horizontal" ? "0.4rem" : "0",
100
+ }));
101
+
102
+ const rootClasses = computed(() => [
103
+ "ui-tabs",
104
+ `ui-tabs--${props.orientation}`,
105
+ `ui-tabs--side-${resolvedSide.value}`,
106
+ `ui-tabs--align-${props.align}`,
107
+ `ui-tabs--tab-${props.tabWidth}`,
108
+ { "ui-tabs--stack-mobile": props.stackOnMobile },
109
+ props.ui.root,
110
+ ]);
111
+
112
+ const selectTab = (item: TabItem, index: number) => {
113
+ if (item.disabled) return;
114
+
115
+ const value = getTabValue(item, index);
116
+ activeValue.value = value;
117
+ emit("update:modelValue", value);
118
+ emit("change", value, item, index);
119
+ };
120
+
121
+ const enabledIndices = computed(() =>
122
+ props.items.flatMap((item, index) => (item.disabled ? [] : [index])),
123
+ );
124
+
125
+ const focusTab = (index: number) => {
126
+ const item = props.items[index];
127
+ if (!item) return;
128
+
129
+ tabButtons.value[index]?.focus();
130
+ selectTab(item, index);
131
+ };
132
+
133
+ const moveFocus = (currentIndex: number, direction: 1 | -1) => {
134
+ const indices = enabledIndices.value;
135
+ const position = indices.indexOf(currentIndex);
136
+ if (position < 0 || !indices.length) return;
137
+
138
+ let nextPosition = position + direction;
139
+ if (props.loop) {
140
+ nextPosition = (nextPosition + indices.length) % indices.length;
141
+ } else {
142
+ nextPosition = Math.min(Math.max(nextPosition, 0), indices.length - 1);
143
+ }
144
+
145
+ focusTab(indices[nextPosition] as number);
146
+ };
147
+
148
+ const onTabKeydown = (event: KeyboardEvent, index: number) => {
149
+ const previousKey =
150
+ props.orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
151
+ const nextKey =
152
+ props.orientation === "vertical" ? "ArrowDown" : "ArrowRight";
153
+
154
+ if (event.key === previousKey || event.key === nextKey) {
155
+ event.preventDefault();
156
+ moveFocus(index, event.key === nextKey ? 1 : -1);
157
+ } else if (event.key === "Home") {
158
+ event.preventDefault();
159
+ focusTab(enabledIndices.value[0] as number);
160
+ } else if (event.key === "End") {
161
+ event.preventDefault();
162
+ focusTab(enabledIndices.value.at(-1) as number);
163
+ }
164
+ };
165
+
166
+ watch(
167
+ () => props.modelValue,
168
+ (value) => {
169
+ if (value !== undefined) activeValue.value = value;
170
+ },
171
+ );
172
+
173
+ watch(
174
+ () =>
175
+ props.items.map((item, index) => [
176
+ getTabValue(item, index),
177
+ item.disabled,
178
+ ]),
179
+ () => {
180
+ const current = props.items[activeIndex.value];
181
+ if (!current || current.disabled)
182
+ activeValue.value = firstEnabledValue();
183
+ },
184
+ { immediate: true },
185
+ );
186
+
187
+ onBeforeUpdate(() => {
188
+ tabButtons.value = [];
189
+ });
190
+ </script>
191
+
192
+ <template>
193
+ <div :class="rootClasses" :style="rootStyles">
194
+ <div
195
+ :class="['ui-tabs__list', ui.list]"
196
+ role="tablist"
197
+ :aria-label="ariaLabel"
198
+ :aria-orientation="orientation"
199
+ >
200
+ <button
201
+ v-for="(item, index) in items"
202
+ :id="`${componentId}-tab-${index}`"
203
+ :key="getTabValue(item, index)"
204
+ :ref="
205
+ (element) =>
206
+ (tabButtons[index] =
207
+ element as HTMLButtonElement | null)
208
+ "
209
+ type="button"
210
+ role="tab"
211
+ :class="[
212
+ 'ui-tabs__tab',
213
+ ui.tab,
214
+ { 'ui-tabs__tab--active': activeIndex === index },
215
+ activeIndex === index && ui.activeTab,
216
+ ]"
217
+ :disabled="item.disabled"
218
+ :tabindex="activeIndex === index ? 0 : -1"
219
+ :aria-selected="activeIndex === index"
220
+ :aria-controls="`${componentId}-panel-${index}`"
221
+ @click="selectTab(item, index)"
222
+ @keydown="onTabKeydown($event, index)"
223
+ >
224
+ <slot
225
+ name="tab"
226
+ :item="item"
227
+ :index="index"
228
+ :active="activeIndex === index"
229
+ :disabled="item.disabled"
230
+ >
231
+ {{ item.label }}
232
+ </slot>
233
+ </button>
234
+ </div>
235
+
236
+ <div class="ui-tabs__panel-wrap">
237
+ <Transition name="ui-tabs-panel" mode="out-in" appear>
238
+ <div
239
+ v-if="activeItem"
240
+ :id="`${componentId}-panel-${activeIndex}`"
241
+ :key="activeValue"
242
+ :class="['ui-tabs__panel', ui.panel]"
243
+ role="tabpanel"
244
+ :aria-labelledby="`${componentId}-tab-${activeIndex}`"
245
+ tabindex="0"
246
+ >
247
+ <slot
248
+ name="panel"
249
+ :item="activeItem"
250
+ :index="activeIndex"
251
+ :value="activeValue"
252
+ >
253
+ {{ activeItem.content }}
254
+ </slot>
255
+ </div>
256
+ </Transition>
257
+ </div>
258
+ </div>
259
+ </template>
260
+
261
+ <style scoped>
262
+ .ui-tabs {
263
+ display: flex;
264
+ width: 100%;
265
+ min-width: 0;
266
+ gap: 1.5rem;
267
+ color: var(--ui-text);
268
+ }
269
+
270
+ .ui-tabs--vertical {
271
+ align-items: flex-start;
272
+ }
273
+
274
+ .ui-tabs--horizontal {
275
+ flex-direction: column;
276
+ gap: 1rem;
277
+ }
278
+
279
+ .ui-tabs--side-right .ui-tabs__list,
280
+ .ui-tabs--side-bottom .ui-tabs__list {
281
+ order: 2;
282
+ }
283
+
284
+ .ui-tabs__list {
285
+ display: flex;
286
+ flex: 0 0 var(--ui-tabs-list-width);
287
+ min-width: 0;
288
+ gap: 0.4rem;
289
+ }
290
+
291
+ .ui-tabs--vertical .ui-tabs__list {
292
+ width: var(--ui-tabs-list-width);
293
+ flex-direction: column;
294
+ }
295
+
296
+ .ui-tabs--horizontal .ui-tabs__list {
297
+ width: 100%;
298
+ flex: none;
299
+ flex-flow: row wrap;
300
+ }
301
+
302
+ .ui-tabs--align-start .ui-tabs__list {
303
+ align-items: flex-start;
304
+ justify-content: flex-start;
305
+ }
306
+
307
+ .ui-tabs--align-center .ui-tabs__list {
308
+ align-items: center;
309
+ justify-content: center;
310
+ }
311
+
312
+ .ui-tabs--align-end .ui-tabs__list {
313
+ align-items: flex-end;
314
+ justify-content: flex-end;
315
+ }
316
+
317
+ .ui-tabs__tab {
318
+ min-height: 2.65rem;
319
+ padding: 0.65rem 0.85rem;
320
+ font: inherit;
321
+ font-size: 0.9rem;
322
+ font-weight: 600;
323
+ line-height: 1.25;
324
+ color: var(--ui-text);
325
+ text-align: inherit;
326
+ background: transparent;
327
+ border: 1px solid transparent;
328
+ border-radius: 0.5rem;
329
+ cursor: pointer;
330
+ transition:
331
+ color 140ms ease,
332
+ background-color 140ms ease,
333
+ border-color 140ms ease;
334
+ }
335
+
336
+ .ui-tabs--align-start .ui-tabs__tab {
337
+ text-align: start;
338
+ }
339
+
340
+ .ui-tabs--align-center .ui-tabs__tab {
341
+ text-align: center;
342
+ }
343
+
344
+ .ui-tabs--align-end .ui-tabs__tab {
345
+ text-align: end;
346
+ }
347
+
348
+ .ui-tabs--tab-full.ui-tabs--vertical .ui-tabs__tab {
349
+ width: 100%;
350
+ }
351
+
352
+ .ui-tabs--tab-full.ui-tabs--horizontal .ui-tabs__tab {
353
+ flex: 1 1 0;
354
+ }
355
+
356
+ .ui-tabs--tab-auto .ui-tabs__tab {
357
+ width: max-content;
358
+ max-width: 100%;
359
+ }
360
+
361
+ .ui-tabs__tab:hover:not(:disabled) {
362
+ background: color-mix(in srgb, var(--ui-text) 5%, var(--ui-bg));
363
+ }
364
+
365
+ .ui-tabs__tab--active {
366
+ background: color-mix(in srgb, var(--ui-text) 10%, var(--ui-bg));
367
+ border-color: color-mix(in srgb, var(--ui-text) 16%, transparent);
368
+ }
369
+
370
+ .ui-tabs__tab:focus-visible {
371
+ outline: 2px solid color-mix(in srgb, var(--ui-text) 70%, transparent);
372
+ outline-offset: 2px;
373
+ }
374
+
375
+ .ui-tabs__tab:disabled {
376
+ cursor: not-allowed;
377
+ opacity: 0.4;
378
+ }
379
+
380
+ .ui-tabs__panel-wrap {
381
+ min-width: 0;
382
+ flex: 1 1 auto;
383
+ }
384
+
385
+ .ui-tabs__panel {
386
+ min-width: 0;
387
+ outline: none;
388
+ }
389
+
390
+ .ui-tabs__panel:focus-visible {
391
+ outline: 2px solid color-mix(in srgb, var(--ui-text) 45%, transparent);
392
+ outline-offset: 0.35rem;
393
+ }
394
+
395
+ .ui-tabs-panel-enter-active,
396
+ .ui-tabs-panel-leave-active {
397
+ transition:
398
+ opacity 160ms ease,
399
+ transform 160ms ease;
400
+ }
401
+
402
+ .ui-tabs-panel-enter-from {
403
+ opacity: 0;
404
+ transform: translate(
405
+ var(--ui-tabs-panel-enter-x),
406
+ var(--ui-tabs-panel-enter-y)
407
+ );
408
+ }
409
+
410
+ .ui-tabs-panel-leave-to {
411
+ opacity: 0;
412
+ }
413
+
414
+ @media (max-width: 767px) {
415
+ .ui-tabs--vertical.ui-tabs--stack-mobile {
416
+ flex-direction: column;
417
+ gap: 1rem;
418
+ }
419
+
420
+ .ui-tabs--vertical.ui-tabs--stack-mobile .ui-tabs__list {
421
+ order: 0;
422
+ width: 100%;
423
+ flex: none;
424
+ flex-flow: row nowrap;
425
+ overflow-x: auto;
426
+ }
427
+
428
+ .ui-tabs--vertical.ui-tabs--stack-mobile.ui-tabs--tab-full .ui-tabs__tab {
429
+ width: auto;
430
+ flex: 1 0 auto;
431
+ }
432
+ }
433
+
434
+ @media (prefers-reduced-motion: reduce) {
435
+ .ui-tabs__tab,
436
+ .ui-tabs-panel-enter-active,
437
+ .ui-tabs-panel-leave-active {
438
+ transition: none;
439
+ }
440
+
441
+ .ui-tabs-panel-enter-from {
442
+ transform: none;
443
+ }
444
+ }
445
+
446
+ @media (forced-colors: active) {
447
+ .ui-tabs__tab--active {
448
+ border-color: CanvasText;
449
+ }
450
+ }
451
+ </style>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(
3
+ defineProps<{
4
+ textSizeClass?: string;
5
+ textColorClass?: string;
6
+ spacingClass?: string;
7
+ }>(),
8
+ {
9
+ textSizeClass: "text-lg leading-relaxed",
10
+ textColorClass: "text-gray-700 dark:text-gray-300",
11
+ spacingClass: "space-y-4 sm:space-y-6",
12
+ },
13
+ );
14
+ </script>
15
+
16
+ <template>
17
+ <div
18
+ class="text-column w-full lg:w-[70%] xl:w-[65%]"
19
+ :class="[textSizeClass, textColorClass, spacingClass]"
20
+ >
21
+ <slot />
22
+ </div>
23
+ </template>
24
+
25
+ <style scoped>
26
+ .text-column :deep(p) {
27
+ font-size: inherit;
28
+ color: inherit;
29
+ }
30
+
31
+ .text-column :deep(ul),
32
+ .text-column :deep(ol) {
33
+ font-size: inherit;
34
+ color: inherit;
35
+ }
36
+ </style>