hyper-element 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * hyper-element - hyperHTML + WebComponents
3
+ * A lightweight library for creating custom elements with hyperHTML templating.
4
+ */
5
+
6
+ /**
7
+ * Tagged template literal function for rendering HTML content.
8
+ * Use as a tagged template: Html`<div>${value}</div>`
9
+ *
10
+ * Supports auto-wire syntax for efficient list rendering:
11
+ * ```javascript
12
+ * Html`<ul>{+each ${users}}<li>{name}</li>{-each}</ul>`;
13
+ * ```
14
+ *
15
+ * This is equivalent to:
16
+ * ```javascript
17
+ * Html`<ul>${users.map(u => Html.wire(u, id)`<li>${u.name}</li>`)}</ul>`;
18
+ * ```
19
+ *
20
+ * Syntax:
21
+ * - `{+each ${array}}...{-each}` - Loop with auto-wire for DOM reuse
22
+ * - `{name}` - Access item property
23
+ * - `{address.city}` - Nested property access
24
+ * - `{...}` or `{ ... }` - Current item value (formatted: primitives escaped, arrays join(","), objects JSON, functions called)
25
+ * - `{@}` - Current array index (0-based)
26
+ * - `{+each {items}}...{-each}` - Nested loop using parent's property
27
+ */
28
+ export interface HtmlFunction {
29
+ /**
30
+ * Render HTML content using tagged template literals
31
+ */
32
+ (strings: TemplateStringsArray, ...values: any[]): any;
33
+
34
+ /**
35
+ * Create a wired template bound to an object for efficient re-rendering
36
+ * @param obj - Object to bind the template to
37
+ * @param id - Optional template identifier
38
+ */
39
+ wire(
40
+ obj: object,
41
+ id?: string
42
+ ): (strings: TemplateStringsArray, ...values: any[]) => any;
43
+
44
+ /**
45
+ * Create a lightweight template without wire binding
46
+ */
47
+ lite(strings: TemplateStringsArray, ...values: any[]): any;
48
+
49
+ /**
50
+ * Mark a string as safe HTML that should not be escaped.
51
+ * Use with caution - only for trusted HTML content.
52
+ * @param html - The HTML string to mark as safe
53
+ */
54
+ raw(html: string): { value: string };
55
+
56
+ /**
57
+ * Template function available when template attribute is used on the element
58
+ */
59
+ template?: (data: Record<string, any>) => any;
60
+ }
61
+
62
+ /**
63
+ * Context object available as `this` in render() and setup() methods
64
+ */
65
+ export interface ElementContext {
66
+ /** The DOM element */
67
+ element: HTMLElement;
68
+ /** Parsed attributes from the element with automatic type coercion */
69
+ attrs: Record<string, any>;
70
+ /** Dataset proxy with automatic type coercion */
71
+ dataset: Record<string, any>;
72
+ /** Store value from setup */
73
+ store?: any;
74
+ /** Text content of element */
75
+ wrappedContent: string;
76
+ }
77
+
78
+ /**
79
+ * Result object returned from fragment methods (methods starting with capital letter)
80
+ */
81
+ export interface FragmentResult {
82
+ /** Rendered content */
83
+ any?: any;
84
+ /** Render only once */
85
+ once?: boolean;
86
+ /** Template string or promise resolving to template */
87
+ template?:
88
+ | string
89
+ | Promise<
90
+ string | { template: string; values: Record<string, any> | any[] }
91
+ >;
92
+ /** Template values */
93
+ values?: Record<string, any> | any[];
94
+ /** Text content */
95
+ text?: string;
96
+ /** HTML content */
97
+ html?: string;
98
+ /** Placeholder content */
99
+ placeholder?: any;
100
+ }
101
+
102
+ /**
103
+ * Callback for store updates in setup.
104
+ * Call this with a store value or getter function to enable reactive updates.
105
+ */
106
+ export type OnNextCallback = (
107
+ store: any | (() => any)
108
+ ) => (...data: any[]) => void;
109
+
110
+ /**
111
+ * Base class for creating custom elements with hyperHTML templating.
112
+ * Extend this class and implement the render() method to create a custom element.
113
+ *
114
+ * @example
115
+ * ```javascript
116
+ * class MyElement extends hyperElement {
117
+ * render(Html) {
118
+ * Html`<div>Hello ${this.attrs.name}!</div>`;
119
+ * }
120
+ * }
121
+ * customElements.define('my-element', MyElement);
122
+ * ```
123
+ */
124
+ export class hyperElement extends HTMLElement {
125
+ /** Unique identifier for this element instance */
126
+ identifier: symbol;
127
+
128
+ /** Parsed attributes from the element with automatic type coercion */
129
+ attrs: Record<string, any>;
130
+
131
+ /** Store value from setup */
132
+ store?: any;
133
+
134
+ /** Dataset proxy with automatic type coercion */
135
+ dataset: Record<string, any>;
136
+
137
+ /** Text content of element */
138
+ wrappedContent: string;
139
+
140
+ /** Reference to the DOM element */
141
+ element: HTMLElement;
142
+
143
+ /** Get the innerHTML of the shadow/element content */
144
+ get innerShadow(): string;
145
+
146
+ /**
147
+ * Optional setup lifecycle method. Called once when the element is connected.
148
+ * Use this to set up stores, subscriptions, or other initialization logic.
149
+ *
150
+ * @param onNext - Call this with a store value or getter to enable reactive updates
151
+ * @returns Optional teardown function called when element is disconnected
152
+ *
153
+ * @example
154
+ * ```javascript
155
+ * setup(onNext) {
156
+ * const store = createStore({ count: 0 });
157
+ * onNext(store.getState);
158
+ * return store.subscribe(() => this.render());
159
+ * }
160
+ * ```
161
+ */
162
+ setup?(onNext: OnNextCallback): void | (() => void);
163
+
164
+ /**
165
+ * Required render lifecycle method. Called on every render cycle.
166
+ * Use the Html template tag to render content to the element.
167
+ *
168
+ * @param Html - Tagged template literal function for rendering
169
+ * @param data - Additional data passed from store updates
170
+ *
171
+ * @example
172
+ * ```javascript
173
+ * render(Html) {
174
+ * Html`<div>Hello ${this.attrs.name}!</div>`;
175
+ * }
176
+ * ```
177
+ */
178
+ render(Html: HtmlFunction, ...data: any[]): void;
179
+ }
180
+
181
+ declare global {
182
+ interface Window {
183
+ hyperElement: typeof hyperElement;
184
+ hyperHTML: any;
185
+ }
186
+ }
187
+
188
+ export default hyperElement;
package/package.json CHANGED
@@ -1,12 +1,27 @@
1
1
  {
2
- "version": "0.10.0",
2
+ "version": "0.12.0",
3
3
  "name": "hyper-element",
4
4
  "author": "Brian Shannon (github.com/codemeasandwich)",
5
5
  "description": "hyperHTML + WebComponents",
6
- "main": "source/hyperElement.js",
6
+ "main": "build/hyperElement.js",
7
+ "types": "index.d.ts",
8
+ "files": [
9
+ "build/hyperElement.min.js",
10
+ "build/hyperElement.min.js.map",
11
+ "index.d.ts"
12
+ ],
7
13
  "scripts": {
8
- "build": "./node_modules/.bin/babel --presets es2015 source/hyperElement.js -o source/bundle.js",
9
- "test": "echo \"Error: no test specified\" && exit 1"
14
+ "build": "node scripts/build.js",
15
+ "test": "npm run build && playwright test; rm -rf build",
16
+ "test:ui": "npm run build && playwright test --ui; rm -rf build",
17
+ "test:headed": "npm run build && playwright test --headed; rm -rf build",
18
+ "kitchensink": "npx serve .",
19
+ "release": "bash scripts/publish.sh",
20
+ "hooks:install": "cp .hooks/pre-commit .git/hooks/ && cp .hooks/commit-msg .git/hooks/ && cp -r .hooks/pre-commit.d .git/hooks/ && chmod +x .git/hooks/pre-commit .git/hooks/commit-msg .git/hooks/pre-commit.d/*.sh",
21
+ "prepare": "npm run hooks:install",
22
+ "lint": "eslint src/",
23
+ "format": "prettier --check .",
24
+ "format:fix": "prettier --write ."
10
25
  },
11
26
  "license": "MIT",
12
27
  "repository": {
@@ -22,11 +37,28 @@
22
37
  "webcomponents"
23
38
  ],
24
39
  "dependencies": {
25
- "hyperhtml": "^2.4.3"
40
+ "hyperhtml": "^2.34.2"
26
41
  },
27
42
  "devDependencies": {
28
- "babel-cli": "^6.26.0",
29
- "babel-core": "^6.26.0",
30
- "babel-preset-es2015": "^6.24.1"
43
+ "@commitlint/cli": "^18.6.1",
44
+ "@commitlint/config-conventional": "^18.6.3",
45
+ "@playwright/test": "^1.40.0",
46
+ "c8": "^10.1.3",
47
+ "decode-uri-component": "^0.2.2",
48
+ "esbuild": "^0.25.0",
49
+ "eslint": "^8.57.1",
50
+ "eslint-config-prettier": "^9.1.2",
51
+ "eslint-plugin-prettier": "^5.5.5",
52
+ "husky": "^9.1.7",
53
+ "lint-staged": "^15.5.2",
54
+ "lodash": "^4.17.21",
55
+ "minimist": "^1.2.8",
56
+ "mkdirp": "^1.0.4",
57
+ "prettier": "^3.8.0",
58
+ "serve": "^14.2.5",
59
+ "set-value": "^2.0.1",
60
+ "typescript": "^5.9.3",
61
+ "union-value": "^2.0.1",
62
+ "v8-to-istanbul": "^9.3.0"
31
63
  }
32
64
  }
