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 +7 -4
- package/collection-browser.js +8 -2
- package/collection-reducer.js +5 -2
- package/model-input.js +28 -4
- package/package.json +1 -1
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 `
|
|
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
|
|
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
|
|
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
|
|
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
|
-->
|
package/collection-browser.js
CHANGED
|
@@ -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
|
-
{
|
|
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 = `
|
package/collection-reducer.js
CHANGED
|
@@ -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
|
-
|
|
219
|
-
|
|
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
|
-
|
|
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(
|
|
310
|
+
Number(inputValue) : inputValue;
|
|
287
311
|
updateConsumerElements(consumers, targetValue, providerTemplate);
|
|
288
312
|
await model.update(fieldName, targetValue);
|
|
289
313
|
}
|