@processmaker/screen-builder 2.99.3 → 3.0.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.
Files changed (48) hide show
  1. package/dist/vue-form-builder.css +1 -1
  2. package/dist/vue-form-builder.es.js +9091 -7132
  3. package/dist/vue-form-builder.es.js.map +1 -1
  4. package/dist/vue-form-builder.umd.js +53 -53
  5. package/dist/vue-form-builder.umd.js.map +1 -1
  6. package/package.json +2 -1
  7. package/src/App.vue +14 -2
  8. package/src/DataProvider.js +42 -1
  9. package/src/VariableDataTypeProperties.js +1 -1
  10. package/src/components/ClipboardButton.vue +77 -0
  11. package/src/components/CssIcon.vue +21 -0
  12. package/src/components/ScreenTemplateCard.vue +257 -0
  13. package/src/components/ScreenTemplates.vue +216 -0
  14. package/src/components/ScreenToolbar.vue +24 -2
  15. package/src/components/SelectUserGroup.vue +274 -0
  16. package/src/components/TabsBar.vue +47 -1
  17. package/src/components/accordions.js +7 -1
  18. package/src/components/editor/loop.vue +22 -1
  19. package/src/components/editor/multi-column.vue +22 -2
  20. package/src/components/editor/pagesDropdown.vue +20 -2
  21. package/src/components/index.js +7 -1
  22. package/src/components/inspector/collection-data-source.vue +200 -0
  23. package/src/components/inspector/collection-display-mode.vue +87 -0
  24. package/src/components/inspector/collection-records-list.vue +156 -0
  25. package/src/components/inspector/column-setup.vue +123 -7
  26. package/src/components/inspector/encrypted-config.vue +78 -0
  27. package/src/components/inspector/index.js +4 -0
  28. package/src/components/inspector/page-select.vue +1 -0
  29. package/src/components/renderer/file-upload.vue +136 -3
  30. package/src/components/renderer/form-collection-record-control.vue +248 -0
  31. package/src/components/renderer/form-collection-view-control.vue +236 -0
  32. package/src/components/renderer/form-masked-input.vue +194 -9
  33. package/src/components/renderer/form-record-list.vue +271 -69
  34. package/src/components/renderer/index.js +2 -0
  35. package/src/components/screen-renderer.vue +2 -0
  36. package/src/components/task.vue +2 -1
  37. package/src/components/vue-form-builder.vue +156 -22
  38. package/src/components/vue-form-renderer.vue +10 -2
  39. package/src/form-builder-controls.js +168 -21
  40. package/src/global-properties.js +8 -0
  41. package/src/main.js +60 -1
  42. package/src/mixins/Clipboard.js +153 -0
  43. package/src/mixins/ScreenBase.js +7 -1
  44. package/src/mixins/index.js +1 -0
  45. package/src/store/modules/ClipboardManager.js +79 -0
  46. package/src/store/modules/clipboardModule.js +210 -0
  47. package/src/stories/ClipboardButton.stories.js +66 -0
  48. package/src/stories/PagesDropdown.stories.js +11 -8
@@ -0,0 +1,78 @@
1
+ <template>
2
+
3
+ <div>
4
+ <div class="form-group">
5
+ <b-form-checkbox
6
+ v-model="settings.encrypted"
7
+ @change="emitChanges"
8
+ switch
9
+ >
10
+ {{ $t("Encrypted") }}
11
+ </b-form-checkbox>
12
+
13
+ <div v-if="settings.encrypted">
14
+ <select-user-group
15
+ :key="componentKey"
16
+ :label="$t('Users/Groups to View')"
17
+ v-model="settings.assignments"
18
+ :multiple="true"
19
+ @input="emitChanges"
20
+ />
21
+ </div>
22
+ </div>
23
+ </div>
24
+
25
+ </template>
26
+
27
+ <script>
28
+ import SelectUserGroup from '../../components/SelectUserGroup.vue';
29
+
30
+ export default {
31
+ components: { SelectUserGroup },
32
+ props: {
33
+ label: String,
34
+ value: {
35
+ type: Object,
36
+ default: () => {
37
+ return {
38
+ encrypted: false,
39
+ assignments: {users: [], groups: []},
40
+ };
41
+ }
42
+ },
43
+ helper: String,
44
+ },
45
+ data() {
46
+ return {
47
+ componentKey: 0,
48
+ settings:{
49
+ encrypted: false,
50
+ assignments: {users: [], groups: []},
51
+ },
52
+ };
53
+ },
54
+ watch: {
55
+ value: {
56
+ handler() {
57
+ this.settings = this.value;
58
+ if (this.value.encrypted) {
59
+ // Force to redraw component
60
+ this.componentKey += 1;
61
+ }
62
+ },
63
+ immediate: true,
64
+ },
65
+ },
66
+ methods: {
67
+ emitChanges() {
68
+ if (!this.settings.encrypted) {
69
+ this.settings.assignments = {users: [], groups: []};
70
+ }
71
+ this.$emit("input", this.settings);
72
+ },
73
+ }
74
+ };
75
+ </script>
76
+
77
+ <style lang="scss" scoped>
78
+ </style>
@@ -1,4 +1,7 @@
1
1
  export { default as CollectionSelectList } from "./collection-select-list.vue";
