react-state-custom 1.0.23 → 1.0.24

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.
Files changed (79) hide show
  1. package/.github/copilot-instructions.md +17 -2
  2. package/.github/workflows/deploy.yml +56 -0
  3. package/API_DOCUMENTATION.md +132 -3
  4. package/README.md +69 -1
  5. package/dist/dev-tool/DataViewComponent.d.ts +6 -0
  6. package/dist/dev-tool/DevTool.d.ts +5 -0
  7. package/dist/dev-tool/DevToolState.d.ts +9 -0
  8. package/dist/dev-tool/StateLabelRender.d.ts +2 -0
  9. package/dist/dev-tool/useHighlight.d.ts +11 -0
  10. package/dist/dev.d.ts +0 -1
  11. package/dist/examples/Playground.d.ts +1 -0
  12. package/dist/examples/cart/app.d.ts +1 -0
  13. package/dist/examples/cart/index.d.ts +3 -0
  14. package/dist/examples/cart/state.d.ts +23 -0
  15. package/dist/examples/cart/view.d.ts +4 -0
  16. package/dist/examples/counter/app.d.ts +1 -0
  17. package/dist/examples/counter/index.d.ts +2 -0
  18. package/dist/examples/counter/state.d.ts +6 -0
  19. package/dist/examples/counter/view.d.ts +2 -0
  20. package/dist/examples/form/app.d.ts +1 -0
  21. package/dist/examples/form/index.d.ts +3 -0
  22. package/dist/examples/form/state.d.ts +16 -0
  23. package/dist/examples/form/view.d.ts +4 -0
  24. package/dist/examples/timer/app.d.ts +1 -0
  25. package/dist/examples/timer/index.d.ts +2 -0
  26. package/dist/examples/timer/state.d.ts +11 -0
  27. package/dist/examples/timer/view.d.ts +4 -0
  28. package/dist/examples/todo/app.d.ts +1 -0
  29. package/dist/examples/todo/index.d.ts +3 -0
  30. package/dist/examples/todo/state.d.ts +17 -0
  31. package/dist/examples/todo/view.d.ts +4 -0
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.es.js +264 -408
  34. package/dist/index.es.js.map +1 -1
  35. package/dist/index.umd.js +1 -1
  36. package/dist/index.umd.js.map +1 -1
  37. package/dist/react-state-custom.css +1 -1
  38. package/dist/state-utils/ctx.d.ts +1 -1
  39. package/package.json +7 -2
  40. package/src/dev-tool/DataViewComponent.tsx +17 -0
  41. package/src/dev-tool/DevTool.css +134 -0
  42. package/src/{DevTool.tsx → dev-tool/DevTool.tsx} +3 -2
  43. package/src/dev-tool/DevToolState.tsx +78 -0
  44. package/src/dev-tool/StateLabelRender.tsx +38 -0
  45. package/src/dev-tool/useHighlight.tsx +56 -0
  46. package/src/dev.tsx +4 -11
  47. package/src/examples/Playground.tsx +180 -0
  48. package/src/examples/cart/app.tsx +16 -0
  49. package/src/examples/cart/index.ts +3 -0
  50. package/src/examples/cart/state.ts +67 -0
  51. package/src/examples/cart/view.tsx +62 -0
  52. package/src/examples/counter/app.tsx +14 -0
  53. package/src/examples/counter/index.ts +2 -0
  54. package/src/examples/counter/state.ts +22 -0
  55. package/src/examples/counter/state.tsx?raw +0 -0
  56. package/src/examples/counter/view.tsx +20 -0
  57. package/src/examples/form/app.tsx +16 -0
  58. package/src/examples/form/index.ts +3 -0
  59. package/src/examples/form/state.ts +58 -0
  60. package/src/examples/form/view.tsx +53 -0
  61. package/src/examples/timer/app.tsx +16 -0
  62. package/src/examples/timer/index.ts +2 -0
  63. package/src/examples/timer/state.ts +43 -0
  64. package/src/examples/timer/view.tsx +26 -0
  65. package/src/examples/todo/app.tsx +16 -0
  66. package/src/examples/todo/index.ts +3 -0
  67. package/src/examples/todo/state.ts +54 -0
  68. package/src/examples/todo/view.tsx +47 -0
  69. package/src/index.ts +2 -1
  70. package/src/state-utils/ctx.ts +3 -3
  71. package/src/vite-env.d.ts +6 -0
  72. package/tsconfig.json +12 -3
  73. package/vite.config.dev.ts +6 -1
  74. package/dist/DevTool.d.ts +0 -4
  75. package/dist/DevToolState.d.ts +0 -15
  76. package/dist/Test.d.ts +0 -1
  77. package/src/DevTool.css +0 -218
  78. package/src/DevToolState.tsx +0 -358
  79. package/src/Test.tsx +0 -97
