apostrophe 3.63.3 → 4.1.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 (184) hide show
  1. package/.eslintrc +13 -4
  2. package/CHANGELOG.md +37 -5
  3. package/defaults.js +2 -1
  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 +16 -16
  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/attachment/index.js +2 -1
  26. package/modules/@apostrophecms/attachment/public/img/missing-icon.svg +14 -0
  27. package/modules/@apostrophecms/busy/ui/apos/apps/AposBusy.js +8 -7
  28. package/modules/@apostrophecms/busy/ui/apos/components/TheAposBusy.vue +1 -1
  29. package/modules/@apostrophecms/command-menu/ui/apos/apps/AposCommandMenu.js +11 -29
  30. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +1 -1
  31. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +6 -6
  32. package/modules/@apostrophecms/command-menu/ui/apos/components/TheAposCommandMenu.vue +10 -7
  33. package/modules/@apostrophecms/doc-type/index.js +34 -13
  34. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +3 -3
  35. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +20 -15
  36. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +1 -1
  37. package/modules/@apostrophecms/i18n/i18n/en.json +13 -0
  38. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +209 -33
  39. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalizeErrors.vue +3 -3
  40. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +5 -5
  41. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +6 -6
  42. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +23 -16
  43. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +11 -11
  44. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +28 -21
  45. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +4 -3
  46. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +5 -4
  47. package/modules/@apostrophecms/login/index.js +18 -1
  48. package/modules/@apostrophecms/login/ui/apos/apps/AposLogin.js +6 -7
  49. package/modules/@apostrophecms/login/ui/apos/components/AposForgotPasswordForm.vue +3 -3
  50. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +10 -10
  51. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +3 -3
  52. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +2 -2
  53. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +1 -16
  54. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +60 -87
  55. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +15 -11
  56. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +17 -11
  57. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +7 -4
  58. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +9 -9
  59. package/modules/@apostrophecms/modal/ui/apos/components/AposModalLip.vue +2 -2
  60. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +8 -8
  61. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +4 -3
  62. package/modules/@apostrophecms/modal/ui/apos/components/AposModalToolbar.vue +7 -7
  63. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +22 -4
  64. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +95 -0
  65. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +2 -3
  66. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
  67. package/modules/@apostrophecms/notification/index.js +4 -4
  68. package/modules/@apostrophecms/notification/ui/apos/apps/AposNotification.js +6 -9
  69. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +12 -8
  70. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +4 -2
  71. package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +12 -10
  72. package/modules/@apostrophecms/page/index.js +1 -0
  73. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +15 -11
  74. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +1 -1
  75. package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +3 -3
  76. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +25 -17
  77. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +24 -20
  78. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +9 -5
  79. package/modules/@apostrophecms/piece-type/ui/apos/components/AposRelationshipEditor.vue +15 -11
  80. package/modules/@apostrophecms/rich-text-widget/index.js +1 -0
  81. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +7 -6
  82. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +31 -30
  83. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapAnchor.vue +12 -10
  84. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapButton.vue +1 -1
  85. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +1 -1
  86. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +9 -8
  87. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -3
  88. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapTable.vue +3 -3
  89. package/modules/@apostrophecms/schema/index.js +69 -8
  90. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +1 -1
  91. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +10 -8
  92. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +5 -3
  93. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +81 -277
  94. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +4 -2
  95. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +24 -14
  96. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +7 -6
  97. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +10 -7
  98. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +3 -3
  99. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +6 -4
  100. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +5 -4
  101. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +9 -6
  102. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +15 -12
  103. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +1 -1
  104. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +16 -12
  105. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +19 -11
  106. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +15 -12
  107. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +75 -22
  108. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
  109. package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +2 -2
  110. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +0 -4
  111. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +3 -3
  112. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +15 -4
  113. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +3 -3
  114. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +7 -7
  115. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +5 -8
  116. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +1 -1
  117. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +1 -1
  118. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +1 -1
  119. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +12 -9
  120. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +3 -3
  121. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +7 -9
  122. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +4 -4
  123. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +42 -13
  124. package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +1 -1
  125. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +9 -9
  126. package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +205 -0
  127. package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +5 -5
  128. package/modules/@apostrophecms/settings/ui/apos/logic/AposSettingsManager.js +4 -4
  129. package/modules/@apostrophecms/submitted-draft/ui/apos/components/AposSubmittedDraftIcon.vue +5 -4
  130. package/modules/@apostrophecms/task/index.js +2 -0
  131. package/modules/@apostrophecms/translation/index.js +233 -0
  132. package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +84 -0
  133. package/modules/@apostrophecms/ui/ui/apos/components/AposAvatar.vue +2 -1
  134. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +4 -4
  135. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +6 -6
  136. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +120 -113
  137. package/modules/@apostrophecms/ui/ui/apos/components/AposCellButton.vue +2 -1
  138. package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +49 -5
  139. package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +19 -19
  140. package/modules/@apostrophecms/ui/ui/apos/components/AposCloudUploadIcon.vue +10 -5
  141. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +15 -15
  142. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +214 -191
  143. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +77 -65
  144. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +1 -1
  145. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuTip.vue +28 -50
  146. package/modules/@apostrophecms/ui/ui/apos/components/AposEmptyState.vue +3 -3
  147. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +5 -5
  148. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +4 -4
  149. package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +1 -1
  150. package/modules/@apostrophecms/ui/ui/apos/components/AposLabel.vue +1 -1
  151. package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +5 -5
  152. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +14 -8
  153. package/modules/@apostrophecms/ui/ui/apos/components/AposPagerDots.vue +2 -1
  154. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +13 -12
  155. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +53 -59
  156. package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +2 -2
  157. package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +2 -2
  158. package/modules/@apostrophecms/ui/ui/apos/components/AposTag.vue +3 -2
  159. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +40 -35
  160. package/modules/@apostrophecms/ui/ui/apos/components/AposTagListItem.vue +2 -1
  161. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +2 -2
  162. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +9 -11
  163. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +5 -3
  164. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +129 -129
  165. package/modules/@apostrophecms/ui/ui/apos/composables/AposTheme.js +11 -0
  166. package/modules/@apostrophecms/ui/ui/apos/lib/click-outside-element.js +4 -4
  167. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +56 -50
  168. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +191 -0
  169. package/modules/@apostrophecms/ui/ui/apos/lib/vue.js +19 -10
  170. package/modules/@apostrophecms/ui/ui/apos/mixins/AposAdvisoryLockMixin.js +1 -1
  171. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +1 -1
  172. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
  173. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tooltips.scss +6 -22
  174. package/modules/@apostrophecms/ui/ui/apos/scss/shared/_table-rows.scss +1 -1
  175. package/modules/@apostrophecms/widget-type/index.js +8 -2
  176. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +26 -22
  177. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +4 -4
  178. package/package.json +31 -44
  179. package/test/attachments.js +5 -0
  180. package/test/schemas.js +138 -0
  181. package/test/translation.js +538 -0
  182. package/test-lib/util.js +21 -0
  183. package/modules/@apostrophecms/ui/ui/apos/lib/localized-v-tooltip.js +0 -63
  184. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip-options.js +0 -13