2
+ export { default as CollectionRecordsList } from "./collection-records-list.vue";
3
+ export { default as collectionDataSource } from "./collection-data-source.vue";
4
+ export { default as CollectionDisplayMode } from "./collection-display-mode.vue";
2
5
  export { default as ColorSelect } from "./color-select.vue";
3
6
  export { default as ColumnSetup } from "./column-setup.vue";
4
7
  export { default as ContainerColumns } from "./container-columns.vue";
@@ -23,3 +26,4 @@ export { default as ValidationSelect } from "./validation-select.vue";
23
26
  export { default as LoadingSubmitButton } from "./loading-submit-button.vue";
24
27
  export { default as LabelSubmitButton } from "./label-submit-button.vue";
25
28
  export { default as AnalyticsSelector } from "./analytics-selector.vue";
29
+ export { default as EncryptedConfig } from "./encrypted-config.vue";
@@ -9,6 +9,7 @@
9
9
  :show-labels="false"
10
10
  :options="options.map(option => option.value)"
11
11
  :custom-label="getLabelFromValue"
12
+ data-cy="inspector-eventData"
12
13
  >
13
14
  <template slot="noResult">
14
15
  {{ $t('No elements found. Consider changing the search query.') }}
@@ -19,7 +19,7 @@
19
19
  >
20
20
  <uploader-unsupport/>
21
21
 
22
- <uploader-drop v-if="uploaderLoaded" class="form-control-file">
22
+ <uploader-drop v-if="uploaderLoaded && !isConversationalForm" class="form-control-file">
23
23
  <p>{{ $t('Drop a file here to upload or') }}</p>
24
24
  <uploader-btn
25
25
  :attrs="nativeButtonAttrs"
@@ -33,7 +33,64 @@
33
33
  </uploader-btn>
34
34
  <span v-if="validation === 'required' && !value" class="required">{{ $t('Required') }}</span>
35
35
  </uploader-drop>
36
- <uploader-list>
36
+
37
+ <!-- Render the conversational form file upload UI when the screen type is conversational forms -->
38
+ <uploader-list v-if="isConversationalForm" class="cf-uploader-list">
39
+ <template v-slot="{fileList}">
40
+ <ul v-if="uploading" class="cf-upload-progress">
41
+ <li v-for="file in fileList" :key="file.id || file.name">
42
+ <uploader-file :file="file" list />
43
+ </li>
44
+ </ul>
45
+ <ul v-else>
46
+ <li v-for="(file, i) in files" :key="file.id || i" :data-cy="file.id" class="cf-file-upload-list">
47
+ <span class="cf-filename text-truncate">
48
+ <i :class="getFileIconClass(file)" class="mr-2"></i>
49
+ {{ nativeFiles[file.id].name }}
50
+ </span>
51
+ <b-btn
52
+ variant="outline"
53
+ @click="removeFile(file)"
54
+ v-b-tooltip.hover :title="$t('Delete')"
55
+ class="pr-0"
56
+ :aria-label="$t('Delete file {{ nativeFiles[file.id].name }}')"
57
+ >
58
+ <i class="fas fa-trash-alt"></i>
59
+ </b-btn>
60
+ </li>
61
+ </ul>
62
+ </template>
63
+ </uploader-list>
64
+ <uploader-drop v-if="uploaderLoaded && isConversationalForm" class="form-control-file">
65
+ <b-button
66
+ v-if="!required"
67
+ @click="cfSkipFileUpload"
68
+ class="btn cf-skip-btn mb-3"
69
+ >
70
+ <i class="fas fa-arrow-left mr-2"></i> {{ $t('Skip Uploading') }}
71
+ </b-button>
72
+
73
+ <uploader-btn
74
+ :attrs="nativeButtonAttrs"
75
+ :class="[
76
+ { disabled: disabled },
77
+ 'btn',
78
+ showSingleUploadButton ? 'cf-single-upload-btn' : (showMultiUploadButton ? 'cf-multi-upload-btn' : '')
79
+ ]"
80
+ tabindex="0"
81
+ v-on:keyup.native="browse"
82
+ :aria-label="$attrs['aria-label']"
83
+ >
84
+ <span v-if="showSingleUploadButton"><i class="far fa-image mr-2"></i> {{ $t('Add File/Photo') }}</span>
85
+ <span v-else-if="showMultiUploadButton"><i class="fas fa-plus mr-2"></i> {{ $t('Add another') }}</span>
86
+ </uploader-btn>
87
+
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>
91
+ </uploader-drop>
92
+
93
+ <uploader-list v-else>
37
94
  <template slot-scope = "{ fileList }">
