@joist/observable 2.0.0-beta.12 → 2.0.0-beta.13

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 CHANGED
@@ -6,7 +6,7 @@ Decorating a class with `@observable` means that instances of that class will BE
6
6
  #### Installation:
7
7
 
8
8
  ```BASH
9
- npm i @joist/observablebeta
9
+ npm i @joist/observable@beta
10
10
  ```
11
11
 
12
12
  #### Example:
@@ -72,13 +72,13 @@ state.todos = [...state.todos, 'Build Shit'];
72
72
  state.userName = 'Danny Blue'
73
73
  ```
74
74
 
75
- #### Attributes
75
+ #### Custom Elements
76
76
 
77
77
  If you are using @observable with custom elements it is very likely that you will want to read from and write to attributes.
78
- Joist accounts for this by giving you an `@attr` decorator.
78
+ In order to appropriately handle reading from and writting to attributes. Any class that extends HTMLElement can use the `@attr` decorator to define attribute behavior.
79
79
 
80
80
  ```TS
81
- import { observable, observe, attr} from '@joist/observable';
81
+ import { observable, observe, attr } from '@joist/observable';
82
82
 
83
83
  @observable
84
84
  class TestElement extends HTMLElement {
@@ -99,3 +99,34 @@ class TestElement extends HTMLElement {
99
99
  count = new Date();
100
100
  }
101
101
  ```
102
+
103
+ ##### Upgrading Custom Element Properties
104
+
105
+ One tricky thing about custom elements and properties is how to handle them when the upgrade. For example.
106
+
107
+ ```HTML
108
+
109
+ <my-element></my-element>
110
+
111
+ <script>
112
+ const el = document.querySelector('my-element');
113
+
114
+ // A property is changed BEFORE the definition is loaded.
115
+ // We still want this value to be available on our custom element
116
+ el.name = "Hello!"
117
+ </script>
118
+
119
+ <script src="./path/to/my-element-defintion.js" type="module">
120
+ ```
121
+
122
+ Joist provides a mixin that solves this issue.
123
+ You can ensure that any properties that are set prior to upgrade time are forwared to your custom element.
124
+
125
+ ```TS
126
+ import { observable, observe, ForwardProps } from '@joist/observable';
127
+
128
+ @observable
129
+ class TestElement extends ForwardProps(HTMLElement) {
130
+ @observe name = ''; // now in our example above this value will be set to "Hello"
131
+ }
132
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joist/observable",
3
- "version": "2.0.0-beta.12",
3
+ "version": "2.0.0-beta.13",
4
4
  "main": "./target/build/lib.js",
5
5
  "module": "./target/build/lib.js",
6
6
  "exports": {
@@ -35,5 +35,5 @@
35
35
  "test": "tsc -p tsconfig.test.json && wtr --config ../../wtr.config.mjs --port 8002",
36
36
  "build": "tsc -p tsconfig.build.json"
37
37
  },
38
- "gitHead": "1962273dca1744f0227fd76c143b07d93566e809"
38
+ "gitHead": "045b15e31e5f2c9d2b32af3d93400bbbcdabc6ea"
39
39
  }
@@ -1,5 +1,5 @@
1
1
  import { AttributeParser } from './attribute-parsers';
2
- export declare function getObservableAttributes(c: typeof HTMLElement): Array<string>;
3
- export declare function getAttributeParsers<T extends typeof HTMLElement>(c: T): Record<string, AttributeParser<unknown>>;
2
+ export declare function getObservableAttributes(c: typeof HTMLElement | Function): Array<string>;
3
+ export declare function getAttributeParsers<T extends typeof HTMLElement | Function>(c: T): Record<string, AttributeParser<unknown>>;
4
4
  export declare function attr<T>(p: Partial<AttributeParser<T>>): <E extends HTMLElement>(t: E, key: string) => void;
5
5
  export declare function attr<T extends HTMLElement>(t: T, key: string): void;
