@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.
package/src/card.tsx CHANGED
@@ -5,266 +5,19 @@ import equal from "deep-equal"
5
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,
15
10
  PiRegisterComponent,
16
- PiRegisterMetaCard,
17
11
  PiRegisterReducerF,
18
12
  ReduxState,
19
- RegisterCardF,
20
13
  StateMapper,
21
14
  StateMapperContext,
22
15
  } from "./types"
23
16
  import { Action, AnyAction, Dispatch } from "@reduxjs/toolkit"
17
+ import { _createCardMapping, _updateCardMapping, _registerCard, cardMappings, cardTypes, dispatch2registerReducer, framework, Mapping } from './register_cards'
24
18
 
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
19
  const logger = getLogger("card")
50
20
 
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
- }
267
-
268
21
  // export type CardProp = {
269
22
  // cardName: PiCardRef
270
23
  // } & { [k: string]: any }
@@ -303,9 +56,6 @@ export function Card(props: CardProp): JSX.Element {
303
56
  }
304
57
 
305
58
  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
308
-
309
59
  const cardType = props.cardName?.cardType
310
60
  if (!cardType) {
311
61
  return "" // not sure what that is
@@ -322,7 +72,6 @@ function checkForAnonymousCard(props: any, id: number, dispatch: Dispatch<AnyAct
322
72
  } else {
323
73
  cardName = `${cardName}#${id}`
324
74
  }
325
- // logger.debug("anonymous", cardName)
326
75
 
327
76
  const mapping = cardMappings[cardName]
328
77
  const parameters = props.cardName as PiCardDef
@@ -335,7 +84,7 @@ function checkForAnonymousCard(props: any, id: number, dispatch: Dispatch<AnyAct
335
84
  if (mapping) {
336
85
  // looks like we already processed it
337
86
  // do update props as they may have changed
338
- _createCardMapping(cardName, parameters, regRed, mapping.cardEvents)
87
+ _updateCardMapping(cardName, parameters, regRed, mapping)
339
88
  } else {
340
89
  _registerCard(cardName, parameters, regRed)
341
90
  }
@@ -344,7 +93,7 @@ function checkForAnonymousCard(props: any, id: number, dispatch: Dispatch<AnyAct
344
93
 
345
94
  function GenericCard(cardName: string, props: CardProp, info: CardInfo, id: number) {
346
95
  const cardProps = useSelector<ReduxState, CompProps>(
347
- (s) => getCardProps(cardName, s, props, info?.mapping),
96
+ (s) => getCardProps(cardName, s, props),
348
97
  propEq,
349
98
  )
350
99
  const dispatch = useDispatch()
@@ -387,8 +136,8 @@ function getCardProps(
387
136
  cardName: string,
388
137
  state: ReduxState,
389
138
  props: CardProp,
390
- mapping: Mapping,
391
139
  ): CompProps {
140
+ const mapping = cardMappings[cardName]
392
141
  const ctxt: StateMapperContext<unknown> = {
393
142
  cardName,
394
143
  cardKey: props.cardKey,
@@ -1,6 +1,4 @@
1
- import React from "react"
2
1
  import ReactDOM from "react-dom/client"
3
- import { Provider } from "react-redux"
4
2
  import { Dispatch } from "react"
5
3
 
6
4
  import {
@@ -13,8 +11,11 @@ import {
13
11
  RegisterCardF,
14
12
  MetaCardMapperF,
15
13
  PiRegisterMetaCard,
14
+ PiMapProps,
15
+ WindowProps,
16
+ GenericCardParameterT,
16
17
  } from "./types"
17
- import { Card, registerCard, registerCardComponent, registerMetacard } from "./card"
18
+ import { createCardDeclaration, registerCard, registerCardComponent, registerMetacard, updateOrRegisterCard } from "./register_cards"
18
19
  import { createReducer } from "./reducer"
19
20
  import { ON_INIT_ACTION, currentRoute, init as routerInit } from "./router"
20
21
 
@@ -32,6 +33,7 @@ import {
32
33
  registerPOST,
33
34
  registerPUT,
34
35
  } from "./rest"
36
+ import { RootComponent } from "./root"
35
37
  const logger = getLogger("root")
36
38
 
37
39
  export type {
@@ -47,9 +49,11 @@ export type {
47
49
  StateMapper,
48
50
  PiReducer,
49
51
  PiRegisterMetaCard,
52
+ WindowProps,
50
53
  } from "./types"
51
54
  export { registerActions, actionTypesToEvents, createOnAction } from "./redux"
52
- export { Card, memo, createCardDeclaration, isCardRef } from "./card"
55
+ export { Card } from "./card"
56
+ export { memo, createCardDeclaration, isCardRef } from "./register_cards"
53
57
  export { getLogger } from "./logger"
54
58
  export type { PiCardProps, PiCardRef } from "./types"
55
59
  export type { ErrorAction as RestErrorAction } from "./rest"
@@ -59,7 +63,12 @@ export { showPage, onInit, onShowPage, createShowPageAction, onNavigateToPage }
59
63
  export type { ShowPageEvent, NavigateToPageEvent } from "./router"
60
64
 
61
65
  export interface PiRegister {
66
+ //window(parameters: PiCardDef): PiCardRef
67
+
68
+ window<S extends ReduxState>(parameters: PiMapProps<WindowProps, S, {}>): PiCardRef
69
+
62
70
  card(name: string, parameters: PiCardDef): PiCardRef
71
+ updateCard(name: string, parameters: { [key: string]: GenericCardParameterT }): PiCardRef
63
72
 
64
73
  cardComponent(declaration: PiRegisterComponent): void
65
74
 
@@ -160,8 +169,16 @@ export function start<S extends Partial<ReduxState>>(
160
169
  dispatchF = store.dispatch
161
170
 
162
171
  const card = registerCard(piReducer.register, dispatchF)
172
+ const updateCard = updateOrRegisterCard(piReducer.register, dispatchF)
173
+ const window = <S extends ReduxState>(p: PiMapProps<WindowProps, S, {}>): PiCardRef => {
174
+ return card("_window", { cardType: "framework", ...p })
175
+ }
176
+
177
+
163
178
  const register: PiRegister = {
179
+ window,
164
180
  card,
181
+ updateCard,
165
182
  cardComponent: registerCardComponent,
166
183
  metaCard: registerMetacard(card),
167
184
  reducer: piReducer,
@@ -181,13 +198,3 @@ export function start<S extends Partial<ReduxState>>(
181
198
 
182
199
  return register
183
200
  }
184
-
185
- function RootComponent(store: any) {
186
- return (
187
- <React.StrictMode>
188
- <Provider store={store}>
189
- <Card cardName="page" parentCard="" />
190
- </Provider>
191
- </React.StrictMode>
192
- )
193
- }
package/src/reducer.ts CHANGED
@@ -14,6 +14,7 @@ import { RegisterCardState, UPDATE_STATE_ACTION } from "./card"
14
14
  import StackTrace from "stacktrace-js"
15
15
  import { getLogger } from "./logger"
16
16
  import { Dispatch } from "react"
17
+ import { currentRoute } from "./router"
17
18
 
18
19
  const logger = getLogger("reducer")
19
20
 
@@ -21,6 +22,7 @@ type ReducerDef<S extends ReduxState, A extends ReduxAction> = {
21
22
  mapperMany?: ReduceF<S, A>
22
23
  mapperOnce?: ReduceOnceF<S, A>
23
24
  priority?: number
25
+ key?: string
24
26
  definedIn?: StackTrace.StackFrame
25
27
  }
26
28
 
@@ -40,34 +42,21 @@ export function createReducer(
40
42
  ): ReduxState => {
41
43
  const s = state || initialState
42
44
  const ra = mappings[action.type]
43
- if (!ra || ra.length === 0) {
45
+ const rany = mappings["*"]
46
+ if ((!ra || ra.length === 0) && (!rany || rany.length === 0)) {
44
47
  return s
45
48
  }
46
49
 
47
50
  const nextState = produce<ReduxState, ReduxState>(s, (draft) => {
48
- const rout: ReducerDef<ReduxState, Action<any>>[] = []
49
- const outDraft = ra.reduce((d, m) => {
50
- try {
51
- if (m.mapperMany) {
52
- const s = m.mapperMany(d, action, delayedDispatcher)
53
- rout.push(m)
54
- return s
55
- } else if (m.mapperOnce) {
56
- const [s, flag] = m.mapperOnce(d, action, delayedDispatcher)
57
- if (!flag) {
58
- rout.push(m)
59
- }
60
- return s
61
- } else {
62
- return d
63
- }
64
- } catch (err: any) {
65
- logger.error(err.message, m.definedIn)
66
- return d
67
- }
68
- }, draft)
69
- mappings[action.type] = rout
70
- return outDraft
51
+ if (ra) {
52
+ const rout = _reduce(ra, draft, action, delayedDispatcher)
53
+ mappings[action.type] = rout
54
+ }
55
+ if (rany) {
56
+ const rout2 = _reduce(rany, draft, action, delayedDispatcher)
57
+ mappings["*"] = rout2
58
+ }
59
+ return
71
60
  })
72
61
  return nextState
73
62
  }
@@ -79,8 +68,9 @@ export function createReducer(
79
68
  eventType: string,
80
69
  mapper: ReduceF<S, A>,
81
70
  priority: number = 0,
71
+ key: string | undefined = undefined
82
72
  ): void => {
83
- addReducer(eventType, { mapperMany: mapper, priority })
73
+ addReducer(eventType, { mapperMany: mapper, priority, key })
84
74
  }
85
75
 
86
76
  const registerOneShot: PiRegisterOneShotReducerF = <
@@ -88,10 +78,11 @@ export function createReducer(
88
78
  A extends ReduxAction,
89
79
  >(
90
80
  eventType: string,
91
- mapper: (state: S, action: A, dispatch: DispatchF) => [S, boolean],
81
+ mapper: (state: S, action: A, dispatch: DispatchF) => boolean,
92
82
  priority: number = 0,
83
+ key: string | undefined = undefined
93
84
  ): void => {
94
- addReducer(eventType, { mapperOnce: mapper, priority })
85
+ addReducer(eventType, { mapperOnce: mapper, priority, key })
95
86
  }
96
87
 
97
88
  function addReducer<S extends ReduxState, A extends ReduxAction>(
@@ -99,7 +90,11 @@ export function createReducer(
99
90
  reducerDef: ReducerDef<S, A>,
100
91
  // mappings: { [k: string]: ReducerDef<ReduxState, Action>[] }
101
92
  ) {
102
- const m = mappings[eventType] || []
93
+ let m = mappings[eventType] || []
94
+ if (reducerDef.key) {
95
+ // remove reducer with same key - if there is one
96
+ m = m.filter((r) => r.key !== reducerDef.key)
97
+ }
103
98
  m.push(reducerDef as any as ReducerDef<ReduxState, Action<any>>) // keep typing happy
104
99
  m.sort((a, b) => (b.priority || 0) - (a.priority || 0))
105
100
  mappings[eventType] = m
@@ -125,3 +120,28 @@ export function createReducer(
125
120
 
126
121
  return [reducer, piReducer]
127
122
  }
123
+
124
+ function _reduce(
125
+ ra: ReducerDef<ReduxState, Action>[],
126
+ draft: ReduxState,
127
+ action: Action,
128
+ delayedDispatcher: (a: any) => void,
129
+ ): ReducerDef<ReduxState, Action<any>>[] {
130
+ const rout: ReducerDef<ReduxState, Action<any>>[] = []
131
+ ra.forEach((m) => {
132
+ try {
133
+ if (m.mapperMany) {
134
+ m.mapperMany(draft, action, delayedDispatcher)
135
+ rout.push(m)
136
+ } else if (m.mapperOnce) {
137
+ const flag = m.mapperOnce(draft, action, delayedDispatcher)
138
+ if (!flag) {
139
+ rout.push(m)
140
+ }
141
+ }
142
+ } catch (err: any) {
143
+ logger.error(err.message, m.definedIn)
144
+ }
145
+ })
146
+ return rout
147
+ }