vue-stack-tabs 1.3.0 → 1.4.1
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/README.en.md +119 -52
- package/README.md +104 -41
- package/README.ru.md +125 -54
- package/dist/{src/lib/StackTabs.vue.d.ts → StackTabs.vue.d.ts} +5 -5
- package/dist/{src/lib/components → components}/ContextMenu/index.vue.d.ts +1 -1
- package/dist/components/StackKeepAlive/emptyPlaceholder.d.ts +3 -0
- package/dist/{src/lib/components → components}/TabHeader/TabHeaderItem.vue.d.ts +9 -0
- package/dist/{src/lib/components → components}/TabHeader/index.vue.d.ts +3 -3
- package/dist/{src/lib/hooks → hooks}/stackTabsContext.d.ts +5 -2
- package/dist/hooks/tabPanel/pageComponentFactory.d.ts +20 -0
- package/dist/{src/lib/hooks → hooks}/tabPanel/scroll.d.ts +2 -0
- package/dist/hooks/useScrollShadow.d.ts +20 -0
- package/dist/{src/lib/hooks → hooks}/useTabActions.d.ts +7 -2
- package/dist/{src/lib/hooks → hooks}/useTabEventBus.d.ts +2 -1
- package/dist/{src/lib/hooks → hooks}/useTabPanel.d.ts +3 -1
- package/dist/iframe-bridge.mjs +1 -1
- package/dist/index.d.ts +22 -6
- package/dist/{src/lib/model → model}/TabModel.d.ts +21 -0
- package/dist/nuxt/module.mjs +10 -6
- package/dist/nuxt/runtime/plugin.mjs +1600 -4
- package/dist/style.css +1 -1
- package/dist/utils/iframeBridge.d.ts +7 -1
- package/dist/{src/lib/utils → utils}/scrollUtils.d.ts +7 -1
- package/dist/{src/lib/versionLogger.d.ts → versionLogger.d.ts} +1 -1
- package/dist/vue-stack-tabs.css +1 -1
- package/dist/vue-stack-tabs.es.js +1 -1
- package/package.json +83 -59
- package/src/lib/StackTabs.vue +16 -3
- package/src/lib/assets/style/common/var.scss +2 -0
- package/src/lib/assets/style/stackTab.scss +43 -4
- package/src/lib/components/ContextMenu/index.vue +12 -3
- package/src/lib/components/PageLoading.vue +10 -2
- package/src/lib/components/StackKeepAlive/StackCacheRenderer.vue +3 -9
- package/src/lib/components/StackKeepAlive/emptyPlaceholder.tsx +18 -0
- package/src/lib/components/TabHeader/TabHeaderItem.vue +30 -2
- package/src/lib/components/TabHeader/index.vue +76 -4
- package/src/lib/hooks/stackTabsContext.ts +23 -18
- package/src/lib/hooks/tabPanel/pageComponentFactory.tsx +142 -0
- package/src/lib/hooks/tabPanel/refresh.ts +3 -0
- package/src/lib/hooks/tabPanel/scroll.ts +110 -7
- package/src/lib/hooks/tabPanel/session.ts +29 -3
- package/src/lib/hooks/useContextMenu.ts +57 -11
- package/src/lib/hooks/useIframeManager.ts +18 -2
- package/src/lib/hooks/useScrollShadow.ts +124 -0
- package/src/lib/hooks/useTabActions.ts +20 -8
- package/src/lib/hooks/useTabEventBus.ts +5 -3
- package/src/lib/hooks/useTabPanel.tsx +132 -180
- package/src/lib/hooks/useTabRouter.ts +11 -2
- package/src/lib/i18n/index.ts +3 -3
- package/src/lib/model/TabModel.ts +40 -1
- package/src/lib/nuxt/module.ts +10 -3
- package/src/lib/nuxt/runtime/plugin.ts +13 -2
- package/src/lib/utils/iframeBridge.ts +9 -1
- package/src/lib/utils/scrollUtils.ts +35 -2
- package/src/lib/utils/tabInfoEncoder.ts +21 -8
- package/src/lib/utils/urlParser.ts +3 -1
- package/src/lib/versionLogger.ts +6 -2
- package/dist/favicon.svg +0 -10
- package/dist/iframe-bridge.html +0 -77
- package/dist/iframe-child.html +0 -88
- package/dist/nuxt/favicon.svg +0 -10
- package/dist/nuxt/iframe-bridge.html +0 -77
- package/dist/nuxt/iframe-child.html +0 -88
- package/dist/nuxt/plugin.mjs +0 -9
- package/dist/src/lib/iframe-bridge.d.ts +0 -8
- package/dist/src/lib/index.d.ts +0 -22
- package/dist/src/lib/nuxt/module.d.ts +0 -6
- package/dist/src/lib/nuxt/runtime/plugin.client.d.ts +0 -2
- package/dist/src/lib/nuxt/runtime/plugin.d.ts +0 -2
- package/dist/src/lib/utils/iframeBridge.d.ts +0 -39
- package/src/lib/nuxt/runtime/plugin.client.ts +0 -10
- /package/dist/{src/lib/components → components}/ContextMenu/ContextMenuItem.vue.d.ts +0 -0
- /package/dist/{src/lib/components → components}/PageLoading.vue.d.ts +0 -0
- /package/dist/{src/lib/components → components}/StackKeepAlive/StackCacheRenderer.vue.d.ts +0 -0
- /package/dist/{src/lib/components → components}/StackKeepAlive/StackKeepAlive.vue.d.ts +0 -0
- /package/dist/{src/lib/components → components}/TabHeader/TabHeaderButton.vue.d.ts +0 -0
- /package/dist/{src/lib/components → components}/TabHeader/TabHeaderScroll.vue.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/tabPanel/evict.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/tabPanel/navigationTransaction.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/tabPanel/refresh.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/tabPanel/session.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/tabPanel/state.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/useContextMenu.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/useIframeManager.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/useTabLoading.d.ts +0 -0
- /package/dist/{src/lib/hooks → hooks}/useTabRouter.d.ts +0 -0
- /package/dist/{src/lib/i18n → i18n}/index.d.ts +0 -0
- /package/dist/{src/lib/i18n → i18n}/lang/en.d.ts +0 -0
- /package/dist/{src/lib/i18n → i18n}/lang/zh-CN.d.ts +0 -0
- /package/dist/{src/lib/i18n → i18n}/stackTabsLocale.d.ts +0 -0
- /package/dist/{src/lib/iframe.vue.d.ts → iframe.vue.d.ts} +0 -0
- /package/dist/{src/lib/utils → utils}/stackTabsA11y.d.ts +0 -0
- /package/dist/{src/lib/utils → utils}/stackTabsMessage.d.ts +0 -0
- /package/dist/{src/lib/utils → utils}/tabInfoEncoder.d.ts +0 -0
- /package/dist/{src/lib/utils → utils}/typeGuards.d.ts +0 -0
- /package/dist/{src/lib/utils → utils}/urlParser.d.ts +0 -0
package/README.ru.md
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# vue-stack-tabs
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/vue-stack-tabs)
|
|
4
|
+
[](https://www.npmjs.com/package/vue-stack-tabs)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://bundlephobia.com/package/vue-stack-tabs)
|
|
7
|
+
[](https://jktantan.github.io/vue-stack-tabs/)
|
|
8
|
+
|
|
3
9
|
[中文](./README.md) | [English](./README.en.md) | **Русский**
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
**📖 [Документация](https://jktantan.github.io/vue-stack-tabs/) · 🎮 [Демо](https://jktantan.github.io/vue-stack-tabs/demo.html)**
|
|
12
|
+
|
|
13
|
+
> Библиотека управления вкладками для Vue 3 на основе Vue Router. Она обеспечивает изоляцию вкладок, подобную iframe, через независимые области видимости Vue: каждая вкладка сохраняет собственный экземпляр страницы, кэш и стек навигации.
|
|
6
14
|
|
|
7
15
|
## Возможности
|
|
8
16
|
|
|
@@ -13,6 +21,7 @@
|
|
|
13
21
|
- **Сохранение сессии** — восстановление последних активных вкладок после обновления браузера
|
|
14
22
|
- **Запоминание позиции прокрутки** — автоматическое восстановление позиции прокрутки при переключении вкладок
|
|
15
23
|
- **Контекстное меню** — встроенные действия закрытия/обновления
|
|
24
|
+
- **Слоты** — кнопки слева/справа на панели вкладок, свои плейсхолдеры загрузки/ошибки iframe
|
|
16
25
|
- **Интернационализация** — встроенные китайский и английский языки, расширяемо
|
|
17
26
|
- **Модуль Nuxt 3/4** — работает из коробки
|
|
18
27
|
|
|
@@ -38,7 +47,7 @@ pnpm add vue-stack-tabs
|
|
|
38
47
|
// main.ts
|
|
39
48
|
import { createApp } from 'vue'
|
|
40
49
|
import VueStackTabs from 'vue-stack-tabs'
|
|
41
|
-
import 'vue-stack-tabs/dist/
|
|
50
|
+
import 'vue-stack-tabs/dist/style.css'
|
|
42
51
|
import App from './App.vue'
|
|
43
52
|
import router from './router'
|
|
44
53
|
|
|
@@ -48,6 +57,8 @@ app.use(VueStackTabs)
|
|
|
48
57
|
app.mount('#app')
|
|
49
58
|
```
|
|
50
59
|
|
|
60
|
+
> `vue-stack-tabs` поддерживает только один экземпляр `<VueStackTabs>` на приложение Vue. Экспорт по умолчанию — плагин Vue для `app.use(VueStackTabs)`; если нужен локальный импорт компонента, используйте именованный экспорт ниже.
|
|
61
|
+
|
|
51
62
|
### 2. Настройка маршрутов
|
|
52
63
|
|
|
53
64
|
Вкладки зависят от Vue Router. Необходим родительский маршрут для размещения `<VueStackTabs>`:
|
|
@@ -100,6 +111,12 @@ const defaultTabs: ITabData[] = [
|
|
|
100
111
|
</script>
|
|
101
112
|
```
|
|
102
113
|
|
|
114
|
+
Если нужен локальный импорт компонента, используйте именованный экспорт `VueStackTabs`; именованный экспорт `StackTab` сохранён как алиас совместимости:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { VueStackTabs as VueStackTabsComponent } from 'vue-stack-tabs'
|
|
118
|
+
```
|
|
119
|
+
|
|
103
120
|
### 4. Открытие вкладок
|
|
104
121
|
|
|
105
122
|
```ts
|
|
@@ -133,9 +150,9 @@ refreshTab('about')
|
|
|
133
150
|
export default defineNuxtConfig({
|
|
134
151
|
modules: ['vue-stack-tabs/nuxt'],
|
|
135
152
|
vueStackTabs: {
|
|
136
|
-
locale: '
|
|
153
|
+
locale: 'zh-CN' // встроенные варианты: 'zh-CN' | 'en'
|
|
137
154
|
},
|
|
138
|
-
css: ['vue-stack-tabs/dist/
|
|
155
|
+
css: ['vue-stack-tabs/dist/style.css']
|
|
139
156
|
})
|
|
140
157
|
```
|
|
141
158
|
|
|
@@ -152,7 +169,9 @@ export default defineNuxtConfig({
|
|
|
152
169
|
<script setup lang="ts">
|
|
153
170
|
import type { ITabData } from 'vue-stack-tabs'
|
|
154
171
|
|
|
155
|
-
const defaultTabs: ITabData[] = [
|
|
172
|
+
const defaultTabs: ITabData[] = [
|
|
173
|
+
{ title: 'Главная', path: '/', closable: false, refreshable: true }
|
|
174
|
+
]
|
|
156
175
|
</script>
|
|
157
176
|
```
|
|
158
177
|
|
|
@@ -163,7 +182,9 @@ const defaultTabs: ITabData[] = [{ title: 'Главная', path: '/', closable:
|
|
|
163
182
|
<template>
|
|
164
183
|
<div>
|
|
165
184
|
<h1>Главная</h1>
|
|
166
|
-
<button @click="openTab({ title: 'О приложении', path: '/about' })"
|
|
185
|
+
<button @click="openTab({ title: 'О приложении', path: '/about' })">
|
|
186
|
+
Открыть страницу «О приложении»
|
|
187
|
+
</button>
|
|
167
188
|
</div>
|
|
168
189
|
</template>
|
|
169
190
|
|
|
@@ -183,7 +204,7 @@ const { openTab } = useTabActions()
|
|
|
183
204
|
|
|
184
205
|
```ts
|
|
185
206
|
import VueStackTabs from 'vue-stack-tabs'
|
|
186
|
-
import 'vue-stack-tabs/dist/
|
|
207
|
+
import 'vue-stack-tabs/dist/style.css'
|
|
187
208
|
```
|
|
188
209
|
|
|
189
210
|
Для iframe-страниц используйте суб-вход без побочных эффектов стилей:
|
|
@@ -219,7 +240,7 @@ const off = onRefreshRequest(() => window.location.reload(), {
|
|
|
219
240
|
/>
|
|
220
241
|
```
|
|
221
242
|
|
|
222
|
-
Sandbox по умолчанию сохраняет типичные возможности бизнес-страниц и ориентирован на совместимость, не на строгую изоляцию. Для строгой изоляции удалите `allow-same-origin` и продолжайте уменьшать токены sandbox по потребностям бизнеса.
|
|
243
|
+
Sandbox по умолчанию сохраняет типичные возможности бизнес-страниц и ориентирован на совместимость, не на строгую изоляцию. Для строгой изоляции удалите `allow-same-origin` и продолжайте уменьшать токены sandbox по потребностям бизнеса. Если ваши страницы того же источника не могут работать под sandbox, можно передать пустую строку для отключения sandbox, но это снижает уровень изоляции.
|
|
223
244
|
|
|
224
245
|
---
|
|
225
246
|
|
|
@@ -233,26 +254,36 @@ Sandbox по умолчанию сохраняет типичные возмож
|
|
|
233
254
|
import { useTabActions } from 'vue-stack-tabs'
|
|
234
255
|
|
|
235
256
|
const {
|
|
236
|
-
openTab, // (tab: ITabData, renew?: boolean) => Promise<string>
|
|
257
|
+
openTab, // (tab: ITabData, renew?: boolean) => Promise<string | undefined>
|
|
237
258
|
closeTab, // (id: string) => string
|
|
238
259
|
closeAllTabs, // () => void
|
|
239
260
|
refreshTab, // (id: string) => void
|
|
240
261
|
refreshAllTabs, // () => void
|
|
241
262
|
activeTab, // (id: string, isRoute?: boolean) => void
|
|
263
|
+
moveTab, // (id: string, targetIndex: number) => boolean
|
|
242
264
|
reset, // () => void
|
|
243
|
-
tabs // Ref<ITabItem[]>
|
|
265
|
+
tabs, // Ref<ITabItem[]>
|
|
266
|
+
setIFramePath, // (path: string) => void
|
|
267
|
+
getWrapper, // ({ tabId?, pageId? }) => HTMLElement | undefined
|
|
268
|
+
openInNewWindow // (id: string) => void
|
|
244
269
|
} = useTabActions()
|
|
245
270
|
```
|
|
246
271
|
|
|
247
|
-
| Метод
|
|
248
|
-
|
|
|
249
|
-
| `openTab(tab, renew?)`
|
|
250
|
-
| `closeTab(id)`
|
|
251
|
-
| `closeAllTabs()`
|
|
252
|
-
| `refreshTab(id)`
|
|
253
|
-
| `refreshAllTabs()`
|
|
254
|
-
| `activeTab(id)`
|
|
255
|
-
| `
|
|
272
|
+
| Метод | Описание |
|
|
273
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
274
|
+
| `openTab(tab, renew?)` | Открыть новую вкладку. При `renew=true` очищает стек страниц и открывает заново |
|
|
275
|
+
| `closeTab(id)` | Закрыть указанную вкладку, возвращает ID новой активной вкладки |
|
|
276
|
+
| `closeAllTabs()` | Закрыть все закрываемые вкладки |
|
|
277
|
+
| `refreshTab(id)` | Обновить указанную вкладку (заменяет ID кэша, пересоздаёт экземпляр компонента) |
|
|
278
|
+
| `refreshAllTabs()` | Обновить все вкладки |
|
|
279
|
+
| `activeTab(id)` | Активировать указанную вкладку (переключить вкладку) |
|
|
280
|
+
| `moveTab(id, targetIndex)` | Переместить вкладку на указанный индекс; панель вкладок также поддерживает перетаскивание |
|
|
281
|
+
| `reset()` | Закрыть все вкладки и сбросить состояние |
|
|
282
|
+
| `setIFramePath(path)` | Установить путь маршрута-заглушки iframe (вызывается внутри компонента) |
|
|
283
|
+
| `getWrapper({ tabId?, pageId? })` | Получить DOM-контейнер содержимого; без параметров вернёт первый контейнер, при нескольких вкладках передавайте `tabId` или `pageId` |
|
|
284
|
+
| `openInNewWindow(id)` | Открыть вкладку iframe в новом окне (fallback, если встраивание невозможно) |
|
|
285
|
+
|
|
286
|
+
> Закрытие слева / справа / остальных — операции только контекстного меню, выполняются внутри `TabHeader`/`ContextMenu` через `useTabPanel`.
|
|
256
287
|
|
|
257
288
|
---
|
|
258
289
|
|
|
@@ -268,9 +299,9 @@ const { forward, backward, addScrollTarget } = useTabRouter()
|
|
|
268
299
|
|
|
269
300
|
| Метод | Описание |
|
|
270
301
|
| --------------------------- | ----------------------------------------------------------- |
|
|
271
|
-
| `forward(to)` | Перейти вперёд к новой странице внутри текущей вкладки
|
|
302
|
+
| `forward(to)` | Перейти вперёд к новой странице внутри текущей вкладки |
|
|
272
303
|
| `backward(to, backQuery?)` | Вернуться назад внутри текущей вкладки |
|
|
273
|
-
| `addScrollTarget(selector)` | Зарегистрировать CSS-селектор DOM для запоминания прокрутки
|
|
304
|
+
| `addScrollTarget(selector)` | Зарегистрировать CSS-селектор DOM для запоминания прокрутки |
|
|
274
305
|
|
|
275
306
|
#### forward
|
|
276
307
|
|
|
@@ -333,35 +364,67 @@ fetchData().finally(() => closeTabLoading())
|
|
|
333
364
|
|
|
334
365
|
Свойства компонента `<VueStackTabs>`:
|
|
335
366
|
|
|
336
|
-
| Свойство | Тип | По умолчанию
|
|
337
|
-
|
|
|
338
|
-
| `iframePath`
|
|
339
|
-
| `iframeSandbox`
|
|
340
|
-
| `iframeReferrerPolicy`
|
|
341
|
-
| `iframeAllow`
|
|
342
|
-
| `iframeLoadTimeout`
|
|
343
|
-
| `defaultTabs`
|
|
344
|
-
| `max`
|
|
345
|
-
| `contextmenu`
|
|
346
|
-
| `pageTransition`
|
|
347
|
-
| `pageTransitionBack`
|
|
348
|
-
| `tabTransition`
|
|
349
|
-
| `tabScrollMode`
|
|
350
|
-
| `
|
|
351
|
-
| `
|
|
352
|
-
| `
|
|
353
|
-
| `
|
|
354
|
-
| `
|
|
355
|
-
| `
|
|
367
|
+
| Свойство | Тип | По умолчанию | Описание |
|
|
368
|
+
| ---------------------- | --------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
369
|
+
| `iframePath` | `string` | **Обязательно** | Путь маршрута-заглушки для iframe |
|
|
370
|
+
| `iframeSandbox` | `string` | `allow-scripts allow-forms allow-popups allow-downloads allow-same-origin` | Политика sandbox для iframe; по умолчанию совместимость, не строгая изоляция — удалите `allow-same-origin` для строгой изоляции; пустая строка отключает sandbox (не рекомендуется) |
|
|
371
|
+
| `iframeReferrerPolicy` | `ReferrerPolicy` | `strict-origin-when-cross-origin` | Атрибут referrerpolicy для iframe |
|
|
372
|
+
| `iframeAllow` | `string` | `''` | Атрибут allow для iframe, например `fullscreen` |
|
|
373
|
+
| `iframeLoadTimeout` | `number` | `15000` | Тайм-аут загрузки iframe в мс |
|
|
374
|
+
| `defaultTabs` | `ITabData[]` | `[]` | Начальный список вкладок |
|
|
375
|
+
| `max` | `number` | `20` | Максимальное количество вкладок |
|
|
376
|
+
| `contextmenu` | `boolean \| object` | `true` | Включить контекстное меню |
|
|
377
|
+
| `pageTransition` | `string` | `'stack-tab-swap'` | Имя анимации перехода при навигации вперёд |
|
|
378
|
+
| `pageTransitionBack` | `string` | `'stack-tab-swap-back'` | Имя анимации перехода при навигации назад |
|
|
379
|
+
| `tabTransition` | `string \| TransitionProps` | `'stack-tab-zoom'` | Эффект перехода при добавлении/удалении вкладок |
|
|
380
|
+
| `tabScrollMode` | `TabScrollMode` | `'both'` | Режим прокрутки панели вкладок: `'wheel'` / `'button'` / `'both'` |
|
|
381
|
+
| `space` | `number` | `300` | Шаг прокрутки панели вкладок (px) |
|
|
382
|
+
| `width` | `string` | `'100%'` | Ширина контейнера |
|
|
383
|
+
| `height` | `string` | `'100%'` | Высота контейнера |
|
|
384
|
+
| `i18n` | `string` | `'zh-CN'` | Локаль интернационализации, встроенные `'zh-CN'` / `'en'` |
|
|
385
|
+
| `globalScroll` | `boolean` | `false` | Включить запоминание прокрутки на уровне страницы |
|
|
386
|
+
| `sessionPrefix` | `string` | `''` | Префикс ключа sessionStorage; полезно для нескольких экземпляров на одном источнике |
|
|
387
|
+
| `iframeAllowedOrigins` | `string[]` | `[]` (плюс текущий источник) | Разрешённые источники для iframe postMessage |
|
|
356
388
|
|
|
357
389
|
---
|
|
358
390
|
|
|
359
391
|
## События (Events)
|
|
360
392
|
|
|
361
|
-
| Событие | Параметры | Описание
|
|
362
|
-
| -------------- | -------------- |
|
|
363
|
-
| `onActive` | `(id: string)` | Срабатывает при активации вкладки
|
|
364
|
-
| `onPageLoaded` | — | Срабатывает при загрузке компонента страницы
|
|
393
|
+
| Событие | Параметры | Описание |
|
|
394
|
+
| -------------- | -------------- | -------------------------------------------- |
|
|
395
|
+
| `onActive` | `(id: string)` | Срабатывает при активации вкладки |
|
|
396
|
+
| `onPageLoaded` | — | Срабатывает при загрузке компонента страницы |
|
|
397
|
+
|
|
398
|
+
```vue
|
|
399
|
+
<VueStackTabs
|
|
400
|
+
iframe-path="/iframe"
|
|
401
|
+
@on-active="(id) => console.log('active:', id)"
|
|
402
|
+
@on-page-loaded="() => console.log('page loaded')"
|
|
403
|
+
/>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Слоты
|
|
409
|
+
|
|
410
|
+
| Слот | Параметры слота | Описание |
|
|
411
|
+
| --------------- | ---------------- | -------------------------------------------------------------------- |
|
|
412
|
+
| `leftButton` | — | Левая область панели вкладок, обычно с `TabHeaderButton` |
|
|
413
|
+
| `rightButton` | — | Правая область панели вкладок |
|
|
414
|
+
| `iframeLoading` | `{ tab }` | Свой плейсхолдер загрузки для вкладки iframe |
|
|
415
|
+
| `iframeError` | `{ tab, retry }` | Свой плейсхолдер ошибки для вкладки iframe; `retry()` — перезагрузка |
|
|
416
|
+
|
|
417
|
+
```vue
|
|
418
|
+
<VueStackTabs iframe-path="/iframe">
|
|
419
|
+
<template #leftButton>
|
|
420
|
+
<TabHeaderButton title="Развернуть" @click="collapse = !collapse" />
|
|
421
|
+
</template>
|
|
422
|
+
<template #iframeError="{ tab, retry }">
|
|
423
|
+
<span>{{ tab.title }} не загрузилась</span>
|
|
424
|
+
<button @click="retry">Повторить</button>
|
|
425
|
+
</template>
|
|
426
|
+
</VueStackTabs>
|
|
427
|
+
```
|
|
365
428
|
|
|
366
429
|
---
|
|
367
430
|
|
|
@@ -451,23 +514,29 @@ window.addEventListener('message', (ev) => {
|
|
|
451
514
|
|
|
452
515
|
## Интернационализация
|
|
453
516
|
|
|
454
|
-
Встроенные языки: `zh-CN` (китайский), `en
|
|
517
|
+
Встроенные языки: `zh-CN` (китайский), `en` (английский).
|
|
455
518
|
|
|
456
519
|
```ts
|
|
457
|
-
// Переключить язык
|
|
458
|
-
|
|
520
|
+
// Переключить язык через prop компонента
|
|
521
|
+
// <VueStackTabs iframe-path="/iframe" i18n="en" />
|
|
522
|
+
|
|
523
|
+
// Или при установке плагина
|
|
524
|
+
app.use(VueStackTabs, [{ locale: 'en' }])
|
|
459
525
|
```
|
|
460
526
|
|
|
461
|
-
Пользовательский языковой
|
|
527
|
+
Пользовательский языковой пакет (`messages` — вложенный объект, глубоко сливается со встроенными языками):
|
|
462
528
|
|
|
463
529
|
```ts
|
|
464
530
|
app.use(VueStackTabs, [
|
|
465
531
|
{
|
|
466
532
|
locale: 'ru-RU',
|
|
467
533
|
messages: {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
534
|
+
VueStackTab: {
|
|
535
|
+
close: 'Закрыть',
|
|
536
|
+
reload: 'Обновить',
|
|
537
|
+
loading: 'Загрузка'
|
|
538
|
+
// ...
|
|
539
|
+
}
|
|
471
540
|
}
|
|
472
541
|
}
|
|
473
542
|
])
|
|
@@ -478,12 +547,14 @@ app.use(VueStackTabs, [
|
|
|
478
547
|
## Определения типов
|
|
479
548
|
|
|
480
549
|
```ts
|
|
550
|
+
import type { LocationQueryRaw } from 'vue-router'
|
|
551
|
+
|
|
481
552
|
/** Данные, передаваемые при открытии вкладки */
|
|
482
553
|
interface ITabData {
|
|
483
554
|
id?: string // ID вкладки (автоматически генерируемый UUID, если не указан)
|
|
484
555
|
title: string // Заголовок вкладки
|
|
485
556
|
path: string // Путь маршрута или URL iframe
|
|
486
|
-
query?:
|
|
557
|
+
query?: LocationQueryRaw // Параметры маршрута
|
|
487
558
|
closable?: boolean // Можно ли закрыть (по умолчанию true)
|
|
488
559
|
refreshable?: boolean // Можно ли обновить (по умолчанию true)
|
|
489
560
|
iframe?: boolean // Является ли вкладкой iframe (по умолчанию false)
|
|
@@ -523,4 +594,4 @@ src/lib/
|
|
|
523
594
|
|
|
524
595
|
## Лицензия
|
|
525
596
|
|
|
526
|
-
[
|
|
597
|
+
[Apache-2.0](./LICENSE)
|
|
@@ -22,9 +22,9 @@ type __VLS_Props = {
|
|
|
22
22
|
iframeAllow?: string;
|
|
23
23
|
iframeLoadTimeout?: number;
|
|
24
24
|
};
|
|
25
|
-
declare var __VLS_10: {}, __VLS_13: {},
|
|
25
|
+
declare var __VLS_10: {}, __VLS_13: {}, __VLS_35: {
|
|
26
26
|
tab: import('.').ITabItem;
|
|
27
|
-
},
|
|
27
|
+
}, __VLS_37: {
|
|
28
28
|
tab: import('.').ITabItem;
|
|
29
29
|
retry: (frameId: string) => void;
|
|
30
30
|
};
|
|
@@ -33,9 +33,9 @@ type __VLS_Slots = {} & {
|
|
|
33
33
|
} & {
|
|
34
34
|
rightButton?: (props: typeof __VLS_13) => any;
|
|
35
35
|
} & {
|
|
36
|
-
iframeLoading?: (props: typeof
|
|
36
|
+
iframeLoading?: (props: typeof __VLS_35) => any;
|
|
37
37
|
} & {
|
|
38
|
-
iframeError?: (props: typeof
|
|
38
|
+
iframeError?: (props: typeof __VLS_37) => any;
|
|
39
39
|
};
|
|
40
40
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
41
41
|
onActive: (...args: any[]) => void;
|
|
@@ -44,9 +44,9 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
44
44
|
onOnActive?: ((...args: any[]) => any) | undefined;
|
|
45
45
|
onOnPageLoaded?: ((...args: any[]) => any) | undefined;
|
|
46
46
|
}>, {
|
|
47
|
+
contextmenu: boolean | object;
|
|
47
48
|
defaultTabs: ITabData[];
|
|
48
49
|
max: number;
|
|
49
|
-
contextmenu: boolean | object;
|
|
50
50
|
space: number;
|
|
51
51
|
width: string;
|
|
52
52
|
tabScrollMode: TabScrollMode;
|
|
@@ -15,7 +15,7 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {
|
|
|
15
15
|
}>, {
|
|
16
16
|
left: number;
|
|
17
17
|
top: number;
|
|
18
|
-
contextMenu:
|
|
18
|
+
contextMenu: IContextMenu[];
|
|
19
19
|
restoreFocusElement: HTMLElement | null;
|
|
20
20
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
21
21
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import { ITabItem } from '../../model/TabModel';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
item: ITabItem;
|
|
4
|
+
dropPosition?: 'before' | 'after';
|
|
4
5
|
};
|
|
5
6
|
declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
6
7
|
active: (...args: any[]) => void;
|
|
7
8
|
close: (...args: any[]) => void;
|
|
8
9
|
contextmenu: (...args: any[]) => void;
|
|
10
|
+
dragend: (...args: any[]) => void;
|
|
11
|
+
dragover: (...args: any[]) => void;
|
|
12
|
+
dragstart: (...args: any[]) => void;
|
|
13
|
+
drop: (...args: any[]) => void;
|
|
9
14
|
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
15
|
onActive?: ((...args: any[]) => any) | undefined;
|
|
11
16
|
onClose?: ((...args: any[]) => any) | undefined;
|
|
12
17
|
onContextmenu?: ((...args: any[]) => any) | undefined;
|
|
18
|
+
onDragend?: ((...args: any[]) => any) | undefined;
|
|
19
|
+
onDragover?: ((...args: any[]) => any) | undefined;
|
|
20
|
+
onDragstart?: ((...args: any[]) => any) | undefined;
|
|
21
|
+
onDrop?: ((...args: any[]) => any) | undefined;
|
|
13
22
|
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
14
23
|
declare const _default: typeof __VLS_export;
|
|
15
24
|
export default _default;
|
|
@@ -7,19 +7,19 @@ type __VLS_Props = {
|
|
|
7
7
|
contextmenu?: boolean | object;
|
|
8
8
|
max?: number;
|
|
9
9
|
};
|
|
10
|
-
declare var __VLS_1: {},
|
|
10
|
+
declare var __VLS_1: {}, __VLS_35: {};
|
|
11
11
|
type __VLS_Slots = {} & {
|
|
12
12
|
leftButton?: (props: typeof __VLS_1) => any;
|
|
13
13
|
} & {
|
|
14
|
-
rightButton?: (props: typeof
|
|
14
|
+
rightButton?: (props: typeof __VLS_35) => any;
|
|
15
15
|
};
|
|
16
16
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
17
17
|
active: (...args: any[]) => void;
|
|
18
18
|
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
19
19
|
onActive?: ((...args: any[]) => any) | undefined;
|
|
20
20
|
}>, {
|
|
21
|
-
max: number;
|
|
22
21
|
contextmenu: boolean | object;
|
|
22
|
+
max: number;
|
|
23
23
|
tabScrollMode: TabScrollMode;
|
|
24
24
|
tabTransition: string | TransitionProps;
|
|
25
25
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
@@ -39,11 +39,14 @@ export declare const stackTabsContextKey: InjectionKey<StackTabsRuntimeContext>;
|
|
|
39
39
|
export declare const maximumKey: InjectionKey<Ref<boolean>>;
|
|
40
40
|
/** 创建一个新的运行时上下文实例 */
|
|
41
41
|
export declare const createStackTabsRuntimeContext: (options?: CreateStackTabsRuntimeContextOptions) => StackTabsRuntimeContext;
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* 获取非 App 容器下注册的兼容上下文。
|
|
44
|
+
* @deprecated App 内的 composable 必须依赖 provide/inject,不应使用这个回退。
|
|
45
|
+
*/
|
|
43
46
|
export declare const getActiveStackTabsRuntimeContext: () => StackTabsRuntimeContext | null;
|
|
44
47
|
/** 注册运行时上下文,重复注册会在开发环境抛出错误 */
|
|
45
48
|
export declare const registerStackTabsRuntimeContext: (context: StackTabsRuntimeContext, options?: RegisterStackTabsRuntimeContextOptions) => boolean;
|
|
46
49
|
/** 注销运行时上下文,组件卸载时调用 */
|
|
47
50
|
export declare const unregisterStackTabsRuntimeContext: (context: StackTabsRuntimeContext, options?: UnregisterStackTabsRuntimeContextOptions) => void;
|
|
48
|
-
/** 解析当前可用的运行时上下文,优先使用 inject
|
|
51
|
+
/** 解析当前可用的运行时上下文,优先使用 inject,兼容回退仅限非 App 容器。 */
|
|
49
52
|
export declare const resolveStackTabsRuntimeContext: () => StackTabsRuntimeContext;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DefineComponent } from 'vue';
|
|
2
|
+
import { ITabBase, ITabItem } from '../../model/TabModel';
|
|
3
|
+
import { StackTabsRuntimeContext } from '../stackTabsContext';
|
|
4
|
+
interface PageComponentFactoryOptions {
|
|
5
|
+
runtimeContext: StackTabsRuntimeContext;
|
|
6
|
+
getTab: (id: string) => ITabItem | null;
|
|
7
|
+
addCache: (cacheName: string) => void;
|
|
8
|
+
evictMarkedCaches: () => void;
|
|
9
|
+
addPageScroller: (pageId: string, ...selectors: string[]) => void;
|
|
10
|
+
saveScroller: (pageId: string) => void;
|
|
11
|
+
restoreScroller: (pageId: string) => void;
|
|
12
|
+
removeScroller: (pageId: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare const createPageComponentFactory: (options: PageComponentFactoryOptions) => {
|
|
15
|
+
resolvePageComponent: (ctx: {
|
|
16
|
+
cacheName: string;
|
|
17
|
+
tabInfo: ITabBase;
|
|
18
|
+
}) => DefineComponent;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -4,5 +4,7 @@ export interface TabPanelScrollApi {
|
|
|
4
4
|
saveScroller: (pageCacheId: string) => void;
|
|
5
5
|
removeScroller: (pageCacheId: string) => void;
|
|
6
6
|
addPageScroller: (pageCacheId: string, ...selectorIds: string[]) => void;
|
|
7
|
+
/** 主动失效某页面的滚动元素缓存,例如外部强制刷新缓存页时 */
|
|
8
|
+
invalidateAutoScrollCache: (pageCacheId: string) => void;
|
|
7
9
|
}
|
|
8
10
|
export declare const createTabPanelScroll: (context: StackTabsRuntimeContext) => TabPanelScrollApi;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
export interface UseScrollShadowOptions {
|
|
3
|
+
/** 容器元素的 ref,可以是 Ref<HTMLElement | null> 或 getter 函数 */
|
|
4
|
+
target: Ref<HTMLElement | null> | (() => HTMLElement | null);
|
|
5
|
+
/**
|
|
6
|
+
* 是否监听内容缩放/尺寸变化(默认 true)。
|
|
7
|
+
* 启用后会用 ResizeObserver 监视目标尺寸,容器或子树尺寸变化都会重新计算阴影边界。
|
|
8
|
+
*/
|
|
9
|
+
watchResize?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* 到顶/到底的判定阈值(px,默认 5)。
|
|
12
|
+
* 距离边界 ≤ 阈值视为已贴边(不显示阴影);距离边界 > 阈值视为可继续滚动(显示阴影)。
|
|
13
|
+
*/
|
|
14
|
+
edgeThreshold?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface UseScrollShadowReturn {
|
|
17
|
+
/** 手动同步一次(用于内容变化/resize 后) */
|
|
18
|
+
sync: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const useScrollShadow: (options: UseScrollShadowOptions) => UseScrollShadowReturn;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ITabData } from '../model/TabModel';
|
|
2
|
+
import { StackTabsRuntimeContext } from './stackTabsContext';
|
|
2
3
|
/**
|
|
3
4
|
* useTabActions - 标签操作对外 Hook
|
|
4
5
|
*
|
|
@@ -12,16 +13,20 @@ import { ITabData } from '../model/TabModel';
|
|
|
12
13
|
* - activeTab(id, isRoute?) 激活标签
|
|
13
14
|
* - reset() 重置(关闭所有)
|
|
14
15
|
*/
|
|
15
|
-
export default function useTabActions(): {
|
|
16
|
+
export default function useTabActions(providedRuntimeContext?: StackTabsRuntimeContext): {
|
|
16
17
|
openTab: (tab: ITabData, renew?: boolean) => Promise<string | undefined>;
|
|
17
18
|
closeTab: (id: string) => string;
|
|
18
19
|
closeAllTabs: () => void;
|
|
19
20
|
refreshTab: (id: string) => void;
|
|
20
21
|
refreshAllTabs: () => void;
|
|
21
22
|
activeTab: (id: string, route?: boolean) => Promise<void | import('vue-router').NavigationFailure | undefined>;
|
|
23
|
+
moveTab: (id: string, targetIndex: number) => boolean;
|
|
22
24
|
reset: () => void;
|
|
23
25
|
tabs: import('vue').Ref<import('..').ITabItem[], import('..').ITabItem[]>;
|
|
24
26
|
setIFramePath: (path: string) => void;
|
|
25
|
-
getWrapper: (
|
|
27
|
+
getWrapper: (target?: {
|
|
28
|
+
tabId?: string;
|
|
29
|
+
pageId?: string;
|
|
30
|
+
}) => HTMLElement | undefined;
|
|
26
31
|
openInNewWindow: (id: string) => void;
|
|
27
32
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InjectionKey } from 'vue';
|
|
2
2
|
import { Emitter, EventType } from 'mitt';
|
|
3
|
+
import { StackTabsRuntimeContext } from './stackTabsContext';
|
|
3
4
|
declare module '@vue/runtime-core' {
|
|
4
5
|
interface ComponentCustomProperties {
|
|
5
6
|
$layerEmitter: Emitter<TabEventPayloadMap>;
|
|
@@ -28,4 +29,4 @@ export interface TabEventPayloadMap extends Record<EventType, unknown> {
|
|
|
28
29
|
}
|
|
29
30
|
export declare const tabEmitterKey: InjectionKey<Emitter<TabEventPayloadMap>>;
|
|
30
31
|
/** 获取标签事件总线,用于组件间通信 */
|
|
31
|
-
export declare const useTabEmitter: () => Emitter<TabEventPayloadMap>;
|
|
32
|
+
export declare const useTabEmitter: (runtimeContext?: StackTabsRuntimeContext) => Emitter<TabEventPayloadMap>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ITabData, ITabItem } from '../model/TabModel';
|
|
2
2
|
import { DefineComponent, VNode } from 'vue';
|
|
3
3
|
import { RouteLocationNormalizedLoaded } from 'vue-router';
|
|
4
|
+
import { StackTabsRuntimeContext } from './stackTabsContext';
|
|
4
5
|
/** 规范化路径(nginx 等可能加尾部斜杠) */
|
|
5
6
|
export declare const normalizePathForCache: (route: RouteLocationNormalizedLoaded) => string;
|
|
6
|
-
declare const _default: () => {
|
|
7
|
+
declare const _default: (providedRuntimeContext?: StackTabsRuntimeContext) => {
|
|
7
8
|
tabs: import('vue').Ref<ITabItem[], ITabItem[]>;
|
|
8
9
|
caches: import('vue').ShallowRef<string[]>;
|
|
9
10
|
refreshKey: import('vue').Ref<number, number>;
|
|
@@ -21,6 +22,7 @@ declare const _default: () => {
|
|
|
21
22
|
addCache: (cacheName: string) => void;
|
|
22
23
|
removeCache: (cacheName: string) => void;
|
|
23
24
|
getTab: (id: string) => ITabItem | null;
|
|
25
|
+
moveTab: (id: string, targetIndex: number) => boolean;
|
|
24
26
|
addTab: (tab: ITabItem) => void;
|
|
25
27
|
removeTab: (id: string) => string;
|
|
26
28
|
removeAllTabs: () => void;
|
package/dist/iframe-bridge.mjs
CHANGED
|
@@ -4,7 +4,7 @@ function n() {
|
|
|
4
4
|
return window.location.origin;
|
|
5
5
|
}
|
|
6
6
|
function r(e, r = {}) {
|
|
7
|
-
typeof window
|
|
7
|
+
typeof window < "u" && window.parent && window.parent.postMessage({
|
|
8
8
|
type: t,
|
|
9
9
|
payload: e
|
|
10
10
|
}, r.targetOrigin ?? n());
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { App } from 'vue';
|
|
2
|
+
import { LocaleMessageOption } from './i18n';
|
|
3
|
+
import { default as StackTab } from './StackTabs.vue';
|
|
4
|
+
import { default as IFrame } from './iframe.vue';
|
|
5
|
+
import { default as useTabLoading } from './hooks/useTabLoading';
|
|
6
|
+
import { default as useTabRouter } from './hooks/useTabRouter';
|
|
7
|
+
import { default as useTabActions } from './hooks/useTabActions';
|
|
8
|
+
import { default as TabHeaderButton } from './components/TabHeader/TabHeaderButton.vue';
|
|
9
|
+
export type VueStackTabsPluginOptions = LocaleMessageOption[];
|
|
10
|
+
export * from './model/TabModel';
|
|
11
|
+
declare module '@vue/runtime-core' {
|
|
12
|
+
interface GlobalComponents {
|
|
13
|
+
VueStackTabs: typeof StackTab;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export { IFrame, useTabActions, useTabLoading, useTabRouter, TabHeaderButton, StackTab, StackTab as VueStackTabs };
|
|
17
|
+
export { postOpenTab, onRefreshRequest, MSG_REFRESH, MSG_OPEN_TAB } from './utils/iframeBridge';
|
|
18
|
+
export type { IframeOpenTabPayload } from './utils/iframeBridge';
|
|
19
|
+
declare const _default: {
|
|
20
|
+
install(Vue: App, options?: VueStackTabsPluginOptions): void;
|
|
21
|
+
};
|
|
22
|
+
export default _default;
|