typo3-typescript-sources 14.3.9 → 14.3.11

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 (198) hide show
  1. package/.last-synced-tag +1 -1
  2. package/README.md +1 -1
  3. package/adminpanel/admin-panel.ts +2 -2
  4. package/backend/bookmark/bookmark-manager.ts +4 -3
  5. package/backend/bookmark/bookmark-store.ts +8 -12
  6. package/backend/clipboard-panel.ts +22 -10
  7. package/backend/code-editor/language/typoscript.ts +1 -1
  8. package/backend/column-selector-button.ts +2 -2
  9. package/backend/context-menu-actions.ts +36 -16
  10. package/backend/context-menu.ts +35 -3
  11. package/backend/contextual-record-edit.ts +115 -33
  12. package/backend/date-time-picker.ts +3 -2
  13. package/backend/drag-uploader.ts +51 -21
  14. package/backend/element/breadcrumb.ts +6 -6
  15. package/backend/element/combobox-element.ts +2 -2
  16. package/backend/element/contextual-record-edit-trigger.ts +100 -65
  17. package/backend/element/datetime-element.ts +2 -1
  18. package/backend/element/editable-page-title.ts +2 -1
  19. package/backend/element/pseudo-button.ts +29 -5
  20. package/backend/element/qrcode-modal-button.ts +2 -2
  21. package/backend/element/sidebar-toggle-element.ts +1 -15
  22. package/backend/element-browser.ts +4 -3
  23. package/backend/enum/viewport/scaffold-identifier.ts +1 -1
  24. package/backend/event/event-dispatcher.ts +4 -2
  25. package/backend/form-engine/container/flex-form-container-container.ts +1 -1
  26. package/backend/form-engine/container/flex-form-section-container.ts +3 -3
  27. package/backend/form-engine/container/inline-control-container.ts +29 -29
  28. package/backend/form-engine/element/abstract-sortable-select-items.ts +1 -1
  29. package/backend/form-engine/element/category-element.ts +5 -5
  30. package/backend/form-engine/element/extra/char-counter.ts +1 -1
  31. package/backend/form-engine/element/group-element.ts +1 -1
  32. package/backend/form-engine/element/select-tree-element.ts +4 -4
  33. package/backend/form-engine/element/select-tree.ts +1 -1
  34. package/backend/form-engine/element/slug-element.ts +9 -7
  35. package/backend/form-engine/element/suggest/result-container.ts +1 -1
  36. package/backend/form-engine/element/suggest/result-item.ts +1 -1
  37. package/backend/form-engine/element/table-permission-element.ts +4 -4
  38. package/backend/form-engine/field-control/add-record.ts +1 -1
  39. package/backend/form-engine/field-control/edit-popup.ts +2 -2
  40. package/backend/form-engine/field-control/insert-clipboard.ts +1 -1
  41. package/backend/form-engine/field-control/link-popup.ts +1 -1
  42. package/backend/form-engine/field-control/list-module.ts +1 -1
  43. package/backend/form-engine/field-control/password-generator.ts +4 -4
  44. package/backend/form-engine/field-control/reset-selection.ts +2 -2
  45. package/backend/form-engine/field-wizard/localization-state-selector.ts +2 -2
  46. package/backend/form-engine/field-wizard/value-picker.ts +1 -1
  47. package/backend/form-engine/field-wizard/value-slider.ts +2 -2
  48. package/backend/form-engine-review.ts +1 -1
  49. package/backend/form-engine-suggest.ts +2 -2
  50. package/backend/form-engine-validation.ts +55 -10
  51. package/backend/form-engine.ts +16 -14
  52. package/backend/global-event-handler.ts +1 -1
  53. package/backend/grid-editor.ts +83 -8
  54. package/backend/hotkeys.ts +2 -1
  55. package/backend/image-manipulation.ts +4 -3
  56. package/backend/info-window.ts +4 -3
  57. package/backend/layout-module/drag-drop.ts +17 -16
  58. package/backend/layout-module/page-layout.ts +7 -2
  59. package/backend/layout-module/paste.ts +3 -3
  60. package/backend/layout-module/toggle-hidden-element.ts +1 -1
  61. package/backend/live-search/live-search-configurator.ts +4 -3
  62. package/backend/live-search/live-search-shortcut.ts +2 -1
  63. package/backend/live-search/live-search.ts +10 -9
  64. package/backend/live-search/result-types/default-result-type.ts +2 -1
  65. package/backend/localization/finisher/localization-submission-service.ts +1 -0
  66. package/backend/localization/label-provider.ts +2 -1
  67. package/backend/localization/localization-button.ts +3 -0
  68. package/backend/localization/localization-wizard.ts +3 -0
  69. package/backend/localization/steps/source-language-step.ts +1 -0
  70. package/backend/login/capslock-warning.ts +84 -0
  71. package/backend/login-refresh.ts +6 -26
  72. package/backend/login.ts +6 -4
  73. package/backend/mail-link-handler.ts +2 -2
  74. package/backend/modal.ts +36 -18
  75. package/backend/module-menu.ts +21 -11
  76. package/backend/module.ts +1 -1
  77. package/backend/multi-record-selection-edit-action.ts +3 -2
  78. package/backend/multi-record-selection.ts +3 -3
  79. package/backend/multi-step-wizard.ts +2 -2
  80. package/backend/new-record-wizard.ts +3 -2
  81. package/backend/notification.ts +4 -20
  82. package/backend/page-wizard/steps/form-engine-step.ts +2 -2
  83. package/backend/popover.ts +1 -1
  84. package/backend/recordlist.ts +13 -11
  85. package/backend/security/element/csp-reports.ts +2 -2
  86. package/backend/security/element/sudo-mode.ts +6 -7
  87. package/backend/settings/editor.ts +9 -9
  88. package/backend/settings/type/page.ts +5 -4
  89. package/backend/severity.ts +2 -22
  90. package/backend/sortable-table.ts +3 -3
  91. package/backend/storage/module-state-storage.ts +3 -1
  92. package/backend/storage/persistent.ts +15 -3
  93. package/backend/switch-user.ts +3 -2
  94. package/backend/tab.ts +3 -3
  95. package/backend/telephone-link-handler.ts +1 -1
  96. package/backend/tests/drag-uploader-test.ts +287 -0
  97. package/backend/tests/element/pseudo-button-test.ts +95 -0
  98. package/backend/tests/form-engine/element/select-tree-test.ts +1 -1
  99. package/backend/tests/form-engine-validation-test.ts +75 -0
  100. package/backend/tests/grid-editor-test.ts +29 -0
  101. package/backend/tests/login/capslock-warning-test.ts +78 -0
  102. package/backend/tests/modal-test.ts +73 -0
  103. package/backend/tests/notification-test.ts +16 -16
  104. package/backend/tests/tree/tree-test.ts +68 -0
  105. package/backend/tests/utility/drag-drop-utility-test.ts +82 -0
  106. package/backend/toolbar/system-information-menu.ts +2 -2
  107. package/backend/tree/file-storage-browser.ts +7 -5
  108. package/backend/tree/file-storage-tree-container.ts +8 -6
  109. package/backend/tree/page-browser.ts +4 -3
  110. package/backend/tree/page-position-select.ts +4 -3
  111. package/backend/tree/page-tree-element.ts +8 -6
  112. package/backend/tree/tree-module-state.ts +12 -0
  113. package/backend/tree/tree-toolbar.ts +1 -1
  114. package/backend/tree/tree.ts +26 -18
  115. package/backend/url-link-handler.ts +1 -1
  116. package/backend/user-pass-login.ts +0 -103
  117. package/backend/user-settings-manager.ts +2 -1
  118. package/backend/utility/collapse-state-search.ts +2 -2
  119. package/backend/utility/drag-drop-utility.ts +7 -10
  120. package/backend/utility/top-level-module-import.ts +3 -1
  121. package/backend/viewport/content-container.ts +3 -2
  122. package/backend/viewport/content-navigation-toggle.ts +5 -12
  123. package/backend/viewport/content-navigation.ts +3 -3
  124. package/backend/viewport/navigation-container.ts +2 -2
  125. package/backend/viewport.ts +5 -4
  126. package/backend/window-manager.ts +5 -4
  127. package/backend/wizard/finisher/noop-finisher.ts +3 -2
  128. package/backend/wizard/finisher/redirect-finisher.ts +4 -3
  129. package/backend/wizard/finisher/reload-finisher.ts +3 -2
  130. package/backend/wizard/move-content-element.ts +7 -7
  131. package/backend/wizard/move-page.ts +5 -4
  132. package/backend/wizard/steps/finisher-step.ts +1 -1
  133. package/belog/backend-log.ts +2 -2
  134. package/beuser/permissions.ts +4 -4
  135. package/core/java-script-item-handler.ts +12 -2
  136. package/core/java-script-item-processor.ts +55 -7
  137. package/core/lit-helper.ts +5 -2
  138. package/core/tests/java-script-item-processor-test.ts +83 -0
  139. package/core/tests/utility/top-window-test.ts +121 -0
  140. package/core/utility/top-window.ts +99 -0
  141. package/dashboard/dashboard.ts +3 -7
  142. package/extensionmanager/dependency-check.ts +2 -1
  143. package/extensionmanager/extension-toggle-button.ts +2 -1
  144. package/extensionmanager/main.ts +1 -1
  145. package/extensionmanager/repository.ts +2 -1
  146. package/extensionmanager/update.ts +1 -1
  147. package/extensionmanager/upload-form.ts +1 -1
  148. package/filelist/browse-files.ts +2 -2
  149. package/filelist/browse-folders.ts +1 -1
  150. package/filelist/context-menu-actions.ts +20 -19
  151. package/filelist/file-delete.ts +4 -3
  152. package/filelist/file-list-actions.ts +1 -1
  153. package/filelist/file-list-dragdrop.ts +7 -6
  154. package/filelist/file-list-rename-handler.ts +2 -1
  155. package/filelist/file-list-replace-handler.ts +2 -1
  156. package/filelist/file-list-transfer-handler.ts +5 -4
  157. package/filelist/file-list.ts +12 -11
  158. package/filelist/linkbrowser-file-handler.ts +1 -1
  159. package/filelist/rename-file.ts +3 -3
  160. package/filelist/resource-creation.ts +1 -1
  161. package/form/backend/form-editor/core.ts +1 -1
  162. package/form/backend/form-editor/inspector-component.ts +4 -4
  163. package/form/backend/form-editor/stage-component.ts +10 -11
  164. package/form/backend/form-editor/view-model.ts +1 -1
  165. package/impexp/context-menu-actions.ts +5 -3
  166. package/info/translation-status.ts +2 -2
  167. package/install/installer.ts +8 -7
  168. package/install/module/abstract-interactable-module.ts +2 -2
  169. package/install/module/environment/folder-structure.ts +1 -1
  170. package/install/module/maintenance/clear-tables.ts +1 -1
  171. package/install/module/maintenance/clear-typo3temp-files.ts +1 -1
  172. package/install/module/maintenance/database-analyzer.ts +2 -2
  173. package/install/module/maintenance/language-packs.ts +2 -1
  174. package/install/module/settings/extension-configuration.ts +3 -4
  175. package/install/module/settings/local-configuration.ts +8 -8
  176. package/install/module/upgrade/upgrade-docs.ts +1 -1
  177. package/install/module/upgrade/upgrade-wizards.ts +8 -8
  178. package/install/renderable/flash-message.ts +2 -2
  179. package/install/renderable/info-box.ts +2 -2
  180. package/install/router.ts +3 -3
  181. package/lowlevel/query-generator.ts +4 -4
  182. package/opendocs/open-document-store.ts +5 -4
  183. package/package.json +2 -2
  184. package/recycler/recycler.ts +19 -18
  185. package/redirects/event-handler.ts +66 -20
  186. package/redirects/short-url-generator.ts +2 -2
  187. package/redirects/tests/event-handler-test.ts +121 -0
  188. package/rte-ckeditor/ckeditor5.ts +2 -2
  189. package/rte-ckeditor/tests/ckeditor5-test.ts +1 -1
  190. package/scheduler/form-engine/element/timing-options-element.ts +2 -2
  191. package/scheduler/scheduler-add-group.ts +5 -4
  192. package/scheduler/scheduler-sortable-groups.ts +2 -2
  193. package/scheduler/scheduler.ts +1 -1
  194. package/styleguide/find-icons.ts +1 -1
  195. package/viewpage/main.ts +7 -7
  196. package/workspaces/backend.ts +33 -31
  197. package/workspaces/preview.ts +11 -10
  198. package/workspaces/workspace-state.ts +6 -5
