@pihanga2/core 0.2.1 → 0.3.1

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/src/card.tsx CHANGED
@@ -1,445 +1,234 @@
1
- import React from 'react'
2
- import { useDispatch, useSelector } from "react-redux"
3
- import equal from "deep-equal"
1
+ import React, { useEffect, useId } from "react";
2
+ import { useDispatch, useSelector, useStore } from "react-redux";
3
+ import equal from "deep-equal";
4
4
 
5
- import { getLogger } from "./logger"
5
+ import { getLogger } from "./logger";
6
6
  import {
7
7
  CSSModuleClasses,
8
- CardAction,
9
8
  CardProp,
10
- DispatchF,
11
- MetaCardMapperF,
12
9
  PiCardDef,
13
- PiCardRef,
14
- PiMapProps,
10
+ PiReducer,
15
11
  PiRegisterComponent,
16
- PiRegisterMetaCard,
17
12
  PiRegisterReducerF,
13
+ ReduceF,
14
+ ReduxAction,
18
15
  ReduxState,
19
- RegisterCardF,
20
16
  StateMapper,
21
17
  StateMapperContext,
22
- } from "./types"
23
- import { Action, AnyAction, Dispatch } from "@reduxjs/toolkit"
24
-
25
- export function isCardRef(p: any): boolean {
26
- return (typeof p === "object" && p.cardType !== undefined)
27
- }
28
-
29
- type Mapping = {
30
- cardType: string
31
- props: { [k: string]: unknown }
32
- eventMappers: { [k: string]: (ev: Action) => Action | null }
33
- cardEvents: { [key: string]: string }
34
- }
35
-
36
- type MetaCard = {
37
- type: string
38
- registerCard: RegisterCardF
39
- mapper: MetaCardMapperF
40
- events?: { [key: string]: string }
41
- }
42
-
43
- const cardTypes: { [k: string]: PiRegisterComponent } = {}
44
- const metacardTypes: { [k: string]: MetaCard } = {}
45
-
46
- let framework: string // name of active UI framework
47
- const cardMappings: { [k: string]: Mapping } = {}
48
- const dispatch2registerReducer: [React.Dispatch<any>, PiRegisterReducerF][] = []
49
- const logger = getLogger("card")
50
-
51
- export function registerCardComponent(card: PiRegisterComponent): void {
52
- if (cardTypes[card.name]) {
53
- logger.warn(`Overwriting definition for card type "${card.name}"`)
54
- }
55
- logger.info(`Register card type "${card.name}"`)
56
- if (!framework) {
57
- // set default framework
58
- const na = card.name.split("/")
59
- if (na.length >= 2) {
60
- framework = na[0]
61
- logger.info(`Setting UI framework to "${framework}"`)
62
- }
63
- }
64
- cardTypes[card.name] = card
65
- }
66
-
67
- export function registerMetacard(registerCard: RegisterCardF) {
68
- function f<C>(declaration: PiRegisterMetaCard) {
69
- const { type, mapper, events } = declaration
70
- if (metacardTypes[type]) {
71
- logger.warn(`Overwriting definition for meta card type "${type}"`)
72
- }
73
- logger.info(`Register meta card type "${type}"`)
74
- metacardTypes[type] = { type, registerCard, mapper, events }
75
- }
76
- return f
77
- }
78
-
79
- export function registerCard(
80
- registerReducer: PiRegisterReducerF,
81
- dispatchF: React.Dispatch<any>,
82
- ) {
83
- // to be used by dynamically registered cards
84
- dispatch2registerReducer.push([dispatchF, registerReducer])
85
- return (name: string, parameters: PiCardDef): PiCardRef => {
86
- return _registerCard(name, parameters, registerReducer)
87
- }
88
- }
89
-
90
- function _registerCard(
91
- name: string,
92
- parameters: PiCardDef,
93
- registerReducer: PiRegisterReducerF,
94
- overrideEvents?: { [key: string]: string },
95
- ): PiCardRef {
96
- if (cardMappings[name]) {
97
- logger.warn(`Overwriting definition for card "${name}"`)
98
- }
99
- let cardType = cardTypes[parameters.cardType]
100
- if (!cardType) {
101
- if (framework) {
102
- cardType = cardTypes[`${framework}/${parameters.cardType}`]
103
- }
104
- if (!cardType) {
105
- // maybe it's a metadata card
106
- if (_registerMetadataCard(name, parameters, registerReducer)) {
107
- return name
108
- }
109
- logger.warn("unknown card type", parameters.cardType)
110
- return name
111
- }
112
- }
113
-
114
- const events = overrideEvents || cardType.events || {}
115
- _createCardMapping(name, parameters, registerReducer, events)
116
- return name
117
- // const props = {} as { [k: string]: unknown }
118
- // const eventMappers = {} as { [k: string]: (ev: Action) => Action }
119
-
120
- // const events = overrideEvents || cardType.events || {}
121
- // Object.entries(parameters).forEach(([k, v]) => {
122
- // if (k === "cardType") return
123
- // if (typeof v === "object") {
124
- // const cd = v as PiCardDef // speculative
125
- // if (cd.cardType) {
126
- // const cardName = `${name}/${k}`
127
- // v = _registerCard(cardName, cd, registerReducer)
128
- // }
129
- // }
130
- // if (
131
- // k.startsWith("on") &&
132
- // processEventParameter(k, v, events, eventMappers, registerReducer, name)
133
- // ) {
134
- // return
135
- // }
136
- // props[k] = v
137
- // })
138
- // cardMappings[name] = { cardType: parameters.cardType, props, eventMappers }
139
- // return name
140
- }
141
-
142
- function _createCardMapping(
143
- name: string,
144
- parameters: PiCardDef,
145
- registerReducer: PiRegisterReducerF,
146
- cardEvents: { [key: string]: string },
147
- ) {
148
- const props = {} as { [k: string]: unknown }
149
- const eventMappers = {} as { [k: string]: (ev: Action) => Action }
150
-
151
- Object.entries(parameters).forEach(([k, v]) => {
152
- if (k === "cardType") return
153
- if (typeof v === "object") {
154
- const cd = v as PiCardDef // speculative
155
- if (cd.cardType) {
156
- const cardName = `${name}/${k}`
157
- v = _registerCard(cardName, cd, registerReducer)
158
- }
159
- }
160
- if (
161
- k.startsWith("on") &&
162
- processEventParameter(k, v, cardEvents, eventMappers, registerReducer, name)
163
- ) {
164
- return
165
- }
166
- props[k] = v
167
- })
168
- cardMappings[name] = { cardType: parameters.cardType, props, eventMappers, cardEvents }
169
- }
170
-
171
- function _registerMetadataCard(
172
- metaName: string,
173
- parameters: PiCardDef,
174
- registerReducer: PiRegisterReducerF,
175
- ): boolean {
176
- let mc = metacardTypes[parameters.cardType]
177
- if (!mc) {
178
- if (framework) {
179
- mc = metacardTypes[`${framework}/${parameters.cardType}`]
180
- }
181
- if (!mc) {
182
- return false
183
- }
184
- }
185
- function registerCard(name: string, parameters: PiCardDef): PiCardRef {
186
- const n = `${metaName}/${name}`
187
- return mc.registerCard(n, parameters)
188
- }
189
- const top = mc.mapper(metaName, parameters, registerCard)
190
- _registerCard(metaName, top, registerReducer, mc.events)
191
- return true
192
- }
193
-
194
- export function createCardDeclaration<Props = {}, Events = {}>(
195
- cardType: string,
196
- ): <S extends ReduxState>(p: PiMapProps<Props, S, Events>) => PiCardDef {
197
- return (p) => ({
198
- ...p,
199
- cardType,
200
- })
201
- }
202
-
203
- function processEventParameter(
204
- propName: string,
205
- value: unknown,
206
- events: { [key: string]: string },
207
- eventMappers: { [k: string]: (ev: Action) => Action },
208
- registerReducer: PiRegisterReducerF,
209
- cardName: string,
210
- ): boolean {
211
- const eva = Object.entries(events).filter((e) => propName.startsWith(e[0]))
212
- if (eva.length === 0) {
213
- logger.warn(
214
- `encountered property '${propName}' for card '${cardName}' which looks like an even but is not defined`,
215
- )
216
- return false
217
- }
218
- // eva could actually return more than one hit if we have similar named events
219
- let handled = false
220
- for (const [evName, actionType] of eva) {
221
- if (propName === evName) {
222
- const r = value as (state: ReduxState, action: CardAction, dispatch: DispatchF) => ReduxState
223
- registerReducer(actionType, (s, a, d) => {
224
- const ca = a as CardAction
225
- if (ca.cardID === cardName) {
226
- s = r(s, ca, d)
227
- }
228
- return s
229
- })
230
- handled = true
231
- break
232
- }
233
- if (propName === `${evName}Mapper`) {
234
- logger.debug("processEventParameter", cardName)
235
-
236
- const m = value as (ev: Action) => Action
237
- eventMappers[evName] = m
238
- handled = true
239
- break
240
- }
241
- }
242
- return handled
243
- }
244
-
245
- export function memo<P, T, S extends ReduxState, C>(
246
- filterF: (state: S, context: StateMapperContext<C>) => P,
247
- mapperF: (partial: P, context: StateMapperContext<C>, state: S) => T,
248
- ): (state: S, context: StateMapperContext<C>) => T {
249
- const lastFilter: { [k: string]: P } = {}
250
- const lastValue: { [k: string]: T } = {}
251
- const isNotFirst: { [k: string]: boolean } = {}
252
-
253
- return (state: S, context: StateMapperContext<C>): T => {
254
- const k = context.cardKey || "-"
255
- const fv = filterF(state, context)
256
- if (isNotFirst[k] && equal(fv, lastFilter[k])) {
257
- // nothing changed
258
- return lastValue[k]
259
- }
260
- lastFilter[k] = fv
261
- const v = mapperF(fv, context, state)
262
- lastValue[k] = v
263
- isNotFirst[k] = true
264
- return v
265
- }
266
- }
18
+ } from "./types";
19
+ import { Action, AnyAction, Dispatch } from "@reduxjs/toolkit";
20
+ import {
21
+ _createCardMapping,
22
+ _updateCardMapping,
23
+ _registerCard,
24
+ cardMappings,
25
+ cardTypes,
26
+ dispatch2registerReducer,
27
+ framework,
28
+ Mapping,
29
+ } from "./register_cards";
30
+
31
+ const logger = getLogger("card");
267
32
 
