@concretecms/bedrock 1.3.6 → 1.4.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 (108) hide show
  1. package/.eslintrc.yml +2 -0
  2. package/assets/account/js/frontend/components/AvatarCropper.vue +159 -0
  3. package/assets/account/js/frontend.js +1 -1
  4. package/assets/account/scss/frontend/_frontend.scss +2 -0
  5. package/assets/account/scss/frontend/avatar/_avatar-cropper.scss +82 -0
  6. package/assets/account/scss/frontend/avatar/_avatar.scss +6 -0
  7. package/assets/bedrock/scss/_frontend.scss +0 -1
  8. package/assets/calendar/js/backend/duration.js +13 -13
  9. package/assets/calendar/js/vendor/fullcalendar.js +1 -0
  10. package/assets/cms/components/Announcement/Action/ExternalLinkAction.vue +25 -0
  11. package/assets/cms/components/Announcement/Action/GuideAction.vue +36 -0
  12. package/assets/cms/components/Announcement/Action/VideoAction.vue +31 -0
  13. package/assets/cms/components/Announcement/Broadcast.vue +63 -0
  14. package/assets/cms/components/Announcement/Button/ExternalLinkButton.vue +26 -0
  15. package/assets/cms/components/Announcement/Header/Header.vue +33 -0
  16. package/assets/cms/components/Announcement/Item/Item.vue +50 -0
  17. package/assets/cms/components/Announcement/Modal/Modal.vue +77 -0
  18. package/assets/cms/components/Announcement/Slide/CollectSiteInformationSlide.vue +61 -0
  19. package/assets/cms/components/Announcement/Slide/FeatureSlide.vue +81 -0
  20. package/assets/cms/components/Announcement/Slide/WelcomeSlide.vue +87 -0
  21. package/assets/cms/components/Help/Modal.vue +48 -0
  22. package/assets/cms/components/Image/ThumbnailEditor.vue +117 -0
  23. package/assets/cms/components/RunningProcessList.vue +6 -1
  24. package/assets/cms/components/customizer/FontFamilyPageCustomizerWidget.vue +27 -7
  25. package/assets/cms/components/file-manager/Chooser/FileManager.vue +0 -1
  26. package/assets/cms/components/file-manager/Chooser/FileUpload.vue +5 -0
  27. package/assets/cms/components/file-manager/Chooser/FolderBookmark.vue +0 -1
  28. package/assets/cms/components/file-manager/Chooser.vue +6 -1
  29. package/assets/cms/components/file-manager/Uploader/UploadFromComputer.vue +49 -9
  30. package/assets/cms/components/file-manager/Uploader.vue +7 -0
  31. package/assets/cms/components/form/ConcreteAjaxSelect.vue +173 -0
  32. package/assets/cms/components/form/ConcreteExpressEntrySelect.vue +74 -0
  33. package/assets/cms/components/form/ConcreteFileDirectoryInput.vue +28 -26
  34. package/assets/cms/components/form/ConcreteFileInput.vue +17 -5
  35. package/assets/cms/components/form/ConcreteGroupInput.vue +134 -0
  36. package/assets/cms/components/form/ConcreteLocaleSelect.vue +58 -0
  37. package/assets/cms/components/form/ConcreteOptionSelect.vue +92 -0
  38. package/assets/cms/components/form/ConcretePageSelect.vue +67 -0
  39. package/assets/cms/components/form/ConcreteSelect.vue +75 -0
  40. package/assets/cms/components/form/ConcreteThemeColorInput.vue +19 -14
  41. package/assets/cms/components/form/ConcreteUserInput.vue +69 -29
  42. package/assets/cms/components/form/ConcreteUserSelect.vue +126 -0
  43. package/assets/cms/components/form/IconSelector.vue +14 -5
  44. package/assets/cms/components/form/PasswordInput.vue +141 -24
  45. package/assets/cms/components/groups/Chooser.vue +6 -5
  46. package/assets/cms/components/index.js +24 -0
  47. package/assets/cms/components/toolbar/ConcreteToolbarSiteList.vue +62 -0
  48. package/assets/cms/components/user/Chooser/Search.vue +5 -0
  49. package/assets/cms/components/user/Chooser/Users.vue +6 -2
  50. package/assets/cms/components/user/Chooser.vue +9 -3
  51. package/assets/cms/js/ajax-request/base.js +13 -4
  52. package/assets/cms/js/alert.js +2 -1
  53. package/assets/cms/js/base.js +3 -10
  54. package/assets/cms/js/edit-mode/area.js +0 -35
  55. package/assets/cms/js/edit-mode/block.js +27 -0
  56. package/assets/cms/js/edit-mode/containerblock.js +33 -3
  57. package/assets/cms/js/edit-mode/editmode.js +12 -0
  58. package/assets/cms/js/edit-mode/layout.js +56 -0
  59. package/assets/cms/js/edit-mode/style-customizer/style-customizer.js +0 -1
  60. package/assets/cms/js/file-manager/uploader.js +64 -209
  61. package/assets/cms/js/help/help.js +11 -8
  62. package/assets/cms/js/in-context-menu.js +5 -0
  63. package/assets/cms/js/jquery-vue.js +22 -0
  64. package/assets/cms/js/legacy-dialog.js +74 -65
  65. package/assets/cms/js/modal.js +73 -0
  66. package/assets/cms/js/panels.js +8 -0
  67. package/assets/cms/js/search/base.js +0 -18
  68. package/assets/cms/js/search/field-selector.js +6 -14
  69. package/assets/cms/js/select-combo-box.js +2 -0
  70. package/assets/cms/js/sitemap/sitemap-selector.js +2 -2
  71. package/assets/cms/js/sitemap/sitemap.js +15 -20
  72. package/assets/cms/js/toolbar.js +25 -2
  73. package/assets/cms/js/tree.js +7 -7
  74. package/assets/cms/js/users/group-manager.js +55 -0
  75. package/assets/cms/js/users/user-manager.js +2 -1
  76. package/assets/cms/js/users.js +1 -0
  77. package/assets/cms/js/vue/Manager.js +6 -3
  78. package/assets/cms/scss/_base.scss +2 -8
  79. package/assets/cms/scss/_cards.scss +7 -0
  80. package/assets/cms/scss/_file-manager.scss +1 -0
  81. package/assets/cms/scss/_file-uploader.scss +13 -3
  82. package/assets/cms/scss/_help.scss +11 -2
  83. package/assets/cms/scss/_item-selector.scss +10 -0
  84. package/assets/cms/scss/_layouts.scss +16 -0
  85. package/assets/cms/scss/_page-areas.scss +517 -245
  86. package/assets/cms/scss/_popover.scss +5 -0
  87. package/assets/cms/scss/_select-combo-box.scss +18 -0
  88. package/assets/cms/scss/_toolbar.scss +5 -14
  89. package/assets/cms/scss/_transitions.scss +13 -0
  90. package/assets/cms/scss/_variables.scss +18 -7
  91. package/assets/cms/scss/bootstrap/_reboot-tags.scss +17 -32
  92. package/assets/cms/scss/bootstrap/_reboot.scss +17 -7
  93. package/assets/cms/scss/bootstrap/_root-modified.scss +41 -20
  94. package/assets/cms/scss/file-manager/_thumbnail-image-editor.scss +45 -0
  95. package/assets/cms/scss/panels/_help.scss +0 -10
  96. package/assets/staging/scss/frontend/_frontend.scss +12 -0
  97. package/assets/staging/scss/frontend.scss +4 -0
  98. package/package.json +9 -9
  99. package/assets/account/js/frontend/components/Avatar/Avatar.js +0 -270
  100. package/assets/account/js/frontend/components/Avatar/Avatar.scss +0 -17
  101. package/assets/account/js/frontend/components/Avatar/Avatar.vue +0 -18
  102. package/assets/account/js/frontend/components/Avatar/Cropper.js +0 -202
  103. package/assets/account/js/frontend/components/Avatar/Cropper.scss +0 -136
  104. package/assets/account/js/frontend/components/Avatar/Cropper.vue +0 -40
  105. package/assets/bedrock/scss/_theme-grid.scss +0 -7
  106. package/assets/cms/js/edit-mode/style-customizer/inline-toolbar.js +0 -279
  107. package/assets/cms/js/modifiable-ajax-bootstrap-select.js +0 -78
  108. package/assets/cms/js/modifiable-bootstrap-select.js +0 -112
