├── .browserslistrc
├── .gitignore
├── README.md
├── babel.config.js
├── jest.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── DemoWithRouter.ts
├── assets
│ ├── fonts
│ │ └── Inter.ttf
│ └── logo.png
├── components
│ ├── Vue3RouterTree
│ │ ├── CaretRight.vue
│ │ ├── index.tsx
│ │ └── style.scss
│ └── icons
│ │ └── Icon.vue
├── devComponents
│ ├── Prism.ts
│ ├── icons
│ │ ├── Icon.vue
│ │ └── IconLogoGithub.vue
│ └── navigation
│ │ └── TabView.vue
├── layouts
│ └── DefaultLayout
│ │ └── DefaultLayout.vue
├── main.ts
├── router
│ └── index.ts
├── shims-vue.d.ts
├── styles
│ ├── index.scss
│ └── prism-nocture-birds.css
├── utils
│ └── snippets.ts
└── views
│ ├── About.vue
│ ├── Home.vue
│ └── guide
│ ├── BasicExample.vue
│ ├── DefaultOpenExample.vue
│ ├── Index.vue
│ ├── OpenAll.vue
│ ├── WithIcons.vue
│ └── WithMetaInfo.vue
├── tailwind.config.js
├── tests
└── unit
│ └── example.spec.ts
├── tsconfig.json
└── vue.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | build
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 router tree
2 |
3 | This component is based on **Vue.js 3**, it represents your routes or items as a tree view, by default takes it takes the routes configuration as items, but you could provide your custom items that respects the following format :
4 |
5 | ```js
6 | [
7 | {
8 | path:'/somePath',//optional
9 | name:'someName',//required
10 | component:SomeComponent //optional but if it's provided the tree node will be a link that redirects to this component
11 | children:[
12 |
13 |
14 | ]
15 | }
16 |
17 | ]
18 |
19 | ```
20 |
21 | You could also add any other field that you need it when you want to customize the items rendering
22 |
23 | ## Demo
24 |
25 | [LIVE DEMO](https://boussadjra.github.io/vue3-router-tree/) or you could check this [boilerplate](https://codesandbox.io/s/vue-3-router-tree-demo-wzxr1?file=/src/App.vue) on codesandbox
26 |
27 | ## Installation
28 |
29 | npm install vue3-router-tree --save
30 |
31 | ## Usage
32 |
33 | #### With router :
34 |
35 | ```html
36 |
37 | ```
38 |
39 | #### with custom items :
40 |
41 | ```html
42 |
43 |
44 |
45 |
46 |
47 |
48 |
104 | ```
105 |
106 | ### props :
107 |
108 | | Name | default | description |
109 | | ------------- | --------- | ----------------------------------------------------------------------------------------------- |
110 | | items | [] | the tree items or if not provided the component renders the current available routes |
111 | | active-color | "#5d1df1" | the color of the active sub node |
112 | | default-open | '' | specify the default opened path |
113 | | exclude-field | '' | In your route config you could specify a field inside meta option which will be used to exclude |
114 | | open-all | '' | Expand all items that have children |
115 |
116 | ## slots :
117 |
118 | | Name | description |
119 | | ---- | ----------------------------------- |
120 | | item | override the default item rendering |
121 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
3 | transform: {
4 | '^.+\\.vue$': 'vue-jest'
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-router-tree",
3 | "version": "0.1.6",
4 | "author": "BOUSSADJRA BRAHIM",
5 | "private": false,
6 | "main": "./build/vue3-router-tree.common.js",
7 | "scripts": {
8 | "serve": "vue-cli-service serve",
9 | "build": "vue-cli-service build",
10 | "bundle": "vue-cli-service build --dest build --target lib --name vue3-router-tree ./src/components/Vue3RouterTree/index.tsx",
11 | "test:unit": "vue-cli-service test:unit"
12 | },
13 | "dependencies": {
14 | "core-js": "^3.6.5",
15 | "prismjs": "^1.23.0",
16 | "vue": "^3.0.0-0",
17 | "vue-router": "^4.0.2"
18 | },
19 | "devDependencies": {
20 | "@types/jest": "^24.0.19",
21 | "@types/prismjs": "^1.16.2",
22 | "@vue/cli-plugin-babel": "~4.5.0",
23 | "@vue/cli-plugin-router": "~4.5.0",
24 | "@vue/cli-plugin-typescript": "~4.5.0",
25 | "@vue/cli-plugin-unit-jest": "~4.5.0",
26 | "@vue/cli-plugin-vuex": "~4.5.0",
27 | "@vue/cli-service": "~4.5.0",
28 | "@vue/compiler-sfc": "^3.0.0-0",
29 | "@vue/test-utils": "^2.0.0-0",
30 | "autoprefixer": "^9.8.6",
31 | "node-sass": "^4.12.0",
32 | "postcss": "^7.0.35",
33 | "sass-loader": "^8.0.2",
34 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2",
35 | "typescript": "~3.9.3",
36 | "vue-jest": "^5.0.0-0"
37 | },
38 | "repository": {
39 | "type": "git",
40 | "url": "git+https://github.com/boussadjra/vue3-router-tree.git"
41 | },
42 | "license": "MIT",
43 | "homepage": "https://boussadjra.github.io/vue3-router-tree/",
44 | "files": [
45 | "build/*",
46 | "src/*",
47 | "*.json"
48 | ],
49 | "keywords": [
50 | "vue.js 3",
51 | "vuejs 3",
52 | "tree view",
53 | "vue tree view",
54 | "vue component",
55 | "web component"
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | }
6 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boussadjra/vue3-router-tree/53c4b24587b7e8e6e75ce7d2ff65f4f93de28d3b/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Vue 3 router tree
11 |
12 |
13 |
14 |
15 |
16 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
18 |
19 |
21 |
--------------------------------------------------------------------------------
/src/DemoWithRouter.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent, h } from 'vue';
2 | import Vue3RouterTree from './components/Vue3RouterTree';
3 |
4 | import {RouterView} from 'vue-router'
5 | export default defineComponent({
6 |
7 | name:"DemoWithRouter",
8 |
9 | render(){
10 | return h("div",{}, [h(Vue3RouterTree),h(RouterView)])
11 | }
12 | })
--------------------------------------------------------------------------------
/src/assets/fonts/Inter.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boussadjra/vue3-router-tree/53c4b24587b7e8e6e75ce7d2ff65f4f93de28d3b/src/assets/fonts/Inter.ttf
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boussadjra/vue3-router-tree/53c4b24587b7e8e6e75ce7d2ff65f4f93de28d3b/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/Vue3RouterTree/CaretRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
--------------------------------------------------------------------------------
/src/components/Vue3RouterTree/index.tsx:
--------------------------------------------------------------------------------
1 | import { computed, defineComponent, h, onMounted, ref, Transition, VNode, watch, watchEffect } from 'vue';
2 | import './style.scss'
3 |
4 | import CaretRight from './CaretRight.vue'
5 | import { RouteRecordRaw, RouterLink, useRouter } from 'vue-router';
6 |
7 | interface TreeNode {
8 | name: string;
9 | path: string;
10 | component: VNode;
11 | children: Array;
12 | id: number;
13 | info?: string | number | boolean
14 | }
15 |
16 |
17 | export default defineComponent({
18 | props: {
19 |
20 | items: {
21 | type: Array as () => Array
22 | },
23 | activeColor: {
24 | type: String,
25 | default: "#5d1df1"
26 | },
27 | defaultOpen: {
28 | type: String,
29 | default: ''
30 | },
31 | excludeField: {
32 | type: String,
33 | default: ''
34 | },
35 | openAll: {
36 | type: Boolean,
37 | default: false
38 | },
39 | noMaxWidth: {
40 | type: Boolean,
41 | default: false
42 | }
43 | },
44 | components: {
45 | CaretRight
46 | },
47 | setup(props, { slots }) {
48 |
49 | const expandedItem = ref(null);
50 | const selectedItem = ref(null);
51 | const id = ref(0);
52 | const navigationPath = ref>([])
53 |
54 | const router = useRouter()
55 |
56 |
57 | const items = computed(() => {
58 |
59 | let routes = router.options?.routes || router.getRoutes();
60 |
61 | let _items = props.items ? props.items : routes.filter(route => !route.meta || route.meta && !route.meta[props.excludeField]);
62 |
63 | return addId(_items)
64 | })
65 |
66 | watchEffect(() => {
67 | findOpenItems(items.value)
68 |
69 |
70 | })
71 |
72 |
73 | /***
74 | * functions
75 | */
76 | function findOpenItems(_items: TreeNode[]) {
77 | _items.forEach((_item: TreeNode) => {
78 | if (_item.children) {
79 | findOpenItems(_item.children);
80 | }
81 |
82 | if (props.defaultOpen.includes(_item.name)) {
83 |
84 | navigationPath.value.push(_item.id)
85 | }
86 | })
87 |
88 |
89 | }
90 |
91 | function renderTree(_items: Array) {
92 | return (_items as any[]).map((item: TreeNode, index) => {
93 | return
94 |
95 |
{
96 | event.stopPropagation();
97 | if (item.children) {
98 | expandItem(item)
99 | } else {
100 | if (expandedItem.value?.children.find((_item) => _item.id === item.id)) {
101 |
102 | } else {
103 | navigationPath.value = []
104 | }
105 |
106 | selectedItem.value = item;
107 | }
108 |
109 | }}
110 | style={{ color: selectedItem.value?.id === item.id ? props.activeColor : '' }}
111 | class={`flex items-center justify-between p-2 hover:text-xl `}
112 |
113 | >
114 | {renderSlot(item)}
115 | {item.children && }
116 |
117 |
118 |
119 |
120 | {((item.children) && (navigationPath.value.includes(item.id) || props.openAll)) && renderTree(item.children)}
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | })
129 | }
130 |
131 | function renderSlot(item: TreeNode) {
132 | return slots.item ? slots.item({
133 | item: item
134 | }) : item.name
135 | }
136 |
137 | function addId(items: Array): any {
138 | return items.map(item => {
139 | return item.children ? { ...item, id: id.value++ ,children: addId(item.children) } : { ...item, id: id.value++, };
140 | })
141 | }
142 |
143 | function expandItem(item: TreeNode) {
144 |
145 | if (navigationPath.value.includes(item.id)) {
146 | let index = navigationPath.value.findIndex(p => p === item.id)
147 | navigationPath.value.splice(index);
148 |
149 | navigationPath.value = navigationPath.value.filter(el => el !== item.id)
150 | } else {
151 | let found =items.value.find((_item: TreeNode) => _item.id === item.id)
152 |
153 |
154 | if (found && navigationPath.value.length > 0) {
155 | navigationPath.value = [];
156 | navigationPath.value.push(item.id)
157 | } else {
158 | navigationPath.value.push(item.id)
159 | }
160 |
161 | expandedItem.value = item;
162 | }
163 |
164 |
165 | }
166 | /**
167 | *
168 | * jsx template
169 | */
170 | return () => (
171 |
172 | {
173 | renderTree(items.value)
174 | }
175 |
176 |
177 | )
178 | }
179 |
180 | });
181 |
--------------------------------------------------------------------------------
/src/components/Vue3RouterTree/style.scss:
--------------------------------------------------------------------------------
1 |
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 |
6 |
7 |
8 | /*.vrt-tree {
9 | // color: #444645;
10 | list-style: none;
11 | padding: 12px;
12 | max-width: 280px;
13 | // width: 20px;
14 | a{
15 | text-decoration: none;
16 | }
17 | &__item {
18 | padding: 10px;
19 | position: relative;
20 | cursor: pointer;
21 | display: grid;
22 | grid-template-columns: 32px auto minmax(32px, 64px);
23 | ;
24 | white-space: nowrap;
25 | &:hover {
26 | background-color: #e1e1e1;
27 | border-radius: 4px;
28 | }
29 | &_caret {
30 | position: absolute;
31 | right: 0px;
32 | padding: 4px 0;
33 | opacity: .6;
34 | cursor: pointer;
35 | transform: rotate(90deg);
36 | transition: transform .3s ease-in;
37 | &--open {
38 | transform: rotate(-90deg);
39 | transition: transform .3s ease-in;
40 | }
41 | }
42 | &--has-more {
43 | padding: 4px 0;
44 | height: 100%;
45 | &:hover {
46 | background-color: unset;
47 | // border-radius: 4px;
48 | }
49 | }
50 | }
51 | &--has-children {
52 | padding: 8px 8px;
53 | position: relative;
54 | }
55 | &__wrapper {
56 | box-shadow: 0 0 10px #ddd;
57 | }
58 | }
59 | */
60 | .slide-enter-active,
61 | .slide-leave-active {
62 | -moz-transition-duration: 0.3s;
63 | -webkit-transition-duration: 0.3s;
64 | -o-transition-duration: 0.3s;
65 | transition-duration: 0.3s;
66 | -moz-transition-timing-function: ease-in;
67 | -webkit-transition-timing-function: ease-in;
68 | -o-transition-timing-function: ease-in;
69 | transition-timing-function: ease-in;
70 | }
71 |
72 | .slide-enter-to,
73 | .slide-leave-from {
74 | max-height: 100px;
75 | overflow: hidden;
76 | }
77 |
78 | .slide-enter-from,
79 | .slide-leave-to {
80 | overflow: hidden;
81 | max-height: 0;
82 | }
--------------------------------------------------------------------------------
/src/components/icons/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
--------------------------------------------------------------------------------
/src/devComponents/Prism.ts:
--------------------------------------------------------------------------------
1 | import * as Vue from 'vue';
2 | import Prism from 'prismjs';
3 | import { Slots, VNode } from 'vue';
4 |
5 | declare type Data = Record;
6 |
7 | export default Vue.defineComponent({
8 | props: {
9 | code: {
10 | type: String,
11 | default:''
12 | },
13 | inline: {
14 | type: Boolean,
15 | default: false,
16 | },
17 | language: {
18 | type: String,
19 | default: 'markup',
20 | },
21 | },
22 | setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) {
23 | const { h } = Vue;
24 | // const slotsData = (slots && slots.default && slots.default()) || [];
25 | const code = props.code ;
26 | const { inline, language } = props;
27 | const prismLanguage = Prism.languages[language];
28 | const className = `language-${language}`;
29 |
30 | if (inline) {
31 | return (): VNode =>
32 | h('code', { ...attrs, class: [attrs.class, className], innerHTML: Prism.highlight(code, prismLanguage,props.language) });
33 | }
34 |
35 | const d = Prism.highlight(code, prismLanguage,props.language);
36 | return (): VNode =>
37 | h('pre', { ...attrs, class: [attrs.class, className] }, [
38 | h('code', {
39 | class: className,
40 | innerHTML: d,
41 | }),
42 | ]);
43 | },
44 | });
--------------------------------------------------------------------------------
/src/devComponents/icons/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
--------------------------------------------------------------------------------
/src/devComponents/icons/IconLogoGithub.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/devComponents/navigation/TabView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
22 | {{ items[currentTab - 1].body }}
23 |
24 |
25 |
26 |
27 |
28 |
60 |
61 |
98 |
--------------------------------------------------------------------------------
/src/layouts/DefaultLayout/DefaultLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Vue 3 Router Tree
5 |
6 |
7 |
8 | Home
9 |
10 |
11 | Guide
12 |
13 |
14 |
15 | About me
16 |
17 |
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
48 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import router from "./router/index";
3 | import App from "./App.vue";
4 | import "./styles/index.scss"
5 | import 'prismjs'
6 | import './styles/prism-nocture-birds.css'
7 | import Prism from '@/devComponents/Prism'
8 | import TabView from "@/devComponents/navigation/TabView.vue";
9 | let app = createApp(App);
10 |
11 | app.component('Prism',Prism)
12 | app.component('TabView',TabView)
13 | app.use(router).mount("#app");
14 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import About from "@/views/About.vue";
2 | import Home from "@/views/Home.vue";
3 | import Guide from "@/views/guide/Index.vue";
4 | import BasicExample from "@/views/guide/BasicExample.vue";
5 | import WithIcons from "@/views/guide/WithIcons.vue";
6 | import WithMetaInfo from "@/views/guide/WithMetaInfo.vue";
7 | import DefaultOpenExample from "@/views/guide/DefaultOpenExample.vue";
8 | import OpenAll from "@/views/guide/OpenAll.vue";
9 |
10 | import { createWebHistory, createRouter } from "vue-router";
11 |
12 |
13 | export const routes = [
14 | {
15 | path: "/", name: "Home", component: Home,
16 | meta: {
17 | exludedOnSidebar: true
18 | }
19 | },
20 | {
21 | path: "/about", name: "About", component: About, meta: {
22 | exludedOnSidebar: true
23 | }
24 | },
25 | {
26 | path: "/guide", name: "Guide", component: Guide, children: [
27 |
28 | {
29 | path: 'basic-example',
30 | name: 'Basic Example',
31 | component: BasicExample
32 | },
33 | {
34 | path: 'with-icons',
35 | name: 'Render items with icons',
36 | component: WithIcons
37 | },
38 | {
39 | path: 'with-meta-info',
40 | name: 'Render items with meta info',
41 | component: WithMetaInfo
42 | },
43 |
44 | {
45 | path: 'default-open-item',
46 | name: 'Default Open one item',
47 | component: DefaultOpenExample
48 | },
49 | {
50 | path: 'expand-all',
51 | name: 'Expand all items',
52 | component: OpenAll
53 | }
54 | ]
55 | },
56 |
57 |
58 | ];
59 | const router = createRouter({
60 | history: createWebHistory(),
61 | routes
62 | });
63 |
64 | export default router;
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { defineComponent } from 'vue'
3 | const component: ReturnType
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | @font-face {
5 | font-family: 'Inter';
6 | src: url('../assets//fonts/Inter.ttf') format("truetype-variations");
7 | font-weight: 1 999;
8 | }
9 |
10 | body{
11 | @apply bg-purple-50 text-gray-500;
12 | overflow-y: hidden;
13 | font-family: 'Inter',Arial, Helvetica, sans-serif;
14 | }
15 |
16 | #app{
17 | overflow-x: hidden;
18 | @apply text-gray-500;
19 | }
20 | ::-webkit-scrollbar {
21 | width: 2px;
22 | border-radius: 5px
23 | }
24 |
25 | ::-webkit-scrollbar-track {
26 | background: inherit;
27 | border-radius: 5px;
28 | }
29 |
30 | ::-webkit-scrollbar-thumb {
31 | @apply bg-p-indigo-500;
32 | border-radius: 5px;
33 | }
34 | .section__title{
35 | @apply text-xl ml-2 pt-16 text-gray-700
36 | }
37 |
38 | .grid--auto-cols{
39 | display: grid;
40 | grid-template-columns: repeat(auto-fill,minmax(320px,1fr));
41 | place-items: center;
42 | &-xs{
43 | grid-template-columns: repeat(auto-fill,minmax(240px,1fr));
44 | }
45 |
46 | }
47 |
48 | .home{
49 | overflow-y: auto;
50 | max-height: calc(100vh - 64px);
51 |
52 | }
53 |
54 | .drop-enter-active,
55 | .drop-leave-active {
56 | transition: all 0.5s ease-out;
57 | max-height: 400px;
58 | // max-width: 280px;
59 | }
60 |
61 | .drop-enter,
62 | .drop-leave-to
63 | /* .drop-leave-active below version 2.1.8 */
64 |
65 | {
66 | max-height: 0;
67 | opacity: 0;
68 | // max-width: 0;
69 | transform: translateY(-200px);
70 | transform: scaleY(0);
71 | }
72 |
--------------------------------------------------------------------------------
/src/styles/prism-nocture-birds.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js default theme for JavaScript, CSS and HTML
3 | * Based on dabblet (http://dabblet.com)
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*="language-"],
8 | pre[class*="language-"] {
9 | color: #ddeBbB;
10 | background: none;
11 | /* text-shadow: 0 1px white; */
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | font-size:13pt;
14 | text-align: left;
15 | white-space: pre;
16 | word-spacing: normal;
17 | word-break: normal;
18 | word-wrap: normal;
19 | line-height: 1.5;
20 |
21 | -moz-tab-size: 4;
22 | -o-tab-size: 4;
23 | tab-size: 4;
24 |
25 | -webkit-hyphens: none;
26 | -moz-hyphens: none;
27 | -ms-hyphens: none;
28 | hyphens: none;
29 | }
30 |
31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
33 | text-shadow: none;
34 | background: #1c1c31;
35 | }
36 |
37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
38 | code[class*="language-"]::selection, code[class*="language-"] ::selection {
39 | text-shadow: none;
40 | background: #1c1c31;
41 | }
42 |
43 | @media print {
44 | code[class*="language-"],
45 | pre[class*="language-"] {
46 | text-shadow: none;
47 | }
48 | }
49 |
50 | /* Code blocks */
51 | pre[class*="language-"] {
52 | padding: 1em;
53 | margin: .5em 0;
54 | overflow: auto;
55 | }
56 |
57 | :not(pre) > code[class*="language-"],
58 | pre[class*="language-"] {
59 | background: #1c1c35;
60 | border-radius:4px;
61 | padding:16px;
62 | }
63 |
64 | /* Inline code */
65 | :not(pre) > code[class*="language-"] {
66 | padding: .1em;
67 | border-radius: .3em;
68 | white-space: normal;
69 | }
70 |
71 | .token.comment,
72 | .token.prolog,
73 | .token.doctype,
74 | .token.cdata {
75 | color: slategray;
76 | }
77 |
78 | .token.punctuation {
79 | color: #65737E;
80 | }
81 |
82 | .token.namespace {
83 | opacity: .7;
84 | }
85 |
86 | .token.property,
87 | .token.tag,
88 | .token.boolean,
89 | .token.number,
90 | .token.constant,
91 | .token.symbol,
92 | .token.deleted {
93 | color: #2eeb9c;
94 | }
95 |
96 | .token.selector,
97 | .token.attr-name,
98 | .token.string,
99 | .token.char,
100 | .token.builtin,
101 | .token.inserted {
102 | color:#f0a7f0;
103 | }
104 |
105 | .token.operator,
106 | .token.entity,
107 | .token.url,
108 | .language-css .token.string,
109 | .style .token.string {
110 | color: #ff0;
111 | /* This background color was intended by the author of this theme. */
112 | /* background: #4545ff; */
113 | }
114 |
115 | .token.atrule,
116 | .token.attr-value,
117 | .token.keyword {
118 | color: #ebd82e;
119 | }
120 |
121 | .token.function,
122 | .token.class-name {
123 | color: #ff3076;
124 | }
125 |
126 | .token.regex,
127 | .token.important,
128 | .token.variable {
129 | color: #e90;
130 | }
131 |
132 | .token.important,
133 | .token.bold {
134 | font-weight: bold;
135 | }
136 | .token.italic {
137 | font-style: italic;
138 | }
139 |
140 | .token.entity {
141 | cursor: help;
142 | }
143 |
--------------------------------------------------------------------------------
/src/utils/snippets.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | BasicExample: `
3 |
4 |
5 |
6 |
7 |
8 |
122 |
123 | `,
124 | WithIcons: `
125 |
126 |
127 |
128 |
129 |
130 | {{ item.name }}
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
252 |
253 | `,
254 | DefaultOpenExample: `
255 |
256 |
257 |
258 |
259 |
260 | {{ item.name }}
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
382 |
383 | `,
384 | OpenAll: `
385 |
386 |
387 |
388 |
389 |
390 | {{ item.name }}
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
512 |
513 | `
514 | };
515 |
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | I'm a web developer who loves code and UI design, I like to build something that works and looks fine. When i have some problem I try to follow the best practices and the modern tools to solve it. I consecrate my free time to help people especially on Stack Overflow, maintain some open source components on Github or to learn new skills
13 |
14 |
15 |
16 | Github
17 |
18 |
19 | StackOverflow
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Vue 3 Router Tree
5 |
6 | This component is based on Vue.js 3 , it
7 | represents your routes or items as a tree view, by default takes it
8 | takes the routes configuration as items, but you could provide your
9 | custom items that respects the following format :
10 |
11 |
14 | Props
15 |
16 |
43 | Slots
44 |
45 |
62 | Demo
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {{ item.name }}
71 |
72 |
{{
73 | item.info
74 | }}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
206 |
--------------------------------------------------------------------------------
/src/views/guide/BasicExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Basic example
4 |
5 | The following example shows a basic usage of the tree menu, for example
6 | for rendering a table of content
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
141 |
--------------------------------------------------------------------------------
/src/views/guide/DefaultOpenExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Render items with meta info
4 |
5 | You could render your tree by setting a default open path
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 | {{ item.name }}
18 | {{ item.info }}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
152 |
--------------------------------------------------------------------------------
/src/views/guide/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 | {{
20 | item.name
21 | }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
61 |
62 |
64 |
--------------------------------------------------------------------------------
/src/views/guide/OpenAll.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Expand all items
4 |
5 | This is useful when working with table of content
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 | {{ item.name }}
17 | {{ item.info }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
170 |
--------------------------------------------------------------------------------
/src/views/guide/WithIcons.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Render items with Icons
4 |
5 | We could use `item` slot to change the default display by adding icons to our items
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 | {{ item.name }}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
150 |
--------------------------------------------------------------------------------
/src/views/guide/WithMetaInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Open a default path
4 |
5 | We could use `item` slot to change the default display by adding icons and meta info to our items
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 | {{ item.name }}
17 | {{ item.info }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
151 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ['./src/**/*.html', './src/**/*.vue', './src/**/*.ts','./src/**/*.tsx','./src/**/*.js'],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {
6 | colors: {
7 | 'p-indigo': {
8 | 100: '#d6cfe2',
9 | 200: '#ad9ec5',
10 | 300: '#846ea7',
11 | 400: '#5b3d8a',
12 | 500: '#320d6d',
13 | 600: '#280a57',
14 | 700: '#1e0841',
15 | 800: '#14052c',
16 | 900: '#0a0316',
17 | },
18 | melon: {
19 | 100: '#fff2f1',
20 | 200: '#ffe5e2',
21 | 300: '#ffd9d4',
22 | 400: '#ffccc5',
23 | 500: '#ffbfb7',
24 | 600: '#cc9992',
25 | 700: '#99736e',
26 | 800: '#664c49',
27 | 900: '#332625',
28 | },
29 | },
30 | transitionProperty: {
31 | height: 'height',
32 | },
33 | },
34 | },
35 | variants: {},
36 | plugins: [],
37 | };
38 |
--------------------------------------------------------------------------------
/tests/unit/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import HelloWorld from '@/components/HelloWorld.vue'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(HelloWorld, {
8 | props: { msg }
9 | })
10 | expect(wrapper.text()).toMatch(msg)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "sourceMap": true,
13 | "baseUrl": ".",
14 | "types": [
15 | "webpack-env",
16 | "jest"
17 | ],
18 | "paths": {
19 | "@/*": [
20 | "src/*"
21 | ]
22 | },
23 | "lib": [
24 | "esnext",
25 | "dom",
26 | "dom.iterable",
27 | "scripthost"
28 | ]
29 | },
30 | "include": [
31 | "src/**/*.ts",
32 | "src/**/*.tsx",
33 | "src/**/*.vue",
34 | "tests/**/*.ts",
35 | "tests/**/*.tsx"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | publicPath: process.env.NODE_ENV === 'production' ? '/vue3-router-tree/' : '/',
3 | configureWebpack: {
4 | output: {
5 | libraryExport: 'default',
6 | },
7 | },
8 | css: {
9 | extract: false,
10 | },
11 | };
12 |
--------------------------------------------------------------------------------