glib-web 3.18.0 → 3.19.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
@@ -22,11 +22,19 @@
22
22
  - `yarn link glib-web`
23
23
  - `bin/vite dev`
24
24
 
25
- ## Clean up to fix strange errors when running vite dev
25
+ ## Clean up to fix strange errors (e.g. tiny-emitter error) when running vite dev
26
26
 
27
27
  - Stop vite server
28
- - `bin/vite clobber`
29
- - `bin/vite dev`
28
+
29
+ - On your glib-web-npm's directory:
30
+ - `rm -rf node_modules`
31
+ - `yarn install`
32
+
33
+ - On your project's directory:
34
+ - `rm -rf node_modules`
35
+ - `yarn install`
36
+ - `bin/vite clobber`
37
+ - `bin/vite dev`
30
38
 
31
39
  ## Prepare for publishing
32
40
 
package/action.js CHANGED
@@ -2,10 +2,6 @@ import TypeUtils from "./utils/type";
2
2
 
3
3
  import ActionsRunMultiple from "./actions/runMultiple";
4
4
 
5
- import ActionsFormsSubmit from "./actions/forms/submit";
6
- import ActionsFieldsReset from "./actions/fields/reset";
7
- import ActionsFieldsFocus from "./actions/fields/focus";
8
-
9
5
  import ActionsHttpGetV1 from "./actions/http/get";
10
6
  import ActionsHttpPostV1 from "./actions/http/post";
11
7
  import ActionsHttpPatchV1 from "./actions/http/patch";
@@ -61,10 +57,15 @@ const ActionToursStop = import("./actions/tours/stop");
61
57
  import ActionPopoversOpen from "./actions/popovers/open";
62
58
  import ActionPopoversClose from "./actions/popovers/close";
63
59
 
60
+ import ActionsFormsSubmit from "./actions/forms/submit";
61
+ import ActionsFieldsReset from "./actions/fields/reset";
62
+ import ActionsFieldsFocus from "./actions/fields/focus";
63
+
64
64
  import ActionComponentsUpdate from "./actions/components/update";
65
65
  import ActionComponentsFind from "./actions/components/find";
66
-
67
- import ActionLabelsSet from "./actions/labels/set";
66
+ import ActionComponentsFindClosest from "./actions/components/findClosest";
67
+ import ActionComponentsReplace from "./actions/components/replace";
68
+ import ActionComponentsSet from "./actions/components/set";
68
69
 
69
70
  import ActionListsAppend from "./actions/lists/append";
70
71
 