package/.last-synced-tag CHANGED
@@ -1 +1 @@
1
- v14.3.9
1
+ v14.3.11
package/README.md CHANGED
@@ -23,7 +23,7 @@ npm install github:maikschneider/typo3-typescript-sources#main
23
23
 
24
24
  - **Original Repository:** [TYPO3/typo3](https://github.com/TYPO3/typo3)
25
25
  - **Source Path:** `Build/Sources/TypeScript/`
26
- - **Current TYPO3 Version:** `v14.3.9`
26
+ - **Current TYPO3 Version:** `v14.3.11`
27
27
 
28
28
  ## 🌿 Branches
29
29
 
@@ -36,7 +36,7 @@
36
36
  private readonly trigger: HTMLElement;
37
37
 
38
38
  constructor() {
39
- this.adminPanel = document.querySelector(AdminPanelSelectors.adminPanelRole) as HTMLFormElement;
39
+ this.adminPanel = document.querySelector<HTMLFormElement>(AdminPanelSelectors.adminPanelRole);
40
40
  this.modules = this.querySelectorAll(AdminPanelSelectors.moduleTriggerRole).map(
41
41
  (moduleTrigger) => {
42
42
  const moduleParent = moduleTrigger.closest(AdminPanelSelectors.moduleParentClass);
@@ -60,7 +60,7 @@
60
60
  new AdminPanelContentSetting(contentSettingElement, contentSettingTrigger);
61
61
  },
62
62
  );
63
- this.trigger = document.querySelector(AdminPanelSelectors.triggerRole) as HTMLElement;
63
+ this.trigger = document.querySelector<HTMLElement>(AdminPanelSelectors.triggerRole);
64
64
 
65
65
  this.initializeEvents();
66
66
  this.addBackdropListener();
@@ -21,6 +21,7 @@ import Modal from '../modal';
21
21
  import Notification from '../notification';
22
22
  import { SeverityEnum } from '../enum/severity';
23
23
  import { PseudoButtonLitElement } from '@typo3/backend/element/pseudo-button';
24
+ import { topDocument } from '@typo3/core/utility/top-window';
24
25
  import '@typo3/backend/element/spinner-element';
25
26
  import '@typo3/backend/element/icon-element';
26
27
  import labels from '~labels/core.bookmarks';
@@ -1119,7 +1120,7 @@ export class BookmarkManagerContentElement extends LitElement {
1119
1120
 
1120
1121
  private async handleDropBookmark(draggedBookmark: Bookmark, target: Bookmark | BookmarkGroup | BookmarkGroupId, dropPosition?: 'before' | 'after'): Promise<void> {
1121
1122
  if (typeof target === 'object' && 'route' in target) {
1122
- const targetBookmark = target as Bookmark;
1123
+ const targetBookmark = target;
1123
1124
  if (draggedBookmark.id === targetBookmark.id) {
1124
1125
  return;
1125
1126
  }
@@ -1208,8 +1209,8 @@ export class BookmarkManagerButtonElement extends PseudoButtonLitElement {
1208
1209
 
1209
1210
  protected override buttonActivated(): void {
1210
1211
  // Create element in top frame to avoid adoptedStyleSheets cross-document issues
1211
- const targetDocument = top?.document ?? document;
1212
- const managerElement = targetDocument.createElement('typo3-backend-bookmark-manager-content') as BookmarkManagerContentElement;
1212
+ const targetDocument = topDocument;
1213
+ const managerElement = targetDocument.createElement('typo3-backend-bookmark-manager-content');
1213
1214
  if (this.editId !== undefined) {
1214
1215
  managerElement.editId = this.editId;
1215
1216
  }
@@ -20,6 +20,7 @@ import Notification from '../notification';
20
20
  import { SeverityEnum } from '../enum/severity';
21
21
  import { BroadcastMessage, type BroadcastEvent } from '@typo3/backend/broadcast-message';
22
22
  import BroadcastService from '@typo3/backend/broadcast-service';
23
+ import { topWindow } from '@typo3/core/utility/top-window';
23
24
 
24
25
  export const BookmarkStoreChangedEvent = 'typo3:bookmark-store:changed';
25
26
 
@@ -683,19 +684,14 @@ class BookmarkStore {
683
684
  * Returns singleton instance from top frame, creating it if needed.
684
685
  */
685
686
  function getOrCreateInstance(): BookmarkStore {
686
- try {
687
- if (top?.TYPO3?.BookmarkStore) {
688
- return top.TYPO3.BookmarkStore;
689
- }
690
- const instance = new BookmarkStore();
691
- if (top?.TYPO3) {
692
- top.TYPO3.BookmarkStore = instance;
693
- }
694
- return instance;
695
- } catch {
696
- // Cross-origin access denied, create local instance
697
- return new BookmarkStore();
687
+ if (topWindow.TYPO3?.BookmarkStore) {
688
+ return topWindow.TYPO3.BookmarkStore;
689
+ }
690
+ const instance = new BookmarkStore();
691
+ if (topWindow.TYPO3) {
692
+ topWindow.TYPO3.BookmarkStore = instance;
698
693
  }
694
+ return instance;
699
695
  }
700
696
 
701
697
  export default getOrCreateInstance();
@@ -21,6 +21,7 @@ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
21
21
  import Notification from '@typo3/backend/notification';
22
22
  import '@typo3/backend/element/spinner-element';
23
23
  import '@typo3/backend/element/icon-element';
24
+ import { topWindow } from '@typo3/core/utility/top-window';
24
25
 
25
26
  enum CopyMode {
26
27
  cut = 'cut',
@@ -60,11 +61,12 @@ interface DispatchArgs {
60
61
  * Module: @typo3/backend/clipboard-panel
61
62
  *
62
63
  * @example
63
- * <typo3-backend-clipboard-panel return-url="/typo3/module" table="_FILE"></typo3-backend-clipboard-panel>
64
+ * <typo3-backend-clipboard-panel return-url="/typo3/module" module-url="/typo3/clipboard/process" table="_FILE"></typo3-backend-clipboard-panel>
64
65
  */
65
66
  @customElement('typo3-backend-clipboard-panel')
66
67
  export class ClipboardPanel extends LitElement {
67
68
  @property({ type: String, attribute: 'return-url' }) returnUrl: string = '';
69
+ @property({ type: String, attribute: 'module-url' }) moduleUrl: string = '';
68
70
  @property({ type: String }) table: string = '';
69
71
 
70
72
  private static renderLoader(): TemplateResult {
@@ -89,8 +91,12 @@ export class ClipboardPanel extends LitElement {
89
91
  `;
90
92
  }
91
93
 
94
+ private getModuleUrl(): string {
95
+ return this.moduleUrl || topWindow.TYPO3.settings.Clipboard.moduleUrl;
96
+ }
97
+
92
98
  private renderPanel(): Promise<TemplateResult> {
93
- return (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl))
99
+ return (new AjaxRequest(this.getModuleUrl()))
94
100
  .withQueryArguments({ action: 'getClipboardData' })
95
101
  .post({ table: this.table })
96
102
  .then(async (response: AjaxResponse): Promise<TemplateResult> => {
@@ -99,11 +105,17 @@ export class ClipboardPanel extends LitElement {
99
105
  const clipboardData: ClipboardData = resolvedBody.data;
100
106
  return html`
101
107
  <div class="panel panel-default" data-clipboard-panel>
102
- <div class="panel-heading">
108
+ <div class="panel-heading" id="clipboard-panel-heading">
103
109
  ${clipboardData.labels.clipboard}
104
110
  </div>
105
111
  <div class="table-fit">
106
- <table class="table">
112
+ <table class="table" aria-labelledby="clipboard-panel-heading">
113
+ <thead>
114
+ <tr>
115
+ <th scope="col" colspan="2" class="p-0 border-0"><span class="visually-hidden">${clipboardData.labels.element}</span></th>
116
+ <th scope="col" class="p-0 border-0"><span class="visually-hidden">${clipboardData.labels.control}</span></th>
117
+ </tr>
118
+ </thead>
107
119
  <tbody>
108
120
  ${clipboardData.tabs.map((tab): TemplateResult => this.renderTab(tab, clipboardData))}
109
121
  </tbody>
@@ -127,8 +139,8 @@ export class ClipboardPanel extends LitElement {
127
139
  private renderTab(tab: ClipboardTab, clipboardData: ClipboardData): TemplateResult {
128
140
  return html`
129
141
  <tr>
130
- <td colspan="2" class="nowrap">
131
- <button type="button" class="btn btn-link" aria-checked="${clipboardData.current === tab.identifier}" title="${tab.description}" data-action="setP" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setP': tab.identifier } })}">
142
+ <th scope="row" colspan="2" class="nowrap fw-normal">
143
+ <button type="button" class="btn btn-link" aria-current="${clipboardData.current === tab.identifier ? 'true' : nothing}" title="${tab.description}" data-action="setP" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setP': tab.identifier } })}">
132
144
  ${clipboardData.current === tab.identifier ? html`
133
145
  <typo3-backend-icon identifier="actions-check-circle-alt" size="small"></typo3-backend-icon>
134
146
  ${tab.title}
@@ -140,7 +152,7 @@ export class ClipboardPanel extends LitElement {
140
152
  </span>
141
153
  `}
142
154
  </button>
143
- </td>
155
+ </th>
144
156
  <td class="col-control nowrap">
145
157
  ${clipboardData.current !== tab.identifier ? nothing : html`
146
158
  <div class="btn-toolbar btn-toolbar-nowrap">
@@ -176,11 +188,11 @@ export class ClipboardPanel extends LitElement {
176
188
  <td class="col-icon nowrap ${classMap({ 'ps-4': !tabItem.identifier })}">
177
189
  ${unsafeHTML(tabItem.icon)}
178
190
  </td>
179
- <td class="col-title col-responsive nowrap">
191
+ <th scope="row" class="col-title col-responsive nowrap fw-normal">
180
192
  ${unsafeHTML(tabItem.title)}
181
193
  ${tabIdentifier === 'normal' ? html`<strong>(${clipboardData.copyMode === CopyMode.copy ? html`${clipboardData.labels.copy}` : html`${clipboardData.labels.cut}`})</strong>` : nothing}
182
194
  ${tabItem.thumb ? html`<div class="d-block">${unsafeHTML(tabItem.thumb)}</div>` : nothing}
183
- </td>
195
+ </th>
184
196
  <td class="col-control nowrap">
185
197
  <div class="btn-group">
186
198
  ${!tabItem.infoDataDispatch ? nothing : html`
@@ -202,7 +214,7 @@ export class ClipboardPanel extends LitElement {
202
214
  private updateClipboard(event: PointerEvent, payload: object): void {
203
215
  event.preventDefault();
204
216
  const target: HTMLElement = event.currentTarget as HTMLElement;
205
- (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl))
217
+ (new AjaxRequest(this.getModuleUrl()))
206
218
  .post(payload)
207
219
  .then(async (response: AjaxResponse): Promise<void> => {
208
220
  const resolvedBody = await response.resolve();
@@ -47,7 +47,7 @@ export function typoscript() {
47
47
 
48
48
  const tsCodeCompletionInitializer = (async (): Promise<TsCodeCompletion> => {
49
49
  await DocumentService.ready();
50
- const effectivePid = parseInt((document.querySelector('input[name="effectivePid"]') as HTMLInputElement)?.value, 10);
50
+ const effectivePid = parseInt((document.querySelector<HTMLInputElement>('input[name="effectivePid"]'))?.value, 10);
51
51
  return new TsCodeCompletion(effectivePid);
52
52
  })();
53
53
 
@@ -168,7 +168,7 @@ export class ColumnSelectorButton extends PseudoButtonLitElement {
168
168
  }
169
169
 
170
170
  private processSelection(currentModal: HTMLElement): void {
171
- const form: HTMLFormElement = currentModal.querySelector('form') as HTMLFormElement;
171
+ const form = currentModal.querySelector('form');
172
172
  if (form === null) {
173
173
  this.abortSelection();
174
174
  return;
@@ -192,7 +192,7 @@ export class ColumnSelectorButton extends PseudoButtonLitElement {
192
192
  }
193
193
 
194
194
  private handleModalContentLoaded(currentModal: HTMLElement): void {
195
- const form: HTMLFormElement = currentModal.querySelector('form') as HTMLFormElement;
195
+ const form = currentModal.querySelector('form');
196
196
  if (form === null) {
197
197
  // Early return if modal content does not include a form
198
198
  return;
@@ -15,6 +15,8 @@ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
15
15
  import { SeverityEnum } from './enum/severity';
16
16
  import AjaxDataHandler from './ajax-data-handler';
17
17
  import AjaxRequest from '@typo3/core/ajax/ajax-request';
18
+ import ClientRequest from './event/client-request';
19
+ import { isContextualRecordEditOpen, openContextualRecordEdit } from './element/contextual-record-edit-trigger';
18
20
  import InfoWindow from './info-window';
19
21
  import Modal, { type ModalElement } from './modal';
20
22
  import ModuleMenu from './module-menu';
@@ -23,6 +25,7 @@ import Viewport from './viewport';
23
25
  import '@typo3/backend/new-record-wizard';
24
26
  import Utility from '@typo3/backend/utility';
25
27
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
28
+ import { topWindow, topDocument } from '@typo3/core/utility/top-window';
26
29
  import { html } from 'lit';
27
30
  import coreCommonLabels from '~labels/core.common';
28
31
  import cacheLabels from '~labels/core.cache';
@@ -38,7 +41,8 @@ class ContextMenuActions {
38
41
  * @returns {string}
39
42
  */
40
43
  public static getReturnUrl(): string {
41
- return encodeURIComponent(top.list_frame.document.location.pathname + top.list_frame.document.location.search);
44
+ const location = topWindow.list_frame.document.location;
45
+ return encodeURIComponent(location.pathname + location.search);
42
46
  }
43
47
 
44
48
  public static editRecord(table: string, uid: number, dataset: DOMStringMap): void {
@@ -50,13 +54,17 @@ class ContextMenuActions {
50
54
  overrideVals = '&overrideVals[pages][sys_language_uid]=' + pageLanguageId;
51
55
  }
52
56
 
53
- Viewport.ContentContainer.setUrl(
54
- top.TYPO3.settings.FormEngine.moduleUrl
55
- + '&edit[' + table + '][' + uid + ']=edit'
57
+ const editParameters = '&edit[' + table + '][' + uid + ']=edit'
56
58
  + overrideVals
57
- + '&module=' + encodeURIComponent(top.TYPO3.ModuleMenu.App.getCurrentModule())
58
- + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
59
- );
59
+ + '&module=' + encodeURIComponent(topWindow.TYPO3.ModuleMenu.App.getCurrentModule())
60
+ + '&returnUrl=' + ContextMenuActions.getReturnUrl();
61
+
62
+ if (isContextualRecordEditOpen()) {
63
+ openContextualRecordEdit(topWindow.TYPO3.settings.FormEngine.contextualUrl + editParameters, table);
64
+ return;
65
+ }
66
+
67
+ Viewport.ContentContainer.setUrl(topWindow.TYPO3.settings.FormEngine.moduleUrl + editParameters);
60
68
  }
61
69
 
62
70
  public static viewRecord(table: string, uid: number, dataset: DOMStringMap): void {
@@ -112,7 +120,7 @@ class ContextMenuActions {
112
120
  pageId: uid
113
121
  },
114
122
  });
115
- top.document.dispatchEvent(event);
123
+ topDocument.dispatchEvent(event);
116
124
  }
117
125
  }
118
126
 
@@ -152,13 +160,13 @@ class ContextMenuActions {
152
160
  }
153
161
 
154
162
  Viewport.ContentContainer.setUrl(
155
- top.TYPO3.settings.FormEngine.moduleUrl + '&edit[' + table + '][' + (table !== 'pages' ? '-' : '') + uid + ']=new&returnUrl=' + ContextMenuActions.getReturnUrl(),
163
+ topWindow.TYPO3.settings.FormEngine.moduleUrl + '&edit[' + table + '][' + (table !== 'pages' ? '-' : '') + uid + ']=new&returnUrl=' + ContextMenuActions.getReturnUrl(),
156
164
  );
157
165
  }
158
166
 
159
167
  public static openHistoryPopUp(table: string, uid: number): void {
160
168
  Viewport.ContentContainer.setUrl(
161
- top.TYPO3.settings.RecordHistory.moduleUrl + '&element=' + table + ':' + uid + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
169
+ topWindow.TYPO3.settings.RecordHistory.moduleUrl + '&element=' + table + ':' + uid + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
162
170
  );
163
171
  }
164
172
 
@@ -195,37 +203,49 @@ class ContextMenuActions {
195
203
  }
196
204
  }
197
205
 
206
+ /**
207
+ * Marks a navigation that commits a record and returns to the current
208
+ * module, so it is not handled as leaving the current view.
209
+ */
210
+ public static createRecordCommitRequest(): ClientRequest {
211
+ return new ClientRequest('typo3.recordCommit');
212
+ }
213
+
198
214
  public static disableRecord(table: string, uid: number, dataset: DOMStringMap): void {
199
215
  const disableFieldName = dataset.disableField || 'hidden';
200
216
  Viewport.ContentContainer.setUrl(
201
- top.TYPO3.settings.RecordCommit.moduleUrl
217
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
202
218
  + '&data[' + table + '][' + uid + '][' + disableFieldName + ']=1'
203
219
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
220
+ ContextMenuActions.createRecordCommitRequest(),
204
221
  );
205
222
  }
206
223
 
207
224
  public static enableRecord(table: string, uid: number, dataset: DOMStringMap): void {
208
225
  const disableFieldName = dataset.disableField || 'hidden';
209
226
  Viewport.ContentContainer.setUrl(
210
- top.TYPO3.settings.RecordCommit.moduleUrl
227
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
211
228
  + '&data[' + table + '][' + uid + '][' + disableFieldName + ']=0'
212
229
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
230
+ ContextMenuActions.createRecordCommitRequest(),
213
231
  );
214
232
  }
215
233
 
216
234
  public static showInMenus(table: string, uid: number): void {
217
235
  Viewport.ContentContainer.setUrl(
218
- top.TYPO3.settings.RecordCommit.moduleUrl
236
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
219
237
  + '&data[' + table + '][' + uid + '][nav_hide]=0'
220
238
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
239
+ ContextMenuActions.createRecordCommitRequest(),
221
240
  );
222
241
  }
223
242
 
224
243
  public static hideInMenus(table: string, uid: number): void {
225
244
  Viewport.ContentContainer.setUrl(
226
- top.TYPO3.settings.RecordCommit.moduleUrl
245
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
227
246
  + '&data[' + table + '][' + uid + '][nav_hide]=1'
228
247
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
248
+ ContextMenuActions.createRecordCommitRequest(),
229
249
  );
230
250
  }
231
251
 
@@ -350,7 +370,7 @@ class ContextMenuActions {
350
370
  + '&redirect=' + ContextMenuActions.getReturnUrl();
351
371
 
352
372
  Viewport.ContentContainer.setUrl(
353
- top.TYPO3.settings.RecordCommit.moduleUrl + url,
373
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl + url,
354
374
  );
355
375
  };
356
376
  if (!dataset.title) {
@@ -384,7 +404,7 @@ class ContextMenuActions {
384
404
  }
385
405
 
386
406
  private static refreshPageTree(): void {
387
- top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
407
+ topDocument.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
388
408
  }
389
409
  }
390
410
 
@@ -18,6 +18,7 @@ import '@typo3/backend/element/spinner-element';
18
18
  import { customElement, queryAll, state } from 'lit/decorators.js';
19
19
  import { html, LitElement, nothing, type TemplateResult } from 'lit';
20
20
  import { delay } from '@typo3/core/lit-helper';
21
+ import { topDocument } from '@typo3/core/utility/top-window';
21
22
  import { styleMap } from 'lit/directives/style-map.js';
22
23
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
23
24
  import { Task, initialState } from '@lit/task';
@@ -99,7 +100,7 @@ class ContextMenu {
99
100
  bubbles: true,
100
101
  composed: true
101
102
  });
102
- top.document.dispatchEvent(contextMenuEvent);
103
+ topDocument.dispatchEvent(contextMenuEvent);
103
104
  }
104
105
 
105
106
  private handleTriggerEvent(event: PointerEvent): void {
@@ -154,6 +155,9 @@ export class ContextMenuElement extends LitElement {
154
155
  @queryAll('.context-menu') contextMenuElements: HTMLDivElement[];
155
156
  @queryAll('.context-menu-item') contextMenuItemElements: HTMLButtonElement[];
156
157
  private readonly rootIdentifier: string = 'root';
158
+ // A modal <dialog> makes everything outside of it inert, so the menu is rendered
159
+ // into a popover that is mounted into the topmost open dialog while it is shown.
160
+ private readonly popoverHost: HTMLDivElement = document.createElement('div');
157
161
 
158
162
  private focusFirstElement: boolean = false;
159
163
 
@@ -210,6 +214,8 @@ export class ContextMenuElement extends LitElement {
210
214
  window.addEventListener('resize', this.hide);
211
215
  document.addEventListener(ContextMenuEvent.open, this.show);
212
216
  document.addEventListener(ContextMenuEvent.close, this.hide);
217
+ document.addEventListener('typo3-modal-show', this.hide);
218
+ document.addEventListener('typo3-modal-hide', this.hide);
213
219
  }
214
220
 
215
221
  public override disconnectedCallback(): void {
@@ -217,6 +223,8 @@ export class ContextMenuElement extends LitElement {
217
223
  window.removeEventListener('resize', this.hide);
218
224
  document.removeEventListener(ContextMenuEvent.open, this.show);
219
225
  document.removeEventListener(ContextMenuEvent.close, this.hide);
226
+ document.removeEventListener('typo3-modal-show', this.hide);
227
+ document.removeEventListener('typo3-modal-hide', this.hide);
220
228
  }
221
229
 
222
230
  protected override updated(): void {
@@ -231,7 +239,10 @@ export class ContextMenuElement extends LitElement {
231
239
  }
232
240
 
233
241
  protected override createRenderRoot(): HTMLElement | ShadowRoot {
234
- return this;
242
+ this.popoverHost.classList.add('context-menu-host');
243
+ this.popoverHost.setAttribute('popover', 'manual');
244
+ this.append(this.popoverHost);
245
+ return this.popoverHost;
235
246
  }
236
247
 
237
248
  protected override render() {
@@ -264,6 +275,7 @@ export class ContextMenuElement extends LitElement {
264
275
  this.uid = detail.uid;
265
276
  this.context = detail.context;
266
277
  this.eventSource = detail.eventSource;
278
+ this.mountPopoverHost();
267
279
 
268
280
  if (detail.originalEvent !== null) {
269
281
  const offset = this.calculateIframeOffset(detail.originalEvent.view, window);
@@ -294,11 +306,31 @@ export class ContextMenuElement extends LitElement {
294
306
  return;
295
307
  }
296
308
  this.open = false;
309
+ this.unmountPopoverHost();
297
310
  this.fetchTask.run();
298
311
  await this.updateComplete;
299
312
  this.eventSource?.focus();
300
313
  };
301
314
 
315
+ private mountPopoverHost(): void {
316
+ const openDialogs = document.querySelectorAll<HTMLDialogElement>('typo3-backend-modal > dialog[open]');
317
+ const target: HTMLElement = openDialogs.item(openDialogs.length - 1) ?? this;
318
+ if (this.popoverHost.matches(':popover-open')) {
319
+ this.popoverHost.hidePopover();
320
+ }
321
+ if (this.popoverHost.parentElement !== target) {
322
+ target.append(this.popoverHost);
323
+ }
324
+ this.popoverHost.showPopover();
325
+ }
326
+
327
+ private unmountPopoverHost(): void {
328
+ if (this.popoverHost.matches(':popover-open')) {
329
+ this.popoverHost.hidePopover();
330
+ }
331
+ this.append(this.popoverHost);
332
+ }
333
+
302
334
  private renderOverlay(): TemplateResult {
303
335
  return html`
304
336
  <div
@@ -593,7 +625,7 @@ export class ContextMenuElement extends LitElement {
593
625
  }
594
626
 
595
627
  private getElementFromNode(node: MenuItemInterface): HTMLElement | null {
596
- return this.querySelector('[data-contextmenu-id="' + this.getNodeIdentifier(node) + '"]');
628
+ return this.renderRoot.querySelector<HTMLElement>('[data-contextmenu-id="' + this.getNodeIdentifier(node) + '"]');
597
629
  }
598
630
 
599
631
  private enhanceNodes(parentIdentifier: string, nodes: RawMenuItemInterface[]): MenuItemInterface[] {