saasufy-components 1.4.0 → 1.5.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.
@@ -0,0 +1,207 @@
1
+ import { debouncer } from './utils.js';
2
+
3
+ const DEFAULT_DEBOUNCE_DELAY = 800;
4
+
5
+ class InputProvider extends HTMLElement {
6
+ constructor() {
7
+ super();
8
+ this.isReady = false;
9
+ this.inputElement = null;
10
+ }
11
+ connectedCallback() {
12
+ this.isReady = true;
13
+ this.destroy = () => {};
14
+ this.render();
15
+ }
16
+
17
+ disconnectedCallback() {
18
+ this.destroy();
19
+ }
20
+
21
+ static get observedAttributes() {
22
+ return [
23
+ 'input-id',
24
+ 'list',
25
+ 'type',
26
+ 'placeholder',
27
+ 'consumers',
28
+ 'debounce-delay',
29
+ 'options',
30
+ 'value',
31
+ 'height'
32
+ ];
33
+ }
34
+
35
+ attributeChangedCallback(name, oldValue, newValue) {
36
+ if (!this.isReady) return;
37
+ this.destroy();
38
+ this.render();
39
+ }
40
+
41
+ triggerChange() {
42
+ this.inputElement && this.inputElement.dispatchEvent(new Event('change'));
43
+ }
44
+
45
+ set value(newValue) {
46
+ if (this.inputElement) {
47
+ let oldValue = this.inputElement.value;
48
+ this.inputElement.value = newValue;
49
+ if (oldValue !== newValue) {
50
+ this.triggerChange();
51
+ }
52
+ }
53
+ }
54
+
55
+ get value() {
56
+ return (this.inputElement && this.inputElement.value) || '';
57
+ }
58
+
59
+ render() {
60
+ this.innerHTML = '';
61
+ let inputId = this.getAttribute('input-id');
62
+ let inputList = this.getAttribute('list');
63
+ let type = this.getAttribute('type') || 'text';
64
+ let placeholder = this.getAttribute('placeholder');
65
+ let options = this.getAttribute('options');
66
+ let height = this.getAttribute('height');
67
+ let value = this.getAttribute('value') || '';
68
+
69
+ let elementType;
70
+ if (type === 'select') {
71
+ elementType = 'select';
72
+ } else if (type === 'textarea') {
73
+ elementType = 'textarea';
74
+ } else {
75
+ elementType = 'input';
76
+ }
77
+ this.inputElement = document.createElement(elementType);
78
+ if (type === 'select') {
79
+ if (options) {
80
+ let optionElements = options
81
+ .split(',')
82
+ .map((option) => {
83
+ return `<option value="${option}">${option}</option>`;
84
+ })
85
+ .join('');
86
+ this.inputElement.innerHTML = optionElements;
87
+ }
88
+ } else if (elementType === 'input') {
89
+ if (inputList) {
90
+ this.inputElement.setAttribute('list', inputList)
91
+ }
92
+ this.inputElement.type = type;
93
+ }
94
+ if (height) {
95
+ this.inputElement.style.height = height;
96
+ }
97
+ if (inputId) {
98
+ this.inputElement.setAttribute('id', inputId);
99
+ }
100
+ if (placeholder) {
101
+ this.inputElement.setAttribute('placeholder', placeholder);
102
+ }
103
+
104
+ this.appendChild(this.inputElement);
105
+
106
+ let destroyHandlers = this.updateConsumerElementsOnEdit();
107
+
108
+ if (value) {
109
+ this.value = value;
110
+ }
111
+ this.destroy = () => {
112
+ destroyHandlers();
113
+ };
114
+ }
115
+
116
+ updateConsumerElements(consumers, value) {
117
+ let parentElement = this.parentElement;
118
+ if (parentElement) {
119
+ let consumerParts = consumers.split(',')
120
+ .filter(part => part)
121
+ .map(part => {
122
+ part = part.trim();
123
+ return part.split(':').map(subPart => subPart.trim());
124
+ })
125
+ .filter(([ selector, attributeName ]) => selector);
126
+
127
+ for (let [ selector, attributeName ] of consumerParts) {
128
+ let matchingElements = parentElement.querySelectorAll(selector);
129
+ if (attributeName) {
130
+ for (let element of matchingElements) {
131
+ element.setAttribute(attributeName, value);
132
+ }
133
+ } else {
134
+ for (let element of matchingElements) {
135
+ if (element.nodeType === 'INPUT') {
136
+ if (element.type === 'checkbox') {
137
+ if (value) {
138
+ element.setAttribute('checked', '');
139
+ } else {
140
+ element.removeAttribute('checked');
141
+ }
142
+ } else {
143
+ element.value = value;
144
+ }
145
+ } else {
146
+ element.innerHTML = value;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ updateConsumerElementsOnEdit() {
155
+ let consumers = this.getAttribute('consumers');
156
+ if (!consumers) return;
157
+
158
+ let inputElement = this.inputElement;
159
+
160
+ let debounceDelay = this.getAttribute('debounce-delay');
161
+ debounceDelay = debounceDelay ? Number(debounceDelay) : DEFAULT_DEBOUNCE_DELAY;
162
+ let debounce = debouncer();
163
+
164
+ let onInputChange = (event) => {
165
+ debounce(async () => {
166
+ if (event.target.value === this.lastValue) return;
167
+ this.lastValue = event.target.value;
168
+ if (inputElement.type === 'checkbox') {
169
+ this.updateConsumerElements(consumers, !!inputElement.checked);
170
+ return;
171
+ }
172
+ if (event.target.value === '') {
173
+ this.updateConsumerElements(consumers, '');
174
+ } else {
175
+ let targetValue = inputElement.type === 'number' ?
176
+ Number(event.target.value) : event.target.value;
177
+ this.updateConsumerElements(consumers, targetValue);
178
+ }
179
+ }, debounceDelay);
180
+ };
181
+ inputElement.addEventListener('change', onInputChange);
182
+
183
+ let onInputKeyUp;
184
+ if (inputElement.type !== 'checkbox') {
185
+ onInputKeyUp = async (event) => {
186
+ debounce(async () => {
187
+ if (event.target.value === this.lastValue) return;
188
+ this.lastValue = event.target.value;
189
+ if (event.target.value === '') {
190
+ this.updateConsumerElements(consumers, '');
191
+ } else {
192
+ let targetValue = inputElement.type === 'number' ?
193
+ Number(event.target.value) : event.target.value;
194
+ this.updateConsumerElements(consumers, targetValue);
195
+ }
196
+ }, debounceDelay);
197
+ };
198
+ inputElement.addEventListener('keyup', onInputKeyUp);
199
+ }
200
+ return () => {
201
+ inputElement.removeEventListener('change', onInputChange);
202
+ onInputKeyUp && inputElement.removeEventListener('keyup', onInputKeyUp);
203
+ };
204
+ }
205
+ }
206
+
207
+ window.customElements.define('input-provider', InputProvider);
package/model-input.js CHANGED
@@ -21,7 +21,7 @@ class ModelInput extends SocketConsumer {
21
21
  static get observedAttributes() {
22
22
  return [
23
23
  'input-id',
24
- 'input-list',
24
+ 'list',
25
25
  'model-instance-property',
26
26
  'type',
27
27
  'placeholder',
@@ -31,7 +31,8 @@ class ModelInput extends SocketConsumer {
31
31
  'model-field',
32
32
  'debounce-delay',
33
33
  'options',
34
- 'height'
34
+ 'height',
35
+ 'hide-error-logs'
35
36
  ];
36
37
  }
37
38
 
@@ -64,8 +65,8 @@ class ModelInput extends SocketConsumer {
64
65
  let modelInstanceProperty = this.getAttribute('model-instance-property');
65
66
  let showErrorMessage = this.hasAttribute('show-error-message');
66
67
  let inputId = this.getAttribute('input-id');
67
- let inputList = this.getAttribute('input-list');
68
- let type = this.getAttribute('type');
68
+ let inputList = this.getAttribute('list');
69
+ let type = this.getAttribute('type') || 'text';
69
70
  let placeholder = this.getAttribute('placeholder');
70
71
  let modelType = this.getAttribute('model-type');
71
72
  let modelId = this.getAttribute('model-id');
package/model-text.js CHANGED
@@ -18,7 +18,8 @@ class ModelText extends SocketConsumer {
18
18
  'model-instance-property',
19
19
  'model-type',
20
20
  'model-id',
21
- 'model-field'
21
+ 'model-field',
22
+ 'hide-error-logs'
22
23
  ];
23
24
  }
24
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/socket.js CHANGED
@@ -41,7 +41,14 @@ export class SocketProvider extends HTMLElement {
41
41
 
42
42
  attributeChangedCallback(name, oldValue, newValue) {
43
43
  if (!this.isReady) return;
44
- this.destroySocket();
44
+ let sanitizedURL = this.getSanitizedURL();
45
+ if (
46
+ this.saasufySocket &&
47
+ this.saasufySocket.transport &&
48
+ this.saasufySocket.transport.uri() !== sanitizedURL
49
+ ) {
50
+ this.saasufySocket.disconnect();
51
+ }
45
52
  this.getSocket();
46
53
  }
47
54
 
@@ -53,14 +60,24 @@ export class SocketProvider extends HTMLElement {
53
60
 
54
61
  getSocket() {
55
62
  if (this.saasufySocket) {
63
+ this.saasufySocket.options = {
64
+ ...this.saasufySocket.options,
65
+ ...this.getSocketOptions()
66
+ };
67
+ this.saasufySocket.connect();
56
68
  return this.saasufySocket;
57
69
  }
58
70
  return this.createSocket();
59
71
  }
60
72
 
61
- createSocket() {
62
- let authTokenName = this.getAttribute('auth-token-name') || null;
73
+ getSanitizedURL() {
63
74
  let url = this.getAttribute('url') || '';
75
+ return url.trim();
76
+ }
77
+
78
+ getSocketOptions() {
79
+ let authTokenName = this.getAttribute('auth-token-name') || null;
80
+ let url = this.getSanitizedURL();
64
81
  let urlOptions;
65
82
  let matchedList = url.match(urlPartsRegExp);
66
83
  if (matchedList) {
@@ -75,12 +92,22 @@ export class SocketProvider extends HTMLElement {
75
92
  path
76
93
  };
77
94
  } else {
95
+ if (url) {
96
+ throw new Error(`The specified URL ${url} was invalid`);
97
+ }
78
98
  urlOptions = {};
79
99
  }
80
- this.saasufySocket = create({
100
+ let socketOptions = {
81
101
  authTokenName,
82
102
  ...urlOptions
83
- });
103
+ };
104
+ socketOptions.autoConnect = !!url;
105
+ return socketOptions;
106
+ }
107
+
108
+ createSocket() {
109
+ let socketOptions = this.getSocketOptions();
110
+ this.saasufySocket = create(socketOptions);
84
111
 
85
112
  return this.saasufySocket;
86
113
  }
package/utils.js CHANGED
@@ -164,6 +164,7 @@ let templateFormatters = {
164
164
  let valueString = String(value);
165
165
  return `${valueString.slice(0, 1).toUpperCase()}${valueString.slice(1)}`;
166
166
  },
167
+ trim: (value) => String(value).trim(),
167
168
  fallback: (...args) => {
168
169
  return args.filter(arg => arg)[0];
169
170
  }