typo3-typescript-sources 14.3.7 → 14.3.9
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/.github/workflows/sync-and-publish.yml +240 -191
- package/.last-synced-tag +1 -1
- package/README.md +13 -5
- package/adminpanel/admin-panel.ts +22 -22
- package/adminpanel/modules/cache.ts +3 -3
- package/backend/action-dispatcher.ts +8 -3
- package/backend/bookmark/bookmark-store.ts +5 -5
- package/backend/clipboard-panel.ts +2 -2
- package/backend/code-editor/element/code-mirror-element.ts +1 -1
- package/backend/code-editor/language/typoscript.ts +3 -3
- package/backend/code-editor/stream-parser/typoscript.ts +2 -2
- package/backend/color-picker.ts +2 -2
- package/backend/column-selector-button.ts +11 -8
- package/backend/context-help.ts +3 -3
- package/backend/context-menu.ts +8 -3
- package/backend/drag-tooltip.ts +9 -4
- package/backend/drag-uploader.ts +81 -89
- package/backend/element/draggable-resizable-element.ts +18 -12
- package/backend/element/icon-element.ts +2 -2
- package/backend/element/thumbnail-element.ts +2 -2
- package/backend/element-browser.ts +1 -1
- package/backend/event/consumer-scope.ts +2 -2
- package/backend/event/interaction-request-assignment.ts +1 -1
- package/backend/event/interaction-request-map.ts +3 -3
- package/backend/form-engine/container/flex-form-container-container.ts +2 -2
- package/backend/form-engine/container/flex-form-section-container.ts +1 -1
- package/backend/form-engine/container/inline-control-container.ts +14 -14
- package/backend/form-engine/element/abstract-sortable-select-items.ts +3 -3
- package/backend/form-engine/element/color-element.ts +1 -1
- package/backend/form-engine/element/datetime-element.ts +1 -1
- package/backend/form-engine/element/extra/char-counter.ts +2 -2
- package/backend/form-engine/element/extra/select-box-filter.ts +1 -1
- package/backend/form-engine/element/mfa-info-element.ts +2 -2
- package/backend/form-engine/element/online-media-form-element.ts +8 -4
- package/backend/form-engine/element/select-multiple-side-by-side-element.ts +2 -2
- package/backend/form-engine/element/select-tree.ts +3 -3
- package/backend/form-engine/element/slug-element.ts +7 -7
- package/backend/form-engine/element/suggest/result-container.ts +1 -1
- package/backend/form-engine/element/table-wizard-element.ts +4 -4
- package/backend/form-engine/inline-relation/ajax-dispatcher.ts +3 -3
- package/backend/form-engine-validation.ts +11 -5
- package/backend/form-engine.ts +19 -23
- package/backend/global-event-handler.ts +1 -1
- package/backend/hotkeys/hotkey-struct.ts +1 -1
- package/backend/hotkeys.ts +3 -3
- package/backend/image-manipulation.ts +19 -19
- package/backend/layout-module/column-restriction.ts +61 -0
- package/backend/layout-module/drag-drop.ts +16 -4
- package/backend/layout-module/paste.ts +8 -1
- package/backend/live-search/element/result/item/action/action-container.ts +1 -1
- package/backend/live-search/element/result/item/item-container.ts +4 -4
- package/backend/live-search/element/result/result-container.ts +5 -0
- package/backend/live-search/element/result/result-pagination.ts +1 -1
- package/backend/live-search/live-search.ts +2 -2
- package/backend/localization/label-provider.ts +4 -4
- package/backend/localization/localization-wizard.ts +4 -4
- package/backend/localization/steps/content-record-selection-step.ts +8 -8
- package/backend/localization/steps/handler-selection-step.ts +4 -4
- package/backend/localization/steps/mode-step.ts +4 -4
- package/backend/localization/steps/source-language-step.ts +4 -4
- package/backend/localization/steps/target-language-step.ts +4 -4
- package/backend/login-refresh.ts +1 -9
- package/backend/login.ts +2 -2
- package/backend/modal.ts +38 -5
- package/backend/module/iframe.ts +10 -0
- package/backend/module/router.ts +10 -0
- package/backend/module-menu.ts +3 -3
- package/backend/multi-record-selection-action.ts +1 -1
- package/backend/multi-record-selection-delete-action.ts +1 -1
- package/backend/multi-record-selection.ts +14 -14
- package/backend/multi-step-wizard.ts +9 -2
- package/backend/new-multiple-pages.ts +2 -2
- package/backend/new-record-wizard.ts +15 -15
- package/backend/notification.ts +90 -6
- package/backend/online-media.ts +1 -3
- package/backend/page-wizard/page-wizard.ts +2 -2
- package/backend/page-wizard/steps/doktype-step.ts +5 -5
- package/backend/pagetsconfig/pagetsconfig-includes.ts +1 -1
- package/backend/popover.ts +5 -5
- package/backend/record-download-button.ts +5 -6
- package/backend/recordlist.ts +11 -6
- package/backend/security/element/csp-reports.ts +8 -8
- package/backend/setup-module.ts +2 -2
- package/backend/sortable-table.ts +10 -4
- package/backend/storage/abstract-client-storage.ts +2 -2
- package/backend/storage/module-state-storage.ts +2 -2
- package/backend/tests/backend/utility/dom-helper-test.ts +2 -2
- package/backend/tests/fixtures/modal-autofocus.html +1 -0
- package/backend/tests/form-engine/element/extra/select-box-filter-test.ts +3 -3
- package/backend/tests/form-engine-validation-test.ts +1 -1
- package/backend/tests/modal-test.ts +91 -0
- package/backend/tests/notification-test.ts +176 -0
- package/backend/tests/tree/tree-test.ts +41 -0
- package/backend/tree/file-storage-browser.ts +1 -1
- package/backend/tree/file-storage-tree-container.ts +4 -4
- package/backend/tree/page-browser.ts +1 -1
- package/backend/tree/page-position-select.ts +1 -1
- package/backend/tree/page-tree-element.ts +9 -2
- package/backend/tree/page-tree.ts +7 -1
- package/backend/tree/tree-module-state.ts +5 -5
- package/backend/tree/tree.ts +15 -8
- package/backend/user-pass-login.ts +2 -2
- package/backend/utility/collapse-state-persister.ts +1 -1
- package/backend/utility/collapse-state-search.ts +6 -6
- package/backend/utility/dom-helper.ts +3 -3
- package/backend/utility.ts +6 -6
- package/backend/viewport/content-navigation.ts +5 -0
- package/backend/viewport/navigation-container.ts +1 -1
- package/backend/viewport/toolbar.ts +3 -3
- package/backend/window-manager.ts +2 -2
- package/backend/wizard/events/auto-advance-event.ts +6 -0
- package/backend/wizard/events/before-next-step-event.ts +6 -0
- package/backend/wizard/events/step-summary-event.ts +6 -0
- package/backend/wizard/move-content-element.ts +1 -1
- package/backend/wizard/wizard.ts +8 -10
- package/belog/backend-log.ts +2 -2
- package/beuser/permissions.ts +2 -2
- package/core/ajax/ajax-response.ts +1 -1
- package/core/ajax/input-transformer.ts +1 -1
- package/core/event/debounce-event.ts +4 -4
- package/core/event/event-interface.ts +3 -3
- package/core/event/regular-event.ts +7 -7
- package/core/event/request-animation-frame-event.ts +4 -4
- package/core/event/throttle-event.ts +4 -4
- package/core/java-script-item-processor.ts +6 -0
- package/core/referrer-refresh.ts +2 -2
- package/core/security-utility.ts +1 -1
- package/dashboard/dashboard-wizard.ts +2 -2
- package/dashboard/dashboard.ts +8 -13
- package/dashboard/wizard/steps/preset-step.ts +3 -3
- package/extensionmanager/main.ts +7 -7
- package/filelist/browse-files.ts +1 -1
- package/filelist/browse-folders.ts +2 -2
- package/filelist/file-list-dragdrop.ts +5 -5
- package/filelist/file-list-rename-handler.ts +3 -6
- package/filelist/file-list-replace-handler.ts +3 -6
- package/filelist/file-list-transfer-handler.ts +2 -2
- package/filelist/file-list.ts +8 -9
- package/form/backend/form-editor/component/date-editor.ts +4 -0
- package/form/backend/form-editor/component/form-element-selector.ts +4 -0
- package/form/backend/form-editor/inspector-component.ts +6 -6
- package/form/backend/form-editor/mediator.ts +36 -36
- package/form/backend/form-editor/modals-component.ts +115 -6
- package/form/backend/form-editor/stage-component.ts +4 -4
- package/form/backend/form-editor/view-model.ts +7 -7
- package/form/backend/form-manager/view-model.ts +5 -5
- package/form/backend/form-wizard/form-wizard.ts +2 -2
- package/form/backend/form-wizard/steps/storage-step.ts +1 -1
- package/impexp/import-export.ts +1 -1
- package/info/translation-status.ts +2 -2
- package/install/ajax/ajax-queue.ts +1 -1
- package/install/module/abstract-interactable-module.ts +2 -2
- package/install/module/environment/image-processing.ts +4 -4
- package/install/module/environment/mail-test.ts +2 -2
- package/install/module/maintenance/clear-tables.ts +3 -3
- package/install/module/maintenance/clear-typo3temp-files.ts +8 -8
- package/install/module/maintenance/create-admin.ts +3 -3
- package/install/module/maintenance/database-analyzer.ts +9 -7
- package/install/module/maintenance/language-packs.ts +4 -4
- package/install/module/maintenance/reference-index.ts +1 -1
- package/install/module/settings/change-install-tool-password.ts +1 -1
- package/install/module/settings/extension-configuration.ts +5 -5
- package/install/module/settings/features.ts +1 -1
- package/install/module/settings/local-configuration.ts +5 -5
- package/install/module/settings/presets.ts +2 -2
- package/install/module/settings/system-maintainer.ts +1 -1
- package/install/module/upgrade/extension-scanner.ts +5 -5
- package/install/module/upgrade/tca-migrations-check.ts +1 -1
- package/install/module/upgrade/upgrade-docs.ts +16 -16
- package/install/module/upgrade/upgrade-wizards.ts +7 -8
- package/install/renderable/language-packs.ts +8 -2
- package/lowlevel/query-generator.ts +2 -2
- package/package.json +2 -2
- package/redirects/event-handler.ts +6 -0
- package/rte-ckeditor/ckeditor5.ts +7 -3
- package/rte-ckeditor/legacy/ckeditor5-bundle.ts +0 -1
- package/rte-ckeditor/plugin/typo3-link.ts +7 -9
- package/rte-ckeditor/rte-link-browser.ts +1 -1
- package/rte-ckeditor/tests/ckeditor5-test.ts +68 -0
- package/scheduler/form-engine/element/timing-options-element.ts +1 -1
- package/scheduler/scheduler-add-group.ts +4 -12
- package/scheduler/scheduler.ts +2 -2
- package/styleguide/checkbox-demo.ts +1 -1
- package/tstemplate/template-analyzer.ts +1 -1
- package/viewpage/main.ts +1 -1
- package/workspaces/backend.ts +5 -5
- package/workspaces/preview.ts +41 -2
- package/workspaces/renderable/record-table.ts +95 -78
- package/workspaces/renderable/send-to-stage-form.ts +1 -1
|
@@ -65,7 +65,7 @@ export class SelectTree extends Tree
|
|
|
65
65
|
* Expand all nodes and refresh view
|
|
66
66
|
*/
|
|
67
67
|
public expandAll(): void {
|
|
68
|
-
this.nodes.forEach((node
|
|
68
|
+
this.nodes.forEach((node) => { this.showChildren(node); });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -105,7 +105,7 @@ export class SelectTree extends Tree
|
|
|
105
105
|
// A plain substring match, no RegExp involved, therefore nothing to escape either
|
|
106
106
|
const foldedSearchTerm = SelectTree.fold(searchTerm ?? '');
|
|
107
107
|
|
|
108
|
-
this.nodes.forEach((node
|
|
108
|
+
this.nodes.forEach((node) => {
|
|
109
109
|
// skip the root node in searches
|
|
110
110
|
if (node === firstNode) {
|
|
111
111
|
return;
|
|
@@ -190,7 +190,7 @@ export class SelectTree extends Tree
|
|
|
190
190
|
*/
|
|
191
191
|
private prepareLoadedNodes(evt: CustomEvent): void {
|
|
192
192
|
const nodes = evt.detail.nodes as Array<TreeNodeInterface>;
|
|
193
|
-
evt.detail.nodes = nodes.map((node
|
|
193
|
+
evt.detail.nodes = nodes.map((node) => {
|
|
194
194
|
if (node.selectable === false) {
|
|
195
195
|
this.settings.unselectableElements.push(node.identifier);
|
|
196
196
|
}
|
|
@@ -88,7 +88,7 @@ class SlugElement {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
private registerEvents(): void {
|
|
91
|
-
const fieldsToListenOnList = Object.values(this.getAvailableFieldsForProposalGeneration()).map((field
|
|
91
|
+
const fieldsToListenOnList = Object.values(this.getAvailableFieldsForProposalGeneration()).map((field) => field.dataset.slugSelector);
|
|
92
92
|
const recreateButton: HTMLButtonElement = this.fullElement.querySelector(Selectors.recreateButton);
|
|
93
93
|
|
|
94
94
|
// Listen on 'listenerFieldNames' for new pages. This is typically the 'title' field
|
|
@@ -158,7 +158,7 @@ class SlugElement {
|
|
|
158
158
|
private sendSlugProposal(mode: ProposalModes): void {
|
|
159
159
|
const input: Record<string, string> = {};
|
|
160
160
|
if (mode === ProposalModes.AUTO || mode === ProposalModes.RECREATE) {
|
|
161
|
-
Object.entries(this.getAvailableFieldsForProposalGeneration()).forEach((entry
|
|
161
|
+
Object.entries(this.getAvailableFieldsForProposalGeneration()).forEach((entry) => {
|
|
162
162
|
input[entry[0]] = entry[1].value;
|
|
163
163
|
});
|
|
164
164
|
if (this.options.includeUidInValues === true) {
|
|
@@ -214,18 +214,18 @@ class SlugElement {
|
|
|
214
214
|
*
|
|
215
215
|
* @return Record<string, string>
|
|
216
216
|
*/
|
|
217
|
-
private getAvailableFieldsForProposalGeneration(): { [key: string]:
|
|
218
|
-
const availableFields: { [key: string]:
|
|
217
|
+
private getAvailableFieldsForProposalGeneration(): { [key: string]: HTMLInputElement|HTMLSelectElement } {
|
|
218
|
+
const availableFields: { [key: string]: HTMLInputElement|HTMLSelectElement } = {};
|
|
219
219
|
for (const [fieldName, selector] of Object.entries(this.fieldsToListenOn)) {
|
|
220
220
|
const selectorCandidates = [
|
|
221
221
|
'[data-formengine-input-name="' + selector + '"]',
|
|
222
222
|
// Also check for fields, which do not point to a hidden input field (e.g. "input type=hidden", readOnly, or select fields)
|
|
223
223
|
'[name="' + selector + '"]'
|
|
224
224
|
];
|
|
225
|
-
let field:
|
|
225
|
+
let field: HTMLInputElement|HTMLSelectElement;
|
|
226
226
|
let usedSelector = '';
|
|
227
|
-
selectorCandidates.some((testSelector
|
|
228
|
-
field = document.querySelector(testSelector)
|
|
227
|
+
selectorCandidates.some((testSelector) => {
|
|
228
|
+
field = document.querySelector<HTMLInputElement|HTMLSelectElement>(testSelector);
|
|
229
229
|
usedSelector = testSelector;
|
|
230
230
|
return field !== null;
|
|
231
231
|
});
|
|
@@ -44,7 +44,7 @@ export class ResultContainer extends LitElement {
|
|
|
44
44
|
if (this.results.length === 0) {
|
|
45
45
|
content = html`<div class="alert alert-info">${labels.get('search.no_records_found')}</div>`;
|
|
46
46
|
} else {
|
|
47
|
-
content = html`${this.results.map((result
|
|
47
|
+
content = html`${this.results.map((result) => this.renderResultItem(result))}`;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -73,7 +73,7 @@ export class TableWizardElement extends LitElement {
|
|
|
73
73
|
const textarea: HTMLTextAreaElement = document.querySelector(this.selectorData);
|
|
74
74
|
const table: string[][] = [];
|
|
75
75
|
|
|
76
|
-
textarea.value.split('\n').forEach((row
|
|
76
|
+
textarea.value.split('\n').forEach((row) => {
|
|
77
77
|
if (row !== '') {
|
|
78
78
|
if(this.enclosure) {
|
|
79
79
|
row = row.replace(new RegExp(this.enclosure, 'g'), '');
|
|
@@ -167,7 +167,7 @@ export class TableWizardElement extends LitElement {
|
|
|
167
167
|
|
|
168
168
|
private renderTemplate(): TemplateResult {
|
|
169
169
|
this.provideMinimalTable();
|
|
170
|
-
const colIndexes = Object.keys(this.firstRow).map((item
|
|
170
|
+
const colIndexes = Object.keys(this.firstRow).map((item) => parseInt(item, 10));
|
|
171
171
|
const lastColIndex = colIndexes[colIndexes.length - 1];
|
|
172
172
|
const lastRowIndex = this.table.length - 1;
|
|
173
173
|
|
|
@@ -176,7 +176,7 @@ export class TableWizardElement extends LitElement {
|
|
|
176
176
|
<table class="table table-center">
|
|
177
177
|
<thead>
|
|
178
178
|
<th>${this.renderTypeButton()}</th>
|
|
179
|
-
${colIndexes.map((colIndex
|
|
179
|
+
${colIndexes.map((colIndex) => html`
|
|
180
180
|
<th>${this.renderColButtons(colIndex, lastColIndex)}</th>
|
|
181
181
|
`)}
|
|
182
182
|
</thead>
|
|
@@ -184,7 +184,7 @@ export class TableWizardElement extends LitElement {
|
|
|
184
184
|
${this.table.map((row: string[], rowIndex: number) => html`
|
|
185
185
|
<tr>
|
|
186
186
|
<td>${this.renderRowButtons(rowIndex, lastRowIndex)}</td>
|
|
187
|
-
${row.map((value
|
|
187
|
+
${row.map((value, colIndex) => html`
|
|
188
188
|
<td>${this.renderDataElement(value, rowIndex, colIndex)}</td>
|
|
189
189
|
`)}
|
|
190
190
|
</tr>
|
|
@@ -57,9 +57,9 @@ export class AjaxDispatcher {
|
|
|
57
57
|
throw 'Undefined endpoint for route "' + routeName + '"';
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
public send(request: AjaxRequest, params: Array<string>): Promise<
|
|
61
|
-
const sentRequest = request.post(this.createRequestBody(params)).then(async (response: AjaxResponse): Promise<
|
|
62
|
-
return this.processResponse(await response.resolve());
|
|
60
|
+
public send<T extends AjaxDispatcherResponse = AjaxDispatcherResponse>(request: AjaxRequest, params: Array<string>): Promise<T> {
|
|
61
|
+
const sentRequest = request.post(this.createRequestBody(params)).then(async (response: AjaxResponse): Promise<T> => {
|
|
62
|
+
return this.processResponse(await response.resolve()) as T;
|
|
63
63
|
});
|
|
64
64
|
sentRequest.catch((reason: Error): void => {
|
|
65
65
|
if (reason instanceof DOMException && reason.name === 'AbortError') {
|
|
@@ -65,7 +65,7 @@ export default class FormEngineValidation {
|
|
|
65
65
|
*/
|
|
66
66
|
public static initialize(formEngine: typeof FormEngine): void {
|
|
67
67
|
formEngineInstance = formEngine;
|
|
68
|
-
formEngineInstance.formElement.querySelectorAll('.' + FormEngineValidation.errorClass).forEach((e
|
|
68
|
+
formEngineInstance.formElement.querySelectorAll<HTMLElement>('.' + FormEngineValidation.errorClass).forEach((e) => e.classList.remove(FormEngineValidation.errorClass));
|
|
69
69
|
|
|
70
70
|
// Initialize input fields
|
|
71
71
|
FormEngineValidation.initializeInputFields();
|
|
@@ -91,7 +91,7 @@ export default class FormEngineValidation {
|
|
|
91
91
|
* Initialize all input fields
|
|
92
92
|
*/
|
|
93
93
|
public static initializeInputFields(): void {
|
|
94
|
-
formEngineInstance.formElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((visibleField
|
|
94
|
+
formEngineInstance.formElement.querySelectorAll<FormEngineFieldElement>(FormEngineValidation.inputSelector).forEach((visibleField): void => {
|
|
95
95
|
// ignore fields which already have been initialized
|
|
96
96
|
if ('formengineInputInitialized' in visibleField.dataset) {
|
|
97
97
|
return;
|
|
@@ -500,7 +500,7 @@ export default class FormEngineValidation {
|
|
|
500
500
|
*/
|
|
501
501
|
public static validate(section?: Element): void {
|
|
502
502
|
if (typeof section === 'undefined' || section instanceof Document) {
|
|
503
|
-
formEngineInstance.formElement.querySelectorAll(FormEngineValidation.markerSelector + ', [role="tablist"] > .nav-item').forEach((tabMenuItem
|
|
503
|
+
formEngineInstance.formElement.querySelectorAll<HTMLElement>(FormEngineValidation.markerSelector + ', [role="tablist"] > .nav-item').forEach((tabMenuItem): void => {
|
|
504
504
|
tabMenuItem.classList.remove(FormEngineValidation.validationErrorClass);
|
|
505
505
|
});
|
|
506
506
|
}
|
|
@@ -568,8 +568,8 @@ export default class FormEngineValidation {
|
|
|
568
568
|
* Find tab by field and mark it as has-validation-error
|
|
569
569
|
*/
|
|
570
570
|
public static markParentTab(element: FormEngineFieldElement, isValid: boolean): void {
|
|
571
|
-
const panes = DomHelper.parents(element, '.tab-pane');
|
|
572
|
-
panes.forEach((pane
|
|
571
|
+
const panes = DomHelper.parents<HTMLElement>(element, '.tab-pane');
|
|
572
|
+
panes.forEach((pane): void => {
|
|
573
573
|
if (isValid) {
|
|
574
574
|
// If incoming element is valid, check for errors in the same sheet
|
|
575
575
|
isValid = pane.querySelector('.has-error') === null;
|
|
@@ -631,3 +631,9 @@ export default class FormEngineValidation {
|
|
|
631
631
|
});
|
|
632
632
|
}
|
|
633
633
|
}
|
|
634
|
+
|
|
635
|
+
declare global {
|
|
636
|
+
interface HTMLElementEventMap {
|
|
637
|
+
't3-formengine-postfieldvalidation': CustomEvent<PostValidationEvent>;
|
|
638
|
+
}
|
|
639
|
+
}
|
package/backend/form-engine.ts
CHANGED
|
@@ -65,7 +65,7 @@ const enum FormAction {
|
|
|
65
65
|
saveAndAddRecord = '_savedokaddrecord',
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
type OnChangeFieldHandlerCallback = (data:
|
|
68
|
+
type OnChangeFieldHandlerCallback = (data: never, e: Event) => void;
|
|
69
69
|
type PreviewActionCallback = (targetName: string, previewUrl: string, $actionElement: JQuery, modal: ModalElement) => void;
|
|
70
70
|
type NewActionCallback = (targetName: string, $actionElement: JQuery) => void;
|
|
71
71
|
type DuplicateActionCallback = (targetName: string, $actionElement: JQuery) => void;
|
|
@@ -236,7 +236,7 @@ export default (function() {
|
|
|
236
236
|
return Modal.advanced({
|
|
237
237
|
type: Modal.types.iframe,
|
|
238
238
|
content: FormEngine.browserUrl + '&' + (new URLSearchParams(queryParams)).toString(),
|
|
239
|
-
size: Modal.sizes.
|
|
239
|
+
size: Modal.sizes.full
|
|
240
240
|
});
|
|
241
241
|
};
|
|
242
242
|
|
|
@@ -318,7 +318,7 @@ export default (function() {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
if (reenableOptions && typeof optionEl !== 'undefined') {
|
|
321
|
-
optionEl.closest('select').querySelectorAll('[disabled]').forEach(function (disabledOption
|
|
321
|
+
optionEl.closest('select').querySelectorAll<HTMLOptionElement>('[disabled]').forEach(function (disabledOption) {
|
|
322
322
|
disabledOption.classList.remove('hidden');
|
|
323
323
|
disabledOption.disabled = false;
|
|
324
324
|
FormEngine.enableOptGroup(disabledOption);
|
|
@@ -394,7 +394,7 @@ export default (function() {
|
|
|
394
394
|
* @param {HTMLElement} originalFieldEl the hidden form field
|
|
395
395
|
*/
|
|
396
396
|
FormEngine.updateHiddenFieldValueFromSelect = function(selectFieldEl: HTMLSelectElement, originalFieldEl: HTMLSelectElement): void {
|
|
397
|
-
const selectedValues: string[] = Array.from(selectFieldEl.options).map((el
|
|
397
|
+
const selectedValues: string[] = Array.from(selectFieldEl.options).map((el) => el.value);
|
|
398
398
|
|
|
399
399
|
// make a comma separated list, if it is a multi-select
|
|
400
400
|
// set the values to the final hidden field
|
|
@@ -672,8 +672,8 @@ export default (function() {
|
|
|
672
672
|
}
|
|
673
673
|
};
|
|
674
674
|
|
|
675
|
-
const minlengthElements = document.querySelectorAll('[minlength]:not([data-input-type="datetimepicker"]):not(.t3js-charcounter-min-initialized)');
|
|
676
|
-
minlengthElements.forEach((field
|
|
675
|
+
const minlengthElements = document.querySelectorAll<HTMLInputElement|HTMLTextAreaElement>('[minlength]:not([data-input-type="datetimepicker"]):not(.t3js-charcounter-min-initialized)');
|
|
676
|
+
minlengthElements.forEach((field) => {
|
|
677
677
|
field.addEventListener('focus', (event) => {
|
|
678
678
|
const minCharacterCountLeft = FormEngine.getMinCharacterLeftCount(field);
|
|
679
679
|
if (minCharacterCountLeft > 0) {
|
|
@@ -720,7 +720,7 @@ export default (function() {
|
|
|
720
720
|
* Initialize input / text field "null" checkbox CSS overlay if no placeholder is set.
|
|
721
721
|
*/
|
|
722
722
|
FormEngine.initializeNullNoPlaceholderCheckboxes = function(): void {
|
|
723
|
-
document.querySelectorAll('.t3-form-field-eval-null-checkbox').forEach((el
|
|
723
|
+
document.querySelectorAll<HTMLElement>('.t3-form-field-eval-null-checkbox').forEach((el) => {
|
|
724
724
|
// Add disabled class to "t3js-formengine-field-item" if the null checkbox is NOT set,
|
|
725
725
|
// This activates a CSS overlay "disabling" the input field and everything around.
|
|
726
726
|
const checkbox = el.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
|
@@ -735,7 +735,7 @@ export default (function() {
|
|
|
735
735
|
* Initialize input / text field "null" checkbox placeholder / real field if placeholder is set.
|
|
736
736
|
*/
|
|
737
737
|
FormEngine.initializeNullWithPlaceholderCheckboxes = function(): void {
|
|
738
|
-
document.querySelectorAll('.t3js-form-field-eval-null-placeholder-checkbox').forEach((el
|
|
738
|
+
document.querySelectorAll<HTMLElement>('.t3js-form-field-eval-null-placeholder-checkbox').forEach((el) => {
|
|
739
739
|
FormEngine.toggleCheckboxField(el.querySelector('input[type="checkbox"]'), false);
|
|
740
740
|
});
|
|
741
741
|
};
|
|
@@ -787,7 +787,7 @@ export default (function() {
|
|
|
787
787
|
* Disable the input field on load if localization state selector is set to "parent" or "source"
|
|
788
788
|
*/
|
|
789
789
|
FormEngine.initializeLocalizationStateSelector = function(): void {
|
|
790
|
-
document.querySelectorAll('.t3js-l10n-state-container').forEach((el
|
|
790
|
+
document.querySelectorAll<HTMLElement>('.t3js-l10n-state-container').forEach((el) => {
|
|
791
791
|
const input = el.closest('.t3js-formengine-field-item')?.querySelector('[data-formengine-input-name]') as HTMLInputElement|null;
|
|
792
792
|
if (input === undefined || input === null) {
|
|
793
793
|
return;
|
|
@@ -1011,7 +1011,7 @@ export default (function() {
|
|
|
1011
1011
|
items.forEach((item) => {
|
|
1012
1012
|
const handler = onFieldChangeHandlers.get(item.name);
|
|
1013
1013
|
if (handler instanceof Function) {
|
|
1014
|
-
handler.call(null, item.data || null, evt);
|
|
1014
|
+
handler.call(null, (item.data || null) as never, evt);
|
|
1015
1015
|
}
|
|
1016
1016
|
});
|
|
1017
1017
|
};
|
|
@@ -1033,8 +1033,12 @@ export default (function() {
|
|
|
1033
1033
|
};
|
|
1034
1034
|
|
|
1035
1035
|
/**
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1036
|
+
* Releases the doc header button bar, which is rendered "inert" as long as the
|
|
1037
|
+
* ui block is shown, to prevent unintended operations while FormEngine is loading.
|
|
1038
|
+
* Called after FormEngine initialization is complete.
|
|
1039
|
+
*
|
|
1040
|
+
* The block is applied to the button bar as a whole, never to the single buttons,
|
|
1041
|
+
* so that the disabled state of individual buttons remains untouched.
|
|
1038
1042
|
*/
|
|
1039
1043
|
FormEngine.enableDocHeaderButtons = function(): void {
|
|
1040
1044
|
const docHeaderBar = document.querySelector('.t3js-module-docheader-buttons');
|
|
@@ -1042,16 +1046,8 @@ export default (function() {
|
|
|
1042
1046
|
return;
|
|
1043
1047
|
}
|
|
1044
1048
|
|
|
1045
|
-
|
|
1046
|
-
docHeaderBar.
|
|
1047
|
-
if (element instanceof HTMLButtonElement || element instanceof HTMLInputElement) {
|
|
1048
|
-
element.disabled = false;
|
|
1049
|
-
} else if (element instanceof HTMLAnchorElement) {
|
|
1050
|
-
// For anchor elements - remove TYPO3 disabled state
|
|
1051
|
-
element.classList.remove('disabled');
|
|
1052
|
-
element.removeAttribute('aria-disabled');
|
|
1053
|
-
}
|
|
1054
|
-
});
|
|
1049
|
+
docHeaderBar.removeAttribute('inert');
|
|
1050
|
+
docHeaderBar.removeAttribute('aria-busy');
|
|
1055
1051
|
};
|
|
1056
1052
|
|
|
1057
1053
|
/**
|
|
@@ -1164,7 +1160,7 @@ export default (function() {
|
|
|
1164
1160
|
}
|
|
1165
1161
|
}
|
|
1166
1162
|
const contentElement = document.createElement('p');
|
|
1167
|
-
contentStrings.forEach((item
|
|
1163
|
+
contentStrings.forEach((item, i): void => {
|
|
1168
1164
|
contentElement.append(item);
|
|
1169
1165
|
if (i !== contentStrings.length - 1) {
|
|
1170
1166
|
contentElement.append(document.createElement('br'));
|
|
@@ -228,7 +228,7 @@ class GlobalEventHandler {
|
|
|
228
228
|
private assignHTMLFormChildElementValue(element: HTMLElement, value: string): void {
|
|
229
229
|
const type: string = element.getAttribute('type');
|
|
230
230
|
if (element instanceof HTMLSelectElement) {
|
|
231
|
-
Array.from(element.options).some((option
|
|
231
|
+
Array.from(element.options).some((option, index) => {
|
|
232
232
|
if (option.value === value) {
|
|
233
233
|
element.selectedIndex = index;
|
|
234
234
|
return true;
|
|
@@ -34,7 +34,7 @@ export class HotkeyStruct {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
public static fromHotkey(hotkey: string[]): HotkeyStruct {
|
|
37
|
-
const nonModifierCodes = hotkey.filter((hotkeyPart
|
|
37
|
+
const nonModifierCodes = hotkey.filter((hotkeyPart) => !Object.values<string>(ModifierKeys).includes(hotkeyPart));
|
|
38
38
|
if (nonModifierCodes.length > 1) {
|
|
39
39
|
throw new Error('Cannot create HotkeyStruct with more than one non-modifier key, "' + nonModifierCodes.join('+') + '" given.');
|
|
40
40
|
}
|
package/backend/hotkeys.ts
CHANGED
|
@@ -60,12 +60,12 @@ class Hotkeys {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
public register(hotkey: Hotkey, handler: (e: KeyboardEvent) => void, options: Partial<Options> = {}): void {
|
|
63
|
-
if (hotkey.filter((hotkeyPart
|
|
63
|
+
if (hotkey.filter((hotkeyPart) => !Object.values<string>(ModifierKeys).includes(hotkeyPart)).length === 0) {
|
|
64
64
|
throw new Error('Attempted to register hotkey "' + hotkey.join('+') + '" without a non-modifier key.');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Normalize trigger
|
|
68
|
-
hotkey = hotkey.map((h
|
|
68
|
+
hotkey = hotkey.map((h) => h.toLowerCase());
|
|
69
69
|
|
|
70
70
|
const mergedConfiguration: Options = { ...this.defaultOptions, ...options };
|
|
71
71
|
if (!this.scopedHotkeyMap.has(mergedConfiguration.scope)) {
|
|
@@ -161,7 +161,7 @@ class Hotkeys {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// The standard requires to have modifier keys to be at first
|
|
164
|
-
parts.sort((a
|
|
164
|
+
parts.sort((a, b): number => {
|
|
165
165
|
const aIsModifierKey = Object.values<string>(ModifierKeys).includes(a);
|
|
166
166
|
const bIsModifierKey = Object.values<string>(ModifierKeys).includes(b);
|
|
167
167
|
|
|
@@ -280,7 +280,7 @@ class ImageManipulation {
|
|
|
280
280
|
/**
|
|
281
281
|
* Assign EventListener to cropVariantTriggers
|
|
282
282
|
*/
|
|
283
|
-
this.cropVariantTriggers.forEach((el
|
|
283
|
+
this.cropVariantTriggers.forEach((el) =>
|
|
284
284
|
el.addEventListener('click', (e: Event): void => {
|
|
285
285
|
/**
|
|
286
286
|
* Is the current cropVariantTrigger is active, bail out.
|
|
@@ -341,8 +341,8 @@ class ImageManipulation {
|
|
|
341
341
|
new RegularEvent('click', (): void => this.openPreview(this.data))
|
|
342
342
|
.delegateTo(this.currentModal, 'button[name=preview]');
|
|
343
343
|
} else {
|
|
344
|
-
this.currentModal.querySelectorAll('button[name=preview]')
|
|
345
|
-
.forEach((previewButton
|
|
344
|
+
this.currentModal.querySelectorAll<HTMLElement>('button[name=preview]')
|
|
345
|
+
.forEach((previewButton) => previewButton.style.display = 'none');
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
/**
|
|
@@ -400,8 +400,8 @@ class ImageManipulation {
|
|
|
400
400
|
this.syncEnabled = target.checked;
|
|
401
401
|
BrowserSession.set('sync-active-' + imageUid, this.syncEnabled ? 'true' : 'false');
|
|
402
402
|
if (this.syncEnabled) {
|
|
403
|
-
this.currentModal.querySelectorAll('.panel-button[data-exclude-from-sync="true"]')
|
|
404
|
-
.forEach((e
|
|
403
|
+
this.currentModal.querySelectorAll<HTMLElement>('.panel-button[data-exclude-from-sync="true"]')
|
|
404
|
+
.forEach((e) => e.setAttribute('disabled', 'disabled'));
|
|
405
405
|
|
|
406
406
|
// if the currently selected crop variant is now disabled, open the first available one.
|
|
407
407
|
const isDisabled = this.currentModal.querySelector('.panel-button[data-crop-variant-id="' + this.currentCropVariant.id + '"][data-exclude-from-sync="true"]') !== null;
|
|
@@ -412,8 +412,8 @@ class ImageManipulation {
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
} else {
|
|
415
|
-
this.currentModal.querySelectorAll('.panel-button[data-exclude-from-sync]')
|
|
416
|
-
.forEach((e
|
|
415
|
+
this.currentModal.querySelectorAll<HTMLElement>('.panel-button[data-exclude-from-sync]')
|
|
416
|
+
.forEach((e) => e.removeAttribute('disabled'));
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
).delegateTo(this.currentModal, '#sync-crop-variants');
|
|
@@ -446,7 +446,7 @@ class ImageManipulation {
|
|
|
446
446
|
this.imageOriginalSizeFactor = parseInt(image.dataset.originalWidth, 10) / imageData.naturalWidth;
|
|
447
447
|
|
|
448
448
|
// iterate over the crop variants and set up their respective preview
|
|
449
|
-
this.cropVariantTriggers.forEach((elem
|
|
449
|
+
this.cropVariantTriggers.forEach((elem): void => {
|
|
450
450
|
const cropVariantId: string = elem.dataset.cropVariantId;
|
|
451
451
|
const cropArea: Area = this.convertRelativeToAbsoluteCropArea(
|
|
452
452
|
this.data[cropVariantId].cropArea,
|
|
@@ -507,9 +507,9 @@ class ImageManipulation {
|
|
|
507
507
|
|
|
508
508
|
if (this.syncAvailable && this.syncEnabled) {
|
|
509
509
|
const data = this.data;
|
|
510
|
-
Object.keys(this.data).forEach((cropVariant
|
|
510
|
+
Object.keys(this.data).forEach((cropVariant): void => {
|
|
511
511
|
let trigger: HTMLElement = null;
|
|
512
|
-
this.cropVariantTriggers.forEach((e
|
|
512
|
+
this.cropVariantTriggers.forEach((e) => {
|
|
513
513
|
if (e.dataset.cropVariantId === cropVariant) {
|
|
514
514
|
trigger = e;
|
|
515
515
|
}
|
|
@@ -540,9 +540,9 @@ class ImageManipulation {
|
|
|
540
540
|
|
|
541
541
|
if (this.syncAvailable && this.syncEnabled) {
|
|
542
542
|
const data = this.data;
|
|
543
|
-
Object.keys(this.data).forEach((cropVariant
|
|
543
|
+
Object.keys(this.data).forEach((cropVariant): void => {
|
|
544
544
|
let trigger: HTMLElement = null;
|
|
545
|
-
this.cropVariantTriggers.forEach((e
|
|
545
|
+
this.cropVariantTriggers.forEach((e) => {
|
|
546
546
|
if (e.dataset.cropVariantId === cropVariant) {
|
|
547
547
|
trigger = e;
|
|
548
548
|
}
|
|
@@ -579,8 +579,8 @@ class ImageManipulation {
|
|
|
579
579
|
this.setAspectRatio(selectedRatio);
|
|
580
580
|
this.setCropArea(temp.cropArea);
|
|
581
581
|
this.currentCropVariant = Object.assign({}, temp, cropVariant);
|
|
582
|
-
this.cropBox?.querySelectorAll(this.coverAreaSelector)?.forEach((el
|
|
583
|
-
this.cropBox?.querySelectorAll(this.focusAreaSelector)?.forEach((el
|
|
582
|
+
this.cropBox?.querySelectorAll<HTMLElement>(this.coverAreaSelector)?.forEach((el) => el.remove());
|
|
583
|
+
this.cropBox?.querySelectorAll<HTMLElement>(this.focusAreaSelector)?.forEach((el) => el.remove());
|
|
584
584
|
|
|
585
585
|
// check if new cropVariant has focusArea
|
|
586
586
|
if (cropVariant.focusArea) {
|
|
@@ -655,7 +655,7 @@ class ImageManipulation {
|
|
|
655
655
|
* @param {Array<Area>} coverAreas - An array of areas to construct the cover area elements from
|
|
656
656
|
*/
|
|
657
657
|
private initCoverAreas(container: HTMLElement, coverAreas: Area[]): void {
|
|
658
|
-
coverAreas.forEach((coverArea
|
|
658
|
+
coverAreas.forEach((coverArea): void => {
|
|
659
659
|
const styles = {
|
|
660
660
|
height: ImageManipulation.toCssPercent(coverArea.height),
|
|
661
661
|
left: ImageManipulation.toCssPercent(coverArea.x),
|
|
@@ -782,7 +782,7 @@ class ImageManipulation {
|
|
|
782
782
|
return false;
|
|
783
783
|
}
|
|
784
784
|
return coverAreas
|
|
785
|
-
.some((coverArea
|
|
785
|
+
.some((coverArea): boolean => {
|
|
786
786
|
return focusArea.x < coverArea.x + coverArea.width
|
|
787
787
|
&& coverArea.x < focusArea.x + focusArea.width
|
|
788
788
|
&& focusArea.y < coverArea.y + coverArea.height
|
|
@@ -831,7 +831,7 @@ class ImageManipulation {
|
|
|
831
831
|
const imageData: Cropper.ImageData = this.cropper.getImageData();
|
|
832
832
|
|
|
833
833
|
// iterate over the crop variants and set up their respective preview
|
|
834
|
-
Object.keys(data).forEach((cropVariantId
|
|
834
|
+
Object.keys(data).forEach((cropVariantId) => {
|
|
835
835
|
const cropVariant: CropVariant = data[cropVariantId];
|
|
836
836
|
const cropData: Area = this.convertRelativeToAbsoluteCropArea(cropVariant.cropArea, imageData);
|
|
837
837
|
|
|
@@ -956,8 +956,8 @@ class ImageManipulation {
|
|
|
956
956
|
|
|
957
957
|
private renderElements(template: TemplateResult, target: HTMLElement, selector?: string): HTMLElement|null {
|
|
958
958
|
const nodes = renderNodes(template);
|
|
959
|
-
const elements = Array.from(nodes).filter((node
|
|
960
|
-
elements.forEach((element
|
|
959
|
+
const elements = Array.from(nodes).filter((node): node is HTMLElement => node instanceof HTMLElement);
|
|
960
|
+
elements.forEach((element) => target.appendChild(element));
|
|
961
961
|
return selector ? target.querySelector(selector) : null;
|
|
962
962
|
}
|
|
963
963
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the TYPO3 CMS project.
|
|
3
|
+
*
|
|
4
|
+
* It is free software; you can redistribute it and/or modify it under
|
|
5
|
+
* the terms of the GNU General Public License, either version 2
|
|
6
|
+
* of the License, or any later version.
|
|
7
|
+
*
|
|
8
|
+
* For the full copyright and license information, please read the
|
|
9
|
+
* LICENSE.txt file that was distributed with this source code.
|
|
10
|
+
*
|
|
11
|
+
* The TYPO3 project - inspiring people to share!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Module: @typo3/backend/layout-module/column-restriction
|
|
16
|
+
*
|
|
17
|
+
* Client-side mirror of the server-side backend layout colPos restriction
|
|
18
|
+
* (\TYPO3\CMS\Backend\View\BackendLayoutView::isCTypeAllowedInColPosByPage()).
|
|
19
|
+
* Used by drag-drop and paste in the page layout module to avoid offering
|
|
20
|
+
* actions that the DataHandler would reject anyway.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
function toList(value: string | undefined): string[] {
|
|
26
|
+
return (value ?? '')
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((item: string): string => item.trim())
|
|
29
|
+
.filter((item: string): boolean => item !== '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Evaluates whether the given CType is allowed in the column described by the
|
|
34
|
+
* "data-allowed-content-types" / "data-disallowed-content-types" attributes.
|
|
35
|
+
* Mirrors the server-side logic: disallowed wins, an empty allow list allows all.
|
|
36
|
+
*/
|
|
37
|
+
export function isContentTypeAllowedInColumn(cType: string, columnElement: HTMLElement | null): boolean {
|
|
38
|
+
if (columnElement === null) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const disallowed = toList(columnElement.dataset.disallowedContentTypes);
|
|
43
|
+
if (disallowed.includes(cType)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const allowed = toList(columnElement.dataset.allowedContentTypes);
|
|
48
|
+
if (allowed.length > 0 && !allowed.includes(cType)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolves the column of the given element (nearest ancestor carrying a colPos)
|
|
57
|
+
* and evaluates whether the CType is allowed there.
|
|
58
|
+
*/
|
|
59
|
+
export function isContentTypeAllowedForTarget(cType: string, target: HTMLElement): boolean {
|
|
60
|
+
return isContentTypeAllowedInColumn(cType, target.closest('[data-colpos]'));
|
|
61
|
+
}
|
|
@@ -22,6 +22,7 @@ import RegularEvent from '@typo3/core/event/regular-event';
|
|
|
22
22
|
import { DataTransferTypes } from '@typo3/backend/enum/data-transfer-types';
|
|
23
23
|
import BroadcastService from '@typo3/backend/broadcast-service';
|
|
24
24
|
import { BroadcastMessage } from '@typo3/backend/broadcast-message';
|
|
25
|
+
import { isContentTypeAllowedInColumn } from '@typo3/backend/layout-module/column-restriction';
|
|
25
26
|
import type ResponseInterface from '../ajax-data-handler/response-interface';
|
|
26
27
|
import type { DragDropThumbnail, DragTooltipMetadata } from '@typo3/backend/drag-tooltip';
|
|
27
28
|
import DragDropUtility from '@typo3/backend/utility/drag-drop-utility';
|
|
@@ -36,6 +37,7 @@ export type ContentElementDragDropData = {
|
|
|
36
37
|
pid: number;
|
|
37
38
|
uid: number;
|
|
38
39
|
language: number;
|
|
40
|
+
cType: string;
|
|
39
41
|
content: string;
|
|
40
42
|
moveElementUrl: string;
|
|
41
43
|
};
|
|
@@ -54,6 +56,8 @@ enum Classes {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
class DragDrop {
|
|
59
|
+
private draggedContentType: string = '';
|
|
60
|
+
|
|
57
61
|
constructor() {
|
|
58
62
|
DocumentService.ready().then((): void => {
|
|
59
63
|
this.initialize();
|
|
@@ -116,10 +120,13 @@ class DragDrop {
|
|
|
116
120
|
protected onDragStart(event: DragEvent, target: HTMLElement): void {
|
|
117
121
|
const content = target.closest(Identifiers.content) as HTMLElement;
|
|
118
122
|
|
|
123
|
+
this.draggedContentType = content.dataset.ctype ?? '';
|
|
124
|
+
|
|
119
125
|
event.dataTransfer.setData(DataTransferTypes.content, JSON.stringify({
|
|
120
126
|
pid: this.getCurrentPageId(),
|
|
121
127
|
uid: parseInt(content.dataset.uid, 10),
|
|
122
128
|
language: parseInt(content.dataset.languageUid, 10),
|
|
129
|
+
cType: this.draggedContentType,
|
|
123
130
|
content: content.outerHTML,
|
|
124
131
|
moveElementUrl: content.dataset.moveElementUrl,
|
|
125
132
|
} as ContentElementDragDropData));
|
|
@@ -133,6 +140,7 @@ class DragDrop {
|
|
|
133
140
|
}
|
|
134
141
|
|
|
135
142
|
protected onDragEnd(): void {
|
|
143
|
+
this.draggedContentType = '';
|
|
136
144
|
this.hideDropZones();
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -176,6 +184,7 @@ class DragDrop {
|
|
|
176
184
|
|
|
177
185
|
// the dragged elements language uid
|
|
178
186
|
let language = contentElementDragDropData.language;
|
|
187
|
+
// Keep in sync with LanguageMarker::ALL_LANGUAGES on the PHP side.
|
|
179
188
|
if (language !== -1) {
|
|
180
189
|
// new elements language must be the same as the column the element is dropped in if element is not -1
|
|
181
190
|
language = parseInt((dropContainer.closest('[data-language-uid]') as HTMLElement).dataset.languageUid, 10);
|
|
@@ -305,9 +314,9 @@ class DragDrop {
|
|
|
305
314
|
iconIdentifier = contentElementIcon.dataset.identifier;
|
|
306
315
|
}
|
|
307
316
|
|
|
308
|
-
const contentImagePreviews = contentElement.querySelectorAll('.preview-thumbnails-element-image img');
|
|
317
|
+
const contentImagePreviews = contentElement.querySelectorAll<HTMLImageElement>('.preview-thumbnails-element-image img');
|
|
309
318
|
if (contentImagePreviews.length > 0) {
|
|
310
|
-
contentImagePreviews.forEach((image
|
|
319
|
+
contentImagePreviews.forEach((image): void => {
|
|
311
320
|
thumbnails.push({
|
|
312
321
|
src: image.src,
|
|
313
322
|
height: image.height,
|
|
@@ -334,7 +343,10 @@ class DragDrop {
|
|
|
334
343
|
}
|
|
335
344
|
|
|
336
345
|
protected showDropZones(): void {
|
|
337
|
-
document.querySelectorAll(Identifiers.dropZone).forEach((element
|
|
346
|
+
document.querySelectorAll<HTMLElement>(Identifiers.dropZone).forEach((element): void => {
|
|
347
|
+
if (!isContentTypeAllowedInColumn(this.draggedContentType, element.closest('[data-colpos]'))) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
338
350
|
element.hidden = false;
|
|
339
351
|
const addContentButton = element.parentElement.querySelector(Identifiers.addContent) as HTMLElement;
|
|
340
352
|
if (addContentButton !== null) {
|
|
@@ -345,7 +357,7 @@ class DragDrop {
|
|
|
345
357
|
}
|
|
346
358
|
|
|
347
359
|
protected hideDropZones(): void {
|
|
348
|
-
document.querySelectorAll(Identifiers.dropZone).forEach((element
|
|
360
|
+
document.querySelectorAll<HTMLElement>(Identifiers.dropZone).forEach((element): void => {
|
|
349
361
|
element.hidden = true;
|
|
350
362
|
const addContentButton = element.parentElement.querySelector(Identifiers.addContent) as HTMLElement;
|
|
351
363
|
if (addContentButton !== null) {
|
|
@@ -24,18 +24,21 @@ import Severity from '../severity';
|
|
|
24
24
|
import '@typo3/backend/element/icon-element';
|
|
25
25
|
import { SeverityEnum } from '../enum/severity';
|
|
26
26
|
import RegularEvent from '@typo3/core/event/regular-event';
|
|
27
|
+
import { isContentTypeAllowedForTarget } from '@typo3/backend/layout-module/column-restriction';
|
|
27
28
|
import type ResponseInterface from '../ajax-data-handler/response-interface';
|
|
28
29
|
import layoutLabels from '~labels/backend.layout';
|
|
29
30
|
|
|
30
31
|
type PasteOptions = {
|
|
31
32
|
itemOnClipboardUid: number;
|
|
32
33
|
itemOnClipboardTitle: string;
|
|
34
|
+
itemOnClipboardCType: string;
|
|
33
35
|
copyMode: string;
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
class Paste {
|
|
37
39
|
private readonly itemOnClipboardUid: number = 0;
|
|
38
40
|
private readonly itemOnClipboardTitle: string = '';
|
|
41
|
+
private readonly itemOnClipboardCType: string = '';
|
|
39
42
|
private readonly copyMode: string = '';
|
|
40
43
|
private readonly elementIdentifier: string = '.t3js-page-ce';
|
|
41
44
|
private pasteAfterLinkTemplate: string = '';
|
|
@@ -47,6 +50,7 @@ class Paste {
|
|
|
47
50
|
constructor(args: PasteOptions) {
|
|
48
51
|
this.itemOnClipboardUid = args.itemOnClipboardUid;
|
|
49
52
|
this.itemOnClipboardTitle = args.itemOnClipboardTitle;
|
|
53
|
+
this.itemOnClipboardCType = args.itemOnClipboardCType;
|
|
50
54
|
this.copyMode = args.copyMode;
|
|
51
55
|
|
|
52
56
|
DocumentService.ready().then((): void => {
|
|
@@ -95,7 +99,10 @@ class Paste {
|
|
|
95
99
|
*/
|
|
96
100
|
private activatePasteIcons(): void {
|
|
97
101
|
if (this.pasteAfterLinkTemplate && this.pasteIntoLinkTemplate) {
|
|
98
|
-
document.querySelectorAll('.t3js-page-new-ce').forEach((el
|
|
102
|
+
document.querySelectorAll<HTMLElement>('.t3js-page-new-ce').forEach((el): void => {
|
|
103
|
+
if (!isContentTypeAllowedForTarget(this.itemOnClipboardCType, el)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
99
106
|
const template = el.parentElement.dataset.page ? this.pasteIntoLinkTemplate : this.pasteAfterLinkTemplate;
|
|
100
107
|
el.append(document.createRange().createContextualFragment(template));
|
|
101
108
|
});
|