@toife/vue 3.2.5 → 3.2.7

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 (299) hide show
  1. package/package.json +20 -21
  2. package/src/components/action/action.composable.ts +32 -0
  3. package/src/components/action/action.html +25 -0
  4. package/src/components/action/action.vue +69 -0
  5. package/src/components/action/index.ts +2 -0
  6. package/src/components/app/app.html +24 -0
  7. package/src/components/app/app.vue +45 -0
  8. package/src/components/app/index.ts +1 -0
  9. package/src/components/avatar/avatar.html +3 -0
  10. package/src/components/avatar/avatar.vue +35 -0
  11. package/src/components/avatar/index.ts +1 -0
  12. package/src/components/button/button.html +6 -0
  13. package/src/components/button/button.vue +54 -0
  14. package/src/components/button/index.ts +1 -0
  15. package/src/components/cable/cable.html +3 -0
  16. package/src/components/cable/cable.vue +33 -0
  17. package/src/components/cable/index.ts +1 -0
  18. package/src/components/card/card/card.html +3 -0
  19. package/src/components/card/card/card.vue +52 -0
  20. package/src/components/card/card/index.ts +1 -0
  21. package/src/components/card/card-body/card-body.html +3 -0
  22. package/src/components/card/card-body/card-body.vue +6 -0
  23. package/src/components/card/card-body/index.ts +1 -0
  24. package/src/components/card/card-footer/card-footer.html +3 -0
  25. package/src/components/card/card-footer/card-footer.vue +18 -0
  26. package/src/components/card/card-footer/index.ts +1 -0
  27. package/src/components/card/card-header/card-header.html +3 -0
  28. package/src/components/card/card-header/card-header.vue +18 -0
  29. package/src/components/card/card-header/index.ts +1 -0
  30. package/src/components/card/index.ts +4 -0
  31. package/src/components/checkbox/checkbox.html +12 -0
  32. package/src/components/checkbox/checkbox.vue +74 -0
  33. package/src/components/checkbox/index.ts +1 -0
  34. package/src/components/collapse/collapse.html +16 -0
  35. package/src/components/collapse/collapse.vue +126 -0
  36. package/src/components/collapse/index.ts +1 -0
  37. package/src/components/container/container.html +1 -0
  38. package/src/components/container/container.vue +7 -0
  39. package/src/components/container/index.ts +1 -0
  40. package/src/components/decision-modal/decision-modal.composable.ts +33 -0
  41. package/src/components/decision-modal/decision-modal.html +29 -0
  42. package/src/components/decision-modal/decision-modal.vue +76 -0
  43. package/src/components/decision-modal/index.ts +2 -0
  44. package/src/components/divider/divider.html +1 -0
  45. package/src/components/divider/divider.vue +26 -0
  46. package/src/components/divider/index.ts +1 -0
  47. package/src/components/dropdown/dropdown.html +6 -0
  48. package/src/components/dropdown/dropdown.vue +101 -0
  49. package/src/components/dropdown/index.ts +1 -0
  50. package/src/components/field/field.html +30 -0
  51. package/src/components/field/field.vue +121 -0
  52. package/src/components/field/index.ts +1 -0
  53. package/src/components/form-group/form-group.html +3 -0
  54. package/src/components/form-group/form-group.vue +15 -0
  55. package/src/components/form-group/index.ts +1 -0
  56. package/src/components/gesture-indicator/gesture-indicator.html +1 -0
  57. package/src/components/gesture-indicator/gesture-indicator.vue +32 -0
  58. package/src/components/gesture-indicator/index.ts +1 -0
  59. package/src/components/image/image.html +1 -0
  60. package/src/components/image/image.vue +32 -0
  61. package/src/components/image/index.ts +1 -0
  62. package/src/components/index.ts +32 -0
  63. package/src/components/layout/flex/flex.html +1 -0
  64. package/src/components/layout/flex/flex.vue +15 -0
  65. package/src/components/layout/flex/index.ts +1 -0
  66. package/src/components/layout/flex-item/flex-item.html +1 -0
  67. package/src/components/layout/flex-item/flex-item.vue +11 -0
  68. package/src/components/layout/flex-item/index.ts +1 -0
  69. package/src/components/layout/grid/grid.html +1 -0
  70. package/src/components/layout/grid/grid.vue +15 -0
  71. package/src/components/layout/grid/index.ts +1 -0
  72. package/src/components/layout/grid-item/grid-item.html +1 -0
  73. package/src/components/layout/grid-item/grid-item.vue +11 -0
  74. package/src/components/layout/grid-item/index.ts +1 -0
  75. package/src/components/layout/index.ts +4 -0
  76. package/src/components/modal/index.ts +1 -0
  77. package/src/components/modal/modal.html +20 -0
  78. package/src/components/modal/modal.vue +248 -0
  79. package/src/components/page/index.ts +1 -0
  80. package/src/components/page/page.html +3 -0
  81. package/src/components/page/page.vue +7 -0
  82. package/src/components/present/index.ts +2 -0
  83. package/src/components/present/present.composable.ts +22 -0
  84. package/src/components/present/present.html +11 -0
  85. package/src/components/present/present.vue +234 -0
  86. package/src/components/radio/index.ts +2 -0
  87. package/src/components/radio/radio/index.ts +1 -0
  88. package/src/components/radio/radio/radio.html +11 -0
  89. package/src/components/radio/radio/radio.vue +84 -0
  90. package/src/components/radio/radio-group/index.ts +1 -0
  91. package/src/components/radio/radio-group/radio-group.html +3 -0
  92. package/src/components/radio/radio-group/radio-group.vue +59 -0
  93. package/src/components/refresher/index.ts +1 -0
  94. package/src/components/refresher/refresher.html +3 -0
  95. package/src/components/refresher/refresher.vue +100 -0
  96. package/src/components/route/index.ts +5 -0
  97. package/src/components/route/route-navigator/index.ts +1 -0
  98. package/src/components/route/route-navigator/route-navigator.html +19 -0
  99. package/src/components/route/route-navigator/route-navigator.vue +254 -0
  100. package/src/components/route/route-outlet/index.ts +1 -0
  101. package/src/components/route/route-outlet/route-outlet.html +1 -0
  102. package/src/components/route/route-outlet/route-outlet.vue +39 -0
  103. package/src/components/route/route-provider/index.ts +1 -0
  104. package/src/components/route/route-provider/route-provider.html +1 -0
  105. package/src/components/route/route-provider/route-provider.vue +24 -0
  106. package/src/components/route/route-wrapper/index.ts +2 -0
  107. package/src/components/route/route-wrapper/route-wrapper.composable.ts +49 -0
  108. package/src/components/route/route-wrapper/route-wrapper.html +3 -0
  109. package/src/components/route/route-wrapper/route-wrapper.vue +44 -0
  110. package/src/components/route/route.util.ts +9 -0
  111. package/src/components/segmented-field/index.ts +1 -0
  112. package/src/components/segmented-field/segmented-field.html +16 -0
  113. package/src/components/segmented-field/segmented-field.vue +243 -0
  114. package/src/components/select/index.ts +1 -0
  115. package/src/components/select/select.html +28 -0
  116. package/src/components/select/select.vue +101 -0
  117. package/src/components/skeleton/index.ts +1 -0
  118. package/src/components/skeleton/skeleton.html +1 -0
  119. package/src/components/skeleton/skeleton.vue +33 -0
  120. package/src/components/slide-range/index.ts +1 -0
  121. package/src/components/slide-range/slide-range.html +20 -0
  122. package/src/components/slide-range/slide-range.vue +269 -0
  123. package/src/components/switch/index.ts +1 -0
  124. package/src/components/switch/switch.html +14 -0
  125. package/src/components/switch/switch.vue +86 -0
  126. package/src/components/tabs/index.ts +2 -0
  127. package/src/components/tabs/tab/index.ts +1 -0
  128. package/src/components/tabs/tab/tab.html +5 -0
  129. package/src/components/tabs/tab/tab.vue +34 -0
  130. package/src/components/tabs/tabs/index.ts +1 -0
  131. package/src/components/tabs/tabs/tabs.html +3 -0
  132. package/src/components/tabs/tabs/tabs.vue +151 -0
  133. package/src/components/tag/index.ts +1 -0
  134. package/src/components/tag/tag.html +1 -0
  135. package/src/components/tag/tag.vue +33 -0
  136. package/src/components/toast/index.ts +3 -0
  137. package/src/components/toast/toast/index.ts +1 -0
  138. package/src/components/toast/toast/toast.html +9 -0
  139. package/src/components/toast/toast/toast.vue +28 -0
  140. package/src/components/toast/toast-content/index.ts +1 -0
  141. package/src/components/toast/toast-content/toast-content.html +1 -0
  142. package/src/components/toast/toast-content/toast-content.vue +51 -0
  143. package/src/components/toast/toast.composable.ts +22 -0
  144. package/src/components/toolbar/index.ts +1 -0
  145. package/src/components/toolbar/toolbar.html +3 -0
  146. package/src/components/toolbar/toolbar.vue +39 -0
  147. package/src/env.d.ts +7 -0
  148. package/src/factory.ts +162 -0
  149. package/src/index.ts +4 -0
  150. package/src/shared/provider-state.ts +8 -0
  151. package/src/types.ts +4 -0
  152. package/dist/components/action/action.composable.d.ts +0 -44
  153. package/dist/components/action/action.vue.d.ts +0 -16
  154. package/dist/components/action/index.d.ts +0 -2
  155. package/dist/components/app/app.vue.d.ts +0 -11
  156. package/dist/components/app/index.d.ts +0 -1
  157. package/dist/components/avatar/avatar.vue.d.ts +0 -8
  158. package/dist/components/avatar/index.d.ts +0 -1
  159. package/dist/components/button/button.vue.d.ts +0 -9
  160. package/dist/components/button/index.d.ts +0 -1
  161. package/dist/components/cable/cable.vue.d.ts +0 -6
  162. package/dist/components/cable/index.d.ts +0 -1
  163. package/dist/components/card/card/card.vue.d.ts +0 -7
  164. package/dist/components/card/card/index.d.ts +0 -1
  165. package/dist/components/card/card-body/card-body.vue.d.ts +0 -2
  166. package/dist/components/card/card-body/index.d.ts +0 -1
  167. package/dist/components/card/card-footer/card-footer.vue.d.ts +0 -2
  168. package/dist/components/card/card-footer/index.d.ts +0 -1
  169. package/dist/components/card/card-header/card-header.vue.d.ts +0 -2
  170. package/dist/components/card/card-header/index.d.ts +0 -1
  171. package/dist/components/card/index.d.ts +0 -4
  172. package/dist/components/checkbox/checkbox.vue.d.ts +0 -18
  173. package/dist/components/checkbox/index.d.ts +0 -1
  174. package/dist/components/collapse/collapse.vue.d.ts +0 -12
  175. package/dist/components/collapse/index.d.ts +0 -1
  176. package/dist/components/container/container.vue.d.ts +0 -2
  177. package/dist/components/container/index.d.ts +0 -1
  178. package/dist/components/decision-modal/decision-modal.composable.d.ts +0 -46
  179. package/dist/components/decision-modal/decision-modal.vue.d.ts +0 -16
  180. package/dist/components/decision-modal/index.d.ts +0 -2
  181. package/dist/components/divider/divider.vue.d.ts +0 -5
  182. package/dist/components/divider/index.d.ts +0 -1
  183. package/dist/components/dropdown/dropdown.vue.d.ts +0 -20
  184. package/dist/components/dropdown/index.d.ts +0 -1
  185. package/dist/components/field/field.vue.d.ts +0 -30
  186. package/dist/components/field/index.d.ts +0 -1
  187. package/dist/components/form-group/form-group.vue.d.ts +0 -5
  188. package/dist/components/form-group/index.d.ts +0 -1
  189. package/dist/components/gesture-indicator/gesture-indicator.vue.d.ts +0 -6
  190. package/dist/components/gesture-indicator/index.d.ts +0 -1
  191. package/dist/components/image/image.vue.d.ts +0 -3
  192. package/dist/components/image/index.d.ts +0 -1
  193. package/dist/components/index.d.ts +0 -32
  194. package/dist/components/layout/flex/flex.vue.d.ts +0 -5
  195. package/dist/components/layout/flex/index.d.ts +0 -1
  196. package/dist/components/layout/flex-item/flex-item.vue.d.ts +0 -5
  197. package/dist/components/layout/flex-item/index.d.ts +0 -1
  198. package/dist/components/layout/grid/grid.vue.d.ts +0 -5
  199. package/dist/components/layout/grid/index.d.ts +0 -1
  200. package/dist/components/layout/grid-item/grid-item.vue.d.ts +0 -5
  201. package/dist/components/layout/grid-item/index.d.ts +0 -1
  202. package/dist/components/layout/index.d.ts +0 -4
  203. package/dist/components/modal/index.d.ts +0 -1
  204. package/dist/components/modal/modal.vue.d.ts +0 -17
  205. package/dist/components/page/index.d.ts +0 -1
  206. package/dist/components/page/page.vue.d.ts +0 -2
  207. package/dist/components/present/index.d.ts +0 -2
  208. package/dist/components/present/present.composable.d.ts +0 -5
  209. package/dist/components/present/present.vue.d.ts +0 -18
  210. package/dist/components/radio/index.d.ts +0 -2
  211. package/dist/components/radio/radio/index.d.ts +0 -1
  212. package/dist/components/radio/radio/radio.vue.d.ts +0 -7
  213. package/dist/components/radio/radio-group/index.d.ts +0 -1
  214. package/dist/components/radio/radio-group/radio-group.vue.d.ts +0 -13
  215. package/dist/components/refresher/index.d.ts +0 -1
  216. package/dist/components/refresher/refresher.vue.d.ts +0 -12
  217. package/dist/components/route/index.d.ts +0 -5
  218. package/dist/components/route/route-navigator/index.d.ts +0 -1
  219. package/dist/components/route/route-navigator/route-navigator.vue.d.ts +0 -11
  220. package/dist/components/route/route-outlet/index.d.ts +0 -1
  221. package/dist/components/route/route-outlet/route-outlet.vue.d.ts +0 -5
  222. package/dist/components/route/route-provider/index.d.ts +0 -1
  223. package/dist/components/route/route-provider/route-provider.vue.d.ts +0 -3
  224. package/dist/components/route/route-wrapper/index.d.ts +0 -2
  225. package/dist/components/route/route-wrapper/route-wrapper.composable.d.ts +0 -6
  226. package/dist/components/route/route-wrapper/route-wrapper.vue.d.ts +0 -5
  227. package/dist/components/route/route.util.d.ts +0 -3
  228. package/dist/components/segmented-field/index.d.ts +0 -1
  229. package/dist/components/segmented-field/segmented-field.vue.d.ts +0 -28
  230. package/dist/components/select/index.d.ts +0 -1
  231. package/dist/components/select/select.vue.d.ts +0 -19
  232. package/dist/components/skeleton/index.d.ts +0 -1
  233. package/dist/components/skeleton/skeleton.vue.d.ts +0 -6
  234. package/dist/components/slide-range/index.d.ts +0 -1
  235. package/dist/components/slide-range/slide-range.vue.d.ts +0 -18
  236. package/dist/components/switch/index.d.ts +0 -1
  237. package/dist/components/switch/switch.vue.d.ts +0 -13
  238. package/dist/components/tabs/index.d.ts +0 -2
  239. package/dist/components/tabs/tab/index.d.ts +0 -1
  240. package/dist/components/tabs/tab/tab.vue.d.ts +0 -5
  241. package/dist/components/tabs/tabs/index.d.ts +0 -1
  242. package/dist/components/tabs/tabs/tabs.vue.d.ts +0 -14
  243. package/dist/components/tag/index.d.ts +0 -1
  244. package/dist/components/tag/tag.vue.d.ts +0 -6
  245. package/dist/components/toast/index.d.ts +0 -3
  246. package/dist/components/toast/toast/index.d.ts +0 -1
  247. package/dist/components/toast/toast/toast.vue.d.ts +0 -5
  248. package/dist/components/toast/toast-content/index.d.ts +0 -1
  249. package/dist/components/toast/toast-content/toast-content.vue.d.ts +0 -11
  250. package/dist/components/toast/toast.composable.d.ts +0 -22
  251. package/dist/components/toolbar/index.d.ts +0 -1
  252. package/dist/components/toolbar/toolbar.vue.d.ts +0 -7
  253. package/dist/factory.d.ts +0 -38
  254. package/dist/index.d.ts +0 -3
  255. package/dist/index.es.js +0 -3522
  256. package/dist/index.umd.js +0 -1
  257. package/dist/shared/provider-state.d.ts +0 -5
  258. package/dist/styles/action.css +0 -1
  259. package/dist/styles/app.css +0 -1
  260. package/dist/styles/avatar.css +0 -1
  261. package/dist/styles/button.css +0 -1
  262. package/dist/styles/cable.css +0 -1
  263. package/dist/styles/card-body.css +0 -1
  264. package/dist/styles/card-footer.css +0 -1
  265. package/dist/styles/card-header.css +0 -1
  266. package/dist/styles/card.css +0 -1
  267. package/dist/styles/checkbox.css +0 -1
  268. package/dist/styles/collapse.css +0 -1
  269. package/dist/styles/container.css +0 -1
  270. package/dist/styles/decision-modal.css +0 -1
  271. package/dist/styles/divider.css +0 -1
  272. package/dist/styles/dropdown.css +0 -1
  273. package/dist/styles/field.css +0 -1
  274. package/dist/styles/flex-item.css +0 -1
  275. package/dist/styles/flex.css +0 -1
  276. package/dist/styles/form-group.css +0 -1
  277. package/dist/styles/gesture-indicator.css +0 -1
  278. package/dist/styles/grid-item.css +0 -1
  279. package/dist/styles/grid.css +0 -1
  280. package/dist/styles/index.css +0 -40
  281. package/dist/styles/modal.css +0 -1
  282. package/dist/styles/page.css +0 -1
  283. package/dist/styles/present.css +0 -1
  284. package/dist/styles/radio-group.css +0 -1
  285. package/dist/styles/radio.css +0 -1
  286. package/dist/styles/refresher.css +0 -1
  287. package/dist/styles/route-navigator.css +0 -1
  288. package/dist/styles/segmented-field.css +0 -1
  289. package/dist/styles/select.css +0 -1
  290. package/dist/styles/skeleton.css +0 -1
  291. package/dist/styles/slide-range.css +0 -1
  292. package/dist/styles/switch.css +0 -1
  293. package/dist/styles/tab.css +0 -1
  294. package/dist/styles/tabs.css +0 -1
  295. package/dist/styles/tag.css +0 -1
  296. package/dist/styles/toast-content.css +0 -1
  297. package/dist/styles/toast.css +0 -1
  298. package/dist/styles/toolbar.css +0 -1
  299. package/dist/types.d.ts +0 -4