package/example/index.ejs DELETED
@@ -1,28 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
5
- <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.19/webcomponents-lite.js"></script>
6
- <script src="https://cdnjs.cloudflare.com/ajax/libs/mobx/3.3.2/mobx.umd.min.js"></script>
7
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
8
- <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js" type="text/javascript"></script>
9
- <script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js" type="text/javascript"></script>
10
- <script src="https://unpkg.com/hyperhtml@latest/min.js"></script>
11
- <script src="https://cdn.jsdelivr.net/npm/hyper-element@latest/source/hyperElement.js"></script>
12
- <script src="./logic.js"></script>
13
- <script src="./test-elem.js"></script>
14
- </head>
15
-
16
- <body>
17
- <div id="app"/>
18
- <test-elem max="50"></test-elem>
19
- <my-profile>my profile</my-profile>
20
-
21
- <script>
22
- window.addEventListener('WebComponentsReady', function(e) {
23
- console.log("App ready!","imports are loaded and elements have been registered")
24
- });
25
- </script>
26
- </body>
27
-
28
- </html>
package/example/index.js DELETED
@@ -1,2 +0,0 @@
1
- //import hyperElement from '../source/hyperElement.js'
2
- //window.hyperElement = hyperElement
package/example/logic.js DELETED
@@ -1,95 +0,0 @@
1
- 'use strict';
2
- //runInAction // https://www.youtube.com/watch?v=uAlxod75FIM
3
- const { observable, observe, autorun, computed, runInAction } = mobx;
4
-
5
- /*
6
- class Todo {
7
- // id = Math.random();
8
- @observable title;
9
- @observable finished = false;
10
- @computed get unfinishedTodoCount() {
11
- return this.finished;
12
- }
13
- constructor(title) {
14
- this.title = title;
15
- }
16
- }*/
17
-
18
-
19
- var todoStore = observable({
20
- /* some observable state */
21
- todos: [],
22
-
23
- /* a derived value */
24
- get completedCount() {
25
- return this.todos.filter(todo => todo.completed).length;
26
- }
27
- });
28
-
29
- /* a function that observes the state */
30
- autorun(function() {
31
- console.log("Completed %d of %d items",
32
- todoStore.completedCount,
33
- todoStore.todos.length
34
- );
35
- });
36
-
37
- /* ..and some actions that modify the state */
38
- todoStore.todos[0] = {
39
- title: "Take a walk",
40
- completed: false
41
- };
42
- // -> synchronously prints 'Completed 0 of 1 items'
43
-
44
- todoStore.todos[0].completed = true;
45
- // -> synchronously prints 'Completed 1 of 1 items'
46
-
47
-
48
-
49
- (()=>{
50
-
51
-
52
- const appState = observable({
53
-
54
- get fullName () {
55
- console.count('fullName');
56
- return this.firstName + ' ' + this.lastName;
57
- },
58
- firstName: 'Matt',
59
- lastName: 'Ruby',
60
- form:'',
61
- top:0,
62
- devices:["d1","d2"],
63
- age: 34,
64
- temperature:80,
65
- todos:[
66
- { title:"foo", done: false },
67
- { title:"bar", done: true },
68
- ],
69
- get completedCount() {
70
- console.count('completedCount');
71
- return this.todos.filter(todo => todo.done).length;
72
- }
73
- })
74
- /*
75
- const appState = new class appState{
76
- @observable firstName = 'Matt'
77
- @observable lastName = 'Ruby'
78
- @observable top =0
79
- @observable devices =["d1","d2"]
80
- @observable age = 34
81
- @observable temperature=80
82
- @observable todos=[
83
- { title:"foo", done: false },
84
- { title:"bar", done: true },
85
- ],
86
- @computed get completedCount() {
87
- console.count('completedCount');
88
- return this.todos.filter(todo => todo.done).length;
89
- }
90
-
91
- }*/
92
-
93
- window.appState = appState;
94
-
95
- })()
@@ -1,15 +0,0 @@
1
- {
2
- "name": "example",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "start":"poi",
8
- "test": "echo \"Error: no test specified\" && exit 1"
9
- },
10
- "author": "",
11
- "license": "ISC",
12
- "dependencies": {
13
- "mobx": "^3.4.1"
14
- }
15
- }
@@ -1,4 +0,0 @@
1
- module.exports = {
2
- minimize:true,
3
- sourceMap:true
4
- }
@@ -1,118 +0,0 @@
1
- 'use strict';
2
- (()=>{
3
-
4
- //=====================================================
5
- //============================= Example: simple element
6
- //=====================================================
7
-
8
- document.registerElement("the-max", class extends hyperElement{
9
- render(Html){
10
- Html`MAX:${this.props.max}`
11
- }
12
- })
13
-
14
- //=====================================================
15
- //============================= Example: using backbone
16
- //=====================================================
17
-
18
- //+++++++++++++++++++++++++++++++++++++ Backbone Model
19
- //++++++++++++++++++++++++++++++++++++++++++++++++++++
20
-
21
- var person = new (Backbone.Model.extend({
22
- defaults: {
23
- name: 'Guest User',
24
- }
25
- }));
26
-
27
- // to change from console !!!!!
28
- window.person = person;
29
-
30
- //++++++++++++++++++++++++++++++++++++++++ The Element
31
- //++++++++++++++++++++++++++++++++++++++++++++++++++++
32
-
33
- document.registerElement("my-profile", class extends hyperElement{
34
-
35
- setup(onNext){
36
- person.on("change",onNext(person.toJSON.bind(person)));
37
- // OR person.on("change",onNext(()=>person.toJSON()));
38
- }
39
-
40
- render(Html,{name}){
41
- Html`Profile: ${name}`
42
- }
43
- })
44
-
45
-
46
- //=====================================================
47
- //================================= Example: using mobx
48
- //======================================== using a wire
49
-
50
- document.registerElement("alex-rocks", class extends hyperElement{
51
-
52
- setup(onNext){
53
- mobx.autorun(onNext(window.appState));
54
- }
55
-
56
- render(Html,{devices}){
57
-
58
- const _ = Html.lite;
59
-
60
- Html`
61
-
62
- Your devices
63
- <ul>${
64
-
65
- devices.map(num=> _`<li>${num}</li>`)
66
-
67
- }</ul>
68
- <the-max max=20 />`
69
- }
70
- })
71
-
72
- //=====================================================
73
- //================================= Example: using mobx
74
- //========================================= with inputs
75
-
76
- document.registerElement("test-elem", class extends hyperElement{
77
-
78
- setup(onNext){
79
- mobx.autorun(onNext(window.appState));
80
- }
81
-
82
- render(Html,store){
83
-
84
- const max = this.props.max || 100
85
-
86
- Html`
87
-
88
- <h1 style=${{color:store.temperature < max ? "green":"red"}}>
89
- temperature: ${store.temperature}
90
- </h1>
91
- ${store.fullName} ${store.completedCount} ${store.todos.length}
92
- <h2>Time is ${new Date().toLocaleTimeString()}.</h2>
93
-
94
- <input oninput=${this.oninput}
95
- value=${store.form}
96
- onkeyup=${this.onkeyup}/>
97
-
98
- <br/>
99
- <alex-rocks />
100
-
101
- `}
102
-
103
- onkeyup({key}){
104
- if("Enter" === key)
105
- this.save()
106
- }
107
-
108
- save(){
109
- this.store.devices.push(this.store.form);
110
- this.store.form = "";
111
- }
112
-
113
- oninput(event){
114
- this.store.form = event.target.value
115
- }
116
- })
117
-
118
- })()