@joist/observable 2.0.0-alpha.14 → 2.0.0-alpha.17
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joist/observable",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.17",
|
|
4
4
|
"main": "./target/build/lib.js",
|
|
5
5
|
"module": "./target/build/lib.js",
|
|
6
6
|
"exports": {
|
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
"target/build"
|
|
13
13
|
],
|
|
14
14
|
"sideEffects": false,
|
|
15
|
-
"description": "
|
|
15
|
+
"description": "Monitor and respond to object changes",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/deebloo/joist.git"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"TypeScript",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"Observable",
|
|
23
|
+
"WebComponents",
|
|
24
|
+
"Reactive"
|
|
24
25
|
],
|
|
25
26
|
"author": "deebloo",
|
|
26
27
|
"license": "MIT",
|
|
@@ -34,5 +35,5 @@
|
|
|
34
35
|
"test": "tsc -p tsconfig.test.json && wtr --config ../../wtr.config.mjs --port 8002",
|
|
35
36
|
"build": "tsc -p tsconfig.build.json"
|
|
36
37
|
},
|
|
37
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "5caed0681c6fca2599a51420d48d344f68871ab4"
|
|
38
39
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface AttributeParser<T> {
|
|
2
|
+
read(val: string): T;
|
|
3
|
+
write(val: T): string;
|
|
4
|
+
mapTo: string;
|
|
5
|
+
}
|
|
6
|
+
export declare type AttributeParsers = Record<string, AttributeParser<unknown>>;
|
|
7
|
+
export declare function defaultParser(mapTo: string): AttributeParser<boolean | string>;
|
|
8
|
+
export declare function propNameToAttrName(prop: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function defaultParser(mapTo) {
|
|
2
|
+
return {
|
|
3
|
+
read(val) {
|
|
4
|
+
// if a boolean assume such
|
|
5
|
+
if (val === 'true' || val === 'false') {
|
|
6
|
+
return val === 'true';
|
|
7
|
+
}
|
|
8
|
+
return val;
|
|
9
|
+
},
|
|
10
|
+
write: String,
|
|
11
|
+
mapTo,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function propNameToAttrName(prop) {
|
|
15
|
+
return prop
|
|
16
|
+
.split(/(?=[A-Z])/)
|
|
17
|
+
.join('-')
|
|
18
|
+
.toLowerCase();
|
|
19
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
+
import { AttributeParser } from './attribute-parsers';
|
|
1
2
|
export declare function getObservableAttributes(c: typeof HTMLElement): Array<string>;
|
|
2
|
-
export declare function getAttributeParsers<T extends typeof HTMLElement>(c: T): Record<string, AttributeParser
|
|
3
|
-
export
|
|
4
|
-
read(val: string): any;
|
|
5
|
-
write(val: unknown): string;
|
|
6
|
-
}
|
|
7
|
-
export declare function attr<T extends HTMLElement>(p: Partial<AttributeParser>): (t: T, key: string) => void;
|
|
3
|
+
export declare function getAttributeParsers<T extends typeof HTMLElement>(c: T): Record<string, AttributeParser<unknown>>;
|
|
4
|
+
export declare function attr<T extends HTMLElement>(p: Partial<AttributeParser<unknown>>): (t: T, key: string) => void;
|
|
8
5
|
export declare function attr<T extends HTMLElement>(t: T, key: string): void;
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
+
import { defaultParser, propNameToAttrName, } from './attribute-parsers';
|
|
1
2
|
export function getObservableAttributes(c) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function defaultRead(val) {
|
|
5
|
-
// if a boolean assume such
|
|
6
|
-
if (val === 'true' || val === 'false') {
|
|
7
|
-
return val === 'true';
|
|
8
|
-
}
|
|
9
|
-
return val;
|
|
3
|
+
const attrs = Reflect.get(c, 'observedAttributes') || [];
|
|
4
|
+
return attrs.map(propNameToAttrName);
|
|
10
5
|
}
|
|
11
6
|
export function getAttributeParsers(c) {
|
|
12
7
|
const parsers = Reflect.get(c, 'attributeParsers') || {};
|
|
@@ -14,11 +9,13 @@ export function getAttributeParsers(c) {
|
|
|
14
9
|
}
|
|
15
10
|
export function attr(targetOrParser, key) {
|
|
16
11
|
if (targetOrParser instanceof HTMLElement) {
|
|
17
|
-
|
|
12
|
+
const attrName = propNameToAttrName(key);
|
|
13
|
+
return defineAttribute(targetOrParser, attrName, key);
|
|
18
14
|
}
|
|
19
15
|
return (target, key) => {
|
|
20
16
|
const parser = targetOrParser;
|
|
21
|
-
|
|
17
|
+
const attrName = propNameToAttrName(key);
|
|
18
|
+
defineAttribute(target, attrName, key);
|
|
22
19
|
const attributeParsers = Reflect.get(target.constructor, 'attributeParsers');
|
|
23
20
|
attributeParsers[key].read = parser.read || attributeParsers[key].read;
|
|
24
21
|
attributeParsers[key].write = parser.write || attributeParsers[key].write;
|
|
@@ -26,22 +23,20 @@ export function attr(targetOrParser, key) {
|
|
|
26
23
|
return void 0;
|
|
27
24
|
};
|
|
28
25
|
}
|
|
29
|
-
function defineAttribute(target,
|
|
26
|
+
function defineAttribute(target, attrName, propName) {
|
|
30
27
|
const observedAttributes = Reflect.get(target.constructor, 'observedAttributes');
|
|
31
28
|
if (observedAttributes) {
|
|
32
|
-
observedAttributes.push(
|
|
29
|
+
observedAttributes.push(attrName);
|
|
33
30
|
}
|
|
34
31
|
else {
|
|
35
|
-
Reflect.set(target.constructor, 'observedAttributes', [
|
|
32
|
+
Reflect.set(target.constructor, 'observedAttributes', [attrName]);
|
|
36
33
|
}
|
|
37
34
|
const attributeParsers = Reflect.get(target.constructor, 'attributeParsers');
|
|
38
35
|
if (attributeParsers) {
|
|
39
|
-
attributeParsers[
|
|
36
|
+
attributeParsers[attrName] = defaultParser(propName);
|
|
40
37
|
}
|
|
41
38
|
else {
|
|
42
|
-
|
|
43
|
-
attributeParsers[key] = { read: defaultRead, write: String };
|
|
44
|
-
Reflect.set(target.constructor, 'attributeParsers', attributeParsers);
|
|
39
|
+
Reflect.set(target.constructor, 'attributeParsers', { [attrName]: defaultParser(propName) });
|
|
45
40
|
}
|
|
46
41
|
return void 0;
|
|
47
42
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAttributeParsers, getObservableAttributes } from './attribute';
|
|
2
|
+
import { propNameToAttrName } from './attribute-parsers';
|
|
2
3
|
export class Change {
|
|
3
4
|
constructor(value, previousValue, firstChange) {
|
|
4
5
|
this.value = value;
|
|
@@ -32,23 +33,23 @@ export function observable(Base) {
|
|
|
32
33
|
Object.defineProperties(this, descriptors);
|
|
33
34
|
}
|
|
34
35
|
connectedCallback() {
|
|
35
|
-
attributes.
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
const propVal = Reflect.get(this,
|
|
36
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
37
|
+
const key = attributes[i];
|
|
38
|
+
const { write, mapTo } = parsers[key];
|
|
39
|
+
if (this.getAttribute(key) === null) {
|
|
40
|
+
const propVal = Reflect.get(this, mapTo);
|
|
40
41
|
if (propVal !== undefined && propVal !== null && propVal !== '') {
|
|
41
42
|
this.setAttribute(key, write(propVal));
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
}
|
|
45
|
+
}
|
|
45
46
|
if (super.connectedCallback) {
|
|
46
47
|
super.connectedCallback();
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
50
|
-
const { read } = parsers[name];
|
|
51
|
-
Reflect.set(this,
|
|
51
|
+
const { read, mapTo } = parsers[name];
|
|
52
|
+
Reflect.set(this, mapTo, read(newVal));
|
|
52
53
|
if (super.attributeChangedCallback) {
|
|
53
54
|
super.attributeChangedCallback(name, oldVal, newVal);
|
|
54
55
|
}
|
|
@@ -57,10 +58,10 @@ export function observable(Base) {
|
|
|
57
58
|
if (this instanceof HTMLElement) {
|
|
58
59
|
for (let change in changes) {
|
|
59
60
|
if (attributes.includes(change)) {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
if (value !== this.getAttribute(
|
|
63
|
-
this.setAttribute(
|
|
61
|
+
const value = parsers[change].write(changes[change].value);
|
|
62
|
+
const attrName = propNameToAttrName(change);
|
|
63
|
+
if (value !== this.getAttribute(attrName)) {
|
|
64
|
+
this.setAttribute(attrName, value);
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -103,17 +104,19 @@ function definePropChange(key, propChange) {
|
|
|
103
104
|
// If there is no previous change defined set it up
|
|
104
105
|
this.propChange = Promise.resolve().then(() => {
|
|
105
106
|
// run onPropChanges here. This makes sure we capture all changes
|
|
106
|
-
|
|
107
|
+
const changes = {};
|
|
108
|
+
// Copy changes and keep track of whether or not this is the first time a given property has changes
|
|
107
109
|
for (let change in this.propChanges) {
|
|
108
|
-
|
|
110
|
+
changes[change] = this.propChanges[change];
|
|
111
|
+
changes[change].firstChange = !this.initializedChanges.has(change);
|
|
109
112
|
this.initializedChanges.add(change);
|
|
110
113
|
}
|
|
114
|
+
// clear out before calling to account for changes made INSIDE of the onPropertyChanged callback
|
|
115
|
+
this.propChange = null;
|
|
116
|
+
this.propChanges = {};
|
|
111
117
|
if (this.onPropertyChanged) {
|
|
112
|
-
this.onPropertyChanged(
|
|
118
|
+
this.onPropertyChanged(changes);
|
|
113
119
|
}
|
|
114
|
-
// reset for next time
|
|
115
|
-
this.propChanges = {};
|
|
116
|
-
this.propChange = null;
|
|
117
120
|
});
|
|
118
121
|
}
|
|
119
122
|
return this.propChange;
|