@@ -0,0 +1,254 @@
1
+ <style lang="scss" src="@toife/core/features/route/route-navigator.scss" scoped></style>
2
+ <template src="./route-navigator.html"></template>
3
+ <script lang="ts" setup>
4
+ import { computed, inject, onMounted, onUnmounted, reactive, ref, watch, unref } from "vue";
5
+ import {
6
+ gesture,
7
+ type GestureFastPayload,
8
+ type GestureMovePayload,
9
+ type GestureUpPayload,
10
+ } from "@toife/gesture";
11
+ import { RouteProvider } from "../route-provider";
12
+ import { RouteOutlet } from "../route-outlet";
13
+ import { useRouter } from "vue-router";
14
+ import { clone } from "../route.util";
15
+ import {
16
+ ROUTE_NAVIGATOR_DEFAULT_PROPS,
17
+ ROUTE_PROVIDER_STATE_KEY,
18
+ getRouteNavigatorAttrs,
19
+ getRouteNavigatorBackdropAttrs,
20
+ getRouteNavigatorComponentAttrs,
21
+ type RouteNavigatorEvent,
22
+ type RouteNavigatorGesture,
23
+ type RouteNavigatorProps,
24
+ type RouteNavigatorTransformState,
25
+ type RouteProviderState,
26
+ type RouteStack,
27
+ } from "@toife/core";
28
+ import type { ProviderStateRefs } from "../../../shared/provider-state";
29
+
30
+ // Component setup (props, emits, injects)
31
+ // ----------------------------------------------------------------------------
32
+ // Max translate offset (%) for the layer behind the active page during swipe.
33
+ // Active uses 0–100%; back uses (peek)*(100−percent)/100, so a peek of 40 makes the
34
+ // back layer move 2.5× slower than the active layer (parallax). Use 100 for 1:1 motion
35
+ // (no edge peek at rest: back starts at -100%).
36
+ const BACK_LAYER_PEEK_PCT = -40;
37
+
38
+ const props = withDefaults(defineProps<RouteNavigatorProps>(), {
39
+ ...ROUTE_NAVIGATOR_DEFAULT_PROPS,
40
+ });
41
+ const emit = defineEmits<RouteNavigatorEvent>();
42
+ const provider = inject<ProviderStateRefs<RouteProviderState>>(ROUTE_PROVIDER_STATE_KEY);
43
+ const router = useRouter();
44
+
45
+ // Reactive state
46
+ // ----------------------------------------------------------------------------
47
+ const navigatorRef = ref<HTMLElement>();
48
+ const ges = ref<unknown>(null);
49
+ const transform = reactive<RouteNavigatorTransformState>({
50
+ back: BACK_LAYER_PEEK_PCT,
51
+ prepare: 100,
52
+ active: 0,
53
+ backdrop: 0,
54
+ duration: undefined,
55
+ });
56
+ const stack = ref<RouteStack[]>([]);
57
+ const activeIndex = ref(0);
58
+ const backdropIndex = ref(0);
59
+
60
+ // Computed properties
61
+ // ----------------------------------------------------------------------------
62
+ const navigatorAttrs = computed(() =>
63
+ getRouteNavigatorAttrs({
64
+ direction: props.direction,
65
+ variant: props.variant,
66
+ moving: transform.active !== 0,
67
+ transform,
68
+ })
69
+ );
70
+
71
+ const componentAttrs = computed(() =>
72
+ getRouteNavigatorComponentAttrs({ direction: props.direction })
73
+ );
74
+
75
+ const backdropAttrs = computed(() =>
76
+ getRouteNavigatorBackdropAttrs({ zIndex: backdropIndex.value * 2 + 99 })
77
+ );
78
+
79
+ const prevPage = computed(() => {
80
+ if (activeIndex.value > 0) {
81
+ return stack.value.at(activeIndex.value - 1) ?? null;
82
+ }
83
+ return null;
84
+ });
85
+
86
+ // Methods
87
+ // ----------------------------------------------------------------------------
88
+ const changeRoute = (value: RouteStack[]) => {
89
+ const data = clone(value);
90
+
91
+ // Case: first time
92
+ if (stack.value.length === 0) {
93
+ stack.value = data;
94
+ return;
95
+ }
96
+
97
+ // Case: update
98
+ if (stack.value.length === data.length) {
99
+ const ln = stack.value.length - 1;
100
+ stack.value[ln].stack = data[ln].stack;
101
+ }
102
+ // Case: back
103
+ else if (stack.value.length > data.length) {
104
+ const newIndex = activeIndex.value - 1;
105
+ resetTransform();
106
+ activeIndex.value = newIndex;
107
+ transform.backdrop = 0;
108
+ emit("transform", transform);
109
+
110
+ setTimeout(() => {
111
+ stack.value = stack.value.slice(0, -1);
112
+ backdropIndex.value = newIndex;
113
+ transform.backdrop = 100;
114
+ emit("transform", transform);
115
+ }, 250);
116
+ }
117
+ // Case: next
118
+ else {
119
+ stack.value.push(data[data.length - 1]);
120
+ transform.duration = "0s";
121
+ transform.backdrop = 0;
122
+ emit("transform", transform);
123
+
124
+ setTimeout(
125
+ () => {
126
+ backdropIndex.value = backdropIndex.value + 1;
127
+ transform.duration = undefined;
128
+ emit("transform", transform);
129
+ },
130
+ props.variant === "swipe" ? 10 : 0
131
+ );
132
+
133
+ setTimeout(
134
+ () => {
135
+ transform.backdrop = 100;
136
+ activeIndex.value = stack.value.length - 1;
137
+ emit("transform", transform);
138
+ },
139
+ props.variant === "swipe" ? 100 : 0
140
+ );
141
+ }
142
+ };
143
+
144
+ const goBack = () => {
145
+ if (activeIndex.value <= 0) return;
146
+ const prev = stack.value[activeIndex.value - 1];
147
+ if (!prev) return;
148
+ router.back();
149
+ };
150
+
151
+ const resetTransform = () => {
152
+ transform.back = BACK_LAYER_PEEK_PCT;
153
+ transform.prepare = 100;
154
+ transform.active = 0;
155
+ transform.duration = undefined;
156
+ emit("transform", transform);
157
+ };
158
+
159
+ const move = (data: RouteNavigatorGesture) => {
160
+ const width = navigatorRef.value?.offsetWidth ?? 0;
161
+ let activePercent = 0;
162
+
163
+ if (props.direction == "left" || props.direction == "right") {
164
+ activePercent = (Math.abs(data.deltaX) / width) * 100;
165
+ } else {
166
+ activePercent = (Math.abs(data.deltaY) / width) * 100;
167
+ }
168
+
169
+ transform.back = ((100 - activePercent) * BACK_LAYER_PEEK_PCT) / 100;
170
+ transform.active = activePercent;
171
+ transform.backdrop = 100 - transform.active;
172
+ transform.duration = "0s";
173
+
174
+ emit("transform", transform);
175
+ };
176
+
177
+ const up = (data: RouteNavigatorGesture) => {
178
+ const width = navigatorRef.value?.offsetWidth ?? 0;
179
+ let percent = 0;
180
+
181
+ if (props.direction == "left" || props.direction == "right") {
182
+ percent = (Math.abs(data.deltaX) / width) * 100;
183
+ } else {
184
+ percent = (Math.abs(data.deltaY) / width) * 100;
185
+ }
186
+
187
+ if (percent >= 60) {
188
+ goBack();
189
+ } else {
190
+ resetTransform();
191
+ }
192
+ };
193
+
194
+ // Lifecycle
195
+ // ----------------------------------------------------------------------------
196
+ watch(
197
+ () => unref(provider?.stack),
198
+ (value) => {
199
+ changeRoute(value as RouteStack[]);
200
+ }
201
+ );
202
+
203
+ onMounted(() => {
204
+ changeRoute(unref(provider?.stack) as RouteStack[]);
205
+
206
+ // Initialize gesture
207
+ ges.value =
208
+ navigatorRef.value &&
209
+ gesture(
210
+ navigatorRef.value,
211
+ {
212
+ options: {
213
+ trackOutsideElement: true,
214
+ },
215
+
216
+ beforeEvent(e: Event) {
217
+ const target = e.target as HTMLElement | null;
218
+ const isEditable = target?.closest("input, textarea, select, button, [contenteditable]");
219
+ if (isEditable || props.variant === "none" || !prevPage.value) return false;
220
+ e.stopPropagation();
221
+ return true;
222
+ },
223
+
224
+ fast({ initialDirection }: GestureFastPayload) {
225
+ if (!initialDirection || initialDirection !== props.direction) return;
226
+ goBack();
227
+ },
228
+
229
+ move({ deltaX, deltaY, initialDirection, event }: GestureMovePayload) {
230
+ if (!initialDirection || initialDirection !== props.direction) return;
231
+ if (event.cancelable) event.preventDefault();
232
+ move({ deltaX, deltaY });
233
+ },
234
+
235
+ up({ deltaX, deltaY, initialDirection }: GestureUpPayload) {
236
+ if (!initialDirection || initialDirection !== props.direction) return;
237
+ up({ deltaX, deltaY });
238
+ },
239
+
240
+ cancel() {
241
+ resetTransform();
242
+ },
243
+ },
244
+ {
245
+ element: { passive: false },
246
+ move: { passive: false },
247
+ }
248
+ );
249
+ });
250
+
251
+ onUnmounted(() => {
252
+ if (ges.value) (ges.value as { destroy: () => void }).destroy();
253
+ });
254
+ </script>
@@ -0,0 +1 @@
1
+ export { default as RouteOutlet } from "./route-outlet.vue";
@@ -0,0 +1 @@
1
+ <component :is="renderComponent" v-if="renderComponent"></component>
@@ -0,0 +1,39 @@
1
+ <template src="./route-outlet.html"></template>
2
+ <script lang="ts" setup>
3
+ import { markRaw, onMounted, ref } from "vue";
4
+
5
+ // Component setup (props, emits, injects)
6
+ // ----------------------------------------------------------------------------
7
+ const props = defineProps<{
8
+ component: unknown;
9
+ }>();
10
+
11
+ // Reactive state
12
+ // ----------------------------------------------------------------------------
13
+ const renderComponent = ref<unknown | null>(null);
14
+
15
+ // Methods
16
+ // ----------------------------------------------------------------------------
17
+ const resolveComponent = async (raw: unknown): Promise<unknown> => {
18
+ if (typeof raw !== "function") return raw;
19
+ const mod = await (raw as () => Promise<{ default?: unknown }>)();
20
+ return mod?.default ?? mod;
21
+ };
22
+
23
+ /** Accepts a component, async loader, or `{ default }` wrapper (common for vue-router). */
24
+ const resolveFromProp = async (raw: unknown): Promise<unknown | null> => {
25
+ if (raw == null) return null;
26
+ const inner =
27
+ typeof raw === "object" && raw !== null && "default" in raw
28
+ ? (raw as { default: unknown }).default
29
+ : raw;
30
+ return resolveComponent(inner);
31
+ };
32
+
33
+ // Lifecycle
34
+ // ----------------------------------------------------------------------------
35
+ onMounted(async () => {
36
+ const resolved = await resolveFromProp(props.component);
37
+ renderComponent.value = resolved ? markRaw(resolved) : null;
38
+ });
39
+ </script>
@@ -0,0 +1 @@
1
+ export { default as RouteProvider } from "./route-provider.vue";
@@ -0,0 +1,24 @@
1
+ <template src="./route-provider.html"></template>
2
+ <script lang="ts" setup>
3
+ import { provide, computed } from "vue";
4
+ import {
5
+ ROUTE_PROVIDER_STATE_KEY,
6
+ type RouteProviderProps,
7
+ type RouteProviderState,
8
+ } from "@toife/core";
9
+ import type { ProviderStateRefs } from "../../../shared/provider-state";
10
+
11
+ // Component setup (props, emits, injects)
12
+ // ----------------------------------------------------------------------------
13
+ const props = defineProps<RouteProviderProps>();
14
+
15
+ // Computed properties
16
+ // ----------------------------------------------------------------------------
17
+ const stack = computed(() => props.stack);
18
+
19
+ // Provide / expose (public API)
20
+ // ----------------------------------------------------------------------------
21
+ provide<ProviderStateRefs<RouteProviderState>>(ROUTE_PROVIDER_STATE_KEY, {
22
+ stack,
23
+ });
24
+ </script>
@@ -0,0 +1,2 @@
1
+ export { default as RouteWrapper } from "./route-wrapper.vue";
2
+ export * from "./route-wrapper.composable";
@@ -0,0 +1,49 @@
1
+ import { shallowRef } from "vue";
2
+ import { type RouteComponent, type RouteLocationMatched } from "vue-router";
3
+ import type { RouteStack } from "@toife/core";
4
+
5
+ const stack = shallowRef<RouteStack[]>([]);
6
+
7
+ /** Recursively builds a nested stack from vue-router `matched` for the navigator. */
8
+ const buildTree = (matched: RouteLocationMatched[], current: RouteStack[]) => {
9
+ if (matched.length === 0) return current;
10
+
11
+ // User navigated back: drop the leaf so we merge into the parent segment
12
+ if (current.length > 1 && current[current.length - 2]?.name === matched[0].name) {
13
+ current.pop();
14
+ }
15
+
16
+ // Same route record: update nested stack only
17
+ if (
18
+ current.length > 0 &&
19
+ current[current.length - 1] &&
20
+ current[current.length - 1].name === matched[0].name
21
+ ) {
22
+ matched.shift();
23
+ current[current.length - 1].stack = buildTree(matched, current[current.length - 1].stack);
24
+ }
25
+ // Push a new segment (forward navigation)
26
+ else {
27
+ const name = String(matched[0].name);
28
+ const component = matched[0].components as RouteComponent;
29
+ matched.shift();
30
+ current.push({
31
+ name,
32
+ component,
33
+ stack: buildTree(matched, []),
34
+ });
35
+ }
36
+
37
+ return current;
38
+ };
39
+
40
+ export const useRouteWrapper = () => {
41
+ const updateRoutes = (matched: RouteLocationMatched[]) => {
42
+ stack.value = buildTree([...matched], [...stack.value]);
43
+ };
44
+
45
+ return {
46
+ stack,
47
+ updateRoutes,
48
+ };
49
+ };
@@ -0,0 +1,3 @@
1
+ <RouteProvider :stack="stack">
2
+ <slot />
3
+ </RouteProvider>
@@ -0,0 +1,44 @@
1
+ <template src="./route-wrapper.html"></template>
2
+ <script lang="ts" setup>
3
+ import { onMounted, watch } from "vue";
4
+ import { useRouteWrapper } from "./route-wrapper.composable";
5
+ import { useRouter, useRoute } from "vue-router";
6
+ import { RouteProvider } from "../route-provider";
7
+ import { ROUTE_WRAPPER_DEFAULT_PROPS, type RouteWrapperProps } from "@toife/core";
8
+ // Component setup (props, emits, injects)
9
+ // ----------------------------------------------------------------------------
10
+ const props = withDefaults(defineProps<RouteWrapperProps>(), {
11
+ ...ROUTE_WRAPPER_DEFAULT_PROPS,
12
+ });
13
+ const { updateRoutes, stack } = useRouteWrapper();
14
+ const route = useRoute();
15
+ const router = useRouter();
16
+
17
+ // Lifecycle
18
+ // ----------------------------------------------------------------------------
19
+ watch(
20
+ () => route.matched,
21
+ () => {
22
+ updateRoutes(route.matched);
23
+ }
24
+ );
25
+
26
+ onMounted(() => {
27
+ // Add home route to stack
28
+ const homeName = props.homeRouteName;
29
+
30
+ if (route.name === homeName) {
31
+ updateRoutes(route.matched);
32
+ } else {
33
+ const homeLocation = router.resolve({ name: homeName });
34
+ if (homeLocation.matched.length > 0) {
35
+ updateRoutes(homeLocation.matched);
36
+ }
37
+
38
+ // Apply current route to stack
39
+ setTimeout(() => {
40
+ updateRoutes(route.matched);
41
+ }, 50);
42
+ }
43
+ });
44
+ </script>
@@ -0,0 +1,9 @@
1
+ import type { RouteStack } from "@toife/core";
2
+
3
+ /** Deep-clone the nested route stack tree (each level has its own `stack` branch). */
4
+ export const clone = (value: RouteStack[]): RouteStack[] => {
5
+ return value.map((item: RouteStack) => ({
6
+ ...item,
7
+ stack: clone(item.stack),
8
+ })) as RouteStack[];
9
+ };
@@ -0,0 +1 @@
1
+ export { default as SegmentedField } from "./segmented-field.vue";
@@ -0,0 +1,16 @@
1
+ <div v-bind="wrapperAttrs">
2
+ <div v-bind="contentAttrs" @pointerup="onFieldClick">
3
+ <Field
4
+ v-for="(_, index) in props.length"
5
+ :key="index"
6
+ v-bind="fieldAttrs"
7
+ :model-value="segments[index]"
8
+ :tabindex="index === firstEmptyIndex ? 0 : -1"
9
+ @beforeinput="onSegmentBeforeinput($event, index)"
10
+ @input="onSegmentInput($event, index)"
11
+ @focus="onSegmentFocus($event, index)"
12
+ ref="fields"
13
+ />
14
+ </div>
15
+ <div v-bind="messageAttrs" v-if="message">{{ message }}</div>
16
+ </div>