@humanspeak/svelte-json-view-lite 0.1.0 → 0.1.2
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 +77 -2
- package/dist/DataRender.svelte +17 -5
- package/dist/ExpandableObject.svelte +4 -7
- package/dist/JsonPrimitiveValue.svelte +99 -48
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -8
- package/dist/styles.module.css +80 -45
- package/package.json +22 -21
package/README.md
CHANGED
|
@@ -5,6 +5,18 @@ Fast, tiny JSON tree viewer for Svelte 5 — a port of
|
|
|
5
5
|
(MIT © 2024 AnyRoad) with runes, SSR, per-type snippet overrides, and zero
|
|
6
6
|
runtime dependencies.
|
|
7
7
|
|
|
8
|
+
[](https://www.npmjs.com/package/@humanspeak/svelte-json-view-lite)
|
|
9
|
+
[](https://github.com/humanspeak/svelte-json-view-lite/actions/workflows/npm-publish.yml)
|
|
10
|
+
[](https://coveralls.io/github/humanspeak/svelte-json-view-lite?branch=main)
|
|
11
|
+
[](https://github.com/humanspeak/svelte-json-view-lite/blob/main/LICENSE)
|
|
12
|
+
[](https://www.npmjs.com/package/@humanspeak/svelte-json-view-lite)
|
|
13
|
+
[](https://github.com/humanspeak/svelte-json-view-lite/actions/workflows/codeql.yml)
|
|
14
|
+
[](https://packagephobia.com/result?p=@humanspeak/svelte-json-view-lite)
|
|
15
|
+
[](https://trunk.io)
|
|
16
|
+
[](http://www.typescriptlang.org/)
|
|
17
|
+
[](https://www.npmjs.com/package/@humanspeak/svelte-json-view-lite)
|
|
18
|
+
[](https://github.com/humanspeak/svelte-json-view-lite/graphs/commit-activity)
|
|
19
|
+
|
|
8
20
|
## Features
|
|
9
21
|
|
|
10
22
|
- Svelte 5 runes throughout (`$props`, `$state`, `$derived`, `$effect`).
|
|
@@ -83,6 +95,65 @@ Override individual slots by spreading:
|
|
|
83
95
|
/>
|
|
84
96
|
```
|
|
85
97
|
|
|
98
|
+
## Retheme via CSS variables
|
|
99
|
+
|
|
100
|
+
Every color in the built-in themes is declared as a CSS custom property
|
|
101
|
+
on the root container, so you can tweak one or more colors without
|
|
102
|
+
swapping the entire `style` prop or bringing your own classname map.
|
|
103
|
+
The variables all live under the `--sjv-*` namespace:
|
|
104
|
+
|
|
105
|
+
| Variable | Default (light) | Default (dark) | Applies to |
|
|
106
|
+
| ------------------- | ------------------ | -------------------- | ------------------------------------------ |
|
|
107
|
+
| `--sjv-background` | `#eee` | `rgb(0, 43, 54)` | Root container background |
|
|
108
|
+
| `--sjv-label` | `#000000` | `rgb(253, 246, 227)` | Field label text |
|
|
109
|
+
| `--sjv-punctuation` | `#000000` | `rgb(253, 246, 227)` | Brackets, colons, commas |
|
|
110
|
+
| `--sjv-string` | `rgb(42, 63, 60)` | `rgb(203, 75, 22)` | String values |
|
|
111
|
+
| `--sjv-number` | `#0b75f5` | `rgb(211, 54, 130)` | Number & BigInt values |
|
|
112
|
+
| `--sjv-boolean` | `rgb(70, 144, 56)` | `rgb(174, 129, 255)` | Boolean values |
|
|
113
|
+
| `--sjv-null` | `#df113a` | `rgb(129, 181, 172)` | `null` literal |
|
|
114
|
+
| `--sjv-undefined` | `#df113a` | `rgb(129, 181, 172)` | `undefined` literal |
|
|
115
|
+
| `--sjv-other` | `#43413d` | `rgb(38, 139, 210)` | Dates, functions, symbols, etc. |
|
|
116
|
+
| `--sjv-expander` | `#000000` | `rgb(253, 246, 227)` | `▸` / `▾` icons and collapsed `...` marker |
|
|
117
|
+
|
|
118
|
+
**Global override** — applies to every `JsonView` on the page:
|
|
119
|
+
|
|
120
|
+
```css
|
|
121
|
+
:root {
|
|
122
|
+
--sjv-string: lavender;
|
|
123
|
+
--sjv-number: #ff71ce;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Scoped override** — target one component (or a subtree):
|
|
128
|
+
|
|
129
|
+
```svelte
|
|
130
|
+
<div class="vaporwave">
|
|
131
|
+
<JsonView {data} style={defaultStyles} />
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<style>
|
|
135
|
+
.vaporwave :global(div[role='tree']) {
|
|
136
|
+
--sjv-background: #2b1055;
|
|
137
|
+
--sjv-string: #01cdfe;
|
|
138
|
+
--sjv-number: #05ffa1;
|
|
139
|
+
--sjv-boolean: #b967ff;
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Per-instance override** — forward inline styles via the root div (ARIA
|
|
145
|
+
attrs and `style` on `<JsonView>` are spread onto the wrapper):
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<JsonView {data} style={defaultStyles} style:--sjv-number="tomato" />
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
All three patterns work with either `defaultStyles` or `darkStyles` —
|
|
152
|
+
the variables are defined on both container classes with theme-specific
|
|
153
|
+
fallback defaults, so one override applies to whichever theme is active.
|
|
154
|
+
|
|
155
|
+
See the `/test/css-variables` playground route for a live example.
|
|
156
|
+
|
|
86
157
|
## Snippet overrides
|
|
87
158
|
|
|
88
159
|
Unlike the React lib, `svelte-json-view-lite` exposes typed `Snippet`
|
|
@@ -183,5 +254,9 @@ pnpm build # vite build + svelte-package + publint
|
|
|
183
254
|
|
|
184
255
|
## License
|
|
185
256
|
|
|
186
|
-
MIT
|
|
187
|
-
|
|
257
|
+
MIT © [Humanspeak, Inc.](LICENSE) Original `react-json-view-lite`
|
|
258
|
+
© 2024 AnyRoad.
|
|
259
|
+
|
|
260
|
+
## Credits
|
|
261
|
+
|
|
262
|
+
Made with ❤️ by [Humanspeak](https://humanspeak.com)
|
package/dist/DataRender.svelte
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
import type { JsonRenderProps } from './types.js'
|
|
5
5
|
import { isArray, isDate, isFunction, isObject } from './utils/dataTypeDetection.js'
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// `{'{'}` inline.
|
|
7
|
+
// Svelte parses `"{"` as an expression opener; stashing the braces as
|
|
8
|
+
// constants avoids the parse error and the no-useless-mustaches lint.
|
|
10
9
|
const OBJECT_OPEN = '{'
|
|
11
10
|
const OBJECT_CLOSE = '}'
|
|
12
11
|
|
|
@@ -16,13 +15,26 @@
|
|
|
16
15
|
// Array check first — arrays are also objects, so order matters.
|
|
17
16
|
const isArr = $derived(isArray(value))
|
|
18
17
|
const isObj = $derived(isObject(value) && !isDate(value) && !isFunction(value))
|
|
18
|
+
|
|
19
|
+
// Derive the children tuple array from `value` so ExpandableObject
|
|
20
|
+
// receives a stable reference while `value` is unchanged — otherwise
|
|
21
|
+
// a fresh array identity on every parent tick invalidates the
|
|
22
|
+
// {#each} key and cascades re-renders down the tree.
|
|
23
|
+
const children = $derived.by<Array<[string | undefined, unknown]>>(() => {
|
|
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
31
|
</script>
|
|
20
32
|
|
|
21
33
|
{#if isArr}
|
|
22
34
|
<ExpandableObject
|
|
23
35
|
{...props}
|
|
24
36
|
value={value as unknown[]}
|
|
25
|
-
data={
|
|
37
|
+
data={children}
|
|
26
38
|
openBracket="["
|
|
27
39
|
closeBracket="]"
|
|
28
40
|
/>
|
|
@@ -30,7 +42,7 @@
|
|
|
30
42
|
<ExpandableObject
|
|
31
43
|
{...props}
|
|
32
44
|
value={value as object}
|
|
33
|
-
data={
|
|
45
|
+
data={children}
|
|
34
46
|
openBracket={OBJECT_OPEN}
|
|
35
47
|
closeBracket={OBJECT_CLOSE}
|
|
36
48
|
/>
|
|
@@ -20,16 +20,14 @@
|
|
|
20
20
|
snippets
|
|
21
21
|
}: ExpandableRenderProps = $props()
|
|
22
22
|
|
|
23
|
-
//
|
|
23
|
+
// Initial expand state captured once on mount; the effect below
|
|
24
|
+
// re-runs only when the callback identity changes so level/value/
|
|
25
|
+
// field mutations from ancestor re-renders don't collapse the node.
|
|
24
26
|
// svelte-ignore state_referenced_locally
|
|
25
27
|
let expanded = $state(shouldExpandNode(level, value, field))
|
|
26
28
|
|
|
27
|
-
// React's useRef<boolean>: plain mutable local.
|
|
28
29
|
let shouldExpandNodeCalled = false
|
|
29
30
|
|
|
30
|
-
// Match React useEffect(fn, [shouldExpandNode]) — fire only when the
|
|
31
|
-
// callback identity changes. We intentionally avoid reading level/value/
|
|
32
|
-
// field so an ancestor re-render doesn't trigger a respectful collapse.
|
|
33
31
|
$effect(() => {
|
|
34
32
|
const fn = shouldExpandNode
|
|
35
33
|
if (!shouldExpandNodeCalled) {
|
|
@@ -39,10 +37,9 @@
|
|
|
39
37
|
expanded = fn(level, value, field)
|
|
40
38
|
})
|
|
41
39
|
|
|
42
|
-
// SSR-stable
|
|
40
|
+
// SSR-stable id for aria-controls linkage.
|
|
43
41
|
const contentsId = $props.id()
|
|
44
42
|
|
|
45
|
-
// bind:this target for focus management.
|
|
46
43
|
let expanderButton = $state<HTMLSpanElement | null>(null)
|
|
47
44
|
|
|
48
45
|
const activeAriaLabels = $derived<AriaLabels>(
|
|
@@ -18,80 +18,131 @@
|
|
|
18
18
|
const hasField = $derived(field !== undefined)
|
|
19
19
|
const labelText = $derived(quoteString(field ?? '', style.quotesForFieldNames))
|
|
20
20
|
|
|
21
|
+
type ValueKind =
|
|
22
|
+
| 'null'
|
|
23
|
+
| 'undefined'
|
|
24
|
+
| 'string'
|
|
25
|
+
| 'boolean'
|
|
26
|
+
| 'number'
|
|
27
|
+
| 'bigint'
|
|
28
|
+
| 'date'
|
|
29
|
+
| 'function'
|
|
30
|
+
| 'other'
|
|
31
|
+
|
|
32
|
+
// A single predicate walk picks the discriminant; `rendered`, the
|
|
33
|
+
// snippet lookup, and the template all key off `kind` so the 8-way
|
|
34
|
+
// type ladder is evaluated once per render instead of three times.
|
|
35
|
+
const kind = $derived.by<ValueKind>(() => {
|
|
36
|
+
if (value === null) return 'null'
|
|
37
|
+
if (value === undefined) return 'undefined'
|
|
38
|
+
if (isString(value)) return 'string'
|
|
39
|
+
if (isBoolean(value)) return 'boolean'
|
|
40
|
+
if (isNumber(value)) return 'number'
|
|
41
|
+
if (isBigInt(value)) return 'bigint'
|
|
42
|
+
if (isDate(value)) return 'date'
|
|
43
|
+
if (isFunction(value)) return 'function'
|
|
44
|
+
return 'other'
|
|
45
|
+
})
|
|
46
|
+
|
|
21
47
|
type Rendered = { text: string; valueStyle: string }
|
|
22
48
|
const rendered = $derived.by<Rendered>(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
text:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
switch (kind) {
|
|
50
|
+
case 'null':
|
|
51
|
+
return { text: 'null', valueStyle: style.nullValue }
|
|
52
|
+
case 'undefined':
|
|
53
|
+
return { text: 'undefined', valueStyle: style.undefinedValue }
|
|
54
|
+
case 'string':
|
|
55
|
+
return {
|
|
56
|
+
text: quoteStringValue(
|
|
57
|
+
value as string,
|
|
58
|
+
!style.noQuotesForStringValues,
|
|
59
|
+
style.stringifyStringValues
|
|
60
|
+
),
|
|
61
|
+
valueStyle: style.stringValue
|
|
62
|
+
}
|
|
63
|
+
case 'boolean':
|
|
64
|
+
return {
|
|
65
|
+
text: (value as boolean) ? 'true' : 'false',
|
|
66
|
+
valueStyle: style.booleanValue
|
|
67
|
+
}
|
|
68
|
+
case 'number':
|
|
69
|
+
return { text: String(value), valueStyle: style.numberValue }
|
|
70
|
+
case 'bigint':
|
|
71
|
+
return {
|
|
72
|
+
text: `${(value as bigint).toString()}n`,
|
|
73
|
+
valueStyle: style.numberValue
|
|
74
|
+
}
|
|
75
|
+
case 'date':
|
|
76
|
+
return { text: (value as Date).toISOString(), valueStyle: style.otherValue }
|
|
77
|
+
case 'function':
|
|
78
|
+
return { text: 'function() { }', valueStyle: style.otherValue }
|
|
79
|
+
default:
|
|
80
|
+
return { text: String(value), valueStyle: style.otherValue }
|
|
81
|
+
}
|
|
41
82
|
})
|
|
42
83
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
84
|
+
const activeSnippet = $derived.by(() => {
|
|
85
|
+
switch (kind) {
|
|
86
|
+
case 'null':
|
|
87
|
+
return snippets.null
|
|
88
|
+
case 'undefined':
|
|
89
|
+
return snippets.undefined
|
|
90
|
+
case 'string':
|
|
91
|
+
return snippets.string
|
|
92
|
+
case 'boolean':
|
|
93
|
+
return snippets.boolean
|
|
94
|
+
case 'number':
|
|
95
|
+
return snippets.number
|
|
96
|
+
case 'bigint':
|
|
97
|
+
return snippets.bigint
|
|
98
|
+
case 'date':
|
|
99
|
+
return snippets.date
|
|
100
|
+
case 'function':
|
|
101
|
+
return snippets.function
|
|
102
|
+
default:
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
53
105
|
})
|
|
54
106
|
</script>
|
|
55
107
|
|
|
56
108
|
<!--
|
|
57
109
|
The entire row body lives on a single prettier-ignored line because Svelte
|
|
58
|
-
preserves template whitespace between adjacent elements
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
natively; we have to hand-collapse it. Every margin we need is provided
|
|
63
|
-
by the CSS module (e.g. `.label { margin-right: 5px; }`).
|
|
110
|
+
preserves template whitespace between adjacent elements as visible spaces
|
|
111
|
+
under the container's `white-space: pre-wrap`. React/JSX strips that
|
|
112
|
+
whitespace natively; we hand-collapse it. Per-type snippet branches key
|
|
113
|
+
off `kind` rather than re-running predicates.
|
|
64
114
|
-->
|
|
65
115
|
<div class={style.basicChildStyle} role="treeitem" aria-selected={false}>
|
|
66
116
|
<!-- prettier-ignore -->
|
|
67
|
-
{#if hasField}{#if snippets.label}{@render snippets.label({ field: field ?? '', level })}{:else}<span class={style.label}>{labelText}:</span>{/if}{/if}{#if
|
|
117
|
+
{#if hasField}{#if snippets.label}{@render snippets.label({ field: field ?? '', level })}{:else}<span class={style.label}>{labelText}:</span>{/if}{/if}{#if activeSnippet && kind === 'null'}{@render snippets.null?.(
|
|
68
118
|
{ value: null, field, level }
|
|
69
|
-
)}{:else if
|
|
119
|
+
)}{:else if activeSnippet && kind === 'undefined'}{@render snippets.undefined?.({
|
|
70
120
|
value: undefined,
|
|
71
121
|
field,
|
|
72
122
|
level
|
|
73
|
-
})}{:else if
|
|
74
|
-
value,
|
|
123
|
+
})}{:else if activeSnippet && kind === 'string'}{@render snippets.string?.({
|
|
124
|
+
value: value as string,
|
|
75
125
|
field,
|
|
76
126
|
level
|
|
77
|
-
})}{:else if
|
|
78
|
-
value,
|
|
127
|
+
})}{:else if activeSnippet && kind === 'boolean'}{@render snippets.boolean?.({
|
|
128
|
+
value: value as boolean,
|
|
79
129
|
field,
|
|
80
130
|
level
|
|
81
|
-
})}{:else if
|
|
82
|
-
value,
|
|
131
|
+
})}{:else if activeSnippet && kind === 'number'}{@render snippets.number?.({
|
|
132
|
+
value: value as number,
|
|
83
133
|
field,
|
|
84
134
|
level
|
|
85
|
-
})}{:else if
|
|
86
|
-
value,
|
|
135
|
+
})}{:else if activeSnippet && kind === 'bigint'}{@render snippets.bigint?.({
|
|
136
|
+
value: value as bigint,
|
|
87
137
|
field,
|
|
88
138
|
level
|
|
89
|
-
})}{:else if
|
|
90
|
-
value,
|
|
139
|
+
})}{:else if activeSnippet && kind === 'date'}{@render snippets.date?.({
|
|
140
|
+
value: value as Date,
|
|
91
141
|
field,
|
|
92
142
|
level
|
|
93
|
-
})}{:else if
|
|
94
|
-
|
|
143
|
+
})}{:else if activeSnippet && kind === 'function'}{@render snippets.function?.({
|
|
144
|
+
// trunk-ignore(eslint/@typescript-eslint/no-unsafe-function-type)
|
|
145
|
+
value: value as Function,
|
|
95
146
|
field,
|
|
96
147
|
level
|
|
97
148
|
})}{:else}<span class={rendered.valueStyle}>{rendered.text}</span
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,9 @@ export type { AriaLabels, BigIntSnippetProps, BooleanSnippetProps, DateSnippetPr
|
|
|
5
5
|
export { allExpanded, collapseAllNested } from './utils/expandStrategies.js';
|
|
6
6
|
export { JsonView };
|
|
7
7
|
/**
|
|
8
|
-
* Correctly-spelled default aria labels
|
|
9
|
-
*
|
|
10
|
-
* `react-json-view-lite
|
|
8
|
+
* Correctly-spelled default aria labels (new in the Svelte port; prefer
|
|
9
|
+
* this over the typoed `StyleProps.ariaLables` shipped by
|
|
10
|
+
* `react-json-view-lite`).
|
|
11
11
|
*/
|
|
12
12
|
export declare const defaultAriaLabels: AriaLabels;
|
|
13
13
|
export declare const defaultStyles: StyleProps;
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,15 @@ import styles from './styles.module.css';
|
|
|
3
3
|
export default JsonView;
|
|
4
4
|
export { allExpanded, collapseAllNested } from './utils/expandStrategies.js';
|
|
5
5
|
export { JsonView };
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Correctly-spelled default aria labels (new in the Svelte port; prefer
|
|
8
|
+
* this over the typoed `StyleProps.ariaLables` shipped by
|
|
9
|
+
* `react-json-view-lite`).
|
|
10
|
+
*/
|
|
11
|
+
export const defaultAriaLabels = {
|
|
7
12
|
collapseJson: 'collapse JSON',
|
|
8
13
|
expandJson: 'expand JSON'
|
|
9
14
|
};
|
|
10
|
-
/**
|
|
11
|
-
* Correctly-spelled default aria labels. Introduced in the Svelte port;
|
|
12
|
-
* prefer this over the typoed `StyleProps.ariaLables` shipped by
|
|
13
|
-
* `react-json-view-lite`.
|
|
14
|
-
*/
|
|
15
|
-
export const defaultAriaLabels = baseAriaLabels;
|
|
16
15
|
const buildStyles = (variant) => ({
|
|
17
16
|
container: styles[`container-${variant}`],
|
|
18
17
|
basicChildStyle: styles['basic-element-style'],
|
|
@@ -31,7 +30,7 @@ const buildStyles = (variant) => ({
|
|
|
31
30
|
collapsedContent: styles[`collapsed-content-${variant}`],
|
|
32
31
|
noQuotesForStringValues: false,
|
|
33
32
|
quotesForFieldNames: false,
|
|
34
|
-
ariaLabels:
|
|
33
|
+
ariaLabels: defaultAriaLabels,
|
|
35
34
|
stringifyStringValues: false
|
|
36
35
|
});
|
|
37
36
|
export const defaultStyles = buildStyles('light');
|
package/dist/styles.module.css
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/*
|
|
2
|
+
* Base styles shared across themes.
|
|
3
|
+
*/
|
|
2
4
|
|
|
3
5
|
.container-base {
|
|
4
6
|
line-height: 1.2;
|
|
@@ -47,11 +49,6 @@
|
|
|
47
49
|
font-size: 0.8em;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
.container-light {
|
|
51
|
-
composes: container-base;
|
|
52
|
-
background: #eee;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
52
|
.basic-element-style {
|
|
56
53
|
margin: 0;
|
|
57
54
|
padding: 0 10px;
|
|
@@ -62,11 +59,35 @@
|
|
|
62
59
|
padding: 0;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
/*
|
|
62
|
+
/*
|
|
63
|
+
* Light theme.
|
|
64
|
+
*
|
|
65
|
+
* Every color is declared as a CSS custom property (`--sjv-*`) on the
|
|
66
|
+
* container so consumers can override individual colors either globally
|
|
67
|
+
* (`:root { --sjv-string: lavender }`) or scoped to one instance
|
|
68
|
+
* (`.my-json-view { --sjv-number: #f0f }`). Descendant selectors read
|
|
69
|
+
* the vars via `var(--sjv-*)` — no fallback is needed because the
|
|
70
|
+
* container always defines the defaults.
|
|
71
|
+
*/
|
|
72
|
+
.container-light {
|
|
73
|
+
composes: container-base;
|
|
74
|
+
--sjv-background: #eee;
|
|
75
|
+
--sjv-label: #000000;
|
|
76
|
+
--sjv-punctuation: #000000;
|
|
77
|
+
--sjv-null: #df113a;
|
|
78
|
+
--sjv-undefined: #df113a;
|
|
79
|
+
--sjv-string: rgb(42, 63, 60);
|
|
80
|
+
--sjv-number: #0b75f5;
|
|
81
|
+
--sjv-boolean: rgb(70, 144, 56);
|
|
82
|
+
--sjv-other: #43413d;
|
|
83
|
+
--sjv-expander: #000000;
|
|
84
|
+
background: var(--sjv-background);
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
.label-light {
|
|
67
88
|
font-weight: 600;
|
|
68
89
|
margin-right: 5px;
|
|
69
|
-
color:
|
|
90
|
+
color: var(--sjv-label);
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
.clickable-label-light {
|
|
@@ -76,77 +97,74 @@
|
|
|
76
97
|
|
|
77
98
|
.punctuation-light {
|
|
78
99
|
composes: punctuation-base;
|
|
79
|
-
color:
|
|
100
|
+
color: var(--sjv-punctuation);
|
|
80
101
|
}
|
|
81
102
|
|
|
82
103
|
.value-null-light {
|
|
83
|
-
color:
|
|
104
|
+
color: var(--sjv-null);
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
.value-undefined-light {
|
|
87
|
-
color:
|
|
108
|
+
color: var(--sjv-undefined);
|
|
88
109
|
}
|
|
89
110
|
|
|
90
111
|
.value-string-light {
|
|
91
|
-
color:
|
|
112
|
+
color: var(--sjv-string);
|
|
92
113
|
}
|
|
93
114
|
|
|
94
115
|
.value-number-light {
|
|
95
|
-
color:
|
|
116
|
+
color: var(--sjv-number);
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
.value-boolean-light {
|
|
99
|
-
color:
|
|
120
|
+
color: var(--sjv-boolean);
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
.value-other-light {
|
|
103
|
-
color:
|
|
124
|
+
color: var(--sjv-other);
|
|
104
125
|
}
|
|
105
126
|
|
|
106
127
|
.collapse-icon-light {
|
|
107
128
|
composes: expander-base;
|
|
108
129
|
composes: collapse-icon;
|
|
109
|
-
color:
|
|
130
|
+
color: var(--sjv-expander);
|
|
110
131
|
}
|
|
111
132
|
|
|
112
133
|
.expand-icon-light {
|
|
113
134
|
composes: expander-base;
|
|
114
135
|
composes: expand-icon;
|
|
115
|
-
color:
|
|
136
|
+
color: var(--sjv-expander);
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
.collapsed-content-light {
|
|
119
140
|
composes: collapsed-content-base;
|
|
120
|
-
color:
|
|
141
|
+
color: var(--sjv-expander);
|
|
121
142
|
}
|
|
122
143
|
|
|
123
|
-
/*
|
|
144
|
+
/*
|
|
145
|
+
* Dark theme. Same variable names, different defaults — so a consumer
|
|
146
|
+
* override like `:root { --sjv-string: lavender }` applies identically
|
|
147
|
+
* to light and dark instances.
|
|
148
|
+
*/
|
|
124
149
|
.container-dark {
|
|
125
|
-
background: rgb(0, 43, 54);
|
|
126
150
|
composes: container-base;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
color: rgb(253, 246, 227);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.collapsed-content-dark {
|
|
142
|
-
composes: collapsed-content-base;
|
|
143
|
-
color: rgb(253, 246, 227);
|
|
151
|
+
--sjv-background: rgb(0, 43, 54);
|
|
152
|
+
--sjv-label: rgb(253, 246, 227);
|
|
153
|
+
--sjv-punctuation: rgb(253, 246, 227);
|
|
154
|
+
--sjv-null: rgb(129, 181, 172);
|
|
155
|
+
--sjv-undefined: rgb(129, 181, 172);
|
|
156
|
+
--sjv-string: rgb(203, 75, 22);
|
|
157
|
+
--sjv-number: rgb(211, 54, 130);
|
|
158
|
+
--sjv-boolean: rgb(174, 129, 255);
|
|
159
|
+
--sjv-other: rgb(38, 139, 210);
|
|
160
|
+
--sjv-expander: rgb(253, 246, 227);
|
|
161
|
+
background: var(--sjv-background);
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
.label-dark {
|
|
147
165
|
font-weight: bolder;
|
|
148
166
|
margin-right: 5px;
|
|
149
|
-
color:
|
|
167
|
+
color: var(--sjv-label);
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
.clickable-label-dark {
|
|
@@ -156,29 +174,46 @@
|
|
|
156
174
|
|
|
157
175
|
.punctuation-dark {
|
|
158
176
|
composes: punctuation-base;
|
|
159
|
-
color:
|
|
177
|
+
color: var(--sjv-punctuation);
|
|
160
178
|
}
|
|
161
179
|
|
|
162
180
|
.value-null-dark {
|
|
163
|
-
color:
|
|
181
|
+
color: var(--sjv-null);
|
|
164
182
|
}
|
|
165
183
|
|
|
166
184
|
.value-undefined-dark {
|
|
167
|
-
color:
|
|
185
|
+
color: var(--sjv-undefined);
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
.value-string-dark {
|
|
171
|
-
color:
|
|
189
|
+
color: var(--sjv-string);
|
|
172
190
|
}
|
|
173
191
|
|
|
174
192
|
.value-number-dark {
|
|
175
|
-
color:
|
|
193
|
+
color: var(--sjv-number);
|
|
176
194
|
}
|
|
177
195
|
|
|
178
196
|
.value-boolean-dark {
|
|
179
|
-
color:
|
|
197
|
+
color: var(--sjv-boolean);
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
.value-other-dark {
|
|
183
|
-
color:
|
|
201
|
+
color: var(--sjv-other);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.expand-icon-dark {
|
|
205
|
+
composes: expander-base;
|
|
206
|
+
composes: expand-icon;
|
|
207
|
+
color: var(--sjv-expander);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.collapse-icon-dark {
|
|
211
|
+
composes: expander-base;
|
|
212
|
+
composes: collapse-icon;
|
|
213
|
+
color: var(--sjv-expander);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.collapsed-content-dark {
|
|
217
|
+
composes: collapsed-content-base;
|
|
218
|
+
color: var(--sjv-expander);
|
|
184
219
|
}
|
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.2",
|
|
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",
|
|
@@ -55,39 +55,39 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@eslint/compat": "^2.0
|
|
58
|
+
"@eslint/compat": "^2.1.0",
|
|
59
59
|
"@eslint/js": "^10.0.1",
|
|
60
|
-
"@playwright/test": "^1.
|
|
60
|
+
"@playwright/test": "^1.60.0",
|
|
61
61
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
62
|
-
"@sveltejs/kit": "^2.
|
|
62
|
+
"@sveltejs/kit": "^2.61.1",
|
|
63
63
|
"@sveltejs/package": "^2.5.7",
|
|
64
|
-
"@sveltejs/vite-plugin-svelte": "^7.
|
|
64
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
65
65
|
"@testing-library/jest-dom": "^6.9.1",
|
|
66
66
|
"@testing-library/svelte": "^5.3.1",
|
|
67
67
|
"@testing-library/user-event": "^14.6.1",
|
|
68
|
-
"@types/node": "^25.
|
|
69
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
70
|
-
"@typescript-eslint/parser": "^8.
|
|
71
|
-
"@vitest/coverage-v8": "^4.1.
|
|
72
|
-
"eslint": "^10.
|
|
68
|
+
"@types/node": "^25.9.1",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^8.60.0",
|
|
70
|
+
"@typescript-eslint/parser": "^8.60.0",
|
|
71
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
72
|
+
"eslint": "^10.4.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.18.0",
|
|
76
76
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
77
|
-
"globals": "^17.
|
|
77
|
+
"globals": "^17.6.0",
|
|
78
78
|
"husky": "^9.1.7",
|
|
79
|
-
"jsdom": "^29.
|
|
80
|
-
"mprocs": "^0.9.
|
|
79
|
+
"jsdom": "^29.1.1",
|
|
80
|
+
"mprocs": "^0.9.3",
|
|
81
81
|
"prettier": "^3.8.3",
|
|
82
82
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
83
|
-
"prettier-plugin-svelte": "^
|
|
84
|
-
"publint": "^0.3.
|
|
85
|
-
"svelte": "^5.55.
|
|
86
|
-
"svelte-check": "^4.4.
|
|
83
|
+
"prettier-plugin-svelte": "^4.0.1",
|
|
84
|
+
"publint": "^0.3.21",
|
|
85
|
+
"svelte": "^5.55.9",
|
|
86
|
+
"svelte-check": "^4.4.8",
|
|
87
87
|
"typescript": "^6.0.3",
|
|
88
|
-
"typescript-eslint": "^8.
|
|
89
|
-
"vite": "^8.0.
|
|
90
|
-
"vitest": "^4.1.
|
|
88
|
+
"typescript-eslint": "^8.60.0",
|
|
89
|
+
"vite": "^8.0.14",
|
|
90
|
+
"vitest": "^4.1.7"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"svelte": "^5.0.0"
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
],
|
|
106
106
|
"scripts": {
|
|
107
107
|
"build": "vite build && npm run package",
|
|
108
|
+
"cf-typegen": "pnpm --filter docs cf-typegen",
|
|
108
109
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
109
110
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
110
111
|
"dev": "vite dev",
|