2
0
mirror of https://github.com/tenrok/vue-json-viewer.git synced 2026-06-08 17:22:32 +03:00

Merge pull request #84 from mlanin-forks/feature/expand-all

Add ability to expand full branch
This commit is contained in:
陈峰
2021-12-18 15:31:30 +08:00
committed by GitHub
26 changed files with 971 additions and 17926 deletions
+6 -1
View File
@@ -146,4 +146,9 @@ public/bundle/*
nbproject/*
.DS_Store
.devhost
examples/dist/
examples/dist/
/vue-json-viewer.js
/ssr.js
/style.css
yarn.lock
package-lock.json
-33
View File
@@ -1,33 +0,0 @@
language : node_js
node_js:
- "10"
branches:
only:
- master
install:
- npm install
os:
- linux
stages:
- name: deploy
jobs:
include:
- stage: deploy
script:
- echo "NPM Deploying Started ..."
- npm version
- npm run build
- echo "NPM Building Finished."
deploy:
provider: npm
email: chenfengjw@hotmail.com
api_key: "$NPM_TOKEN"
skip_cleanup: true
on:
all_branches: true
+32
View File
@@ -0,0 +1,32 @@
## 2.2.20 2021-10-31
- 给box加getPath方法 [pr](https://github.com/chenfengjw163/vue-json-viewer/pull/79)
- chore: move "vue" to "peerDependencies" [pr](https://github.com/chenfengjw163/vue-json-viewer/pull/75)
## 2.2.17 2020-12-29
- fix: issues [issues](https://github.com/chenfengjw163/vue-json-viewer/issues/64)
- fix: issues [issues](https://github.com/chenfengjw163/vue-json-viewer/issues/65)
- fix: issues [issues](https://github.com/chenfengjw163/vue-json-viewer/issues/66)
## 2.2.17 2020-12-29
- fix: long string expand arrow display error [pr](https://github.com/chenfengjw163/vue-json-viewer/pull/62)
## 2.2.16 2020-11-13
- support long string expand [issues](https://github.com/chenfengjw163/vue-json-viewer/issues/60)
## 2.2.15 2020-10-13
- Add Preview Mode
## 2.2.14 2020-07-27
- Allow add specific style for float and integer numbers [pr](https://github.com/chenfengjw163/vue-json-viewer/pull/51)
## 2.2.13 2020-07-14
- Add timeformat props to support custom time format [pr](https://github.com/chenfengjw163/vue-json-viewer/pull/48)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 陈峰
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+55 -6
View File
@@ -1,24 +1,37 @@
# vue-json-viewer
Simple JSON viewer component, for Vue.js 2
[中文版](https://github.com/chenfengjw163/vue-json-viewer/blob/master/README_CN.md)
[![Build Status](https://travis-ci.org/<chenfengjw163>/<vue-json-viewer>.svg?branch=master)](https://travis-ci.org/<chenfengjw163>/<vue-json-viewer>)
Simple JSON viewer component, for Vue.js 2 or 3.
Support for incremental update components
[![Travis](https://img.shields.io/travis/chenfengjw163/vue-json-viewer/master.svg?style=flat-square)](https://travis-ci.org/chenfengjw163/vue-json-viewer)
[![npm](https://img.shields.io/npm/v/vue-json-viewer.svg?style=flat-square)](https://www.npmjs.com/package/vue-json-viewer)
![npm](https://img.shields.io/npm/dw/vue-json-viewer.svg?style=flat-square)
- [Installing](#installing)
- [Example](#example)
- [Options](#options)
- [Listeners](#listeners)
- [Slots](#slots)
- [Theming](#theming)
## Installing
Using npm:
```
$ npm install vue-json-viewer --save
$ npm install vue-json-viewer@2 --save
// Vue2
$ npm install vue-json-viewer@3 --save
// Vue3
```
Using yarn:
```
$ yarn add vue-json-viewer
$ yarn add vue-json-viewer@2
// Vue2
$ yarn add vue-json-viewer@3
// Vue3
```
## Example
@@ -38,10 +51,13 @@ $ yarn add vue-json-viewer
``` js
import Vue from 'vue'
import JsonViewer from '../lib'
import JsonViewer from 'vue-json-viewer'
// Import JsonViewer as a Vue.js plugin
Vue.use(JsonViewer)
// or
// components: {JsonViewer}
new Vue({
el: '#app',
@@ -111,6 +127,22 @@ new Vue({
}
})
```
### SSR
``` js
import JsonViewer from 'vue-json-viewer/ssr'
// Import JsonViewer as a Vue.js plugin
Vue.use(JsonViewer)
// or
// components: {JsonViewer}
```
and
``` js
import 'vue-json-viewer/style.css'
```
### Preview
![preview](./example/preview.png)
@@ -121,10 +153,25 @@ new Vue({
| ----------- |:------------- | ----------- |
| `value` | JSON data (can be used with `v-model`) | **Required** |
| `expand-depth` | Collapse blocs under this depth | `1` |
| `copyable` | Display the copy button | `false` |
| `copyable` | Display the copy button, you can customize copy text just set `{copyText: 'copy', copiedText: 'copied', timeout: 2000}` or set `true` use default copytext | `false` |
| `sort` | Sort keys before displaying | `false` |
| `boxed` | Add a fancy "boxed" style to component | `false` |
| `theme` | Add a custom CSS class for theming purposes | `jv-light` |
| `expanded` | Default expand the view | `false` |
| `timeformat` | custom time format function | time => time.toLocaleString() |
| `preview-mode` | no expand mode | `false` |
## Listeners
| Listener | Description | Value |
| ----------- |:------------- | ----------- |
| `copied` | Emits copyEvent after text copied | Clipboard success event |
## Slots
| Name | Description | Scope |
| ----------- |:------------- | ----------- |
| `copy` | Custom content for copy button | `{copied: boolean}` |
## Theming
@@ -160,6 +207,8 @@ To create custom theme, (e.g. `my-awesome-json-theme`), in two easy steps:
&.jv-boolean { color: #fc1e70 }
&.jv-function { color: #067bca }
&.jv-number { color: #fc1e70 }
&.jv-number-float { color: #fc1e70 }
&.jv-number-integer { color: #fc1e70 }
&.jv-object { color: #111111 }
&.jv-undefined { color: #e08331 }
&.jv-string {
+216
View File
@@ -0,0 +1,216 @@
# vue-json-viewer
简单易用的json内容展示组件,支持vue@2和3,支持SSR,组件支持增量渲染即使大文件json也可以快速渲染。
[![Travis](https://img.shields.io/travis/chenfengjw163/vue-json-viewer/master.svg?style=flat-square)](https://travis-ci.org/chenfengjw163/vue-json-viewer)
[![npm](https://img.shields.io/npm/v/vue-json-viewer.svg?style=flat-square)](https://www.npmjs.com/package/vue-json-viewer)
![npm](https://img.shields.io/npm/dw/vue-json-viewer.svg?style=flat-square)
- [安装](#安装)
- [示例](#示例)
- [选项](#选项)
- [主题](#主题)
## 安装
使用 npm:
```
$ npm install vue-json-viewer@2 --save
// Vue2
$ npm install vue-json-viewer@3 --save
// Vue3
```
使用 yarn:
```
$ yarn add vue-json-viewer@2
// Vue2
$ yarn add vue-json-viewer@3
// Vue3
```
## 示例
``` html
<json-viewer :value="jsonData"></json-viewer>
<hr />
<json-viewer
:value="jsonData"
:expand-depth=5
copyable
boxed
sort></json-viewer>
```
``` js
import Vue from 'vue'
import JsonViewer from 'vue-json-viewer'
// Import JsonViewer as a Vue.js plugin
Vue.use(JsonViewer)
// or
// components: {JsonViewer}
new Vue({
el: '#app',
data() {
return {
jsonData: {
total: 25,
limit: 10,
skip: 0,
links: {
previous: undefined,
next: function () {},
},
data: [
{
id: '5968fcad629fa84ab65a5247',
firstname: 'Ada',
lastname: 'Lovelace',
awards: null,
known: [
'mathematics',
'computing'
],
position: {
lat: 44.563836,
lng: 6.495139
},
description: `Augusta Ada King, Countess of Lovelace (née Byron; 10 December 1815 27 November 1852) was an English mathematician and writer,
chiefly known for her work on Charles Babbage's proposed mechanical general-purpose computer,
the Analytical Engine. She was the first to recognise that the machine had applications beyond pure calculation,
and published the first algorithm intended to be carried out by such a machine.
As a result, she is sometimes regarded as the first to recognise the full potential of a "computing machine" and the first computer programmer.`,
bornAt: '1815-12-10T00:00:00.000Z',
diedAt: '1852-11-27T00:00:00.000Z'
}, {
id: '5968fcad629fa84ab65a5246',
firstname: 'Grace',
lastname: 'Hopper',
awards: [
'Defense Distinguished Service Medal',
'Legion of Merit',
'Meritorious Service Medal',
'American Campaign Medal',
'World War II Victory Medal',
'National Defense Service Medal',
'Armed Forces Reserve Medal',
'Naval Reserve Medal',
'Presidential Medal of Freedom'
],
known: null,
position: {
lat: 43.614624,
lng: 3.879995
},
description: `Grace Brewster Murray Hopper (née Murray; December 9, 1906 January 1, 1992)
was an American computer scientist and United States Navy rear admiral.
One of the first programmers of the Harvard Mark I computer,
she was a pioneer of computer programming who invented one of the first compiler related tools.
She popularized the idea of machine-independent programming languages, which led to the development of COBOL,
an early high-level programming language still in use today.`,
bornAt: '1815-12-10T00:00:00.000Z',
diedAt: '1852-11-27T00:00:00.000Z'
}
]
}
}
}
})
```
### 支持SSR
``` js
import JsonViewer from 'vue-json-viewer/ssr'
// Import JsonViewer as a Vue.js plugin
Vue.use(JsonViewer)
// or
// components: {JsonViewer}
```
and
``` js
import 'vue-json-viewer/style.css'
```
### 图片预览
![preview](./example/preview.png)
## 选项
| 属性 | 描述 | 默认值 |
| ----------- |:------------- | ----------- |
| `value` | json对象的值,可以使用v-model,支持响应式 | **必填** |
| `expand-depth` | 默认展开的层级 | `1` |
| `copyable` | 展示复制按钮,默认文案为:copy、copied!, 你可以设置一个对象`{copyText: 'copy', copiedText: 'copied'}` 来自定义复制按钮文案 | `false` |
| `sort` | 按照key排序展示 | `false` |
| `boxed` | 为组件添加一个盒样式 | `false` |
| `theme` | 添加一个自定义的样式class用作主题 | `jv-light` |
| `expanded` | 默认展开视图 | `false` |
| `timeformat` | 自定义时间格式函数 | time => time.toLocaleString() |
| `preview-mode` | 不可折叠模式,默认全部展开 | `false` |
## 主题
有两个办法创建自定义主题, (e.g. `my-awesome-json-theme`):
1. 添加 `theme="my-awesome-json-theme"` JsonViewer的组件属性
2. 复制粘贴下面的模板并且根据自定义的theme名称做对应调整:
``` scss
// values are default one from jv-light template
.my-awesome-json-theme {
background: #fff;
white-space: nowrap;
color: #525252;
font-size: 14px;
font-family: Consolas, Menlo, Courier, monospace;
.jv-ellipsis {
color: #999;
background-color: #eee;
display: inline-block;
line-height: 0.9;
font-size: 0.9em;
padding: 0px 4px 2px 4px;
border-radius: 3px;
vertical-align: 2px;
cursor: pointer;
user-select: none;
}
.jv-button { color: #49b3ff }
.jv-key { color: #111111 }
.jv-item {
&.jv-array { color: #111111 }
&.jv-boolean { color: #fc1e70 }
&.jv-function { color: #067bca }
&.jv-number { color: #fc1e70 }
&.jv-number-float { color: #fc1e70 }
&.jv-number-integer { color: #fc1e70 }
&.jv-object { color: #111111 }
&.jv-undefined { color: #e08331 }
&.jv-string {
color: #42b983;
word-break: break-word;
white-space: normal;
}
}
.jv-code {
.jv-toggle {
&:before {
padding: 0px 2px;
border-radius: 2px;
}
&:hover {
&:before {
background: #eee;
}
}
}
}
}
```
+27 -16
View File
@@ -1,14 +1,29 @@
const path = require('path');
const webpack = require('webpack')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
mode: 'production',
entry: './lib/index.js',
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
uglifyOptions: {
comments: false
}
}),
],
},
output: {
path: path.join(__dirname, '../dist'),
path: path.join(__dirname, '../'),
filename: 'vue-json-viewer.js',
libraryTarget: 'umd',
library: 'JsonView'
library: 'JsonView',
globalObject: 'this'
},
resolve: {
extensions: ['.js', '.vue'],
@@ -17,7 +32,8 @@ module.exports = {
]
},
externals: {
vue: 'vue'
vue: 'vue',
clipboard: 'clipboard'
},
module: {
rules: [
@@ -27,8 +43,8 @@ module.exports = {
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader', 'autoprefixer-loader']
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
@@ -55,15 +71,10 @@ module.exports = {
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
parallel: true,
uglifyOptions: {
compress: {
warnings: false
},
comments: false
}
}),
// new BundleAnalyzerPlugin()
new VueLoaderPlugin()
]
}
if (process.argv.some(a => a === '--report')) {
module.exports.plugins.push(new BundleAnalyzerPlugin());
}
+89
View File
@@ -0,0 +1,89 @@
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
mode: 'production',
entry: './lib/index.js',
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false,
uglifyOptions: {
comments: false
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: { safe: true, sourceMap: false}
})
],
},
output: {
path: path.join(__dirname, '../'),
filename: 'ssr.js',
libraryTarget: 'umd',
library: 'JsonView',
globalObject: 'this'
},
resolve: {
extensions: ['.js', '.vue'],
modules: [
'node_modules'
]
},
externals: {
vue: 'vue',
clipboard: 'clipboard'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.s?css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader'
}]
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: 'style.css',
allChunks: true,
})
]
}
if (process.argv.some(a => a === '--report')) {
module.exports.plugins.push(new BundleAnalyzerPlugin());
}
+6
View File
@@ -0,0 +1,6 @@
.jv-number-float {
color: #faa !important;
}
.jv-key-node {
display: flex;
}
+44 -6
View File
@@ -1,21 +1,57 @@
import Vue from 'vue'
import JsonViewer from '../dist/vue-json-viewer'
import JsonViewer from '../lib'
import './app.css'
Vue.use(JsonViewer)
new Vue({
el: '#app',
render() {
const scopedSlots = {
copy: ({ copied }) => {
if (copied) return <button disabled>Copied!</button>
return <button>Copy me!</button>
},
}
const onCopied = (copyEvent) => {
alert(`Text successfully copied!\n${copyEvent.text}`);
}
return (
<div>
<json-viewer
preview-mode
value={{
data: {
data: {
data: {
a: 1
}
}
}
}}></json-viewer>
<json-viewer value={this.jsonData}></json-viewer>
<hr />
<json-viewer
value={this.jsonData}
expand-depth={5}
copyable
copyable={{
copyText: '复制',
copiedText: '复制成功',
align: 'left'
}}
boxed
timeformat={time => new Date(time)}
sort></json-viewer>
<hr />
<json-viewer
value={this.jsonData}
expand-depth={1}
copyable={{
timeout: 4000,
align: 'left'
}}
scopedSlots={scopedSlots}
onCopied={onCopied}></json-viewer>
</div>
)
},
@@ -25,6 +61,7 @@ new Vue({
total: 25,
limit: 10,
skip: 0,
numbers: 10.11,
success: true,
links: {
previous: undefined,
@@ -34,6 +71,7 @@ new Vue({
{
id: '5968fcad629fa84ab65a5247',
firstname: 'Ada',
link: 'http://google.com',
lastname: 'Lovelace',
awards: null,
known: [
@@ -49,8 +87,8 @@ new Vue({
the Analytical Engine. She was the first to recognise that the machine had applications beyond pure calculation,
and published the first algorithm intended to be carried out by such a machine.
As a result, she is sometimes regarded as the first to recognise the full potential of a "computing machine" and the first computer programmer.`,
bornAt: '1815-12-10T00:00:00.000Z',
diedAt: '1852-11-27T00:00:00.000Z'
bornAt: new Date('1815-12-10T00:00:00.000Z'),
diedAt: new Date('1852-11-27T00:00:00.000Z')
}, {
id: '5968fcad629fa84ab65a5246',
firstname: 'Grace',
@@ -77,8 +115,8 @@ new Vue({
she was a pioneer of computer programming who invented one of the first compiler related tools.
She popularized the idea of machine-independent programming languages, which led to the development of COBOL,
an early high-level programming language still in use today.`,
bornAt: '1815-12-10T00:00:00.000Z',
diedAt: '1852-11-27T00:00:00.000Z'
bornAt: new Date('1815-12-10T00:00:00.000Z'),
diedAt: new Date('1852-11-27T00:00:00.000Z')
}
]
}
+7 -3
View File
@@ -1,7 +1,9 @@
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './example/app.js',
output: {
path: path.join(__dirname, '../dist'),
@@ -9,7 +11,8 @@ module.exports = {
},
devtool: 'cheap-module-source-map',
devServer: {
port: 8081
port: 8082,
disableHostCheck: true
},
resolve: {
extensions: ['.js', '.vue'],
@@ -26,8 +29,8 @@ module.exports = {
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader', 'autoprefixer-loader']
test: /\.s?css$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
@@ -59,5 +62,6 @@ module.exports = {
template: './example/index.html',
inject: true
}),
new VueLoaderPlugin(),
]
}
+5
View File
@@ -0,0 +1,5 @@
<svg height="16" width="8" xmlns="http://www.w3.org/2000/svg">
<polygon points="0,0 8,8 0,16"
style="fill:#666;stroke:purple;stroke-width:0" />
</svg>

After

Width:  |  Height:  |  Size: 152 B

+56 -11
View File
@@ -6,13 +6,14 @@ import JsonBoolean from './types/json-boolean'
import JsonObject from './types/json-object'
import JsonArray from './types/json-array'
import JsonFunction from './types/json-function'
import JsonDate from './types/json-date'
export default {
name: 'JsonBox',
inject: ['expandDepth'],
props: {
value: {
type: [Object, Array, String, Number, Boolean, Function],
type: [Object, Array, String, Number, Boolean, Function, Date],
default: null
},
keyName: {
@@ -23,20 +24,32 @@ export default {
depth: {
type: Number,
default: 0
}
},
previewMode: Boolean,
forceExpand: Boolean,
},
data() {
return {
expand: true
expand: true,
forceExpandMe: this.forceExpand,
}
},
mounted() {
this.expand = this.depth >= this.expandDepth ? false : true
this.expand = this.previewMode || (this.depth >= this.expandDepth ? false : true) || this.forceExpandMe
},
methods: {
toggle() {
this.expand = !this.expand
this.dispatchEvent()
},
toggleAll() {
this.expand = !this.expand
this.forceExpandMe = this.expand
this.dispatchEvent()
},
dispatchEvent() {
try {
this.$el.dispatchEvent(new Event('resized'))
} catch (e) {
@@ -45,16 +58,29 @@ export default {
evt.initEvent('resized', true, false)
this.$el.dispatchEvent(evt)
}
},
getPath() {
const path = [this.keyName];
let p = this.$parent;
while(p.depth) {
if (p.$el.classList.contains('jv-node')) {
path.push(p.keyName);
}
p = p.$parent;
}
return path.reverse()
}
},
render (h) {
let elements = []
let dataType
if (this.value === null || this.value === undefined) {
if (this.value === null || this.value === undefined) {
dataType = JsonUndefined
} else if (Array.isArray(this.value)) {
dataType = JsonArray
} else if (Object.prototype.toString.call(this.value) === '[object Date]') {
dataType = JsonDate
} else if (typeof this.value === 'object') {
dataType = JsonObject
} else if (typeof this.value === 'number') {
@@ -66,15 +92,22 @@ export default {
} else if (typeof this.value === 'function') {
dataType = JsonFunction
}
const complex = this.keyName && (this.value && (Array.isArray(this.value) || (typeof this.value === 'object' && Object.prototype.toString.call(this.value) !== '[object Date]')))
if (this.keyName && (this.value && (Array.isArray(this.value) || typeof this.value === 'object'))) {
if (!this.previewMode && complex) {
elements.push(h('span', {
class: {
'jv-toggle': true,
open: !!this.expand
},
on: {
click: this.toggle
click: (event) => {
if (event.altKey) {
this.toggleAll()
} else {
this.toggle()
}
}
}
}))
}
@@ -85,7 +118,7 @@ export default {
'jv-key': true
},
domProps: {
innerHTML: `${this.keyName}:`
innerText: `${this.keyName}:`
}
}))
}
@@ -99,18 +132,26 @@ export default {
keyName: this.keyName,
sort: this.sort,
depth: this.depth,
expand: this.expand
expand: this.expand,
previewMode: this.previewMode,
forceExpand: this.forceExpandMe
},
on: {
'update:expand': value => {
this.expand = value
},
'update:expandAll': value => {
this.expand = value
this.forceExpandMe = this.expand
}
}
}))
return h('div', {
class: {
'jv-node': true
class: {
'jv-node': true,
'jv-key-node': Boolean(this.keyName) && !complex,
'toggle': !this.previewMode && complex
}
}, elements)
}
@@ -130,6 +171,10 @@ export default {
}
}
&.toggle {
margin-left: 13px !important;
}
& .jv-node {
margin-left: 25px;
}
+109 -52
View File
@@ -1,28 +1,38 @@
<template>
<div :class="jvClass">
<div
ref="viewer"
:class="jvClass"
>
<div
v-if="copyable"
class="jv-tooltip"
:class="`jv-tooltip ${copyText.align || 'right'}`"
>
<span
ref="clip"
class="jv-button"
:class="{copied}"
@click="clip"
>{{ copied ? 'copied!' : 'copy' }}</span>
>
<slot
name="copy"
:copied="copied"
>
{{ copied ? copyText.copiedText : copyText.copyText }}
</slot>
</span>
</div>
<div
class="jv-code"
:class="{'open': expandCode}"
:class="{'open': expandCode, boxed}"
>
<json-box
ref="jsonBox"
:value="value"
:sort="sort"
:preview-mode="previewMode"
/>
</div>
<div
v-if="expandableCode"
v-if="expandableCode && boxed"
class="jv-more"
@click="toggleExpandCode"
>
@@ -38,6 +48,7 @@
import Vue from 'vue'
import JsonBox from './json-box'
import Clipboard from 'clipboard'
import {debounce} from './utils';
export default {
name: 'JsonViewer',
@@ -49,12 +60,16 @@ export default {
type: [Object, Array, String, Number, Boolean, Function],
required: true
},
expanded: {
type: Boolean,
default: false
},
expandDepth: {
type: Number,
default: 1
},
copyable: {
type: Boolean,
type: [Boolean, Object],
default: false
},
sort: {
@@ -69,31 +84,73 @@ export default {
type: String,
default: 'jv-light'
},
timeformat: {
type: Function,
default: value => value.toLocaleString(),
},
previewMode: {
type: Boolean,
default: false,
}
},
provide () {
return {
expandDepth: this.expandDepth,
timeformat: this.timeformat,
}
},
data () {
return {
copied: false,
expandableCode: false,
expandCode: false
expandCode: this.expanded
}
},
computed: {
jvClass () {
jvClass() {
return 'jv-container ' + this.theme + (this.boxed ? ' boxed' : '')
},
copyText() {
const { copyText, copiedText, timeout, align } = this.copyable
return {
copyText: copyText || 'copy',
copiedText: copiedText || 'copied!',
timeout: timeout || 2000,
align,
}
}
},
watch: {
value() {
this.onResized()
}
},
mounted: function () {
this.onResized()
this.$el.addEventListener("resized", this.onResized, true)
this.debounceResized = debounce(this.debResized.bind(this), 200);
if (this.boxed && this.$refs.jsonBox) {
this.onResized()
this.$refs.jsonBox.$el.addEventListener("resized", this.onResized, true)
}
if (this.copyable) {
const clipBoard = new Clipboard(this.$refs.clip, {
container: this.$refs.viewer,
text: () => {
return JSON.stringify(this.value, null, 2)
}
});
clipBoard.on('success', (e) => {
this.onCopied(e)
})
}
},
methods: {
onResized () {
this.debounceResized();
},
debResized() {
this.$nextTick(() => {
if (!this.$refs.jsonBox) return;
if (this.$refs.jsonBox.$el.clientHeight >= 250) {
this.expandableCode = true
} else {
@@ -101,25 +158,15 @@ export default {
}
})
},
clip () {
onCopied(copyEvent) {
if (this.copied) {
return
return;
}
const clipBoard = new Clipboard(this.$refs.clip, {
text: () => {
return JSON.stringify(this.value, null, 2)
}
})
clipBoard.on('success', () => {
this.copied = true
setTimeout(() => {
this.copied = false
}, 2000)
this.$emit('copied')
clipBoard.destroy()
})
this.copied = true
setTimeout(() => {
this.copied = false
}, this.copyText.timeout)
this.$emit('copied', copyEvent)
},
toggleExpandCode () {
this.expandCode = !this.expandCode
@@ -194,6 +241,10 @@ export default {
color: #42b983;
word-break: break-word;
white-space: normal;
.jv-link {
color: #0366d6;
}
}
}
.jv-code {
@@ -212,12 +263,15 @@ export default {
}
.jv-code {
max-height: 300px;
overflow: hidden;
padding: 20px;
padding: 30px 20px;
&.boxed {
max-height: 300px;
}
&.open {
max-height: initial;
max-height: initial !important;
overflow: visible;
overflow-x: auto;
padding-bottom: 45px;
@@ -225,25 +279,19 @@ export default {
}
.jv-toggle {
background-image: url(./icon.svg);
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
cursor: pointer;
&:before {
content: "⏷";
padding: 0px 2px;
border-radius: 2px;
position: absolute;
}
&:after {
content: " ";
position: relative;
display: inline-block;
width: 16px;
}
width: 10px;
height: 10px;
margin-right: 2px;
display: inline-block;
transition: transform 0.1s;
&.open {
&:before {
content: "⏶";
}
transform: rotate(90deg)
}
}
@@ -264,7 +312,11 @@ export default {
z-index: 2;
color: #888;
transition: all 0.1s;
// background: red;
transform: rotate(90deg);
&.open {
transform: rotate(-90deg)
}
}
&:after {
@@ -278,14 +330,14 @@ export default {
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 20%,
rgba(230, 230, 230, 1) 100%
rgba(230, 230, 230, 0.3) 100%
);
transition: all 0.1s;
}
&:hover {
.jv-toggle {
top: 50%;
top: 50%;
color: #111;
}
@@ -314,8 +366,13 @@ export default {
.jv-tooltip {
position: absolute;
right: 15px;
top: 10px;
&.right {
right: 15px;
}
&.left {
left: 15px;
}
}
.j-icon {
+70 -42
View File
@@ -17,23 +17,44 @@ export default {
default: 0
},
sort: Boolean,
expand: Boolean
expand: Boolean,
forceExpand: Boolean,
previewMode: Boolean,
},
computed: {
ordered () {
let value = this.jsonValue
if (!this.sort) {
return value
}
return value.sort()
data() {
return {
value: []
}
},
watch: {
jsonValue(newVal) {
this.setValue(newVal);
}
},
mounted() {
this.setValue(this.jsonValue);
},
methods: {
setValue(vals, index = 0) {
if (index === 0) {
this.value = [];
}
setTimeout(() => {
if (vals.length > index) {
this.value.push(vals[index]);
this.setValue(vals, index + 1);
}
}, 0);
},
toggle() {
this.$emit('update:expand', !this.expand)
this.dispatchEvent();
},
toggleAll() {
this.$emit('update:expandAll', !this.expand)
this.dispatchEvent();
},
dispatchEvent() {
try {
this.$el.dispatchEvent(new Event('resized'))
} catch (e) {
@@ -47,14 +68,20 @@ export default {
render (h) {
let elements = []
if (!this.keyName) {
if (!this.previewMode && !this.keyName) {
elements.push(h('span', {
class: {
'jv-toggle': true,
'open': !!this.expand,
'open': !!this.expand,
},
on: {
click: this.toggle
click: (event) => {
if (event.altKey) {
this.toggleAll()
} else {
this.toggle()
}
}
}
}))
}
@@ -62,46 +89,47 @@ export default {
elements.push(h('span', {
class: {
'jv-item': true,
'jv-array': true,
'jv-array': true,
},
domProps: {
innerHTML: '['
innerText: '['
}
}))
for (let key in this.ordered) {
let value = this.ordered[key]
elements.push(h(JsonBox, {
key,
style: {
display: !this.expand ? 'none' : undefined
},
props: {
sort: this.sort,
// keyName: key,
depth: this.depth + 1,
value,
}
}))
if (this.expand) {
this.value.forEach((value, key) => {
elements.push(h(JsonBox, {
key,
props: {
sort: this.sort,
keyName: `${key}`,
depth: this.depth + 1,
value,
previewMode: this.previewMode,
forceExpand: this.forceExpand,
}
}))
})
}
if (!this.expand) {
if (!this.expand && this.value.length) {
elements.push(h('span', {
style: {
display: this.expand ? 'none' : undefined
},
class: {
'jv-ellipsis': true,
'jv-ellipsis': true,
},
on: {
click: this.toggle
click: (event) => {
if (event.altKey) {
this.toggleAll()
} else {
this.toggle()
}
}
},
attrs: {
title: `click to reveal ${this.jsonValue.length} hidden items`
title: `click to reveal ${this.value.length} hidden items`
},
domProps: {
innerHTML: '...'
innerText: '...'
}
}))
}
@@ -109,10 +137,10 @@ export default {
elements.push(h('span', {
class: {
'jv-item': true,
'jv-array': true,
'jv-array': true,
},
domProps: {
innerHTML: ']'
innerText: ']'
}
}))
+1 -1
View File
@@ -12,7 +12,7 @@ export default {
'jv-boolean': true,
},
domProps: {
innerHTML: props.jsonValue.toString()
innerText: props.jsonValue.toString()
}
})
}
+27
View File
@@ -0,0 +1,27 @@
<script>
export default {
name: 'JsonDate',
inject: ['timeformat'],
functional: true,
props: {
jsonValue: {
type: Date,
required: true
}
},
render (h, { props, injections }) {
const value = props.jsonValue;
const timeformat = injections.timeformat;
return h('span', {
class: {
'jv-item': true,
'jv-string': true,
},
domProps: {
innerText: `"${timeformat(value)}"`
}
})
}
};
</script>
+5 -1
View File
@@ -9,13 +9,17 @@ export default {
}
},
render (h, { props }) {
const isInteger = Number.isInteger(props.jsonValue)
return h('span', {
class: {
'jv-item': true,
'jv-number': true,
'jv-number-integer': isInteger,
'jv-number-float': !isInteger,
},
domProps: {
innerHTML: props.jsonValue.toString()
innerText: props.jsonValue.toString()
}
})
}
+70 -34
View File
@@ -17,25 +17,51 @@ export default {
default: 0
},
expand: Boolean,
sort: Boolean
forceExpand: Boolean,
sort: Boolean,
previewMode: Boolean,
},
data() {
return {
value: {}
}
},
computed: {
ordered () {
if (!this.sort) {
return this.jsonValue
return this.value
}
const ordered = {}
Object.keys(this.jsonValue).sort().forEach(key => {
ordered[key] = this.jsonValue[key]
Object.keys(this.value).sort().forEach(key => {
ordered[key] = this.value[key]
})
return ordered
}
},
watch: {
jsonValue(newVal) {
this.setValue(newVal);
}
},
mounted() {
this.setValue(this.jsonValue);
},
methods: {
setValue(val) {
setTimeout(() => {
this.value = val;
}, 0);
},
toggle() {
this.$emit('update:expand', !this.expand)
this.dispatchEvent();
},
toggleAll() {
this.$emit('update:expandAll', !this.expand)
this.dispatchEvent();
},
dispatchEvent() {
try {
this.$el.dispatchEvent(new Event('resized'))
} catch (e) {
@@ -49,14 +75,20 @@ export default {
render (h) {
let elements = []
if (!this.keyName) {
if (!this.previewMode && !this.keyName) {
elements.push(h('span', {
class: {
'jv-toggle': true,
'open': !!this.expand,
'open': !!this.expand,
},
on: {
click: this.toggle
click: (event) => {
if (event.altKey) {
this.toggleAll()
} else {
this.toggle()
}
}
}
}))
}
@@ -64,48 +96,52 @@ export default {
elements.push(h('span', {
class: {
'jv-item': true,
'jv-object': true,
'jv-object': true,
},
domProps: {
innerHTML: '{'
innerText: '{'
}
}))
for (let key in this.ordered) {
if (this.ordered.hasOwnProperty(key)) {
let value = this.ordered[key]
if (this.expand) {
for (let key in this.ordered) {
if (this.ordered.hasOwnProperty(key)) {
let value = this.ordered[key]
elements.push(h(JsonBox, {
key,
style: {
display: !this.expand ? 'none' : undefined
},
props: {
sort: this.sort,
keyName: key,
depth: this.depth + 1,
value,
}
}))
elements.push(h(JsonBox, {
key,
props: {
sort: this.sort,
keyName: key,
depth: this.depth + 1,
value,
previewMode: this.previewMode,
forceExpand: this.forceExpand,
}
}))
}
}
}
if (!this.expand) {
if (!this.expand && Object.keys(this.value).length) {
elements.push(h('span', {
style: {
display: this.expand ? 'none' : undefined
},
class: {
'jv-ellipsis': true,
'jv-ellipsis': true,
},
on: {
click: this.toggle
click: (event) => {
if (event.altKey) {
this.toggleAll()
} else {
this.toggle()
}
}
},
attrs: {
title: `click to reveal object content (keys: ${Object.keys(this.ordered).join(', ')})`
},
domProps: {
innerHTML: '...'
innerText: '...'
}
}))
}
@@ -113,10 +149,10 @@ export default {
elements.push(h('span', {
class: {
'jv-item': true,
'jv-object': true,
'jv-object': true,
},
domProps: {
innerHTML: '}'
innerText: '}'
}
}))
+73 -10
View File
@@ -1,23 +1,86 @@
<script>
const REG_LINK = /^\w+:\/\//;
export default {
name: 'JsonString',
functional: true,
props: {
jsonValue: {
type: String,
required: true
}
},
render (h, { props }) {
return h('span', {
class: {
'jv-item': true,
'jv-string': true,
},
domProps: {
innerHTML: `"${props.jsonValue.toString()}"`
data() {
return {
expand: true,
canExtend: false,
}
},
mounted() {
if (this.$refs.itemRef.offsetHeight > this.$refs.holderRef.offsetHeight) {
this.canExtend = true;
}
},
methods: {
toggle() {
this.expand = !this.expand;
}
},
render (h) {
let value = this.jsonValue;
const islink = REG_LINK.test(value)
let domItem
if (!this.expand) {
domItem = {
class: {
'jv-ellipsis': true,
},
on: {
click: this.toggle
},
domProps: {
innerText: '...'
}
};
} else {
domItem = {
class: {
'jv-item': true,
'jv-string': true,
},
ref: 'itemRef',
}
})
if (islink) {
value = `<a href="${value}" target="_blank" class="jv-link">${value}</a>`;
domItem.domProps = {
innerHTML: `"${value.toString()}"`
}
} else {
domItem.domProps = {
innerText: `"${value.toString()}"`
}
}
}
return h('span', {}, [
this.canExtend && h('span', {
class: {
'jv-toggle': true,
open: this.expand,
},
on: {
click: this.toggle,
}
}),
h('span', {
class: {
'jv-holder-node': true,
},
ref: 'holderRef'
}),
h('span', domItem)
])
}
}
</script>
+1 -1
View File
@@ -15,7 +15,7 @@ export default {
'jv-undefined': true,
},
domProps: {
innerHTML: props.jsonValue === null ? 'null' : 'undefined'
innerText: props.jsonValue === null ? 'null' : 'undefined'
}
})
}
+14
View File
@@ -0,0 +1,14 @@
export const debounce = function(func, wait) {
let startTime = Date.now();
let timer;
return (...args) => {
if (Date.now() - startTime < wait && timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func(...args);
}, wait);
startTime = Date.now();
}
}
-11619
View File
File diff suppressed because it is too large Load Diff
+29 -25
View File
@@ -1,33 +1,30 @@
{
"name": "vue-json-viewer",
"version": "2.0.2",
"version": "2.2.20",
"description": "vuejs展示json的组件",
"main": "dist/vue-json-viewer.js",
"files": [
"lib/*"
],
"main": "vue-json-viewer.js",
"files": ["vue-json-viewer.js", "ssr.js", "style.css"],
"directories": {
"lib": "./lib",
"example": "./example"
},
"scripts": {
"build": "webpack --config ./build/webpack.config.js",
"build": "npm run build:browser && npm run build:ssr",
"build:browser": "webpack --config ./build/webpack.config.js",
"build:ssr": "webpack --config ./build/webpack.ssr.config.js",
"example": "webpack-dev-server --config ./example/build/webpack.dev.conf.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/chenfengjw163/vue-json-viewer.git"
},
"keywords": [
"vue",
"json"
],
"keywords": ["vue", "json"],
"homepage": "https://github.com/chenfengjw163/vue-json-viewer#readme",
"author": {
"name": "陈峰",
"email": "chenfengjw@hotmail.com"
},
"license": "ISC",
"license": "MIT",
"bugs": {
"url": "https://github.com/chenfengjw163/vue-json-viewer/issues"
},
@@ -42,12 +39,10 @@
}
],
"dependencies": {
"clipboard": "^1.7.1",
"vue": "^2.5.2"
"clipboard": "^2.0.4"
},
"devDependencies": {
"autoprefixer": "^7.1.5",
"autoprefixer-loader": "^3.2.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.1",
"babel-loader": "^7.1.2",
@@ -57,21 +52,30 @@
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-runtime": "^6.26.0",
"css-loader": "^0.28.7",
"eslint": "^5.6.0",
"css-loader": "^3.0.0",
"eslint": "^6.0.1",
"eslint-plugin-vue": "^5.0.0-beta.3",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.5.0",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.19.0",
"uglifyjs-webpack-plugin": "^2.1.2",
"url-loader": "^0.6.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.3",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.8.1",
"webpack-bundle-analyzer": "^3.0.2",
"webpack-dev-server": "^2.9.2",
"vue": "^2.6.9",
"vue-loader": "^15.7.0",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.9",
"webpack": "=4.29.6",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"webpack-merge": "^4.1.0"
},
"peerDependencies": {
"vue": "^2.6.9"
}
}
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
sourceMap: false,
plugins: [
require('autoprefixer')({
browsers: ['> 1%', 'android >=4', 'ios >=8']
})
]
};
-6065
View File
File diff suppressed because it is too large Load Diff