typo3-typescript-sources 13.4.20 → 13.4.22
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 +8 -4
- package/.last-synced-tag +1 -1
- package/README.md +5 -1
- package/backend/date-time-picker.ts +9 -0
- package/backend/form-engine-link-browser-adapter.ts +5 -1
- package/backend/security/element/csp-reports.ts +83 -4
- package/backend/tree/tree.ts +14 -10
- package/install/renderable/language-packs.ts +1 -0
- package/package.json +2 -2
- package/rte-ckeditor/plugin/typo3-link.ts +33 -0
- package/workspaces/preview.ts +1 -0
- package/workspaces/workspaces.ts +3 -1
|
@@ -178,6 +178,10 @@ jobs:
|
|
|
178
178
|
cat > README.md << 'EOF'
|
|
179
179
|
# TYPO3 TypeScript Sources
|
|
180
180
|
|
|
181
|
+
[](https://github.com/maikschneider/typo3-typescript-sources/actions/workflows/sync-and-publish.yml)
|
|
182
|
+
[](https://www.npmjs.com/package/typo3-typescript-sources)
|
|
183
|
+
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
184
|
+
|
|
181
185
|
This repository contains the TypeScript sources from the TYPO3 Core, automatically extracted and published to npm.
|
|
182
186
|
|
|
183
187
|
## 📦 Installation
|
|
@@ -225,12 +229,12 @@ jobs:
|
|
|
225
229
|
Source: https://github.com/TYPO3/typo3/tree/${TYPO3_VERSION}
|
|
226
230
|
Path: Build/Sources/TypeScript/"
|
|
227
231
|
|
|
228
|
-
# Erstelle Tag
|
|
232
|
+
# Erstelle Tag (entferne v-Prefix falls vorhanden)
|
|
229
233
|
NPM_VERSION="${TYPO3_VERSION#v}"
|
|
230
|
-
git tag -a "
|
|
234
|
+
git tag -a "${NPM_VERSION}" -m "Release ${NPM_VERSION} from TYPO3 ${TYPO3_VERSION}"
|
|
231
235
|
|
|
232
236
|
git push origin main
|
|
233
|
-
git push origin "
|
|
237
|
+
git push origin "${NPM_VERSION}"
|
|
234
238
|
|
|
235
239
|
- name: Setup Node.js
|
|
236
240
|
uses: actions/setup-node@v4
|
|
@@ -254,7 +258,7 @@ jobs:
|
|
|
254
258
|
env:
|
|
255
259
|
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
|
|
256
260
|
with:
|
|
257
|
-
tag_name:
|
|
261
|
+
tag_name: ${{ needs.check-and-sync.outputs.typo3_version }}
|
|
258
262
|
release_name: TYPO3 ${{ needs.check-and-sync.outputs.typo3_version }}
|
|
259
263
|
body: |
|
|
260
264
|
Synchronized with TYPO3 Core ${{ needs.check-and-sync.outputs.typo3_version }}
|
package/.last-synced-tag
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v13.4.
|
|
1
|
+
v13.4.22
|
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# TYPO3 TypeScript Sources
|
|
2
2
|
|
|
3
|
+
[](https://github.com/maikschneider/typo3-typescript-sources/actions/workflows/sync-and-publish.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/typo3-typescript-sources)
|
|
5
|
+
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
6
|
+
|
|
3
7
|
This repository contains the TypeScript sources from the TYPO3 Core, automatically extracted and published to npm.
|
|
4
8
|
|
|
5
9
|
## 📦 Installation
|
|
@@ -12,7 +16,7 @@ npm install typo3-typescript-sources
|
|
|
12
16
|
|
|
13
17
|
- **Original Repository:** [TYPO3/typo3](https://github.com/TYPO3/typo3)
|
|
14
18
|
- **Source Path:** `Build/Sources/TypeScript/`
|
|
15
|
-
- **Current TYPO3 Version:** `v13.4.
|
|
19
|
+
- **Current TYPO3 Version:** `v13.4.22`
|
|
16
20
|
|
|
17
21
|
## 📝 License
|
|
18
22
|
|
|
@@ -185,6 +185,8 @@ class DateTimePicker {
|
|
|
185
185
|
// collides with using altInput – sigh.
|
|
186
186
|
self.altInput.id = self.input.id;
|
|
187
187
|
self.input.removeAttribute('id');
|
|
188
|
+
// Disable browser autofill to prevent interference with the datepicker
|
|
189
|
+
self.altInput.setAttribute('autocomplete', 'off');
|
|
188
190
|
self.altInput.clearable();
|
|
189
191
|
if (self.input.dataset.formengineInputName !== undefined) {
|
|
190
192
|
self.altInput.dataset.formengineDatepickerRealInputName = self.input.dataset.formengineInputName;
|
|
@@ -196,6 +198,13 @@ class DateTimePicker {
|
|
|
196
198
|
self.altInput.classList.toggle('has-error', !e.detail.isValid);
|
|
197
199
|
}
|
|
198
200
|
});
|
|
201
|
+
|
|
202
|
+
// Move the hidden input (self.input) to right after the clearable wrapper
|
|
203
|
+
// This prevents it from affecting :first-child CSS selectors while maintaining proper DOM order
|
|
204
|
+
const wrapper = self.altInput.closest('.form-control-clearable-wrapper');
|
|
205
|
+
if (wrapper !== null) {
|
|
206
|
+
wrapper.insertAdjacentElement('afterend', self.input);
|
|
207
|
+
}
|
|
199
208
|
}
|
|
200
209
|
},
|
|
201
210
|
onChange: (dates: Date[], currentDateString: string, self: flatpickr.Instance): void => {
|
|
@@ -72,9 +72,13 @@ export default (function() {
|
|
|
72
72
|
const attributeValues = LinkBrowser.getLinkAttributeValues();
|
|
73
73
|
// encode link on server
|
|
74
74
|
attributeValues.url = input;
|
|
75
|
+
const searchParams = new URLSearchParams();
|
|
76
|
+
for (const [key, value] of Object.entries(attributeValues)) {
|
|
77
|
+
searchParams.set(key, value);
|
|
78
|
+
}
|
|
75
79
|
|
|
76
80
|
(new AjaxRequest(TYPO3.settings.ajaxUrls.link_browser_encodetypolink))
|
|
77
|
-
.withQueryArguments(
|
|
81
|
+
.withQueryArguments(searchParams)
|
|
78
82
|
.get()
|
|
79
83
|
.then(async (response: AjaxResponse): Promise<void> => {
|
|
80
84
|
const data: Response = await response.resolve();
|
|
@@ -96,6 +96,7 @@ export class CspReports extends LitElement {
|
|
|
96
96
|
@state() suggestions: MutationSuggestion[] = [];
|
|
97
97
|
|
|
98
98
|
private peripheralEvent: RegularEvent;
|
|
99
|
+
private previouslyFocusedRow: HTMLElement | null = null;
|
|
99
100
|
|
|
100
101
|
public override connectedCallback(): void {
|
|
101
102
|
super.connectedCallback();
|
|
@@ -114,6 +115,17 @@ export class CspReports extends LitElement {
|
|
|
114
115
|
this.peripheralEvent?.release();
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
protected override updated(changedProperties: Map<string, any>): void {
|
|
119
|
+
super.updated(changedProperties);
|
|
120
|
+
if (changedProperties.has('selectedReport') && this.selectedReport !== null) {
|
|
121
|
+
// Focus the details panel when a report is selected
|
|
122
|
+
this.updateComplete.then(() => {
|
|
123
|
+
const detailsPanel = this.querySelector('#report-details') as HTMLElement;
|
|
124
|
+
detailsPanel?.focus();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
117
129
|
protected override createRenderRoot(): HTMLElement | ShadowRoot {
|
|
118
130
|
return this;
|
|
119
131
|
}
|
|
@@ -148,7 +160,11 @@ export class CspReports extends LitElement {
|
|
|
148
160
|
` : nothing}
|
|
149
161
|
${this.reports.map((report: SummarizedCspReport) => html`
|
|
150
162
|
<tr class=${classMap({ 'table-info': this.selectedReport === report })} data-mutation-group=${report.mutationHashes.join('-')}
|
|
151
|
-
@click=${() => this.selectReport(report)}
|
|
163
|
+
@click=${() => this.selectReport(report)}
|
|
164
|
+
@keydown=${(e: KeyboardEvent) => this.handleReportKeydown(e, report)}
|
|
165
|
+
tabindex="0"
|
|
166
|
+
role="button"
|
|
167
|
+
aria-label="${lll('label.showDetails', report.details.effectiveDirective, report.details.blockedUri) || 'Show details'}">
|
|
152
168
|
<td>${report.created}</td>
|
|
153
169
|
<td>${report.scope}</td>
|
|
154
170
|
<td>
|
|
@@ -218,7 +234,7 @@ export class CspReports extends LitElement {
|
|
|
218
234
|
const report = this.selectedReport;
|
|
219
235
|
return html`${report ? html`
|
|
220
236
|
<div class="infolist-info-record">
|
|
221
|
-
<div class="card mb-0">
|
|
237
|
+
<div class="card mb-0" tabindex="-1" id="report-details" @keydown=${(e: KeyboardEvent) => this.handleDetailsKeydown(e)}>
|
|
222
238
|
<div class="card-header">
|
|
223
239
|
<h3>${ lll('label.details') || 'Details'}</h3>
|
|
224
240
|
</div>
|
|
@@ -309,14 +325,75 @@ export class CspReports extends LitElement {
|
|
|
309
325
|
private selectReport(report: SummarizedCspReport): void {
|
|
310
326
|
this.suggestions = [];
|
|
311
327
|
if (report !== null && this.selectedReport !== report) {
|
|
328
|
+
// Store the currently focused element before changing selection
|
|
329
|
+
this.previouslyFocusedRow = document.activeElement as HTMLElement;
|
|
312
330
|
this.selectedReport = report;
|
|
313
331
|
this.invokeHandleReportAction(report)
|
|
314
332
|
.then((suggestions: MutationSuggestion[]) => this.suggestions = suggestions);
|
|
315
333
|
} else {
|
|
316
334
|
this.selectedReport = null;
|
|
335
|
+
// Restore focus to the previously focused row when closing
|
|
336
|
+
if (this.previouslyFocusedRow) {
|
|
337
|
+
this.updateComplete.then(() => {
|
|
338
|
+
this.previouslyFocusedRow?.focus();
|
|
339
|
+
this.previouslyFocusedRow = null;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private handleReportKeydown(event: KeyboardEvent, report: SummarizedCspReport): void {
|
|
346
|
+
// Handle Enter/Space to select report
|
|
347
|
+
if (event.key === 'Enter' || event.key === 'Space' || event.key === ' ') {
|
|
348
|
+
event.preventDefault();
|
|
349
|
+
this.selectReport(report);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Arrow key navigation
|
|
354
|
+
const currentRow = event.currentTarget as HTMLElement;
|
|
355
|
+
let targetRow: HTMLElement | null = null;
|
|
356
|
+
|
|
357
|
+
switch (event.key) {
|
|
358
|
+
case 'ArrowDown':
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
targetRow = currentRow.nextElementSibling as HTMLElement;
|
|
361
|
+
break;
|
|
362
|
+
case 'ArrowUp':
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
targetRow = currentRow.previousElementSibling as HTMLElement;
|
|
365
|
+
break;
|
|
366
|
+
case 'Home':
|
|
367
|
+
event.preventDefault();
|
|
368
|
+
targetRow = currentRow.parentElement?.firstElementChild as HTMLElement;
|
|
369
|
+
break;
|
|
370
|
+
case 'End':
|
|
371
|
+
event.preventDefault();
|
|
372
|
+
targetRow = currentRow.parentElement?.lastElementChild as HTMLElement;
|
|
373
|
+
break;
|
|
374
|
+
default:
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (targetRow && targetRow.hasAttribute('tabindex')) {
|
|
379
|
+
targetRow.focus();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private handleDetailsKeydown(event: KeyboardEvent): void {
|
|
384
|
+
if (event.key === 'Escape') {
|
|
385
|
+
event.preventDefault();
|
|
386
|
+
this.selectReport(null);
|
|
317
387
|
}
|
|
318
388
|
}
|
|
319
389
|
|
|
390
|
+
private focusFirstReportRow(): void {
|
|
391
|
+
this.updateComplete.then(() => {
|
|
392
|
+
const firstRow = this.querySelector('tbody tr[tabindex="0"]') as HTMLElement;
|
|
393
|
+
firstRow?.focus();
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
320
397
|
private selectScope(scope: string): void {
|
|
321
398
|
this.selectedScope = scope;
|
|
322
399
|
this.fetchReports();
|
|
@@ -363,14 +440,16 @@ export class CspReports extends LitElement {
|
|
|
363
440
|
(new AjaxRequest(this.controlUri))
|
|
364
441
|
.post({ action: 'muteReport', summaries: [report.summary] })
|
|
365
442
|
.then((response: AjaxResponse) => response.resolve('application/json'))
|
|
366
|
-
.then((response: CspReportUuids) => this.filterReports(...response.uuids))
|
|
443
|
+
.then((response: CspReportUuids) => this.filterReports(...response.uuids))
|
|
444
|
+
.then(() => this.focusFirstReportRow());
|
|
367
445
|
}
|
|
368
446
|
|
|
369
447
|
private invokeDeleteReportAction(report: SummarizedCspReport): void {
|
|
370
448
|
(new AjaxRequest(this.controlUri))
|
|
371
449
|
.post({ action: 'deleteReport', summaries: [report.summary] })
|
|
372
450
|
.then((response: AjaxResponse) => response.resolve('application/json'))
|
|
373
|
-
.then((response: CspReportUuids) => this.filterReports(...response.uuids))
|
|
451
|
+
.then((response: CspReportUuids) => this.filterReports(...response.uuids))
|
|
452
|
+
.then(() => this.focusFirstReportRow());
|
|
374
453
|
}
|
|
375
454
|
|
|
376
455
|
private invokeDeleteReportsAction(): void {
|
package/backend/tree/tree.ts
CHANGED
|
@@ -1117,22 +1117,26 @@ export class Tree extends LitElement {
|
|
|
1117
1117
|
|
|
1118
1118
|
protected createNodeContentLabel(node: TreeNodeInterface): TemplateResult
|
|
1119
1119
|
{
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
const labelNode = document.createElement('div');
|
|
1124
|
-
labelNode.textContent = label;
|
|
1125
|
-
label = labelNode.innerHTML;
|
|
1120
|
+
const label = (node.prefix || '') + node.name + (node.suffix || '');
|
|
1121
|
+
|
|
1122
|
+
let nodeContent: Array<TemplateResult|string> | string = label;
|
|
1126
1123
|
if (this.searchTerm) {
|
|
1127
1124
|
// Escape all meta characters of regular expressions: ( ) [ ] $ * + ? . { } / | ^ -
|
|
1128
|
-
|
|
1129
|
-
|
|
1125
|
+
// Also wrap the search term in a capture group so the regex split retains the separator
|
|
1126
|
+
const regexp = new RegExp(`(${this.searchTerm.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&')})`, 'gi');
|
|
1127
|
+
const parts = label.split(regexp);
|
|
1128
|
+
if (parts.length > 1) {
|
|
1129
|
+
// grouped regex splits contain the match in every second spot, to keep things short we use a modulo-flip with the index
|
|
1130
|
+
nodeContent = parts.map((part, index): TemplateResult | string =>
|
|
1131
|
+
(index % 2 === 1) ? html`<span class="node-highlight-text">${part}</span>` : part
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1130
1134
|
}
|
|
1131
1135
|
|
|
1132
1136
|
return html`
|
|
1133
1137
|
<div class="node-contentlabel">
|
|
1134
|
-
|
|
1135
|
-
|
|
1138
|
+
<div class="node-name">${nodeContent}</div>
|
|
1139
|
+
${node.note ? html`<div class="node-note">${node.note}</div>` : nothing}
|
|
1136
1140
|
</div>`;
|
|
1137
1141
|
}
|
|
1138
1142
|
|
|
@@ -279,6 +279,7 @@ export class ExtensionMatrixElement extends LitElement {
|
|
|
279
279
|
<button class="btn btn-default" title=${tooltip}
|
|
280
280
|
@click=${() => this.dispatchEvent(new CustomEvent<DownloadPacksEvent>('download-packs', eventData))}>
|
|
281
281
|
<typo3-backend-icon identifier="actions-download" size="small"></typo3-backend-icon>
|
|
282
|
+
${language}
|
|
282
283
|
</button>
|
|
283
284
|
</td>
|
|
284
285
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typo3-typescript-sources",
|
|
3
|
-
"version": "13.4.
|
|
3
|
+
"version": "13.4.22",
|
|
4
4
|
"description": "TYPO3 Core TypeScript Sources - Extracted from TYPO3/typo3 repository",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"typo3": {
|
|
30
30
|
"sourceRepository": "https://github.com/TYPO3/typo3",
|
|
31
|
-
"sourceTag": "v13.4.
|
|
31
|
+
"sourceTag": "v13.4.22",
|
|
32
32
|
"sourcePath": "Build/Sources/TypeScript"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -254,6 +254,7 @@ export class Typo3UnlinkCommand extends Core.Command {
|
|
|
254
254
|
writer.removeAttribute('linkTarget', range);
|
|
255
255
|
writer.removeAttribute('linkTitle', range);
|
|
256
256
|
writer.removeAttribute('linkRel', range);
|
|
257
|
+
writer.removeAttribute('linkDataRteError', range);
|
|
257
258
|
}
|
|
258
259
|
});
|
|
259
260
|
}
|
|
@@ -735,11 +736,43 @@ export class Typo3LinkUI extends Core.Plugin {
|
|
|
735
736
|
return position.getAncestors().find((ancestor: any) => LinkUtils.isLinkElement(ancestor));
|
|
736
737
|
}
|
|
737
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Expands the current selection to encompass the entire link element.
|
|
741
|
+
* This prevents link splitting when editing a link with partial text selection.
|
|
742
|
+
*/
|
|
743
|
+
private expandSelectionToFullLink(): void {
|
|
744
|
+
const model = this.editor.model;
|
|
745
|
+
const selection = model.document.selection;
|
|
746
|
+
|
|
747
|
+
// Only expand if we have a link attribute in the selection
|
|
748
|
+
if (!selection.hasAttribute('linkHref')) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const linkHref = selection.getAttribute('linkHref') as string;
|
|
753
|
+
if (!linkHref) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
model.change(writer => {
|
|
758
|
+
const position = selection.getFirstPosition();
|
|
759
|
+
// Find the full range of the link using the linkHref attribute
|
|
760
|
+
const linkRange = Typing.findAttributeRange(position, 'linkHref', linkHref, model);
|
|
761
|
+
|
|
762
|
+
if (linkRange) {
|
|
763
|
+
// Expand selection to cover the entire link
|
|
764
|
+
writer.setSelection(linkRange);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
738
769
|
private openLinkBrowser(editor: Core.Editor): void {
|
|
739
770
|
const linkCommand = editor.commands.get('link') as unknown as Typo3LinkCommand;
|
|
740
771
|
let additionalParameters = '';
|
|
741
772
|
|
|
773
|
+
// If editing an existing link, expand selection to full link first
|
|
742
774
|
if (linkCommand.value) {
|
|
775
|
+
this.expandSelectionToFullLink();
|
|
743
776
|
additionalParameters += '&P[curUrl][url]=' + encodeURIComponent(linkCommand.value);
|
|
744
777
|
for (const [attr, value] of Object.entries(linkCommand.attrs)) {
|
|
745
778
|
additionalParameters += '&P[curUrl][' + encodeURIComponent(attr) + ']=' + encodeURIComponent(value);
|
package/workspaces/preview.ts
CHANGED
|
@@ -40,6 +40,7 @@ enum Identifiers {
|
|
|
40
40
|
* buttons, preview mode selector, preview slider and so on.
|
|
41
41
|
*/
|
|
42
42
|
class Preview extends Workspaces {
|
|
43
|
+
override ajaxRoute: string = 'workspace_preview';
|
|
43
44
|
private currentSlidePosition: number = 100;
|
|
44
45
|
private readonly elements: { [key: string]: HTMLElement } = {};
|
|
45
46
|
|
package/workspaces/workspaces.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { default as Modal, type ModalElement } from '@typo3/backend/modal';
|
|
|
19
19
|
import { html } from 'lit';
|
|
20
20
|
|
|
21
21
|
export default class Workspaces {
|
|
22
|
+
protected readonly ajaxRoute: string = 'workspace_dispatch';
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
25
|
* Renders the send to stage window
|
|
24
26
|
* @param {Object} response
|
|
@@ -70,7 +72,7 @@ export default class Workspaces {
|
|
|
70
72
|
protected sendRemoteRequest(payload: object, progressContainer: string = '#workspace-content-wrapper'): Promise<AjaxResponse> {
|
|
71
73
|
NProgress.configure({ parent: progressContainer, showSpinner: false });
|
|
72
74
|
NProgress.start();
|
|
73
|
-
return (new AjaxRequest(TYPO3.settings.ajaxUrls.
|
|
75
|
+
return (new AjaxRequest(TYPO3.settings.ajaxUrls[this.ajaxRoute])).post(
|
|
74
76
|
payload,
|
|
75
77
|
{
|
|
76
78
|
headers: {
|