@processmaker/screen-builder 3.0.1 → 3.0.2

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.
@@ -1,32 +1,29 @@
1
1
  <template>
2
- <div
3
- class="screen-templates-container"
4
- data-cy="screen-templates-section"
5
- >
2
+ <div class="screen-templates-container" data-cy="screen-templates-section">
6
3
  <div class="d-flex justify-content-between">
7
4
  <h6 class="pt-2">{{ $t("Select a Template") }}</h6>
8
5
  <button
9
6
  class="panel-close-btn"
10
- @click="$emit('close-templates-panel')"
11
7
  data-cy="close-templates-section"
8
+ @click="$emit('close-templates-panel')"
12
9
  >
13
10
  <i class="fas fa-times"></i>
14
11
  </button>
15
12
  </div>
16
13
  <div class="d-flex template-tabs justify-content-center">
17
14
  <b-button
18
- @click="showMyTemplates"
19
15
  class="d-inline default-template-btn px-1"
20
16
  :class="{ 'my-templates-selected': myTemplatesSelected }"
21
17
  data-cy="my-templates-tab"
18
+ @click="showMyTemplates"
22
19
  >
23
20
  {{ $t("My Templates") }}
24
21
  </b-button>
25
22
  <b-button
26
- @click="showSharedTemplates"
27
23
  class="d-inline default-template-btn"
28
24
  :class="{ 'shared-templates-selected': sharedTemplatesSelected }"
29
25
  data-cy="shared-templates-tab"
26
+ @click="showSharedTemplates"
30
27
  >
31
28
  {{ $t("Shared Templates") }}
32
29
  </b-button>
@@ -38,6 +35,9 @@
38
35
  data-cy="my-templates-list"
39
36
  >
40
37
  <b-card-group>
38
+ <b-card-body v-if="loading" class="d-flex justify-content-center">
39
+ <b-spinner variant="primary" label="Spinning"></b-spinner>
40
+ </b-card-body>
41
41
  <b-card-body
42
42
  v-if="noMyTemplatesFound"
43
43
  class="p-2 h-100 overflow-auto"
@@ -45,12 +45,14 @@
45
45
  <h5>{{ $t("No templates found.") }}</h5>
46
46
  </b-card-body>
47
47
  <screen-template-card
48
- v-else
49
48
  v-for="template in myTemplatesData"
49
+ v-else
50
50
  :key="template.id"
51
51
  :template="template"
52
52
  :screen-id="screenId"
53
- :currentScreenPage="currentScreenPage"
53
+ :current-screen-page="currentScreenPage"
54
+ :active-template-id="activeTemplateId"
55
+ @toggle-active="setActiveTemplate"
54
56
  />
55
57
  </b-card-group>
56
58
  </div>
@@ -60,6 +62,9 @@
60
62
  data-cy="shared-templates-list"
61
63
  >
62
64
  <b-card-group>
65
+ <b-card-body v-if="loading" class="d-flex justify-content-center">
66
+ <b-spinner variant="primary" label="Spinning"></b-spinner>
67
+ </b-card-body>
63
68
  <b-card-body
64
69
  v-if="noSharedTemplatesFound"
65
70
  class="p-2 h-100 overflow-auto"
@@ -67,96 +72,116 @@
67
72
  <h5>{{ $t("No templates found.") }}</h5>
68
73
  </b-card-body>
69
74
  <screen-template-card
70
- v-else
71
75
  v-for="template in sharedTemplatesData"
76
+ v-else
72
77
  :key="template.id"
73
78
  :template="template"
74
79
  :screen-id="screenId"
75
- :currentScreenPage="currentScreenPage"
80
+ :current-screen-page="currentScreenPage"
81
+ :active-template-id="activeTemplateId"
82
+ @toggle-active="setActiveTemplate"
76
83
  />
77
84
  </b-card-group>
78
85
  </div>
79
86
  </div>
80
87
  </div>
81
88
  </template>
82
-
89
+
83
90
  <script>
84
- import ScreenTemplateCard from './ScreenTemplateCard.vue';
91
+ import ScreenTemplateCard from "./ScreenTemplateCard.vue";
85
92
 
