atom.io 0.33.12 → 0.33.13
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/dist/react-devtools/index.css +85 -53
- package/dist/react-devtools/index.css.map +1 -1
- package/dist/react-devtools/index.d.ts +1 -0
- package/dist/react-devtools/index.d.ts.map +1 -1
- package/dist/react-devtools/index.js +143 -71
- package/dist/react-devtools/index.js.map +1 -1
- package/package.json +5 -5
- package/src/react-devtools/Button.tsx +12 -4
- package/src/react-devtools/StateEditor.tsx +13 -1
- package/src/react-devtools/StateIndex.tsx +40 -20
- package/src/react-devtools/TimelineIndex.tsx +1 -1
- package/src/react-devtools/TransactionIndex.tsx +1 -1
- package/src/react-devtools/devtools.css +85 -53
- package/src/react-devtools/json-editor/developer-interface.tsx +2 -1
- package/src/react-devtools/json-editor/editors-by-type/array-editor.tsx +23 -20
- package/src/react-devtools/json-editor/editors-by-type/object-editor.tsx +7 -23
- package/src/react-devtools/json-editor/json-editor-internal.tsx +94 -77
- package/src/react-devtools/store.ts +97 -6
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { actUponStore, arbitrary } from "atom.io/internal"
|
|
1
2
|
import { jsonRefinery } from "atom.io/introspection"
|
|
2
3
|
import type { JsonTypes } from "atom.io/json"
|
|
3
4
|
import { isJson } from "atom.io/json"
|
|
4
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
type CSSProperties,
|
|
7
|
+
type FC,
|
|
8
|
+
type ReactElement,
|
|
9
|
+
useContext,
|
|
10
|
+
} from "react"
|
|
5
11
|
|
|
6
12
|
import { button } from "../Button"
|
|
7
13
|
import { ElasticInput } from "../elastic-input"
|
|
14
|
+
import { DevtoolsContext } from "../store"
|
|
8
15
|
import type { SetterOrUpdater } from "."
|
|
9
16
|
import { SubEditors } from "."
|
|
10
17
|
import type { JsonEditorComponents } from "./default-components"
|
|
@@ -46,6 +53,8 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
46
53
|
setIsOpen,
|
|
47
54
|
testid,
|
|
48
55
|
}: JsonEditorProps_INTERNAL<T>): ReactElement | null => {
|
|
56
|
+
const { openCloseAllTX, store } = useContext(DevtoolsContext)
|
|
57
|
+
|
|
49
58
|
const dataIsJson = isJson(data)
|
|
50
59
|
const refined = jsonRefinery.refine<unknown>(data) ?? {
|
|
51
60
|
type: `non-json`,
|
|
@@ -58,6 +67,7 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
58
67
|
const disabled = isReadonly(path)
|
|
59
68
|
|
|
60
69
|
const dataIsTree = refined.type === `array` || refined.type === `object`
|
|
70
|
+
const dataIsExpandable = dataIsTree && isOpen !== undefined && setIsOpen
|
|
61
71
|
|
|
62
72
|
return isHidden(path) ? null : (
|
|
63
73
|
<Components.ErrorBoundary>
|
|
@@ -67,6 +77,89 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
67
77
|
testid={testid}
|
|
68
78
|
>
|
|
69
79
|
<header>
|
|
80
|
+
<main>
|
|
81
|
+
{remove || dataIsExpandable ? (
|
|
82
|
+
<button.OpenClose
|
|
83
|
+
isOpen={isOpen ?? false}
|
|
84
|
+
testid={`${testid}-open-close`}
|
|
85
|
+
onShiftClick={() => {
|
|
86
|
+
actUponStore(store, openCloseAllTX, arbitrary())(path, isOpen)
|
|
87
|
+
return false
|
|
88
|
+
}}
|
|
89
|
+
setIsOpen={setIsOpen}
|
|
90
|
+
disabled={!dataIsExpandable}
|
|
91
|
+
/>
|
|
92
|
+
) : null}
|
|
93
|
+
{rename && (
|
|
94
|
+
<Components.KeyWrapper>
|
|
95
|
+
<ElasticInput
|
|
96
|
+
value={name}
|
|
97
|
+
onChange={(e) => {
|
|
98
|
+
rename(e.target.value)
|
|
99
|
+
}}
|
|
100
|
+
disabled={disabled}
|
|
101
|
+
data-testid={`${testid}-rename`}
|
|
102
|
+
/>
|
|
103
|
+
</Components.KeyWrapper>
|
|
104
|
+
)}
|
|
105
|
+
{dataIsTree ? (
|
|
106
|
+
<>
|
|
107
|
+
{isOpen !== undefined && setIsOpen ? (
|
|
108
|
+
<span className="json_viewer">{JSON.stringify(data)}</span>
|
|
109
|
+
) : null}
|
|
110
|
+
{recast ? (
|
|
111
|
+
<select
|
|
112
|
+
onChange={(e) => {
|
|
113
|
+
recast(e.target.value as keyof JsonTypes)
|
|
114
|
+
}}
|
|
115
|
+
value={refined.type}
|
|
116
|
+
disabled={disabled}
|
|
117
|
+
data-testid={`${testid}-recast`}
|
|
118
|
+
>
|
|
119
|
+
{Object.keys(SubEditors).map((type) => (
|
|
120
|
+
<option key={type} value={type}>
|
|
121
|
+
{type}
|
|
122
|
+
</option>
|
|
123
|
+
))}
|
|
124
|
+
</select>
|
|
125
|
+
) : null}
|
|
126
|
+
</>
|
|
127
|
+
) : (
|
|
128
|
+
<>
|
|
129
|
+
<SubEditor
|
|
130
|
+
data={refined.data as never}
|
|
131
|
+
set={set}
|
|
132
|
+
remove={remove}
|
|
133
|
+
rename={rename}
|
|
134
|
+
path={path}
|
|
135
|
+
isReadonly={isReadonly}
|
|
136
|
+
isHidden={isHidden}
|
|
137
|
+
Components={Components}
|
|
138
|
+
testid={testid}
|
|
139
|
+
/>
|
|
140
|
+
{recast && dataIsJson ? (
|
|
141
|
+
<select
|
|
142
|
+
onChange={
|
|
143
|
+
disabled
|
|
144
|
+
? undefined
|
|
145
|
+
: (e) => {
|
|
146
|
+
recast(e.target.value as keyof JsonTypes)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
value={refined.type}
|
|
150
|
+
disabled={disabled}
|
|
151
|
+
data-testid={`${testid}-recast`}
|
|
152
|
+
>
|
|
153
|
+
{Object.keys(SubEditors).map((type) => (
|
|
154
|
+
<option key={type} value={type}>
|
|
155
|
+
{type}
|
|
156
|
+
</option>
|
|
157
|
+
))}
|
|
158
|
+
</select>
|
|
159
|
+
) : null}
|
|
160
|
+
</>
|
|
161
|
+
)}
|
|
162
|
+
</main>
|
|
70
163
|
{remove ? (
|
|
71
164
|
<Components.Button
|
|
72
165
|
disabled={disabled}
|
|
@@ -78,82 +171,6 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
78
171
|
<Components.DeleteIcon />
|
|
79
172
|
</Components.Button>
|
|
80
173
|
) : null}
|
|
81
|
-
{dataIsTree && isOpen !== undefined && setIsOpen ? (
|
|
82
|
-
<button.OpenClose
|
|
83
|
-
isOpen={isOpen}
|
|
84
|
-
testid={`${testid}-open-close`}
|
|
85
|
-
setIsOpen={setIsOpen}
|
|
86
|
-
/>
|
|
87
|
-
) : null}
|
|
88
|
-
{rename && (
|
|
89
|
-
<Components.KeyWrapper>
|
|
90
|
-
<ElasticInput
|
|
91
|
-
value={name}
|
|
92
|
-
onChange={(e) => {
|
|
93
|
-
rename(e.target.value)
|
|
94
|
-
}}
|
|
95
|
-
disabled={disabled}
|
|
96
|
-
data-testid={`${testid}-rename`}
|
|
97
|
-
/>
|
|
98
|
-
</Components.KeyWrapper>
|
|
99
|
-
)}
|
|
100
|
-
{dataIsTree ? (
|
|
101
|
-
<>
|
|
102
|
-
{recast ? (
|
|
103
|
-
<select
|
|
104
|
-
onChange={(e) => {
|
|
105
|
-
recast(e.target.value as keyof JsonTypes)
|
|
106
|
-
}}
|
|
107
|
-
value={refined.type}
|
|
108
|
-
disabled={disabled}
|
|
109
|
-
data-testid={`${testid}-recast`}
|
|
110
|
-
>
|
|
111
|
-
{Object.keys(SubEditors).map((type) => (
|
|
112
|
-
<option key={type} value={type}>
|
|
113
|
-
{type}
|
|
114
|
-
</option>
|
|
115
|
-
))}
|
|
116
|
-
</select>
|
|
117
|
-
) : null}
|
|
118
|
-
{isOpen !== undefined && setIsOpen ? (
|
|
119
|
-
<span className="json_viewer">{JSON.stringify(data)}</span>
|
|
120
|
-
) : null}
|
|
121
|
-
</>
|
|
122
|
-
) : (
|
|
123
|
-
<>
|
|
124
|
-
<SubEditor
|
|
125
|
-
data={refined.data as never}
|
|
126
|
-
set={set}
|
|
127
|
-
remove={remove}
|
|
128
|
-
rename={rename}
|
|
129
|
-
path={path}
|
|
130
|
-
isReadonly={isReadonly}
|
|
131
|
-
isHidden={isHidden}
|
|
132
|
-
Components={Components}
|
|
133
|
-
testid={testid}
|
|
134
|
-
/>
|
|
135
|
-
{recast && dataIsJson ? (
|
|
136
|
-
<select
|
|
137
|
-
onChange={
|
|
138
|
-
disabled
|
|
139
|
-
? undefined
|
|
140
|
-
: (e) => {
|
|
141
|
-
recast(e.target.value as keyof JsonTypes)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
value={refined.type}
|
|
145
|
-
disabled={disabled}
|
|
146
|
-
data-testid={`${testid}-recast`}
|
|
147
|
-
>
|
|
148
|
-
{Object.keys(SubEditors).map((type) => (
|
|
149
|
-
<option key={type} value={type}>
|
|
150
|
-
{type}
|
|
151
|
-
</option>
|
|
152
|
-
))}
|
|
153
|
-
</select>
|
|
154
|
-
) : null}
|
|
155
|
-
</>
|
|
156
|
-
)}
|
|
157
174
|
</header>
|
|
158
175
|
|
|
159
176
|
{dataIsTree && isOpen !== false ? (
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AtomToken,
|
|
3
|
+
RegularAtomFamilyToken,
|
|
4
|
+
RegularAtomToken,
|
|
5
|
+
SelectorToken,
|
|
6
|
+
TransactionToken,
|
|
7
|
+
} from "atom.io"
|
|
2
8
|
import {
|
|
3
9
|
createAtomFamily,
|
|
4
10
|
createStandaloneAtom,
|
|
11
|
+
createTransaction,
|
|
5
12
|
IMPLICIT,
|
|
6
13
|
type Store,
|
|
7
14
|
} from "atom.io/internal"
|
|
8
|
-
import type {
|
|
9
|
-
|
|
15
|
+
import type {
|
|
16
|
+
IntrospectionStates,
|
|
17
|
+
WritableTokenIndex,
|
|
18
|
+
} from "atom.io/introspection"
|
|
19
|
+
import { attachIntrospectionStates, isPlainObject } from "atom.io/introspection"
|
|
10
20
|
import { persistSync } from "atom.io/web"
|
|
11
21
|
import type { Context } from "react"
|
|
12
22
|
import { createContext } from "react"
|
|
@@ -17,7 +27,10 @@ export type DevtoolsStates = {
|
|
|
17
27
|
devtoolsAreOpenState: RegularAtomToken<boolean>
|
|
18
28
|
devtoolsViewSelectionState: RegularAtomToken<DevtoolsView>
|
|
19
29
|
devtoolsViewOptionsState: RegularAtomToken<DevtoolsView[]>
|
|
20
|
-
viewIsOpenAtoms: RegularAtomFamilyToken<boolean, string>
|
|
30
|
+
viewIsOpenAtoms: RegularAtomFamilyToken<boolean, readonly (number | string)[]>
|
|
31
|
+
openCloseAllTX: TransactionToken<
|
|
32
|
+
(path: readonly (number | string)[], current?: boolean) => void
|
|
33
|
+
>
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
export function attachDevtoolsStates(
|
|
@@ -52,13 +65,90 @@ export function attachDevtoolsStates(
|
|
|
52
65
|
: [persistSync(window.localStorage, JSON, `🔍 Devtools View Options`)],
|
|
53
66
|
})
|
|
54
67
|
|
|
55
|
-
const viewIsOpenAtoms = createAtomFamily<
|
|
68
|
+
const viewIsOpenAtoms = createAtomFamily<
|
|
69
|
+
boolean,
|
|
70
|
+
readonly (number | string)[]
|
|
71
|
+
>(store, {
|
|
56
72
|
key: `🔍 Devtools View Is Open`,
|
|
57
73
|
default: false,
|
|
58
74
|
effects: (key) =>
|
|
59
75
|
typeof window === `undefined`
|
|
60
76
|
? []
|
|
61
|
-
: [persistSync(window.localStorage, JSON,
|
|
77
|
+
: [persistSync(window.localStorage, JSON, `view-is-open:${key.join()}`)],
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const openCloseAllTX: TransactionToken<
|
|
81
|
+
(path: readonly (number | string)[], current?: boolean) => void
|
|
82
|
+
> = createTransaction<
|
|
83
|
+
(path: readonly (number | string)[], current?: boolean) => void
|
|
84
|
+
>(store, {
|
|
85
|
+
key: `openCloseMultiView`,
|
|
86
|
+
do: ({ get, set }, path, current) => {
|
|
87
|
+
const currentView = get(devtoolsViewSelectionState)
|
|
88
|
+
let states:
|
|
89
|
+
| WritableTokenIndex<AtomToken<unknown>>
|
|
90
|
+
| WritableTokenIndex<SelectorToken<unknown>>
|
|
91
|
+
switch (currentView) {
|
|
92
|
+
case `atoms`:
|
|
93
|
+
states = get(introspectionStates.atomIndex)
|
|
94
|
+
break
|
|
95
|
+
case `selectors`:
|
|
96
|
+
states = get(introspectionStates.selectorIndex)
|
|
97
|
+
break
|
|
98
|
+
case `transactions`:
|
|
99
|
+
case `timelines`:
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
switch (path.length) {
|
|
104
|
+
case 1:
|
|
105
|
+
{
|
|
106
|
+
for (const [key] of states) {
|
|
107
|
+
set(viewIsOpenAtoms, [key], !current)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
break
|
|
111
|
+
default: {
|
|
112
|
+
const item = states.get(path[0] as string)
|
|
113
|
+
let value: unknown
|
|
114
|
+
let segments: (number | string)[]
|
|
115
|
+
if (item) {
|
|
116
|
+
if (`familyMembers` in item) {
|
|
117
|
+
if (path.length === 2) {
|
|
118
|
+
for (const [subKey] of item.familyMembers) {
|
|
119
|
+
set(viewIsOpenAtoms, [path[0], subKey], !current)
|
|
120
|
+
}
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
// biome-ignore lint/style/noNonNullAssertion: fine here
|
|
124
|
+
const token = item.familyMembers.get(path[1] as string)!
|
|
125
|
+
value = get(token)
|
|
126
|
+
segments = path.slice(2, -1)
|
|
127
|
+
} else {
|
|
128
|
+
value = get(item)
|
|
129
|
+
segments = path.slice(1, -1)
|
|
130
|
+
}
|
|
131
|
+
for (const segment of segments) {
|
|
132
|
+
if (value && typeof value === `object`) {
|
|
133
|
+
value = value[segment as keyof typeof value]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const head = path.slice(0, -1)
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
for (let i = 0; i < value.length; i++) {
|
|
139
|
+
set(viewIsOpenAtoms, [...head, i], !current)
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
if (isPlainObject(value)) {
|
|
143
|
+
for (const key of Object.keys(value)) {
|
|
144
|
+
set(viewIsOpenAtoms, [...head, key], !current)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
62
152
|
})
|
|
63
153
|
|
|
64
154
|
return {
|
|
@@ -67,6 +157,7 @@ export function attachDevtoolsStates(
|
|
|
67
157
|
devtoolsViewSelectionState,
|
|
68
158
|
devtoolsViewOptionsState,
|
|
69
159
|
viewIsOpenAtoms,
|
|
160
|
+
openCloseAllTX,
|
|
70
161
|
store,
|
|
71
162
|
}
|
|
72
163
|
}
|