@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,92 @@
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="value"
8
+ @change="updateSelected"
9
+ :form-data="formData"
10
+ :max-items="maxItems"
11
+ :remove-button="maxItems != 1"
12
+ :allow-create="true"
13
+ >
14
+ </concrete-ajax-select>
15
+ </template>
16
+
17
+ <script>
18
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
19
+ /* globals TomSelect */
20
+ import ConcreteAjaxSelect from './ConcreteAjaxSelect'
21
+ export default {
22
+ components: { ConcreteAjaxSelect },
23
+ prop: ['value'],
24
+ model: {
25
+ prop: 'value',
26
+ event: 'change'
27
+ },
28
+ props: {
29
+ dataSourceUrl: {
30
+ type: String,
31
+ required: true
32
+ },
33
+ selectedOptionsUrl: {
34
+ type: String,
35
+ required: true
36
+ },
37
+ allowMultipleValues: {
38
+ type: Boolean,
39
+ default: false
40
+ },
41
+ attributeKeyId: {
42
+ type: [String, Number],
43
+ required: true
44
+ },
45
+ accessToken: {
46
+ type: String,
47
+ required: true
48
+ },
49
+ inputName: {
50
+ type: String
51
+ },
52
+ value: {
53
+ type: Array,
54
+ required: false
55
+ }
56
+ },
57
+ computed: {
58
+ maxItems() {
59
+ if (this.allowMultipleValues) {
60
+ return null
61
+ } else {
62
+ return 1
63
+ }
64
+ },
65
+ formData() {
66
+ return {
67
+ akID: this.attributeKeyId,
68
+ optionId: this.value
69
+ }
70
+ }
71
+ },
72
+ data() {
73
+ return {
74
+ selectedValue: null
75
+ }
76
+ },
77
+ watch: {
78
+ entryId: {
79
+ immediate: true,
80
+ handler: function(value) {
81
+ this.selectedValue = value
82
+ }
83
+ }
84
+ },
85
+ methods: {
86
+ updateSelected(value) {
87
+ this.selectedValue = value
88
+ this.$emit('change', this.selectedValue)
89
+ }
90
+ }
91
+ }
92
+ </script>
@@ -0,0 +1,67 @@
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="pageId"
8
+ @change="updateSelected"
9
+ :form-data="formData"
10
+ >
11
+ </concrete-ajax-select>
12
+ </template>
13
+
14
+ <script>
15
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
16
+ /* globals TomSelect */
17
+ import ConcreteAjaxSelect from './ConcreteAjaxSelect'
18
+ export default {
19
+ components: { ConcreteAjaxSelect },
20
+ prop: ['pageId'],
21
+ model: {
22
+ prop: 'pageId',
23
+ event: 'change'
24
+ },
25
+ props: {
26
+ accessToken: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ inputName: {
31
+ type: String
32
+ },
33
+ pageId: {
34
+ type: [Number, String, Array],
35
+ required: false
36
+ }
37
+ },
38
+ computed: {
39
+ formData() {
40
+ return {
41
+ pageId: this.pageId
42
+ }
43
+ }
44
+ },
45
+ data() {
46
+ return {
47
+ dataSourceUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/page/autocomplete',
48
+ selectedOptionsUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/page/autocomplete/get_selected',
49
+ selectedPageId: null
50
+ }
51
+ },
52
+ watch: {
53
+ pageId: {
54
+ immediate: true,
55
+ handler: function(pageId) {
56
+ this.selectedPageId = pageId
57
+ }
58
+ }
59
+ },
60
+ methods: {
61
+ updateSelected(value) {
62
+ this.selectedPageId = value
63
+ this.$emit('change', this.selectedPageId)
64
+ }
65
+ }
66
+ }
67
+ </script>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <select :name="name" :multiple="multiple" v-model="selectedValue">
3
+ <template v-if="hasOptGroups">
4
+ <optgroup v-for="(optgroupOptions, optgroupLabel) in options" :label="optgroupLabel">
5
+ <option v-for="(label, value) in optgroupOptions" :value="value">
6
+ {{label}}
7
+ </option>
8
+ </optgroup>
9
+ </template>
10
+ <template v-else>
11
+ <option v-for="(label, value) in options" :value="value" :key="value">
12
+ {{label}}
13
+ </option>
14
+ </template>
15
+ </select>
16
+ </template>
17
+
18
+ <script>
19
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
20
+ /* globals TomSelect */
21
+ export default {
22
+ data() {
23
+ return {
24
+ selectedValue: this.value
25
+ }
26
+ },
27
+ props: {
28
+ name: {
29
+ type: String,
30
+ required: true
31
+ },
32
+ value: {
33
+ required: false
34
+ },
35
+ multiple: {
36
+ type: Boolean,
37
+ required: false,
38
+ default: false
39
+ },
40
+ options: {
41
+ type: Object,
42
+ required: true
43
+ }
44
+ },
45
+ watch: {
46
+
47
+ },
48
+ mounted() {
49
+ var options = {}
50
+ options.maxOptions = null
51
+ if (this.multiple) {
52
+ options.plugins = {
53
+ remove_button: {
54
+
55
+ }
56
+ }
57
+ }
58
+ this.$nextTick(() => {
59
+ new TomSelect(this.$el, options)
60
+ })
61
+ },
62
+ computed: {
63
+ hasOptGroups: function() {
64
+ if (this.options && Object.keys(this.options).length > 0) {
65
+ if (typeof (this.options[Object.keys(this.options)[0]]) === 'object') {
66
+ return true
67
+ }
68
+ }
69
+ return false
70
+ }
71
+ },
72
+ methods: {
73
+ }
74
+ }
75
+ </script>
@@ -1,16 +1,18 @@
1
1
  <template>
