apostrophe 3.63.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/.eslintrc +13 -4
  2. package/CHANGELOG.md +21 -0
  3. package/defaults.js +1 -0
  4. package/modules/@apostrophecms/admin-bar/ui/apos/apps/AposAdminBar.js +7 -17
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +14 -16
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +1 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +22 -15
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarUser.vue +2 -2
  9. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +13 -8
  10. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +18 -10
  11. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +4 -4
  12. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextUndoRedo.vue +14 -8
  13. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposSavingIndicator.vue +2 -1
  14. package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +36 -54
  15. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +20 -25
  16. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +5 -12
  17. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +11 -3
  18. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +6 -6
  19. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenuItem.vue +3 -2
  20. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +31 -44
  21. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +6 -6
  22. package/modules/@apostrophecms/asset/index.js +25 -12
  23. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +3 -3
  24. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.vue.js +7 -1
  25. package/modules/@apostrophecms/busy/ui/apos/apps/AposBusy.js +8 -7
  26. package/modules/@apostrophecms/busy/ui/apos/components/TheAposBusy.vue +1 -1
  27. package/modules/@apostrophecms/command-menu/ui/apos/apps/AposCommandMenu.js +11 -29
  28. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +6 -6
  29. package/modules/@apostrophecms/command-menu/ui/apos/components/TheAposCommandMenu.vue +10 -7
  30. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +20 -15
  31. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +1 -1
  32. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +36 -27
  33. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalizeErrors.vue +3 -3
  34. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +5 -5
  35. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +6 -6
  36. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +23 -16
  37. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +11 -11
  38. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +28 -21
  39. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -1
  40. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +5 -4
  41. package/modules/@apostrophecms/login/ui/apos/apps/AposLogin.js +6 -7
  42. package/modules/@apostrophecms/login/ui/apos/components/AposForgotPasswordForm.vue +3 -3
  43. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +10 -10
  44. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +1 -1
  45. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +2 -2
  46. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +60 -87
  47. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +15 -11
  48. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +8 -2
  49. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +7 -4
  50. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +5 -5
  51. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +8 -8
  52. package/modules/@apostrophecms/modal/ui/apos/components/AposModalToolbar.vue +7 -7
  53. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +22 -4
  54. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +95 -0
  55. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +2 -3
  56. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
  57. package/modules/@apostrophecms/multisite-i18n/i18n/aposMultisite/en.json +48 -0
  58. package/modules/@apostrophecms/multisite-i18n/index.js +7 -0
  59. package/modules/@apostrophecms/notification/index.js +4 -4
  60. package/modules/@apostrophecms/notification/ui/apos/apps/AposNotification.js +6 -9
  61. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +12 -8
  62. package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +4 -4
  63. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +15 -11
  64. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +1 -1
  65. package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +3 -3
  66. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +25 -17
  67. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +24 -20
  68. package/modules/@apostrophecms/piece-type/ui/apos/components/AposRelationshipEditor.vue +15 -11
  69. package/modules/@apostrophecms/rich-text-widget/index.js +1 -0
  70. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +7 -6
  71. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +31 -30
  72. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapAnchor.vue +12 -10
  73. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +9 -8
  74. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -3
  75. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapTable.vue +3 -3
  76. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +10 -8
  77. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +5 -3
  78. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +81 -277
  79. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +4 -2
  80. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +24 -14
  81. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +7 -6
  82. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +10 -7
  83. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +3 -3
  84. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +5 -4
  85. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +15 -12
  86. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +1 -1
  87. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +16 -12
  88. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +19 -11
  89. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +1 -1
  90. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +65 -21
  91. package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +2 -2
  92. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +0 -4
  93. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +3 -3
  94. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +15 -4
  95. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +3 -3
  96. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +7 -7
  97. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +5 -8
  98. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +1 -1
  99. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +1 -1
  100. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +1 -1
  101. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +12 -9
  102. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +2 -2
  103. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +6 -8
  104. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +39 -13
  105. package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +1 -1
  106. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +9 -9
  107. package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +205 -0
  108. package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +4 -4
  109. package/modules/@apostrophecms/settings/ui/apos/logic/AposSettingsManager.js +4 -4
  110. package/modules/@apostrophecms/submitted-draft/ui/apos/components/AposSubmittedDraftIcon.vue +5 -4
  111. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +4 -4
  112. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +6 -6
  113. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +120 -113
  114. package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +19 -19
  115. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +15 -15
  116. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +216 -191
  117. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +77 -65
  118. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +1 -1
  119. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuTip.vue +28 -50
  120. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +5 -5
  121. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +4 -4
  122. package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +5 -5
  123. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +14 -8
  124. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +2 -2
  125. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +53 -59
  126. package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +2 -2
  127. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +40 -35
  128. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +2 -2
  129. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +9 -11
  130. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +5 -3
  131. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +129 -129
  132. package/modules/@apostrophecms/ui/ui/apos/composables/AposTheme.js +11 -0
  133. package/modules/@apostrophecms/ui/ui/apos/lib/click-outside-element.js +4 -4
  134. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +56 -50
  135. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +191 -0
  136. package/modules/@apostrophecms/ui/ui/apos/lib/vue.js +19 -10
  137. package/modules/@apostrophecms/ui/ui/apos/mixins/AposAdvisoryLockMixin.js +1 -1
  138. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +1 -1
  139. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tooltips.scss +6 -22
  140. package/modules/@apostrophecms/ui/ui/apos/scss/shared/_table-rows.scss +1 -1
  141. package/modules/@apostrophecms/widget-type/index.js +8 -2
  142. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +26 -22
  143. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +4 -4
  144. package/package.json +31 -44
  145. package/modules/@apostrophecms/ui/ui/apos/lib/localized-v-tooltip.js +0 -63
  146. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip-options.js +0 -13
