Skip to content

Commit 00729a3

Browse files
keithamuskoddsson
andcommitted
initial implementation
Co-authored-by: Kristján Oddsson <koddsson@gmail.com>
1 parent 8505914 commit 00729a3

File tree

4 files changed

+182
-32
lines changed

4 files changed

+182
-32
lines changed

examples/index.html

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,34 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
5-
<title>custom-element demo</title>
5+
<title>tree-view demo</title>
66
</head>
77
<body>
8-
<custom-element></custom-element>
8+
<json-view>
9+
<script type="application/json">
10+
{
11+
"a": {
12+
"b": {
13+
"c": [1,2,3]
14+
},
15+
"d": {
16+
"e": [4,5,6]
17+
},
18+
"g": [
19+
{"one": 1},
20+
{"two": 2}
21+
],
22+
"f": "hello world",
23+
"null": null,
24+
"bool": true
25+
}
26+
}
27+
</script>
28+
</json-view>
929

1030
<script type="module">
11-
// import 'https://unpkg.com/@github/custom-element-boilerplate@latest/dist/custom-element.js'
12-
import '../src/custom-element.ts'
31+
// import 'https://unpkg.com/@github/tree-view-boilerplate@latest/dist/tree-view.js'
32+
import '../src/json-view-element.ts'
1333
</script>
1434
</body>
1535
</html>

src/custom-element.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/json-view-element.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const css = String.raw
2+
const stylesheet = new CSSStyleSheet()
3+
stylesheet.replaceSync(css`
4+
:host {
5+
font-family: monospace;
6+
}
7+
details > * {
8+
padding-inline-start: 0.5rem;
9+
}
10+
details > details {
11+
padding-inline-start: 1rem;
12+
}
13+
details > summary {
14+
padding-inline-start: 0;
15+
display: inline-block;
16+
}
17+
summary::before {
18+
content: '+ '
19+
}
20+
details[open] > summary::before {
21+
content: '- '
22+
}
23+
details[open] > summary::after {
24+
content: ':'
25+
}
26+
.boolean {
27+
color: orange;
28+
}
29+
.string {
30+
color: red;
31+
}
32+
.number {
33+
color: hotpink;
34+
}
35+
.null {
36+
color: grey;
37+
}
38+
`)
39+
40+
/**
41+
* An example Custom Element. This documentation ends up in the
42+
* README so describe how this elements works here.
43+
*
44+
* You can event add examples on the element is used with Markdown.
45+
*
46+
* ```
47+
* <tree-view></tree-view>
48+
* ```
49+
*/
50+
class JSONViewElement extends HTMLElement {
51+
#renderRoot!: ShadowRoot
52+
53+
get json() {
54+
return JSON.parse(this.querySelector('script[type="application/json"]')?.textContent)
55+
}
56+
57+
get expanded() {
58+
return this.hasAttribute('expanded')
59+
}
60+
61+
connectedCallback(): void {
62+
this.#renderRoot = this.attachShadow({mode: 'open'})
63+
this.#renderRoot.append(this.#buildHTML())
64+
this.#renderRoot.adoptedStyleSheets = [stylesheet]
65+
}
66+
67+
#buildHTML(): Node {
68+
const fragment = document.createDocumentFragment()
69+
try {
70+
for(const [key, value] of Object.entries(this.json)) {
71+
fragment.append(this.#buildNode(key, value))
72+
}
73+
} catch (error) {
74+
const span = document.createElement('span')
75+
span.classList.add('error')
76+
span.textContent = error.message
77+
return span
78+
}
79+
return fragment
80+
}
81+
82+
#buildNode(key: string, value: unknown): Node {
83+
const details = document.createElement('details')
84+
details.open = this.expanded
85+
const summary = document.createElement('summary')
86+
summary.textContent = key
87+
details.append(summary)
88+
if (value && Array.isArray(value)) {
89+
const span = document.createElement('span')
90+
span.textContent = '[]'
91+
details.append(span)
92+
for (const item of Object.entries(value)) {
93+
details.append(this.#buildNode(...item))
94+
}
95+
} else if (value && typeof value === 'object') {
96+
const span = document.createElement('span')
97+
span.textContent = '{}'
98+
details.append(span)
99+
for (const item of Object.entries(value)) {
100+
details.append(this.#buildNode(...item))
101+
}
102+
} else if (typeof value === 'string') {
103+
const span = document.createElement('span')
104+
span.classList.add(typeof value)
105+
span.textContent = `"${value}"`
106+
details.append(span)
107+
details.open = true
108+
} else if (!value || typeof value === 'boolean' || typeof value === 'number') {
109+
const span = document.createElement('span')
110+
span.classList.add(value === null ? 'null' : typeof value)
111+
span.textContent = `${value}`
112+
details.append(span)
113+
details.open = true
114+
}
115+
return details
116+
}
117+
}
118+
119+
declare global {
120+
interface Window {
121+
JSONViewElement: typeof JSONViewElement
122+
}
123+
}
124+
125+
export default JSONViewElement
126+
127+
if (!window.customElements.get('json-view')) {
128+
window.JSONViewElement = JSONViewElement
129+
window.customElements.define('json-view', JSONViewElement)
130+
}

src/tree-view-element.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* An example Custom Element. This documentation ends up in the
3+
* README so describe how this elements works here.
4+
*
5+
* You can event add examples on the element is used with Markdown.
6+
*
7+
* ```
8+
* <tree-view></tree-view>
9+
* ```
10+
*/
11+
class TreeViewElement extends HTMLElement {
12+
connectedCallback(): void {
13+
14+
}
15+
}
16+
17+
declare global {
18+
interface Window {
19+
TreeViewElement: typeof TreeViewElement
20+
}
21+
}
22+
23+
export default TreeViewElement
24+
25+
if (!window.customElements.get('tree-view')) {
26+
window.TreeViewElement = TreeViewElement
27+
window.customElements.define('tree-view', TreeViewElement)
28+
}

0 commit comments

Comments
 (0)