@pihanga2/core 0.2.0 → 0.3.0

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.
@@ -0,0 +1,308 @@
1
+ import equal from "deep-equal"
2
+ import { getLogger } from "./logger"
3
+ import {
4
+ CSSModuleClasses,
5
+ CardAction,
6
+ CardProp,
7
+ DispatchF,
8
+ GenericCardParameterT,
9
+ MetaCardMapperF,
10
+ PiCardDef,
11
+ PiCardRef,
12
+ PiMapProps,
13
+ PiRegisterComponent,
14
+ PiRegisterMetaCard,
15
+ PiRegisterReducerF,
16
+ ReduxState,
17
+ RegisterCardF,
18
+ StateMapper,
19
+ StateMapperContext,
20
+ } from "./types"
21
+ import { Action, AnyAction, Dispatch } from "@reduxjs/toolkit"
22
+
23
+ const logger = getLogger("card-register")
24
+
25
+ export function isCardRef(p: any): boolean {
26
+ return (typeof p === "object" && p.cardType !== undefined)
27
+ }
28
+
29
+ export type Mapping = {
30
+ cardType: string
31
+ props: { [k: string]: unknown }
32
+ eventMappers: { [k: string]: (ev: Action) => Action | null }
33
+ cardEvents: { [key: string]: string }
34
+ parameters: PiCardDef // original
35
+ }
36
+
37
+ export type MetaCard = {
38
+ type: string
39
+ registerCard: RegisterCardF
40
+ mapper: MetaCardMapperF
41
+ events?: { [key: string]: string }
42
+ }
43
+
44
+ export const cardTypes: { [k: string]: PiRegisterComponent } = {}
45
+ export const metacardTypes: { [k: string]: MetaCard } = {}
46
+
47
+ export let framework: string // name of active UI framework
48
+ export const cardMappings: { [k: string]: Mapping } = {}
49
+ export const dispatch2registerReducer: [React.Dispatch<any>, PiRegisterReducerF][] = []
50
+
51
+
52
+ export function registerCardComponent(card: PiRegisterComponent): void {
53
+ if (cardTypes[card.name]) {
54
+ logger.warn(`Overwriting definition for card type "${card.name}"`)
55
+ }
56
+ logger.info(`Register card type "${card.name}"`)
57
+ if (!framework) {
58
+ // set default framework
59
+ const na = card.name.split("/")
60
+ if (na.length >= 2) {
61
+ framework = na[0]
62
+ logger.info(`Setting UI framework to "${framework}"`)
63
+ }
64
+ }
65
+ cardTypes[card.name] = card
66
+ }
67
+
68
+ export function registerMetacard(registerCard: RegisterCardF) {
69
+ function f<C>(declaration: PiRegisterMetaCard) {
70
+ const { type, mapper, events } = declaration
71
+ if (metacardTypes[type]) {
72
+ logger.warn(`Overwriting definition for meta card type "${type}"`)
73
+ }
74
+ logger.info(`Register meta card type "${type}"`)
75
+ metacardTypes[type] = { type, registerCard, mapper, events }
76
+ }
77
+ return f
78
+ }
79
+
80
+
81
+ export function registerCard(
82
+ registerReducer: PiRegisterReducerF,
83
+ dispatchF: React.Dispatch<any>,
84
+ ) {
85
+ // to be used by dynamically registered cards
86
+ dispatch2registerReducer.push([dispatchF, registerReducer])
87
+ return (name: string, parameters: PiCardDef): PiCardRef => {
88
+ return _registerCard(name, parameters, registerReducer)
89
+ }
90
+ }
91
+
92
+ export function updateOrRegisterCard(
93
+ registerReducer: PiRegisterReducerF,
94
+ dispatchF: React.Dispatch<any>,
95
+ ) {
96
+ // to be used by dynamically registered cards
97
+ dispatch2registerReducer.push([dispatchF, registerReducer])
98
+ return (name: string, parameters: { [key: string]: GenericCardParameterT }): PiCardRef => {
99
+ return _updateCard(name, parameters, registerReducer)
100
+ }
101
+ }
102
+
103
+ export function _registerCard(
104
+ name: string,
105
+ parameters: PiCardDef,
106
+ registerReducer: PiRegisterReducerF,
107
+ overrideEvents?: { [key: string]: string },
108
+ ): PiCardRef {
109
+ if (cardMappings[name]) {
110
+ logger.warn(`Overwriting definition for card "${name}"`)
111
+ }
112
+ let cardType = cardTypes[parameters.cardType]
113
+ if (!cardType) {
114
+ if (framework) {
115
+ cardType = cardTypes[`${framework}/${parameters.cardType}`]
116
+ }
117
+ if (!cardType) {
118
+ // maybe it's a metadata card
119
+ if (_registerMetadataCard(name, parameters, registerReducer)) {
120
+ return name
121
+ }
122
+ logger.warn("unknown card type", parameters.cardType)
123
+ return name
124
+ }
125
+ }
126
+
127
+ const events = overrideEvents || cardType.events || {}
128
+ _createCardMapping(name, parameters, registerReducer, events)
129
+ return name
130
+ }
131
+
132
+ export function _updateCard(
133
+ name: string,
134
+ parameters: { [key: string]: GenericCardParameterT },
135
+ registerReducer: PiRegisterReducerF,
136
+ overrideEvents?: { [key: string]: string },
137
+ ): PiCardRef {
138
+ const mappings = cardMappings[name]
139
+ if (!mappings) {
140
+ // first time
141
+ if (!parameters.cardType) {
142
+ logger.warn("missing 'cardType'", name)
143
+ return name
144
+ }
145
+ const p: any = parameters
146
+ return _registerCard(name, p, registerReducer, overrideEvents)
147
+ }
148
+
149
+ const p = { ...mappings.parameters, ...parameters }
150
+ _createCardMapping(name, p, registerReducer, mappings.cardEvents)
151
+ return name
152
+
153
+ }
154
+
155
+ export function _createCardMapping(
156
+ name: string,
157
+ parameters: PiCardDef,
158
+ registerReducer: PiRegisterReducerF,
159
+ cardEvents: { [key: string]: string },
160
+ ) {
161
+ const props = {} as { [k: string]: unknown }
162
+ const eventMappers = {} as { [k: string]: (ev: Action) => Action }
163
+
164
+ Object.entries(parameters).forEach(([k, v]) => {
165
+ if (k === "cardType") return
166
+ if (typeof v === "object") {
167
+ const cd = v as PiCardDef // speculative
168
+ if (cd.cardType) {
169
+ const cardName = `${name}/${k}`
170
+ v = _registerCard(cardName, cd, registerReducer)
171
+ }
172
+ }
173
+ if (
174
+ k.startsWith("on") &&
175
+ processEventParameter(k, v, cardEvents, eventMappers, registerReducer, name)
176
+ ) {
177
+ return
178
+ }
179
+ props[k] = v
180
+ })
181
+ cardMappings[name] = { cardType: parameters.cardType, props, eventMappers, cardEvents, parameters }
182
+ }
183
+
184
+ export function _updateCardMapping(
185
+ name: string,
186
+ parameters: PiCardDef,
187
+ registerReducer: PiRegisterReducerF,
188
+ mappings: Mapping,
189
+ ) {
190
+ return _createCardMapping(name, parameters, registerReducer, mappings.cardEvents)
191
+ }
192
+
193
+ function _registerMetadataCard(
194
+ metaName: string,
195
+ parameters: PiCardDef,
196
+ registerReducer: PiRegisterReducerF,
197
+ ): boolean {
198
+ let mc = metacardTypes[parameters.cardType]
199
+ if (!mc) {
200
+ if (framework) {
201
+ mc = metacardTypes[`${framework}/${parameters.cardType}`]
202
+ }
203
+ if (!mc) {
204
+ return false
205
+ }
206
+ }
207
+ function registerCard(name: string, parameters: PiCardDef): PiCardRef {
208
+ const n = `${metaName}/${name}`
209
+ return mc.registerCard(n, parameters)
210
+ }
211
+ const top = mc.mapper(metaName, parameters, registerCard)
212
+ _registerCard(metaName, top, registerReducer, mc.events)
213
+ return true
214
+ }
215
+
216
+ // NOT IMPLEMENTED YET
217
+ // function _updateMetadataCard(
218
+ // metaName: string,
219
+ // parameters: PiCardDef,
220
+ // registerReducer: PiRegisterReducerF,
221
+ // ): boolean {
222
+ // let mc = metacardTypes[parameters.cardType]
223
+ // if (!mc) {
224
+ // if (framework) {
225
+ // mc = metacardTypes[`${framework}/${parameters.cardType}`]
226
+ // }
227
+ // if (!mc) {
228
+ // return false
229
+ // }
230
+ // }
231
+ // function updateCard(name: string, parameters: PiCardDef): PiCardRef {
232
+ // const n = `${metaName}/${name}`
233
+ // return mc.updateCard(n, parameters)
234
+ // }
235
+ // const top = mc.mapper(metaName, parameters, updateCard)
236
+ // _updateCard(metaName, top, registerReducer, mc.events)
237
+ // return true
238
+ // }
239
+
240
+ export function createCardDeclaration<Props = {}, Events = {}>(
241
+ cardType: string,
242
+ ): <S extends ReduxState>(p: PiMapProps<Props, S, Events>) => PiCardDef {
243
+ return (p) => ({
244
+ ...p,
245
+ cardType,
246
+ })
247
+ }
248
+
249
+ function processEventParameter(
250
+ propName: string,
251
+ value: unknown,
252
+ events: { [key: string]: string },
253
+ eventMappers: { [k: string]: (ev: Action) => Action },
254
+ registerReducer: PiRegisterReducerF,
255
+ cardName: string,
256
+ ): boolean {
257
+ const eva = Object.entries(events).find(([n, _]) => {
258
+ return propName === n || propName === `${n}Mapper`
259
+ })
260
+ if (!eva) {
261
+ logger.warn(
262
+ `encountered property '${propName}' for card '${cardName}' which looks like an even but is not defined`,
263
+ )
264
+ return false
265
+ }
266
+
267
+ const [evName, actionType] = eva
268
+ if (propName === evName) {
269
+ const r = value as (state: ReduxState, action: CardAction, dispatch: DispatchF) => ReduxState
270
+ registerReducer(actionType, (s, a, d) => {
271
+ const ca = a as CardAction
272
+ if (ca.cardID === cardName) {
273
+ s = r(s, ca, d)
274
+ }
275
+ return s
276
+ }, 0, `${cardName}|${propName}`)
277
+ }
278
+ if (propName === `${evName}Mapper`) {
279
+ logger.debug("processEventParameter", cardName)
280
+
281
+ const m = value as (ev: Action) => Action
282
+ eventMappers[evName] = m
283
+ }
284
+ return true
285
+ }
286
+
287
+ export function memo<P, T, S extends ReduxState, C>(
288
+ filterF: (state: S, context: StateMapperContext<C>) => P,
289
+ mapperF: (partial: P, context: StateMapperContext<C>, state: S) => T,
290
+ ): (state: S, context: StateMapperContext<C>) => T {
291
+ const lastFilter: { [k: string]: P } = {}
292
+ const lastValue: { [k: string]: T } = {}
293
+ const isNotFirst: { [k: string]: boolean } = {}
294
+
295
+ return (state: S, context: StateMapperContext<C>): T => {
296
+ const k = context.cardKey || "-"
297
+ const fv = filterF(state, context)
298
+ if (isNotFirst[k] && equal(fv, lastFilter[k])) {
299
+ // nothing changed
300
+ return lastValue[k]
301
+ }
302
+ lastFilter[k] = fv
303
+ const v = mapperF(fv, context, state)
304
+ lastValue[k] = v
305
+ isNotFirst[k] = true
306
+ return v
307
+ }
308
+ }
package/src/rest/types.ts CHANGED
@@ -57,7 +57,7 @@ export type RegisterGenericProps<
57
57
  reply: R,
