typo3-typescript-sources 13.4.20

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.
Files changed (415) hide show
  1. package/.github/workflows/sync-and-publish.yml +265 -0
  2. package/.last-synced-tag +1 -0
  3. package/LICENSE.txt +339 -0
  4. package/README-TYPO3.md +166 -0
  5. package/README.md +28 -0
  6. package/adminpanel/admin-panel.ts +375 -0
  7. package/adminpanel/modules/cache.ts +27 -0
  8. package/adminpanel/modules/preview.ts +75 -0
  9. package/backend/action-button/abstract-action.ts +22 -0
  10. package/backend/action-button/deferred-action.ts +41 -0
  11. package/backend/action-button/immediate-action.ts +31 -0
  12. package/backend/action-dispatcher.ts +104 -0
  13. package/backend/ajax-data-handler/message-interface.ts +18 -0
  14. package/backend/ajax-data-handler/response-interface.ts +20 -0
  15. package/backend/ajax-data-handler.ts +214 -0
  16. package/backend/backend-exception.ts +22 -0
  17. package/backend/broadcast-message.ts +50 -0
  18. package/backend/broadcast-service.ts +52 -0
  19. package/backend/browse-database.ts +38 -0
  20. package/backend/clear-cache.ts +88 -0
  21. package/backend/clipboard-panel.ts +240 -0
  22. package/backend/close-current-window.ts +2 -0
  23. package/backend/code-editor/autocomplete/completion-result.ts +105 -0
  24. package/backend/code-editor/autocomplete/ts-code-completion.ts +142 -0
  25. package/backend/code-editor/autocomplete/ts-parser.ts +485 -0
  26. package/backend/code-editor/autocomplete/ts-ref.ts +164 -0
  27. package/backend/code-editor/element/code-mirror-element.ts +282 -0
  28. package/backend/code-editor/language/typoscript.ts +181 -0
  29. package/backend/code-editor/stream-parser/typoscript.ts +1600 -0
  30. package/backend/color-picker.ts +158 -0
  31. package/backend/color-scheme-switch.ts +179 -0
  32. package/backend/column-selector-button.ts +302 -0
  33. package/backend/context-help.ts +68 -0
  34. package/backend/context-menu-actions.ts +322 -0
  35. package/backend/context-menu.ts +751 -0
  36. package/backend/copy-to-clipboard.ts +79 -0
  37. package/backend/date-time-picker.ts +270 -0
  38. package/backend/document-save-actions.ts +159 -0
  39. package/backend/drag-tooltip.ts +285 -0
  40. package/backend/drag-uploader.ts +914 -0
  41. package/backend/element/alert-element.ts +116 -0
  42. package/backend/element/dispatch-modal-button.ts +50 -0
  43. package/backend/element/draggable-resizable-element.ts +249 -0
  44. package/backend/element/editable-page-title.ts +306 -0
  45. package/backend/element/icon-element.ts +84 -0
  46. package/backend/element/immediate-action-element.ts +89 -0
  47. package/backend/element/pagination.ts +59 -0
  48. package/backend/element/progress-bar-element.ts +172 -0
  49. package/backend/element/progress-tracker-element.ts +119 -0
  50. package/backend/element/pseudo-button.ts +58 -0
  51. package/backend/element/spinner-element.ts +51 -0
  52. package/backend/element/thumbnail-element.ts +80 -0
  53. package/backend/element-browser.ts +182 -0
  54. package/backend/enum/data-transfer-types.ts +21 -0
  55. package/backend/enum/icon-types.ts +31 -0
  56. package/backend/enum/key-types.ts +27 -0
  57. package/backend/enum/severity.ts +20 -0
  58. package/backend/enum/viewport/scaffold-identifier.ts +24 -0
  59. package/backend/event/client-request.ts +25 -0
  60. package/backend/event/consumable.ts +18 -0
  61. package/backend/event/consumer-scope.ts +54 -0
  62. package/backend/event/event-dispatcher.ts +26 -0
  63. package/backend/event/interaction-request-assignment.ts +24 -0
  64. package/backend/event/interaction-request-map.ts +75 -0
  65. package/backend/event/interaction-request.ts +48 -0
  66. package/backend/event/trigger-request.ts +56 -0
  67. package/backend/form/submit-interceptor.ts +66 -0
  68. package/backend/form-engine/container/files-control-container.ts +716 -0
  69. package/backend/form-engine/container/flex-form-container-container.ts +166 -0
  70. package/backend/form-engine/container/flex-form-section-container.ts +210 -0
  71. package/backend/form-engine/container/inline-control-container.ts +1230 -0
  72. package/backend/form-engine/container/site-language-container.ts +616 -0
  73. package/backend/form-engine/element/abstract-sortable-select-items.ts +186 -0
  74. package/backend/form-engine/element/category-element.ts +160 -0
  75. package/backend/form-engine/element/color-element.ts +81 -0
  76. package/backend/form-engine/element/datetime-element.ts +66 -0
  77. package/backend/form-engine/element/extra/char-counter.ts +112 -0
  78. package/backend/form-engine/element/extra/select-box-filter.ts +85 -0
  79. package/backend/form-engine/element/folder-element.ts +66 -0
  80. package/backend/form-engine/element/group-element.ts +47 -0
  81. package/backend/form-engine/element/json-element.ts +52 -0
  82. package/backend/form-engine/element/link-element.ts +121 -0
  83. package/backend/form-engine/element/mfa-info-element.ts +169 -0
  84. package/backend/form-engine/element/modifier/resizable.ts +27 -0
  85. package/backend/form-engine/element/modifier/tabbable.ts +29 -0
  86. package/backend/form-engine/element/online-media-form-element.ts +65 -0
  87. package/backend/form-engine/element/password-element.ts +71 -0
  88. package/backend/form-engine/element/select-multiple-side-by-side-element.ts +83 -0
  89. package/backend/form-engine/element/select-single-element.ts +82 -0
  90. package/backend/form-engine/element/select-tree-element.ts +148 -0
  91. package/backend/form-engine/element/select-tree-toolbar.ts +118 -0
  92. package/backend/form-engine/element/select-tree.ts +210 -0
  93. package/backend/form-engine/element/slug-element.ts +243 -0
  94. package/backend/form-engine/element/suggest/result-container.ts +118 -0
  95. package/backend/form-engine/element/suggest/result-item.ts +90 -0
  96. package/backend/form-engine/element/table-permission-element.ts +101 -0
  97. package/backend/form-engine/element/table-wizard-element.ts +427 -0
  98. package/backend/form-engine/element/text-element.ts +52 -0
  99. package/backend/form-engine/element/text-table-element.ts +31 -0
  100. package/backend/form-engine/field-control/add-record.ts +40 -0
  101. package/backend/form-engine/field-control/edit-popup.ts +64 -0
  102. package/backend/form-engine/field-control/insert-clipboard.ts +50 -0
  103. package/backend/form-engine/field-control/link-popup.ts +53 -0
  104. package/backend/form-engine/field-control/list-module.ts +40 -0
  105. package/backend/form-engine/field-control/password-generator.ts +102 -0
  106. package/backend/form-engine/field-control/reset-selection.ts +48 -0
  107. package/backend/form-engine/field-wizard/localization-state-selector.ts +67 -0
  108. package/backend/form-engine/field-wizard/value-picker.ts +123 -0
  109. package/backend/form-engine/field-wizard/value-slider.ts +86 -0
  110. package/backend/form-engine/inline-relation/ajax-dispatcher.ts +124 -0
  111. package/backend/form-engine/inline-relation/inline-response-interface.ts +41 -0
  112. package/backend/form-engine-link-browser-adapter.ts +129 -0
  113. package/backend/form-engine-review.ts +155 -0
  114. package/backend/form-engine-suggest.ts +133 -0
  115. package/backend/form-engine-validation.ts +686 -0
  116. package/backend/form-engine.ts +1391 -0
  117. package/backend/global-event-handler.ts +252 -0
  118. package/backend/grid-editor.ts +1077 -0
  119. package/backend/hashing/md5.ts +252 -0
  120. package/backend/helper.ts +24 -0
  121. package/backend/hotkeys/hotkey-storage.ts +63 -0
  122. package/backend/hotkeys.ts +216 -0
  123. package/backend/icons.ts +236 -0
  124. package/backend/image-manipulation.ts +885 -0
  125. package/backend/info-window.ts +46 -0
  126. package/backend/input/clearable.ts +128 -0
  127. package/backend/input/index.d.ts +8 -0
  128. package/backend/java-script-module-import-event-handler.ts +19 -0
  129. package/backend/key-bindings.ts +42 -0
  130. package/backend/layout-module/drag-drop.ts +359 -0
  131. package/backend/layout-module/paste.ts +165 -0
  132. package/backend/link-browser.ts +89 -0
  133. package/backend/live-search/element/backend-search.ts +34 -0
  134. package/backend/live-search/element/hint.ts +33 -0
  135. package/backend/live-search/element/provider/default-result-item.ts +52 -0
  136. package/backend/live-search/element/provider/page-provider-result-item.ts +52 -0
  137. package/backend/live-search/element/result/item/action/action-container.ts +135 -0
  138. package/backend/live-search/element/result/item/action/action.ts +56 -0
  139. package/backend/live-search/element/result/item/item-container.ts +224 -0
  140. package/backend/live-search/element/result/item/item.ts +74 -0
  141. package/backend/live-search/element/result/result-container.ts +110 -0
  142. package/backend/live-search/element/result/result-detail-container.ts +50 -0
  143. package/backend/live-search/element/result/result-pagination.ts +122 -0
  144. package/backend/live-search/element/search-option-item.ts +70 -0
  145. package/backend/live-search/live-search-configurator.ts +54 -0
  146. package/backend/live-search/live-search-shortcut.ts +29 -0
  147. package/backend/live-search/result-types/backend-module-result-type.ts +8 -0
  148. package/backend/live-search/result-types/default-result-type.ts +21 -0
  149. package/backend/live-search/result-types/page-result-type.ts +23 -0
  150. package/backend/localization.ts +390 -0
  151. package/backend/login-refresh.ts +525 -0
  152. package/backend/login.ts +179 -0
  153. package/backend/mail-link-handler.ts +45 -0
  154. package/backend/modal-interface.ts +19 -0
  155. package/backend/modal.ts +685 -0
  156. package/backend/module/iframe.ts +125 -0
  157. package/backend/module/router.ts +275 -0
  158. package/backend/module-menu.ts +550 -0
  159. package/backend/module.ts +133 -0
  160. package/backend/multi-record-selection-action.ts +44 -0
  161. package/backend/multi-record-selection-delete-action.ts +108 -0
  162. package/backend/multi-record-selection-edit-action.ts +56 -0
  163. package/backend/multi-record-selection.ts +468 -0
  164. package/backend/multi-step-wizard.ts +568 -0
  165. package/backend/new-content-element-wizard-button.ts +51 -0
  166. package/backend/new-multiple-pages.ts +108 -0
  167. package/backend/new-record-wizard.ts +680 -0
  168. package/backend/notification.ts +366 -0
  169. package/backend/offset.ts +40 -0
  170. package/backend/online-media.ts +110 -0
  171. package/backend/page-actions.ts +91 -0
  172. package/backend/page-link-handler.ts +51 -0
  173. package/backend/pagetsconfig/pagetsconfig-includes.ts +77 -0
  174. package/backend/popover.ts +165 -0
  175. package/backend/record-download-button.ts +104 -0
  176. package/backend/record-link-handler.ts +31 -0
  177. package/backend/record-search.ts +44 -0
  178. package/backend/recordlist.ts +230 -0
  179. package/backend/resource/resource.ts +35 -0
  180. package/backend/security/element/csp-reports.ts +406 -0
  181. package/backend/security/element/sudo-mode.ts +249 -0
  182. package/backend/security/sudo-mode-interceptor.ts +19 -0
  183. package/backend/settings/editor/editable-setting.ts +231 -0
  184. package/backend/settings/editor.ts +451 -0
  185. package/backend/settings/enum/settings-mode.enum.ts +24 -0
  186. package/backend/settings/type/base.ts +200 -0
  187. package/backend/settings/type/bool.ts +56 -0
  188. package/backend/settings/type/color.ts +64 -0
  189. package/backend/settings/type/int.ts +85 -0
  190. package/backend/settings/type/number.ts +61 -0
  191. package/backend/settings/type/page.ts +100 -0
  192. package/backend/settings/type/string.ts +77 -0
  193. package/backend/settings/type/stringlist.ts +87 -0
  194. package/backend/severity.ts +88 -0
  195. package/backend/site-inline-actions.ts +32 -0
  196. package/backend/sortable-table.ts +208 -0
  197. package/backend/storage/abstract-client-storage.ts +76 -0
  198. package/backend/storage/browser-session.ts +28 -0
  199. package/backend/storage/client.ts +28 -0
  200. package/backend/storage/module-state-storage.ts +118 -0
  201. package/backend/storage/persistent.ts +204 -0
  202. package/backend/switch-user.ts +82 -0
  203. package/backend/tabs.ts +72 -0
  204. package/backend/telephone-link-handler.ts +40 -0
  205. package/backend/tests/backend/storage/browser-storage-test.ts +87 -0
  206. package/backend/tests/backend/storage/client-test.ts +87 -0
  207. package/backend/tests/backend/utility/dom-helper-test.ts +51 -0
  208. package/backend/tests/backend-exception-test.ts +28 -0
  209. package/backend/tests/element/immediate-action-element-test.ts +81 -0
  210. package/backend/tests/form-engine-validation-test.ts +304 -0
  211. package/backend/tests/grid-editor-test.ts +29 -0
  212. package/backend/tests/hashing/md5Test.ts +28 -0
  213. package/backend/tests/icons-test.ts +23 -0
  214. package/backend/tests/notification-test.ts +161 -0
  215. package/backend/tests/popover-test.ts +83 -0
  216. package/backend/toolbar/clear-cache-menu.ts +88 -0
  217. package/backend/toolbar/live-search.ts +284 -0
  218. package/backend/toolbar/shortcut-menu.ts +254 -0
  219. package/backend/toolbar/system-information-menu.ts +161 -0
  220. package/backend/toolbar.ts +41 -0
  221. package/backend/tree/file-storage-browser.ts +194 -0
  222. package/backend/tree/file-storage-tree-container.ts +465 -0
  223. package/backend/tree/file-storage-tree.ts +42 -0
  224. package/backend/tree/page-browser.ts +272 -0
  225. package/backend/tree/page-tree-element.ts +602 -0
  226. package/backend/tree/page-tree.ts +152 -0
  227. package/backend/tree/tree-module-state.ts +159 -0
  228. package/backend/tree/tree-node-toggle.ts +31 -0
  229. package/backend/tree/tree-node.ts +97 -0
  230. package/backend/tree/tree-toolbar.ts +117 -0
  231. package/backend/tree/tree.ts +1595 -0
  232. package/backend/url-link-handler.ts +36 -0
  233. package/backend/user-pass-login.ts +255 -0
  234. package/backend/user-settings-manager.ts +189 -0
  235. package/backend/utility/collapse-state-persister.ts +205 -0
  236. package/backend/utility/collapse-state-search.ts +183 -0
  237. package/backend/utility/dom-helper.ts +91 -0
  238. package/backend/utility/drag-drop-utility.ts +47 -0
  239. package/backend/utility/message-utility.ts +45 -0
  240. package/backend/utility/top-level-module-import.ts +32 -0
  241. package/backend/utility.ts +194 -0
  242. package/backend/viewport/abstract-container.ts +22 -0
  243. package/backend/viewport/content-container.ts +98 -0
  244. package/backend/viewport/loader.ts +28 -0
  245. package/backend/viewport/navigation-container.ts +119 -0
  246. package/backend/viewport/resizable-navigation.ts +176 -0
  247. package/backend/viewport/toolbar.ts +27 -0
  248. package/backend/viewport/topbar.ts +39 -0
  249. package/backend/viewport.ts +46 -0
  250. package/backend/window-manager.ts +84 -0
  251. package/backend/wizard/move-content-element.ts +107 -0
  252. package/backend/wizard/move-page.ts +74 -0
  253. package/backend/wizard.ts +331 -0
  254. package/belog/backend-log.ts +88 -0
  255. package/beuser/backend-user-listing.ts +40 -0
  256. package/beuser/permissions.ts +291 -0
  257. package/core/ajax/ajax-request-types.ts +15 -0
  258. package/core/ajax/ajax-request.ts +202 -0
  259. package/core/ajax/ajax-response.ts +57 -0
  260. package/core/ajax/input-transformer.ts +92 -0
  261. package/core/ajax/simple-response-interface.ts +5 -0
  262. package/core/authentication/mfa-provider/totp.ts +54 -0
  263. package/core/cache/dedupe-async-task.ts +95 -0
  264. package/core/directive/markdown.ts +82 -0
  265. package/core/directive/nl2br.ts +16 -0
  266. package/core/document-service.ts +35 -0
  267. package/core/event/debounce-event.ts +40 -0
  268. package/core/event/event-interface.ts +24 -0
  269. package/core/event/regular-event.ts +58 -0
  270. package/core/event/request-animation-frame-event.ts +42 -0
  271. package/core/event/throttle-event.ts +54 -0
  272. package/core/java-script-item-handler.ts +29 -0
  273. package/core/java-script-item-processor.ts +171 -0
  274. package/core/lit-helper.ts +126 -0
  275. package/core/literals.ts +17 -0
  276. package/core/referrer-refresh.ts +2 -0
  277. package/core/security-utility.ts +89 -0
  278. package/core/tests/ajax/ajax-request-test.ts +290 -0
  279. package/core/tests/ajax/input-transformer-test.ts +64 -0
  280. package/core/tests/cache/dedupe-async-task-test.ts +152 -0
  281. package/core/tests/event/throttle-event-test.ts +47 -0
  282. package/core/tests/literals-test.ts +24 -0
  283. package/core/tests/security-utility-test.ts +50 -0
  284. package/core/utility/types.ts +9 -0
  285. package/dashboard/chart-initializer.ts +136 -0
  286. package/dashboard/dashboard.ts +1487 -0
  287. package/extensionmanager/distribution-image.ts +55 -0
  288. package/extensionmanager/main.ts +291 -0
  289. package/extensionmanager/repository.ts +195 -0
  290. package/extensionmanager/update.ts +118 -0
  291. package/extensionmanager/upload-form.ts +42 -0
  292. package/filelist/browse-files.ts +148 -0
  293. package/filelist/browse-folders.ts +94 -0
  294. package/filelist/context-menu-actions.ts +335 -0
  295. package/filelist/create-folder.ts +69 -0
  296. package/filelist/file-delete.ts +69 -0
  297. package/filelist/file-list-actions.ts +132 -0
  298. package/filelist/file-list-dragdrop.ts +139 -0
  299. package/filelist/file-list-rename-handler.ts +128 -0
  300. package/filelist/file-list-transfer-handler.ts +138 -0
  301. package/filelist/file-list.ts +396 -0
  302. package/filelist/file-replace.ts +37 -0
  303. package/filelist/linkbrowser-file-handler.ts +94 -0
  304. package/filelist/linkbrowser-folder-handler.ts +74 -0
  305. package/filelist/rename-file.ts +109 -0
  306. package/form/backend/form-editor/component/property-grid-editor.ts +340 -0
  307. package/form/backend/form-editor/core.ts +2104 -0
  308. package/form/backend/form-editor/helper.ts +226 -0
  309. package/form/backend/form-editor/inspector-component.ts +2323 -0
  310. package/form/backend/form-editor/mediator.ts +941 -0
  311. package/form/backend/form-editor/modals-component.ts +433 -0
  312. package/form/backend/form-editor/stage-component.ts +908 -0
  313. package/form/backend/form-editor/tree-component.ts +548 -0
  314. package/form/backend/form-editor/view-model.ts +1343 -0
  315. package/form/backend/form-editor.ts +846 -0
  316. package/form/backend/form-manager/main.ts +39 -0
  317. package/form/backend/form-manager/view-model.ts +789 -0
  318. package/form/backend/form-manager.ts +180 -0
  319. package/form/backend/helper.ts +60 -0
  320. package/form/frontend/date-picker.ts +38 -0
  321. package/frontend/default_frontend.ts +82 -0
  322. package/impexp/context-menu-actions.ts +50 -0
  323. package/impexp/import-export.ts +62 -0
  324. package/indexed-search/pagination.ts +26 -0
  325. package/indexed-search/statistics.ts +50 -0
  326. package/info/translation-status.ts +48 -0
  327. package/install/ajax/ajax-queue.ts +83 -0
  328. package/install/init-install.ts +2 -0
  329. package/install/init-installer.ts +2 -0
  330. package/install/install.ts +25 -0
  331. package/install/installer.ts +458 -0
  332. package/install/message-interface.ts +18 -0
  333. package/install/module/abstract-inline-module.ts +27 -0
  334. package/install/module/abstract-interactable-module.ts +107 -0
  335. package/install/module/environment/environment-check.ts +92 -0
  336. package/install/module/environment/folder-structure.ts +145 -0
  337. package/install/module/environment/image-processing.ts +141 -0
  338. package/install/module/environment/mail-test.ts +127 -0
  339. package/install/module/environment/php-info.ts +50 -0
  340. package/install/module/environment/system-information.ts +50 -0
  341. package/install/module/maintenance/cache.ts +59 -0
  342. package/install/module/maintenance/clear-tables.ts +134 -0
  343. package/install/module/maintenance/clear-typo3temp-files.ts +137 -0
  344. package/install/module/maintenance/create-admin.ts +126 -0
  345. package/install/module/maintenance/database-analyzer.ts +266 -0
  346. package/install/module/maintenance/dump-autoload.ts +58 -0
  347. package/install/module/maintenance/language-packs.ts +338 -0
  348. package/install/module/maintenance/reset-backend-user-uc.ts +58 -0
  349. package/install/module/password-strength.ts +56 -0
  350. package/install/module/settings/change-install-tool-password.ts +101 -0
  351. package/install/module/settings/extension-configuration.ts +224 -0
  352. package/install/module/settings/features.ts +102 -0
  353. package/install/module/settings/local-configuration.ts +316 -0
  354. package/install/module/settings/presets.ts +147 -0
  355. package/install/module/settings/system-maintainer.ts +111 -0
  356. package/install/module/upgrade/core-update.ts +255 -0
  357. package/install/module/upgrade/extension-compat-tester.ts +195 -0
  358. package/install/module/upgrade/extension-scanner.ts +381 -0
  359. package/install/module/upgrade/tca-ext-tables-check.ts +93 -0
  360. package/install/module/upgrade/tca-migrations-check.ts +101 -0
  361. package/install/module/upgrade/upgrade-docs.ts +310 -0
  362. package/install/module/upgrade/upgrade-wizards.ts +612 -0
  363. package/install/renderable/clearable.ts +113 -0
  364. package/install/renderable/flash-message.ts +64 -0
  365. package/install/renderable/info-box.ts +66 -0
  366. package/install/renderable/language-packs.ts +304 -0
  367. package/install/renderable/offset-group.ts +38 -0
  368. package/install/renderable/severity.ts +49 -0
  369. package/install/renderable/wrap-group.ts +36 -0
  370. package/install/router.ts +468 -0
  371. package/linkvalidator/linkvalidator-modal.ts +45 -0
  372. package/linkvalidator/linkvalidator.ts +101 -0
  373. package/lowlevel/query-generator.ts +132 -0
  374. package/lowlevel/reference-index.ts +44 -0
  375. package/opendocs/toolbar/opendocs-menu.ts +117 -0
  376. package/package.json +34 -0
  377. package/recycler/recycler.ts +539 -0
  378. package/redirects/event-handler.ts +108 -0
  379. package/redirects/form-engine-evaluation.ts +34 -0
  380. package/redirects/redirects-module.ts +58 -0
  381. package/rte-ckeditor/ckeditor5.ts +402 -0
  382. package/rte-ckeditor/css-prefixer.ts +94 -0
  383. package/rte-ckeditor/legacy/ckeditor5-bundle.ts +17 -0
  384. package/rte-ckeditor/legacy/ckeditor5-inspector.ts +2 -0
  385. package/rte-ckeditor/plugin/typo3-link.ts +810 -0
  386. package/rte-ckeditor/plugin/whitespace.ts +114 -0
  387. package/rte-ckeditor/rte-link-browser.ts +112 -0
  388. package/rte-ckeditor/tests/css-prefixer-test.ts +314 -0
  389. package/scheduler/scheduler-add-group.ts +119 -0
  390. package/scheduler/scheduler-editable-group-name.ts +307 -0
  391. package/scheduler/scheduler-sortable-groups.ts +59 -0
  392. package/scheduler/scheduler.ts +466 -0
  393. package/setup/setup-module.ts +122 -0
  394. package/styleguide/checkbox-demo.ts +5 -0
  395. package/styleguide/element/theme-switcher-element.ts +101 -0
  396. package/styleguide/filter.ts +22 -0
  397. package/styleguide/find-icons.ts +78 -0
  398. package/styleguide/processing-indicator.ts +78 -0
  399. package/styleguide/render-notifications.ts +64 -0
  400. package/sys-note/element/delete-button.ts +78 -0
  401. package/tstemplate/constant-editor.ts +64 -0
  402. package/tstemplate/information-module.ts +31 -0
  403. package/tstemplate/template-analyzer.ts +77 -0
  404. package/viewpage/main.ts +253 -0
  405. package/workspaces/backend.ts +979 -0
  406. package/workspaces/preview.ts +232 -0
  407. package/workspaces/renderable/comment-view.ts +79 -0
  408. package/workspaces/renderable/diff-view.ts +58 -0
  409. package/workspaces/renderable/history-view.ts +97 -0
  410. package/workspaces/renderable/record-information.ts +118 -0
  411. package/workspaces/renderable/record-table.ts +355 -0
  412. package/workspaces/renderable/send-to-stage-form.ts +108 -0
  413. package/workspaces/toolbar/workspaces-menu.ts +190 -0
  414. package/workspaces/utility/icon-helper.ts +57 -0
  415. package/workspaces/workspaces.ts +136 -0