268
33
  // export type CardProp = {
269
34
  // cardName: PiCardRef
270
35
  // } & { [k: string]: any }
271
36
 
272
- type CompProps = { [k: string]: any }
37
+ type CompProps = { [k: string]: any };
273
38
  type CardInfo = {
274
- mapping: Mapping
275
- cardType: PiRegisterComponent
276
- }
39
+ mapping: Mapping;
40
+ cardType: PiRegisterComponent;
41
+ };
277
42
 
278
43
  export function Card(props: CardProp): JSX.Element {
279
- let cardName: string
44
+ let cardName: string;
280
45
 
281
- const [id, _] = React.useState<number>(Math.floor(Math.random() * 10000))
282
- const dispatch = useDispatch() // never change the order of hooks called
46
+ const [id, _] = React.useState<number>(Math.floor(Math.random() * 10000));
47
+ const dispatch = useDispatch(); // never change the order of hooks called
283
48
 
284
49
  if (typeof props.cardName === "string") {
285
- cardName = props.cardName
50
+ cardName = props.cardName;
286
51
  } else {
287
52
  // lets fix it
288
- cardName = checkForAnonymousCard(props, id, dispatch)
53
+ cardName = checkForAnonymousCard(props, id, dispatch);
289
54
  }
290
55
  if (cardName === "") {
291
- logger.error("card name is not of type string", props.cardName)
292
- return ErrorCard(<div>Unknown type of cardName '{`${props.cardName}`}'</div>)
56
+ logger.error("card name is not of type string", props.cardName);
57
+ return ErrorCard(
58
+ <div>Unknown type of cardName '{`${props.cardName}`}'</div>
59
+ );
293
60
  }
294
61
 
295
- const [info, errCard] = getCardInfo(cardName)
62
+ const [info, errCard] = getCardInfo(cardName);
296
63
  if (errCard) {
297
- return ErrorCard(errCard)
64
+ return ErrorCard(errCard);
298
65
  }
299
66
  if (!info) {
300
- throw new Error("info is empty, should never happen")
67
+ throw new Error("info is empty, should never happen");
301
68
  }
302
- return GenericCard(cardName, props, info, id)
69
+ return GenericCard(cardName, props, info, id);
303
70
  }
