saasufy-components 1.11.0 → 1.13.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.
package/input-provider.js CHANGED
@@ -1,4 +1,4 @@
1
- import { debouncer } from './utils.js';
1
+ import { debouncer, updateConsumerElements } from './utils.js';
2
2
 
3
3
  const DEFAULT_DEBOUNCE_DELAY = 800;
4
4
 
@@ -113,52 +113,6 @@ class InputProvider extends HTMLElement {
113
113
  };
114
114
  }
115
115
 
116
- updateConsumerElements(consumers, value) {
117
- let parentElement = this.parentElement;
118
- if (parentElement) {
119
- let consumerParts = consumers.split(',')
120
- .filter(part => part)
121
- .map(part => {
122
- part = part.trim();
123
- return part.split(':').map(subPart => subPart.trim());
124
- })
125
- .filter(([ selector, attributeName ]) => selector);
126
-
127
- for (let [ selector, attributeName ] of consumerParts) {
128
- let matchingElements = parentElement.querySelectorAll(selector);
129
- if (attributeName) {
130
- for (let element of matchingElements) {
131
- if (typeof value === 'boolean') {
132
- if (value) {
133
- element.setAttribute(attributeName, '');
134
- } else {
135
- element.removeAttribute(attributeName);
136
- }
137
- } else {
138
- element.setAttribute(attributeName, value);
139
- }
140
- }
141
- } else {
142
- for (let element of matchingElements) {
143
- if (element.nodeName === 'INPUT') {
144
- if (element.type === 'checkbox') {
145
- if (value) {
146
- element.setAttribute('checked', '');
147
- } else {
148
- element.removeAttribute('checked');
149
- }
150
- } else {
151
- element.value = value;
152
- }
153
- } else {
154
- element.innerHTML = value;
155
- }
156
- }
157
- }
158
- }
159
- }
160
- }
161
-
162
116
  updateConsumerElementsOnEdit() {
163
117
  let consumers = this.getAttribute('consumers');
164
118
  if (!consumers) return;
@@ -175,16 +129,16 @@ class InputProvider extends HTMLElement {
175
129
  if (inputElement.type === 'checkbox') {
176
130
  let checked = !!inputElement.checked;
177
131
  this.lastValue = checked;
178
- this.updateConsumerElements(consumers, checked);
132
+ updateConsumerElements(this.parentElement, consumers, checked);
179
133
  return;
180
134
  }
181
135
  this.lastValue = event.target.value;
182
136
  if (event.target.value === '') {
183
- this.updateConsumerElements(consumers, '');
137
+ updateConsumerElements(this.parentElement, consumers, '');
184
138
  } else {
185
139
  let targetValue = inputElement.type === 'number' ?
186
140
  Number(event.target.value) : event.target.value;
187
- this.updateConsumerElements(consumers, targetValue);
141
+ updateConsumerElements(this.parentElement, consumers, targetValue);
188
142
  }
189
143
  }, debounceDelay);
190
144
  };
@@ -197,11 +151,11 @@ class InputProvider extends HTMLElement {
197
151
  if (event.target.value === this.lastValue) return;
198
152
  this.lastValue = event.target.value;
199
153
  if (event.target.value === '') {
200
- this.updateConsumerElements(consumers, '');
154
+ updateConsumerElements(this.parentElement, consumers, '');
201
155
  } else {
202
156
  let targetValue = inputElement.type === 'number' ?
203
157
  Number(event.target.value) : event.target.value;
204
- this.updateConsumerElements(consumers, targetValue);
158
+ updateConsumerElements(this.parentElement, consumers, targetValue);
205
159
  }
206
160
  }, debounceDelay);
207
161
  };
package/model-input.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SocketConsumer } from './socket.js';
2
- import { debouncer } from './utils.js';
2
+ import { debouncer, updateConsumerElements } from './utils.js';
3
3
  import AGModel from '/node_modules/ag-model/ag-model.js';
4
4
 
