saasufy-components 3.1.6 → 3.3.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
@@ -226,6 +226,7 @@ Can also perform basic CRUD operations such as deleting or creating records by l
226
226
  - `collection-view-params` (required): Parameters for the view, specified as comma-separated key-value pairs (e.g., key1=value1,key2=value2). These parameters can customize the behavior of the collection view. The keys must match `paramFields` specified in your Saasufy service under the relevant `View`.
227
227
  - `collection-page-size`: Sets how many items from the collection are displayed at once. This is useful for pagination, allowing users to navigate through large sets of data in chunks.
228
228
  - `collection-page-offset`: Indicates the current page offset in the collection's data. It’s like telling the browser which page of data you want to display initially.
229
+ - `collection-get-count`: If this attribute is present, the component will get the record count of the target view. The count can be rendered into the template by prefixing the model name with a dollar sign and accessing the `count` property like this: `{{$MyModelName.count}}`.
229
230
  - `type-alias`: Allows you to provide an alternative name for your `Model` to use when injecting values inside the template. This is useful for situations where you may have multiple `collection-browser` elements and/or `model-viewer` elements of the same type nested inside each other and want to avoid `Model` name clashes in the nested template definitions. For example, if the `type-alias` in the snippet above was set to `SubChat`, then `{{Chat.message}}` would become `{{SubChat.message}}`.
230
231
  - `hide-error-logs`: A flag which, when present, suppresses error logs from being printed to the console.
231
232
  - `max-show-loader`: If this attribute is present, your slotted `loader` element will be shown as often as possible; this includes situations where the collection is merely refreshing itself. It is disabled by default.
@@ -369,6 +370,7 @@ In the example above, the name of the first element can be accessed with `{{Cate
369
370
  - `collection-view-params` (required): Parameters for the view, specified as comma-separated key-value pairs (e.g., key1=value1,key2=value2). These parameters can customize the behavior of the collection view. The keys must match `paramFields` specified in your Saasufy service under the relevant `View`.
370
371
  - `collection-page-size`: Sets how many items from the collection are displayed at once. This is useful for pagination, allowing users to navigate through large sets of data in chunks.
371
372
  - `collection-page-offset`: Indicates the current page offset in the collection's data. It’s like telling the browser which page of data you want to display initially.
373
+ - `collection-get-count`: If this attribute is present, the component will get the record count of the target view. The count can be rendered into the template by prefixing the model name with a dollar sign and accessing the `count` property like this: `{{$MyModelName.count}}`.
372
374
  - `type-alias`: Allows you to provide an alternative name for your `Model` to use when injecting values inside the template. This is useful for situations where you may have multiple `collection-browser` elements and/or `model-viewer` elements of the same type nested inside each other and want to avoid `Model` name clashes in the nested template definitions. For example, if the `type-alias` in the snippet above was set to `SubChat`, then `{{Chat.message}}` would become `{{SubChat.message}}`.
373
375
  - `hide-error-logs`: A flag which, when present, suppresses error logs from being printed to the console.
374
376
 
@@ -402,9 +404,9 @@ Used for displaying and editing a single field of a model instance in real time.
402
404
  - `debounce-delay`: The delay in milliseconds to wait before sending an update to the server. This is useful to batch multiple updates together (as is common when user is typing). Default is 300ms.
403
405
  - `input-id`: Can be used to set an `id` attribute on the inner `input` element.
404
406
  - `list`: If specified, this sets the list attribute of the inner `input` element to provide input suggestions (only works with standard input elements).
405
- - `type`: The type of the input component; can be `text`, `number`, `select`, `textarea` or `checkbox`.
407
+ - `type`: The type of the input component; can be `text`, `number`, `select`, `textarea`, `checkbox` or `file`.
406
408
  - `placeholder`: Can be used to set the placeholder text on the inner input element.
407
- - `consumers`: This allows you to connect this `model-input` to other elements on your page which share the same parent element. It takes a list of selectors with optional attributes to target. For example `.my-input:value` will find all elements with a `my-input` class and update their `value` attributes with the value of the `model-input` component in real-time. You can specify multiple selectors separated by commas such as `.my-input,my-div` - In this case, because attribute names are not specified, values will be injected into the `value` attribute (for input elements) or into the `innerHTML` property (for other kinds of elements). The default attribute/property depends on the element type.
409
+ - `consumers`: This allows you to connect this `model-input` to other elements on your page. It takes a list of selectors with optional attributes to target. For example `.my-input:value` will find all elements with a `my-input` class and update their `value` attributes with the value of the `model-input` component in real-time. You can specify multiple selectors separated by commas such as `.my-input,my-div` - In this case, because attribute names are not specified, values will be injected into the `value` attribute (for input elements) or into the `innerHTML` property (for other kinds of elements). The default attribute/property depends on the element type.
408
410
  - `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers.
409
411
  - `show-error-message`: If this attribute is present, an error message will be displayed above the input if it fails to update the value.
410
412
  - `options`: A comma-separated list of options to provide - This only works if the input type is `select`.
