vue3-router-tab 0.0.1 → 1.0.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.
@@ -0,0 +1,325 @@
1
+ @use "sass:math";
2
+ @use "sass:color";
3
+
4
+ // ----------------------
5
+ // Variables
6
+ // ----------------------
7
+ $color-primary: #42b983;
8
+ $border-color: #eaecef;
9
+ $text-color: #4d4d4d;
10
+
11
+ $font-size: 14px;
12
+
13
+ $border: 1px solid $border-color;
14
+ $tab-trans: all 0.3s ease-in-out;
15
+
16
+ $hd-height: 40px;
17
+
18
+ $tab-padding: 20px;
19
+ $close-icon-margin: 4px;
20
+ $close-icon-size: 13px;
21
+
22
+ // ----------------------
23
+ // Router Tab Component
24
+ // ----------------------
25
+ .router-tab {
26
+ display: flex;
27
+ flex-direction: column;
28
+ min-height: 300px;
29
+
30
+ &__header {
31
+ position: relative;
32
+ z-index: 9;
33
+ display: flex;
34
+ flex: none;
35
+ box-sizing: border-box;
36
+ height: $hd-height;
37
+ border-bottom: 1px solid $border-color;
38
+ transition: all 0.2s ease-in-out;
39
+ }
40
+
41
+ &__scroll {
42
+ position: relative;
43
+ flex: 1 1 0px;
44
+ height: $hd-height;
45
+ overflow: hidden;
46
+
47
+ &-container {
48
+ width: 100%;
49
+ height: 100%;
50
+ overflow: hidden;
51
+
52
+ &.is-mobile {
53
+ overflow-x: auto;
54
+ overflow-y: hidden;
55
+ }
56
+ }
57
+ }
58
+
59
+ &__scrollbar {
60
+ $h: 3px;
61
+
62
+ position: absolute;
63
+ right: 0;
64
+ bottom: 0;
65
+ left: 0;
66
+ height: $h;
67
+ background-color: rgba(0, 0, 0, 0.1);
68
+ border-radius: $h;
69
+ opacity: 0;
70
+ transition: opacity 0.3s ease-in-out;
71
+
72
+ .router-tab__scroll:hover &,
73
+ &.is-dragging {
74
+ opacity: 1;
75
+ }
76
+
77
+ &-thumb {
78
+ position: absolute;
79
+ top: 0;
80
+ left: 0;
81
+ height: 100%;
82
+ background-color: rgba(0, 0, 0, 0.1);
83
+ border-radius: $h;
84
+ transition: background-color 0.3s ease-in-out;
85
+
86
+ &:hover,
87
+ .router-tab__scrollbar.is-dragging & {
88
+ background-color: rgba($color-primary, 0.8);
89
+ }
90
+ }
91
+ }
92
+
93
+ &__nav {
94
+ position: relative;
95
+ display: inline-flex;
96
+ flex-wrap: nowrap;
97
+ height: 100%;
98
+ margin: 0;
99
+ padding: 0;
100
+ list-style: none;
101
+ }
102
+
103
+ &__item {
104
+ position: relative;
105
+ display: flex;
106
+ flex: none;
107
+ align-items: center;
108
+ padding: 0 $tab-padding;
109
+ color: $text-color;
110
+ font-size: $font-size;
111
+ border: $border;
112
+ border-left: none;
113
+ transform-origin: left bottom;
114
+ cursor: pointer;
115
+ transition: $tab-trans;
116
+ user-select: none;
117
+
118
+ &:first-child {
119
+ border-left: $border;
120
+ }
121
+
122
+ &.is-contextmenu {
123
+ color: #000;
124
+ }
125
+
126
+ &:hover,
127
+ &.is-active {
128
+ color: $color-primary;
129
+
130
+ &.is-closable {
131
+ padding: 0 (
132
+ $tab-padding - math.div($close-icon-size + $close-icon-margin, 2)
133
+ );
134
+ }
135
+
136
+ .router-tab__item-close {
137
+ width: $close-icon-size;
138
+ margin-left: $close-icon-margin;
139
+
140
+ &::before,
141
+ &::after {
142
+ border-color: $color-primary;
143
+ }
144
+ }
145
+ }
146
+
147
+ &.is-active {
148
+ border-bottom-color: #fff;
149
+ }
150
+
151
+ &.is-drag-over {
152
+ background: rgba(0, 0, 0, 0.05);
153
+ transition: background 0.15s ease;
154
+ }
155
+
156
+ &-title {
157
+ min-width: 30px;
158
+ max-width: 100px;
159
+ overflow: hidden;
160
+ white-space: nowrap;
161
+ text-overflow: ellipsis;
162
+ }
163
+
164
+ &-icon {
165
+ margin-right: 5px;
166
+ font-size: 16px;
167
+ }
168
+
169
+ &-close {
170
+ $inner: 8px;
171
+
172
+ position: relative;
173
+ display: block;
174
+ width: 0;
175
+ height: $close-icon-size;
176
+ margin-left: 0;
177
+ overflow: hidden;
178
+ border-radius: 50%;
179
+ cursor: pointer;
180
+ transition: $tab-trans;
181
+
182
+ &::before,
183
+ &::after {
184
+ position: absolute;
185
+ top: 6px;
186
+ left: 50%;
187
+ display: block;
188
+ width: $inner;
189
+ height: 1px;
190
+ margin-left: math.div(-$inner, 2);
191
+ background-color: $text-color;
192
+ transition: background-color 0.2s ease-in-out;
193
+ content: "";
194
+ }
195
+
196
+ &::before {
197
+ transform: rotate(-45deg);
198
+ }
199
+
200
+ &::after {
201
+ transform: rotate(45deg);
202
+ }
203
+
204
+ &:hover {
205
+ background-color: color.mix($text-color, #fff, 50%);
206
+ &::before,
207
+ &::after {
208
+ background-color: #fff;
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ &__container {
215
+ position: relative;
216
+ flex: 1;
217
+ overflow-x: hidden;
218
+ overflow-y: auto;
219
+ background: #fff;
220
+ transition: all 0.4s ease-in-out;
221
+
222
+ > .router-alive {
223
+ height: 100%;
224
+ }
225
+ }
226
+
227
+ &__iframe {
228
+ position: absolute;
229
+ top: 0;
230
+ left: 0;
231
+ width: 100%;
232
+ height: 100%;
233
+ }
234
+
235
+ &__contextmenu {
236
+ position: fixed;
237
+ z-index: 999;
238
+ min-width: 120px;
239
+ padding: 8px 0;
240
+ font-size: $font-size;
241
+ background: #fff;
242
+ border: $border;
243
+ box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, 0.1);
244
+ transform-origin: left top;
245
+ transition: all 0.25s ease-in;
246
+
247
+ &-item {
248
+ position: relative;
249
+ display: block;
250
+ padding: 0 20px;
251
+ color: $text-color;
252
+ line-height: 30px;
253
+ cursor: pointer;
254
+ transition: all 0.2s ease-in-out;
255
+ user-select: none;
256
+
257
+ &:hover,
258
+ &:active {
259
+ color: $color-primary;
260
+ }
261
+
262
+ &[disabled],
263
+ &[aria-disabled='true'] {
264
+ color: #aaa;
265
+ background: none;
266
+ cursor: default;
267
+ pointer-events: none;
268
+ }
269
+
270
+ .has-icon & {
271
+ padding-left: 30px;
272
+ }
273
+ }
274
+
275
+ &-icon {
276
+ position: absolute;
277
+ top: 0;
278
+ left: 8px;
279
+ display: none;
280
+ line-height: 30px;
281
+
282
+ .has-icon & {
283
+ display: block;
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ // ----------------------
290
+ // Transitions
291
+ // ----------------------
292
+ .router-tab-zoom {
293
+ &-enter-active,
294
+ &-leave-active {
295
+ transition: all 0.4s;
296
+ }
297
+
298
+ &-enter,
299
+ &-leave-to {
300
+ transform: scale(0);
301
+ opacity: 0;
302
+ }
303
+ }
304
+
305
+ .router-tab-swap {
306
+ $trans: 30px;
307
+
308
+ &-enter-active,
309
+ &-leave-active {
310
+ transition: all 0.5s;
311
+ }
312
+
313
+ &-enter,
314
+ &-leave-to {
315
+ opacity: 0;
316
+ }
317
+
318
+ &-enter {
319
+ transform: translateX(-#{$trans});
320
+ }
321
+
322
+ &-leave-to {
323
+ transform: translateX(#{$trans});
324
+ }
325
+ }
@@ -0,0 +1,29 @@
1
+ import { getCurrentInstance, inject } from 'vue'
2
+ import type { RouterTabsContext } from './core/types'
3
+ import { routerTabsKey } from './constants'
4
+
5
+ export interface UseRouterTabsOptions {
6
+ optional?: boolean
7
+ }
8
+
9
+ export function useRouterTabs(options: UseRouterTabsOptions = {}): RouterTabsContext | null {
10
+ const { optional = false } = options
11
+
12
+ const injected = inject(routerTabsKey, null)
13
+ if (injected) return injected
14
+
15
+ const legacy = inject<RouterTabsContext | null>('$tabs' as any, null)
16
+ if (legacy) return legacy
17
+
18
+ const instance = getCurrentInstance()
19
+ const globalTabs = instance?.appContext.config.globalProperties.$tabs as RouterTabsContext | undefined
20
+ if (globalTabs) return globalTabs
21
+
22
+ if (!optional) {
23
+ throw new Error('[RouterTabs] useRouterTabs must be used within <router-tab>.')
24
+ }
25
+
26
+ return null
27
+ }
28
+
29
+ export default useRouterTabs
@@ -0,0 +1,34 @@
1
+ import type { TransitionLike } from '../core/types'
2
+
3
+ export const emptyArray: never[] = []
4
+
5
+ export function getTransOpt(trans?: TransitionLike): Record<string, unknown> {
6
+ if (!trans) return {}
7
+ return typeof trans === 'string' ? { name: trans } : trans
8
+ }
9
+
10
+ export function normalizeClass(value: unknown): Record<string, boolean> {
11
+ if (!value) return {}
12
+ if (typeof value === 'string') {
13
+ return value.split(/\s+/).reduce<Record<string, boolean>>((acc, cls) => {
14
+ if (cls) acc[cls] = true
15
+ return acc
16
+ }, {})
17
+ }
18
+ if (Array.isArray(value)) {
19
+ return value.reduce<Record<string, boolean>>((acc, item) => {
20
+ Object.assign(acc, normalizeClass(item))
21
+ return acc
22
+ }, {})
23
+ }
24
+ if (typeof value === 'object') {
25
+ return Object.entries(value as Record<string, boolean>).reduce<Record<string, boolean>>(
26
+ (acc, [key, val]) => {
27
+ if (val) acc[key] = true
28
+ return acc
29
+ },
30
+ {}
31
+ )
32
+ }
33
+ return {}
34
+ }
package/package.json CHANGED
@@ -1,19 +1,66 @@
1
1
  {
2
2
  "name": "vue3-router-tab",
3
- "version": "0.0.1",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "lib",
8
+ "index.d.ts"
9
+ ],
10
+ "main": "dist/vue3-router-tab.umd.cjs",
11
+ "module": "dist/vue3-router-tab.js",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.d.ts",
15
+ "import": "./dist/vue3-router-tab.js",
16
+ "require": "./dist/vue3-router-tab.umd.cjs"
17
+ },
18
+ "./style": "./dist/style.css"
19
+ },
5
20
  "scripts": {
6
21
  "build": "vite build"
7
22
  },
8
23
  "dependencies": {
9
- "vue": "^3.3.4"
24
+ "vue": "^3.5.22",
25
+ "vue-router": "^4.5.1"
10
26
  },
11
27
  "devDependencies": {
12
- "@vitejs/plugin-vue": "^4.2.3",
13
- "vite": "^4.4.5"
28
+ "@vitejs/plugin-vue": "^6.0.1",
29
+ "pinia": "^2.1.7",
30
+ "sass": "^1.93.2",
31
+ "sass-loader": "^16.0.5",
32
+ "typescript": "^5.9.2",
33
+ "vite": "^7.1.7",
34
+ "vite-plugin-dts": "^4.5.4",
35
+ "vite-plugin-libcss": "^1.1.2"
36
+ },
37
+ "keywords": [
38
+ "vue3-router-tab",
39
+ "vue3",
40
+ "router",
41
+ "tabs",
42
+ "tab"
43
+ ],
44
+ "license": "MIT",
45
+ "homepage": "https://github.com/anilshr25/vue3-router-tab/#readme",
46
+ "description": "Simple Confirm Dialog verification plugin with Vue 3.",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/anilshr25/vue3-router-tab.git"
14
50
  },
15
- "description": "This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.",
16
- "main": "vite.config.js",
17
51
  "author": "",
18
- "license": "ISC"
52
+ "bugs": {
53
+ "url": "https://github.com/anilshr25/vue3-router-tab/issues"
54
+ },
55
+ "types": "index.d.ts",
56
+ "pnpm": {
57
+ "onlyBuiltDependencies": [
58
+ "@parcel/watcher",
59
+ "esbuild",
60
+ "vue-demi"
61
+ ]
62
+ },
63
+ "peerDependencies": {
64
+ "pinia": "^2.1.7"
65
+ }
19
66
  }
@@ -1,3 +0,0 @@
1
- {
2
- "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3
- }
package/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite + Vue</title>
8
- </head>
9
- <body>
10
- <div id="app"></div>
11
- <script type="module" src="/src/main.js"></script>
12
- </body>
13
- </html>
package/public/vite.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/src/App.vue DELETED
@@ -1,30 +0,0 @@
1
- <script setup>
2
- import HelloWorld from './components/HelloWorld.vue'
3
- </script>
4
-
5
- <template>
6
- <div>
7
- <a href="https://vitejs.dev" target="_blank">
8
- <img src="/vite.svg" class="logo" alt="Vite logo" />
9
- </a>
10
- <a href="https://vuejs.org/" target="_blank">
11
- <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
12
- </a>
13
- </div>
14
- <HelloWorld msg="Vite + Vue" />
15
- </template>
16
-
17
- <style scoped>
18
- .logo {
19
- height: 6em;
20
- padding: 1.5em;
21
- will-change: filter;
22
- transition: filter 300ms;
23
- }
24
- .logo:hover {
25
- filter: drop-shadow(0 0 2em #646cffaa);
26
- }
27
- .logo.vue:hover {
28
- filter: drop-shadow(0 0 2em #42b883aa);
29
- }
30
- </style>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
@@ -1,40 +0,0 @@
1
- <script setup>
2
- import { ref } from 'vue'
3
-
4
- defineProps({
5
- msg: String,
6
- })
7
-
8
- const count = ref(0)
9
- </script>
10
-
11
- <template>
12
- <h1>{{ msg }}</h1>
13
-
14
- <div class="card">
15
- <button type="button" @click="count++">count is {{ count }}</button>
16
- <p>
17
- Edit
18
- <code>components/HelloWorld.vue</code> to test HMR
19
- </p>
20
- </div>
21
-
22
- <p>
23
- Check out
24
- <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
25
- >create-vue</a
26
- >, the official Vue + Vite starter
27
- </p>
28
- <p>
29
- Install
30
- <a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
31
- in your IDE for a better DX
32
- </p>
33
- <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
34
- </template>
35
-
36
- <style scoped>
37
- .read-the-docs {
38
- color: #888;
39
- }
40
- </style>
package/src/main.js DELETED
@@ -1,5 +0,0 @@
1
- import { createApp } from 'vue'
2
- import './style.css'
3
- import App from './App.vue'
4
-
5
- createApp(App).mount('#app')
package/src/style.css DELETED
@@ -1,89 +0,0 @@
1
- :root {
2
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- -webkit-text-size-adjust: 100%;
15
- }
16
-
17
- a {
18
- font-weight: 500;
19
- color: #646cff;
20
- text-decoration: inherit;
21
- }
22
- a:hover {
23
- color: #535bf2;
24
- }
25
-
26
- a {
27
- font-weight: 500;
28
- color: #646cff;
29
- text-decoration: inherit;
30
- }
31
- a:hover {
32
- color: #535bf2;
33
- }
34
-
35
- body {
36
- margin: 0;
37
- display: flex;
38
- place-items: center;
39
- min-width: 320px;
40
- min-height: 100vh;
41
- }
42
-
43
- h1 {
44
- font-size: 3.2em;
45
- line-height: 1.1;
46
- }
47
-
48
- button {
49
- border-radius: 8px;
50
- border: 1px solid transparent;
51
- padding: 0.6em 1.2em;
52
- font-size: 1em;
53
- font-weight: 500;
54
- font-family: inherit;
55
- background-color: #1a1a1a;
56
- cursor: pointer;
57
- transition: border-color 0.25s;
58
- }
59
- button:hover {
60
- border-color: #646cff;
61
- }
62
- button:focus,
63
- button:focus-visible {
64
- outline: 4px auto -webkit-focus-ring-color;
65
- }
66
-
67
- .card {
68
- padding: 2em;
69
- }
70
-
71
- #app {
72
- max-width: 1280px;
73
- margin: 0 auto;
74
- padding: 2rem;
75
- text-align: center;
76
- }
77
-
78
- @media (prefers-color-scheme: light) {
79
- :root {
80
- color: #213547;
81
- background-color: #ffffff;
82
- }
83
- a:hover {
84
- color: #747bff;
85
- }
86
- button {
87
- background-color: #f9f9f9;
88
- }
89
- }
package/vite.config.js DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import vue from '@vitejs/plugin-vue'
3
-
4
- // https://vitejs.dev/config/
5
- export default defineConfig({
6
- plugins: [vue()],
7
- })