hyper-element 0.11.1 → 0.12.1
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/README.md +789 -395
- package/index.d.ts +188 -0
- package/package.json +40 -8
- package/example/index.ejs +0 -28
- package/example/index.js +0 -2
- package/example/logic.js +0 -95
- package/example/package.json +0 -15
- package/example/poi.config.js +0 -4
- package/example/test-elem.js +0 -118
- package/source/bundle.js +0 -562
- package/source/hyperElement.js +0 -483
- package/test.html +0 -322
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.
|
|
2
|
+
"version": "0.12.1",
|
|
3
3
|
"name": "hyper-element",
|
|
4
4
|
"author": "Brian Shannon (github.com/codemeasandwich)",
|
|
5
5
|
"description": "hyperHTML + WebComponents",
|
|
6
|
-
"main": "
|
|
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": "
|
|
9
|
-
"test": "
|
|
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.
|
|
40
|
+
"hyperhtml": "^2.34.2"
|
|
26
41
|
},
|
|
27
42
|
"devDependencies": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
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
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
|
-
})()
|
package/example/package.json
DELETED
|
@@ -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
|
-
}
|
package/example/poi.config.js
DELETED
package/example/test-elem.js
DELETED
|
@@ -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
|
-
})()
|