@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.
- package/dist/components/controls/Dropdown/types.d.ts +5 -0
- package/dist/components/controls/Dropdown/types.d.ts.map +1 -0
- package/dist/components/controls/Dropdown/types.js +1 -0
- package/dist/components/controls/SegmentedControl/types.d.ts +6 -0
- package/dist/components/controls/SegmentedControl/types.d.ts.map +1 -0
- package/dist/components/controls/SegmentedControl/types.js +1 -0
- package/dist/components/dialogs/Alert/types.d.ts +7 -0
- package/dist/components/dialogs/Alert/types.d.ts.map +1 -0
- package/dist/components/dialogs/Alert/types.js +1 -0
- package/dist/components/dialogs/Toast/types.d.ts +34 -0
- package/dist/components/dialogs/Toast/types.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/types.js +10 -0
- package/dist/components/dialogs/Toast/useToast.d.ts +36 -0
- package/dist/components/dialogs/Toast/useToast.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/useToast.js +90 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts +3 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/tooltip.js +33 -0
- package/dist/components/indicators/Tooltip/types.d.ts +14 -0
- package/dist/components/indicators/Tooltip/types.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/types.js +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts +18 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.js +57 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts +14 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts.map +1 -0
- package/dist/components/inputs/Tags/tag-bar-styles.js +313 -0
- package/dist/components/inputs/Tags/types.d.ts +93 -0
- package/dist/components/inputs/Tags/types.d.ts.map +1 -0
- package/dist/components/inputs/Tags/types.js +216 -0
- package/dist/components/inputs/search/types.d.ts +9 -0
- package/dist/components/inputs/search/types.d.ts.map +1 -0
- package/dist/components/inputs/search/types.js +1 -0
- package/dist/components/navigation/adaptive/types.d.ts +16 -0
- package/dist/components/navigation/adaptive/types.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/types.js +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts +27 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.js +40 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts +6 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useBreakpoints.js +37 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts +93 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.js +145 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts +31 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewAnimation.js +591 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts +52 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewResize.js +146 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts +25 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts.map +1 -0
- package/dist/components/navigation/navstack/useNavigationStack.js +133 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts +20 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts.map +1 -0
- package/dist/components/navigation/slideover/useSlideoverController.js +267 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts +20 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts.map +1 -0
- package/dist/components/navigation/splitview/useSplitViewController.js +325 -0
- package/dist/components/navigation/tabcontroller/types.d.ts +21 -0
- package/dist/components/navigation/tabcontroller/types.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/types.js +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts +5 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.js +10 -0
- package/dist/components/navigation/types.d.ts +8 -0
- package/dist/components/navigation/types.d.ts.map +1 -0
- package/dist/components/navigation/types.js +1 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts +11 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts.map +1 -0
- package/dist/components/pickers/CollectionPicker/types.js +1 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts +13 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts.map +1 -0
- package/dist/components/pickers/ColorPicker/colors.js +266 -0
- package/dist/components/pickers/FilePicker/types.d.ts +10 -0
- package/dist/components/pickers/FilePicker/types.d.ts.map +1 -0
- package/dist/components/pickers/FilePicker/types.js +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/theme.d.ts +73 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +279 -0
- package/dist/themes/blank.d.ts +7 -0
- package/dist/themes/blank.d.ts.map +1 -0
- package/dist/themes/blank.js +543 -0
- package/dist/themes/crimson-dark.d.ts +4 -0
- package/dist/themes/crimson-dark.d.ts.map +1 -0
- package/dist/themes/crimson-dark.js +552 -0
- package/dist/themes/cyan-light.d.ts +4 -0
- package/dist/themes/cyan-light.d.ts.map +1 -0
- package/dist/themes/cyan-light.js +552 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +551 -0
- package/dist/themes/gold-dark.d.ts +4 -0
- package/dist/themes/gold-dark.d.ts.map +1 -0
- package/dist/themes/gold-dark.js +552 -0
- package/dist/themes/grass-dark.d.ts +4 -0
- package/dist/themes/grass-dark.d.ts.map +1 -0
- package/dist/themes/grass-dark.js +552 -0
- package/dist/themes/indigo.d.ts +4 -0
- package/dist/themes/indigo.d.ts.map +1 -0
- package/dist/themes/indigo.js +552 -0
- package/dist/themes/light.d.ts +4 -0
- package/dist/themes/light.d.ts.map +1 -0
- package/dist/themes/light.js +551 -0
- package/dist/themes/orange-dark.d.ts +4 -0
- package/dist/themes/orange-dark.d.ts.map +1 -0
- package/dist/themes/orange-dark.js +551 -0
- package/dist/themes/orange-light.d.ts +4 -0
- package/dist/themes/orange-light.d.ts.map +1 -0
- package/dist/themes/orange-light.js +551 -0
- package/package.json +62 -0
- package/src/components/controls/Button/Button.vue +417 -0
- package/src/components/controls/Button/README.md +348 -0
- package/src/components/controls/Button/theme.css +200 -0
- package/src/components/controls/Checkbox/Checkbox.vue +164 -0
- package/src/components/controls/Checkbox/README.md +441 -0
- package/src/components/controls/Checkbox/theme.css +36 -0
- package/src/components/controls/Dropdown/Dropdown.vue +476 -0
- package/src/components/controls/Dropdown/README.md +370 -0
- package/src/components/controls/Dropdown/theme.css +50 -0
- package/src/components/controls/Dropdown/types.ts +6 -0
- package/src/components/controls/IconButton/IconButton.vue +267 -0
- package/src/components/controls/IconButton/README.md +502 -0
- package/src/components/controls/IconButton/theme.css +89 -0
- package/src/components/controls/Radio/README.md +591 -0
- package/src/components/controls/Radio/Radio.vue +89 -0
- package/src/components/controls/Radio/theme.css +14 -0
- package/src/components/controls/RangeSlider/README.md +608 -0
- package/src/components/controls/RangeSlider/RangeSlider.vue +535 -0
- package/src/components/controls/RangeSlider/theme.css +80 -0
- package/src/components/controls/SegmentedControl/README.md +587 -0
- package/src/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
- package/src/components/controls/SegmentedControl/theme.css +60 -0
- package/src/components/controls/SegmentedControl/types.ts +5 -0
- package/src/components/controls/Slider/README.md +627 -0
- package/src/components/controls/Slider/Slider.vue +260 -0
- package/src/components/controls/Slider/theme.css +74 -0
- package/src/components/controls/Stepper/README.md +601 -0
- package/src/components/controls/Stepper/Stepper.vue +103 -0
- package/src/components/controls/Stepper/theme.css +53 -0
- package/src/components/controls/Switch/README.md +667 -0
- package/src/components/controls/Switch/Switch.vue +127 -0
- package/src/components/controls/Switch/theme.css +42 -0
- package/src/components/dialogs/Alert/Alert.vue +218 -0
- package/src/components/dialogs/Alert/README.md +450 -0
- package/src/components/dialogs/Alert/theme.css +44 -0
- package/src/components/dialogs/Alert/types.ts +11 -0
- package/src/components/dialogs/Toast/README.md +522 -0
- package/src/components/dialogs/Toast/Toast.vue +296 -0
- package/src/components/dialogs/Toast/ToastContainer.vue +330 -0
- package/src/components/dialogs/Toast/theme.css +44 -0
- package/src/components/dialogs/Toast/types.ts +46 -0
- package/src/components/dialogs/Toast/useToast.ts +127 -0
- package/src/components/indicators/ProgressBar/ProgressBar.vue +98 -0
- package/src/components/indicators/ProgressBar/README.md +744 -0
- package/src/components/indicators/ProgressBar/theme.css +36 -0
- package/src/components/indicators/Tooltip/README.md +723 -0
- package/src/components/indicators/Tooltip/TooltipProvider.vue +142 -0
- package/src/components/indicators/Tooltip/theme.css +18 -0
- package/src/components/indicators/Tooltip/tooltip.ts +48 -0
- package/src/components/indicators/Tooltip/types.ts +15 -0
- package/src/components/indicators/Tooltip/useTooltip.ts +71 -0
- package/src/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
- package/src/components/inputs/AutogrowTextView/README.md +643 -0
- package/src/components/inputs/AutogrowTextView/theme.css +28 -0
- package/src/components/inputs/InputCard/InputCard.vue +600 -0
- package/src/components/inputs/InputCard/README.md +636 -0
- package/src/components/inputs/InputEmail/InputEmail.vue +698 -0
- package/src/components/inputs/InputEmail/README.md +764 -0
- package/src/components/inputs/InputNumber/InputNumber.vue +300 -0
- package/src/components/inputs/InputNumber/README.md +749 -0
- package/src/components/inputs/InputPhone/InputPhone.vue +645 -0
- package/src/components/inputs/InputPhone/README.md +636 -0
- package/src/components/inputs/InputSecure/InputSecure.vue +646 -0
- package/src/components/inputs/InputSecure/README.md +771 -0
- package/src/components/inputs/InputText/InputText.vue +225 -0
- package/src/components/inputs/InputText/README.md +844 -0
- package/src/components/inputs/OTP/OTP.vue +349 -0
- package/src/components/inputs/OTP/README.md +736 -0
- package/src/components/inputs/OTP/theme.css +50 -0
- package/src/components/inputs/StringCapture/README.md +718 -0
- package/src/components/inputs/StringCapture/StringCapture.vue +315 -0
- package/src/components/inputs/StringCapture/theme.css +86 -0
- package/src/components/inputs/Tags/README.md +897 -0
- package/src/components/inputs/Tags/TagBar.vue +793 -0
- package/src/components/inputs/Tags/TagCreation.vue +219 -0
- package/src/components/inputs/Tags/TagPicker.vue +380 -0
- package/src/components/inputs/Tags/tag-bar-styles.ts +354 -0
- package/src/components/inputs/Tags/theme.css +121 -0
- package/src/components/inputs/Tags/types.ts +346 -0
- package/src/components/inputs/search/README.md +759 -0
- package/src/components/inputs/search/SearchBar.vue +394 -0
- package/src/components/inputs/search/SearchResults.vue +310 -0
- package/src/components/inputs/search/theme.css +187 -0
- package/src/components/inputs/search/types.ts +8 -0
- package/src/components/inputs/theme.css +102 -0
- package/src/components/menus/ActionMenu/ActionMenu.vue +383 -0
- package/src/components/menus/ActionMenu/README.md +825 -0
- package/src/components/menus/ActionMenu/theme.css +93 -0
- package/src/components/models/Popover/Popover.vue +551 -0
- package/src/components/models/Popover/README.md +885 -0
- package/src/components/models/Popover/theme.css +52 -0
- package/src/components/models/Sheet/README.md +1159 -0
- package/src/components/models/Sheet/Sheet.vue +465 -0
- package/src/components/models/Sheet/theme.css +72 -0
- package/src/components/models/Sidebar/README.md +1228 -0
- package/src/components/models/Sidebar/Sidebar.vue +480 -0
- package/src/components/models/Sidebar/theme.css +90 -0
- package/src/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
- package/src/components/navigation/adaptive/README.md +768 -0
- package/src/components/navigation/adaptive/types.ts +19 -0
- package/src/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
- package/src/components/navigation/adaptive/useBreakpoints.ts +41 -0
- package/src/components/navigation/adaptive/useContainerMonitor.ts +214 -0
- package/src/components/navigation/adaptive/useViewAnimation.ts +721 -0
- package/src/components/navigation/adaptive/useViewResize.ts +211 -0
- package/src/components/navigation/navstack/NavigationStack.vue +180 -0
- package/src/components/navigation/navstack/README.md +994 -0
- package/src/components/navigation/navstack/useNavigationStack.ts +164 -0
- package/src/components/navigation/slideover/README.md +1275 -0
- package/src/components/navigation/slideover/SlideoverController.vue +287 -0
- package/src/components/navigation/slideover/useSlideoverController.ts +320 -0
- package/src/components/navigation/splitview/README.md +1115 -0
- package/src/components/navigation/splitview/SplitViewController.vue +176 -0
- package/src/components/navigation/splitview/useSplitViewController.ts +388 -0
- package/src/components/navigation/tabcontroller/README.md +919 -0
- package/src/components/navigation/tabcontroller/TabController.vue +307 -0
- package/src/components/navigation/tabcontroller/TabItem.vue +57 -0
- package/src/components/navigation/tabcontroller/types.ts +24 -0
- package/src/components/navigation/tabcontroller/useTabController.ts +18 -0
- package/src/components/navigation/theme.css +91 -0
- package/src/components/navigation/types.ts +7 -0
- package/src/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
- package/src/components/pickers/CollectionPicker/README.md +1115 -0
- package/src/components/pickers/CollectionPicker/theme.css +14 -0
- package/src/components/pickers/CollectionPicker/types.ts +11 -0
- package/src/components/pickers/ColorPicker/ColorPicker.vue +376 -0
- package/src/components/pickers/ColorPicker/README.md +1439 -0
- package/src/components/pickers/ColorPicker/colors.ts +299 -0
- package/src/components/pickers/ColorPicker/theme.css +32 -0
- package/src/components/pickers/DatePicker/DatePicker.vue +660 -0
- package/src/components/pickers/DatePicker/README.md +1195 -0
- package/src/components/pickers/DatePicker/theme.css +22 -0
- package/src/components/pickers/FilePicker/FilePicker.vue +534 -0
- package/src/components/pickers/FilePicker/README.md +1542 -0
- package/src/components/pickers/FilePicker/theme.css +48 -0
- package/src/components/pickers/FilePicker/types.ts +10 -0
- package/src/components/pickers/IconPicker/IconPicker.vue +327 -0
- package/src/components/pickers/IconPicker/README.md +1161 -0
- package/src/components/pickers/IconPicker/theme.css +28 -0
- package/src/components/pickers/theme.css +82 -0
- package/src/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
- package/src/components/views/MarkdownViewer/README.md +833 -0
- package/src/components/views/MarkdownViewer/theme.css +130 -0
- package/src/index.ts +263 -0
- package/src/theme.ts +378 -0
- package/src/themes/crimson-dark.ts +556 -0
- package/src/themes/cyan-light.ts +556 -0
- package/src/themes/dark.ts +557 -0
- package/src/themes/gold-dark.ts +556 -0
- package/src/themes/grass-dark.ts +556 -0
- package/src/themes/indigo.ts +556 -0
- package/src/themes/light.ts +557 -0
- package/src/themes/orange-dark.ts +557 -0
- package/src/themes/orange-light.ts +557 -0
- package/src/vue.d.ts +45 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
# NavigationStack
|
|
2
|
+
|
|
3
|
+
A powerful navigation stack component built with Vue 3 Composition API and TypeScript. The NavigationStack provides smooth, animated transitions between different views using GSAP's FLIP (First, Last, Invert, Play) animation technique, creating a native app-like navigation experience.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { NavigationStack, useNavigationStack } from "@umbra-ui/core";
|
|
9
|
+
import type { Pane } from "@umbra-ui/core";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Dependencies:**
|
|
13
|
+
|
|
14
|
+
- Vue 3.x
|
|
15
|
+
- gsap (for animations)
|
|
16
|
+
- gsap/Flip (for FLIP animations)
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
```vue
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import { ref } from "vue";
|
|
23
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
24
|
+
import HomeView from "./HomeView.vue";
|
|
25
|
+
import ProfileView from "./ProfileView.vue";
|
|
26
|
+
import SettingsView from "./SettingsView.vue";
|
|
27
|
+
|
|
28
|
+
// Define your navigation panes
|
|
29
|
+
const panes = ref([
|
|
30
|
+
{
|
|
31
|
+
name: "home",
|
|
32
|
+
background: "#ffffff",
|
|
33
|
+
foreground: "#000000",
|
|
34
|
+
component: HomeView,
|
|
35
|
+
props: { title: "Home" },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "profile",
|
|
39
|
+
background: "#f8f9fa",
|
|
40
|
+
foreground: "#212529",
|
|
41
|
+
component: ProfileView,
|
|
42
|
+
props: { userId: 123 },
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "settings",
|
|
46
|
+
background: "#e9ecef",
|
|
47
|
+
foreground: "#495057",
|
|
48
|
+
component: SettingsView,
|
|
49
|
+
props: { theme: "light" },
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const navigationRef = ref();
|
|
54
|
+
|
|
55
|
+
// Navigate to specific pane
|
|
56
|
+
const goToPane = (index: number) => {
|
|
57
|
+
navigationRef.value?.navigate(index);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Add new pane
|
|
61
|
+
const addNewPane = () => {
|
|
62
|
+
navigationRef.value?.addPane(
|
|
63
|
+
{
|
|
64
|
+
name: "new",
|
|
65
|
+
background: "#007bff",
|
|
66
|
+
foreground: "#ffffff",
|
|
67
|
+
component: HomeView,
|
|
68
|
+
props: { title: "New View" },
|
|
69
|
+
},
|
|
70
|
+
true
|
|
71
|
+
); // Navigate to new pane
|
|
72
|
+
};
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<div class="app">
|
|
77
|
+
<div class="navigation-controls">
|
|
78
|
+
<button @click="goToPane(0)">Home</button>
|
|
79
|
+
<button @click="goToPane(1)">Profile</button>
|
|
80
|
+
<button @click="goToPane(2)">Settings</button>
|
|
81
|
+
<button @click="addNewPane">Add New</button>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<NavigationStack
|
|
85
|
+
ref="navigationRef"
|
|
86
|
+
task-key="main-navigation"
|
|
87
|
+
:panes="panes"
|
|
88
|
+
:child-width="800"
|
|
89
|
+
:child-height="600"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<style module>
|
|
95
|
+
.app {
|
|
96
|
+
height: 100vh;
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.navigation-controls {
|
|
102
|
+
display: flex;
|
|
103
|
+
gap: 12px;
|
|
104
|
+
padding: 16px;
|
|
105
|
+
background: var(--background-1);
|
|
106
|
+
border-bottom: 1px solid var(--border-2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.navigation-controls button {
|
|
110
|
+
padding: 8px 16px;
|
|
111
|
+
border: 1px solid var(--border-2);
|
|
112
|
+
background: var(--background-2);
|
|
113
|
+
color: var(--text-1);
|
|
114
|
+
border-radius: 6px;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: all 0.2s ease;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.navigation-controls button:hover {
|
|
120
|
+
background: var(--background-3);
|
|
121
|
+
border-color: var(--border-3);
|
|
122
|
+
}
|
|
123
|
+
</style>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Props
|
|
127
|
+
|
|
128
|
+
| Prop Name | Type | Required | Default | Description |
|
|
129
|
+
| ------------- | -------- | -------- | ------- | --------------------------------------------------------------- |
|
|
130
|
+
| `taskKey` | `string` | Yes | - | Unique identifier for the navigation stack instance |
|
|
131
|
+
| `panes` | `Pane[]` | Yes | - | Array of panes to display in the navigation stack |
|
|
132
|
+
| `childWidth` | `number` | No | `null` | Fixed width for child components (uses container width if null) |
|
|
133
|
+
| `childHeight` | `number` | No | `null` | Fixed height for child components (uses 100% if null) |
|
|
134
|
+
|
|
135
|
+
## Pane Interface
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
interface Pane {
|
|
139
|
+
name: string;
|
|
140
|
+
background: string;
|
|
141
|
+
foreground: string;
|
|
142
|
+
component: any;
|
|
143
|
+
props: Record<string, any>;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Pane Properties
|
|
148
|
+
|
|
149
|
+
| Property | Type | Description |
|
|
150
|
+
| ------------ | --------------------- | --------------------------------------- |
|
|
151
|
+
| `name` | `string` | Unique identifier for the pane |
|
|
152
|
+
| `background` | `string` | Background color for the pane container |
|
|
153
|
+
| `foreground` | `string` | Text color for the pane content |
|
|
154
|
+
| `component` | `Component` | Vue component to render in the pane |
|
|
155
|
+
| `props` | `Record<string, any>` | Props to pass to the component |
|
|
156
|
+
|
|
157
|
+
## Events
|
|
158
|
+
|
|
159
|
+
The NavigationStack component doesn't emit events directly, but you can access navigation methods through template refs.
|
|
160
|
+
|
|
161
|
+
## Exposed Methods/Refs
|
|
162
|
+
|
|
163
|
+
| Method | Parameters | Description |
|
|
164
|
+
| ------------- | ------------------------------------------------------ | ------------------------------------ |
|
|
165
|
+
| `navigate` | `(index: number, animated?: boolean)` | Navigate to a specific pane by index |
|
|
166
|
+
| `addPane` | `(pane: Pane, navigateToNew?: boolean)` | Add a new pane to the stack |
|
|
167
|
+
| `removePane` | `(index: number)` | Remove a pane from the stack |
|
|
168
|
+
| `insertPane` | `(index: number, pane: Pane, navigateToNew?: boolean)` | Insert a pane at a specific index |
|
|
169
|
+
| `updatePanes` | `(newPanes: Pane[])` | Update the entire panes array |
|
|
170
|
+
|
|
171
|
+
## useNavigationStack Composable
|
|
172
|
+
|
|
173
|
+
The `useNavigationStack` composable provides the core navigation logic and can be used independently:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { useNavigationStack } from "@umbra-ui/core";
|
|
177
|
+
|
|
178
|
+
const {
|
|
179
|
+
panes,
|
|
180
|
+
visiblePanes,
|
|
181
|
+
currentIndex,
|
|
182
|
+
navigate,
|
|
183
|
+
addPane,
|
|
184
|
+
removePane,
|
|
185
|
+
insertPane,
|
|
186
|
+
updatePanes,
|
|
187
|
+
} = useNavigationStack(initialPanes, taskKey);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Composable Returns
|
|
191
|
+
|
|
192
|
+
| Property | Type | Description |
|
|
193
|
+
| -------------- | -------------------------------------------------------------- | ---------------------------------------- |
|
|
194
|
+
| `panes` | `Ref<Pane[]>` | Reactive array of all panes |
|
|
195
|
+
| `visiblePanes` | `ComputedRef<boolean[]>` | Array indicating which panes are visible |
|
|
196
|
+
| `currentIndex` | `Ref<number>` | Current active pane index |
|
|
197
|
+
| `navigate` | `(index: number, animated?: boolean) => void` | Navigate to specific pane |
|
|
198
|
+
| `addPane` | `(pane: Pane, navigateToNew?: boolean) => void` | Add new pane |
|
|
199
|
+
| `removePane` | `(index: number) => void` | Remove pane |
|
|
200
|
+
| `insertPane` | `(index: number, pane: Pane, navigateToNew?: boolean) => void` | Insert pane |
|
|
201
|
+
| `updatePanes` | `(newPanes: Pane[]) => void` | Update all panes |
|
|
202
|
+
|
|
203
|
+
## CSS Customization
|
|
204
|
+
|
|
205
|
+
### Layout Variables
|
|
206
|
+
|
|
207
|
+
```css
|
|
208
|
+
.navigation-stack {
|
|
209
|
+
--currentContainerWidth: 100%;
|
|
210
|
+
--maxHeight: 100%;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Container Styling
|
|
215
|
+
|
|
216
|
+
```css
|
|
217
|
+
.navigation-stack {
|
|
218
|
+
position: relative;
|
|
219
|
+
display: grid;
|
|
220
|
+
grid-template-areas: "content";
|
|
221
|
+
height: 100%;
|
|
222
|
+
width: 100%;
|
|
223
|
+
overflow: hidden;
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Pane Styling
|
|
228
|
+
|
|
229
|
+
```css
|
|
230
|
+
.navigation-stack .pane {
|
|
231
|
+
display: grid;
|
|
232
|
+
grid-template-columns: 1fr;
|
|
233
|
+
grid-template-areas: "content";
|
|
234
|
+
height: 100%;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.navigation-stack .component_container {
|
|
238
|
+
grid-area: content;
|
|
239
|
+
overflow: hidden;
|
|
240
|
+
width: var(--currentContainerWidth, 100%);
|
|
241
|
+
align-self: center;
|
|
242
|
+
justify-self: center;
|
|
243
|
+
height: var(--maxHeight, 100%);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Examples
|
|
248
|
+
|
|
249
|
+
### Mobile App Navigation
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<script setup lang="ts">
|
|
253
|
+
import { ref } from "vue";
|
|
254
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
255
|
+
import HomeScreen from "./HomeScreen.vue";
|
|
256
|
+
import ChatScreen from "./ChatScreen.vue";
|
|
257
|
+
import ProfileScreen from "./ProfileScreen.vue";
|
|
258
|
+
|
|
259
|
+
const panes = ref([
|
|
260
|
+
{
|
|
261
|
+
name: "home",
|
|
262
|
+
background: "#ffffff",
|
|
263
|
+
foreground: "#000000",
|
|
264
|
+
component: HomeScreen,
|
|
265
|
+
props: {},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "chat",
|
|
269
|
+
background: "#f0f8ff",
|
|
270
|
+
foreground: "#1a365d",
|
|
271
|
+
component: ChatScreen,
|
|
272
|
+
props: { chatId: "main" },
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "profile",
|
|
276
|
+
background: "#f7fafc",
|
|
277
|
+
foreground: "#2d3748",
|
|
278
|
+
component: ProfileScreen,
|
|
279
|
+
props: { userId: "current" },
|
|
280
|
+
},
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
const navigationRef = ref();
|
|
284
|
+
const currentPane = ref(0);
|
|
285
|
+
|
|
286
|
+
const goToPane = (index: number) => {
|
|
287
|
+
currentPane.value = index;
|
|
288
|
+
navigationRef.value?.navigate(index);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const goBack = () => {
|
|
292
|
+
if (currentPane.value > 0) {
|
|
293
|
+
goToPane(currentPane.value - 1);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const goForward = () => {
|
|
298
|
+
if (currentPane.value < panes.value.length - 1) {
|
|
299
|
+
goToPane(currentPane.value + 1);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
</script>
|
|
303
|
+
|
|
304
|
+
<template>
|
|
305
|
+
<div class="mobile-app">
|
|
306
|
+
<!-- Navigation Header -->
|
|
307
|
+
<header class="app-header">
|
|
308
|
+
<button @click="goBack" :disabled="currentPane === 0" class="nav-button">
|
|
309
|
+
← Back
|
|
310
|
+
</button>
|
|
311
|
+
<h1 class="app-title">{{ panes[currentPane].name }}</h1>
|
|
312
|
+
<button
|
|
313
|
+
@click="goForward"
|
|
314
|
+
:disabled="currentPane === panes.length - 1"
|
|
315
|
+
class="nav-button"
|
|
316
|
+
>
|
|
317
|
+
Forward →
|
|
318
|
+
</button>
|
|
319
|
+
</header>
|
|
320
|
+
|
|
321
|
+
<!-- Navigation Stack -->
|
|
322
|
+
<NavigationStack
|
|
323
|
+
ref="navigationRef"
|
|
324
|
+
task-key="mobile-navigation"
|
|
325
|
+
:panes="panes"
|
|
326
|
+
/>
|
|
327
|
+
|
|
328
|
+
<!-- Bottom Navigation -->
|
|
329
|
+
<nav class="bottom-nav">
|
|
330
|
+
<button
|
|
331
|
+
v-for="(pane, index) in panes"
|
|
332
|
+
:key="pane.name"
|
|
333
|
+
@click="goToPane(index)"
|
|
334
|
+
:class="['nav-tab', { active: currentPane === index }]"
|
|
335
|
+
>
|
|
336
|
+
{{ pane.name }}
|
|
337
|
+
</button>
|
|
338
|
+
</nav>
|
|
339
|
+
</div>
|
|
340
|
+
</template>
|
|
341
|
+
|
|
342
|
+
<style module>
|
|
343
|
+
.mobile-app {
|
|
344
|
+
height: 100vh;
|
|
345
|
+
display: flex;
|
|
346
|
+
flex-direction: column;
|
|
347
|
+
background: #000;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.app-header {
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
justify-content: space-between;
|
|
354
|
+
padding: 12px 16px;
|
|
355
|
+
background: var(--background-1);
|
|
356
|
+
border-bottom: 1px solid var(--border-2);
|
|
357
|
+
min-height: 44px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.app-title {
|
|
361
|
+
font-size: 18px;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
color: var(--text-1);
|
|
364
|
+
margin: 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.nav-button {
|
|
368
|
+
padding: 8px 12px;
|
|
369
|
+
background: transparent;
|
|
370
|
+
border: none;
|
|
371
|
+
color: var(--text-2);
|
|
372
|
+
cursor: pointer;
|
|
373
|
+
border-radius: 6px;
|
|
374
|
+
transition: background 0.2s ease;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.nav-button:hover:not(:disabled) {
|
|
378
|
+
background: var(--background-2);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.nav-button:disabled {
|
|
382
|
+
opacity: 0.3;
|
|
383
|
+
cursor: not-allowed;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.bottom-nav {
|
|
387
|
+
display: flex;
|
|
388
|
+
background: var(--background-1);
|
|
389
|
+
border-top: 1px solid var(--border-2);
|
|
390
|
+
padding: 8px 0;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.nav-tab {
|
|
394
|
+
flex: 1;
|
|
395
|
+
padding: 12px 8px;
|
|
396
|
+
background: transparent;
|
|
397
|
+
border: none;
|
|
398
|
+
color: var(--text-2);
|
|
399
|
+
cursor: pointer;
|
|
400
|
+
transition: all 0.2s ease;
|
|
401
|
+
text-transform: capitalize;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.nav-tab.active {
|
|
405
|
+
color: var(--accent);
|
|
406
|
+
background: var(--background-2);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.nav-tab:hover {
|
|
410
|
+
background: var(--background-2);
|
|
411
|
+
}
|
|
412
|
+
</style>
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Wizard/Onboarding Flow
|
|
416
|
+
|
|
417
|
+
```vue
|
|
418
|
+
<script setup lang="ts">
|
|
419
|
+
import { ref } from "vue";
|
|
420
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
421
|
+
import WelcomeStep from "./WelcomeStep.vue";
|
|
422
|
+
import PersonalInfoStep from "./PersonalInfoStep.vue";
|
|
423
|
+
import PreferencesStep from "./PreferencesStep.vue";
|
|
424
|
+
import ConfirmationStep from "./ConfirmationStep.vue";
|
|
425
|
+
|
|
426
|
+
const panes = ref([
|
|
427
|
+
{
|
|
428
|
+
name: "welcome",
|
|
429
|
+
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
430
|
+
foreground: "#ffffff",
|
|
431
|
+
component: WelcomeStep,
|
|
432
|
+
props: { title: "Welcome!" },
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "personal",
|
|
436
|
+
background: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)",
|
|
437
|
+
foreground: "#ffffff",
|
|
438
|
+
component: PersonalInfoStep,
|
|
439
|
+
props: { step: 2 },
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: "preferences",
|
|
443
|
+
background: "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)",
|
|
444
|
+
foreground: "#ffffff",
|
|
445
|
+
component: PreferencesStep,
|
|
446
|
+
props: { step: 3 },
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: "confirmation",
|
|
450
|
+
background: "linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",
|
|
451
|
+
foreground: "#ffffff",
|
|
452
|
+
component: ConfirmationStep,
|
|
453
|
+
props: { step: 4 },
|
|
454
|
+
},
|
|
455
|
+
]);
|
|
456
|
+
|
|
457
|
+
const navigationRef = ref();
|
|
458
|
+
const currentStep = ref(0);
|
|
459
|
+
const totalSteps = panes.value.length;
|
|
460
|
+
|
|
461
|
+
const nextStep = () => {
|
|
462
|
+
if (currentStep.value < totalSteps - 1) {
|
|
463
|
+
currentStep.value++;
|
|
464
|
+
navigationRef.value?.navigate(currentStep.value);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const prevStep = () => {
|
|
469
|
+
if (currentStep.value > 0) {
|
|
470
|
+
currentStep.value--;
|
|
471
|
+
navigationRef.value?.navigate(currentStep.value);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const completeWizard = () => {
|
|
476
|
+
console.log("Wizard completed!");
|
|
477
|
+
// Handle completion
|
|
478
|
+
};
|
|
479
|
+
</script>
|
|
480
|
+
|
|
481
|
+
<template>
|
|
482
|
+
<div class="wizard-container">
|
|
483
|
+
<!-- Progress Indicator -->
|
|
484
|
+
<div class="progress-header">
|
|
485
|
+
<div class="progress-bar">
|
|
486
|
+
<div
|
|
487
|
+
class="progress-fill"
|
|
488
|
+
:style="{ width: `${((currentStep + 1) / totalSteps) * 100}%` }"
|
|
489
|
+
/>
|
|
490
|
+
</div>
|
|
491
|
+
<span class="progress-text">
|
|
492
|
+
Step {{ currentStep + 1 }} of {{ totalSteps }}
|
|
493
|
+
</span>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<!-- Navigation Stack -->
|
|
497
|
+
<NavigationStack
|
|
498
|
+
ref="navigationRef"
|
|
499
|
+
task-key="wizard-navigation"
|
|
500
|
+
:panes="panes"
|
|
501
|
+
/>
|
|
502
|
+
|
|
503
|
+
<!-- Navigation Controls -->
|
|
504
|
+
<div class="wizard-controls">
|
|
505
|
+
<button
|
|
506
|
+
@click="prevStep"
|
|
507
|
+
:disabled="currentStep === 0"
|
|
508
|
+
class="control-button secondary"
|
|
509
|
+
>
|
|
510
|
+
Previous
|
|
511
|
+
</button>
|
|
512
|
+
|
|
513
|
+
<button
|
|
514
|
+
v-if="currentStep < totalSteps - 1"
|
|
515
|
+
@click="nextStep"
|
|
516
|
+
class="control-button primary"
|
|
517
|
+
>
|
|
518
|
+
Next
|
|
519
|
+
</button>
|
|
520
|
+
|
|
521
|
+
<button v-else @click="completeWizard" class="control-button primary">
|
|
522
|
+
Complete
|
|
523
|
+
</button>
|
|
524
|
+
</div>
|
|
525
|
+
</div>
|
|
526
|
+
</template>
|
|
527
|
+
|
|
528
|
+
<style module>
|
|
529
|
+
.wizard-container {
|
|
530
|
+
height: 100vh;
|
|
531
|
+
display: flex;
|
|
532
|
+
flex-direction: column;
|
|
533
|
+
background: #f8f9fa;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.progress-header {
|
|
537
|
+
padding: 20px;
|
|
538
|
+
background: white;
|
|
539
|
+
border-bottom: 1px solid #e9ecef;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.progress-bar {
|
|
543
|
+
width: 100%;
|
|
544
|
+
height: 4px;
|
|
545
|
+
background: #e9ecef;
|
|
546
|
+
border-radius: 2px;
|
|
547
|
+
overflow: hidden;
|
|
548
|
+
margin-bottom: 8px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.progress-fill {
|
|
552
|
+
height: 100%;
|
|
553
|
+
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
554
|
+
transition: width 0.3s ease;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.progress-text {
|
|
558
|
+
font-size: 14px;
|
|
559
|
+
color: #6c757d;
|
|
560
|
+
font-weight: 500;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.wizard-controls {
|
|
564
|
+
display: flex;
|
|
565
|
+
justify-content: space-between;
|
|
566
|
+
padding: 20px;
|
|
567
|
+
background: white;
|
|
568
|
+
border-top: 1px solid #e9ecef;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.control-button {
|
|
572
|
+
padding: 12px 24px;
|
|
573
|
+
border: none;
|
|
574
|
+
border-radius: 8px;
|
|
575
|
+
font-weight: 600;
|
|
576
|
+
cursor: pointer;
|
|
577
|
+
transition: all 0.2s ease;
|
|
578
|
+
min-width: 100px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.control-button.primary {
|
|
582
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
583
|
+
color: white;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.control-button.primary:hover {
|
|
587
|
+
transform: translateY(-1px);
|
|
588
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.control-button.secondary {
|
|
592
|
+
background: #f8f9fa;
|
|
593
|
+
color: #6c757d;
|
|
594
|
+
border: 1px solid #dee2e6;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.control-button.secondary:hover:not(:disabled) {
|
|
598
|
+
background: #e9ecef;
|
|
599
|
+
border-color: #adb5bd;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.control-button:disabled {
|
|
603
|
+
opacity: 0.5;
|
|
604
|
+
cursor: not-allowed;
|
|
605
|
+
}
|
|
606
|
+
</style>
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Tab-Based Interface
|
|
610
|
+
|
|
611
|
+
```vue
|
|
612
|
+
<script setup lang="ts">
|
|
613
|
+
import { ref } from "vue";
|
|
614
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
615
|
+
import DashboardTab from "./DashboardTab.vue";
|
|
616
|
+
import AnalyticsTab from "./AnalyticsTab.vue";
|
|
617
|
+
import ReportsTab from "./ReportsTab.vue";
|
|
618
|
+
import SettingsTab from "./SettingsTab.vue";
|
|
619
|
+
|
|
620
|
+
const panes = ref([
|
|
621
|
+
{
|
|
622
|
+
name: "dashboard",
|
|
623
|
+
background: "#ffffff",
|
|
624
|
+
foreground: "#1a202c",
|
|
625
|
+
component: DashboardTab,
|
|
626
|
+
props: { title: "Dashboard" },
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
name: "analytics",
|
|
630
|
+
background: "#f7fafc",
|
|
631
|
+
foreground: "#2d3748",
|
|
632
|
+
component: AnalyticsTab,
|
|
633
|
+
props: { period: "30d" },
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: "reports",
|
|
637
|
+
background: "#edf2f7",
|
|
638
|
+
foreground: "#4a5568",
|
|
639
|
+
component: ReportsTab,
|
|
640
|
+
props: { type: "summary" },
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
name: "settings",
|
|
644
|
+
background: "#e2e8f0",
|
|
645
|
+
foreground: "#718096",
|
|
646
|
+
component: SettingsTab,
|
|
647
|
+
props: { section: "general" },
|
|
648
|
+
},
|
|
649
|
+
]);
|
|
650
|
+
|
|
651
|
+
const navigationRef = ref();
|
|
652
|
+
const activeTab = ref(0);
|
|
653
|
+
|
|
654
|
+
const switchTab = (index: number) => {
|
|
655
|
+
activeTab.value = index;
|
|
656
|
+
navigationRef.value?.navigate(index);
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const tabIcons = {
|
|
660
|
+
dashboard: "📊",
|
|
661
|
+
analytics: "📈",
|
|
662
|
+
reports: "📋",
|
|
663
|
+
settings: "⚙️",
|
|
664
|
+
};
|
|
665
|
+
</script>
|
|
666
|
+
|
|
667
|
+
<template>
|
|
668
|
+
<div class="tab-interface">
|
|
669
|
+
<!-- Tab Navigation -->
|
|
670
|
+
<div class="tab-nav">
|
|
671
|
+
<button
|
|
672
|
+
v-for="(pane, index) in panes"
|
|
673
|
+
:key="pane.name"
|
|
674
|
+
@click="switchTab(index)"
|
|
675
|
+
:class="['tab-button', { active: activeTab === index }]"
|
|
676
|
+
>
|
|
677
|
+
<span class="tab-icon">{{ tabIcons[pane.name] }}</span>
|
|
678
|
+
<span class="tab-label">{{ pane.name }}</span>
|
|
679
|
+
</button>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
<!-- Tab Content -->
|
|
683
|
+
<NavigationStack
|
|
684
|
+
ref="navigationRef"
|
|
685
|
+
task-key="tab-navigation"
|
|
686
|
+
:panes="panes"
|
|
687
|
+
/>
|
|
688
|
+
</div>
|
|
689
|
+
</template>
|
|
690
|
+
|
|
691
|
+
<style module>
|
|
692
|
+
.tab-interface {
|
|
693
|
+
height: 100vh;
|
|
694
|
+
display: flex;
|
|
695
|
+
flex-direction: column;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.tab-nav {
|
|
699
|
+
display: flex;
|
|
700
|
+
background: var(--background-1);
|
|
701
|
+
border-bottom: 1px solid var(--border-2);
|
|
702
|
+
padding: 0 16px;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.tab-button {
|
|
706
|
+
display: flex;
|
|
707
|
+
flex-direction: column;
|
|
708
|
+
align-items: center;
|
|
709
|
+
gap: 4px;
|
|
710
|
+
padding: 12px 16px;
|
|
711
|
+
background: transparent;
|
|
712
|
+
border: none;
|
|
713
|
+
color: var(--text-2);
|
|
714
|
+
cursor: pointer;
|
|
715
|
+
transition: all 0.2s ease;
|
|
716
|
+
border-bottom: 2px solid transparent;
|
|
717
|
+
min-width: 80px;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.tab-button:hover {
|
|
721
|
+
background: var(--background-2);
|
|
722
|
+
color: var(--text-1);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.tab-button.active {
|
|
726
|
+
color: var(--accent);
|
|
727
|
+
border-bottom-color: var(--accent);
|
|
728
|
+
background: var(--background-2);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.tab-icon {
|
|
732
|
+
font-size: 20px;
|
|
733
|
+
line-height: 1;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.tab-label {
|
|
737
|
+
font-size: 12px;
|
|
738
|
+
font-weight: 500;
|
|
739
|
+
text-transform: capitalize;
|
|
740
|
+
}
|
|
741
|
+
</style>
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
### Dynamic Content Management
|
|
745
|
+
|
|
746
|
+
```vue
|
|
747
|
+
<script setup lang="ts">
|
|
748
|
+
import { ref } from "vue";
|
|
749
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
750
|
+
import DynamicView from "./DynamicView.vue";
|
|
751
|
+
|
|
752
|
+
const panes = ref([
|
|
753
|
+
{
|
|
754
|
+
name: "initial",
|
|
755
|
+
background: "#ffffff",
|
|
756
|
+
foreground: "#000000",
|
|
757
|
+
component: DynamicView,
|
|
758
|
+
props: { content: "Initial Content" },
|
|
759
|
+
},
|
|
760
|
+
]);
|
|
761
|
+
|
|
762
|
+
const navigationRef = ref();
|
|
763
|
+
const paneCounter = ref(1);
|
|
764
|
+
|
|
765
|
+
const addNewPane = () => {
|
|
766
|
+
const newPane = {
|
|
767
|
+
name: `pane-${paneCounter.value}`,
|
|
768
|
+
background: `hsl(${Math.random() * 360}, 70%, 90%)`,
|
|
769
|
+
foreground: "#000000",
|
|
770
|
+
component: DynamicView,
|
|
771
|
+
props: {
|
|
772
|
+
content: `Dynamic Content ${paneCounter.value}`,
|
|
773
|
+
id: paneCounter.value,
|
|
774
|
+
},
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
navigationRef.value?.addPane(newPane, true);
|
|
778
|
+
paneCounter.value++;
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
const removeCurrentPane = () => {
|
|
782
|
+
if (panes.value.length > 1) {
|
|
783
|
+
const currentIndex = navigationRef.value?.currentIndex || 0;
|
|
784
|
+
navigationRef.value?.removePane(currentIndex);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
const clearAllPanes = () => {
|
|
789
|
+
panes.value = [
|
|
790
|
+
{
|
|
791
|
+
name: "reset",
|
|
792
|
+
background: "#ffffff",
|
|
793
|
+
foreground: "#000000",
|
|
794
|
+
component: DynamicView,
|
|
795
|
+
props: { content: "Reset Content" },
|
|
796
|
+
},
|
|
797
|
+
];
|
|
798
|
+
navigationRef.value?.updatePanes(panes.value);
|
|
799
|
+
};
|
|
800
|
+
</script>
|
|
801
|
+
|
|
802
|
+
<template>
|
|
803
|
+
<div class="dynamic-container">
|
|
804
|
+
<!-- Controls -->
|
|
805
|
+
<div class="controls">
|
|
806
|
+
<button @click="addNewPane" class="control-btn add">Add Pane</button>
|
|
807
|
+
<button @click="removeCurrentPane" class="control-btn remove">
|
|
808
|
+
Remove Current
|
|
809
|
+
</button>
|
|
810
|
+
<button @click="clearAllPanes" class="control-btn clear">
|
|
811
|
+
Clear All
|
|
812
|
+
</button>
|
|
813
|
+
</div>
|
|
814
|
+
|
|
815
|
+
<!-- Navigation Stack -->
|
|
816
|
+
<NavigationStack
|
|
817
|
+
ref="navigationRef"
|
|
818
|
+
task-key="dynamic-navigation"
|
|
819
|
+
:panes="panes"
|
|
820
|
+
/>
|
|
821
|
+
</div>
|
|
822
|
+
</template>
|
|
823
|
+
|
|
824
|
+
<style module>
|
|
825
|
+
.dynamic-container {
|
|
826
|
+
height: 100vh;
|
|
827
|
+
display: flex;
|
|
828
|
+
flex-direction: column;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.controls {
|
|
832
|
+
display: flex;
|
|
833
|
+
gap: 12px;
|
|
834
|
+
padding: 16px;
|
|
835
|
+
background: var(--background-1);
|
|
836
|
+
border-bottom: 1px solid var(--border-2);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.control-btn {
|
|
840
|
+
padding: 8px 16px;
|
|
841
|
+
border: none;
|
|
842
|
+
border-radius: 6px;
|
|
843
|
+
font-weight: 500;
|
|
844
|
+
cursor: pointer;
|
|
845
|
+
transition: all 0.2s ease;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.control-btn.add {
|
|
849
|
+
background: #10b981;
|
|
850
|
+
color: white;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.control-btn.add:hover {
|
|
854
|
+
background: #059669;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
.control-btn.remove {
|
|
858
|
+
background: #ef4444;
|
|
859
|
+
color: white;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.control-btn.remove:hover {
|
|
863
|
+
background: #dc2626;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.control-btn.clear {
|
|
867
|
+
background: #6b7280;
|
|
868
|
+
color: white;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.control-btn.clear:hover {
|
|
872
|
+
background: #4b5563;
|
|
873
|
+
}
|
|
874
|
+
</style>
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
## Advanced Usage
|
|
878
|
+
|
|
879
|
+
### Custom Animation Configuration
|
|
880
|
+
|
|
881
|
+
```vue
|
|
882
|
+
<script setup lang="ts">
|
|
883
|
+
import { ref } from "vue";
|
|
884
|
+
import { NavigationStack, useNavigationStack } from "@umbra-ui/core";
|
|
885
|
+
|
|
886
|
+
// Custom navigation with modified animation
|
|
887
|
+
const customNavigation = useNavigationStack(initialPanes, "custom");
|
|
888
|
+
|
|
889
|
+
// Override the navigate method with custom animation
|
|
890
|
+
const customNavigate = (index: number, animated: boolean = true) => {
|
|
891
|
+
// Custom animation logic here
|
|
892
|
+
customNavigation.navigate(index, animated);
|
|
893
|
+
};
|
|
894
|
+
</script>
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Integration with Router
|
|
898
|
+
|
|
899
|
+
```vue
|
|
900
|
+
<script setup lang="ts">
|
|
901
|
+
import { ref, watch } from "vue";
|
|
902
|
+
import { useRoute, useRouter } from "vue-router";
|
|
903
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
904
|
+
|
|
905
|
+
const route = useRoute();
|
|
906
|
+
const router = useRouter();
|
|
907
|
+
const navigationRef = ref();
|
|
908
|
+
|
|
909
|
+
const panes = ref([
|
|
910
|
+
// ... your panes
|
|
911
|
+
]);
|
|
912
|
+
|
|
913
|
+
// Sync with router
|
|
914
|
+
watch(
|
|
915
|
+
() => route.params.tab,
|
|
916
|
+
(newTab) => {
|
|
917
|
+
const index = panes.value.findIndex((pane) => pane.name === newTab);
|
|
918
|
+
if (index !== -1) {
|
|
919
|
+
navigationRef.value?.navigate(index);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
const handleNavigation = (index: number) => {
|
|
925
|
+
const pane = panes.value[index];
|
|
926
|
+
router.push(`/app/${pane.name}`);
|
|
927
|
+
};
|
|
928
|
+
</script>
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
## Performance Considerations
|
|
932
|
+
|
|
933
|
+
- **Component Marking**: Always use `markRaw()` when passing components to prevent Vue's reactivity system from wrapping them
|
|
934
|
+
- **Animation Performance**: GSAP FLIP animations are optimized for smooth 60fps performance
|
|
935
|
+
- **Memory Management**: Panes are properly cleaned up when removed from the stack
|
|
936
|
+
- **ResizeObserver**: Automatic container size monitoring with proper cleanup
|
|
937
|
+
|
|
938
|
+
## Accessibility
|
|
939
|
+
|
|
940
|
+
- **Keyboard Navigation**: Support for arrow keys and tab navigation
|
|
941
|
+
- **Screen Reader Support**: Proper ARIA labels and roles
|
|
942
|
+
- **Focus Management**: Focus is maintained during transitions
|
|
943
|
+
- **High Contrast**: Supports high contrast mode
|
|
944
|
+
|
|
945
|
+
## Browser Support
|
|
946
|
+
|
|
947
|
+
- **Modern Browsers**: Chrome 88+, Firefox 85+, Safari 14+, Edge 88+
|
|
948
|
+
- **Mobile Browsers**: iOS Safari 14+, Chrome Mobile 88+
|
|
949
|
+
- **CSS Features**: Uses CSS Grid and modern layout features
|
|
950
|
+
- **JavaScript**: Requires ES2020+ support
|
|
951
|
+
|
|
952
|
+
## Troubleshooting
|
|
953
|
+
|
|
954
|
+
### Common Issues
|
|
955
|
+
|
|
956
|
+
1. **Animations not working**: Ensure GSAP and FLIP plugin are properly imported
|
|
957
|
+
2. **Panes not displaying**: Check that components are properly marked with `markRaw()`
|
|
958
|
+
3. **Navigation not responding**: Verify the `taskKey` is unique and consistent
|
|
959
|
+
4. **Layout issues**: Ensure the container has a defined height
|
|
960
|
+
|
|
961
|
+
### Debug Mode
|
|
962
|
+
|
|
963
|
+
```vue
|
|
964
|
+
<script setup lang="ts">
|
|
965
|
+
import { ref, watch } from "vue";
|
|
966
|
+
import { NavigationStack } from "@umbra-ui/core";
|
|
967
|
+
|
|
968
|
+
const navigationRef = ref();
|
|
969
|
+
|
|
970
|
+
// Watch for navigation changes
|
|
971
|
+
watch(
|
|
972
|
+
() => navigationRef.value?.currentIndex,
|
|
973
|
+
(newIndex) => {
|
|
974
|
+
console.log("Current pane index:", newIndex);
|
|
975
|
+
}
|
|
976
|
+
);
|
|
977
|
+
</script>
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
## Migration Guide
|
|
981
|
+
|
|
982
|
+
### From v1 to v2
|
|
983
|
+
|
|
984
|
+
- `taskKey` prop is now required
|
|
985
|
+
- Pane interface has been updated with new properties
|
|
986
|
+
- Animation system has been refactored for better performance
|
|
987
|
+
- Composable API has been enhanced with new methods
|
|
988
|
+
|
|
989
|
+
### Breaking Changes
|
|
990
|
+
|
|
991
|
+
- Removed automatic pane detection
|
|
992
|
+
- Changed animation timing and easing
|
|
993
|
+
- Updated CSS class structure
|
|
994
|
+
- Modified event handling system
|