atom.io 0.33.10 → 0.33.12

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.
@@ -1,18 +1,30 @@
1
1
  main[data-css="atom_io_devtools"] {
2
2
  --fg-color: #ccc;
3
3
  --fg-light: #aaa;
4
+ --fg-soft: #777;
4
5
  --fg-faint: #555;
6
+ --fg-hint: #333;
7
+ --fg-max: #fff;
5
8
  --bg-color: #111;
6
9
  --bg-accent: #00f;
10
+ --bg-max: #000;
7
11
  --bg-tint1: #222;
12
+ --bg-tint2: #333;
8
13
  --fg-border: 1px solid var(--fg-color);
14
+ --fg-border-soft: 1px solid var(--fg-soft);
15
+ --fg-border-hint: 1px solid var(--fg-hint);
9
16
  @media (prefers-color-scheme: light) {
10
17
  --fg-color: #444;
11
18
  --fg-light: #777;
19
+ --fg-soft: #888;
12
20
  --fg-faint: #999;
21
+ --fg-hint: #ccc;
22
+ --fg-max: #000;
13
23
  --bg-color: #ddd;
14
24
  --bg-accent: #0ff;
25
+ --bg-max: #fff;
15
26
  --bg-tint1: #e3e3e3;
27
+ --bg-tint2: #f3f3f3;
16
28
  }
17
29
  & {
18
30
  pointer-events: all;
@@ -42,14 +54,20 @@ main[data-css="atom_io_devtools"] {
42
54
  display: flex;
43
55
  justify-content: space-between;
44
56
  h1 {
45
- margin: 0;
57
+ margin: 2px 4px;
46
58
  font-size: 24px;
47
59
  font-family: charter, serif;
48
60
  }
49
61
  nav {
62
+ position: relative;
50
63
  display: flex;
51
64
  flex-flow: row nowrap;
65
+ margin-top: 4px;
66
+ overflow-x: scroll;
67
+ overflow-y: visible;
68
+ height: 24px;
52
69
  button {
70
+ height: 24px;
53
71
  cursor: pointer;
54
72
  background: none;
55
73
  border: none;
@@ -58,10 +76,9 @@ main[data-css="atom_io_devtools"] {
58
76
  z-index: 1000;
59
77
  &:disabled {
60
78
  cursor: default;
61
- background-color: var(--bg-tint1);
62
- color: var(--fg-color);
79
+ background-color: var(--fg-color);
80
+ color: var(--bg-color);
63
81
  border: var(--fg-border);
64
- border-bottom: none;
65
82
  }
66
83
  }
67
84
  }
@@ -69,6 +86,10 @@ main[data-css="atom_io_devtools"] {
69
86
  > main {
70
87
  background: var(--bg-tint1);
71
88
  }
89
+ > main::before {
90
+ background-color: black;
91
+ height: 10px;
92
+ }
72
93
  main {
73
94
  overflow-y: scroll;
74
95
  flex-grow: 1;
@@ -86,7 +107,7 @@ main[data-css="atom_io_devtools"] {
86
107
  }
87
108
  .node > .node {
88
109
  margin: 0px 2px 2px 18px;
89
- border-left: var(--fg-border);
110
+ border-left: var(--fg-border-soft);
90
111
  &:last-of-type {
91
112
  margin-bottom: 6px;
92
113
  }
@@ -100,32 +121,21 @@ main[data-css="atom_io_devtools"] {
100
121
  &.transaction_update {
101
122
  padding: 0;
102
123
  }
103
- header {
124
+ > header {
104
125
  display: flex;
105
126
  flex-flow: row;
106
127
  position: sticky;
107
128
  z-index: 999;
108
129
  top: 0;
109
130
  height: 22px;
110
- button.carat {
111
- cursor: pointer;
112
- background: none;
113
- border: none;
114
- width: 20px;
115
- color: var(--fg-light);
116
- &.open {
117
- transform: rotate(90deg);
118
- }
119
- &:disabled {
120
- cursor: default;
121
- color: var(--fg-faint);
122
- }
123
- }
131
+ background: var(--bg-tint2);
132
+ border-bottom: var(--fg-border-soft);
124
133
  > main {
125
134
  display: flex;
126
135
  flex-flow: row;
127
136
  cursor: help;
128
137
  align-items: center;
138
+ flex-shrink: 0;
129
139
  * {
130
140
  height: 100%;
131
141
  display: flex;
@@ -146,25 +156,34 @@ main[data-css="atom_io_devtools"] {
146
156
  }
147
157
  > .json_viewer {
148
158
  color: var(--fg-light);
159
+ font-size: 14px;
160
+ flex-shrink: 1;
161
+ overflow-x: scroll;
149
162
  }
150
163
  > .json_editor,
151
164
  > .json_viewer {
152
165
  display: flex;
166
+ flex-shrink: 1;
167
+ z-index: -1;
168
+ overflow-x: scroll;
153
169
  align-items: center;
154
- border-left: 1px solid var(--fg-light);
155
- background-color: var(--bg-tint1);
156
- * {
170
+ border-left: var(--fg-border-soft);
171
+ white-space: pre;
172
+ background: var(--bg-tint2);
173
+ &:focus-within {
174
+ background-color: var(--bg-max);
175
+ outline: 2px solid var(--fg-max);
176
+ * {
177
+ color: var(--fg-max);
178
+ }
179
+ }
180
+ &.nu * {
157
181
  display: flex;
158
182
  height: 100%;
159
183
  }
160
184
  > span {
161
185
  padding: 0px 5px;
162
- border-left: var(--fg-color);
163
186
  z-index: 0;
164
- &:focus-within {
165
- outline: 4px solid var(--fg-color);
166
- background: var(--bg-accent);
167
- }
168
187
  }
169
188
  input {
170
189
  outline: none;
@@ -281,6 +300,41 @@ main[data-css="atom_io_devtools"] {
281
300
  }
282
301
 
283
302
  .json_editor {
303
+ display: flex;
304
+ flex-flow: column;
305
+ align-items: flex-start;
306
+ > header {
307
+ display: flex;
308
+ flex-flow: row;
309
+ width: 100%;
310
+ position: relative;
311
+ align-items: baseline;
312
+ overflow: hidden;
313
+ white-space: nowrap;
314
+ > .json_viewer {
315
+ flex-shrink: 1;
316
+ overflow-x: scroll;
317
+ height: 21px;
318
+ font-size: 14px;
319
+ display: flex;
320
+ align-items: center;
321
+ margin-left: 6px;
322
+ color: var(--fg-light);
323
+ }
324
+ > button {
325
+ padding: 0;
326
+ &.carat {
327
+ line-height: 0.5em;
328
+ font-size: 14px;
329
+ align-self: flex-start;
330
+ }
331
+ }
332
+ }
333
+ > .json_editor_array,
334
+ > .json_editor_object {
335
+ width: 100%;
336
+ flex-grow: 1;
337
+ }
284
338
  input {
285
339
  font-family: theia, monospace;
286
340
  background: none;
@@ -288,12 +342,36 @@ main[data-css="atom_io_devtools"] {
288
342
  border: none;
289
343
  }
290
344
  }
345
+ input,
346
+ button,
347
+ select {
348
+ &:focus-within {
349
+ outline: 2px solid var(--fg-max);
350
+ background: var(--bg-max);
351
+ color: var(--fg-max);
352
+ }
353
+ }
354
+ button:disabled {
355
+ cursor: default;
356
+ > span.json_editor_icon {
357
+ color: var(--fg-hint);
358
+ }
359
+ &:hover {
360
+ background: none;
361
+ }
362
+ }
363
+ input::selection {
364
+ background: var(--bg-accent);
365
+ }
366
+
291
367
  button {
292
368
  background: none;
293
369
  color: #777;
294
370
  border: none;
295
371
  font-family: theia, monospace;
296
372
  font-size: 14px;
373
+ height: 21px;
374
+ min-width: 21px;
297
375
  margin: none;
298
376
  padding: 4px;
299
377
  padding-bottom: 6px;
@@ -323,25 +401,78 @@ main[data-css="atom_io_devtools"] {
323
401
  background-color: #f055;
324
402
  }
325
403
  .json_editor_key {
326
- padding-right: 10px;
404
+ padding-right: 0px;
405
+ &::after {
406
+ content: ":";
407
+ }
327
408
  input {
328
- color: #999;
329
- @media (prefers-color-scheme: light) {
330
- color: #777;
331
- }
409
+ color: var(--fg-color);
332
410
  }
333
411
  }
334
- .json_editor_object {
335
- border-left: 2px solid #333;
336
- padding-left: 20px;
337
- @media (prefers-color-scheme: light) {
338
- border-color: #ccc;
339
- }
340
- .json_editor_properties {
341
- > * {
342
- border-bottom: var(--fg-border);
412
+ .json_editor_object,
413
+ .json_editor_array {
414
+ border-left: var(--fg-border-soft);
415
+ padding-left: 10px;
416
+ margin-left: 10px;
417
+ width: calc(100% - 21px);
418
+ .json_editor_properties,
419
+ .json_editor_elements {
420
+ .json_editor_property,
421
+ .json_editor_element {
422
+ display: flex;
423
+ border-bottom: var(--fg-border-soft);
343
424
  margin-bottom: 2px;
425
+ min-height: 23px;
426
+ > header {
427
+ width: 100%;
428
+
429
+ > span {
430
+ input {
431
+ min-width: 10px;
432
+ }
433
+ > * {
434
+ border: var(--fg-border-hint);
435
+ }
436
+ }
437
+ }
344
438
  }
439
+ &.readonly {
440
+ .json_editor_property,
441
+ .json_editor_element {
442
+ &:last-of-type {
443
+ border-bottom: none;
444
+ }
445
+ > span > * {
446
+ border: 1px solid transparent;
447
+ }
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+
454
+ .json_editor_icon {
455
+ color: var(--fg-soft);
456
+ }
457
+
458
+ button.carat {
459
+ border: none;
460
+ cursor: pointer;
461
+ background: none;
462
+ &:focus-within {
463
+ outline: none;
464
+ background: none;
465
+ }
466
+ > .json_editor_icon {
467
+ line-height: 4px;
468
+ }
469
+ &.open {
470
+ transform: rotate(90deg);
471
+ }
472
+ &:disabled {
473
+ > .json_editor_icon {
474
+ cursor: default;
475
+ color: var(--fg-hint);
345
476
  }
346
477
  }
347
478
  }
@@ -18,6 +18,7 @@ export type JsonEditorComponents = {
18
18
  disabled?: boolean
19
19
  }>
20
20
  DeleteIcon: FC
21
+ AddIcon: FC
21
22
 
22
23
  EditorWrapper: WC<{
23
24
  style?: CSSProperties | undefined
@@ -86,8 +87,10 @@ export const DEFAULT_JSON_EDITOR_COMPONENTS: JsonEditorComponents = {
86
87
  <span className="json_editor_null" data-testid={testid} />
87
88
  ),
88
89
  DeleteIcon: () => (
89
- <span className="json_editor_icon json_editor_delete">x</span>
90
+ <span className="json_editor_icon json_editor_delete">×</span>
90
91
  ),
92
+ // big plus
93
+ AddIcon: () => <span className="json_editor_icon json_editor_add">+</span>,
91
94
  MissingPropertyWrapper: ({ children, testid }) => (
92
95
  <span className="json_editor_missing_property" data-testid={testid}>
93
96
  {children}
@@ -1,9 +1,66 @@
1
- import type { Json } from "atom.io/json"
2
- import type { ReactElement } from "react"
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"
13
+ import {
14
+ makePropertyRecasters,
15
+ makePropertyRemovers,
16
+ } from "./utilities/object-properties"
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, 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
+ }
7
64
 
8
65
  export const ArrayEditor = ({
9
66
  path = [],
@@ -14,25 +71,53 @@ export const ArrayEditor = ({
14
71
  Components,
15
72
  testid,
16
73
  }: JsonEditorProps_INTERNAL<Json.Tree.Array>): ReactElement => {
74
+ const { viewIsOpenAtoms, store } = useContext(DevtoolsContext)
75
+ const disabled = isReadonly(path)
76
+
17
77
  const setElement = makeElementSetters(data, set)
78
+ const removeElement = makePropertyRemovers(data, set)
79
+ const recastElement = makePropertyRecasters(data, set)
80
+
18
81
  return (
19
- <>
20
- {data.map((element, index) => {
21
- const newPath = [...path, index]
22
- return (
23
- <JsonEditor_INTERNAL
24
- key={newPath.join(``)}
25
- path={newPath}
26
- isReadonly={isReadonly}
27
- isHidden={isHidden}
28
- data={element}
29
- set={setElement[index]}
30
- Components={Components}
31
- className="json_editor_element"
32
- testid={`${testid}-element-${index}`}
33
- />
34
- )
35
- })}
36
- </>
82
+ <Components.ArrayWrapper>
83
+ <div className={`json_editor_elements${disabled ? ` readonly` : ``}`}>
84
+ {data.map((element, index) => {
85
+ const elementPath = [...path, index]
86
+ const pathKey = elementPath.join(`,`)
87
+ const viewIsOpenAtom = findInStore(
88
+ store,
89
+ viewIsOpenAtoms,
90
+ `${testid}__${pathKey}`,
91
+ )
92
+ return (
93
+ <ArrayElement
94
+ key={pathKey}
95
+ path={elementPath}
96
+ isReadonly={isReadonly}
97
+ isHidden={isHidden}
98
+ data={element}
99
+ set={setElement[index]}
100
+ remove={removeElement[index]}
101
+ recast={recastElement[index]}
102
+ Components={Components}
103
+ testid={testid}
104
+ viewIsOpenAtom={viewIsOpenAtom}
105
+ />
106
+ )
107
+ })}
108
+ </div>
109
+ <Components.Button
110
+ testid={`${testid}-add-element`}
111
+ disabled={disabled}
112
+ onClick={() => {
113
+ set((current) => {
114
+ const newData = [...current, JSON_DEFAULTS.string]
115
+ return newData
116
+ })
117
+ }}
118
+ >
119
+ <Components.AddIcon />
120
+ </Components.Button>
121
+ </Components.ArrayWrapper>
37
122
  )
38
123
  }
@@ -1,8 +1,14 @@
1
- import type { Json } from "atom.io/json"
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, 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>>(
@@ -72,57 +130,74 @@ export const ObjectEditor = <T extends Json.Tree.Object>({
72
130
  const makePropertyAdder = makePropertyCreationInterface(data, set)
73
131
 
74
132
  return (
75
- <>
76
- <Components.Button
77
- testid={`${testid}-sort-properties`}
78
- onClick={() => {
79
- sortProperties()
80
- }}
81
- disabled={disabled}
82
- >
83
- Sort
84
- </Components.Button>
85
- <Components.ObjectWrapper>
86
- <div className="json_editor_properties">
87
- {Object.keys(data).map((key) => {
88
- const originalKey = stableKeyMap.current[key]
89
- const newPath = [...path, key]
90
- const originalPath = [...path, originalKey]
133
+ <Components.ObjectWrapper>
134
+ <div className={`json_editor_properties${disabled ? ` readonly` : ``}`}>
135
+ {Object.keys(data).map((key) => {
136
+ const originalKey = stableKeyMap.current[key]
137
+ const propertyPath = [...path, key]
138
+ const originalPropertyPath = [...path, originalKey]
139
+ const stablePathKey = originalPropertyPath.join(`.`)
140
+ const viewIsOpenAtom = findInStore(
141
+ store,
142
+ viewIsOpenAtoms,
143
+ `${testid}__${stablePathKey}`,
144
+ )
91
145
 
92
- return (
93
- <JsonEditor_INTERNAL
94
- key={originalPath.join(`.`)}
95
- path={newPath}
96
- name={key}
97
- isReadonly={isReadonly}
98
- isHidden={isHidden}
99
- data={data[key as keyof T]}
100
- set={setProperty[key as keyof T]}
101
- rename={renameProperty[key as keyof T]}
102
- remove={removeProperty[key as keyof T]}
103
- recast={recastProperty[key as keyof T]}
104
- className="json_editor_property"
105
- Components={Components}
106
- testid={`${testid}-property-${key}`}
107
- />
108
- )
109
- })}
110
- </div>
111
- {disabled ? (
112
- <Components.Button disabled testid={`${testid}-add-property`}>
113
- +
114
- </Components.Button>
115
- ) : (
146
+ return (
147
+ // <JsonEditor_INTERNAL
148
+ // key={originalPath.join(`.`)}
149
+ // path={newPath}
150
+ // name={key}
151
+ // isReadonly={isReadonly}
152
+ // isHidden={isHidden}
153
+ // data={data[key as keyof T]}
154
+ // set={setProperty[key as keyof T]}
155
+ // rename={renameProperty[key as keyof T]}
156
+ // remove={removeProperty[key as keyof T]}
157
+ // recast={recastProperty[key as keyof T]}
158
+ // className="json_editor_property"
159
+ // Components={Components}
160
+ // testid={`${testid}-property-${key}`}
161
+ // />
162
+ <ObjectProperty
163
+ key={stablePathKey}
164
+ path={propertyPath}
165
+ isReadonly={isReadonly}
166
+ isHidden={isHidden}
167
+ data={data[key]}
168
+ set={setProperty[key]}
169
+ rename={renameProperty[key]}
170
+ remove={removeProperty[key]}
171
+ recast={recastProperty[key]}
172
+ Components={Components}
173
+ testid={testid}
174
+ viewIsOpenAtom={viewIsOpenAtom}
175
+ />
176
+ )
177
+ })}
178
+ </div>
179
+ {disabled ? null : (
180
+ <>
116
181
  <Components.Button
182
+ disabled={disabled}
117
183
  testid={`${testid}-add-property`}
118
184
  onClick={() => {
119
185
  makePropertyAdder(`new_property`, `string`)()
120
186
  }}
121
187
  >
122
- +
188
+ <Components.AddIcon />
189
+ </Components.Button>
190
+ <Components.Button
191
+ testid={`${testid}-sort-properties`}
192
+ onClick={() => {
193
+ sortProperties()
194
+ }}
195
+ disabled={disabled}
196
+ >
197
+ Sort
123
198
  </Components.Button>
124
- )}
125
- </Components.ObjectWrapper>
126
- </>
199
+ </>
200
+ )}
201
+ </Components.ObjectWrapper>
127
202
  )
128
203
  }