typo3-typescript-sources 14.3.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/.last-synced-tag CHANGED
@@ -1 +1 @@
1
- v14.3.0
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.3.0`
19
+ - **Current TYPO3 Version:** `v14.3.1`
20
20
 
21
21
  ## 🌿 Branches
22
22
 
@@ -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-group">
147
- <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' } })}">
148
- <label class="btn btn-default btn-sm" for="clipboard-copymode-copy">
149
- <typo3-backend-icon identifier="actions-edit-copy" size="small"></typo3-backend-icon>
150
- ${clipboardData.labels.copyElements}
151
- </label>
152
- <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' } })}">
153
- <label class="btn btn-default btn-sm" for="clipboard-copymode-move">
154
- <typo3-backend-icon identifier="actions-cut" size="small"></typo3-backend-icon>
155
- ${clipboardData.labels.moveElements}
156
- </label>
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="nowrap" style="width: 95%">
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}
@@ -73,7 +73,7 @@ class DateTimePicker {
73
73
  // Custom "first day of week" user preference
74
74
  const dow = Persistent.get('dateTimeFirstDayOfWeek');
75
75
 
76
- if (dow !== '') {
76
+ if (dow != null && dow !== '') {
77
77
  // stored number is 1-index based, convert to 0-index.
78
78
  const dowNumber = parseInt(dow, 10) - 1;
79
79
 
@@ -477,7 +477,8 @@ class InlineControlContainer extends HTMLElement {
477
477
 
478
478
  if (hiddenValueCheckBox !== null && hiddenValueInput !== null) {
479
479
  hiddenValueCheckBox.checked = !hiddenValueCheckBox.checked;
480
- hiddenValueInput.value = hiddenValueCheckBox.checked ? '1' : '0';
480
+ const active = hiddenValueCheckBox.checked !== (hiddenValueCheckBox.dataset.invertStateDisplay === 'true');
481
+ hiddenValueInput.value = active ? '1' : '0';
481
482
  FormEngine.markFieldAsChanged(hiddenValueCheckBox);
482
483
  }
483
484
 
@@ -0,0 +1,123 @@
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/velocity-scroll
16
+ *
17
+ * Enables auto-scrolling while dragging near the viewport edge.
18
+ *
19
+ * Self-binds a delegated `dragstart` listener for all `[draggable="true"]`
20
+ * elements on the page. May also be invoked directly:
21
+ *
22
+ * ```js
23
+ * // inside a dragstart event handler:
24
+ * initVelocityScroll(event);
25
+ * ```
26
+ */
27
+ import DocumentService from '@typo3/core/document-service';
28
+ import RegularEvent from '@typo3/core/event/regular-event';
29
+
30
+ const EDGE_RATIO = 0.2;
31
+ const MAX_STRENGTH_RATIO = 2.5;
32
+
33
+ DocumentService.ready().then((): void => {
34
+ new RegularEvent('dragstart', (event: Event): void => {
35
+ initVelocityScroll(event as DragEvent);
36
+ }).delegateTo(document, '[draggable="true"]');
37
+ });
38
+
39
+ export function initVelocityScroll(event: DragEvent): void {
40
+ const scrollingElement = document.scrollingElement;
41
+ if (!(scrollingElement instanceof HTMLElement)) {
42
+ console.warn('Scrolling element is not an HTMLElement. Velocity scroll will not work.');
43
+ return;
44
+ }
45
+
46
+ // Horizontal scrolling may live on an inner container (e.g. the page module's
47
+ // .t3-grid-container with overflow-x: auto) rather than the document, so target
48
+ // the nearest horizontally scrollable ancestor of the drag source if any exists.
49
+ const horizontalElement = findHorizontalScrollAncestor(event.target as Element | null) ?? scrollingElement;
50
+
51
+ let scrollSpeedVertical = 0;
52
+ let scrollSpeedHorizontal = 0;
53
+ let running = true;
54
+ let lastFrameTime = performance.now();
55
+
56
+ // Disable smooth scrolling for the duration of the drag and restore on dragend
57
+ const originalVerticalScrollBehavior = scrollingElement.style.scrollBehavior;
58
+ const originalHorizontalScrollBehavior = horizontalElement.style.scrollBehavior;
59
+ scrollingElement.style.scrollBehavior = 'auto';
60
+ horizontalElement.style.scrollBehavior = 'auto';
61
+
62
+ const tick = (now: number): void => {
63
+ const dt = (now - lastFrameTime) / 1000;
64
+ lastFrameTime = now;
65
+
66
+ const maxVertical = scrollingElement.scrollHeight - scrollingElement.clientHeight;
67
+ const maxHorizontal = horizontalElement.scrollWidth - horizontalElement.clientWidth;
68
+ scrollingElement.scrollTop = Math.max(0, Math.min(scrollingElement.scrollTop + scrollSpeedVertical * dt, maxVertical));
69
+ horizontalElement.scrollLeft = Math.max(0, Math.min(horizontalElement.scrollLeft + scrollSpeedHorizontal * dt, maxHorizontal));
70
+
71
+ if (running) {
72
+ requestAnimationFrame(tick);
73
+ }
74
+ };
75
+ requestAnimationFrame(tick);
76
+
77
+ // Progress within the edge zone goes from 0 (just entered) to 1 (at the edge).
78
+ // Squaring it makes scrolling accelerate noticeably near the edge.
79
+ const axisStrength = (distance: number, edge: number, max: number): number =>
80
+ distance < edge ? ((1 - distance / edge) ** 2) * max : 0;
81
+
82
+ const autoScroll = (event: DragEvent): void => {
83
+ const verticalEdge = window.innerHeight * EDGE_RATIO;
84
+ const horizontalEdge = window.innerWidth * EDGE_RATIO;
85
+ const maxVerticalStrength = window.innerHeight * MAX_STRENGTH_RATIO;
86
+ const maxHorizontalStrength = window.innerWidth * MAX_STRENGTH_RATIO;
87
+
88
+ scrollSpeedVertical =
89
+ axisStrength(window.innerHeight - event.clientY, verticalEdge, maxVerticalStrength) -
90
+ axisStrength(event.clientY, verticalEdge, maxVerticalStrength);
91
+ scrollSpeedHorizontal =
92
+ axisStrength(window.innerWidth - event.clientX, horizontalEdge, maxHorizontalStrength) -
93
+ axisStrength(event.clientX, horizontalEdge, maxHorizontalStrength);
94
+ };
95
+ autoScroll(event);
96
+ window.addEventListener('dragover', autoScroll);
97
+
98
+ // Bind teardown on window: the source element may be detached or replaced
99
+ // during the drop (drag-drop.ts re-renders content via AJAX), in which case
100
+ // a `dragend` listener on the source element would never fire.
101
+ window.addEventListener('dragend', (): void => {
102
+ window.removeEventListener('dragover', autoScroll);
103
+ scrollSpeedVertical = 0;
104
+ scrollSpeedHorizontal = 0;
105
+ running = false;
106
+ scrollingElement.style.scrollBehavior = originalVerticalScrollBehavior;
107
+ horizontalElement.style.scrollBehavior = originalHorizontalScrollBehavior;
108
+ }, { once: true });
109
+ }
110
+
111
+ function findHorizontalScrollAncestor(element: Element | null): HTMLElement | null {
112
+ let current = element;
113
+ while (current instanceof HTMLElement) {
114
+ if (current.scrollWidth > current.clientWidth) {
115
+ const overflowX = window.getComputedStyle(current).overflowX;
116
+ if (overflowX === 'auto' || overflowX === 'scroll') {
117
+ return current;
118
+ }
119
+ }
120
+ current = current.parentElement;
121
+ }
122
+ return null;
123
+ }
@@ -11,7 +11,7 @@
11
11
  * The TYPO3 project - inspiring people to share!
12
12
  */
13
13
 
14
- import Modal from '@typo3/backend/modal';
14
+ import Modal, { Sizes } from '@typo3/backend/modal';
15
15
  import '@typo3/backend/element/icon-element';
16
16
  import '@typo3/backend/live-search/element/hint';
17
17
  import '@typo3/backend/live-search/element/result/result-pagination';
@@ -105,7 +105,7 @@ class LiveSearch {
105
105
  content: url.toString(),
106
106
  title: coreLabels.get('labels.search'),
107
107
  severity: SeverityEnum.notice,
108
- size: Modal.sizes.medium,
108
+ size: Sizes.large,
109
109
  ajaxCallback: (): void => {
110
110
  const liveSearchContainer = modal.querySelector('typo3-backend-live-search');
111
111
  const searchForm = liveSearchContainer.querySelector('form');
package/backend/modal.ts CHANGED
@@ -46,6 +46,30 @@ export enum Sizes {
46
46
  expand = 'expand',
47
47
  }
48
48
 
49
+ export enum Size {
50
+ small = 'small',
51
+ default = 'default',
52
+ medium = 'medium',
53
+ large = 'large',
54
+ full = 'full',
55
+ }
56
+
57
+ export type SizeConfig = { width?: Size; height?: Size };
58
+
59
+ const isValidSize = (value: unknown): value is Sizes | SizeConfig => {
60
+ if (typeof value === 'string') {
61
+ return value in Sizes;
62
+ }
63
+ if (typeof value !== 'object' || value === null) {
64
+ return false;
65
+ }
66
+ const config = value as SizeConfig;
67
+ return Object.keys(value).length > 0
68
+ && Object.keys(value).every(key => key === 'width' || key === 'height')
69
+ && (config.width === undefined || config.width in Size)
70
+ && (config.height === undefined || config.height in Size);
71
+ };
72
+
49
73
  export enum Positions {
50
74
  center = 'center',
51
75
  top = 'top',
@@ -89,7 +113,7 @@ export interface Configuration {
89
113
  severity: SeverityEnum;
90
114
  buttons: Array<Button>;
91
115
  style: Styles;
92
- size: Sizes;
116
+ size: Sizes | SizeConfig;
93
117
  position: Positions;
94
118
  additionalCssClasses: Array<string>;
95
119
  callback: ModalCallbackFunction | null;
@@ -110,7 +134,6 @@ export class ModalElement extends LitElement {
110
134
  @property({ type: String, reflect: true }) type: Types = Types.default;
111
135
  @property({ type: String, reflect: true }) severity: SeverityEnum = SeverityEnum.notice;
112
136
  @property({ type: String, reflect: true }) variant: Styles = Styles.default;
113
- @property({ type: String, reflect: true }) size: Sizes = Sizes.default;
114
137
  @property({ type: String, reflect: true }) position: Positions = Positions.center;
115
138
  @property({ type: Boolean }) staticBackdrop: boolean = false;
116
139
  @property({ type: Boolean }) hideCloseButton: boolean = false;
@@ -128,6 +151,7 @@ export class ModalElement extends LitElement {
128
151
  public userData: { [key: string]: any } = {};
129
152
 
130
153
  private readonly uniqueId: number;
154
+ #size: Sizes | SizeConfig = Sizes.default;
131
155
 
132
156
  constructor() {
133
157
  super();
@@ -135,6 +159,43 @@ export class ModalElement extends LitElement {
135
159
  this.uniqueId = ++uniqueIdCounter;
136
160
  }
137
161
 
162
+ @property({
163
+ reflect: true,
164
+ converter: {
165
+ toAttribute: (value: Sizes | SizeConfig): string =>
166
+ typeof value === 'string' ? value : JSON.stringify(value),
167
+ fromAttribute: (value: string | null): unknown => {
168
+ if (value === null) {
169
+ return Sizes.default;
170
+ }
171
+ if (isValidSize(value)) {
172
+ return value;
173
+ }
174
+ try {
175
+ return JSON.parse(value);
176
+ } catch {
177
+ return value;
178
+ }
179
+ },
180
+ },
181
+ })
182
+ get size(): Sizes | SizeConfig {
183
+ return this.#size;
184
+ }
185
+ set size(value: unknown) {
186
+ const valid = isValidSize(value);
187
+ this.#size = valid ? value : Sizes.default;
188
+ if (!valid && this.isConnected) {
189
+ // Input was invalid and got sanitized: force the attribute to match,
190
+ // because Lit suppresses attribute reflection on the attribute → property
191
+ // path, which would otherwise leave the invalid string in the DOM.
192
+ const expected = typeof this.#size === 'string' ? this.#size : JSON.stringify(this.#size);
193
+ if (this.getAttribute('size') !== expected) {
194
+ this.setAttribute('size', expected);
195
+ }
196
+ }
197
+ }
198
+
138
199
  public setContent(content: TemplateResult | JQuery | Element | DocumentFragment): void {
139
200
  this.templateResultContent = content;
140
201
  }
@@ -201,7 +262,7 @@ export class ModalElement extends LitElement {
201
262
  `modal-type-${this.type}`,
202
263
  `modal-style-${this.variant}`,
203
264
  `modal-severity-${Severity.getCssClass(this.severity)}`,
204
- `modal-size-${this.size}`,
265
+ ...this.getSizeClasses(),
205
266
  `modal-position-${this.position}`,
206
267
  ...this.additionalCssClasses,
207
268
  ]);
@@ -236,6 +297,20 @@ export class ModalElement extends LitElement {
236
297
  `;
