saasufy-components 3.24.0 → 3.26.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 +8 -6
- package/model-viewer.js +17 -3
- package/package.json +2 -2
- package/socket.js +32 -17
package/README.md
CHANGED
|
@@ -250,6 +250,7 @@ A Saasufy component which integrates with data from Saasufy is known as a `socke
|
|
|
250
250
|
- `url` (required): Specifies the URL for the Saasufy service to use as the data store for all components which are placed inside it.
|
|
251
251
|
- `auth-token-name`: Allows you to specify a custom key for your token - This will be used as the key in localStorage. You generally do not need to set this attribute. It is intended for situations were an app has multiple `socket-provider` elements connecting different services with different/separate authentication flows.
|
|
252
252
|
- `disable-tab-sync`: By default, the socket provider synchronizes socket auth state across multiple tabs via localStorage. If this attribute is set, then the socket auth state will not sync automatically and a manual page refresh may be necessary to update to the latest auth state.
|
|
253
|
+
- `socket-options`: Can be used to set options on the inner `socket`. Must be in the format `option1:type1=value1,option2:type2=value2`; the type of each option can be string, boolean or number. If not specified, the default type is string.
|
|
253
254
|
|
|
254
255
|
### collection-browser
|
|
255
256
|
|
|
@@ -491,7 +492,7 @@ Used for displaying and editing a single field of a model instance in real time.
|
|
|
491
492
|
- `list`: If specified, this sets the list attribute of the inner `input` element to provide input suggestions (only works with standard input elements).
|
|
492
493
|
- `type`: The type of the input component; can be `text`, `number`, `select`, `textarea`, `checkbox` or `file`.
|
|
493
494
|
- `placeholder`: Can be used to set the placeholder text on the inner input element.
|
|
494
|
-
- `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 in the format `element-selector:attribute-name`. 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
|
|
495
|
+
- `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 in the format `element-selector:attribute-name`. 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. Note that `model-input` elements of the `file` type are treated as write-only unless the `consumers` attribute is present.
|
|
495
496
|
- `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers.
|
|
496
497
|
- `show-error-message`: If this attribute is present, an error message will be displayed above the input if it fails to update the value.
|
|
497
498
|
- `options`: A comma-separated list of options to provide - This only works if the input type is `select`.
|
|
@@ -499,7 +500,7 @@ Used for displaying and editing a single field of a model instance in real time.
|
|
|
499
500
|
- `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.
|
|
500
501
|
- `value`: Used to set the model's value.
|
|
501
502
|
- `computable-value`: If this attribute is present, it will be possible to execute expressions using the `{{expression}}` syntax inside the `value` attribute.
|
|
502
|
-
- `slice-to`: Optional
|
|
503
|
+
- `slice-to`: Optional attribute which can be set to a number to trim strings down to a maximum number of characters. This is useful when dealing with potentially very long field values. Extra care should be taken when using this attribute on a `model-input` element as it will cause values to be overwritten with shortened values if the user edits the input box. Note also that it may affect caching if the same field is being referenced in multiple parts of the application at the same time within the same `socket-provider`.
|
|
503
504
|
- `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.
|
|
504
505
|
- `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.
|
|
505
506
|
- `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.
|
|
@@ -534,7 +535,7 @@ Used to displaying a field of a model instance in real time.
|
|
|
534
535
|
- `model-type` (required): Specifies the type of model to bind to. This should match a `Model` available in your Saasufy service.
|
|
535
536
|
- `model-id` (required): The id of the model instance/record to bind to.
|
|
536
537
|
- `model-field` (required): The field of the model instance/record to bind to for reading.
|
|
537
|
-
- `slice-to`: Optional
|
|
538
|
+
- `slice-to`: Optional attribute which can be set to a number to trim strings down to a maximum number of characters when reading. This is useful when dealing with potentially very long field values. Note that it may affect caching if the same field is being referenced in multiple parts of the application at the same time within the same `socket-provider`.
|
|
538
539
|
- `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.
|
|
539
540
|
|
|
540
541
|
### model-viewer
|
|
@@ -573,6 +574,7 @@ Used for rendering a single model resource using a template. This is the single-
|
|
|
573
574
|
- `model-id` (required): The id of the model instance/record to bind to.
|
|
574
575
|
- `model-fields` (required): A comma-separated list of fields to bind to. Note that unlike with `model-input` and `model-text`, the attribute name is plural and can bind to multiple fields.
|
|
575
576
|
- `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 `model-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 `MyProduct`, then `{{Product.name}}` would become `{{MyProduct.name}}`.
|
|
577
|
+
- `fields-slice-to`: Optional attribute which can be used to trim strings down to a maximum number of characters when reading. The format is `fieldName1=number1,fieldName2=number2`. This is useful when dealing with potentially very long field values. Note that it may affect caching if the same field is being referenced in multiple parts of the application at the same time within the same `socket-provider`.
|
|
576
578
|
- `hide-error-logs`: A flag which, when present, suppresses error logs from being printed to the console.
|
|
577
579
|
|
|
578
580
|
### input-provider
|
|
@@ -604,7 +606,7 @@ This component can be configured to provide its data to multiple components (con
|
|
|
604
606
|
- `list`: If specified, this sets the list attribute of the inner `input` element to provide input suggestions (only works with standard input elements).
|
|
605
607
|
- `type`: The type of the input component; can be `text`, `number`, `select`, `textarea` or `checkbox`.
|
|
606
608
|
- `placeholder`: Can be used to set the placeholder text on the inner input element.
|
|
607
|
-
- `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 in the format `element-selector:attribute-name`. 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
|
|
609
|
+
- `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 in the format `element-selector:attribute-name`. 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.
|
|
608
610
|
- `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers.
|
|
609
611
|
- `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.
|
|
610
612
|
- `options`: A comma-separated list of options to provide - This only works if the input type is `select`.
|
|
@@ -665,7 +667,7 @@ The `combineFilters` function produces a combined query string (joined with `%AN
|
|
|
665
667
|
|
|
666
668
|
**Attributes**
|
|
667
669
|
|
|
668
|
-
- `consumers`: This allows you to connect this `input-combiner` to other elements on your page. It takes a list of selectors with optional attributes to target in the format `element-selector:attribute-name`. 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
|
|
670
|
+
- `consumers`: This allows you to connect this `input-combiner` to other elements on your page. It takes a list of selectors with optional attributes to target in the format `element-selector:attribute-name`. 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.
|
|
669
671
|
- `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers. In the case of the `input-combiner`, the value is an object wherein each key represents the unique label associated with different `input-provider` components.
|
|
670
672
|
- `debounce-delay`: The delay in milliseconds to wait before triggering a change event. This is useful to batch multiple updates together. Default is 0ms.
|
|
671
673
|
|
|
@@ -705,7 +707,7 @@ The following example shows how to bind an `input-provider` component to an `inp
|
|
|
705
707
|
|
|
706
708
|
**Attributes**
|
|
707
709
|
|
|
708
|
-
- `consumers`: This allows you to connect this `input-transformer` to other elements on your page. It takes a list of selectors with optional attributes to target in the format `element-selector:attribute-name`. 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
|
|
710
|
+
- `consumers`: This allows you to connect this `input-transformer` to other elements on your page. It takes a list of selectors with optional attributes to target in the format `element-selector:attribute-name`. 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.
|
|
709
711
|
- `provider-template`: A template string within which the `{{value}}` can be injected before passing to consumers.
|
|
710
712
|
- `debounce-delay`: The delay in milliseconds to wait before triggering a change event. This is useful to batch multiple updates together. Default is 0ms.
|
|
711
713
|
|
package/model-viewer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SocketConsumer } from './socket.js';
|
|
2
2
|
import AGModel from '/node_modules/ag-model/ag-model.js';
|
|
3
|
-
import { renderTemplate } from './utils.js';
|
|
3
|
+
import { renderTemplate, convertStringToFieldParams } from './utils.js';
|
|
4
4
|
|
|
5
5
|
class ModelViewer extends SocketConsumer {
|
|
6
6
|
constructor() {
|
|
@@ -30,9 +30,11 @@ class ModelViewer extends SocketConsumer {
|
|
|
30
30
|
|
|
31
31
|
static get observedAttributes() {
|
|
32
32
|
return [
|
|
33
|
+
'socket-instance-property',
|
|
33
34
|
'model-type',
|
|
34
35
|
'model-id',
|
|
35
36
|
'model-fields',
|
|
37
|
+
'fields-slice-to',
|
|
36
38
|
'type-alias',
|
|
37
39
|
'hide-error-logs'
|
|
38
40
|
];
|
|
@@ -85,12 +87,22 @@ class ModelViewer extends SocketConsumer {
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
render() {
|
|
90
|
+
let socketInstanceProperty = this.getAttribute('socket-instance-property');
|
|
88
91
|
let modelType = this.getAttribute('model-type');
|
|
89
92
|
let modelId = this.getAttribute('model-id');
|
|
90
93
|
let modelFields = this.getAttribute('model-fields') || '';
|
|
91
|
-
let
|
|
94
|
+
let fieldsSliceTo = this.getAttribute('fields-slice-to') || '';
|
|
92
95
|
let hideErrorLogs = this.hasAttribute('hide-error-logs');
|
|
93
96
|
|
|
97
|
+
let {
|
|
98
|
+
fieldValues: sliceToMap
|
|
99
|
+
} = convertStringToFieldParams(fieldsSliceTo);
|
|
100
|
+
|
|
101
|
+
let fieldTransformations = {};
|
|
102
|
+
for (let [ fieldName, sliceTo ] of Object.entries(sliceToMap)) {
|
|
103
|
+
fieldTransformations[fieldName] = { sliceTo: Number(sliceTo) };
|
|
104
|
+
}
|
|
105
|
+
|
|
94
106
|
if (this.model) this.model.destroy();
|
|
95
107
|
|
|
96
108
|
let socket;
|
|
@@ -114,11 +126,13 @@ class ModelViewer extends SocketConsumer {
|
|
|
114
126
|
socket = this.getSocket();
|
|
115
127
|
}
|
|
116
128
|
this.socket = socket;
|
|
129
|
+
|
|
117
130
|
this.model = new AGModel({
|
|
118
131
|
socket: this.socket,
|
|
119
132
|
type: modelType,
|
|
120
133
|
id: modelId,
|
|
121
|
-
fields: modelFields.split(',').map(field => field.trim()).filter(field => field)
|
|
134
|
+
fields: modelFields.split(',').map(field => field.trim()).filter(field => field),
|
|
135
|
+
fieldTransformations
|
|
122
136
|
});
|
|
123
137
|
|
|
124
138
|
this.shadowRoot.innerHTML = `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saasufy-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.26.0",
|
|
4
4
|
"description": "Components for Saasufy.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"ag-collection": "^5.1.0",
|
|
21
21
|
"ag-model": "^3.1.0",
|
|
22
22
|
"capitalisk-auth-client": "^1.5.1",
|
|
23
|
-
"socketcluster-client": "^19.1.
|
|
23
|
+
"socketcluster-client": "^19.1.1",
|
|
24
24
|
"uuid": "^8.3.2"
|
|
25
25
|
},
|
|
26
26
|
"bugs": {
|
package/socket.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { create } from '/node_modules/socketcluster-client/socketcluster-client.min.js';
|
|
2
|
+
import { convertStringToFieldParams } from './utils.js';
|
|
2
3
|
|
|
3
4
|
let globalSocket;
|
|
4
5
|
|
|
@@ -21,23 +22,12 @@ export class SocketProvider extends HTMLElement {
|
|
|
21
22
|
super();
|
|
22
23
|
this.saasufySocket = null;
|
|
23
24
|
this.isReady = false;
|
|
25
|
+
this.lastExtraSocketOptionsString = '';
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
connectedCallback() {
|
|
27
29
|
this.isReady = true;
|
|
28
30
|
this.getSocket();
|
|
29
|
-
let disableTabSync = this.hasAttribute('disable-tab-sync');
|
|
30
|
-
if (!disableTabSync) {
|
|
31
|
-
if (this.storageChangeWatcher) {
|
|
32
|
-
window.removeEventListener('storage', this.storageChangeWatcher);
|
|
33
|
-
}
|
|
34
|
-
this.storageChangeWatcher = (event) => {
|
|
35
|
-
if (event.storageArea === window.localStorage && event.key === this.saasufySocket.options.authTokenName) {
|
|
36
|
-
this.saasufySocket.reconnect();
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
window.addEventListener('storage', this.storageChangeWatcher);
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
disconnectedCallback() {
|
|
@@ -50,7 +40,9 @@ export class SocketProvider extends HTMLElement {
|
|
|
50
40
|
static get observedAttributes() {
|
|
51
41
|
return [
|
|
52
42
|
'url',
|
|
53
|
-
'auth-token-name'
|
|
43
|
+
'auth-token-name',
|
|
44
|
+
'disable-tab-sync',
|
|
45
|
+
'socket-options'
|
|
54
46
|
];
|
|
55
47
|
}
|
|
56
48
|
|
|
@@ -72,6 +64,7 @@ export class SocketProvider extends HTMLElement {
|
|
|
72
64
|
|
|
73
65
|
getSocketOptions() {
|
|
74
66
|
let authTokenName = this.getAttribute('auth-token-name') || null;
|
|
67
|
+
let socketOptionsString = this.getAttribute('socket-options') || '';
|
|
75
68
|
let url = this.getSanitizedURL();
|
|
76
69
|
let urlOptions;
|
|
77
70
|
let matchedList = url.match(urlPartsRegExp);
|
|
@@ -92,18 +85,24 @@ export class SocketProvider extends HTMLElement {
|
|
|
92
85
|
}
|
|
93
86
|
urlOptions = {};
|
|
94
87
|
}
|
|
88
|
+
let {
|
|
89
|
+
fieldValues: extraSocketOptions
|
|
90
|
+
} = convertStringToFieldParams(socketOptionsString);
|
|
95
91
|
let socketOptions = {
|
|
96
92
|
authTokenName,
|
|
93
|
+
autoConnect: !!url,
|
|
94
|
+
...extraSocketOptions,
|
|
97
95
|
...urlOptions
|
|
98
96
|
};
|
|
99
|
-
socketOptions.autoConnect = !!url;
|
|
100
97
|
return socketOptions;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
getSocket() {
|
|
101
|
+
let socket;
|
|
102
|
+
let socketOptionsString = this.getAttribute('socket-options') || '';
|
|
104
103
|
if (this.saasufySocket) {
|
|
105
104
|
let sanitizedURL = this.getSanitizedURL();
|
|
106
|
-
if (this.lastSaasufySocketURL !== sanitizedURL) {
|
|
105
|
+
if (this.lastSaasufySocketURL !== sanitizedURL || this.lastExtraSocketOptionsString !== socketOptionsString) {
|
|
107
106
|
this.saasufySocket.connect(
|
|
108
107
|
this.getSocketOptions()
|
|
109
108
|
);
|
|
@@ -111,15 +110,31 @@ export class SocketProvider extends HTMLElement {
|
|
|
111
110
|
this.saasufySocket.connect();
|
|
112
111
|
}
|
|
113
112
|
this.lastSaasufySocketURL = sanitizedURL;
|
|
114
|
-
|
|
113
|
+
this.lastExtraSocketOptionsString = socketOptionsString;
|
|
114
|
+
socket = this.saasufySocket;
|
|
115
|
+
} else {
|
|
116
|
+
socket = this.createSocket();
|
|
117
|
+
}
|
|
118
|
+
if (this.storageChangeWatcher) {
|
|
119
|
+
window.removeEventListener('storage', this.storageChangeWatcher);
|
|
115
120
|
}
|
|
116
|
-
|
|
121
|
+
let disableTabSync = this.hasAttribute('disable-tab-sync');
|
|
122
|
+
if (!disableTabSync) {
|
|
123
|
+
this.storageChangeWatcher = (event) => {
|
|
124
|
+
if (event.storageArea === window.localStorage && event.key === this.saasufySocket.options.authTokenName) {
|
|
125
|
+
this.saasufySocket.reconnect();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
window.addEventListener('storage', this.storageChangeWatcher);
|
|
129
|
+
}
|
|
130
|
+
return socket;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
createSocket() {
|
|
120
134
|
let socketOptions = this.getSocketOptions();
|
|
121
135
|
this.saasufySocket = create(socketOptions);
|
|
122
136
|
this.lastSaasufySocketURL = this.getSanitizedURL();
|
|
137
|
+
this.lastExtraSocketOptionsString = this.getAttribute('socket-options') || '';
|
|
123
138
|
|
|
124
139
|
return this.saasufySocket;
|
|
125
140
|
}
|