-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathtypes.ts
392 lines (349 loc) · 11.1 KB
/
types.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
import { type Options as AssignOptions } from 'object-property-assigner'
import { type LocalisedStrings, type TranslateFunction } from './localisation'
export type JsonData = CollectionData | ValueData
export interface JsonEditorProps {
data: JsonData
setData?: (data: JsonData) => void
rootName?: string
onUpdate?: UpdateFunction
onEdit?: UpdateFunction
onDelete?: UpdateFunction
onAdd?: UpdateFunction
onChange?: OnChangeFunction
onError?: OnErrorFunction
showErrorMessages?: boolean
enableClipboard?: boolean | CopyFunction
theme?: ThemeInput
icons?: IconReplacements
className?: string
id?: string
indent?: number
collapse?: boolean | number | FilterFunction
collapseAnimationTime?: number // ms
showCollectionCount?: boolean | 'when-closed'
restrictEdit?: boolean | FilterFunction
restrictDelete?: boolean | FilterFunction
restrictAdd?: boolean | FilterFunction
restrictTypeSelection?: boolean | DataType[] | TypeFilterFunction
restrictDrag?: boolean | FilterFunction
viewOnly?: boolean
searchText?: string
searchFilter?: 'key' | 'value' | 'all' | SearchFilterFunction
searchDebounceTime?: number
keySort?: boolean | CompareFunction
showArrayIndices?: boolean
showStringQuotes?: boolean
defaultValue?: unknown
minWidth?: string | number
maxWidth?: string | number
rootFontSize?: string | number
stringTruncate?: number
translations?: Partial<LocalisedStrings>
customNodeDefinitions?: CustomNodeDefinition[]
customText?: CustomTextDefinitions
customButtons?: CustomButtonDefinition[]
jsonParse?: (input: string) => JsonData
jsonStringify?: (input: JsonData) => string
TextEditor?: React.FC<TextEditorProps>
errorMessageTimeout?: number // ms
keyboardControls?: KeyboardControls
insertAtTop?: boolean | 'array' | 'object'
}
const ValueDataTypes = ['string', 'number', 'boolean', 'null'] as const
const CollectionDataTypes = ['object', 'array'] as const
export const DataTypes = [...ValueDataTypes, ...CollectionDataTypes] as const
export type CollectionDataType = (typeof CollectionDataTypes)[number]
export type DataType = (typeof DataTypes)[number] | 'invalid'
export type CollectionKey = string | number
export type CollectionData = object | unknown[]
export type ErrorString = string
export type TabDirection = 'next' | 'prev'
export interface IconReplacements {
add?: JSX.Element
edit?: JSX.Element
delete?: JSX.Element
copy?: JSX.Element
ok?: JSX.Element
cancel?: JSX.Element
chevron?: JSX.Element
}
export interface TextEditorProps {
value: string
onChange: (value: string) => void
onKeyDown: (e: React.KeyboardEvent) => void
}
/**
* FUNCTIONS
*/
export interface UpdateFunctionProps {
newData: JsonData
currentData: JsonData
newValue: unknown
currentValue: unknown
name: CollectionKey
path: CollectionKey[]
}
export type UpdateFunctionReturn = ['error' | 'value', JsonData]
export type UpdateFunction = (
props: UpdateFunctionProps
) =>
| void
| ErrorString
| boolean
| UpdateFunctionReturn
| Promise<boolean | ErrorString | void | UpdateFunctionReturn>
export type OnChangeFunction = (props: {
currentData: JsonData
newValue: ValueData
currentValue: ValueData
name: CollectionKey
path: CollectionKey[]
}) => ValueData
export interface JerError {
code: 'UPDATE_ERROR' | 'DELETE_ERROR' | 'ADD_ERROR' | 'INVALID_JSON' | 'KEY_EXISTS'
message: ErrorString
}
export type OnErrorFunction = (props: {
currentData: JsonData
errorValue: JsonData
currentValue: JsonData
name: CollectionKey
path: CollectionKey[]
error: JerError
}) => unknown
export type FilterFunction = (input: NodeData) => boolean
export type TypeFilterFunction = (input: NodeData) => boolean | DataType[]
export type CustomTextFunction = (input: NodeData) => string | null
export type DefaultValueFunction = (input: NodeData) => unknown
export type SearchFilterFunction = (inputData: NodeData, searchText: string) => boolean
export type SearchFilterInputFunction = (
inputData: Partial<NodeData>,
searchText: string
) => boolean
export type CopyType = 'path' | 'value'
export type CopyFunction = (input: {
success: boolean
errorMessage: string | null
key: CollectionKey
path: CollectionKey[]
value: unknown
stringValue: string
type: CopyType
}) => void
// Only using `any` here as that's the type expected by the JS "sort" method.
export type CompareFunction = (
a: [string | number, unknown],
b: [string | number, unknown]
) => number
export type SortFunction = <T>(arr: T[], nodeMap: (input: T) => [string | number, unknown]) => void
// Internal update
export type InternalUpdateFunction = (
value: unknown,
path: CollectionKey[],
options?: AssignOptions
) => Promise<string | void>
// For drag-n-drop
export type Position = 'above' | 'below'
export type InternalMoveFunction = (
source: CollectionKey[] | null,
dest: CollectionKey[],
position: Position
) => Promise<string | void>
export interface KeyEvent {
key: string
modifier?: React.ModifierKey | React.ModifierKey[]
}
export interface KeyboardControls {
confirm?: KeyEvent | string // value node defaults, key entry
cancel?: KeyEvent | string // all "Cancel" operations
objectConfirm?: KeyEvent | string
objectLineBreak?: KeyEvent | string
stringConfirm?: KeyEvent | string
stringLineBreak?: KeyEvent | string // for Value nodes
booleanConfirm?: KeyEvent | string
booleanToggle?: KeyEvent | string
numberConfirm?: KeyEvent | string
numberUp?: KeyEvent | string
numberDown?: KeyEvent | string
tabForward?: KeyEvent | string
tabBack?: KeyEvent | string
clipboardModifier?: React.ModifierKey | React.ModifierKey[]
collapseModifier?: React.ModifierKey | React.ModifierKey[]
}
export type KeyboardControlsFull = Omit<
Required<{ [Property in keyof KeyboardControls]: KeyEvent }>,
'clipboardModifier' | 'collapseModifier'
> & {
clipboardModifier: React.ModifierKey[]
collapseModifier: React.ModifierKey[]
}
/**
* NODES
*/
export interface NodeData {
key: CollectionKey
path: CollectionKey[]
level: number
index: number
value: JsonData
size: number | null
parentData: object | null
fullData: JsonData
collapsed?: boolean
}
interface BaseNodeProps {
data: unknown
parentData: CollectionData | null
nodeData: NodeData
onEdit: InternalUpdateFunction
onDelete: InternalUpdateFunction
onError?: OnErrorFunction
showErrorMessages: boolean
onMove: InternalMoveFunction
enableClipboard: boolean | CopyFunction
restrictEditFilter: FilterFunction
restrictDeleteFilter: FilterFunction
restrictAddFilter: FilterFunction
restrictDragFilter: FilterFunction
canDragOnto: boolean
searchFilter?: SearchFilterFunction
searchText?: string
restrictTypeSelection: boolean | DataType[] | TypeFilterFunction
stringTruncate: number
indent: number
sort: SortFunction
translate: TranslateFunction
customNodeDefinitions: CustomNodeDefinition[]
customButtons: CustomButtonDefinition[]
errorMessageTimeout: number
keyboardControls: KeyboardControlsFull
handleKeyboard: (
e: React.KeyboardEvent,
eventMap: Partial<Record<keyof KeyboardControlsFull, () => void>>
) => void
}
export interface CollectionNodeProps extends BaseNodeProps {
mainContainerRef: React.MutableRefObject<Element>
data: CollectionData
collapseFilter: FilterFunction
collapseAnimationTime: number
onAdd: InternalUpdateFunction
showArrayIndices: boolean
showCollectionCount: boolean | 'when-closed'
showStringQuotes: boolean
defaultValue: unknown
jsonParse: (input: string) => JsonData
jsonStringify: (data: JsonData) => string
insertAtTop: { object: boolean; array: boolean }
TextEditor?: React.FC<TextEditorProps>
}
export type ValueData = string | number | boolean
export interface ValueNodeProps extends BaseNodeProps {
data: ValueData
showLabel: boolean
showStringQuotes: boolean
onChange?: OnChangeFunction
}
export interface CustomNodeProps<T = Record<string, unknown>> extends BaseNodeProps {
value: JsonData
customNodeProps?: T
parentData: CollectionData | null
setValue: (value: ValueData) => void
handleEdit: () => void
handleCancel: () => void
handleKeyPress: (e: React.KeyboardEvent) => void
isEditing: boolean
setIsEditing: React.Dispatch<React.SetStateAction<boolean>>
getStyles: (element: ThemeableElement, nodeData: NodeData) => React.CSSProperties
children?: JSX.Element | JSX.Element[] | null
}
export interface CustomNodeDefinition<T = Record<string, unknown>, U = Record<string, unknown>> {
condition: FilterFunction
element?: React.FC<CustomNodeProps<T>>
name?: string // appears in "Type" selector
customNodeProps?: T
hideKey?: boolean
defaultValue?: unknown
showInTypesSelector?: boolean // default false
showOnEdit?: boolean // default false
showOnView?: boolean // default true
showEditTools?: boolean // default true
// For collection nodes only:
showCollectionWrapper?: boolean // default true
wrapperElement?: React.FC<CustomNodeProps<U>>
wrapperProps?: Record<string, unknown>
}
export type CustomTextDefinitions = Partial<{ [key in keyof LocalisedStrings]: CustomTextFunction }>
export interface CustomButtonDefinition {
Element: React.FC<{ nodeData: NodeData }>
onClick: (nodeData: NodeData, e: React.MouseEvent) => void
}
export interface InputProps {
value: unknown
setValue: (value: ValueData) => void
isEditing: boolean
setIsEditing: React.Dispatch<React.SetStateAction<boolean>>
handleEdit: () => void
path: CollectionKey[]
stringTruncate: number
showStringQuotes: boolean
nodeData: NodeData
translate: TranslateFunction
handleKeyboard: (
e: React.KeyboardEvent,
eventMap: Partial<Record<keyof KeyboardControlsFull, () => void>>
) => void
keyboardCommon: Partial<Record<keyof KeyboardControlsFull, () => void>>
}
/**
* THEMES
*/
// Object passed to main "theme" prop
export type ThemeInput = Theme | Partial<ThemeStyles> | Array<Theme | Partial<ThemeStyles>>
const themeableElements = [
'container',
'collection',
'collectionInner',
'collectionElement',
'dropZone',
'property',
'bracket',
'itemCount',
'string',
'number',
'boolean',
'null',
'input',
'inputHighlight',
'error',
'iconCollection',
'iconEdit',
'iconDelete',
'iconAdd',
'iconCopy',
'iconOk',
'iconCancel',
] as const
export type ThemeableElement = (typeof themeableElements)[number]
export type ThemeFunction = (nodeData: NodeData) => React.CSSProperties | null | undefined
export type ThemeValue =
| string
| React.CSSProperties
| Array<string | React.CSSProperties | ThemeFunction>
| ThemeFunction
// e.g. "#FFFFF", {backgroundColor: "grey"}, ["smaller", {fontWeight: "bold"}]
export type ThemeStyles = Record<ThemeableElement, ThemeValue>
type Fragments = Record<string, React.CSSProperties | string>
export interface Theme {
displayName?: string
fragments?: Fragments
styles: Partial<ThemeStyles>
}
// Same as "Theme", but we know every property in styles is defined
export interface DefaultTheme extends Theme {
displayName: 'Default'
styles: ThemeStyles
}
// All the fragments and shorthand defined in Theme is compiled into a single
// CSS "Style" object before being passed to components
export type CompiledStyles = Record<ThemeableElement, ThemeFunction | React.CSSProperties>