compostjs 0.0.12 → 0.2.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/.github/workflows/test.yml +25 -0
- package/CLAUDE.md +78 -0
- package/README.md +14 -0
- package/dist/compost.js +332 -33
- package/dist/core.js +130 -77
- package/dist/html.js +10 -1
- package/package.json +10 -6
- package/test/compost.test.js +116 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-dotnet@v4
|
|
16
|
+
with:
|
|
17
|
+
dotnet-version: '8.0.x'
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '22'
|
|
22
|
+
|
|
23
|
+
- run: dotnet tool restore
|
|
24
|
+
- run: npm install
|
|
25
|
+
- run: npm test
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Build Commands
|
|
6
|
+
|
|
7
|
+
Prerequisites: .NET SDK 8+, Node.js, npm. Fable is installed as a dotnet local tool (`.config/dotnet-tools.json`).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
dotnet tool restore # Install Fable 4.28.0
|
|
11
|
+
npm install # Install Vite + virtual-dom
|
|
12
|
+
|
|
13
|
+
npm start # Dev server at http://localhost:8080 (Fable watch + Vite)
|
|
14
|
+
npm run build # Compile F# to ES modules in dist/ (for npm package)
|
|
15
|
+
npm run rebuild # Clean dist/ and rebuild
|
|
16
|
+
npm run standalone # Bundle standalone IIFE to docs/releases/
|
|
17
|
+
npm test # Compile F# then run Vitest tests (test/*.test.js)
|
|
18
|
+
|
|
19
|
+
npm run release # Full release (runs the three steps below)
|
|
20
|
+
npm run release:version # Bump version in package.json, create git commit + tag (via np)
|
|
21
|
+
npm run release:standalone # Build standalone bundle and commit to git
|
|
22
|
+
npm run release:publish # Rebuild dist/ and npm publish (prompts for 2FA)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Tests use Vitest against the Fable-compiled JS output. F# must be compiled before tests run (`npm test` handles this). Test files go in `test/`. Use `c.foldDom(f, acc, node)` to traverse `DomNode` trees in tests — the callback receives `(acc, tag, attrs)` where `attrs` is a plain JS object of string attribute values. CI runs `npm test` on pushes and PRs to `master` via GitHub Actions (`.github/workflows/test.yml`).
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
|
|
29
|
+
Compost.js is a composable data visualization library. The core is written in **F#** and compiled to **JavaScript** via **Fable 4**. The runtime dependency is `virtual-dom` for efficient DOM updates in interactive charts.
|
|
30
|
+
|
|
31
|
+
### F# Source (`src/compost/`)
|
|
32
|
+
|
|
33
|
+
Three files, compiled in this order (defined in `compost.fsproj`):
|
|
34
|
+
|
|
35
|
+
1. **`html.fs`** — `Compost.Html` module. Low-level JS interop layer:
|
|
36
|
+
- `Common` module: `[<Emit>]`-based helpers for JS operations (property access, typeof, date formatting)
|
|
37
|
+
- `Virtualdom` module: `[<Import>]` bindings to `virtual-dom` (h, diff, patch, createElement)
|
|
38
|
+
- `DomNode`/`DomAttribute` types and HTML/SVG rendering
|
|
39
|
+
- `foldDom`: generic fold over `DomNode` trees (used in tests to inspect rendered SVG)
|
|
40
|
+
- `createVirtualDomApp`: stateful interactive app loop using virtual-dom diffing
|
|
41
|
+
|
|
42
|
+
2. **`core.fs`** — `Compost` namespace. The visualization engine (~800 lines):
|
|
43
|
+
- **Domain types**: `Value<'u>` (continuous `COV` or categorical `CAR`), `Scale<'v>`, `Shape<'vx,'vy>`, `Style`, `EventHandler`
|
|
44
|
+
- **Shape** is a discriminated union (Line, Bubble, Shape, Text, Image, Layered, Interactive, Axes, NestX/Y, Padding, etc.) — this is the core composable DSL
|
|
45
|
+
- Uses F# units of measure (`[<Measure>]`) for type-safe coordinate spaces
|
|
46
|
+
- `Scales` module: axis generation, range calculation
|
|
47
|
+
- `Projections` module: coordinate transformation (data space → pixel space)
|
|
48
|
+
- `Drawing` module: converts shapes → SVG elements
|
|
49
|
+
- `Events` module: routes mouse/touch events to interactive handlers
|
|
50
|
+
- `Derived` module: higher-level combinators (FillColor, StrokeColor, Column, Bar, Area)
|
|
51
|
+
|
|
52
|
+
3. **`compost.fs`** — `main` module. The JavaScript API surface:
|
|
53
|
+
- Exposes `scale` (JsScale) and `compost` (JsCompost) objects
|
|
54
|
+
- Handles JS↔F# value conversion via `parseValue`/`formatValue` (numbers become `COV`, `[string, number]` arrays become `CAR`)
|
|
55
|
+
- All ~20 API methods (`render`, `interactive`, `overlay`, `axes`, `on`, etc.) are defined here
|
|
56
|
+
|
|
57
|
+
### JavaScript Entry Points (`src/project/`)
|
|
58
|
+
|
|
59
|
+
- **`standalone.js`** — Imports `scale`/`compost` from F# and assigns to `window.s`/`window.c`
|
|
60
|
+
- **`demos.js`** — Interactive demo examples used by the dev server (`index.html`)
|
|
61
|
+
- **`data.js`** — Demo datasets (elections, exchange rates, iris)
|
|
62
|
+
|
|
63
|
+
### Build Outputs
|
|
64
|
+
|
|
65
|
+
- **`dist/`** — ES modules for npm (`compost.js` is the entry point, `"main"` in package.json)
|
|
66
|
+
- **`docs/releases/`** — Standalone IIFE bundles for `<script>` tag usage
|
|
67
|
+
|
|
68
|
+
### How Fable Compilation Works
|
|
69
|
+
|
|
70
|
+
`dotnet fable` compiles `.fs` files to `.fs.js` files. During `npm start` (watch mode), these appear next to the source files (`src/compost/*.fs.js`). During `npm run build`, they go to `dist/` via the `-o` flag. Fable library dependencies go into `fable_modules/`. Vite (or any bundler) then treats the `.js` output as standard ES modules.
|
|
71
|
+
|
|
72
|
+
## Key Patterns
|
|
73
|
+
|
|
74
|
+
- The JS API uses `obj` and `box`/`unbox` heavily for dynamic typing at the JS boundary. The F# internals are fully typed with units of measure.
|
|
75
|
+
- `[<Emit("...")>]` is used for inline JS expressions (property access, typeof, Date operations).
|
|
76
|
+
- `[<Import("name","module")>]` is used for virtual-dom imports.
|
|
77
|
+
- `JsInterop.createObj` creates plain JS objects from F# key-value sequences.
|
|
78
|
+
- Interactive charts use a virtual-dom update cycle: event → update function → render function → diff/patch.
|
package/README.md
CHANGED
|
@@ -33,6 +33,20 @@ JavaScript file that is added to the `releases` folder of the `docs` with the cu
|
|
|
33
33
|
version number in the filename (and also updates the `latest` file).
|
|
34
34
|
This should all happen automatically when using `npm run release`.
|
|
35
35
|
|
|
36
|
+
### Releasing Compost
|
|
37
|
+
|
|
38
|
+
Before releasing, make sure all changes are committed. Then run:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
npm run release
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This runs three steps in order:
|
|
45
|
+
|
|
46
|
+
1. **`npm run release:version`** — bumps the version in `package.json` and creates a git commit and tag (via `np`)
|
|
47
|
+
2. **`npm run release:standalone`** — builds the standalone bundle and commits it to git
|
|
48
|
+
3. **`npm run release:publish`** — rebuilds `dist/` and publishes to npm (prompts for 2FA)
|
|
49
|
+
|
|
36
50
|
## What is the story behind the name??
|
|
37
51
|
|
|
38
52
|

