atom.io 0.33.11 → 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/index.js +2 -43
- package/dist/react/index.js.map +1 -1
- package/dist/react-devtools/index.css +140 -52
- package/dist/react-devtools/index.css.map +1 -1
- package/dist/react-devtools/index.d.ts +4 -0
- package/dist/react-devtools/index.d.ts.map +1 -1
- package/dist/react-devtools/index.js +302 -149
- package/dist/react-devtools/index.js.map +1 -1
- package/dist/use-o-BrXc7Qro.js +47 -0
- package/dist/use-o-BrXc7Qro.js.map +1 -0
- package/package.json +5 -5
- package/src/react-devtools/Button.tsx +13 -5
- 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 +140 -52
- package/src/react-devtools/json-editor/default-components.tsx +4 -1
- package/src/react-devtools/json-editor/developer-interface.tsx +2 -1
- package/src/react-devtools/json-editor/editors-by-type/array-editor.tsx +85 -25
- package/src/react-devtools/json-editor/editors-by-type/object-editor.tsx +80 -18
- package/src/react-devtools/json-editor/json-editor-internal.tsx +125 -58
- package/src/react-devtools/store.ts +97 -6
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import type { RegularAtomToken } from "atom.io"
|
|
2
|
+
import { findInStore } from "atom.io/internal"
|
|
3
|
+
import type { Json, JsonTypes } from "atom.io/json"
|
|
4
|
+
import { JSON_DEFAULTS } from "atom.io/json"
|
|
5
|
+
import { useI, useO } from "atom.io/react"
|
|
6
|
+
import { DevtoolsContext } from "atom.io/react-devtools/store"
|
|
7
|
+
import { type ReactElement, useContext } from "react"
|
|
3
8
|
|
|
9
|
+
import type { JsonEditorComponents, SetterOrUpdater } from ".."
|
|
4
10
|
import type { JsonEditorProps_INTERNAL } from "../json-editor-internal"
|
|
5
11
|
import { JsonEditor_INTERNAL } from "../json-editor-internal"
|
|
6
12
|
import { makeElementSetters } from "./utilities/array-elements"
|
|
@@ -9,6 +15,53 @@ import {
|
|
|
9
15
|
makePropertyRemovers,
|
|
10
16
|
} from "./utilities/object-properties"
|
|
11
17
|
|
|
18
|
+
type ArrayElementProps = {
|
|
19
|
+
path: ReadonlyArray<number | string>
|
|
20
|
+
isReadonly: (path: ReadonlyArray<number | string>) => boolean
|
|
21
|
+
isHidden: (path: ReadonlyArray<number | string>) => boolean
|
|
22
|
+
data: unknown
|
|
23
|
+
set: SetterOrUpdater<Json.Tree.Array>
|
|
24
|
+
remove: (() => void) | undefined
|
|
25
|
+
recast: (newType: keyof JsonTypes) => void
|
|
26
|
+
Components: JsonEditorComponents
|
|
27
|
+
testid?: string | undefined
|
|
28
|
+
viewIsOpenAtom: RegularAtomToken<boolean, readonly (number | string)[]>
|
|
29
|
+
}
|
|
30
|
+
const ArrayElement = ({
|
|
31
|
+
path,
|
|
32
|
+
isReadonly,
|
|
33
|
+
isHidden,
|
|
34
|
+
data,
|
|
35
|
+
set,
|
|
36
|
+
remove,
|
|
37
|
+
recast,
|
|
38
|
+
Components,
|
|
39
|
+
testid,
|
|
40
|
+
viewIsOpenAtom,
|
|
41
|
+
}: ArrayElementProps): ReactElement => {
|
|
42
|
+
const index = path[path.length - 1]
|
|
43
|
+
const viewIsOpen = useO(viewIsOpenAtom)
|
|
44
|
+
const setViewIsOpen = useI(viewIsOpenAtom)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<JsonEditor_INTERNAL
|
|
48
|
+
path={path}
|
|
49
|
+
name={`${index}`}
|
|
50
|
+
isReadonly={isReadonly}
|
|
51
|
+
isHidden={isHidden}
|
|
52
|
+
data={data}
|
|
53
|
+
set={set}
|
|
54
|
+
remove={remove}
|
|
55
|
+
recast={recast}
|
|
56
|
+
className="json_editor_element"
|
|
57
|
+
Components={Components}
|
|
58
|
+
isOpen={viewIsOpen}
|
|
59
|
+
setIsOpen={setViewIsOpen}
|
|
60
|
+
testid={`${testid}-element-${index}`}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
12
65
|
export const ArrayEditor = ({
|
|
13
66
|
path = [],
|
|
14
67
|
isReadonly = () => false,
|
|
@@ -18,6 +71,7 @@ export const ArrayEditor = ({
|
|
|
18
71
|
Components,
|
|
19
72
|
testid,
|
|
20
73
|
}: JsonEditorProps_INTERNAL<Json.Tree.Array>): ReactElement => {
|
|
74
|
+
const { viewIsOpenAtoms, store } = useContext(DevtoolsContext)
|
|
21
75
|
const disabled = isReadonly(path)
|
|
22
76
|
|
|
23
77
|
const setElement = makeElementSetters(data, set)
|
|
@@ -26,41 +80,47 @@ export const ArrayEditor = ({
|
|
|
26
80
|
|
|
27
81
|
return (
|
|
28
82
|
<Components.ArrayWrapper>
|
|
29
|
-
<
|
|
83
|
+
<main className={`json_editor_elements${disabled ? ` readonly` : ``}`}>
|
|
30
84
|
{data.map((element, index) => {
|
|
31
|
-
const
|
|
85
|
+
const elementPath = [...path, index]
|
|
86
|
+
const pathKey = elementPath.join(`,`)
|
|
87
|
+
const viewIsOpenAtom = findInStore(store, viewIsOpenAtoms, [
|
|
88
|
+
...path,
|
|
89
|
+
index,
|
|
90
|
+
])
|
|
32
91
|
return (
|
|
33
|
-
<
|
|
34
|
-
key={
|
|
35
|
-
path={
|
|
36
|
-
name={`${index}`}
|
|
92
|
+
<ArrayElement
|
|
93
|
+
key={pathKey}
|
|
94
|
+
path={elementPath}
|
|
37
95
|
isReadonly={isReadonly}
|
|
38
96
|
isHidden={isHidden}
|
|
39
97
|
data={element}
|
|
40
98
|
set={setElement[index]}
|
|
41
99
|
remove={removeElement[index]}
|
|
42
100
|
recast={recastElement[index]}
|
|
43
|
-
className="json_editor_element"
|
|
44
101
|
Components={Components}
|
|
45
|
-
testid={
|
|
102
|
+
testid={testid}
|
|
103
|
+
viewIsOpenAtom={viewIsOpenAtom}
|
|
46
104
|
/>
|
|
47
105
|
)
|
|
48
106
|
})}
|
|
49
|
-
</
|
|
50
|
-
{disabled ?
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
107
|
+
</main>
|
|
108
|
+
{!disabled ? (
|
|
109
|
+
<footer>
|
|
110
|
+
<Components.Button
|
|
111
|
+
testid={`${testid}-add-element`}
|
|
112
|
+
disabled={disabled}
|
|
113
|
+
onClick={() => {
|
|
114
|
+
set((current) => {
|
|
115
|
+
const newData = [...current, JSON_DEFAULTS.string]
|
|
116
|
+
return newData
|
|
117
|
+
})
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<Components.AddIcon />
|
|
121
|
+
</Components.Button>
|
|
122
|
+
</footer>
|
|
123
|
+
) : null}
|
|
64
124
|
</Components.ArrayWrapper>
|
|
65
125
|
)
|
|
66
126
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RegularAtomToken } from "atom.io"
|
|
2
|
+
import { findInStore } from "atom.io/internal"
|
|
3
|
+
import type { Json, JsonTypes } from "atom.io/json"
|
|
4
|
+
import { useI } from "atom.io/react/use-i"
|
|
5
|
+
import { useO } from "atom.io/react/use-o"
|
|
6
|
+
import { DevtoolsContext } from "atom.io/react-devtools/store"
|
|
2
7
|
import type { FC, ReactElement } from "react"
|
|
3
|
-
import { useRef } from "react"
|
|
8
|
+
import { useContext, useRef } from "react"
|
|
4
9
|
|
|
5
10
|
import { ElasticInput } from "../../elastic-input"
|
|
11
|
+
import type { SetterOrUpdater } from ".."
|
|
6
12
|
import type { JsonEditorComponents } from "../default-components"
|
|
7
13
|
import type { JsonEditorProps_INTERNAL } from "../json-editor-internal"
|
|
8
14
|
import { JsonEditor_INTERNAL } from "../json-editor-internal"
|
|
@@ -43,6 +49,56 @@ export const PropertyAdder: FC<PropertyAdderProps> = ({
|
|
|
43
49
|
</Components.MissingPropertyWrapper>
|
|
44
50
|
)
|
|
45
51
|
|
|
52
|
+
type ObjectPropertyProps = {
|
|
53
|
+
path: ReadonlyArray<number | string>
|
|
54
|
+
isReadonly: (path: ReadonlyArray<number | string>) => boolean
|
|
55
|
+
isHidden: (path: ReadonlyArray<number | string>) => boolean
|
|
56
|
+
data: unknown
|
|
57
|
+
set: SetterOrUpdater<Json.Tree.Object>
|
|
58
|
+
rename: (newKey: string) => void
|
|
59
|
+
remove: (() => void) | undefined
|
|
60
|
+
recast: (newType: keyof JsonTypes) => void
|
|
61
|
+
Components: JsonEditorComponents
|
|
62
|
+
testid?: string | undefined
|
|
63
|
+
viewIsOpenAtom: RegularAtomToken<boolean, readonly (number | string)[]>
|
|
64
|
+
}
|
|
65
|
+
const ObjectProperty = ({
|
|
66
|
+
path,
|
|
67
|
+
isReadonly,
|
|
68
|
+
isHidden,
|
|
69
|
+
data,
|
|
70
|
+
set,
|
|
71
|
+
rename,
|
|
72
|
+
remove,
|
|
73
|
+
recast,
|
|
74
|
+
Components,
|
|
75
|
+
testid,
|
|
76
|
+
viewIsOpenAtom,
|
|
77
|
+
}: ObjectPropertyProps): ReactElement => {
|
|
78
|
+
const key = path[path.length - 1]
|
|
79
|
+
const viewIsOpen = useO(viewIsOpenAtom)
|
|
80
|
+
const setViewIsOpen = useI(viewIsOpenAtom)
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<JsonEditor_INTERNAL
|
|
84
|
+
path={path}
|
|
85
|
+
name={`${key}`}
|
|
86
|
+
isReadonly={isReadonly}
|
|
87
|
+
isHidden={isHidden}
|
|
88
|
+
data={data}
|
|
89
|
+
set={set}
|
|
90
|
+
rename={rename}
|
|
91
|
+
remove={remove}
|
|
92
|
+
recast={recast}
|
|
93
|
+
className="json_editor_property"
|
|
94
|
+
Components={Components}
|
|
95
|
+
isOpen={viewIsOpen}
|
|
96
|
+
setIsOpen={setViewIsOpen}
|
|
97
|
+
testid={`${testid}-property-${key}`}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
46
102
|
export const ObjectEditor = <T extends Json.Tree.Object>({
|
|
47
103
|
path = [],
|
|
48
104
|
isReadonly = () => false,
|
|
@@ -52,6 +108,8 @@ export const ObjectEditor = <T extends Json.Tree.Object>({
|
|
|
52
108
|
Components,
|
|
53
109
|
testid,
|
|
54
110
|
}: JsonEditorProps_INTERNAL<T>): ReactElement => {
|
|
111
|
+
const { viewIsOpenAtoms, store } = useContext(DevtoolsContext)
|
|
112
|
+
|
|
55
113
|
const disabled = isReadonly(path)
|
|
56
114
|
|
|
57
115
|
const stableKeyMap = useRef<Record<keyof T, keyof T>>(
|
|
@@ -76,30 +134,34 @@ export const ObjectEditor = <T extends Json.Tree.Object>({
|
|
|
76
134
|
<div className={`json_editor_properties${disabled ? ` readonly` : ``}`}>
|
|
77
135
|
{Object.keys(data).map((key) => {
|
|
78
136
|
const originalKey = stableKeyMap.current[key]
|
|
79
|
-
const
|
|
80
|
-
const
|
|
137
|
+
const propertyPath = [...path, key]
|
|
138
|
+
const originalPropertyPath = [...path, originalKey]
|
|
139
|
+
const stablePathKey = originalPropertyPath.join(`.`)
|
|
140
|
+
const viewIsOpenAtom = findInStore(store, viewIsOpenAtoms, [
|
|
141
|
+
...path,
|
|
142
|
+
key,
|
|
143
|
+
])
|
|
81
144
|
|
|
82
145
|
return (
|
|
83
|
-
<
|
|
84
|
-
key={
|
|
85
|
-
path={
|
|
86
|
-
name={key}
|
|
146
|
+
<ObjectProperty
|
|
147
|
+
key={stablePathKey}
|
|
148
|
+
path={propertyPath}
|
|
87
149
|
isReadonly={isReadonly}
|
|
88
150
|
isHidden={isHidden}
|
|
89
|
-
data={data[key
|
|
90
|
-
set={setProperty[key
|
|
91
|
-
rename={renameProperty[key
|
|
92
|
-
remove={removeProperty[key
|
|
93
|
-
recast={recastProperty[key
|
|
94
|
-
className="json_editor_property"
|
|
151
|
+
data={data[key]}
|
|
152
|
+
set={setProperty[key]}
|
|
153
|
+
rename={renameProperty[key]}
|
|
154
|
+
remove={removeProperty[key]}
|
|
155
|
+
recast={recastProperty[key]}
|
|
95
156
|
Components={Components}
|
|
96
|
-
testid={
|
|
157
|
+
testid={testid}
|
|
158
|
+
viewIsOpenAtom={viewIsOpenAtom}
|
|
97
159
|
/>
|
|
98
160
|
)
|
|
99
161
|
})}
|
|
100
162
|
</div>
|
|
101
163
|
{disabled ? null : (
|
|
102
|
-
|
|
164
|
+
<footer>
|
|
103
165
|
<Components.Button
|
|
104
166
|
disabled={disabled}
|
|
105
167
|
testid={`${testid}-add-property`}
|
|
@@ -107,7 +169,7 @@ export const ObjectEditor = <T extends Json.Tree.Object>({
|
|
|
107
169
|
makePropertyAdder(`new_property`, `string`)()
|
|
108
170
|
}}
|
|
109
171
|
>
|
|
110
|
-
|
|
172
|
+
<Components.AddIcon />
|
|
111
173
|
</Components.Button>
|
|
112
174
|
<Components.Button
|
|
113
175
|
testid={`${testid}-sort-properties`}
|
|
@@ -118,7 +180,7 @@ export const ObjectEditor = <T extends Json.Tree.Object>({
|
|
|
118
180
|
>
|
|
119
181
|
Sort
|
|
120
182
|
</Components.Button>
|
|
121
|
-
|
|
183
|
+
</footer>
|
|
122
184
|
)}
|
|
123
185
|
</Components.ObjectWrapper>
|
|
124
186
|
)
|
|
@@ -1,9 +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
|
|
|
12
|
+
import { button } from "../Button"
|
|
6
13
|
import { ElasticInput } from "../elastic-input"
|
|
14
|
+
import { DevtoolsContext } from "../store"
|
|
7
15
|
import type { SetterOrUpdater } from "."
|
|
8
16
|
import { SubEditors } from "."
|
|
9
17
|
import type { JsonEditorComponents } from "./default-components"
|
|
@@ -23,6 +31,8 @@ export type JsonEditorProps_INTERNAL<T> = {
|
|
|
23
31
|
style?: CSSProperties | undefined
|
|
24
32
|
Header?: FC<{ data: T }> | undefined
|
|
25
33
|
Components: JsonEditorComponents
|
|
34
|
+
isOpen?: boolean
|
|
35
|
+
setIsOpen?: (newValue: boolean) => void
|
|
26
36
|
testid?: string | undefined
|
|
27
37
|
}
|
|
28
38
|
|
|
@@ -38,10 +48,13 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
38
48
|
isHidden = () => false,
|
|
39
49
|
className,
|
|
40
50
|
style,
|
|
41
|
-
Header: HeaderDisplay,
|
|
42
51
|
Components,
|
|
52
|
+
isOpen,
|
|
53
|
+
setIsOpen,
|
|
43
54
|
testid,
|
|
44
55
|
}: JsonEditorProps_INTERNAL<T>): ReactElement | null => {
|
|
56
|
+
const { openCloseAllTX, store } = useContext(DevtoolsContext)
|
|
57
|
+
|
|
45
58
|
const dataIsJson = isJson(data)
|
|
46
59
|
const refined = jsonRefinery.refine<unknown>(data) ?? {
|
|
47
60
|
type: `non-json`,
|
|
@@ -53,6 +66,9 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
53
66
|
|
|
54
67
|
const disabled = isReadonly(path)
|
|
55
68
|
|
|
69
|
+
const dataIsTree = refined.type === `array` || refined.type === `object`
|
|
70
|
+
const dataIsExpandable = dataIsTree && isOpen !== undefined && setIsOpen
|
|
71
|
+
|
|
56
72
|
return isHidden(path) ? null : (
|
|
57
73
|
<Components.ErrorBoundary>
|
|
58
74
|
<Components.EditorWrapper
|
|
@@ -60,64 +76,115 @@ export const JsonEditor_INTERNAL = <T,>({
|
|
|
60
76
|
style={style}
|
|
61
77
|
testid={testid}
|
|
62
78
|
>
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
+
}
|
|
84
148
|
}
|
|
85
|
-
|
|
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>
|
|
163
|
+
{remove ? (
|
|
164
|
+
<Components.Button
|
|
86
165
|
disabled={disabled}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
value={refined.type}
|
|
112
|
-
disabled={disabled}
|
|
113
|
-
data-testid={`${testid}-recast`}
|
|
114
|
-
>
|
|
115
|
-
{Object.keys(SubEditors).map((type) => (
|
|
116
|
-
<option key={type} value={type}>
|
|
117
|
-
{type}
|
|
118
|
-
</option>
|
|
119
|
-
))}
|
|
120
|
-
</select>
|
|
166
|
+
onClick={() => {
|
|
167
|
+
remove()
|
|
168
|
+
}}
|
|
169
|
+
testid={`${testid}-delete`}
|
|
170
|
+
>
|
|
171
|
+
<Components.DeleteIcon />
|
|
172
|
+
</Components.Button>
|
|
173
|
+
) : null}
|
|
174
|
+
</header>
|
|
175
|
+
|
|
176
|
+
{dataIsTree && isOpen !== false ? (
|
|
177
|
+
<SubEditor
|
|
178
|
+
data={refined.data as never}
|
|
179
|
+
set={set}
|
|
180
|
+
remove={remove}
|
|
181
|
+
rename={rename}
|
|
182
|
+
path={path}
|
|
183
|
+
isReadonly={isReadonly}
|
|
184
|
+
isHidden={isHidden}
|
|
185
|
+
Components={Components}
|
|
186
|
+
testid={testid}
|
|
187
|
+
/>
|
|
121
188
|
) : null}
|
|
122
189
|
</Components.EditorWrapper>
|
|
123
190
|
</Components.ErrorBoundary>
|
|
@@ -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
|
}
|