@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/dist/card.d.ts +2 -8
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +24 -192
- package/dist/card.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -9
- package/dist/index.js.map +1 -1
- package/dist/reducer.d.ts.map +1 -1
- package/dist/reducer.js +52 -32
- package/dist/reducer.js.map +1 -1
- package/dist/register_cards.d.ts +56 -0
- package/dist/register_cards.d.ts.map +1 -0
- package/dist/register_cards.js +204 -0
- package/dist/register_cards.js.map +1 -0
- package/dist/rest/types.d.ts +1 -1
- package/dist/rest/types.d.ts.map +1 -1
- package/dist/root.d.ts +3 -0
- package/dist/root.d.ts.map +1 -0
- package/dist/root.js +9 -0
- package/dist/root.js.map +1 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/types.d.ts +9 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -10
- package/src/card.tsx +237 -437
- package/src/{index.tsx → index.ts} +25 -14
- package/src/reducer.ts +67 -32
- package/src/register_cards.ts +308 -0
- package/src/rest/types.ts +1 -1
- package/src/root.tsx +14 -0
- package/src/types.ts +14 -5
package/src/card.tsx
CHANGED
|
@@ -1,445 +1,234 @@
|
|
|
1
|
-
import React from
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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(
|
|
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
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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>(
|
|
362
|
-
|
|
363
|
-
|
|
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(
|
|
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 = (
|
|
569
|
-
|
|
570
|
-
|
|
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 (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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 ===
|
|
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
|
}
|