@@ -1,175 +1,250 @@
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, 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
+ setDropdownPosition();
157
+ } else {
158
+ window.removeEventListener('resize', setDropdownPosition);
159
+ window.removeEventListener('scroll', setDropdownPosition);
160
+ }
161
+ }, { flush: 'post' });
162
+
163
+ const { themeClass } = useAposTheme();
164
+
165
+ onMounted(() => {
166
+ apos.bus.$on('context-menu-opened', hideWhenOtherOpen);
167
+ apos.bus.$on('widget-focus', hide);
168
+ });
169
+
170
+ onBeforeUnmount(() => {
171
+ apos.bus.$off('context-menu-opened', hideWhenOtherOpen);
172
+ apos.bus.$off('widget-focus', hide);
173
+ });
174
+
175
+ function hideWhenOtherOpen(id) {
176
+ if (menuId.value !== id) {
177
+ hide();
178
+ }
179
+ }
180
+
181
+ function hide() {
182
+ isOpen.value = false;
183
+ }
184
+
185
+ function buttonClicked(e) {
186
+ apos.bus.$emit('context-menu-opened', menuId.value);
187
+ isOpen.value = !isOpen.value;
188
+ event.value = e;
189
+ }
190
+
191
+ function setArrow(el) {
192
+ arrowEl.value = el;
193
+ }
194
+
195
+ function menuItemClicked(name) {
196
+ emit('item-clicked', name);
197
+ hide();
198
+ }
199
+
200
+ async function setDropdownPosition() {
201
+ if (!dropdown.value || !dropdownContent.value) {
202
+ return;
203
+ }
204
+ const {
205
+ x, y, middlewareData, placement: dropdownPlacement
206
+ } = await computePosition(dropdown.value, dropdownContent.value, {
207
+ placement: props.menuPlacement,
208
+ middleware: [
209
+ offset(15),
210
+ shift({ padding: 5 }),
211
+ flip(),
212
+ arrow({
213
+ element: arrowEl.value,
214
+ padding: 5
215
+ })
216
+ ]
217
+ });
218
+
219
+ placement.value = dropdownPlacement;
220
+ dropdownContentStyle.value = {
221
+ left: `${x}px`,
222
+ top: `${y}px`
223
+ };
224
+
225
+ const { x: arrowX, y: arrowY } = middlewareData.arrow;
226
+ Object.assign(arrowEl.value.style, {
227
+ ...arrowX && { left: `${arrowX}px` },
228
+ ...arrowY && { top: `${arrowY}px` }
229
+ });
230
+ }
167
231
  </script>
168
232
 
169
233
  <style lang="scss">
234
+ .apos-context-menu__dropdown-content {
235
+ z-index: $z-index-notifications;
236
+ position: absolute;
237
+ width: max-content;
170
238
 
171
- .apos-context-menu {
172
- position: relative;
239
+ &[aria-hidden='true'] {
240
+ visibility: hidden;
241
+ opacity: 0;
242
+ }
243
+
244
+ &[aria-hidden='false'] {
245
+ visibility: visible;
246
+ opacity: 1;
247
+ }
173
248
  }
174
249
 
175
250
  .apos-context-menu__popup--unpadded .apos-context-menu__pane {
@@ -216,56 +291,4 @@ export default {
216
291
  margin-block-end: 0;
217
292
  padding: 10px 0;
218
293
  }
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
294
  </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>