@@ -0,0 +1,231 @@
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 { html, LitElement, type TemplateResult, nothing } from 'lit';
15
+ import { customElement, property, state } from 'lit/decorators';
16
+ import { until } from 'lit/directives/until.js';
17
+ import '@typo3/backend/element/icon-element';
18
+ import { copyToClipboard } from '@typo3/backend/copy-to-clipboard';
19
+ import Notification from '@typo3/backend/notification';
20
+ import { lll } from '@typo3/core/lit-helper';
21
+ import { markdown } from '@typo3/core/directive/markdown';
22
+ import AjaxRequest from '@typo3/core/ajax/ajax-request';
23
+ import type { BaseElement } from '@typo3/backend/settings/type/base';
24
+ import { SettingsMode, sanitizeSettingsMode } from '@typo3/backend/settings/enum/settings-mode.enum';
25
+
26
+ type ValueType = string|number|boolean|string[]|null;
27
+
28
+ /** @see \TYPO3\CMS\Core\Settings\SettingDefinition */
29
+ interface SettingDefinition {
30
+ key: string,
31
+ type: string,
32
+ default: ValueType,
33
+ label: string,
34
+ description: string|null,
35
+ readonly: boolean,
36
+ // @todo php json_encode encodes ['0' => 'foo'] as ['foo'] instead of {'0' => 'foo'}
37
+ enum: Record<string, string>|Array<string>,
38
+ categories: string[],
39
+ tags: string[],
40
+ options: Record<string, unknown>,
41
+ }
42
+
43
+ /** @see \TYPO3\CMS\Backend\Dto\Settings\EditableSetting */
44
+ interface EditableSetting {
45
+ definition: SettingDefinition,
46
+ value: ValueType,
47
+ systemDefault: ValueType,
48
+ status: string,
49
+ warnings: string[],
50
+ typeImplementation: string,
51
+ }
52
+
53
+ @customElement('typo3-backend-editable-setting')
54
+ export class EditableSettingElement extends LitElement {
55
+
56
+ @property({ type: Object }) setting: EditableSetting;
57
+ @property({ type: String }) dumpuri: string;
58
+ @property({ type: String, converter: sanitizeSettingsMode }) mode: SettingsMode = SettingsMode.basic;
59
+
60
+ @state() hasChange: boolean = false;
61
+
62
+ typeElement: BaseElement<unknown> = null;
63
+
64
+ protected override createRenderRoot(): HTMLElement | ShadowRoot {
65
+ return this;
66
+ }
67
+
68
+ protected override render(): TemplateResult {
69
+ const { value, systemDefault, definition } = this.setting;
70
+ return html`
71
+ <div
72
+ class=${`settings-item settings-item-${definition.type} ${this.hasChange ? 'has-change' : ''}`}
73
+ tabindex="0"
74
+ data-status=${JSON.stringify(value) === JSON.stringify(systemDefault) ? 'none' : 'modified'}
75
+ >
76
+ <!-- data-status=modified|error|none-->
77
+ <div class="settings-item-indicator"></div>
78
+ <div class="settings-item-title">
79
+ <label for=${`setting-${definition.key}`} class="settings-item-label">${definition.label}</label>
80
+ <div class="settings-item-description">${markdown(definition.description ?? '', 'minimal')}</div>
81
+ ${this.mode === SettingsMode.advanced ? html`<div class="settings-item-key">${definition.key}</div>` : nothing}
82
+ </div>
83
+ <div class="settings-item-control">
84
+ ${until(this.renderField(), nothing)}
85
+ </div>
86
+ <div class="settings-item-message"></div>
87
+ <div class="settings-item-actions">
88
+ ${this.renderActions()}
89
+ </div>
90
+ </div>
91
+ `;
92
+ }
93
+
94
+ protected renderField(): HTMLElement|Promise<HTMLElement> {
95
+ if (this.typeElement !== null) {
96
+ this.updateFieldAttributes(this.typeElement);
97
+ return this.typeElement;
98
+ }
99
+
100
+ return (async (): Promise<HTMLElement> => {
101
+ const { typeImplementation } = this.setting;
102
+ const implementation = await import(typeImplementation);
103
+ if (!('componentName' in implementation)) {
104
+ throw new Error(`module ${typeImplementation} is missing the "componentName" export`);
105
+ }
106
+ const element = document.createElement(implementation.componentName);
107
+
108
+ element.addEventListener('typo3:setting:changed', (e: CustomEvent) => {
109
+ this.hasChange = JSON.stringify(this.setting.value) !== JSON.stringify(e.detail.value);
110
+ });
111
+
112
+ this.updateFieldAttributes(element);
113
+ this.typeElement = element;
114
+ return element;
115
+ })();
116
+ }
117
+
118
+ protected updateFieldAttributes(element: HTMLElement): void {
119
+ const { definition, value } = this.setting;
120
+ // Force conversion to an object, as PHP json_encode encodes ['0' => 'foo'] as
121
+ // ['foo'] instead of {'0' => 'foo'}
122
+ const enumEntries = Object.entries(definition.enum || {});
123
+
124
+ const attributes = {
125
+ key: definition.key,
126
+ formid: `setting-${definition.key}`,
127
+ name: `settings[${definition.key}]`,
128
+ value: Array.isArray(value) ? JSON.stringify(value) : String(value),
129
+ debug: this.mode === SettingsMode.advanced,
130
+ readonly: definition.readonly,
131
+ enum: enumEntries.length > 0 ? JSON.stringify(Object.fromEntries(enumEntries)) : false,
132
+ default: Array.isArray(definition.default) ? JSON.stringify(definition.default) : String(definition.default),
133
+ options: definition.options ? (Array.isArray(definition.options) && definition.options.length === 0 ? '{}' : JSON.stringify(definition.options)) : '{}',
134
+ };
135
+ for (const [key, value] of Object.entries(attributes)) {
136
+ if (typeof value === 'boolean') {
137
+ if (value && !element.hasAttribute(key)) {
138
+ element.setAttribute(key, '');
139
+ }
140
+ if (!value && element.hasAttribute(key)) {
141
+ element.removeAttribute(key);
142
+ }
143
+ continue;
144
+ }
145
+ if (element.getAttribute(key) !== value) {
146
+ element.setAttribute(key, value);
147
+ }
148
+ }
149
+ }
150
+
151
+ protected renderActions(): TemplateResult {
152
+ const { definition } = this.setting;
153
+ return html`
154
+ <div class="dropdown">
155
+ <button class="dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
156
+ <typo3-backend-icon identifier="actions-cog" size="small"></typo3-backend-icon>
157
+ <span class="visually-hidden">More actions</span>
158
+ </button>
159
+ <ul class="dropdown-menu">
160
+ <li>
161
+ <button class="dropdown-item dropdown-item-spaced"
162
+ type="button"
163
+ ?disabled=${definition.readonly}
164
+ @click="${() => this.setToDefaultValue()}">
165
+ <typo3-backend-icon identifier="actions-undo" size="small"></typo3-backend-icon> ${lll('settingseditor.edit.resetSetting')}
166
+ </button>
167
+ </li>
168
+ ${this.mode === SettingsMode.advanced ? html`
169
+ <li><hr class="dropdown-divider"></li>
170
+ <li>
171
+ <typo3-copy-to-clipboard
172
+ text=${definition.key}
173
+ class="dropdown-item dropdown-item-spaced"
174
+ >
175
+ <typo3-backend-icon identifier="actions-clipboard" size="small"></typo3-backend-icon> ${lll('settingseditor.edit.copySettingsIdentifier')}
176
+ </typo3-copy-to-clipboard>
177
+ </li>
178
+ ${this.dumpuri ? html`
179
+ <li>
180
+ <button class="dropdown-item dropdown-item-spaced"
181
+ type="button"
182
+ @click="${() => this.copyAsYaml()}">
183
+ <typo3-backend-icon identifier="actions-clipboard-paste" size="small"></typo3-backend-icon> ${lll('settingseditor.edit.copyAsYaml')}
184
+ </a>
185
+ </li>
186
+ ` : nothing}
187
+ ` : nothing}
188
+ </ul>
189
+ </div>
190
+ `;
191
+ }
192
+
193
+ protected setToDefaultValue(): void {
194
+ if (this.typeElement) {
195
+ this.typeElement.value = this.setting.systemDefault as unknown;
196
+ // Explicitly request update, since the live value may be different
197
+ // to the property value, if the live value is an invalid value
198
+ this.typeElement.requestUpdate('value');
199
+ }
200
+ }
201
+
202
+ protected async copyAsYaml(): Promise<void> {
203
+ const formData = new FormData(this.typeElement.form);
204
+ const name = `settings[${this.setting.definition.key}]`;
205
+ const value = formData.get(name);
206
+
207
+ const data = new FormData();
208
+ data.append('specificSetting', this.setting.definition.key);
209
+ data.append(name, value);
210
+
211
+ // @todo hookup with NProgress
212
+ const response = await new AjaxRequest(this.dumpuri).post(
213
+ data
214
+ );
215
+
216
+ const result = await response.resolve();
217
+
218
+ if (typeof result.yaml === 'string') {
219
+ copyToClipboard(result.yaml);
220
+ } else {
221
+ console.warn('Value can not be copied to clipboard.', typeof result.yaml);
222
+ Notification.error(lll('copyToClipboard.error'));
223
+ }
224
+ }
225
+ }
226
+
227
+ declare global {
228
+ interface HTMLElementTagNameMap {
229
+ 'typo3-backend-editable-setting': EditableSettingElement;
230
+ }
231
+ }
@@ -0,0 +1,451 @@
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 { html, LitElement, type TemplateResult, nothing, type PropertyValues } from 'lit';
15
+ import { customElement, property, state } from 'lit/decorators';
16
+ import { live } from 'lit/directives/live.js';
17
+ import '@typo3/backend/element/spinner-element';
18
+ import '@typo3/backend/element/icon-element';
19
+ import Notification from '@typo3/backend/notification';
20
+ import DomHelper from '@typo3/backend/utility/dom-helper';
21
+ import AjaxRequest from '@typo3/core/ajax/ajax-request';
22
+ import { copyToClipboard } from '@typo3/backend/copy-to-clipboard';
23
+ import { lll } from '@typo3/core/lit-helper';
24
+ import { markdown } from '@typo3/core/directive/markdown';
25
+ import '@typo3/backend/settings/editor/editable-setting';
26
+ import { SettingsMode, sanitizeSettingsMode } from '@typo3/backend/settings/enum/settings-mode.enum';
27
+ import '@typo3/backend/element/icon-element';
28
+
29
+ // preload known/common types
30
+ import '@typo3/backend/settings/type/bool';
31
+ import '@typo3/backend/settings/type/int';
32
+ import '@typo3/backend/settings/type/number';
33
+ import '@typo3/backend/settings/type/string';
34
+ import '@typo3/backend/settings/type/stringlist';
35
+
36
+ type ValueType = string|number|boolean|string[]|null;
37
+
38
+ export interface Category {
39
+ key: string,
40
+ label: string,
41
+ description: string,
42
+ icon: string,
43
+ settings: EditableSetting[],
44
+ categories: Category[],
45
+ }
46
+
47
+ interface FilteredCategory extends Category {
48
+ settings: FilteredEditableSetting[],
49
+ categories: FilteredCategory[],
50
+ // runtime value calculated depending on filter (user entered search term)
51
+ __hidden: boolean,
52
+ }
53
+
54
+ /** @see \TYPO3\CMS\Core\Settings\SettingDefinition */
55
+ export interface SettingDefinition {
56
+ key: string,
57
+ type: string,
58
+ default: ValueType,
59
+ label: string,
60
+ description?: string|null,
61
+ enum: ValueType[],
62
+ categories: string[],
63
+ tags: string[],
64
+ options: Record<string, unknown>,
65
+ }
66
+
67
+ /** @see \TYPO3\CMS\Backend\Dto\Settings\EditableSetting */
68
+ export interface EditableSetting {
69
+ definition: SettingDefinition,
70
+ value: ValueType,
71
+ systemDefault: ValueType,
72
+ status: string,
73
+ warnings: string[],
74
+ typeImplementation: string,
75
+ }
76
+
77
+ interface FilteredEditableSetting extends EditableSetting {
78
+ // runtime value calculated depending on filter (user entered search term)
79
+ __hidden: boolean,
80
+ }
81
+
82
+ export type SettingsEditorNormalizedFormData = Record<string, string> & {
83
+ settings: Record<string, string>;
84
+ };
85
+
86
+ export class SettingsEditorSubmitEvent extends Event {
87
+ static readonly eventName = 'typo3:settings-editor:submit';
88
+
89
+ constructor(
90
+ public readonly originalEvent: SubmitEvent,
91
+ public readonly formData: SettingsEditorNormalizedFormData,
92
+ ) {
93
+ super(SettingsEditorSubmitEvent.eventName, {
94
+ bubbles: true,
95
+ composed: true,
96
+ cancelable: false,
97
+ });
98
+ }
99
+ }
100
+
101
+ @customElement('typo3-backend-settings-editor')
102
+ export class SettingsEditorElement extends LitElement {
103
+
104
+ @property({ type: Array }) categories: Category[];
105
+ @property({ type: String, attribute: 'form-name' }) formName: string = 'settings_form';
106
+ @property({ type: String, attribute: 'action-url' }) actionUrl: string;
107
+ @property({ type: String, attribute: 'dump-url' }) dumpUrl: string;
108
+ @property({ type: Object, attribute: 'custom-form-data' }) customFormData: Record<string, string> = {};
109
+ @property({ type: String, converter: sanitizeSettingsMode }) mode: SettingsMode = SettingsMode.basic;
110
+
111
+ @state() searchTerm: string = '';
112
+ @state() activeCategory: string = '';
113
+
114
+ visibleCategories: Record<string, boolean> = {};
115
+ observer: IntersectionObserver = null;
116
+
117
+ public override disconnectedCallback() {
118
+ super.disconnectedCallback();
119
+ this.observer?.disconnect();
120
+ }
121
+
122
+ protected override createRenderRoot(): HTMLElement | ShadowRoot {
123
+ return this;
124
+ }
125
+
126
+ protected adjustNavigationSize(): void {
127
+ const scrollableParent = DomHelper.scrollableParent(this);
128
+ const container = this.querySelector('.settings-navigation-inner') as HTMLElement;
129
+ const settingsSearchElement = this.querySelector('.settings-search') as HTMLElement | null;
130
+ const settingsNavigationElement = this.querySelector('.settings-navigation') as HTMLElement | null;
131
+ if (settingsNavigationElement === null) {
132
+ return;
133
+ }
134
+
135
+ if (container) {
136
+ const scrollableParentRect = scrollableParent.getBoundingClientRect();
137
+ const navigationRect = settingsNavigationElement.getBoundingClientRect();
138
+ const startPosition = settingsSearchElement?.getBoundingClientRect().bottom ?? Math.max(scrollableParentRect.top, container.getBoundingClientRect().top);
139
+ const maxHeight = scrollableParentRect.bottom - Math.max(0, scrollableParentRect.bottom - navigationRect.bottom) - startPosition;
140
+ container.style.maxHeight = `${maxHeight}px`;
141
+ }
142
+ }
143
+
144
+ protected override firstUpdated(): void {
145
+ const scrollableParent = DomHelper.scrollableParent(this);
146
+ scrollableParent.addEventListener('scroll', () => {
147
+ this.adjustNavigationSize();
148
+ });
149
+ }
150
+
151
+ protected override updated(changedProperties: PropertyValues<this>): void {
152
+ if (changedProperties.has('mode') && this.mode === SettingsMode.minimal) {
153
+ this.observer?.disconnect();
154
+ this.observer = null;
155
+ } else if (changedProperties.has('mode') && this.mode !== SettingsMode.minimal) {
156
+ this.observer = new IntersectionObserver(
157
+ (entries) => {
158
+ entries.forEach(entry => {
159
+ const key = (entry.target as HTMLElement).dataset.key;
160
+ this.visibleCategories[key] = entry.isIntersecting;
161
+ });
162
+ const flatten = (list: Category[]): string[] => list.reduce((acc, c) => [...acc, c.key, ...flatten(c.categories)], []);
163
+ const active = flatten(this.categories).filter(key => this.visibleCategories[key])[0] || '';
164
+ if (active) {
165
+ this.activeCategory = active;
166
+ }
167
+ },
168
+ {
169
+ root: DomHelper.scrollableParent(this),
170
+ threshold: 0.1,
171
+ }
172
+ );
173
+ }
174
+
175
+ [...this.renderRoot.querySelectorAll('.settings-category')].map(entry => this.observer?.observe(entry));
176
+ this.adjustNavigationSize();
177
+
178
+ if (changedProperties.has('activeCategory')) {
179
+ const container = this.querySelector('.settings-navigation-inner');
180
+ const activeElement = this.querySelector('.settings-navigation-item.active') as HTMLElement;
181
+ if (container && activeElement) {
182
+ const currentScrollPosition = container.scrollTop;
183
+ const containerHeight = container.getBoundingClientRect().height;
184
+ const nodeHeight = activeElement.getBoundingClientRect().height;
185
+ const nodeFitsTop = activeElement.offsetTop >= currentScrollPosition;
186
+ const nodeFitsBottom = activeElement.offsetTop + nodeHeight <= currentScrollPosition + containerHeight;
187
+ if (!nodeFitsTop) {
188
+ this.querySelector('.settings-navigation-inner').scrollTo({ top: Math.max(0, activeElement.offsetTop - nodeHeight), behavior: 'auto' });
189
+ } else if (!nodeFitsBottom) {
190
+ this.querySelector('.settings-navigation-inner').scrollTo({ top: Math.max(0, activeElement.offsetTop + nodeHeight), behavior: 'auto' });
191
+ }
192
+ }
193
+ }
194
+ }
195
+
196
+ protected renderCategoryTree(categories: FilteredCategory[], level: number): TemplateResult {
197
+ return html`
198
+ <ul data-level=${level}>
199
+ ${categories.map(category => html`
200
+ <li ?hidden=${category.__hidden}>
201
+ <button
202
+ type="button"
203
+ @click=${(event: PointerEvent) => { event.preventDefault(); this.selectCategory(category);}}
204
+ class="settings-navigation-item ${this.activeCategory === category.key ? 'active' : ''}"
205
+ >
206
+ <span class="settings-navigation-item-icon">
207
+ <typo3-backend-icon identifier=${category.icon ? category.icon : 'actions-chevron-end'} size="small"></typo3-backend-icon>
208
+ </span>
209
+ <span class="settings-navigation-item-label">${category.label}</span>
210
+ </button>
211
+ ${category.categories.length === 0 ? nothing : html`
212
+ ${this.renderCategoryTree(category.categories, level + 1)}
213
+ `}
214
+ </li>
215
+ `)}
216
+ </ul>
217
+ `;
218
+ }
219
+
220
+ protected renderSettings(categories: FilteredCategory[], level: number): TemplateResult[] {
221
+ return categories.map(category => html`
222
+ <div class="settings-category-list" data-key=${category.key}>
223
+ <div class="settings-category" data-key=${category.key} ?hidden=${category.__hidden}>
224
+ ${this.renderHeadline(Math.min(level + 1, 6), `category-headline-${category.key}`, category.icon, html`${category.label}`)}
225
+ <div class="settings-category-description">
226
+ ${category.description ? markdown(category.description, 'minimal') : nothing}
227
+ </div>
228
+ </div>
229
+ ${category.settings.map((setting): TemplateResult => html`
230
+ <typo3-backend-editable-setting
231
+ ?hidden=${setting.__hidden}
232
+ .setting=${setting}
233
+ .dumpuri=${this.dumpUrl}
234
+ .mode=${this.mode}
235
+ ></typo3-backend-editable-setting>
236
+ `)}
237
+ </div>
238
+ ${category.categories.length === 0 ? nothing : html`
239
+ ${this.renderSettings(category.categories, level + 1)}
240
+ `}
241
+ `);
242
+ }
243
+
244
+ protected renderHeadline(level: number, id: string, icon: string, content: TemplateResult): TemplateResult {
245
+ switch (level) {
246
+ case 1:
247
+ return html`<h1 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h1>`;
248
+ case 2:
249
+ return html`<h2 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h2>`;
250
+ case 3:
251
+ return html`<h3 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h3>`;
252
+ case 4:
253
+ return html`<h4 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h4>`;
254
+ case 5:
255
+ return html`<h5 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h5>`;
256
+ case 6:
257
+ return html`<h6 class="settings-category-headline" id=${id}>${icon ? html`<typo3-backend-icon identifier=${icon}></typo3-backend-icon>` : nothing}${content}</h6>`;
258
+ default:
259
+ throw new Error(`Invalid header level: ${level}`);
260
+ }
261
+ }
262
+
263
+ protected selectCategory(category: FilteredCategory): void {
264
+ const targetSelector = `#category-headline-${category.key}`;
265
+ const target = (this.renderRoot.querySelector(targetSelector.replaceAll('.', '\\.')) as HTMLElement);
266
+ const scrollableParent = DomHelper.scrollableParent(this);
267
+ const searchOffset = (this.renderRoot.querySelector('.settings-search') as HTMLElement).offsetHeight;
268
+ const bodyOffset = parseInt(window.getComputedStyle(this.renderRoot.querySelector('.settings-body-inner') as HTMLElement).paddingTop, 10);
269
+ const topPosition = target.offsetTop - searchOffset - bodyOffset;
270
+ scrollableParent.scrollTo({
271
+ top: topPosition,
272
+ behavior: 'smooth'
273
+ });
274
+ this.activeCategory = category.key;
275
+ }
276
+
277
+ protected async onSubmit(e: SubmitEvent): Promise<void> {
278
+ const form = e.target as HTMLFormElement;
279
+ const formData = new FormData(form);
280
+ const normalizedData = {
281
+ settings: {},
282
+ } as SettingsEditorNormalizedFormData;
283
+ formData.forEach((value, key) => {
284
+ const match = key.match(/^settings\[(.+?)\]$/);
285
+ if (match) {
286
+ normalizedData.settings[match[1]] = typeof value === 'string' ? value : (value as File).name;
287
+ } else {
288
+ normalizedData[key] = typeof value === 'string' ? value : (value as File).name;
289
+ }
290
+ });
291
+ this.dispatchEvent(new SettingsEditorSubmitEvent(e, normalizedData));
292
+ if (e.defaultPrevented) {
293
+ return;
294
+ }
295
+
296
+ if ((e.submitter as HTMLButtonElement|null)?.value === 'export') {
297
+ e.preventDefault();
298
+ const formData = new FormData(form);
299
+ const response = await new AjaxRequest(this.dumpUrl).post(formData);
300
+
301
+ const result = await response.resolve();
302
+ if (typeof result.yaml === 'string') {
303
+ copyToClipboard(result.yaml);
304
+ } else {
305
+ console.warn('Value can not be copied to clipboard.', typeof result.yaml);
306
+ Notification.error(lll('copyToClipboard.error'));
307
+ }
308
+ }
309
+ }
310
+
311
+ protected async onSearch(e: Event): Promise<void> {
312
+ e.preventDefault();
313
+ this.searchTerm = (e.currentTarget as HTMLInputElement).value;
314
+ }
315
+
316
+ protected override render(): TemplateResult {
317
+ const categories = this.filterCategories();
318
+ const hasVisibleCategories = categories.filter(c => !c.__hidden).length > 0;
319
+ return html`
320
+ <form class="settings-container"
321
+ id=${this.formName}
322
+ name=${this.formName}
323
+ action=${this.actionUrl}
324
+ method="post"
325
+ @submit=${(e: SubmitEvent) => this.onSubmit(e)}
326
+ >
327
+ ${Object.entries(this.customFormData).map(([name, value]) => html`
328
+ <input type="hidden" name=${name} value=${value}>
329
+ `)}
330
+
331
+ <div class="settings">
332
+
333
+ ${this.mode !== SettingsMode.minimal ? html`
334
+ <div class="settings-search">
335
+ <label for="settings-search" class="visually-hidden">
336
+ ${lll('settingseditor.search.searchTermVisuallyHiddenLabel')}
337
+ </label>
338
+ <input
339
+ type="search"
340
+ id="settings-search"
341
+ class="form-control"
342
+ placeholder=${lll('settingseditor.search.searchTermPlaceholder')}
343
+ .value=${live(this.searchTerm)}
344
+ @change=${(e: Event) => this.onSearch(e)}
345
+ @input=${(e: Event) => this.onSearch(e)}>
346
+ </div>
347
+ ` : nothing }
348
+
349
+ ${this.mode !== SettingsMode.minimal ? html`
350
+ <div class="settings-navigation" ?hidden=${!hasVisibleCategories}>
351
+ <div
352
+ class="settings-navigation-inner"
353
+ @transitionend="${() => this.adjustNavigationSize()}"
354
+ >
355
+ ${this.renderCategoryTree(categories ?? [], 1)}
356
+ </div>
357
+ </div>
358
+ ` : nothing }
359
+
360
+ <div class="settings-body" ?hidden=${!hasVisibleCategories}>
361
+ <div class="settings-body-inner">
362
+ ${this.renderSettings(categories ?? [], 1)}
363
+ </div>
364
+ </div>
365
+ </div>
366
+
367
+ ${hasVisibleCategories ? nothing : html`
368
+ <div class="callout callout-info mt-3">
369
+ <div class="callout-icon">
370
+ <span class="icon-emphasized">
371
+ <typo3-backend-icon identifier="actions-info" size="small"></typo3-backend-icon>
372
+ </span>
373
+ </div>
374
+ <div class="callout-content">
375
+ <div class="callout-title">${lll('settingseditor.search.noResultsTitle')}</div>
376
+ <div class="callout-body">
377
+ <p>${lll('settingseditor.search.noResultsMessage')}</p>
378
+ <button
379
+ type="button"
380
+ class="btn btn-default"
381
+ @click=${() => this.searchTerm = ''}
382
+ >${lll('settingseditor.search.noResultsResetButtonLabel')}</button>
383
+ </div>
384
+ </div>
385
+ </div>
386
+ `}
387
+ </form>
388
+ `;
389
+ }
390
+
391
+ protected filterCategories(categories: Category[] = null): FilteredCategory[] {
392
+ categories ??= this.categories;
393
+ return categories.map(category => {
394
+ const settings = this.filterSettings(category.settings);
395
+ const subcategories = this.filterCategories(category.categories);
396
+ const hasVisibleSettings = settings.filter(setting => !setting.__hidden).length > 0;
397
+ const hasVisibleSubcategories = subcategories.filter(c => !c.__hidden).length > 0;
398
+ return {
399
+ ...category,
400
+ settings,
401
+ categories: subcategories,
402
+ __hidden: !hasVisibleSettings && !hasVisibleSubcategories
403
+ };
404
+ });
405
+ }
406
+
407
+ protected filterSettings(settings: EditableSetting[]): FilteredEditableSetting[] {
408
+ return settings.map((setting) => {
409
+ return {
410
+ ...setting,
411
+ __hidden: !(
412
+ this.matchesSearchTerm(setting.definition.key) ||
413
+ this.matchesSearchTerm(setting.definition.label) ||
414
+ this.matchesSearchTerm(setting.definition.description ?? '') ||
415
+ this.valueMatchesSearchTerm(setting.value) ||
416
+ setting.definition.tags.filter(tag => this.matchesSearchTerm(tag)).length > 0
417
+ )
418
+ };
419
+ });
420
+ }
421
+
422
+ protected matchesSearchTerm(input: string): boolean {
423
+ if (this.searchTerm === '') {
424
+ return true;
425
+ }
426
+ return this.matchesSubstring(input, this.searchTerm);
427
+ }
428
+
429
+ protected valueMatchesSearchTerm(value: ValueType): boolean {
430
+ if (typeof value === 'string') {
431
+ return this.matchesSearchTerm(value);
432
+ }
433
+ if (Array.isArray(value)) {
434
+ return value.filter(v => typeof v === 'string' && this.matchesSearchTerm(v)).length > 0;
435
+ }
436
+ return false;
437
+ }
438
+
439
+ protected matchesSubstring(input: string, searchString: string): boolean {
440
+ return input.toLowerCase().includes(searchString.toLowerCase());
441
+ }
442
+ }
443
+
444
+ declare global {
445
+ interface HTMLElementTagNameMap {
446
+ 'typo3-backend-settings-editor': SettingsEditorElement;
447
+ }
448
+ interface HTMLElementEventMap {
449
+ [SettingsEditorSubmitEvent.eventName]: SettingsEditorSubmitEvent;
450
+ }
451
+ }