typo3-typescript-sources 14.3.10 → 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.
- package/.last-synced-tag +1 -1
- package/README.md +1 -1
- package/adminpanel/admin-panel.ts +2 -2
- package/backend/bookmark/bookmark-manager.ts +2 -2
- package/backend/clipboard-panel.ts +13 -7
- package/backend/code-editor/language/typoscript.ts +1 -1
- package/backend/column-selector-button.ts +2 -2
- package/backend/context-menu-actions.ts +23 -5
- package/backend/context-menu.ts +33 -2
- package/backend/contextual-record-edit.ts +114 -33
- package/backend/date-time-picker.ts +1 -1
- package/backend/drag-uploader.ts +49 -20
- package/backend/element/breadcrumb.ts +6 -6
- package/backend/element/combobox-element.ts +2 -2
- package/backend/element/contextual-record-edit-trigger.ts +97 -63
- package/backend/enum/viewport/scaffold-identifier.ts +1 -1
- package/backend/form-engine/container/flex-form-container-container.ts +1 -1
- package/backend/form-engine/container/flex-form-section-container.ts +3 -3
- package/backend/form-engine/container/inline-control-container.ts +29 -29
- package/backend/form-engine/element/abstract-sortable-select-items.ts +1 -1
- package/backend/form-engine/element/category-element.ts +5 -5
- package/backend/form-engine/element/group-element.ts +1 -1
- package/backend/form-engine/element/select-tree-element.ts +4 -4
- package/backend/form-engine/element/select-tree.ts +1 -1
- package/backend/form-engine/element/slug-element.ts +9 -7
- package/backend/form-engine/element/suggest/result-container.ts +1 -1
- package/backend/form-engine/element/suggest/result-item.ts +1 -1
- package/backend/form-engine/element/table-permission-element.ts +4 -4
- package/backend/form-engine/field-control/add-record.ts +1 -1
- package/backend/form-engine/field-control/edit-popup.ts +2 -2
- package/backend/form-engine/field-control/insert-clipboard.ts +1 -1
- package/backend/form-engine/field-control/link-popup.ts +1 -1
- package/backend/form-engine/field-control/list-module.ts +1 -1
- package/backend/form-engine/field-control/password-generator.ts +4 -4
- package/backend/form-engine/field-control/reset-selection.ts +2 -2
- package/backend/form-engine/field-wizard/localization-state-selector.ts +2 -2
- package/backend/form-engine/field-wizard/value-picker.ts +1 -1
- package/backend/form-engine/field-wizard/value-slider.ts +2 -2
- package/backend/form-engine-review.ts +1 -1
- package/backend/form-engine-suggest.ts +2 -2
- package/backend/form-engine-validation.ts +55 -10
- package/backend/form-engine.ts +12 -11
- package/backend/global-event-handler.ts +1 -1
- package/backend/grid-editor.ts +83 -8
- package/backend/image-manipulation.ts +2 -2
- package/backend/layout-module/drag-drop.ts +15 -15
- package/backend/layout-module/page-layout.ts +7 -2
- package/backend/layout-module/paste.ts +3 -3
- package/backend/layout-module/toggle-hidden-element.ts +1 -1
- package/backend/live-search/live-search.ts +6 -6
- package/backend/localization/finisher/localization-submission-service.ts +1 -0
- package/backend/localization/localization-button.ts +3 -0
- package/backend/localization/localization-wizard.ts +3 -0
- package/backend/localization/steps/source-language-step.ts +1 -0
- package/backend/login/capslock-warning.ts +84 -0
- package/backend/login-refresh.ts +2 -2
- package/backend/login.ts +3 -2
- package/backend/mail-link-handler.ts +2 -2
- package/backend/modal.ts +27 -3
- package/backend/module-menu.ts +17 -8
- package/backend/module.ts +1 -1
- package/backend/multi-record-selection.ts +3 -3
- package/backend/multi-step-wizard.ts +2 -2
- package/backend/new-record-wizard.ts +1 -1
- package/backend/notification.ts +1 -1
- package/backend/page-wizard/steps/form-engine-step.ts +2 -2
- package/backend/popover.ts +1 -1
- package/backend/recordlist.ts +7 -7
- package/backend/security/element/csp-reports.ts +2 -2
- package/backend/settings/editor.ts +9 -9
- package/backend/settings/type/page.ts +2 -2
- package/backend/sortable-table.ts +3 -3
- package/backend/tab.ts +3 -3
- package/backend/telephone-link-handler.ts +1 -1
- package/backend/tests/drag-uploader-test.ts +287 -0
- package/backend/tests/form-engine/element/select-tree-test.ts +1 -1
- package/backend/tests/form-engine-validation-test.ts +75 -0
- package/backend/tests/grid-editor-test.ts +29 -0
- package/backend/tests/login/capslock-warning-test.ts +78 -0
- package/backend/tests/modal-test.ts +73 -0
- package/backend/tests/notification-test.ts +15 -15
- package/backend/tests/tree/tree-test.ts +68 -0
- package/backend/tests/utility/drag-drop-utility-test.ts +82 -0
- package/backend/toolbar/system-information-menu.ts +2 -2
- package/backend/tree/file-storage-browser.ts +2 -2
- package/backend/tree/page-browser.ts +2 -2
- package/backend/tree/page-position-select.ts +2 -2
- package/backend/tree/page-tree-element.ts +3 -2
- package/backend/tree/tree-module-state.ts +12 -0
- package/backend/tree/tree-toolbar.ts +1 -1
- package/backend/tree/tree.ts +24 -17
- package/backend/url-link-handler.ts +1 -1
- package/backend/user-pass-login.ts +0 -103
- package/backend/utility/collapse-state-search.ts +2 -2
- package/backend/utility/drag-drop-utility.ts +5 -9
- package/backend/viewport/content-container.ts +1 -1
- package/backend/viewport/content-navigation-toggle.ts +2 -2
- package/backend/viewport/content-navigation.ts +3 -3
- package/backend/viewport/navigation-container.ts +2 -2
- package/backend/wizard/move-content-element.ts +7 -7
- package/backend/wizard/move-page.ts +3 -3
- package/backend/wizard/steps/finisher-step.ts +1 -1
- package/belog/backend-log.ts +2 -2
- package/beuser/permissions.ts +4 -4
- package/core/java-script-item-handler.ts +12 -2
- package/core/java-script-item-processor.ts +52 -6
- package/core/tests/java-script-item-processor-test.ts +83 -0
- package/dashboard/dashboard.ts +1 -6
- package/extensionmanager/main.ts +1 -1
- package/extensionmanager/update.ts +1 -1
- package/extensionmanager/upload-form.ts +1 -1
- package/filelist/browse-files.ts +2 -2
- package/filelist/browse-folders.ts +1 -1
- package/filelist/file-list-actions.ts +1 -1
- package/filelist/file-list-dragdrop.ts +4 -4
- package/filelist/file-list.ts +5 -5
- package/filelist/linkbrowser-file-handler.ts +1 -1
- package/filelist/rename-file.ts +3 -3
- package/filelist/resource-creation.ts +1 -1
- package/form/backend/form-editor/core.ts +1 -1
- package/form/backend/form-editor/inspector-component.ts +4 -4
- package/form/backend/form-editor/stage-component.ts +10 -11
- package/form/backend/form-editor/view-model.ts +1 -1
- package/info/translation-status.ts +2 -2
- package/install/installer.ts +6 -6
- package/install/module/environment/folder-structure.ts +1 -1
- package/install/module/maintenance/clear-tables.ts +1 -1
- package/install/module/maintenance/clear-typo3temp-files.ts +1 -1
- package/install/module/maintenance/database-analyzer.ts +2 -2
- package/install/module/settings/local-configuration.ts +8 -8
- package/install/module/upgrade/upgrade-docs.ts +1 -1
- package/install/module/upgrade/upgrade-wizards.ts +8 -8
- package/install/router.ts +3 -3
- package/lowlevel/query-generator.ts +4 -4
- package/package.json +2 -2
- package/recycler/recycler.ts +17 -17
- package/redirects/event-handler.ts +64 -19
- package/redirects/short-url-generator.ts +2 -2
- package/redirects/tests/event-handler-test.ts +121 -0
- package/rte-ckeditor/ckeditor5.ts +2 -2
- package/rte-ckeditor/tests/ckeditor5-test.ts +1 -1
- package/scheduler/form-engine/element/timing-options-element.ts +2 -2
- package/scheduler/scheduler-add-group.ts +3 -3
- package/scheduler/scheduler-sortable-groups.ts +2 -2
- package/scheduler/scheduler.ts +1 -1
- package/styleguide/find-icons.ts +1 -1
- package/viewpage/main.ts +7 -7
- package/workspaces/backend.ts +25 -25
- package/workspaces/preview.ts +8 -8
- package/workspaces/workspace-state.ts +1 -1
package/.last-synced-tag
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v14.3.
|
|
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.
|
|
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)
|
|
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)
|
|
63
|
+
this.trigger = document.querySelector<HTMLElement>(AdminPanelSelectors.triggerRole);
|
|
64
64
|
|
|
65
65
|
this.initializeEvents();
|
|
66
66
|
this.addBackdropListener();
|
|
@@ -1120,7 +1120,7 @@ export class BookmarkManagerContentElement extends LitElement {
|
|
|
1120
1120
|
|
|
1121
1121
|
private async handleDropBookmark(draggedBookmark: Bookmark, target: Bookmark | BookmarkGroup | BookmarkGroupId, dropPosition?: 'before' | 'after'): Promise<void> {
|
|
1122
1122
|
if (typeof target === 'object' && 'route' in target) {
|
|
1123
|
-
const targetBookmark = target
|
|
1123
|
+
const targetBookmark = target;
|
|
1124
1124
|
if (draggedBookmark.id === targetBookmark.id) {
|
|
1125
1125
|
return;
|
|
1126
1126
|
}
|
|
@@ -1210,7 +1210,7 @@ export class BookmarkManagerButtonElement extends PseudoButtonLitElement {
|
|
|
1210
1210
|
protected override buttonActivated(): void {
|
|
1211
1211
|
// Create element in top frame to avoid adoptedStyleSheets cross-document issues
|
|
1212
1212
|
const targetDocument = topDocument;
|
|
1213
|
-
const managerElement = targetDocument.createElement('typo3-backend-bookmark-manager-content')
|
|
1213
|
+
const managerElement = targetDocument.createElement('typo3-backend-bookmark-manager-content');
|
|
1214
1214
|
if (this.editId !== undefined) {
|
|
1215
1215
|
managerElement.editId = this.editId;
|
|
1216
1216
|
}
|
|
@@ -105,11 +105,17 @@ export class ClipboardPanel extends LitElement {
|
|
|
105
105
|
const clipboardData: ClipboardData = resolvedBody.data;
|
|
106
106
|
return html`
|
|
107
107
|
<div class="panel panel-default" data-clipboard-panel>
|
|
108
|
-
<div class="panel-heading">
|
|
108
|
+
<div class="panel-heading" id="clipboard-panel-heading">
|
|
109
109
|
${clipboardData.labels.clipboard}
|
|
110
110
|
</div>
|
|
111
111
|
<div class="table-fit">
|
|
112
|
-
<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>
|
|
113
119
|
<tbody>
|
|
114
120
|
${clipboardData.tabs.map((tab): TemplateResult => this.renderTab(tab, clipboardData))}
|
|
115
121
|
</tbody>
|
|
@@ -133,8 +139,8 @@ export class ClipboardPanel extends LitElement {
|
|
|
133
139
|
private renderTab(tab: ClipboardTab, clipboardData: ClipboardData): TemplateResult {
|
|
134
140
|
return html`
|
|
135
141
|
<tr>
|
|
136
|
-
<
|
|
137
|
-
<button type="button" class="btn btn-link" aria-
|
|
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 } })}">
|
|
138
144
|
${clipboardData.current === tab.identifier ? html`
|
|
139
145
|
<typo3-backend-icon identifier="actions-check-circle-alt" size="small"></typo3-backend-icon>
|
|
140
146
|
${tab.title}
|
|
@@ -146,7 +152,7 @@ export class ClipboardPanel extends LitElement {
|
|
|
146
152
|
</span>
|
|
147
153
|
`}
|
|
148
154
|
</button>
|
|
149
|
-
</
|
|
155
|
+
</th>
|
|
150
156
|
<td class="col-control nowrap">
|
|
151
157
|
${clipboardData.current !== tab.identifier ? nothing : html`
|
|
152
158
|
<div class="btn-toolbar btn-toolbar-nowrap">
|
|
@@ -182,11 +188,11 @@ export class ClipboardPanel extends LitElement {
|
|
|
182
188
|
<td class="col-icon nowrap ${classMap({ 'ps-4': !tabItem.identifier })}">
|
|
183
189
|
${unsafeHTML(tabItem.icon)}
|
|
184
190
|
</td>
|
|
185
|
-
<
|
|
191
|
+
<th scope="row" class="col-title col-responsive nowrap fw-normal">
|
|
186
192
|
${unsafeHTML(tabItem.title)}
|
|
187
193
|
${tabIdentifier === 'normal' ? html`<strong>(${clipboardData.copyMode === CopyMode.copy ? html`${clipboardData.labels.copy}` : html`${clipboardData.labels.cut}`})</strong>` : nothing}
|
|
188
194
|
${tabItem.thumb ? html`<div class="d-block">${unsafeHTML(tabItem.thumb)}</div>` : nothing}
|
|
189
|
-
</
|
|
195
|
+
</th>
|
|
190
196
|
<td class="col-control nowrap">
|
|
191
197
|
<div class="btn-group">
|
|
192
198
|
${!tabItem.infoDataDispatch ? nothing : html`
|
|
@@ -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"]')
|
|
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
|
|
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
|
|
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';
|
|
@@ -52,13 +54,17 @@ class ContextMenuActions {
|
|
|
52
54
|
overrideVals = '&overrideVals[pages][sys_language_uid]=' + pageLanguageId;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
topWindow.TYPO3.settings.FormEngine.moduleUrl
|
|
57
|
-
+ '&edit[' + table + '][' + uid + ']=edit'
|
|
57
|
+
const editParameters = '&edit[' + table + '][' + uid + ']=edit'
|
|
58
58
|
+ overrideVals
|
|
59
59
|
+ '&module=' + encodeURIComponent(topWindow.TYPO3.ModuleMenu.App.getCurrentModule())
|
|
60
|
-
+ '&returnUrl=' + ContextMenuActions.getReturnUrl()
|
|
61
|
-
|
|
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);
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
public static viewRecord(table: string, uid: number, dataset: DOMStringMap): void {
|
|
@@ -197,12 +203,21 @@ class ContextMenuActions {
|
|
|
197
203
|
}
|
|
198
204
|
}
|
|
199
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
|
+
|
|
200
214
|
public static disableRecord(table: string, uid: number, dataset: DOMStringMap): void {
|
|
201
215
|
const disableFieldName = dataset.disableField || 'hidden';
|
|
202
216
|
Viewport.ContentContainer.setUrl(
|
|
203
217
|
topWindow.TYPO3.settings.RecordCommit.moduleUrl
|
|
204
218
|
+ '&data[' + table + '][' + uid + '][' + disableFieldName + ']=1'
|
|
205
219
|
+ '&redirect=' + ContextMenuActions.getReturnUrl(),
|
|
220
|
+
ContextMenuActions.createRecordCommitRequest(),
|
|
206
221
|
);
|
|
207
222
|
}
|
|
208
223
|
|
|
@@ -212,6 +227,7 @@ class ContextMenuActions {
|
|
|
212
227
|
topWindow.TYPO3.settings.RecordCommit.moduleUrl
|
|
213
228
|
+ '&data[' + table + '][' + uid + '][' + disableFieldName + ']=0'
|
|
214
229
|
+ '&redirect=' + ContextMenuActions.getReturnUrl(),
|
|
230
|
+
ContextMenuActions.createRecordCommitRequest(),
|
|
215
231
|
);
|
|
216
232
|
}
|
|
217
233
|
|
|
@@ -220,6 +236,7 @@ class ContextMenuActions {
|
|
|
220
236
|
topWindow.TYPO3.settings.RecordCommit.moduleUrl
|
|
221
237
|
+ '&data[' + table + '][' + uid + '][nav_hide]=0'
|
|
222
238
|
+ '&redirect=' + ContextMenuActions.getReturnUrl(),
|
|
239
|
+
ContextMenuActions.createRecordCommitRequest(),
|
|
223
240
|
);
|
|
224
241
|
}
|
|
225
242
|
|
|
@@ -228,6 +245,7 @@ class ContextMenuActions {
|
|
|
228
245
|
topWindow.TYPO3.settings.RecordCommit.moduleUrl
|
|
229
246
|
+ '&data[' + table + '][' + uid + '][nav_hide]=1'
|
|
230
247
|
+ '&redirect=' + ContextMenuActions.getReturnUrl(),
|
|
248
|
+
ContextMenuActions.createRecordCommitRequest(),
|
|
231
249
|
);
|
|
232
250
|
}
|
|
233
251
|
|
package/backend/context-menu.ts
CHANGED
|
@@ -155,6 +155,9 @@ export class ContextMenuElement extends LitElement {
|
|
|
155
155
|
@queryAll('.context-menu') contextMenuElements: HTMLDivElement[];
|
|
156
156
|
@queryAll('.context-menu-item') contextMenuItemElements: HTMLButtonElement[];
|
|
157
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');
|
|
158
161
|
|
|
159
162
|
private focusFirstElement: boolean = false;
|
|
160
163
|
|
|
@@ -211,6 +214,8 @@ export class ContextMenuElement extends LitElement {
|
|
|
211
214
|
window.addEventListener('resize', this.hide);
|
|
212
215
|
document.addEventListener(ContextMenuEvent.open, this.show);
|
|
213
216
|
document.addEventListener(ContextMenuEvent.close, this.hide);
|
|
217
|
+
document.addEventListener('typo3-modal-show', this.hide);
|
|
218
|
+
document.addEventListener('typo3-modal-hide', this.hide);
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
public override disconnectedCallback(): void {
|
|
@@ -218,6 +223,8 @@ export class ContextMenuElement extends LitElement {
|
|
|
218
223
|
window.removeEventListener('resize', this.hide);
|
|
219
224
|
document.removeEventListener(ContextMenuEvent.open, this.show);
|
|
220
225
|
document.removeEventListener(ContextMenuEvent.close, this.hide);
|
|
226
|
+
document.removeEventListener('typo3-modal-show', this.hide);
|
|
227
|
+
document.removeEventListener('typo3-modal-hide', this.hide);
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
protected override updated(): void {
|
|
@@ -232,7 +239,10 @@ export class ContextMenuElement extends LitElement {
|
|
|
232
239
|
}
|
|
233
240
|
|
|
234
241
|
protected override createRenderRoot(): HTMLElement | ShadowRoot {
|
|
235
|
-
|
|
242
|
+
this.popoverHost.classList.add('context-menu-host');
|
|
243
|
+
this.popoverHost.setAttribute('popover', 'manual');
|
|
244
|
+
this.append(this.popoverHost);
|
|
245
|
+
return this.popoverHost;
|
|
236
246
|
}
|
|
237
247
|
|
|
238
248
|
protected override render() {
|
|
@@ -265,6 +275,7 @@ export class ContextMenuElement extends LitElement {
|
|
|
265
275
|
this.uid = detail.uid;
|
|
266
276
|
this.context = detail.context;
|
|
267
277
|
this.eventSource = detail.eventSource;
|
|
278
|
+
this.mountPopoverHost();
|
|
268
279
|
|
|
269
280
|
if (detail.originalEvent !== null) {
|
|
270
281
|
const offset = this.calculateIframeOffset(detail.originalEvent.view, window);
|
|
@@ -295,11 +306,31 @@ export class ContextMenuElement extends LitElement {
|
|
|
295
306
|
return;
|
|
296
307
|
}
|
|
297
308
|
this.open = false;
|
|
309
|
+
this.unmountPopoverHost();
|
|
298
310
|
this.fetchTask.run();
|
|
299
311
|
await this.updateComplete;
|
|
300
312
|
this.eventSource?.focus();
|
|
301
313
|
};
|
|
302
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
|
+
|
|
303
334
|
private renderOverlay(): TemplateResult {
|
|
304
335
|
return html`
|
|
305
336
|
<div
|
|
@@ -594,7 +625,7 @@ export class ContextMenuElement extends LitElement {
|
|
|
594
625
|
}
|
|
595
626
|
|
|
596
627
|
private getElementFromNode(node: MenuItemInterface): HTMLElement | null {
|
|
597
|
-
return this.querySelector('[data-contextmenu-id="' + this.getNodeIdentifier(node) + '"]');
|
|
628
|
+
return this.renderRoot.querySelector<HTMLElement>('[data-contextmenu-id="' + this.getNodeIdentifier(node) + '"]');
|
|
598
629
|
}
|
|
599
630
|
|
|
600
631
|
private enhanceNodes(parentIdentifier: string, nodes: RawMenuItemInterface[]): MenuItemInterface[] {
|
|
@@ -12,12 +12,15 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import FormEngine from '@typo3/backend/form-engine';
|
|
15
|
-
import
|
|
15
|
+
import type { Consumable } from '@typo3/backend/event/consumable';
|
|
16
|
+
import type TriggerRequest from '@typo3/backend/event/trigger-request';
|
|
17
|
+
import Modal, { type ModalElement } from '@typo3/backend/modal';
|
|
16
18
|
import Severity from '@typo3/backend/severity';
|
|
17
19
|
import DocumentService from '@typo3/core/document-service';
|
|
18
|
-
import { topWindow } from '@typo3/core/utility/top-window';
|
|
20
|
+
import { topDocument, topWindow } from '@typo3/core/utility/top-window';
|
|
19
21
|
import '@typo3/backend/element/icon-element';
|
|
20
22
|
import labels from '~labels/backend.alt_doc';
|
|
23
|
+
import coreCommonLabels from '~labels/core.common';
|
|
21
24
|
|
|
22
25
|
type ContextualRecordEditOptions = {
|
|
23
26
|
justSaved?: boolean;
|
|
@@ -38,6 +41,8 @@ class ContextualRecordEdit {
|
|
|
38
41
|
private closeButton: HTMLButtonElement | null = null;
|
|
39
42
|
private fullscreenButton: HTMLAnchorElement | null = null;
|
|
40
43
|
private readonly options: ContextualRecordEditOptions;
|
|
44
|
+
// "<table>:<uid>" of the edited record
|
|
45
|
+
private readonly record: string = ContextualRecordEdit.resolveRecord();
|
|
41
46
|
|
|
42
47
|
constructor(options: ContextualRecordEditOptions = {}) {
|
|
43
48
|
this.options = options;
|
|
@@ -48,11 +53,22 @@ class ContextualRecordEdit {
|
|
|
48
53
|
});
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
private static resolveRecord(): string {
|
|
57
|
+
for (const key of new URL(window.location.href).searchParams.keys()) {
|
|
58
|
+
const match = key.match(/^edit\[([^\]]+)]\[(\d+)]$/);
|
|
59
|
+
if (match !== null) {
|
|
60
|
+
return match[1] + ':' + match[2];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
51
66
|
private initialize(): void {
|
|
52
67
|
this.initializeCloseButton();
|
|
53
68
|
this.initializeFullscreenButton();
|
|
54
69
|
this.initializeEscapeKey();
|
|
55
70
|
this.initializeParentMessages();
|
|
71
|
+
this.initializeNavigationGuard();
|
|
56
72
|
this.handlePostSaveState();
|
|
57
73
|
if (!this.options.justSaved) {
|
|
58
74
|
this.focusFirstElement();
|
|
@@ -74,42 +90,101 @@ class ContextualRecordEdit {
|
|
|
74
90
|
}
|
|
75
91
|
this.fullscreenButton.addEventListener('click', (e: Event): void => {
|
|
76
92
|
e.preventDefault();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// postMessage for this failed silently in Firefox.
|
|
93
|
+
// Navigate the content frame directly (same-origin access), using
|
|
94
|
+
// postMessage for this failed silently in Firefox. The navigation
|
|
95
|
+
// guard asks to discard unsaved changes and closes the sheet.
|
|
96
|
+
FormEngine.ready().then((): void => {
|
|
82
97
|
topWindow.TYPO3?.Backend?.ContentContainer?.setUrl(this.fullscreenButton.href);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Navigating the content frame while the sheet is open (e.g. via a context
|
|
104
|
+
* menu action) would load the target behind the sheet. The sheet therefore
|
|
105
|
+
* replaces FormEngine as navigation guard and closes itself instead.
|
|
106
|
+
*/
|
|
107
|
+
private initializeNavigationGuard(): void {
|
|
108
|
+
const consumerScope = topWindow.TYPO3?.Backend?.consumerScope;
|
|
109
|
+
if (!consumerScope) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const navigationGuard: Consumable = {
|
|
113
|
+
consume: (interactionRequest: TriggerRequest): Promise<void> | null => {
|
|
114
|
+
if (!interactionRequest.concernsTypes(['typo3.setUrl'])) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// Stays pending if the user keeps editing, which cancels the navigation
|
|
118
|
+
return new Promise<void>((resolve): void => {
|
|
119
|
+
if (interactionRequest.concernsTypes(['typo3.recordCommit'])) {
|
|
120
|
+
// A record commit returns to the current module, so the sheet stays
|
|
121
|
+
// open and is reloaded once the commit is done to show its result.
|
|
122
|
+
this.confirmDiscardChanges((): void => {
|
|
123
|
+
topDocument.addEventListener('typo3-module-loaded', () => this.reload(), { once: true });
|
|
124
|
+
resolve();
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.confirmDiscardChanges((): void => {
|
|
129
|
+
resolve();
|
|
130
|
+
window.parent.postMessage(
|
|
131
|
+
{ actionName: 'typo3:editform:navigate' },
|
|
132
|
+
window.location.origin
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
let pageHidden = false;
|
|
139
|
+
window.addEventListener('pagehide', (): void => {
|
|
140
|
+
pageHidden = true;
|
|
141
|
+
consumerScope.detach(navigationGuard);
|
|
142
|
+
}, { once: true });
|
|
143
|
+
FormEngine.ready().then((): void => {
|
|
144
|
+
if (pageHidden) {
|
|
145
|
+
return;
|
|
109
146
|
}
|
|
147
|
+
consumerScope.detach(FormEngine);
|
|
148
|
+
consumerScope.attach(navigationGuard);
|
|
110
149
|
});
|
|
111
150
|
}
|
|
112
151
|
|
|
152
|
+
private reload(): void {
|
|
153
|
+
const url = new URL(window.location.href);
|
|
154
|
+
url.searchParams.delete('justSaved');
|
|
155
|
+
window.location.replace(url.toString());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private confirmDiscardChanges(onDiscard: () => void, onSave: (() => void) | null = null): void {
|
|
159
|
+
if (!FormEngine.hasChange()) {
|
|
160
|
+
onDiscard();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
Modal.confirm(
|
|
164
|
+
labels.get('label.confirm.close_without_save.title'),
|
|
165
|
+
labels.get('label.confirm.close_without_save.content'),
|
|
166
|
+
Severity.warning,
|
|
167
|
+
[
|
|
168
|
+
{
|
|
169
|
+
text: labels.get('buttons.confirm.close_without_save.no'),
|
|
170
|
+
btnClass: 'btn-default',
|
|
171
|
+
trigger: (_e, modal): void => { modal.hideModal(); },
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
text: labels.get('buttons.confirm.close_without_save.yes'),
|
|
175
|
+
btnClass: 'btn-default',
|
|
176
|
+
trigger: (_e, modal): void => { modal.hideModal(); onDiscard(); },
|
|
177
|
+
},
|
|
178
|
+
...(onSave !== null && document.querySelector('.has-error') === null ? [{
|
|
179
|
+
text: coreCommonLabels.get('save'),
|
|
180
|
+
btnClass: 'btn-primary',
|
|
181
|
+
active: true,
|
|
182
|
+
trigger: (_e: Event, modal: ModalElement): void => { modal.hideModal(); onSave(); },
|
|
183
|
+
}] : []),
|
|
184
|
+
]
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
113
188
|
private initializeEscapeKey(): void {
|
|
114
189
|
document.addEventListener('keydown', (e: KeyboardEvent): void => {
|
|
115
190
|
if (e.key === 'Escape') {
|
|
@@ -127,6 +202,11 @@ class ContextualRecordEdit {
|
|
|
127
202
|
if (event.data?.actionName === 'typo3:editform:requestclose') {
|
|
128
203
|
this.requestClose();
|
|
129
204
|
}
|
|
205
|
+
if (event.data?.actionName === 'typo3:editform:reload') {
|
|
206
|
+
// Saving the stale form of the record that was just saved elsewhere would revert that change
|
|
207
|
+
const canSave = event.data.savedRecord !== this.record;
|
|
208
|
+
this.confirmDiscardChanges((): void => this.reload(), canSave ? (): void => FormEngine.saveDocument() : null);
|
|
209
|
+
}
|
|
130
210
|
});
|
|
131
211
|
}
|
|
132
212
|
|
|
@@ -139,6 +219,7 @@ class ContextualRecordEdit {
|
|
|
139
219
|
{
|
|
140
220
|
actionName: 'typo3:editform:saved',
|
|
141
221
|
recordTitle: this.options.savedRecordTitle ?? '',
|
|
222
|
+
record: this.record,
|
|
142
223
|
},
|
|
143
224
|
window.location.origin
|
|
144
225
|
);
|
|
@@ -145,7 +145,7 @@ class DateTimePicker {
|
|
|
145
145
|
*/
|
|
146
146
|
private getScrollEvent(): ThrottleEvent {
|
|
147
147
|
return new ThrottleEvent('scroll', (): void => {
|
|
148
|
-
const activeFlatpickrElement = document.querySelector('.flatpickr-input.active')
|
|
148
|
+
const activeFlatpickrElement = document.querySelector<FlatpickrInputElement>('.flatpickr-input.active');
|
|
149
149
|
if (activeFlatpickrElement === null) {
|
|
150
150
|
return;
|
|
151
151
|
}
|