58
58
  dispatcher: DispatchF,
59
59
  result: ResultAction<A>,
60
- ) => S
60
+ ) => void
61
61
  error?: (
62
62
  state: S,
63
63
  error: ErrorAction<A>,
package/src/root.tsx ADDED
@@ -0,0 +1,14 @@
1
+ import React from "react"
2
+ import { Provider } from "react-redux"
3
+
4
+ import { Card } from "./card"
5
+
6
+ export function RootComponent(store: any) {
7
+ return (
8
+ <React.StrictMode>
9
+ <Provider store={store}>
10
+ <Card cardName="_window" parentCard="" />
11
+ </Provider>
12
+ </React.StrictMode>
13
+ )
14
+ }
package/src/types.ts CHANGED
@@ -31,13 +31,13 @@ export type ReduceF<S extends ReduxState, A extends ReduxAction> = (
31
31
  state: S,
32
32
  action: A,
33
33
  dispatch: DispatchF,
34
- ) => S
34
+ ) => void // S
35
35
 
36
36
  export type ReduceOnceF<S extends ReduxState, A extends ReduxAction> = (
37
37
  state: S,
38
38
  action: A,
39
39
  dispatch: DispatchF,
40
- ) => [S, boolean]
40
+ ) => boolean // [S, boolean]
41
41
 
