@phoenix-cg/v-tabs 0.1.21 → 0.2.0-beta.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/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ [*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
2
+ charset = utf-8
3
+ indent_size = 2
4
+ indent_style = space
5
+ insert_final_newline = true
6
+ trim_trailing_whitespace = true
7
+
8
+ end_of_line = lf
9
+ max_line_length = 100
package/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,8 @@
1
+ {
2
+ "recommendations": [
3
+ "Vue.volar",
4
+ "vitest.explorer",
5
+ "dbaeumer.vscode-eslint",
6
+ "EditorConfig.EditorConfig"
7
+ ]
8
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "explorer.fileNesting.enabled": true,
3
+ "explorer.fileNesting.patterns": {
4
+ "tsconfig.json": "tsconfig.*.json, env.d.ts",
5
+ "vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
6
+ "package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
7
+ },
8
+ "editor.codeActionsOnSave": {
9
+ "source.fixAll": "explicit"
10
+ }
11
+ }
package/README.md CHANGED
@@ -1,29 +1,41 @@
1
- # v-tabs
1
+ # tabs
2
2
 
3
- ## Project setup
4
- ```
3
+ This template should help get you started developing with Vue 3 in Vite.
4
+
5
+ ## Recommended IDE Setup
6
+
7
+ [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8
+
9
+ ## Customize configuration
10
+
11
+ See [Vite Configuration Reference](https://vite.dev/config/).
12
+
13
+ ## Project Setup
14
+
15
+ ```sh
5
16
  npm install
6
17
  ```
7
18
 
8
- ### Compiles and hot-reloads for development
9
- ```
10
- npm run serve
11
- ```
19
+ ### Compile and Hot-Reload for Development
12
20
 
13
- ### Compiles and minifies for production
21
+ ```sh
22
+ npm run dev
14
23
  ```
24
+
25
+ ### Compile and Minify for Production
26
+
27
+ ```sh
15
28
  npm run build
16
29
  ```
17
30
 
18
- ### Run your unit tests
19
- ```
31
+ ### Run Unit Tests with [Vitest](https://vitest.dev/)
32
+
33
+ ```sh
20
34
  npm run test:unit
21
35
  ```
22
36
 
23
- ### Lints and fixes files
24
- ```
37
+ ### Lint with [ESLint](https://eslint.org/)
38
+
39
+ ```sh
25
40
  npm run lint
26
41
  ```
27
-
28
- ### Customize configuration
29
- See [Configuration Reference](https://cli.vuejs.org/config/).
@@ -0,0 +1,32 @@
1
+ import js from '@eslint/js'
2
+ import pluginVue from 'eslint-plugin-vue'
3
+ import globals from 'globals'
4
+ import pluginVitest from '@vitest/eslint-plugin'
5
+
6
+ export default [
7
+ {
8
+ name: 'app/files-to-lint',
9
+ files: ['**/*.{js,mjs,jsx,vue}'],
10
+ },
11
+
12
+ {
13
+ name: 'app/files-to-ignore',
14
+ ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
15
+ },
16
+
17
+ {
18
+ languageOptions: {
19
+ globals: {
20
+ ...globals.browser,
21
+ },
22
+ },
23
+ },
24
+
25
+ js.configs.recommended,
26
+ ...pluginVue.configs['flat/essential'],
27
+
28
+ {
29
+ ...pluginVitest.configs.recommended,
30
+ files: ['src/**/__tests__/*'],
31
+ },
32
+ ]
package/export.js ADDED
@@ -0,0 +1,3 @@
1
+ import VTabs from './src/components/VTabs.vue'
2
+
3
+ export default VTabs
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link rel="icon" href="/favicon.ico">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Vite App</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.js"></script>
12
+ </body>
13
+ </html>
package/jsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./src/*"]
5
+ }
6
+ },
7
+ "exclude": ["node_modules", "dist"]
8
+ }
package/package.json CHANGED
@@ -1,39 +1,30 @@
1
1
  {
2
2
  "name": "@phoenix-cg/v-tabs",
3
- "version": "0.1.21",
3
+ "version": "0.2.0-beta.0",
4
4
  "private": false,
5
+ "type": "module",
6
+ "main": "./export.js",
5
7
  "scripts": {
6
- "serve": "vue-cli-service serve",
7
- "build": "vue-cli-service build --target lib --name v-tabs src/export.js",
8
- "test:unit": "vue-cli-service test:unit",
9
- "lint": "vue-cli-service lint"
8
+ "dev": "vite",
9
+ "build": "vite build",
10
+ "preview": "vite preview",
11
+ "test:unit": "vitest",
12
+ "lint": "eslint . --fix"
10
13
  },
11
14
  "dependencies": {
12
- "core-js": "^3.6.5",
13
- "vue": "^2.6.11"
15
+ "vue": "^3.5.13"
14
16
  },
15
- "main": "./dist/v-tabs.common.js",
16
17
  "devDependencies": {
17
- "@vue/cli-plugin-babel": "~4.5.0",
18
- "@vue/cli-plugin-eslint": "~4.5.0",
19
- "@vue/cli-plugin-unit-jest": "~4.5.0",
20
- "@vue/cli-service": "~4.5.0",
21
- "@vue/test-utils": "^1.0.3",
22
- "babel-eslint": "^10.1.0",
23
- "eslint": "^6.7.2",
24
- "eslint-plugin-prettier": "^3.1.3",
25
- "eslint-plugin-vue": "^6.2.2",
26
- "vue-template-compiler": "^2.6.11"
27
- },
28
- "description": "## Project setup ``` npm install ```",
29
- "directories": {
30
- "test": "tests"
31
- },
32
- "repository": {
33
- "type": "git",
34
- "url": "git+ssh://git@bitbucket.org/cos_is/v-tabs.git"
35
- },
36
- "author": "Cos_is",
37
- "license": "ISC",
38
- "homepage": "https://bitbucket.org/cos_is/v-tabs#readme"
18
+ "@eslint/js": "^9.21.0",
19
+ "@vitejs/plugin-vue": "^5.2.1",
20
+ "@vitest/eslint-plugin": "^1.1.36",
21
+ "@vue/test-utils": "^2.4.6",
22
+ "eslint": "^9.21.0",
23
+ "eslint-plugin-vue": "~10.0.0",
24
+ "globals": "^16.0.0",
25
+ "jsdom": "^26.0.0",
26
+ "vite": "^6.2.1",
27
+ "vite-plugin-vue-devtools": "^7.7.2",
28
+ "vitest": "^3.0.8"
29
+ }
39
30
  }
package/src/App.vue CHANGED
@@ -1,46 +1,29 @@
1
1
  <template>
2
- <div id="app">
3
- <v-tabs :list="list" v-model="currentTab" with-slider></v-tabs>
2
+ <div>
3
+ <v-tabs with-slider :list="list" v-model="currentTab" />
4
4
  </div>
5
5
  </template>
6
6
 
7
- <script>
8
- import VTabs from './components/VTabs'
7
+ <script setup>
8
+ import { ref } from 'vue'
9
+ import VTabs from './components/VTabs.vue'
9
10
 
10
- export default {
11
- name: 'App',
12
- components: {
13
- VTabs
11
+ const list = [
12
+ {
13
+ _id: '0',
14
+ title: 'Tab one',
14
15
  },
15
- data () {
16
- return {
17
- list: [
18
- {
19
- id: '0',
20
- title: 'Tab one',
21
- },
22
- {
23
- id: '1',
24
- title: 'Tab two',
25
- },
26
- {
27
- id: '2',
28
- title: 'Tab three',
29
- }
30
- ],
31
- currentTab: null
32
- }
16
+ {
17
+ _id: '1',
18
+ title: 'Tab two',
19
+ },
20
+ {
21
+ _id: '2',
22
+ title: 'Tab three',
33
23
  }
34
- };
24
+ ]
25
+ const currentTab = ref(null)
35
26
  </script>
36
27
 
37
28
  <style>
38
- #app {
39
- font-family: Avenir, Helvetica, Arial, sans-serif;
40
- -webkit-font-smoothing: antialiased;
41
- -moz-osx-font-smoothing: grayscale;
42
- text-align: center;
43
- color: #2c3e50;
44
- margin-top: 60px;
45
- }
46
29
  </style>
@@ -0,0 +1,86 @@
1
+ /* color palette from <https://github.com/vuejs/theme> */
2
+ :root {
3
+ --vt-c-white: #ffffff;
4
+ --vt-c-white-soft: #f8f8f8;
5
+ --vt-c-white-mute: #f2f2f2;
6
+
7
+ --vt-c-black: #181818;
8
+ --vt-c-black-soft: #222222;
9
+ --vt-c-black-mute: #282828;
10
+
11
+ --vt-c-indigo: #2c3e50;
12
+
13
+ --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14
+ --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15
+ --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16
+ --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17
+
18
+ --vt-c-text-light-1: var(--vt-c-indigo);
19
+ --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20
+ --vt-c-text-dark-1: var(--vt-c-white);
21
+ --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22
+ }
23
+
24
+ /* semantic color variables for this project */
25
+ :root {
26
+ --color-background: var(--vt-c-white);
27
+ --color-background-soft: var(--vt-c-white-soft);
28
+ --color-background-mute: var(--vt-c-white-mute);
29
+
30
+ --color-border: var(--vt-c-divider-light-2);
31
+ --color-border-hover: var(--vt-c-divider-light-1);
32
+
33
+ --color-heading: var(--vt-c-text-light-1);
34
+ --color-text: #000;
35
+
36
+ --section-gap: 160px;
37
+ }
38
+
39
+ @media (prefers-color-scheme: dark) {
40
+ :root {
41
+ --color-background: var(--vt-c-black);
42
+ --color-background-soft: var(--vt-c-black-soft);
43
+ --color-background-mute: var(--vt-c-black-mute);
44
+
45
+ --color-border: var(--vt-c-divider-dark-2);
46
+ --color-border-hover: var(--vt-c-divider-dark-1);
47
+
48
+ --color-heading: var(--vt-c-text-dark-1);
49
+ --color-text: #000;
50
+ }
51
+ }
52
+
53
+ *,
54
+ *::before,
55
+ *::after {
56
+ box-sizing: border-box;
57
+ margin: 0;
58
+ font-weight: normal;
59
+ }
60
+
61
+ body {
62
+ min-height: 100vh;
63
+ color: var(--color-text);
64
+ background: var(--color-background);
65
+ transition:
66
+ color 0.5s,
67
+ background-color 0.5s;
68
+ line-height: 1.6;
69
+ font-family:
70
+ Inter,
71
+ -apple-system,
72
+ BlinkMacSystemFont,
73
+ 'Segoe UI',
74
+ Roboto,
75
+ Oxygen,
76
+ Ubuntu,
77
+ Cantarell,
78
+ 'Fira Sans',
79
+ 'Droid Sans',
80
+ 'Helvetica Neue',
81
+ sans-serif;
82
+ font-size: 15px;
83
+ text-rendering: optimizeLegibility;
84
+ -webkit-font-smoothing: antialiased;
85
+ -moz-osx-font-smoothing: grayscale;
86
+ }
@@ -0,0 +1,21 @@
1
+ @import './base.css';
2
+
3
+ #app {
4
+ max-width: 1280px;
5
+ margin: 0 auto;
6
+ padding: 2rem;
7
+ font-weight: normal;
8
+ }
9
+
10
+ @media (min-width: 1024px) {
11
+ body {
12
+ display: flex;
13
+ place-items: center;
14
+ }
15
+
16
+ #app {
17
+ display: grid;
18
+ grid-template-columns: 1fr 1fr;
19
+ padding: 0 2rem;
20
+ }
21
+ }
@@ -1,14 +1,14 @@
1
1
  <template>
2
- <div class="v-tabs" :class="[`__${direction}`]">
2
+ <div class="v-tabs" :class="[`__${props.direction}`]" ref="elRef">
3
3
  <div class="v-tabs_inner">
4
4
  <div
5
- v-for="tab in list"
5
+ v-for="tab in props.list"
6
6
  :key="tab[trackBy]"
7
7
  ref="tabs"
8
8
  class="v-tabs_tab"
9
9
  :data-id="tab[trackBy]"
10
10
  :class="{
11
- __active: value && value[trackBy] === tab[trackBy],
11
+ __active: model && model[trackBy] === tab[trackBy],
12
12
  _disabled: tab && tab._disabled
13
13
  }"
14
14
  @click="handleTabSelect(tab, $event)"
@@ -25,98 +25,121 @@
25
25
  </slot>
26
26
  </div>
27
27
  </div>
28
- <div v-if="withSlider" :style="sliderStyle" class="v-tabs_slider" />
28
+ <div v-if="props.withSlider" :style="sliderStyle" class="v-tabs_slider" />
29
29
  </div>
30
30
  </template>
31
31
 
32
- <script>
33
- export default {
34
- props: {
35
- direction: {
36
- type: String,
37
- default: 'horizontal'
38
- },
39
- trackBy: {
40
- type: String,
41
- default: 'id'
42
- },
43
- label: {
44
- type: String,
45
- default: 'title'
46
- },
47
- list: {
48
- type: Array,
49
- required: true
50
- },
51
- value: {
52
- type: Object
53
- },
54
- selectFirst: {
55
- type: Boolean,
56
- default: true
57
- },
58
- withSlider: {
59
- type: Boolean,
60
- default: false
61
- },
62
- initialTab: {
63
- type: [Number, String],
64
- default: null
65
- },
66
- wrapTitle: {
67
- type: Boolean,
68
- default: false
69
- }
32
+ <script setup>
33
+ import {
34
+ nextTick,
35
+ onBeforeUnmount,
36
+ onMounted,
37
+ ref,
38
+ useTemplateRef,
39
+ watch
40
+ } from 'vue'
41
+ const props = defineProps({
42
+ direction: {
43
+ type: String,
44
+ default: 'horizontal'
70
45
  },
71
- data () {
72
- return {
73
- sliderStyle: {}
74
- }
46
+ trackBy: {
47
+ type: String,
48
+ default: '_id'
75
49
  },
76
- watch: {
77
- value () {
78
- this.withSlider && this.calcSlider()
79
- },
80
- uid () {
81
- return this._uid
82
- }
50
+ label: {
51
+ type: String,
52
+ default: 'title'
83
53
  },
84
- mounted () {
85
- if (this.selectFirst && this.list.length && !this.value) {
86
- this.handleTabSelect(this.list[0])
87
- }
88
- if (this.initialTab && this.list.length && !this.value) {
89
- const tab = this.list.find(tab => tab[this.trackBy] === this.initialTab)
90
- tab && this.handleTabSelect(tab)
91
- }
54
+ list: {
55
+ type: Array,
56
+ required: true
57
+ },
58
+ selectFirst: {
59
+ type: Boolean,
60
+ default: true
92
61
  },
93
- methods: {
94
- handleTabSelect (tab) {
95
- this.$emit('input', tab)
96
- this.calcSlider()
97
- },
98
- async calcSlider () {
99
- if (!this.withSlider || !this.$el) {
100
- return
101
- }
102
- await this.$nextTick()
103
- const { left: tabsLeft } = this.$el.getBoundingClientRect()
104
- const [currentTabEl] = this.$el.getElementsByClassName('__active')
105
- if (currentTabEl) {
106
- const {
107
- left: currentTabLeft,
108
- width
109
- } = currentTabEl.getBoundingClientRect()
110
- const tabsLeftScroll = this.$el.scrollLeft
111
- const leftPoisiton = currentTabLeft + tabsLeftScroll - tabsLeft
112
- this.sliderStyle = {
113
- transform: `translateX(${leftPoisiton}px)`,
114
- width: `${width}px`
115
- }
116
- }
62
+ withSlider: {
63
+ type: Boolean,
64
+ default: false
65
+ },
66
+ initialTab: {
67
+ type: [Number, String],
68
+ default: null
69
+ },
70
+ wrapTitle: {
71
+ type: Boolean,
72
+ default: false
73
+ }
74
+ })
75
+ const model = defineModel()
76
+ const emit = defineEmits(['update:modelValue'])
77
+ const sliderStyle = ref({})
78
+ let resizeObserver = null
79
+ const tabsRefs = useTemplateRef('tabs')
80
+ const elRef = useTemplateRef('elRef')
81
+
82
+ function createResizeObserver () {
83
+ if (props.withSlider) {
84
+ resizeObserver = new ResizeObserver(() => {
85
+ calcSlider()
86
+ })
87
+ tabsRefs.value.forEach(tabEl => {
88
+ resizeObserver.observe(tabEl)
89
+ })
90
+ }
91
+ }
92
+
93
+ function destroyResizeObserver () {
94
+ resizeObserver?.disconnect()
95
+ resizeObserver = null
96
+ }
97
+
98
+ function handleTabSelect (tab) {
99
+ emit('update:modelValue', tab)
100
+ calcSlider()
101
+ }
102
+
103
+ async function calcSlider () {
104
+ if (!props.withSlider || !elRef.value) {
105
+ return
106
+ }
107
+ const el = elRef.value
108
+ await nextTick()
109
+ const { left: tabsLeft } = el.getBoundingClientRect()
110
+ const [currentTabEl] = el.getElementsByClassName('__active')
111
+ if (currentTabEl) {
112
+ const {
113
+ left: currentTabLeft,
114
+ width
115
+ } = currentTabEl.getBoundingClientRect()
116
+ const tabsLeftScroll = el.scrollLeft
117
+ const leftPoisiton = currentTabLeft + tabsLeftScroll - tabsLeft
118
+ sliderStyle.value = {
119
+ transform: `translateX(${leftPoisiton}px)`,
120
+ width: `${width}px`
117
121
  }
118
122
  }
119
123
  }
124
+
125
+ onMounted(() => {
126
+ if (props.selectFirst && props.list.length && !model.value) {
127
+ handleTabSelect(props.list[0])
128
+ }
129
+ if (props.initialTab && props.list.length && !model.value) {
130
+ const tab = props.list.find(tab => tab[props.trackBy] === props.initialTab)
131
+ tab && handleTabSelect(tab)
132
+ }
133
+ createResizeObserver()
134
+ })
135
+
136
+ onBeforeUnmount(() => {
137
+ destroyResizeObserver()
138
+ })
139
+
140
+ watch(model, () => {
141
+ props.withSlider && calcSlider()
142
+ })
120
143
  </script>
121
144
 
122
145
  <style>
package/src/main.js CHANGED
@@ -1,8 +1,6 @@
1
- import Vue from 'vue';
2
- import App from './App.vue';
1
+ import './assets/main.css'
3
2
 
4
- Vue.config.productionTip = false;
3
+ import { createApp } from 'vue'
4
+ import App from './App.vue'
5
5
 
6
- new Vue({
7
- render: h => h(App)
8
- }).$mount('#app');
6
+ createApp(App).mount('#app')
package/vite.config.js ADDED
@@ -0,0 +1,25 @@
1
+ import { fileURLToPath, URL } from 'node:url'
2
+ import path from 'path'
3
+ import { defineConfig } from 'vite'
4
+ import vue from '@vitejs/plugin-vue'
5
+ import vueDevTools from 'vite-plugin-vue-devtools'
6
+
7
+ // https://vite.dev/config/
8
+ export default defineConfig({
9
+ build: {
10
+ lib: {
11
+ entry: path.resolve(__dirname, 'src/export.js'),
12
+ name: 'VTabs',
13
+ fileName: (format) => `v-tabs.${format}.js`
14
+ }
15
+ },
16
+ plugins: [
17
+ vue(),
18
+ vueDevTools(),
19
+ ],
20
+ resolve: {
21
+ alias: {
22
+ '@': fileURLToPath(new URL('./src', import.meta.url))
23
+ },
24
+ },
25
+ })
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath } from 'node:url'
2
+ import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
3
+ import viteConfig from './vite.config'
4
+
5
+ export default mergeConfig(
6
+ viteConfig,
7
+ defineConfig({
8
+ test: {
9
+ environment: 'jsdom',
10
+ exclude: [...configDefaults.exclude, 'e2e/**'],
11
+ root: fileURLToPath(new URL('./', import.meta.url)),
12
+ },
13
+ }),
14
+ )
package/.browserslistrc DELETED
@@ -1,3 +0,0 @@
1
- > 1%
2
- last 2 versions
3
- not dead