@lordfokas/yrframe 0.2.0 → 0.4.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.
@@ -23,7 +23,7 @@ export declare class Component<A extends Attributes> extends HTMLElement {
23
23
  /** Redraw this component. Works by deleting all children, calling render() and appending the results. */
24
24
  redraw(): void;
25
25
  /** Called after redraw() to do special manipulation of children nodes. */
26
- inject(): void;
26
+ renderedCallback(): void;
27
27
  /** Get this component's real width in pixels. */
28
28
  width(): number;
29
29
  /** Get this component's real height in pixels. */
package/dist/Component.js CHANGED
@@ -67,10 +67,10 @@ export class Component extends HTMLElement {
67
67
  ComponentFactory.appendChildren(this, child);
68
68
  }
69
69
  }
70
- this.inject();
70
+ this.renderedCallback();
71
71
  }
72
72
  /** Called after redraw() to do special manipulation of children nodes. */
73
- inject() { }
73
+ renderedCallback() { }
74
74
  /** Get this component's real width in pixels. */
75
75
  width() {
76
76
  return parseFloat(getComputedStyle(this, null).width.replace("px", ""));
@@ -15,7 +15,10 @@ export declare class ComponentEvents {
15
15
  private triggers;
16
16
  private readonly component;
17
17
  private readonly owner;
18
+ /** Used to keep track of internal mechanisms (triggers and sources) */
19
+ private internals;
18
20
  constructor(component: HTMLElement);
21
+ isEmpty(): boolean;
19
22
  /** Called by the component when entering DOM */
20
23
  connect(): void;
21
24
  /** Called by the component when leaving DOM */
@@ -35,8 +38,8 @@ export declare class ComponentEvents {
35
38
  * If the target configuration has no path, the whole event is stopped instead.
36
39
  */
37
40
  attachRemover<T extends Event, K1 extends keyof T>(target: EventTarget<T, K1> | undefined, predicate: Source<boolean>, name: string, optional?: Optional): void;
38
- /** Attach a listener that disables a component based on a boolean event field. */
39
- attachDisabler<T extends Event, K1 extends keyof T>(target: EventTarget<T, K1> | undefined, source: Source<HTMLElement>, name: string, optional?: Optional): void;
41
+ /** Attach a listener that adds or removes a flag attribute from a component based on a boolean event field. */
42
+ attachFlagger<T extends Event, K1 extends keyof T>(target: EventTarget<T, K1> | undefined, source: Source<HTMLElement>, flag: string, ref: boolean, name: string, optional?: Optional): void;
40
43
  /** Create a function that will fire an event and process data from it. */
41
44
  attachSource<T extends Event, K1 extends keyof T>(target: EventTarget<T, K1> | undefined, name: string, optional?: Optional): void;
42
45
  /** Create a fake source function that supplies a static or local value. */
@@ -17,10 +17,15 @@ export class ComponentEvents {
17
17
  triggers = {};
18
18
  component;
19
19
  owner;
20
+ /** Used to keep track of internal mechanisms (triggers and sources) */
21
+ internals = 0;
20
22
  constructor(component) {
21
23
  this.component = component;
22
24
  this.owner = component.constructor.name;
23
25
  }
26
+ isEmpty() {
27
+ return (this.listeners.length + this.internals) === 0;
28
+ }
24
29
  /** Called by the component when entering DOM */
25
30
  connect() {
26
31
  this.listeners.forEach(l => EventBus.GLOBAL.subscribe(l.type, l));
@@ -94,19 +99,19 @@ export class ComponentEvents {
94
99
  throw new Error("Unsupported path length > 1 for removers");
95
100
  }, nice).named(name, this.owner));
96
101
  }
97
- /** Attach a listener that disables a component based on a boolean event field. */
98
- attachDisabler(target, source, name, optional) {
99
- if (this.skip(target, name, "disabler", optional))
102
+ /** Attach a listener that adds or removes a flag attribute from a component based on a boolean event field. */
103
+ attachFlagger(target, source, flag, ref, name, optional) {
104
+ if (this.skip(target, name, "flagger", optional))
100
105
  return;
101
106
  this.createListener(target, name, (value, _) => {
102
107
  if (typeof value !== "boolean")
103
108
  return;
104
109
  const element = source.call(this);
105
- if (value) {
106
- element.setAttribute("disabled", "");
110
+ if (value === ref) {
111
+ element.setAttribute(flag, "");
107
112
  }
108
113
  else {
109
- element.removeAttribute("disabled");
114
+ element.removeAttribute(flag);
110
115
  }
111
116
  });
112
117
  }
@@ -120,6 +125,7 @@ export class ComponentEvents {
120
125
  callback.call(this.component, event.traverse(...path), event);
121
126
  });
122
127
  };
128
+ this.internals++;
123
129
  }
124
130
  /** Create a fake source function that supplies a static or local value. */
125
131
  attachStatic(value, source, name, optional) {
@@ -132,6 +138,7 @@ export class ComponentEvents {
132
138
  this.sources[name] = (callback) => {
133
139
  callback.call(this.component, source.call(this.component), undefined);
134
140
  };
141
+ this.internals++;
135
142
  }
136
143
  /** Fire a source function and process the returned data. */
137
144
  seek(name, callback) {
@@ -157,6 +164,7 @@ export class ComponentEvents {
157
164
  throw new Error("Unsupported path length > 1 for triggers");
158
165
  event.publish();
159
166
  };
167
+ this.internals++;
160
168
  }
161
169
  /** Fire a trigger function with the given data. */
162
170
  fire(name, data = {}, parent) {
package/dist/Facade.d.ts CHANGED
@@ -13,7 +13,6 @@ type FacadeElement = HTMLElement & EventHost & {
13
13
  * Used for aliasing and adapting components from other libraries.
14
14
  */
15
15
  export declare class Facade<A extends Attributes> {
16
- static readonly AEQ = "yr:";
17
16
  protected readonly node: FacadeElement;
18
17
  protected readonly isCustom: boolean;
19
18
  protected events(): ComponentEvents;
package/dist/Facade.js CHANGED
@@ -1,36 +1,23 @@
1
1
  import { ComponentEvents } from "./ComponentEvents.js";
2
2
  import { ComponentFactory } from "./ComponentFactory.js";
3
+ import { specialAttributes } from "./utils.js";
3
4
  const evt = Symbol('evt');
4
5
  /**
5
6
  * Fake component that constructs and returns another component instead.
6
7
  * Used for aliasing and adapting components from other libraries.
7
8
  */
8
9
  export class Facade {
9
- static AEQ = 'yr:'; // TODO: centralize these
10
10
  node;
11
11
  isCustom;
12
12
  events() {
13
+ if (!this.node[evt]) { // Lazily instantiate event manager
14
+ this.node[evt] = new ComponentEvents(this.node);
15
+ }
13
16
  return this.node[evt];
14
17
  }
15
18
  constructor(tag, props, defaults) {
16
19
  const node = this.node = document.createElement(tag);
17
- const events = node[evt] = new ComponentEvents(node);
18
20
  this.isCustom = tag.includes('-');
19
- // Attach to existing lifecycle callbacks.
20
- if (this.isCustom) {
21
- const { connectedCallback, disconnectedCallback } = node;
22
- node.connectedCallback = () => {
23
- events.connect();
24
- return connectedCallback?.call(node);
25
- };
26
- node.disconnectedCallback = () => {
27
- events.disconnect();
28
- return disconnectedCallback?.call(node);
29
- };
30
- }
31
- else { // TODO: rethink this, there's clearly use cases for it.
32
- throw new Error(`Facade for native element '${tag}' cannot hook to lifecycle calls.`);
33
- }
34
21
  const all = {};
35
22
  // Assign all defaults to object and overwrite with existing values in provided props
36
23
  if (defaults)
@@ -39,13 +26,35 @@ export class Facade {
39
26
  Object.assign(all, props);
40
27
  const attrs = {};
41
28
  for (const [k, v] of Object.entries(all)) {
42
- if (!k.startsWith(Facade.AEQ)) {
29
+ if (!specialAttributes.test(k)) {
43
30
  attrs[k] = v;
44
31
  }
45
32
  }
46
33
  ComponentFactory.setAttributesAndEvents(node, attrs);
47
34
  }
48
35
  content() {
36
+ // Dispose of unused event manager
37
+ if (this.node[evt]?.isEmpty()) {
38
+ delete this.node[evt];
39
+ }
49
40
  return this.node;
50
41
  }
51
42
  }
43
+ (function FacadeMutationObserver() {
44
+ new MutationObserver((records, _mo) => {
45
+ for (const record of records) {
46
+ if (record.type == "childList") {
47
+ for (const added of record.addedNodes) {
48
+ if (added instanceof HTMLElement) {
49
+ added[evt]?.connect();
50
+ }
51
+ }
52
+ for (const removed of record.removedNodes) {
53
+ if (removed instanceof HTMLElement) {
54
+ removed[evt]?.connect();
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }).observe(document.body, { subtree: true, childList: true });
60
+ })();
package/dist/utils.d.ts CHANGED
@@ -9,3 +9,4 @@ export type Attributes = {
9
9
  export type Consumer<T> = (t: T) => void;
10
10
  export type Source<T> = () => T;
11
11
  export declare function has($: any): boolean;
12
+ export declare const specialAttributes: RegExp;
package/dist/utils.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export function has($) {
2
2
  return $ !== undefined;
3
3
  }
4
+ export const specialAttributes = /^[a-z]+\:/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lordfokas/yrframe",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "A Typescript class-based WebComponents library using Material Design.",
5
5
  "main": "dist/yrframe.js",
6
6
  "scripts": {
@@ -20,6 +20,6 @@
20
20
  "typescript": "^5.3.3"
21
21
  },
22
22
  "dependencies": {
23
- "@lordfokas/event-bus": "^1.0.4"
23
+ "@lordfokas/event-bus": "^1.0.22"
24
24
  }
25
25
  }