saasufy-components 1.1.2 → 1.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/app-router.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getSafeHTML } from './utils.js';
1
+ import { toSafeHTML } from './utils.js';
2
2
  import { renderTemplate } from './utils.js';
3
3
 
4
4
  export class AppRouter extends HTMLElement {
@@ -155,7 +155,10 @@ class CollectionBrowser extends SocketConsumer {
155
155
  let items = [];
156
156
 
157
157
  for (let modelItem of this.collection.value) {
158
- let itemString = renderTemplate(itemTemplate.innerHTML, modelItem, this.collection.type);
158
+ let itemString = renderTemplate(
159
+ itemTemplate.innerHTML,
160
+ { [this.collection.type]: modelItem }
161
+ );
159
162
  items.push(itemString);
160
163
  }
161
164
  let noItemTemplate = this.shadowRoot.querySelector('slot[name="no-item"]').assignedNodes()[0];
package/model-input.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SocketConsumer } from './socket.js';
2
- import { getSafeHTML, debouncer } from './utils.js';
2
+ import { debouncer } from './utils.js';
3
3
  import AGModel from '/node_modules/ag-model/ag-model.js';
4
4
 
5
5
  class ModelInput extends SocketConsumer {
@@ -166,7 +166,7 @@ class ModelInput extends SocketConsumer {
166
166
 
167
167
  syncInputElementWithModel(inputElement, model, fieldName, messageContainerElement) {
168
168
  if (model.isLoaded) {
169
- let fieldValue = getSafeHTML(model.value[fieldName]);
169
+ let fieldValue = model.value[fieldName];
170
170
  if (inputElement.type === 'checkbox') {
171
171
  inputElement.checked = !!fieldValue;
172
172
  } else {
@@ -178,7 +178,7 @@ class ModelInput extends SocketConsumer {
178
178
  (async () => {
179
179
  for await (let event of changeConsumer) {
180
180
  if (event.resourceField !== fieldName || !event.isRemote) continue;
181
- let fieldValue = getSafeHTML(model.value[fieldName]);
181
+ let fieldValue = model.value[fieldName];
182
182
  if (inputElement.type === 'checkbox') {
183
183
  inputElement.checked = fieldValue;
184
184
  } else {
package/model-text.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SocketConsumer } from './socket.js';
2
- import { getSafeHTML } from './utils.js';
2
+ import { toSafeHTML } from './utils.js';
3
3
  import AGModel from '/node_modules/ag-model/ag-model.js';
4
4
 
5
5
  class ModelText extends SocketConsumer {
@@ -79,12 +79,12 @@ class ModelText extends SocketConsumer {
79
79
  (async () => {
80
80
  for await (let event of changeConsumer) {
81
81
  if (event.resourceField !== modelField || !event.isRemote) continue;
82
- let fieldValue = getSafeHTML(model.value[modelField]);
82
+ let fieldValue = toSafeHTML(model.value[modelField]);
83
83
  this.innerHTML = fieldValue;
84
84
  }
85
85
  })();
86
86
  if (model.isLoaded) {
87
- this.innerHTML = getSafeHTML(model.value[modelField]);
87
+ this.innerHTML = toSafeHTML(model.value[modelField]);
88
88
  }
89
89
  this.destroy = () => {
90
90
  changeConsumer.kill();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saasufy-components",
3
- "version": "1.1.2",
3
+ "version": "1.3.0",
4
4
  "description": "Components for Saasufy.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,8 +17,9 @@
17
17
  "author": "Jonathan Gros-Dubois",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "ag-collection": "^3.2.1",
21
- "ag-model": "^1.3.6",
20
+ "ag-collection": "^3.3.0",
21
+ "ag-model": "^1.4.1",
22
+ "expr-eval": "^2.0.2",
22
23
  "socketcluster-client": "^19.0.2"
23
24
  },
24
25
  "bugs": {
package/utils.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import AGCollection from '/node_modules/ag-collection/ag-collection.js';
2
2
  import AGModel from '/node_modules/ag-model/ag-model.js';
3
+ import { Parser } from '/node_modules/expr-eval/dist/index.mjs';
3
4
 
4
5
  const DEFAULT_DEBOUNCE_DELAY = 300;
5
6
  const DEFAULT_RELOAD_DELAY = 0;
6
7
 
7
- export function getSafeHTML(text) {
8
+ export function toSafeHTML(text) {
8
9
  if (typeof text === 'string') {
9
10
  return text.replace(/&/g, '&')
10
11
  .replace(/</g, '&lt;')
@@ -29,10 +30,10 @@ export function debouncer() {
29
30
  };
30
31
  }
31
32
 
32
- export function getSafeModelValue(value) {
33
+ export function toSafeModelValue(value) {
33
34
  return Object.fromEntries(
34
35
  Object.entries(value || {}).map(
35
- ([key, value]) => [ key, getSafeHTML(value) ]
36
+ ([key, value]) => [ key, toSafeHTML(value) ]
36
37
  )
37
38
  );
38
39
  }
@@ -60,7 +61,7 @@ export function createReactiveCollection(collectionOptions, callback) {
60
61
 
61
62
  if (!collection.isLoaded) continue;
62
63
 
63
- collection.safeValue = collection.value.map(getSafeModelValue);
64
+ collection.safeValue = collection.value.map(toSafeModelValue);
64
65
 
65
66
  callback({
66
67
  changes
@@ -102,7 +103,7 @@ export function createReactiveModel(modelOptions, callback) {
102
103
  changes[event.resourceField] = event.isRemote;
103
104
  }
104
105
 
105
- model.safeValue = getSafeModelValue(model.value);
106
+ model.safeValue = toSafeModelValue(model.value);
106
107
  callback({
107
108
  changes
108
109
  });
@@ -128,7 +129,7 @@ export function createCollection(collectionOptions) {
128
129
  (async () => {
129
130
  for await (let event of collection.listener('change')) {
130
131
  if (!collection.isLoaded) continue;
131
- collection.safeValue = collection.value.map(getSafeModelValue);
132
+ collection.safeValue = collection.value.map(toSafeModelValue);
132
133
  }
133
134
  })();
134
135
 
@@ -148,7 +149,7 @@ export function createModel(modelOptions) {
148
149
 
149
150
  (async () => {
150
151
  for await (let event of model.listener('change')) {
151
- model.safeValue = getSafeModelValue(model.value);
152
+ model.safeValue = toSafeModelValue(model.value);
152
153
  }
153
154
  })();
154
155
 
@@ -162,20 +163,32 @@ let templateFormatters = {
162
163
  capitalize: (value) => {
163
164
  let valueString = String(value);
164
165
  return `${valueString.slice(0, 1).toUpperCase()}${valueString.slice(1)}`;
166
+ },
167
+ fallback: (...args) => {
168
+ return args.filter(arg => arg)[0];
165
169
  }
166
170
  };
167
171
 
168
- export function renderTemplate(templateString, data, dataType) {
169
- for (let [ field, value ] of Object.entries(data)) {
170
- let safeValue = getSafeHTML(value);
171
- for (let [ formatName, formatFunction ] of Object.entries(templateFormatters)) {
172
- let formatRegExp = new RegExp(`{{${formatName}:${dataType ? dataType + '.' : ''}${field}}}`, 'g');
173
- templateString = templateString.replace(formatRegExp, formatFunction(safeValue));
172
+ let templateTagsRegExp = /{{[^}]+}}/g;
173
+
174
+ export function renderTemplate(templateString, data) {
175
+ return templateString.replace(templateTagsRegExp, (match) => {
176
+ let expString = match.slice(2, -2);
177
+ let options = {
178
+ ...templateFormatters,
179
+ ...data
180
+ };
181
+ try {
182
+ return toSafeHTML(
183
+ Parser.evaluate(
184
+ expString,
185
+ options
186
+ )
187
+ );
188
+ } catch (error) {
189
+ return match;
174
190
  }
175
- let regExp = new RegExp(`{{${dataType ? dataType + '.' : ''}${field}}}`, 'g');
176
- templateString = templateString.replace(regExp, safeValue);
177
- }
178
- return templateString;
191
+ });
179
192
  }
180
193
 
181
194
  export function wait(duration) {