86
- export default {
87
- components: {
88
- ScreenTemplateCard,
93
+ export default {
94
+ components: {
95
+ ScreenTemplateCard
96
+ },
97
+ props: {
98
+ screenId: {
99
+ type: Number,
100
+ required: true
101
+ },
102
+ currentScreenPage: {
103
+ type: Number,
104
+ default: 0
89
105
  },
90
- props: {
91
- screenId: {
92
- type: Number,
93
- required: true,
94
- },
95
- currentScreenPage: {
96
- type: Number,
97
- default: 0,
98
- },
99
- screenType: {
100
- type: String,
101
- default: 'FORM',
102
- }
106
+ screenType: {
107
+ type: String,
108
+ default: "FORM"
109
+ }
110
+ },
111
+ data() {
112
+ return {
113
+ myTemplatesData: null,
114
+ sharedTemplatesData: null,
115
+ myTemplatesSelected: true,
116
+ sharedTemplatesSelected: false,
117
+ noMyTemplatesFound: false,
118
+ noSharedTemplatesFound: false,
119
+ activeTemplateId: null,
120
+ loading: false
121
+ };
122
+ },
123
+ mounted() {
124
+ this.showMyTemplates();
125
+ },
126
+ methods: {
127
+ setActiveTemplate(id) {
128
+ // If the same template that is already active is clicked, it deactivates it.
129
+ this.activeTemplateId = this.activeTemplateId === id ? null : id;
103
130
  },
104
- data() {
105
- return {
106
- myTemplatesData: null,
107
- sharedTemplatesData: null,
108
- myTemplatesSelected: true,
109
- sharedTemplatesSelected: false,
110
- noMyTemplatesFound: false,
111
- noSharedTemplatesFound: false,
112
- };
131
+ showMyTemplates() {
132
+ this.myTemplatesSelected = true;
133
+ this.sharedTemplatesSelected = false;
134
+ this.activeTemplateId = null;
135
+ this.fetchMyTemplates();
113
136
  },
114
- methods: {
115
- showMyTemplates() {
116
- this.myTemplatesSelected = true;
117
- this.sharedTemplatesSelected = false;
118
- this.fetchMyTemplates();
119
- },
120
- fetchMyTemplates() {
121
- ProcessMaker.apiClient
122
- .get(
123
- `templates/screen?is_public=0&screen_type=${this.screenType}`,
124
- )
137
+ fetchMyTemplates() {
138
+ this.loading = true;
139
+ ProcessMaker.apiClient
140
+ .get(`templates/screen?is_public=0&screen_type=${this.screenType}`)
125
141
  .then((response) => {
126
142
  this.myTemplatesData = response.data.data;
127
- if (this.myTemplatesData.length === 0 || this.myTemplatesData === undefined) {
143
+ if (
144
+ this.myTemplatesData.length === 0 ||
145
+ this.myTemplatesData === undefined
146
+ ) {
128
147
  this.noMyTemplatesFound = true;
129
148
  }
130
149
  })
131
150
  .catch((error) => {
132
151
  console.error(error);
152
+ })
153
+ .finally(() => {
154
+ this.loading = false;
133
155
  });
134
- },
135
- fetchSharedTemplates() {
156
+ },
157
+ fetchSharedTemplates() {
158
+ this.loading = true;
136
159
  ProcessMaker.apiClient
137
- .get(
138
- `templates/screen?is_public=1&screen_type=${this.screenType}`,
139
- )
160
+ .get(`templates/screen?is_public=1&screen_type=${this.screenType}`)
140
161
  .then((response) => {
141
162
  this.sharedTemplatesData = response.data.data;
142
- if (this.sharedTemplatesData.length === 0 || this.sharedTemplatesData === undefined) {
163
+ if (
164
+ this.sharedTemplatesData.length === 0 ||
165
+ this.sharedTemplatesData === undefined
166
+ ) {
143
167
  this.noSharedTemplatesFound = true;
144
168
  }
145
169
  })
146
170
  .catch((error) => {
147
171
  console.error(error);
172
+ })
173
+ .finally(() => {
174
+ this.loading = false;
148
175
  });
149
- },
150
- showSharedTemplates() {
151
- this.myTemplatesSelected = false;
152
- this.sharedTemplatesSelected = true;
153
- this.fetchSharedTemplates();
154
- },
155
176
  },
156
- mounted() {
157
- this.showMyTemplates();
177
+ showSharedTemplates() {
178
+ this.myTemplatesSelected = false;
179
+ this.sharedTemplatesSelected = true;
180
+ this.activeTemplateId = null;
181
+ this.fetchSharedTemplates();
158
182
  }
159
- };
183
+ }
184
+ };
160
185
  </script>
161
186
 
162
187
  <style lang="scss" scoped>
@@ -164,53 +189,56 @@
164
189
  height: 100%;
165
190
  }
166
191
 
167
- .panel-close-btn {
168
- background-color: transparent;
169
- border: none;
170
- color: #596372;
171
- }
172
-
173
- .template-tabs {
174
- padding: 4px;
175
- background-color: #E9ECF1;
176
- border-radius: 8px;
177
- margin-top: 0.5rem;
178
- margin-bottom: 0.5rem;
179
- }
192
+ .panel-close-btn {
193
+ background-color: transparent;
194
+ border: none;
195
+ color: #596372;
196
+ }
180
197
 
181
- .default-template-btn {
182
- width: 50%;
183
- background-color: transparent;
184
- border: none;
185
- color: #596372;
186
- font-size: 12px;
187
- padding-left: 0px;
188
- padding-right: 0px;
189
- text-transform: none;
190
- }
198
+ .template-tabs {
199
+ padding: 4px;
200
+ background-color: #e9ecf1;
201
+ border-radius: 8px;
202
+ margin-top: 0.5rem;
203
+ margin-bottom: 0.5rem;
204
+ }
191
205
 
192
- .my-templates-selected {
193
- background-color: #ffffff;
194
- color: #20242A;
195
- border-radius: 8px;
196
- border: none;
197
- font-weight: 600;
198
- font-size: 12px;
199
- padding-left: 0px;
200
- padding-right: 0px;
201
- box-shadow: 0px 3px 6px -3px rgb(0, 0, 0, 0.05), 0px 2px 4px -2px rgba(0, 0, 0, 0.05), 0px 1px 2px -1px rgb(0, 0, 0, 0.05), 0px 1px 0px -1px rgb(0, 0, 0, 0.05);
202
- }
206
+ .default-template-btn {
207
+ width: 50%;
208
+ background-color: transparent;
209
+ border: none;
210
+ color: #596372;
211
+ font-size: 12px;
212
+ padding-left: 0px;
213
+ padding-right: 0px;
214
+ text-transform: none;
215
+ }
203
216
 
204
- .shared-templates-selected {
205
- background-color: #ffffff;
206
- color: #20242A;
207
- border-radius: 8px;
208
- border: none;
209
- font-weight: 600;
210
- font-size: 12px;
211
- padding-left: 0px;
212
- padding-right: 0px;
213
- box-shadow: 0px 3px 6px -3px rgb(0, 0, 0, 0.05), 0px 2px 4px -2px rgba(0, 0, 0, 0.05), 0px 1px 2px -1px rgb(0, 0, 0, 0.05), 0px 1px 0px -1px rgb(0, 0, 0, 0.05);
214
- }
217
+ .my-templates-selected {
218
+ background-color: #ffffff;
219
+ color: #20242a;
220
+ border-radius: 8px;
221
+ border: none;
222
+ font-weight: 600;
223
+ font-size: 12px;
224
+ padding-left: 0px;
225
+ padding-right: 0px;
226
+ box-shadow: 0px 3px 6px -3px rgb(0, 0, 0, 0.05),
227
+ 0px 2px 4px -2px rgba(0, 0, 0, 0.05), 0px 1px 2px -1px rgb(0, 0, 0, 0.05),
228
+ 0px 1px 0px -1px rgb(0, 0, 0, 0.05);
229
+ }
215
230
 
231
+ .shared-templates-selected {
232
+ background-color: #ffffff;
233
+ color: #20242a;
234
+ border-radius: 8px;
235
+ border: none;
236
+ font-weight: 600;
237
+ font-size: 12px;
238
+ padding-left: 0px;
239
+ padding-right: 0px;
240
+ box-shadow: 0px 3px 6px -3px rgb(0, 0, 0, 0.05),
241
+ 0px 2px 4px -2px rgba(0, 0, 0, 0.05), 0px 1px 2px -1px rgb(0, 0, 0, 0.05),
242
+ 0px 1px 0px -1px rgb(0, 0, 0, 0.05);
243
+ }
216
244
  </style>
@@ -58,7 +58,7 @@ export default [
58
58
  },
59
59
  {
60
60
  name: 'Design',
61
- fields: ['color', 'bgcolor', 'variant', 'toggle', 'height', 'width'],
61
+ fields: ['color', 'bgcolor', 'variant', 'toggle', 'height', 'width', 'designerMode', 'bgcolormodern'],
62
62
  open: false,
63
63
  },
64
64
  {
@@ -73,7 +73,6 @@
73
73
  "variableStore",
74
74
  "dataSelectionOptions",
75
75
  "singleField"
76
-
77
76
  ];
78
77
  export default {
79
78
  components: {
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div>
3
+ <div>
4
+ <label for="collectiondesigner">{{ $t("Table Style") }}</label>
5
+ <b-form-select
6
+ id="collectiondesigner"
7
+ v-model="designerOptions"
8
+ :options="designerListOptions"
9
+ data-cy="inspector-collection-designer-model"
10
+ />
11
+ </div>
12
+ </div>
13
+ </template>
14
+ <script>
15
+
16
+ import { cloneDeep } from "lodash";
17
+
18
+ const CONFIG_FIELDS = [
19
+ "designerOptions"
20
+ ];
21
+ export default {
22
+ props: ["value", "screenType"],
23
+ data() {
24
+ return {
25
+ fields: [],
26
+ designerOptions: "Classic",
27
+ designerListOptions: [
28
+ {
29
+ text: this.$t('Classic'),
30
+ value: 'Classic',
31
+ },
32
+ {
33
+ text: this.$t('Modern'),
34
+ value: 'Modern',
35
+ },
36
+ ],
37
+ };
38
+ },
39
+ computed: {
40
+ options() {
41
+ return Object.fromEntries(
42
+ CONFIG_FIELDS.map((field) => [field, this[field]])
43
+ );
44
+ }
45
+ },
46
+ watch: {
47
+ value: {
48
+ handler(value) {
49
+ if (!value) {
50
+ return;
51
+ }
52
+ CONFIG_FIELDS.forEach((field) => (this[field] = value[field]));
53
+ },
54
+ immediate: true
55
+ },
56
+ options: {
57
+ handler() {
58
+ this.$emit("input", this.options);
59
+ this.$root.$emit("style-mode", this.options.designerOptions);
60
+ },
61
+ deep: true
62
+ },
63
+ },
64
+ };
65
+ </script>
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <div class="form-group">
3
+ <div>
4
+ <b-button-toolbar>
5
+ <b-button-group size="lg">
6
+ <b-button
7
+ v-for="option in options"
8
+ :key="option.value"
9
+ size="sm"
10
+ variant="outline-light"
11
+ class="color-option"
12
+ :class="['bg-' + parsedColor(option.value)]"
13
+ :title="option.content"
14
+ >
15
+ <i
16
+ class="fas fa-check"
17
+ :class="[
18
+ option.value === value
19
+ ? 'text-light'
20
+ : 'text-' + parsedColor(option.value)
21
+ ]"
22
+ @click="selectColor(option.value)"
23
+ />
24
+ </b-button>
25
+ </b-button-group>
26
+ </b-button-toolbar>
27
+ </div>
28
+ </div>
29
+ </template>
30
+
31
+ <script>
32
+ export default {
33
+ components: {},
34
+ props: {
35
+ /**
36
+ * The label for the color select
37
+ */
38
+ label: {},
39
+ /**
40
+ * The value of the color select. eg. `alert alert-success`
41
+ */
42
+ value: {},
43
+ /**
44
+ * The helper text for the color select (not visible yet)
45
+ */
46
+ helper: {},
47
+ /**
48
+ * The options for the color select
49
+ */
50
+ options: {},
51
+ },
52
+ data() {
53
+ return {
54
+ newColor: ""
55
+ };
56
+ },
57
+ computed: {
58
+ hasColor() {
59
+ return Boolean(this.value);
60
+ }
61
+ },
62
+ methods: {
63
+ emitChanges(value) {
64
+ this.$emit("input", value);
65
+ this.$emit("update-state");
66
+ },
67
+ checkColor() {
68
+ if (this.hasColor) {
69
+ this.emitChanges("");
70
+ }
71
+ },
72
+ selectColor(color) {
73
+ this.emitChanges(color);
74
+ },
75
+ parsedColor(color) {
76
+ return color.split("-")[1];
77
+ }
78
+ }
79
+ };
80
+ </script>
81
+
82
+ <style lang="scss" scoped>
83
+ .image-preview {
84
+ border: 1px solid #ced4da;
85
+ border-radius: 4px;
86
+ height: 4em;
87
+ text-align: center;
88
+ overflow: hidden;
89
+ }
90
+ .color-option {
91
+ left: -8px;
92
+ border-radius: 4px;
93
+ width: 48px;
94
+ height: 48px;
95
+ margin-right: 15px;
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: center;
99
+ position: relative;
100
+ }
101
+ </style>
@@ -2,7 +2,10 @@ export { default as CollectionSelectList } from "./collection-select-list.vue";
2
2
  export { default as CollectionRecordsList } from "./collection-records-list.vue";
3
3
  export { default as collectionDataSource } from "./collection-data-source.vue";
4
4
  export { default as CollectionDisplayMode } from "./collection-display-mode.vue";
5
+ export { default as CollectionDesignerMode } from "./collection-designer-mode.vue";
5
6
  export { default as ColorSelect } from "./color-select.vue";
7
+ export { default as ColorSelectRecord } from "./color-select.vue";
8
+ export { default as ColorSelectModern } from "./color-select-modern.vue";
6
9
  export { default as ColumnSetup } from "./column-setup.vue";
7
10
  export { default as ContainerColumns } from "./container-columns.vue";
8
11
  export { default as DataMapping } from "./data-mapping.vue";
@@ -61,32 +61,34 @@
61
61
  </ul>
62
62
  </template>
63
63
  </uploader-list>
64
- <uploader-drop v-if="uploaderLoaded && isConversationalForm" class="form-control-file">
64
+ <uploader-drop v-if="uploaderLoaded && isConversationalForm" class="form-control-file"
65
+ :class="this.files.length > 0 && !this.multipleUpload ? 'cf-single-file-upload' : ''"
66
+ >
65
67
  <b-button
66
68
  v-if="!required"
67
69
  @click="cfSkipFileUpload"
68
- class="btn cf-skip-btn mb-3"
70
+ class="btn cf-skip-btn"
71
+ :class="showMultiUploadButton ? 'mb-3' : ''"
69
72
  >
70
73
  <i class="fas fa-arrow-left mr-2"></i> {{ $t('Skip Uploading') }}
71
74
  </b-button>
72
-
73
75
  <uploader-btn
74
76
  :attrs="nativeButtonAttrs"
75
77
  :class="[
76
78
  { disabled: disabled },
77
79
  'btn',
78
- showSingleUploadButton ? 'cf-single-upload-btn' : (showMultiUploadButton ? 'cf-multi-upload-btn' : '')
80
+ showSingleUploadButton || files.length === 0 && !showMultiUploadButton ? 'cf-single-upload-btn mt-3' : (showMultiUploadButton ? 'cf-multi-upload-btn' : '')
79
81
  ]"
80
82
  tabindex="0"
81
83
  v-on:keyup.native="browse"
82
84
  :aria-label="$attrs['aria-label']"
83
85
  >
84
- <span v-if="showSingleUploadButton"><i class="far fa-image mr-2"></i> {{ $t('Add File/Photo') }}</span>
86
+ <span v-if="showSingleUploadButton || files.length === 0 && !showMultiUploadButton"><i class="far fa-image mr-2"></i> {{ $t('Add File/Photo') }}</span>
85
87
  <span v-else-if="showMultiUploadButton"><i class="fas fa-plus mr-2"></i> {{ $t('Add another') }}</span>
86
88
  </uploader-btn>
87
89
 
88
- <b-button v-if="files.length !== 0" class="cf-file-upload-submit" variant="primary" @click="emitConversationalFormSubmit" :aria-label="$t('Submit')">
89
- <i class="fas fa-paper-plane"></i> <span v-if="files.length !== 0 && showMultiUploadButton">{{ $t('Send All') }}</span>
90
+ <b-button v-if="files.length !== 0" class="cf-file-upload-submit" :class="!showMultiUploadButton ? 'w-100 mt-3' : ''" variant="primary" @click="emitConversationalFormSubmit" :aria-label="$t('Submit')">
91
+ <i class="fas fa-paper-plane"></i> <span v-if="showMultiUploadButton">{{ $t('Send All') }}</span> <span v-else>{{ $t('Submit File/Photo') }}</span>
90
92
  </b-button>
91
93
  </uploader-drop>
92
94
 
@@ -270,7 +272,7 @@ export default {
270
272
  return this.screenType === 'conversational-forms';
271
273
  },
272
274
  showSingleUploadButton() {
273
- return this.files.length === 0 || !this.multipleUpload;
275
+ return this.files.length === 0 && !this.multipleUpload;
274
276
  },
275
277
  showMultiUploadButton() {
276
278
  return this.files.length !== 0 && this.multipleUpload;
@@ -402,7 +404,6 @@ export default {
402
404
  },
403
405
  setRequestFiles() {
404
406
  _.set(window, `PM4ConfigOverrides.requestFiles["${this.fileDataName}"]`, this.files);
405
- console.log("!!!!!! SET REQUEST FILES", this.valueToSend());
406
407
  this.$emit('input', this.valueToSend());
407
408
  },
408
409
  valueToSend() {
@@ -491,7 +492,7 @@ export default {
491
492
  }
492
493
  this.$delete(this.nativeFiles, id);
493
494
  }
494
-
495
+ this.$emit('file-deleted', this.files);
495
496
  },
496
497
  addToFiles(fileInfo) {
497
498
  if (this.multipleUpload) {
@@ -528,6 +529,19 @@ export default {
528
529
  this.prefix = parent.loopContext + '.';
529
530
  }
530
531
  },
532
+ validateFile(file, acceptFiles){
533
+ const extensions = acceptFiles.filter(item => item.startsWith('.'));
534
+ const mimeTypes = acceptFiles.filter(item => !item.startsWith('.'));
535
+ const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
536
+ const fileType = file.fileType;
537
+ const isExtensionValid = extensions.includes(fileExtension);
538
+ const isMimeTypeValid = mimeTypes.includes(fileType);
539
+
540
+ if (!isExtensionValid && !isMimeTypeValid) {
541
+ return false;
542
+ }
543
+ return true;
544
+ },
531
545
  addFile(file) {
532
546
  if (this.disabled) {
533
547
  file.ignored = true;
@@ -544,7 +558,7 @@ export default {
544
558
 
545
559
  if (this.filesAccept) {
546
560
  file.ignored = true;
547
- if (this.filesAccept.indexOf(file.fileType) !== -1) {
561
+ if (this.validateFile(file, this.filesAccept)) {
548
562
  file.ignored = false;
549
563
  }
550
564
  if (file.ignored) {
@@ -612,7 +626,6 @@ export default {
612
626
  this.$set(this.nativeFiles, id, rootFile);
613
627
  this.addToFiles(fileInfo);
614
628
  } else {
615
- console.log("!!!!!! FILE UPLOADED", name);
616
629
  this.$emit('input', name);
617
630
  }
618
631
  },
@@ -750,4 +763,8 @@ export default {
750
763
  box-shadow: 0px 12px 24px -12px #0000001F;
751
764
  }
752
765
 
766
+ .form-control-file.cf-single-file-upload label {
767
+ display: none;
768
+ }
769
+
753
770
  </style>