@rettangoli/fe 1.1.0 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/fe",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Frontend framework for building reactive web components",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -7,7 +7,7 @@ import {
7
7
  cleanupEventRateLimitState,
8
8
  syncRefIds,
9
9
  } from "./componentRuntime.js";
10
- import { buildOnUpdateChanges } from "./lifecycle.js";
10
+ import { buildOnPropUpdateChanges, buildOnUpdateChanges } from "./lifecycle.js";
11
11
  import { normalizeAttributeValue, toCamelCase } from "./props.js";
12
12
 
13
13
  export const createRuntimeDepsForInstance = ({ instance }) => {
@@ -112,6 +112,39 @@ export const runAttributeChangedComponentLifecycle = ({
112
112
  });
113
113
  };
114
114
 
115
+ export const runPropChangedComponentLifecycle = ({
116
+ instance,
117
+ propName,
118
+ oldValue,
119
+ newValue,
120
+ scheduleFrameFn,
121
+ }) => {
122
+ if (oldValue === newValue || !instance.render) {
123
+ return;
124
+ }
125
+
126
+ if (instance.isConnected === false || !instance.renderTarget) {
127
+ return;
128
+ }
129
+
130
+ if (instance.handlers?.handleOnUpdate) {
131
+ const runtimeDeps = createRuntimeDepsForInstance({ instance });
132
+ const changes = buildOnPropUpdateChanges({
133
+ propName,
134
+ oldValue,
135
+ newValue,
136
+ deps: runtimeDeps,
137
+ propsSchemaKeys: instance._propsSchemaKeys,
138
+ });
139
+ instance.handlers.handleOnUpdate(runtimeDeps, changes);
140
+ return;
141
+ }
142
+
143
+ scheduleFrameFn(() => {
144
+ instance.render();
145
+ });
146
+ };
147
+
115
148
  export const runRenderComponentLifecycle = ({
116
149
  instance,
117
150
  createComponentUpdateHookFn,
@@ -122,3 +122,42 @@ export const buildOnUpdateChanges = ({
122
122
  newProps,
123
123
  };
124
124
  };
125
+
126
+ export const buildOnPropUpdateChanges = ({
127
+ propName,
128
+ oldValue,
129
+ newValue,
130
+ deps,
131
+ propsSchemaKeys,
132
+ }) => {
133
+ const newProps = {};
134
+
135
+ propsSchemaKeys.forEach((propKey) => {
136
+ const propValue = deps.props[propKey];
137
+ if (propValue !== undefined) {
138
+ newProps[propKey] = propValue;
139
+ }
140
+ });
141
+
142
+ const oldProps = {
143
+ ...newProps,
144
+ };
145
+
146
+ if (oldValue === undefined) {
147
+ delete oldProps[propName];
148
+ } else {
149
+ oldProps[propName] = oldValue;
150
+ }
151
+
152
+ if (newValue === undefined) {
153
+ delete newProps[propName];
154
+ } else {
155
+ newProps[propName] = newValue;
156
+ }
157
+
158
+ return {
159
+ changedProp: propName,
160
+ oldProps,
161
+ newProps,
162
+ };
163
+ };
@@ -28,6 +28,81 @@ export const readPropFallbackFromAttributes = (source, propName) => {
28
28
  return undefined;
29
29
  };
30
30
 
31
+ const REACTIVE_PROP_VALUES = Symbol("rtglReactivePropValues");
32
+
33
+ const ensureReactivePropValues = (source) => {
34
+ if (!Object.prototype.hasOwnProperty.call(source, REACTIVE_PROP_VALUES)) {
35
+ Object.defineProperty(source, REACTIVE_PROP_VALUES, {
36
+ value: Object.create(null),
37
+ enumerable: false,
38
+ configurable: false,
39
+ writable: false,
40
+ });
41
+ }
42
+
43
+ return source[REACTIVE_PROP_VALUES];
44
+ };
45
+
46
+ export const installReactiveProps = ({
47
+ source,
48
+ allowedKeys = [],
49
+ onPropChange,
50
+ }) => {
51
+ const reactiveValues = ensureReactivePropValues(source);
52
+
53
+ allowedKeys.forEach((propName) => {
54
+ if (typeof propName !== "string" || propName.length === 0) {
55
+ return;
56
+ }
57
+
58
+ const presetValue = Object.prototype.hasOwnProperty.call(source, propName)
59
+ ? source[propName]
60
+ : undefined;
61
+ const hadPresetValue = Object.prototype.hasOwnProperty.call(source, propName);
62
+
63
+ if (hadPresetValue) {
64
+ delete source[propName];
65
+ }
66
+
67
+ Object.defineProperty(source, propName, {
68
+ configurable: true,
69
+ enumerable: true,
70
+ get() {
71
+ if (Object.prototype.hasOwnProperty.call(reactiveValues, propName)) {
72
+ return reactiveValues[propName];
73
+ }
74
+
75
+ return readPropFallbackFromAttributes(source, propName);
76
+ },
77
+ set(value) {
78
+ const oldValue = Object.prototype.hasOwnProperty.call(reactiveValues, propName)
79
+ ? reactiveValues[propName]
80
+ : readPropFallbackFromAttributes(source, propName);
81
+
82
+ if (value === undefined) {
83
+ delete reactiveValues[propName];
84
+ } else {
85
+ reactiveValues[propName] = value;
86
+ }
87
+
88
+ if (oldValue === value) {
89
+ return;
90
+ }
91
+
92
+ onPropChange?.({
93
+ propName,
94
+ oldValue,
95
+ newValue: value,
96
+ });
97
+ },
98
+ });
99
+
100
+ if (hadPresetValue) {
101
+ reactiveValues[propName] = presetValue;
102
+ }
103
+ });
104
+ };
105
+
31
106
  export const createPropsProxy = (source, allowedKeys) => {
32
107
  const allowed = new Set(allowedKeys);
33
108
  return new Proxy(
@@ -7,9 +7,10 @@ import {
7
7
  runAttributeChangedComponentLifecycle,
8
8
  runConnectedComponentLifecycle,
9
9
  runDisconnectedComponentLifecycle,
10
+ runPropChangedComponentLifecycle,
10
11
  runRenderComponentLifecycle,
11
12
  } from "../core/runtime/componentOrchestrator.js";
12
- import { createPropsProxy, toKebabCase } from "../core/runtime/props.js";
13
+ import { createPropsProxy, installReactiveProps, toKebabCase } from "../core/runtime/props.js";
13
14
  import {
14
15
  buildObservedAttributes,
15
16
  } from "../core/runtime/componentRuntime.js";
@@ -120,6 +121,19 @@ export const createWebComponentClass = ({
120
121
  fileConstants: constants,
121
122
  });
122
123
  this.propsSchema = propsSchema;
124
+ installReactiveProps({
125
+ source: this,
126
+ allowedKeys: propsSchemaKeys,
127
+ onPropChange: ({ propName, oldValue, newValue }) => {
128
+ runPropChangedComponentLifecycle({
129
+ instance: this,
130
+ propName,
131
+ oldValue,
132
+ newValue,
133
+ scheduleFrameFn: scheduleFrame,
134
+ });
135
+ },
136
+ });
123
137
  this.props = propsSchema
124
138
  ? createPropsProxy(this, propsSchemaKeys)
125
139
  : {};