5
5
  class ModelInput extends SocketConsumer {
@@ -25,11 +25,13 @@ class ModelInput extends SocketConsumer {
25
25
  'model-instance-property',
26
26
  'type',
27
27
  'placeholder',
28
+ 'consumers',
28
29
  'show-error-message',
29
30
  'model-type',
30
31
  'model-id',
31
32
  'model-field',
32
33
  'debounce-delay',
34
+ 'enable-rebound',
33
35
  'options',
34
36
  'height',
35
37
  'hide-error-logs'
@@ -74,23 +76,34 @@ class ModelInput extends SocketConsumer {
74
76
  let options = this.getAttribute('options');
75
77
  let height = this.getAttribute('height');
76
78
  let hideErrorLogs = this.hasAttribute('hide-error-logs');
79
+ let enableRebound = this.hasAttribute('enable-rebound');
77
80
  let currentNode = this.parentNode;
78
81
  let model;
79
82
  let isModelLocal = false;
80
83
  if (modelInstanceProperty) {
81
84
  while (currentNode) {
82
85
  model = currentNode[modelInstanceProperty];
86
+ if (model && modelType && (model.type !== modelType || !(model.fields || []).includes(modelField))) {
87
+ model = null;
88
+ }
83
89
  if (model) break;
84
90
  currentNode = currentNode.getRootNode().host || currentNode.parentNode;
85
91
  }
86
- if (!model) return;
92
+ if (!model) {
93
+ throw new Error(
94
+ `The ${
95
+ this.nodeName.toLowerCase()
96
+ } element failed to obtain a model via the specified model-instance-property - Ensure that the element is nested inside a parent element which exposes a model instance of the same type which has the relevant field`
97
+ );
98
+ };
87
99
  } else {
88
100
  this.socket = this.getSocket();
89
101
  model = new AGModel({
90
102
  socket: this.socket,
91
103
  type: modelType,
92
104
  id: modelId,
93
- fields: [ modelField ]
105
+ fields: [ modelField ],
106
+ enableRebound
94
107
  });
95
108
  if (!hideErrorLogs) {
96
109
  (async () => {
@@ -167,25 +180,34 @@ class ModelInput extends SocketConsumer {
167
180
 
168
181
  syncInputElementWithModel(inputElement, model, fieldName, messageContainerElement) {
169
182
  if (model.isLoaded) {
183
+ let consumers = this.getAttribute('consumers');
170
184
  let fieldValue = model.value[fieldName];
171
185
  if (inputElement.type === 'checkbox') {
172
- inputElement.checked = !!fieldValue;
186
+ let checked = !!fieldValue;
187
+ inputElement.checked = checked;
188
+ updateConsumerElements(this.parentElement, consumers, checked);
173
189
  } else {
174
190
  let defaultValue = this.getAttribute('default-value') || '';
175
191
  inputElement.value = fieldValue == null ? defaultValue : fieldValue;
192
+ updateConsumerElements(this.parentElement, consumers, inputElement.value);
176
193
  }
177
194
  }
178
195
  let stopSaving = this.saveInputElementOnEdit(inputElement, model, fieldName, messageContainerElement);
179
196
  let changeConsumer = model.listener('change').createConsumer();
180
197
  (async () => {
181
198
  for await (let event of changeConsumer) {
182
- if (event.resourceField !== fieldName || !event.isRemote) continue;
199
+ let hasFocus = document.activeElement === this.inputElement;
200
+ if (event.resourceField !== fieldName || (hasFocus && !event.isRemote)) continue;
201
+ let consumers = this.getAttribute('consumers');
183
202
  let fieldValue = model.value[fieldName];
184
203
  if (inputElement.type === 'checkbox') {
185
- inputElement.checked = !!fieldValue;
204
+ let checked = !!fieldValue;
205
+ inputElement.checked = checked;
206
+ updateConsumerElements(this.parentElement, consumers, checked);
186
207
  } else {
187
208
  let defaultValue = this.getAttribute('default-value') || '';
188
209
  inputElement.value = fieldValue == null ? defaultValue : fieldValue;
210
+ updateConsumerElements(this.parentElement, consumers, inputElement.value);
189
211
  }
190
212
  }
191
213
  })();
@@ -212,6 +234,7 @@ class ModelInput extends SocketConsumer {
212
234
  messageContainerElement.classList.add('hidden');
213
235
  };
214
236
 
237
+ let consumers = this.getAttribute('consumers');
215
238
  let debounceDelay = this.getAttribute('debounce-delay');
216
239
  debounceDelay = debounceDelay ? Number(debounceDelay) : null;
217
240
  let debounce = debouncer();
@@ -221,17 +244,21 @@ class ModelInput extends SocketConsumer {
221
244
  debounce(async () => {
222
245
  try {
223
246
  if (inputElement.type === 'checkbox') {
224
- await model.update(fieldName, !!inputElement.checked);
247
+ let checked = !!inputElement.checked;
248
+ updateConsumerElements(this.parentElement, consumers, checked);
249
+ await model.update(fieldName, checked);
225
250
  inputElement.classList.remove(errorStyleClass);
226
251
  inputElement.classList.add(successStyleClass);
227
252
  hideErrorMessage(fieldName);
228
253
  return;
229
254
  }
230
255
  if (event.target.value === '') {
256
+ updateConsumerElements(this.parentElement, consumers, '');
231
257
  await model.delete(fieldName);
232
258
  } else {
233
259
  let targetValue = inputElement.type === 'number' ?
234
260
  Number(event.target.value) : event.target.value;
261
+ updateConsumerElements(this.parentElement, consumers, targetValue);
235
262
  await model.update(fieldName, targetValue);
236
263
  }
237
264
  inputElement.classList.remove(errorStyleClass);
@@ -253,9 +280,11 @@ class ModelInput extends SocketConsumer {
253
280
  try {
254
281
  if (event.target.value === '') {
255
282
  await model.delete(fieldName);
283
+ updateConsumerElements(this.parentElement, consumers, '');
256
284
  } else {
257
285
  let targetValue = inputElement.type === 'number' ?
258
- Number(event.target.value) : event.target.value;
286
+ Number(event.target.value) : event.target.value;
287
+ updateConsumerElements(this.parentElement, consumers, targetValue);
259
288
  await model.update(fieldName, targetValue);
260
289
  }
261
290
  inputElement.classList.remove(errorStyleClass);
package/model-text.js CHANGED
@@ -42,10 +42,19 @@ class ModelText extends SocketConsumer {
42
42
  if (modelInstanceProperty) {
43
43
  while (currentNode) {
44
44
  model = currentNode[modelInstanceProperty];
45
+ if (model && modelType && (model.type !== modelType || !(model.fields || []).includes(modelField))) {
46
+ model = null;
47
+ }
45
48
  if (model) break;
46
49
  currentNode = currentNode.getRootNode().host || currentNode.parentNode;
47
50
  }
48
- if (!model) return;
51
+ if (!model) {
52
+ throw new Error(
53
+ `The ${
54
+ this.nodeName.toLowerCase()
55
+ } element failed to obtain a model via the specified model-instance-property - Ensure that the element is nested inside a parent element which exposes a model instance of the same type which has the relevant field`
56
+ );
57
+ };
49
58
  } else {
50
59
  this.socket = this.getSocket();
51
60
  model = new AGModel({
@@ -79,7 +88,7 @@ class ModelText extends SocketConsumer {
79
88
  let changeConsumer = model.listener('change').createConsumer();
80
89
  (async () => {
81
90
  for await (let event of changeConsumer) {
82
- if (event.resourceField !== modelField || !event.isRemote) continue;
91
+ if (event.resourceField !== modelField) continue;
83
92
  let fieldValue = toSafeHTML(model.value[modelField]);
84
93
  this.innerHTML = fieldValue;
85
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,8 +17,8 @@
17
17
  "author": "Jonathan Gros-Dubois",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "ag-collection": "^3.3.0",
21
- "ag-model": "^1.4.1",
20
+ "ag-collection": "^3.4.0",
21
+ "ag-model": "^1.5.0",
22
22
  "capitalisk-auth-client": "^1.5.1",
23
23
  "socketcluster-client": "^19.1.0"
24
24
  },
package/socket.js CHANGED
@@ -123,7 +123,7 @@ export class SocketConsumer extends HTMLElement {
123
123
  throw new Error(
124
124
  `The ${
125
125
  this.nodeName.toLowerCase()
126
- } element failed to obtain a socket - Make sure it is nested inside a socket-provider element`
126
+ } element failed to obtain a socket - Ensure that it is nested inside a socket-provider element`
127
127
  );
128
128
  }
129
129
  return socket;
package/utils.js CHANGED
@@ -223,6 +223,51 @@ export function renderTemplate(templateString, data, socket) {
223
223
  });
224
224
  }
225
225
 
226
+ export function updateConsumerElements(parentElement, consumers, value) {
227
+ if (parentElement && consumers) {
228
+ let consumerParts = consumers.split(',')
229
+ .filter(part => part)
230
+ .map(part => {
231
+ part = part.trim();
232
+ return part.split(':').map(subPart => subPart.trim());
233
+ })
234
+ .filter(([ selector, attributeName ]) => selector);
235
+
236
+ for (let [ selector, attributeName ] of consumerParts) {
237
+ let matchingElements = parentElement.querySelectorAll(selector);
238
+ if (attributeName) {
239
+ for (let element of matchingElements) {
240
+ if (typeof value === 'boolean') {
241
+ if (value) {
242
+ element.setAttribute(attributeName, '');
243
+ } else {
244
+ element.removeAttribute(attributeName);
245
+ }
246
+ } else {
247
+ element.setAttribute(attributeName, value);
248
+ }
249
+ }
250
+ } else {
251
+ for (let element of matchingElements) {
252
+ if (element.nodeName === 'INPUT') {
253
+ if (element.type === 'checkbox') {
254
+ if (value) {
255
+ element.setAttribute('checked', '');
256
+ } else {
257
+ element.removeAttribute('checked');
258
+ }
259
+ } else {
260
+ element.value = value;
261
+ }
262
+ } else {
263
+ element.innerHTML = value;
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+
226
271
  export function wait(duration) {
227
272
  return new Promise((resolve) => setTimeout(resolve, duration));
228
273
  }