apostrophe 4.5.4 → 4.6.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 (51) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/lib/mongodb-connect.js +9 -2
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +6 -1
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +18 -180
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +6 -2
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +1 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +11 -0
  8. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +2 -0
  9. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +6 -1
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +63 -11
  11. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +12 -15
  12. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +10 -1
  13. package/modules/@apostrophecms/db/index.js +2 -3
  14. package/modules/@apostrophecms/doc-type/index.js +7 -1
  15. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +183 -109
  16. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocLocalePicker.vue +177 -0
  17. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +4 -0
  18. package/modules/@apostrophecms/i18n/i18n/de.json +1 -0
  19. package/modules/@apostrophecms/i18n/i18n/en.json +7 -1
  20. package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
  21. package/modules/@apostrophecms/i18n/i18n/fr.json +1 -0
  22. package/modules/@apostrophecms/i18n/i18n/it.json +1 -0
  23. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -0
  24. package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
  25. package/modules/@apostrophecms/i18n/index.js +22 -0
  26. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +1 -0
  27. package/modules/@apostrophecms/i18n/ui/src/index.js +3 -0
  28. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +1 -0
  29. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +22 -30
  30. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +24 -1
  31. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +48 -38
  32. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +7 -0
  33. package/modules/@apostrophecms/page/index.js +3 -1
  34. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +1 -0
  35. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +17 -3
  36. package/modules/@apostrophecms/piece-type/index.js +3 -1
  37. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +25 -4
  38. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +1 -1
  39. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +4 -3
  40. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +17 -10
  41. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +35 -22
  42. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +1 -0
  43. package/modules/@apostrophecms/ui/ui/apos/components/AposLocale.vue +36 -0
  44. package/modules/@apostrophecms/ui/ui/apos/components/AposLocalePicker.vue +283 -0
  45. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +56 -18
  46. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -1
  47. package/modules/@apostrophecms/ui/ui/apos/mixins/AposArchiveMixin.js +4 -2
  48. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +12 -6
  49. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +10 -1
  50. package/modules/@apostrophecms/util/ui/src/http.js +12 -5
  51. package/package.json +4 -4
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <AposModal
3
3
  :modal="modal"
4
+ :modal-data="modalData"
4
5
  modal-title="apostrophe:managePages"
5
6
  @esc="confirmAndCancel"
6
7
  @inactive="modal.active = false"
