Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
maxott committed Oct 8, 2020
2 parents a954065 + 65a10ec commit 349994e
Show file tree
Hide file tree
Showing 56 changed files with 2,789 additions and 7,542 deletions.
9 changes: 6 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pihanga/core",
"version": "0.4.7",
"version": "0.5.0",
"description": "Core module of the Pihanga framework",
"homepage": "https://github.com/n1analytics/pihanga#readme",
"main": "lib/index.js",
Expand Down Expand Up @@ -32,7 +32,8 @@
"build:es": "babel src --out-dir dist/es",
"build:umd": "cross-env NODE_ENV=development rollup -c -o dist/pihanga-core.js",
"build:umd:min": "cross-env NODE_ENV=production rollup -c -o dist/pihanga-core.min.js",
"build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
"build": "npm run build:commonjs && npm run build:es",
"x-build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
"clean": "rimraf dist coverage",
"format": "prettier --write \"{src,test}/**/*.{js,ts}\" index.d.ts \"docs/**/*.md\"",
"lint": "eslint src test/utils test/components",
Expand All @@ -46,6 +47,7 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"form-data": "^3.0.0",
"history": "^4.9.0",
"humanize-duration": "^3.20.1",
"lodash.every": "^4.6.0",
Expand Down Expand Up @@ -113,5 +115,6 @@
"transform": [
"loose-envify"
]
}
},
"gitHead": "b6072b484fb23e64930847c2cad18f53126f3f39"
}
114 changes: 90 additions & 24 deletions packages/core/src/card.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import isFunction from 'lodash.isfunction';
import { connect } from 'react-redux';
import { getState, registerActions } from './redux';
import { createLogger } from './logger';
import { errorMonitor } from 'form-data';

const logger = createLogger('card.service');

// const cards = {};
const cards2 = {};
const cards = {};
const metaCards = {};
const metaCard2cards = {}; // mapping from instantiated meta cards to their respective cards
const cardComponents = {};

