saasufy-components 2.10.0 → 2.12.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 -2
- package/model-input.js +21 -7
- package/model-text.js +15 -6
- package/model-viewer.js +1 -1
- package/package.json +3 -3
- package/switch-group.js +71 -0
- package/utils.js +5 -0
package/README.md
CHANGED
|
@@ -225,12 +225,17 @@ A component which can be used to guarantee that certain children components are
|
|
|
225
225
|
|
|
226
226
|
### if-group
|
|
227
227
|
|
|
228
|
-
A
|
|
228
|
+
A component which exposes a `show-content` property which can be set to true or false to show or hide its content.
|
|
229
229
|
It's intended to be placed inside a `model-viewer`, `collection-browser` or `collection-reducer` component such that the true/false value of the `show-content` attribute can be computed using a template `{{expression}}` placeholder. Unlike `conditional-group`, this component requires a template and a viewport to be slotted in. The content of the template will not be processed unless the `show-content` condition is met.
|
|
230
230
|
|
|
231
|
+
### switch-group
|
|
232
|
+
|
|
233
|
+
A component which exposes a `show-cases` property which can be set to key-value pairs in the format `key1=true,key2=false`. It can be used to conditionally display multiple slotted elements based on multiple conditions. It helps to keep HTML clean when the conditions are complex.
|
|
234
|
+
This element is intended to be placed inside a `model-viewer`, `collection-browser` or `collection-reducer` component such that the true/false values of the `show-cases` attribute can be computed using template `{{expression}}` placeholders. This component requires one or more templates and a viewport to be slotted in. The content of the templates will not be processed unless the `show-cases` condition is met. All templates must have a `slot="content"` attribute and a name attribute in the format `name="key1"` where the value `key1` corresponds to the key specified inside the `show-cases` attribute of the `switch-group`.
|
|
235
|
+
|
|
231
236
|
### conditional-group
|
|
232
237
|
|
|
233
|
-
A
|
|
238
|
+
A component which exposes a `show-content` property which can be set to true or false to show or hide its content.
|
|
234
239
|
It's intended to be placed inside a `model-viewer`, `collection-browser` or `collection-reducer` component such that the true/false value of the `show-content` attribute can be computed using a template `{{expression}}` placeholder. Unlike `if-group`, this component does not require a template, however, the trade-off is that the slotted content will be pre-processed by the browser even when the expression is false. This may not be suitable if you want to (for example) render child images conditionally because the browser will preload images based on their `src` attributes, even if they are not shown.
|
|
235
240
|
|
|
236
241
|
### collection-adder-group
|
package/model-input.js
CHANGED
|
@@ -23,6 +23,7 @@ class ModelInput extends SocketConsumer {
|
|
|
23
23
|
'input-id',
|
|
24
24
|
'list',
|
|
25
25
|
'model-instance-property',
|
|
26
|
+
'collection-instance-property',
|
|
26
27
|
'bind-to-model',
|
|
27
28
|
'bind-to-collection',
|
|
28
29
|
'type',
|
|
@@ -36,6 +37,8 @@ class ModelInput extends SocketConsumer {
|
|
|
36
37
|
'enable-rebound',
|
|
37
38
|
'options',
|
|
38
39
|
'height',
|
|
40
|
+
'default-value',
|
|
41
|
+
'value',
|
|
39
42
|
'hide-error-logs',
|
|
40
43
|
'autocapitalize',
|
|
41
44
|
'autocorrect'
|
|
@@ -44,8 +47,12 @@ class ModelInput extends SocketConsumer {
|
|
|
44
47
|
|
|
45
48
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
46
49
|
if (!this.isReady) return;
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
if (name === 'value') {
|
|
51
|
+
this.value = newValue;
|
|
52
|
+
} else {
|
|
53
|
+
this.destroy();
|
|
54
|
+
this.render();
|
|
55
|
+
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
triggerChange() {
|
|
@@ -69,8 +76,9 @@ class ModelInput extends SocketConsumer {
|
|
|
69
76
|
render() {
|
|
70
77
|
this.innerHTML = '';
|
|
71
78
|
let bindToModel = this.hasAttribute('bind-to-model');
|
|
72
|
-
let bindToCollection = this.hasAttribute('bind-to-collection');
|
|
73
79
|
let modelInstanceProperty = this.getAttribute('model-instance-property');
|
|
80
|
+
let bindToCollection = this.hasAttribute('bind-to-collection');
|
|
81
|
+
let collectionInstanceProperty = this.getAttribute('collection-instance-property');
|
|
74
82
|
let showErrorMessage = this.hasAttribute('show-error-message');
|
|
75
83
|
let inputId = this.getAttribute('input-id');
|
|
76
84
|
let autocapitalize = this.getAttribute('autocapitalize');
|
|
@@ -89,13 +97,16 @@ class ModelInput extends SocketConsumer {
|
|
|
89
97
|
let model;
|
|
90
98
|
let isModelLocal = false;
|
|
91
99
|
if (bindToCollection) {
|
|
100
|
+
collectionInstanceProperty = 'collection';
|
|
101
|
+
}
|
|
102
|
+
if (collectionInstanceProperty) {
|
|
92
103
|
let collection;
|
|
93
104
|
while (currentNode) {
|
|
94
|
-
collection = currentNode
|
|
95
|
-
if (collection &&
|
|
105
|
+
collection = currentNode[collectionInstanceProperty];
|
|
106
|
+
if (collection && collection.type !== modelType) {
|
|
96
107
|
collection = null;
|
|
97
108
|
}
|
|
98
|
-
if (collection && (!collection.agModels || !collection.agModels[modelId]
|
|
109
|
+
if (collection && (!collection.agModels || !collection.agModels[modelId])) {
|
|
99
110
|
collection = null;
|
|
100
111
|
}
|
|
101
112
|
if (collection) break;
|
|
@@ -103,6 +114,9 @@ class ModelInput extends SocketConsumer {
|
|
|
103
114
|
}
|
|
104
115
|
if (collection) {
|
|
105
116
|
model = collection.agModels[modelId];
|
|
117
|
+
if (!model.agFields[modelField]) {
|
|
118
|
+
model.addField(modelField);
|
|
119
|
+
}
|
|
106
120
|
}
|
|
107
121
|
}
|
|
108
122
|
if (!model) {
|
|
@@ -126,7 +140,7 @@ class ModelInput extends SocketConsumer {
|
|
|
126
140
|
throw new Error(
|
|
127
141
|
`The ${
|
|
128
142
|
this.nodeName.toLowerCase()
|
|
129
|
-
} element failed to
|
|
143
|
+
} element failed to bind to a model - Ensure that the element is nested inside a parent element which exposes a model instance of the same type with a ${modelField} field`
|
|
130
144
|
);
|
|
131
145
|
};
|
|
132
146
|
} else {
|
package/model-text.js
CHANGED
|
@@ -21,7 +21,9 @@ class ModelText extends SocketConsumer {
|
|
|
21
21
|
static get observedAttributes() {
|
|
22
22
|
return [
|
|
23
23
|
'model-instance-property',
|
|
24
|
+
'collection-instance-property',
|
|
24
25
|
'bind-to-collection',
|
|
26
|
+
'bind-to-model',
|
|
25
27
|
'model-type',
|
|
26
28
|
'model-id',
|
|
27
29
|
'model-field',
|
|
@@ -38,8 +40,9 @@ class ModelText extends SocketConsumer {
|
|
|
38
40
|
render() {
|
|
39
41
|
this.innerHTML = '';
|
|
40
42
|
let bindToModel = this.hasAttribute('bind-to-model');
|
|
41
|
-
let bindToCollection = this.hasAttribute('bind-to-collection');
|
|
42
43
|
let modelInstanceProperty = this.getAttribute('model-instance-property');
|
|
44
|
+
let bindToCollection = this.hasAttribute('bind-to-collection');
|
|
45
|
+
let collectionInstanceProperty = this.getAttribute('collection-instance-property');
|
|
43
46
|
let modelType = this.getAttribute('model-type');
|
|
44
47
|
let modelId = this.getAttribute('model-id');
|
|
45
48
|
let modelField = this.getAttribute('model-field');
|
|
@@ -48,13 +51,16 @@ class ModelText extends SocketConsumer {
|
|
|
48
51
|
let model;
|
|
49
52
|
let isModelLocal = false;
|
|
50
53
|
if (bindToCollection) {
|
|
54
|
+
collectionInstanceProperty = 'collection';
|
|
55
|
+
}
|
|
56
|
+
if (collectionInstanceProperty) {
|
|
51
57
|
let collection;
|
|
52
58
|
while (currentNode) {
|
|
53
|
-
collection = currentNode
|
|
54
|
-
if (collection &&
|
|
59
|
+
collection = currentNode[collectionInstanceProperty];
|
|
60
|
+
if (collection && collection.type !== modelType) {
|
|
55
61
|
collection = null;
|
|
56
62
|
}
|
|
57
|
-
if (collection && (!collection.agModels || !collection.agModels[modelId]
|
|
63
|
+
if (collection && (!collection.agModels || !collection.agModels[modelId])) {
|
|
58
64
|
collection = null;
|
|
59
65
|
}
|
|
60
66
|
if (collection) break;
|
|
@@ -62,6 +68,9 @@ class ModelText extends SocketConsumer {
|
|
|
62
68
|
}
|
|
63
69
|
if (collection) {
|
|
64
70
|
model = collection.agModels[modelId];
|
|
71
|
+
if (!model.agFields[modelField]) {
|
|
72
|
+
model.addField(modelField);
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
if (!model) {
|
|
@@ -85,7 +94,7 @@ class ModelText extends SocketConsumer {
|
|
|
85
94
|
throw new Error(
|
|
86
95
|
`The ${
|
|
87
96
|
this.nodeName.toLowerCase()
|
|
88
|
-
} element failed to
|
|
97
|
+
} element failed to bind to a model - Ensure that the element is nested inside a parent element which exposes a model instance of the same type with a ${modelField} field`
|
|
89
98
|
);
|
|
90
99
|
};
|
|
91
100
|
} else {
|
|
@@ -100,7 +109,7 @@ class ModelText extends SocketConsumer {
|
|
|
100
109
|
(async () => {
|
|
101
110
|
for await (let { error } of model.listener('error')) {
|
|
102
111
|
console.error(
|
|
103
|
-
`Model
|
|
112
|
+
`Model text encountered an error: ${error.message}`
|
|
104
113
|
);
|
|
105
114
|
}
|
|
106
115
|
})();
|
package/model-viewer.js
CHANGED
|
@@ -114,7 +114,7 @@ class ModelViewer extends SocketConsumer {
|
|
|
114
114
|
throw new Error(
|
|
115
115
|
`The ${
|
|
116
116
|
this.nodeName.toLowerCase()
|
|
117
|
-
} 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
|
|
117
|
+
} 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 fields`
|
|
118
118
|
);
|
|
119
119
|
};
|
|
120
120
|
this.model = model;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saasufy-components",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.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": "^4.
|
|
21
|
-
"ag-model": "^2.
|
|
20
|
+
"ag-collection": "^4.1.0",
|
|
21
|
+
"ag-model": "^2.1.0",
|
|
22
22
|
"capitalisk-auth-client": "^1.5.1",
|
|
23
23
|
"socketcluster-client": "^19.1.0"
|
|
24
24
|
},
|
package/switch-group.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { debouncer } from './utils.js';
|
|
2
|
+
|
|
3
|
+
const DEBOUNCE_DELAY = 0;
|
|
4
|
+
|
|
5
|
+
class SwitchGroup extends HTMLElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.isReady = false;
|
|
9
|
+
this.attachShadow({ mode: 'open' });
|
|
10
|
+
this.shadowRoot.innerHTML = '<slot name="content"></slot><slot name="viewport"></slot>';
|
|
11
|
+
|
|
12
|
+
let debounce = debouncer();
|
|
13
|
+
|
|
14
|
+
this.processTemplates = () => {
|
|
15
|
+
let viewportSlot = this.shadowRoot.querySelector('slot[name="viewport"]');
|
|
16
|
+
let viewportNode = viewportSlot.assignedNodes()[0];
|
|
17
|
+
if (!viewportNode) return;
|
|
18
|
+
|
|
19
|
+
let showCases = this.getAttribute('show-cases');
|
|
20
|
+
if (!showCases) return;
|
|
21
|
+
|
|
22
|
+
let caseEntries = showCases.split(',').map((caseString) => {
|
|
23
|
+
let sanitizedCase = caseString.trim();
|
|
24
|
+
return sanitizedCase.split('=').map(part => part.trim());
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let contentList = [];
|
|
28
|
+
|
|
29
|
+
for (let [ caseName, showContent ] of caseEntries) {
|
|
30
|
+
if (
|
|
31
|
+
!showContent ||
|
|
32
|
+
showContent === 'false' ||
|
|
33
|
+
showContent === 'null' ||
|
|
34
|
+
showContent === 'undefined'
|
|
35
|
+
) continue;
|
|
36
|
+
|
|
37
|
+
let contentSlot = this.shadowRoot.querySelector('slot[name="content"]');
|
|
38
|
+
|
|
39
|
+
let contentElements = contentSlot.assignedElements().filter((element) => {
|
|
40
|
+
return element.getAttribute('name') === caseName;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for (let element of contentElements) {
|
|
44
|
+
contentList.push(element.innerHTML);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
viewportNode.innerHTML = contentList.join('');
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
this.debouncedProcessTemplates = debounce(this.processTemplates, DEBOUNCE_DELAY);
|
|
52
|
+
this.shadowRoot.addEventListener('slotchange', this.debouncedProcessTemplates);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static get observedAttributes() {
|
|
56
|
+
return [
|
|
57
|
+
'show-cases'
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
attributeChangedCallback() {
|
|
62
|
+
if (!this.isReady) return;
|
|
63
|
+
this.processTemplates();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
connectedCallback() {
|
|
67
|
+
this.isReady = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
window.customElements.define('switch-group', SwitchGroup);
|
package/utils.js
CHANGED
|
@@ -61,6 +61,9 @@ export function createReactiveCollection(collectionOptions, callback) {
|
|
|
61
61
|
(async () => {
|
|
62
62
|
let changes = {};
|
|
63
63
|
for await (let event of collection.listener('change')) {
|
|
64
|
+
// Ignore change events which originate from this collection instance.
|
|
65
|
+
if (!event.isRemote) continue;
|
|
66
|
+
|
|
64
67
|
if (event.resourceId != null && changes[event.resourceId] !== false) {
|
|
65
68
|
changes[event.resourceId] = event.isRemote;
|
|
66
69
|
}
|
|
@@ -265,6 +268,8 @@ export function updateConsumerElements(parentElement, consumers, value) {
|
|
|
265
268
|
} else {
|
|
266
269
|
element.value = value;
|
|
267
270
|
}
|
|
271
|
+
} else if (element.nodeName === 'MODEL-INPUT') {
|
|
272
|
+
element.value = value;
|
|
268
273
|
} else {
|
|
269
274
|
element.innerHTML = value;
|
|
270
275
|
}
|