From 8cd319b3cd19052fa9d57f3ad88e222e753dcaae Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 02:29:01 +0100 Subject: [PATCH 01/13] feat: add basic darkmode support --- example/App.tsx | 79 +++++++++++++++++++++++++---- example/Basic.vue | 5 ++ example/Editable.vue | 5 ++ example/SelectControl.vue | 5 ++ example/VirtualList.vue | 5 ++ example/styles.less | 61 +++++++++++++++------- src/components/Tree/index.tsx | 6 +++ src/components/TreeNode/index.tsx | 5 ++ src/components/TreeNode/styles.less | 7 +++ src/themes.less | 3 +- 10 files changed, 151 insertions(+), 30 deletions(-) diff --git a/example/App.tsx b/example/App.tsx index 730fd7f..0afb672 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,4 +1,4 @@ -import { defineComponent, reactive } from 'vue'; +import { defineComponent, reactive, provide, onMounted } from 'vue'; import Basic from './Basic.vue'; import VirtualList from './VirtualList.vue'; import SelectControl from './SelectControl.vue'; @@ -34,13 +34,56 @@ const list = [ // }, ]; +const SunIcon = () => ( + + + + + + + + + + + +); + +const MoonIcon = () => ( + + + +); + export default defineComponent({ setup() { const state = reactive({ activeKey: list[0].key, opened: [list[0].key], + isDarkMode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches, }); + provide('darkModeState', state); + const onActiveChange = (key: string) => { state.activeKey = key; if (!state.opened.includes(key)) { @@ -48,6 +91,10 @@ export default defineComponent({ } }; + onMounted(() => { + document.body.classList.toggle('dark-mode', state.isDarkMode); + }); + return { state, onActiveChange, @@ -57,8 +104,13 @@ export default defineComponent({ render() { const { state, onActiveChange } = this; + const toggleDarkMode = () => { + state.isDarkMode = !state.isDarkMode; + document.body.classList.toggle('dark-mode', state.isDarkMode); + }; + return ( -
+

Vue Json Pretty

Welcome to the demo space of Vue Json Pretty, here we provide the following different @@ -67,15 +119,20 @@ export default defineComponent({

- {list.map(({ title, key }) => ( -
onActiveChange(key)} - > - {title} -
- ))} +
+ {list.map(({ title, key }) => ( +
onActiveChange(key)} + > + {title} +
+ ))} +
+
+ {state.isDarkMode ? : } +
diff --git a/example/Basic.vue b/example/Basic.vue index ae8d500..38650ca 100644 --- a/example/Basic.vue +++ b/example/Basic.vue @@ -46,6 +46,10 @@
+
+ + +

Slots:

@@ -63,6 +67,7 @@

vue-json-pretty:

+
+ + +

vue-json-pretty:

4
+
+ + +

v-model:selectedValue:

{{ state.selectedValue }}
@@ -68,6 +72,7 @@ +
+ + +

vue-json-pretty(10000+ items):

void>, }, + darkMode: { + type: Boolean, + default: false, + }, }, slots: ['renderNodeKey', 'renderNodeValue'], @@ -281,6 +285,7 @@ export default defineComponent({ key={item.id} node={item} collapsed={!!state.hiddenPaths[item.path]} + darkMode={props.darkMode} showDoubleQuotes={props.showDoubleQuotes} showLength={props.showLength} checked={selectedPaths.value.includes(item.path)} @@ -316,6 +321,7 @@ export default defineComponent({ class={{ 'vjs-tree': true, 'is-virtual': props.virtual, + 'dark-mode': props.darkMode, }} onScroll={props.virtual ? handleTreeScroll : undefined} style={ diff --git a/src/components/TreeNode/index.tsx b/src/components/TreeNode/index.tsx index fa5b0bb..c4862b7 100644 --- a/src/components/TreeNode/index.tsx +++ b/src/components/TreeNode/index.tsx @@ -64,6 +64,10 @@ export const treeNodePropsPass = { type: Boolean, default: false, }, + darkMode: { + type: Boolean, + default: false, + }, showKeyValueSpace: { type: Boolean, default: true, @@ -213,6 +217,7 @@ export default defineComponent({ 'has-selector': props.showSelectController, 'has-carets': props.showIcon, 'is-highlight': props.highlightSelectedNode && props.checked, + 'dark-mode': props.darkMode, }} onClick={handleNodeClick} style={props.style} diff --git a/src/components/TreeNode/styles.less b/src/components/TreeNode/styles.less index 6748a73..04a7ee4 100644 --- a/src/components/TreeNode/styles.less +++ b/src/components/TreeNode/styles.less @@ -35,6 +35,13 @@ border-left: 1px dashed @border-color; } } + + &.dark-mode { + &.is-highlight, + &:hover { + background-color: @highlight-bg-color-dark; + } + } } .@{css-prefix}-node-index { diff --git a/src/themes.less b/src/themes.less index d857ba0..96d100d 100644 --- a/src/themes.less +++ b/src/themes.less @@ -6,7 +6,7 @@ @color-info: #1d8ce0; @color-error: #ff4949; @color-success: #13ce66; -@color-nil: #D55FDE; +@color-nil: #d55fde; /* field values color */ @color-string: @color-success; @@ -17,6 +17,7 @@ /* highlight */ @highlight-bg-color: #e6f7ff; +@highlight-bg-color-dark: #2e4558; /* comment */ @comment-color: #bfcbd9; From 59d06eb375cd5ebd998509a43bacc30abf2ce833 Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 03:42:24 +0100 Subject: [PATCH 02/13] refactor: renamed to darkHighlightMode --- example/App.tsx | 29 ++++++++++++++++++++--------- example/Basic.vue | 13 +++++++++---- example/Editable.vue | 14 ++++++++++---- example/SelectControl.vue | 13 +++++++++---- example/VirtualList.vue | 13 +++++++++---- src/components/Tree/index.tsx | 6 +++--- src/components/TreeNode/index.tsx | 4 ++-- src/components/TreeNode/styles.less | 2 +- 8 files changed, 63 insertions(+), 31 deletions(-) diff --git a/example/App.tsx b/example/App.tsx index 0afb672..a6b92ab 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,4 +1,4 @@ -import { defineComponent, reactive, provide, onMounted } from 'vue'; +import { defineComponent, reactive, provide, onMounted, watch } from 'vue'; import Basic from './Basic.vue'; import VirtualList from './VirtualList.vue'; import SelectControl from './SelectControl.vue'; @@ -79,10 +79,13 @@ export default defineComponent({ const state = reactive({ activeKey: list[0].key, opened: [list[0].key], + }); + + const globalDarkModeState = reactive({ isDarkMode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches, }); - provide('darkModeState', state); + provide('darkModeState', globalDarkModeState); const onActiveChange = (key: string) => { state.activeKey = key; @@ -91,23 +94,31 @@ export default defineComponent({ } }; + const toggleDarkMode = () => { + globalDarkModeState.isDarkMode = !globalDarkModeState.isDarkMode; + }; + onMounted(() => { - document.body.classList.toggle('dark-mode', state.isDarkMode); + document.body.classList.toggle('dark-mode', globalDarkModeState.isDarkMode); }); + watch( + () => globalDarkModeState.isDarkMode, + newVal => { + document.body.classList.toggle('dark-mode', newVal); + }, + { immediate: true }, + ); + return { state, onActiveChange, + toggleDarkMode, }; }, render() { - const { state, onActiveChange } = this; - - const toggleDarkMode = () => { - state.isDarkMode = !state.isDarkMode; - document.body.classList.toggle('dark-mode', state.isDarkMode); - }; + const { state, onActiveChange, toggleDarkMode } = this; return (
diff --git a/example/Basic.vue b/example/Basic.vue index 38650ca..893f4c6 100644 --- a/example/Basic.vue +++ b/example/Basic.vue @@ -2,7 +2,7 @@

JSON:

-

Options:

@@ -47,8 +47,8 @@
- - + +
@@ -67,7 +67,7 @@

vue-json-pretty:

import { defineComponent, reactive, watch } from 'vue'; +import { useDarkMode } from './useDarkMode'; import VueJsonPretty from 'src'; const defaultData = { @@ -152,6 +153,8 @@ export default defineComponent({ showKeyValueSpace: true, }); + const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const pathCollapsible = node => { return node.key === 'members'; }; @@ -170,6 +173,8 @@ export default defineComponent({ return { state, pathCollapsible, + localDarkMode, + toggleLocalDarkMode, }; }, }); diff --git a/example/Editable.vue b/example/Editable.vue index 9d6e0e1..52ed5a0 100644 --- a/example/Editable.vue +++ b/example/Editable.vue @@ -2,7 +2,7 @@

JSON:

-

Options:

@@ -35,15 +35,15 @@
- - + +

vue-json-pretty:

import { defineComponent, reactive, watch } from 'vue'; import VueJsonPretty from 'src'; +import { useDarkMode } from './useDarkMode'; const defaultData = { status: 200, @@ -103,6 +104,8 @@ export default defineComponent({ deep: 3, }); + const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + watch( () => state.val, newVal => { @@ -127,7 +130,10 @@ export default defineComponent({ return { state, + localDarkMode, + toggleLocalDarkMode, }; }, }); +./useDarkMode diff --git a/example/SelectControl.vue b/example/SelectControl.vue index e71b67c..e099922 100644 --- a/example/SelectControl.vue +++ b/example/SelectControl.vue @@ -2,7 +2,7 @@

JSON:

-

Options:

@@ -58,8 +58,8 @@
- - + +

v-model:selectedValue:

@@ -72,7 +72,7 @@ import { defineComponent, reactive, watch, nextTick } from 'vue'; import VueJsonPretty from 'src'; +import { useDarkMode } from './useDarkMode'; const defaultData = { status: 200, @@ -153,6 +154,8 @@ export default defineComponent({ showIcon: false, }); + const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const handleNodeClick = node => { state.node = node; }; @@ -191,6 +194,8 @@ export default defineComponent({ state, handleNodeClick, handleAll, + localDarkMode, + toggleLocalDarkMode, }; }, }); diff --git a/example/VirtualList.vue b/example/VirtualList.vue index 79fdf2f..607fe4e 100644 --- a/example/VirtualList.vue +++ b/example/VirtualList.vue @@ -2,7 +2,7 @@

JSON:

-

Options:

@@ -35,14 +35,14 @@
- - + +

vue-json-pretty(10000+ items):

import { defineComponent, reactive, watch } from 'vue'; import VueJsonPretty from 'src'; +import { useDarkMode } from './useDarkMode'; const defaultData = { status: 200, @@ -91,6 +92,8 @@ export default defineComponent({ itemHeight: 20, }); + const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + watch( () => state.val, newVal => { @@ -104,6 +107,8 @@ export default defineComponent({ return { state, + localDarkMode, + toggleLocalDarkMode, }; }, }); diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index 37a078d..dfd5e2e 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -70,7 +70,7 @@ export default defineComponent({ onSelectedChange: { type: Function as PropType<(newVal: string | string[], oldVal: string | string[]) => void>, }, - darkMode: { + darkHighlightMode: { type: Boolean, default: false, }, @@ -285,7 +285,7 @@ export default defineComponent({ key={item.id} node={item} collapsed={!!state.hiddenPaths[item.path]} - darkMode={props.darkMode} + darkHighlightMode={props.darkHighlightMode} showDoubleQuotes={props.showDoubleQuotes} showLength={props.showLength} checked={selectedPaths.value.includes(item.path)} @@ -321,7 +321,7 @@ export default defineComponent({ class={{ 'vjs-tree': true, 'is-virtual': props.virtual, - 'dark-mode': props.darkMode, + 'dark-highlight-mode': props.darkHighlightMode, }} onScroll={props.virtual ? handleTreeScroll : undefined} style={ diff --git a/src/components/TreeNode/index.tsx b/src/components/TreeNode/index.tsx index c4862b7..9a1bcd7 100644 --- a/src/components/TreeNode/index.tsx +++ b/src/components/TreeNode/index.tsx @@ -64,7 +64,7 @@ export const treeNodePropsPass = { type: Boolean, default: false, }, - darkMode: { + darkHighlightMode: { type: Boolean, default: false, }, @@ -217,7 +217,7 @@ export default defineComponent({ 'has-selector': props.showSelectController, 'has-carets': props.showIcon, 'is-highlight': props.highlightSelectedNode && props.checked, - 'dark-mode': props.darkMode, + 'dark-highlight-mode': props.darkHighlightMode, }} onClick={handleNodeClick} style={props.style} diff --git a/src/components/TreeNode/styles.less b/src/components/TreeNode/styles.less index 04a7ee4..62e06e2 100644 --- a/src/components/TreeNode/styles.less +++ b/src/components/TreeNode/styles.less @@ -36,7 +36,7 @@ } } - &.dark-mode { + &.dark-highlight-mode { &.is-highlight, &:hover { background-color: @highlight-bg-color-dark; From 0418610474e63003b1245bd8f2587f18a3c24b16 Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 03:43:57 +0100 Subject: [PATCH 03/13] feat: created darkmode composable --- example/styles.less | 6 ++++++ example/useDarkMode.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 example/useDarkMode.ts diff --git a/example/styles.less b/example/styles.less index 498eaa2..4db97c7 100644 --- a/example/styles.less +++ b/example/styles.less @@ -121,3 +121,9 @@ body { text-overflow: ellipsis; } } + +.dark-textarea { + background-color: #333; + color: #fff; + border-color: #444; +} diff --git a/example/useDarkMode.ts b/example/useDarkMode.ts new file mode 100644 index 0000000..7993383 --- /dev/null +++ b/example/useDarkMode.ts @@ -0,0 +1,19 @@ +import { inject, ref, watch } from 'vue'; + +export function useDarkMode() { + const darkModeState = inject('darkModeState'); + const localDarkMode = ref(darkModeState.isDarkMode); + + watch( + () => darkModeState.isDarkMode, + newVal => { + localDarkMode.value = newVal; + }, + ); + + const toggleLocalDarkMode = () => { + darkModeState.isDarkMode = !darkModeState.isDarkMode; + }; + + return { localDarkMode, toggleLocalDarkMode }; +} From 8cd0524e84ae252c2e387611cbbbeec13eaa5146 Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 03:54:47 +0100 Subject: [PATCH 04/13] fix: dont affect textbox by localDarkMode state --- example/Basic.vue | 5 +++-- example/Editable.vue | 5 +++-- example/SelectControl.vue | 5 +++-- example/VirtualList.vue | 5 +++-- example/useDarkMode.ts | 4 +++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/example/Basic.vue b/example/Basic.vue index 893f4c6..4ecff08 100644 --- a/example/Basic.vue +++ b/example/Basic.vue @@ -2,7 +2,7 @@

JSON:

- +

Options:

@@ -153,7 +153,7 @@ export default defineComponent({ showKeyValueSpace: true, }); - const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const { localDarkMode, toggleLocalDarkMode, globalDarkModeState } = useDarkMode(); const pathCollapsible = node => { return node.key === 'members'; @@ -175,6 +175,7 @@ export default defineComponent({ pathCollapsible, localDarkMode, toggleLocalDarkMode, + globalDarkModeState, }; }, }); diff --git a/example/Editable.vue b/example/Editable.vue index 52ed5a0..9253ab5 100644 --- a/example/Editable.vue +++ b/example/Editable.vue @@ -2,7 +2,7 @@

JSON:

- +

Options:

@@ -104,7 +104,7 @@ export default defineComponent({ deep: 3, }); - const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const { localDarkMode, toggleLocalDarkMode, globalDarkModeState } = useDarkMode(); watch( () => state.val, @@ -132,6 +132,7 @@ export default defineComponent({ state, localDarkMode, toggleLocalDarkMode, + globalDarkModeState, }; }, }); diff --git a/example/SelectControl.vue b/example/SelectControl.vue index e099922..d1c9175 100644 --- a/example/SelectControl.vue +++ b/example/SelectControl.vue @@ -2,7 +2,7 @@

JSON:

- +

Options:

@@ -154,7 +154,7 @@ export default defineComponent({ showIcon: false, }); - const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const { localDarkMode, toggleLocalDarkMode, globalDarkModeState } = useDarkMode(); const handleNodeClick = node => { state.node = node; @@ -196,6 +196,7 @@ export default defineComponent({ handleAll, localDarkMode, toggleLocalDarkMode, + globalDarkModeState, }; }, }); diff --git a/example/VirtualList.vue b/example/VirtualList.vue index 607fe4e..b95d65c 100644 --- a/example/VirtualList.vue +++ b/example/VirtualList.vue @@ -2,7 +2,7 @@

JSON:

- +

Options:

@@ -92,7 +92,7 @@ export default defineComponent({ itemHeight: 20, }); - const { localDarkMode, toggleLocalDarkMode } = useDarkMode(); + const { localDarkMode, toggleLocalDarkMode, globalDarkModeState } = useDarkMode(); watch( () => state.val, @@ -109,6 +109,7 @@ export default defineComponent({ state, localDarkMode, toggleLocalDarkMode, + globalDarkModeState, }; }, }); diff --git a/example/useDarkMode.ts b/example/useDarkMode.ts index 7993383..02a973e 100644 --- a/example/useDarkMode.ts +++ b/example/useDarkMode.ts @@ -2,12 +2,14 @@ import { inject, ref, watch } from 'vue'; export function useDarkMode() { const darkModeState = inject('darkModeState'); + const globalDarkModeState = ref(darkModeState.isDarkMode); const localDarkMode = ref(darkModeState.isDarkMode); watch( () => darkModeState.isDarkMode, newVal => { localDarkMode.value = newVal; + globalDarkModeState.value = newVal; }, ); @@ -15,5 +17,5 @@ export function useDarkMode() { darkModeState.isDarkMode = !darkModeState.isDarkMode; }; - return { localDarkMode, toggleLocalDarkMode }; + return { localDarkMode, toggleLocalDarkMode, globalDarkModeState }; } From b97b625ee915334ebc4b9c1d9c12b242c8896b09 Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 04:26:08 +0100 Subject: [PATCH 05/13] refactor: put icons into seperate component --- example/App.tsx | 41 +---------------------------------------- example/Icons.tsx | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 example/Icons.tsx diff --git a/example/App.tsx b/example/App.tsx index a6b92ab..70ed8dd 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -5,6 +5,7 @@ import SelectControl from './SelectControl.vue'; import Editable from './Editable.vue'; // import Tsx from './Tsx'; import './styles.less'; +import { MoonIcon, SunIcon } from './Icons'; const list = [ { @@ -34,46 +35,6 @@ const list = [ // }, ]; -const SunIcon = () => ( - - - - - - - - - - - -); - -const MoonIcon = () => ( - - - -); - export default defineComponent({ setup() { const state = reactive({ diff --git a/example/Icons.tsx b/example/Icons.tsx new file mode 100644 index 0000000..fcf1407 --- /dev/null +++ b/example/Icons.tsx @@ -0,0 +1,39 @@ +export const SunIcon = () => ( + + + + + + + + + + + +); + +export const MoonIcon = () => ( + + + +); From 90d8524ac147659fa32b2c27677110dbf85ca88d Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 05:06:37 +0100 Subject: [PATCH 06/13] docs: add darkHighlightMode to docs --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 38a308e..5206c3f 100644 --- a/README.md +++ b/README.md @@ -106,31 +106,32 @@ plugins: [ ## Props -| Property | Description | Type | Default | -|---------------------------|---------------------------------------------------------------------------------|-----------------------------------| ------- | -| data(v-model) | JSON data, support v-model when use editable | JSON object | - | -| collapsedNodeLength | Objects or arrays which length is greater than this threshold will be collapsed | number | - | -| deep | Paths greater than this depth will be collapsed | number | - | -| showLength | Show the length when collapsed | boolean | false | -| showLine | Show the line | boolean | true | -| showLineNumber | Show the line number | boolean | false | -| showIcon | Show the icon | boolean | false | -| showDoubleQuotes | Show doublequotes on key | boolean | true | -| virtual | Use virtual scroll | boolean | false | -| height | The height of list when using virtual | number | 400 | -| itemHeight | The height of node when using virtual | number | 20 | -| selectedValue(v-model) | Selected data path | string, array | - | -| rootPath | Root data path | string | `root` | -| nodeSelectable | Defines whether a node supports selection | (node) => boolean | - | -| selectableType | Support path select, default none | `multiple` \| `single` | - | -| showSelectController | Show the select controller | boolean | false | -| selectOnClickNode | Trigger select when click node | boolean | true | -| highlightSelectedNode | Support highlighting selected nodes | boolean | true | -| collapsedOnClickBrackets | Support click brackets to collapse | boolean | true | -| renderNodeKey | render node key, or use slot #renderNodeKey | ({ node, defaultKey }) => vNode | - | -| renderNodeValue | render node value, or use slot #renderNodeValue | ({ node, defaultValue }) => vNode | - | -| editable | Support editable | boolean | false | -| editableTrigger | Trigger | `click` \| `dblclick` | `click` | +| Property | Description | Type | Default | +| ------------------------ | ------------------------------------------------------------------------------------ | --------------------------------- | ------- | +| data(v-model) | JSON data, support v-model when use editable | JSON object | - | +| collapsedNodeLength | Objects or arrays which length is greater than this threshold will be collapsed | number | - | +| deep | Paths greater than this depth will be collapsed | number | - | +| showLength | Show the length when collapsed | boolean | false | +| showLine | Show the line | boolean | true | +| showLineNumber | Show the line number | boolean | false | +| showIcon | Show the icon | boolean | false | +| showDoubleQuotes | Show doublequotes on key | boolean | true | +| virtual | Use virtual scroll | boolean | false | +| height | The height of list when using virtual | number | 400 | +| itemHeight | The height of node when using virtual | number | 20 | +| selectedValue(v-model) | Selected data path | string, array | - | +| rootPath | Root data path | string | `root` | +| nodeSelectable | Defines whether a node supports selection | (node) => boolean | - | +| selectableType | Support path select, default none | `multiple` \| `single` | - | +| showSelectController | Show the select controller | boolean | false | +| selectOnClickNode | Trigger select when click node | boolean | true | +| highlightSelectedNode | Support highlighting selected nodes | boolean | true | +| collapsedOnClickBrackets | Support click brackets to collapse | boolean | true | +| renderNodeKey | render node key, or use slot #renderNodeKey | ({ node, defaultKey }) => vNode | - | +| renderNodeValue | render node value, or use slot #renderNodeValue | ({ node, defaultValue }) => vNode | - | +| editable | Support editable | boolean | false | +| editableTrigger | Trigger | `click` \| `dblclick` | `click` | +| darkHighlightMode | Enables a dark theme for hover highlights, improving visibility on dark backgrounds. | boolean | false | ## Events @@ -145,7 +146,7 @@ plugins: [ | Slot Name | Description | Parameters | | --------------- | ----------------- | ---------------------- | -| renderNodeKey | render node key | { node, defaultKey } | +| renderNodeKey | render node key | { node, defaultKey } | | renderNodeValue | render node value | { node, defaultValue } | ## Contributors From 4706ef9243c8f38302788b4753a05959a8faea5b Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 14:06:05 +0100 Subject: [PATCH 07/13] fix: fixed icon not changing on darkmode switch --- example/App.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example/App.tsx b/example/App.tsx index 70ed8dd..44b4f8b 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -75,11 +75,12 @@ export default defineComponent({ state, onActiveChange, toggleDarkMode, + globalDarkModeState, }; }, render() { - const { state, onActiveChange, toggleDarkMode } = this; + const { state, onActiveChange, toggleDarkMode, globalDarkModeState } = this; return (
@@ -103,7 +104,7 @@ export default defineComponent({ ))}
- {state.isDarkMode ? : } + {globalDarkModeState.isDarkMode ? : }
From 80e705ca8cc1f4d7bae9ba6a3d805f8603fec907 Mon Sep 17 00:00:00 2001 From: Daniel Oppermann Date: Thu, 1 Feb 2024 14:39:06 +0100 Subject: [PATCH 08/13] refactor: use theme prop instead of darkHighlightMode --- README.md | 52 ++++++++++++++--------------- example/Basic.vue | 9 +++-- example/Editable.vue | 9 +++-- example/SelectControl.vue | 9 +++-- example/VirtualList.vue | 9 +++-- example/useDarkMode.ts | 5 +-- src/components/Tree/index.tsx | 10 +++--- src/components/TreeNode/index.tsx | 8 ++--- src/components/TreeNode/styles.less | 2 +- 9 files changed, 63 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 5206c3f..37a6296 100644 --- a/README.md +++ b/README.md @@ -106,32 +106,32 @@ plugins: [ ## Props -| Property | Description | Type | Default | -| ------------------------ | ------------------------------------------------------------------------------------ | --------------------------------- | ------- | -| data(v-model) | JSON data, support v-model when use editable | JSON object | - | -| collapsedNodeLength | Objects or arrays which length is greater than this threshold will be collapsed | number | - | -| deep | Paths greater than this depth will be collapsed | number | - | -| showLength | Show the length when collapsed | boolean | false | -| showLine | Show the line | boolean | true | -| showLineNumber | Show the line number | boolean | false | -| showIcon | Show the icon | boolean | false | -| showDoubleQuotes | Show doublequotes on key | boolean | true | -| virtual | Use virtual scroll | boolean | false | -| height | The height of list when using virtual | number | 400 | -| itemHeight | The height of node when using virtual | number | 20 | -| selectedValue(v-model) | Selected data path | string, array | - | -| rootPath | Root data path | string | `root` | -| nodeSelectable | Defines whether a node supports selection | (node) => boolean | - | -| selectableType | Support path select, default none | `multiple` \| `single` | - | -| showSelectController | Show the select controller | boolean | false | -| selectOnClickNode | Trigger select when click node | boolean | true | -| highlightSelectedNode | Support highlighting selected nodes | boolean | true | -| collapsedOnClickBrackets | Support click brackets to collapse | boolean | true | -| renderNodeKey | render node key, or use slot #renderNodeKey | ({ node, defaultKey }) => vNode | - | -| renderNodeValue | render node value, or use slot #renderNodeValue | ({ node, defaultValue }) => vNode | - | -| editable | Support editable | boolean | false | -| editableTrigger | Trigger | `click` \| `dblclick` | `click` | -| darkHighlightMode | Enables a dark theme for hover highlights, improving visibility on dark backgrounds. | boolean | false | +| Property | Description | Type | Default | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------- | ------- | +| data(v-model) | JSON data, support v-model when use editable | JSON object | - | +| collapsedNodeLength | Objects or arrays which length is greater than this threshold will be collapsed | number | - | +| deep | Paths greater than this depth will be collapsed | number | - | +| showLength | Show the length when collapsed | boolean | false | +| showLine | Show the line | boolean | true | +| showLineNumber | Show the line number | boolean | false | +| showIcon | Show the icon | boolean | false | +| showDoubleQuotes | Show doublequotes on key | boolean | true | +| virtual | Use virtual scroll | boolean | false | +| height | The height of list when using virtual | number | 400 | +| itemHeight | The height of node when using virtual | number | 20 | +| selectedValue(v-model) | Selected data path | string, array | - | +| rootPath | Root data path | string | `root` | +| nodeSelectable | Defines whether a node supports selection | (node) => boolean | - | +| selectableType | Support path select, default none | `multiple` \| `single` | - | +| showSelectController | Show the select controller | boolean | false | +| selectOnClickNode | Trigger select when click node | boolean | true | +| highlightSelectedNode | Support highlighting selected nodes | boolean | true | +| collapsedOnClickBrackets | Support click brackets to collapse | boolean | true | +| renderNodeKey | render node key, or use slot #renderNodeKey | ({ node, defaultKey }) => vNode | - | +| renderNodeValue | render node value, or use slot #renderNodeValue | ({ node, defaultValue }) => vNode | - | +| editable | Support editable | boolean | false | +| editableTrigger | Trigger | `click` \| `dblclick` | `click` | +| theme | Sets the theme of the component. Options are 'light' or 'dark', with dark mode enhancing visibility on dark backgrounds | `'light' \| 'dark'` | `light` | ## Events diff --git a/example/Basic.vue b/example/Basic.vue index 4ecff08..64df3a1 100644 --- a/example/Basic.vue +++ b/example/Basic.vue @@ -47,8 +47,11 @@
- - + +
@@ -67,7 +70,7 @@

vue-json-pretty:

- - + +

vue-json-pretty:

- - + +

v-model:selectedValue:

@@ -72,7 +75,7 @@
- - + +

vue-json-pretty(10000+ items):

darkModeState.isDarkMode, newVal => { - localDarkMode.value = newVal; + localDarkMode.value = newVal ? 'dark' : 'light'; globalDarkModeState.value = newVal; }, ); const toggleLocalDarkMode = () => { darkModeState.isDarkMode = !darkModeState.isDarkMode; + localDarkMode.value = darkModeState.isDarkMode ? 'dark' : 'light'; }; return { localDarkMode, toggleLocalDarkMode, globalDarkModeState }; diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index dfd5e2e..6957e8a 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -70,9 +70,9 @@ export default defineComponent({ onSelectedChange: { type: Function as PropType<(newVal: string | string[], oldVal: string | string[]) => void>, }, - darkHighlightMode: { - type: Boolean, - default: false, + theme: { + type: String as PropType<'light' | 'dark'>, + default: 'light', }, }, @@ -285,7 +285,7 @@ export default defineComponent({ key={item.id} node={item} collapsed={!!state.hiddenPaths[item.path]} - darkHighlightMode={props.darkHighlightMode} + theme={props.theme} showDoubleQuotes={props.showDoubleQuotes} showLength={props.showLength} checked={selectedPaths.value.includes(item.path)} @@ -321,7 +321,7 @@ export default defineComponent({ class={{ 'vjs-tree': true, 'is-virtual': props.virtual, - 'dark-highlight-mode': props.darkHighlightMode, + dark: props.theme === 'dark', }} onScroll={props.virtual ? handleTreeScroll : undefined} style={ diff --git a/src/components/TreeNode/index.tsx b/src/components/TreeNode/index.tsx index 9a1bcd7..6c6a27c 100644 --- a/src/components/TreeNode/index.tsx +++ b/src/components/TreeNode/index.tsx @@ -64,9 +64,9 @@ export const treeNodePropsPass = { type: Boolean, default: false, }, - darkHighlightMode: { - type: Boolean, - default: false, + theme: { + type: String as PropType<'light' | 'dark'>, + default: 'light', }, showKeyValueSpace: { type: Boolean, @@ -217,7 +217,7 @@ export default defineComponent({ 'has-selector': props.showSelectController, 'has-carets': props.showIcon, 'is-highlight': props.highlightSelectedNode && props.checked, - 'dark-highlight-mode': props.darkHighlightMode, + dark: props.theme === 'dark', }} onClick={handleNodeClick} style={props.style} diff --git a/src/components/TreeNode/styles.less b/src/components/TreeNode/styles.less index 62e06e2..57292f9 100644 --- a/src/components/TreeNode/styles.less +++ b/src/components/TreeNode/styles.less @@ -36,7 +36,7 @@ } } - &.dark-highlight-mode { + &.dark { &.is-highlight, &:hover { background-color: @highlight-bg-color-dark; From 304aa6a4fc4cc6d8679fef371ca2b756df7cc949 Mon Sep 17 00:00:00 2001 From: Noah Kreiger <32901937+nkreiger@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:17:06 -0500 Subject: [PATCH 09/13] Add Path to emit Receiving true or false doesn't provide much context to which bracket got collapsed in order to build smarter functionality and optimizations based on the collapsed field. --- src/components/Tree/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index 6957e8a..1c16422 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -233,12 +233,12 @@ export default defineComponent({ if (props.collapsedOnClickBrackets) { updateCollapsedPaths(collapsed, path); } - emit('bracketsClick', collapsed); + emit('bracketsClick', collapsed, path); }; const handleIconClick = (collapsed: boolean, path: string) => { updateCollapsedPaths(collapsed, path); - emit('iconClick', collapsed); + emit('iconClick', collapsed, path); }; const handleValueChange = (value: unknown, path: string) => { From e37573a0acae353c9719c2dbf1b16f2e27841cb6 Mon Sep 17 00:00:00 2001 From: leezng Date: Mon, 4 Mar 2024 22:42:49 +0800 Subject: [PATCH 10/13] feat: support esm --- build/webpack.prod.conf.js | 31 +++++++++++++++++++++++-------- package.json | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 6067ec2..b1c81ff 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -10,8 +10,9 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin'); +const isEsm = process.env.ESM; const isExampleEnv = process.env.EXAMPLE_ENV; -const distPath = '../lib'; +const distPath = isEsm ? '../esm' : '../lib'; const env = process.env.NODE_ENV === 'testing' ? require('../config/test.env') : config.build.env; @@ -32,21 +33,35 @@ const webpackConfig = merge(baseWebpackConfig, { }); if (!isExampleEnv) { - webpackConfig.entry = { - 'vue-json-pretty': './src/index.ts', - }; webpackConfig.output = { path: path.resolve(__dirname, distPath), filename: `${distPath}/[name].js`, - globalObject: 'this', - library: 'VueJsonPretty', - libraryTarget: 'umd', }; + if (isEsm) { + webpackConfig.entry = { + 'vue-json-pretty': './src/index.ts', + }; + webpackConfig.experiments = { + outputModule: true, + }; + webpackConfig.output.library = { type: 'module' }; + webpackConfig.output.chunkFormat = 'module'; + webpackConfig.target = 'es2019'; + } else { + webpackConfig.entry = { + 'vue-json-pretty': './src/index.ts', + }; + webpackConfig.output.globalObject = 'this'; + webpackConfig.output.library = 'VueJsonPretty'; + webpackConfig.output.libraryTarget = 'umd'; + } webpackConfig.externals = { vue: { root: 'Vue', - commonjs: 'vue', commonjs2: 'vue', + commonjs: 'vue', + amd: 'vue', + module: 'vue', }, }; webpackConfig.plugins.push( diff --git a/package.json b/package.json index 3336725..7b35be8 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "description": "A JSON tree view component that is easy to use and also supports data selection.", "author": "leezng ", "main": "lib/vue-json-pretty.js", + "module": "esm/vue-json-pretty.js", "scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js", + "build:esm": "cross-env ESM=true node build/build.js", "build:example": "cross-env EXAMPLE_ENV=true node build/build.js", "build:dts": "tsc --p tsconfig.dts.json && tsc-alias -p ./tsconfig.dts.json", "test": "cypress open", From a32896a73b58c0e9d7cf69f962233e3903a4fc3d Mon Sep 17 00:00:00 2001 From: leezng Date: Mon, 4 Mar 2024 22:44:27 +0800 Subject: [PATCH 11/13] feat: use spawn to build esm --- .gitignore | 1 + build/build.js | 8 ++++++++ package.json | 1 + 3 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index 95eb21b..1a56824 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules/ example-dist/ dist/ lib/ +esm/ types/ npm-debug.log* yarn-debug.log* diff --git a/build/build.js b/build/build.js index ba556e8..804aad3 100644 --- a/build/build.js +++ b/build/build.js @@ -39,6 +39,14 @@ webpack(webpackConfig, (err, stats) => { ), ); } else { + const buildEsmProcess = spawn('npm', ['run', 'build:esm'], { + stdio: 'inherit', + }); + + buildEsmProcess.on('close', () => { + console.log(chalk.cyan('Build esm complete.\n')); + }); + const buildTypesProcess = spawn('npm', ['run', 'build:dts'], { stdio: 'inherit', }); diff --git a/package.json b/package.json index 7b35be8..8325362 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ ], "files": [ "lib", + "esm", "types" ], "peerDependencies": { From d35d855a0c6d030112af4427d049e53933a57aa8 Mon Sep 17 00:00:00 2001 From: leezng Date: Wed, 13 Mar 2024 22:00:44 +0800 Subject: [PATCH 12/13] feat: support esm --- build/build.js | 29 +++++++++-------------------- package.json | 3 ++- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/build/build.js b/build/build.js index 804aad3..3fee5fa 100644 --- a/build/build.js +++ b/build/build.js @@ -2,15 +2,19 @@ require('./check-versions')(); process.env.NODE_ENV = 'production'; -const fs = require('fs'); -const path = require('path'); const chalk = require('chalk'); const webpack = require('webpack'); -const { spawn } = require('child_process'); const webpackConfig = require('./webpack.prod.conf'); +const isEsm = process.env.ESM; const isExampleEnv = process.env.EXAMPLE_ENV; +const successText = { + main: 'Build main sources complete.', + esm: 'Build esm sources complete.', + example: 'Build example page complete.', +}; + webpack(webpackConfig, (err, stats) => { if (err) throw err; @@ -29,7 +33,8 @@ webpack(webpackConfig, (err, stats) => { process.exit(1); } - console.log(chalk.cyan('Build sources complete.\n')); + const text = isExampleEnv ? successText.example : isEsm ? successText.esm : successText.main; + console.log(chalk.cyan(`${text}\n`)); if (isExampleEnv) { console.log( @@ -38,21 +43,5 @@ webpack(webpackConfig, (err, stats) => { "Opening index.html over file:// won't work.\n", ), ); - } else { - const buildEsmProcess = spawn('npm', ['run', 'build:esm'], { - stdio: 'inherit', - }); - - buildEsmProcess.on('close', () => { - console.log(chalk.cyan('Build esm complete.\n')); - }); - - const buildTypesProcess = spawn('npm', ['run', 'build:dts'], { - stdio: 'inherit', - }); - - buildTypesProcess.on('close', () => { - console.log(chalk.cyan('Build types(.d.ts) complete.\n')); - }); } }); diff --git a/package.json b/package.json index 8325362..e04cd07 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "module": "esm/vue-json-pretty.js", "scripts": { "dev": "node build/dev-server.js", - "build": "node build/build.js", + "build": "npm run build:main && npm run build:esm && npm run build:dts", + "build:main": "node build/build.js", "build:esm": "cross-env ESM=true node build/build.js", "build:example": "cross-env EXAMPLE_ENV=true node build/build.js", "build:dts": "tsc --p tsconfig.dts.json && tsc-alias -p ./tsconfig.dts.json", From 17ce791f5e2eb84522fe6e53c251d5b53946639f Mon Sep 17 00:00:00 2001 From: leezng Date: Mon, 18 Mar 2024 19:08:02 +0800 Subject: [PATCH 13/13] fix: add node data on bracketsClick & iconClick --- README.md | 12 ++++++------ README.zh_CN.md | 19 ++++++++++--------- example/Icons.tsx | 12 ++++++------ src/components/Tree/index.tsx | 12 ++++++------ src/components/TreeNode/index.tsx | 8 ++++---- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 37a6296..c6bfa9e 100644 --- a/README.md +++ b/README.md @@ -135,12 +135,12 @@ plugins: [ ## Events -| Event Name | Description | Parameters | -| -------------- | ---------------------------------------- | -------------------- | -| nodeClick | triggers when click node | (node: NodeData) | -| bracketsClick | triggers when click brackets | (collapsed: boolean) | -| iconClick | triggers when click icon | (collapsed: boolean) | -| selectedChange | triggers when the selected value changed | (newVal, oldVal) | +| Event Name | Description | Parameters | +| -------------- | ---------------------------------------- | ------------------------------------ | +| nodeClick | triggers when click node | (node: NodeData) | +| bracketsClick | triggers when click brackets | (collapsed: boolean, node: NodeData) | +| iconClick | triggers when click icon | (collapsed: boolean, node: NodeData) | +| selectedChange | triggers when the selected value changed | (newVal, oldVal) | ## Slots diff --git a/README.zh_CN.md b/README.zh_CN.md index 8f40943..65247b5 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -13,7 +13,7 @@ | 属性 | 说明 | 类型 | 默认值 | | ------------------------ | ------------------------------------------- | --------------------------------- | ------------- | | data(v-model) | 源数据,注意不是 `JSON` 字符串 | `JSON` 数据对象 | - | -| collapsedNodeLength | 长度大于此阈值的对象或数组将被折叠 | number | Infinity | +| collapsedNodeLength | 长度大于此阈值的对象或数组将被折叠 | number | Infinity | | deep | 深度,大于该深度的节点将被折叠 | number | Infinity | | showLength | 在数据折叠的时候展示长度 | boolean | false | | showLine | 展示标识线 | boolean | true | @@ -31,23 +31,24 @@ | selectOnClickNode | 支持点击节点的时候触发选择 | boolean | true | | highlightSelectedNode | 支持高亮已选择节点 | boolean | true | | collapsedOnClickBrackets | 支持点击括号折叠 | boolean | true | -| renderNodeKey | 渲染节点键,也可使用 #renderNodeKey | ({ node, defaultKey }) => vNode | - | +| renderNodeKey | 渲染节点键,也可使用 #renderNodeKey | ({ node, defaultKey }) => vNode | - | | renderNodeValue | 自定义渲染节点值,也可使用 #renderNodeValue | ({ node, defaultValue }) => vNode | - | | editable | 支持可编辑 | boolean | false | | editableTrigger | 触发编辑的时机 | `click` \| `dblclick` | `click` | +| theme | 主题色 | `'light' \| 'dark'` | `light` | ## Events -| 事件名称 | 说明 | 回调参数 | -| -------------- | -------------------- | -------------------- | -| nodeClick | 点击节点时触发 | (node: NodeData) | -| bracketsClick | 点击括号时触发 | (collapsed: boolean) | -| iconClick | 点击图标时触发 | (collapsed: boolean) | -| selectedChange | 选中值发生变化时触发 | (newVal, oldVal) | +| 事件名称 | 说明 | 回调参数 | +| -------------- | -------------------- | ------------------------------------ | +| nodeClick | 点击节点时触发 | (node: NodeData) | +| bracketsClick | 点击括号时触发 | (collapsed: boolean, node: NodeData) | +| iconClick | 点击图标时触发 | (collapsed: boolean, node: NodeData) | +| selectedChange | 选中值发生变化时触发 | (newVal, oldVal) | ## Slots | 插槽名 | 描述 | 参数 | | --------------- | ---------- | ---------------------- | -| renderNodeKey | 渲染节点键 | { node, defaultKey } | +| renderNodeKey | 渲染节点键 | { node, defaultKey } | | renderNodeValue | 渲染节点值 | { node, defaultValue } | diff --git a/example/Icons.tsx b/example/Icons.tsx index fcf1407..01ab657 100644 --- a/example/Icons.tsx +++ b/example/Icons.tsx @@ -6,9 +6,9 @@ export const SunIcon = () => ( viewBox="0 0 24 24" fill="none" stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" + stroke-width="2" + stroke-linecap="round" + stroke-linejoin="round" > @@ -30,9 +30,9 @@ export const MoonIcon = () => ( viewBox="0 0 24 24" fill="none" stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" + stroke-width="2" + stroke-linecap="round" + stroke-linejoin="round" > diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index 1c16422..aebc40c 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -229,16 +229,16 @@ export default defineComponent({ } }; - const handleBracketsClick = (collapsed: boolean, path: string) => { + const handleBracketsClick = (collapsed: boolean, node: NodeDataType) => { if (props.collapsedOnClickBrackets) { - updateCollapsedPaths(collapsed, path); + updateCollapsedPaths(collapsed, node.path); } - emit('bracketsClick', collapsed, path); + emit('bracketsClick', collapsed, node); }; - const handleIconClick = (collapsed: boolean, path: string) => { - updateCollapsedPaths(collapsed, path); - emit('iconClick', collapsed, path); + const handleIconClick = (collapsed: boolean, node: NodeDataType) => { + updateCollapsedPaths(collapsed, node.path); + emit('iconClick', collapsed, node); }; const handleValueChange = (value: unknown, path: string) => { diff --git a/src/components/TreeNode/index.tsx b/src/components/TreeNode/index.tsx index 6c6a27c..c955dd5 100644 --- a/src/components/TreeNode/index.tsx +++ b/src/components/TreeNode/index.tsx @@ -84,10 +84,10 @@ export const treeNodePropsPass = { type: Function as PropType<(node: NodeDataType) => void>, }, onBracketsClick: { - type: Function as PropType<(collapsed: boolean, path: string) => void>, + type: Function as PropType<(collapsed: boolean, node: NodeDataType) => void>, }, onIconClick: { - type: Function as PropType<(collapsed: boolean, path: string) => void>, + type: Function as PropType<(collapsed: boolean, node: NodeDataType) => void>, }, onValueChange: { type: Function as PropType<(value: boolean, path: string) => void>, @@ -171,11 +171,11 @@ export default defineComponent({ }; const handleBracketsClick = () => { - emit('bracketsClick', !props.collapsed, props.node.path); + emit('bracketsClick', !props.collapsed, props.node); }; const handleIconClick = () => { - emit('iconClick', !props.collapsed, props.node.path); + emit('iconClick', !props.collapsed, props.node); }; const handleSelectedChange = () => {