/**
Expand All @@ -31,15 +32,15 @@ export function registerMetaCard(type, transformF) {

export function registerCards(newCards) {
Object.keys(newCards).forEach((k) => {
if (cards2[k]) {
if (cards[k]) {
logger.warn(`Overwriting card "${k}"`);
}
const cardDef = { ...newCards[k] };
registerSingleCard(k, cardDef);
addCard(k, cardDef);
});
}

function registerSingleCard(cardName, cardDef) {
export function addCard(cardName, cardDef) {
const { cardType } = cardDef;
if (!cardType) {
logger.error(`Reject registration of card "${cardName}" due to missing "cardType"`);
Expand All @@ -65,12 +66,24 @@ function registerSingleCard(cardName, cardDef) {
}
return p;
}, { props: {}, eventProps: {} });
cards2[cardName] = {
cardType, props, eventProps, events, defaults,
cards[cardName] = {
cardType, props, eventProps, events, defaults, childCards: [],
};
// cards[cardName] = cardDef;
}

export function removeCard(cardName) {
if (metaCard2cards[cardName]) {
metaCard2cards[cardName].forEach((cn) => removeCard(cn));
delete metaCard2cards[cardName];
return;
}

if (!(delete cards[cardName])) {
logger.warn(`Attempting to remove unknown card '${cardName}'.`);
}
}

export function expandMetaCard(cardType, cardName, cardDef) {
const transform = metaCards[cardType];
if (!transform) {
Expand All @@ -79,6 +92,7 @@ export function expandMetaCard(cardType, cardName, cardDef) {
return;
}
const newCards = transform(cardName, cardDef);
metaCard2cards[cardName] = Object.keys(newCards);
registerCards(newCards);
}

Expand Down Expand Up @@ -116,12 +130,12 @@ export function ref(cardNameOrF, paramName) {
return vd;
}
}
const refDef = cards2[cardName];
const refDef = cards[cardName];
if (!refDef) {
logger.warn(`Requested reference to unknown card "${cardName}"`);
return null;
}
const v = getValue(paramName, refDef.props || {}, state, ctxtProps);
const v = getValue(paramName, refDef.props || {}, state, ctxtProps, cardName);
return v;
};
}
Expand Down Expand Up @@ -155,7 +169,7 @@ export function pQuery(cardName, propName, match, resProps) {
const cName = isFunction(cardName) ? cardName(s) : cardName;
const pName = isFunction(propName) ? propName(s) : propName;
const matchIsFunction = isFunction(match);
const cardNames = cName ? [cName] : Object.keys(cards2);
const cardNames = cName ? [cName] : Object.keys(cards);
const result = [];
const addResult = (cn, pn, v) => {
const params = { cardName: cn };
Expand Down Expand Up @@ -195,13 +209,53 @@ export function pQuery(cardName, propName, match, resProps) {
};
}

function getValue(paramName, cardDef, state, ctxtProps = {}) {
let v = cardDef[paramName];
export function getParamValue(paramName, cardName, state, ctxtProps = {}, includeDefaults = true) {
const cache = cardStates[cardName] || {};
if (cache.state === state) {
return cache.cardState[paramName];
}

const cardDef = cards[cardName];
if (!cardDef) {
throw new Error(`Unknonw card '${cardName}'`);
}

// Step 1: Dynamic value in state.pihanga
const dynState = state.pihanga[cardName];
if (dynState) {
const vd = dynState[paramName];
if (vd !== undefined) {
return vd;
}
}

// Step 2: Static definition
let v = ctxtProps[paramName];
if (typeof v === 'undefined') {
v = cardDef.props[paramName]; // need to avoid boolean
}
// Step 3: Use defaults
if (typeof v === 'undefined' && includeDefaults && cardDef.defaults) {
v = cardDef.defaults[paramName];
}
if (isFunction(v)) {
v = v(state, (cn, pn) => {
const cn2 = cn || cardName;
const rv = getParamValue(pn, cn2, state, ctxtProps, cn2 !== cardName);
return rv;
}, {}, cardName);
}
return v;
}

function getValue(paramName, cardDef, state, ctxtProps, cardName) {
let v = ctxtProps[paramName];
if (typeof v === 'undefined') v = cardDef[paramName]; // need to avoid boolean
if (isFunction(v)) {
v = v(state, (cn, pn) => {
const rv = ref(cn, pn)(state, ctxtProps);
const rv = ref(cn || cardName, pn)(state, ctxtProps);
return rv;
}, ctxtProps);
}, ctxtProps, cardName);
}
return v;
}
Expand All @@ -227,7 +281,7 @@ export const Card = (props) => {
};

const createConnectedCard = (cardName, ctxtProps) => {
const cardDef = cards2[cardName];
const cardDef = cards[cardName];
if (!cardDef) {
return null;
}
Expand Down Expand Up @@ -263,12 +317,23 @@ const createConnectedCard = (cardName, ctxtProps) => {
} else {
// set default event handler
f = (opts = {}) => {
dispatch({
type: evtType,
id: cardName, // DEPRECATE
cardID: cardName,
...opts,
});
let o = opts;
if ('type' in o) {
logger.error(`event options for "${evtType}" from "${cardName}" cannot not include "type". Will change to "_type"`);
o = {
_type: opts.type,
...opts,
};
delete o.type;
}
setTimeout(() => {
dispatch({
type: evtType,
// id: cardName, // DEPRECATE
cardID: cardName,
...o,
});
}, 0);
};
}
h[name] = f;
Expand All @@ -285,7 +350,7 @@ const createConnectedCard = (cardName, ctxtProps) => {
};

const UnknownCard = (cardName) => {
const s = `Unknown card "${cardName}" - (${Object.keys(cards2).join(', ')})`;
const s = `Unknown card "${cardName}" - (${Object.keys(cards).join(', ')})`;
return React.createElement('div', null, s);
};

Expand All @@ -304,7 +369,7 @@ export function getCardState(cardName, state, ctxtProps = {}) {
}

// const cardDef = cards[cardName];
const cardDef2 = cards2[cardName];
const cardDef2 = cards[cardName];
if (!cardDef2.cardType) {
return undefined;
}
Expand All @@ -313,7 +378,7 @@ export function getCardState(cardName, state, ctxtProps = {}) {
const oldCardState = cache.cardState || {};
let hasChanged = false;
for (const k of Object.keys(cardState)) {
const v = getValue(k, cardState, state, ctxtProps);
const v = getValue(k, cardState, state, ctxtProps, cardName);
const ov = oldCardState[k];
// As redux and related state is supposed to be close to immutable
// a simple equivalence check should suffice.
Expand All @@ -327,6 +392,7 @@ export function getCardState(cardName, state, ctxtProps = {}) {
}
cardState[k] = v;
}
// console.log('>>> CARD STATE', cardName, hasChanged, cardState);
if (hasChanged) {
cardState.cardName = cardName;
cacheCardState(cardName, cardState, state, ctxtProps);
Expand Down
35 changes: 33 additions & 2 deletions packages/core/src/redux/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ export function registerActions(namespace, actionsAL) {
*
* @param {string} namespace
*/
export function actions(namespace) {
return ns2Actions[namespace] || {};
export function actions(...args) {
const [namespace, name] = args;
if (namespace && name) {
return action(namespace, name);
}

if (!namespace) {
throw Error('Missing namespace argument');
}
const as = ns2Actions[namespace];
if (as) {
logger.warn(`Upgrade 'action(${namespace})' to 'action(${namespace}, name)'`);
return as;
} else {
logger.warn(`Requesting actions from unknown namespace '${namespace}'`);
return {};
}
}

export function action(namespace, name) {
if (!namespace || !name) {
throw Error('Missing namespace or name argument');
}
const as = ns2Actions[namespace];
if (as) {
const fullName = as[name];
if (!name) {
throw Error(`Requesting unknown action '${name}' from namespace '${namespace}'`);
}
return fullName;
} else {
throw Error(`Requesting action '${name}' from unknown namespace '${namespace}'`);
}
}
2 changes: 1 addition & 1 deletion packages/core/src/redux/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './reducer';
export { ACTION_TYPES as REDUX_ACTION_TYPES } from './redux.actions';
export * from './redux.actions';
export * from './store';
export * from './update';
export * from './actions';
46 changes: 39 additions & 7 deletions packages/core/src/redux/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import stackinfo from 'stackinfo';

import { createLogger } from '../logger/logger';
import { ACTION_TYPES, emitError } from './redux.actions';
import { action as toAction } from './actions';

const logger = createLogger('reducer');
const logger = createLogger('@pihanga:core:reducer');

const DEF_PRIORITY = 500;

function dispatchError(msg, e) {
const si = stackinfo(e).map(s => s.traceline);
Expand All @@ -26,34 +29,63 @@ export class Reducer {
this.reducerByAction = reducerByAction;
}

registerReducer(type, reducer) {
registerReducer(...args) {
const [type, reducer, priority] = Reducer.parseReducerArgs(args);
if (!type) {
throw new Error('Missing type');
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.');
}
let d = this.reducerByAction[type];
if (d === undefined) {
this.reducerByAction[type] = [];
d = this.reducerByAction[type];
}
d.push(reducer);
d.push({ reducer, priority });
d.sort((a, b) => b.priority - a.priority);

logger.infoSilently(`Register reducer for type: ${type} with priority ${priority}`);
}

logger.infoSilently(`Register reducer for type: ${type}`);
static parseReducerArgs(args) {
switch (args.length) {
case 2: {
return [...args, DEF_PRIORITY];
}
case 3: {
if (typeof args[1] === 'function') {
return args;
} else {
return [toAction(args[0], args[1]), args[2], DEF_PRIORITY];
}
}
case 4: {
if (typeof args[2] === 'function') {
return args;
} else {
throw new Error('Illegal type of argument, expected function as 3rd argument');
}
}
default:
throw new Error('Illegal number of arguments, expected 2, 3 or 4');
}
}

rootReducer(state, action) {
const type = getType(action);
const tr = this.reducerByAction[type];
if (tr) {
return tr.reduce((s, reducer) => {
return tr.reduce((s, { reducer }) => {
try {
const s2 = reducer(s, action);
if (!isPlainObject(s2)) {
dispatchError(`Reducer '${reducer}' returns unexpected value '${s2}'`);
dispatchError(`Reducer '${reducer}' for action '${action}' returns unexpected value '${s2}'`);
return s; // ignore 'reducer'
}
return s2;
} catch (e) {
dispatchError(`While executing ${reducer} - ${e}`, e);
dispatchError(`While executing action '${action}' with reducer '${reducer}' - ${e}`, e);
return s; // ignore 'reducer'
}
}, state);
Expand Down
Loading

0 comments on commit 349994e

Please sign in to comment.