@@ -1,175 +1,252 @@
1
1
  <template>
2
2
  <div class="apos-context-menu">
3
3
  <slot name="prebutton" />
4
- <v-popover
5
- @hide="hide"
6
- @show="show"
7
- :offset="menuOffset"
8
- trigger="manual"
9
- :placement="menuPlacement"
10
- :open="isOpen"
11
- :delay="{ show: 0, hide: 0 }"
12
- :popover-class="popoverClass"
13
- popover-wrapper-class="apos-popover__wrapper"
14
- popover-inner-class="apos-popover__inner"
4
+ <div
5
+ ref="dropdown"
6
+ class="apos-popover__btn apos-context-menu__dropdown"
15
7
  >
16
- <!-- TODO refactor buttons to take a single config obj -->
17
8
  <AposButton
9
+ v-bind="button"
10
+ ref="button"
18
11
  class="apos-context-menu__btn"
19
12
  data-apos-test="contextMenuTrigger"
20
- @click.stop="buttonClicked($event)"
21
- v-bind="button"
13
+ role="button"
22
14
  :state="buttonState"
23
- ref="button"
24
15
  :disabled="disabled"
25
16
  :tooltip="tooltip"
26
- role="button"
27
17
  :attrs="{
28
18
  'aria-haspopup': 'menu',
29
19
  'aria-expanded': isOpen ? true : false
30
20
  }"
21
+ @click.stop="buttonClicked($event)"
31
22
  />
32
- <template #popover class="apos-popover__slot">
33
- <AposContextMenuDialog
34
- :menu-placement="menuPlacement"
35
- :class-list="classList"
36
- :menu="menu"
37
- @item-clicked="menuItemClicked"
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"
38
33
  >
39
- <slot />
40
- </AposContextMenuDialog>
41
- </template>
42
- </v-popover>
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>
45
+ </div>
43
46
  </div>
44
47
  </template>
45
48
 
46
- <script>
49
+ <script setup>
50
+ import {
51
+ ref, computed, watch, nextTick, onMounted, onBeforeUnmount
52
+ } from 'vue';
47
53
  import {
48
- VPopover
49
- } from 'v-tooltip';
50
- import AposThemeMixin from 'Modules/@apostrophecms/ui/mixins/AposThemeMixin';
54
+ computePosition, offset, shift, flip, arrow
55
+ } from '@floating-ui/dom';
56
+ import { useAposTheme } from 'Modules/@apostrophecms/ui/composables/AposTheme';
57
+ import cuid from 'cuid';
51
58
 
