@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,176 @@
1
+ <!--SplitViewController.vue-->
2
+ <script setup lang="ts">
3
+ import { gsap } from "gsap";
4
+ import { Flip } from "gsap/Flip";
5
+ import { ref, onMounted } from "vue";
6
+ import { useSplitViewController } from "./useSplitViewController";
7
+ import "../theme.css";
8
+ import { Pane } from "../types";
9
+
10
+ gsap.registerPlugin(Flip);
11
+
12
+ // Define props
13
+ const props = defineProps<{
14
+ panes: Array<Pane>;
15
+ controller?: ReturnType<typeof useSplitViewController>;
16
+ componentId?: string;
17
+ }>();
18
+
19
+ // Use provided componentId or generate a unique one
20
+ const componentId =
21
+ props.componentId ||
22
+ `panel-layout-${Math.random().toString(36).substr(2, 9)}`;
23
+
24
+ // Use provided controller or create internal one
25
+ const controller =
26
+ props.controller || useSplitViewController(props.panes, componentId);
27
+
28
+ // Helper functions to get unique element IDs
29
+ const getOffscreenId = () => `${componentId}-offscreen`;
30
+ const getOnscreenId = () => `${componentId}-onscreen`;
31
+ const getPaneId = (index: number) => `${componentId}-pane-${index}`;
32
+
33
+ // Initialize all panes as visible on mount (they'll be positioned by CSS order)
34
+ onMounted(() => {
35
+ // The controller already initializes visiblePanes, so we just need to ensure
36
+ // the DOM structure matches the initial state
37
+ const onscreen = document.getElementById(getOnscreenId());
38
+ const offscreen = document.getElementById(getOffscreenId());
39
+
40
+ if (onscreen && offscreen) {
41
+ // Move any panes that should be hidden to offscreen
42
+ for (let i = 0; i < props.panes.length - 1; i++) {
43
+ if (!controller.visiblePanes.value[i]) {
44
+ const pane = document.getElementById(getPaneId(i));
45
+ if (pane && pane.parentNode === onscreen) {
46
+ pane.parentNode.removeChild(pane);
47
+ offscreen.appendChild(pane);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ });
53
+
54
+ // Mouse event handlers
55
+ const onMouseEnter = (event: Event) => {
56
+ const element = (event.target as HTMLElement).lastChild;
57
+ gsap.to(element, {
58
+ duration: 0.5,
59
+ width: "var(--splitview-handle-hover-width)",
60
+ height: "var(--splitview-handle-hover-height)",
61
+ opacity: "var(--splitview-handle-hover-opacity)",
62
+ ease: "circ.inOut",
63
+ });
64
+ };
65
+
66
+ const onMouseLeave = (event: Event) => {
67
+ const element = (event.target as HTMLElement).lastChild;
68
+ gsap.to(element, {
69
+ duration: 0.5,
70
+ width: "var(--splitview-handle-default-width)",
71
+ height: "var(--splitview-handle-default-height)",
72
+ opacity: "var(--splitview-handle-default-opacity)",
73
+ ease: "circ.outOut",
74
+ });
75
+ };
76
+ </script>
77
+
78
+ <template>
79
+ <div :class="$style.container">
80
+ <div :id="getOffscreenId()" :class="$style.offscreen"></div>
81
+ <div :id="getOnscreenId()" :class="$style.onscreen">
82
+ <div
83
+ v-for="(pane, index) in panes"
84
+ :key="index"
85
+ :id="getPaneId(index)"
86
+ :class="[
87
+ $style.pane,
88
+ { [$style.pane_full]: index === panes.length - 1 },
89
+ ]"
90
+ :style="{ order: index }"
91
+ >
92
+ <div
93
+ :class="$style.component_container"
94
+ :style="{ backgroundColor: pane.background }"
95
+ >
96
+ <component :is="pane.component" v-bind="pane.props"></component>
97
+ </div>
98
+ <div
99
+ v-if="index > 0"
100
+ :class="$style.handle_container"
101
+ @mouseenter="onMouseEnter"
102
+ @mouseleave="onMouseLeave"
103
+ @click="() => controller.togglePane(index)"
104
+ :style="{ backgroundColor: 'transparent' }"
105
+ >
106
+ <div
107
+ :class="$style.handle"
108
+ :style="{ backgroundColor: pane.foreground }"
109
+ ></div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </template>
115
+
116
+ <style module>
117
+ .container {
118
+ position: relative;
119
+ display: grid;
120
+ grid-template-areas: "content";
121
+ height: 100%;
122
+ width: 100%;
123
+ grid-template-rows: 1fr;
124
+ grid-template-columns: 1fr;
125
+ overflow: hidden;
126
+ }
127
+ .offscreen {
128
+ position: absolute;
129
+ top: 0;
130
+ left: -20rem;
131
+ bottom: 0;
132
+ width: 20rem;
133
+ display: flex;
134
+ justify-content: end;
135
+ }
136
+ .onscreen {
137
+ grid-area: content;
138
+ display: flex;
139
+ height: 100%;
140
+ }
141
+ .pane {
142
+ display: grid;
143
+ grid-template-columns: 1fr;
144
+ grid-template-areas: "content";
145
+ height: 100%;
146
+ /* Ensure non-full panes size to their content (e.g., Organizer's fixed width) */
147
+ flex: 0 0 auto;
148
+ }
149
+ .pane_full {
150
+ /* Allow the last pane to grow and shrink to fill remaining space */
151
+ flex: 1 1 auto;
152
+ min-width: 0;
153
+ }
154
+ .component_container {
155
+ grid-area: content;
156
+ overflow: hidden;
157
+ height: 100%;
158
+ }
159
+ .handle_container {
160
+ grid-area: content;
161
+ width: 15px;
162
+ min-height: 30px;
163
+ max-height: 100%;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ height: 100%;
168
+ }
169
+ .handle {
170
+ width: var(--splitview-handle-default-width);
171
+ height: var(--splitview-handle-default-height);
172
+ opacity: var(--splitview-handle-default-opacity);
173
+ background-color: var(--splitview-handle-bg);
174
+ border-radius: 999px;
175
+ }
176
+ </style>
@@ -0,0 +1,388 @@
1
+ //useSplitViewController.ts
2
+ import { ref, Ref, computed, unref, watch } from "vue";
3
+ import { gsap } from "gsap";
4
+ import { Flip } from "gsap/Flip";
5
+ import { Pane } from "../types";
6
+
7
+ gsap.registerPlugin(Flip);
8
+
9
+ export const useSplitViewController = (
10
+ panes: Pane[] | Ref<Pane[]>,
11
+ componentId: string
12
+ ) => {
13
+ // Get the actual panes array
14
+ const panesArray = computed(() => unref(panes));
15
+
16
+ // State to track visibility of panes
17
+ const visiblePanes: Ref<boolean[]> = ref(panesArray.value.map(() => true));
18
+
19
+ // Vue-native event stream for visibility changes
20
+ const lastVisibilityEvent: Ref<{
21
+ type: "show" | "hide";
22
+ index: number;
23
+ } | null> = ref(null);
24
+
25
+ // Watch for changes in panes and update visiblePanes accordingly
26
+ watch(panesArray, (newPanes) => {
27
+ visiblePanes.value = newPanes.map((_, index) =>
28
+ index === newPanes.length - 1 ? true : visiblePanes.value[index] || false
29
+ );
30
+ });
31
+
32
+ // Track the current visible pane indices
33
+ const currentPaneIndex = computed(() => {
34
+ // Find the highest index that's visible (excluding the last pane which is always visible)
35
+ for (let i = visiblePanes.value.length - 2; i >= 0; i--) {
36
+ if (visiblePanes.value[i]) {
37
+ return i;
38
+ }
39
+ }
40
+ return -1;
41
+ });
42
+
43
+ // Helper functions to get unique element IDs
44
+ const getOffscreenId = () => `${componentId}-offscreen`;
45
+ const getOnscreenId = () => `${componentId}-onscreen`;
46
+ const getPaneId = (index: number) => `${componentId}-pane-${index}`;
47
+
48
+ // Show a specific pane
49
+ const showPane = (index: number) => {
50
+ if (index < 0 || index >= panesArray.value.length - 1) return;
51
+
52
+ const offscreen = document.getElementById(getOffscreenId());
53
+ const onscreen = document.getElementById(getOnscreenId());
54
+
55
+ if (!offscreen || !onscreen) {
56
+ console.error(
57
+ `Elements are null for key '${componentId}'. Unable to animate`
58
+ );
59
+ return;
60
+ }
61
+
62
+ // Get state for animation
63
+ const paneElements = panesArray.value
64
+ .map((_, i) => document.getElementById(getPaneId(i)))
65
+ .filter((el) => el !== null);
66
+ const state = Flip.getState(paneElements);
67
+
68
+ // Show the specified pane
69
+ const pane = document.getElementById(getPaneId(index));
70
+ if (pane && !visiblePanes.value[index]) {
71
+ visiblePanes.value[index] = true;
72
+ pane.parentNode?.removeChild(pane);
73
+
74
+ // Insert at correct position
75
+ let insertBefore = null;
76
+ for (let i = index + 1; i < panesArray.value.length; i++) {
77
+ const nextPane = document.getElementById(getPaneId(i));
78
+ if (nextPane && nextPane.parentNode === onscreen) {
79
+ insertBefore = nextPane;
80
+ break;
81
+ }
82
+ }
83
+
84
+ if (insertBefore) {
85
+ onscreen.insertBefore(pane, insertBefore);
86
+ } else {
87
+ onscreen.appendChild(pane);
88
+ }
89
+
90
+ lastVisibilityEvent.value = { type: "show", index };
91
+ }
92
+
93
+ // Animate
94
+ Flip.from(state, {
95
+ duration: 0.3,
96
+ ease: "power1.inOut",
97
+ absolute: true,
98
+ });
99
+ };
100
+
101
+ // Hide a specific pane
102
+ const hidePane = (index: number) => {
103
+ if (index < 0 || index >= panesArray.value.length - 1) return;
104
+
105
+ const offscreen = document.getElementById(getOffscreenId());
106
+ const onscreen = document.getElementById(getOnscreenId());
107
+
108
+ if (!offscreen || !onscreen) {
109
+ console.error(
110
+ `Elements are null for key '${componentId}'. Unable to animate`
111
+ );
112
+ return;
113
+ }
114
+
115
+ // Get state for animation
116
+ const paneElements = panesArray.value
117
+ .map((_, i) => document.getElementById(getPaneId(i)))
118
+ .filter((el) => el !== null);
119
+ const state = Flip.getState(paneElements);
120
+
121
+ // Hide the specified pane
122
+ const pane = document.getElementById(getPaneId(index));
123
+ if (pane && visiblePanes.value[index]) {
124
+ visiblePanes.value[index] = false;
125
+ pane.parentNode?.removeChild(pane);
126
+ offscreen.appendChild(pane);
127
+ lastVisibilityEvent.value = { type: "hide", index };
128
+ }
129
+
130
+ // Animate
131
+ Flip.from(state, {
132
+ duration: 0.3,
133
+ ease: "power1.inOut",
134
+ absolute: true,
135
+ });
136
+ };
137
+
138
+ // Toggle a pane at a specific index (handle click behavior)
139
+ const togglePane = (index: number) => {
140
+ if (index <= 0) return;
141
+
142
+ const offscreen = document.getElementById(getOffscreenId());
143
+ const onscreen = document.getElementById(getOnscreenId());
144
+
145
+ if (!offscreen || !onscreen) {
146
+ console.error(
147
+ `Elements are null for key '${componentId}'. Unable to animate`
148
+ );
149
+ return;
150
+ }
151
+
152
+ // Get state for animation
153
+ const paneElements = panesArray.value
154
+ .map((_, i) => document.getElementById(getPaneId(i)))
155
+ .filter((el) => el !== null);
156
+ const state = Flip.getState(paneElements);
157
+
158
+ // Hide all panes before the clicked pane
159
+ if (visiblePanes.value[index - 1]) {
160
+ for (let i = 0; i < index; i++) {
161
+ const pane = document.getElementById(getPaneId(i));
162
+ if (pane) {
163
+ visiblePanes.value[i] = false;
164
+ pane.parentNode?.removeChild(pane);
165
+ offscreen.appendChild(pane);
166
+ lastVisibilityEvent.value = { type: "hide", index: i };
167
+ }
168
+ }
169
+ } else {
170
+ // Show only the pane immediately before the clicked pane
171
+ const pane = document.getElementById(getPaneId(index - 1));
172
+ if (pane) {
173
+ visiblePanes.value[index - 1] = true;
174
+ pane.parentNode?.removeChild(pane);
175
+ onscreen.insertBefore(pane, onscreen.children[index - 1] || null);
176
+ lastVisibilityEvent.value = { type: "show", index: index - 1 };
177
+ }
178
+ }
179
+
180
+ // Animate
181
+ Flip.from(state, {
182
+ duration: 0.3,
183
+ ease: "power1.inOut",
184
+ absolute: true,
185
+ });
186
+ };
187
+
188
+ // Show all panes
189
+ const showAll = () => {
190
+ const offscreen = document.getElementById(getOffscreenId());
191
+ const onscreen = document.getElementById(getOnscreenId());
192
+
193
+ if (!offscreen || !onscreen) {
194
+ console.error(
195
+ `Elements are null for key '${componentId}'. Unable to animate`
196
+ );
197
+ return;
198
+ }
199
+
200
+ const paneElements = panesArray.value
201
+ .map((_, i) => document.getElementById(getPaneId(i)))
202
+ .filter((el) => el !== null);
203
+ const state = Flip.getState(paneElements);
204
+
205
+ // Show all panes except the last one (which is always visible)
206
+ for (let i = 0; i < panesArray.value.length - 1; i++) {
207
+ const pane = document.getElementById(getPaneId(i));
208
+ if (pane && !visiblePanes.value[i]) {
209
+ visiblePanes.value[i] = true;
210
+ pane.parentNode?.removeChild(pane);
211
+
212
+ // Insert at correct position
213
+ let insertBefore = null;
214
+ for (let j = i + 1; j < panesArray.value.length; j++) {
215
+ const nextPane = document.getElementById(getPaneId(j));
216
+ if (nextPane && nextPane.parentNode === onscreen) {
217
+ insertBefore = nextPane;
218
+ break;
219
+ }
220
+ }
221
+
222
+ if (insertBefore) {
223
+ onscreen.insertBefore(pane, insertBefore);
224
+ } else {
225
+ onscreen.appendChild(pane);
226
+ }
227
+
228
+ lastVisibilityEvent.value = { type: "show", index: i };
229
+ }
230
+ }
231
+
232
+ Flip.from(state, {
233
+ duration: 0.3,
234
+ ease: "power1.inOut",
235
+ absolute: true,
236
+ });
237
+ };
238
+
239
+ // Hide all panes except the last one
240
+ const hideAll = () => {
241
+ const offscreen = document.getElementById(getOffscreenId());
242
+ const onscreen = document.getElementById(getOnscreenId());
243
+
244
+ if (!offscreen || !onscreen) {
245
+ console.error(
246
+ `Elements are null for key '${componentId}'. Unable to animate`
247
+ );
248
+ return;
249
+ }
250
+
251
+ const paneElements = panesArray.value
252
+ .map((_, i) => document.getElementById(getPaneId(i)))
253
+ .filter((el) => el !== null);
254
+ const state = Flip.getState(paneElements);
255
+
256
+ // Hide all panes except the last one
257
+ for (let i = 0; i < panesArray.value.length - 1; i++) {
258
+ const pane = document.getElementById(getPaneId(i));
259
+ if (pane && visiblePanes.value[i]) {
260
+ visiblePanes.value[i] = false;
261
+ pane.parentNode?.removeChild(pane);
262
+ offscreen.appendChild(pane);
263
+ lastVisibilityEvent.value = { type: "hide", index: i };
264
+ }
265
+ }
266
+
267
+ Flip.from(state, {
268
+ duration: 0.3,
269
+ ease: "power1.inOut",
270
+ absolute: true,
271
+ });
272
+ };
273
+
274
+ // Navigate to show only a specific pane (and hide others)
275
+ const navigate = (index: number, animated: boolean = true) => {
276
+ if (index < 0 || index >= panesArray.value.length) return;
277
+
278
+ // If navigating to the last pane, hide all others
279
+ if (index === panesArray.value.length - 1) {
280
+ hideAll();
281
+ return;
282
+ }
283
+
284
+ const offscreen = document.getElementById(getOffscreenId());
285
+ const onscreen = document.getElementById(getOnscreenId());
286
+
287
+ if (!offscreen || !onscreen) {
288
+ console.error(
289
+ `Elements are null for key '${componentId}'. Unable to animate`
290
+ );
291
+ return;
292
+ }
293
+
294
+ if (!animated) {
295
+ // Direct manipulation without animation
296
+ for (let i = 0; i < panesArray.value.length - 1; i++) {
297
+ const pane = document.getElementById(getPaneId(i));
298
+ if (pane) {
299
+ pane.parentNode?.removeChild(pane);
300
+ if (i === index) {
301
+ visiblePanes.value[i] = true;
302
+ // Find correct position
303
+ let insertBefore = null;
304
+ for (let j = i + 1; j < panesArray.value.length; j++) {
305
+ const nextPane = document.getElementById(getPaneId(j));
306
+ if (nextPane && nextPane.parentNode === onscreen) {
307
+ insertBefore = nextPane;
308
+ break;
309
+ }
310
+ }
311
+
312
+ if (insertBefore) {
313
+ onscreen.insertBefore(pane, insertBefore);
314
+ } else {
315
+ onscreen.appendChild(pane);
316
+ }
317
+ lastVisibilityEvent.value = { type: "show", index: i };
318
+ } else {
319
+ visiblePanes.value[i] = false;
320
+ offscreen.appendChild(pane);
321
+ lastVisibilityEvent.value = { type: "hide", index: i };
322
+ }
323
+ }
324
+ }
325
+ } else {
326
+ // Animated navigation
327
+ const paneElements = panesArray.value
328
+ .map((_, i) => document.getElementById(getPaneId(i)))
329
+ .filter((el) => el !== null);
330
+ const state = Flip.getState(paneElements);
331
+
332
+ // First hide all panes
333
+ for (let i = 0; i < panesArray.value.length - 1; i++) {
334
+ if (i !== index && visiblePanes.value[i]) {
335
+ const pane = document.getElementById(getPaneId(i));
336
+ if (pane) {
337
+ visiblePanes.value[i] = false;
338
+ pane.parentNode?.removeChild(pane);
339
+ offscreen.appendChild(pane);
340
+ lastVisibilityEvent.value = { type: "hide", index: i };
341
+ }
342
+ }
343
+ }
344
+
345
+ // Then show the target pane
346
+ const targetPane = document.getElementById(getPaneId(index));
347
+ if (targetPane && !visiblePanes.value[index]) {
348
+ visiblePanes.value[index] = true;
349
+ targetPane.parentNode?.removeChild(targetPane);
350
+
351
+ // Find correct position
352
+ let insertBefore = null;
353
+ for (let j = index + 1; j < panesArray.value.length; j++) {
354
+ const nextPane = document.getElementById(getPaneId(j));
355
+ if (nextPane && nextPane.parentNode === onscreen) {
356
+ insertBefore = nextPane;
357
+ break;
358
+ }
359
+ }
360
+
361
+ if (insertBefore) {
362
+ onscreen.insertBefore(targetPane, insertBefore);
363
+ } else {
364
+ onscreen.appendChild(targetPane);
365
+ }
366
+ lastVisibilityEvent.value = { type: "show", index };
367
+ }
368
+
369
+ Flip.from(state, {
370
+ duration: 0.3,
371
+ ease: "power1.inOut",
372
+ absolute: true,
373
+ });
374
+ }
375
+ };
376
+
377
+ return {
378
+ visiblePanes,
379
+ currentPaneIndex,
380
+ lastVisibilityEvent,
381
+ showPane,
382
+ hidePane,
383
+ togglePane,
384
+ showAll,
385
+ hideAll,
386
+ navigate,
387
+ };
388
+ };