38
95
  <ul v-if="uploading">
39
96
  <li v-for="file in fileList" :key="file.id">
@@ -94,7 +151,7 @@ const ignoreErrors = [
94
151
  export default {
95
152
  components: { ...uploader, RequiredAsterisk },
96
153
  mixins: [uniqIdsMixin],
97
- props: ['label', 'error', 'helper', 'name', 'value', 'controlClass', 'endpoint', 'accept', 'validation', 'parent', 'config', 'multipleUpload'],
154
+ props: ['label', 'error', 'helper', 'name', 'value', 'controlClass', 'endpoint', 'accept', 'validation', 'parent', 'config', 'multipleUpload', 'screenType'],
98
155
  updated() {
99
156
  this.removeDefaultClasses();
100
157
  },
@@ -209,6 +266,15 @@ export default {
209
266
  fileDataName() {
210
267
  return this.prefix + this.name + (this.row_id ? '.' + this.row_id : '');
211
268
  },
269
+ isConversationalForm() {
270
+ return this.screenType === 'conversational-forms';
271
+ },
272
+ showSingleUploadButton() {
273
+ return this.files.length === 0 || !this.multipleUpload;
274
+ },
275
+ showMultiUploadButton() {
276
+ return this.files.length !== 0 && this.multipleUpload;
277
+ },
212
278
  },
213
279
  watch: {
214
280
  filesData: {
@@ -336,6 +402,7 @@ export default {
336
402
  },
337
403
  setRequestFiles() {
338
404
  _.set(window, `PM4ConfigOverrides.requestFiles["${this.fileDataName}"]`, this.files);
405
+ console.log("!!!!!! SET REQUEST FILES", this.valueToSend());
339
406
  this.$emit('input', this.valueToSend());
340
407
  },
341
408
  valueToSend() {
@@ -545,6 +612,7 @@ export default {
545
612
  this.$set(this.nativeFiles, id, rootFile);
546
613
  this.addToFiles(fileInfo);
547
614
  } else {
615
+ console.log("!!!!!! FILE UPLOADED", name);
548
616
  this.$emit('input', name);
549
617
  }
550
618
  },
@@ -608,6 +676,17 @@ export default {
608
676
  : null;
609
677
  }
610
678
  },
679
+ getFileIconClass(file) {
680
+ return this.nativeFiles[file.id].fileType.includes('image/')
681
+ ? 'far fa-image'
682
+ : 'far fa-file';
683
+ },
684
+ emitConversationalFormSubmit($event) {
685
+ this.$emit('cf-submit', $event);
686
+ },
687
+ cfSkipFileUpload() {
688
+ this.$emit('cf-skip-file-upload');
689
+ }
611
690
  },
612
691
  };
613
692
  </script>
@@ -617,4 +696,58 @@ export default {
617
696
  color: red;
618
697
  font-size: 0.8em;
619
698
  }
699
+
700
+ /* Conversational Forms Styling */
701
+ .cf-single-upload-btn {
702
+ background-color: #EAF2FF;
703
+ border: 1px solid #81AFFF;
704
+ color: #81AFFF;
705
+ width: 100%;
706
+ padding: 12px 10px;
707
+ text-transform: capitalize;
708
+ margin: 0;
709
+ }
710
+ .cf-multi-upload-btn {
711
+ background: #fff;
712
+ color: #20242A;
713
+ border: 1px solid #D7DDE5;
714
+ font-size: 12px;
715
+ width: 57%;
716
+ padding: 7px;
717
+ border-radius: 8px;
718
+ margin: 0;
719
+ text-transform: capitalize;
720
+ margin-right: 30px;
721
+ }
722
+ .cf-file-upload-submit {
723
+ border-radius: 8px;
724
+ color: #fff;
725
+ font-size: 12px;
726
+ text-transform: capitalize;
727
+ padding: 7px;
728
+ }
729
+ .cf-filename {
730
+ width: 88%;
731
+ display: inline-block;
732
+ vertical-align: middle;
733
+ }
734
+ .cf-uploader-list {
735
+ margin-bottom: 14px;
736
+ max-height: 75px;
737
+ overflow:auto;
738
+ }
739
+ .cf-file-upload-list {
740
+ color: #20242A;
741
+ margin-bottom: 3px;
742
+ border-bottom: 1px solid #F3F5F7;
743
+ }
744
+ .cf-skip-btn {
745
+ background-color: #FFF;
746
+ border: 1px solid #D7DDE5;
747
+ color: #20242A;
748
+ width: 100%;
749
+ padding: 12px 10px;
750
+ box-shadow: 0px 12px 24px -12px #0000001F;
751
+ }
752
+
620
753
  </style>
@@ -0,0 +1,248 @@
1
+ <template>
2
+ <vue-form-renderer
3
+ ref="collectionRecordControl"
4
+ class="form-collection-record-control"
5
+ :placeholder="placeholder"
6
+ v-model="data"
7
+ mode="preview"
8
+ :config="validatedConfig"
9
+ :computed="computed"
10
+ :custom-css="customCss"
11
+ :watchers="watchers"
12
+ :_parent="_parent"
13
+ />
14
+ </template>
15
+
16
+ <script>
17
+ import VueFormRenderer from "../vue-form-renderer.vue";
18
+ import CollectionRecordsList from "../inspector/collection-records-list.vue";
19
+
20
+ const globalObject = typeof window === "undefined" ? global : window;
21
+
22
+ const defaultConfig = [
23
+ {
24
+ name: "empty",
25
+ items: []
26
+ }
27
+ ];
28
+
29
+ export default {
30
+ components: {
31
+ VueFormRenderer,
32
+ CollectionRecordsList
33
+ },
34
+ props: {
35
+ name: String,
36
+ validationData: null,
37
+ _parent: null,
38
+ record: null,
39
+ collection: {
40
+ type: Object
41
+ },
42
+ collectionmode: {
43
+ type: Object
44
+ },
45
+ taskdraft: Object,
46
+ },
47
+ data() {
48
+ return {
49
+ localData: {},
50
+ config: defaultConfig,
51
+ computed: [],
52
+ customCSS: null,
53
+ watchers: [],
54
+ screenTitle: null,
55
+ selCollectionId: Number,
56
+ selRecordId: Number,
57
+ selDisplayMode: String,
58
+ screenCollectionId: null,
59
+ placeholder: "Select a collection",
60
+ screenType: "",
61
+ hasMustache: false,
62
+ flagDraft: {},
63
+ taskDraft: {}
64
+ };
65
+ },
66
+ computed: {
67
+ validatedConfig() {
68
+ return this.config && this.config[0] ? this.config : defaultConfig;
69
+ },
70
+ data: {
71
+ get() {
72
+ if(this.hasMustache) {
73
+ this.clearDataObject();
74
+ }
75
+ return this.localData;
76
+ },
77
+ set(data) {
78
+ Object.keys(data).forEach((variable) => {
79
+ this.validationData && this.$set(this.validationData, variable, data[variable]);
80
+ });
81
+
82
+ if (this.collection) {
83
+ this.$set(this.collection, 'data', Array.isArray(data) ? data : [data]);
84
+ this.$set(this.collection, 'screen', this.screenCollectionId);
85
+ }
86
+ },
87
+ },
88
+ },
89
+ methods: {
90
+ isSubmitButton(item) {
91
+ return (
92
+ item.config &&
93
+ item.component === "FormButton" &&
94
+ item.config.event === "submit"
95
+ );
96
+ },
97
+ hideSubmitButtons(config) {
98
+ config.forEach((item) => {
99
+ //If the element has containers
100
+ if (Array.isArray(item)) {
101
+ this.hideSubmitButtons(item);
102
+ }
103
+
104
+ //If the element has items
105
+ if (item.items) {
106
+ this.hideSubmitButtons(item.items);
107
+ }
108
+
109
+ //hidden buttons
110
+ if (this.isSubmitButton(item)) {
111
+ item.config.hidden = true;
112
+ }
113
+ });
114
+ },
115
+ disableForm(config) {
116
+ config.forEach((item) => {
117
+ //If the element has containers
118
+ if (Array.isArray(item)) {
119
+ this.disableForm(item);
120
+ }
121
+
122
+ //If the element has items
123
+ if (item.items) {
124
+ this.disableForm(item.items);
125
+ }
126
+
127
+ //Disable element
128
+ if (item && item.config) {
129
+ item.config.disabled = true;
130
+ item.config.readonly = true;
131
+ item.config.editable = false;
132
+ }
133
+ });
134
+ },
135
+ loadScreen(id) {
136
+ this.config = defaultConfig;
137
+ this.computed = [];
138
+ this.customCSS = null;
139
+ this.watchers = [];
140
+ this.screenTitle = null;
141
+
142
+ if (id) {
143
+ this.$dataProvider.getScreen(id).then((response) => {
144
+ this.config = response.data.config;
145
+ this.hideSubmitButtons(this.config);
146
+ this.computed = response.data.computed;
147
+ this.customCSS = response.data.custom_css;
148
+ this.watchers = response.data.watchers;
149
+ this.screenTitle = response.data.title;
150
+
151
+ if (this.$attrs["disabled"]) {
152
+ this.disableForm(this.config);
153
+ }
154
+ });
155
+ }
156
+ },
157
+ callbackRecord() {
158
+ this.hasMustache = true;
159
+ this.loadRecordCollection(this.selCollectionId, 1, this.selDisplayMode);
160
+ },
161
+ errors() {
162
+ this.$refs.nestedScreen.isValid();
163
+ return this.$refs.nestedScreen.errors;
164
+ },
165
+ loadRecordCollection(collectionId, recordId, modeId) {
166
+ this.selCollectionId = collectionId;
167
+ this.selRecordId = recordId;
168
+ this.selDisplayMode = modeId;
169
+
170
+ this.$dataProvider
171
+ .getCollectionRecordsView(collectionId, recordId)
172
+ .then((response) => {
173
+ this.placeholder = "";
174
+ const respData = response.data;
175
+ const viewScreen = response.collection.read_screen_id;
176
+ const editScreen = response.collection.update_screen_id;
177
+ //Choose screen id regarding of the display Mode
178
+ this.screenCollectionId =
179
+ this.selDisplayMode === "View" ? viewScreen : editScreen;
180
+
181
+ this.loadScreen(this.screenCollectionId);
182
+
183
+ //This section validates if Collection has draft data
184
+ if(this.taskDraft?.draft?.data == null || this.taskDraft.draft.data === '') {
185
+ this.localData = respData;
186
+ }else{
187
+ this.localData = this.taskDraft.draft.data;
188
+ }
189
+
190
+ })
191
+ .catch(() => {
192
+ this.localData = {};
193
+ globalObject.ProcessMaker.alert(this.$t('This content does not exist. We could not locate indicated data'), "danger");
194
+ });;
195
+ },
196
+ isMustache(record) {
197
+ return /\{\{.*\}\}/.test(record);
198
+ },
199
+ clearDataObject() {
200
+ Object.keys(this.localData).forEach(key => {
201
+ if (key !== "id") {
202
+ this.localData[key] = "";
203
+ }
204
+ });
205
+ },
206
+ },
207
+ watch: {
208
+ collection(collection) {
209
+ if(collection) {
210
+ this.selCollectionId = collection.collectionId;
211
+ }
212
+ },
213
+ record(record) {
214
+ this.hasMustache = false;
215
+ if (record && !isNaN(record) && record > 0 && this.collection) {
216
+ this.selRecordId = record;
217
+ this.loadRecordCollection(this.selCollectionId, record, this.collectionmode);
218
+ } else {
219
+ if (this.isMustache(record)) {
220
+ this.callbackRecord();
221
+ }
222
+ this.localData = {};
223
+ }
224
+ },
225
+ collectionmode(collectionmode) {
226
+ if(collectionmode) {
227
+ this.selDisplayMode = collectionmode.modeId;
228
+ }
229
+ this.loadRecordCollection(this.selCollectionId, this.selRecordId, this.selDisplayMode);
230
+ },
231
+ },
232
+ mounted() {
233
+ this.$root.$on("taskdraft-input", (val)=>{
234
+ this.taskDraft = val;
235
+ });
236
+
237
+ if (this.collection && this.record) {
238
+ this.loadRecordCollection(this.collection.collectionId, this.record, this.collectionmode.modeId);
239
+ }
240
+ },
241
+ };
242
+ </script>
243
+
244
+ <style lang="scss">
245
+ .prevent-interaction.form-collection-record-control::after {
246
+ content: attr(placeholder);
247
+ }
248
+ </style>