saasufy-components 1.3.1 → 1.4.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
@@ -13,7 +13,7 @@ A form component for inserting data into collections.
13
13
 
14
14
  ### collection-browser
15
15
 
16
- Used for rendering collections as lists, tables or other sequences based on a specific view.
16
+ Used for rendering collections as lists, tables or other sequences based on a specific view using a template.
17
17
  Supports pagination by allowing you to specify custom buttons or links to navigate between pages.
18
18
  Can also perform basic CRUD operations such as deleting or creating records.
19
19
 
@@ -21,6 +21,10 @@ Can also perform basic CRUD operations such as deleting or creating records.
21
21
 
22
22
  Used for displaying and editing a field of a model instance in real time.
23
23
 
24
+ ### model-viewer
25
+
26
+ Used for rendering models using a template.
27
+
24
28
  ### confirm-modal
25
29
 
26
30
  A modal component to prompt the user for confirmation before performing sensitive operations.
@@ -90,7 +90,8 @@ class CollectionBrowser extends SocketConsumer {
90
90
  'collection-view',
91
91
  'collection-view-params',
92
92
  'collection-page-size',
93
- 'collection-page-offset'
93
+ 'collection-page-offset',
94
+ 'hide-error-logs'
94
95
  ];
95
96
  }
96
97
 
@@ -148,23 +149,23 @@ class CollectionBrowser extends SocketConsumer {
148
149
 
149
150
  renderList() {
150
151
  if (!this.collection || !this.collection.isLoaded) return;
151
- let itemTemplate = this.shadowRoot.querySelector('slot[name="item"]').assignedNodes()[0];
152
- if (!itemTemplate) return;
153
152
  let viewportNode = this.shadowRoot.querySelector('slot[name="viewport"]').assignedNodes()[0];
154
153
  if (!viewportNode) return;
155
- let items = [];
156
-
157
- for (let modelItem of this.collection.value) {
158
- let itemString = renderTemplate(
159
- itemTemplate.innerHTML,
160
- { [this.collection.type]: modelItem }
161
- );
162
- items.push(itemString);
163
- }
154
+
155
+ let itemTemplate = this.shadowRoot.querySelector('slot[name="item"]').assignedNodes()[0];
164
156
  let noItemTemplate = this.shadowRoot.querySelector('slot[name="no-item"]').assignedNodes()[0];
165
- if (noItemTemplate && !items.length) {
157
+
158
+ if (noItemTemplate && !this.collection.value.length) {
166
159
  viewportNode.innerHTML = noItemTemplate.innerHTML;
167
- } else {
160
+ } else if (itemTemplate) {
161
+ let items = [];
162
+ for (let modelItem of this.collection.value) {
163
+ let itemString = renderTemplate(
164
+ itemTemplate.innerHTML,
165
+ { [this.collection.type]: modelItem }
166
+ );
167
+ items.push(itemString);
168
+ }
168
169
  viewportNode.innerHTML = items.join('');
169
170
  }
170
171
  }
@@ -0,0 +1,114 @@
1
+ import { SocketConsumer } from './socket.js';
2
+ import AGModel from '/node_modules/ag-model/ag-model.js';
3
+ import { renderTemplate } from './utils.js';
4
+
5
+ class ModelViewer extends SocketConsumer {
6
+ constructor() {
7
+ super();
8
+ this.attachShadow({ mode: 'open' });
9
+
10
+ this.handleSlotChangeEvent = () => {
11
+ this.renderItem();
12
+ };
13
+ }
14
+
15
+ connectedCallback() {
16
+ this.isReady = true;
17
+ this.socket = this.getSocket();
18
+ this.shadowRoot.addEventListener('slotchange', this.handleSlotChangeEvent);
19
+ this.render();
20
+ }
21
+
22
+ disconnectedCallback() {
23
+ if (this.model) this.model.destroy();
24
+ this.shadowRoot.removeEventListener('slotchange', this.handleSlotChangeEvent);
25
+ }
26
+
27
+ static get observedAttributes() {
28
+ return [
29
+ 'model-type',
30
+ 'model-id',
31
+ 'model-fields',
32
+ 'hide-error-logs'
33
+ ];
34
+ }
35
+
36
+ attributeChangedCallback(name, oldValue, newValue) {
37
+ if (!this.isReady) return;
38
+ this.render();
39
+ }
40
+
41
+ modelValueExists(modelValue) {
42
+ let { id, ...otherProps } = modelValue;
43
+ return !!Object.values(otherProps).filter(value => value != null).length;
44
+ }
45
+
46
+ renderItem() {
47
+ if (!this.model || !this.model.isLoaded) return;
48
+ let viewportNode = this.shadowRoot.querySelector('slot[name="viewport"]').assignedNodes()[0];
49
+ if (!viewportNode) return;
50
+
51
+ let itemTemplate = this.shadowRoot.querySelector('slot[name="item"]').assignedNodes()[0];
52
+ let modelValue = this.model.value;
53
+
54
+ let noItemTemplate = this.shadowRoot.querySelector('slot[name="no-item"]').assignedNodes()[0];
55
+ if (noItemTemplate && !this.modelValueExists(modelValue)) {
56
+ viewportNode.innerHTML = noItemTemplate.innerHTML;
57
+ } else if (itemTemplate) {
58
+ let itemString = renderTemplate(
59
+ itemTemplate.innerHTML,
60
+ { [this.model.type]: modelValue }
61
+ );
62
+ viewportNode.innerHTML = itemString;
63
+ }
64
+ }
65
+
66
+ render() {
67
+ let modelType = this.getAttribute('model-type');
68
+ let modelId = this.getAttribute('model-id');
69
+ let modelFields = this.getAttribute('model-fields') || '';
70
+ let hideErrorLogs = this.hasAttribute('hide-error-logs');
71
+
72
+ if (this.model) this.model.destroy();
73
+
74
+ this.model = new AGModel({
75
+ socket: this.socket,
76
+ type: modelType,
77
+ id: modelId,
78
+ fields: modelFields.split(',').map(field => field.trim()).filter(field => field)
79
+ });
80
+
81
+ this.shadowRoot.innerHTML = `
82
+ <slot name="item"></slot>
83
+ <slot name="no-item"></slot>
84
+ <slot name="viewport"></slot>
85
+ `;
86
+
87
+ (async () => {
88
+ await this.model.listener('load').once();
89
+ if (!this.modelValueExists(this.model.value)) {
90
+ this.renderItem();
91
+ }
92
+ this.setAttribute('is-loaded', '');
93
+ this.dispatchEvent(new CustomEvent('load', { bubbles: true }));
94
+ })();
95
+
96
+ (async () => {
97
+ for await (let event of this.model.listener('change')) {
98
+ this.renderItem();
99
+ }
100
+ })();
101
+
102
+ if (!hideErrorLogs) {
103
+ (async () => {
104
+ for await (let { error } of this.model.listener('error')) {
105
+ console.error(
106
+ `Model viewer encountered an error: ${error.message}`
107
+ );
108
+ }
109
+ })();
110
+ }
111
+ }
112
+ }
113
+
114
+ window.customElements.define('model-viewer', ModelViewer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {