@umbra.ui/core 0.1.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 (272) hide show
  1. package/dist/components/controls/Dropdown/types.d.ts +5 -0
  2. package/dist/components/controls/Dropdown/types.d.ts.map +1 -0
  3. package/dist/components/controls/Dropdown/types.js +1 -0
  4. package/dist/components/controls/SegmentedControl/types.d.ts +6 -0
  5. package/dist/components/controls/SegmentedControl/types.d.ts.map +1 -0
  6. package/dist/components/controls/SegmentedControl/types.js +1 -0
  7. package/dist/components/dialogs/Alert/types.d.ts +7 -0
  8. package/dist/components/dialogs/Alert/types.d.ts.map +1 -0
  9. package/dist/components/dialogs/Alert/types.js +1 -0
  10. package/dist/components/dialogs/Toast/types.d.ts +34 -0
  11. package/dist/components/dialogs/Toast/types.d.ts.map +1 -0
  12. package/dist/components/dialogs/Toast/types.js +10 -0
  13. package/dist/components/dialogs/Toast/useToast.d.ts +36 -0
  14. package/dist/components/dialogs/Toast/useToast.d.ts.map +1 -0
  15. package/dist/components/dialogs/Toast/useToast.js +90 -0
  16. package/dist/components/indicators/Tooltip/tooltip.d.ts +3 -0
  17. package/dist/components/indicators/Tooltip/tooltip.d.ts.map +1 -0
  18. package/dist/components/indicators/Tooltip/tooltip.js +33 -0
  19. package/dist/components/indicators/Tooltip/types.d.ts +14 -0
  20. package/dist/components/indicators/Tooltip/types.d.ts.map +1 -0
  21. package/dist/components/indicators/Tooltip/types.js +1 -0
  22. package/dist/components/indicators/Tooltip/useTooltip.d.ts +18 -0
  23. package/dist/components/indicators/Tooltip/useTooltip.d.ts.map +1 -0
  24. package/dist/components/indicators/Tooltip/useTooltip.js +57 -0
  25. package/dist/components/inputs/Tags/tag-bar-styles.d.ts +14 -0
  26. package/dist/components/inputs/Tags/tag-bar-styles.d.ts.map +1 -0
  27. package/dist/components/inputs/Tags/tag-bar-styles.js +313 -0
  28. package/dist/components/inputs/Tags/types.d.ts +93 -0
  29. package/dist/components/inputs/Tags/types.d.ts.map +1 -0
  30. package/dist/components/inputs/Tags/types.js +216 -0
  31. package/dist/components/inputs/search/types.d.ts +9 -0
  32. package/dist/components/inputs/search/types.d.ts.map +1 -0
  33. package/dist/components/inputs/search/types.js +1 -0
  34. package/dist/components/navigation/adaptive/types.d.ts +16 -0
  35. package/dist/components/navigation/adaptive/types.d.ts.map +1 -0
  36. package/dist/components/navigation/adaptive/types.js +1 -0
  37. package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts +27 -0
  38. package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts.map +1 -0
  39. package/dist/components/navigation/adaptive/useAdaptiveLayout.js +40 -0
  40. package/dist/components/navigation/adaptive/useBreakpoints.d.ts +6 -0
  41. package/dist/components/navigation/adaptive/useBreakpoints.d.ts.map +1 -0
  42. package/dist/components/navigation/adaptive/useBreakpoints.js +37 -0
  43. package/dist/components/navigation/adaptive/useContainerMonitor.d.ts +93 -0
  44. package/dist/components/navigation/adaptive/useContainerMonitor.d.ts.map +1 -0
  45. package/dist/components/navigation/adaptive/useContainerMonitor.js +145 -0
  46. package/dist/components/navigation/adaptive/useViewAnimation.d.ts +31 -0
  47. package/dist/components/navigation/adaptive/useViewAnimation.d.ts.map +1 -0
  48. package/dist/components/navigation/adaptive/useViewAnimation.js +591 -0
  49. package/dist/components/navigation/adaptive/useViewResize.d.ts +52 -0
  50. package/dist/components/navigation/adaptive/useViewResize.d.ts.map +1 -0
  51. package/dist/components/navigation/adaptive/useViewResize.js +146 -0
  52. package/dist/components/navigation/navstack/useNavigationStack.d.ts +25 -0
  53. package/dist/components/navigation/navstack/useNavigationStack.d.ts.map +1 -0
  54. package/dist/components/navigation/navstack/useNavigationStack.js +133 -0
  55. package/dist/components/navigation/slideover/useSlideoverController.d.ts +20 -0
  56. package/dist/components/navigation/slideover/useSlideoverController.d.ts.map +1 -0
  57. package/dist/components/navigation/slideover/useSlideoverController.js +267 -0
  58. package/dist/components/navigation/splitview/useSplitViewController.d.ts +20 -0
  59. package/dist/components/navigation/splitview/useSplitViewController.d.ts.map +1 -0
  60. package/dist/components/navigation/splitview/useSplitViewController.js +325 -0
  61. package/dist/components/navigation/tabcontroller/types.d.ts +21 -0
  62. package/dist/components/navigation/tabcontroller/types.d.ts.map +1 -0
  63. package/dist/components/navigation/tabcontroller/types.js +1 -0
  64. package/dist/components/navigation/tabcontroller/useTabController.d.ts +5 -0
  65. package/dist/components/navigation/tabcontroller/useTabController.d.ts.map +1 -0
  66. package/dist/components/navigation/tabcontroller/useTabController.js +10 -0
  67. package/dist/components/navigation/types.d.ts +8 -0
  68. package/dist/components/navigation/types.d.ts.map +1 -0
  69. package/dist/components/navigation/types.js +1 -0
  70. package/dist/components/pickers/CollectionPicker/types.d.ts +11 -0
  71. package/dist/components/pickers/CollectionPicker/types.d.ts.map +1 -0
  72. package/dist/components/pickers/CollectionPicker/types.js +1 -0
  73. package/dist/components/pickers/ColorPicker/colors.d.ts +13 -0
  74. package/dist/components/pickers/ColorPicker/colors.d.ts.map +1 -0
  75. package/dist/components/pickers/ColorPicker/colors.js +266 -0
  76. package/dist/components/pickers/FilePicker/types.d.ts +10 -0
  77. package/dist/components/pickers/FilePicker/types.d.ts.map +1 -0
  78. package/dist/components/pickers/FilePicker/types.js +1 -0
  79. package/dist/index.d.ts +91 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +196 -0
  82. package/dist/theme.d.ts +73 -0
  83. package/dist/theme.d.ts.map +1 -0
  84. package/dist/theme.js +279 -0
  85. package/dist/themes/blank.d.ts +7 -0
  86. package/dist/themes/blank.d.ts.map +1 -0
  87. package/dist/themes/blank.js +543 -0
  88. package/dist/themes/crimson-dark.d.ts +4 -0
  89. package/dist/themes/crimson-dark.d.ts.map +1 -0
  90. package/dist/themes/crimson-dark.js +552 -0
  91. package/dist/themes/cyan-light.d.ts +4 -0
  92. package/dist/themes/cyan-light.d.ts.map +1 -0
  93. package/dist/themes/cyan-light.js +552 -0
  94. package/dist/themes/dark.d.ts +4 -0
  95. package/dist/themes/dark.d.ts.map +1 -0
  96. package/dist/themes/dark.js +551 -0
  97. package/dist/themes/gold-dark.d.ts +4 -0
  98. package/dist/themes/gold-dark.d.ts.map +1 -0
  99. package/dist/themes/gold-dark.js +552 -0
  100. package/dist/themes/grass-dark.d.ts +4 -0
  101. package/dist/themes/grass-dark.d.ts.map +1 -0
  102. package/dist/themes/grass-dark.js +552 -0
  103. package/dist/themes/indigo.d.ts +4 -0
  104. package/dist/themes/indigo.d.ts.map +1 -0
  105. package/dist/themes/indigo.js +552 -0
  106. package/dist/themes/light.d.ts +4 -0
  107. package/dist/themes/light.d.ts.map +1 -0
  108. package/dist/themes/light.js +551 -0
  109. package/dist/themes/orange-dark.d.ts +4 -0
  110. package/dist/themes/orange-dark.d.ts.map +1 -0
  111. package/dist/themes/orange-dark.js +551 -0
  112. package/dist/themes/orange-light.d.ts +4 -0
  113. package/dist/themes/orange-light.d.ts.map +1 -0
  114. package/dist/themes/orange-light.js +551 -0
  115. package/package.json +62 -0
  116. package/src/components/controls/Button/Button.vue +417 -0
  117. package/src/components/controls/Button/README.md +348 -0
  118. package/src/components/controls/Button/theme.css +200 -0
  119. package/src/components/controls/Checkbox/Checkbox.vue +164 -0
  120. package/src/components/controls/Checkbox/README.md +441 -0
  121. package/src/components/controls/Checkbox/theme.css +36 -0
  122. package/src/components/controls/Dropdown/Dropdown.vue +476 -0
  123. package/src/components/controls/Dropdown/README.md +370 -0
  124. package/src/components/controls/Dropdown/theme.css +50 -0
  125. package/src/components/controls/Dropdown/types.ts +6 -0
  126. package/src/components/controls/IconButton/IconButton.vue +267 -0
  127. package/src/components/controls/IconButton/README.md +502 -0
  128. package/src/components/controls/IconButton/theme.css +89 -0
  129. package/src/components/controls/Radio/README.md +591 -0
  130. package/src/components/controls/Radio/Radio.vue +89 -0
  131. package/src/components/controls/Radio/theme.css +14 -0
  132. package/src/components/controls/RangeSlider/README.md +608 -0
  133. package/src/components/controls/RangeSlider/RangeSlider.vue +535 -0
  134. package/src/components/controls/RangeSlider/theme.css +80 -0
  135. package/src/components/controls/SegmentedControl/README.md +587 -0
  136. package/src/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
  137. package/src/components/controls/SegmentedControl/theme.css +60 -0
  138. package/src/components/controls/SegmentedControl/types.ts +5 -0
  139. package/src/components/controls/Slider/README.md +627 -0
  140. package/src/components/controls/Slider/Slider.vue +260 -0
  141. package/src/components/controls/Slider/theme.css +74 -0
  142. package/src/components/controls/Stepper/README.md +601 -0
  143. package/src/components/controls/Stepper/Stepper.vue +103 -0
  144. package/src/components/controls/Stepper/theme.css +53 -0
  145. package/src/components/controls/Switch/README.md +667 -0
  146. package/src/components/controls/Switch/Switch.vue +127 -0
  147. package/src/components/controls/Switch/theme.css +42 -0
  148. package/src/components/dialogs/Alert/Alert.vue +218 -0
  149. package/src/components/dialogs/Alert/README.md +450 -0
  150. package/src/components/dialogs/Alert/theme.css +44 -0
  151. package/src/components/dialogs/Alert/types.ts +11 -0
  152. package/src/components/dialogs/Toast/README.md +522 -0
  153. package/src/components/dialogs/Toast/Toast.vue +296 -0
  154. package/src/components/dialogs/Toast/ToastContainer.vue +330 -0
  155. package/src/components/dialogs/Toast/theme.css +44 -0
  156. package/src/components/dialogs/Toast/types.ts +46 -0
  157. package/src/components/dialogs/Toast/useToast.ts +127 -0
  158. package/src/components/indicators/ProgressBar/ProgressBar.vue +98 -0
  159. package/src/components/indicators/ProgressBar/README.md +744 -0
  160. package/src/components/indicators/ProgressBar/theme.css +36 -0
  161. package/src/components/indicators/Tooltip/README.md +723 -0
  162. package/src/components/indicators/Tooltip/TooltipProvider.vue +142 -0
  163. package/src/components/indicators/Tooltip/theme.css +18 -0
  164. package/src/components/indicators/Tooltip/tooltip.ts +48 -0
  165. package/src/components/indicators/Tooltip/types.ts +15 -0
  166. package/src/components/indicators/Tooltip/useTooltip.ts +71 -0
  167. package/src/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
  168. package/src/components/inputs/AutogrowTextView/README.md +643 -0
  169. package/src/components/inputs/AutogrowTextView/theme.css +28 -0
  170. package/src/components/inputs/InputCard/InputCard.vue +600 -0
  171. package/src/components/inputs/InputCard/README.md +636 -0
  172. package/src/components/inputs/InputEmail/InputEmail.vue +698 -0
  173. package/src/components/inputs/InputEmail/README.md +764 -0
  174. package/src/components/inputs/InputNumber/InputNumber.vue +300 -0
  175. package/src/components/inputs/InputNumber/README.md +749 -0
  176. package/src/components/inputs/InputPhone/InputPhone.vue +645 -0
  177. package/src/components/inputs/InputPhone/README.md +636 -0
  178. package/src/components/inputs/InputSecure/InputSecure.vue +646 -0
  179. package/src/components/inputs/InputSecure/README.md +771 -0
  180. package/src/components/inputs/InputText/InputText.vue +225 -0
  181. package/src/components/inputs/InputText/README.md +844 -0
  182. package/src/components/inputs/OTP/OTP.vue +349 -0
  183. package/src/components/inputs/OTP/README.md +736 -0
  184. package/src/components/inputs/OTP/theme.css +50 -0
  185. package/src/components/inputs/StringCapture/README.md +718 -0
  186. package/src/components/inputs/StringCapture/StringCapture.vue +315 -0
  187. package/src/components/inputs/StringCapture/theme.css +86 -0
  188. package/src/components/inputs/Tags/README.md +897 -0
  189. package/src/components/inputs/Tags/TagBar.vue +793 -0
  190. package/src/components/inputs/Tags/TagCreation.vue +219 -0
  191. package/src/components/inputs/Tags/TagPicker.vue +380 -0
  192. package/src/components/inputs/Tags/tag-bar-styles.ts +354 -0
  193. package/src/components/inputs/Tags/theme.css +121 -0
  194. package/src/components/inputs/Tags/types.ts +346 -0
  195. package/src/components/inputs/search/README.md +759 -0
  196. package/src/components/inputs/search/SearchBar.vue +394 -0
  197. package/src/components/inputs/search/SearchResults.vue +310 -0
  198. package/src/components/inputs/search/theme.css +187 -0
  199. package/src/components/inputs/search/types.ts +8 -0
  200. package/src/components/inputs/theme.css +102 -0
  201. package/src/components/menus/ActionMenu/ActionMenu.vue +383 -0
  202. package/src/components/menus/ActionMenu/README.md +825 -0
  203. package/src/components/menus/ActionMenu/theme.css +93 -0
  204. package/src/components/models/Popover/Popover.vue +551 -0
  205. package/src/components/models/Popover/README.md +885 -0
  206. package/src/components/models/Popover/theme.css +52 -0
  207. package/src/components/models/Sheet/README.md +1159 -0
  208. package/src/components/models/Sheet/Sheet.vue +465 -0
  209. package/src/components/models/Sheet/theme.css +72 -0
  210. package/src/components/models/Sidebar/README.md +1228 -0
  211. package/src/components/models/Sidebar/Sidebar.vue +480 -0
  212. package/src/components/models/Sidebar/theme.css +90 -0
  213. package/src/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
  214. package/src/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
  215. package/src/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
  216. package/src/components/navigation/adaptive/README.md +768 -0
  217. package/src/components/navigation/adaptive/types.ts +19 -0
  218. package/src/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
  219. package/src/components/navigation/adaptive/useBreakpoints.ts +41 -0
  220. package/src/components/navigation/adaptive/useContainerMonitor.ts +214 -0
  221. package/src/components/navigation/adaptive/useViewAnimation.ts +721 -0
  222. package/src/components/navigation/adaptive/useViewResize.ts +211 -0
  223. package/src/components/navigation/navstack/NavigationStack.vue +180 -0
  224. package/src/components/navigation/navstack/README.md +994 -0
  225. package/src/components/navigation/navstack/useNavigationStack.ts +164 -0
  226. package/src/components/navigation/slideover/README.md +1275 -0
  227. package/src/components/navigation/slideover/SlideoverController.vue +287 -0
  228. package/src/components/navigation/slideover/useSlideoverController.ts +320 -0
  229. package/src/components/navigation/splitview/README.md +1115 -0
  230. package/src/components/navigation/splitview/SplitViewController.vue +176 -0
  231. package/src/components/navigation/splitview/useSplitViewController.ts +388 -0
  232. package/src/components/navigation/tabcontroller/README.md +919 -0
  233. package/src/components/navigation/tabcontroller/TabController.vue +307 -0
  234. package/src/components/navigation/tabcontroller/TabItem.vue +57 -0
  235. package/src/components/navigation/tabcontroller/types.ts +24 -0
  236. package/src/components/navigation/tabcontroller/useTabController.ts +18 -0
  237. package/src/components/navigation/theme.css +91 -0
  238. package/src/components/navigation/types.ts +7 -0
  239. package/src/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
  240. package/src/components/pickers/CollectionPicker/README.md +1115 -0
  241. package/src/components/pickers/CollectionPicker/theme.css +14 -0
  242. package/src/components/pickers/CollectionPicker/types.ts +11 -0
  243. package/src/components/pickers/ColorPicker/ColorPicker.vue +376 -0
  244. package/src/components/pickers/ColorPicker/README.md +1439 -0
  245. package/src/components/pickers/ColorPicker/colors.ts +299 -0
  246. package/src/components/pickers/ColorPicker/theme.css +32 -0
  247. package/src/components/pickers/DatePicker/DatePicker.vue +660 -0
  248. package/src/components/pickers/DatePicker/README.md +1195 -0
  249. package/src/components/pickers/DatePicker/theme.css +22 -0
  250. package/src/components/pickers/FilePicker/FilePicker.vue +534 -0
  251. package/src/components/pickers/FilePicker/README.md +1542 -0
  252. package/src/components/pickers/FilePicker/theme.css +48 -0
  253. package/src/components/pickers/FilePicker/types.ts +10 -0
  254. package/src/components/pickers/IconPicker/IconPicker.vue +327 -0
  255. package/src/components/pickers/IconPicker/README.md +1161 -0
  256. package/src/components/pickers/IconPicker/theme.css +28 -0
  257. package/src/components/pickers/theme.css +82 -0
  258. package/src/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
  259. package/src/components/views/MarkdownViewer/README.md +833 -0
  260. package/src/components/views/MarkdownViewer/theme.css +130 -0
  261. package/src/index.ts +263 -0
  262. package/src/theme.ts +378 -0
  263. package/src/themes/crimson-dark.ts +556 -0
  264. package/src/themes/cyan-light.ts +556 -0
  265. package/src/themes/dark.ts +557 -0
  266. package/src/themes/gold-dark.ts +556 -0
  267. package/src/themes/grass-dark.ts +556 -0
  268. package/src/themes/indigo.ts +556 -0
  269. package/src/themes/light.ts +557 -0
  270. package/src/themes/orange-dark.ts +557 -0
  271. package/src/themes/orange-light.ts +557 -0
  272. package/src/vue.d.ts +45 -0