52
- export default {
53
- name: 'AposContextMenu',
54
- components: {
55
- 'v-popover': VPopover
56
- },
57
- mixins: [ AposThemeMixin ],
58
- props: {
59
- menu: {
60
- type: Array,
61
- default: null
62
- },
63
- unpadded: {
64
- type: Boolean,
65
- default: false
66
- },
67
- modifiers: {
68
- type: Array,
69
- default() {
70
- return [];
71
- }
72
- },
73
- button: {
74
- type: Object,
75
- default() {
76
- return {
77
- label: 'Context Menu Label',
78
- iconOnly: true,
79
- icon: 'label-icon',
80
- type: 'outline'
81
- };
82
- }
83
- },
84
- menuPlacement: {
85
- type: String,
86
- default: 'bottom'
87
- },
88
- menuOffset: {
89
- type: [ Number, String ],
90
- default: 15
91
- },
92
- disabled: {
93
- type: Boolean,
94
- default: false
95
- },
96
- tooltip: {
97
- type: [ String, Boolean ],
98
- default: false
99
- },
100
- popoverModifiers: {
101
- type: Array,
102
- default() {
103
- return [];
104
- }
105
- }
59
+ const props = defineProps({
60
+ menu: {
61
+ type: Array,
62
+ default: null
106
63
  },
107
- emits: [ 'open', 'close', 'item-clicked' ],
108
- data() {
109
- return {
110
- isOpen: false,
111
- position: '',
112
- event: null
113
- };
64
+ unpadded: {
65
+ type: Boolean,
66
+ default: false
114
67
  },
115
- computed: {
116
- popoverClass() {
117
- const classes = [ 'apos-popover' ].concat(this.themeClass);
118
- this.popoverModifiers.forEach(m => {
119
- classes.push(`apos-popover--${m}`);
120
- });
121
- return classes;
122
- },
123
- classList() {
124
- const classes = [];
125
- const baseClass = 'apos-context-menu__popup';
126
- classes.push(`${baseClass}--tip-alignment-${this.menuPlacement}`);
127
- if (this.modifiers) {
128
- this.modifiers.forEach((m) => {
129
- classes.push(`${baseClass}--${m}`);
130
- });
131
- }
132
- if (this.menu || this.unpadded) {
133
- classes.push(`${baseClass}--unpadded`);
134
- }
135
- return classes.join(' ');
136
- },
137
- buttonState() {
138
- return this.open ? [ 'active' ] : null;
68
+ modifiers: {
69
+ type: Array,
70
+ default() {
71
+ return [];
139
72
  }
140
73
  },
141
- watch: {
142
- isOpen(newVal, oldVal) {
143
- if (newVal) {
144
- this.$emit('open', this.event);
145
- } else {
146
- this.$emit('close', this.event);
147
- }
74
+ button: {
75
+ type: Object,
76
+ default() {
77
+ return {
78
+ label: 'Context Menu Label',
79
+ iconOnly: true,
80
+ icon: 'label-icon',
81
+ type: 'outline'
82
+ };
148
83
  }
149
84
  },
150
- methods: {
151
- show() {
152
- this.isOpen = true;
153
- },
154
- hide() {
155
- this.isOpen = false;
156
- },
157
- buttonClicked(e) {
158
- this.isOpen = !this.isOpen;
159
- this.event = e;
160
- },
161
- menuItemClicked(name) {
162
- this.$emit('item-clicked', name);
163
- this.hide();
85
+ menuPlacement: {
86
+ type: String,
87
+ default: 'bottom'
88
+ },
89
+ menuOffset: {
90
+ type: [ Number, String ],
91
+ default: 15
92
+ },
93
+ disabled: {
94
+ type: Boolean,
95
+ default: false
96
+ },
97
+ tooltip: {
98
+ type: [ String, Boolean ],
99
+ default: false
100
+ },
101
+ popoverModifiers: {
102
+ type: Array,
103
+ default() {
104
+ return [];
164
105
  }
165
106
  }
166
- };
107
+ });
108
+
109
+ const emit = defineEmits([ 'open', 'close', 'item-clicked' ]);
110
+
111
+ const menuId = ref(cuid());
112
+ const isOpen = ref(false);
113
+ const placement = ref(props.menuPlacement);
114
+ const event = ref(null);
115
+ const dropdown = ref();
116
+ const dropdownContent = ref();
117
+ const dropdownContentStyle = ref({});
118
+ const arrowEl = ref();
119
+
120
+ defineExpose({
121
+ hide
122
+ });
123
+
124
+ const popoverClass = computed(() => {
125
+ const classes = [ 'apos-popover' ].concat(themeClass.value);
126
+ props.popoverModifiers.forEach(m => {
127
+ classes.push(`apos-popover--${m}`);
128
+ });
129
+ return classes;
130
+ });
131
+
132
+ const classList = computed(() => {
133
+ const classes = [];
134
+ const baseClass = 'apos-context-menu__popup';
135
+ classes.push(`${baseClass}--tip-alignment-${props.menuPlacement}`);
136
+ if (props.modifiers) {
137
+ props.modifiers.forEach((m) => {
138
+ classes.push(`${baseClass}--${m}`);
139
+ });
140
+ }
141
+ if (props.menu || props.unpadded) {
142
+ classes.push(`${baseClass}--unpadded`);
143
+ }
144
+ return classes.join(' ');
145
+ });
146
+
147
+ const buttonState = computed(() => {
148
+ return isOpen.value ? [ 'active' ] : null;
149
+ });
150
+
151
+ watch(isOpen, (newVal) => {
152
+ emit(newVal ? 'open' : 'close', event.value);
153
+ if (newVal) {
154
+ window.addEventListener('resize', setDropdownPosition);
155
+ window.addEventListener('scroll', setDropdownPosition);
156
+ nextTick(() => {
157
+ setDropdownPosition();
158
+ });
159
+ } else {
160
+ window.removeEventListener('resize', setDropdownPosition);
161
+ window.removeEventListener('scroll', setDropdownPosition);
162
+ }
163
+ });
164
+
165
+ const { themeClass } = useAposTheme();
166
+
167
+ onMounted(() => {
168
+ apos.bus.$on('context-menu-opened', hideWhenOtherOpen);
169
+ apos.bus.$on('widget-focus', hide);
170
+ });
171
+
172
+ onBeforeUnmount(() => {
173
+ apos.bus.$off('context-menu-opened', hideWhenOtherOpen);
174
+ apos.bus.$off('widget-focus', hide);
175
+ });
176
+
177
+ function hideWhenOtherOpen(id) {
178
+ if (menuId.value !== id) {
179
+ hide();
180
+ }
181
+ }
182
+
183
+ function hide() {
184
+ isOpen.value = false;
185
+ }
186
+
187
+ function buttonClicked(e) {
188
+ apos.bus.$emit('context-menu-opened', menuId.value);
189
+ isOpen.value = !isOpen.value;
190
+ event.value = e;
191
+ }
192
+
193
+ function setArrow(el) {
194
+ arrowEl.value = el;
195
+ }
196
+
197
+ function menuItemClicked(name) {
198
+ emit('item-clicked', name);
199
+ hide();
200
+ }
201
+
202
+ async function setDropdownPosition() {
203
+ if (!dropdown.value || !dropdownContent.value) {
204
+ return;
205
+ }
206
+ const {
207
+ x, y, middlewareData, placement: dropdownPlacement
208
+ } = await computePosition(dropdown.value, dropdownContent.value, {
209
+ placement: props.menuPlacement,
210
+ middleware: [
211
+ offset(15),
212
+ shift({ padding: 5 }),
213
+ flip(),
214
+ arrow({
215
+ element: arrowEl.value,
216
+ padding: 5
217
+ })
218
+ ]
219
+ });
220
+
221
+ placement.value = dropdownPlacement;
222
+ dropdownContentStyle.value = {
223
+ left: `${x}px`,
224
+ top: `${y}px`
225
+ };
226
+
227
+ const { x: arrowX, y: arrowY } = middlewareData.arrow;
228
+ Object.assign(arrowEl.value.style, {
229
+ ...arrowX && { left: `${arrowX}px` },
230
+ ...arrowY && { top: `${arrowY}px` }
231
+ });
232
+ }
167
233
  </script>
168
234
 
169
235
  <style lang="scss">
236
+ .apos-context-menu__dropdown-content {
237
+ z-index: $z-index-notifications;
238
+ position: absolute;
239
+ width: max-content;
170
240
 
171
- .apos-context-menu {
172
- position: relative;
241
+ &[aria-hidden='true'] {
242
+ visibility: hidden;
243
+ opacity: 0;
244
+ }
245
+
246
+ &[aria-hidden='false'] {
247
+ visibility: visible;
248
+ opacity: 1;
249
+ }
173
250
  }
174
251
 
175
252
  .apos-context-menu__popup--unpadded .apos-context-menu__pane {
@@ -216,56 +293,4 @@ export default {
216
293
  margin-block-end: 0;
217
294
  padding: 10px 0;
218
295
  }
219
-
220
- .apos-context-menu {
221
- & ::v-deep .apos-popover__wrapper,
222
- & ::v-deep div:not([class]),
223
- & ::v-deep .apos-context-menu__dialog,
224
- & ::v-deep .apos-popover,
225
- & ::v-deep .apos-popover__inner {
226
- &:focus {
227
- outline: none;
228
- }
229
- }
230
- }
231
-
232
- .apos-popover {
233
- z-index: $z-index-modal;
234
- display: block;
235
-
236
- .tooltip-arrow {
237
- display: none;
238
- }
239
-
240
- &[x-placement^='top'] {
241
- margin-bottom: 5px;
242
- }
243
-
244
- &[x-placement^='bottom'] {
245
- margin-top: 5px;
246
- }
247
-
248
- &[x-placement$='end'] {
249
- margin-right: -15px;
250
- }
251
-
252
- &[x-placement$='start'] {
253
- margin-left: -15px;
254
- }
255
-
256
- &[aria-hidden='true'] {
257
- visibility: hidden;
258
- opacity: 0;
259
- }
260
-
261
- &[aria-hidden='false'] {
262
- visibility: visible;
263
- opacity: 1;
264
- }
265
- }
266
-
267
- .apos-popover--z-index-in-context {
268
- z-index: $z-index-widget-focused-controls;
269
- }
270
-
271
296
  </style>
@@ -6,23 +6,25 @@
6
6
  >
7
7
  <AposContextMenuTip
8
8
  v-if="hasTip"
9
+ class="apos-context-menu__tip"
9
10
  :align="tipAlignment"
10
- :origin="menuOrigin"
11
- x-placement="bottom"
11
+ :x-placement="placementSide"
12
+ @set-arrow="emitSetArrow"
12
13
  />
13
14
  <div class="apos-context-menu__pane">
14
15
  <slot>
15
16
  <ul
16
17
  v-if="menu"
17
- class="apos-context-menu__items" role="menu"
18
+ class="apos-context-menu__items"
19
+ role="menu"
18
20
  >
19
21
  <AposContextMenuItem
20
22
  v-for="item in menu"
21
23
  :key="item.action"
22
24
  :data-apos-test-context-menu-item="item.action"
23
25
  :menu-item="item"
24
- @clicked="menuItemClicked"
25
26
  :open="isOpen"
27
+ @clicked="menuItemClicked"
26
28
  />
27
29
  </ul>
28
30
  </slot>
@@ -30,71 +32,71 @@
30
32
  </div>
31
33
  </template>
32
34
 
33
- <script>
34
-
35
- export default {
36
- name: 'AposContextMenuDialog',
37
- props: {
38
- menuPlacement: {
39
- type: String,
40
- required: true
41
- },
42
- menu: {
43
- type: Array,
44
- default: null
45
- },
46
- classList: {
47
- type: String,
48
- default: ''
49
- },
50
- isOpen: {
51
- type: Boolean,
52
- default: false
53
- },
54
- modifiers: {
55
- type: Array,
56
- default() {
57
- return [];
58
- }
59
- },
60
- hasTip: {
61
- type: Boolean,
62
- default: true
63
- }
35
+ <script setup>
36
+ import {
37
+ computed
38
+ } from 'vue';
39
+
40
+ const props = defineProps({
41
+ menuPlacement: {
42
+ type: String,
43
+ required: true
64
44
  },
65
- emits: [ 'item-clicked' ],
66
- data() {
67
- return {
68
- };
45
+ menu: {
46
+ type: Array,
47
+ default: null
69
48
  },
70
- computed: {
71
- menuPositions () {
72
- return this.menuPlacement.split('-');
73
- },
74
- menuOrigin() {
75
- return this.menuPositions[0];
76
- },
77
- tipAlignment() {
78
- if (!this.menuPositions[1]) {
79
- return 'center';
80
- } else {
81
- return this.menuPositions[1];
82
- }
83
- },
84
- classes() {
85
- const classes = this.classList.split(' ');
86
- this.modifiers.forEach(c => {
87
- classes.push(`apos-context-menu__dialog--${c}`);
88
- });
89
- return classes.join(' ');
90
- }
49
+ classList: {
50
+ type: String,
51
+ default: ''
91
52
  },
92
- methods: {
93
- menuItemClicked(action) {
94
- this.$emit('item-clicked', action);
53
+ isOpen: {
54
+ type: Boolean,
55
+ default: false
56
+ },
57
+ modifiers: {
58
+ type: Array,
59
+ default() {
60
+ return [];
95
61
  }
62
+ },
63
+ hasTip: {
64
+ type: Boolean,
65
+ default: true
96
66
  }
97
- };
67
+ });
68
+
69
+ const emit = defineEmits([ 'item-clicked', 'set-arrow' ]);
70
+
71
+ const menuPositions = computed(() => {
72
+ return props.menuPlacement.split('-');
73
+ });
74
+
75
+ const placementSide = computed(() => {
76
+ return menuPositions.value[0];
77
+ });
78
+
79
+ const tipAlignment = computed(() => {
80
+ return !menuPositions.value[1]
81
+ ? 'center'
82
+ : menuPositions.value[1];
83
+ });
84
+
85
+ const classes = computed(() => {
86
+ const classes = props.classList.split(' ');
87
+ props.modifiers.forEach(c => {
88
+ classes.push(`apos-context-menu__dialog--${c}`);
89
+ });
90
+ return classes.join(' ');
91
+ });
92
+
93
+ function menuItemClicked(action) {
94
+ emit('item-clicked', action);
95
+ }
96
+
97
+ function emitSetArrow(arrowEl) {
98
+ emit('set-arrow', arrowEl);
99
+ }
98
100
  </script>
99
101
 
100
102
  <style lang="scss">
@@ -141,11 +143,21 @@ export default {
141
143
  padding: 10px 0;
142
144
  }
143
145
 
144
- .apos-context-menu__dialog ::v-deep .apos-schema .apos-field {
146
+ .apos-context-menu__dialog :deep(.apos-schema .apos-field) {
145
147
  margin-bottom: 20px;
146
148
  .apos-field__help {
147
149
  margin-top: 5px;
148
150
  }
149
151
  }
150
152
 
153
+ .apos-context-menu__tip[x-placement^='bottom'] {
154
+ top: -10px;
155
+ bottom: auto;
156
+ }
157
+
158
+ .apos-context-menu__tip[x-placement^='top'] {
159
+ top: auto;
160
+ bottom: -10px;
161
+ transform: rotate(180deg);
162
+ }
151
163
  </style>
@@ -3,8 +3,8 @@
3
3
  <button
4
4
  class="apos-context-menu__button"
5
5
  :class="modifiers"
6
- @click="click"
7
6
  :tabindex="tabindex"
7
+ @click="click"
8
8
  >
9
9
  {{ $t(label) }}
10
10
  </button>