Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | 47x 47x 24x 47x 24x 10x 14x 47x 17x 47x 24x 47x 24x 22x 2x 2x 1x 1x 47x 24x 47x 47x 11x 36x 36x 5x 5x | import React, { useCallback, useEffect, useMemo, useState } from 'react';
import cartesian from 'cartesian';
import { normalizeClean, getPermutations } from './helpers';
import useTaoInlineSubscription from './useTaoInlineSubscription';
/** @typedef {import('./helpers').TrigramPart} TrigramPart */
/** @typedef {import('./helpers').TrigramProps} TrigramProps */
/** @typedef {import('./helpers').TaoSignal} TaoSignal */
/**
* Render-prop children of {@link RenderHandler}: called with the matched
* AppCon's trigram and data. Read named DataHandler data with
* `useTaoData(name)` in a child component.
* @callback RenderHandlerChildren
* @param {TaoSignal} tao - trigram of the AppCon that matched (or `initialTao`)
* @param {*} data - data of that AppCon (or `initialData`)
* @returns {import('react').ReactNode}
*/
/**
* Props for {@link RenderHandler}. Trigram parts (`t`/`term`, `a`/`action`,
* `o`/`orient`) accept single values or arrays (multi-match); missing parts
* are wildcards.
* @typedef {Object} RenderHandlerProps
* @property {TrigramPart} [term] - the term: the domain thing
* @property {TrigramPart} [action] - the action: the operation on the term
* @property {TrigramPart} [orient] - the orient: perspective / role / surface
* @property {TrigramPart} [t] - the term (short key)
* @property {TrigramPart} [a] - the action (short key)
* @property {TrigramPart} [o] - the orient (short key)
* @property {TrigramProps} [refreshOn] - extra trigram parts merged over the
* match trigram to also re-render on (e.g. `{ action: 'Refresh' }`);
* ignored when it normalizes to empty
* @property {boolean} [debug=false] - log subscription/render diagnostics
* @property {boolean} [shouldRender=false] - render immediately with
* `initialTao`/`initialData` before any signal arrives
* @property {TaoSignal} [initialTao] - trigram passed to children before the
* first signal (with `shouldRender`)
* @property {*} [initialData] - data passed to children before the first
* signal (with `shouldRender`)
* @property {RenderHandlerChildren} children - render prop `(tao, data) => …`
*/
/**
* Subscribes to the trigram(s) and calls its `children` render prop with the
* latest matching AppCon (`(tao, data) => …`). Renders nothing until a
* signal arrives unless `shouldRender` is set.
* @param {RenderHandlerProps} props
* @returns {import('react').ReactElement|null}
*/
function RenderHandler({
term,
action,
orient,
t,
a,
o,
refreshOn,
// Stryker disable next-line BooleanLiteral: debug defaults false; logging is optional
debug = false,
shouldRender: shouldRenderProp,
initialTao,
initialData,
children,
}) {
// Stryker disable next-line ObjectLiteral: initial snap shape
const [snap, setSnap] = useState(() => ({
shouldRender: !!shouldRenderProp,
tao: initialTao,
data: initialData,
}));
useEffect(() => {
// Stryker disable all: optional debug logging
debug &&
console.log('RenderHandler::props:', {
term,
action,
orient,
t,
a,
o,
refreshOn,
shouldRender: shouldRenderProp,
debug,
});
// Stryker restore all
}, [debug, term, action, orient, t, a, o, refreshOn, shouldRenderProp]);
useEffect(() => {
if (!shouldRenderProp) {
return;
}
setSnap((prev) => ({
shouldRender: true,
tao: initialTao !== undefined ? initialTao : prev.tao,
data: initialData !== undefined ? initialData : prev.data,
}));
}, [shouldRenderProp, initialTao, initialData]);
const onSignal = useCallback((tao, data) => {
setSnap({ shouldRender: true, tao, data });
}, []);
const matchTrigrams = useMemo(
() => getPermutations({ term, action, orient, t, a, o }),
[term, action, orient, t, a, o],
);
const refreshTrigrams = useMemo(() => {
if (!refreshOn) {
return [];
}
const refresh = normalizeClean(refreshOn);
if (!Object.keys(refresh).length) {
return [];
}
return cartesian({
...normalizeClean({ term, action, orient, t, a, o }),
...refresh,
});
}, [term, action, orient, t, a, o, refreshOn]);
const trigrams = useMemo(
() => [...matchTrigrams, ...refreshTrigrams],
[matchTrigrams, refreshTrigrams],
);
useTaoInlineSubscription(trigrams, onSignal);
if (!snap.shouldRender) {
return null;
}
const { tao, data } = snap;
return <React.Fragment>{children(tao, data)}</React.Fragment>;
}
RenderHandler.displayName = 'RenderHandler';
RenderHandler.isTaoRenderHandler = true;
export default RenderHandler;
|