glib-web 2.2.0 → 2.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/action.js CHANGED
@@ -49,6 +49,7 @@ import ActionsCreditCard from "./actions/auth/creditCard";
49
49
  import ActionsAnalyticsLogEvent from "./actions/analytics/logEvent";
50
50
 
51
51
  import ActionCommandsCopy from "./actions/commands/copy";
52
+ import ActionCommandsCustom from "./actions/commands/custom";
52
53
 
53
54
  import ActionToursStart from "./actions/tours/start";
54
55
 
@@ -102,11 +103,16 @@ const actions = {
102
103
  "auth/creditCard": ActionsCreditCard,
103
104
 
104
105
  "analytics/logEvent": ActionsAnalyticsLogEvent,
106
+
105
107
  "commands/copy": ActionCommandsCopy,
108
+ "commands/custom": ActionCommandsCustom,
109
+
106
110
  "tours/start": ActionToursStart,
107
111
  "components/update": ActionComponentsUpdate
108
112
  };
109
113
 
114
+ const customActions = {};
115
+
110
116
  export default class Action {
111
117
  static execute(spec, component, params = {}) {
112
118
  if (!TypeUtils.isObject(spec)) {
@@ -148,17 +154,21 @@ export default class Action {
148
154
  }
149
155
 
150
156
  static executeGlobal(name, spec, component, params) {
157
+ this._executeInternal(name, spec, component, params, actions);
158
+ }
159
+
160
+ static _executeInternal(name, spec, component, params, registry) {
151
161
  const actionName = name.replace(/-v1$/, "");
152
162
 
153
163
  try {
154
- const action = new actions[actionName]();
164
+ const action = new registry[actionName]();
155
165
  const logDisabled = action.logDisabled && action.logDisabled();
156
166
  if (!logDisabled) {
157
167
  console.log(`Executing "${actionName}"`);
158
168
  }
159
169
  action.execute(spec, component, params);
160
170
  } catch (e) {
161
- console.log(
171
+ console.error(
162
172
  "Failed executing command",
163
173
  actionName,
164
174
  `Error: '${e.message}'`
@@ -178,6 +188,18 @@ export default class Action {
178
188
  GLib.action.execute(response.onResponse, component);
179
189
  window.vueApp.temp.analytics = null;
180
190
  }
191
+
192
+ static registerCustom(actionName, action) {
193
+ if (customActions[actionName]) {
194
+ console.error("Command already registered", actionName);
195
+ } else {
196
+ customActions[actionName] = action;
197
+ }
198
+ }
199
+
200
+ static executeCustom(name, spec, component, params) {
201
+ this._executeInternal(name, spec, component, params, customActions);
202
+ }
181
203
  }
182
204
 
183
205
  window.Action = Action;
@@ -0,0 +1,10 @@
1
+ import Hash from "../../utils/hash";
2
+
3
+ export default class {
4
+ execute(spec, component) {
5
+ const properties = new Hash(spec.properties);
6
+ // `spec` might contain params that were coming from other actions (e.g. `formData`).
7
+ Object.assign(properties, spec);
8
+ GLib.action.executeCustom(spec.name, properties, component);
9
+ }
10
+ }
@@ -5,11 +5,14 @@ export default class {
5
5
  if (!spec.views.length > 1) {
6
6
  console.warn("Make sure views only have 1 child!");
7
7
  }
8
- const target = component.updatables[spec.targetId];
9
- if (!target) console.error("no component found :" + spec.targetId);
8
+ // const target = component.updatables[spec.targetId];
9
+ // if (!target) console.error("no component found :" + spec.targetId);
10
10
 
11
- Object.assign(target.spec, spec.views[0]);
12
- this.updateComponent(target);
11
+ const target = GLib.component.findById(spec.targetId);
12
+ if (target) {
13
+ Object.assign(target.spec, spec.views[0]);
14
+ this.updateComponent(target);
15
+ }
13
16
  }
14
17
 
15
18
  updateComponent(component) {
package/app.vue CHANGED
@@ -1,7 +1,11 @@
1
1
  <template>
2
2
  <v-app :class="page.styleClasses">
3
- <component :is="containerComponent" :spec="formSpec">
4
- <nav-appbar :page="page" />
3
+ <component
4
+ :is="containerComponent"
5
+ :spec="formSpec"
6
+ :style="'height: 100%;'"
7
+ >
8
+ <nav-appbar ref="appBar" :page="page" />
5
9
 
6
10
  <v-progress-linear
7
11
  v-if="$root.vueApp.indicator"
@@ -11,29 +15,24 @@
11
15
  >
12
16
  </v-progress-linear>
13
17
 
14
- <div class="pages-header">
15
- <layouts-content
16
- :template="page.template"
17
- :spec="header"
18
- :full-height="false"
19
- />
20
- </div>
21
-
22
- <div id="page_body" class="pages-body body-wrapper">
23
- <layouts-content
24
- :template="page.template"
25
- :spec="body"
26
- :full-height="true"
27
- />
28
- </div>
29
-
30
- <div class="pages-footer">
31
- <layouts-content
32
- :template="page.template"
33
- :spec="footer"
34
- :full-height="false"
35
- />
36
- </div>
18
+ <v-main :style="`height: ${mainHeight}px;`">
19
+ <v-container
20
+ :fluid="page.template == 'fullWidth'"
21
+ :class="containerClasses"
22
+ >
23
+ <div class="pages-header">
24
+ <panels-responsive :spec="header" />
25
+ </div>
26
+
27
+ <div id="page_body" class="pages-body body-wrapper">
28
+ <panels-responsive :spec="body" />
29
+ </div>
30
+
31
+ <div class="pages-footer">
32
+ <panels-responsive :spec="footer" />
33
+ </div>
34
+ </v-container>
35
+ </v-main>
37
36
  </component>
38
37
  </v-app>
39
38
  </template>
@@ -41,7 +40,6 @@
41
40
  <script>
42
41
  import NavAppBar from "./nav/appbar";
43
42
  import Utils from "./utils/helper";
44
- import ContentLayout from "./nav/content";
45
43
  import phoenixSocketMixin from "./components/mixins/ws/phoenixSocket.js";
46
44
  import actionCableMixin from "./components/mixins/ws/actionCable.js";
47
45
  import FormPanel from "./components/panels/form";
@@ -49,7 +47,6 @@ import FormPanel from "./components/panels/form";
49
47
  export default {
50
48
  components: {
51
49
  "nav-appbar": NavAppBar,
52
- "layouts-content": ContentLayout,
53
50
  "panels-form": FormPanel
54
51
  },
55
52
  mixins: [phoenixSocketMixin, actionCableMixin],
@@ -58,7 +55,8 @@ export default {
58
55
  },
59
56
  data() {
60
57
  return {
61
- title: "..."
58
+ title: "...",
59
+ mainHeight: 0
62
60
  };
63
61
  },
64
62
  computed: {
@@ -77,6 +75,11 @@ export default {
77
75
  }
78
76
  return "div";
79
77
  },
78
+ containerClasses() {
79
+ const classes = ["hamburger"];
80
+ classes.push(this.page.containerStyleClasses || []);
81
+ return classes;
82
+ },
80
83
  // Use computed to ensure that the spec gets updated when the user navigates to another page.
81
84
  formSpec() {
82
85
  return this.page.fullPageForm;
@@ -96,6 +99,15 @@ export default {
96
99
  Utils.http.promptIfDirtyOnUnload();
97
100
  },
98
101
  methods: {
102
+ $mounted() {
103
+ window.addEventListener(
104
+ "resize",
105
+ event => {
106
+ this.updateMainHeight();
107
+ },
108
+ true
109
+ );
110
+ },
99
111
  $ready() {
100
112
  document.title = this.page.title;
101
113
 
@@ -125,6 +137,15 @@ export default {
125
137
  GLib.action.execute(this.page.onLoad, this);
126
138
  });
127
139
  }
140
+
141
+ // Use nextTick() to allow time for the appBar to complete initialization.
142
+ this.$nextTick(() => {
143
+ this.updateMainHeight();
144
+ });
145
+ },
146
+ updateMainHeight() {
147
+ console.debug("Setting body height");
148
+ this.mainHeight = window.innerHeight - this.$refs.appBar.$el.offsetHeight;
128
149
  }
129
150
  }
130
151
  };
@@ -178,11 +199,12 @@ body,
178
199
 
179
200
  <style scoped>
180
201
  /*** Header/footer support ***/
181
- .application--wrap {
202
+ .hamburger {
182
203
  display: flex;
183
204
  flex-direction: column;
184
205
  justify-content: space-between;
185
206
  height: 100%;
207
+ padding: 0;
186
208
  }
187
209
 
188
210
  .body-wrapper {
@@ -15,6 +15,7 @@
15
15
  :rounded="$classes().includes('rounded')"
16
16
  :depressed="$classes().includes('depressed')"
17
17
  @click="type == 'submit' ? null : $onClick()"
18
+ v-on="eventHandlers"
18
19
  >
19
20
  <span><common-icon :spec="spec.icon || {}"/></span>
20
21
  <div :class="hideTextOnXs && spec.icon ? 'd-none d-sm-flex' : null">
@@ -30,7 +31,8 @@ export default {
30
31
  spec: { type: Object, required: true },
31
32
  type: { type: String, default: "button" },
32
33
  disabled: { type: Boolean, required: true },
33
- hideTextOnXs: { type: Boolean }
34
+ hideTextOnXs: { type: Boolean },
35
+ eventHandlers: { type: Object, default: null }
34
36
  },
35
37
  data: function() {
36
38
  return {
@@ -1,37 +1,29 @@
1
1
  <template>
2
- <v-tooltip
3
- :disabled="!spec.tooltip"
4
- :top="tooltipPositionMatches('top')"
5
- :right="tooltipPositionMatches('right')"
6
- :bottom="tooltipPositionMatches('bottom')"
7
- :left="tooltipPositionMatches('left')"
8
- >
2
+ <common-tooltip :spec="spec">
9
3
  <template v-slot:activator="{ on }">
10
- <span v-on="on">
11
- <common-badge :spec="spec">
12
- <v-chip
13
- v-if="spec.onClick"
14
- :style="genericStyles()"
15
- :class="$classes()"
16
- :href="$href()"
17
- @click="$onClick()"
18
- >
19
- {{ spec.text }}
20
- </v-chip>
21
- <v-chip v-else :style="genericStyles()" :class="$classes()">
22
- {{ spec.text }}
23
- </v-chip>
24
- </common-badge>
25
- </span>
4
+ <common-badge :spec="spec">
5
+ <v-chip
6
+ v-if="spec.onClick"
7
+ :style="genericStyles()"
8
+ :class="$classes()"
9
+ :href="$href()"
10
+ @click="$onClick()"
11
+ v-on="on"
12
+ >
13
+ {{ spec.text }}
14
+ </v-chip>
15
+ <v-chip v-else :style="genericStyles()" :class="$classes()" v-on="on">
16
+ {{ spec.text }}
17
+ </v-chip>
18
+ </common-badge>
26
19
  </template>
27
- <span>{{ tooltip.text }}</span>
28
- </v-tooltip>
20
+ </common-tooltip>
29
21
  </template>
30
22
 
31
23
  <script>
32
- import TooltipMixins from "./mixins/tooltip";
24
+ // import TooltipMixins from "./mixins/tooltip";
33
25
  export default {
34
- mixins: [TooltipMixins],
26
+ // mixins: [TooltipMixins],
35
27
  props: {
36
28
  spec: { type: Object, required: true }
37
29
  },
@@ -50,7 +42,7 @@ export default {
50
42
 
51
43
  <style lang="scss" scoped>
52
44
  // Prevent hover effect if the chip is not clickable.
53
- span.theme--light.v-chip:hover:before {
45
+ .v-chip:not(.v-chip--clickable).theme--light:hover:before {
54
46
  opacity: 0;
55
47
  }
56
48
  </style>
@@ -5,6 +5,7 @@
5
5
  :style="$styles()"
6
6
  :href="$href()"
7
7
  @click="$onClick()"
8
+ v-on="eventHandlers"
8
9
  >
9
10
  <v-row no-gutters class="full-height">
10
11
  <template v-for="(item, index) in spec.childViews">
@@ -31,13 +32,8 @@ import Vue from "vue";
31
32
 
32
33
  export default {
33
34
  props: {
34
- spec: {
35
- type: Object,
36
- required: true,
37
- default: function() {
38
- return {};
39
- }
40
- }
35
+ spec: { type: Object, required: true },
36
+ eventHandlers: { type: Object, default: null }
41
37
  },
42
38
  data() {
43
39
  return {
@@ -46,12 +42,18 @@ export default {
46
42
  },
47
43
  computed: {
48
44
  cssClasses() {
49
- // This panel will be nameless when used in predefined layout (e.g. page.body, list.header, etc.)
50
- this.spec.view = this.spec.view || "panels/responsive";
51
- return this.$classes();
45
+ // Provide a default name so the panel will not be nameless when used in predefined layouts (e.g. page.body, list.header, etc.)
46
+ return this.$classes(
47
+ Object.assign(
48
+ {
49
+ view: "panels/responsive"
50
+ },
51
+ this.spec
52
+ )
53
+ );
52
54
  },
53
55
  componentName() {
54
- return this.$href() ? "a" : "div";
56
+ return this.spec.onClick ? "a" : "div";
55
57
  }
56
58
  },
57
59
  methods: {
@@ -91,7 +93,7 @@ export default {
91
93
  display: flex;
92
94
  flex-direction: column;
93
95
  }
94
- /* Needed to ensure that split's sub panels have the same height */
96
+ // Needed to ensure that split's sub panels have the same height
95
97
  .full-height {
96
98
  height: 100%;
97
99
  }
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <v-tooltip
3
+ :disabled="tooltip.disabled"
4
+ :top="tooltipPositionMatches('top')"
5
+ :right="tooltipPositionMatches('right')"
6
+ :bottom="tooltipPositionMatches('bottom')"
7
+ :left="tooltipPositionMatches('left')"
8
+ >
9
+ <template v-slot:activator="{ on }">
10
+ <!-- Pass the slot props through to the caller using the same slot name (i.e. `activator`) -->
11
+ <slot name="activator" :on="on" />
12
+ </template>
13
+ <span> {{ tooltip.text }} </span>
14
+ </v-tooltip>
15
+ </template>
16
+
17
+ <script>
18
+ // import tooltipMixin from "./mixins/tooltip";
19
+
20
+ export default {
21
+ // mixins: [tooltipMixin],
22
+ props: {
23
+ spec: { type: Object, required: true }
24
+ },
25
+ data() {
26
+ return {
27
+ tooltip: {},
28
+ childSpec: Object.assign({}, this.spec, { id: undefined })
29
+ };
30
+ },
31
+ methods: {
32
+ $ready() {
33
+ this.tooltip = this.spec.tooltip || { disabled: true };
34
+ this.childSpec = Object.assign({}, this.spec, { id: undefined });
35
+ },
36
+ // $initAccessories() {
37
+ // this.tooltip = this.spec.tooltip || { disabled: true };
38
+ // },
39
+ tooltipPositionMatches(position) {
40
+ if (this.spec.tooltip && this.spec.tooltip.position) {
41
+ return position == this.spec.tooltip.position;
42
+ } else {
43
+ return position == "bottom";
44
+ }
45
+ }
46
+ }
47
+ };
48
+ </script>
49
+
50
+ <style lang="scss" scoped></style>
@@ -1,11 +1,5 @@
1
1
  <template>
2
- <v-tooltip
3
- :disabled="tooltip.disabled"
4
- :top="tooltipPositionMatches('top')"
5
- :right="tooltipPositionMatches('right')"
6
- :bottom="tooltipPositionMatches('bottom')"
7
- :left="tooltipPositionMatches('left')"
8
- >
2
+ <common-tooltip :spec="spec">
9
3
  <template v-slot:activator="{ on }">
10
4
  <common-badge :spec="spec">
11
5
  <v-avatar :size="spec.size">
@@ -14,31 +8,18 @@
14
8
  :style="$styles()"
15
9
  :src="spec.url || spec.base64Data"
16
10
  @click="$onClick()"
11
+ v-on="on"
17
12
  />
18
13
  </v-avatar>
19
14
  </common-badge>
20
15
  </template>
21
- <span>{{ tooltip.text }}</span>
22
- </v-tooltip>
16
+ </common-tooltip>
23
17
  </template>
24
18
 
25
19
  <script>
26
- import TooltipMixins from "./mixins/tooltip";
27
-
28
20
  export default {
29
- mixins: [TooltipMixins],
30
21
  props: {
31
22
  spec: { type: Object, required: true }
32
- },
33
- data() {
34
- return {
35
- styles: {}
36
- };
37
- },
38
- methods: {
39
- $ready() {
40
- this.$initAccessories();
41
- }
42
23
  }
43
24
  };
44
25
  </script>
@@ -1,41 +1,15 @@
1
1
  <template>
2
- <div>
3
- <v-tooltip
4
- :disabled="tooltip.disabled"
5
- :top="tooltipPositionMatches('top')"
6
- :right="tooltipPositionMatches('right')"
7
- :bottom="tooltipPositionMatches('bottom')"
8
- :left="tooltipPositionMatches('left')"
9
- >
10
- <template v-slot:activator="{ on }">
11
- <div v-on="on">
12
- <common-button :spec="childSpec" :disabled="$isBusy" />
13
- </div>
14
- </template>
15
- <span> {{ tooltip.text }} </span>
16
- </v-tooltip>
17
- </div>
2
+ <common-tooltip :spec="spec">
3
+ <template v-slot:activator="{ on }">
4
+ <common-button :spec="spec" :disabled="$isBusy" :event-handlers="on" />
5
+ </template>
6
+ </common-tooltip>
18
7
  </template>
19
8
 
20
9
  <script>
21
- import tooltipMixin from "./mixins/tooltip";
22
-
23
10
  export default {
24
- mixins: [tooltipMixin],
25
11
  props: {
26
12
  spec: { type: Object, required: true }
27
- },
28
- data() {
29
- return {
30
- tooltip: {},
31
- childSpec: Object.assign({}, this.spec, { id: undefined })
32
- };
33
- },
34
- methods: {
35
- $ready() {
36
- this.tooltip = this.spec.tooltip || { disabled: true };
37
- this.childSpec = Object.assign({}, this.spec, { id: undefined });
38
- }
39
13
  }
40
14
  };
41
15
  </script>
@@ -10,10 +10,10 @@
10
10
  // https://chartkick.com/vue
11
11
 
12
12
  import annotation from "../mixins/chart/annotation.js";
13
- import tooltip from "../mixins/chart/tooltip.js";
13
+ // import tooltip from "../mixins/chart/tooltip.js";
14
14
 
15
15
  export default {
16
- mixins: [annotation, tooltip],
16
+ mixins: [annotation],
17
17
  props: {
18
18
  spec: { type: Object, required: true }
19
19
  },
@@ -9,10 +9,10 @@
9
9
 
10
10
  <script>
11
11
  import annotation from "../mixins/chart/annotation.js";
12
- import tooltip from "../mixins/chart/tooltip.js";
12
+ // import tooltip from "../mixins/chart/tooltip.js";
13
13
 
14
14
  export default {
15
- mixins: [annotation, tooltip],
15
+ mixins: [annotation],
16
16
  props: {
17
17
  spec: { type: Object, required: true }
18
18
  },
@@ -3,28 +3,9 @@
3
3
  </template>
4
4
 
5
5
  <script>
6
- import TooltipMixins from "./mixins/tooltip";
7
6
  export default {
8
- mixins: [TooltipMixins],
9
7
  props: {
10
8
  spec: { type: Object, required: true }
11
- },
12
- data: function() {
13
- return {
14
- tooltip: {}
15
- };
16
- },
17
- methods: {
18
- $ready() {
19
- this.tooltip = this.spec.tooltip || {};
20
- }
21
9
  }
22
10
  };
23
11
  </script>
24
-
25
- <style lang="scss" scoped>
26
- // Prevent hover effect if the chip is not clickable.
27
- span.theme--light.v-chip:hover:before {
28
- opacity: 0;
29
- }
30
- </style>
@@ -60,12 +60,7 @@ export default {
60
60
  },
61
61
  methods: {
62
62
  $ready() {
63
- this.options = this.normalizedOptions();
64
- this.append = this.spec.append || {};
65
- this.rules = this.$validation();
66
- if (this.defaultValue) {
67
- this.fieldModel = this.defaultValue;
68
- }
63
+ this.updateData();
69
64
  },
70
65
  normalizedOptions() {
71
66
  return this.spec.options.map(i => {
@@ -88,6 +83,17 @@ export default {
88
83
  GLib.action.execute(onChange, this);
89
84
  });
90
85
  });
86
+ },
87
+ updateData() {
88
+ this.options = this.normalizedOptions();
89
+ this.append = this.spec.append || {};
90
+ this.rules = this.$validation();
91
+ if (this.defaultValue) {
92
+ this.fieldModel = this.defaultValue;
93
+ }
94
+ },
95
+ $registryEnabled() {
96
+ return false;
91
97
  }
92
98
  }
93
99
  };
@@ -1,20 +1,10 @@
1
1
  <template>
2
2
  <div :style="genericStyles()" :class="$classes()">
3
3
  <div v-for="(group, groupIndex) in groupSpecs" :key="`group${groupIndex}`">
4
- <input
5
- v-if="isDeleted(groupIndex)"
6
- type="hidden"
7
- :name="`${spec.name}[${groupIndex}][_destroy]`"
8
- value="1"
9
- />
4
+ <input v-if="isDeleted(groupIndex)" type="hidden" :name="`${spec.name}[${groupIndex}][_destroy]`" value="1" />
10
5
  <div :style="{ display: isDeleted(groupIndex) ? 'none' : 'block' }">
11
- <v-icon
12
- class="float-left mr-2"
13
- color="error"
14
- @click="removeGroup(groupIndex)"
15
- >remove_circle</v-icon
16
- >
17
- <h4>{{ spec.titlePrefix }} {{ groupIndex + 1 }}</h4>
6
+ <v-icon class="float-left mr-2" color="error" @click="removeGroup(groupIndex)">remove_circle</v-icon>
7
+ <h4 v-if="spec.titlePrefix">{{ spec.titlePrefix }} {{ groupIndex + 1 }}</h4>
18
8
  <panels-responsive :spec="group" />
19
9
  </div>
20
10
  </div>
@@ -1,13 +1,17 @@
1
1
  <template>
2
- <div :class="$classes()">
3
- <v-tooltip
2
+ <common-tooltip :spec="spec">
3
+ <template v-slot:activator="{ on }">
4
+ <!-- <common-button :spec="spec" :disabled="$isBusy" :event-handlers="on" /> -->
5
+
6
+ <div :class="$classes()">
7
+ <!-- <v-tooltip
4
8
  :disabled="tooltip.disabled"
5
9
  :top="tooltipPositionMatches('top')"
6
10
  :right="tooltipPositionMatches('right')"
7
11
  :bottom="tooltipPositionMatches('bottom')"
8
12
  :left="tooltipPositionMatches('left')"
9
13
  >
10
- <template v-slot:activator="{ on }">
14
+ <template v-slot:activator="{ on }"> -->
11
15
  <v-radio
12
16
  :label="spec.label"
13
17
  :value="spec.value.presence() || vuetifyEmptyString"
@@ -22,29 +26,31 @@
22
26
  <glib-component :spec="item" />
23
27
  </div>
24
28
  </div>
25
- </template>
29
+ <!-- </template>
26
30
  <span>{{ tooltip.text }}</span>
27
- </v-tooltip>
28
- </div>
31
+ </v-tooltip> -->
32
+ </div>
33
+ </template>
34
+ </common-tooltip>
29
35
  </template>
30
36
 
31
37
  <script>
32
- import TooltipMixins from "../mixins/tooltip";
38
+ // import TooltipMixins from "../mixins/tooltip";
33
39
 
34
40
  export default {
35
- mixins: [TooltipMixins],
41
+ // mixins: [TooltipMixins],
36
42
  props: {
37
43
  spec: { type: Object, required: true }
38
- },
39
- data() {
40
- return {
41
- tooltip: {}
42
- };
43
- },
44
- methods: {
45
- $ready() {
46
- this.tooltip = this.spec.tooltip || { disabled: true };
47
- }
48
44
  }
45
+ // data() {
46
+ // return {
47
+ // tooltip: {}
48
+ // };
49
+ // },
50
+ // methods: {
51
+ // $ready() {
52
+ // this.tooltip = this.spec.tooltip || { disabled: true };
53
+ // }
54
+ // }
49
55
  };
50
56
  </script>
@@ -1,10 +1,15 @@
1
1
  <template>
2
+ <!-- In the current Vuetify, there seems to be a bug with validate-on-blur where once a radioGroup has a validation error,
3
+ the error can only be removed by submitting the form. But this is better than the alternative, which is radioGroup showing
4
+ validation error on page load.
5
+ -->
2
6
  <v-radio-group
3
7
  v-model="fieldModel"
4
8
  :name="fieldName"
5
9
  :disabled="spec.readOnly"
6
10
  :rules="$validation()"
7
11
  :row="spec.row"
12
+ validate-on-blur
8
13
  @change="onChange"
9
14
  >
10
15
  <div v-for="(childView, index) in spec.childViews" :key="index">
@@ -56,7 +56,7 @@ export default {
56
56
  data: () => ({
57
57
  customToolbar: [
58
58
  ["bold", "italic", "strike"],
59
- [{ header: 1 }, { header: 2 }],
59
+ [{ header: 1 }, { header: 2 }, { header: 3 }],
60
60
  [{ list: "ordered" }, { list: "bullet" }],
61
61
  ["image", "link"]
62
62
  ],
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <fields-genericSelect :spec="spec" />
2
+ <fields-genericSelect ref="delegate" :spec="updatedSpec" />
3
3
  </template>
4
4
 
5
5
  <script>
@@ -12,6 +12,20 @@ export default {
12
12
  },
13
13
  props: {
14
14
  spec: { type: Object, required: true }
15
+ },
16
+ data() {
17
+ return {
18
+ updatedSpec: this.spec
19
+ };
20
+ },
21
+ methods: {
22
+ $ready() {
23
+ this.updatedSpec = this.spec;
24
+ },
25
+ action_merge(mergedSpec) {
26
+ Object.assign(this.updatedSpec, mergedSpec);
27
+ this.$refs.delegate.updateData();
28
+ }
15
29
  }
16
30
  };
17
31
  </script>
@@ -1,34 +1,27 @@
1
1
  <template>
2
- <v-tooltip
3
- :disabled="tooltip.disabled"
4
- :top="tooltipPositionMatches('top')"
5
- :right="tooltipPositionMatches('right')"
6
- :bottom="tooltipPositionMatches('bottom')"
7
- :left="tooltipPositionMatches('left')"
8
- >
2
+ <common-tooltip :spec="spec">
9
3
  <template v-slot:activator="{ on }">
10
4
  <common-badge :spec="spec">
11
5
  <!-- TODO: Add support for href and :rel="$rel()" -->
12
6
  <v-img
13
7
  :src="spec.url || spec.base64Data"
14
8
  :style="styles"
15
- v-on="on"
16
9
  @click="$onClick()"
10
+ v-on="on"
17
11
  >
18
12
  <!-- <v-progress-circular v-if="$isBusy" indeterminate /> -->
19
13
  </v-img>
20
14
  </common-badge>
21
15
  </template>
22
- <span>{{ tooltip.text }}</span>
23
- </v-tooltip>
16
+ </common-tooltip>
24
17
  </template>
25
18
 
26
19
  <script>
27
20
  import Vue from "vue";
28
- import TooltipMixins from "./mixins/tooltip";
21
+ // import TooltipMixins from "./mixins/tooltip";
29
22
 
30
23
  export default {
31
- mixins: [TooltipMixins],
24
+ // mixins: [TooltipMixins],
32
25
  props: {
33
26
  spec: { type: Object, required: true }
34
27
  },
@@ -39,7 +32,7 @@ export default {
39
32
  },
40
33
  methods: {
41
34
  $ready() {
42
- this.$initAccessories();
35
+ // this.$initAccessories();
43
36
 
44
37
  const styles = this.genericStyles(Object.assign({}, this.spec));
45
38
  this.styles = styles;
@@ -23,7 +23,6 @@ export default {
23
23
  Utils.type.ifObject(val.format, spec => {
24
24
  augmentedRules = augmentedRules.concat([
25
25
  v => {
26
- console.log("VALIDATE1");
27
26
  if (v && !v.match(spec.regex)) {
28
27
  return spec.message;
29
28
  }
@@ -202,10 +202,7 @@ export default {
202
202
  // Has to be executed before $ready(). This executes regardless of whether a form is found because fields
203
203
  // may be used without a form.
204
204
  this.fieldName = this.spec.name;
205
- // Don't initialize fieldModel unnecessarily because this will trigger validations on page load
206
- if (this.spec.value) {
207
- this.fieldModel = this._sanitizeValue(this.spec.value);
208
- }
205
+ this.fieldModel = this._sanitizeValue(this.spec.value);
209
206
  }
210
207
  },
211
208
  $classes(spec) {
@@ -5,13 +5,21 @@
5
5
  :md="md.cols"
6
6
  :sm="sm.cols"
7
7
  :style="cssStyles()"
8
+ :class="$classes()"
8
9
  :cols="xs.cols || 12"
10
+ :order-xl="xl.order"
11
+ :order-lg="lg.order"
12
+ :order-md="md.order"
13
+ :order-sm="sm.order"
14
+ :order="xs.order"
9
15
  >
10
- <panels-responsive :spec="spec" />
16
+ <panels-responsive :spec="innerSpec()" />
11
17
  </v-col>
12
18
  </template>
13
19
 
14
20
  <script>
21
+ import Hash from "../../utils/hash";
22
+
15
23
  export default {
16
24
  props: {
17
25
  spec: { type: Object, required: true }
@@ -33,8 +41,12 @@ export default {
33
41
  this.sm = this.spec.sm || this.sm;
34
42
  this.xs = this.spec.xs || this.xs;
35
43
  },
44
+ innerSpec() {
45
+ // Remove properties that are handled by the container (i.e. v-col).
46
+ return new Hash(this.spec).without("padding").without("styleClasses");
47
+ },
36
48
  cssStyles() {
37
- const styles = this.$styles();
49
+ const styles = Object.assign({}, this.$styles());
38
50
  switch (this.$vuetify.breakpoint.name) {
39
51
  case "xl":
40
52
  this.applyStyles(styles, this.xs);
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div style="display: contents;">
3
- <common-responsive v-if="!hoverViewsSpec" :spec="spec" />
3
+ <!-- <common-responsive v-if="!hoverViewsSpec" :spec="spec" /> -->
4
4
  <v-menu
5
- v-else
5
+ v-if="hoverViewsSpec"
6
6
  :close-on-content-click="false"
7
7
  open-on-hover
8
8
  offset-x
@@ -18,6 +18,12 @@
18
18
  <common-responsive :spec="hoverViewsSpec" />
19
19
  </v-card>
20
20
  </v-menu>
21
+ <common-tooltip v-else :spec="spec">
22
+ <template v-slot:activator="{ on }">
23
+ <common-responsive :spec="spec" :event-handlers="on" />
24
+ <!-- <common-button :spec="spec" :disabled="$isBusy" :event-handlers="on" /> -->
25
+ </template>
26
+ </common-tooltip>
21
27
  </div>
22
28
  </template>
23
29
 
@@ -31,4 +31,7 @@ ul.breadcrumbs {
31
31
  content: "/\00a0";
32
32
  }
33
33
  }
34
+ li > * {
35
+ vertical-align: text-top;
36
+ }
34
37
  </style>
package/index.js CHANGED
@@ -50,6 +50,7 @@ import ResponsivePanel from "./components/panels/responsive";
50
50
  import Component from "./components/component";
51
51
  import CommonIcon from "./components/_icon";
52
52
  import CommonBadge from "./components/_badge";
53
+ import CommonTooltip from "./components/_tooltip";
53
54
  import CommonButton from "./components/_button";
54
55
  import CommonChip from "./components/_chip";
55
56
  import CommonMessage from "./components/_message";
@@ -61,6 +62,7 @@ Vue.component("panels-responsive", ResponsivePanel);
61
62
  Vue.component("common-button", CommonButton);
62
63
  Vue.component("common-icon", CommonIcon);
63
64
  Vue.component("common-badge", CommonBadge);
65
+ Vue.component("common-tooltip", CommonTooltip);
64
66
  Vue.component("common-chip", CommonChip);
65
67
  Vue.component("common-message", CommonMessage);
66
68
  Vue.component("common-dropdownMenu", CommonDropdownMenu);
@@ -174,6 +176,7 @@ document.addEventListener("DOMContentLoaded", () => {
174
176
  stateUpdatedAt: null,
175
177
  webSocket: { channels: {}, header: {} },
176
178
  actionCable: { channels: {} },
179
+ registeredComponents: [],
177
180
  temp: {}
178
181
  };
179
182
  new Vue({
package/nav/appbar.vue CHANGED
@@ -1,22 +1,11 @@
1
1
  <template>
2
2
  <div>
3
- <nav-drawer
4
- v-if="page.leftDrawer"
5
- :trigger="drawerTrigger"
6
- :permanent="isPermanent"
7
- :spec="page.leftDrawer"
8
- />
3
+ <nav-drawer v-if="page.leftDrawer" :trigger="drawerTrigger" :permanent="isPermanent" :spec="page.leftDrawer" />
9
4
 
10
- <v-toolbar :style="style" :height="navBar.height" :class="cssClasses()">
11
- <v-app-bar-nav-icon
12
- v-if="page.leftDrawer && !isPermanent"
13
- style="color: inherit;"
14
- @click="drawerTrigger = new Date()"
15
- ></v-app-bar-nav-icon>
16
- <v-progress-circular
17
- v-if="$root.vueApp.indicator"
18
- indeterminate
19
- ></v-progress-circular>
5
+ <v-toolbar v-if="navBar.height" :style="style" :height="navBar.height" :class="cssClasses()">
6
+ <v-app-bar-nav-icon v-if="page.leftDrawer && !isPermanent" style="color: inherit;"
7
+ @click="drawerTrigger = new Date()"></v-app-bar-nav-icon>
8
+ <v-progress-circular v-if="$root.vueApp.indicator" indeterminate></v-progress-circular>
20
9
  <div v-else>
21
10
  <panels-responsive v-if="navBar.logo" :spec="navBar.logo" />
22
11
  <v-toolbar-title v-else-if="navBar.showTitle">{{
@@ -28,35 +17,16 @@
28
17
  <!-- See https://codesandbox.io/embed/14ry9r3lll -->
29
18
  <v-toolbar-items class="hidden-sm-and-down">
30
19
  <template v-for="(btn, index) in navBar.rightButtons">
31
- <common-dropdownMenu
32
- v-if="btn.childButtons"
33
- :key="index"
34
- :spec="btn"
35
- :disabled="$isBusy"
36
- />
37
- <common-button
38
- v-else
39
- :key="`right_${index}`"
40
- :spec="buttonSpec(btn)"
41
- :disabled="$isBusy"
42
- />
20
+ <common-dropdownMenu v-if="btn.childButtons" :key="index" :spec="btn" :disabled="$isBusy" />
21
+ <common-button v-else :key="`right_${index}`" :spec="buttonSpec(btn)" :disabled="$isBusy" />
43
22
  </template>
44
23
  </v-toolbar-items>
45
24
 
46
- <common-dropdownMenu
47
- v-if="showMobileMenu"
48
- class="hidden-md-and-up"
49
- :spec="mobileMenu"
50
- />
25
+ <common-dropdownMenu v-if="showMobileMenu" class="hidden-md-and-up" :spec="mobileMenu" />
51
26
 
52
27
  <views-avatar v-if="navBar.imageUrl" :spec="avatarSpec(navBar)" />
53
28
 
54
- <v-btn
55
- v-if="viewSourceEnabled()"
56
- icon
57
- style="color: inherit;"
58
- @click="viewSource"
59
- >
29
+ <v-btn v-if="viewSourceEnabled()" icon style="color: inherit;" @click="viewSource">
60
30
  <v-icon>zoom_in</v-icon>
61
31
  </v-btn>
62
32
  </v-toolbar>
@@ -75,7 +45,7 @@ export default {
75
45
  props: {
76
46
  page: { type: Object, required: true }
77
47
  },
78
- data: function() {
48
+ data: function () {
79
49
  return {
80
50
  drawerTrigger: null,
81
51
  navBar: {},
@@ -84,11 +54,11 @@ export default {
84
54
  };
85
55
  },
86
56
  computed: {
87
- style: function() {
57
+ style: function () {
88
58
  return Object.assign(this.genericStyles(this.navBar), this.leftPadding);
89
59
  },
90
60
  // We could've used the `app` attribute on `v-toolbar` if it didn't conflict with our header-footer css.
91
- leftPadding: function() {
61
+ leftPadding: function () {
92
62
  switch (this.$vuetify.breakpoint.name) {
93
63
  case "lg":
94
64
  case "xl":
@@ -124,11 +94,11 @@ export default {
124
94
  styleClasses: styleClasses
125
95
  });
126
96
  },
127
- viewSourceEnabled: function() {
97
+ viewSourceEnabled: function () {
128
98
  // return process.env.NODE_ENV === "development";
129
99
  return false;
130
100
  },
131
- viewSource: function() {
101
+ viewSource: function () {
132
102
  const properties = { message: this.page };
133
103
  Utils.launch.dialog.open(Object.assign({}, properties));
134
104
  },
@@ -157,11 +127,13 @@ export default {
157
127
  &.icon {
158
128
  color: inherit;
159
129
  }
130
+
160
131
  &.text {
161
132
  color: inherit;
162
133
  }
163
134
  }
164
135
  }
136
+
165
137
  .pages-navBar {
166
138
  box-shadow: none !important;
167
139
  }
package/nav/drawer.vue CHANGED
@@ -51,6 +51,7 @@
51
51
  // import UrlUtils from "../utils/url";
52
52
  import NavDrawerButton from "./drawerButton";
53
53
  import NavDrawerLabel from "./drawerLabel";
54
+ import bus from "../utils/eventBus";
54
55
 
55
56
  export default {
56
57
  components: {
@@ -102,12 +103,10 @@ export default {
102
103
  methods: {
103
104
  $ready() {
104
105
  this.shrunk = this.mini = this.cssClasses.includes("mini");
105
-
106
- // this.$el.addEventListener('drawers/clickButton', () => { this.updateState(false) }, false)
107
- this.$el.addEventListener("drawers/clickButton", () => {
108
- if (!this.permanent) {
109
- this.updateState(false);
110
- }
106
+ },
107
+ $mounted() {
108
+ bus.$on("drawers/toggle", () => {
109
+ this.updateState(!this.state);
111
110
  });
112
111
  },
113
112
  updateState(state) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -45,4 +45,4 @@
45
45
  "eslint-plugin-vuetify": "^1.0.0-beta.3",
46
46
  "prettier": "^1.18.2"
47
47
  }
48
- }
48
+ }
@@ -1,13 +1,22 @@
1
1
  export default {
2
2
  install: (Vue, options) => {
3
- Vue.prototype.updatables = {};
3
+ // Vue.prototype.updatables = {};
4
+
4
5
  Vue.mixin({
5
6
  created: function() {
6
7
  let spec = this.spec;
7
- if (spec && spec.id) {
8
- Object.assign(this.updatables, {
9
- [spec.id]: this
10
- });
8
+ if (spec && spec.id && this.$registryEnabled()) {
9
+ GLib.component.register(spec.id, this);
10
+ // Object.assign(this.updatables, {
11
+ // [spec.id]: this
12
+ // });
13
+ // console.log("U", this.updatables);
14
+ }
15
+ },
16
+ methods: {
17
+ $registryEnabled() {
18
+ // Common classes such as `_select` need to return false so that it doesn't override its parent (e.g. `select`).
19
+ return true;
11
20
  }
12
21
  }
13
22
  });
package/utils/app.js CHANGED
@@ -1,3 +1,4 @@
1
+ // TODO: Move to component.js
1
2
  export default class {
2
3
  static componentName(jsonName) {
3
4
  const name = `${jsonName.replace("/", "-").replace(/-v1$/, "")}`;
@@ -0,0 +1,12 @@
1
+ export default class {
2
+ static findById(id) {
3
+ const component = window.vueApp.registeredComponents[id];
4
+ if (component) {
5
+ return component;
6
+ }
7
+ console.error("Component not found: " + id);
8
+ }
9
+ static register(id, component) {
10
+ window.vueApp.registeredComponents[id] = component;
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ import Vue from "vue";
2
+ export default new Vue();
package/utils/public.js CHANGED
@@ -3,6 +3,7 @@ import Url from "./url";
3
3
  import Http from "./http";
4
4
  import Type from "./type";
5
5
  import Form from "./form";
6
+ import Component from "./component";
6
7
 
7
8
  export default class {
8
9
  static get action() {
@@ -20,4 +21,7 @@ export default class {
20
21
  static get form() {
21
22
  return Form;
22
23
  }
24
+ static get component() {
25
+ return Component;
26
+ }
23
27
  }
@@ -1,20 +0,0 @@
1
- // TODO: Make tooltip reusable component. See _badge.vue
2
- export default {
3
- data() {
4
- return {
5
- tooltip: {}
6
- };
7
- },
8
- methods: {
9
- $initAccessories() {
10
- this.tooltip = this.spec.tooltip || { disabled: true };
11
- },
12
- tooltipPositionMatches(position) {
13
- if (this.spec.tooltip && this.spec.tooltip.position) {
14
- return position == this.spec.tooltip.position;
15
- } else {
16
- return position == "bottom";
17
- }
18
- }
19
- }
20
- };
package/nav/content.vue DELETED
@@ -1,40 +0,0 @@
1
- <template>
2
- <v-main :style="outerStyle()">
3
- <panels-responsive v-if="template == 'fullWidth'" :spec="spec" />
4
- <v-col v-else cols="12" md="8" offset-md="2" :style="innerStyle()">
5
- <template v-if="spec">
6
- <panels-responsive v-if="template == 'flatCentered'" :spec="spec" />
7
- <v-card v-else :height="$length(spec.height)">
8
- <panels-responsive :spec="spec" />
9
- </v-card>
10
- </template>
11
- </v-col>
12
- </v-main>
13
- </template>
14
-
15
- <script>
16
- export default {
17
- props: {
18
- spec: { type: Object, required: true },
19
- template: { type: String, default: null },
20
- fullHeight: { type: Boolean, required: true }
21
- },
22
- methods: {
23
- outerStyle() {
24
- if (this.fullHeight) {
25
- return { height: "100%" };
26
- }
27
- return {};
28
- },
29
- innerStyle() {
30
- const style = { padding: "0" };
31
- if (this.fullHeight) {
32
- return Object.assign(style, { height: "100%" });
33
- }
34
- return style;
35
- }
36
- }
37
- };
38
- </script>
39
-
40
- <style scoped></style>