@@ -4,7 +4,7 @@
4
4
  <div class="dz-default dz-message">
5
5
  <button type="button" class="dz-button">
6
6
  <img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjEuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMzIgMTMyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMzIgMTMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe29wYWNpdHk6MC45O2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6I0U2RjVGRjtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDF7ZmlsbDojNEE5MEUyO30KCS5zdDJ7ZmlsbDpub25lO3N0cm9rZTojRThGNkZGO3N0cm9rZS13aWR0aDo4O30KPC9zdHlsZT4KPGcgaWQ9IlBhZ2UtMSI+Cgk8ZyBpZD0iRmlsZS1NYW5hZ2VyLS0tRmlsZVNldHMtRHJvcGRvd24iIHRyYW5zZm9ybT0idHJhbnNsYXRlKC02NTUuMDAwMDAwLCAtNjY3LjAwMDAwMCkiPgoJCTxyZWN0IGlkPSJSZWN0YW5nbGUiIHg9IjEiIHk9IjQ2OCIgY2xhc3M9InN0MCIgd2lkdGg9IjE0NDAiIGhlaWdodD0iNTQwIi8+CgkJPGcgaWQ9Imljb25zOC1kcmFnLWFuZC1kcm9wIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NTUuMDAwMDAwLCA2NjcuMDAwMDAwKSI+CgkJCTxwYXRoIGlkPSJTaGFwZSIgY2xhc3M9InN0MSIgZD0iTTMuMiwwYzAsMC0wLjEsMC0wLjEsMEMzLjEsMCwzLDAsMi45LDBDMi44LDAsMi43LDAuMSwyLjYsMC4xQzIuNCwwLjIsMi4xLDAuMiwxLjgsMC4zCgkJCQlDMS43LDAuNCwxLjUsMC41LDEuNCwwLjZDMS4yLDAuOCwxLDAuOSwwLjksMUMwLjgsMS4xLDAuNywxLjMsMC42LDEuNEMwLjUsMS42LDAuMywxLjgsMC4yLDIuMUMwLjIsMi4yLDAuMiwyLjQsMC4xLDIuNQoJCQkJQzAuMSwyLjcsMCwyLjksMCwzLjF2MTMuMWMwLDEuNywxLjQsMy4xLDMuMiwzLjFzMy4yLTEuNCwzLjItMy4xVjYuNGgxM2MxLjcsMCwzLjEtMS40LDMuMS0zLjJTMjEuMSwwLDE5LjQsMEgzLjUKCQkJCUMzLjQsMCwzLjQsMCwzLjIsMEMzLjMsMCwzLjIsMCwzLjIsMHogTTMyLjEsMEMzMC40LDAsMjksMS40LDI5LDMuMnMxLjQsMy4yLDMuMSwzLjJoNi42YzEuNywwLDMuMS0xLjQsMy4xLTMuMlM0MC40LDAsMzguNywwCgkJCQlIMzIuMXogTTUxLjQsMGMtMS43LDAtMy4xLDEuNC0zLjEsMy4yczEuNCwzLjIsMy4xLDMuMmg2LjZjMS43LDAsMy4xLTEuNCwzLjEtMy4yUzU5LjgsMCw1OC4xLDBINTEuNHogTTcwLjcsMAoJCQkJYy0xLjcsMC0zLjEsMS40LTMuMSwzLjJzMS40LDMuMiwzLjEsMy4yaDYuNmMxLjcsMCwzLjEtMS40LDMuMS0zLjJTNzkuMSwwLDc3LjQsMEg3MC43eiBNOTAsMGMtMS43LDAtMy4xLDEuNC0zLjEsMy4yCgkJCQlzMS40LDMuMiwzLjEsMy4yaDEzdjkuOGMwLDEuNywxLjQsMy4xLDMuMiwzLjFjMS44LDAsMy4yLTEuNCwzLjItMy4xVjMuMWMwLTAuMi0wLjEtMC40LTAuMS0wLjZjMC0wLjIsMC0wLjMtMC4xLTAuNQoJCQkJYy0wLjEtMC4yLTAuMi0wLjQtMC40LTAuN2MtMC4xLTAuMS0wLjItMC4zLTAuMy0wLjRjLTAuMi0wLjItMC4zLTAuMy0wLjUtMC40Yy0wLjItMC4xLTAuMy0wLjItMC41LTAuMwoJCQkJYy0wLjItMC4xLTAuNS0wLjEtMC44LTAuMmMtMC4xLDAtMC4yLTAuMS0wLjMtMC4xYy0wLjEsMC0wLjEsMC0wLjIsMGMwLDAtMC4xLDAtMC4xLDBjMCwwLDAsMC0wLjEsMGMtMC4xLDAtMC4xLDAtMC4yLDBIOTB6CgkJCQkgTTMuMiwyNS44Yy0xLjgsMC0zLjIsMS40LTMuMiwzLjF2Ni42YzAsMS43LDEuNCwzLjEsMy4yLDMuMXMzLjItMS40LDMuMi0zLjF2LTYuNkM2LjQsMjcuMiw1LDI1LjgsMy4yLDI1Ljh6IE0xMDYuMiwyNS44CgkJCQljLTEuOCwwLTMuMiwxLjQtMy4yLDMuMXY2LjZjMCwxLjcsMS40LDMuMSwzLjIsMy4xYzEuOCwwLDMuMi0xLjQsMy4yLTMuMXYtNi42QzEwOS41LDI3LjIsMTA4LDI1LjgsMTA2LjIsMjUuOHogTTMuMiw0NS4xCgkJCQljLTEuOCwwLTMuMiwxLjQtMy4yLDMuMXY2LjZDMCw1Ni41LDEuNCw1OCwzLjIsNThzMy4yLTEuNCwzLjItMy4xdi02LjZDNi40LDQ2LjUsNSw0NS4xLDMuMiw0NS4xeiBNNDEuOSw0NS4xCgkJCQlDMzQuOCw0NS4xLDI5LDUwLjksMjksNTh2NjEuMmMwLDcuMSw1LjgsMTIuOSwxMi45LDEyLjloNzcuM2M3LjEsMCwxMi45LTUuOCwxMi45LTEyLjlWNThjMC03LjEtNS44LTEyLjktMTIuOS0xMi45SDQxLjl6CgkJCQkgTTQxLjksNTEuNWg3Ny4zYzMuNiwwLDYuNCwyLjgsNi40LDYuNHY2MS4yYzAsMy42LTIuOCw2LjQtNi40LDYuNEg0MS45Yy0zLjYsMC02LjQtMi44LTYuNC02LjRWNTgKCQkJCUMzNS40LDU0LjQsMzguMyw1MS41LDQxLjksNTEuNXogTTMuMiw2NC40Yy0xLjgsMC0zLjIsMS40LTMuMiwzLjF2Ni42YzAsMS43LDEuNCwzLjEsMy4yLDMuMXMzLjItMS40LDMuMi0zLjF2LTYuNgoJCQkJQzYuNCw2NS44LDUsNjQuNCwzLjIsNjQuNHogTTc0LDcwLjhWMTAzbDcuOC02LjZsNC45LDExLjVsNC4xLTEuOGwtNS4yLTExLjNsMTAuOS0xLjRMNzQsNzAuOHogTTMuMiw4My43CgkJCQljLTEuOCwwLTMuMiwxLjQtMy4yLDMuMXYxMi43YzAsMC4xLDAsMC4xLDAsMC4yYzAsMCwwLDAsMCwwLjFjMCwwLDAsMC4xLDAsMC4xYzAsMC4xLDAsMC4xLDAsMC4yYzAsMC4xLDAuMSwwLjIsMC4xLDAuMwoJCQkJYzAuMSwwLjMsMC4xLDAuNSwwLjIsMC44YzAuMSwwLjIsMC4yLDAuMywwLjMsMC41YzAuMSwwLjIsMC4yLDAuNCwwLjQsMC41YzAuMSwwLjEsMC4zLDAuMiwwLjQsMC4zYzAuMiwwLjEsMC40LDAuMywwLjcsMC40CgkJCQljMC4xLDAuMSwwLjMsMC4xLDAuNSwwLjFjMC4yLDAsMC40LDAuMSwwLjYsMC4xaDE2LjNjMS43LDAsMy4xLTEuNCwzLjEtMy4ycy0xLjQtMy4yLTMuMS0zLjJoLTEzdi05LjhDNi40LDg1LjEsNSw4My43LDMuMiw4My43CgkJCQl6Ii8+CgkJPC9nPgoJCTxyZWN0IGlkPSJSZWN0YW5nbGUtQ29weS00IiB4PSI0IiB5PSIxNzciIGNsYXNzPSJzdDIiIHdpZHRoPSIxNDMyIiBoZWlnaHQ9IjgyOSIvPgoJPC9nPgo8L2c+Cjwvc3ZnPgo=" alt="Drop files here or click to upload">