@@ -1,358 +0,0 @@
1
- import { getContext } from "./state-utils/ctx"
2
- import React, { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"
3
- import "./devTool.css"
4
- import { debounce } from "./state-utils/utils"
5
-
6
- const cache = getContext.cache
7
-
8
- export const DevToolState = ({ }) => {
9
- const [filterString, setFilterString] = useState("")
10
- const [selectedKey, setKey] = useState("")
11
-
12
- const filterFn = useMemo(
13
- () => {
14
- const preFilter = filterString
15
- .toLowerCase()
16
- .split(" ")
17
- return (e: string) => {
18
- const sLow = e.toLowerCase()
19
- return preFilter.every(token => sLow.includes(token))
20
- }
21
- },
22
- [filterString]
23
- )
24
- return <div className="main-panel">
25
- <div className="state-list">
26
- <input
27
- placeholder="Type to Filter ..."
28
- className="state-filter"
29
- value={filterString}
30
- onChange={(ev) => setFilterString(ev.target.value)}
31
- />
32
-
33
- {[...cache.keys()]
34
- .map(e => JSON.parse(e)?.[0])
35
- .filter(e => e != "auto-ctx" && e)
36
- .filter(filterFn)
37
- .map(e => <SelectedKeyRender {...{
38
- selectedKey, setKey,
39
- currentKey: e
40
- }} />)}
41
- </div>
42
- <div className="state-view" >
43
- <StateView dataKey={selectedKey} key={selectedKey} />
44
- </div>
45
- </div>
46
- }
47
-
48
- const SelectedKeyRender: React.FC<any> = ({ selectedKey, setKey, currentKey, ...props }) => {
49
- const ctx = getContext(currentKey)
50
- const divRef = useRef<HTMLDivElement>(undefined)
51
-
52
- useEffect(() => {
53
- if (divRef.current) {
54
- let flashKeyDebounce = debounce(() => {
55
- if (divRef.current) {
56
- divRef.current?.classList.add("state-key-updated");
57
- requestAnimationFrame(() => divRef.current?.classList.remove("state-key-updated"));
58
- }
59
- }, 16);
60
- return ctx.subscribeAll(flashKeyDebounce)
61
- }
62
-
63
- }, [ctx, divRef])
64
-
65
- return <div
66
- ref={divRef}
67
- className="state-key"
68
- data-active={currentKey == selectedKey}
69
- onClick={() => setKey(currentKey)}
70
- {...props}
71
- >
72
- {currentKey}
73
- </div>
74
- }
75
-
76
- export const StateView: React.FC<{ dataKey: string }> = ({ dataKey }) => {
77
- const ctx = getContext(dataKey)
78
- const [currentData, setCurrentData] = useState({ ...ctx?.data })
79
-
80
- useEffect(() => {
81
- let updateDataDebounce = debounce(setCurrentData, 16)
82
- return ctx
83
- .subscribeAll((changeKey, newData) => updateDataDebounce({ ...newData }))
84
-
85
- }, [ctx])
86
-
87
- return <JSONView
88
- value={currentData}
89
- name={dataKey}
90
- expandLevel={1}
91
- style={{}}
92
- />
93
- }
94
-
95
- type JSONViewProps = {
96
- value: any,
97
- path?: string[],
98
- name?: string,
99
- expandRoot: Record<string, boolean>,
100
- setExpandRoot: Dispatch<SetStateAction<Record<string, boolean>>>,
101
- expandLevel: number | boolean,
102
- currentField?: any
103
- currentType?: any,
104
- isGrouped?: boolean,
105
- }
106
-
107
- const splitArray = <T,>(array: T[]) => {
108
- let max = array.length < 120 ? 10 : 100
109
- return Object.fromEntries(
110
- new Array(Math.ceil((array.length + 1) / max))
111
- .fill(0)
112
- .map((_, i, a) => new Array(i == a.length - 1 ? array.length % max : max)
113
- .fill(0)
114
- .map((_, j) => i * max + j)
115
- )
116
- .filter(e => e.length)
117
- .map(keys => [`${keys.at(0)}..${keys.at(-1)}`, Object.fromEntries(
118
- keys.map(k => [k, array[k]])
119
- )])
120
- )
121
- }
122
-
123
-
124
- const splitObject = (object: any, max = 25) => {
125
- const keys = Object.keys(object);
126
- return Object.fromEntries(
127
- Array(Math.ceil((keys.length + 1) / max))
128
- .fill(0)
129
- .map((_, i, a) => new Array(i == a.length - 1 ? keys.length % max : max)
130
- .fill(0)
131
- .map((_, j) => i * max + j)
132
- )
133
- .filter(e => e.length)
134
- .map((e) => e.map(i => keys.at(i)))
135
- .map(sortedKeys => [
136
- `${sortedKeys.at(0)?.slice(0, 15)}...${sortedKeys.at(-1)?.slice(0, 15)}`,
137
- Object.fromEntries(sortedKeys.map(key => [key, object[key as any]]))]
138
- )
139
- )
140
- }
141
-
142
-
143
- const useExpandState = ({ path, expandLevel, expandRoot, setExpandRoot }: JSONViewProps) => {
144
- const expandKeys = path?.join("%") ?? "";
145
-
146
- const defaultExpand = typeof expandLevel == "boolean"
147
- ? expandLevel
148
- : (typeof expandLevel == 'number' && expandLevel > 0)
149
-
150
- const isExpand = useMemo(
151
- () => expandRoot?.[expandKeys] ?? defaultExpand,
152
- [expandRoot?.[expandKeys], expandKeys]
153
- )
154
-
155
- const setExpand = useCallback(
156
- (value: boolean) => setExpandRoot((r: object) => ({ ...r, [expandKeys]: value })),
157
- [expandRoot, expandKeys]
158
- )
159
-
160
- return { isExpand, setExpand }
161
-
162
- }
163
-
164
- export const ChangeFlashWrappper: React.FC<React.ComponentProps<'div'> & { value: any, deepCompare?: boolean }> = ({ value, deepCompare = false, ...rest }) => {
165
-
166
- const ref = useRef<HTMLElement>(undefined)
167
- const refValue = useRef(value);
168
-
169
- useEffect(() => {
170
- if (ref.current) {
171
- let isDiff = deepCompare && value && refValue.current
172
- ? (
173
- Object.keys(value).length != Object.keys(refValue.current).length
174
- || Object.keys(value).some(key => value[key] != refValue.current[key])
175
- ) : value != refValue.current
176
- if (isDiff) {
177
- refValue.current = value;
178
- ref.current.classList.add('jv-updated');
179
- let t = requestAnimationFrame(() => ref.current?.classList.remove('jv-updated'));
180
- return () => cancelAnimationFrame(t)
181
- }
182
- }
183
-
184
- }, [value, deepCompare, ref])
185
-
186
- return <div {...rest} ref={ref as any} />
187
- }
188
-
189
- const JSONViewObj: React.FC<JSONViewProps> = (props) => {
190
-
191
- const {
192
- currentField,
193
- value, path = [], name, expandRoot, setExpandRoot,
194
- expandLevel,
195
- isGrouped,
196
- } = props
197
-
198
- const isArray = value instanceof Array
199
-
200
- const { isExpand, setExpand } = useExpandState(props)
201
-
202
- const childExpandLevel = typeof expandLevel == "number" ? expandLevel - 1 : expandLevel
203
-
204
- const shouldGroup = Object.entries(value).length > (value instanceof Array ? 10 : 25);
205
-
206
- const ableToExpand = Object.entries(value).length > 0
207
-
208
- const groupedChilds = useMemo(
209
- () => shouldGroup
210
- ? (value instanceof Array) ? splitArray(value) : splitObject(value, 25)
211
- : value,
212
- [value, shouldGroup, splitArray]
213
- )
214
-
215
-
216
- return (isExpand && ableToExpand) ? <ChangeFlashWrappper className="jv-field jv-field-obj" value={value} deepCompare={isGrouped}>
217
- {currentField && <div>
218
- <div onClick={() => setExpand(false)}>
219
- <span className="jv-name">{currentField}</span>
220
- <span>:</span>
221
- <span>[-]</span>
222
- <span className="jv-type">{Object.keys(value).length} items </span>
223
- <span> {isArray ? "[" : "{"} </span>
224
- </div>
225
- </div>}
226
- <div className="jv-value">
227
- {Object
228
- .entries(groupedChilds)
229
- .map(([k, v], index) => <JSONViewCurr
230
- {...{
231
- name, expandRoot, setExpandRoot,
232
- expandLevel: childExpandLevel,
233
- value: v,
234
- isGrouped: shouldGroup,
235
- }}
236
- key={[...path, shouldGroup ? index : k].join("%")}
237
- path={[...path, k]}
238
- />)}
239
- </div>
240
- {currentField && <div>
241
- <span> {isArray ? "]" : "}"} </span>
242
- </div>}
243
- </ChangeFlashWrappper> : <ChangeFlashWrappper className="jv-field jv-field-obj" value={value} deepCompare={isGrouped}>
244
- <div>
245
- <div onClick={() => ableToExpand && setExpand(true)}>
246
- <span className="jv-name">{currentField}</span>
247
- {currentField && <span>:</span>}
248
- {currentField && ableToExpand && <span>[+]</span>}
249
- <span className="jv-type">{Object.keys(value).length} items </span>
250
- <span> {isArray ? "[" : "{"} </span>
251
- {ableToExpand && <span> ... </span>}
252
- <span> {isArray ? "]" : "}"} </span>
253
- </div>
254
- </div>
255
- </ChangeFlashWrappper>
256
-
257
- }
258
-
259
- const StringViewObj: React.FC<JSONViewProps> = (props) => {
260
-
261
- const { currentType, currentField, value, } = props
262
-
263
- const { isExpand, setExpand } = useExpandState(props)
264
-
265
- const useExpand = String(value).length > 50
266
-
267
- const renderString = useExpand && !isExpand
268
- ? `${String(value).slice(0, 15)}...${String(value).slice(-15, -1)}`
269
- : String(value)
270
-
271
- return <ChangeFlashWrappper
272
- value={props.value}
273
- className={`jv-field jv-field-${currentType} ${useExpand ? 'jv-cursor' : ''}`}
274
- onClick={() => setExpand(!isExpand)}>
275
- <span className="jv-name">{currentField}</span>
276
- <span>:</span>
277
- <span className="jv-type">{currentType}, lng={value?.length}</span>
278
- <span className="jv-value">"{renderString}"</span>
279
- <span>,</span>
280
- </ChangeFlashWrappper>
281
- }
282
-
283
-
284
- const FunctionViewObj: React.FC<JSONViewProps> = (props) => {
285
-
286
- const { currentType, currentField, value, } = props
287
-
288
- const { isExpand, setExpand } = useExpandState(props)
289
-
290
- const useExpand = String(value).length > 50
291
-
292
- const renderString = useExpand && !isExpand
293
- ? `${String(value).slice(0, 15)}...${String(value).slice(-15, -1)}`
294
- : String(value)
295
-
296
- return <ChangeFlashWrappper
297
- value={props.value}
298
- className={`jv-field jv-field-${currentType} ${useExpand ? 'jv-cursor' : ''}`}
299
- onClick={() => setExpand(!isExpand)}>
300
- <span className="jv-name">{currentField}</span>
301
- <span>:</span>
302
- <span className="jv-type">{currentType}</span>
303
- <span className="jv-value">"{renderString}"</span>
304
- <span>,</span>
305
- </ChangeFlashWrappper>
306
- }
307
-
308
- const DefaultValueView: React.FC<JSONViewProps> = (props) => {
309
-
310
- const { currentType, currentField, value, } = props
311
-
312
- return <ChangeFlashWrappper
313
- value={props.value}
314
- className={`jv-field jv-field-${currentType}`}>
315
- <span className="jv-name">{currentField}</span>
316
- <span>:</span>
317
- <span className="jv-type">{currentType}</span>
318
- <span className="jv-value">{String(value)}</span>
319
- <span>,</span>
320
- </ChangeFlashWrappper>
321
- }
322
-
323
- const JSONViewCurr: React.FC<Omit<JSONViewProps, 'currentField'>> = (props) => {
324
-
325
- const { value, path = [], name } = props
326
-
327
- const currentField = path.at(-1) ?? name ?? undefined;
328
-
329
- const currentType = typeof value
330
-
331
- switch (currentType) {
332
- case "object":
333
- return <JSONViewObj {...props} {...{ currentField, currentType }} />
334
- case "string":
335
- return <StringViewObj {...props} {...{ currentField, currentType }} />
336
- case "function":
337
- return <FunctionViewObj {...props} {...{ currentField, currentType }} />
338
- case "number":
339
- case "boolean":
340
- case "bigint":
341
- case "symbol":
342
- case "undefined":
343
- default:
344
- return <DefaultValueView {...props} {...{ currentField, currentType }} />
345
- }
346
- }
347
-
348
- export const JSONView: React.FC<{ value: any, name?: string, style?: any, expandLevel?: number | boolean }> = ({ value, name, style, expandLevel = false }) => {
349
-
350
- const [expandRoot, setExpandRoot] = useState<Record<string, boolean>>({})
351
-
352
- return <div className="jv-root" style={style}>
353
- <JSONViewCurr
354
- path={[]}
355
- {...{ name, value, expandRoot, setExpandRoot, expandLevel }}
356
- />
357
- </div>
358
- }
package/src/Test.tsx DELETED
@@ -1,97 +0,0 @@
1
-
2
- import { createAutoCtx } from './state-utils/createAutoCtx'
3
- import { createRootCtx } from './state-utils/createRootCtx'
4
- import { useCallback, useState } from 'react'
5
- import { useQuickSubscribe } from './state-utils/useQuickSubscribe'
6
-
7
- const { useCtxState: useDevCtx } = createAutoCtx(
8
- createRootCtx(
9
- "devState",
10
- ({ }) => {
11
- const [state, setState] = useState(0)
12
- return {
13
- state,
14
- increase: useCallback(() => setState(f => f + 1), [setState]),
15
- decrease: useCallback(() => setState(f => f - 1), [setState]),
16
- }
17
- }
18
- )
19
- )
20
-
21
-
22
-
23
- const { useCtxState: useDevAdvanceCtx } = createAutoCtx(
24
- createRootCtx(
25
- "devADVState",
26
- ({ id }: { id: string }) => {
27
- const [counter, setCounter] = useState(0)
28
- const [state, setState] = useState(0)
29
- const [history, setHistory] = useState([state])
30
- const [historyMap, setHistoryMap] = useState({})
31
-
32
- return {
33
- id,
34
- state,
35
- bifIntState: BigInt(state),
36
- computed: id + state,
37
- history,
38
- counter,
39
- historyMap,
40
- state1:9,
41
- state2:9,
42
- state3:9,
43
- state4:9,
44
- state6:9,
45
- statedd:9,
46
- state55:9,
47
- testSrt: "aiwioj ".repeat(counter * 3),
48
- increase: useCallback(() => setState(f => {
49
- setCounter(c => {
50
- setHistory(h => [...h, f]);
51
- setHistoryMap(m => ({ ...m, ['state--' + c]: { f, d: Date.now() } }));
52
- return c + 1
53
- })
54
- return f + 1
55
- }), [setState]),
56
- decrease: useCallback(() => setState(f => {
57
- setCounter(c => {
58
- setHistory(h => [...h, f]);
59
- setHistoryMap(m => ({ ...m, ['state--' + c]: { f, d: Date.now() } }));
60
- return c + 1
61
- })
62
- return f - 1
63
- }), [setState])
64
- }
65
- }
66
- )
67
- )
68
-
69
-
70
- export const Test = ({ }) => {
71
- const { state, decrease, increase } = useQuickSubscribe(useDevCtx({}))
72
- const { state: advState, computed: advComputed, increase: advIncreaser, decrease: advDecrise } = useQuickSubscribe(useDevAdvanceCtx({ id: "name" }))
73
-
74
- useDevAdvanceCtx({ id: "2123132" })
75
- useDevAdvanceCtx({ id: "dfgfd" })
76
- useDevAdvanceCtx({ id: "443" })
77
- useDevAdvanceCtx({ id: "w3sef" })
78
- useDevAdvanceCtx({ id: "erere" })
79
- useDevAdvanceCtx({ id: "sdfdsf" })
80
- useDevAdvanceCtx({ id: "asdasd" })
81
- useDevAdvanceCtx({ id: "66666" })
82
- useDevAdvanceCtx({ id: "dddd" })
83
- useDevAdvanceCtx({ id: "eeeee" })
84
- useDevAdvanceCtx({ id: "44444" })
85
- useDevAdvanceCtx({ id: ";;;;" })
86
-
87
- return <div>
88
- <hr />
89
- <button onClick={increase}>[+]</button>
90
- <span>{state}</span>
91
- <button onClick={decrease}>[-]</button>
92
- <hr />
93
- <button onClick={advIncreaser}>[+]</button>
94
- <span>{advState}:{advComputed}:</span>
95
- <button onClick={advDecrise}>[-]</button>
96
- </div>
97
- }