@@ -0,0 +1,307 @@
1
+ // ============= TabController.vue =============
2
+ <script setup lang="ts">
3
+ import { ref, watch, provide, computed, useCssModule, type Ref } from "vue";
4
+ import { TabControllerKey } from "./useTabController";
5
+ import { icons, type IconKey } from "@umbra-ui/icons";
6
+ import type { Tab, TabPosition } from "./types";
7
+ import "../theme.css";
8
+
9
+ interface Props {
10
+ modelValue?: string;
11
+ position?: TabPosition;
12
+ layout?: "row" | "column";
13
+ barColor?: string;
14
+ tabItemActiveColor?: string;
15
+ }
16
+
17
+ const props = withDefaults(defineProps<Props>(), {
18
+ modelValue: "",
19
+ position: "bottom",
20
+ layout: "column",
21
+ barColor: "#111111",
22
+ tabItemActiveColor: "#0090ff",
23
+ });
24
+
25
+ const emit = defineEmits<{
26
+ "update:modelValue": [value: string];
27
+ change: [value: string];
28
+ }>();
29
+
30
+ const $style = useCssModule();
31
+
32
+ const tabs: Ref<Tab[]> = ref([]);
33
+ const activeTab = ref(props.modelValue);
34
+
35
+ const registerTab = (tab: Tab) => {
36
+ const existingIndex = tabs.value.findIndex((t) => t.id === tab.id);
37
+
38
+ if (existingIndex === -1) {
39
+ tabs.value.push(tab);
40
+ } else {
41
+ // Update existing tab
42
+ tabs.value[existingIndex] = tab;
43
+ }
44
+
45
+ // Set first tab as active if none selected
46
+ if (!activeTab.value && tabs.value.length === 1) {
47
+ setActiveTab(tab.id);
48
+ }
49
+ };
50
+
51
+ const unregisterTab = (id: string) => {
52
+ const index = tabs.value.findIndex((t) => t.id === id);
53
+ if (index > -1) {
54
+ tabs.value.splice(index, 1);
55
+
56
+ // If active tab was removed, select another
57
+ if (activeTab.value === id && tabs.value.length > 0) {
58
+ setActiveTab(tabs.value[Math.min(index, tabs.value.length - 1)].id);
59
+ }
60
+ }
61
+ };
62
+
63
+ const setActiveTab = (id: string) => {
64
+ const tab = tabs.value.find((t) => t.id === id);
65
+ if (tab && !tab.disabled && id !== activeTab.value) {
66
+ activeTab.value = id;
67
+ emit("update:modelValue", id);
68
+ emit("change", id);
69
+ }
70
+ };
71
+
72
+ const goToTab = (index: number) => {
73
+ if (index >= 0 && index < tabs.value.length) {
74
+ setActiveTab(tabs.value[index].id);
75
+ }
76
+ };
77
+
78
+ const nextTab = () => {
79
+ const currentIndex = tabs.value.findIndex((t) => t.id === activeTab.value);
80
+ const nextIndex = (currentIndex + 1) % tabs.value.length;
81
+ goToTab(nextIndex);
82
+ };
83
+
84
+ const previousTab = () => {
85
+ const currentIndex = tabs.value.findIndex((t) => t.id === activeTab.value);
86
+ const prevIndex =
87
+ currentIndex === 0 ? tabs.value.length - 1 : currentIndex - 1;
88
+ goToTab(prevIndex);
89
+ };
90
+
91
+ // Sync external model value changes
92
+ watch(
93
+ () => props.modelValue,
94
+ (newVal) => {
95
+ if (newVal !== activeTab.value) {
96
+ activeTab.value = newVal;
97
+ }
98
+ }
99
+ );
100
+
101
+ // Provide context for child components
102
+ provide(TabControllerKey, {
103
+ tabs,
104
+ activeTab,
105
+ registerTab,
106
+ unregisterTab,
107
+ setActiveTab,
108
+ goToTab,
109
+ nextTab,
110
+ previousTab,
111
+ animated: false,
112
+ });
113
+
114
+ const containerClasses = computed(() => ({
115
+ [$style.container]: true,
116
+ [$style["container--top"]]: props.position === "top",
117
+ }));
118
+
119
+ const tabItemClasses = (tab: Tab) => ({
120
+ [$style.tabItem]: true,
121
+ [$style["tabItem--active"]]: activeTab.value === tab.id,
122
+ [$style["tabItem--disabled"]]: tab.disabled,
123
+ [$style["tabItem--row"]]: props.layout === "row",
124
+ [$style["tabItem--column"]]: props.layout === "column",
125
+ });
126
+
127
+ const tabBarClasses = computed(() => ({
128
+ [$style.tabBar]: true,
129
+ [$style["tabBar--row"]]: props.layout === "row",
130
+ [$style["tabBar--column"]]: props.layout === "column",
131
+ }));
132
+
133
+ const getIconComponent = (iconKey?: string) => {
134
+ if (!iconKey) return null;
135
+ return icons[iconKey as IconKey] || null;
136
+ };
137
+ </script>
138
+
139
+ <template>
140
+ <div :class="containerClasses">
141
+ <!-- Top Tab Bar -->
142
+ <div v-if="position === 'top'" :class="tabBarClasses">
143
+ <button
144
+ v-for="tab in tabs"
145
+ :key="tab.id"
146
+ :class="tabItemClasses(tab)"
147
+ :disabled="tab.disabled"
148
+ @click="setActiveTab(tab.id)"
149
+ >
150
+ <component
151
+ v-if="tab.icon"
152
+ :is="getIconComponent(tab.icon)"
153
+ :class="$style.tabIcon"
154
+ size="18"
155
+ />
156
+ <span :class="[$style.tabLabel]">{{ tab.label }}</span>
157
+ <span v-if="tab.badge" :class="$style.tabBadge">{{ tab.badge }}</span>
158
+ </button>
159
+ </div>
160
+
161
+ <!-- Tab Content -->
162
+ <div :class="$style.tabContent">
163
+ <slot></slot>
164
+ </div>
165
+
166
+ <!-- Bottom Tab Bar -->
167
+ <div v-if="position === 'bottom'" :class="tabBarClasses">
168
+ <button
169
+ v-for="tab in tabs"
170
+ :key="tab.id"
171
+ :class="tabItemClasses(tab)"
172
+ :disabled="tab.disabled"
173
+ @click="setActiveTab(tab.id)"
174
+ >
175
+ <component
176
+ v-if="tab.icon"
177
+ :is="getIconComponent(tab.icon)"
178
+ :class="$style.tabIcon"
179
+ />
180
+ <span :class="$style.tabLabel">{{ tab.label }}</span>
181
+ <span v-if="tab.badge" :class="$style.tabBadge">{{ tab.badge }}</span>
182
+ </button>
183
+ </div>
184
+ </div>
185
+ </template>
186
+
187
+ <style module>
188
+ .container {
189
+ display: flex;
190
+ flex-direction: column;
191
+ height: 100%;
192
+ background: var(--tabcontroller-container-bg);
193
+ overflow: hidden;
194
+ }
195
+
196
+ .container--top {
197
+ flex-direction: column;
198
+ }
199
+
200
+ .tabBar {
201
+ display: flex;
202
+ background: var(--tabcontroller-bar-bg);
203
+ border-top: 1px solid var(--tabcontroller-bar-border);
204
+ padding: 0.353rem;
205
+ }
206
+
207
+ .tabBar--column {
208
+ gap: 0.353rem;
209
+ }
210
+
211
+ .tabBar--row {
212
+ gap: 0.5rem;
213
+ }
214
+
215
+ .container--top .tabBar {
216
+ border-top: none;
217
+ border-bottom: 1px solid var(--tabcontroller-bar-border);
218
+ }
219
+
220
+ .tabItem {
221
+ flex: 1;
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ padding: 8px 4px;
226
+ background: none;
227
+ border: none;
228
+ cursor: pointer;
229
+ transition: all 0.2s ease;
230
+ color: var(--tabcontroller-tab-text);
231
+ font-family: inherit;
232
+ position: relative;
233
+ border-radius: 0.353rem;
234
+ font-size: 12px;
235
+ }
236
+
237
+ .tabItem--column {
238
+ flex-direction: column;
239
+ }
240
+
241
+ .tabItem--row {
242
+ flex-direction: row;
243
+ gap: 0.5rem;
244
+ }
245
+
246
+ .tabItem:hover:not(.tabItem--disabled) {
247
+ background: var(--tabcontroller-tab-hover-bg);
248
+ }
249
+
250
+ .tabItem--active {
251
+ color: var(--tabcontroller-tab-active-text);
252
+ }
253
+
254
+ .tabItem--disabled {
255
+ opacity: var(--tabcontroller-tab-disabled-opacity);
256
+ cursor: not-allowed;
257
+ }
258
+
259
+ .tabIcon {
260
+ font-size: 20px;
261
+ transition: transform 0.2s ease;
262
+ }
263
+
264
+ .tabItem--column .tabIcon {
265
+ margin-bottom: 4px;
266
+ }
267
+
268
+ .tabItem--row .tabIcon {
269
+ margin-right: 0;
270
+ }
271
+
272
+ .tabItem--active .tabIcon {
273
+ transform: scale(1.1);
274
+ }
275
+
276
+ .tabLabel {
277
+ font-weight: 500;
278
+ }
279
+
280
+ .tabBadge {
281
+ position: absolute;
282
+ background: var(--tabcontroller-badge-bg);
283
+ color: var(--tabcontroller-badge-text);
284
+ font-size: 10px;
285
+ font-weight: 600;
286
+ padding: 2px 5px;
287
+ border-radius: 10px;
288
+ min-width: 16px;
289
+ text-align: center;
290
+ }
291
+
292
+ .tabItem--column .tabBadge {
293
+ top: 4px;
294
+ right: calc(50% - 16px);
295
+ }
296
+
297
+ .tabItem--row .tabBadge {
298
+ position: relative;
299
+ }
300
+
301
+ .tabContent {
302
+ flex: 1;
303
+ overflow-y: auto;
304
+ position: relative;
305
+ height: 100%;
306
+ }
307
+ </style>
@@ -0,0 +1,57 @@
1
+ // ============= TabItem.vue =============
2
+ <script setup lang="ts">
3
+ import { computed, onMounted, onBeforeUnmount, watch } from "vue";
4
+ import { useTabController } from "./useTabController";
5
+ import type { Tab } from "./types";
6
+ import "../theme.css";
7
+
8
+ interface Props {
9
+ id: string;
10
+ label: string;
11
+ icon?: string;
12
+ badge?: string | number;
13
+ disabled?: boolean;
14
+ }
15
+
16
+ const props = defineProps<Props>();
17
+
18
+ const { activeTab, registerTab, unregisterTab } = useTabController();
19
+
20
+ const isActive = computed(() => activeTab.value === props.id);
21
+
22
+ const tabInfo = computed<Tab>(() => ({
23
+ id: props.id,
24
+ label: props.label,
25
+ icon: props.icon,
26
+ badge: props.badge,
27
+ disabled: props.disabled,
28
+ }));
29
+
30
+ onMounted(() => {
31
+ registerTab(tabInfo.value);
32
+ });
33
+
34
+ onBeforeUnmount(() => {
35
+ unregisterTab(props.id);
36
+ });
37
+
38
+ // Re-register when props change
39
+ watch(
40
+ () => tabInfo.value,
41
+ (newTab) => {
42
+ registerTab(newTab);
43
+ }
44
+ );
45
+ </script>
46
+
47
+ <template>
48
+ <div v-show="isActive" :class="$style.panel">
49
+ <slot></slot>
50
+ </div>
51
+ </template>
52
+
53
+ <style module>
54
+ .panel {
55
+ height: 100%;
56
+ }
57
+ </style>
@@ -0,0 +1,24 @@
1
+ // ============= types.ts =============
2
+ import type { Ref } from "vue";
3
+
4
+ export interface Tab {
5
+ id: string;
6
+ label: string;
7
+ icon?: string;
8
+ badge?: string | number;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ export interface TabControllerContext {
13
+ tabs: Ref<Tab[]>;
14
+ activeTab: Ref<string>;
15
+ registerTab: (tab: Tab) => void;
16
+ unregisterTab: (id: string) => void;
17
+ setActiveTab: (id: string) => void;
18
+ goToTab: (index: number) => void;
19
+ nextTab: () => void;
20
+ previousTab: () => void;
21
+ animated: boolean;
22
+ }
23
+
24
+ export type TabPosition = "top" | "bottom";
@@ -0,0 +1,18 @@
1
+ // ============= useTabController.ts =============
2
+ import { inject, type InjectionKey } from "vue";
3
+ import type { TabControllerContext } from "./types";
4
+
5
+ export const TabControllerKey: InjectionKey<TabControllerContext> =
6
+ Symbol("TabController");
7
+
8
+ export const useTabController = (): TabControllerContext => {
9
+ const context = inject(TabControllerKey);
10
+
11
+ if (!context) {
12
+ throw new Error(
13
+ "useTabController must be used within a TabController component"
14
+ );
15
+ }
16
+
17
+ return context;
18
+ };
@@ -0,0 +1,91 @@
1
+ /* Light theme using Colors */
2
+ :root {
3
+ /* Navigation container colors */
4
+ --navigation-container-bg: #ffffff; /* white - light background for light mode */
5
+
6
+ /* TabController colors */
7
+ --tabcontroller-container-bg: #ffffff; /* white - light background for light mode */
8
+ --tabcontroller-bar-bg: #f3f4f6; /* gray1 - light background for light mode */
9
+ --tabcontroller-bar-border: #e5e7eb; /* gray2 - border for light mode */
10
+ --tabcontroller-tab-text: #6b7280; /* gray5 - tab text for light mode */
11
+ --tabcontroller-tab-hover-bg: rgba(
12
+ 0,
13
+ 0,
14
+ 0,
15
+ 0.05
16
+ ); /* blackA6 - hover background for light mode */
17
+ --tabcontroller-tab-active-text: #0090ff; /* blue9 - active tab text for light mode */
18
+ --tabcontroller-tab-disabled-opacity: 0.4; /* disabled opacity for light mode */
19
+ --tabcontroller-badge-bg: #dc2626; /* red6 - badge background for light mode */
20
+ --tabcontroller-badge-text: #ffffff; /* white - badge text for light mode */
21
+
22
+ /* SlideoverController colors */
23
+ --slideover-darken-bg: rgba(
24
+ 0,
25
+ 0,
26
+ 0,
27
+ 0.5
28
+ ); /* blackA8 - darken background for light mode */
29
+ --slideover-breadcrumb-hover-opacity: 1; /* breadcrumb hover opacity for light mode */
30
+ --slideover-breadcrumb-default-opacity: 0.5; /* breadcrumb default opacity for light mode */
31
+ --slideover-handle-bg: #6b7280; /* gray5 - handle background for light mode */
32
+ --slideover-handle-hover-width: 4px; /* handle hover width for light mode */
33
+ --slideover-handle-hover-height: 24px; /* handle hover height for light mode */
34
+ --slideover-handle-hover-opacity: 1; /* handle hover opacity for light mode */
35
+ --slideover-handle-default-width: 2px; /* handle default width for light mode */
36
+ --slideover-handle-default-height: 20px; /* handle default height for light mode */
37
+ --slideover-handle-default-opacity: 0; /* handle default opacity for light mode */
38
+
39
+ /* SplitViewController colors */
40
+ --splitview-handle-bg: #6b7280; /* gray5 - handle background for light mode */
41
+ --splitview-handle-hover-width: 4px; /* handle hover width for light mode */
42
+ --splitview-handle-hover-height: 24px; /* handle hover height for light mode */
43
+ --splitview-handle-hover-opacity: 1; /* handle hover opacity for light mode */
44
+ --splitview-handle-default-width: 2px; /* handle default width for light mode */
45
+ --splitview-handle-default-height: 20px; /* handle default height for light mode */
46
+ --splitview-handle-default-opacity: 0; /* handle default opacity for light mode */
47
+ }
48
+
49
+ /* Dark theme */
50
+ .dark,
51
+ .dark-theme {
52
+ /* Navigation container colors */
53
+ --navigation-container-bg: #000000; /* Original dark mode value */
54
+
55
+ /* TabController colors */
56
+ --tabcontroller-container-bg: #000000; /* Original dark mode value */
57
+ --tabcontroller-bar-bg: #111111; /* Original dark mode value */
58
+ --tabcontroller-bar-border: #2a2a2a; /* Original dark mode value */
59
+ --tabcontroller-tab-text: #b4b4b4; /* Original dark mode value */
60
+ --tabcontroller-tab-hover-bg: rgba(
61
+ 255,
62
+ 255,
63
+ 255,
64
+ 0.12
65
+ ); /* Original dark mode value */
66
+ --tabcontroller-tab-active-text: #0090ff; /* Original dark mode value */
67
+ --tabcontroller-tab-disabled-opacity: 0.4; /* Original dark mode value */
68
+ --tabcontroller-badge-bg: #e5484d; /* Original dark mode value */
69
+ --tabcontroller-badge-text: #ffffff; /* Original dark mode value */
70
+
71
+ /* SlideoverController colors */
72
+ --slideover-darken-bg: rgba(0, 0, 0, 0.5); /* Original dark mode value */
73
+ --slideover-breadcrumb-hover-opacity: 1; /* Original dark mode value */
74
+ --slideover-breadcrumb-default-opacity: 0.5; /* Original dark mode value */
75
+ --slideover-handle-bg: #eeeeee; /* Original dark mode value */
76
+ --slideover-handle-hover-width: 4px; /* Original dark mode value */
77
+ --slideover-handle-hover-height: 24px; /* Original dark mode value */
78
+ --slideover-handle-hover-opacity: 1; /* Original dark mode value */
79
+ --slideover-handle-default-width: 2px; /* Original dark mode value */
80
+ --slideover-handle-default-height: 20px; /* Original dark mode value */
81
+ --slideover-handle-default-opacity: 0; /* Original dark mode value */
82
+
83
+ /* SplitViewController colors */
84
+ --splitview-handle-bg: #eeeeee; /* Original dark mode value */
85
+ --splitview-handle-hover-width: 4px; /* Original dark mode value */
86
+ --splitview-handle-hover-height: 24px; /* Original dark mode value */
87
+ --splitview-handle-hover-opacity: 1; /* Original dark mode value */
88
+ --splitview-handle-default-width: 2px; /* Original dark mode value */
89
+ --splitview-handle-default-height: 20px; /* Original dark mode value */
90
+ --splitview-handle-default-opacity: 0; /* Original dark mode value */
91
+ }
@@ -0,0 +1,7 @@
1
+ export interface Pane {
2
+ name: string;
3
+ background: string;
4
+ foreground: string;
5
+ component: any;
6
+ props: Record<string, any>;
7
+ }