@@ -73,10 +74,6 @@ import { vueApp } from "./store";
73
74
  const actions = {
74
75
  runMultiple: ActionsRunMultiple,
75
76
 
76
- "forms/submit": ActionsFormsSubmit,
77
- "fields/reset": ActionsFieldsReset,
78
- "fields/focus": ActionsFieldsFocus,
79
-
80
77
  "http/get": ActionsHttpGetV1,
81
78
  "http/post": ActionsHttpPostV1,
82
79
  "http/patch": ActionsHttpPatchV1,
@@ -131,10 +128,16 @@ const actions = {
131
128
  "popovers/open": ActionPopoversOpen,
132
129
  "popovers/close": ActionPopoversClose,
133
130
 
131
+ "forms/submit": ActionsFormsSubmit,
132
+ "fields/reset": ActionsFieldsReset,
133
+ "fields/focus": ActionsFieldsFocus,
134
+
134
135
  "components/update": ActionComponentsUpdate,
135
136
  "components/find": ActionComponentsFind,
137
+ "components/findClosest": ActionComponentsFindClosest,
138
+ "components/replace": ActionComponentsReplace,
139
+ "components/set": ActionComponentsSet,
136
140
 
137
- "labels/set": ActionLabelsSet,
138
141
  "lists/append": ActionListsAppend
139
142
  };
140
143
 
@@ -0,0 +1,6 @@
1
+ export default class {
2
+ execute(spec, component) {
3
+ const target = component.$closest(spec.view);
4
+ Action.execute(spec.onFind, target);
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ // Experimental
2
+ export default class {
3
+ execute(spec, component) {
4
+ const target = GLib.component.findById(spec.targetId);
5
+ if (target) {
6
+ Object.assign(target.spec, spec.newView);
7
+ }
8
+
9
+ Action.execute(properties.onReplace, targetComponent);
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ export default class {
2
+ execute(spec, component) {
3
+ let targetComponent = component;
4
+
5
+ if (spec.targetId) {
6
+ targetComponent = GLib.component.findById(spec.targetId);
7
+ }
8
+
9
+ Utils.type.ifObject(spec.data, (data) => {
10
+ targetComponent.action_merge(data)
11
+ })
12
+
13
+ GLib.action.execute(spec.onSet, targetComponent);
14
+ }
15
+ }
@@ -1,6 +1,13 @@
1
1
  export default class {
2
- execute(properties, component) {
3
- const target = GLib.component.findById(properties.targetId);
4
- target.action_focus();
2
+ execute(spec, component) {
3
+ let targetComponent = component;
4
+
5
+ if (spec.targetId) {
6
+ targetComponent = GLib.component.findById(spec.targetId);
7
+ }
8
+
9
+ targetComponent.action_focus();
10
+
11
+ Action.execute(spec.onFocus, targetComponent);
5
12
  }
6
13
  }
@@ -56,6 +56,9 @@ export default {
56
56
  return item.view == "panels/column-v1";
57
57
  },
58
58
  viewKey(item, index) {
59
+ // force update if child is charts
60
+ if (item.view.toString().startsWith('charts/')) return item;
61
+
59
62
  return index;
60
63
 
61
64
  // return `view${index}_${item.view}`;
@@ -55,16 +55,28 @@ function useChart({ dataSeries, spec, multiple = true }) {
55
55
  options.plugins.datalabels = datalabels;
56
56
  options.plugins.datalabels.formatter = (value, context) => {
57
57
  const type = datalabels.formatType || 'value';
58
+ let val = null;
58
59
 
59
60
  switch (type) {
60
61
  case 'label':
61
- return context.chart.data.labels[context.dataIndex];
62
+ val = context.chart.data.labels[context.dataIndex];
62
63
  break;
63
64
 
64
65
  case 'value':
65
- return `${spec.prefix || ''}${value}${spec.suffix || ''}`;
66
+ val = `${spec.prefix || ''}${value}${spec.suffix || ''}`;
66
67
  break;
67
68
  }
69
+
70
+ const { dataIndex, datasetIndex } = context;
71
+ const dataset = dataSeries[datasetIndex];
72
+ if (multiple && Array.isArray(dataset.points) && dataset.points[dataIndex].label) {
73
+ val = dataset.points[dataIndex].label;
74
+ }
75
+ if (!multiple && dataSeries[dataIndex].label) {
76
+ val = dataSeries[dataIndex].label;
77
+ }
78
+
79
+ return val;
68
80
  };
69
81
  }
70
82
  if (centerLabel) {
@@ -273,6 +273,9 @@ export default {
273
273
  $registryEnabled() {
274
274
  return false;
275
275
  },
276
+ $jsonLogicEnabled() {
277
+ return false;
278
+ },
276
279
  handlePopover(spec) {
277
280
  GLib.action.execute(spec, this);
278
281
  }
@@ -12,8 +12,8 @@ export function useDirtyState() {
12
12
  ctx().isFormDirty = false;
13
13
  };
14
14
 
15
- const updateDirtyState = (val, oldVal, spec) => {
16
- if (!spec.disableDirtyCheck && val != oldVal && val != spec.value) {
15
+ const updateDirtyState = (val, oldVal, initValue, spec) => {
16
+ if (!spec.disableDirtyCheck && val != oldVal && val != initValue) {
17
17
  ctx().isFormDirty = true;
18
18
  }
19
19
  };
@@ -2,8 +2,8 @@
2
2
  <div :style="$styles()" :class="classes()">
3
3
  <!-- See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date for why we need to use `pattern` -->
4
4
  <v-text-field v-model="fieldModel" :name="fieldName" :label="spec.label" :hint="spec.hint" :type="type"
5
- :readonly="spec.readOnly" :disabled="inputDisabled" :min="$sanitizeValue(spec.min)" :max="$sanitizeValue(spec.max)"
6
- :pattern="pattern" :rules="$validation()" :style="$styles()"
5
+ :readonly="spec.readOnly" :disabled="inputDisabled" :min="$sanitizeValue(spec.min)"
6
+ :max="$sanitizeValue(spec.max)" :pattern="pattern" :rules="$validation()" :style="$styles()"
7
7
  :density="$classes().includes('compact') ? 'compact' : 'default'" clearable @change="onChange" :variant="variant"
8
8
  validate-on="blur" persistent-placeholder />
9
9
  </div>
@@ -24,14 +24,14 @@ export default {
24
24
  if (Utils.type.isString(val)) {
25
25
  return new Date(val).getTime() / 1000;
26
26
  }
27
- return null;
27
+ return val;
28
28
  },
29
29
  $externalizeValue(val) {
30
30
  if (Utils.type.isNumber(val)) {
31
31
  const date = new Date(val * 1000);
32
- return Utils.format.local_iso8601(date);
32
+ return date.toISOString();
33
33
  }
34
- return null;
34
+ return val;
35
35
  },
36
36
  $sanitizeValue(val) {
37
37
  if (val) {
@@ -20,6 +20,13 @@
20
20
  </v-list-item>
21
21
  </template>
22
22
 
23
+ <template v-slot:prepend-item v-if="spec.header">
24
+ <common-responsive :spec="spec.header" />
25
+ </template>
26
+
27
+ <template v-slot:append-item v-if="spec.footer">
28
+ <common-responsive :spec="spec.footer" />
29
+ </template>
23
30
  </v-autocomplete>
24
31
 
25
32
  <input v-for="(item, index) in values" :key="index" type="hidden" :disabled="inputDisabled" :name="fieldName"
@@ -63,7 +70,7 @@ export default {
63
70
  return determineDensity(this.spec.styleClasses);
64
71
  },
65
72
  label() {
66
- if (this.focused) {
73
+ if (this.focused && this.spec.hint) {
67
74
  return `${this.spec.label} ${this.spec.hint}`
68
75
  }
69
76
  return this.spec.label
@@ -23,9 +23,7 @@
23
23
  {{ buttonLabels.delete }}
24
24
  </v-btn>
25
25
 
26
-
27
26
  <glib-component v-if="spec.infoSpec" :spec="spec.infoSpec"></glib-component>
28
-
29
27
  </div>
30
28
  </div>
31
29
  <input ref="directUploadFile" style="display: none" type="file" @change="uploadFiles" />
@@ -26,8 +26,9 @@
26
26
  <div class="status">
27
27
  <v-icon class="icon">{{ file[1].isImage() ? 'photo_library' : 'description' }}</v-icon>
28
28
  <div class="progress">
29
- <label class="label">{{ `${file[1].name} ${file[1].status === 'failed' ? uploadFailedText : ''}`
30
- }}</label>
29
+ <div class="label"><span>{{ `${file[1].name}` }}</span><span v-if="file[1].message"
30
+ style="font-size: 0.75em">{{
31
+ file[1].message }}</span></div>
31
32
  <div class="background" v-show="file[1].progress.value > 0">
32
33
  <div class="value" :style="{ width: `${file[1].progress.value}%` }"></div>
33
34
  </div>
@@ -96,12 +97,16 @@
96
97
 
97
98
  .progress {
98
99
  display: flex;
99
- width: 100%;
100
+ flex-grow: 1;
100
101
  flex-direction: column;
101
102
  justify-content: space-between;
102
103
 
103
104
  .label {
104
105
  /* mb-1 */
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: space-between;
109
+ flex-wrap: nowrap;
105
110
  margin-bottom: 4px;
106
111
  }
107
112
 
@@ -239,6 +244,9 @@ export default defineComponent({
239
244
  const container = ref(null);
240
245
  const icon = ref(null);
241
246
 
247
+ const responseMessages = spec.responseMessages;
248
+ const uploadTitle = spec.uploadTitle || 'File added';
249
+
242
250
  let files = ref({});
243
251
  if (spec.files && spec.files.length > 0) {
244
252
  files = ref(spec.files.reduce((prev, curr) => {
@@ -308,11 +316,14 @@ export default defineComponent({
308
316
  if (!error) {
309
317
  Object.assign(files.value[key], {
310
318
  status: 'completed',
311
- signedId: blob.signed_id
319
+ signedId: blob.signed_id,
320
+ message: responseMessages['200']
312
321
  });
313
322
  } else {
323
+ const message = responseMessages[error.slice(-3) || 'else'];
314
324
  Object.assign(files.value[key], {
315
- status: 'failed'
325
+ status: 'failed',
326
+ message: message
316
327
  });
317
328
  }
318
329
  });
@@ -366,9 +377,6 @@ export default defineComponent({
366
377
  fileSelect.value.click();
367
378
  }
368
379
 
369
- const uploadFailedText = spec.uploadFailedText || '(UPLOAD FAILED)';
370
- const uploadTitle = spec.uploadTitle || 'File added';
371
-
372
380
  return {
373
381
  files,
374
382
  fileSelect,
@@ -380,7 +388,7 @@ export default defineComponent({
380
388
  handleClick,
381
389
  handleRemoveFile,
382
390
  showUploadedFile,
383
- uploadFailedText,
391
+ responseMessages,
384
392
  uploadTitle
385
393
  };
386
394
  }
@@ -53,7 +53,7 @@ span.muted {
53
53
  }
54
54
 
55
55
  span.small {
56
- font-size: 80%;
56
+ font-size: 90%;
57
57
  }
58
58
 
59
59
  a:hover {
@@ -4,7 +4,7 @@ export default {
4
4
  methods: {
5
5
  $componentName() { // Can be overridden
6
6
  if (this.spec) {
7
- return this.spec.view;
7
+ return this.spec.view?.replace('-v1', '');
8
8
  }
9
9
  },
10
10
  $closest(name) {
@@ -12,8 +12,8 @@ export default {
12
12
  while (parent != null) {
13
13
  if (
14
14
  (Utils.type.isObject(parent.spec) || Utils.type.isObject(parent.formSpec)) &&
15
- parent.$componentName() == name
16
- // GLib.component.vueName(parent) == name
15
+ parent.$componentName() == name &&
16
+ parent.$registryEnabled()
17
17
  ) {
18
18
  return parent;
19
19
  }
@@ -78,6 +78,9 @@ export default {
78
78
  Utils.ws.handleResponse(payload.onResponse, this);
79
79
  });
80
80
  }
81
+ },
82
+ action_merge(mergedSpec) {
83
+ Object.assign(this.spec, mergedSpec);
81
84
  }
82
85
  }
83
86
  };
@@ -49,7 +49,8 @@ export default {
49
49
 
50
50
  Object.assign(fieldModels, { [this.fieldName]: this.$internalizeValue(val) });
51
51
 
52
- updateDirtyState(val, oldVal, this.spec);
52
+ const initValue = this.$sanitizeValue(this.$externalizeValue(this.spec.value));
53
+ updateDirtyState(val, oldVal, initValue, this.spec);
53
54
  },
54
55
  spec: {
55
56
  handler(spec, oldSpec) {
@@ -101,17 +102,29 @@ export default {
101
102
  this._watchers.push(watcher1);
102
103
  }
103
104
 
104
- if (this.spec && (this.spec.showIf || this.spec.loadIf)) {
105
+ if (this.spec && (this.spec.showIf || this.spec.loadIf) && this.$jsonLogicEnabled()) {
105
106
  watcher2 = watchFieldModels(this.spec.showIf || this.spec.loadIf, (value) => {
107
+ const oldValue = this._show;
108
+
106
109
  if (value) {
107
110
  this._show = true;
108
111
  } else {
109
112
  this._show = false;
110
113
  }
114
+
115
+ if (oldValue != value) {
116
+ this.$nextTick(() => {
117
+ // Call either onIfTrue or onIfFalse.
118
+ Action.execute(this.spec[`onIf${value.toString().capitalize()}`], this);
119
+ });
120
+ }
111
121
  });
112
122
  this._watchers.push(watcher2);
113
123
  }
114
124
  },
125
+ $jsonLogicEnabled() {
126
+ return true;
127
+ },
115
128
  // TODO: Deprecated
116
129
  genericStyles(spec) {
117
130
  return this.$styles(spec);
@@ -45,3 +45,7 @@ String.prototype.compare = function(str) {
45
45
  String.prototype.squish = function() {
46
46
  return this.replace(/\s+/g, " ").trim();
47
47
  };
48
+
49
+ String.prototype.capitalize = function() {
50
+ return this.charAt(0).toUpperCase() + this.slice(1);
51
+ };
package/index.js CHANGED
@@ -80,9 +80,6 @@ Vue.mixin(stylesMixin);
80
80
  import scrollingMixin from "./components/mixins/scrolling.js";
81
81
  Vue.mixin(scrollingMixin);
82
82
 
83
- import datasetMixins from "./components/mixins/dataset.js";
84
- Vue.mixin(datasetMixins);
85
-
86
83
  Vue.config.globalProperties.extension = {};
87
84
  import extension from "./components/mixins/extension.js";
88
85
  Vue.mixin(extension);
package/nav/dialog.vue CHANGED
@@ -10,7 +10,7 @@
10
10
  </h3>
11
11
  <div v-else></div>
12
12
 
13
- <v-btn size="small" text icon @click="close" variant="flat">
13
+ <v-btn v-if="!disableCloseButton" size="small" text icon @click="close" variant="flat">
14
14
  <v-icon>close</v-icon>
15
15
  </v-btn>
16
16
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/utils/settings.js CHANGED
@@ -6,7 +6,7 @@ class MutableSettings {
6
6
  this.themes = {};
7
7
  this.gtagId = null;
8
8
  this.errorHandler = (err, message) => {
9
- console.error(message || err.message);
9
+ console.error(message || err.message, err);
10
10
  };
11
11
  this.headerAugmenter = (_url, _method) => {
12
12
  // Set a custom augmenter to add custom HTTP headers.
@@ -1,16 +0,0 @@
1
- import Action from "../../action";
2
-
3
- export default class {
4
- execute(properties, component) {
5
- let targetComponent = component;
6
- if (properties.targetId) {
7
- targetComponent = GLib.component.findById(properties.targetId);
8
- }
9
-
10
- targetComponent.text = properties.text;
11
-
12
- if (properties.onSet) {
13
- Action.execute(properties.onSet, targetComponent);
14
- }
15
- }
16
- }
@@ -1,10 +0,0 @@
1
- export default {
2
- mounted() {
3
- if (this.spec && this.spec.dataset) {
4
- const data = this.spec.dataset;
5
- for (const key in this.spec.dataset) {
6
- this.$el.dataset[key] = data[key];
7
- }
8
- }
9
- }
10
- };