@@ -0,0 +1,8 @@
1
+ import { Changes } from './observable';
2
+ export declare class ObservableElement extends HTMLElement {
3
+ __upgradedProps: Map<keyof this, unknown>;
4
+ constructor();
5
+ connectedCallback(): void;
6
+ attributeChangedCallback(name: string, _: string, newVal: string): void;
7
+ onPropertyChanged(changes: Changes): void;
8
+ }
@@ -0,0 +1,47 @@
1
+ import { getAttributeParsers, getObservableAttributes } from './attribute';
2
+ import { propNameToAttrName } from './attribute-parsers';
3
+ export class ObservableElement extends HTMLElement {
4
+ constructor() {
5
+ super();
6
+ this.__upgradedProps = new Map();
7
+ for (let prop in this) {
8
+ if (this.hasOwnProperty(prop) && prop !== 'upgradedProps') {
9
+ this.__upgradedProps.set(prop, this[prop]);
10
+ }
11
+ }
12
+ }
13
+ connectedCallback() {
14
+ const attributes = getObservableAttributes(this.constructor);
15
+ const parsers = getAttributeParsers(this.constructor);
16
+ for (let i = 0; i < attributes.length; i++) {
17
+ const key = attributes[i];
18
+ const { write, mapTo } = parsers[key];
19
+ if (this.getAttribute(key) === null) {
20
+ const propVal = Reflect.get(this, mapTo);
21
+ if (propVal !== undefined && propVal !== null && propVal !== '') {
22
+ this.setAttribute(key, write(propVal));
23
+ }
24
+ }
25
+ }
26
+ }
27
+ attributeChangedCallback(name, _, newVal) {
28
+ const parsers = getAttributeParsers(this.constructor);
29
+ const { read, mapTo } = parsers[name];
30
+ Reflect.set(this, mapTo, read(newVal));
31
+ }
32
+ onPropertyChanged(changes) {
33
+ const attributes = getObservableAttributes(this.constructor);
34
+ const parsers = getAttributeParsers(this.constructor);
35
+ if (this instanceof ObservableElement) {
36
+ for (let change in changes) {
37
+ const attrName = propNameToAttrName(change);
38
+ if (attributes.includes(attrName)) {
39
+ const value = parsers[attrName].write(changes[change].value);
40
+ if (value !== this.getAttribute(attrName)) {
41
+ this.setAttribute(attrName, value);
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
@@ -9,10 +9,319 @@ export interface OnPropertyChanged {
9
9
  onPropertyChanged(changes: Changes): void;
10
10
  }
11
11
  export declare function getObservableProperties(c: any): Array<string | symbol>;
12
+ export declare function ForwardProps<T extends new (...args: any[]) => HTMLElement>(Base: T): {
13
+ new (...args: any[]): {
14
+ __upgradedProps: Map<keyof any, unknown>;
15
+ accessKey: string;
16
+ readonly accessKeyLabel: string;
17
+ autocapitalize: string;
18
+ dir: string;
19
+ draggable: boolean;
20
+ hidden: boolean;
21
+ innerText: string;
22
+ lang: string;
23
+ readonly offsetHeight: number;
24
+ readonly offsetLeft: number;
25
+ readonly offsetParent: Element | null;
26
+ readonly offsetTop: number;
27
+ readonly offsetWidth: number;
28
+ outerText: string;
29
+ spellcheck: boolean;
30
+ title: string;
31
+ translate: boolean;
32
+ attachInternals(): ElementInternals;
33
+ click(): void;
34
+ addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void;
35
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void;
36
+ removeEventListener<K_1 extends keyof HTMLElementEventMap>(type: K_1, listener: (this: HTMLElement, ev: HTMLElementEventMap[K_1]) => any, options?: boolean | EventListenerOptions | undefined): void;
37
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions | undefined): void;
38
+ connectedCallback?(): void;
39
+ disconnectedCallback?(): void;
40
+ attributeChangedCallback?(name: string, oldVal: string, newVal: string): void;
41
+ readonly attributes: NamedNodeMap;
42
+ readonly classList: DOMTokenList;
43
+ className: string;
44
+ readonly clientHeight: number;
45
+ readonly clientLeft: number;
46
+ readonly clientTop: number;
47
+ readonly clientWidth: number;
48
+ id: string;
49
+ readonly localName: string;
50
+ readonly namespaceURI: string | null;
51
+ onfullscreenchange: ((this: Element, ev: Event) => any) | null;
52
+ onfullscreenerror: ((this: Element, ev: Event) => any) | null;
53
+ outerHTML: string;
54
+ readonly ownerDocument: Document;
55
+ readonly part: DOMTokenList;
56
+ readonly prefix: string | null;
57
+ readonly scrollHeight: number;
58
+ scrollLeft: number;
59
+ scrollTop: number;
60
+ readonly scrollWidth: number;
61
+ readonly shadowRoot: ShadowRoot | null;
62
+ slot: string;
63
+ readonly tagName: string;
64
+ attachShadow(init: ShadowRootInit): ShadowRoot;
65
+ closest<K_2 extends keyof HTMLElementTagNameMap>(selector: K_2): HTMLElementTagNameMap[K_2] | null;
66
+ closest<K_3 extends keyof SVGElementTagNameMap>(selector: K_3): SVGElementTagNameMap[K_3] | null;
67
+ closest<E extends Element = Element>(selectors: string): E | null;
68
+ getAttribute(qualifiedName: string): string | null;
69
+ getAttributeNS(namespace: string | null, localName: string): string | null;
70
+ getAttributeNames(): string[];
71
+ getAttributeNode(qualifiedName: string): Attr | null;
72
+ getAttributeNodeNS(namespace: string | null, localName: string): Attr | null;
73
+ getBoundingClientRect(): DOMRect;
74
+ getClientRects(): DOMRectList;
75
+ getElementsByClassName(classNames: string): HTMLCollectionOf<Element>;
76
+ getElementsByTagName<K_4 extends keyof HTMLElementTagNameMap>(qualifiedName: K_4): HTMLCollectionOf<HTMLElementTagNameMap[K_4]>;
77
+ getElementsByTagName<K_5 extends keyof SVGElementTagNameMap>(qualifiedName: K_5): HTMLCollectionOf<SVGElementTagNameMap[K_5]>;
78
+ getElementsByTagName(qualifiedName: string): HTMLCollectionOf<Element>;
79
+ getElementsByTagNameNS(namespaceURI: "http://www.w3.org/1999/xhtml", localName: string): HTMLCollectionOf<HTMLElement>;
80
+ getElementsByTagNameNS(namespaceURI: "http://www.w3.org/2000/svg", localName: string): HTMLCollectionOf<SVGElement>;
81
+ getElementsByTagNameNS(namespace: string | null, localName: string): HTMLCollectionOf<Element>;
82
+ hasAttribute(qualifiedName: string): boolean;
83
+ hasAttributeNS(namespace: string | null, localName: string): boolean;
84
+ hasAttributes(): boolean;
85
+ hasPointerCapture(pointerId: number): boolean;
86
+ insertAdjacentElement(where: InsertPosition, element: Element): Element | null;
87
+ insertAdjacentHTML(position: InsertPosition, text: string): void;
88
+ insertAdjacentText(where: InsertPosition, data: string): void;
89
+ matches(selectors: string): boolean;
90
+ releasePointerCapture(pointerId: number): void;
91
+ removeAttribute(qualifiedName: string): void;
92
+ removeAttributeNS(namespace: string | null, localName: string): void;
93
+ removeAttributeNode(attr: Attr): Attr;
94
+ requestFullscreen(options?: FullscreenOptions | undefined): Promise<void>;
95
+ requestPointerLock(): void;
96
+ scroll(options?: ScrollToOptions | undefined): void;
97
+ scroll(x: number, y: number): void;
98
+ scrollBy(options?: ScrollToOptions | undefined): void;
99
+ scrollBy(x: number, y: number): void;
100
+ scrollIntoView(arg?: boolean | ScrollIntoViewOptions | undefined): void;
101
+ scrollTo(options?: ScrollToOptions | undefined): void;
102
+ scrollTo(x: number, y: number): void;
103
+ setAttribute(qualifiedName: string, value: string): void;
104
+ setAttributeNS(namespace: string | null, qualifiedName: string, value: string): void;
105
+ setAttributeNode(attr: Attr): Attr | null;
106
+ setAttributeNodeNS(attr: Attr): Attr | null;
107
+ setPointerCapture(pointerId: number): void;
108
+ toggleAttribute(qualifiedName: string, force?: boolean | undefined): boolean;
109
+ webkitMatchesSelector(selectors: string): boolean;
110
+ readonly baseURI: string;
111
+ readonly childNodes: NodeListOf<ChildNode>;
112
+ readonly firstChild: ChildNode | null;
113
+ readonly isConnected: boolean;
114
+ readonly lastChild: ChildNode | null;
115
+ readonly nextSibling: ChildNode | null;
116
+ readonly nodeName: string;
117
+ readonly nodeType: number;
118
+ nodeValue: string | null;
119
+ readonly parentElement: HTMLElement | null;
120
+ readonly parentNode: ParentNode | null;
121
+ readonly previousSibling: ChildNode | null;
122
+ textContent: string | null;
123
+ appendChild<T_1 extends Node>(node: T_1): T_1;
124
+ cloneNode(deep?: boolean | undefined): Node;
125
+ compareDocumentPosition(other: Node): number;
126
+ contains(other: Node | null): boolean;
127
+ getRootNode(options?: GetRootNodeOptions | undefined): Node;
128
+ hasChildNodes(): boolean;
129
+ insertBefore<T_2 extends Node>(node: T_2, child: Node | null): T_2;
130
+ isDefaultNamespace(namespace: string | null): boolean;
131
+ isEqualNode(otherNode: Node | null): boolean;
132
+ isSameNode(otherNode: Node | null): boolean;
133
+ lookupNamespaceURI(prefix: string | null): string | null;
134
+ lookupPrefix(namespace: string | null): string | null;
135
+ normalize(): void;
136
+ removeChild<T_3 extends Node>(child: T_3): T_3;
137
+ replaceChild<T_4 extends Node>(node: Node, child: T_4): T_4;
138
+ readonly ATTRIBUTE_NODE: number;
139
+ readonly CDATA_SECTION_NODE: number;
140
+ readonly COMMENT_NODE: number;
141
+ readonly DOCUMENT_FRAGMENT_NODE: number;
142
+ readonly DOCUMENT_NODE: number;
143
+ readonly DOCUMENT_POSITION_CONTAINED_BY: number;
144
+ readonly DOCUMENT_POSITION_CONTAINS: number;
145
+ readonly DOCUMENT_POSITION_DISCONNECTED: number;
146
+ readonly DOCUMENT_POSITION_FOLLOWING: number;
147
+ readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
148
+ readonly DOCUMENT_POSITION_PRECEDING: number;
149
+ readonly DOCUMENT_TYPE_NODE: number;
150
+ readonly ELEMENT_NODE: number;
151
+ readonly ENTITY_NODE: number;
152
+ readonly ENTITY_REFERENCE_NODE: number;
153
+ readonly NOTATION_NODE: number;
154
+ readonly PROCESSING_INSTRUCTION_NODE: number;
155
+ readonly TEXT_NODE: number;
156
+ dispatchEvent(event: Event): boolean;
157
+ ariaAtomic: string | null;
158
+ ariaAutoComplete: string | null;
159
+ ariaBusy: string | null;
160
+ ariaChecked: string | null;
161
+ ariaColCount: string | null;
162
+ ariaColIndex: string | null;
163
+ ariaColSpan: string | null;
164
+ ariaCurrent: string | null;
165
+ ariaDisabled: string | null;
166
+ ariaExpanded: string | null;
167
+ ariaHasPopup: string | null;
168
+ ariaHidden: string | null;
169
+ ariaKeyShortcuts: string | null;
170
+ ariaLabel: string | null;
171
+ ariaLevel: string | null;
172
+ ariaLive: string | null;
173
+ ariaModal: string | null;
174
+ ariaMultiLine: string | null;
175
+ ariaMultiSelectable: string | null;
176
+ ariaOrientation: string | null;
177
+ ariaPlaceholder: string | null;
178
+ ariaPosInSet: string | null;
179
+ ariaPressed: string | null;
180
+ ariaReadOnly: string | null;
181
+ ariaRequired: string | null;
182
+ ariaRoleDescription: string | null;
183
+ ariaRowCount: string | null;
184
+ ariaRowIndex: string | null;
185
+ ariaRowSpan: string | null;
186
+ ariaSelected: string | null;
187
+ ariaSetSize: string | null;
188
+ ariaSort: string | null;
189
+ ariaValueMax: string | null;
190
+ ariaValueMin: string | null;
191
+ ariaValueNow: string | null;
192
+ ariaValueText: string | null;
193
+ animate(keyframes: PropertyIndexedKeyframes | Keyframe[] | null, options?: number | KeyframeAnimationOptions | undefined): Animation;
194
+ getAnimations(options?: GetAnimationsOptions | undefined): Animation[];
195
+ after(...nodes: (string | Node)[]): void;
196
+ before(...nodes: (string | Node)[]): void;
197
+ remove(): void;
198
+ replaceWith(...nodes: (string | Node)[]): void;
199
+ innerHTML: string;
200
+ readonly nextElementSibling: Element | null;
201
+ readonly previousElementSibling: Element | null;
202
+ readonly childElementCount: number;
203
+ readonly children: HTMLCollection;
204
+ readonly firstElementChild: Element | null;
205
+ readonly lastElementChild: Element | null;
206
+ append(...nodes: (string | Node)[]): void;
207
+ prepend(...nodes: (string | Node)[]): void;
208
+ querySelector<K_6 extends keyof HTMLElementTagNameMap>(selectors: K_6): HTMLElementTagNameMap[K_6] | null;
209
+ querySelector<K_7 extends keyof SVGElementTagNameMap>(selectors: K_7): SVGElementTagNameMap[K_7] | null;
210
+ querySelector<E_1 extends Element = Element>(selectors: string): E_1 | null;
211
+ querySelectorAll<K_8 extends keyof HTMLElementTagNameMap>(selectors: K_8): NodeListOf<HTMLElementTagNameMap[K_8]>;
212
+ querySelectorAll<K_9 extends keyof SVGElementTagNameMap>(selectors: K_9): NodeListOf<SVGElementTagNameMap[K_9]>;
213
+ querySelectorAll<E_2 extends Element = Element>(selectors: string): NodeListOf<E_2>;
214
+ replaceChildren(...nodes: (string | Node)[]): void;
215
+ readonly assignedSlot: HTMLSlotElement | null;
216
+ oncopy: ((this: DocumentAndElementEventHandlers, ev: ClipboardEvent) => any) | null;
217
+ oncut: ((this: DocumentAndElementEventHandlers, ev: ClipboardEvent) => any) | null;
218
+ onpaste: ((this: DocumentAndElementEventHandlers, ev: ClipboardEvent) => any) | null;
219
+ readonly style: CSSStyleDeclaration;
220
+ contentEditable: string;
221
+ enterKeyHint: string;
222
+ inputMode: string;
223
+ readonly isContentEditable: boolean;
224
+ onabort: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null;
225
+ onanimationcancel: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null;
226
+ onanimationend: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null;
227
+ onanimationiteration: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null;
228
+ onanimationstart: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null;
229
+ onauxclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
230
+ onblur: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null;
231
+ oncanplay: ((this: GlobalEventHandlers, ev: Event) => any) | null;
232
+ oncanplaythrough: ((this: GlobalEventHandlers, ev: Event) => any) | null;
233
+ onchange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
234
+ onclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
235
+ onclose: ((this: GlobalEventHandlers, ev: Event) => any) | null;
236
+ oncontextmenu: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
237
+ oncuechange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
238
+ ondblclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
239
+ ondrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
240
+ ondragend: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
241
+ ondragenter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
242
+ ondragleave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
243
+ ondragover: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
244
+ ondragstart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
245
+ ondrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null;
246
+ ondurationchange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
247
+ onemptied: ((this: GlobalEventHandlers, ev: Event) => any) | null;
248
+ onended: ((this: GlobalEventHandlers, ev: Event) => any) | null;
249
+ onerror: OnErrorEventHandler;
250
+ onfocus: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null;
251
+ onformdata: ((this: GlobalEventHandlers, ev: FormDataEvent) => any) | null;
252
+ ongotpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
253
+ oninput: ((this: GlobalEventHandlers, ev: Event) => any) | null;
254
+ oninvalid: ((this: GlobalEventHandlers, ev: Event) => any) | null;
255
+ onkeydown: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null;
256
+ onkeypress: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null;
257
+ onkeyup: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null;
258
+ onload: ((this: GlobalEventHandlers, ev: Event) => any) | null;
259
+ onloadeddata: ((this: GlobalEventHandlers, ev: Event) => any) | null;
260
+ onloadedmetadata: ((this: GlobalEventHandlers, ev: Event) => any) | null;
261
+ onloadstart: ((this: GlobalEventHandlers, ev: Event) => any) | null;
262
+ onlostpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
263
+ onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
264
+ onmouseenter: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
265
+ onmouseleave: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
266
+ onmousemove: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
267
+ onmouseout: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
268
+ onmouseover: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
269
+ onmouseup: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
270
+ onpause: ((this: GlobalEventHandlers, ev: Event) => any) | null;
271
+ onplay: ((this: GlobalEventHandlers, ev: Event) => any) | null;
272
+ onplaying: ((this: GlobalEventHandlers, ev: Event) => any) | null;
273
+ onpointercancel: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
274
+ onpointerdown: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
275
+ onpointerenter: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
276
+ onpointerleave: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
277
+ onpointermove: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
278
+ onpointerout: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
279
+ onpointerover: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
280
+ onpointerup: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null;
281
+ onprogress: ((this: GlobalEventHandlers, ev: ProgressEvent<EventTarget>) => any) | null;
282
+ onratechange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
283
+ onreset: ((this: GlobalEventHandlers, ev: Event) => any) | null;
284
+ onresize: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null;
285
+ onscroll: ((this: GlobalEventHandlers, ev: Event) => any) | null;
286
+ onsecuritypolicyviolation: ((this: GlobalEventHandlers, ev: SecurityPolicyViolationEvent) => any) | null;
287
+ onseeked: ((this: GlobalEventHandlers, ev: Event) => any) | null;
288
+ onseeking: ((this: GlobalEventHandlers, ev: Event) => any) | null;
289
+ onselect: ((this: GlobalEventHandlers, ev: Event) => any) | null;
290
+ onselectionchange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
291
+ onselectstart: ((this: GlobalEventHandlers, ev: Event) => any) | null;
292
+ onslotchange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
293
+ onstalled: ((this: GlobalEventHandlers, ev: Event) => any) | null;
294
+ onsubmit: ((this: GlobalEventHandlers, ev: SubmitEvent) => any) | null;
295
+ onsuspend: ((this: GlobalEventHandlers, ev: Event) => any) | null;
296
+ ontimeupdate: ((this: GlobalEventHandlers, ev: Event) => any) | null;
297
+ ontoggle: ((this: GlobalEventHandlers, ev: Event) => any) | null;
298
+ ontouchcancel?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null | undefined;
299
+ ontouchend?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null | undefined;
300
+ ontouchmove?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null | undefined;
301
+ ontouchstart?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null | undefined;
302
+ ontransitioncancel: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null;
303
+ ontransitionend: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null;
304
+ ontransitionrun: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null;
305
+ ontransitionstart: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null;
306
+ onvolumechange: ((this: GlobalEventHandlers, ev: Event) => any) | null;
307
+ onwaiting: ((this: GlobalEventHandlers, ev: Event) => any) | null;
308
+ onwebkitanimationend: ((this: GlobalEventHandlers, ev: Event) => any) | null;
309
+ onwebkitanimationiteration: ((this: GlobalEventHandlers, ev: Event) => any) | null;
310
+ onwebkitanimationstart: ((this: GlobalEventHandlers, ev: Event) => any) | null;
311
+ onwebkittransitionend: ((this: GlobalEventHandlers, ev: Event) => any) | null;
312
+ onwheel: ((this: GlobalEventHandlers, ev: WheelEvent) => any) | null;
313
+ autofocus: boolean;
314
+ readonly dataset: DOMStringMap;
315
+ nonce?: string | undefined;
316
+ tabIndex: number;
317
+ blur(): void;
318
+ focus(options?: FocusOptions | undefined): void;
319
+ };
320
+ } & T;
12
321
  export interface ObservableBase {
13
- propChanges: Changes;
14
- propChange: Promise<void> | null;
15
- initializedChanges: Set<string | symbol>;
322
+ __propChanges: Map<string | symbol, Change>;
323
+ __propChange: Promise<void> | null;
324
+ __initializedChanges: Set<string | symbol>;
16
325
  definePropChange(key: string | symbol, propChange: Change): Promise<void>;
17
326
  onPropertyChanged?(changes: Changes): void;
18
327
  }
@@ -20,14 +329,12 @@ export declare function observe(target: any, key: string): void;
20
329
  export declare function observable<T extends new (...args: any[]) => any>(Base: T): {
21
330
  new (...args: any[]): {
22
331
  [x: string]: any;
23
- propChanges: Changes;
24
- propChange: Promise<void> | null;
25
- initializedChanges: Set<string | symbol>;
26
- definePropChange: typeof definePropChange;
27
- connectedCallback(this: HTMLElement & any): void;
28
- attributeChangedCallback(this: HTMLElement & any, name: string, oldVal: string, newVal: string): void;
332
+ __propChanges: Map<any, any>;
333
+ __propChange: Promise<void> | null;
334
+ __initializedChanges: Set<string | symbol>;
335
+ connectedCallback(this: HTMLElement): void;
336
+ attributeChangedCallback(this: HTMLElement, name: string, oldVal: string, newVal: string): void;
29
337
  onPropertyChanged(changes: Changes): void;
338
+ definePropChange(key: string | symbol, propChange: Change): Promise<void>;
30
339
  };
31
340
  } & T;
32
- declare function definePropChange(this: ObservableBase, key: string | symbol, propChange: Change): Promise<void>;
33
- export {};
@@ -11,67 +11,130 @@ const PROPERTY_KEY = 'observedProperties';
11
11
  export function getObservableProperties(c) {
12
12
  return c[PROPERTY_KEY] || [];
13
13
  }
14
+ export function ForwardProps(Base) {
15
+ return class Foo extends Base {
16
+ constructor(...args) {
17
+ super(...args);
18
+ this.__upgradedProps = new Map();
19
+ for (let prop in this) {
20
+ if (this.hasOwnProperty(prop) && prop !== 'upgradedProps') {
21
+ this.__upgradedProps.set(prop, this[prop]);
22
+ }
23
+ }
24
+ }
25
+ };
26
+ }
14
27
  export function observe(target, key) {
15
28
  target.constructor[PROPERTY_KEY] = target.constructor[PROPERTY_KEY] || [];
16
29
  target.constructor[PROPERTY_KEY].push(key);
17
30
  }
18
31
  export function observable(Base) {
19
32
  const properties = getObservableProperties(Base);
20
- const attributes = getObservableAttributes(Base);
21
- const parsers = getAttributeParsers(Base);
22
33
  const descriptors = createPropertyDescripors(properties);
34
+ const parsers = getAttributeParsers(Base);
35
+ const attributes = getObservableAttributes(Base);
23
36
  return class Observable extends Base {
24
37
  constructor(...args) {
25
38
  super(...args);
26
- this.propChanges = {};
27
- this.propChange = null;
28
- this.initializedChanges = new Set();
29
- this.definePropChange = definePropChange;
30
- for (let prop in descriptors) {
31
- Reflect.set(this, createPrivateKey(prop), Reflect.get(this, prop));
32
- }
33
- Object.defineProperties(this, descriptors);
39
+ this.__propChanges = new Map();
40
+ this.__propChange = null;
41
+ this.__initializedChanges = new Set();
42
+ init.call(this, descriptors);
34
43
  }
35
44
  connectedCallback() {
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);
41
- if (propVal !== undefined && propVal !== null && propVal !== '') {
42
- this.setAttribute(key, write(propVal));
43
- }
44
- }
45
- }
45
+ connectedCallback.call(this, attributes, parsers);
46
46
  if (super.connectedCallback) {
47
47
  super.connectedCallback();
48
48
  }
49
49
  }
50
50
  attributeChangedCallback(name, oldVal, newVal) {
51
- const { read, mapTo } = parsers[name];
52
- Reflect.set(this, mapTo, read(newVal));
51
+ attributeChangedCallback.call(this, name, newVal, parsers);
53
52
  if (super.attributeChangedCallback) {
54
53
  super.attributeChangedCallback(name, oldVal, newVal);
55
54
  }
56
55
  }
57
56
  onPropertyChanged(changes) {
58
- if (this instanceof HTMLElement) {
59
- for (let change in changes) {
60
- const attrName = propNameToAttrName(change);
61
- if (attributes.includes(attrName)) {
62
- const value = parsers[attrName].write(changes[change].value);
63
- if (value !== this.getAttribute(attrName)) {
64
- this.setAttribute(attrName, value);
65
- }
66
- }
67
- }
68
- }
57
+ onPropertyChanged.call(this, attributes, parsers, changes);
69
58
  if (super.onPropertyChanged) {
70
59
  super.onPropertyChanged(changes);
71
60
  }
72
61
  }
62
+ definePropChange(key, propChange) {
63
+ return definePropChange.call(this, key, propChange);
64
+ }
73
65
  };
74
66
  }
67
+ function init(descriptors) {
68
+ // Set initial props if ForwardPropsed from ObservableElement
69
+ if ('__upgradedProps' in this && this['__upgradedProps'] instanceof Map) {
70
+ for (let [key, value] of this.__upgradedProps) {
71
+ Reflect.set(this, key, value);
72
+ }
73
+ }
74
+ for (let prop in descriptors) {
75
+ Object.defineProperty(this, createPrivateKey(prop), {
76
+ value: Reflect.get(this, prop),
77
+ enumerable: false,
78
+ writable: true,
79
+ });
80
+ }
81
+ Object.defineProperties(this, descriptors);
82
+ }
83
+ function connectedCallback(attributes, parsers) {
84
+ for (let i = 0; i < attributes.length; i++) {
85
+ const key = attributes[i];
86
+ const { write, mapTo } = parsers[key];
87
+ if (this.getAttribute(key) === null) {
88
+ const propVal = Reflect.get(this, mapTo);
89
+ if (propVal !== undefined && propVal !== null && propVal !== '') {
90
+ this.setAttribute(key, write(propVal));
91
+ }
92
+ }
93
+ }
94
+ }
95
+ function attributeChangedCallback(name, newVal, parsers) {
96
+ const { read, mapTo } = parsers[name];
97
+ Reflect.set(this, mapTo, read(newVal));
98
+ }
99
+ function onPropertyChanged(attributes, parsers, changes) {
100
+ if (this instanceof HTMLElement) {
101
+ for (let change in changes) {
102
+ const attrName = propNameToAttrName(change);
103
+ if (attributes.includes(attrName)) {
104
+ const value = parsers[attrName].write(changes[change].value);
105
+ if (value !== this.getAttribute(attrName)) {
106
+ this.setAttribute(attrName, value);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ function definePropChange(key, propChange) {
113
+ if (!this.__propChanges.has(key)) {
114
+ this.__propChanges.set(key, propChange);
115
+ }
116
+ this.__propChanges.get(key).value = propChange.value;
117
+ if (!this.__propChange) {
118
+ // If there is no previous change defined set it up
119
+ this.__propChange = Promise.resolve().then(() => {
120
+ // run onPropChanges here. This makes sure we capture all changes
121
+ const changes = {};
122
+ // Copy changes and keep track of whether or not this is the first time a given property has changes
123
+ for (let [key, value] of this.__propChanges) {
124
+ changes[key] = value;
125
+ changes[key].firstChange = !this.__initializedChanges.has(key);
126
+ this.__initializedChanges.add(key);
127
+ }
128
+ // clear out before calling to account for changes made INSIDE of the onPropertyChanged callback
129
+ this.__propChange = null;
130
+ this.__propChanges.clear();
131
+ if (this.onPropertyChanged) {
132
+ this.onPropertyChanged(changes);
133
+ }
134
+ });
135
+ }
136
+ return this.__propChange;
137
+ }
75
138
  function createPrivateKey(key) {
76
139
  return `__${key.toString()}`;
77
140
  }
@@ -95,29 +158,3 @@ function createPropertyDescripors(props) {
95
158
  }
96
159
  return descriptors;
97
160
  }
98
- function definePropChange(key, propChange) {
99
- if (!this.propChanges[key]) {
100
- this.propChanges[key] = propChange;
101
- }
102
- this.propChanges[key].value = propChange.value;
103
- if (!this.propChange) {
104
- // If there is no previous change defined set it up
105
- this.propChange = Promise.resolve().then(() => {
106
- // run onPropChanges here. This makes sure we capture all changes
107
- const changes = {};
108
- // Copy changes and keep track of whether or not this is the first time a given property has changes
109
- for (let change in this.propChanges) {
110
- changes[change] = this.propChanges[change];
111
- changes[change].firstChange = !this.initializedChanges.has(change);
112
- this.initializedChanges.add(change);
113
- }
114
- // clear out before calling to account for changes made INSIDE of the onPropertyChanged callback
115
- this.propChange = null;
116
- this.propChanges = {};
117
- if (this.onPropertyChanged) {
118
- this.onPropertyChanged(changes);
119
- }
120
- });
121
- }
122
- return this.propChange;
123
- }
@@ -1,2 +1,2 @@
1
- export { observable, Change, OnPropertyChanged, observe, Changes } from './lib/observable';
1
+ export { observable, Change, OnPropertyChanged, observe, Changes, ForwardProps, } from './lib/observable';
2
2
  export { attr } from './lib/attribute';
@@ -1,2 +1,2 @@
1
- export { observable, Change, observe } from './lib/observable';
1
+ export { observable, Change, observe, ForwardProps, } from './lib/observable';
2
2
  export { attr } from './lib/attribute';