304
71
 
305
- function checkForAnonymousCard(props: any, id: number, dispatch: Dispatch<AnyAction>): string {
306
- // const [id, _] = React.useState<number>(Math.floor(Math.random() * 10000))
307
- // const dispatch = useDispatch() // never change the order of hooks called
72
+ export function usePiReducer<S extends ReduxState, A extends ReduxAction>(
73
+ eventType: string,
74
+ mapper: ReduceF<S, A> // (state: S, action: A, dispatch: DispatchF) => S,
75
+ ) {
76
+ const store = useStore();
77
+ const id = useId();
78
+ useEffect(() => {
79
+ const r = (store as any).piReducer as PiReducer;
80
+ return r.register(eventType, mapper, 0, id);
81
+ });
82
+ }
308
83
 
309
- const cardType = props.cardName?.cardType
84
+ function checkForAnonymousCard(
85
+ props: any,
86
+ id: number,
87
+ dispatch: Dispatch<AnyAction>
88
+ ): string {
89
+ const cardType = props.cardName?.cardType;
310
90
  if (!cardType) {
311
- return "" // not sure what that is
91
+ return ""; // not sure what that is
312
92
  }
313
93
  // looks like a potentially unregistered card
314
- let cardName: string
94
+ let cardName: string;
315
95
  if (props.parentCard) {
316
- cardName = `${props.parentCard}/${cardType.split('/').pop()}`
96
+ cardName = `${props.parentCard}/${cardType.split("/").pop()}`;
317
97
  } else {
318
- cardName = cardType
98
+ cardName = cardType;
319
99
  }
320
100
  if (props.cardKey) {
321
- cardName = `${cardName}#${props.cardKey}-${id}`
101
+ cardName = `${cardName}#${props.cardKey}-${id}`;
322
102
  } else {
323
- cardName = `${cardName}#${id}`
103
+ cardName = `${cardName}#${id}`;
324
104
  }
325
- // logger.debug("anonymous", cardName)
326
105
 
327
- const mapping = cardMappings[cardName]
328
- const parameters = props.cardName as PiCardDef
329
- const el = dispatch2registerReducer.find(([d, _]) => d === dispatch)
106
+ const mapping = cardMappings[cardName];
107
+ const parameters = props.cardName as PiCardDef;
108
+ const el = dispatch2registerReducer.find(([d, _]) => d === dispatch);
330
109
  if (!el) {
331
- logger.warn("unexpected missing mapping between dispatcher and reducerF")
332
- return ""
110
+ logger.warn("unexpected missing mapping between dispatcher and reducerF");
111
+ return "";
333
112
  }
334
- const regRed = el[1]
113
+ const regRed = el[1];
335
114
  if (mapping) {
336
115
  // looks like we already processed it
337
116
  // do update props as they may have changed
338
- _createCardMapping(cardName, parameters, regRed, mapping.cardEvents)
117
+ _updateCardMapping(cardName, parameters, regRed, mapping);
339
118
  } else {
340
- _registerCard(cardName, parameters, regRed)
119
+ _registerCard(cardName, parameters, regRed);
341
120
  }
342
- return cardName
121
+ return cardName;
343
122
  }
344
123
 
345
- function GenericCard(cardName: string, props: CardProp, info: CardInfo, id: number) {
124
+ function GenericCard(
125
+ cardName: string,
126
+ props: CardProp,
127
+ info: CardInfo,
128
+ id: number
129
+ ) {
346
130
  const cardProps = useSelector<ReduxState, CompProps>(
347
- (s) => getCardProps(cardName, s, props, info?.mapping),
348
- propEq,
349
- )
350
- const dispatch = useDispatch()
351
-
352
- const extCardProps = appendEventHandlers(info, cardProps, cardName, dispatch)
353
- extCardProps._cls = cls_f(cardName, info.mapping.cardType)
354
- return React.createElement(info.cardType.component, extCardProps, props.children)
131
+ (s) => getCardProps(cardName, s, props),
132
+ propEq
133
+ );
134
+ const dispatch = useDispatch();
135
+
136
+ const extCardProps = appendEventHandlers(info, cardProps, cardName, dispatch);
137
+ extCardProps._cls = cls_f(cardName, info.mapping.cardType);
138
+ return React.createElement(
139
+ info.cardType.component,
140
+ extCardProps,
141
+ props.children
142
+ );
355
143
  }
356
144
 
357
- const EmptyCompProps = {} as CompProps
145
+ const EmptyCompProps = {} as CompProps;
358
146
 
359
147
  function ErrorCard(el: JSX.Element) {
360
148
  // Note: call the EXACT same hooks as 'GenericCard'
361
- useSelector<ReduxState, CompProps>((s) => EmptyCompProps, (a, b) => false)
362
- useDispatch()
363
- return el
149
+ useSelector<ReduxState, CompProps>(
150
+ (s) => EmptyCompProps,
151
+ (a, b) => false
152
+ );
153
+ useDispatch();
154
+ return el;
364
155
  }
365
156
 
366
-
367
157
  function getCardInfo(cardName: string): [CardInfo?, JSX.Element?] {
368
- const mapping = cardMappings[cardName]
158
+ const mapping = cardMappings[cardName];
369
159
  if (!mapping) {
370
- return [undefined, renderUnknownCard(cardName)]
160
+ return [undefined, renderUnknownCard(cardName)];
371
161
  }
372
- let cardType = cardTypes[mapping.cardType]
162
+ let cardType = cardTypes[mapping.cardType];
373
163
  if (!cardType) {
374
164
  if (framework) {
375
- cardType = cardTypes[`${framework}/${mapping.cardType}`]
165
+ cardType = cardTypes[`${framework}/${mapping.cardType}`];
376
166
  }
377
167
  if (!cardType) {
378
- return [undefined, renderUnknownCardType(mapping.cardType)]
168
+ return [undefined, renderUnknownCardType(mapping.cardType)];
379
169
  }
380
170
  }
381
- const info = { mapping, cardType }
382
- return [info, undefined]
171
+ const info = { mapping, cardType };
172
+ return [info, undefined];
383
173
  }
384
174
 
385
-
386
175
  function getCardProps(
387
176
  cardName: string,
388
177
  state: ReduxState,
389
- props: CardProp,
390
- mapping: Mapping,
178
+ props: CardProp
391
179
  ): CompProps {
180
+ const mapping = cardMappings[cardName];
392
181
  const ctxt: StateMapperContext<unknown> = {
393
182
  cardName,
394
183
  cardKey: props.cardKey,
395
184
  ctxtProps: props,
396
- }
185
+ };
397
186
  const init: CompProps = {
398
187
  cardName,
399
188
  cardKey: props.cardKey,
400
- }
189
+ };
401
190
  const cprops = Object.entries(mapping.props).reduce((p, [key, vf]) => {
402
- let v = vf
191
+ let v = vf;
403
192
  if (typeof vf === "function") {
404
- const f = vf as StateMapper<unknown, ReduxState, any>
193
+ const f = vf as StateMapper<unknown, ReduxState, any>;
405
194
  try {
406
- v = f(state, ctxt)
195
+ v = f(state, ctxt);
407
196
  } catch (ex) {
408
- logger.error(`while resolving property '${key}'`, ex)
197
+ logger.error(`while resolving property '${key}'`, ex);
409
198
  }
410
199
  } else if (key in props) {
411
- v = props[key]
200
+ v = props[key];
412
201
  }
413
- p[key] = v
414
- return p
415
- }, init)
416
- return cprops
202
+ p[key] = v;
203
+ return p;
204
+ }, init);
205
+ return cprops;
417
206
  }
