Current section
26 Versions
Jump to
Current section
26 Versions
Compare versions
21
files changed
+223
additions
-120
deletions
| @@ -2,11 +2,10 @@ | |
| 2 2 | |
| 3 3 | Build rich, interactive UIs entirely in Elixir using Hologram's declarative component system. Your client-side code is intelligently transpiled to JavaScript, providing modern frontend capabilities without relying on any JavaScript frameworks. |
| 4 4 | |
| 5 | - Website: https://hologram.page/ |
| 5 | + Website: https://hologram.page |
| 6 6 | |
| 7 7 | ## Sponsors |
| 8 8 | |
| 9 | - ### Early Adopters (monthly tier) |
| 10 9 | * Calancea Daniel, [@D4no0](https://github.com/D4no0) |
| 11 10 | * Lucas Sifoni, [@Lucassifoni](https://github.com/Lucassifoni) |
| @@ -0,0 +1,18 @@ | |
| 1 | + "use strict"; |
| 2 | + |
| 3 | + import Type from "../type.mjs"; |
| 4 | + |
| 5 | + export default class SelectEvent { |
| 6 | + static buildOperationParam(event) { |
| 7 | + const value = event.target.value.substring( |
| 8 | + event.target.selectionStart, |
| 9 | + event.target.selectionEnd, |
| 10 | + ); |
| 11 | + |
| 12 | + return Type.map([[Type.atom("value"), Type.bitstring(value)]]); |
| 13 | + } |
| 14 | + |
| 15 | + static isEventIgnored(_event) { |
| 16 | + return false; |
| 17 | + } |
| 18 | + } |
| @@ -1,17 +1,25 @@ | |
| 1 1 | "use strict"; |
| 2 2 | |
| 3 3 | export default class GlobalRegistry { |
| 4 | + // Made public to make tests easier |
| 4 5 | static rootKey = "hologram"; |
| 5 6 | |
| 7 | + static append(key, item) { |
| 8 | + const items = $.get(key) || []; |
| 9 | + $.set(key, [...items, item]); |
| 10 | + } |
| 11 | + |
| 6 12 | static get(key) { |
| 7 | - return globalThis?.[GlobalRegistry.rootKey]?.[key] || null; |
| 13 | + return globalThis?.[$.rootKey]?.[key] || null; |
| 8 14 | } |
| 9 15 | |
| 10 16 | static set(key, value) { |
| 11 | - if (!globalThis[GlobalRegistry.rootKey]) { |
| 12 | - globalThis[GlobalRegistry.rootKey] = {}; |
| 17 | + if (!globalThis[$.rootKey]) { |
| 18 | + globalThis[$.rootKey] = {}; |
| 13 19 | } |
| 14 20 | |
| 15 | - globalThis[GlobalRegistry.rootKey][key] = value; |
| 21 | + globalThis[$.rootKey][key] = value; |
| 16 22 | } |
| 17 23 | } |
| 24 | + |
| 25 | + const $ = GlobalRegistry; |
| @@ -25,6 +25,7 @@ import ChangeEvent from "./events/change_event.mjs"; | |
| 25 25 | import FocusEvent from "./events/focus_event.mjs"; |
| 26 26 | import MouseEvent from "./events/mouse_event.mjs"; |
| 27 27 | import PointerEvent from "./events/pointer_event.mjs"; |
| 28 | + import SelectEvent from "./events/select_event.mjs"; |
| 28 29 | import SubmitEvent from "./events/submit_event.mjs"; |
| 29 30 | import TransitionEvent from "./events/transition_event.mjs"; |
| 30 31 | |
| @@ -37,15 +38,7 @@ import ManuallyPortedElixirIO from "./elixir/io.mjs"; | |
| 37 38 | import ManuallyPortedElixirKernel from "./elixir/kernel.mjs"; |
| 38 39 | import ManuallyPortedElixirString from "./elixir/string.mjs"; |
| 39 40 | |
| 40 | - import { |
| 41 | - attributesModule, |
| 42 | - eventListenersModule, |
| 43 | - init, |
| 44 | - toVNode, |
| 45 | - vnode, |
| 46 | - } from "snabbdom"; |
| 47 | - |
| 48 | - const patch = init([attributesModule, eventListenersModule]); |
| 41 | + import {toVNode} from "snabbdom"; |
| 49 42 | |
| 50 43 | // TODO: test |
| 51 44 | export default class Hologram { |
| @@ -161,8 +154,7 @@ export default class Hologram { | |
| 161 154 | |
| 162 155 | Hologram.executeAction(nextAction); |
| 163 156 | } else { |
| 164 | - // TODO: consider: remove when there is a proper client-side bitstring implementation in place |
| 165 | - window.requestAnimationFrame(() => Hologram.render()); |
| 157 | + Hologram.render(); |
| 166 158 | } |
| 167 159 | |
| 168 160 | if (!Type.isNil(nextPage)) { |
| @@ -303,6 +295,7 @@ export default class Hologram { | |
| 303 295 | window.requestAnimationFrame(() => { |
| 304 296 | Hologram.#patchPage(html); |
| 305 297 | window.scrollTo(0, 0); |
| 298 | + |
| 306 299 | history.pushState($.#historyId, null, pagePath); |
| 307 300 | }); |
| 308 301 | } |
| @@ -316,7 +309,7 @@ export default class Hologram { | |
| 316 309 | Hologram.#pageParams, |
| 317 310 | ); |
| 318 311 | |
| 319 | - Hologram.virtualDocument = patch( |
| 312 | + Hologram.virtualDocument = Vdom.patchVirtualDocument( |
| 320 313 | Hologram.virtualDocument, |
| 321 314 | newVirtualDocument, |
| 322 315 | ); |
| @@ -495,6 +488,9 @@ export default class Hologram { | |
| 495 488 | case "pointerup": |
| 496 489 | return PointerEvent; |
| 497 490 | |
| 491 | + case "select": |
| 492 | + return SelectEvent; |
| 493 | + |
| 498 494 | case "submit": |
| 499 495 | return SubmitEvent; |
| 500 496 | |
| @@ -689,47 +685,9 @@ export default class Hologram { | |
| 689 685 | |
| 690 686 | const newVirtualDocument = Vdom.from(html); |
| 691 687 | |
| 692 | - // First patch the root html element itself to handle its attributes |
| 693 | - Hologram.virtualDocument = patch( |
| 688 | + Hologram.virtualDocument = Vdom.patchVirtualDocument( |
| 694 689 | Hologram.virtualDocument, |
| 695 | - vnode( |
| 696 | - "html", |
| 697 | - {attrs: newVirtualDocument.data.attrs || {}}, |
| 698 | - Hologram.virtualDocument.children, |
| 699 | - ), |
| 700 | - ); |
| 701 | - |
| 702 | - // Then patch head and body separately to preserve JavaScript/CSS handling |
| 703 | - |
| 704 | - const oldHead = Hologram.virtualDocument.children.find( |
| 705 | - (child) => child.sel === "head", |
| 706 | - ); |
| 707 | - |
| 708 | - const newHead = newVirtualDocument.children.find( |
| 709 | - (child) => child.sel === "head", |
| 710 | - ); |
| 711 | - |
| 712 | - const oldBody = Hologram.virtualDocument.children.find( |
| 713 | - (child) => child.sel === "body", |
| 714 | - ); |
| 715 | - |
| 716 | - const newBody = newVirtualDocument.children.find( |
| 717 | - (child) => child.sel === "body", |
| 718 | - ); |
| 719 | - |
| 720 | - Hologram.virtualDocument.children = Hologram.virtualDocument.children.map( |
| 721 | - (child) => { |
| 722 | - switch (child.sel) { |
| 723 | - case "body": |
| 724 | - return patch(oldBody, newBody); |
| 725 | - |
| 726 | - case "head": |
| 727 | - return patch(oldHead, newHead); |
| 728 | - |
| 729 | - default: |
| 730 | - return child; |
| 731 | - } |
| 732 | - }, |
| 690 | + newVirtualDocument, |
| 733 691 | ); |
| 734 692 | } |
| @@ -114,7 +114,7 @@ export default class Interpreter { | |
| 114 114 | return `function ${moduleName}.${functionName}/${arity} is undefined or private`; |
| 115 115 | } |
| 116 116 | |
| 117 | - return `function ${moduleName}.${functionName}/${arity} is undefined (module ${moduleName} is not available)`; |
| 117 | + return `function ${moduleName}.${functionName}/${arity} is undefined (module ${moduleName} is not available). Make sure the module name is correct and has been specified in full (or that an alias has been defined)`; |
| 118 118 | } |
| 119 119 | |
| 120 120 | // callAnonymousFunction() has no unit tests in interpreter_test.mjs, only: |
Loading more files…