2
- <div class="mb-3 ccm-context-theme">
3
- <select :name="inputName" data-select="theme-colors" data-width="100%">
4
- <option v-for="color in colorCollection.colors" :selected="selectedColor == color.variable" :data-content="dataContentAttribute(color)" :value="color.variable">{{ color.name }}</option>
2
+ <div class="ccm-context-theme">
3
+ <select :name="inputName" v-model="selectedColor">
4
+ <option v-for="color in colorCollection.colors" :selected="selectedColor == color.variable" :value="color.variable">{{ color.name }}</option>
5
5
  </select>
6
6
  </div>
7
7
  </template>
8
8
 
9
9
  <script>
10
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
11
+ /* globals TomSelect */
10
12
  export default {
11
13
  data() {
12
14
  return {
13
- selectedColor: ''
15
+ selectedColor: this.color
14
16
  }
15
17
  },
16
18
  props: {
@@ -35,18 +37,21 @@ export default {
35
37
  }
36
38
  },
37
39
  mounted() {
38
- if (this.color) {
39
- this.selectedColor = this.color
40
- }
41
-
42
- var $el = this.$el
43
- setTimeout(function() {
44
- $($el.querySelector('select[data-select=theme-colors]')).selectpicker()
45
- }, 5)
40
+ var my = this
41
+ new TomSelect(this.$el.querySelector('select'), {
42
+ render: {
43
+ option: function (data, escape) {
44
+ return my.dataContentAttribute(data)
45
+ },
46
+ item: function (item, escape) {
47
+ return my.dataContentAttribute(item)
48
+ }
49
+ }
50
+ })
46
51
  },
47
52
  methods: {
48
- dataContentAttribute: function(color) {
49
- return "<span style='background-color: var(--bs-" + color.variable + ")' class='btn me-2 p-2'></span> " + color.name
53
+ dataContentAttribute: function(item) {
54
+ return '<div><span style="background-color: var(--bs-' + item.value + ')" class="btn me-2 p-2"></span> ' + item.text + '</div>'
50
55
  }
51
56
  }
52
57
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="ccm-item-selector-group">
2
+ <div :class="cssClasses">
3
3
  <input type="hidden" :name="inputName" :value="selectedUserID" v-if="inputName !== ''" />
4
4
 
5
5
  <div v-if="isLoading">
@@ -43,9 +43,21 @@ export default {
43
43
  computed: {
44
44
  isLoading() {
45
45
  return this.isLoadingUserID > 0
46
+ },
47
+ cssClasses() {
48
+ let classes = ['ccm-item-selector-group']
49
+ if (this.cssClass) {
50
+ classes = [...classes, this.cssClass]
51
+ }
52
+ return classes
46
53
  }
47
54
  },
48
55
  props: {
56
+ cssClass: {
57
+ type: String,
58
+ default: '',
59
+ required: false
60
+ },
49
61
  inputName: {
50
62
  type: String,
51
63
  default: ''
@@ -63,7 +75,18 @@ export default {
63
75
  default: false
64
76
  }
65
77
  },
78
+ prop: ['userId'],
79
+ model: {
80
+ prop: 'userId',
81
+ event: 'change'
82
+ },
66
83
  watch: {
84
+ userId: {
85
+ immediate: true,
86
+ handler(value) {
87
+ this.selectedUserID = this.userId
88
+ }
89
+ },
67
90
  selectedUserID: {
68
91
  immediate: true,
69
92
  handler(value) {
@@ -77,9 +100,6 @@ export default {
77
100
  }
78
101
  },
79
102
  mounted() {
80
- if (this.userId) {
81
- this.selectedUserID = this.userId
82
- }
83
103
  },
84
104
  methods: {
85
105
  chooseFile: function(selectedUsers) {
@@ -0,0 +1,126 @@
1
+ <template>
2
+ <concrete-ajax-select
3
+ :name="inputName"
4
+ :access-token="accessToken"
5
+ :css-classes="['ccm-form-select-user']"
6
+ :data-source-url="dataSourceUrl"
7
+ :selected-options-url="selectedOptionsUrl"
8
+ :value="userId"
9
+ @change="updateSelected"
10
+ :form-data="formData"
11
+ >
12
+ </concrete-ajax-select>
13
+ </template>
14
+
15
+ <script>
16
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
17
+ /* globals TomSelect */
18
+ import ConcreteAjaxSelect from './ConcreteAjaxSelect'
19
+ export default {
20
+ components: { ConcreteAjaxSelect },
21
+ prop: ['userId'],
22
+ model: {
23
+ prop: 'userId',
24
+ event: 'change'
25
+ },
26
+ props: {
27
+ includeAvatar: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ labelFormat: {
32
+ type: String,
33
+ default: 'auto'
34
+ },
35
+ accessToken: {
36
+ type: String,
37
+ required: true
38
+ },
39
+ inputName: {
40
+ type: String
41
+ },
42
+ userId: {
43
+ type: [Number, String, Array],
44
+ required: false
45
+ }
46
+ },
47
+ computed: {
48
+ formData() {
49
+ return {
50
+ labelFormat: this.labelFormat,
51
+ includeAvatar: this.includeAvatar,
52
+ userId: this.userId
53
+ }
54
+ }
55
+ },
56
+ data() {
57
+ return {
58
+ dataSourceUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/user/autocomplete',
59
+ selectedOptionsUrl: CCM_DISPATCHER_FILENAME + '/ccm/system/user/autocomplete/get_selected',
60
+ selectedUserId: null
61
+ }
62
+ },
63
+ watch: {
64
+ userId: {
65
+ immediate: true,
66
+ handler: function(userId) {
67
+ this.selectedUserId = userId
68
+ }
69
+ }
70
+ },
71
+ methods: {
72
+ updateSelected(value) {
73
+ this.selectedUserId = value
74
+ this.$emit('change', this.selectedUserId)
75
+ },
76
+ renderOption(item) {
77
+ var option = '<div>'
78
+
79
+ if (item.avatar) {
80
+ option += `<div class="ccm-form-select-user-avatar">
81
+ <img src="${item.avatar}"/>
82
+ </div>`
83
+ }
84
+
85
+ option += '<div>'
86
+ option += `<div class="ccm-form-select-user-label-primary">
87
+ ${escape(item.primary_label)}
88
+ </div>`
89
+
90
+ if (item.secondary_label) {
91
+ option += `<div class="ccm-form-select-user-label-secondary text-secondary">
92
+ ${escape(item.secondary_label)}
93
+ </div>`
94
+ }
95
+
96
+ option += '</div>'
97
+ option += '</div>'
98
+ return option
99
+ }
100
+ }
101
+ }
102
+ </script>
103
+ <style lang="scss">
104
+ .ccm-form-select-user {
105
+ .item,
106
+ .option {
107
+ align-items: center;
108
+ display: flex;
109
+ }
110
+
111
+ .ccm-form-select-user-label-secondary {
112
+ font-size: 0.8rem;
113
+ }
114
+
115
+ .ccm-form-select-user-avatar {
116
+ flex: 0 0 20px; // Ensure this is at least 30px wide
117
+ margin-right: 12px;
118
+
119
+ img {
120
+ border-radius: 50%;
121
+ height: auto;
122
+ max-width: 100%;
123
+ }
124
+ }
125
+ }
126
+ </style>
@@ -1,10 +1,12 @@
1
1
  <template>
2
- <select class="form-control" :name="name" :title="title" v-model="selectedOption" ref="iconSelector">
3
- <option v-for="icon in icons" :value="icon.value" :data-icon="icon.value + ' fa-fw'">{{ icon.label }}</option>
2
+ <select :name="name" :title="title" v-model="selectedOption" ref="iconSelector">
3
+ <option v-for="icon in icons" :value="icon.value">{{ icon.label }}</option>
4
4
  </select>
5
5
  </template>
6
6
 
7
7
  <script>
8
+ /* eslint-disable no-new, no-unused-vars, camelcase, eqeqeq */
9
+ /* globals TomSelect */
8
10
  import { icons } from '../iconlist'
9
11
 
10
12
  // Export our component definition
@@ -58,9 +60,16 @@ export default {
58
60
  }
59
61
  },
60
62
  mounted() {
61
- $(this.$refs.iconSelector).selectpicker({
62
- liveSearch: true,
63
- width: 'fit'
63
+ new TomSelect(this.$el, {
64
+ maxOptions: null,
65
+ render: {
66
+ option: function (data, escape) {
67
+ return '<div class="d-flex align-items-center"><span class="d-flex align-items-center justify-content-center me-2" style="width: 32px"><i class="' + data.value + '"></i></span><span>' + data.text + '</span></div>'
68
+ },
69
+ item: function (item, escape) {
70
+ return '<div class="d-flex align-items-center"><span class="d-flex align-items-center justify-content-center me-2" style="width: 32px"><i class="' + item.value + '"></i></span><span>' + item.text + '</span></div>'
71
+ }
72
+ }
64
73
  })
65
74
  }
66
75
  }
@@ -1,26 +1,56 @@
1
1
  <template>
2
+ <div class="password-widget">
2
3
  <div class="input-group">
3
- <input
4
- class="form-control"
5
- autocomplete="off"
6
- value=""
7
- :name="name"
8
- :id="name"
9
- :type="inputType"
10
- />
11
- <button v-if="toggler" type="button" class="input-group-icon" @click="toggle()">
12
- <i :class="`fas fa-${iconName}`" aria-hidden="true"></i>
13
- </button>
4
+ <input
5
+ class="form-control"
6
+ autocomplete="off"
7
+ value=""
8
+ :name="name"
9
+ :id="name"
10
+ :type="inputType"
11
+ v-model="enteredPassword"
12
+ />
13
+ <button
14
+ v-if="toggler"
15
+ type="button"
16
+ class="input-group-icon"
17
+ @click="passwordVisible = !passwordVisible"
18
+ >
19
+ <i aria-hidden="true" class="fas" :class="iconClasses"></i>
20
+ </button>
14
21
  </div>
22
+
23
+ <ul
24
+ v-if="strengthMeter"
25
+ class="d-flex align-items-center mt-2 password-strength-meter"
26
+ >
27
+ <li
28
+ v-for="bar in bars"
29
+ class="flex-grow-1 rounded h-100 me-2 bg-light bar"
30
+ :class="strength.id >= bar.id ? 'active' : ''"
31
+ :title="bar.value"
32
+ ></li>
33
+ </ul>
34
+ </div>
15
35
  </template>
16
36
 
17
37
  <script>
38
+ import { passwordStrength } from 'check-password-strength'
39
+
18
40
  export default {
19
- data() {
41
+ data: () => {
20
42
  return {
43
+ i18n: {
44
+ invalid: 'Invalid',
45
+ tooWeak: 'Too Weak',
46
+ weak: 'Weak',
47
+ medium: 'Medium',
48
+ strong: 'Strong',
49
+ veryStrong: 'Very Strong'
50
+ },
51
+ enteredPassword: '',
21
52
  passwordVisible: false,
22
- inputType: 'password',
23
- iconName: 'eye'
53
+ passwordStrengthOptions: []
24
54
  }
25
55
  },
26
56
  props: {
@@ -30,20 +60,107 @@ export default {
30
60
  toggler: {
31
61
  type: Boolean,
32
62
  default: true
63
+ },
64
+ strengthMeter: {
65
+ type: Boolean,
66
+ default: false
33
67
  }
34
68
  },
35
- methods: {
36
- toggle() {
37
- this.passwordVisible = !this.passwordVisible
38
-
39
- if (this.passwordVisible === true) {
40
- this.inputType = 'text'
41
- this.iconName = 'eye-slash'
42
- } else {
43
- this.inputType = 'password'
44
- this.iconName = 'eye'
69
+ computed: {
70
+ inputType() {
71
+ return this.passwordVisible ? 'text' : 'password'
72
+ },
73
+ iconClasses() {
74
+ return {
75
+ 'fa-eye': !this.passwordVisible,
76
+ 'fa-eye-slash': this.passwordVisible
45
77
  }
78
+ },
79
+ strength() {
80
+ return passwordStrength(
81
+ this.enteredPassword,
82
+ this.passwordStrengthOptions
83
+ )
84
+ },
85
+ bars() {
86
+ return this.passwordStrengthOptions.filter(
87
+ (option) => option.id >= 0
88
+ )
46
89
  }
90
+ },
91
+ mounted() {
92
+ if (window.ccmi18n_passwordInput) {
93
+ for (const key in this.i18n) {
94
+ if (window.ccmi18n_passwordInput[key]) {
95
+ this.i18n[key] = window.ccmi18n_passwordInput[key]
96
+ }
97
+ }
98
+ }
99
+
100
+ this.passwordStrengthOptions = [
101
+ {
102
+ id: -1,
103
+ value: this.i18n.invalid, // concretecms requirement: min 5 character
104
+ minDiversity: 0,
105
+ minLength: 0
106
+ },
107
+ {
108
+ id: 0,
109
+ value: this.i18n.tooWeak,
110
+ minDiversity: 1,
111
+ minLength: 5
112
+ },
113
+ {
114
+ id: 1,
115
+ value: this.i18n.weak,
116
+ minDiversity: 2,
117
+ minLength: 6
118
+ },
119
+ {
120
+ id: 2,
121
+ value: this.i18n.medium,
122
+ minDiversity: 4,
123
+ minLength: 8
124
+ },
125
+ {
126
+ id: 3,
127
+ value: this.i18n.strong,
128
+ minDiversity: 4,
129
+ minLength: 12
130
+ },
131
+ {
132
+ id: 4,
133
+ value: this.i18n.veryStrong,
134
+ minDiversity: 4,
135
+ minLength: 16
136
+ }
137
+ ]
47
138
  }
48
139
  }
49
140
  </script>
141
+
142
+ <style lang="scss" scoped>
143
+ * {
144
+ --var-active-color: #b8d935;
145
+ }
146
+
147
+ .password-strength-meter {
148
+ height: 3px;
149
+ list-style: none;
150
+ padding: 0;
151
+
152
+ .bar {
153
+ box-shadow: 0 0 0 0 var(--var-active-color) inset;
154
+ cursor: help;
155
+ transition: all ease-in 200ms;
156
+
157
+ &.active {
158
+ box-shadow: 1000px 0 0 0 var(--var-active-color) inset;
159
+ }
160
+
161
+ &:last-child {
162
+ margin-right: 0 !important;
163
+ }
164
+ }
165
+ }
166
+ </style>