@concretecms/bedrock 1.3.7 → 1.4.1

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 (105) 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/FolderBookmark.vue +0 -1
  27. package/assets/cms/components/file-manager/Chooser.vue +1 -1
  28. package/assets/cms/components/form/ConcreteAjaxSelect.vue +173 -0
  29. package/assets/cms/components/form/ConcreteExpressEntrySelect.vue +74 -0
  30. package/assets/cms/components/form/ConcreteFileDirectoryInput.vue +28 -26
  31. package/assets/cms/components/form/ConcreteFileInput.vue +17 -5
  32. package/assets/cms/components/form/ConcreteGroupInput.vue +134 -0
  33. package/assets/cms/components/form/ConcreteLocaleSelect.vue +58 -0
  34. package/assets/cms/components/form/ConcreteOptionSelect.vue +92 -0
  35. package/assets/cms/components/form/ConcretePageSelect.vue +67 -0
  36. package/assets/cms/components/form/ConcreteSelect.vue +75 -0
  37. package/assets/cms/components/form/ConcreteThemeColorInput.vue +19 -14
  38. package/assets/cms/components/form/ConcreteUserInput.vue +24 -4
  39. package/assets/cms/components/form/ConcreteUserSelect.vue +126 -0
  40. package/assets/cms/components/form/IconSelector.vue +14 -5
  41. package/assets/cms/components/form/PasswordInput.vue +141 -24
  42. package/assets/cms/components/groups/Chooser.vue +6 -5
  43. package/assets/cms/components/index.js +24 -0
  44. package/assets/cms/components/toolbar/ConcreteToolbarSiteList.vue +62 -0
  45. package/assets/cms/components/user/Chooser/Search.vue +5 -0
  46. package/assets/cms/components/user/Chooser/Users.vue +6 -2
  47. package/assets/cms/components/user/Chooser.vue +9 -3
  48. package/assets/cms/js/ajax-request/base.js +13 -4
  49. package/assets/cms/js/alert.js +2 -1
  50. package/assets/cms/js/base.js +3 -10
  51. package/assets/cms/js/edit-mode/area.js +0 -35
  52. package/assets/cms/js/edit-mode/block.js +27 -0
  53. package/assets/cms/js/edit-mode/containerblock.js +33 -3
  54. package/assets/cms/js/edit-mode/editmode.js +12 -0
  55. package/assets/cms/js/edit-mode/layout.js +56 -0
  56. package/assets/cms/js/edit-mode/style-customizer/style-customizer.js +0 -1
  57. package/assets/cms/js/file-manager/uploader.js +30 -206
  58. package/assets/cms/js/help/help.js +11 -8
  59. package/assets/cms/js/in-context-menu.js +5 -0
  60. package/assets/cms/js/jquery-vue.js +22 -0
  61. package/assets/cms/js/legacy-dialog.js +74 -65
  62. package/assets/cms/js/modal.js +73 -0
  63. package/assets/cms/js/panels.js +8 -0
  64. package/assets/cms/js/search/base.js +0 -18
  65. package/assets/cms/js/search/field-selector.js +6 -14
  66. package/assets/cms/js/select-combo-box.js +2 -0
  67. package/assets/cms/js/sitemap/sitemap-selector.js +2 -2
  68. package/assets/cms/js/sitemap/sitemap.js +15 -20
  69. package/assets/cms/js/toolbar.js +25 -2
  70. package/assets/cms/js/tree.js +7 -7
  71. package/assets/cms/js/users/group-manager.js +55 -0
  72. package/assets/cms/js/users/user-manager.js +2 -1
  73. package/assets/cms/js/users.js +1 -0
  74. package/assets/cms/js/vue/Manager.js +6 -3
  75. package/assets/cms/scss/_base.scss +2 -8
  76. package/assets/cms/scss/_cards.scss +7 -0
  77. package/assets/cms/scss/_file-manager.scss +1 -0
  78. package/assets/cms/scss/_file-uploader.scss +13 -3
  79. package/assets/cms/scss/_help.scss +11 -2
  80. package/assets/cms/scss/_item-selector.scss +10 -0
  81. package/assets/cms/scss/_layouts.scss +16 -0
  82. package/assets/cms/scss/_page-areas.scss +524 -245
  83. package/assets/cms/scss/_popover.scss +5 -0
  84. package/assets/cms/scss/_select-combo-box.scss +18 -0
  85. package/assets/cms/scss/_toolbar.scss +5 -14
  86. package/assets/cms/scss/_transitions.scss +13 -0
  87. package/assets/cms/scss/_variables.scss +18 -7
  88. package/assets/cms/scss/bootstrap/_reboot-tags.scss +17 -32
  89. package/assets/cms/scss/bootstrap/_reboot.scss +17 -7
  90. package/assets/cms/scss/bootstrap/_root-modified.scss +41 -20
  91. package/assets/cms/scss/file-manager/_thumbnail-image-editor.scss +45 -0
  92. package/assets/cms/scss/panels/_help.scss +0 -10
  93. package/assets/staging/scss/frontend/_frontend.scss +12 -0
  94. package/assets/staging/scss/frontend.scss +4 -0
  95. package/package.json +9 -9
  96. package/assets/account/js/frontend/components/Avatar/Avatar.js +0 -270
  97. package/assets/account/js/frontend/components/Avatar/Avatar.scss +0 -17
  98. package/assets/account/js/frontend/components/Avatar/Avatar.vue +0 -18
  99. package/assets/account/js/frontend/components/Avatar/Cropper.js +0 -202
  100. package/assets/account/js/frontend/components/Avatar/Cropper.scss +0 -136
  101. package/assets/account/js/frontend/components/Avatar/Cropper.vue +0 -40
  102. package/assets/bedrock/scss/_theme-grid.scss +0 -7
  103. package/assets/cms/js/edit-mode/style-customizer/inline-toolbar.js +0 -279
  104. package/assets/cms/js/modifiable-ajax-bootstrap-select.js +0 -78
  105. package/assets/cms/js/modifiable-bootstrap-select.js +0 -112
