saasufy-components 3.9.0 → 3.10.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/README.md CHANGED
@@ -419,6 +419,7 @@ Used for displaying and editing a single field of a model instance in real time.
419
419
  - `height`: Allows you to set the height of the inner `input` element programmatically.
420
420
  - `default-value`: A default value to show the user if the underlying model field's value is null or undefined. Note that setting a default value will not affect the underlying model's value.
421
421
  - `value`: Used to set the model's value.
422
+ - `computable-value`: If this attribute is present, it will be possible to execute expressions using the `{{expression}}` syntax inside the `value` attribute.
422
423
  - `hide-error-logs`: By default, this component will log errors to the console. If set, this attribute will suppress such errors from showing up on the console.
423
424
  - `autocapitalize`: Can be `on` or `off` - It will set the auto-capitalize attribute on the inner input element. This is useful for mobile devices to enable or disable auto-capitalization of the first character which is typed into the input element.
424
425
  - `autocorrect`: Can be `on` or `off` - It will set the auto-correct attribute on the inner input element. This is useful for mobile devices to enable or disable auto-correct.
@@ -526,6 +527,7 @@ This component can be configured to provide its data to multiple components (con
526
527
  - `options`: A comma-separated list of options to provide - This only works if the input type is `select`.
527
528
  - `height`: Allows you to set the height of the inner `input` element programmatically.
528
529
  - `value`: Used to set the input's value.
530
+ - `computable-value`: If this attribute is present, it will be possible to execute expressions using the `{{expression}}` syntax inside the `value` attribute.
529
531
  - `autocapitalize`: Can be `on` or `off` - It will set the auto-capitalize attribute on the inner input element. This is useful for mobile devices to enable or disable auto-capitalization of the first character which is typed into the input element.
530
532
  - `autocorrect`: Can be `on` or `off` - It will set the auto-correct attribute on the inner input element. This is useful for mobile devices to enable or disable auto-correct.
531
533
 
@@ -546,21 +548,26 @@ This component can be configured to provide its data to multiple components (con
546
548
  The following example shows how to bind multiple `input-provider` components to an `input-combiner`.
547
549
  In this example, the `input-combiner` component forwards the combined, formatted values to the `collection-view-params` attribute of a `company-browser` component.
548
550
 
551
+ Note that the `value` inside the `provider-template` attribute of the `input-combiner` is an object with keys that represent the `name` attribute of the different source `input-provider` components.
552
+ The `combineFilters` function produces a combined query string (joined with `%AND%`) by iterating over the `value` object which holds different parts of the query as provided by different `input-provider` components.
553
+
549
554
  ```html
550
555
  <input-provider
551
556
  class="desc-input"
557
+ name="desc"
552
558
  type="text"
553
559
  consumers=".search-combiner"
554
- provider-template="desc:description contains (?i){{value}}"
560
+ provider-template="description contains (?i){{value}}"
555
561
  value=""
556
562
  placeholder="Description filter"
557
563
  ></input-provider>
558
564
 
559
565
  <input-provider
560
566
  class="city-input"
567
+ name="city"
561
568
  type="text"
562
569
  consumers=".search-combiner"
563
- provider-template="city:city contains (?i){{value}}"
570
+ provider-template="city contains (?i){{value}}"
564
571
  value=""
565
572
  placeholder="City filter"
566
573
  ></input-provider>
package/input-provider.js CHANGED
@@ -1,4 +1,8 @@
1
- import { debouncer, updateConsumerElements } from './utils.js';
1
+ import {
2
+ debouncer,
3
+ renderTemplate,
4
+ updateConsumerElements
5
+ } from './utils.js';
2
6
 
3
7
  const DEFAULT_DEBOUNCE_DELAY = 800;
4
8
 
@@ -46,6 +50,9 @@ class InputProvider extends HTMLElement {
46
50
 
47
51
  set value(newValue) {
48
52
  if (this.inputElement) {
53
+ if (newValue && this.hasAttribute('computable-value')) {
54
+ newValue = renderTemplate(newValue);
55
+ }
49
56
  let oldValue = this.inputElement.value;
50
57
  this.inputElement.value = newValue;
51
58
  if (oldValue !== newValue) {
@@ -135,21 +142,22 @@ class InputProvider extends HTMLElement {
135
142
 
136
143
  let onInputChange = (event) => {
137
144
  debounce(async () => {
145
+ let elementName = this.getAttribute('name');
138
146
  let providerTemplate = this.getAttribute('provider-template');
139
147
  if (event.target.value === this.lastValue) return;
140
148
  if (inputElement.type === 'checkbox') {
141
149
  let checked = !!inputElement.checked;
142
150
  this.lastValue = checked;
143
- updateConsumerElements(consumers, checked, providerTemplate);
151
+ updateConsumerElements(consumers, checked, providerTemplate, elementName);
144
152
  return;
145
153
  }
146
154
  this.lastValue = event.target.value;
147
155
  if (event.target.value === '') {
148
- updateConsumerElements(consumers, '', providerTemplate);
156
+ updateConsumerElements(consumers, '', providerTemplate, elementName);
149
157
  } else {
150
158
  let targetValue = inputElement.type === 'number' ?
151
159
  Number(event.target.value) : event.target.value;
152
- updateConsumerElements(consumers, targetValue, providerTemplate);
160
+ updateConsumerElements(consumers, targetValue, providerTemplate, elementName);
153
161
  }
154
162
  }, debounceDelay);
155
163
  };
@@ -159,15 +167,16 @@ class InputProvider extends HTMLElement {
159
167
  if (inputElement.type !== 'checkbox') {
160
168
  onInputKeyUp = async (event) => {
161
169
  debounce(async () => {
170
+ let elementName = this.getAttribute('name');
162
171
  let providerTemplate = this.getAttribute('provider-template');
163
172
  if (event.target.value === this.lastValue) return;
164
173
  this.lastValue = event.target.value;
165
174
  if (event.target.value === '') {
166
- updateConsumerElements(consumers, '', providerTemplate);
175
+ updateConsumerElements(consumers, '', providerTemplate, elementName);
167
176
  } else {
168
177
  let targetValue = inputElement.type === 'number' ?
169
178
  Number(event.target.value) : event.target.value;
170
- updateConsumerElements(consumers, targetValue, providerTemplate);
179
+ updateConsumerElements(consumers, targetValue, providerTemplate, elementName);
171
180
  }
172
181
  }, debounceDelay);
173
182
  };
package/model-input.js CHANGED
@@ -60,6 +60,9 @@ class ModelInput extends SocketConsumer {
60
60
 
61
61
  set value(newValue) {
62
62
  if (this.inputElement) {
63
+ if (newValue && this.hasAttribute('computable-value')) {
64
+ newValue = renderTemplate(newValue);
65
+ }
63
66
  if (this.inputElement.type === 'checkbox') {
64
67
  let oldValue = this.inputElement.checked;
65
68
  this.inputElement.checked = newValue;
@@ -223,14 +226,14 @@ class ModelInput extends SocketConsumer {
223
226
  if (inputElement.type === 'checkbox') {
224
227
  let checked = !!fieldValue;
225
228
  inputElement.checked = checked;
226
- updateConsumerElements(consumers, checked, providerTemplate);
229
+ updateConsumerElements(consumers, checked, providerTemplate, this.getAttribute('name'));
227
230
  } else {
228
231
  let defaultValue = this.getAttribute('default-value') || '';
229
232
  let elementValue = fieldValue == null ? defaultValue : fieldValue;
230
233
  if (inputElement.type !== 'file') {
231
234
  inputElement.value = elementValue;
232
235
  }
233
- updateConsumerElements(consumers, elementValue, providerTemplate);
236
+ updateConsumerElements(consumers, elementValue, providerTemplate, this.getAttribute('name'));
234
237
  }
235
238
  }
236
239
 
@@ -299,7 +302,7 @@ class ModelInput extends SocketConsumer {
299
302
  try {
300
303
  if (inputElement.type === 'checkbox') {
301
304
  let checked = !!inputElement.checked;
302
- updateConsumerElements(consumers, checked, providerTemplate);
305
+ updateConsumerElements(consumers, checked, providerTemplate, this.getAttribute('name'));
303
306
  await model.update(fieldName, checked);
304
307
  inputElement.classList.remove(errorStyleClass);
305
308
  inputElement.classList.add(successStyleClass);
@@ -323,12 +326,12 @@ class ModelInput extends SocketConsumer {
323
326
  inputValue = event.target.value;
324
327
  }
325
328
  if (inputValue === '') {
326
- updateConsumerElements(consumers, '', providerTemplate);
329
+ updateConsumerElements(consumers, '', providerTemplate, this.getAttribute('name'));
327
330
  await model.delete(fieldName);
328
331
  } else {
329
332
  let targetValue = inputElement.type === 'number' ?
330
333
  Number(inputValue) : inputValue;
331
- updateConsumerElements(consumers, targetValue, providerTemplate);
334
+ updateConsumerElements(consumers, targetValue, providerTemplate, this.getAttribute('name'));
332
335
  await model.update(fieldName, targetValue);
333
336
  }
334
337
  inputElement.classList.remove(errorStyleClass);
@@ -353,11 +356,11 @@ class ModelInput extends SocketConsumer {
353
356
  try {
354
357
  if (event.target.value === '') {
355
358
  await model.delete(fieldName);
356
- updateConsumerElements(consumers, '', providerTemplate);
359
+ updateConsumerElements(consumers, '', providerTemplate, this.getAttribute('name'));
357
360
  } else {
358
361
  let targetValue = inputElement.type === 'number' ?
359
362
  Number(event.target.value) : event.target.value;
360
- updateConsumerElements(consumers, targetValue, providerTemplate);
363
+ updateConsumerElements(consumers, targetValue, providerTemplate, this.getAttribute('name'));
361
364
  await model.update(fieldName, targetValue);
362
365
  }
363
366
  inputElement.classList.remove(errorStyleClass);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/utils.js CHANGED
@@ -251,7 +251,7 @@ export function renderTemplate(templateString, data, socket) {
251
251
  });
252
252
  }
253
253
 
254
- export function updateConsumerElements(consumers, value, template) {
254
+ export function updateConsumerElements(consumers, value, template, sourceElementName) {
255
255
  if (consumers) {
256
256
  let consumerParts = consumers.split(',')
257
257
  .filter(part => part)
@@ -297,7 +297,10 @@ export function updateConsumerElements(consumers, value, template) {
297
297
  } else if (element.nodeName === 'MODEL-INPUT') {
298
298
  element.value = value;
299
299
  } else if (element.nodeName === 'INPUT-COMBINER') {
300
- element.setAttribute('value', value);
300
+ element.setAttribute(
301
+ 'value',
302
+ sourceElementName ? `${sourceElementName}:${value}` : value
303
+ );
301
304
  } else {
302
305
  element.innerHTML = value;
303
306
  }