7
- <span>Drop files here or click to upload</span>
7
+ <span>{{ this.dropzoneOptions && this.dropzoneOptions.dictDefaultMessage ? this.dropzoneOptions.dictDefaultMessage : 'Drop files here or click to upload' }}</span>
8
8
  </button>
9
9
  </div>
10
10
  <input type="file" class="ccm-file-uploader-container-dropzone-file-element d-none" multiple="multiple">
@@ -57,6 +57,10 @@ export default {
57
57
  type: Object,
58
58
  default: () => ({})
59
59
  },
60
+ dropzoneOptions: {
61
+ type: Object,
62
+ default: () => ({})
63
+ },
60
64
  replaceFileId: {
61
65
  type: Number,
62
66
  required: false,
@@ -72,7 +76,7 @@ export default {
72
76
  },
73
77
  dropzoneSettings() {
74
78
  const me = this
75
- return {
79
+ const dropzoneOptions = {
76
80
  url: `${CCM_DISPATCHER_FILENAME}/ccm/system/file/upload`,
77
81
  previewTemplate: this.itemTemplate,
78
82
  autoProcessQueue: false,
@@ -126,8 +130,8 @@ export default {
126
130
 
127
131
  queuecomplete: function () {
128
132
  if (this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0) {
133
+ const fileIds = []
129
134
  if (me.uploadedFiles.length !== 0) {
130
- const fileIds = []
131
135
  me.uploadedFiles.forEach(function(file) {
132
136
  fileIds.push(file.fID)
133
137
  })
@@ -135,7 +139,7 @@ export default {
135
139
  // ConcreteEvent.publish('FileManagerSelectFile', { fID: fileIds })
136
140
  me.uploadedFiles = []
137
141
  }
138
- me.uploadComplete()
142
+ me.uploadComplete(fileIds)
139
143
  }
140
144
  me.refresh()
141
145
  },
@@ -161,6 +165,38 @@ export default {
161
165
  $fileElement.addClass('in-progress')
162
166
  }
163
167
  }
168
+ if (this.dropzoneOptions) {
169
+ for (const key in this.dropzoneOptions) {
170
+ let skipMimeTypes
171
+ switch (key) {
172
+ case '_dontResizeMimeTypes':
173
+ skipMimeTypes = this.dropzoneOptions._dontResizeMimeTypes
174
+ if (skipMimeTypes && skipMimeTypes.length) {
175
+ dropzoneOptions.transformFile = function(file, done) {
176
+ if (
177
+ (this.options.resizeWidth || this.options.resizeHeight) &&
178
+ file && file.type && file.type.match(/image.*/) &&
179
+ skipMimeTypes.indexOf(file.type) < 0
180
+ ) {
181
+ return this.resizeImage(
182
+ file,
183
+ this.options.resizeWidth,
184
+ this.options.resizeHeight,
185
+ this.options.resizeMethod,
186
+ done
187
+ )
188
+ }
189
+ return done(file)
190
+ }
191
+ }
192
+ break
193
+ default:
194
+ dropzoneOptions[key] = this.dropzoneOptions[key]
195
+ break
196
+ }
197
+ }
198
+ }
199
+ return dropzoneOptions
164
200
  }
165
201
  },
166
202
  mounted() {
@@ -170,14 +206,14 @@ export default {
170
206
  $(me.$refs.dropzoneElement).dropzone(settings)
171
207
  $(window).on('resize', _.throttle(me.refresh, 100))
172
208
 
173
- ConcreteEvent.subscribe('FileUploaderUploadSelectedFiles', function(e) {
174
- me.dropzone.options.autoProcessQueue = true
175
- me.dropzone.processQueue()
209
+ ConcreteEvent.subscribe('FileUploaderUploadSelectedFiles', () => {
210
+ this.startUploading()
176
211
  })
177
212
  },
178
213
  watch: {
179
214
  filesInQueue() {
180
215
  const filesInQueue = this.filesInQueue
216
+ this.$emit('files-ready-to-upload', filesInQueue)
181
217
  ConcreteEvent.publish('FileUploaderFilesReadyToUpload', filesInQueue)
182
218
  },
183
219
  isUploadInProgress(val, oldVal) {
@@ -222,8 +258,12 @@ export default {
222
258
 
223
259
  this.dropzone.removeAllFiles(true)
224
260
  },
225
- uploadComplete() {
226
- this.$emit('upload-complete')
261
+ startUploading() {
262
+ this.dropzone.options.autoProcessQueue = true
263
+ this.dropzone.processQueue()
264
+ },
265
+ uploadComplete(fileIds) {
266
+ this.$emit('upload-complete', fileIds)
227
267
 
228
268
  ConcreteAlert.notify({
229
269
  title: 'Complete',
@@ -2,6 +2,7 @@
2
2
  <div>
3
3
  <upload-from-computer
4
4
  :upload-directory-id="uploadDirectoryId"
5
+ :dropzone-options="dropzoneOptions"
5
6
  @upload-complete="$emit('upload-complete')"
6
7
  @uploadProgressStateChange="isUploadInProgress = $event"/>
7
8
 
@@ -23,6 +24,12 @@ export default {
23
24
  UploadFromComputer,
24
25
  ConcreteFileDirectoryInput
25
26
  },
27
+ props: {
28
+ dropzoneOptions: {
29
+ type: Object,
30
+ default: () => ({})
31
+ }
32
+ },
26
33
  data: () => ({
27
34
  uploadDirectoryId: 0,
28
35
  isUploadInProgress: false
@@ -0,0 +1,173 @@
1
+ <template>
2
+ <input :class="cssClasses" :name="name" v-if="maxItems == 1" />
3
+ <select multiple :class="cssClasses" :name="name" v-else />
4
+ </template>
5
+
6
+ <script>
7
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
8
+ /* globals TomSelect */
9
+ export default {
10
+ props: {
11
+ // If we use this component in a traditional non-vue-based form, we need to
12
+ // have a name for the input field. This controls that. Can be safely left blank
13
+ // if you're just using this in Vue w/v-model
14
+ name: {
15
+ type: String
16
+ },
17
+ // Do we want our input/select to have any custom classes?
18
+ cssClasses: {
19
+ type: Array
20
+ },
21
+ // The value of whatever item we're selecting, be it user ID, page ID, array of page IDs, etc...
22
+ value: {
23
+ type: [Array, String, Number]
24
+ },
25
+ // The URL used to search
26
+ dataSourceUrl: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ // The selected options URL
31
+ selectedOptionsUrl: {
32
+ type: String,
33
+ required: true
34
+ },
35
+ // The data sent to the server with every request. Paired with an access token to ensure
36
+ // that only authorized requests are responded to.
37
+ formData: {
38
+ type: Object,
39
+ required: true
40
+ },
41
+ // Used to authorize requests
42
+ accessToken: {
43
+ type: String,
44
+ required: true
45
+ },
46
+ // Whether to include the remove button on each selected item
47
+ removeButton: {
48
+ type: Boolean,
49
+ required: false,
50
+ default: false
51
+ },
52
+ maxItems: {
53
+ required: false,
54
+ default: 1
55
+ },
56
+ allowCreate: {
57
+ type: Boolean,
58
+ required: false,
59
+ default: false
60
+ }
61
+ },
62
+ data() {
63
+ return {}
64
+ },
65
+ mounted() {
66
+ var my = this
67
+ var config = {
68
+ maxOptions: null,
69
+ maxItems: my.maxItems,
70
+ searchField: 'primary_label',
71
+ labelField: 'primary_label',
72
+ valueField: 'id',
73
+ load: function (query, callback) {
74
+ var formData = my.getFormData()
75
+ formData.append('query', query)
76
+ var url = my.dataSourceUrl
77
+ fetch(url, {
78
+ method: 'POST',
79
+ body: formData
80
+ })
81
+ .then(response => response.json())
82
+ .then(json => {
83
+ callback(json)
84
+ }).catch(() => {
85
+ callback()
86
+ })
87
+ },
88
+ render: {
89
+ option: function (item, escape) {
90
+ return (typeof (my.$parent.renderOption) === 'function')
91
+ ? my.$parent.renderOption(item) : my.renderOption(item)
92
+ },
93
+ item: function (item, escape) {
94
+ return (typeof (my.$parent.renderItem) === 'function')
95
+ ? my.$parent.renderItem(item) : my.renderItem(item)
96
+ }
97
+ }
98
+ }
99
+
100
+ if (this.removeButton) {
101
+ config.plugins = ['remove_button']
102
+ }
103
+
104
+ if (!this.maxItems) {
105
+ config.onItemAdd = function () {
106
+ // Without this method, Tom Select persists the search string even after we search for something,
107
+ // requiring us to delete the string in order to continue searching for something else - it's kind
108
+ // of annoying. it only seems to be a problem in multi-select mode ?!
109
+ this.setTextboxValue('')
110
+ this.refreshOptions()
111
+ }
112
+ }
113
+
114
+ if (this.allowCreate) {
115
+ config.createOnBlur = true
116
+ config.create = true
117
+ }
118
+
119
+ my.select = new TomSelect(this.$el, config)
120
+ my.select.on('change', function (value) {
121
+ my.$emit('change', value)
122
+ })
123
+ },
124
+ watch: {
125
+ value: {
126
+ immediate: true,
127
+ handler: function () {
128
+ var my = this
129
+ if (my.value) {
130
+ var formData = my.getFormData()
131
+ var url = my.selectedOptionsUrl
132
+ fetch(url, {
133
+ method: 'POST',
134
+ body: formData
135
+ })
136
+ .then(response => response.json())
137
+ .then(json => {
138
+ json.forEach((item) => {
139
+ my.select.addOption(item)
140
+ my.select.addItem(item.id)
141
+ })
142
+ }).catch(() => {})
143
+ }
144
+ }
145
+ }
146
+ },
147
+ methods: {
148
+ getFormData() {
149
+ var formData = new FormData()
150
+ for (const [key, value] of Object.entries(this.formData)) {
151
+ if (Array.isArray(value)) {
152
+ // Multi-select, as in the case of ConcreteExpressEntrySelect, etc...
153
+ value.forEach((thisValue) => {
154
+ formData.append(key + '[]', thisValue)
155
+ })
156
+ } else if (typeof (value) === 'boolean') {
157
+ formData.append(key, value ? 1 : 0)
158
+ } else {
159
+ formData.append(key, value)
160
+ }
161
+ }
162
+ formData.append('accessToken', this.accessToken)
163
+ return formData
164
+ },
165
+ renderOption(item) {
166
+ return `<div>${(item.primary_label)}</div>`
167
+ },
168
+ renderItem(item) {
169
+ return `<div>${(item.primary_label)}</div>`
170
+ }
171
+ }
172
+ }
173
+ </script>
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <concrete-ajax-select
3
+ :name="inputName"
4
+ :access-token="accessToken"
5
+ :data-source-url="dataSourceUrl"
6
+ :selected-options-url="selectedOptionsUrl"
7
+ :value="entryId"
8
+ @change="updateSelected"
9
+ :form-data="formData"
10
+ :max-items="null"
11
+ :remove-button="true"
12
+ >
13
+ </concrete-ajax-select>
14
+ </template>
15
+
16
+ <script>
17
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
18
+ /* globals TomSelect */
19
+ import ConcreteAjaxSelect from './ConcreteAjaxSelect'
20
+ export default {
21
+ components: { ConcreteAjaxSelect },
22
+ prop: ['entryId'],
23
+ model: {
24
+ prop: 'entryId',
25
+ event: 'change'
26
+ },
27
+ props: {
28
+ entity: {
29
+ type: String,
30
+ required: true
31
+ },
32
+ accessToken: {
33
+ type: String,
34
+ required: true
35
+ },
36
+ inputName: {
37
+ type: String
38
+ },
39
+ entryId: {
40
+ type: [Number, String, Array],
41
+ required: false
42
+ }
43
+ },
44
+ computed: {
45
+ formData() {
46
+ return {
47
+ entity: this.entity,
48
+ entryId: this.entryId
49
+ }
50
+ }
51
+ },
52
+ data() {
53
+ return {
54
+ dataSourceUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/express/entry/autocomplete',
55
+ selectedOptionsUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/express/entry/autocomplete/get_selected',
56
+ selectedEntryId: null
57
+ }
58
+ },
59
+ watch: {
60
+ entryId: {
61
+ immediate: true,
62
+ handler: function(entryId) {
63
+ this.selectedEntryId = entryId
64
+ }
65
+ }
66
+ },
67
+ methods: {
68
+ updateSelected(value) {
69
+ this.selectedEntryId = value
70
+ this.$emit('change', this.selectedEntryId)
71
+ }
72
+ }
73
+ }
74
+ </script>
@@ -4,22 +4,14 @@
4
4
  <div class="form-group">
5
5
  <label class="form-label" :for="directorySelectInputId" v-if="inputLabel">{{inputLabel}}</label>
6
6
  <div v-if="showAddDirectoryButton" class="input-group">
7
- <select :id="directorySelectInputId" :name="inputName" data-size="5" data-live-search="true" class="ccm-directory-selector form-control" v-model="selectedDirectoryID" ref="directoryInput" :disabled="disabled">
8
- <option v-for="directory in directories" :class="`level-${directory.directoryLevel}`" :value="directory.directoryId" data-icon="fas fa-folder">
9
- {{ directory.directoryName }}
10
- </option>
11
- </select>
7
+ <input :id="directorySelectInputId" :name="inputName" v-model="selectedDirectoryID" ref="directoryInput" :disabled="disabled" />
12
8
  <button type="button"
13
- :class="{'btn': true, 'btn-outline-secondary': true, 'ccm-create-new-directory-button': true, 'disabled': disabled === true}"
9
+ :class="{'btn': true, 'btn-secondary': true, 'ccm-create-new-directory-button': true, 'disabled': disabled === true}"
14
10
  @click="toggleDirectoryInput" :disabled="disabled">
15
11
  {{ i18n.createNewFolder }}
16
12
  </button>
17
13
  </div>
18
- <select v-else :id="directorySelectInputId" :name="inputName" class="ccm-directory-selector form-select" v-model="selectedDirectoryID" ref="directoryInput" :disabled="disabled">
19
- <option v-for="directory in directories" :class="`level-${directory.directoryLevel}`" :value="directory.directoryId" data-icon="fas fa-folder">
20
- {{ directory.directoryName }}
21
- </option>
22
- </select>
14
+ <input v-else :id="directorySelectInputId" :name="inputName" v-model="selectedDirectoryID" ref="directoryInput" :disabled="disabled" />
23
15
  </div>
24
16
  </div>
25
17
  <div v-if="showAddDirectoryButton" v-show="showAddDirectoryInput" class="ccm-new-directory-name-container">
@@ -31,7 +23,7 @@
31
23
  :placeholder="i18n.specifyName" class="ccm-new-directory-name form-control"
32
24
  v-model="newDirectoryName" @keyup.enter.stop.prevent="createDirectory" :disabled="disabled">
33
25
  <button type="button"
34
- :class="{'btn': true, 'btn-outline-secondary': true, 'disabled': disabled === true}"
26
+ :class="{'btn': true, 'btn-secondary': true, 'disabled': disabled === true}"
35
27
  @click.stop.prevent="createDirectory" :disabled="disabled">
36
28
  {{ i18n.add }}
37
29
  </button>
@@ -43,7 +35,7 @@
43
35
 
44
36
  <script>
45
37
  /* eslint-disable no-new */
46
- /* global CCM_DISPATCHER_FILENAME, CCM_SECURITY_TOKEN, ConcreteAjaxRequest, _ */
38
+ /* global TomSelect, CCM_DISPATCHER_FILENAME, CCM_SECURITY_TOKEN, ConcreteAjaxRequest, _ */
47
39
  export default {
48
40
  data: () => ({
49
41
  i18n: {
@@ -81,15 +73,11 @@ export default {
81
73
  },
82
74
  watch: {
83
75
  selectedDirectoryID() {
84
- $(this.$refs.directoryInput).selectpicker('val', this.selectedDirectoryID)
85
-
86
76
  this.$emit('change', parseInt(this.selectedDirectoryID))
87
77
  },
88
78
  directories() {
89
79
  const me = this
90
80
  me.$nextTick(() => {
91
- $(me.$refs.directoryInput).selectpicker('refresh')
92
-
93
81
  if (me.directories.length > 0) {
94
82
  const isSelectedOptionInDirList = _.findWhere(me.directories, { directoryId: me.selectedDirectoryID }) !== undefined
95
83
 
@@ -102,12 +90,11 @@ export default {
102
90
 
103
91
  me.showAddDirectoryInput = false
104
92
  me.newDirectoryName = ''
105
- })
106
- },
107
- disabled() {
108
- const me = this
109
- me.$nextTick(function () {
110
- $(me.$refs.directoryInput).selectpicker('refresh')
93
+
94
+ this.selectMenu.clear(true)
95
+ this.selectMenu.clearOptions()
96
+ this.selectMenu.addOptions(me.directories)
97
+ this.selectMenu.setValue(me.selectedDirectoryID)
111
98
  })
112
99
  }
113
100
  },
@@ -122,11 +109,26 @@ export default {
122
109
  }
123
110
  }
124
111
  }
125
- $(this.$refs.directoryInput).selectpicker()
126
-
127
112
  if (this.directoryId) {
128
113
  this.selectedDirectoryID = this.directoryId
129
114
  }
115
+
116
+ this.selectMenu = new TomSelect(this.$refs.directoryInput, {
117
+ maxOptions: 200,
118
+ maxItems: 1,
119
+ items: [this.selectedDirectoryID],
120
+ options: [],
121
+ valueField: 'directoryId',
122
+ searchField: 'directoryName',
123
+ render: {
124
+ option: function (data, escape) {
125
+ return `<div class="level-${data.directoryLevel}"><i class="fa fa-folder"></i> ${data.directoryName}</div>`
126
+ },
127
+ item: function (item, escape) {
128
+ return `<div class="level-${item.directoryLevel}"><i class="fa fa-folder"></i> ${item.directoryName}</div>`
129
+ }
130
+ }
131
+ })
130
132
  },
131
133
  methods: {
132
134
  createDirectory() {
@@ -144,8 +146,8 @@ export default {
144
146
  },
145
147
  success: function (r) {
146
148
  // re-fetch the directories and select the new folder
147
- me.fetchDirectories()
148
149
  me.selectedDirectoryID = r.directoryId
150
+ me.fetchDirectories()
149
151
  }
150
152
  })
151
153
  },
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="ccm-item-selector-group">
3
- <input type="hidden" :name="inputName" :value="selectedFileID" />
3
+ <input type="hidden" :name="inputName" :value="selectedFileID" ref="input" />
4
4
 
5
5
  <div class="ccm-item-selector-choose" v-if="!selectedFile && !isLoading">
6
6
  <button type="button" @click="openChooser" class="btn btn-secondary">
@@ -45,10 +45,11 @@ export default {
45
45
  props: {
46
46
  inputName: {
47
47
  type: String,
48
- required: true
48
+ default: ''
49
49
  },
50
50
  fileId: {
51
- type: Number
51
+ type: Number,
52
+ default: 0
52
53
  },
53
54
  chooseText: {
54
55
  type: String
@@ -57,7 +58,18 @@ export default {
57
58
  type: Array
58
59
  }
59
60
  },
61
+ prop: ['fileId'],
62
+ model: {
63
+ prop: 'fileId',
64
+ event: 'change'
65
+ },
60
66
  watch: {
67
+ fileId: {
68
+ immediate: true,
69
+ handler(value) {
70
+ this.selectedFileID = this.fileId
71
+ }
72
+ },
61
73
  selectedFileID: {
62
74
  immediate: true,
63
75
  handler(value) {
@@ -72,11 +84,11 @@ export default {
72
84
  this.$emit('change', null)
73
85
  }
74
86
  }
75
- if (!this.isFirstRun) {
87
+ if (!this.isFirstRun && this.inputName) {
76
88
  // Fire the jQuery change event.
77
89
  // @deprecated - do not use this unless you have to. Use this component directly instead and listen
78
90
  // to its change event. This will be removed when the jQuery dependency is removed.
79
- $('input[name=' + this.inputName + ']').trigger('change')
91
+ $(this.$refs.input).trigger('change')
80
92
  }
81
93
  this.isFirstRun = false
82
94
  }