@@ -415,6 +417,7 @@ Used for displaying and editing a single field of a model instance in real time.
415
417
  - `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.
416
418
  - `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.
417
419
  - `enable-rebound`: By default, for efficiency reasons, real-time updates performed via `model-input` components are not sent back to the publishing client; it is usually unnecessary because state changes are shared locally between all components which are bound to the same `socket-provider`. You can add the `enable-rebound` attribute to the `model-input` component to force real-time updates to rebound back to the publisher to support more advanced scenarios. Note that enabling rebound comes with additional performance overheads.
420
+ - `accept`: This attribute should only be used if the `type` is set to `file`; it serves to constrain the types of files which can be selected by the user.
418
421
 
419
422
  ### model-text
420
423
 
@@ -511,7 +514,7 @@ This component can be configured to provide its data to multiple components (con
511
514
  - `list`: If specified, this sets the list attribute of the inner `input` element to provide input suggestions (only works with standard input elements).
512
515
  - `type`: The type of the input component; can be `text`, `number`, `select`, `textarea` or `checkbox`.
513
516
  - `placeholder`: Can be used to set the placeholder text on the inner input element.
514
- - `consumers`: This allows you to connect this `input-provider` to other elements on your page which share the same parent element. It takes a list of selectors with optional attributes to target. For example `.my-input:value` will find all elements with a `my-input` class and update their `value` attributes with the value of the `input-provider` component in real-time. You can specify multiple selectors separated by commas such as `.my-input,my-div` - In this case, because attribute names are not specified, values will be injected into the `value` attribute (for input elements) or into the `innerHTML` property (for other kinds of elements). The default attribute/property depends on the element type.
517
+ - `consumers`: This allows you to connect this `input-provider` to other elements on your page. It takes a list of selectors with optional attributes to target. For example `.my-input:value` will find all elements with a `my-input` class and update their `value` attributes with the value of the `input-provider` component in real-time. You can specify multiple selectors separated by commas such as `.my-input,my-div` - In this case, because attribute names are not specified, values will be injected into the `value` attribute (for input elements) or into the `innerHTML` property (for other kinds of elements). The default attribute/property depends on the element type.
515
518
  - `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers.
516
519
  - `debounce-delay`: The delay in milliseconds to wait before triggering a change event. This is useful to batch multiple updates together (as is common when user is typing). Default is 800ms.
517
520
  - `options`: A comma-separated list of options to provide - This only works if the input type is `select`.
@@ -736,7 +739,7 @@ A component which can be used to group together multiple `collection-adder` comp
736
739
 
737
740
  ```html
738
741
  <!--
739
- This collection-adder-group adds two pre-defined records into a Product
742
+ This collection-adder-group adds multiple pre-defined records into a Product
740
743
  collection using a single button click. This example assumes that the name
741
744
  field is the only required field in the Product model.
742
745
  -->
@@ -103,6 +103,7 @@ class CollectionBrowser extends SocketConsumer {
103
103
  'collection-view-params',
104
104
  'collection-page-size',
105
105
  'collection-page-offset',
106
+ 'collection-get-count',
106
107
  'max-show-loader',
107
108
  'type-alias',
108
109
  'hide-error-logs'
@@ -203,7 +204,10 @@ class CollectionBrowser extends SocketConsumer {
203
204
  for (let modelItem of this.collection.value) {
204
205
  let itemString = renderTemplate(
205
206
  itemTemplate.innerHTML,
206
- { [type]: modelItem },
207
+ {
208
+ [type]: modelItem,
209
+ [`$${type}`]: this.collection.meta
210
+ },
207
211
  this.socket
208
212
  );
209
213
  items.push(itemString);
@@ -222,6 +226,7 @@ class CollectionBrowser extends SocketConsumer {
222
226
  let collectionPageSize = this.getAttribute('collection-page-size');
223
227
  let collectionViewParams = this.getAttribute('collection-view-params') || '';
224
228
  let collectionPageOffset = this.getAttribute('collection-page-offset');
229
+ let collectionGetCount = this.hasAttribute('collection-get-count');
225
230
  let hideErrorLogs = this.hasAttribute('hide-error-logs');
226
231
  let collectionReloadDelay = Number(
227
232
  this.getAttribute('collection-reload-delay') || DEFAULT_RELOAD_DELAY
@@ -239,7 +244,8 @@ class CollectionBrowser extends SocketConsumer {
239
244
  viewParams,
240
245
  pageSize: Number(collectionPageSize || 10),
241
246
  pageOffset: Number(collectionPageOffset || 0),
242
- changeReloadDelay: collectionReloadDelay
247
+ changeReloadDelay: collectionReloadDelay,
248
+ getCount: collectionGetCount
243
249
  });
244
250
 
245
251
  this.shadowRoot.innerHTML = `
@@ -74,6 +74,7 @@ class CollectionReducer extends SocketConsumer {
74
74
  'collection-view-params',
75
75
  'collection-page-size',
76
76
  'collection-page-offset',
77
+ 'collection-get-count',
77
78
  'max-show-loader',
78
79
  'type-alias',
79
80
  'hide-error-logs'
@@ -165,7 +166,7 @@ class CollectionReducer extends SocketConsumer {
165
166
  let type = this.getAttribute('type-alias') || this.collection.type;
166
167
  let itemString = renderTemplate(
167
168
  itemTemplate.innerHTML,
168
- { [type]: this.collection.value },
169
+ { [type]: this.collection.value, [`$${type}`]: this.collection.meta },
169
170
  this.socket
170
171
  );
171
172
  viewportNode.innerHTML = itemString;
@@ -179,6 +180,7 @@ class CollectionReducer extends SocketConsumer {
179
180
  let collectionPageSize = this.getAttribute('collection-page-size');
180
181
  let collectionViewParams = this.getAttribute('collection-view-params') || '';
181
182
  let collectionPageOffset = this.getAttribute('collection-page-offset');
183
+ let collectionGetCount = this.hasAttribute('collection-get-count');
182
184
  let hideErrorLogs = this.hasAttribute('hide-error-logs');
183
185
  let collectionReloadDelay = Number(
184
186
  this.getAttribute('collection-reload-delay') || DEFAULT_RELOAD_DELAY
@@ -196,7 +198,8 @@ class CollectionReducer extends SocketConsumer {
196
198
  viewParams,
197
199
  pageSize: Number(collectionPageSize || 10),
198
200
  pageOffset: Number(collectionPageOffset || 0),
199
- changeReloadDelay: collectionReloadDelay
201
+ changeReloadDelay: collectionReloadDelay,
202
+ getCount: collectionGetCount
200
203
  });
201
204
 
202
205
  this.shadowRoot.innerHTML = `
package/model-input.js CHANGED
@@ -26,6 +26,7 @@ class ModelInput extends SocketConsumer {
26
26
  'socket-instance-property',
27
27
  'type',
28
28
  'placeholder',
29
+ 'accept',
29
30
  'consumers',
30
31
  'show-error-message',
31
32
  'model-type',
@@ -95,6 +96,7 @@ class ModelInput extends SocketConsumer {
95
96
  let inputList = this.getAttribute('list');
96
97
  let type = this.getAttribute('type') || 'text';
97
98
  let placeholder = this.getAttribute('placeholder');
99
+ let accept = this.getAttribute('accept');
98
100
  let modelType = this.getAttribute('model-type');
99
101
  let modelId = this.getAttribute('model-id');
100
102
  let modelField = this.getAttribute('model-field');
@@ -191,6 +193,9 @@ class ModelInput extends SocketConsumer {
191
193
  if (placeholder) {
192
194
  this.inputElement.setAttribute('placeholder', placeholder);
193
195
  }
196
+ if (accept) {
197
+ this.inputElement.setAttribute('accept', accept);
198
+ }
194
199
  let errorMessageContainer = null;
195
200
  if (showErrorMessage) {
196
201
  errorMessageContainer = document.createElement('div');
@@ -215,8 +220,11 @@ class ModelInput extends SocketConsumer {
215
220
  updateConsumerElements(consumers, checked, providerTemplate);
216
221
  } else {
217
222
  let defaultValue = this.getAttribute('default-value') || '';
218
- inputElement.value = fieldValue == null ? defaultValue : fieldValue;
219
- updateConsumerElements(consumers, inputElement.value, providerTemplate);
223
+ let elementValue = fieldValue == null ? defaultValue : fieldValue;
224
+ if (inputElement.type !== 'file') {
225
+ inputElement.value = elementValue;
226
+ }
227
+ updateConsumerElements(consumers, elementValue, providerTemplate);
220
228
  }
221
229
  }
222
230
 
@@ -278,12 +286,28 @@ class ModelInput extends SocketConsumer {
278
286
  hideErrorMessage(fieldName);
279
287
  return;
280
288
  }
281
- if (event.target.value === '') {
289
+ let inputValue;
290
+ if (inputElement.type === 'file' && inputElement.files && inputElement.files.length) {
291
+ let reader = new FileReader();
292
+ let readerLoadPromise = new Promise((resolve, reject) => {
293
+ reader.addEventListener('load', () => {
294
+ resolve(reader.result);
295
+ });
296
+ reader.addEventListener('error', () => {
297
+ reject(new Error('Failed to read file'));
298
+ });
299
+ });
300
+ reader.readAsDataURL(inputElement.files[0]);
301
+ inputValue = await readerLoadPromise;
302
+ } else {
303
+ inputValue = event.target.value;
304
+ }
305
+ if (inputValue === '') {
282
306
  updateConsumerElements(consumers, '', providerTemplate);
283
307
  await model.delete(fieldName);
284
308
  } else {
285
309
  let targetValue = inputElement.type === 'number' ?
286
- Number(event.target.value) : event.target.value;
310
+ Number(inputValue) : inputValue;
287
311
  updateConsumerElements(consumers, targetValue, providerTemplate);
288
312
  await model.update(fieldName, targetValue);
289
313
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "3.1.6",
3
+ "version": "3.3.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {