git.iko.soy/purestack-dev/purescript-plum
on master help sign in
something
commit8cf656571263c9e90a075cec5c136e094c8b4f4b authoriko <mail@iko.soy> dateFri 16 May 2025, 15:18 (1 year ago)
16 files changed+2745
.gitignore+10 example/.gitignore+10 example/spago.dhall+5 example/src/Main.purs+10 example/test/Main.purs+11 packages.dhall+6 plum/.gitignore+10 plum/index.html+14 plum/index.js+1467 plum/spago.dhall+21 plum/src/Main.purs+34 plum/src/Maquette.js+953 plum/src/Maquette.purs+33 plum/src/Plum.purs+36 plum/src/Plum/View.purs+114 plum/test/Main.purs+11
.gitignorenew file+10
@@ -0,0 +1,10 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/generated-docs/
6+/.psc-package/
7+/.psc*
8+/.purs*
9+/.psa*
10+/.spago
example/.gitignorenew file+10
@@ -0,0 +1,10 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/generated-docs/
6+/.psc-package/
7+/.psc*
8+/.purs*
9+/.psa*
10+/.spago
example/spago.dhallnew file+5
@@ -0,0 +1,5 @@
1+{ name = "my-project"
2+, dependencies = [ "console", "effect", "prelude", "plum" ]
3+, packages = ../packages.dhall
4+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
5+}
example/src/Main.pursnew file+10
@@ -0,0 +1,10 @@
1+module Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Console (log)
7+
8+main :: Effect Unit
9+main = do
10+ log "๐Ÿ"
example/test/Main.pursnew file+11
@@ -0,0 +1,11 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Class.Console (log)
7+
8+main :: Effect Unit
9+main = do
10+ log "๐Ÿ"
11+ log "You should add some tests."
packages.dhallnew file+6
@@ -0,0 +1,6 @@
1+let upstream =
2+ https://github.com/purescript/package-sets/releases/download/psc-0.15.15-20250506/packages.dhall
3+ sha256:6334d33a0be43ed68478323e8210bd64437acbd824bf270c2f9f6cb232bf782a
4+
5+in upstream
6+ with plum = ./plum/spago.dhall as Location
plum/.gitignorenew file+10
@@ -0,0 +1,10 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/generated-docs/
6+/.psc-package/
7+/.psc*
8+/.purs*
9+/.psa*
10+/.spago
plum/index.htmlnew file+14
@@ -0,0 +1,14 @@
1+<!DOCTYPE html>
2+<html lang="en">
3+ <head>
4+ <meta charset="UTF-8">
5+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
7+ <title>HTML 5 Boilerplate</title>
8+ <link rel="stylesheet" href="style.css">
9+ </head>
10+ <body>
11+ <div id="plum"></div>
12+ <script src="index.js"></script>
13+ </body>
14+</html>
plum/index.jsnew file+1467
@@ -0,0 +1,1467 @@
1+(() => {
2+ // output/Data.Functor/foreign.js
3+ var arrayMap = function(f) {
4+ return function(arr) {
5+ var l = arr.length;
6+ var result = new Array(l);
7+ for (var i = 0; i < l; i++) {
8+ result[i] = f(arr[i]);
9+ }
10+ return result;
11+ };
12+ };
13+
14+ // output/Data.Unit/foreign.js
15+ var unit = void 0;
16+
17+ // output/Data.Functor/index.js
18+ var map = function(dict) {
19+ return dict.map;
20+ };
21+ var functorArray = {
22+ map: arrayMap
23+ };
24+
25+ // output/Control.Apply/index.js
26+ var apply = function(dict) {
27+ return dict.apply;
28+ };
29+
30+ // output/Control.Applicative/index.js
31+ var pure = function(dict) {
32+ return dict.pure;
33+ };
34+ var liftA1 = function(dictApplicative) {
35+ var apply2 = apply(dictApplicative.Apply0());
36+ var pure1 = pure(dictApplicative);
37+ return function(f) {
38+ return function(a) {
39+ return apply2(pure1(f))(a);
40+ };
41+ };
42+ };
43+
44+ // output/Effect/foreign.js
45+ var pureE = function(a) {
46+ return function() {
47+ return a;
48+ };
49+ };
50+ var bindE = function(a) {
51+ return function(f) {
52+ return function() {
53+ return f(a())();
54+ };
55+ };
56+ };
57+
58+ // output/Control.Bind/foreign.js
59+ var arrayBind = typeof Array.prototype.flatMap === "function" ? function(arr) {
60+ return function(f) {
61+ return arr.flatMap(f);
62+ };
63+ } : function(arr) {
64+ return function(f) {
65+ var result = [];
66+ var l = arr.length;
67+ for (var i = 0; i < l; i++) {
68+ var xs = f(arr[i]);
69+ var k = xs.length;
70+ for (var j = 0; j < k; j++) {
71+ result.push(xs[j]);
72+ }
73+ }
74+ return result;
75+ };
76+ };
77+
78+ // output/Control.Bind/index.js
79+ var bind = function(dict) {
80+ return dict.bind;
81+ };
82+
83+ // output/Control.Monad/index.js
84+ var ap = function(dictMonad) {
85+ var bind3 = bind(dictMonad.Bind1());
86+ var pure3 = pure(dictMonad.Applicative0());
87+ return function(f) {
88+ return function(a) {
89+ return bind3(f)(function(f$prime) {
90+ return bind3(a)(function(a$prime) {
91+ return pure3(f$prime(a$prime));
92+ });
93+ });
94+ };
95+ };
96+ };
97+
98+ // output/Data.Semigroup/index.js
99+ var append = function(dict) {
100+ return dict.append;
101+ };
102+
103+ // output/Data.Monoid/index.js
104+ var mempty = function(dict) {
105+ return dict.mempty;
106+ };
107+
108+ // output/Effect/index.js
109+ var $runtime_lazy = function(name14, moduleName, init) {
110+ var state2 = 0;
111+ var val;
112+ return function(lineNumber) {
113+ if (state2 === 2) return val;
114+ if (state2 === 1) throw new ReferenceError(name14 + " was needed before it finished initializing (module " + moduleName + ", line " + lineNumber + ")", moduleName, lineNumber);
115+ state2 = 1;
116+ val = init();
117+ state2 = 2;
118+ return val;
119+ };
120+ };
121+ var monadEffect = {
122+ Applicative0: function() {
123+ return applicativeEffect;
124+ },
125+ Bind1: function() {
126+ return bindEffect;
127+ }
128+ };
129+ var bindEffect = {
130+ bind: bindE,
131+ Apply0: function() {
132+ return $lazy_applyEffect(0);
133+ }
134+ };
135+ var applicativeEffect = {
136+ pure: pureE,
137+ Apply0: function() {
138+ return $lazy_applyEffect(0);
139+ }
140+ };
141+ var $lazy_functorEffect = /* @__PURE__ */ $runtime_lazy("functorEffect", "Effect", function() {
142+ return {
143+ map: liftA1(applicativeEffect)
144+ };
145+ });
146+ var $lazy_applyEffect = /* @__PURE__ */ $runtime_lazy("applyEffect", "Effect", function() {
147+ return {
148+ apply: ap(monadEffect),
149+ Functor0: function() {
150+ return $lazy_functorEffect(0);
151+ }
152+ };
153+ });
154+ var functorEffect = /* @__PURE__ */ $lazy_functorEffect(20);
155+
156+ // output/Data.Bounded/foreign.js
157+ var topChar = String.fromCharCode(65535);
158+ var bottomChar = String.fromCharCode(0);
159+ var topNumber = Number.POSITIVE_INFINITY;
160+ var bottomNumber = Number.NEGATIVE_INFINITY;
161+
162+ // output/Data.Show/foreign.js
163+ var showIntImpl = function(n) {
164+ return n.toString();
165+ };
166+
167+ // output/Data.Show/index.js
168+ var showInt = {
169+ show: showIntImpl
170+ };
171+ var show = function(dict) {
172+ return dict.show;
173+ };
174+
175+ // output/Data.Maybe/index.js
176+ var Nothing = /* @__PURE__ */ function() {
177+ function Nothing2() {
178+ }
179+ ;
180+ Nothing2.value = new Nothing2();
181+ return Nothing2;
182+ }();
183+ var Just = /* @__PURE__ */ function() {
184+ function Just2(value0) {
185+ this.value0 = value0;
186+ }
187+ ;
188+ Just2.create = function(value0) {
189+ return new Just2(value0);
190+ };
191+ return Just2;
192+ }();
193+
194+ // output/Effect.Ref/foreign.js
195+ var _new = function(val) {
196+ return function() {
197+ return { value: val };
198+ };
199+ };
200+ var read = function(ref) {
201+ return function() {
202+ return ref.value;
203+ };
204+ };
205+ var write = function(val) {
206+ return function(ref) {
207+ return function() {
208+ ref.value = val;
209+ };
210+ };
211+ };
212+
213+ // output/Effect.Ref/index.js
214+ var $$new = _new;
215+
216+ // output/Maquette/foreign.js
217+ var NAMESPACE_W3 = "http://www.w3.org/";
218+ var NAMESPACE_SVG = NAMESPACE_W3 + "2000/svg";
219+ var NAMESPACE_XLINK = NAMESPACE_W3 + "1999/xlink";
220+ var emptyArray = [];
221+ var extend = function(base, overrides) {
222+ var result = {};
223+ Object.keys(base).forEach(function(key) {
224+ result[key] = base[key];
225+ });
226+ if (overrides) {
227+ Object.keys(overrides).forEach(function(key) {
228+ result[key] = overrides[key];
229+ });
230+ }
231+ return result;
232+ };
233+ var same = function(vnode1, vnode2) {
234+ if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
235+ return false;
236+ }
237+ if (vnode1.properties && vnode2.properties) {
238+ if (vnode1.properties.key !== vnode2.properties.key) {
239+ return false;
240+ }
241+ return vnode1.properties.bind === vnode2.properties.bind;
242+ }
243+ return !vnode1.properties && !vnode2.properties;
244+ };
245+ var checkStyleValue = function(styleValue) {
246+ if (typeof styleValue !== "string") {
247+ throw new Error("Style values must be strings");
248+ }
249+ };
250+ var findIndexOfChild = function(children, sameAs, start2) {
251+ if (sameAs.vnodeSelector !== "") {
252+ for (var i = start2; i < children.length; i++) {
253+ if (same(children[i], sameAs)) {
254+ return i;
255+ }
256+ }
257+ }
258+ return -1;
259+ };
260+ var checkDistinguishable = function(childNodes, indexToCheck, parentVNode, operation) {
261+ var childNode = childNodes[indexToCheck];
262+ if (childNode.vnodeSelector === "") {
263+ return;
264+ }
265+ var properties = childNode.properties;
266+ var key = properties ? properties.key === void 0 ? properties.bind : properties.key : void 0;
267+ if (!key) {
268+ for (var i = 0; i < childNodes.length; i++) {
269+ if (i !== indexToCheck) {
270+ var node = childNodes[i];
271+ if (same(node, childNode)) {
272+ throw {
273+ error: new Error(parentVNode.vnodeSelector + " had a " + childNode.vnodeSelector + " child " + (operation === "added" ? operation : "removed") + ", but there is now more than one. You must add unique key properties to make them distinguishable."),
274+ parentNode: parentVNode,
275+ childNode
276+ };
277+ }
278+ }
279+ }
280+ }
281+ };
282+ var nodeAdded = function(vNode) {
283+ if (vNode.properties) {
284+ var enterAnimation = vNode.properties.enterAnimation;
285+ if (enterAnimation) {
286+ enterAnimation(vNode.domNode, vNode.properties);
287+ }
288+ }
289+ };
290+ var removedNodes = [];
291+ var requestedIdleCallback = false;
292+ var visitRemovedNode = function(node) {
293+ (node.children || []).forEach(visitRemovedNode);
294+ if (node.properties && node.properties.afterRemoved) {
295+ node.properties.afterRemoved.apply(node.properties.bind || node.properties, [
296+ node.domNode
297+ ]);
298+ }
299+ };
300+ var processPendingNodeRemovals = function() {
301+ requestedIdleCallback = false;
302+ removedNodes.forEach(visitRemovedNode);
303+ removedNodes.length = 0;
304+ };
305+ var scheduleNodeRemoval = function(vNode) {
306+ removedNodes.push(vNode);
307+ if (!requestedIdleCallback) {
308+ requestedIdleCallback = true;
309+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
310+ window.requestIdleCallback(processPendingNodeRemovals, { timeout: 16 });
311+ } else {
312+ setTimeout(processPendingNodeRemovals, 16);
313+ }
314+ }
315+ };
316+ var nodeToRemove = function(vNode) {
317+ var domNode = vNode.domNode;
318+ if (vNode.properties) {
319+ var exitAnimation = vNode.properties.exitAnimation;
320+ if (exitAnimation) {
321+ domNode.style.pointerEvents = "none";
322+ var removeDomNode = function() {
323+ if (domNode.parentNode) {
324+ domNode.parentNode.removeChild(domNode);
325+ scheduleNodeRemoval(vNode);
326+ }
327+ };
328+ exitAnimation(domNode, removeDomNode, vNode.properties);
329+ return;
330+ }
331+ }
332+ if (domNode.parentNode) {
333+ domNode.parentNode.removeChild(domNode);
334+ scheduleNodeRemoval(vNode);
335+ }
336+ };
337+ var vnodeOnlyProps = [
338+ "afterCreate",
339+ "afterUpdate",
340+ "afterRemoved",
341+ "enterAnimation",
342+ "exitAnimation",
343+ "updateAnimation"
344+ ];
345+ function filterVNodeOnlyProps(name14) {
346+ return !vnodeOnlyProps.includes(name14);
347+ }
348+ var setProperties = function(domNode, properties, projectionOptions) {
349+ if (!properties) {
350+ return;
351+ }
352+ var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
353+ var propNames = Object.keys(properties).filter(filterVNodeOnlyProps);
354+ var propCount = propNames.length;
355+ for (var i = 0; i < propCount; i++) {
356+ var propName = propNames[i];
357+ var propValue = properties[propName];
358+ if (propName === "className") {
359+ throw new Error('Property "className" is not supported, use "class".');
360+ } else if (propName === "class") {
361+ toggleClasses(domNode, propValue, true);
362+ } else if (propName === "classes") {
363+ var classNames = Object.keys(propValue);
364+ var classNameCount = classNames.length;
365+ for (var j = 0; j < classNameCount; j++) {
366+ var className = classNames[j];
367+ if (propValue[className]) {
368+ domNode.classList.add(className);
369+ }
370+ }
371+ } else if (propName === "styles") {
372+ var styleNames = Object.keys(propValue);
373+ var styleCount = styleNames.length;
374+ for (var j = 0; j < styleCount; j++) {
375+ var styleName = styleNames[j];
376+ var styleValue = propValue[styleName];
377+ if (styleValue) {
378+ checkStyleValue(styleValue);
379+ projectionOptions.styleApplyer(domNode, styleName, styleValue);
380+ }
381+ }
382+ } else if (propName === "on" && propValue) {
383+ for (var _i = 0, _a = Object.entries(properties.on); _i < _a.length; _i++) {
384+ var _b = _a[_i], key = _b[0], handler = _b[1];
385+ var listener = typeof handler === "function" ? handler : handler.listener;
386+ if (eventHandlerInterceptor) {
387+ listener = eventHandlerInterceptor(key, listener, domNode, properties);
388+ }
389+ if (listener) {
390+ domNode.addEventListener(key, listener, typeof handler === "function" ? void 0 : handler.options);
391+ }
392+ }
393+ } else if (propName !== "key" && propValue !== null && propValue !== void 0) {
394+ var type = typeof propValue;
395+ if (type === "function") {
396+ if (propName.lastIndexOf("on", 0) === 0) {
397+ if (eventHandlerInterceptor) {
398+ propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);
399+ }
400+ }
401+ domNode[propName] = propValue;
402+ } else if (projectionOptions.namespace === NAMESPACE_SVG) {
403+ if (propName === "href") {
404+ domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
405+ } else {
406+ domNode.setAttribute(propName, propValue);
407+ }
408+ } else if (type === "string" && propName !== "value" && propName !== "innerHTML") {
409+ domNode.setAttribute(propName, propValue);
410+ } else {
411+ domNode[propName] = propValue;
412+ }
413+ }
414+ }
415+ };
416+ var addChildren = function(domNode, children, projectionOptions) {
417+ if (!children) {
418+ return;
419+ }
420+ for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
421+ var child = children_1[_i];
422+ createDom(child, domNode, void 0, projectionOptions);
423+ }
424+ };
425+ var initPropertiesAndChildren = function(domNode, vnode, projectionOptions) {
426+ addChildren(domNode, vnode.children, projectionOptions);
427+ if (vnode.text) {
428+ domNode.textContent = vnode.text;
429+ }
430+ setProperties(domNode, vnode.properties, projectionOptions);
431+ if (vnode.properties && vnode.properties.afterCreate) {
432+ vnode.properties.afterCreate.apply(vnode.properties.bind || vnode.properties, [
433+ domNode,
434+ projectionOptions,
435+ vnode.vnodeSelector,
436+ vnode.properties,
437+ vnode.children
438+ ]);
439+ }
440+ };
441+ var createDom = function(vnode, parentNode, insertBefore, projectionOptions) {
442+ var _a;
443+ var domNode;
444+ var start2 = 0;
445+ var vnodeSelector = vnode.vnodeSelector;
446+ var doc = parentNode.ownerDocument;
447+ if (vnodeSelector === "") {
448+ if (vnode.domNode) {
449+ vnode.domNode.nodeValue = vnode.text;
450+ } else {
451+ domNode = vnode.domNode = doc.createTextNode(vnode.text);
452+ if (insertBefore !== void 0) {
453+ parentNode.insertBefore(domNode, insertBefore);
454+ } else {
455+ parentNode.appendChild(domNode);
456+ }
457+ }
458+ } else {
459+ for (var i = 0; i <= vnodeSelector.length; ++i) {
460+ var c = vnodeSelector.charAt(i);
461+ if (i === vnodeSelector.length || c === "." || c === "#") {
462+ var type = vnodeSelector.charAt(start2 - 1);
463+ var found = vnodeSelector.slice(start2, i);
464+ if (type === ".") {
465+ domNode.classList.add(found);
466+ } else if (type === "#") {
467+ domNode.id = found;
468+ } else {
469+ if (found === "svg") {
470+ projectionOptions = extend(projectionOptions, {
471+ namespace: NAMESPACE_SVG
472+ });
473+ }
474+ if (projectionOptions.namespace !== void 0) {
475+ domNode = vnode.domNode = doc.createElementNS(projectionOptions.namespace, found);
476+ } else {
477+ domNode = vnode.domNode = vnode.domNode || (((_a = vnode.properties) === null || _a === void 0 ? void 0 : _a.is) ? doc.createElement(found, { is: vnode.properties.is }) : doc.createElement(found));
478+ if (found === "input" && vnode.properties && vnode.properties.type !== void 0) {
479+ domNode.setAttribute("type", vnode.properties.type);
480+ }
481+ }
482+ if (insertBefore !== void 0) {
483+ parentNode.insertBefore(domNode, insertBefore);
484+ } else if (domNode.parentNode !== parentNode) {
485+ parentNode.appendChild(domNode);
486+ }
487+ }
488+ start2 = i + 1;
489+ }
490+ }
491+ initPropertiesAndChildren(domNode, vnode, projectionOptions);
492+ }
493+ };
494+ var updateDom;
495+ var toggleClasses = function(domNode, classes, on2) {
496+ if (!classes) {
497+ return;
498+ }
499+ classes.split(" ").forEach(function(classToToggle) {
500+ if (classToToggle) {
501+ domNode.classList.toggle(classToToggle, on2);
502+ }
503+ });
504+ };
505+ var updateProperties = function(domNode, previousProperties, properties, projectionOptions) {
506+ if (!properties) {
507+ return;
508+ }
509+ var propertiesUpdated = false;
510+ var propNames = Object.keys(properties).filter(filterVNodeOnlyProps);
511+ var propCount = propNames.length;
512+ for (var i = 0; i < propCount; i++) {
513+ var propName = propNames[i];
514+ var propValue = properties[propName];
515+ var previousValue = previousProperties[propName];
516+ if (propName === "class") {
517+ if (previousValue !== propValue) {
518+ toggleClasses(domNode, previousValue, false);
519+ toggleClasses(domNode, propValue, true);
520+ }
521+ } else if (propName === "classes") {
522+ var classList = domNode.classList;
523+ var classNames = Object.keys(propValue);
524+ var classNameCount = classNames.length;
525+ for (var j = 0; j < classNameCount; j++) {
526+ var className = classNames[j];
527+ var on2 = !!propValue[className];
528+ var previousOn = !!previousValue[className];
529+ if (on2 === previousOn) {
530+ continue;
531+ }
532+ propertiesUpdated = true;
533+ if (on2) {
534+ classList.add(className);
535+ } else {
536+ classList.remove(className);
537+ }
538+ }
539+ } else if (propName === "styles") {
540+ var styleNames = Object.keys(propValue);
541+ var styleCount = styleNames.length;
542+ for (var j = 0; j < styleCount; j++) {
543+ var styleName = styleNames[j];
544+ var newStyleValue = propValue[styleName];
545+ var oldStyleValue = previousValue[styleName];
546+ if (newStyleValue === oldStyleValue) {
547+ continue;
548+ }
549+ propertiesUpdated = true;
550+ if (newStyleValue) {
551+ checkStyleValue(newStyleValue);
552+ projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
553+ } else {
554+ projectionOptions.styleApplyer(domNode, styleName, "");
555+ }
556+ }
557+ } else {
558+ if (!propValue && typeof previousValue === "string") {
559+ propValue = "";
560+ }
561+ if (propName === "value") {
562+ var domValue = domNode[propName];
563+ if (domValue !== propValue && // The 'value' in the DOM tree !== newValue
564+ propValue !== previousValue) {
565+ domNode[propName] = propValue;
566+ }
567+ if (propValue !== previousValue) {
568+ propertiesUpdated = true;
569+ }
570+ } else if (propValue !== previousValue) {
571+ var type = typeof propValue;
572+ if (type !== "function" || !projectionOptions.eventHandlerInterceptor) {
573+ if (projectionOptions.namespace === NAMESPACE_SVG) {
574+ if (propName === "href") {
575+ domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
576+ } else {
577+ domNode.setAttribute(propName, propValue);
578+ }
579+ } else if (type === "string" && propName !== "innerHTML") {
580+ if (propName === "role" && propValue === "") {
581+ domNode.removeAttribute(propName);
582+ } else {
583+ domNode.setAttribute(propName, propValue);
584+ }
585+ } else if (domNode[propName] !== propValue) {
586+ domNode[propName] = propValue;
587+ }
588+ propertiesUpdated = true;
589+ }
590+ }
591+ }
592+ }
593+ return propertiesUpdated;
594+ };
595+ var updateChildren = function(vnode, domNode, oldChildren, newChildren, projectionOptions) {
596+ if (oldChildren === newChildren) {
597+ return false;
598+ }
599+ oldChildren = oldChildren || emptyArray;
600+ newChildren = newChildren || emptyArray;
601+ var oldChildrenLength = oldChildren.length;
602+ var newChildrenLength = newChildren.length;
603+ var oldIndex = 0;
604+ var newIndex = 0;
605+ var i;
606+ var textUpdated = false;
607+ while (newIndex < newChildrenLength) {
608+ var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : void 0;
609+ var newChild = newChildren[newIndex];
610+ if (oldChild !== void 0 && same(oldChild, newChild)) {
611+ textUpdated = updateDom(oldChild, newChild, projectionOptions, domNode, oldChildren) || textUpdated;
612+ oldIndex++;
613+ } else {
614+ var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
615+ if (findOldIndex >= 0) {
616+ for (i = oldIndex; i < findOldIndex; i++) {
617+ nodeToRemove(oldChildren[i]);
618+ checkDistinguishable(oldChildren, i, vnode, "removed");
619+ }
620+ textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions, domNode, oldChildren) || textUpdated;
621+ oldIndex = findOldIndex + 1;
622+ } else {
623+ createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : void 0, projectionOptions);
624+ nodeAdded(newChild);
625+ checkDistinguishable(newChildren, newIndex, vnode, "added");
626+ }
627+ }
628+ newIndex++;
629+ }
630+ if (oldChildrenLength > oldIndex) {
631+ for (i = oldIndex; i < oldChildrenLength; i++) {
632+ nodeToRemove(oldChildren[i]);
633+ checkDistinguishable(oldChildren, i, vnode, "removed");
634+ }
635+ }
636+ return textUpdated;
637+ };
638+ updateDom = function(previous, vnode, projectionOptions, parentNode, oldChildren) {
639+ var domNode = previous.domNode;
640+ var textUpdated = false;
641+ if (previous === vnode) {
642+ return false;
643+ }
644+ var updated = false;
645+ if (vnode.vnodeSelector === "") {
646+ if (vnode.text !== previous.text) {
647+ var newTextNode = domNode.ownerDocument.createTextNode(vnode.text);
648+ try {
649+ parentNode.replaceChild(newTextNode, domNode);
650+ } catch (e) {
651+ parentNode.replaceChild(newTextNode, parentNode.childNodes[oldChildren.indexOf(previous)]);
652+ }
653+ vnode.domNode = newTextNode;
654+ textUpdated = true;
655+ return textUpdated;
656+ }
657+ vnode.domNode = domNode;
658+ } else {
659+ if (vnode.vnodeSelector.lastIndexOf("svg", 0) === 0) {
660+ projectionOptions = extend(projectionOptions, {
661+ namespace: NAMESPACE_SVG
662+ });
663+ }
664+ if (previous.text !== vnode.text) {
665+ updated = true;
666+ if (vnode.text === void 0) {
667+ domNode.removeChild(domNode.firstChild);
668+ } else {
669+ domNode.textContent = vnode.text;
670+ }
671+ }
672+ vnode.domNode = domNode;
673+ updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
674+ updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
675+ if (vnode.properties && vnode.properties.afterUpdate) {
676+ vnode.properties.afterUpdate.apply(vnode.properties.bind || vnode.properties, [
677+ domNode,
678+ projectionOptions,
679+ vnode.vnodeSelector,
680+ vnode.properties,
681+ vnode.children
682+ ]);
683+ }
684+ }
685+ if (updated && vnode.properties && vnode.properties.updateAnimation) {
686+ vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
687+ }
688+ return textUpdated;
689+ };
690+ var createProjection = function(vnode, projectionOptions) {
691+ return {
692+ getLastRender: function() {
693+ return vnode;
694+ },
695+ update: function(updatedVnode) {
696+ if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
697+ throw new Error("The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)");
698+ }
699+ var previousVNode = vnode;
700+ vnode = updatedVnode;
701+ updateDom(previousVNode, updatedVnode, projectionOptions, previousVNode.domNode.parentNode, [previousVNode]);
702+ },
703+ domNode: vnode.domNode
704+ };
705+ };
706+ var DEFAULT_PROJECTION_OPTIONS = {
707+ namespace: void 0,
708+ performanceLogger: function() {
709+ return void 0;
710+ },
711+ eventHandlerInterceptor: void 0,
712+ styleApplyer: function(domNode, styleName, value12) {
713+ if (styleName.charAt(0) === "-") {
714+ domNode.style.setProperty(styleName, value12);
715+ } else {
716+ domNode.style[styleName] = value12;
717+ }
718+ }
719+ };
720+ var applyDefaultProjectionOptions = function(projectorOptions) {
721+ return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
722+ };
723+ var dom = {
724+ /**
725+ * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
726+ * its [[Projection.domNode|domNode]] property.
727+ * This is a low-level method. Users will typically use a [[Projector]] instead.
728+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
729+ * objects may only be rendered once.
730+ * @param projectionOptions - Options to be used to create and update the projection.
731+ * @returns The [[Projection]] which also contains the DOM Node that was created.
732+ */
733+ create: function(vnode, projectionOptions) {
734+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
735+ createDom(vnode, document.createElement("div"), void 0, projectionOptions);
736+ return createProjection(vnode, projectionOptions);
737+ },
738+ /**
739+ * Appends a new child node to the DOM which is generated from a [[VNode]].
740+ * This is a low-level method. Users will typically use a [[Projector]] instead.
741+ * @param parentNode - The parent node for the new child node.
742+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
743+ * objects may only be rendered once.
744+ * @param projectionOptions - Options to be used to create and update the [[Projection]].
745+ * @returns The [[Projection]] that was created.
746+ */
747+ append: function(parentNode, vnode, projectionOptions) {
748+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
749+ createDom(vnode, parentNode, void 0, projectionOptions);
750+ return createProjection(vnode, projectionOptions);
751+ },
752+ /**
753+ * Inserts a new DOM node which is generated from a [[VNode]].
754+ * This is a low-level method. Users wil typically use a [[Projector]] instead.
755+ * @param beforeNode - The node that the DOM Node is inserted before.
756+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
757+ * NOTE: [[VNode]] objects may only be rendered once.
758+ * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
759+ * @returns The [[Projection]] that was created.
760+ */
761+ insertBefore: function(beforeNode, vnode, projectionOptions) {
762+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
763+ createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
764+ return createProjection(vnode, projectionOptions);
765+ },
766+ /**
767+ * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
768+ * This means that the virtual DOM and the real DOM will have one overlapping element.
769+ * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
770+ * This is a low-level method. Users wil typically use a [[Projector]] instead.
771+ * @param element - The existing element to adopt as the root of the new virtual DOM. Existing attributes and child nodes are preserved.
772+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
773+ * may only be rendered once.
774+ * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
775+ * @returns The [[Projection]] that was created.
776+ */
777+ merge: function(element, vnode, projectionOptions) {
778+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
779+ vnode.domNode = element;
780+ initPropertiesAndChildren(element, vnode, projectionOptions);
781+ return createProjection(vnode, projectionOptions);
782+ },
783+ /**
784+ * Replaces an existing DOM node with a node generated from a [[VNode]].
785+ * This is a low-level method. Users will typically use a [[Projector]] instead.
786+ * @param element - The node for the [[VNode]] to replace.
787+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
788+ * objects may only be rendered once.
789+ * @param projectionOptions - Options to be used to create and update the [[Projection]].
790+ * @returns The [[Projection]] that was created.
791+ */
792+ replace: function(element, vnode, projectionOptions) {
793+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
794+ createDom(vnode, element.parentNode, element, projectionOptions);
795+ element.parentNode.removeChild(element);
796+ return createProjection(vnode, projectionOptions);
797+ }
798+ };
799+ var toTextVNode = function(data) {
800+ return {
801+ vnodeSelector: "",
802+ properties: void 0,
803+ children: void 0,
804+ text: data.toString(),
805+ domNode: null
806+ };
807+ };
808+ var appendChildren = function(parentSelector, insertions, main2) {
809+ for (var i = 0, length_1 = insertions.length; i < length_1; i++) {
810+ var item = insertions[i];
811+ if (Array.isArray(item)) {
812+ appendChildren(parentSelector, item, main2);
813+ } else {
814+ if (item !== null && item !== void 0 && item !== false) {
815+ if (typeof item === "string") {
816+ item = toTextVNode(item);
817+ }
818+ main2.push(item);
819+ }
820+ }
821+ }
822+ };
823+ function h(selector, properties, children) {
824+ if (Array.isArray(properties)) {
825+ children = properties;
826+ properties = void 0;
827+ } else if (properties && (typeof properties === "string" || properties.vnodeSelector) || children && (typeof children === "string" || children.vnodeSelector)) {
828+ throw new Error("h called with invalid arguments");
829+ }
830+ var text5;
831+ var flattenedChildren;
832+ if (children && children.length === 1 && typeof children[0] === "string") {
833+ text5 = children[0];
834+ } else if (children) {
835+ flattenedChildren = [];
836+ appendChildren(selector, children, flattenedChildren);
837+ if (flattenedChildren.length === 0) {
838+ flattenedChildren = void 0;
839+ }
840+ }
841+ return {
842+ vnodeSelector: selector,
843+ properties,
844+ children: flattenedChildren,
845+ text: text5 === "" ? void 0 : text5,
846+ domNode: null
847+ };
848+ }
849+ var createParentNodePath = function(node, rootNode) {
850+ var parentNodePath = [];
851+ while (node && node !== rootNode) {
852+ parentNodePath.push(node);
853+ node = node.parentNode;
854+ }
855+ return parentNodePath;
856+ };
857+ var find;
858+ if (Array.prototype.find) {
859+ find = function(items, predicate) {
860+ return items.find(predicate);
861+ };
862+ } else {
863+ find = function(items, predicate) {
864+ return items.filter(predicate)[0];
865+ };
866+ }
867+ var findVNodeByParentNodePath = function(vnode, parentNodePath) {
868+ var result = vnode;
869+ parentNodePath.forEach(function(node) {
870+ result = result && result.children ? find(result.children, function(child) {
871+ return child.domNode === node;
872+ }) : void 0;
873+ });
874+ return result;
875+ };
876+ var createEventHandlerInterceptor = function(projector, getProjection, performanceLogger) {
877+ return function(propertyName, eventHandler, domNode, properties) {
878+ return modifiedEventHandler;
879+ };
880+ function modifiedEventHandler(evt) {
881+ var _a, _b;
882+ performanceLogger("domEvent", evt);
883+ var projection = getProjection();
884+ var parentNodePath = createParentNodePath(evt.currentTarget, projection.domNode);
885+ parentNodePath.reverse();
886+ var matchingVNode = findVNodeByParentNodePath(projection.getLastRender(), parentNodePath);
887+ projector.scheduleRender();
888+ var result;
889+ if (matchingVNode) {
890+ var listener = (_b = (_a = matchingVNode.properties["on" + evt.type]) !== null && _a !== void 0 ? _a : matchingVNode.properties.on[evt.type].listener) !== null && _b !== void 0 ? _b : matchingVNode.properties.on[evt.type];
891+ result = listener.apply(matchingVNode.properties.bind || this, arguments);
892+ }
893+ performanceLogger("domEventProcessed", evt);
894+ return result;
895+ }
896+ };
897+ var createProjector = function(projectorOptions) {
898+ var projector;
899+ var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
900+ var performanceLogger = projectionOptions.performanceLogger;
901+ var renderCompleted = true;
902+ var scheduled;
903+ var stopped = false;
904+ var projections = [];
905+ var renderFunctions = [];
906+ var addProjection = function(domFunction, node, renderFunction) {
907+ var projection;
908+ var getProjection = function() {
909+ return projection;
910+ };
911+ projectionOptions.eventHandlerInterceptor = createEventHandlerInterceptor(projector, getProjection, performanceLogger);
912+ projection = domFunction(node, renderFunction(), projectionOptions);
913+ projections.push(projection);
914+ renderFunctions.push(renderFunction);
915+ };
916+ var doRender = function() {
917+ scheduled = void 0;
918+ if (!renderCompleted) {
919+ return;
920+ }
921+ renderCompleted = false;
922+ performanceLogger("renderStart", void 0);
923+ for (var i = 0; i < projections.length; i++) {
924+ var updatedVnode = renderFunctions[i]();
925+ performanceLogger("rendered", void 0);
926+ projections[i].update(updatedVnode);
927+ performanceLogger("patched", void 0);
928+ }
929+ performanceLogger("renderDone", void 0);
930+ renderCompleted = true;
931+ };
932+ projector = {
933+ renderNow: doRender,
934+ scheduleRender: function() {
935+ if (!scheduled && !stopped) {
936+ scheduled = requestAnimationFrame(doRender);
937+ }
938+ },
939+ stop: function() {
940+ if (scheduled) {
941+ cancelAnimationFrame(scheduled);
942+ scheduled = void 0;
943+ }
944+ stopped = true;
945+ },
946+ resume: function() {
947+ stopped = false;
948+ renderCompleted = true;
949+ projector.scheduleRender();
950+ },
951+ append: function(parentNode, renderFunction) {
952+ addProjection(dom.append, parentNode, renderFunction);
953+ },
954+ insertBefore: function(beforeNode, renderFunction) {
955+ addProjection(dom.insertBefore, beforeNode, renderFunction);
956+ },
957+ merge: function(domNode, renderFunction) {
958+ addProjection(dom.merge, domNode, renderFunction);
959+ },
960+ replace: function(domNode, renderFunction) {
961+ addProjection(dom.replace, domNode, renderFunction);
962+ },
963+ detach: function(renderFunction) {
964+ for (var i = 0; i < renderFunctions.length; i++) {
965+ if (renderFunctions[i] === renderFunction) {
966+ renderFunctions.splice(i, 1);
967+ return projections.splice(i, 1)[0];
968+ }
969+ }
970+ throw new Error("renderFunction was not found");
971+ }
972+ };
973+ return projector;
974+ };
975+ function _h(selector) {
976+ return function(properties) {
977+ return function(children) {
978+ return h(selector, properties, children);
979+ };
980+ };
981+ }
982+ function replace(projector) {
983+ return function(el) {
984+ return function(render) {
985+ return function() {
986+ projector.replace(el, render);
987+ };
988+ };
989+ };
990+ }
991+
992+ // output/Unsafe.Coerce/foreign.js
993+ var unsafeCoerce2 = function(x) {
994+ return x;
995+ };
996+
997+ // output/Maquette/index.js
998+ var string = unsafeCoerce2;
999+ var h2 = _h;
1000+
1001+ // output/Data.Array/foreign.js
1002+ var replicateFill = function(count, value12) {
1003+ if (count < 1) {
1004+ return [];
1005+ }
1006+ var result = new Array(count);
1007+ return result.fill(value12);
1008+ };
1009+ var replicatePolyfill = function(count, value12) {
1010+ var result = [];
1011+ var n = 0;
1012+ for (var i = 0; i < count; i++) {
1013+ result[n++] = value12;
1014+ }
1015+ return result;
1016+ };
1017+ var replicateImpl = typeof Array.prototype.fill === "function" ? replicateFill : replicatePolyfill;
1018+
1019+ // output/Control.Monad.ST.Internal/foreign.js
1020+ var map_ = function(f) {
1021+ return function(a) {
1022+ return function() {
1023+ return f(a());
1024+ };
1025+ };
1026+ };
1027+ var pure_ = function(a) {
1028+ return function() {
1029+ return a;
1030+ };
1031+ };
1032+ var bind_ = function(a) {
1033+ return function(f) {
1034+ return function() {
1035+ return f(a())();
1036+ };
1037+ };
1038+ };
1039+
1040+ // output/Control.Monad.ST.Internal/index.js
1041+ var $runtime_lazy2 = function(name14, moduleName, init) {
1042+ var state2 = 0;
1043+ var val;
1044+ return function(lineNumber) {
1045+ if (state2 === 2) return val;
1046+ if (state2 === 1) throw new ReferenceError(name14 + " was needed before it finished initializing (module " + moduleName + ", line " + lineNumber + ")", moduleName, lineNumber);
1047+ state2 = 1;
1048+ val = init();
1049+ state2 = 2;
1050+ return val;
1051+ };
1052+ };
1053+ var functorST = {
1054+ map: map_
1055+ };
1056+ var monadST = {
1057+ Applicative0: function() {
1058+ return applicativeST;
1059+ },
1060+ Bind1: function() {
1061+ return bindST;
1062+ }
1063+ };
1064+ var bindST = {
1065+ bind: bind_,
1066+ Apply0: function() {
1067+ return $lazy_applyST(0);
1068+ }
1069+ };
1070+ var applicativeST = {
1071+ pure: pure_,
1072+ Apply0: function() {
1073+ return $lazy_applyST(0);
1074+ }
1075+ };
1076+ var $lazy_applyST = /* @__PURE__ */ $runtime_lazy2("applyST", "Control.Monad.ST.Internal", function() {
1077+ return {
1078+ apply: ap(monadST),
1079+ Functor0: function() {
1080+ return functorST;
1081+ }
1082+ };
1083+ });
1084+
1085+ // output/Data.Foldable/foreign.js
1086+ var foldrArray = function(f) {
1087+ return function(init) {
1088+ return function(xs) {
1089+ var acc = init;
1090+ var len = xs.length;
1091+ for (var i = len - 1; i >= 0; i--) {
1092+ acc = f(xs[i])(acc);
1093+ }
1094+ return acc;
1095+ };
1096+ };
1097+ };
1098+ var foldlArray = function(f) {
1099+ return function(init) {
1100+ return function(xs) {
1101+ var acc = init;
1102+ var len = xs.length;
1103+ for (var i = 0; i < len; i++) {
1104+ acc = f(acc)(xs[i]);
1105+ }
1106+ return acc;
1107+ };
1108+ };
1109+ };
1110+
1111+ // output/Data.Foldable/index.js
1112+ var foldr = function(dict) {
1113+ return dict.foldr;
1114+ };
1115+ var foldMapDefaultR = function(dictFoldable) {
1116+ var foldr2 = foldr(dictFoldable);
1117+ return function(dictMonoid) {
1118+ var append2 = append(dictMonoid.Semigroup0());
1119+ var mempty2 = mempty(dictMonoid);
1120+ return function(f) {
1121+ return foldr2(function(x) {
1122+ return function(acc) {
1123+ return append2(f(x))(acc);
1124+ };
1125+ })(mempty2);
1126+ };
1127+ };
1128+ };
1129+ var foldableArray = {
1130+ foldr: foldrArray,
1131+ foldl: foldlArray,
1132+ foldMap: function(dictMonoid) {
1133+ return foldMapDefaultR(foldableArray)(dictMonoid);
1134+ }
1135+ };
1136+ var foldMap = function(dict) {
1137+ return dict.foldMap;
1138+ };
1139+
1140+ // output/Data.Array/index.js
1141+ var foldMap1 = /* @__PURE__ */ foldMap(foldableArray);
1142+ var foldMap2 = function(dictMonoid) {
1143+ return foldMap1(dictMonoid);
1144+ };
1145+
1146+ // output/Foreign.Object/foreign.js
1147+ function _copyST(m) {
1148+ return function() {
1149+ var r = {};
1150+ for (var k in m) {
1151+ if (hasOwnProperty.call(m, k)) {
1152+ r[k] = m[k];
1153+ }
1154+ }
1155+ return r;
1156+ };
1157+ }
1158+ var empty2 = {};
1159+ function runST(f) {
1160+ return f();
1161+ }
1162+ function _foldM(bind3) {
1163+ return function(f) {
1164+ return function(mz) {
1165+ return function(m) {
1166+ var acc = mz;
1167+ function g(k2) {
1168+ return function(z) {
1169+ return f(z)(k2)(m[k2]);
1170+ };
1171+ }
1172+ for (var k in m) {
1173+ if (hasOwnProperty.call(m, k)) {
1174+ acc = bind3(acc)(g(k));
1175+ }
1176+ }
1177+ return acc;
1178+ };
1179+ };
1180+ };
1181+ }
1182+ function toArrayWithKey(f) {
1183+ return function(m) {
1184+ var r = [];
1185+ for (var k in m) {
1186+ if (hasOwnProperty.call(m, k)) {
1187+ r.push(f(k)(m[k]));
1188+ }
1189+ }
1190+ return r;
1191+ };
1192+ }
1193+ var keys = Object.keys || toArrayWithKey(function(k) {
1194+ return function() {
1195+ return k;
1196+ };
1197+ });
1198+
1199+ // output/Foreign.Object.ST/foreign.js
1200+ function poke2(k) {
1201+ return function(v) {
1202+ return function(m) {
1203+ return function() {
1204+ m[k] = v;
1205+ return m;
1206+ };
1207+ };
1208+ };
1209+ }
1210+
1211+ // output/Foreign.Object/index.js
1212+ var thawST = _copyST;
1213+ var mutate = function(f) {
1214+ return function(m) {
1215+ return runST(function __do() {
1216+ var s2 = thawST(m)();
1217+ f(s2)();
1218+ return s2;
1219+ });
1220+ };
1221+ };
1222+ var insert = function(k) {
1223+ return function(v) {
1224+ return mutate(poke2(k)(v));
1225+ };
1226+ };
1227+ var fromHomogeneous = function() {
1228+ return unsafeCoerce2;
1229+ };
1230+ var foldM = function(dictMonad) {
1231+ var bind1 = bind(dictMonad.Bind1());
1232+ var pure1 = pure(dictMonad.Applicative0());
1233+ return function(f) {
1234+ return function(z) {
1235+ return _foldM(bind1)(f)(pure1(z));
1236+ };
1237+ };
1238+ };
1239+ var foldM1 = /* @__PURE__ */ foldM(monadST);
1240+ var union = function(m) {
1241+ return mutate(function(s2) {
1242+ return foldM1(function(s$prime) {
1243+ return function(k) {
1244+ return function(v) {
1245+ return poke2(k)(v)(s$prime);
1246+ };
1247+ };
1248+ })(s2)(m);
1249+ });
1250+ };
1251+
1252+ // output/Plum.View/index.js
1253+ var fromHomogeneous2 = /* @__PURE__ */ fromHomogeneous();
1254+ var show2 = /* @__PURE__ */ show(showInt);
1255+ var map2 = /* @__PURE__ */ map(functorArray);
1256+ var Flexbox = /* @__PURE__ */ function() {
1257+ function Flexbox2() {
1258+ }
1259+ ;
1260+ Flexbox2.value = new Flexbox2();
1261+ return Flexbox2;
1262+ }();
1263+ var Spacing = /* @__PURE__ */ function() {
1264+ function Spacing2(value0, value1) {
1265+ this.value0 = value0;
1266+ this.value1 = value1;
1267+ }
1268+ ;
1269+ Spacing2.create = function(value0) {
1270+ return function(value1) {
1271+ return new Spacing2(value0, value1);
1272+ };
1273+ };
1274+ return Spacing2;
1275+ }();
1276+ var semigroupRenderedAttribut = {
1277+ append: function(v1) {
1278+ return function(v2) {
1279+ return {
1280+ styles: union(v1.styles)(v2.styles)
1281+ };
1282+ };
1283+ }
1284+ };
1285+ var monoidRenderedAttribute = {
1286+ mempty: {
1287+ styles: empty2
1288+ },
1289+ Semigroup0: function() {
1290+ return semigroupRenderedAttribut;
1291+ }
1292+ };
1293+ var foldMap3 = /* @__PURE__ */ foldMap2(monoidRenderedAttribute);
1294+ var withStyles = function() {
1295+ return function(v1) {
1296+ return function(styles) {
1297+ return {
1298+ styles: union(v1.styles)(fromHomogeneous2(styles))
1299+ };
1300+ };
1301+ };
1302+ };
1303+ var withStyles1 = /* @__PURE__ */ withStyles();
1304+ var unView = function(v1) {
1305+ return v1;
1306+ };
1307+ var spacing = function(x) {
1308+ return new Spacing(x, x);
1309+ };
1310+ var s = function(x) {
1311+ return function(v1) {
1312+ return string(x);
1313+ };
1314+ };
1315+ var renderAttribute = function(_ctx) {
1316+ return function(attr) {
1317+ return {
1318+ styles: insert("gap")(show2(attr.value0) + ("px " + (show2(attr.value1) + "px")))(empty2)
1319+ };
1320+ };
1321+ };
1322+ var renderAttributes = function(ctx) {
1323+ return foldMap3(renderAttribute(ctx));
1324+ };
1325+ var renderWithStyles = function() {
1326+ return function(ctx) {
1327+ return function(attrs) {
1328+ return function(styles) {
1329+ return withStyles1(renderAttributes(ctx)(attrs))(styles);
1330+ };
1331+ };
1332+ };
1333+ };
1334+ var renderWithStyles1 = /* @__PURE__ */ renderWithStyles();
1335+ var button = function(v1) {
1336+ return function(v2) {
1337+ return function(fire) {
1338+ return h2("div")({
1339+ onclick: fire(v2.onPress)
1340+ })([unView(v2.view)(fire)]);
1341+ };
1342+ };
1343+ };
1344+ var _v = function(el) {
1345+ return function(attr) {
1346+ return function(children) {
1347+ return function(fire) {
1348+ return h2(el)(attr)(map2(function(v1) {
1349+ return v1(fire);
1350+ })(children));
1351+ };
1352+ };
1353+ };
1354+ };
1355+ var row = function(attrs) {
1356+ return function(children) {
1357+ return _v("div")(renderWithStyles1(Flexbox.value)(attrs)({
1358+ display: "flex",
1359+ "flex-direction": "row"
1360+ }))(children);
1361+ };
1362+ };
1363+
1364+ // output/Web.DOM.NonElementParentNode/foreign.js
1365+ function _getElementById(id) {
1366+ return function(node) {
1367+ return function() {
1368+ return node.getElementById(id);
1369+ };
1370+ };
1371+ }
1372+
1373+ // output/Data.Nullable/foreign.js
1374+ function nullable(a, r, f) {
1375+ return a == null ? r : f(a);
1376+ }
1377+
1378+ // output/Data.Nullable/index.js
1379+ var toMaybe = function(n) {
1380+ return nullable(n, Nothing.value, Just.create);
1381+ };
1382+
1383+ // output/Web.DOM.NonElementParentNode/index.js
1384+ var map3 = /* @__PURE__ */ map(functorEffect);
1385+ var getElementById = function(eid) {
1386+ var $2 = map3(toMaybe);
1387+ var $3 = _getElementById(eid);
1388+ return function($4) {
1389+ return $2($3($4));
1390+ };
1391+ };
1392+
1393+ // output/Web.HTML/foreign.js
1394+ var windowImpl = function() {
1395+ return window;
1396+ };
1397+
1398+ // output/Web.HTML.HTMLDocument/index.js
1399+ var toNonElementParentNode = unsafeCoerce2;
1400+
1401+ // output/Web.HTML.Window/foreign.js
1402+ function document2(window2) {
1403+ return function() {
1404+ return window2.document;
1405+ };
1406+ }
1407+
1408+ // output/Plum/index.js
1409+ var bind2 = /* @__PURE__ */ bind(bindEffect);
1410+ var run3 = function(id) {
1411+ return function(plum2) {
1412+ return function __do() {
1413+ var modelRef = bind2(plum2.init)($$new)();
1414+ var v = bind2(bind2(windowImpl)(document2))(function() {
1415+ var $6 = getElementById(id);
1416+ return function($7) {
1417+ return $6(toNonElementParentNode($7));
1418+ };
1419+ }())();
1420+ if (v instanceof Just) {
1421+ var projector = createProjector();
1422+ return replace(projector)(v.value0)(function __do2() {
1423+ var model = read(modelRef)();
1424+ return unView(plum2.view(model))(function(msg) {
1425+ return function __do3() {
1426+ var m = read(modelRef)();
1427+ var m_ = plum2.update(msg)(m)();
1428+ return write(m_)(modelRef)();
1429+ };
1430+ });
1431+ })();
1432+ }
1433+ ;
1434+ if (v instanceof Nothing) {
1435+ return unit;
1436+ }
1437+ ;
1438+ throw new Error("Failed pattern match at Plum (line 24, column 92 - line 36, column 25): " + [v.constructor.name]);
1439+ };
1440+ };
1441+ };
1442+
1443+ // output/Main/index.js
1444+ var pure2 = /* @__PURE__ */ pure(applicativeEffect);
1445+ var plum = {
1446+ init: /* @__PURE__ */ pure2({
1447+ text: "Test"
1448+ }),
1449+ view: function(model) {
1450+ return row([spacing(16)])([s(model.text), button([])({
1451+ onPress: unit,
1452+ view: s("Button")
1453+ })]);
1454+ },
1455+ update: function(msg) {
1456+ return function(model) {
1457+ return pure2({
1458+ text: "Button pressed"
1459+ });
1460+ };
1461+ }
1462+ };
1463+ var main = /* @__PURE__ */ run3("plum")(plum);
1464+
1465+ // <stdin>
1466+ main();
1467+})();
plum/spago.dhallnew file+21
@@ -0,0 +1,21 @@
1+{ name = "my-project"
2+, dependencies =
3+ [ "arrays"
4+ , "console"
5+ , "debug"
6+ , "effect"
7+ , "foreign-object"
8+ , "maybe"
9+ , "numbers"
10+ , "prelude"
11+ , "refs"
12+ , "strings-extra"
13+ , "typelevel-prelude"
14+ , "unsafe-coerce"
15+ , "untagged-union"
16+ , "web-dom"
17+ , "web-html"
18+ ]
19+, packages = ../packages.dhall
20+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
21+}
plum/src/Main.pursnew file+34
@@ -0,0 +1,34 @@
1+module Main where
2+
3+import Prelude
4+import Web.HTML
5+
6+import Data.Maybe (Maybe(..))
7+import Effect (Effect)
8+import Effect.Console (log)
9+import Maquette (createProjector, h, replace, scheduleRender, string)
10+import Plum as Plum
11+import Plum.View (button, s)
12+import Plum.View as V
13+import Web.DOM.NonElementParentNode as Web
14+import Web.HTML as Web
15+import Web.HTML.HTMLDocument as Web
16+import Web.HTML.Window as Web
17+
18+main :: Effect Unit
19+main = do
20+ Plum.run "plum" plum
21+
22+type Model = { text :: String }
23+
24+plum :: Plum.Plum Unit Model
25+plum =
26+ { init: pure { text: "Test" }
27+ , view: \model ->
28+ V.row [ V.spacing 16 ]
29+ [ s model.text
30+ , button [] { onPress: unit, view: s "Button" }
31+ ]
32+ , update: \msg model -> case msg of
33+ _ -> pure { text: "Button pressed" }
34+ }
plum/src/Maquette.jsnew file+953
@@ -0,0 +1,953 @@
1+'use strict';
2+
3+var NAMESPACE_W3 = "http://www.w3.org/";
4+var NAMESPACE_SVG = NAMESPACE_W3 + "2000/svg";
5+var NAMESPACE_XLINK = NAMESPACE_W3 + "1999/xlink";
6+var emptyArray = [];
7+var extend = function (base, overrides) {
8+ var result = {};
9+ Object.keys(base).forEach(function (key) {
10+ result[key] = base[key];
11+ });
12+ if (overrides) {
13+ Object.keys(overrides).forEach(function (key) {
14+ result[key] = overrides[key];
15+ });
16+ }
17+ return result;
18+};
19+var same = function (vnode1, vnode2) {
20+ if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
21+ return false;
22+ }
23+ if (vnode1.properties && vnode2.properties) {
24+ if (vnode1.properties.key !== vnode2.properties.key) {
25+ return false;
26+ }
27+ return vnode1.properties.bind === vnode2.properties.bind;
28+ }
29+ return !vnode1.properties && !vnode2.properties;
30+};
31+var checkStyleValue = function (styleValue) {
32+ if (typeof styleValue !== "string") {
33+ throw new Error("Style values must be strings");
34+ }
35+};
36+var findIndexOfChild = function (children, sameAs, start) {
37+ if (sameAs.vnodeSelector !== "") {
38+ // Never scan for text-nodes
39+ for (var i = start; i < children.length; i++) {
40+ if (same(children[i], sameAs)) {
41+ return i;
42+ }
43+ }
44+ }
45+ return -1;
46+};
47+var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
48+ var childNode = childNodes[indexToCheck];
49+ if (childNode.vnodeSelector === "") {
50+ return; // Text nodes need not be distinguishable
51+ }
52+ var properties = childNode.properties;
53+ var key = properties
54+ ? properties.key === undefined
55+ ? properties.bind
56+ : properties.key
57+ : undefined;
58+ if (!key) {
59+ // A key is just assumed to be unique
60+ for (var i = 0; i < childNodes.length; i++) {
61+ if (i !== indexToCheck) {
62+ var node = childNodes[i];
63+ if (same(node, childNode)) {
64+ throw {
65+ error: new Error(parentVNode.vnodeSelector + " had a " + childNode.vnodeSelector + " child " + (operation === "added" ? operation : "removed") + ", but there is now more than one. You must add unique key properties to make them distinguishable."),
66+ parentNode: parentVNode,
67+ childNode: childNode,
68+ };
69+ }
70+ }
71+ }
72+ }
73+};
74+var nodeAdded = function (vNode) {
75+ if (vNode.properties) {
76+ var enterAnimation = vNode.properties.enterAnimation;
77+ if (enterAnimation) {
78+ enterAnimation(vNode.domNode, vNode.properties);
79+ }
80+ }
81+};
82+var removedNodes = [];
83+var requestedIdleCallback = false;
84+var visitRemovedNode = function (node) {
85+ (node.children || []).forEach(visitRemovedNode);
86+ if (node.properties && node.properties.afterRemoved) {
87+ node.properties.afterRemoved.apply(node.properties.bind || node.properties, [
88+ node.domNode,
89+ ]);
90+ }
91+};
92+var processPendingNodeRemovals = function () {
93+ requestedIdleCallback = false;
94+ removedNodes.forEach(visitRemovedNode);
95+ removedNodes.length = 0;
96+};
97+var scheduleNodeRemoval = function (vNode) {
98+ removedNodes.push(vNode);
99+ if (!requestedIdleCallback) {
100+ requestedIdleCallback = true;
101+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
102+ window.requestIdleCallback(processPendingNodeRemovals, { timeout: 16 });
103+ }
104+ else {
105+ setTimeout(processPendingNodeRemovals, 16);
106+ }
107+ }
108+};
109+var nodeToRemove = function (vNode) {
110+ var domNode = vNode.domNode;
111+ if (vNode.properties) {
112+ var exitAnimation = vNode.properties.exitAnimation;
113+ if (exitAnimation) {
114+ domNode.style.pointerEvents = "none";
115+ var removeDomNode = function () {
116+ if (domNode.parentNode) {
117+ domNode.parentNode.removeChild(domNode);
118+ scheduleNodeRemoval(vNode);
119+ }
120+ };
121+ exitAnimation(domNode, removeDomNode, vNode.properties);
122+ return;
123+ }
124+ }
125+ if (domNode.parentNode) {
126+ domNode.parentNode.removeChild(domNode);
127+ scheduleNodeRemoval(vNode);
128+ }
129+};
130+var vnodeOnlyProps = [
131+ "afterCreate",
132+ "afterUpdate",
133+ "afterRemoved",
134+ "enterAnimation",
135+ "exitAnimation",
136+ "updateAnimation",
137+];
138+function filterVNodeOnlyProps(name) {
139+ return !vnodeOnlyProps.includes(name);
140+}
141+var setProperties = function (domNode, properties, projectionOptions) {
142+ if (!properties) {
143+ return;
144+ }
145+ var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
146+ var propNames = Object.keys(properties).filter(filterVNodeOnlyProps);
147+ var propCount = propNames.length;
148+ for (var i = 0; i < propCount; i++) {
149+ var propName = propNames[i];
150+ var propValue = properties[propName];
151+ if (propName === "className") {
152+ throw new Error('Property "className" is not supported, use "class".');
153+ }
154+ else if (propName === "class") {
155+ toggleClasses(domNode, propValue, true);
156+ }
157+ else if (propName === "classes") {
158+ // object with string keys and boolean values
159+ var classNames = Object.keys(propValue);
160+ var classNameCount = classNames.length;
161+ for (var j = 0; j < classNameCount; j++) {
162+ var className = classNames[j];
163+ if (propValue[className]) {
164+ domNode.classList.add(className);
165+ }
166+ }
167+ }
168+ else if (propName === "styles") {
169+ // object with string keys and string (!) values
170+ var styleNames = Object.keys(propValue);
171+ var styleCount = styleNames.length;
172+ for (var j = 0; j < styleCount; j++) {
173+ var styleName = styleNames[j];
174+ var styleValue = propValue[styleName];
175+ if (styleValue) {
176+ checkStyleValue(styleValue);
177+ projectionOptions.styleApplyer(domNode, styleName, styleValue);
178+ }
179+ }
180+ }
181+ else if (propName === "on" && propValue) {
182+ // object with string keys and function values
183+ for (var _i = 0, _a = Object.entries(properties.on); _i < _a.length; _i++) {
184+ var _b = _a[_i], key = _b[0], handler = _b[1];
185+ var listener = typeof handler === "function" ? handler : handler.listener;
186+ if (eventHandlerInterceptor) {
187+ listener = eventHandlerInterceptor(key, listener, domNode, properties);
188+ }
189+ if (listener) {
190+ domNode.addEventListener(key, listener, typeof handler === "function" ? undefined : handler.options);
191+ }
192+ }
193+ }
194+ else if (propName !== "key" && propValue !== null && propValue !== undefined) {
195+ var type = typeof propValue;
196+ if (type === "function") {
197+ if (propName.lastIndexOf("on", 0) === 0) {
198+ // lastIndexOf(,0)===0 -> startsWith
199+ if (eventHandlerInterceptor) {
200+ propValue = eventHandlerInterceptor(propName, propValue, domNode, properties); // intercept eventhandlers
201+ }
202+ }
203+ domNode[propName] = propValue;
204+ }
205+ else if (projectionOptions.namespace === NAMESPACE_SVG) {
206+ if (propName === "href") {
207+ domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
208+ }
209+ else {
210+ // all SVG attributes are read-only in DOM, so...
211+ domNode.setAttribute(propName, propValue);
212+ }
213+ }
214+ else if (type === "string" && propName !== "value" && propName !== "innerHTML") {
215+ domNode.setAttribute(propName, propValue);
216+ }
217+ else {
218+ domNode[propName] = propValue;
219+ }
220+ }
221+ }
222+};
223+var addChildren = function (domNode, children, projectionOptions) {
224+ if (!children) {
225+ return;
226+ }
227+ for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
228+ var child = children_1[_i];
229+ createDom(child, domNode, undefined, projectionOptions);
230+ }
231+};
232+var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
233+ addChildren(domNode, vnode.children, projectionOptions); // children before properties, needed for value property of <select>.
234+ if (vnode.text) {
235+ domNode.textContent = vnode.text;
236+ }
237+ setProperties(domNode, vnode.properties, projectionOptions);
238+ if (vnode.properties && vnode.properties.afterCreate) {
239+ vnode.properties.afterCreate.apply(vnode.properties.bind || vnode.properties, [
240+ domNode,
241+ projectionOptions,
242+ vnode.vnodeSelector,
243+ vnode.properties,
244+ vnode.children,
245+ ]);
246+ }
247+};
248+var createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
249+ var _a;
250+ var domNode;
251+ var start = 0;
252+ var vnodeSelector = vnode.vnodeSelector;
253+ var doc = parentNode.ownerDocument;
254+ if (vnodeSelector === "") {
255+ if (vnode.domNode) {
256+ vnode.domNode.nodeValue = vnode.text;
257+ }
258+ else {
259+ domNode = vnode.domNode = doc.createTextNode(vnode.text);
260+ if (insertBefore !== undefined) {
261+ parentNode.insertBefore(domNode, insertBefore);
262+ }
263+ else {
264+ parentNode.appendChild(domNode);
265+ }
266+ }
267+ }
268+ else {
269+ for (var i = 0; i <= vnodeSelector.length; ++i) {
270+ var c = vnodeSelector.charAt(i);
271+ if (i === vnodeSelector.length || c === "." || c === "#") {
272+ var type = vnodeSelector.charAt(start - 1);
273+ var found = vnodeSelector.slice(start, i);
274+ if (type === ".") {
275+ domNode.classList.add(found);
276+ }
277+ else if (type === "#") {
278+ domNode.id = found;
279+ }
280+ else {
281+ if (found === "svg") {
282+ projectionOptions = extend(projectionOptions, {
283+ namespace: NAMESPACE_SVG,
284+ });
285+ }
286+ if (projectionOptions.namespace !== undefined) {
287+ domNode = vnode.domNode = doc.createElementNS(projectionOptions.namespace, found);
288+ }
289+ else {
290+ domNode = vnode.domNode =
291+ vnode.domNode ||
292+ (((_a = vnode.properties) === null || _a === void 0 ? void 0 : _a.is) ? doc.createElement(found, { is: vnode.properties.is })
293+ : doc.createElement(found));
294+ if (found === "input" && vnode.properties && vnode.properties.type !== undefined) {
295+ // IE8 and older don't support setting input type after the DOM Node has been added to the document
296+ domNode.setAttribute("type", vnode.properties.type);
297+ }
298+ }
299+ if (insertBefore !== undefined) {
300+ parentNode.insertBefore(domNode, insertBefore);
301+ }
302+ else if (domNode.parentNode !== parentNode) {
303+ parentNode.appendChild(domNode);
304+ }
305+ }
306+ start = i + 1;
307+ }
308+ }
309+ initPropertiesAndChildren(domNode, vnode, projectionOptions);
310+ }
311+};
312+var updateDom;
313+/**
314+ * Adds or removes classes from an Element
315+ * @param domNode the element
316+ * @param classes a string separated list of classes
317+ * @param on true means add classes, false means remove
318+ */
319+var toggleClasses = function (domNode, classes, on) {
320+ if (!classes) {
321+ return;
322+ }
323+ classes.split(" ").forEach(function (classToToggle) {
324+ if (classToToggle) {
325+ domNode.classList.toggle(classToToggle, on);
326+ }
327+ });
328+};
329+var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
330+ if (!properties) {
331+ return;
332+ }
333+ var propertiesUpdated = false;
334+ var propNames = Object.keys(properties).filter(filterVNodeOnlyProps);
335+ var propCount = propNames.length;
336+ for (var i = 0; i < propCount; i++) {
337+ var propName = propNames[i];
338+ // assuming that properties will be nullified instead of missing is by design
339+ var propValue = properties[propName];
340+ var previousValue = previousProperties[propName];
341+ if (propName === "class") {
342+ if (previousValue !== propValue) {
343+ toggleClasses(domNode, previousValue, false);
344+ toggleClasses(domNode, propValue, true);
345+ }
346+ }
347+ else if (propName === "classes") {
348+ var classList = domNode.classList;
349+ var classNames = Object.keys(propValue);
350+ var classNameCount = classNames.length;
351+ for (var j = 0; j < classNameCount; j++) {
352+ var className = classNames[j];
353+ var on = !!propValue[className];
354+ var previousOn = !!previousValue[className];
355+ if (on === previousOn) {
356+ continue;
357+ }
358+ propertiesUpdated = true;
359+ if (on) {
360+ classList.add(className);
361+ }
362+ else {
363+ classList.remove(className);
364+ }
365+ }
366+ }
367+ else if (propName === "styles") {
368+ var styleNames = Object.keys(propValue);
369+ var styleCount = styleNames.length;
370+ for (var j = 0; j < styleCount; j++) {
371+ var styleName = styleNames[j];
372+ var newStyleValue = propValue[styleName];
373+ var oldStyleValue = previousValue[styleName];
374+ if (newStyleValue === oldStyleValue) {
375+ continue;
376+ }
377+ propertiesUpdated = true;
378+ if (newStyleValue) {
379+ checkStyleValue(newStyleValue);
380+ projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
381+ }
382+ else {
383+ projectionOptions.styleApplyer(domNode, styleName, "");
384+ }
385+ }
386+ }
387+ else {
388+ if (!propValue && typeof previousValue === "string") {
389+ propValue = "";
390+ }
391+ if (propName === "value") {
392+ // value can be manipulated by the user directly and using event.preventDefault() is not an option
393+ var domValue = domNode[propName];
394+ if (domValue !== propValue && // The 'value' in the DOM tree !== newValue
395+ propValue !== previousValue // Only update the value if the vdom changed
396+ ) {
397+ // The edge cases are described in the tests
398+ domNode[propName] = propValue; // Reset the value, even if the virtual DOM did not change
399+ } // else do not update the domNode, otherwise the cursor position would be changed
400+ if (propValue !== previousValue) {
401+ propertiesUpdated = true;
402+ }
403+ }
404+ else if (propValue !== previousValue) {
405+ var type = typeof propValue;
406+ if (type !== "function" || !projectionOptions.eventHandlerInterceptor) {
407+ // Function updates are expected to be handled by the EventHandlerInterceptor
408+ if (projectionOptions.namespace === NAMESPACE_SVG) {
409+ if (propName === "href") {
410+ domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
411+ }
412+ else {
413+ // all SVG attributes are read-only in DOM, so...
414+ domNode.setAttribute(propName, propValue);
415+ }
416+ }
417+ else if (type === "string" && propName !== "innerHTML") {
418+ if (propName === "role" && propValue === "") {
419+ domNode.removeAttribute(propName);
420+ }
421+ else {
422+ domNode.setAttribute(propName, propValue);
423+ }
424+ }
425+ else if (domNode[propName] !== propValue) {
426+ // Comparison is here for side-effects in Edge with scrollLeft and scrollTop
427+ domNode[propName] = propValue;
428+ }
429+ propertiesUpdated = true;
430+ }
431+ }
432+ }
433+ }
434+ return propertiesUpdated;
435+};
436+var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
437+ if (oldChildren === newChildren) {
438+ return false;
439+ }
440+ oldChildren = oldChildren || emptyArray;
441+ newChildren = newChildren || emptyArray;
442+ var oldChildrenLength = oldChildren.length;
443+ var newChildrenLength = newChildren.length;
444+ var oldIndex = 0;
445+ var newIndex = 0;
446+ var i;
447+ var textUpdated = false;
448+ while (newIndex < newChildrenLength) {
449+ var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
450+ var newChild = newChildren[newIndex];
451+ if (oldChild !== undefined && same(oldChild, newChild)) {
452+ textUpdated =
453+ updateDom(oldChild, newChild, projectionOptions, domNode, oldChildren) || textUpdated;
454+ oldIndex++;
455+ }
456+ else {
457+ var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
458+ if (findOldIndex >= 0) {
459+ // Remove preceding missing children
460+ for (i = oldIndex; i < findOldIndex; i++) {
461+ nodeToRemove(oldChildren[i]);
462+ checkDistinguishable(oldChildren, i, vnode, "removed");
463+ }
464+ textUpdated =
465+ updateDom(oldChildren[findOldIndex], newChild, projectionOptions, domNode, oldChildren) ||
466+ textUpdated;
467+ oldIndex = findOldIndex + 1;
468+ }
469+ else {
470+ // New child
471+ createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
472+ nodeAdded(newChild);
473+ checkDistinguishable(newChildren, newIndex, vnode, "added");
474+ }
475+ }
476+ newIndex++;
477+ }
478+ if (oldChildrenLength > oldIndex) {
479+ // Remove child fragments
480+ for (i = oldIndex; i < oldChildrenLength; i++) {
481+ nodeToRemove(oldChildren[i]);
482+ checkDistinguishable(oldChildren, i, vnode, "removed");
483+ }
484+ }
485+ return textUpdated;
486+};
487+updateDom = function (previous, vnode, projectionOptions, parentNode, oldChildren) {
488+ var domNode = previous.domNode;
489+ var textUpdated = false;
490+ if (previous === vnode) {
491+ return false; // By contract, VNode objects may not be modified anymore after passing them to maquette
492+ }
493+ var updated = false;
494+ if (vnode.vnodeSelector === "") {
495+ if (vnode.text !== previous.text) {
496+ var newTextNode = domNode.ownerDocument.createTextNode(vnode.text);
497+ try {
498+ parentNode.replaceChild(newTextNode, domNode);
499+ }
500+ catch (e) {
501+ // Text nodes can be substituted by google translate
502+ parentNode.replaceChild(newTextNode, parentNode.childNodes[oldChildren.indexOf(previous)]);
503+ }
504+ vnode.domNode = newTextNode;
505+ textUpdated = true;
506+ return textUpdated;
507+ }
508+ vnode.domNode = domNode;
509+ }
510+ else {
511+ if (vnode.vnodeSelector.lastIndexOf("svg", 0) === 0) {
512+ // lastIndexOf(needle,0)===0 means StartsWith
513+ projectionOptions = extend(projectionOptions, {
514+ namespace: NAMESPACE_SVG,
515+ });
516+ }
517+ if (previous.text !== vnode.text) {
518+ updated = true;
519+ if (vnode.text === undefined) {
520+ domNode.removeChild(domNode.firstChild); // the only textnode presumably
521+ }
522+ else {
523+ domNode.textContent = vnode.text;
524+ }
525+ }
526+ vnode.domNode = domNode;
527+ updated =
528+ updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) ||
529+ updated;
530+ updated =
531+ updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) ||
532+ updated;
533+ if (vnode.properties && vnode.properties.afterUpdate) {
534+ vnode.properties.afterUpdate.apply(vnode.properties.bind || vnode.properties, [
535+ domNode,
536+ projectionOptions,
537+ vnode.vnodeSelector,
538+ vnode.properties,
539+ vnode.children,
540+ ]);
541+ }
542+ }
543+ if (updated && vnode.properties && vnode.properties.updateAnimation) {
544+ vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
545+ }
546+ return textUpdated;
547+};
548+var createProjection = function (vnode, projectionOptions) {
549+ return {
550+ getLastRender: function () { return vnode; },
551+ update: function (updatedVnode) {
552+ if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
553+ throw new Error("The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)");
554+ }
555+ var previousVNode = vnode;
556+ vnode = updatedVnode;
557+ updateDom(previousVNode, updatedVnode, projectionOptions, previousVNode.domNode.parentNode, [previousVNode]);
558+ },
559+ domNode: vnode.domNode,
560+ };
561+};
562+
563+var DEFAULT_PROJECTION_OPTIONS = {
564+ namespace: undefined,
565+ performanceLogger: function () { return undefined; },
566+ eventHandlerInterceptor: undefined,
567+ styleApplyer: function (domNode, styleName, value) {
568+ if (styleName.charAt(0) === "-") {
569+ // CSS variables must be set using setProperty
570+ domNode.style.setProperty(styleName, value);
571+ }
572+ else {
573+ // properties like 'backgroundColor' must be set as a js-property
574+ domNode.style[styleName] = value;
575+ }
576+ },
577+};
578+var applyDefaultProjectionOptions = function (projectorOptions) {
579+ return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
580+};
581+export var dom = {
582+ /**
583+ * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
584+ * its [[Projection.domNode|domNode]] property.
585+ * This is a low-level method. Users will typically use a [[Projector]] instead.
586+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
587+ * objects may only be rendered once.
588+ * @param projectionOptions - Options to be used to create and update the projection.
589+ * @returns The [[Projection]] which also contains the DOM Node that was created.
590+ */
591+ create: function (vnode, projectionOptions) {
592+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
593+ createDom(vnode, document.createElement("div"), undefined, projectionOptions);
594+ return createProjection(vnode, projectionOptions);
595+ },
596+ /**
597+ * Appends a new child node to the DOM which is generated from a [[VNode]].
598+ * This is a low-level method. Users will typically use a [[Projector]] instead.
599+ * @param parentNode - The parent node for the new child node.
600+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
601+ * objects may only be rendered once.
602+ * @param projectionOptions - Options to be used to create and update the [[Projection]].
603+ * @returns The [[Projection]] that was created.
604+ */
605+ append: function (parentNode, vnode, projectionOptions) {
606+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
607+ createDom(vnode, parentNode, undefined, projectionOptions);
608+ return createProjection(vnode, projectionOptions);
609+ },
610+ /**
611+ * Inserts a new DOM node which is generated from a [[VNode]].
612+ * This is a low-level method. Users wil typically use a [[Projector]] instead.
613+ * @param beforeNode - The node that the DOM Node is inserted before.
614+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
615+ * NOTE: [[VNode]] objects may only be rendered once.
616+ * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
617+ * @returns The [[Projection]] that was created.
618+ */
619+ insertBefore: function (beforeNode, vnode, projectionOptions) {
620+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
621+ createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
622+ return createProjection(vnode, projectionOptions);
623+ },
624+ /**
625+ * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
626+ * This means that the virtual DOM and the real DOM will have one overlapping element.
627+ * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
628+ * This is a low-level method. Users wil typically use a [[Projector]] instead.
629+ * @param element - The existing element to adopt as the root of the new virtual DOM. Existing attributes and child nodes are preserved.
630+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
631+ * may only be rendered once.
632+ * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
633+ * @returns The [[Projection]] that was created.
634+ */
635+ merge: function (element, vnode, projectionOptions) {
636+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
637+ vnode.domNode = element;
638+ initPropertiesAndChildren(element, vnode, projectionOptions);
639+ return createProjection(vnode, projectionOptions);
640+ },
641+ /**
642+ * Replaces an existing DOM node with a node generated from a [[VNode]].
643+ * This is a low-level method. Users will typically use a [[Projector]] instead.
644+ * @param element - The node for the [[VNode]] to replace.
645+ * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
646+ * objects may only be rendered once.
647+ * @param projectionOptions - Options to be used to create and update the [[Projection]].
648+ * @returns The [[Projection]] that was created.
649+ */
650+ replace: function (element, vnode, projectionOptions) {
651+ projectionOptions = applyDefaultProjectionOptions(projectionOptions);
652+ createDom(vnode, element.parentNode, element, projectionOptions);
653+ element.parentNode.removeChild(element);
654+ return createProjection(vnode, projectionOptions);
655+ },
656+};
657+
658+var toTextVNode = function (data) {
659+ return {
660+ vnodeSelector: "",
661+ properties: undefined,
662+ children: undefined,
663+ text: data.toString(),
664+ domNode: null,
665+ };
666+};
667+var appendChildren = function (parentSelector, insertions, main) {
668+ for (var i = 0, length_1 = insertions.length; i < length_1; i++) {
669+ var item = insertions[i];
670+ if (Array.isArray(item)) {
671+ appendChildren(parentSelector, item, main);
672+ }
673+ else {
674+ if (item !== null && item !== undefined && item !== false) {
675+ if (typeof item === "string") {
676+ item = toTextVNode(item);
677+ }
678+ main.push(item);
679+ }
680+ }
681+ }
682+};
683+function h(selector, properties, children) {
684+ if (Array.isArray(properties)) {
685+ children = properties;
686+ properties = undefined;
687+ }
688+ else if ((properties && (typeof properties === "string" || properties.vnodeSelector)) ||
689+ (children && (typeof children === "string" || children.vnodeSelector))) {
690+ throw new Error("h called with invalid arguments");
691+ }
692+ var text;
693+ var flattenedChildren;
694+ // Recognize a common special case where there is only a single text node
695+ if (children && children.length === 1 && typeof children[0] === "string") {
696+ text = children[0];
697+ }
698+ else if (children) {
699+ flattenedChildren = [];
700+ appendChildren(selector, children, flattenedChildren);
701+ if (flattenedChildren.length === 0) {
702+ flattenedChildren = undefined;
703+ }
704+ }
705+ return {
706+ vnodeSelector: selector,
707+ properties: properties,
708+ children: flattenedChildren,
709+ text: text === "" ? undefined : text,
710+ domNode: null,
711+ };
712+}
713+
714+var createParentNodePath = function (node, rootNode) {
715+ var parentNodePath = [];
716+ while (node && node !== rootNode) {
717+ parentNodePath.push(node);
718+ node = node.parentNode;
719+ }
720+ return parentNodePath;
721+};
722+var find;
723+if (Array.prototype.find) {
724+ find = function (items, predicate) { return items.find(predicate); };
725+}
726+else {
727+ find = function (items, predicate) { return items.filter(predicate)[0]; };
728+}
729+var findVNodeByParentNodePath = function (vnode, parentNodePath) {
730+ var result = vnode;
731+ parentNodePath.forEach(function (node) {
732+ result =
733+ result && result.children
734+ ? find(result.children, function (child) { return child.domNode === node; })
735+ : undefined;
736+ });
737+ return result;
738+};
739+var createEventHandlerInterceptor = function (projector, getProjection, performanceLogger) {
740+ return function (propertyName, eventHandler, domNode, properties) { return modifiedEventHandler; };
741+ function modifiedEventHandler(evt) {
742+ var _a, _b;
743+ performanceLogger("domEvent", evt);
744+ var projection = getProjection();
745+ var parentNodePath = createParentNodePath(evt.currentTarget, projection.domNode);
746+ parentNodePath.reverse();
747+ var matchingVNode = findVNodeByParentNodePath(projection.getLastRender(), parentNodePath);
748+ projector.scheduleRender();
749+ var result;
750+ if (matchingVNode) {
751+ /* eslint-disable prefer-rest-params */
752+ var listener = (_b = (_a = matchingVNode.properties["on" + evt.type]) !== null && _a !== void 0 ? _a : matchingVNode.properties.on[evt.type].listener) !== null && _b !== void 0 ? _b : matchingVNode.properties.on[evt.type];
753+ result = listener.apply(matchingVNode.properties.bind || this, arguments);
754+ /* eslint-enable prefer-rest-params */
755+ }
756+ performanceLogger("domEventProcessed", evt);
757+ return result;
758+ }
759+};
760+/**
761+ * Creates a [[Projector]] instance using the provided projectionOptions.
762+ *
763+ * For more information, see [[Projector]].
764+ *
765+ * @param projectorOptions Options that influence how the DOM is rendered and updated.
766+ */
767+export var createProjector = function (projectorOptions) {
768+ var projector;
769+ var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
770+ var performanceLogger = projectionOptions.performanceLogger;
771+ var renderCompleted = true;
772+ var scheduled;
773+ var stopped = false;
774+ var projections = [];
775+ var renderFunctions = []; // matches the projections array
776+ var addProjection = function (
777+ /* one of: dom.append, dom.insertBefore, dom.replace, dom.merge */
778+ domFunction,
779+ /* the parameter of the domFunction */
780+ node, renderFunction) {
781+ var projection;
782+ var getProjection = function () { return projection; };
783+ projectionOptions.eventHandlerInterceptor = createEventHandlerInterceptor(projector, getProjection, performanceLogger);
784+ projection = domFunction(node, renderFunction(), projectionOptions);
785+ projections.push(projection);
786+ renderFunctions.push(renderFunction);
787+ };
788+ var doRender = function () {
789+ scheduled = undefined;
790+ if (!renderCompleted) {
791+ return; // The last render threw an error, it should have been logged in the browser console.
792+ }
793+ renderCompleted = false;
794+ performanceLogger("renderStart", undefined);
795+ for (var i = 0; i < projections.length; i++) {
796+ var updatedVnode = renderFunctions[i]();
797+ performanceLogger("rendered", undefined);
798+ projections[i].update(updatedVnode);
799+ performanceLogger("patched", undefined);
800+ }
801+ performanceLogger("renderDone", undefined);
802+ renderCompleted = true;
803+ };
804+ projector = {
805+ renderNow: doRender,
806+ scheduleRender: function () {
807+ if (!scheduled && !stopped) {
808+ scheduled = requestAnimationFrame(doRender);
809+ }
810+ },
811+ stop: function () {
812+ if (scheduled) {
813+ cancelAnimationFrame(scheduled);
814+ scheduled = undefined;
815+ }
816+ stopped = true;
817+ },
818+ resume: function () {
819+ stopped = false;
820+ renderCompleted = true;
821+ projector.scheduleRender();
822+ },
823+ append: function (parentNode, renderFunction) {
824+ addProjection(dom.append, parentNode, renderFunction);
825+ },
826+ insertBefore: function (beforeNode, renderFunction) {
827+ addProjection(dom.insertBefore, beforeNode, renderFunction);
828+ },
829+ merge: function (domNode, renderFunction) {
830+ addProjection(dom.merge, domNode, renderFunction);
831+ },
832+ replace: function (domNode, renderFunction) {
833+ addProjection(dom.replace, domNode, renderFunction);
834+ },
835+ detach: function (renderFunction) {
836+ for (var i = 0; i < renderFunctions.length; i++) {
837+ if (renderFunctions[i] === renderFunction) {
838+ renderFunctions.splice(i, 1);
839+ return projections.splice(i, 1)[0];
840+ }
841+ }
842+ throw new Error("renderFunction was not found");
843+ },
844+ };
845+ return projector;
846+};
847+
848+/**
849+ * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
850+ * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
851+ * For more information, see [[CalculationCache]].
852+ *
853+ * @param <Result> The type of the value that is cached.
854+ */
855+export var createCache = function () {
856+ var cachedInputs;
857+ var cachedOutcome;
858+ return {
859+ invalidate: function () {
860+ cachedOutcome = undefined;
861+ cachedInputs = undefined;
862+ },
863+ result: function (inputs, calculation) {
864+ if (cachedInputs) {
865+ for (var i = 0; i < inputs.length; i++) {
866+ if (cachedInputs[i] !== inputs[i]) {
867+ cachedOutcome = undefined;
868+ }
869+ }
870+ }
871+ if (!cachedOutcome) {
872+ cachedOutcome = calculation();
873+ cachedInputs = inputs;
874+ }
875+ return cachedOutcome;
876+ },
877+ };
878+};
879+
880+/**
881+ * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
882+ * See {@link http://maquettejs.org/docs/arrays.html Working with arrays}.
883+ *
884+ * @param <Source> The type of source items. A database-record for instance.
885+ * @param <Target> The type of target items. A [[MaquetteComponent]] for instance.
886+ * @param getSourceKey `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
887+ * @param createResult `function(source, index)` that must create a new result object from a given source. This function is identical
888+ * to the `callback` argument in `Array.map(callback)`.
889+ * @param updateResult `function(source, target, index)` that updates a result to an updated source.
890+ */
891+export var createMapping = function (getSourceKey, createResult, updateResult) {
892+ var keys = [];
893+ var results = [];
894+ return {
895+ results: results,
896+ map: function (newSources) {
897+ var newKeys = newSources.map(getSourceKey);
898+ var oldTargets = results.slice();
899+ var oldIndex = 0;
900+ for (var i = 0; i < newSources.length; i++) {
901+ var source = newSources[i];
902+ var sourceKey = newKeys[i];
903+ if (sourceKey === keys[oldIndex]) {
904+ results[i] = oldTargets[oldIndex];
905+ updateResult(source, oldTargets[oldIndex], i);
906+ oldIndex++;
907+ }
908+ else {
909+ var found = false;
910+ for (var j = 1; j < keys.length + 1; j++) {
911+ var searchIndex = (oldIndex + j) % keys.length;
912+ if (keys[searchIndex] === sourceKey) {
913+ results[i] = oldTargets[searchIndex];
914+ updateResult(newSources[i], oldTargets[searchIndex], i);
915+ oldIndex = searchIndex + 1;
916+ found = true;
917+ break;
918+ }
919+ }
920+ if (!found) {
921+ results[i] = createResult(source, i);
922+ }
923+ }
924+ }
925+ results.length = newSources.length;
926+ keys = newKeys;
927+ },
928+ };
929+};
930+
931+export function _h(selector) {
932+ return function (properties) {
933+ return function (children) {
934+ return h(selector, properties, children)
935+ }
936+ }
937+}
938+
939+export function scheduleRender(projector) {
940+ return function () {
941+ projector.scheduleRender()
942+ }
943+}
944+
945+export function replace(projector) {
946+ return function (el) {
947+ return function (render) {
948+ return function () {
949+ projector.replace(el, render)
950+ }
951+ }
952+ }
953+}
plum/src/Maquette.pursnew file+33
@@ -0,0 +1,33 @@
1+module Maquette
2+ ( Projector
3+ , createProjector
4+ , string
5+ , VNode
6+ , h
7+ , replace
8+ , scheduleRender
9+ ) where
10+
11+import Prelude
12+
13+import Effect (Effect)
14+import Unsafe.Coerce (unsafeCoerce)
15+import Web.DOM (Element)
16+
17+foreign import data Projector :: Type
18+
19+foreign import createProjector :: Effect Projector
20+
21+foreign import data VNode :: Type
22+
23+foreign import _h :: forall props. String -> { | props } -> Array VNode -> VNode
24+
25+h :: forall props. String -> Record props -> Array VNode -> VNode
26+h = _h
27+
28+string :: String -> VNode
29+string = unsafeCoerce
30+
31+foreign import scheduleRender :: Projector -> Effect Unit
32+
33+foreign import replace :: Projector -> Element -> Effect VNode -> Effect Unit
plum/src/Plum.pursnew file+36
@@ -0,0 +1,36 @@
1+module Plum (Plum, run) where
2+
3+import Prelude
4+
5+import Data.Maybe (Maybe(..))
6+import Effect (Effect)
7+import Effect.Ref as Ref
8+import Maquette as Maquette
9+import Plum.View (View, unView)
10+import Web.DOM.NonElementParentNode (getElementById) as Web
11+import Web.HTML (window) as Web
12+import Web.HTML.HTMLDocument (toNonElementParentNode) as Web
13+import Web.HTML.Window (document) as Web
14+
15+type Plum msg model =
16+ { init :: Effect model
17+ , update :: msg -> model -> Effect model
18+ , view :: model -> View msg
19+ }
20+
21+run :: forall msg model. String -> Plum msg model -> Effect Unit
22+run id plum = do
23+ modelRef <- plum.init >>= Ref.new
24+ Web.window >>= Web.document >>= Web.getElementById id <<< Web.toNonElementParentNode >>= case _ of
25+ Just element -> do
26+ projector <- Maquette.createProjector
27+ Maquette.replace projector element do
28+ model <- Ref.read modelRef
29+ pure $ unView (plum.view model)
30+ ( \msg -> do
31+ m <- Ref.read modelRef
32+ m_ <- plum.update msg m
33+ Ref.write m_ modelRef
34+ )
35+
36+ Nothing -> pure unit
plum/src/Plum/View.pursnew file+114
@@ -0,0 +1,114 @@
1+module Plum.View
2+ ( View
3+ , unView
4+ , Attribute
5+ , spacing
6+ , row
7+ , wrappedRow
8+ , column
9+ , wrappedColumn
10+ , v
11+ , s
12+ , button
13+ ) where
14+
15+import Prelude
16+
17+import Data.Array as Array
18+import Effect (Effect)
19+import Foreign.Object (Object)
20+import Foreign.Object as Object
21+import Maquette (VNode, h, string)
22+import Type.Row.Homogeneous (class Homogeneous)
23+import Unsafe.Coerce (unsafeCoerce)
24+
25+newtype View :: Type -> Type
26+newtype View msg = View ((msg -> Effect Unit) -> VNode)
27+
28+unView :: forall msg. View msg -> (msg -> Effect Unit) -> VNode
29+unView (View node) = node
30+
31+data Attribute msg =
32+ Spacing Int Int
33+
34+spacing :: forall msg. Int -> Attribute msg
35+spacing x = Spacing x x
36+
37+data Context = Flexbox
38+
39+newtype RenderedAttribute = RenderedAttribute { styles :: Object String }
40+
41+instance Semigroup RenderedAttribute where
42+ append (RenderedAttribute x) (RenderedAttribute y) =
43+ RenderedAttribute { styles: Object.union x.styles y.styles }
44+
45+instance Monoid RenderedAttribute where
46+ mempty = RenderedAttribute $ { styles: Object.empty }
47+
48+renderAttribute :: forall msg. Context -> Attribute msg -> RenderedAttribute
49+renderAttribute _ctx attr = RenderedAttribute $ case attr of
50+ Spacing x y -> { styles: Object.insert "gap" (show x <> "px " <> show y <> "px") Object.empty }
51+
52+renderWithStyles :: forall styles msg. Homogeneous styles String => Context -> Array (Attribute msg) -> { | styles } -> RenderedAttribute
53+renderWithStyles ctx attrs styles = withStyles (renderAttributes ctx attrs) styles
54+
55+withStyles :: forall styles. Homogeneous styles String => RenderedAttribute -> { | styles } -> RenderedAttribute
56+withStyles (RenderedAttribute x) styles =
57+ RenderedAttribute $ { styles: Object.union x.styles (Object.fromHomogeneous styles) }
58+
59+renderAttributes :: forall msg. Context -> Array (Attribute msg) -> RenderedAttribute
60+renderAttributes ctx = Array.foldMap (renderAttribute ctx)
61+
62+v :: forall msg. Array (Attribute msg) -> View msg -> View msg
63+v _ (View child) = View $ \fire -> h "div" {} [ child fire ]
64+
65+row :: forall msg. Array (Attribute msg) -> Array (View msg) -> View msg
66+row attrs children =
67+ _v "div"
68+ ( renderWithStyles Flexbox attrs
69+ { display: "flex"
70+ , "flex-direction": "row"
71+ }
72+ )
73+ children
74+
75+wrappedRow :: forall msg. Array (Attribute msg) -> Array (View msg) -> View msg
76+wrappedRow attrs children =
77+ _v "div"
78+ ( renderWithStyles Flexbox attrs
79+ { display: "flex"
80+ , "flex-direction": "row"
81+ , "flex-wrap": "wrap"
82+ }
83+ )
84+ children
85+
86+column :: forall msg. Array (Attribute msg) -> Array (View msg) -> View msg
87+column attrs children =
88+ _v "div"
89+ ( renderWithStyles Flexbox attrs
90+ { display: "flex"
91+ , "flex-direction": "column"
92+ }
93+ )
94+ children
95+
96+wrappedColumn :: forall msg. Array (Attribute msg) -> Array (View msg) -> View msg
97+wrappedColumn attrs children =
98+ _v "div"
99+ ( renderWithStyles Flexbox attrs
100+ { display: "flex"
101+ , "flex-direction": "column"
102+ , "flex-wrap": "wrap"
103+ }
104+ )
105+ children
106+
107+button :: forall msg. Array (Attribute msg) -> { onPress :: msg, view :: View msg } -> View msg
108+button _ { onPress, view } = View $ \fire -> h "div" { onclick: fire onPress } [ unView view fire ]
109+
110+s :: forall msg. String -> View msg
111+s x = View $ \_ -> string x
112+
113+_v :: forall msg. String -> RenderedAttribute -> Array (View msg) -> View msg
114+_v el attr children = View $ \fire -> h el (unsafeCoerce attr) (map (\(View f) -> f fire) children)
plum/test/Main.pursnew file+11
@@ -0,0 +1,11 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Effect (Effect)
6+import Effect.Class.Console (log)
7+
8+main :: Effect Unit
9+main = do
10+ log "๐Ÿ"
11+ log "You should add some tests."