418
207
 
419
208
  function cls_f(
420
209
  cardName: string,
421
210
  cardComp: string,
422
- prefix: string = "pi",
211
+ prefix: string = "pi"
423
212
  ): (nodeName: string | string[], styles?: CSSModuleClasses) => string {
424
- const cn = cardName.replaceAll(/[/:]/g, "_")
425
- const cp = cardComp.replaceAll(/[/:]/g, "_")
213
+ const cn = cardName.replaceAll(/[/:]/g, "_");
214
+ const cp = cardComp.replaceAll(/[/:]/g, "_");
426
215
  return (nodeName: string | string[], styles?: CSSModuleClasses): string => {
427
- const na: string[] = typeof nodeName === "string" ? [nodeName] : nodeName
428
- const ca = [] as string[]
216
+ const na: string[] = typeof nodeName === "string" ? [nodeName] : nodeName;
217
+ const ca = [] as string[];
429
218
  na.forEach((n) => {
430
- const s = styles?.[n]
431
- if (s) ca.push(s)
432
-
433
- const nn = n.replaceAll(/[/:]/g, "_")
434
- ca.push(`${prefix}-${cn}-${nn}`)
435
- ca.push(`${prefix}-${cp}-${nn}`)
436
- })
437
- return ca.join(" ")
438
- }
219
+ const s = styles?.[n];
220
+ if (s) ca.push(s);
221
+
222
+ const nn = n.replaceAll(/[/:]/g, "_");
223
+ ca.push(`${prefix}-${cn}-${nn}`);
224
+ ca.push(`${prefix}-${cp}-${nn}`);
225
+ });
226
+ return ca.join(" ");
227
+ };
439
228
  }
