From 3c1b3b0893cd1e21b94934ddf0260c328b8bcf0d Mon Sep 17 00:00:00 2001 From: Ayo Ayco Date: Fri, 8 Dec 2023 21:31:05 +0100 Subject: [PATCH] feat: templating (#9) --- examples/templating/index.html | 12 ++ examples/templating/index.js | 16 +++ package.json | 2 +- src/WebComponent.js | 29 ++++- src/html.js | 11 ++ src/index.js | 1 + src/utils/create-element.js | 28 +++++ src/utils/index.js | 1 + src/vendors/htm/LICENSE.txt | 202 +++++++++++++++++++++++++++++++++ 9 files changed, 295 insertions(+), 7 deletions(-) create mode 100644 examples/templating/index.html create mode 100644 examples/templating/index.js create mode 100644 src/html.js create mode 100644 src/utils/create-element.js create mode 100644 src/vendors/htm/LICENSE.txt diff --git a/examples/templating/index.html b/examples/templating/index.html new file mode 100644 index 0000000..6aeeac2 --- /dev/null +++ b/examples/templating/index.html @@ -0,0 +1,12 @@ + + + + + + WC demo + + + + + + diff --git a/examples/templating/index.js b/examples/templating/index.js new file mode 100644 index 0000000..829d22a --- /dev/null +++ b/examples/templating/index.js @@ -0,0 +1,16 @@ +import { WebComponent, html } from "../../src/index.js"; + +export class Counter extends WebComponent { + static props = { + count: 123, + }; + get template() { + return html` + + `; + } +} + +customElements.define("my-counter", Counter); diff --git a/package.json b/package.json index 95f96a9..c4d6c6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web-component-base", - "version": "2.0.0-beta.6", + "version": "2.0.0-beta.7", "description": "A zero-dependency, ~600 Bytes (minified & gzipped), JS base class for creating reactive custom elements easily", "type": "module", "exports": { diff --git a/src/WebComponent.js b/src/WebComponent.js index 341b82f..60f682a 100644 --- a/src/WebComponent.js +++ b/src/WebComponent.js @@ -1,4 +1,4 @@ -import { getKebabCase, getCamelCase, serialize, deserialize } from "./utils/index.js"; +import { createElement, getKebabCase, getCamelCase, serialize, deserialize } from "./utils/index.js"; /** * A minimal base class to reduce the complexity of creating reactive custom elements @@ -69,11 +69,7 @@ export class WebComponent extends HTMLElement { */ onChanges(changes) {} - render() { - if (typeof this.template === "string") this.innerHTML = this.template; - } - - constructor() { + constructor() { super(); this.#initializeProps(); } @@ -175,4 +171,25 @@ export class WebComponent extends HTMLElement { ); } } + + #prevDOM; + render() { + if (typeof this.template === "string") { + this.innerHTML = this.template; + return; + } else if (typeof this.template === 'object') { + const tree = this.template; + + // TODO: smart diffing + if (JSON.stringify(this.prev) !== JSON.stringify(tree)) { + // render + const el = createElement(tree); + if (el) { + if (Array.isArray(el)) this.replaceChildren(...el); + else this.replaceChildren(el); + } + this.#prevDOM = tree; + } + } + } } diff --git a/src/html.js b/src/html.js new file mode 100644 index 0000000..966955b --- /dev/null +++ b/src/html.js @@ -0,0 +1,11 @@ +/** + * htm -- https://github.com/developit/htm + * For license information please see ./vendors/htm/LICENSE.txt + */ +const htm=(new Map,function(n){for(var e,l,s=arguments,t=1,u="",r="",o=[0],f=function(n){1===t&&(n||(u=u.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?o.push(n?s[n]:u):3===t&&(n||u)?(o[1]=n?s[n]:u,t=2):2===t&&"..."===u&&n?o[2]=Object.assign(o[2]||{},s[n]):2===t&&u&&!n?(o[2]=o[2]||{})[u]=!0:t>=5&&(5===t?((o[2]=o[2]||{})[l]=n?u?u+s[n]:s[n]:u,t=6):(n||u)&&(o[2][l]+=n?u+s[n]:u)),u=""},i=0;i"===e?(t=1,u=""):u=e+u[0]:r?e===r?r="":u+=e:'"'===e||"'"===e?r=e:">"===e?(f(),t=1):t&&("="===e?(t=5,l=u,u=""):"/"===e&&(t<5||">"===n[i][p+1])?(f(),3===t&&(o=o[0]),t=o,(o=o[0]).push(this.apply(null,t.slice(1))),t=0):" "===e||"\t"===e||"\n"===e||"\r"===e?(f(),t=2):u+=e),3===t&&"!--"===u&&(t=4,o=o[0])}return f(),o.length>2?o.slice(1):o[1]}); + +function h(type, props, ...children) { + return {type, props, children}; +} + +export const html = htm.bind(h); diff --git a/src/index.js b/src/index.js index da242ef..9602d9f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,2 +1,3 @@ export { attachEffect } from "./attach-effect.js"; export { WebComponent } from "./WebComponent.js"; +export { html } from "./html.js"; diff --git a/src/utils/create-element.js b/src/utils/create-element.js new file mode 100644 index 0000000..380f303 --- /dev/null +++ b/src/utils/create-element.js @@ -0,0 +1,28 @@ +export function createElement(tree) { + if (!tree.type) { + const leaves = typeof tree === "object" ? Object.keys(tree) : []; + if (leaves?.length > 1) { + return leaves.map((leaf) => createElement(tree[leaf])); + } + return document.createTextNode(tree); + } else { + const el = document.createElement(tree.type); + const eventAttrs = tree.props + ? Object.keys(tree.props) + .filter((key) => key.startsWith("on:")) + .map((key) => ({ key, cb: tree.props[key] })) + : []; + eventAttrs.forEach((onEvent) => { + const { key, cb } = onEvent; + const eventId = key.replace("on:", ""); + el.addEventListener(eventId, cb); + }); + tree.children?.forEach((child) => { + const childEl = createElement(child); + if (childEl) { + el.appendChild(childEl); + } + }); + return el; + } +} diff --git a/src/utils/index.js b/src/utils/index.js index 355dd09..d94dcba 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -2,3 +2,4 @@ export { serialize } from "./serialize.js"; export { deserialize } from "./deserialize.js"; export { getCamelCase } from "./get-camel-case.js"; export { getKebabCase } from "./get-kebab-case.js"; +export {createElement} from "./create-element.js"; diff --git a/src/vendors/htm/LICENSE.txt b/src/vendors/htm/LICENSE.txt new file mode 100644 index 0000000..f107611 --- /dev/null +++ b/src/vendors/htm/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file