Bump postcss from 7.0.35 to 7.0.36 (#8)
[ustaxviewer.git] / src / FormView.tsx
1 // Copyright 2020 Blue Static <https://www.bluestatic.org>
2 // This program is free software licensed under the GNU General Public License,
3 // version 3.0. The full text of the license can be found in LICENSE.txt.
4 // SPDX-License-Identifier: GPL-3.0-only
5
6 import { createEffect, createMemo, createState } from 'solid-js';
7 import { For, Show } from 'solid-js/dom';
8 import { TaxReturn, Form, Line } from 'ustaxlib/core';
9 import * as Trace from 'ustaxlib/core/Trace';
10 import { Edge } from 'ustaxlib/core/Trace';
11 import { graphviz } from 'd3-graphviz';
12
13 const S = require('./FormView.css');
14
15 interface FormProps {
16 tr: TaxReturn;
17 form: Form<any>;
18 }
19
20 export default function FormView(props: FormProps) {
21 const lines = createMemo(() => {
22 const keys = Object.keys(props.form.lines);
23 keys.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
24 return keys.map(k => props.form.lines[k]);
25 });
26
27 return (
28 <div class={S.form}>
29 <h2 class={S.formName}>Form {props.form.name}</h2>
30
31 <div class={S.table}>
32 <For each={lines()}>
33 {line => <LineView tr={props.tr} line={line} />}
34 </For>
35 </div>
36 </div>
37 );
38 }
39
40 interface LineProps {
41 tr: TaxReturn;
42 line: Line<any>;
43 }
44
45 function LineView(props: LineProps) {
46 const { tr, line } = props;
47
48 const [ state, setState ] = createState({
49 value: undefined as any,
50 error: undefined as any,
51 trace: [] as readonly Edge[],
52 showTrace: false
53 });
54
55 createEffect(() => {
56 const newState = {
57 value: undefined,
58 error: undefined,
59 trace: [] as readonly Edge[]
60 };
61 try {
62 Trace.reset();
63 newState.value = line.value(tr);
64 } catch (e) {
65 newState.error = e;
66 }
67 newState.trace = Trace.getLastTraceList();
68 setState(newState);
69 });
70
71 const valueDisplay = createMemo(() => {
72 if (state.error) {
73 return <span class={S.error} title={state.error.stack}>{state.error.message}</span>;
74 }
75 return JSON.stringify(state.value, null, 1);
76 });
77
78 const toggleTrace = () => setState('showTrace', !state.showTrace);
79
80 return (
81 <>
82 <div class={S.line}>
83 <div class={S.id} onclick={toggleTrace}>{line.id}</div>
84 <div class={S.description}>
85 {line.description}
86
87 </div>
88 <div class={S.value}>{valueDisplay()}</div>
89 </div>
90 <Show when={state.showTrace}>
91 <TraceViewer line={line} trace={state.trace} onClose={() => setState('showTrace', false)} />
92 </Show>
93 </>
94 );
95 }
96
97 interface TraceProps {
98 line: Line<any>;
99 trace: readonly Edge[];
100
101 onClose: () => void;
102 }
103
104 function TraceViewer(props: TraceProps) {
105 const renderGraph = (ref) => {
106 let graph = '';
107 for (const edge of props.trace) {
108 graph += `"${edge[1]}" -> "${edge[0]}"; `;
109 }
110 graphviz(ref)
111 .zoomScaleExtent([0.1, 1])
112 .renderDot(`digraph { ${graph} }`, () => {
113 if (ref.querySelector('svg').clientWidth > ref.parentNode.clientWidth) {
114 ref.parentNode.classList.add(S.large);
115 }
116 });
117 };
118 return (
119 <div class={S.traceViewer}>
120 <h2>Trace {props.line.id} <button class={S.close} onclick={props.onClose}>\u24E7</button></h2>
121 <div forwardRef={renderGraph}></div>
122 </div>
123 );
124 }