├── .browserslistrc
├── .gitignore
├── .prettierrc.js
├── .storybook
├── main.js
└── webpack.config.js
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── babel.config.js
├── example
├── App.vue
├── main.js
└── public
│ └── index.html
├── jest.config.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── src
├── components
│ ├── connected-tree-node.vue
│ ├── object-label.vue
│ ├── object-preview.vue
│ ├── object-root-label.vue
│ ├── object-value.vue
│ ├── tree-node.vue
│ └── tree-view.vue
├── index.js
├── libs
│ ├── data.js
│ ├── pathUtils.js
│ ├── pathUtils.spec.js
│ └── propertyUtils.js
├── object-inspector.vue
└── styles
│ ├── chromedark
│ ├── index.less
│ ├── object.less
│ └── tree.less
│ ├── chromelight
│ ├── index.less
│ ├── object.less
│ └── tree.less
│ └── index.less
├── stories
├── stories-array.js
├── stories-basic-type.js
├── stories-maps-sets-functions.js
├── stories-object.js
└── stories-themes.js
└── vue.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // default
3 | printWidth: 80,
4 | useTabs: false,
5 | tabWidth: 2,
6 | bracketSpacing: true,
7 |
8 | // modify
9 | semi: false,
10 | singleQuote: true,
11 | trailingComma: 'es5',
12 | endOfLint: 'lf',
13 | }
14 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: [
3 | '../stories/stories-object.js',
4 | '../stories/stories-array.js',
5 | '../stories/stories-maps-sets-functions.js',
6 | '../stories/stories-basic-type.js',
7 | '../stories/stories-themes.js',
8 | ],
9 | addons: [
10 | // "@storybook/addon-links",
11 | // "@storybook/addon-essentials"
12 | ],
13 | }
14 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | // Export a function. Accept the base config as the only param.
4 | module.exports = async ({ config, mode }) => {
5 | // `mode` has a value of 'DEVELOPMENT' or 'PRODUCTION'
6 | // You can change the configuration based on that.
7 | // 'PRODUCTION' is used when building the static version of storybook.
8 |
9 | // Make whatever fine-grained changes you need
10 | config.module.rules.push({
11 | test: /\.less/,
12 | use: ['style-loader', 'css-loader', 'less-loader'],
13 | include: path.resolve(__dirname, '../'),
14 | })
15 | Object.assign(config.resolve.alias, {
16 | '@': path.resolve(__dirname, '../src'),
17 | '@assets': path.resolve(__dirname, '../src/assets/'),
18 | })
19 | // Return the altered config
20 | return config
21 | }
22 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "[javascript]": {
4 | "editor.defaultFormatter": "esbenp.prettier-vscode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 VikydZhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-object-inspector
2 |
3 | Vue component used as an js/json object inspector/viewer, inspired by [react-inspector](https://github.com/storybookjs/react-inspector) .
4 |
5 | > Nodes will not be rendered if parent is collapsed.
6 |
7 | ## Online Playground
8 |
9 | Online Playground:
10 |
11 | https://codesandbox.io/s/vue-object-inspector-demo-gjs9h?file=/src/components/InspectorDemo.vue
12 |
13 | https://codesandbox.io/s/vue-object-inspector-demo-dark-bouxd?file=/src/components/InspectorDemo.vue
14 |
15 | ## Examples
16 |
17 | 
18 |
19 | 
20 |
21 | 
22 |
23 | 
24 |
25 | 
26 |
27 | ## Usage
28 |
29 | ```sh
30 | npm install vue-object-inspector
31 | ```
32 |
33 | In Vue component:
34 |
35 | ```vue
36 |
37 |
38 |
39 |
40 |
41 |
42 |
62 | ```
63 |
64 | ## Vue `props`
65 |
66 | This component supports some Vue props, similar to [react-inspector](https://github.com/storybookjs/react-inspector/blob/v5.1.0/README.md#api) , just a bit different.
67 |
68 | ### `data`
69 |
70 | - what: JavaScript Object or any var you would like to inspect
71 | - type: any
72 | - mandatory: true
73 |
74 | ### `name`
75 |
76 | - what: root node prefix name
77 | - type: String
78 | - mandatory: false
79 |
80 | ### `expandLevel`
81 |
82 | - what: an integer specifying to which level the tree should be initially expanded
83 | - type: Integer
84 | - mandatory: false
85 | - default: `0`
86 |
87 | ### `expandPaths`
88 |
89 | - what: an array containing all the paths that should be expanded when the component is initialized, or a string of just one path
90 | - type: Array of String
91 | - mandatory: false
92 | - details: syntax like [JSONPath](https://goessner.net/articles/JsonPath/)
93 | - default: `[]`
94 | - examples:
95 | - `['$']`: expand root node, `$` always refers to the root node
96 | - `['$.myKey']`: expand to `myKey` node (will also expand all parent nodes)
97 | - this is different from [react-inspector](https://github.com/storybookjs/react-inspector)
98 | - `['$.myKey.myArr']`: expand to `myArr` node (will also expand all parent nodes)
99 | - `['$.a', '$.b']`: expand first level nodes `a` and `b`
100 | - `['$.1']`: expand by array index
101 | - `['$.*']`: wildcard, expand all level 2 nodes, equivalent to `:expandLevel="2"`
102 | - `['$.*.*']`: wildcard, expand all level 3 nodes, equivalent to `:expandLevel="3"`
103 |
104 | ### `expandPaths` vs `expandPaths`
105 |
106 | Both are reactive.
107 |
108 | In most case, don't use both at the same time.
109 |
110 | One of them changes, only the changed one will take effect and ignore the other one.
111 |
112 | If want to expand all level, change `expandLevel` to a very big number.
113 |
114 | If want to collapse all level, change `expandLevel` to 0.
115 |
116 | If already change expand by hand, change the `expandLevel` to a nagative number, then change it back in `$nextTick`.
117 |
118 | ### `showNonenumerable`
119 |
120 | - what: show non-enumerable properties, like `__proto__`, `length`, `constructor` ...
121 | - type: Boolean
122 | - mandatory: false
123 | - default: `false`
124 |
125 | ### `sortObjectKeys`
126 |
127 | - what: sort object keys like [Array.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
128 | - type: Boolean or Function
129 | - mandatory: false
130 | - default: no sort
131 | - examples:
132 | - `true`: keys of objects are sorted in alphabetical order except for arrays
133 | - function in Vue component methods:
134 | ```js
135 | reverseSortFunc(a, b) {
136 | return b > a ? 1 : -1
137 | }
138 | ```
139 |
140 | ### `nodeRenderer`
141 |
142 | - what: use a custom `nodeRenderer` to render the object properties
143 | - type: Function, should return [JSX](https://cn.vuejs.org/v2/guide/render-function.html#ad) elements
144 | - function parameters: `depth`, `name`, `data`, `isNonenumerable`
145 | - `isNonenumerable` refers to default hidden properties like `__proto__`, `length` ...
146 | - mandatory: false
147 | - default: a default `nodeRenderer`
148 | - examples:
149 | - function in Vue component methods:
150 | ```js
151 | myNodeRenderer(depth, name, data, isNonenumerable) {
152 | return (
153 |
156 | )
157 | }
158 | ```
159 |
160 | ### `objectMaxProperties`
161 |
162 | - what: max count object properties should be list in preview label
163 | - type: Interger
164 | - mandatory: false
165 | - default: `5`
166 |
167 | ### `arrayMaxProperties`
168 |
169 | - what: max count array properties should be list in preview label
170 | - type: Interger
171 | - mandatory: false
172 | - default: `10`
173 |
174 | ### `theme`
175 |
176 | - what: use light or dark theme
177 | - type: String
178 | - mandatory: false
179 | - default: light theme, keep this prop empty
180 | - dark theme value: `chromeDark`
181 |
182 | ## Development
183 |
184 | Local preview page is [example/App.vue](example/App.vue) .
185 |
186 | ```sh
187 | # Install dependencies
188 | npm install
189 |
190 | # Compiles and hot-reloads for development
191 | npm run serve
192 |
193 | # Compiles and minifies for production
194 | npm run build
195 | ```
196 |
197 | ## Storybook Preview
198 |
199 | After `npm install`, you can also run this command to see lots of live examples.
200 |
201 | ```sh
202 | npm run storybook
203 | ```
204 |
205 | See `stories` folder for source code of examples.
206 |
207 | ## Thanks
208 |
209 | - [react-inspector](https://github.com/storybookjs/react-inspector)
210 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@vue/cli-plugin-babel/preset'],
3 | }
4 |
--------------------------------------------------------------------------------
/example/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Vue Object Inspector - Example Page
4 |
5 |
More examples: run storybook localy, see README.md
6 |
7 |
8 |
9 |
10 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
111 |
112 |
127 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | Vue.config.productionTip = false
5 |
6 | new Vue({
7 | render: (h) => h(App),
8 | }).$mount('#app')
9 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: '@vue/cli-plugin-unit-jest',
3 | }
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["./src/*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-object-inspector",
3 | "version": "1.0.12",
4 | "description": "Vue component used as an js/json object inspector/viewer",
5 | "keywords": [
6 | "vue",
7 | "object",
8 | "chrome",
9 | "devtools",
10 | "component",
11 | "inspector",
12 | "object-inspector",
13 | "object-viewer",
14 | "array",
15 | "array-viewer",
16 | "tree",
17 | "tree-view",
18 | "treeview",
19 | "ui",
20 | "view"
21 | ],
22 | "license": "MIT",
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/vikyd/vue-object-inspector.git"
26 | },
27 | "scripts": {
28 | "serve": "vue-cli-service serve",
29 | "build": "vue-cli-service build --target lib src/index.js",
30 | "test:unit": "vue-cli-service test:unit",
31 | "storybook": "start-storybook -p 6006",
32 | "build-storybook": "build-storybook"
33 | },
34 | "main": "dist/vue-object-inspector.umd.js",
35 | "dependencies": {
36 | "core-js": "^3.6.5",
37 | "vue": "^2.6.11"
38 | },
39 | "devDependencies": {
40 | "@babel/core": "^7.12.10",
41 | "@storybook/addon-actions": "^6.1.11",
42 | "@storybook/addon-essentials": "^6.1.11",
43 | "@storybook/addon-links": "^6.1.11",
44 | "@storybook/vue": "^6.1.11",
45 | "@vue/cli-plugin-babel": "~4.5.0",
46 | "@vue/cli-plugin-unit-jest": "~4.5.0",
47 | "@vue/cli-service": "~4.5.0",
48 | "@vue/test-utils": "^1.0.3",
49 | "babel-loader": "^8.2.2",
50 | "less": "^3.0.4",
51 | "less-loader": "^5.0.0",
52 | "vue-template-compiler": "^2.6.11"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/connected-tree-node.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
26 |
27 | null
28 |
29 |
30 |
31 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/components/object-label.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ name }}:
9 |
10 |
11 |
12 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/components/object-preview.vue:
--------------------------------------------------------------------------------
1 |
2 | {{
6 | object.length === 0 ? '' : `(${object.length})\xa0`
7 | }}[, , …] {{ desc }}{, {{ item.key || `""` }}: …}
28 |
29 |
30 |
31 |
32 |
99 |
--------------------------------------------------------------------------------
/src/components/object-root-label.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ name }}
5 | :
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/object-value.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ calcObject.text
6 | }}ƒ {{ object.name }}()
13 |
14 |
15 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/components/tree-node.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 | ▶
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/components/tree-view.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import ObjectInspector from './object-inspector'
2 |
3 | export default ObjectInspector
4 |
--------------------------------------------------------------------------------
/src/libs/data.js:
--------------------------------------------------------------------------------
1 | import { getPropertyValue } from '@/libs/propertyUtils'
2 |
3 | export const createIterator = (showNonenumerable, sortObjectKeys) => {
4 | const objectIterator = function* (data) {
5 | const shouldIterate =
6 | (typeof data === 'object' && data !== null) || typeof data === 'function'
7 | if (!shouldIterate) return
8 |
9 | const dataIsArray = Array.isArray(data)
10 |
11 | // iterable objects (except arrays)
12 | if (!dataIsArray && data[Symbol.iterator]) {
13 | let i = 0
14 | for (let entry of data) {
15 | if (Array.isArray(entry) && entry.length === 2) {
16 | const [k, v] = entry
17 | yield {
18 | name: k,
19 | data: v,
20 | }
21 | } else {
22 | yield {
23 | name: i.toString(),
24 | data: entry,
25 | }
26 | }
27 | i++
28 | }
29 | } else {
30 | const keys = Object.getOwnPropertyNames(data)
31 | if (sortObjectKeys === true && !dataIsArray) {
32 | // Array keys should not be sorted in alphabetical order
33 | keys.sort()
34 | } else if (typeof sortObjectKeys === 'function') {
35 | keys.sort(sortObjectKeys)
36 | }
37 |
38 | for (const propertyName of keys) {
39 | if (propertyIsEnumerable.call(data, propertyName)) {
40 | const propertyValue = getPropertyValue(data, propertyName)
41 | yield {
42 | name: propertyName || `""`,
43 | data: propertyValue,
44 | }
45 | } else if (showNonenumerable) {
46 | // To work around the error (happens some time when propertyName === 'caller' || propertyName === 'arguments')
47 | // 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
48 | // http://stackoverflow.com/questions/31921189/caller-and-arguments-are-restricted-function-properties-and-cannot-be-access
49 | let propertyValue
50 | try {
51 | propertyValue = getPropertyValue(data, propertyName)
52 | } catch (e) {
53 | // console.warn(e)
54 | }
55 |
56 | if (propertyValue !== undefined) {
57 | yield {
58 | name: propertyName,
59 | data: propertyValue,
60 | isNonenumerable: true,
61 | }
62 | }
63 | }
64 | }
65 |
66 | // [[Prototype]] of the object: `Object.getPrototypeOf(data)`
67 | // the property name is shown as "__proto__"
68 | if (showNonenumerable && data !== Object.prototype /* already added */) {
69 | yield {
70 | name: '__proto__',
71 | data: Object.getPrototypeOf(data),
72 | isNonenumerable: true,
73 | }
74 | }
75 | }
76 | }
77 |
78 | return objectIterator
79 | }
80 |
--------------------------------------------------------------------------------
/src/libs/pathUtils.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT_ROOT_PATH = '$'
2 |
3 | const WILDCARD = '*'
4 |
5 | export function hasChildNodes(data, dataIterator) {
6 | return !dataIterator(data).next().done
7 | }
8 |
9 | export const wildcardPathsFromLevel = (level) => {
10 | // i is depth
11 | return Array.from({ length: level }, (_, i) =>
12 | [DEFAULT_ROOT_PATH].concat(Array.from({ length: i }, () => '*')).join('.')
13 | )
14 | }
15 |
16 | export const getExpandedPaths = (
17 | data,
18 | dataIterator,
19 | expandPaths,
20 | expandLevel,
21 | prevExpandedPaths
22 | ) => {
23 | let wildcardPaths = []
24 | .concat(wildcardPathsFromLevel(expandLevel))
25 | .concat(expandPaths)
26 | .filter((path) => typeof path === 'string') // could be undefined
27 |
28 | const expandedPaths = []
29 | wildcardPaths.forEach((wildcardPath) => {
30 | const keyPaths = wildcardPath.split('.')
31 | const populatePaths = (curData, curPath, depth) => {
32 | if (depth === keyPaths.length) {
33 | expandedPaths.push(curPath)
34 | return
35 | }
36 | const key = keyPaths[depth]
37 | if (depth === 0) {
38 | if (
39 | hasChildNodes(curData, dataIterator) &&
40 | (key === DEFAULT_ROOT_PATH || key === WILDCARD)
41 | ) {
42 | populatePaths(curData, DEFAULT_ROOT_PATH, depth + 1)
43 | }
44 | } else {
45 | if (key === WILDCARD) {
46 | for (let { name, data } of dataIterator(curData)) {
47 | if (hasChildNodes(data, dataIterator)) {
48 | populatePaths(data, `${curPath}.${name}`, depth + 1)
49 | }
50 | }
51 | } else {
52 | const value = curData[key]
53 | if (hasChildNodes(value, dataIterator)) {
54 | populatePaths(value, `${curPath}.${key}`, depth + 1)
55 | }
56 | }
57 | }
58 | }
59 |
60 | populatePaths(data, '', 0)
61 | })
62 |
63 | return expandedPaths.reduce(
64 | (obj, path) => {
65 | obj[path] = true
66 | return obj
67 | },
68 | { ...prevExpandedPaths }
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/src/libs/pathUtils.spec.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 |
3 | import { DEFAULT_ROOT_PATH, wildcardPathsFromLevel } from './pathUtils'
4 |
5 | const root = DEFAULT_ROOT_PATH
6 |
7 | describe('PathUtils', () => {
8 | beforeEach(() => {})
9 |
10 | it('wildcardPathsFromLevel works', () => {
11 | expect(wildcardPathsFromLevel(-1)).toEqual([])
12 |
13 | expect(wildcardPathsFromLevel(0)).toEqual([])
14 |
15 | expect(wildcardPathsFromLevel(1)).toEqual([root])
16 |
17 | expect(wildcardPathsFromLevel(2)).toEqual([root, `${root}.*`])
18 |
19 | expect(wildcardPathsFromLevel(3)).toEqual([
20 | root,
21 | `${root}.*`,
22 | `${root}.*.*`,
23 | ])
24 |
25 | expect(wildcardPathsFromLevel(4)).toEqual([
26 | root,
27 | `${root}.*`,
28 | `${root}.*.*`,
29 | `${root}.*.*.*`,
30 | ])
31 | })
32 |
33 | // it('getExpandedPaths works', () => {
34 | // })
35 | })
36 |
--------------------------------------------------------------------------------
/src/libs/propertyUtils.js:
--------------------------------------------------------------------------------
1 | export function getPropertyValue(object, propertyName) {
2 | const propertyDescriptor = Object.getOwnPropertyDescriptor(
3 | object,
4 | propertyName
5 | )
6 | if (propertyDescriptor.get) {
7 | try {
8 | return propertyDescriptor.get()
9 | } catch {
10 | return propertyDescriptor.get
11 | }
12 | }
13 |
14 | return object[propertyName]
15 | }
16 |
--------------------------------------------------------------------------------
/src/object-inspector.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
16 |
17 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/src/styles/chromedark/index.less:
--------------------------------------------------------------------------------
1 | .vue-object-inspector-chromedark {
2 | @import './tree.less';
3 | @import './object.less';
4 | }
5 |
--------------------------------------------------------------------------------
/src/styles/chromedark/object.less:
--------------------------------------------------------------------------------
1 | .object-name {
2 | color: rgb(227, 110, 236);
3 | }
4 |
5 | .object-name-dimmed {
6 | opacity: 0.6;
7 | }
8 |
9 | .object-preview-desc {
10 | font-style: italic;
11 | }
12 |
13 | .object-preview {
14 | font-style: italic;
15 | }
16 |
17 | .object-value-null {
18 | color: rgb(127, 127, 127);
19 | }
20 | .object-value-undefined {
21 | color: rgb(127, 127, 127);
22 | }
23 | .object-value-regexp {
24 | color: rgb(233, 63, 59);
25 | }
26 | .object-value-string {
27 | color: rgb(233, 63, 59);
28 | }
29 | .object-value-symbol {
30 | color: rgb(233, 63, 59);
31 | }
32 | .object-value-number {
33 | color: hsl(252, 100%, 75%);
34 | }
35 | .object-value-boolean {
36 | color: hsl(252, 100%, 75%);
37 | }
38 | .object-value-function-prefix {
39 | // color: rgb(13, 34, 170);
40 | color: rgb(85, 106, 242);
41 | font-style: italic;
42 | }
43 | .object-value-function-name {
44 | font-style: italic;
45 | }
46 |
--------------------------------------------------------------------------------
/src/styles/chromedark/tree.less:
--------------------------------------------------------------------------------
1 | .tree-view-outline {
2 | margin: 0;
3 | padding: 0;
4 | list-style-type: none;
5 | }
6 |
7 | .tree-node {
8 | color: rgb(213, 213, 213);
9 | background-color: rgb(36, 36, 36);
10 |
11 | line-height: 1.2;
12 | cursor: default;
13 |
14 | box-sizing: border-box;
15 | list-style: none;
16 |
17 | font-family: Menlo, monospace;
18 | font-size: 11px;
19 | }
20 |
21 | // .tree-node-preview-container {
22 | // }
23 |
24 | .tree-node-placeholder {
25 | white-space: pre;
26 |
27 | font-size: 12px;
28 | margin-right: 3px;
29 | }
30 |
31 | .tree-node-arrow {
32 | color: rgb(145, 145, 145);
33 | display: inline-block;
34 | font-size: 12px;
35 | margin-right: 3px;
36 | }
37 | .tree-node-arrow-expanded {
38 | transform: rotate(90deg);
39 | }
40 | .tree-node-arrow-collapsed {
41 | transform: rotate(0deg);
42 | }
43 |
44 | .tree-node-child-nodes-container {
45 | margin: 0;
46 | padding-left: 12px;
47 | }
48 |
--------------------------------------------------------------------------------
/src/styles/chromelight/index.less:
--------------------------------------------------------------------------------
1 | .vue-object-inspector {
2 | @import './tree.less';
3 | @import './object.less';
4 | }
5 |
--------------------------------------------------------------------------------
/src/styles/chromelight/object.less:
--------------------------------------------------------------------------------
1 | .object-name {
2 | color: rgb(136, 19, 145);
3 | }
4 |
5 | .object-name-dimmed {
6 | opacity: 0.6;
7 | }
8 |
9 | .object-preview-desc {
10 | font-style: italic;
11 | }
12 |
13 | .object-preview {
14 | font-style: italic;
15 | }
16 |
17 | .object-value-null {
18 | color: rgb(128, 128, 128);
19 | }
20 | .object-value-undefined {
21 | color: rgb(128, 128, 128);
22 | }
23 | .object-value-regexp {
24 | color: rgb(196, 26, 22);
25 | }
26 | .object-value-string {
27 | color: rgb(196, 26, 22);
28 | }
29 | .object-value-symbol {
30 | color: rgb(196, 26, 22);
31 | }
32 | .object-value-number {
33 | color: rgb(28, 0, 207);
34 | }
35 | .object-value-boolean {
36 | color: rgb(28, 0, 207);
37 | }
38 | .object-value-function-prefix {
39 | // color: rgb(13, 34, 170);
40 | color: rgb(170, 13, 145);
41 | font-style: italic;
42 | }
43 | .object-value-function-name {
44 | font-style: italic;
45 | }
46 |
--------------------------------------------------------------------------------
/src/styles/chromelight/tree.less:
--------------------------------------------------------------------------------
1 | .tree-view-outline {
2 | margin: 0;
3 | padding: 0;
4 | list-style-type: none;
5 | }
6 |
7 | .tree-node {
8 | color: #000;
9 | background-color: #fff;
10 |
11 | line-height: 1.2;
12 | cursor: default;
13 |
14 | box-sizing: border-box;
15 | list-style: none;
16 |
17 | font-family: Menlo, monospace;
18 | font-size: 11px;
19 | }
20 |
21 | // .tree-node-preview-container {
22 | // }
23 |
24 | .tree-node-placeholder {
25 | white-space: pre;
26 |
27 | font-size: 12px;
28 | margin-right: 3px;
29 | }
30 |
31 | .tree-node-arrow {
32 | color: #6e6e6e;
33 | display: inline-block;
34 | font-size: 12px;
35 | margin-right: 3px;
36 | }
37 | .tree-node-arrow-expanded {
38 | transform: rotate(90deg);
39 | }
40 | .tree-node-arrow-collapsed {
41 | transform: rotate(0deg);
42 | }
43 |
44 | .tree-node-child-nodes-container {
45 | margin: 0;
46 | padding-left: 12px;
47 | }
48 |
--------------------------------------------------------------------------------
/src/styles/index.less:
--------------------------------------------------------------------------------
1 | @import './chromelight/index.less';
2 | @import './chromedark/index.less';
3 |
--------------------------------------------------------------------------------
/stories/stories-array.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { storiesOf } from '@storybook/vue'
3 |
4 | import ObjectInspector from '../src/index.js'
5 |
6 | storiesOf('Arrays', module)
7 | .add('Empty Array', () => ({
8 | components: { ObjectInspector },
9 | template: '',
10 | }))
11 | .add('Empty Array (show non-enumerable properties)', () => ({
12 | components: { ObjectInspector },
13 | template:
14 | '',
15 | }))
16 | .add('Basic Array', () => ({
17 | components: { ObjectInspector },
18 | template: '',
19 | }))
20 | .add('Array with different types of elements', () => ({
21 | components: { ObjectInspector },
22 | template: '',
23 | }))
24 | .add('Long Array', () => ({
25 | components: { ObjectInspector },
26 | template: '',
27 | data() {
28 | return { data: new Array(1000).fill(0).map((x, i) => i + '') }
29 | },
30 | }))
31 | .add('Array with big objects', () => ({
32 | components: { ObjectInspector },
33 | template: '',
34 | data() {
35 | return {
36 | data: new Array(100).fill(0).map((x, i) => ({
37 | key: i,
38 | name: `John #${i}`,
39 | dateOfBirth: new Date(i * 10e8),
40 | address: `${i} Main Street`,
41 | zip: 90210 + i,
42 | })),
43 | }
44 | },
45 | }))
46 | .add('Uint32Array', () => ({
47 | components: { ObjectInspector },
48 | template: '',
49 | data() {
50 | return { data: new Uint32Array(1000) }
51 | },
52 | }))
53 |
--------------------------------------------------------------------------------
/stories/stories-basic-type.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { storiesOf } from '@storybook/vue'
3 |
4 | import ObjectInspector from '../src/index.js'
5 |
6 | storiesOf('Numbers', module)
7 | .add('positive', () => ({
8 | components: { ObjectInspector },
9 | template: '',
10 | }))
11 | .add('zero', () => ({
12 | components: { ObjectInspector },
13 | template: '',
14 | }))
15 | .add('negative', () => ({
16 | components: { ObjectInspector },
17 | template: '',
18 | }))
19 | .add('float', () => ({
20 | components: { ObjectInspector },
21 | template: '',
22 | }))
23 | .add('exponential', () => ({
24 | components: { ObjectInspector },
25 | template: '',
26 | }))
27 | .add('NaN', () => ({
28 | components: { ObjectInspector },
29 | template: '',
30 | }))
31 | .add('Infinity', () => ({
32 | components: { ObjectInspector },
33 | template: '',
34 | }))
35 |
36 | storiesOf('BigInts', module)
37 | .add('positive', () => ({
38 | components: { ObjectInspector },
39 | template: '',
40 | }))
41 | .add('zero', () => ({
42 | components: { ObjectInspector },
43 | template: '',
44 | }))
45 | .add('negative', () => ({
46 | components: { ObjectInspector },
47 | template: '',
48 | }))
49 |
50 | storiesOf('Strings', module)
51 | .add('empty string', () => ({
52 | components: { ObjectInspector },
53 | template: '',
54 | }))
55 | .add('simple', () => ({
56 | components: { ObjectInspector },
57 | template: '',
58 | }))
59 |
60 | storiesOf('Booleans', module)
61 | .add('true', () => ({
62 | components: { ObjectInspector },
63 | template: '',
64 | }))
65 | .add('false', () => ({
66 | components: { ObjectInspector },
67 | template: '',
68 | }))
69 |
70 | storiesOf('Undefined', module).add('undefined', () => ({
71 | components: { ObjectInspector },
72 | template: '',
73 | }))
74 |
75 | storiesOf('Null', module).add('null', () => ({
76 | components: { ObjectInspector },
77 | template: '',
78 | }))
79 |
80 | storiesOf('Symbols', module).add('test', () => ({
81 | components: { ObjectInspector },
82 | template: '',
83 | data() {
84 | return { data: Symbol.for('test') }
85 | },
86 | }))
87 |
--------------------------------------------------------------------------------
/stories/stories-maps-sets-functions.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { storiesOf } from '@storybook/vue'
3 |
4 | import ObjectInspector from '../src/index.js'
5 |
6 | storiesOf('Maps', module)
7 | .add('Map: Empty Map', () => ({
8 | components: { ObjectInspector },
9 | template: '',
10 | }))
11 | .add('Map: Boolean keys', () => ({
12 | components: { ObjectInspector },
13 | template:
14 | '',
15 | }))
16 | .add('Map: Regex keys', () => ({
17 | components: { ObjectInspector },
18 | template:
19 | '',
20 | }))
21 | .add('Map: String keys', () => ({
22 | components: { ObjectInspector },
23 | template:
24 | '',
25 | }))
26 | .add('Map: Object keys', () => ({
27 | components: { ObjectInspector },
28 | template:
29 | '',
30 | }))
31 | .add('Map: Array keys', () => ({
32 | components: { ObjectInspector },
33 | template:
34 | '',
35 | }))
36 | .add('Map: Map keys', () => ({
37 | components: { ObjectInspector },
38 | template:
39 | '',
40 | }))
41 |
42 | storiesOf('Sets', module)
43 | .add('Set: Empty Set', () => ({
44 | components: { ObjectInspector },
45 | template: '',
46 | }))
47 | .add('Set: Simple Set', () => ({
48 | components: { ObjectInspector },
49 | template:
50 | '',
51 | }))
52 | .add('Set: Nested Set', () => ({
53 | components: { ObjectInspector },
54 | template:
55 | '',
56 | }))
57 |
58 | storiesOf('Functions', module)
59 | .add('Functions: anonymous function', () => ({
60 | components: { ObjectInspector },
61 | template: '',
62 | }))
63 | .add('Functions: anonymous arrow function', () => ({
64 | components: { ObjectInspector },
65 | template: '',
66 | }))
67 | .add('Functions: named function', () => ({
68 | components: { ObjectInspector },
69 | template: '',
70 | data() {
71 | return {
72 | data: function namedFunction() {},
73 | }
74 | },
75 | }))
76 | .add('Functions: named function (show non-enumerable properties)', () => ({
77 | components: { ObjectInspector },
78 | template:
79 | '',
80 | data() {
81 | return {
82 | data: function namedFunction() {},
83 | }
84 | },
85 | }))
86 |
--------------------------------------------------------------------------------
/stories/stories-object.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { storiesOf } from '@storybook/vue'
3 |
4 | import ObjectInspector from '../src/index.js'
5 |
6 | storiesOf('Nested object examples', module)
7 | .add('Ice sculpture', () => ({
8 | components: { ObjectInspector },
9 | template: '',
10 | data() {
11 | return {
12 | data: {
13 | id: 2,
14 | name: 'An ice sculpture',
15 | // "price": 12.50,
16 | tags: ['cold', 'ice'],
17 | dimensions: {
18 | length: 7.0,
19 | width: 12.0,
20 | height: 9.5,
21 | },
22 | warehouseLocation: {
23 | latitude: -78.75,
24 | longitude: 20.4,
25 | },
26 | },
27 | }
28 | },
29 | }))
30 | .add('Github', () => ({
31 | components: { ObjectInspector },
32 | template: '',
33 | data() {
34 | return {
35 | data: {
36 | login: 'vikyd',
37 | id: 2208880,
38 | avatar_url: 'https://avatars0.githubusercontent.com/u/2208880?v=4',
39 | gravatar_id: '',
40 | url: 'https://api.github.com/users/vikyd',
41 | html_url: 'https://github.com/vikyd',
42 | followers_url: 'https://api.github.com/users/vikyd/followers',
43 | following_url:
44 | 'https://api.github.com/users/vikyd/following{/other_user}',
45 | gists_url: 'https://api.github.com/users/vikyd/gists{/gist_id}',
46 | starred_url:
47 | 'https://api.github.com/users/vikyd/starred{/owner}{/repo}',
48 | subscriptions_url: 'https://api.github.com/users/vikyd/subscriptions',
49 | organizations_url: 'https://api.github.com/users/vikyd/orgs',
50 | repos_url: 'https://api.github.com/users/vikyd/repos',
51 | events_url: 'https://api.github.com/users/vikyd/events{/privacy}',
52 | received_events_url:
53 | 'https://api.github.com/users/vikyd/received_events',
54 | type: 'User',
55 | site_admin: false,
56 | name: 'VikydZhang',
57 | company: null,
58 | blog: 'https://github.com/vikyd/note',
59 | location: null,
60 | email: null,
61 | hireable: null,
62 | bio: 'Why am I here ?',
63 | twitter_username: null,
64 | public_repos: 76,
65 | public_gists: 4,
66 | followers: 15,
67 | following: 119,
68 | created_at: '2012-08-24T03:24:37Z',
69 | updated_at: '2021-01-05T07:52:04Z',
70 | },
71 | }
72 | },
73 | }))
74 | .add('Glossary', () => ({
75 | components: { ObjectInspector },
76 | template: '',
77 | data() {
78 | return {
79 | data: {
80 | glossary: {
81 | title: 'example glossary',
82 | GlossDiv: {
83 | title: 'S',
84 | GlossList: {
85 | GlossEntry: {
86 | ID: 'SGML',
87 | SortAs: 'SGML',
88 | GlossTerm: 'Standard Generalized Markup Language',
89 | Acronym: 'SGML',
90 | Abbrev: 'ISO 8879:1986',
91 | GlossDef: {
92 | para:
93 | 'A meta-markup language, used to create markup languages such as DocBook.',
94 | GlossSeeAlso: ['GML', 'XML'],
95 | },
96 | GlossSee: 'markup',
97 | },
98 | },
99 | },
100 | },
101 | },
102 | }
103 | },
104 | }))
105 | .add('Contrived example', () => ({
106 | components: { ObjectInspector },
107 | template: '',
108 | data() {
109 | return {
110 | data: {
111 | a1: 1,
112 | a2: 'A2',
113 | a3: true,
114 | a4: undefined,
115 | a5: {
116 | 'a5-1': null,
117 | 'a5-2': ['a5-2-1', 'a5-2-2'],
118 | 'a5-3': {},
119 | },
120 | a6: function () {
121 | // eslint-disable-next-line
122 | console.log('hello world')
123 | },
124 | a7: new Date('2005-04-03'),
125 | },
126 | }
127 | },
128 | }))
129 |
130 | storiesOf('Objects', module)
131 | .add('Object: Date', () => ({
132 | components: { ObjectInspector },
133 | template: '',
134 | }))
135 | .add('Object: Regular Expressin', () => ({
136 | components: { ObjectInspector },
137 | template: '',
138 | }))
139 | .add('Object: Empty Object', () => ({
140 | components: { ObjectInspector },
141 | template:
142 | '',
143 | }))
144 | .add('Object: Empty String key', () => ({
145 | components: { ObjectInspector },
146 | template: ``,
147 | }))
148 | .add('Object: Object with getter property', () => ({
149 | components: { ObjectInspector },
150 | template:
151 | '',
152 | }))
153 | .add('Object: Object with getter property that throws', () => ({
154 | components: { ObjectInspector },
155 | template: '',
156 | data() {
157 | // TODO: this example will not working now
158 | return {
159 | data: {
160 | get prop() {
161 | throw new Error()
162 | },
163 | },
164 | }
165 | },
166 | }))
167 | .add('Object: Simple Object', () => ({
168 | components: { ObjectInspector },
169 | template:
170 | '',
171 | }))
172 | .add('Object: inherited object', () => ({
173 | components: { ObjectInspector },
174 | template:
175 | '',
176 | }))
177 | .add('Object: `Object`', () => ({
178 | components: { ObjectInspector },
179 | template:
180 | '',
181 | }))
182 | .add('Object: `Object.prototype`', () => ({
183 | components: { ObjectInspector },
184 | template:
185 | '',
186 | }))
187 | .add('Object: Simple Object with name', () => ({
188 | components: { ObjectInspector },
189 | template:
190 | '',
191 | }))
192 | .add(
193 | 'Object: `Object.create(null) (Empty object with null prototype)`',
194 | () => ({
195 | components: { ObjectInspector },
196 | template:
197 | '',
198 | })
199 | )
200 | .add('Object: Object with null prototype', () => ({
201 | components: { ObjectInspector },
202 | template:
203 | '',
204 | }))
205 |
--------------------------------------------------------------------------------
/stories/stories-themes.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { storiesOf } from '@storybook/vue'
3 |
4 | import ObjectInspector from '../src/index.js'
5 |
6 | const data = { a: 1, b: 'abc', c: [1, 2, 3] }
7 |
8 | storiesOf('Themes', module)
9 | .add('chromeLight', () => ({
10 | components: { ObjectInspector },
11 | template: '',
12 | data() {
13 | return {
14 | data: data,
15 | }
16 | },
17 | }))
18 | .add('chromeDark', () => ({
19 | components: { ObjectInspector },
20 | template:
21 | '',
22 | data() {
23 | return {
24 | data: data,
25 | }
26 | },
27 | }))
28 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // modify dev page
3 | pages: {
4 | index: {
5 | // dev entry
6 | entry: 'example/main.js',
7 | template: 'example/public/index.html',
8 | filename: 'index.html',
9 | },
10 | },
11 | css: { extract: false },
12 | }
13 |
--------------------------------------------------------------------------------