440
229
 
441
230
  function propEq(oldP: CompProps, newP: CompProps): boolean {
442
- let isUnchanged = equal(oldP, newP)
231
+ let isUnchanged = equal(oldP, newP);
443
232
  // for (const [k, v] of Object.entries(newP)) {
444
233
  // const ov = oldP[k]
445
234
  // if (ov !== v) {
@@ -450,179 +239,190 @@ function propEq(oldP: CompProps, newP: CompProps): boolean {
450
239
  // }
451
240
  // }
452
241
  // }
453
- RegisterCardState.changed(newP.cardName, isUnchanged, newP)
454
- return isUnchanged
242
+ RegisterCardState.changed(newP.cardName, isUnchanged, newP);
243
+ return isUnchanged;
455
244
  }
456
245
 
457
246
  function appendEventHandlers(
458
247
  info: CardInfo,
459
248
  cardProps: CompProps,
460
249
  cardName: string,
461
- dispatch: Dispatch<Action>,
250
+ dispatch: Dispatch<Action>
462
251
  ): CompProps {
463
- RegisterCardState.props(cardName, cardProps, dispatch)
252
+ RegisterCardState.props(cardName, cardProps, dispatch);
464
253
  const cp: CompProps = {
465
254
  ...cardProps,
466
- _dispatch: (a: AnyAction) => dispatch(a)
467
- }
255
+ _dispatch: (a: AnyAction) => dispatch(a),
256
+ };
468
257
 
469
- const events = info.cardType?.events
258
+ const events = info.cardType?.events;
470
259
  if (!events) {
471
- return cp
260
+ return cp;
472
261
  }
473
- const eventMappers = info.mapping.eventMappers
262
+ const eventMappers = info.mapping.eventMappers;
474
263
  Object.entries(events).forEach(([name, actionType]) => {
475
- const m = eventMappers[name]
264
+ const m = eventMappers[name];
476
265
  if (m) {
477
- logger.debug("setup mapper", cardName)
266
+ logger.debug("setup mapper", cardName);
478
267
  cp[name] = (a: AnyAction) => {
479
- a.cardID = cardName
480
- const a2 = m(a)
481
- if (a2) dispatch(a2)
482
- }
268
+ a.cardID = cardName;
269
+ const a2 = m(a);
270
+ if (a2) dispatch(a2);
271
+ };
483
272
  } else {
484
273
  cp[name] = (a: AnyAction) => {
485
- a.type = actionType
486
- a.cardID = cardName
487
- dispatch(a)
488
- }
274
+ a.type = actionType;
275
+ a.cardID = cardName;
276
+ dispatch(a);
277
+ };
489
278
  }
490
- })
491
- return cp
279
+ });
280
+ return cp;
492
281
  }
