@humanspeak/svelte-json-view-lite 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/DataRender.svelte +7 -14
- package/dist/ExpandableObject.svelte +43 -6
- package/dist/types.d.ts +3 -1
- package/package.json +20 -20
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ runtime dependencies.
|
|
|
7
7
|
|
|
8
8
|
[](https://www.npmjs.com/package/@humanspeak/svelte-json-view-lite)
|
|
9
9
|
[](https://github.com/humanspeak/svelte-json-view-lite/actions/workflows/npm-publish.yml)
|
|
10
|
+
[](https://tokenmaxing.app/card/humanspeak/svelte-json-view-lite)
|
|
10
11
|
[](https://coveralls.io/github/humanspeak/svelte-json-view-lite?branch=main)
|
|
11
12
|
[](https://github.com/humanspeak/svelte-json-view-lite/blob/main/LICENSE)
|
|
12
13
|
[](https://www.npmjs.com/package/@humanspeak/svelte-json-view-lite)
|
|
@@ -252,11 +253,31 @@ pnpm test # vitest + coverage
|
|
|
252
253
|
pnpm build # vite build + svelte-package + publint
|
|
253
254
|
```
|
|
254
255
|
|
|
256
|
+
<!-- docs-kit:ecosystem start -->
|
|
257
|
+
|
|
258
|
+
## Svelte 5 ecosystem
|
|
259
|
+
|
|
260
|
+
Part of the [Humanspeak](https://humanspeak.com) family of runes-native Svelte 5 packages:
|
|
261
|
+
|
|
262
|
+
| Package | Description |
|
|
263
|
+
| --- | --- |
|
|
264
|
+
| [@humanspeak/svelte-markdown](https://markdown.svelte.page) | Runtime markdown renderer for Svelte |
|
|
265
|
+
| [@humanspeak/svelte-virtual-list](https://virtuallist.svelte.page) | Virtual scrolling for Svelte |
|
|
266
|
+
| [@humanspeak/svelte-motion](https://motion.svelte.page) | Framer Motion for Svelte 5 |
|
|
267
|
+
| [@humanspeak/svelte-headless-table](https://table.svelte.page) | Headless data tables for Svelte |
|
|
268
|
+
| [@humanspeak/svelte-diff-match-patch](https://diff.svelte.page) | Diff comparison for Svelte |
|
|
269
|
+
| [@humanspeak/svelte-purify](https://purify.svelte.page) | HTML sanitisation for Svelte |
|
|
270
|
+
| [@humanspeak/svelte-virtual-chat](https://virtualchat.svelte.page) | Virtual chat viewport for Svelte 5 |
|
|
271
|
+
| [@humanspeak/memory-cache](https://memory.svelte.page) | In-memory cache for TypeScript |
|
|
272
|
+
| **[@humanspeak/svelte-json-view-lite](https://jsonview.svelte.page)** — _this package_ | JSON tree viewer for Svelte 5 |
|
|
273
|
+
| [@humanspeak/svelte-scoped-props](https://scoped.svelte.page) | Scoped class props for Svelte |
|
|
274
|
+
|
|
255
275
|
## License
|
|
256
276
|
|
|
257
|
-
MIT © [Humanspeak, Inc.](LICENSE)
|
|
258
|
-
© 2024 AnyRoad.
|
|
277
|
+
MIT © [Humanspeak, Inc.](LICENSE)
|
|
259
278
|
|
|
260
279
|
## Credits
|
|
261
280
|
|
|
262
281
|
Made with ❤️ by [Humanspeak](https://humanspeak.com)
|
|
282
|
+
|
|
283
|
+
<!-- docs-kit:ecosystem end -->
|
package/dist/DataRender.svelte
CHANGED
|
@@ -16,25 +16,18 @@
|
|
|
16
16
|
const isArr = $derived(isArray(value))
|
|
17
17
|
const isObj = $derived(isObject(value) && !isDate(value) && !isFunction(value))
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// a
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
if (isArr) return (value as unknown[]).map((el) => [undefined, el])
|
|
25
|
-
if (isObj) {
|
|
26
|
-
const obj = value as Record<string, unknown>
|
|
27
|
-
return Object.keys(obj).map((k) => [k, obj[k]])
|
|
28
|
-
}
|
|
29
|
-
return []
|
|
30
|
-
})
|
|
19
|
+
// The children tuple array is NOT built here: a collapsed node only needs
|
|
20
|
+
// its child *count*, and materializing `Array<[key, value]>` for every
|
|
21
|
+
// node (a 100k-element array = 100k throwaway tuples at mount under
|
|
22
|
+
// collapseAllNested) is pure waste. ExpandableObject derives the tuples
|
|
23
|
+
// lazily, gated on its own `expanded` state. See issue #21.
|
|
31
24
|
</script>
|
|
32
25
|
|
|
33
26
|
{#if isArr}
|
|
34
27
|
<ExpandableObject
|
|
35
28
|
{...props}
|
|
36
29
|
value={value as unknown[]}
|
|
37
|
-
|
|
30
|
+
isArray={true}
|
|
38
31
|
openBracket="["
|
|
39
32
|
closeBracket="]"
|
|
40
33
|
/>
|
|
@@ -42,7 +35,7 @@
|
|
|
42
35
|
<ExpandableObject
|
|
43
36
|
{...props}
|
|
44
37
|
value={value as object}
|
|
45
|
-
|
|
38
|
+
isArray={false}
|
|
46
39
|
openBracket={OBJECT_OPEN}
|
|
47
40
|
closeBracket={OBJECT_CLOSE}
|
|
48
41
|
/>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { untrack } from 'svelte'
|
|
2
3
|
import DataRender from './DataRender.svelte'
|
|
3
4
|
import EmptyObject from './EmptyObject.svelte'
|
|
4
5
|
import type { AriaLabels, ExpandableRenderProps } from './types.js'
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
const {
|
|
8
9
|
field,
|
|
9
10
|
value,
|
|
10
|
-
|
|
11
|
+
isArray,
|
|
11
12
|
lastElement,
|
|
12
13
|
openBracket,
|
|
13
14
|
closeBracket,
|
|
@@ -26,15 +27,32 @@
|
|
|
26
27
|
// svelte-ignore state_referenced_locally
|
|
27
28
|
let expanded = $state(shouldExpandNode(level, value, field))
|
|
28
29
|
|
|
30
|
+
// Once a node has been opened we keep its children materialized, even after
|
|
31
|
+
// it collapses again: re-deriving the tuple array (and re-reading N child
|
|
32
|
+
// values) on every re-expand would be its own waste. Latches true, stays true.
|
|
33
|
+
// svelte-ignore state_referenced_locally
|
|
34
|
+
let hasMaterialized = $state(expanded)
|
|
35
|
+
|
|
29
36
|
let shouldExpandNodeCalled = false
|
|
30
37
|
|
|
38
|
+
// Single chokepoint for flipping expansion — every path (mount effect,
|
|
39
|
+
// click, keyboard) routes through here, so the materialization high-water
|
|
40
|
+
// mark can't be forgotten when a new expansion path is added.
|
|
41
|
+
function applyExpanded(next: boolean) {
|
|
42
|
+
expanded = next
|
|
43
|
+
if (next) hasMaterialized = true
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
$effect(() => {
|
|
32
47
|
const fn = shouldExpandNode
|
|
33
48
|
if (!shouldExpandNodeCalled) {
|
|
34
49
|
shouldExpandNodeCalled = true
|
|
35
50
|
return
|
|
36
51
|
}
|
|
37
|
-
|
|
52
|
+
// Track only the callback identity: untrack level/value/field so an
|
|
53
|
+
// ancestor re-render that mutates them can't rerun this effect and
|
|
54
|
+
// overwrite the user's expansion state.
|
|
55
|
+
applyExpanded(untrack(() => fn(level, value, field)))
|
|
38
56
|
})
|
|
39
57
|
|
|
40
58
|
// SSR-stable id for aria-controls linkage.
|
|
@@ -54,16 +72,35 @@
|
|
|
54
72
|
expanded ? activeAriaLabels.collapseJson : activeAriaLabels.expandJson
|
|
55
73
|
)
|
|
56
74
|
const childLevel = $derived(level + 1)
|
|
57
|
-
const lastIndex = $derived(data.length - 1)
|
|
58
75
|
const hasField = $derived(field !== undefined)
|
|
59
76
|
const labelText = $derived(quoteString(field ?? '', style.quotesForFieldNames))
|
|
60
77
|
|
|
78
|
+
// Object keys resolved once and shared by the count and the tuple builder,
|
|
79
|
+
// so an expanded object node enumerates its keys a single time. Arrays skip
|
|
80
|
+
// this entirely — their length is free.
|
|
81
|
+
const objectKeys = $derived(isArray ? null : Object.keys(value as Record<string, unknown>))
|
|
82
|
+
|
|
83
|
+
// Child *count* is cheap — array length, or the key count for objects.
|
|
84
|
+
// Neither touches child *values*, so a collapsed node stays allocation-free.
|
|
85
|
+
const count = $derived(objectKeys ? objectKeys.length : (value as unknown[]).length)
|
|
86
|
+
const lastIndex = $derived(count - 1)
|
|
87
|
+
|
|
88
|
+
// The tuple array is materialized lazily on first open, then kept: a node
|
|
89
|
+
// that is never expanded never allocates its N tuples or reads its N child
|
|
90
|
+
// values (#21); one that has been opened doesn't re-read them on re-expand.
|
|
91
|
+
const entries = $derived.by<Array<[string | undefined, unknown]>>(() => {
|
|
92
|
+
if (!hasMaterialized) return []
|
|
93
|
+
if (isArray) return (value as unknown[]).map((el) => [undefined, el])
|
|
94
|
+
const obj = value as Record<string, unknown>
|
|
95
|
+
return (objectKeys as string[]).map((k) => [k, obj[k]])
|
|
96
|
+
})
|
|
97
|
+
|
|
61
98
|
function setExpandWithCallback(newExpandValue: boolean) {
|
|
62
99
|
if (expanded === newExpandValue) return
|
|
63
100
|
if (beforeExpandChange && !beforeExpandChange({ level, value, field, newExpandValue })) {
|
|
64
101
|
return
|
|
65
102
|
}
|
|
66
|
-
|
|
103
|
+
applyExpanded(newExpandValue)
|
|
67
104
|
}
|
|
68
105
|
|
|
69
106
|
function onKeyDown(e: KeyboardEvent) {
|
|
@@ -102,7 +139,7 @@
|
|
|
102
139
|
}
|
|
103
140
|
</script>
|
|
104
141
|
|
|
105
|
-
{#if
|
|
142
|
+
{#if count === 0}
|
|
106
143
|
<EmptyObject {field} {openBracket} {closeBracket} {lastElement} {style} />
|
|
107
144
|
{:else}
|
|
108
145
|
<div
|
|
@@ -131,7 +168,7 @@
|
|
|
131
168
|
>{:else}<span class={style.label}>{labelText}:</span>{/if}{/if}<span
|
|
132
169
|
class={style.punctuation}>{openBracket}</span
|
|
133
170
|
>{#if expanded}<ul id={contentsId} role="group" class={style.childFieldsContainer}>
|
|
134
|
-
{#each
|
|
171
|
+
{#each entries as [childField, childValue], index (childField ?? index)}<DataRender
|
|
135
172
|
field={childField}
|
|
136
173
|
value={childValue}
|
|
137
174
|
{style}
|
package/dist/types.d.ts
CHANGED
|
@@ -132,7 +132,9 @@ export interface JsonRenderProps<T> extends CommonRenderProps {
|
|
|
132
132
|
export interface ExpandableRenderProps extends CommonRenderProps {
|
|
133
133
|
field?: string;
|
|
134
134
|
value: object | unknown[];
|
|
135
|
-
|
|
135
|
+
/** Whether `value` is an array. Resolved once by DataRender (the type
|
|
136
|
+
* dispatcher) so ExpandableObject never re-tests the value's shape. */
|
|
137
|
+
isArray: boolean;
|
|
136
138
|
openBracket: string;
|
|
137
139
|
closeBracket: string;
|
|
138
140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-json-view-lite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Fast, tiny JSON tree viewer for Svelte 5 — port of react-json-view-lite with runes, SSR, snippet overrides, and zero runtime dependencies",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -57,37 +57,37 @@
|
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@eslint/compat": "^2.1.0",
|
|
59
59
|
"@eslint/js": "^10.0.1",
|
|
60
|
-
"@playwright/test": "^1.
|
|
60
|
+
"@playwright/test": "^1.61.1",
|
|
61
61
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
62
|
-
"@sveltejs/kit": "^2.
|
|
63
|
-
"@sveltejs/package": "^2.5.
|
|
62
|
+
"@sveltejs/kit": "^2.69.1",
|
|
63
|
+
"@sveltejs/package": "^2.5.8",
|
|
64
64
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
65
65
|
"@testing-library/jest-dom": "^6.9.1",
|
|
66
|
-
"@testing-library/svelte": "^5.
|
|
66
|
+
"@testing-library/svelte": "^5.4.2",
|
|
67
67
|
"@testing-library/user-event": "^14.6.1",
|
|
68
|
-
"@types/node": "^
|
|
69
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
70
|
-
"@typescript-eslint/parser": "^8.
|
|
71
|
-
"@vitest/coverage-v8": "^4.1.
|
|
72
|
-
"eslint": "^10.
|
|
68
|
+
"@types/node": "^26.1.0",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^8.62.1",
|
|
70
|
+
"@typescript-eslint/parser": "^8.62.1",
|
|
71
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
72
|
+
"eslint": "^10.6.0",
|
|
73
73
|
"eslint-config-prettier": "^10.1.8",
|
|
74
74
|
"eslint-plugin-import": "^2.32.0",
|
|
75
|
-
"eslint-plugin-svelte": "^3.
|
|
75
|
+
"eslint-plugin-svelte": "^3.20.0",
|
|
76
76
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
77
|
-
"globals": "^17.
|
|
77
|
+
"globals": "^17.7.0",
|
|
78
78
|
"husky": "^9.1.7",
|
|
79
79
|
"jsdom": "^29.1.1",
|
|
80
|
-
"mprocs": "^0.9.
|
|
81
|
-
"prettier": "^3.
|
|
80
|
+
"mprocs": "^0.9.6",
|
|
81
|
+
"prettier": "^3.9.4",
|
|
82
82
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
83
|
-
"prettier-plugin-svelte": "^4.
|
|
83
|
+
"prettier-plugin-svelte": "^4.1.1",
|
|
84
84
|
"publint": "^0.3.21",
|
|
85
|
-
"svelte": "^5.
|
|
86
|
-
"svelte-check": "^4.
|
|
85
|
+
"svelte": "^5.56.4",
|
|
86
|
+
"svelte-check": "^4.7.1",
|
|
87
87
|
"typescript": "^6.0.3",
|
|
88
|
-
"typescript-eslint": "^8.
|
|
89
|
-
"vite": "^8.
|
|
90
|
-
"vitest": "^4.1.
|
|
88
|
+
"typescript-eslint": "^8.62.1",
|
|
89
|
+
"vite": "^8.1.3",
|
|
90
|
+
"vitest": "^4.1.9"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"svelte": "^5.0.0"
|