42
42
  export type DispatchF = <T extends ReduxAction>(a: T) => void
43
43
 
@@ -52,6 +52,7 @@ export type PiRegisterReducerF = <S extends ReduxState, A extends ReduxAction>(
52
52
  eventType: string,
53
53
  mapper: ReduceF<S, A>, // (state: S, action: A, dispatch: DispatchF) => S,
54
54
  priority?: number,
55
+ key?: string,
55
56
  ) => void
56
57
 
57
58
  export type PiRegisterOneShotReducerF = <
@@ -76,6 +77,12 @@ export type CardProp = {
76
77
  parentCard: string
77
78
  } & PiDefCtxtProps
78
79
 
80
+ // props for the 'root' of all cards
81
+ export type WindowProps = {
82
+ page: PiCardRef
83
+ framework?: string // select framework to render window
84
+ theme?: any // depends on framework
85
+ }
79
86
 
80
87
  // type which needs to be implemented by card components
81
88
  export type PiCardProps<P, E = {}> = P & {
@@ -84,8 +91,8 @@ export type PiCardProps<P, E = {}> = P & {
84
91
  _cls: (elName: string | string[], styles?: CSSModuleClasses) => string
85
92
  _dispatch: DispatchF
86
93
  } & {
87
- [Key in keyof E]: (ev: E[Key]) => void
88
- }
94
+ [Key in keyof E]: (ev: E[Key]) => void
95
+ }
89
96
 
90
97
  export type CSSModuleClasses = { readonly [key: string]: string }
91
98