493
282
 
494
283
  function renderUnknownCard(cardName: string): JSX.Element {
495
- return <div>Unknown card '{cardName}'</div>
284
+ return <div>Unknown card '{cardName}'</div>;
496
285
  }
497
286
 
498
287
  function renderUnknownCardType(cardType: string): JSX.Element {
499
- return <div>Unknown card type '{cardType}'</div>
288
+ return <div>Unknown card type '{cardType}'</div>;
500
289
  }
501
290
 
502
291
  // Adding card state to redux state for debugging
503
292
 
504
- export const UPDATE_STATE_ACTION = "pi/card/update_state"
293
+ export const UPDATE_STATE_ACTION = "pi/card/update_state";
505
294
 
506
295
  type CardState = {
507
296
  props: (
508
297
  cardName: string,
509
298
  cardProps: CompProps,
510
- dispatch: Dispatch<Action>,
511
- ) => void
512
- changed: (cardName: string, isUnchanged: boolean, props: CompProps) => void
513
- reducer: (state: ReduxState, action: Action) => ReduxState
514
- }
299
+ dispatch: Dispatch<Action>
300
+ ) => void;
301
+ changed: (cardName: string, isUnchanged: boolean, props: CompProps) => void;
302
+ reducer: (state: ReduxState, action: Action) => ReduxState;
303
+ };
515
304
 