237
298
  }
238
299
 
300
+ private getSizeClasses(): string[] {
301
+ if (typeof this.size === 'string') {
302
+ return [`modal-size-${this.size}`];
303
+ }
304
+ const classes: string[] = [];
305
+ if (this.size.width !== undefined) {
306
+ classes.push(`modal-width-${this.size.width}`);
307
+ }
308
+ if (this.size.height !== undefined) {
309
+ classes.push(`modal-height-${this.size.height}`);
310
+ }
311
+ return classes;
312
+ }
313
+
239
314
  private handleDialogClose(): void {
240
315
  this.trigger('typo3-modal-hidden');
241
316
  }
@@ -599,7 +674,7 @@ class Modal {
599
674
  ? configuration.severity
600
675
  : this.defaultConfiguration.severity;
601
676
  configuration.buttons = <Array<Button>>configuration.buttons || this.defaultConfiguration.buttons;
602
- configuration.size = typeof configuration.size === 'string' && configuration.size in Sizes
677
+ configuration.size = isValidSize(configuration.size)
603
678
  ? configuration.size
604
679
  : this.defaultConfiguration.size;
605
680
  configuration.style = typeof configuration.style === 'string' && configuration.style in Styles
@@ -13,7 +13,7 @@
13
13
 
14
14
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
15
15
  import { html } from 'lit';
16
- import Modal from '@typo3/backend/modal';
16
+ import Modal, { Size } from '@typo3/backend/modal';
17
17
  import { SeverityEnum } from '@typo3/backend/enum/severity';
18
18
  import backendLayoutLabels from '~labels/backend.layout';
19
19
  import type { PageWizardConfiguration } from '@typo3/backend/page-wizard/page-wizard-configuration';
@@ -25,7 +25,10 @@ export const openPageWizardModal = async(configuration?: PageWizardConfiguration
25
25
  title: backendLayoutLabels.get('newPage'),
26
26
  content: html`<typo3-backend-page-wizard .configuration="${configuration}"></typo3-backend-page-wizard>`,
27
27
  severity: SeverityEnum.notice,
28
- size: Modal.sizes.medium,
28
+ size: {
29
+ width: Size.medium,
30
+ height: Size.large
31
+ },
29
32
  staticBackdrop: true,
30
33
  buttons: []
31
34
  });
