typo3-typescript-sources 14.2.0 → 14.3.1
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 +6 -6
- package/.last-synced-tag +1 -1
- package/README.md +1 -1
- package/backend/clear-cache.ts +7 -7
- package/backend/clipboard-panel.ts +20 -17
- package/backend/color-picker.ts +44 -36
- package/backend/context-menu-actions.ts +7 -7
- package/backend/date-time-picker.ts +38 -16
- package/backend/element/contextual-record-edit-trigger.ts +4 -2
- package/backend/element/editable-page-title.ts +17 -14
- package/backend/form-engine/container/inline-control-container.ts +2 -1
- package/backend/form-engine/element/datetime-element.ts +1 -1
- package/{styleguide/filter.ts → backend/layout-module/page-layout-event.ts} +8 -8
- package/backend/layout-module/page-layout.ts +102 -0
- package/backend/layout-module/toggle-hidden-element.ts +126 -0
- package/backend/layout-module/velocity-scroll.ts +123 -0
- package/backend/live-search/live-search.ts +2 -2
- package/backend/modal.ts +79 -4
- package/backend/page-wizard/helper/wizard-helper.ts +5 -2
- package/backend/security/element/csp-reports.ts +1 -6
- package/backend/settings/editor/editable-setting.ts +3 -8
- package/backend/settings/type/base.ts +1 -1
- package/{setup → backend}/setup-module.ts +3 -3
- package/backend/toolbar/clear-cache-menu.ts +11 -4
- package/backend/tree/page-position-select.ts +52 -41
- package/backend/tree/tree.ts +27 -6
- package/dashboard/dashboard.ts +0 -3
- package/form/backend/form-editor/inspector-component.ts +1 -1
- package/form/backend/form-editor/stage-component.ts +3 -3
- package/form/backend/form-manager/view-model.ts +15 -13
- package/linkvalidator/linkvalidator.ts +13 -46
- package/package.json +2 -2
- package/workspaces/renderable/record-table.ts +1 -1
- package/backend/page-actions.ts +0 -91
|
@@ -90,6 +90,7 @@ jobs:
|
|
|
90
90
|
echo "New version found: $NEW_TAG (previous: $LAST_SYNCED)"
|
|
91
91
|
echo "has_new_version=true" >> $GITHUB_OUTPUT
|
|
92
92
|
echo "typo3_version=$NEW_TAG" >> $GITHUB_OUTPUT
|
|
93
|
+
echo "release_tag=${NEW_TAG#v}" >> $GITHUB_OUTPUT
|
|
93
94
|
fi
|
|
94
95
|
|
|
95
96
|
- name: Setup Git
|
|
@@ -271,12 +272,11 @@ jobs:
|
|
|
271
272
|
Source: https://github.com/TYPO3/typo3/tree/${TYPO3_VERSION}
|
|
272
273
|
Path: Build/Sources/TypeScript/"
|
|
273
274
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
git tag -a "${NPM_VERSION}" -m "Release ${NPM_VERSION} from TYPO3 ${TYPO3_VERSION}"
|
|
275
|
+
RELEASE_TAG="${{ steps.check.outputs.release_tag }}"
|
|
276
|
+
git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG} from TYPO3 ${TYPO3_VERSION}"
|
|
277
277
|
|
|
278
278
|
git push origin "${TARGET_BRANCH}"
|
|
279
|
-
git push origin "${
|
|
279
|
+
git push origin "${RELEASE_TAG}"
|
|
280
280
|
|
|
281
281
|
- name: Setup Node.js
|
|
282
282
|
if: steps.check.outputs.has_new_version == 'true'
|
|
@@ -306,8 +306,8 @@ jobs:
|
|
|
306
306
|
env:
|
|
307
307
|
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
|
|
308
308
|
with:
|
|
309
|
-
tag_name: ${{ steps.check.outputs.
|
|
310
|
-
release_name: TYPO3 ${{ steps.check.outputs.
|
|
309
|
+
tag_name: ${{ steps.check.outputs.release_tag }}
|
|
310
|
+
release_name: TYPO3 ${{ steps.check.outputs.release_tag }}
|
|
311
311
|
body: |
|
|
312
312
|
Synchronized with TYPO3 Core ${{ steps.check.outputs.typo3_version }}
|
|
313
313
|
|
package/.last-synced-tag
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v14.
|
|
1
|
+
v14.3.1
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install typo3-typescript-sources
|
|
|
16
16
|
|
|
17
17
|
- **Original Repository:** [TYPO3/typo3](https://github.com/TYPO3/typo3)
|
|
18
18
|
- **Source Path:** `Build/Sources/TypeScript/`
|
|
19
|
-
- **Current TYPO3 Version:** `v14.
|
|
19
|
+
- **Current TYPO3 Version:** `v14.3.1`
|
|
20
20
|
|
|
21
21
|
## 🌿 Branches
|
|
22
22
|
|
package/backend/clear-cache.ts
CHANGED
|
@@ -46,15 +46,15 @@ class ClearCache {
|
|
|
46
46
|
const request = new AjaxRequest(TYPO3.settings.ajaxUrls.clearcache_page).post({ id: pageId });
|
|
47
47
|
request.then(async (response: AjaxResponse): Promise<void> => {
|
|
48
48
|
const data = await response.resolve();
|
|
49
|
-
if (data
|
|
50
|
-
Notification.
|
|
51
|
-
data.title,
|
|
52
|
-
data.message
|
|
49
|
+
if (data?.success === false) {
|
|
50
|
+
Notification.error(
|
|
51
|
+
data.title ?? cacheLabels.get('notification.error.title'),
|
|
52
|
+
data.message ?? cacheLabels.get('notification.error.message'),
|
|
53
53
|
);
|
|
54
54
|
} else {
|
|
55
|
-
Notification.
|
|
56
|
-
data.title,
|
|
57
|
-
data.message
|
|
55
|
+
Notification.success(
|
|
56
|
+
data?.title ?? cacheLabels.get('notification.success.title'),
|
|
57
|
+
data?.message ?? cacheLabels.get('notification.success.message'),
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
60
|
}, (): void => {
|
|
@@ -143,23 +143,26 @@ export class ClipboardPanel extends LitElement {
|
|
|
143
143
|
</td>
|
|
144
144
|
<td class="col-control nowrap">
|
|
145
145
|
${clipboardData.current !== tab.identifier ? nothing : html`
|
|
146
|
-
<div class="btn-
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
<div class="btn-toolbar btn-toolbar-nowrap">
|
|
147
|
+
<div class="btn-group">
|
|
148
|
+
<input type="radio" class="btn-check" id="clipboard-copymode-copy" data-action="setCopyMode" ?checked=${clipboardData.copyMode === CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setCopyMode': '1' } })}">
|
|
149
|
+
<label class="btn btn-default btn-sm" for="clipboard-copymode-copy">
|
|
150
|
+
<typo3-backend-icon identifier="actions-edit-copy" size="small"></typo3-backend-icon>
|
|
151
|
+
${clipboardData.labels.copyElements}
|
|
152
|
+
</label>
|
|
153
|
+
<input type="radio" class="btn-check" id="clipboard-copymode-move" data-action="setCopyMode" ?checked=${clipboardData.copyMode !== CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setCopyMode': '0' } })}">
|
|
154
|
+
<label class="btn btn-default btn-sm" for="clipboard-copymode-move">
|
|
155
|
+
<typo3-backend-icon identifier="actions-cut" size="small"></typo3-backend-icon>
|
|
156
|
+
${clipboardData.labels.moveElements}
|
|
157
|
+
</label>
|
|
158
|
+
</div>
|
|
159
|
+
${!clipboardData.elementCount ? nothing : html`
|
|
160
|
+
<button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeAll}" data-action="removeAll" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'removeAll': tab.identifier } })}">
|
|
161
|
+
<typo3-backend-icon identifier="actions-minus" size="small"></typo3-backend-icon>
|
|
162
|
+
${clipboardData.labels.removeAll}
|
|
163
|
+
</button>
|
|
164
|
+
`}
|
|
157
165
|
</div>
|
|
158
|
-
${!clipboardData.elementCount ? nothing : html`
|
|
159
|
-
<button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeAll}" data-action="removeAll" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'removeAll': tab.identifier } })}">
|
|
160
|
-
<typo3-backend-icon identifier="actions-minus" size="small"></typo3-backend-icon>
|
|
161
|
-
${clipboardData.labels.removeAll}
|
|
162
|
-
</button>`}
|
|
163
166
|
`}
|
|
164
167
|
</td>
|
|
165
168
|
</tr>
|
|
@@ -173,7 +176,7 @@ export class ClipboardPanel extends LitElement {
|
|
|
173
176
|
<td class="col-icon nowrap ${classMap({ 'ps-4': !tabItem.identifier })}">
|
|
174
177
|
${unsafeHTML(tabItem.icon)}
|
|
175
178
|
</td>
|
|
176
|
-
<td class="
|
|
179
|
+
<td class="col-title col-responsive nowrap">
|
|
177
180
|
${unsafeHTML(tabItem.title)}
|
|
178
181
|
${tabIdentifier === 'normal' ? html`<strong>(${clipboardData.copyMode === CopyMode.copy ? html`${clipboardData.labels.copy}` : html`${clipboardData.labels.cut}`})</strong>` : nothing}
|
|
179
182
|
${tabItem.thumb ? html`<div class="d-block">${unsafeHTML(tabItem.thumb)}</div>` : nothing}
|
package/backend/color-picker.ts
CHANGED
|
@@ -67,45 +67,53 @@ export class Typo3BackendColorPicker extends LitElement {
|
|
|
67
67
|
await DocumentService.ready();
|
|
68
68
|
|
|
69
69
|
const inputElement = this.getInputElement();
|
|
70
|
-
if (inputElement) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} else {
|
|
74
|
-
this.color = inputElement.value;
|
|
75
|
-
}
|
|
70
|
+
if (!inputElement) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
if (!inputElement.value && this.color) {
|
|
75
|
+
inputElement.value = this.color;
|
|
76
|
+
} else {
|
|
77
|
+
this.color = inputElement.value;
|
|
78
|
+
}
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
format: 'hex',
|
|
84
|
-
opacity: this.opacity,
|
|
85
|
-
swatches: this.swatches,
|
|
86
|
-
preset: false,
|
|
87
|
-
color: this.color,
|
|
88
|
-
// Casting to `unknown` to prevent TypeScript Error
|
|
89
|
-
// > TS2589: Type instantiation is excessively deep and possibly infinite.
|
|
90
|
-
parent: (this.closest('dialog') ?? '') as unknown,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
alwan.on('color', (e): void => {
|
|
94
|
-
this.color = e.hex;
|
|
95
|
-
inputElement.value = this.color;
|
|
96
|
-
inputElement.dispatchEvent(new Event('blur'));
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// input: react on user input
|
|
100
|
-
// change: react on indirect changes, e.g. a value picker
|
|
101
|
-
['input', 'change'].forEach((eventName: string): void => {
|
|
102
|
-
new RegularEvent(eventName, (e: Event): void => {
|
|
103
|
-
const input = (e.target as HTMLInputElement);
|
|
104
|
-
this.color = input.value;
|
|
105
|
-
alwan.setColor(this.color);
|
|
106
|
-
}).bindTo(inputElement);
|
|
107
|
-
});
|
|
80
|
+
if (inputElement.disabled || inputElement.readOnly) {
|
|
81
|
+
return;
|
|
108
82
|
}
|
|
83
|
+
|
|
84
|
+
const alwan = new Alwan(inputElement, {
|
|
85
|
+
position: 'bottom-start',
|
|
86
|
+
format: 'hex',
|
|
87
|
+
opacity: this.opacity,
|
|
88
|
+
swatches: this.swatches,
|
|
89
|
+
preset: false,
|
|
90
|
+
color: this.color,
|
|
91
|
+
// Casting to `unknown` to prevent TypeScript Error
|
|
92
|
+
// > TS2589: Type instantiation is excessively deep and possibly infinite.
|
|
93
|
+
parent: (this.closest('dialog') ?? '') as unknown,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// When the color picker opens, the input loses focus, making value changes difficult.
|
|
97
|
+
// Restore focus to the input after the picker is opened.
|
|
98
|
+
alwan.on('open', (): void => {
|
|
99
|
+
inputElement.focus();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
alwan.on('color', (e): void => {
|
|
103
|
+
this.color = e.hex;
|
|
104
|
+
inputElement.value = this.color;
|
|
105
|
+
inputElement.dispatchEvent(new Event('blur'));
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// input: react on user input
|
|
109
|
+
// change: react on indirect changes, e.g. a value picker
|
|
110
|
+
['input', 'change'].forEach((eventName: string): void => {
|
|
111
|
+
new RegularEvent(eventName, (e: Event): void => {
|
|
112
|
+
const input = (e.target as HTMLInputElement);
|
|
113
|
+
this.color = input.value;
|
|
114
|
+
alwan.setColor(this.color);
|
|
115
|
+
}).bindTo(inputElement);
|
|
116
|
+
});
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
protected override render(): TemplateResult {
|
|
@@ -304,15 +304,15 @@ class ContextMenuActions {
|
|
|
304
304
|
(new AjaxRequest(TYPO3.settings.ajaxUrls.clearcache_page)).post({ id: uid }).then(
|
|
305
305
|
async (response: AjaxResponse): Promise<void> => {
|
|
306
306
|
const data = await response.resolve();
|
|
307
|
-
if (data
|
|
308
|
-
Notification.
|
|
309
|
-
data.title,
|
|
310
|
-
data.message
|
|
307
|
+
if (data?.success === false) {
|
|
308
|
+
Notification.error(
|
|
309
|
+
data.title ?? cacheLabels.get('notification.error.title'),
|
|
310
|
+
data.message ?? cacheLabels.get('notification.error.message'),
|
|
311
311
|
);
|
|
312
312
|
} else {
|
|
313
|
-
Notification.
|
|
314
|
-
data.title,
|
|
315
|
-
data.message
|
|
313
|
+
Notification.success(
|
|
314
|
+
data?.title ?? cacheLabels.get('notification.success.title'),
|
|
315
|
+
data?.message ?? cacheLabels.get('notification.success.message'),
|
|
316
316
|
);
|
|
317
317
|
}
|
|
318
318
|
},
|
|
@@ -15,6 +15,7 @@ import flatpickr from 'flatpickr';
|
|
|
15
15
|
import Persistent from '@typo3/backend/storage/persistent';
|
|
16
16
|
import ShortcutButtonsPlugin from 'shortcut-buttons-flatpickr';
|
|
17
17
|
import { DateTime } from 'luxon';
|
|
18
|
+
import DomHelper from '@typo3/backend/utility/dom-helper';
|
|
18
19
|
import ThrottleEvent from '@typo3/core/event/throttle-event';
|
|
19
20
|
import type { PostValidationEvent } from '@typo3/backend/form-engine-validation';
|
|
20
21
|
import type { DateConfiguration } from '@typo3/backend/type/date-configuration';
|
|
@@ -39,7 +40,7 @@ class DateTimePicker {
|
|
|
39
40
|
* note: this function can be called multiple times (e.g. after AJAX requests) because it only
|
|
40
41
|
* applies to fields which haven't been used yet.
|
|
41
42
|
*/
|
|
42
|
-
public initialize(element: HTMLInputElement): void {
|
|
43
|
+
public initialize(element: HTMLInputElement, appendTo: HTMLElement | null = null): void {
|
|
43
44
|
if (!(element instanceof HTMLInputElement) || typeof element.dataset.datepickerInitialized !== 'undefined') {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
@@ -55,22 +56,24 @@ class DateTimePicker {
|
|
|
55
56
|
|
|
56
57
|
element.dataset.datepickerInitialized = '1';
|
|
57
58
|
import('flatpickr/dist/l10n').then((): void => {
|
|
58
|
-
this.initializeField(element, userLocale as flatpickr.Options.LocaleKey);
|
|
59
|
+
this.initializeField(element, userLocale as flatpickr.Options.LocaleKey, appendTo);
|
|
59
60
|
});
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
64
|
* Initialize a single field
|
|
64
65
|
*/
|
|
65
|
-
private initializeField(inputElement: HTMLInputElement, locale: flatpickr.Options.LocaleKey): void {
|
|
66
|
-
const scrollEvent = this.getScrollEvent();
|
|
66
|
+
private initializeField(inputElement: HTMLInputElement, locale: flatpickr.Options.LocaleKey, appendTo: HTMLElement | null = null): void {
|
|
67
67
|
const options = this.getDateOptions(inputElement);
|
|
68
68
|
options.locale = locale;
|
|
69
|
+
if (appendTo) {
|
|
70
|
+
options.appendTo = appendTo;
|
|
71
|
+
}
|
|
69
72
|
|
|
70
73
|
// Custom "first day of week" user preference
|
|
71
74
|
const dow = Persistent.get('dateTimeFirstDayOfWeek');
|
|
72
75
|
|
|
73
|
-
if (dow !== '') {
|
|
76
|
+
if (dow != null && dow !== '') {
|
|
74
77
|
// stored number is 1-index based, convert to 0-index.
|
|
75
78
|
const dowNumber = parseInt(dow, 10) - 1;
|
|
76
79
|
|
|
@@ -80,14 +83,35 @@ class DateTimePicker {
|
|
|
80
83
|
flatpickr.l10ns.default.firstDayOfWeek = dowNumber;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
const usePopover = appendTo instanceof HTMLElement && appendTo.localName === 'typo3-formengine-element-datetime';
|
|
87
|
+
|
|
88
|
+
if (usePopover) {
|
|
89
|
+
// Disable flatpickr's own positioning; native popover + CSS anchor positioning handle it.
|
|
90
|
+
options.position = (): void => { /* handled by popover */ };
|
|
91
|
+
options.onOpen = [
|
|
92
|
+
(_dates: Date[], _currentDateString: string, self: flatpickr.Instance): void => {
|
|
93
|
+
if (!self.calendarContainer.hasAttribute('popover')) {
|
|
94
|
+
self.calendarContainer.setAttribute('popover', 'manual');
|
|
95
|
+
}
|
|
96
|
+
self.calendarContainer.showPopover();
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
options.onClose = (_dates: Date[], _currentDateString: string, self: flatpickr.Instance): void => {
|
|
100
|
+
if (self.calendarContainer.matches(':popover-open')) {
|
|
101
|
+
self.calendarContainer.hidePopover();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
const scrollEvent = this.getScrollEvent();
|
|
106
|
+
options.onOpen = [
|
|
107
|
+
(): void => {
|
|
108
|
+
scrollEvent.bindTo(DomHelper.scrollEventTarget(inputElement));
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
options.onClose = (): void => {
|
|
112
|
+
scrollEvent.release();
|
|
113
|
+
};
|
|
114
|
+
}
|
|
91
115
|
|
|
92
116
|
// initialize the date time picker on this element
|
|
93
117
|
const dateTimePicker = flatpickr(inputElement, options);
|
|
@@ -116,9 +140,7 @@ class DateTimePicker {
|
|
|
116
140
|
|
|
117
141
|
/**
|
|
118
142
|
* Due to some whack CSS the scrollPosition of the document stays 0 which renders a stuck date time picker.
|
|
119
|
-
* Because of this the position is recalculated on scrolling
|
|
120
|
-
*
|
|
121
|
-
* @return {ThrottleEvent}
|
|
143
|
+
* Because of this the position is recalculated on scrolling the nearest scrollable ancestor.
|
|
122
144
|
*/
|
|
123
145
|
private getScrollEvent(): ThrottleEvent {
|
|
124
146
|
return new ThrottleEvent('scroll', (): void => {
|
|
@@ -61,12 +61,14 @@ export class ContextualRecordEditTriggerElement extends PseudoButtonLitElement {
|
|
|
61
61
|
private setupMessageHandling(modal: ModalElement): void {
|
|
62
62
|
const messageTarget = top;
|
|
63
63
|
let savedRecordTitle = '';
|
|
64
|
+
let hasSaved = false;
|
|
64
65
|
let closeConfirmed = false;
|
|
65
66
|
const messageHandler = (event: MessageEvent): void => {
|
|
66
67
|
if (event.origin !== window.location.origin) {
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
70
|
if (event.data?.actionName === 'typo3:editform:saved') {
|
|
71
|
+
hasSaved = true;
|
|
70
72
|
savedRecordTitle = event.data.recordTitle ?? '';
|
|
71
73
|
}
|
|
72
74
|
if (event.data?.actionName === 'typo3:editform:closed') {
|
|
@@ -94,14 +96,14 @@ export class ContextualRecordEditTriggerElement extends PseudoButtonLitElement {
|
|
|
94
96
|
|
|
95
97
|
modal.addEventListener('typo3-modal-hidden', () => {
|
|
96
98
|
messageTarget.removeEventListener('message', messageHandler);
|
|
97
|
-
if (
|
|
99
|
+
if (hasSaved) {
|
|
98
100
|
top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
|
|
99
101
|
if (top.TYPO3?.Backend?.ContentContainer) {
|
|
100
102
|
top.TYPO3.Backend.ContentContainer.refresh();
|
|
101
103
|
}
|
|
102
104
|
Notification.success(
|
|
103
105
|
labels.get('notification.record_updated.title'),
|
|
104
|
-
labels.get('notification.record_updated.message', [savedRecordTitle]),
|
|
106
|
+
savedRecordTitle !== '' ? labels.get('notification.record_updated.message', [savedRecordTitle]) : undefined,
|
|
105
107
|
);
|
|
106
108
|
} else {
|
|
107
109
|
this.focus();
|
|
@@ -84,13 +84,19 @@ export class EditablePageTitle extends LitElement {
|
|
|
84
84
|
.wrapper {
|
|
85
85
|
position: relative;
|
|
86
86
|
margin: -1px 0;
|
|
87
|
+
padding-inline-end: 1.5em;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
.wrapper:has(> form) .page-title {
|
|
91
|
+
visibility: hidden;
|
|
92
|
+
pointer-events: none;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
.wrapper > form {
|
|
96
|
+
position: absolute;
|
|
97
|
+
top: 0;
|
|
98
|
+
left: 0;
|
|
99
|
+
right: 0;
|
|
94
100
|
padding-inline-end: 2.5em;
|
|
95
101
|
}
|
|
96
102
|
|
|
@@ -190,18 +196,15 @@ export class EditablePageTitle extends LitElement {
|
|
|
190
196
|
return html`<div class="wrapper"><h1>${this.pageTitle}</h1></div>`;
|
|
191
197
|
}
|
|
192
198
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
<div class="wrapper">
|
|
199
|
+
return html`
|
|
200
|
+
<div class="wrapper">
|
|
201
|
+
<div class="page-title">
|
|
197
202
|
<h1 @dblclick="${(): void => { this.startEditing(); }}">${this.pageTitle}</h1>
|
|
198
203
|
${this.composeEditButton()}
|
|
199
|
-
</div
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return content;
|
|
204
|
+
</div>
|
|
205
|
+
${this._isEditing ? this.composeEditForm() : nothing}
|
|
206
|
+
</div>
|
|
207
|
+
`;
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
private isEditable(): boolean {
|
|
@@ -268,7 +271,7 @@ export class EditablePageTitle extends LitElement {
|
|
|
268
271
|
|
|
269
272
|
private composeEditForm(): TemplateResult {
|
|
270
273
|
return html`
|
|
271
|
-
<form
|
|
274
|
+
<form @submit="${ this.updatePageTitle }">
|
|
272
275
|
<label class="screen-reader" for="input">${this.labels.input}</label>
|
|
273
276
|
<input
|
|
274
277
|
autocomplete="off"
|
|
@@ -477,7 +477,8 @@ class InlineControlContainer extends HTMLElement {
|
|
|
477
477
|
|
|
478
478
|
if (hiddenValueCheckBox !== null && hiddenValueInput !== null) {
|
|
479
479
|
hiddenValueCheckBox.checked = !hiddenValueCheckBox.checked;
|
|
480
|
-
|
|
480
|
+
const active = hiddenValueCheckBox.checked !== (hiddenValueCheckBox.dataset.invertStateDisplay === 'true');
|
|
481
|
+
hiddenValueInput.value = active ? '1' : '0';
|
|
481
482
|
FormEngine.markFieldAsChanged(hiddenValueCheckBox);
|
|
482
483
|
}
|
|
483
484
|
|
|
@@ -47,7 +47,7 @@ class DatetimeElement extends HTMLElement {
|
|
|
47
47
|
|
|
48
48
|
this.registerEventHandler();
|
|
49
49
|
import('../../date-time-picker').then(({ default: DateTimePicker }): void => {
|
|
50
|
-
DateTimePicker.initialize(this.element);
|
|
50
|
+
DateTimePicker.initialize(this.element, this);
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* The TYPO3 project - inspiring people to share!
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export class HiddenContentCountChangedEvent extends CustomEvent<{ count: number }> {
|
|
15
|
+
static readonly eventName = 'typo3:page-layout:hidden-content-count-changed';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
}
|
|
17
|
+
constructor(count: number) {
|
|
18
|
+
super(HiddenContentCountChangedEvent.eventName, {
|
|
19
|
+
detail: { count },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
import AjaxRequest from '@typo3/core/ajax/ajax-request';
|
|
15
|
+
import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
|
|
16
|
+
import Notification from '@typo3/backend/notification';
|
|
17
|
+
import RegularEvent from '@typo3/core/event/regular-event';
|
|
18
|
+
import { sudoModeInterceptor } from '@typo3/backend/security/sudo-mode-interceptor';
|
|
19
|
+
import layoutLabels from '~labels/backend.layout';
|
|
20
|
+
import { HiddenContentCountChangedEvent } from './page-layout-event';
|
|
21
|
+
import '@typo3/backend/element/icon-element';
|
|
22
|
+
import type { PageLayoutToggleHidden } from './toggle-hidden-element';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Module: @typo3/backend/layout-module/page-layout
|
|
26
|
+
*
|
|
27
|
+
* Main entry point for the page layout module.
|
|
28
|
+
* Registers all web components and provides inline visibility toggling.
|
|
29
|
+
*/
|
|
30
|
+
new RegularEvent('click', async (event: Event, delegateTarget?: Element): Promise<void> => {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
event.stopPropagation();
|
|
33
|
+
|
|
34
|
+
const target = delegateTarget as HTMLButtonElement;
|
|
35
|
+
if (target.disabled) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
target.disabled = true;
|
|
39
|
+
|
|
40
|
+
const contentElement = target.closest('.t3js-page-ce') as HTMLElement | null;
|
|
41
|
+
if (contentElement === null) {
|
|
42
|
+
target.disabled = false;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const table = contentElement.dataset.table;
|
|
47
|
+
const uid = parseInt(contentElement.dataset.uid ?? '0', 10);
|
|
48
|
+
const isHidden = contentElement.classList.contains('t3js-hidden-record');
|
|
49
|
+
const iconElement = target.querySelector('typo3-backend-icon');
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const response: AjaxResponse = await new AjaxRequest(TYPO3.settings.ajaxUrls.record_toggle_visibility)
|
|
53
|
+
.addMiddleware(sudoModeInterceptor)
|
|
54
|
+
.post({
|
|
55
|
+
table: table,
|
|
56
|
+
uid: uid,
|
|
57
|
+
action: isHidden ? 'show' : 'hide',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const data = await response.resolve();
|
|
61
|
+
|
|
62
|
+
// Update hidden state classes on the content element wrapper
|
|
63
|
+
contentElement.classList.toggle('t3-page-ce-hidden', !data.isVisible);
|
|
64
|
+
contentElement.classList.toggle('t3js-hidden-record', !data.isVisible);
|
|
65
|
+
|
|
66
|
+
// If the element was just hidden and the "show hidden" toggle is inactive,
|
|
67
|
+
// hide the element from view so it disappears from the layout.
|
|
68
|
+
if (!data.isVisible) {
|
|
69
|
+
const toggleHidden = document.querySelector('typo3-backend-page-layout-toggle-hidden') as PageLayoutToggleHidden | null;
|
|
70
|
+
if (toggleHidden && !toggleHidden.active) {
|
|
71
|
+
contentElement.style.display = 'none';
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
contentElement.style.display = '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Update toggle button icon and title
|
|
78
|
+
iconElement?.setAttribute('identifier', data.isVisible ? 'actions-edit-hide' : 'actions-edit-unhide');
|
|
79
|
+
target.title = data.isVisible ? layoutLabels.get('hide') : layoutLabels.get('unHide');
|
|
80
|
+
|
|
81
|
+
// Update record icon in context menu trigger (reflects hidden overlay)
|
|
82
|
+
const recordIconElement = contentElement.querySelector('.t3-page-ce-header-left [data-contextmenu-trigger] .t3js-icon');
|
|
83
|
+
if (recordIconElement && data.icon) {
|
|
84
|
+
recordIconElement.replaceWith(document.createRange().createContextualFragment(data.icon));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Update hidden element counter in the toolbar toggle button
|
|
88
|
+
const count = document.querySelectorAll('.t3js-hidden-record').length;
|
|
89
|
+
document.dispatchEvent(new HiddenContentCountChangedEvent(count));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error && typeof (error as AjaxResponse).resolve === 'function') {
|
|
92
|
+
const data = await (error as AjaxResponse).resolve();
|
|
93
|
+
for (const message of data.messages ?? []) {
|
|
94
|
+
Notification.error(message.title, message.message);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} finally {
|
|
98
|
+
target.disabled = false;
|
|
99
|
+
}
|
|
100
|
+
}).delegateTo(document, 'button[data-action="content-element-visibility-toggle"]');
|
|
101
|
+
|
|
102
|
+
export { HiddenContentCountChangedEvent } from './page-layout-event';
|