516
- export const RegisterCardState = createCardState()
305
+ export const RegisterCardState = createCardState();
517
306
 
518
307
  function createCardState(): CardState {
519
308
  type S = {
520
- cardProps?: CompProps
521
- changedAt: number
522
- reportedAt: number
523
- }
524
- const s: { [name: string]: S } = {}
525
- let dispatch: Dispatch<Action>
526
- let timer: number
527
- let lastReport = 0
309
+ cardProps?: CompProps;
310
+ changedAt: number;
311
+ reportedAt: number;
312
+ };
313
+ const s: { [name: string]: S } = {};
314
+ let dispatch: Dispatch<Action>;
315
+ let timer: number;
316
+ let lastReport = 0;
528
317
 
529
318
  // const timer
530
319
  const getS = (cardName: string, props: CompProps): S => {
531
- const name = cardName
532
- let e = s[name]
320
+ const name = cardName;
321
+ let e = s[name];
533
322
  if (!e) {
534
- const ts = Date.now()
323
+ const ts = Date.now();
535
324
  e = {
536
325
  changedAt: ts,
537
326
  reportedAt: ts,
538
- } as S
539
- s[name] = e
540
- resetTimer()
327
+ } as S;
328
+ s[name] = e;
329
+ resetTimer();
541
330
  }