@@ -72,7 +72,7 @@ class SetupModule {
72
72
  if (typeof evt.data.objectGroup === 'undefined') {
73
73
  throw new Error('No object group defined for message');
74
74
  }
75
- const avatarMatches = evt.data.objectGroup.match(/avatar-(.+)$/);
75
+ const avatarMatches = evt.data.objectGroup.match(/(?:^|-)avatar-(.+)$/);
76
76
  if (avatarMatches === null) {
77
77
  // Received message isn't provisioned for current InlineControlContainer instance
78
78
  return;
@@ -1821,10 +1821,10 @@ export function renderRequiredValidatorEditor(
1821
1821
  getCurrentlySelectedFormElement().set(propertyPath, propertyValue);
1822
1822
  }
1823
1823
  } else {
1824
- getPublisherSubscriber().publish('view/inspector/removeCollectionElement/perform', [validatorIdentifier, 'validators']);
1825
1824
  if (getUtility().isNonEmptyString(propertyPath)) {
1826
1825
  getCurrentlySelectedFormElement().unset(propertyPath);
1827
1826
  }
1827
+ getPublisherSubscriber().publish('view/inspector/removeCollectionElement/perform', [validatorIdentifier, 'validators']);
1828
1828
  propertyData = getCurrentlySelectedFormElement().get(validationErrorMessagePropertyPath);
1829
1829
  if (getUtility().isUndefinedOrNull(propertyData)) { propertyData = []; }
1830
1830
  getCurrentlySelectedFormElement().set(validationErrorMessagePropertyPath, renewValidationErrorMessages(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typo3-typescript-sources",
3
- "version": "14.3.0",
3
+ "version": "14.3.1",
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": "v14.3.0",
31
+ "sourceTag": "v14.3.1",
32
32
  "sourcePath": "Build/Sources/TypeScript"
33
33
  }
34
34
  }
@@ -89,7 +89,7 @@ export class RecordTableElement extends LitElement {
89
89
  <thead>
90
90
  <tr>
91
91
  <th>
92
- <div class="btn-group dropdown">
92
+ <div class="dropdown">
93
93
  <button type="button" class="dropdown-toggle dropdown-toggle-link t3js-multi-record-selection-check-actions-toggle" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false" aria-label="${coreLabels.get('labels.openSelectionOptions')}">
94
94
  <typo3-backend-icon identifier="actions-selection" size="small"></typo3-backend-icon>
95
95
  </button>