@@ -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
  }
@@ -0,0 +1,134 @@
1
+ <template>
2
+ <div class="ccm-item-selector-group">
3
+ <input type="hidden" :name="inputName" :value="selectedGroup ? selectedGroup.gID : ''" v-if="inputName !== ''" />
4
+
5
+ <div v-if="isLoading">
6
+ <div class="btn-group">
7
+ <div class="btn btn-secondary"><svg class="ccm-loader-dots"><use xlink:href="#icon-loader-circles" /></svg></div>
8
+ <button type="button" @click="reset" :disabled="readonly" class="ccm-item-selector-reset btn btn-secondary">
9
+ <i class="fa fa-times-circle"></i>
10
+ </button>
11
+ </div>
12
+ </div>
13
+
14
+ <div v-else-if="selectedGroup === null" class="ccm-item-selector-choose">
15
+ <button type="button" @click="openChooser" :disabled="readonly" class="btn btn-secondary">
16
+ {{chooseText}}
17
+ </button>
18
+ </div>
19
+
20
+ <div v-else class="ccm-item-selector-loaded">
21
+ <div class="btn-group">
22
+ <div class="btn btn-secondary" :class="{disabled: readonly}" @click="openChooser">
23
+ <span class="ccm-item-selector-title" v-bind:title="`ID: ${selectedGroup.gID}`">{{selectedGroup.gDisplayName}}</span>
24
+ </div>
25
+ <button type="button" @click="reset" :disabled="readonly" class="ccm-item-selector-reset btn btn-secondary">
26
+ <i class="fa fa-times-circle"></i>
27
+ </button>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+
35
+ export default {
36
+ data() {
37
+ return {
38
+ isLoadingGroupId: 0 /* integer */,
39
+ selectedGroup: null, /* json object */
40
+ mounted: false
41
+ }
42
+ },
43
+ computed: {
44
+ isLoading() {
45
+ return this.isLoadingGroupId !== 0
46
+ }
47
+ },
48
+ props: {
49
+ inputName: {
50
+ type: String,
51
+ default: ''
52
+ },
53
+ groupId: {
54
+ type: Number,
55
+ default: 0
56
+ },
57
+ chooseText: {
58
+ type: String,
59
+ default: 'Choose a Group'
60
+ },
61
+ readonly: {
62
+ type: Boolean,
63
+ default: false
64
+ }
65
+ },
66
+ watch: {
67
+ groupId: {
68
+ immediate: true,
69
+ handler(value) {
70
+ if (this.mounted) {
71
+ this.loadGroup()
72
+ }
73
+ }
74
+ }
75
+ },
76
+ beforeMount() {
77
+ this.mounted = true
78
+ },
79
+ mounted() {
80
+ this.loadGroup(true)
81
+ },
82
+ beforeUnmount() {
83
+ this.mounted = false
84
+ },
85
+ methods: {
86
+ setSelectedGroup(group, triggerChange) {
87
+ const currentlySelectedGroupID = parseInt(this.selectedGroup?.gID) || 0
88
+ const newlySelectedGroupID = parseInt(group?.gID) || 0
89
+ this.isLoadingGroupId = 0
90
+ this.selectedGroup = group
91
+ if (triggerChange && currentlySelectedGroupID !== newlySelectedGroupID) {
92
+ this.$emit('change', this.selectedGroup)
93
+ }
94
+ },
95
+ openChooser: function() {
96
+ window.ConcreteUserGroupManager.launchDialog((group) => {
97
+ this.setSelectedGroup(group, true)
98
+ })
99
+ },
100
+ loadGroup(initial) {
101
+ const wantedGroupId = Math.max(parseInt(this.groupId) || 0)
102
+ if (wantedGroupId === 0) {
103
+ this.setSelectedGroup(null, !initial)
104
+ return
105
+ }
106
+ const currentGroupId = parseInt(this.selectedGroup?.gID) || 0
107
+ if (wantedGroupId === currentGroupId) {
108
+ this.isLoadingGroupId = 0
109
+ return
110
+ }
111
+ this.isLoadingGroupId = wantedGroupId
112
+ window.ConcreteUserGroupManager.getGroupDetails(
113
+ wantedGroupId,
114
+ (r) => {
115
+ if (this.isLoadingGroupId !== wantedGroupId) {
116
+ return
117
+ }
118
+ this.setSelectedGroup(r.groups[0], !initial)
119
+ },
120
+ (r) => {
121
+ if (this.isLoadingGroupId !== wantedGroupId) {
122
+ return
123
+ }
124
+ this.setSelectedGroup(null, !initial)
125
+ window.ConcreteAlert.dialog(ccmi18n.error, ConcreteAjaxRequest.renderJsonError(r, true))
126
+ }
127
+ )
128
+ },
129
+ reset() {
130
+ this.setSelectedGroup(null, true)
131
+ }
132
+ }
133
+ }
134
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <input :name="name" v-model="selectedValue" />
3
+ </template>
4
+
5
+ <script>
6
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
7
+ /* globals TomSelect, WebFont */
8
+ export default {
9
+ data() {
10
+ return {
11
+ selectedValue: this.value
12
+ }
13
+ },
14
+ props: {
15
+ value: {
16
+ },
17
+ name: {
18
+ type: String,
19
+ required: true
20
+ },
21
+ selectedLocale: {
22
+ required: false
23
+ },
24
+ locales: {
25
+ type: Array,
26
+ required: true
27
+ }
28
+ },
29
+ watch: {
30
+ },
31
+ mounted() {
32
+ var config = {}
33
+ config.maxOptions = null
34
+ config.maxItems = 1
35
+ config.items = [this.selectedLocale]
36
+ config.options = this.locales
37
+ config.valueField = 'id'
38
+ config.render = {
39
+ option: function (data, escape) {
40
+ return `<div><img class="me-2" src="${data.flag}">${data.language.text}</div>`
41
+ },
42
+ item: function (item, escape) {
43
+ return `<div><img class="me-2" src="${item.flag}">${item.language.text}</div>`
44
+ }
45
+ }
46
+ this.$nextTick(() => {
47
+ const select = new TomSelect(this.$el, config)
48
+ select.on('change', (option) => {
49
+ this.selectedValue = option
50
+ })
51
+ select.sync()
52
+ })
53
+ },
54
+ methods: {
55
+
56
+ }
57
+ }
58
+ </script>