@@ -10,8 +10,13 @@ export default {
10
10
  name: 'AposPagesManager',
11
11
  mixins: [ AposModifiedMixin, AposDocsManagerMixin, AposArchiveMixin, AposPublishMixin ],
12
12
  emits: [ 'archive', 'search', 'modal-result' ],
13
+ props: {
14
+ modalData: {
15
+ type: Object,
16
+ required: true
17
+ }
18
+ },
13
19
  data() {
14
-
15
20
  return {
16
21
  moduleName: '@apostrophecms/page',
17
22
  modal: {
@@ -149,12 +154,12 @@ export default {
149
154
  await this.getPages();
150
155
  this.modal.triggerFocusRefresh++;
151
156
 
152
- apos.bus.$on('content-changed', this.getPages);
157
+ apos.bus.$on('content-changed', this.onContentChanged);
153
158
  apos.bus.$on('command-menu-manager-create-new', this.create);
154
159
  apos.bus.$on('command-menu-manager-close', this.confirmAndCancel);
155
160
  },
156
161
  unmounted() {
157
- apos.bus.$off('content-changed', this.getPages);
162
+ apos.bus.$off('content-changed', this.onContentChanged);
158
163
  apos.bus.$off('command-menu-manager-create-new', this.create);
159
164
  apos.bus.$off('command-menu-manager-close', this.confirmAndCancel);
160
165
  },
@@ -164,6 +169,15 @@ export default {
164
169
  this.create();
165
170
  }
166
171
  },
172
+ onContentChanged({ doc }) {
173
+ if (
174
+ !doc ||
175
+ !doc.aposLocale ||
176
+ doc.aposLocale.split(':')[0] === this.modalData.locale
177
+ ) {
178
+ this.getPages();
179
+ }
180
+ },
167
181
  async getPages () {
168
182
  const self = this;
169
183
  if (this.gettingPages) {
@@ -820,8 +820,10 @@ module.exports = {
820
820
  async convertInsertAndRefresh(req, input, options) {
821
821
  const piece = self.newInstance();
822
822
  const copyingId = self.apos.launder.id(input._copyingId);
823
+ const createId = self.apos.launder.id(input._createId);
823
824
  await self.convert(req, input, piece, {
824
- copyingId
825
+ copyingId,
826
+ createId
825
827
  });
826
828
  await self.emit('afterConvert', req, input, piece);
827
829
  await self.insert(req, piece);
@@ -3,6 +3,7 @@
3
3
  ref="modal"
4
4
  :modal="modal"
5
5
  :modal-title="modalTitle"
6
+ :modal-data="modalData"
6
7
  @esc="confirmAndCancel"
7
8
  @inactive="modal.active = false"
8
9
  @show-modal="modal.showModal = true"
@@ -139,6 +140,10 @@ export default {
139
140
  moduleName: {
140
141
  type: String,
141
142
  required: true
143
+ },
144
+ modalData: {
145
+ type: Object,
146
+ required: true
142
147
  }
143
148
  },
144
149
  emits: [ 'archive' ],
@@ -354,7 +359,9 @@ export default {
354
359
  async selectAllPieces () {
355
360
  const { results: docs } = await this.request({
356
361
  project: {
357
- _id: 1
362
+ _id: 1,
363
+ _url: 1,
364
+ title: 1
358
365
  },
359
366
  attachments: false,
360
367
  perPage: this.allPiecesSelection.total
@@ -469,6 +476,11 @@ export default {
469
476
  : this.moduleLabels.plural
470
477
  }
471
478
  });
479
+ if (action === 'archive') {
480
+ await this.getPieces();
481
+ this.getAllPiecesTotal();
482
+ this.checked = [];
483
+ }
472
484
  } catch (error) {
473
485
  apos.notify('apostrophe:errorBatchOperationNoti', {
474
486
  interpolate: { operation: label },
@@ -479,15 +491,24 @@ export default {
479
491
  }
480
492
  },
481
493
  setCheckedDocs(checked) {
482
- this.checkedDocs = checked;
494
+ this.checkedDocs = checked.slice(0, this.relationshipField?.max || checked.length);
483
495
  this.checked = this.checkedDocs.map(item => {
484
496
  return item._id;
485
497
  });
486
498
  },
487
499
 
488
500
  async onContentChanged({ doc, action }) {
489
- await this.getPieces();
490
- this.getAllPiecesTotal();
501
+ if (
502
+ !doc ||
503
+ !doc.aposLocale ||
504
+ doc.aposLocale.split(':')[0] === this.modalData.locale
505
+ ) {
506
+ await this.getPieces();
507
+ this.getAllPiecesTotal();
508
+ if (action === 'archive') {
509
+ this.checked = this.checked.filter(checkedId => doc._id !== checkedId);
510
+ }
511
+ }
491
512
  }
492
513
  }
493
514
  };
@@ -63,7 +63,7 @@ export default {
63
63
  showSelectAll() {
64
64
  if (
65
65
  this.allPiecesSelection.isSelected ||
66
- (this.selectedState === 'checked' && this.allPiecesSelection.total > this.displayedItems)
66
+ ([ 'checked', 'indeterminate' ].includes(this.selectedState) && this.allPiecesSelection.total > this.displayedItems)
67
67
  ) {
68
68
  return true;
69
69
  }
@@ -138,7 +138,7 @@ export default {
138
138
  },
139
139
  active(newVal) {
140
140
  if (newVal) {
141
- this.hasLinkOnOpen = !!(this.docFields.data.href);
141
+ this.hasLinkOnOpen = !!(this.attributes.href);
142
142
  window.addEventListener('keydown', this.keyboardHandler);
143
143
  } else {
144
144
  window.removeEventListener('keydown', this.keyboardHandler);
@@ -207,10 +207,10 @@ export default {
207
207
  });
208
208
  },
209
209
  keyboardHandler(e) {
210
- if (e.keyCode === 27) {
210
+ if (e.key === 'Escape') {
211
211
  this.close();
212
212
  }
213
- if (e.keyCode === 13) {
213
+ if (e.key === 'Enter') {
214
214
  if (this.docFields.data.href || e.metaKey) {
215
215
  this.save();
216
216
  this.close();
@@ -268,6 +268,7 @@ export default {
268
268
  } finally {
269
269
  this.generation++;
270
270
  }
271
+ this.evaluateConditions();
271
272
  }
272
273
  }
273
274
  };
@@ -9,7 +9,8 @@
9
9
  v-bind="attrs"
10
10
  :is="href ? 'a' : 'button'"
11
11
  :id="attrs.id ? attrs.id : id"
12
- :href="href.length ? href : false"
12
+ :target="target"
13
+ :href="href"
13
14
  class="apos-button"
14
15
  :class="modifierClass"
15
16
  :tabindex="tabindex"
@@ -82,8 +83,8 @@ export default {
82
83
  default: null
83
84
  },
84
85
  href: {
85
- type: [ String, Boolean ],
86
- default: false
86
+ type: String,
87
+ default: null
87
88
  },
88
89
  iconSize: {
89
90
  type: Number,
@@ -121,16 +122,20 @@ export default {
121
122
  },
122
123
  disableFocus: Boolean,
123
124
  buttonType: {
124
- type: [ String, Boolean ],
125
- default: false
125
+ type: String,
126
+ default: null
126
127
  },
127
128
  role: {
128
- type: [ String, Boolean ],
129
- default: false
129
+ type: String,
130
+ default: null
130
131
  },
131
132
  tooltip: {
132
133
  type: [ String, Object, Boolean ],
133
134
  default: false
135
+ },
136
+ target: {
137
+ type: String,
138
+ default: null
134
139
  }
135
140
  },
136
141
  emits: [ 'click' ],
@@ -207,7 +212,7 @@ export default {
207
212
  return null;
208
213
  },
209
214
  isDisabled() {
210
- return this.disabled || this.busy;
215
+ return (this.disabled || this.busy) || null;
211
216
  },
212
217
  actionTestLabel() {
213
218
  return this.action
@@ -384,6 +389,10 @@ export default {
384
389
  text-decoration: none;
385
390
  background-color: var(--a-base-10);
386
391
  }
392
+
393
+ &:focus {
394
+ outline: 1px solid var(--a-base-7);
395
+ }
387
396
  }
388
397
 
389
398
  .apos-button--gradient-on-hover {
@@ -661,8 +670,6 @@ export default {
661
670
  &:hover:not([disabled]),
662
671
  &:focus:not([disabled]) {
663
672
  transform: none;
664
- box-shadow: none;
665
- outline: none;
666
673
  }
667
674
  }
668
675
 
@@ -9,8 +9,8 @@
9
9
  v-bind="button"
10
10
  ref="button"
11
11
  class="apos-context-menu__btn"
12
- data-apos-test="contextMenuTrigger"
13
12
  role="button"
13
+ :data-apos-test="identifier"
14
14
  :state="buttonState"
15
15
  :disabled="disabled"
16
16
  :tooltip="tooltip"
@@ -20,28 +20,27 @@
20
20
  }"
21
21
  @click.stop="buttonClicked($event)"
22
22
  />
23
- <Teleport to="body">
24
- <div
25
- v-if="isOpen"
26
- ref="dropdownContent"
27
- v-click-outside-element="hide"
28
- class="apos-context-menu__dropdown-content"
29
- :class="popoverClass"
30
- data-apos-menu
31
- :style="dropdownContentStyle"
32
- :aria-hidden="!isOpen"
23
+ <div
24
+ v-if="isOpen"
25
+ ref="dropdownContent"
26
+ v-click-outside-element="hide"
27
+ class="apos-context-menu__dropdown-content"
28
+ :class="popoverClass"
29
+ data-apos-menu
30
+ :style="dropdownContentStyle"
31
+ :aria-hidden="!isOpen"
32
+ >
33
+ <AposContextMenuDialog
34
+ :menu-placement="placement"
35
+ :class-list="classList"
36
+ :menu="menu"
37
+ :is-open="isOpen"
38
+ @item-clicked="menuItemClicked"
39
+ @set-arrow="setArrow"
33
40
  >
34
- <AposContextMenuDialog
35
- :menu-placement="placement"
36
- :class-list="classList"
37
- :menu="menu"
38
- @item-clicked="menuItemClicked"
39
- @set-arrow="setArrow"
40
- >
41
- <slot />
42
- </AposContextMenuDialog>
43
- </div>
44
- </Teleport>
41
+ <slot />
42
+ </AposContextMenuDialog>
43
+ </div>
45
44
  </div>
46
45
  </div>
47
46
  </template>
@@ -57,6 +56,10 @@ import { useAposTheme } from 'Modules/@apostrophecms/ui/composables/AposTheme';
57
56
  import { createId } from '@paralleldrive/cuid2';
58
57
 
59
58
  const props = defineProps({
59
+ identifier: {
60
+ type: String,
61
+ default: 'contextMenuTrigger'
62
+ },
60
63
  menu: {
61
64
  type: Array,
62
65
  default: null
@@ -155,10 +158,14 @@ watch(isOpen, (newVal) => {
155
158
  if (newVal) {
156
159
  window.addEventListener('resize', setDropdownPosition);
157
160
  window.addEventListener('scroll', setDropdownPosition);
161
+ window.addEventListener('keydown', handleKeyboard);
158
162
  setDropdownPosition();
163
+ dropdownContent.value.querySelector('[tabindex]')?.focus();
159
164
  } else {
160
165
  window.removeEventListener('resize', setDropdownPosition);
161
166
  window.removeEventListener('scroll', setDropdownPosition);
167
+ window.removeEventListener('keydown', handleKeyboard);
168
+ dropdown.value.querySelector('[tabindex]').focus();
162
169
  }
163
170
  }, { flush: 'post' });
164
171
 
@@ -237,6 +244,12 @@ async function setDropdownPosition() {
237
244
  ...arrowY && { top: `${arrowY}px` }
238
245
  });
239
246
  }
247
+
248
+ function handleKeyboard(event) {
249
+ if (event.key === 'Escape') {
250
+ hide();
251
+ }
252
+ }
240
253
  </script>
241
254
 
242
255
  <style lang="scss">
@@ -4,6 +4,7 @@
4
4
  class="apos-context-menu__button"
5
5
  :class="modifiers"
6
6
  :tabindex="tabindex"
7
+ role="menuitem"
7
8
  data-apos-test="context-menu-item"
8
9
  :data-apos-test-selected="selected"
9
10
  :data-apos-test-danger="danger"
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div class="apos-locale">
3
+ <span class="apos-locale__label">
4
+ {{ $t('apostrophe:locale') }}:
5
+ </span> <span class="apos-locale__label-name">
6
+ {{ locale }}
7
+ </span>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { computed } from 'vue';
13
+ const props = defineProps({
14
+ locale: {
15
+ type: String,
16
+ required: true
17
+ }
18
+ });
19
+
20
+ const locale = computed(() => {
21
+ const label = apos.i18n.locales[props.locale]?.label;
22
+ return label ? `${label} (${props.locale})` : props.locale;
23
+ });
24
+ </script>
25
+
26
+ <style lang="scss" scoped>
27
+ .apos-locale {
28
+ @include type-base;
29
+
30
+ font-weight: var(--a-weight-bold);
31
+ }
32
+
33
+ .apos-locale__label-name {
34
+ color: var(--a-primary);
35
+ }
36
+ </style>
@@ -0,0 +1,283 @@
1
+ <template>
2
+ <div
3
+ class="apos-locales-picker"
4
+ data-apos-test="localePicker"
5
+ >
6
+ <div class="apos-input-wrapper">
7
+ <input
8
+ v-model="search"
9
+ type="text"
10
+ class="apos-locales-picker__filter"
11
+ :placeholder="$t('apostrophe:searchLocalesPlaceholder')"
12
+ >
13
+ </div>
14
+ <ul class="apos-locales-picker__list">
15
+ <li
16
+ v-for="locale in filteredLocales"
17
+ :key="locale.name"
18
+ v-apos-tooltip="getForbiddenTooltip(locale)"
19
+ :class="localeClasses(locale)"
20
+ class="apos-locale-picker__item"
21
+ data-apos-test="localeItem"
22
+ @click="switchLocale(locale)"
23
+ >
24
+ <button :tabindex="isOpen ? '0' : '-1'" class="apos-locale-picker__locale-display">
25
+ <AposIndicator
26
+ v-if="isForbidden(locale)"
27
+ icon="lock-icon"
28
+ icon-color="var(--a-base-5)"
29
+ class="apos-locale-picker__check"
30
+ :icon-size="12"
31
+ :title="$t('apostrophe:forbidden')"
32
+ />
33
+ <AposIndicator
34
+ v-else-if="isActive(locale)"
35
+ icon="check-bold-icon"
36
+ icon-color="var(--a-primary)"
37
+ class="apos-locale-picker__check"
38
+ :icon-size="12"
39
+ :title="$t('apostrophe:currentLocale')"
40
+ />
41
+ {{ locale.label }}
42
+ <span class="apos-locale-picker__name">
43
+ ({{ locale.name }})
44
+ </span>
45
+ <span
46
+ class="apos-locale-picker__localized"
47
+ :class="{ 'apos-state-is-localized': isLocalized(locale) }"
48
+ />
49
+ </button>
50
+ </li>
51
+ </ul>
52
+ <div class="apos-locales-picker__available">
53
+ <p class="apos-locales-picker__available-desc">
54
+ {{ $t('apostrophe:documentExistsInLocales') }}
55
+ </p>
56
+ <AposButton
57
+ v-for="locale in availableLocales"
58
+ :key="locale.name"
59
+ class="apos-locales-picker__available-locale"
60
+ :label="locale.label"
61
+ type="quiet"
62
+ :modifiers="['no-motion']"
63
+ @click="switchLocale(locale)"
64
+ />
65
+ </div>
66
+ </div>
67
+ </template>
68
+
69
+ <script setup>
70
+ import { ref, computed } from 'vue';
71
+
72
+ const emit = defineEmits([ 'switch-locale' ]);
73
+ const props = defineProps({
74
+ currentLocale: {
75
+ type: String,
76
+ required: true
77
+ },
78
+ localized: {
79
+ type: Object,
80
+ required: true
81
+ },
82
+ forbidden: {
83
+ type: Array,
84
+ default: () => ([])
85
+ },
86
+ forbiddenTooltip: {
87
+ type: String,
88
+ default: null
89
+ },
90
+ isOpen: {
91
+ type: Boolean,
92
+ default: false
93
+ }
94
+ });
95
+
96
+ const search = ref('');
97
+ const locales = ref(Object.entries(window.apos.i18n.locales).map(
98
+ ([ locale, options ]) => {
99
+ return {
100
+ name: locale,
101
+ label: options.label || locale
102
+ };
103
+ }
104
+ ));
105
+
106
+ const filteredLocales = computed(() => {
107
+ return locales.value.filter(({ name, label }) => {
108
+ const matches = term =>
109
+ term.toLowerCase().includes(search.value.toLowerCase());
110
+ return matches(name) || matches(label);
111
+ });
112
+ });
113
+
114
+ const availableLocales = computed(() => {
115
+ return locales.value.filter(locale => isLocalized(locale));
116
+ });
117
+
118
+ function isLocalized(locale) {
119
+ return Boolean(props.localized[locale.name]);
120
+ }
121
+
122
+ function isActive(locale) {
123
+ return props.currentLocale === locale.name;
124
+ }
125
+
126
+ function isForbidden(locale) {
127
+ return props.forbidden.includes(locale.name);
128
+ }
129
+
130
+ function switchLocale(locale) {
131
+ emit('switch-locale', locale);
132
+ }
133
+
134
+ function localeClasses(locale) {
135
+ return {
136
+ 'apos-active': isActive(locale),
137
+ 'apos-exists': isLocalized(locale),
138
+ 'apos-forbidden': isForbidden(locale)
139
+ };
140
+ }
141
+
142
+ function getForbiddenTooltip(locale) {
143
+ return isForbidden(locale) && {
144
+ content: props.forbiddenTooltip,
145
+ placement: 'left'
146
+ };
147
+ }
148
+ </script>
149
+
150
+ <style lang="scss" scoped>
151
+ .apos-locale-picker__locale-display {
152
+ @include apos-button-reset();
153
+
154
+ display: block;
155
+ box-sizing: border-box;
156
+ width: 100%;
157
+ padding: 12px 35px;
158
+
159
+ &:focus, &:active {
160
+ outline: none;
161
+ border: none;
162
+ background-color: var(--a-base-10);
163
+ }
164
+
165
+ &:hover {
166
+ background-color: var(--a-base-9);
167
+ }
168
+
169
+ }
170
+
171
+ .apos-locales-picker {
172
+ width: 315px;
173
+ }
174
+
175
+ .apos-locales-picker__filter {
176
+ @include type-large;
177
+
178
+ box-sizing: border-box;
179
+ width: 100%;
180
+ padding: 20px 45px 20px 20px;
181
+ border-top: 0;
182
+ border-right: 0;
183
+ border-bottom: 1px solid var(--a-base-9);
184
+ border-left: 0;
185
+ border-top-right-radius: var(--a-border-radius);
186
+ border-top-left-radius: var(--a-border-radius);
187
+
188
+ &::placeholder {
189
+ color: var(--a-base-4);
190
+ font-style: italic;
191
+ }
192
+
193
+ &:focus {
194
+ outline: none;
195
+ background-color: var(--a-base-10);
196
+ }
197
+ }
198
+
199
+ .apos-locales-picker__list {
200
+ margin: $spacing-base 0;
201
+ padding-left: 0;
202
+ list-style-type: none;
203
+ max-height: 350px;
204
+ overflow-y: scroll;
205
+ font-weight: var(--a-weight-base);
206
+ }
207
+
208
+ .apos-locale-picker__item {
209
+ position: relative;
210
+ line-height: 1;
211
+ cursor: pointer;
212
+
213
+ .state {
214
+ opacity: 0;
215
+ }
216
+
217
+ &:hover {
218
+ background-color: var(--a-base-10);
219
+ }
220
+
221
+ .apos-locale-picker__check {
222
+ position: absolute;
223
+ top: 50%;
224
+ left: 18px;
225
+ transform: translateY(-50%);
226
+ color: var(--a-primary);
227
+ stroke: var(--a-primary);
228
+
229
+ :deep(.material-design-icon__svg) {
230
+ stroke: none;
231
+ }
232
+ }
233
+
234
+ .apos-locale-picker__localized {
235
+ position: relative;
236
+ top: -1px;
237
+ left: 5px;
238
+ display: inline-block;
239
+ width: 3px;
240
+ height: 3px;
241
+ border: 1px solid var(--a-base-5);
242
+ border-radius: 50%;
243
+
244
+ &.apos-state-is-localized {
245
+ background-color: var(--a-success);
246
+ border-color: var(--a-success);
247
+ }
248
+ }
249
+ }
250
+
251
+ .apos-forbidden {
252
+ color: var(--a-base-5);
253
+ cursor: not-allowed;
254
+
255
+ &:hover {
256
+ background-color: var(--a-base-10);
257
+ }
258
+ }
259
+
260
+ .apos-locales-picker__name {
261
+ text-transform: uppercase;
262
+ }
263
+
264
+ .apos-locales-picker__available {
265
+ padding: $spacing-double;
266
+ border-top: 1px solid var(--a-base-9);
267
+ }
268
+
269
+ .apos-locales-picker__available-locale {
270
+ display: inline-block;
271
+ color: var(--a-primary);
272
+ font-size: var(--a-type-small);
273
+
274
+ &:not(:last-of-type) {
275
+ margin-right: 10px;
276
+ margin-bottom: 5px;
277
+ }
278
+ }
279
+
280
+ .apos-locales-picker__available-desc {
281
+ margin-top: 0;
282
+ }
283
+ </style>