-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #479 from madeindjs/jsonViewver
feat: implement `JsonViewer` widget
- Loading branch information
Showing
10 changed files
with
541 additions
and
1 deletion.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<template> | ||
<details | ||
class="collapsible" | ||
:open="open" | ||
:disabled="disabled" | ||
@toggle="onToggle" | ||
> | ||
<summary><slot name="title" /></summary> | ||
<div class="content"> | ||
<slot name="content" /> | ||
</div> | ||
</details> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
defineProps({ | ||
open: { type: Boolean, required: false }, | ||
disabled: { type: Boolean, required: false }, | ||
}); | ||
const emit = defineEmits({ | ||
toggle: (open: boolean) => typeof open === "boolean", | ||
}); | ||
function onToggle(event) { | ||
emit("toggle", event.newState === "open"); | ||
} | ||
</script> | ||
|
||
<style scoped> | ||
/* customize the triangle and animate it */ | ||
details { | ||
box-sizing: border-box; | ||
} | ||
details summary::-webkit-details-marker { | ||
display: none; | ||
} | ||
details[open] > summary:before { | ||
transform: rotate(90deg); | ||
} | ||
summary { | ||
outline: none; | ||
display: flex; | ||
padding-left: 16px; | ||
position: relative; | ||
cursor: pointer; | ||
} | ||
summary:before { | ||
content: ""; | ||
border-width: 6px; | ||
border-style: solid; | ||
border-color: transparent transparent transparent var(--accentColor); | ||
position: absolute; | ||
left: 4px; | ||
top: 2px; | ||
transform: rotate(0); | ||
transform-origin: 3px 50%; | ||
transition: 0.3s transform ease; | ||
} | ||
summary:focus-visible:before { | ||
border-color: transparent transparent transparent var(--primaryTextColor); | ||
} | ||
@media (prefers-reduced-motion) { | ||
summary:before { | ||
transition: unset; | ||
} | ||
} | ||
/* small animation on the content */ | ||
details[open] summary ~ .content { | ||
animation: sweep 0.2s ease-in-out; | ||
} | ||
@media (prefers-reduced-motion) { | ||
details[open] summary ~ .content { | ||
animation: unset; | ||
} | ||
} | ||
@keyframes sweep { | ||
0% { | ||
opacity: 0; | ||
margin-top: -12px; | ||
} | ||
100% { | ||
opacity: 1; | ||
margin-top: 0; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import type { | ||
JsonData, | ||
JsonValue, | ||
JsonViewerTogglePayload, | ||
} from "./BaseJsonViewer.vue"; | ||
|
||
export function isJSONValue(data: JsonData): data is JsonValue { | ||
if (["string", "number", "boolean"].includes(typeof data)) return true; | ||
if (data === null) return true; | ||
return false; | ||
} | ||
|
||
export function isJSONArray(data: JsonData): data is JsonData[] { | ||
if (isJSONValue(data)) return false; | ||
return Array.isArray(data); | ||
} | ||
|
||
export function isJSONObject( | ||
data: JsonData, | ||
): data is { [x: string]: JsonData } { | ||
return !isJSONArray(data) && typeof data === "object" && data !== null; | ||
} | ||
|
||
export function getJSONLength(data: JsonData): number { | ||
return isJSONValue(data) ? 1 : Object.keys(data).length; | ||
} | ||
|
||
export function jsonViewerToggleEmitDefinition( | ||
payload: JsonViewerTogglePayload, | ||
) { | ||
return typeof payload.open === "boolean" && Array.isArray(payload.path); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<template> | ||
<template v-if="isJSONObject(data) || isJSONArray(data)"> | ||
<BaseJsonViewerCollapsible | ||
v-if="isRoot" | ||
:open="isRootOpen" | ||
:data="data" | ||
@toggle="$emit('toggle', { path: [], open: $event })" | ||
> | ||
<BaseJsonViewerObject | ||
:data="data" | ||
:path="path" | ||
:initial-depth="initialDepth" | ||
@toggle="$emit('toggle', $event)" | ||
/> | ||
</BaseJsonViewerCollapsible> | ||
<BaseJsonViewerObject | ||
v-else | ||
:data="data" | ||
:path="path" | ||
:initial-depth="initialDepth" | ||
@toggle="$emit('toggle', $event)" | ||
/> | ||
</template> | ||
<BaseJsonViewerValue v-else-if="isJSONValue(data)" :data="data" /> | ||
</template> | ||
|
||
<script lang="ts"> | ||
export type JsonValue = string | number | boolean | null; | ||
export type JsonData = JsonValue | { [x: string]: JsonData } | JsonData[]; | ||
export type JsonPath = string[]; | ||
export type JsonViewerTogglePayload = { path: JsonPath; open: boolean }; | ||
</script> | ||
|
||
<script setup lang="ts"> | ||
/** | ||
* This component will detect the shape of the JSON and redirect the right dedicated component. | ||
*/ | ||
import { PropType, computed } from "vue"; | ||
import { | ||
isJSONArray, | ||
isJSONObject, | ||
isJSONValue, | ||
jsonViewerToggleEmitDefinition, | ||
} from "./BaseJsonViewer.utils"; | ||
import BaseJsonViewerCollapsible from "./BaseJsonViewerCollapsible.vue"; | ||
import BaseJsonViewerObject from "./BaseJsonViewerObject.vue"; | ||
import BaseJsonViewerValue from "./BaseJsonViewerValue.vue"; | ||
const props = defineProps({ | ||
data: { | ||
type: Object as PropType<JsonData>, | ||
required: true, | ||
}, | ||
path: { | ||
type: Array as PropType<JsonPath>, | ||
default: () => [], | ||
}, | ||
initialDepth: { type: Number, default: 0 }, | ||
}); | ||
defineEmits({ | ||
toggle: jsonViewerToggleEmitDefinition, | ||
}); | ||
const isRoot = computed(() => props.path.length === 0); | ||
const isRootOpen = computed( | ||
() => props.initialDepth === -1 || props.initialDepth > 0, | ||
); | ||
</script> |
40 changes: 40 additions & 0 deletions
40
src/ui/src/core_components/base/BaseJsonViewerChildrenCounter.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<template> | ||
<span>{{ text }}</span> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { PropType, computed } from "vue"; | ||
import { | ||
getJSONLength, | ||
isJSONArray, | ||
isJSONObject, | ||
} from "./BaseJsonViewer.utils"; | ||
import type { JsonData } from "./BaseJsonViewer.vue"; | ||
const props = defineProps({ | ||
data: { | ||
type: Object as PropType<JsonData>, | ||
required: true, | ||
}, | ||
}); | ||
const printObject = (length: number) => `Object{${length}}`; | ||
const printArray = (length: number) => `Array[${length}]`; | ||
const text = computed(() => { | ||
const count = getJSONLength(props.data); | ||
if (count === 0) return printObject(0); | ||
if (isJSONArray(props.data)) return printArray(count); | ||
if (isJSONObject(props.data)) return printObject(count); | ||
return printObject(0); | ||
}); | ||
</script> | ||
|
||
<style scoped> | ||
span { | ||
color: var(--secondaryTextColor); | ||
font-family: monospace; | ||
font-size: 12px; | ||
} | ||
</style> |
58 changes: 58 additions & 0 deletions
58
src/ui/src/core_components/base/BaseJsonViewerCollapsible.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<template> | ||
<BaseCollapsible | ||
:open="open" | ||
:disabled="disabled" | ||
@toggle="$emit('toggle', $event)" | ||
> | ||
<template #title> | ||
<div class="BaseJsonViewerCollapsible__title"> | ||
<span v-if="title">{{ title }}</span> | ||
<BaseJsonViewerChildrenCounter v-if="data" :data="data" /> | ||
</div> | ||
</template> | ||
<template #content> | ||
<div class="BaseJsonViewerCollapsible__content"> | ||
<slot /> | ||
</div> | ||
</template> | ||
</BaseCollapsible> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import type { PropType } from "vue"; | ||
import BaseCollapsible from "./BaseCollapsible.vue"; | ||
import type { JsonData } from "./BaseJsonViewer.vue"; | ||
import BaseJsonViewerChildrenCounter from "./BaseJsonViewerChildrenCounter.vue"; | ||
defineProps({ | ||
open: { type: Boolean, required: false }, | ||
disabled: { type: Boolean, required: false }, | ||
title: { type: String, required: false, default: undefined }, | ||
data: { | ||
type: [Object, Array] as PropType<JsonData>, | ||
required: false, | ||
default: undefined, | ||
}, | ||
}); | ||
defineEmits({ | ||
toggle: (open: boolean) => typeof open === "boolean", | ||
}); | ||
</script> | ||
|
||
<style scoped> | ||
.BaseJsonViewerCollapsible__title { | ||
font-family: monospace; | ||
font-size: 12px; | ||
display: flex; | ||
gap: 8px; | ||
} | ||
.BaseJsonViewerCollapsible__content { | ||
margin-left: 7px; | ||
padding-left: var(--jsonViewerIndentationSpacing, 8px); | ||
padding-top: 4px; | ||
padding-bottom: 4px; | ||
border-left: 1px solid var(--separatorColor); | ||
} | ||
</style> |
Oops, something went wrong.