542
- return e
543
- }
331
+ return e;
332
+ };
544
333
  const resetTimer = () => {
545
334
  if (timer) {
546
- clearTimeout(timer)
335
+ clearTimeout(timer);
547
336
  }
548
337
  timer = window.setTimeout(() => {
549
338
  //logger.debug("... timer went off") // , s, dispatch)
550
339
  if (dispatch) {
551
- const changed = Object.values(s).filter((s) => s.changedAt > lastReport)
340
+ const changed = Object.values(s).filter(
341
+ (s) => s.changedAt > lastReport
342
+ );
552
343
  if (changed.length > 0) {
553
- clearTimeout(timer) // just in case
554
- dispatch({ type: UPDATE_STATE_ACTION })
344
+ clearTimeout(timer); // just in case
345
+ dispatch({ type: UPDATE_STATE_ACTION });
555
346
  }
556
347
  }
557
- }, 1000)
558
- }
348
+ }, 1000);
349
+ };
559
350
  const props = (
560
351
  cardName: string,
561
352
  cardProps: CompProps,
562
- _dispatch: Dispatch<Action>,
353
+ _dispatch: Dispatch<Action>
563
354
  ) => {
564
- const e = getS(cardName, cardProps)
565
- e.cardProps = cardProps
566
- dispatch = _dispatch
567
- }
568
- const changed = (cardName: string, isUnchanged: boolean, props: CompProps) => {
569
- const e = getS(cardName, props)
570
- e.reportedAt = Date.now()
355
+ const e = getS(cardName, cardProps);
356
+ e.cardProps = cardProps;
357
+ dispatch = _dispatch;
358
+ };
359
+ const changed = (
360
+ cardName: string,
361
+ isUnchanged: boolean,
362
+ props: CompProps
363
+ ) => {
364
+ const e = getS(cardName, props);
365
+ e.reportedAt = Date.now();
571
366
  if (!isUnchanged) {
572
- logger.debug("card has changed:", cardName)
573
- e.changedAt = Date.now()
574
- resetTimer()
367
+ logger.debug("card has changed:", cardName);
368
+ e.changedAt = Date.now();
369
+ resetTimer();
575
370
  }
576
- }
371
+ };
577
372
  const reducer = (state: ReduxState): ReduxState => {
578
373
  const pi = Object.values(s)
579
374
  .filter((s) => s.reportedAt > lastReport)
580
375
  .reduce((p, s) => {
581
- const cname = s.cardProps?.cardName
376
+ const cname = s.cardProps?.cardName;
582
377
  if (!cname) {
583
- logger.warn("Unexpected missing card name", s)
584
- return p
378
+ logger.warn("Unexpected missing card name", s);
379
+ return p;
585
380
  }
586
- const name = cname
587
- const props = copySafeProps(s.cardProps || {})
588
- delete props.cardName
589
- delete props._cls
590
- p[name] = props
591
- return p
592
- }, {} as { [k: string]: any })
593
- state.pihanga = pi
594
- lastReport = Date.now()
595
- return state
596
- }
597
- return { props, changed, reducer }
381
+ const name = cname;
382
+ const props = copySafeProps(s.cardProps || {});
383
+ delete props.cardName;
384
+ delete props._cls;
385
+ p[name] = props;
386
+ return p;
387
+ }, {} as { [k: string]: any });
388
+ state.pihanga = pi;
389
+ lastReport = Date.now();
390
+ return state;
391
+ };
392
+ return { props, changed, reducer };
598
393
  }
599
394
 
600
395
  function copySafeProps(props: CompProps): CompProps {
601
396
  return Object.entries(props).reduce((p, [k, v]) => {
602
397
  // const ok = (typeof v === 'undefined' || typeof v === 'string' || typeof v === 'boolean' || typeof v === 'number' || Array.isArray(v));
603
- const sv = makeSafe(v)
604
- p[k] = sv
605
- return p
606
- }, {} as CompProps)
398
+ const sv = makeSafe(v);
399
+ p[k] = sv;
400
+ return p;
401
+ }, {} as CompProps);
607
402
  }
608
403
 
609
404
  function makeSafe(v: any): any {
610
- const t = typeof v
611
- if (t === 'undefined' || t === 'string' || t === 'boolean' || t === 'number') {
612
- return v
613
- }
614
- if (t === 'function') {
615
- return "f(...)"
405
+ const t = typeof v;
406
+ if (
407
+ t === "undefined" ||
408
+ t === "string" ||
409
+ t === "boolean" ||
410
+ t === "number"
411
+ ) {
412
+ return v;
413
+ }
414
+ if (t === "function") {
415
+ return "f(...)";
616
416
  }
617
417
  if (Array.isArray(v)) {
618
- return v.map(makeSafe)
418
+ return v.map(makeSafe);
619
419
  }
620
- if (t === 'object') {
420
+ if (t === "object") {
621
421
  return Object.entries(v).reduce((p, [k, v]) => {
622
- p[k] = makeSafe(v)
623
- return p
624
- }, {} as { [k: string]: any })
422
+ p[k] = makeSafe(v);
423
+ return p;
424
+ }, {} as { [k: string]: any });
625
425
  }
626
- logger.warn(">>> reject", v, typeof v)
627
- return "..."
426
+ logger.warn(">>> reject", v, typeof v);
427
+ return "...";
628
428
  }