|
package/dist/compost.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Compost_createSvg, EventHandler,
|
|
2
|
-
import { printf, toFail } from "./fable_modules/fable-library-js.4.28.0/String.js";
|
|
1
|
+
import { Compost_createSvg, EventHandler, Derived_Bar, Derived_Column, Derived_Font, Derived_StrokeColor, Derived_PreserveAspectRatio, Derived_FillColor, Shape, StyleConfig, Scale, HorizontalAlign, VerticalAlign, categorical, Value, continuous } from "./core.js";
|
|
2
|
+
import { join, printf, toFail } from "./fable_modules/fable-library-js.4.28.0/String.js";
|
|
3
3
|
import { map as map_1, item } from "./fable_modules/fable-library-js.4.28.0/Array.js";
|
|
4
|
-
import { toList, map, delay, toArray } from "./fable_modules/fable-library-js.4.28.0/Seq.js";
|
|
5
|
-
import { ofArray } from "./fable_modules/fable-library-js.4.28.0/List.js";
|
|
6
|
-
import {
|
|
7
|
-
import { renderTo, createVirtualDomApp, DomNode, DomAttribute } from "./html.js";
|
|
4
|
+
import { empty, append, toList, map, singleton, collect, delay, toArray } from "./fable_modules/fable-library-js.4.28.0/Seq.js";
|
|
5
|
+
import { ofArray, ofSeq } from "./fable_modules/fable-library-js.4.28.0/List.js";
|
|
6
|
+
import { createObj, equals, defaultOf } from "./fable_modules/fable-library-js.4.28.0/Util.js";
|
|
7
|
+
import { foldDom, renderTo, createVirtualDomApp, DomNode, DomAttribute } from "./html.js";
|
|
8
8
|
|
|
9
9
|
export function Helpers_formatValue(v) {
|
|
10
10
|
if (v.tag === 1) {
|
|
@@ -34,6 +34,282 @@ export function Helpers_parseValue(v) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function Serialization_serValue() {
|
|
38
|
+
return Helpers_formatValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function Serialization_deserValue() {
|
|
42
|
+
return Helpers_parseValue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Serialization_serializeVerticalAlign(_arg) {
|
|
46
|
+
switch (_arg.tag) {
|
|
47
|
+
case 1:
|
|
48
|
+
return "middle";
|
|
49
|
+
case 2:
|
|
50
|
+
return "hanging";
|
|
51
|
+
default:
|
|
52
|
+
return "baseline";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Serialization_deserializeVerticalAlign(_arg) {
|
|
57
|
+
switch (_arg) {
|
|
58
|
+
case "baseline":
|
|
59
|
+
return new VerticalAlign(0, []);
|
|
60
|
+
case "hanging":
|
|
61
|
+
return new VerticalAlign(2, []);
|
|
62
|
+
default:
|
|
63
|
+
return new VerticalAlign(1, []);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Serialization_serializeHorizontalAlign(_arg) {
|
|
68
|
+
switch (_arg.tag) {
|
|
69
|
+
case 1:
|
|
70
|
+
return "center";
|
|
71
|
+
case 2:
|
|
72
|
+
return "end";
|
|
73
|
+
default:
|
|
74
|
+
return "start";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function Serialization_deserializeHorizontalAlign(_arg) {
|
|
79
|
+
switch (_arg) {
|
|
80
|
+
case "start":
|
|
81
|
+
return new HorizontalAlign(0, []);
|
|
82
|
+
case "end":
|
|
83
|
+
return new HorizontalAlign(2, []);
|
|
84
|
+
default:
|
|
85
|
+
return new HorizontalAlign(1, []);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function Serialization_serializeScale(_arg) {
|
|
90
|
+
if (_arg.tag === 1) {
|
|
91
|
+
return {
|
|
92
|
+
kind: "categorical",
|
|
93
|
+
cats: toArray(delay(() => collect((matchValue) => singleton(matchValue.fields[0]), _arg.fields[0]))),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return {
|
|
98
|
+
kind: "continuous",
|
|
99
|
+
lo: _arg.fields[0].fields[0],
|
|
100
|
+
hi: _arg.fields[1].fields[0],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function Serialization_deserializeScale(o) {
|
|
106
|
+
const matchValue = o["kind"];
|
|
107
|
+
switch (matchValue) {
|
|
108
|
+
case "continuous":
|
|
109
|
+
return new Scale(0, [new continuous(o["lo"]), new continuous(o["hi"])]);
|
|
110
|
+
case "categorical":
|
|
111
|
+
return new Scale(1, [toArray(delay(() => map((c) => (new categorical(c)), o["cats"])))]);
|
|
112
|
+
default:
|
|
113
|
+
return toFail(printf("Unknown scale kind: %s"))(matchValue);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Serialization_serializeStyleConfig(_arg) {
|
|
118
|
+
switch (_arg.tag) {
|
|
119
|
+
case 2:
|
|
120
|
+
return {
|
|
121
|
+
kind: "stroke",
|
|
122
|
+
color: _arg.fields[0],
|
|
123
|
+
};
|
|
124
|
+
case 3:
|
|
125
|
+
return {
|
|
126
|
+
kind: "font",
|
|
127
|
+
font: _arg.fields[0],
|
|
128
|
+
color: _arg.fields[1],
|
|
129
|
+
};
|
|
130
|
+
case 4:
|
|
131
|
+
return {
|
|
132
|
+
kind: "aspect",
|
|
133
|
+
value: _arg.fields[0],
|
|
134
|
+
};
|
|
135
|
+
case 0:
|
|
136
|
+
throw new Error("Cannot serialize Custom style config");
|
|
137
|
+
default:
|
|
138
|
+
return {
|
|
139
|
+
kind: "fill",
|
|
140
|
+
color: _arg.fields[0],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function Serialization_deserializeStyleConfig(o) {
|
|
146
|
+
const matchValue = o["kind"];
|
|
147
|
+
switch (matchValue) {
|
|
148
|
+
case "fill":
|
|
149
|
+
return new StyleConfig(1, [o["color"]]);
|
|
150
|
+
case "stroke":
|
|
151
|
+
return new StyleConfig(2, [o["color"]]);
|
|
152
|
+
case "font":
|
|
153
|
+
return new StyleConfig(3, [o["font"], o["color"]]);
|
|
154
|
+
case "aspect":
|
|
155
|
+
return new StyleConfig(4, [o["value"]]);
|
|
156
|
+
default:
|
|
157
|
+
return toFail(printf("Unknown style config kind: %s"))(matchValue);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function Serialization_serializePoints(pts) {
|
|
162
|
+
return toArray(delay(() => collect((matchValue) => singleton([Serialization_serValue()(matchValue[0]), Serialization_serValue()(matchValue[1])]), pts)));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function Serialization_serializeShape(s) {
|
|
166
|
+
switch (s.tag) {
|
|
167
|
+
case 9:
|
|
168
|
+
return {
|
|
169
|
+
kind: "shape",
|
|
170
|
+
points: Serialization_serializePoints(ofSeq(s.fields[0])),
|
|
171
|
+
};
|
|
172
|
+
case 8:
|
|
173
|
+
return {
|
|
174
|
+
kind: "bubble",
|
|
175
|
+
x: Serialization_serValue()(s.fields[0]),
|
|
176
|
+
y: Serialization_serValue()(s.fields[1]),
|
|
177
|
+
w: s.fields[2],
|
|
178
|
+
h: s.fields[3],
|
|
179
|
+
};
|
|
180
|
+
case 2:
|
|
181
|
+
return {
|
|
182
|
+
kind: "text",
|
|
183
|
+
x: Serialization_serValue()(s.fields[0]),
|
|
184
|
+
y: Serialization_serValue()(s.fields[1]),
|
|
185
|
+
valign: Serialization_serializeVerticalAlign(s.fields[2]),
|
|
186
|
+
halign: Serialization_serializeHorizontalAlign(s.fields[3]),
|
|
187
|
+
rotation: s.fields[4],
|
|
188
|
+
text: s.fields[5],
|
|
189
|
+
};
|
|
190
|
+
case 0:
|
|
191
|
+
return {
|
|
192
|
+
kind: "image",
|
|
193
|
+
href: s.fields[0],
|
|
194
|
+
p1: [Serialization_serValue()(s.fields[1][0]), Serialization_serValue()(s.fields[1][1])],
|
|
195
|
+
p2: [Serialization_serValue()(s.fields[2][0]), Serialization_serValue()(s.fields[2][1])],
|
|
196
|
+
};
|
|
197
|
+
case 10:
|
|
198
|
+
return {
|
|
199
|
+
kind: "layered",
|
|
200
|
+
shapes: toArray(delay(() => map(Serialization_serializeShape, s.fields[0]))),
|
|
201
|
+
};
|
|
202
|
+
case 11:
|
|
203
|
+
return {
|
|
204
|
+
kind: "axes",
|
|
205
|
+
axes: join(" ", toList(delay(() => append(s.fields[0] ? singleton("top") : empty(), delay(() => append(s.fields[1] ? singleton("right") : empty(), delay(() => append(s.fields[2] ? singleton("bottom") : empty(), delay(() => (s.fields[3] ? singleton("left") : empty())))))))))),
|
|
206
|
+
shape: Serialization_serializeShape(s.fields[4]),
|
|
207
|
+
};
|
|
208
|
+
case 13:
|
|
209
|
+
return {
|
|
210
|
+
kind: "padding",
|
|
211
|
+
top: s.fields[0][0],
|
|
212
|
+
right: s.fields[0][1],
|
|
213
|
+
bottom: s.fields[0][2],
|
|
214
|
+
left: s.fields[0][3],
|
|
215
|
+
shape: Serialization_serializeShape(s.fields[1]),
|
|
216
|
+
};
|
|
217
|
+
case 5:
|
|
218
|
+
return {
|
|
219
|
+
kind: "nestx",
|
|
220
|
+
lx: Serialization_serValue()(s.fields[0]),
|
|
221
|
+
hx: Serialization_serValue()(s.fields[1]),
|
|
222
|
+
shape: Serialization_serializeShape(s.fields[2]),
|
|
223
|
+
};
|
|
224
|
+
case 6:
|
|
225
|
+
return {
|
|
226
|
+
kind: "nesty",
|
|
227
|
+
ly: Serialization_serValue()(s.fields[0]),
|
|
228
|
+
hy: Serialization_serValue()(s.fields[1]),
|
|
229
|
+
shape: Serialization_serializeShape(s.fields[2]),
|
|
230
|
+
};
|
|
231
|
+
case 4: {
|
|
232
|
+
const sy = s.fields[1];
|
|
233
|
+
const sx = s.fields[0];
|
|
234
|
+
return {
|
|
235
|
+
kind: "scale",
|
|
236
|
+
sx: (sx == null) ? defaultOf() : Serialization_serializeScale(sx),
|
|
237
|
+
sy: (sy == null) ? defaultOf() : Serialization_serializeScale(sy),
|
|
238
|
+
shape: Serialization_serializeShape(s.fields[2]),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
case 1:
|
|
242
|
+
return {
|
|
243
|
+
kind: "styled",
|
|
244
|
+
param: Serialization_serializeStyleConfig(s.fields[0]),
|
|
245
|
+
shape: Serialization_serializeShape(s.fields[1]),
|
|
246
|
+
};
|
|
247
|
+
case 3:
|
|
248
|
+
return {
|
|
249
|
+
kind: "autoscale",
|
|
250
|
+
x: s.fields[0],
|
|
251
|
+
y: s.fields[1],
|
|
252
|
+
shape: Serialization_serializeShape(s.fields[2]),
|
|
253
|
+
};
|
|
254
|
+
case 14:
|
|
255
|
+
return {
|
|
256
|
+
kind: "offset",
|
|
257
|
+
dx: s.fields[0][0],
|
|
258
|
+
dy: s.fields[0][1],
|
|
259
|
+
shape: Serialization_serializeShape(s.fields[1]),
|
|
260
|
+
};
|
|
261
|
+
case 12:
|
|
262
|
+
throw new Error("Cannot serialize Interactive shapes");
|
|
263
|
+
default:
|
|
264
|
+
return {
|
|
265
|
+
kind: "line",
|
|
266
|
+
points: Serialization_serializePoints(ofSeq(s.fields[0])),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function Serialization_deserializeShape(o) {
|
|
272
|
+
const matchValue = o["kind"];
|
|
273
|
+
switch (matchValue) {
|
|
274
|
+
case "line":
|
|
275
|
+
return new Shape(7, [toList(delay(() => map((p) => [Serialization_deserValue()(item(0, p)), Serialization_deserValue()(item(1, p))], o["points"])))]);
|
|
276
|
+
case "shape":
|
|
277
|
+
return new Shape(9, [toList(delay(() => map((p_1) => [Serialization_deserValue()(item(0, p_1)), Serialization_deserValue()(item(1, p_1))], o["points"])))]);
|
|
278
|
+
case "bubble":
|
|
279
|
+
return new Shape(8, [Serialization_deserValue()(o["x"]), Serialization_deserValue()(o["y"]), o["w"], o["h"]]);
|
|
280
|
+
case "text":
|
|
281
|
+
return new Shape(2, [Serialization_deserValue()(o["x"]), Serialization_deserValue()(o["y"]), Serialization_deserializeVerticalAlign(o["valign"]), Serialization_deserializeHorizontalAlign(o["halign"]), o["rotation"], o["text"]]);
|
|
282
|
+
case "image": {
|
|
283
|
+
const matchValue_1 = o["p1"];
|
|
284
|
+
const p2 = o["p2"];
|
|
285
|
+
const p1 = matchValue_1;
|
|
286
|
+
return new Shape(0, [o["href"], [Serialization_deserValue()(item(0, p1)), Serialization_deserValue()(item(1, p1))], [Serialization_deserValue()(item(0, p2)), Serialization_deserValue()(item(1, p2))]]);
|
|
287
|
+
}
|
|
288
|
+
case "layered":
|
|
289
|
+
return new Shape(10, [toList(delay(() => map(Serialization_deserializeShape, o["shapes"])))]);
|
|
290
|
+
case "axes": {
|
|
291
|
+
const a = o["axes"];
|
|
292
|
+
return new Shape(11, [a.indexOf("top") >= 0, a.indexOf("right") >= 0, a.indexOf("bottom") >= 0, a.indexOf("left") >= 0, Serialization_deserializeShape(o["shape"])]);
|
|
293
|
+
}
|
|
294
|
+
case "padding":
|
|
295
|
+
return new Shape(13, [[o["top"], o["right"], o["bottom"], o["left"]], Serialization_deserializeShape(o["shape"])]);
|
|
296
|
+
case "nestx":
|
|
297
|
+
return new Shape(5, [Serialization_deserValue()(o["lx"]), Serialization_deserValue()(o["hx"]), Serialization_deserializeShape(o["shape"])]);
|
|
298
|
+
case "nesty":
|
|
299
|
+
return new Shape(6, [Serialization_deserValue()(o["ly"]), Serialization_deserValue()(o["hy"]), Serialization_deserializeShape(o["shape"])]);
|
|
300
|
+
case "scale":
|
|
301
|
+
return new Shape(4, [equals(o["sx"], defaultOf()) ? undefined : Serialization_deserializeScale(o["sx"]), equals(o["sy"], defaultOf()) ? undefined : Serialization_deserializeScale(o["sy"]), Serialization_deserializeShape(o["shape"])]);
|
|
302
|
+
case "styled":
|
|
303
|
+
return new Shape(1, [Serialization_deserializeStyleConfig(o["param"]), Serialization_deserializeShape(o["shape"])]);
|
|
304
|
+
case "autoscale":
|
|
305
|
+
return new Shape(3, [o["x"], o["y"], Serialization_deserializeShape(o["shape"])]);
|
|
306
|
+
case "offset":
|
|
307
|
+
return new Shape(14, [[o["dx"], o["dy"]], Serialization_deserializeShape(o["shape"])]);
|
|
308
|
+
default:
|
|
309
|
+
return toFail(printf("Unknown shape kind: %s"))(matchValue);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
37
313
|
export const scale = {
|
|
38
314
|
continuous(lo, hi) {
|
|
39
315
|
return new Scale(0, [new continuous(lo), new continuous(hi)]);
|
|
@@ -45,37 +321,40 @@ export const scale = {
|
|
|
45
321
|
|
|
46
322
|
export const compost = {
|
|
47
323
|
scaleX(sc, sh) {
|
|
48
|
-
return new Shape(
|
|
324
|
+
return new Shape(4, [sc, undefined, sh]);
|
|
49
325
|
},
|
|
50
326
|
scaleY(sc_1, sh_1) {
|
|
51
|
-
return new Shape(
|
|
327
|
+
return new Shape(4, [undefined, sc_1, sh_1]);
|
|
52
328
|
},
|
|
53
329
|
scale(sx, sy, sh_2) {
|
|
54
|
-
return new Shape(
|
|
330
|
+
return new Shape(4, [sx, sy, sh_2]);
|
|
55
331
|
},
|
|
56
332
|
nestX(lx, hx, s) {
|
|
57
|
-
return new Shape(
|
|
333
|
+
return new Shape(5, [Helpers_parseValue(lx), Helpers_parseValue(hx), s]);
|
|
58
334
|
},
|
|
59
335
|
nestY(ly, hy, s_1) {
|
|
60
|
-
return new Shape(
|
|
336
|
+
return new Shape(6, [Helpers_parseValue(ly), Helpers_parseValue(hy), s_1]);
|
|
61
337
|
},
|
|
62
338
|
nest(lx_1, hx_1, ly_1, hy_1, s_2) {
|
|
63
|
-
return new Shape(
|
|
339
|
+
return new Shape(6, [Helpers_parseValue(ly_1), Helpers_parseValue(hy_1), new Shape(5, [Helpers_parseValue(lx_1), Helpers_parseValue(hx_1), s_2])]);
|
|
64
340
|
},
|
|
65
341
|
overlay(sh_3) {
|
|
66
|
-
return new Shape(
|
|
342
|
+
return new Shape(10, [ofArray(sh_3)]);
|
|
67
343
|
},
|
|
68
344
|
padding(t, r, b, l, s_3) {
|
|
69
|
-
return new Shape(
|
|
345
|
+
return new Shape(13, [[t, r, b, l], s_3]);
|
|
70
346
|
},
|
|
71
347
|
fillColor(c, s_4) {
|
|
72
348
|
return Derived_FillColor(c, s_4);
|
|
73
349
|
},
|
|
74
|
-
|
|
75
|
-
return
|
|
350
|
+
preserveAspectRatio(pa, s_5) {
|
|
351
|
+
return Derived_PreserveAspectRatio(pa, s_5);
|
|
352
|
+
},
|
|
353
|
+
strokeColor(c_1, s_6) {
|
|
354
|
+
return Derived_StrokeColor(c_1, s_6);
|
|
76
355
|
},
|
|
77
|
-
font(f, c_2,
|
|
78
|
-
return Derived_Font(f, c_2,
|
|
356
|
+
font(f, c_2, s_7) {
|
|
357
|
+
return Derived_Font(f, c_2, s_7);
|
|
79
358
|
},
|
|
80
359
|
column(xp, yp) {
|
|
81
360
|
return Derived_Column(new categorical(xp), new continuous(yp));
|
|
@@ -84,26 +363,29 @@ export const compost = {
|
|
|
84
363
|
return Derived_Bar(new continuous(xp_1), new categorical(yp_1));
|
|
85
364
|
},
|
|
86
365
|
bubble(xp_2, yp_2, w, h) {
|
|
87
|
-
return new Shape(
|
|
366
|
+
return new Shape(8, [Helpers_parseValue(xp_2), Helpers_parseValue(yp_2), w, h]);
|
|
88
367
|
},
|
|
89
|
-
text(xp_3, yp_3, t_1,
|
|
368
|
+
text(xp_3, yp_3, t_1, s_8, r_1) {
|
|
90
369
|
const r_2 = equals(r_1, defaultOf()) ? 0 : r_1;
|
|
91
|
-
const
|
|
92
|
-
const va = (
|
|
93
|
-
const ha = (
|
|
94
|
-
return new Shape(
|
|
370
|
+
const s_9 = equals(s_8, defaultOf()) ? "" : s_8;
|
|
371
|
+
const va = (s_9.indexOf("baseline") >= 0) ? (new VerticalAlign(0, [])) : ((s_9.indexOf("hanging") >= 0) ? (new VerticalAlign(2, [])) : (new VerticalAlign(1, [])));
|
|
372
|
+
const ha = (s_9.indexOf("start") >= 0) ? (new HorizontalAlign(0, [])) : ((s_9.indexOf("end") >= 0) ? (new HorizontalAlign(2, [])) : (new HorizontalAlign(1, [])));
|
|
373
|
+
return new Shape(2, [Helpers_parseValue(xp_3), Helpers_parseValue(yp_3), va, ha, r_2, t_1]);
|
|
95
374
|
},
|
|
96
375
|
shape(a) {
|
|
97
|
-
return new Shape(
|
|
376
|
+
return new Shape(9, [toList(delay(() => map((p) => [Helpers_parseValue(item(0, p)), Helpers_parseValue(item(1, p))], a)))]);
|
|
98
377
|
},
|
|
99
378
|
line(a_1) {
|
|
100
|
-
return new Shape(
|
|
379
|
+
return new Shape(7, [toList(delay(() => map((p_1) => [Helpers_parseValue(item(0, p_1)), Helpers_parseValue(item(1, p_1))], a_1)))]);
|
|
380
|
+
},
|
|
381
|
+
image(href, pt1, pt2) {
|
|
382
|
+
return new Shape(0, [href, [Helpers_parseValue(item(0, pt1)), Helpers_parseValue(item(1, pt1))], [Helpers_parseValue(item(0, pt2)), Helpers_parseValue(item(1, pt2))]]);
|
|
101
383
|
},
|
|
102
|
-
axes(a_2,
|
|
103
|
-
return new Shape(
|
|
384
|
+
axes(a_2, s_10) {
|
|
385
|
+
return new Shape(11, [a_2.indexOf("top") >= 0, a_2.indexOf("right") >= 0, a_2.indexOf("bottom") >= 0, a_2.indexOf("left") >= 0, s_10]);
|
|
104
386
|
},
|
|
105
|
-
on(o,
|
|
106
|
-
return new Shape(
|
|
387
|
+
on(o, s_11) {
|
|
388
|
+
return new Shape(12, [toList(delay(() => map((k) => {
|
|
107
389
|
let f_2;
|
|
108
390
|
const f_1 = o[k];
|
|
109
391
|
f_2 = ((args) => {
|
|
@@ -126,7 +408,7 @@ export const compost = {
|
|
|
126
408
|
}])) : ((k === "touchend") ? (new EventHandler(6, [(me_7) => {
|
|
127
409
|
f_2([me_7]);
|
|
128
410
|
}])) : toFail(printf("Unsupported event type \'%s\' passed to the \'on\' primitive."))(k))))))));
|
|
129
|
-
}, Object.keys(o)))),
|
|
411
|
+
}, Object.keys(o)))), s_11]);
|
|
130
412
|
},
|
|
131
413
|
svg(w_1, h_1, shape) {
|
|
132
414
|
return Compost_createSvg(false, false, w_1, h_1, shape);
|
|
@@ -149,9 +431,9 @@ export const compost = {
|
|
|
149
431
|
return new DomNode(1, [defaultOf(), tag, attrs_1, children_1]);
|
|
150
432
|
},
|
|
151
433
|
interactive(id, init, update, render) {
|
|
152
|
-
createVirtualDomApp(id, init, (t_2,
|
|
434
|
+
createVirtualDomApp(id, init, (t_2, s_13) => {
|
|
153
435
|
const el = document.getElementById(id);
|
|
154
|
-
const res = render(t_2,
|
|
436
|
+
const res = render(t_2, s_13);
|
|
155
437
|
if (equals(res["constructor"], (new DomNode(0, [""]))["constructor"])) {
|
|
156
438
|
return res;
|
|
157
439
|
}
|
|
@@ -164,5 +446,22 @@ export const compost = {
|
|
|
164
446
|
const el_1 = document.getElementById(id_1);
|
|
165
447
|
renderTo(el_1, Compost_createSvg(false, false, el_1.clientWidth, el_1.clientHeight, viz));
|
|
166
448
|
},
|
|
449
|
+
foldDom(f_3, acc, node) {
|
|
450
|
+
return foldDom((acc_1, _ns, tag_1, attrs_3) => f_3(acc_1, tag_1, createObj(toArray(delay(() => collect((matchValue) => {
|
|
451
|
+
const matchValue_1 = matchValue[1];
|
|
452
|
+
if (matchValue_1.tag === 1) {
|
|
453
|
+
return singleton([matchValue[0], matchValue_1.fields[0]]);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
return empty();
|
|
457
|
+
}
|
|
458
|
+
}, attrs_3))))), acc, node);
|
|
459
|
+
},
|
|
460
|
+
serialize(s_15) {
|
|
461
|
+
return Serialization_serializeShape(s_15);
|
|
462
|
+
},
|
|
463
|
+
deserialize(o_1) {
|
|
464
|
+
return Serialization_deserializeShape(o_1);
|
|
465
|
+
},
|
|
167
466
|
};
|
|
168
467
|
|
package/dist/core.js
CHANGED
|
@@ -164,7 +164,7 @@ export function Scale_$reflection(gen0) {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
export class Style extends Record {
|
|
167
|
-
constructor(StrokeColor, StrokeWidth, StrokeDashArray, Fill, Animation, Font, Cursor, FormatAxisXLabel, FormatAxisYLabel) {
|
|
167
|
+
constructor(StrokeColor, StrokeWidth, StrokeDashArray, Fill, Animation, Font, Cursor, PreserveAspectRatio, FormatAxisXLabel, FormatAxisYLabel) {
|
|
168
168
|
super();
|
|
169
169
|
this.StrokeColor = StrokeColor;
|
|
170
170
|
this.StrokeWidth = StrokeWidth;
|
|
@@ -173,13 +173,46 @@ export class Style extends Record {
|
|
|
173
173
|
this.Animation = Animation;
|
|
174
174
|
this.Font = Font;
|
|
175
175
|
this.Cursor = Cursor;
|
|
176
|
+
this.PreserveAspectRatio = PreserveAspectRatio;
|
|
176
177
|
this.FormatAxisXLabel = FormatAxisXLabel;
|
|
177
178
|
this.FormatAxisYLabel = FormatAxisYLabel;
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
export function Style_$reflection() {
|
|
182
|
-
return record_type("Compost.Style", [], Style, () => [["StrokeColor", tuple_type(float64_type, Color_$reflection())], ["StrokeWidth", Width_$reflection()], ["StrokeDashArray", class_type("System.Collections.Generic.IEnumerable`1", [Number$_$reflection()])], ["Fill", FillStyle_$reflection()], ["Animation", option_type(tuple_type(int32_type, string_type, lambda_type(Style_$reflection(), Style_$reflection())))], ["Font", string_type], ["Cursor", string_type], ["FormatAxisXLabel", lambda_type(Scale_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), lambda_type(Value_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), string_type))], ["FormatAxisYLabel", lambda_type(Scale_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), lambda_type(Value_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), string_type))]]);
|
|
183
|
+
return record_type("Compost.Style", [], Style, () => [["StrokeColor", tuple_type(float64_type, Color_$reflection())], ["StrokeWidth", Width_$reflection()], ["StrokeDashArray", class_type("System.Collections.Generic.IEnumerable`1", [Number$_$reflection()])], ["Fill", FillStyle_$reflection()], ["Animation", option_type(tuple_type(int32_type, string_type, lambda_type(Style_$reflection(), Style_$reflection())))], ["Font", string_type], ["Cursor", string_type], ["PreserveAspectRatio", string_type], ["FormatAxisXLabel", lambda_type(Scale_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), lambda_type(Value_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), string_type))], ["FormatAxisYLabel", lambda_type(Scale_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), lambda_type(Value_$reflection(class_type("Microsoft.FSharp.Core.CompilerServices.MeasureOne")), string_type))]]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export class StyleConfig extends Union {
|
|
187
|
+
constructor(tag, fields) {
|
|
188
|
+
super();
|
|
189
|
+
this.tag = tag;
|
|
190
|
+
this.fields = fields;
|
|
191
|
+
}
|
|
192
|
+
cases() {
|
|
193
|
+
return ["Custom", "FillColor", "StrokeColor", "Font", "PreserveAspectRatio"];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function StyleConfig_$reflection() {
|
|
198
|
+
return union_type("Compost.StyleConfig", [], StyleConfig, () => [[["Item", lambda_type(Style_$reflection(), Style_$reflection())]], [["Item", string_type]], [["Item", string_type]], [["Item1", string_type], ["Item2", string_type]], [["Item", string_type]]]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function StyleConfig__Apply_4E2CA0C5(this$, style) {
|
|
202
|
+
switch (this$.tag) {
|
|
203
|
+
case 1:
|
|
204
|
+
return new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, new FillStyle(0, [[1, new Color(1, [this$.fields[0]])]]), style.Animation, style.Font, style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
205
|
+
case 2:
|
|
206
|
+
return new Style([1, new Color(1, [this$.fields[0]])], style.StrokeWidth, style.StrokeDashArray, style.Fill, style.Animation, style.Font, style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
207
|
+
case 3: {
|
|
208
|
+
const clr_2 = this$.fields[1];
|
|
209
|
+
return new Style([0, new Color(1, [clr_2])], style.StrokeWidth, style.StrokeDashArray, new FillStyle(0, [[1, new Color(1, [clr_2])]]), style.Animation, this$.fields[0], style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
210
|
+
}
|
|
211
|
+
case 4:
|
|
212
|
+
return new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, style.Fill, style.Animation, style.Font, style.Cursor, this$.fields[0], style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
213
|
+
default:
|
|
214
|
+
return this$.fields[0](style);
|
|
215
|
+
}
|
|
183
216
|
}
|
|
184
217
|
|
|
185
218
|
export class EventHandler extends Union {
|
|
@@ -219,12 +252,12 @@ export class Shape extends Union {
|
|
|
219
252
|
this.fields = fields;
|
|
220
253
|
}
|
|
221
254
|
cases() {
|
|
222
|
-
return ["Style", "Text", "AutoScale", "InnerScale", "NestX", "NestY", "Line", "Bubble", "Shape", "Layered", "Axes", "Interactive", "Padding", "Offset"];
|
|
255
|
+
return ["Image", "Style", "Text", "AutoScale", "InnerScale", "NestX", "NestY", "Line", "Bubble", "Shape", "Layered", "Axes", "Interactive", "Padding", "Offset"];
|
|
223
256
|
}
|
|
224
257
|
}
|
|
225
258
|
|
|
226
259
|
export function Shape_$reflection(gen0, gen1) {
|
|
227
|
-
return union_type("Compost.Shape", [gen0, gen1], Shape, () => [[["Item1",
|
|
260
|
+
return union_type("Compost.Shape", [gen0, gen1], Shape, () => [[["Item1", string_type], ["Item2", tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))], ["Item3", tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))]], [["Item1", StyleConfig_$reflection()], ["Item2", Shape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen1)], ["Item3", VerticalAlign_$reflection()], ["Item4", HorizontalAlign_$reflection()], ["Item5", float64_type], ["Item6", string_type]], [["Item1", bool_type], ["Item2", bool_type], ["Item3", Shape_$reflection(gen0, gen1)]], [["Item1", option_type(Scale_$reflection(gen0))], ["Item2", option_type(Scale_$reflection(gen1))], ["Item3", Shape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen0)], ["Item3", Shape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen1)], ["Item2", Value_$reflection(gen1)], ["Item3", Shape_$reflection(gen0, gen1)]], [["Item", class_type("System.Collections.Generic.IEnumerable`1", [tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))])]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen1)], ["Item3", float64_type], ["Item4", float64_type]], [["Item", class_type("System.Collections.Generic.IEnumerable`1", [tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))])]], [["Item", class_type("System.Collections.Generic.IEnumerable`1", [Shape_$reflection(gen0, gen1)])]], [["Item1", bool_type], ["Item2", bool_type], ["Item3", bool_type], ["Item4", bool_type], ["Item5", Shape_$reflection(gen0, gen1)]], [["Item1", class_type("System.Collections.Generic.IEnumerable`1", [EventHandler_$reflection(gen0, gen1)])], ["Item2", Shape_$reflection(gen0, gen1)]], [["Item1", tuple_type(float64_type, float64_type, float64_type, float64_type)], ["Item2", Shape_$reflection(gen0, gen1)]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", Shape_$reflection(gen0, gen1)]]]);
|
|
228
261
|
}
|
|
229
262
|
|
|
230
263
|
export class Svg_StringBuilder {
|
|
@@ -271,17 +304,17 @@ export class Svg_Svg extends Union {
|
|
|
271
304
|
this.fields = fields;
|
|
272
305
|
}
|
|
273
306
|
cases() {
|
|
274
|
-
return ["Path", "Ellipse", "Rect", "Text", "Combine", "Empty"];
|
|
307
|
+
return ["Image", "Path", "Ellipse", "Rect", "Text", "Combine", "Empty"];
|
|
275
308
|
}
|
|
276
309
|
}
|
|
277
310
|
|
|
278
311
|
export function Svg_Svg_$reflection() {
|
|
279
|
-
return union_type("Compost.Svg.Svg", [], Svg_Svg, () => [[["Item1", array_type(Svg_PathSegment_$reflection())], ["Item2", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", tuple_type(float64_type, float64_type)], ["Item3", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", tuple_type(float64_type, float64_type)], ["Item3", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", string_type], ["Item3", float64_type], ["Item4", string_type]], [["Item", array_type(Svg_Svg_$reflection())]], []]);
|
|
312
|
+
return union_type("Compost.Svg.Svg", [], Svg_Svg, () => [[["Item1", string_type], ["Item2", tuple_type(float64_type, float64_type)], ["Item3", tuple_type(float64_type, float64_type)], ["Item4", string_type]], [["Item1", array_type(Svg_PathSegment_$reflection())], ["Item2", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", tuple_type(float64_type, float64_type)], ["Item3", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", tuple_type(float64_type, float64_type)], ["Item3", string_type]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", string_type], ["Item3", float64_type], ["Item4", string_type]], [["Item", array_type(Svg_Svg_$reflection())]], []]);
|
|
280
313
|
}
|
|
281
314
|
|
|
282
315
|
export function Svg_mapSvg(f, _arg) {
|
|
283
|
-
if (_arg.tag ===
|
|
284
|
-
return new Svg_Svg(
|
|
316
|
+
if (_arg.tag === 5) {
|
|
317
|
+
return new Svg_Svg(5, [map((_arg_1) => Svg_mapSvg(f, _arg_1), _arg.fields[0])]);
|
|
285
318
|
}
|
|
286
319
|
else {
|
|
287
320
|
return f(_arg);
|
|
@@ -322,17 +355,19 @@ export function Svg_RenderingContext_$reflection() {
|
|
|
322
355
|
export function Svg_renderSvg(ctx, svg) {
|
|
323
356
|
return delay(() => {
|
|
324
357
|
switch (svg.tag) {
|
|
325
|
-
case
|
|
358
|
+
case 4: {
|
|
326
359
|
const y = svg.fields[0][1];
|
|
327
360
|
const x = svg.fields[0][0];
|
|
328
361
|
const rotation = svg.fields[2];
|
|
329
362
|
return singleton(El_op_Dynamic_Z451691CD(s_6, "text")(toList(delay(() => append(singleton(op_EqualsGreater("style", svg.fields[3])), delay(() => ((rotation === 0) ? append(singleton(op_EqualsGreater("x", x.toString())), delay(() => singleton(op_EqualsGreater("y", y.toString())))) : append(singleton(op_EqualsGreater("x", "0")), delay(() => append(singleton(op_EqualsGreater("y", "0")), delay(() => singleton(op_EqualsGreater("transform", toText(printf("translate(%f,%f) rotate(%f)"))(x)(y)(rotation)))))))))))))(singleton_1(text(svg.fields[1]))));
|
|
330
363
|
}
|
|
331
|
-
case
|
|
364
|
+
case 5:
|
|
332
365
|
return collect((s) => Svg_renderSvg(ctx, s), svg.fields[0]);
|
|
333
|
-
case
|
|
366
|
+
case 2:
|
|
334
367
|
return singleton(El_op_Dynamic_Z451691CD(s_6, "ellipse")(ofArray([op_EqualsGreater("cx", svg.fields[0][0].toString()), op_EqualsGreater("cy", svg.fields[0][1].toString()), op_EqualsGreater("rx", svg.fields[1][0].toString()), op_EqualsGreater("ry", svg.fields[1][1].toString()), op_EqualsGreater("style", svg.fields[2])]))(empty()));
|
|
335
|
-
case
|
|
368
|
+
case 0:
|
|
369
|
+
return singleton(El_op_Dynamic_Z451691CD(s_6, "image")(ofArray([op_EqualsGreater("href", svg.fields[0]), op_EqualsGreater("preserveAspectRatio", svg.fields[3]), op_EqualsGreater("x", svg.fields[1][0].toString()), op_EqualsGreater("y", svg.fields[1][1].toString()), op_EqualsGreater("width", svg.fields[2][0].toString()), op_EqualsGreater("height", svg.fields[2][1].toString())]))(empty()));
|
|
370
|
+
case 3: {
|
|
336
371
|
const y2 = svg.fields[1][1];
|
|
337
372
|
const y1 = svg.fields[0][1];
|
|
338
373
|
const x2 = svg.fields[1][0];
|
|
@@ -343,7 +378,7 @@ export function Svg_renderSvg(ctx, svg) {
|
|
|
343
378
|
const matchValue_3 = Math.abs(y1 - y2);
|
|
344
379
|
return singleton(El_op_Dynamic_Z451691CD(s_6, "rect")(ofArray([op_EqualsGreater("x", matchValue.toString()), op_EqualsGreater("y", matchValue_1.toString()), op_EqualsGreater("width", matchValue_2.toString()), op_EqualsGreater("height", matchValue_3.toString()), op_EqualsGreater("style", svg.fields[2])]))(empty()));
|
|
345
380
|
}
|
|
346
|
-
case
|
|
381
|
+
case 1:
|
|
347
382
|
return singleton(El_op_Dynamic_Z451691CD(s_6, "path")(ofArray([op_EqualsGreater("d", Svg_formatPath(svg.fields[0])), op_EqualsGreater("style", svg.fields[1])]))(empty()));
|
|
348
383
|
default: {
|
|
349
384
|
return empty_1();
|
|
@@ -382,8 +417,8 @@ export function Svg_formatStyle(defs, style) {
|
|
|
382
417
|
const ease = matchValue[1];
|
|
383
418
|
const anim = matchValue[2];
|
|
384
419
|
const id = "anim_" + replace((copyOfStruct = newGuid(), copyOfStruct), "-", "");
|
|
385
|
-
const fromstyle = Svg_formatStyle(defs, new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, style.Fill, undefined, style.Font, style.Cursor, style.FormatAxisXLabel, style.FormatAxisYLabel));
|
|
386
|
-
const tostyle = Svg_formatStyle(defs, (bind$0040 = anim(style), new Style(bind$0040.StrokeColor, bind$0040.StrokeWidth, bind$0040.StrokeDashArray, bind$0040.Fill, undefined, bind$0040.Font, bind$0040.Cursor, bind$0040.FormatAxisXLabel, bind$0040.FormatAxisYLabel)));
|
|
420
|
+
const fromstyle = Svg_formatStyle(defs, new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, style.Fill, undefined, style.Font, style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel));
|
|
421
|
+
const tostyle = Svg_formatStyle(defs, (bind$0040 = anim(style), new Style(bind$0040.StrokeColor, bind$0040.StrokeWidth, bind$0040.StrokeDashArray, bind$0040.Fill, undefined, bind$0040.Font, bind$0040.Cursor, bind$0040.PreserveAspectRatio, bind$0040.FormatAxisXLabel, bind$0040.FormatAxisYLabel)));
|
|
387
422
|
const item = El_op_Dynamic_Z451691CD(h_2, "style")(empty())(singleton_1(text(toText(printf("@keyframes %s { from { %s } to { %s } }"))(id)(fromstyle)(tostyle))));
|
|
388
423
|
void (defs.push(item));
|
|
389
424
|
patternInput = [anim(style), toText(printf("animation: %s %dms %s; "))(id)(ms)(ease)];
|
|
@@ -399,12 +434,12 @@ export class Scales_ScaledShape extends Union {
|
|
|
399
434
|
this.fields = fields;
|
|
400
435
|
}
|
|
401
436
|
cases() {
|
|
402
|
-
return ["ScaledStyle", "ScaledText", "ScaledLine", "ScaledBubble", "ScaledShape", "ScaledLayered", "ScaledInteractive", "ScaledPadding", "ScaledOffset", "ScaledNestX", "ScaledNestY"];
|
|
437
|
+
return ["ScaledStyle", "ScaledText", "ScaledLine", "ScaledBubble", "ScaledShape", "ScaledImage", "ScaledLayered", "ScaledInteractive", "ScaledPadding", "ScaledOffset", "ScaledNestX", "ScaledNestY"];
|
|
403
438
|
}
|
|
404
439
|
}
|
|
405
440
|
|
|
406
441
|
export function Scales_ScaledShape_$reflection(gen0, gen1) {
|
|
407
|
-
return union_type("Compost.Scales.ScaledShape", [gen0, gen1], Scales_ScaledShape, () => [[["Item1",
|
|
442
|
+
return union_type("Compost.Scales.ScaledShape", [gen0, gen1], Scales_ScaledShape, () => [[["Item1", StyleConfig_$reflection()], ["Item2", Scales_ScaledShape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen1)], ["Item3", VerticalAlign_$reflection()], ["Item4", HorizontalAlign_$reflection()], ["Item5", float64_type], ["Item6", string_type]], [["Item", array_type(tuple_type(Value_$reflection(gen0), Value_$reflection(gen1)))]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen1)], ["Item3", float64_type], ["Item4", float64_type]], [["Item", array_type(tuple_type(Value_$reflection(gen0), Value_$reflection(gen1)))]], [["Item1", string_type], ["Item2", tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))], ["Item3", tuple_type(Value_$reflection(gen0), Value_$reflection(gen1))]], [["Item", array_type(Scales_ScaledShape_$reflection(gen0, gen1))]], [["Item1", class_type("System.Collections.Generic.IEnumerable`1", [EventHandler_$reflection(gen0, gen1)])], ["Item2", Scale_$reflection(gen0)], ["Item3", Scale_$reflection(gen1)], ["Item4", Scales_ScaledShape_$reflection(gen0, gen1)]], [["Item1", tuple_type(float64_type, float64_type, float64_type, float64_type)], ["Item2", Scale_$reflection(gen0)], ["Item3", Scale_$reflection(gen1)], ["Item4", Scales_ScaledShape_$reflection(gen0, gen1)]], [["Item1", tuple_type(float64_type, float64_type)], ["Item2", Scales_ScaledShape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen0)], ["Item2", Value_$reflection(gen0)], ["Item3", Scale_$reflection(gen0)], ["Item4", Scales_ScaledShape_$reflection(gen0, gen1)]], [["Item1", Value_$reflection(gen1)], ["Item2", Value_$reflection(gen1)], ["Item3", Scale_$reflection(gen1)], ["Item4", Scales_ScaledShape_$reflection(gen0, gen1)]]]);
|
|
408
443
|
}
|
|
409
444
|
|
|
410
445
|
export function Scales_getExtremes(_arg) {
|
|
@@ -597,25 +632,25 @@ export function Scales_calculateShapeScales(points) {
|
|
|
597
632
|
export function Scales_calculateScales(style, shape) {
|
|
598
633
|
const calculateScales = (shape_2) => Scales_calculateScales(style, shape_2);
|
|
599
634
|
switch (shape.tag) {
|
|
600
|
-
case
|
|
635
|
+
case 5: {
|
|
601
636
|
const nx2 = shape.fields[1];
|
|
602
637
|
const nx1 = shape.fields[0];
|
|
603
638
|
const patternInput_1 = calculateScales(shape.fields[2]);
|
|
604
|
-
return [[Scales_calculateShapeScale([nx1, nx2]), patternInput_1[0][1]], new Scales_ScaledShape(
|
|
639
|
+
return [[Scales_calculateShapeScale([nx1, nx2]), patternInput_1[0][1]], new Scales_ScaledShape(10, [nx1, nx2, patternInput_1[0][0], patternInput_1[1]])];
|
|
605
640
|
}
|
|
606
|
-
case
|
|
641
|
+
case 6: {
|
|
607
642
|
const ny2 = shape.fields[1];
|
|
608
643
|
const ny1 = shape.fields[0];
|
|
609
644
|
const patternInput_2 = calculateScales(shape.fields[2]);
|
|
610
|
-
return [[patternInput_2[0][0], Scales_calculateShapeScale([ny1, ny2])], new Scales_ScaledShape(
|
|
645
|
+
return [[patternInput_2[0][0], Scales_calculateShapeScale([ny1, ny2])], new Scales_ScaledShape(11, [ny1, ny2, patternInput_2[0][1], patternInput_2[1]])];
|
|
611
646
|
}
|
|
612
|
-
case
|
|
647
|
+
case 4: {
|
|
613
648
|
const sy = shape.fields[1];
|
|
614
649
|
const sx = shape.fields[0];
|
|
615
650
|
const patternInput_3 = calculateScales(shape.fields[2]);
|
|
616
651
|
return [[(sx != null) ? sx : patternInput_3[0][0], (sy != null) ? sy : patternInput_3[0][1]], patternInput_3[1]];
|
|
617
652
|
}
|
|
618
|
-
case
|
|
653
|
+
case 3: {
|
|
619
654
|
const patternInput_4 = calculateScales(shape.fields[2]);
|
|
620
655
|
const isy_3 = patternInput_4[0][1];
|
|
621
656
|
const isx_3 = patternInput_4[0][0];
|
|
@@ -630,17 +665,17 @@ export function Scales_calculateScales(style, shape) {
|
|
|
630
665
|
};
|
|
631
666
|
return [[shape.fields[0] ? autoScale(isx_3) : isx_3, shape.fields[1] ? autoScale(isy_3) : isy_3], patternInput_4[1]];
|
|
632
667
|
}
|
|
633
|
-
case
|
|
668
|
+
case 14: {
|
|
634
669
|
const patternInput_6 = calculateScales(shape.fields[1]);
|
|
635
|
-
return [patternInput_6[0], new Scales_ScaledShape(
|
|
670
|
+
return [patternInput_6[0], new Scales_ScaledShape(9, [shape.fields[0], patternInput_6[1]])];
|
|
636
671
|
}
|
|
637
|
-
case
|
|
672
|
+
case 13: {
|
|
638
673
|
const patternInput_7 = calculateScales(shape.fields[1]);
|
|
639
674
|
const sy_3 = patternInput_7[0][1];
|
|
640
675
|
const sx_3 = patternInput_7[0][0];
|
|
641
|
-
return [[sx_3, sy_3], new Scales_ScaledShape(
|
|
676
|
+
return [[sx_3, sy_3], new Scales_ScaledShape(8, [shape.fields[0], sx_3, sy_3, patternInput_7[1]])];
|
|
642
677
|
}
|
|
643
|
-
case
|
|
678
|
+
case 8: {
|
|
644
679
|
const y = shape.fields[1];
|
|
645
680
|
const x = shape.fields[0];
|
|
646
681
|
const makeSingletonScale = (_arg_1) => {
|
|
@@ -654,7 +689,7 @@ export function Scales_calculateScales(style, shape) {
|
|
|
654
689
|
};
|
|
655
690
|
return [[makeSingletonScale(x), makeSingletonScale(y)], new Scales_ScaledShape(3, [x, y, shape.fields[2], shape.fields[3]])];
|
|
656
691
|
}
|
|
657
|
-
case
|
|
692
|
+
case 2: {
|
|
658
693
|
const y_1 = shape.fields[1];
|
|
659
694
|
const x_1 = shape.fields[0];
|
|
660
695
|
const makeSingletonScale_1 = (_arg_2) => {
|
|
@@ -668,15 +703,20 @@ export function Scales_calculateScales(style, shape) {
|
|
|
668
703
|
};
|
|
669
704
|
return [[makeSingletonScale_1(x_1), makeSingletonScale_1(y_1)], new Scales_ScaledShape(1, [x_1, y_1, shape.fields[2], shape.fields[3], shape.fields[4], shape.fields[5]])];
|
|
670
705
|
}
|
|
671
|
-
case
|
|
706
|
+
case 7: {
|
|
672
707
|
const line_1 = toArray(shape.fields[0]);
|
|
673
708
|
return [Scales_calculateShapeScales(line_1), new Scales_ScaledShape(2, [line_1])];
|
|
674
709
|
}
|
|
675
|
-
case
|
|
710
|
+
case 0: {
|
|
711
|
+
const pt2 = shape.fields[2];
|
|
712
|
+
const pt1 = shape.fields[1];
|
|
713
|
+
return [Scales_calculateShapeScales([pt1, pt2]), new Scales_ScaledShape(5, [shape.fields[0], pt1, pt2])];
|
|
714
|
+
}
|
|
715
|
+
case 9: {
|
|
676
716
|
const points_1 = toArray(shape.fields[0]);
|
|
677
717
|
return [Scales_calculateShapeScales(points_1), new Scales_ScaledShape(4, [points_1])];
|
|
678
718
|
}
|
|
679
|
-
case
|
|
719
|
+
case 11: {
|
|
680
720
|
const showTop = shape.fields[0];
|
|
681
721
|
const showRight = shape.fields[1];
|
|
682
722
|
const showLeft = shape.fields[3];
|
|
@@ -691,25 +731,25 @@ export function Scales_calculateScales(style, shape) {
|
|
|
691
731
|
const lx = matchValue[0];
|
|
692
732
|
const hy = matchValue_1[1];
|
|
693
733
|
const hx = matchValue[1];
|
|
694
|
-
const LineStyle = (clr, alpha, width, shape_18) => (new Shape(0, [(s) => (new Style([alpha, new Color(1, [clr])], new Width(width), s.StrokeDashArray, new FillStyle(0, [[1, new Color(1, ["transparent"])]]), s.Animation, s.Font, s.Cursor, s.FormatAxisXLabel, s.FormatAxisYLabel)), shape_18]));
|
|
695
|
-
const FontStyle = (style_2, shape_19) => (new Shape(0, [(s_1) => (new Style([0, new Color(1, ["transparent"])], s_1.StrokeWidth, s_1.StrokeDashArray, new FillStyle(0, [[1, new Color(1, ["black"])]]), s_1.Animation, style_2, s_1.Cursor, s_1.FormatAxisXLabel, s_1.FormatAxisYLabel)), shape_19]));
|
|
696
|
-
return calculateScales(new Shape(
|
|
734
|
+
const LineStyle = (clr, alpha, width, shape_18) => (new Shape(1, [new StyleConfig(0, [(s) => (new Style([alpha, new Color(1, [clr])], new Width(width), s.StrokeDashArray, new FillStyle(0, [[1, new Color(1, ["transparent"])]]), s.Animation, s.Font, s.Cursor, s.PreserveAspectRatio, s.FormatAxisXLabel, s.FormatAxisYLabel))]), shape_18]));
|
|
735
|
+
const FontStyle = (style_2, shape_19) => (new Shape(1, [new StyleConfig(0, [(s_1) => (new Style([0, new Color(1, ["transparent"])], s_1.StrokeWidth, s_1.StrokeDashArray, new FillStyle(0, [[1, new Color(1, ["black"])]]), s_1.Animation, style_2, s_1.Cursor, s_1.PreserveAspectRatio, s_1.FormatAxisXLabel, s_1.FormatAxisYLabel))]), shape_19]));
|
|
736
|
+
return calculateScales(new Shape(13, [[showTop ? 30 : 0, showRight ? 50 : 0, showBottom ? 30 : 0, showLeft ? 50 : 0], new Shape(10, [toList(delay(() => append(singleton(new Shape(4, [sx_4, sy_4, new Shape(10, [toList(delay(() => append(map_1((x_2) => LineStyle("#e4e4e4", 1, 1, new Shape(7, [[[x_2, ly], [x_2, hy]]])), Scales_generateAxisSteps(sx_4)), delay(() => map_1((y_2) => LineStyle("#e4e4e4", 1, 1, new Shape(7, [[[lx, y_2], [hx, y_2]]])), Scales_generateAxisSteps(sy_4))))))])])), delay(() => append(showTop ? append(singleton(LineStyle("black", 1, 2, new Shape(7, [[[lx, hy], [hx, hy]]]))), delay(() => collect((matchValue_2) => singleton(FontStyle("9pt sans-serif", new Shape(14, [[0, -10], new Shape(2, [matchValue_2[0], hy, new VerticalAlign(0, []), new HorizontalAlign(1, []), 0, matchValue_2[1]])]))), Scales_generateAxisLabels(style.FormatAxisXLabel, sx_4)))) : empty_1(), delay(() => append(showRight ? append(singleton(LineStyle("black", 1, 2, new Shape(7, [[[hx, hy], [hx, ly]]]))), delay(() => collect((matchValue_3) => singleton(FontStyle("9pt sans-serif", new Shape(14, [[10, 0], new Shape(2, [hx, matchValue_3[0], new VerticalAlign(1, []), new HorizontalAlign(0, []), 0, matchValue_3[1]])]))), Scales_generateAxisLabels(style.FormatAxisYLabel, sy_4)))) : empty_1(), delay(() => append(showBottom ? append(singleton(LineStyle("black", 1, 2, new Shape(7, [[[lx, ly], [hx, ly]]]))), delay(() => collect((matchValue_4) => singleton(FontStyle("9pt sans-serif", new Shape(14, [[0, 10], new Shape(2, [matchValue_4[0], ly, new VerticalAlign(2, []), new HorizontalAlign(1, []), 0, matchValue_4[1]])]))), Scales_generateAxisLabels(style.FormatAxisXLabel, sx_4)))) : empty_1(), delay(() => append(showLeft ? append(singleton(LineStyle("black", 1, 2, new Shape(7, [[[lx, hy], [lx, ly]]]))), delay(() => collect((matchValue_5) => singleton(FontStyle("9pt sans-serif", new Shape(14, [[-10, 0], new Shape(2, [lx, matchValue_5[0], new VerticalAlign(1, []), new HorizontalAlign(2, []), 0, matchValue_5[1]])]))), Scales_generateAxisLabels(style.FormatAxisYLabel, sy_4)))) : empty_1(), delay(() => singleton(shape_17)))))))))))))])]));
|
|
697
737
|
}
|
|
698
|
-
case
|
|
738
|
+
case 10: {
|
|
699
739
|
const scaled = map(calculateScales, Array.from(shape.fields[0]));
|
|
700
740
|
const sxs = map((tupledArg) => tupledArg[0][0], scaled);
|
|
701
741
|
const sys = map((tupledArg_1) => tupledArg_1[0][1], scaled);
|
|
702
|
-
return [[sxs.reduce(Scales_unionScales), sys.reduce(Scales_unionScales)], new Scales_ScaledShape(
|
|
742
|
+
return [[sxs.reduce(Scales_unionScales), sys.reduce(Scales_unionScales)], new Scales_ScaledShape(6, [map((tuple) => tuple[1], scaled)])];
|
|
703
743
|
}
|
|
704
|
-
case
|
|
744
|
+
case 12: {
|
|
705
745
|
const patternInput_10 = calculateScales(shape.fields[1]);
|
|
706
|
-
const
|
|
707
|
-
return [
|
|
746
|
+
const scales_9 = patternInput_10[0];
|
|
747
|
+
return [scales_9, new Scales_ScaledShape(7, [shape.fields[0], scales_9[0], scales_9[1], patternInput_10[1]])];
|
|
708
748
|
}
|
|
709
749
|
default: {
|
|
710
|
-
const
|
|
711
|
-
const patternInput = Scales_calculateScales(
|
|
712
|
-
return [patternInput[0], new Scales_ScaledShape(0, [
|
|
750
|
+
const sc = shape.fields[0];
|
|
751
|
+
const patternInput = Scales_calculateScales(StyleConfig__Apply_4E2CA0C5(sc, style), shape.fields[1]);
|
|
752
|
+
return [patternInput[0], new Scales_ScaledShape(0, [sc, patternInput[1]])];
|
|
713
753
|
}
|
|
714
754
|
}
|
|
715
755
|
}
|
|
@@ -795,12 +835,12 @@ export function Drawing_DrawingContext_$reflection() {
|
|
|
795
835
|
|
|
796
836
|
export function Drawing_hideFill(style) {
|
|
797
837
|
let matchValue, f;
|
|
798
|
-
return new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, new FillStyle(0, [[0, new Color(0, [0, 0, 0])]]), (matchValue = style.Animation, (matchValue != null) ? ((f = matchValue[2], [matchValue[0], matchValue[1], (arg) => Drawing_hideFill(f(arg))])) : undefined), style.Font, style.Cursor, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
838
|
+
return new Style(style.StrokeColor, style.StrokeWidth, style.StrokeDashArray, new FillStyle(0, [[0, new Color(0, [0, 0, 0])]]), (matchValue = style.Animation, (matchValue != null) ? ((f = matchValue[2], [matchValue[0], matchValue[1], (arg) => Drawing_hideFill(f(arg))])) : undefined), style.Font, style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
799
839
|
}
|
|
800
840
|
|
|
801
841
|
export function Drawing_hideStroke(style) {
|
|
802
842
|
let matchValue, f;
|
|
803
|
-
return new Style([0, style.StrokeColor[1]], style.StrokeWidth, style.StrokeDashArray, style.Fill, (matchValue = style.Animation, (matchValue != null) ? ((f = matchValue[2], [matchValue[0], matchValue[1], (arg) => Drawing_hideStroke(f(arg))])) : undefined), style.Font, style.Cursor, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
843
|
+
return new Style([0, style.StrokeColor[1]], style.StrokeWidth, style.StrokeDashArray, style.Fill, (matchValue = style.Animation, (matchValue != null) ? ((f = matchValue[2], [matchValue[0], matchValue[1], (arg) => Drawing_hideStroke(f(arg))])) : undefined), style.Font, style.Cursor, style.PreserveAspectRatio, style.FormatAxisXLabel, style.FormatAxisYLabel);
|
|
804
844
|
}
|
|
805
845
|
|
|
806
846
|
export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut, area__3_mut, scales__mut, scales__1_mut, shape_mut) {
|
|
@@ -817,7 +857,7 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
817
857
|
const sx = scales_1[0];
|
|
818
858
|
const project = (tupledArg) => [Projections_projectOneX(x1, x2)(sx)(tupledArg[0]), Projections_projectOneY(y1, y2)(sy)(tupledArg[1])];
|
|
819
859
|
switch (shape.tag) {
|
|
820
|
-
case
|
|
860
|
+
case 11: {
|
|
821
861
|
ctx_mut = ctx;
|
|
822
862
|
area__mut = x1;
|
|
823
863
|
area__1_mut = Projections_projectOneY(y1, y2)(sy)(shape.fields[0]);
|
|
@@ -828,7 +868,7 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
828
868
|
shape_mut = shape.fields[3];
|
|
829
869
|
continue Drawing_drawShape;
|
|
830
870
|
}
|
|
831
|
-
case
|
|
871
|
+
case 9: {
|
|
832
872
|
const dy = shape.fields[0][1];
|
|
833
873
|
const dx = shape.fields[0][0];
|
|
834
874
|
ctx_mut = ctx;
|
|
@@ -841,10 +881,10 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
841
881
|
shape_mut = shape.fields[1];
|
|
842
882
|
continue Drawing_drawShape;
|
|
843
883
|
}
|
|
844
|
-
case
|
|
845
|
-
return new Svg_Svg(
|
|
884
|
+
case 6:
|
|
885
|
+
return new Svg_Svg(5, [map((shape_4) => Drawing_drawShape(ctx, area_1[0], area_1[1], area_1[2], area_1[3], scales_1[0], scales_1[1], shape_4), shape.fields[0])]);
|
|
846
886
|
case 0: {
|
|
847
|
-
ctx_mut = (new Drawing_DrawingContext(shape.fields[0]
|
|
887
|
+
ctx_mut = (new Drawing_DrawingContext(StyleConfig__Apply_4E2CA0C5(shape.fields[0], ctx.Style), ctx.Definitions));
|
|
848
888
|
area__mut = area_1[0];
|
|
849
889
|
area__1_mut = area_1[1];
|
|
850
890
|
area__2_mut = area_1[2];
|
|
@@ -856,9 +896,18 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
856
896
|
}
|
|
857
897
|
case 4: {
|
|
858
898
|
const points = shape.fields[0];
|
|
859
|
-
return new Svg_Svg(
|
|
899
|
+
return new Svg_Svg(1, [toArray(delay(() => append(singleton(new Svg_PathSegment(0, [project(item_2(0, points))])), delay(() => append(map_1((pt) => (new Svg_PathSegment(1, [project(pt)])), skip(1, points)), delay(() => singleton(new Svg_PathSegment(1, [project(item_2(0, points))])))))))), Svg_formatStyle(ctx.Definitions, Drawing_hideStroke(ctx.Style))]);
|
|
860
900
|
}
|
|
861
|
-
case
|
|
901
|
+
case 5: {
|
|
902
|
+
const patternInput = project(shape.fields[1]);
|
|
903
|
+
const y1_1 = patternInput[1];
|
|
904
|
+
const x1_1 = patternInput[0];
|
|
905
|
+
const patternInput_1 = project(shape.fields[2]);
|
|
906
|
+
const y2_1 = patternInput_1[1];
|
|
907
|
+
const x2_1 = patternInput_1[0];
|
|
908
|
+
return new Svg_Svg(0, [shape.fields[0], [min(x1_1, x2_1), min(y1_1, y2_1)], [Math.abs(x2_1 - x1_1), Math.abs(y2_1 - y1_1)], ctx.Style.PreserveAspectRatio]);
|
|
909
|
+
}
|
|
910
|
+
case 8: {
|
|
862
911
|
const isy_1 = shape.fields[2];
|
|
863
912
|
const isx_1 = shape.fields[1];
|
|
864
913
|
const calculateNestedRange = (rev) => ((tupledArg_1) => ((ins) => {
|
|
@@ -883,13 +932,13 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
883
932
|
}
|
|
884
933
|
};
|
|
885
934
|
}));
|
|
886
|
-
const
|
|
887
|
-
const
|
|
935
|
+
const patternInput_2 = calculateNestedRange(false)([x1, x2])(isx_1)(sx);
|
|
936
|
+
const patternInput_3 = calculateNestedRange(true)([y1, y2])(isy_1)(sy);
|
|
888
937
|
ctx_mut = ctx;
|
|
889
|
-
area__mut = (
|
|
890
|
-
area__1_mut = (
|
|
891
|
-
area__2_mut = (
|
|
892
|
-
area__3_mut = (
|
|
938
|
+
area__mut = (patternInput_2[0] + shape.fields[0][3]);
|
|
939
|
+
area__1_mut = (patternInput_3[0] + shape.fields[0][0]);
|
|
940
|
+
area__2_mut = (patternInput_2[1] - shape.fields[0][1]);
|
|
941
|
+
area__3_mut = (patternInput_3[1] - shape.fields[0][2]);
|
|
893
942
|
scales__mut = isx_1;
|
|
894
943
|
scales__1_mut = isy_1;
|
|
895
944
|
shape_mut = shape.fields[3];
|
|
@@ -897,18 +946,18 @@ export function Drawing_drawShape(ctx_mut, area__mut, area__1_mut, area__2_mut,
|
|
|
897
946
|
}
|
|
898
947
|
case 2: {
|
|
899
948
|
const line = shape.fields[0];
|
|
900
|
-
return new Svg_Svg(
|
|
949
|
+
return new Svg_Svg(1, [Array.from(delay(() => append(singleton(new Svg_PathSegment(0, [project(head(line))])), delay(() => map_1((pt_1) => (new Svg_PathSegment(1, [project(pt_1)])), skip(1, line)))))), Svg_formatStyle(ctx.Definitions, Drawing_hideFill(ctx.Style))]);
|
|
901
950
|
}
|
|
902
951
|
case 1: {
|
|
903
952
|
const va = shape.fields[2];
|
|
904
953
|
const ha = shape.fields[3];
|
|
905
954
|
const va_1 = (va.tag === 2) ? "hanging" : ((va.tag === 1) ? "middle" : "baseline");
|
|
906
955
|
const ha_1 = (ha.tag === 1) ? "middle" : ((ha.tag === 2) ? "end" : "start");
|
|
907
|
-
return new Svg_Svg(
|
|
956
|
+
return new Svg_Svg(4, [project([shape.fields[0], shape.fields[1]]), shape.fields[5], shape.fields[4], toText(printf("alignment-baseline:%s; text-anchor:%s;"))(va_1)(ha_1) + Svg_formatStyle(ctx.Definitions, ctx.Style)]);
|
|
908
957
|
}
|
|
909
958
|
case 3:
|
|
910
|
-
return new Svg_Svg(
|
|
911
|
-
case
|
|
959
|
+
return new Svg_Svg(2, [project([shape.fields[0], shape.fields[1]]), [shape.fields[2], shape.fields[3]], Svg_formatStyle(ctx.Definitions, ctx.Style)]);
|
|
960
|
+
case 7: {
|
|
912
961
|
ctx_mut = ctx;
|
|
913
962
|
area__mut = area_1[0];
|
|
914
963
|
area__1_mut = area_1[1];
|
|
@@ -1123,7 +1172,7 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1123
1172
|
continue Events_triggerEvent;
|
|
1124
1173
|
break;
|
|
1125
1174
|
}
|
|
1126
|
-
case
|
|
1175
|
+
case 9: {
|
|
1127
1176
|
const dy = shape.fields[0][1];
|
|
1128
1177
|
const dx = shape.fields[0][0];
|
|
1129
1178
|
area__mut = (x1 + dx);
|
|
@@ -1138,7 +1187,7 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1138
1187
|
continue Events_triggerEvent;
|
|
1139
1188
|
break;
|
|
1140
1189
|
}
|
|
1141
|
-
case
|
|
1190
|
+
case 10: {
|
|
1142
1191
|
area__mut = Projections_projectOneX(x1, x2)(sx)(shape.fields[0]);
|
|
1143
1192
|
area__1_mut = y1;
|
|
1144
1193
|
area__2_mut = Projections_projectOneX(x1, x2)(sx)(shape.fields[1]);
|
|
@@ -1151,7 +1200,7 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1151
1200
|
continue Events_triggerEvent;
|
|
1152
1201
|
break;
|
|
1153
1202
|
}
|
|
1154
|
-
case
|
|
1203
|
+
case 11: {
|
|
1155
1204
|
area__mut = x1;
|
|
1156
1205
|
area__1_mut = Projections_projectOneY(y1, y2)(sy)(shape.fields[0]);
|
|
1157
1206
|
area__2_mut = x2;
|
|
@@ -1164,7 +1213,7 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1164
1213
|
continue Events_triggerEvent;
|
|
1165
1214
|
break;
|
|
1166
1215
|
}
|
|
1167
|
-
case
|
|
1216
|
+
case 8: {
|
|
1168
1217
|
const isy_1 = shape.fields[2];
|
|
1169
1218
|
const isx_1 = shape.fields[1];
|
|
1170
1219
|
const calculateNestedRange = (rev) => ((tupledArg) => ((ins) => {
|
|
@@ -1203,14 +1252,14 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1203
1252
|
continue Events_triggerEvent;
|
|
1204
1253
|
break;
|
|
1205
1254
|
}
|
|
1206
|
-
case
|
|
1255
|
+
case 6: {
|
|
1207
1256
|
const shapes = shape.fields[0];
|
|
1208
1257
|
for (let idx = 0; idx <= (shapes.length - 1); idx++) {
|
|
1209
1258
|
Events_triggerEvent(area_1[0], area_1[1], area_1[2], area_1[3], scales_1[0], scales_1[1], item_2(idx, shapes), jse, event);
|
|
1210
1259
|
}
|
|
1211
1260
|
break;
|
|
1212
1261
|
}
|
|
1213
|
-
case
|
|
1262
|
+
case 7: {
|
|
1214
1263
|
const localEvent = Events_projectEvent(area_1[0], area_1[1], area_1[2], area_1[3], scales_1[0], scales_1[1], event);
|
|
1215
1264
|
if (Events_inScales(scales_1[0], scales_1[1], localEvent)) {
|
|
1216
1265
|
const enumerator = getEnumerator(shape.fields[0]);
|
|
@@ -1355,20 +1404,24 @@ export function Events_triggerEvent(area__mut, area__1_mut, area__2_mut, area__3
|
|
|
1355
1404
|
}
|
|
1356
1405
|
}
|
|
1357
1406
|
|
|
1407
|
+
export function Derived_PreserveAspectRatio(pa, s) {
|
|
1408
|
+
return new Shape(1, [new StyleConfig(4, [pa]), s]);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1358
1411
|
export function Derived_StrokeColor(clr, s) {
|
|
1359
|
-
return new Shape(
|
|
1412
|
+
return new Shape(1, [new StyleConfig(2, [clr]), s]);
|
|
1360
1413
|
}
|
|
1361
1414
|
|
|
1362
1415
|
export function Derived_FillColor(clr, s) {
|
|
1363
|
-
return new Shape(
|
|
1416
|
+
return new Shape(1, [new StyleConfig(1, [clr]), s]);
|
|
1364
1417
|
}
|
|
1365
1418
|
|
|
1366
1419
|
export function Derived_Font(font, clr, s) {
|
|
1367
|
-
return new Shape(
|
|
1420
|
+
return new Shape(1, [new StyleConfig(3, [font, clr]), s]);
|
|
1368
1421
|
}
|
|
1369
1422
|
|
|
1370
1423
|
export function Derived_Area(line) {
|
|
1371
|
-
return new Shape(
|
|
1424
|
+
return new Shape(9, [delay(() => {
|
|
1372
1425
|
const line_1 = Array.from(line);
|
|
1373
1426
|
const matchValue = item_2(0, line_1)[0];
|
|
1374
1427
|
const matchValue_1 = item_2(line_1.length - 1, line_1)[0];
|
|
@@ -1378,7 +1431,7 @@ export function Derived_Area(line) {
|
|
|
1378
1431
|
}
|
|
1379
1432
|
|
|
1380
1433
|
export function Derived_VArea(line) {
|
|
1381
|
-
return new Shape(
|
|
1434
|
+
return new Shape(9, [delay(() => {
|
|
1382
1435
|
const line_1 = Array.from(line);
|
|
1383
1436
|
const matchValue = item_2(0, line_1)[1];
|
|
1384
1437
|
const matchValue_1 = item_2(line_1.length - 1, line_1)[1];
|
|
@@ -1388,7 +1441,7 @@ export function Derived_VArea(line) {
|
|
|
1388
1441
|
}
|
|
1389
1442
|
|
|
1390
1443
|
export function Derived_VShiftedArea(offs, line) {
|
|
1391
|
-
return new Shape(
|
|
1444
|
+
return new Shape(9, [delay(() => {
|
|
1392
1445
|
const line_1 = Array.from(line);
|
|
1393
1446
|
const matchValue = item_2(0, line_1)[1];
|
|
1394
1447
|
const matchValue_1 = item_2(line_1.length - 1, line_1)[1];
|
|
@@ -1398,11 +1451,11 @@ export function Derived_VShiftedArea(offs, line) {
|
|
|
1398
1451
|
}
|
|
1399
1452
|
|
|
1400
1453
|
export function Derived_Bar(x, y) {
|
|
1401
|
-
return new Shape(
|
|
1454
|
+
return new Shape(9, [delay(() => append(singleton([new Value(1, [x]), new Value(0, [y, 0])]), delay(() => append(singleton([new Value(1, [x]), new Value(0, [y, 1])]), delay(() => append(singleton([new Value(1, [new continuous(0)]), new Value(0, [y, 1])]), delay(() => singleton([new Value(1, [new continuous(0)]), new Value(0, [y, 0])]))))))))]);
|
|
1402
1455
|
}
|
|
1403
1456
|
|
|
1404
1457
|
export function Derived_Column(x, y) {
|
|
1405
|
-
return new Shape(
|
|
1458
|
+
return new Shape(9, [delay(() => append(singleton([new Value(0, [x, 0]), new Value(1, [y])]), delay(() => append(singleton([new Value(0, [x, 1]), new Value(1, [y])]), delay(() => append(singleton([new Value(0, [x, 1]), new Value(1, [new continuous(0)])]), delay(() => singleton([new Value(0, [x, 0]), new Value(1, [new continuous(0)])]))))))))]);
|
|
1406
1459
|
}
|
|
1407
1460
|
|
|
1408
1461
|
export function Compost_niceNumber(num, decs) {
|
|
@@ -1445,7 +1498,7 @@ export function Compost_defaultFormat(scale, value) {
|
|
|
1445
1498
|
}
|
|
1446
1499
|
}
|
|
1447
1500
|
|
|
1448
|
-
export const Compost_defstyle = new Style([1, new Color(0, [256, 0, 0])], new Width(2), [], new FillStyle(0, [[1, new Color(0, [196, 196, 196])]]), undefined, "10pt sans-serif", "default", Compost_defaultFormat, Compost_defaultFormat);
|
|
1501
|
+
export const Compost_defstyle = new Style([1, new Color(0, [256, 0, 0])], new Width(2), [], new FillStyle(0, [[1, new Color(0, [196, 196, 196])]]), undefined, "10pt sans-serif", "default", "", Compost_defaultFormat, Compost_defaultFormat);
|
|
1449
1502
|
|
|
1450
1503
|
export function Compost_getRelativeLocation(el, x, y) {
|
|
1451
1504
|
const getOffset = (parent_mut, tupledArg_mut) => {
|
package/dist/html.js
CHANGED
|
@@ -7,7 +7,7 @@ import { equals, createAtom, defaultOf, createObj, disposeSafe, getEnumerator }
|
|
|
7
7
|
import { array_type, tuple_type, union_type, obj_type, string_type, lambda_type, unit_type, class_type } from "./fable_modules/fable-library-js.4.28.0/Reflection.js";
|
|
8
8
|
import { patch, diff, h as h_1 } from "virtual-dom";
|
|
9
9
|
import { toArray as toArray_1, singleton, empty, append as append_1 } from "./fable_modules/fable-library-js.4.28.0/List.js";
|
|
10
|
-
import { item, map as map_1 } from "./fable_modules/fable-library-js.4.28.0/Array.js";
|
|
10
|
+
import { fold, item, map as map_1 } from "./fable_modules/fable-library-js.4.28.0/Array.js";
|
|
11
11
|
import { Event as Event$ } from "./fable_modules/fable-library-js.4.28.0/Event.js";
|
|
12
12
|
import { startImmediate } from "./fable_modules/fable-library-js.4.28.0/Async.js";
|
|
13
13
|
import { singleton as singleton_1 } from "./fable_modules/fable-library-js.4.28.0/AsyncBuilder.js";
|
|
@@ -212,6 +212,15 @@ export function createVirtualDomApp(id, initial, r, u) {
|
|
|
212
212
|
}, event.Publish);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
export function foldDom(f, acc, node) {
|
|
216
|
+
if (node.tag === 1) {
|
|
217
|
+
return fold((acc_2, node_1) => foldDom(f, acc_2, node_1), f(acc, node.fields[0], node.fields[1], node.fields[2]), node.fields[3]);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
return acc;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
215
224
|
export function text(s_1) {
|
|
216
225
|
return new DomNode(0, [s_1]);
|
|
217
226
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compostjs",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable data visualization library for JavaScript",
|
|
6
6
|
"author": "Tomas Petricek",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"start": "dotnet fable watch src/compost/compost.fsproj --run npx vite",
|
|
10
|
-
"build": "dotnet fable src/compost/compost.fsproj -o dist && rm dist/fable_modules/.gitignore",
|
|
10
|
+
"build": "dotnet fable src/compost/compost.fsproj -o dist && rm -f dist/fable_modules/.gitignore",
|
|
11
11
|
"rebuild": "rm -rf dist && npm run build",
|
|
12
|
-
"standalone": "dotnet fable src/compost/compost.fsproj --run npx vite build && copy-latest.sh",
|
|
13
|
-
"
|
|
14
|
-
"release": "np --yolo --no-release-draft --no-publish
|
|
12
|
+
"standalone": "dotnet fable src/compost/compost.fsproj --run npx vite build && sh copy-latest.sh",
|
|
13
|
+
"test": "dotnet fable src/compost/compost.fsproj && npx vitest run",
|
|
14
|
+
"release:version": "np --yolo --no-release-draft --no-publish",
|
|
15
|
+
"release:standalone": "npm run standalone && git add . && git commit -m \"Update standalone release file in docs\"",
|
|
16
|
+
"release:publish": "npm run rebuild && npm publish",
|
|
17
|
+
"release": "npm run release:version && npm run release:standalone && npm run release:publish"
|
|
15
18
|
},
|
|
16
19
|
"repository": {
|
|
17
20
|
"type": "git",
|
|
@@ -32,6 +35,7 @@
|
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
37
|
"np": "^11.0.2",
|
|
35
|
-
"vite": "^6.0.0"
|
|
38
|
+
"vite": "^6.0.0",
|
|
39
|
+
"vitest": "^4.0.18"
|
|
36
40
|
}
|
|
37
41
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { scale as s, compost as c } from '../src/compost/compost.fs.js';
|
|
3
|
+
|
|
4
|
+
// Collect all elements matching a predicate using the F# foldDom helper.
|
|
5
|
+
// The fold callback receives (acc, tag, attrs) where attrs is a plain
|
|
6
|
+
// object with string attribute values (Events/Properties are excluded).
|
|
7
|
+
function findElements(node, pred) {
|
|
8
|
+
return c.foldDom((acc, tag, attrs) => {
|
|
9
|
+
if (pred(tag, attrs)) acc.push({ tag, attrs });
|
|
10
|
+
return acc;
|
|
11
|
+
}, [], node);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Parse an SVG path "d" attribute into an array of {cmd, x, y} objects.
|
|
15
|
+
// Handles format like "M0 200 L200 0 " where command letter is directly
|
|
16
|
+
// followed by the x coordinate.
|
|
17
|
+
function parsePath(d) {
|
|
18
|
+
const coords = [];
|
|
19
|
+
for (const m of d.matchAll(/([ML])(-?[\d.]+)\s+(-?[\d.]+)/g)) {
|
|
20
|
+
coords.push({ cmd: m[1], x: parseFloat(m[2]), y: parseFloat(m[3]) });
|
|
21
|
+
}
|
|
22
|
+
return coords;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('style rendering', () => {
|
|
26
|
+
it('renders a shape with fill color set', () => {
|
|
27
|
+
const sh = c.shape([[0, 0], [1, 0], [1, 1], [0, 1]]);
|
|
28
|
+
const svg = c.svg(200, 200, c.fillColor("red", sh));
|
|
29
|
+
const paths = findElements(svg, (tag) => tag === 'path');
|
|
30
|
+
expect(paths.length).toBe(1);
|
|
31
|
+
expect(paths[0].attrs.style).toContain('fill:red');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders a line with stroke color set', () => {
|
|
35
|
+
const line = c.line([[0, 0], [1, 1]]);
|
|
36
|
+
const svg = c.svg(200, 200, c.strokeColor("blue", line));
|
|
37
|
+
const paths = findElements(svg, (tag) => tag === 'path');
|
|
38
|
+
expect(paths.length).toBe(1);
|
|
39
|
+
expect(paths[0].attrs.style).toContain('stroke:blue');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('svg rendering', () => {
|
|
44
|
+
it('renders a line from (0,0) to (1,1) as an SVG path', () => {
|
|
45
|
+
const line = c.line([[0, 0], [1, 1]]);
|
|
46
|
+
const svg = c.svg(200, 200, line);
|
|
47
|
+
|
|
48
|
+
const paths = findElements(svg, (tag) => tag === 'path');
|
|
49
|
+
expect(paths.length).toBe(1);
|
|
50
|
+
|
|
51
|
+
const coords = parsePath(paths[0].attrs.d);
|
|
52
|
+
|
|
53
|
+
// X: 0->0, 1->200. Y: 0->200, 1->0 (SVG Y-axis is inverted)
|
|
54
|
+
expect(coords).toHaveLength(2);
|
|
55
|
+
expect(coords[0]).toEqual({ cmd: 'M', x: 0, y: 200 });
|
|
56
|
+
expect(coords[1]).toEqual({ cmd: 'L', x: 200, y: 0 });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('renders a column as a path filling the full SVG area', () => {
|
|
60
|
+
const col = c.column("test", 10);
|
|
61
|
+
const svg = c.svg(200, 200, col);
|
|
62
|
+
|
|
63
|
+
const paths = findElements(svg, (tag) => tag === 'path');
|
|
64
|
+
expect(paths.length).toBe(1);
|
|
65
|
+
|
|
66
|
+
const coords = parsePath(paths[0].attrs.d);
|
|
67
|
+
|
|
68
|
+
// Single column auto-scales to fill the entire 200x200 area
|
|
69
|
+
// Closed path covering all four corners
|
|
70
|
+
expect(coords).toHaveLength(5);
|
|
71
|
+
expect(coords[0]).toEqual({ cmd: 'M', x: 0, y: 0 });
|
|
72
|
+
expect(coords[1]).toEqual({ cmd: 'L', x: 200, y: 0 });
|
|
73
|
+
expect(coords[2]).toEqual({ cmd: 'L', x: 200, y: 200 });
|
|
74
|
+
expect(coords[3]).toEqual({ cmd: 'L', x: 0, y: 200 });
|
|
75
|
+
expect(coords[4]).toEqual({ cmd: 'L', x: 0, y: 0 });
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('serialization', () => {
|
|
80
|
+
it('round-trips a line through serialize/deserialize', () => {
|
|
81
|
+
const line = c.line([[0, 0], [1, 1]]);
|
|
82
|
+
const line2 = c.deserialize(c.serialize(line));
|
|
83
|
+
const paths1 = findElements(c.svg(200, 200, line), (tag) => tag === 'path');
|
|
84
|
+
const paths2 = findElements(c.svg(200, 200, line2), (tag) => tag === 'path');
|
|
85
|
+
expect(paths2[0].attrs.d).toEqual(paths1[0].attrs.d);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('round-trips a styled shape through serialize/deserialize', () => {
|
|
89
|
+
const shape = c.fillColor("red", c.strokeColor("blue", c.line([[0, 0], [1, 1]])));
|
|
90
|
+
const shape2 = c.deserialize(c.serialize(shape));
|
|
91
|
+
const paths1 = findElements(c.svg(200, 200, shape), (tag) => tag === 'path');
|
|
92
|
+
const paths2 = findElements(c.svg(200, 200, shape2), (tag) => tag === 'path');
|
|
93
|
+
expect(paths2[0].attrs.style).toEqual(paths1[0].attrs.style);
|
|
94
|
+
expect(paths2[0].attrs.d).toEqual(paths1[0].attrs.d);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('round-trips a multi-series bar chart through serialize/deserialize', () => {
|
|
98
|
+
const chart = c.axes("left bottom",
|
|
99
|
+
c.scaleY(s.continuous(0, 50),
|
|
100
|
+
c.overlay([
|
|
101
|
+
c.padding(0, 5, 0, 5, c.fillColor("#DC4B4A", c.column("apples", 30))),
|
|
102
|
+
c.padding(0, 5, 0, 5, c.fillColor("#424498", c.column("plums", 20))),
|
|
103
|
+
c.padding(0, 5, 0, 5, c.fillColor("#A0CB5B", c.column("kiwi", 40))),
|
|
104
|
+
])
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
const chart2 = c.deserialize(c.serialize(chart));
|
|
108
|
+
const paths1 = findElements(c.svg(400, 300, chart), (tag) => tag === 'path');
|
|
109
|
+
const paths2 = findElements(c.svg(400, 300, chart2), (tag) => tag === 'path');
|
|
110
|
+
expect(paths2.length).toBe(paths1.length);
|
|
111
|
+
paths1.forEach((p, i) => {
|
|
112
|
+
expect(paths2[i].attrs.d).toEqual(p.attrs.d);
|
|
113
|
+
expect(paths2[i].attrs.style).toEqual(p.attrs.style);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|