@taon-dev/cms 21.0.25 → 21.0.27
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/VERIFIED-BUILD-DATA.jsonc +3 -3
- package/assets/.gitkeep +0 -0
- package/assets/css/primeicons/fonts/primeicons.ttf +0 -0
- package/assets/css/primeicons/fonts/primeicons.woff +0 -0
- package/assets/css/primeicons/fonts/primeicons.woff2 +0 -0
- package/assets/generated/pwa/android-chrome-144x144.png +0 -0
- package/assets/generated/pwa/android-chrome-192x192.png +0 -0
- package/assets/generated/pwa/android-chrome-256x256.png +0 -0
- package/assets/generated/pwa/android-chrome-36x36.png +0 -0
- package/assets/generated/pwa/android-chrome-384x384.png +0 -0
- package/assets/generated/pwa/android-chrome-48x48.png +0 -0
- package/assets/generated/pwa/android-chrome-512x512.png +0 -0
- package/assets/generated/pwa/android-chrome-72x72.png +0 -0
- package/assets/generated/pwa/android-chrome-96x96.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-1024x1024.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-114x114.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-120x120.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-144x144.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-152x152.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-167x167.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-180x180.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-57x57.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-60x60.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-72x72.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-76x76.png +0 -0
- package/assets/generated/pwa/apple-touch-icon-precomposed.png +0 -0
- package/assets/generated/pwa/apple-touch-icon.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1125x2436.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1136x640.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1170x2532.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1179x2556.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1242x2208.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1242x2688.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1284x2778.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1290x2796.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1334x750.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1488x2266.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1536x2048.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1620x2160.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1640x2160.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1668x2224.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1668x2388.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-1792x828.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2048x1536.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2048x2732.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2160x1620.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2160x1640.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2208x1242.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2224x1668.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2266x1488.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2388x1668.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2436x1125.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2532x1170.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2556x1179.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2688x1242.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2732x2048.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2778x1284.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-2796x1290.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-640x1136.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-750x1334.png +0 -0
- package/assets/generated/pwa/apple-touch-startup-image-828x1792.png +0 -0
- package/assets/generated/pwa/browserconfig.xml +12 -0
- package/assets/generated/pwa/favicon-16x16.png +0 -0
- package/assets/generated/pwa/favicon-32x32.png +0 -0
- package/assets/generated/pwa/favicon-48x48.png +0 -0
- package/assets/generated/pwa/favicon.ico +0 -0
- package/assets/generated/pwa/manifest.webmanifest +69 -0
- package/assets/generated/pwa/mstile-144x144.png +0 -0
- package/assets/generated/pwa/mstile-150x150.png +0 -0
- package/assets/generated/pwa/mstile-310x150.png +0 -0
- package/assets/generated/pwa/mstile-310x310.png +0 -0
- package/assets/generated/pwa/mstile-70x70.png +0 -0
- package/assets/generated/pwa/yandex-browser-50x50.png +0 -0
- package/assets/generated/pwa/yandex-browser-manifest.json +9 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.html.de-DE.i18n.data-Bv4175Nc.mjs → cms-browser-taon-cms-post.component.html.de-DE.i18n.data-GLOdlJtK.mjs} +152 -137
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.de-DE.i18n.data-GLOdlJtK.mjs.map +1 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.html.en-US.i18n.data-CY1znnmK.mjs → cms-browser-taon-cms-post.component.html.en-US.i18n.data-Sti5WK0f.mjs} +149 -137
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.en-US.i18n.data-Sti5WK0f.mjs.map +1 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.html.pl-PL.i18n.data-BcbuzOtn.mjs → cms-browser-taon-cms-post.component.html.pl-PL.i18n.data-D4hUUPRh.mjs} +152 -137
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.pl-PL.i18n.data-D4hUUPRh.mjs.map +1 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.ts.de-DE.i18n.data-DzuYOTSn.mjs → cms-browser-taon-cms-post.component.ts.de-DE.i18n.data-DFs0vM5m.mjs} +89 -70
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.de-DE.i18n.data-DFs0vM5m.mjs.map +1 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.ts.en-US.i18n.data-MgewtvKr.mjs → cms-browser-taon-cms-post.component.ts.en-US.i18n.data-C2yhA0CQ.mjs} +87 -70
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.en-US.i18n.data-C2yhA0CQ.mjs.map +1 -0
- package/browser/fesm2022/{cms-browser-taon-cms-post.component.ts.pl-PL.i18n.data-B1ILfsGK.mjs → cms-browser-taon-cms-post.component.ts.pl-PL.i18n.data-BSX4auNB.mjs} +89 -70
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.pl-PL.i18n.data-BSX4auNB.mjs.map +1 -0
- package/browser/fesm2022/cms-browser.mjs +1463 -157
- package/browser/fesm2022/cms-browser.mjs.map +1 -1
- package/browser/package.json +1 -1
- package/browser/types/cms-browser.d.ts +124 -9
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.html.de-DE.i18n.data-DpFy5ZSO.mjs → cms-browser-prod-taon-cms-post.component.html.de-DE.i18n.data-DCh3tUVC.mjs} +152 -137
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.de-DE.i18n.data-DCh3tUVC.mjs.map +1 -0
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.html.en-US.i18n.data-DLaPQZZj.mjs → cms-browser-prod-taon-cms-post.component.html.en-US.i18n.data-CmuKeBoj.mjs} +149 -137
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.en-US.i18n.data-CmuKeBoj.mjs.map +1 -0
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.html.pl-PL.i18n.data-alQxEnOY.mjs → cms-browser-prod-taon-cms-post.component.html.pl-PL.i18n.data-DlP8ezIr.mjs} +152 -137
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.pl-PL.i18n.data-DlP8ezIr.mjs.map +1 -0
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.ts.de-DE.i18n.data-4KAdwPda.mjs → cms-browser-prod-taon-cms-post.component.ts.de-DE.i18n.data-BnlwPUxT.mjs} +89 -70
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.de-DE.i18n.data-BnlwPUxT.mjs.map +1 -0
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.ts.en-US.i18n.data-CyuTYn77.mjs → cms-browser-prod-taon-cms-post.component.ts.en-US.i18n.data-CX4JzE60.mjs} +87 -70
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.en-US.i18n.data-CX4JzE60.mjs.map +1 -0
- package/browser-prod/fesm2022/{cms-browser-prod-taon-cms-post.component.ts.pl-PL.i18n.data-CgmdVwt_.mjs → cms-browser-prod-taon-cms-post.component.ts.pl-PL.i18n.data-C4xmbOD4.mjs} +89 -70
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.pl-PL.i18n.data-C4xmbOD4.mjs.map +1 -0
- package/browser-prod/fesm2022/cms-browser-prod.mjs +1463 -157
- package/browser-prod/fesm2022/cms-browser-prod.mjs.map +1 -1
- package/browser-prod/package.json +1 -1
- package/browser-prod/types/cms-browser-prod.d.ts +124 -9
- package/full-dts/build-info._auto-generated_.d.ts +1 -1
- package/full-dts/index._auto-generated_.d.ts +3 -0
- package/full-dts/index.d.ts +1 -0
- package/full-dts/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.d.ts +2 -0
- package/full-dts/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.d.ts +2 -0
- package/full-dts/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.d.ts +2 -0
- package/full-dts/taon-cms-content/taon-cms-content-api.service.d.ts +4 -2
- package/full-dts/taon-cms-content/taon-cms-content-storage.controller.d.ts +12 -1
- package/full-dts/taon-cms-content/taon-cms-content.config.service.d.ts +1 -0
- package/full-dts/taon-cms-content/taon-cms-content.constants.d.ts +5 -1
- package/full-dts/taon-cms-content/taon-cms-content.controller.d.ts +4 -0
- package/full-dts/taon-cms-content/taon-cms-content.models.d.ts +2 -1
- package/full-dts/taon-cms-content/taon-cms-content.provider.d.ts +4 -0
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.d.ts +2 -0
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.d.ts +2 -0
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.d.ts +2 -0
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.d.ts +10 -2
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.d.ts +4 -0
- package/full-dts/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.d.ts +10 -2
- package/full-dts/taon-posts/taon-cms-post/taon-cms-post.component.d.ts +18 -2
- package/full-dts/taon-spreadsheet-table/index.d.ts +4 -0
- package/full-dts/taon-spreadsheet-table/taon-spreadsheet-table.component.d.ts +51 -0
- package/full-dts/taon-spreadsheet-table/taon-spreadsheet-table.models.d.ts +13 -0
- package/full-dts/taon-spreadsheet-table/taon-spreadsheet-table.utils.d.ts +10 -0
- package/full-dts/taon-spreadsheet-table/taon-spreadsheet-table.utils.spec.d.ts +2 -0
- package/html/lib/taon-posts/taon-cms-post/taon-cms-post.component.html +50 -5
- package/html/lib/taon-spreadsheet-table/taon-spreadsheet-table.component.html +364 -0
- package/i18n/app.ts.de-DE.po +42 -0
- package/i18n/app.ts.en-US.po +42 -0
- package/i18n/app.ts.pl-PL.po +42 -0
- package/lib/build-info._auto-generated_.d.ts +1 -1
- package/lib/build-info._auto-generated_.js +1 -1
- package/lib/index._auto-generated_.d.ts +2 -0
- package/lib/index._auto-generated_.js +3 -0
- package/lib/index._auto-generated_.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/lib-info.md +8 -0
- package/lib/migrations/migrations-info.md +6 -0
- package/lib/package.json +1 -1
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.html.de-DE.po +21 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.html.en-US.po +21 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.html.pl-PL.po +21 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.ts.de-DE.po +13 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.ts.en-US.po +13 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-asset-preview.component.ts.pl-PL.po +13 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.html.de-DE.po +17 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.html.en-US.po +17 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.html.pl-PL.po +17 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.ts.de-DE.po +17 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.ts.en-US.po +17 -0
- package/lib/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.component.ts.pl-PL.po +17 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.html.de-DE.po +10 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.html.en-US.po +10 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.html.pl-PL.po +10 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.ts.de-DE.po +5 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.ts.en-US.po +5 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.component.ts.pl-PL.po +5 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.routes.ts.de-DE.po +33 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.routes.ts.en-US.po +33 -0
- package/lib/taon-cms-backoffice/i18n/taon-cms-backoffice.routes.ts.pl-PL.po +33 -0
- package/lib/taon-cms-backoffice/index.d.ts +1 -1
- package/lib/taon-cms-backoffice/index.js +2 -2
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.html.de-DE.po +30 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.html.en-US.po +30 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.html.pl-PL.po +30 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.ts.de-DE.po +41 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.ts.en-US.po +41 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.component.ts.pl-PL.po +41 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/index.d.ts +1 -1
- package/lib/taon-cms-backoffice/taon-cms-website-features/index.js +2 -2
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.constants.ts.de-DE.po +9 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.constants.ts.en-US.po +9 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.constants.ts.pl-PL.po +9 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.controller.ts.de-DE.po +9 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.controller.ts.en-US.po +9 -0
- package/lib/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.controller.ts.pl-PL.po +9 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.controller.ts.de-DE.po +9 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.controller.ts.en-US.po +9 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.controller.ts.pl-PL.po +9 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.repository.ts.de-DE.po +17 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.repository.ts.en-US.po +17 -0
- package/lib/taon-cms-category/i18n/taon-cms-category.repository.ts.pl-PL.po +17 -0
- package/lib/taon-cms-category/i18n/taon-cms-taxonomy.validation.ts.de-DE.po +17 -0
- package/lib/taon-cms-category/i18n/taon-cms-taxonomy.validation.ts.en-US.po +17 -0
- package/lib/taon-cms-category/i18n/taon-cms-taxonomy.validation.ts.pl-PL.po +17 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comment.controller.ts.de-DE.po +77 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comment.controller.ts.en-US.po +77 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comment.controller.ts.pl-PL.po +77 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.html.de-DE.po +64 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.html.en-US.po +64 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.html.pl-PL.po +64 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.ts.de-DE.po +25 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.ts.en-US.po +25 -0
- package/lib/taon-cms-comment/i18n/taon-cms-comments.component.ts.pl-PL.po +25 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.component.ts.de-DE.po +5 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.component.ts.en-US.po +5 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.component.ts.pl-PL.po +5 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.routes.ts.de-DE.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.routes.ts.en-US.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.routes.ts.pl-PL.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/index.d.ts +1 -1
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/index.js +2 -2
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.html.de-DE.po +66 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.html.en-US.po +66 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.html.pl-PL.po +66 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.ts.de-DE.po +41 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.ts.en-US.po +41 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.component.ts.pl-PL.po +41 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.d.ts +1 -1
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.js +2 -2
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.html.de-DE.po +50 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.html.en-US.po +50 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.html.pl-PL.po +50 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.ts.de-DE.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.ts.en-US.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.component.ts.pl-PL.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.d.ts +1 -1
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.js +2 -2
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.html.de-DE.po +33 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.html.en-US.po +33 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.html.pl-PL.po +33 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.ts.de-DE.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.ts.en-US.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.component.ts.pl-PL.po +17 -0
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.d.ts +1 -1
- package/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.js +2 -2
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.js +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.po +9 -0
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.js +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.po +9 -0
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.js +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.po +9 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.d.ts +2 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js +30 -10
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js.map +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.po +49 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.d.ts +2 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js +26 -10
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js.map +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.po +49 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.d.ts +2 -0
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js +30 -10
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.po +49 -0
- package/lib/taon-cms-content/taon-cms-content-storage.controller.d.ts +12 -1
- package/lib/taon-cms-content/taon-cms-content-storage.controller.js +44 -0
- package/lib/taon-cms-content/taon-cms-content-storage.controller.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.constants.d.ts +4 -0
- package/lib/taon-cms-content/taon-cms-content.constants.js +5 -1
- package/lib/taon-cms-content/taon-cms-content.constants.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.controller.d.ts +4 -0
- package/lib/taon-cms-content/taon-cms-content.controller.js +71 -0
- package/lib/taon-cms-content/taon-cms-content.controller.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.models.d.ts +2 -1
- package/lib/taon-cms-content/taon-cms-content.models.js +1 -0
- package/lib/taon-cms-content/taon-cms-content.models.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.provider.d.ts +4 -0
- package/lib/taon-cms-content/taon-cms-content.provider.js +4 -0
- package/lib/taon-cms-content/taon-cms-content.provider.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.repository.js +17 -1
- package/lib/taon-cms-content/taon-cms-content.repository.js.map +1 -1
- package/lib/taon-cms-content/taon-cms-content.validation.js +1 -1
- package/lib/taon-cms-content/taon-cms-content.validation.js.map +1 -1
- package/lib/taon-cms-tag/i18n/taon-cms-tag.controller.ts.de-DE.po +9 -0
- package/lib/taon-cms-tag/i18n/taon-cms-tag.controller.ts.en-US.po +9 -0
- package/lib/taon-cms-tag/i18n/taon-cms-tag.controller.ts.pl-PL.po +9 -0
- package/lib/taon-cms-tag/i18n/taon-cms-tag.repository.ts.de-DE.po +13 -0
- package/lib/taon-cms-tag/i18n/taon-cms-tag.repository.ts.en-US.po +13 -0
- package/lib/taon-cms-tag/i18n/taon-cms-tag.repository.ts.pl-PL.po +13 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.html.de-DE.po +69 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.html.en-US.po +69 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.html.pl-PL.po +69 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.ts.de-DE.po +54 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.ts.en-US.po +54 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.component.ts.pl-PL.po +54 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-quick-create-dialog.component.ts.de-DE.po +33 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-quick-create-dialog.component.ts.en-US.po +33 -0
- package/lib/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-quick-create-dialog.component.ts.pl-PL.po +33 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.html.de-DE.po +43 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.html.en-US.po +43 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.html.pl-PL.po +43 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.ts.de-DE.po +21 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.ts.en-US.po +21 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.component.ts.pl-PL.po +21 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.html.de-DE.po +30 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.html.en-US.po +30 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.html.pl-PL.po +30 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.ts.de-DE.po +29 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.ts.en-US.po +29 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-contents.component.ts.pl-PL.po +29 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-cover.component.ts.de-DE.po +9 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-cover.component.ts.en-US.po +9 -0
- package/lib/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-cover.component.ts.pl-PL.po +9 -0
- package/lib/taon-posts/taon-cms-course-backoffice/index.d.ts +1 -1
- package/lib/taon-posts/taon-cms-course-backoffice/index.js +2 -2
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.html.de-DE.po +13 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.html.en-US.po +13 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.html.pl-PL.po +13 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.ts.de-DE.po +5 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.ts.en-US.po +5 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-choice-dialog.component.ts.pl-PL.po +5 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-thumbnail.component.ts.de-DE.po +9 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-thumbnail.component.ts.en-US.po +9 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post-thumbnail.component.ts.pl-PL.po +9 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.d.ts +2 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js +151 -136
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.po +505 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.d.ts +2 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js +148 -136
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.po +505 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.d.ts +2 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js +151 -136
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.po +505 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.d.ts +10 -2
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js +88 -69
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.po +291 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.d.ts +4 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js +86 -69
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.po +291 -0
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.d.ts +10 -2
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js +88 -69
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.po +291 -0
- package/lib/taon-posts/taon-cms-post/index.d.ts +1 -1
- package/lib/taon-posts/taon-cms-post/index.js +2 -2
- package/lib/taon-posts/taon-cms-post/taon-cms-thumbnail.js +1 -0
- package/lib/taon-posts/taon-cms-post/taon-cms-thumbnail.js.map +1 -1
- package/lib/taon-posts/taon-cms-post-details-page/i18n/taon-cms-post-details-page.component.ts.de-DE.po +13 -0
- package/lib/taon-posts/taon-cms-post-details-page/i18n/taon-cms-post-details-page.component.ts.en-US.po +13 -0
- package/lib/taon-posts/taon-cms-post-details-page/i18n/taon-cms-post-details-page.component.ts.pl-PL.po +13 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-post-table-actions.directive.ts.de-DE.po +69 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-post-table-actions.directive.ts.en-US.po +69 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-post-table-actions.directive.ts.pl-PL.po +69 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.html.de-DE.po +41 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.html.en-US.po +41 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.html.pl-PL.po +41 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.ts.de-DE.po +17 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.ts.en-US.po +17 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.component.ts.pl-PL.po +17 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.routes.ts.de-DE.po +13 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.routes.ts.en-US.po +13 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.routes.ts.pl-PL.po +13 -0
- package/lib/taon-posts/taon-cms-posts-backoffice/index.d.ts +1 -1
- package/lib/taon-posts/taon-cms-posts-backoffice/index.js +2 -2
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.html.de-DE.po +56 -0
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.html.en-US.po +56 -0
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.html.pl-PL.po +56 -0
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.ts.de-DE.po +17 -0
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.ts.en-US.po +17 -0
- package/lib/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.component.ts.pl-PL.po +17 -0
- package/lib/taon-posts/taon-cms-revision-chooser/index.d.ts +1 -1
- package/lib/taon-posts/taon-cms-revision-chooser/index.js +2 -2
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.html.de-DE.po +38 -0
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.html.en-US.po +38 -0
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.html.pl-PL.po +38 -0
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.ts.de-DE.po +13 -0
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.ts.en-US.po +13 -0
- package/lib/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.component.ts.pl-PL.po +13 -0
- package/lib/taon-posts/taon-related-post-chooser/index.d.ts +1 -1
- package/lib/taon-posts/taon-related-post-chooser/index.js +2 -2
- package/lib/taon-spreadsheet-table/index.d.ts +2 -0
- package/lib/taon-spreadsheet-table/index.js +20 -0
- package/lib/taon-spreadsheet-table/index.js.map +1 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.d.ts +12 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.js +3 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.js.map +1 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.d.ts +9 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.js +393 -0
- package/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.js.map +1 -0
- package/lib-esm/lib/build-info._auto-generated_.d.ts +1 -1
- package/lib-esm/lib/build-info._auto-generated_.js +1 -1
- package/lib-esm/lib/index._auto-generated_.d.ts +2 -0
- package/lib-esm/lib/index._auto-generated_.js +3 -0
- package/lib-esm/lib/index._auto-generated_.js.map +1 -1
- package/lib-esm/lib/index.d.ts +1 -0
- package/lib-esm/lib/index.js +1 -0
- package/lib-esm/lib/index.js.map +1 -1
- package/lib-esm/lib/taon-cms-backoffice/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-backoffice/index.js +1 -1
- package/lib-esm/lib/taon-cms-backoffice/taon-cms-website-features/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-backoffice/taon-cms-website-features/index.js +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/index.js +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.js +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.js +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.d.ts +1 -1
- package/lib-esm/lib/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.js +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.js +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.js +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.js +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js +30 -10
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js +26 -10
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js +30 -10
- package/lib-esm/lib/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content-storage.controller.d.ts +12 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content-storage.controller.js +44 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content-storage.controller.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.constants.d.ts +4 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.constants.js +4 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.constants.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.controller.d.ts +4 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.controller.js +71 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.controller.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.models.d.ts +2 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.models.js +1 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.models.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.provider.d.ts +4 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.provider.js +4 -0
- package/lib-esm/lib/taon-cms-content/taon-cms-content.provider.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.repository.js +17 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.repository.js.map +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.validation.js +1 -1
- package/lib-esm/lib/taon-cms-content/taon-cms-content.validation.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-course-backoffice/index.d.ts +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-course-backoffice/index.js +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js +151 -136
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js +148 -136
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.d.ts +2 -0
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js +151 -136
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.d.ts +10 -2
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js +88 -69
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.d.ts +4 -0
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js +86 -69
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.d.ts +10 -2
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js +88 -69
- package/lib-esm/lib/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/index.d.ts +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/index.js +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-post/taon-cms-thumbnail.js +1 -0
- package/lib-esm/lib/taon-posts/taon-cms-post/taon-cms-thumbnail.js.map +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-posts-backoffice/index.d.ts +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-posts-backoffice/index.js +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-revision-chooser/index.d.ts +1 -1
- package/lib-esm/lib/taon-posts/taon-cms-revision-chooser/index.js +1 -1
- package/lib-esm/lib/taon-posts/taon-related-post-chooser/index.d.ts +1 -1
- package/lib-esm/lib/taon-posts/taon-related-post-chooser/index.js +1 -1
- package/lib-esm/lib/taon-spreadsheet-table/index.d.ts +2 -0
- package/lib-esm/lib/taon-spreadsheet-table/index.js +4 -0
- package/lib-esm/lib/taon-spreadsheet-table/index.js.map +1 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.d.ts +12 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.js +2 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.models.js.map +1 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.d.ts +9 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.js +383 -0
- package/lib-esm/lib/taon-spreadsheet-table/taon-spreadsheet-table.utils.js.map +1 -0
- package/lib-prod/build-info._auto-generated_.d.ts +1 -1
- package/lib-prod/build-info._auto-generated_.js +1 -1
- package/lib-prod/index._auto-generated_.d.ts +2 -0
- package/lib-prod/index._auto-generated_.js +3 -0
- package/lib-prod/index._auto-generated_.js.map +1 -1
- package/lib-prod/index.d.ts +1 -0
- package/lib-prod/index.js +1 -0
- package/lib-prod/index.js.map +1 -1
- package/lib-prod/package.json +1 -1
- package/lib-prod/taon-cms-backoffice/index.d.ts +1 -1
- package/lib-prod/taon-cms-backoffice/index.js +1 -1
- package/lib-prod/taon-cms-backoffice/taon-cms-website-features/index.d.ts +1 -1
- package/lib-prod/taon-cms-backoffice/taon-cms-website-features/index.js +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/index.d.ts +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/index.js +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.d.ts +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/index.js +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.d.ts +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/index.js +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.d.ts +1 -1
- package/lib-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/index.js +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.js +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.js +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.js +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.d.ts +2 -0
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js +30 -10
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.js.map +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.d.ts +2 -0
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js +26 -10
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.js.map +1 -1
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.d.ts +2 -0
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js +30 -10
- package/lib-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content-storage.controller.d.ts +12 -1
- package/lib-prod/taon-cms-content/taon-cms-content-storage.controller.js +44 -0
- package/lib-prod/taon-cms-content/taon-cms-content-storage.controller.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.constants.d.ts +4 -0
- package/lib-prod/taon-cms-content/taon-cms-content.constants.js +4 -0
- package/lib-prod/taon-cms-content/taon-cms-content.constants.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.controller.d.ts +4 -0
- package/lib-prod/taon-cms-content/taon-cms-content.controller.js +71 -0
- package/lib-prod/taon-cms-content/taon-cms-content.controller.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.models.d.ts +2 -1
- package/lib-prod/taon-cms-content/taon-cms-content.models.js +1 -0
- package/lib-prod/taon-cms-content/taon-cms-content.models.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.provider.d.ts +4 -0
- package/lib-prod/taon-cms-content/taon-cms-content.provider.js +4 -0
- package/lib-prod/taon-cms-content/taon-cms-content.provider.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.repository.js +17 -1
- package/lib-prod/taon-cms-content/taon-cms-content.repository.js.map +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.validation.js +1 -1
- package/lib-prod/taon-cms-content/taon-cms-content.validation.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-course-backoffice/index.d.ts +1 -1
- package/lib-prod/taon-posts/taon-cms-course-backoffice/index.js +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.d.ts +2 -0
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js +151 -136
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.d.ts +2 -0
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js +148 -136
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.d.ts +2 -0
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js +151 -136
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.d.ts +10 -2
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js +88 -69
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.d.ts +4 -0
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js +86 -69
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.d.ts +10 -2
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js +88 -69
- package/lib-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-post/index.d.ts +1 -1
- package/lib-prod/taon-posts/taon-cms-post/index.js +1 -1
- package/lib-prod/taon-posts/taon-cms-post/taon-cms-thumbnail.js +1 -0
- package/lib-prod/taon-posts/taon-cms-post/taon-cms-thumbnail.js.map +1 -1
- package/lib-prod/taon-posts/taon-cms-posts-backoffice/index.d.ts +1 -1
- package/lib-prod/taon-posts/taon-cms-posts-backoffice/index.js +1 -1
- package/lib-prod/taon-posts/taon-cms-revision-chooser/index.d.ts +1 -1
- package/lib-prod/taon-posts/taon-cms-revision-chooser/index.js +1 -1
- package/lib-prod/taon-posts/taon-related-post-chooser/index.d.ts +1 -1
- package/lib-prod/taon-posts/taon-related-post-chooser/index.js +1 -1
- package/lib-prod/taon-spreadsheet-table/index.d.ts +2 -0
- package/lib-prod/taon-spreadsheet-table/index.js +4 -0
- package/lib-prod/taon-spreadsheet-table/index.js.map +1 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.d.ts +12 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.js +2 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.js.map +1 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.d.ts +9 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.js +383 -0
- package/lib-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.js.map +1 -0
- package/package.json +1 -1
- package/scss/lib/taon-posts/taon-cms-post/taon-cms-post.component.scss +5 -0
- package/scss/lib/taon-spreadsheet-table/taon-spreadsheet-table.component.scss +177 -0
- package/ts-source/browser-code/build-info._auto-generated_.ts +1 -1
- package/ts-source/browser-code/index._auto-generated_.ts +3 -0
- package/ts-source/browser-code/index.ts +1 -0
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.ts +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.po +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.ts +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.po +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.ts +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.po +1 -1
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.ts +32 -10
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.po +20 -10
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.ts +28 -10
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.po +20 -10
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.ts +32 -10
- package/ts-source/browser-code/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.po +20 -10
- package/ts-source/browser-code/taon-cms-content/taon-cms-content-api.service.ts +40 -42
- package/ts-source/browser-code/taon-cms-content/taon-cms-content-storage.controller.ts +15 -1
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.config.service.ts +7 -0
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.constants.ts +1 -0
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.controller.ts +22 -0
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.models.ts +2 -1
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.provider.ts +1 -0
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.repository.ts +16 -0
- package/ts-source/browser-code/taon-cms-content/taon-cms-content.validation.ts +1 -1
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.ts +153 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.po +145 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.ts +150 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.po +145 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.ts +153 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.po +145 -136
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.ts +92 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.po +85 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.ts +90 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.po +85 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.ts +92 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.po +85 -69
- package/ts-source/browser-code/taon-posts/taon-cms-post/taon-cms-post.component.html +50 -5
- package/ts-source/browser-code/taon-posts/taon-cms-post/taon-cms-post.component.scss +5 -0
- package/ts-source/browser-code/taon-posts/taon-cms-post/taon-cms-post.component.ts +169 -14
- package/ts-source/browser-code/taon-posts/taon-cms-post/taon-cms-thumbnail.ts +1 -0
- package/ts-source/browser-code/taon-spreadsheet-table/index.ts +15 -0
- package/ts-source/browser-code/taon-spreadsheet-table/taon-spreadsheet-table.component.html +377 -0
- package/ts-source/browser-code/taon-spreadsheet-table/taon-spreadsheet-table.component.scss +188 -0
- package/ts-source/browser-code/taon-spreadsheet-table/taon-spreadsheet-table.component.ts +621 -0
- package/ts-source/browser-code/taon-spreadsheet-table/taon-spreadsheet-table.models.ts +24 -0
- package/ts-source/browser-code/taon-spreadsheet-table/taon-spreadsheet-table.utils.ts +399 -0
- package/ts-source/browser-code-prod/build-info._auto-generated_.ts +1 -1
- package/ts-source/browser-code-prod/index._auto-generated_.ts +3 -0
- package/ts-source/browser-code-prod/index.ts +1 -0
- package/ts-source/browser-code-prod/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-backoffice/i18n/taon-cms-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-category/i18n/taon-cms-category.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-comment/i18n/taon-cms-comment.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.po +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.po +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.po +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.ts +32 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.po +20 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.ts +28 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.po +20 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.ts +32 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.po +20 -10
- package/ts-source/browser-code-prod/taon-cms-content/i18n/taon-cms-content.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content-api.service.ts +40 -42
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content-storage.controller.ts +15 -1
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.config.service.ts +7 -0
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.constants.ts +1 -0
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.controller.ts +22 -0
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.models.ts +2 -1
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.provider.ts +1 -0
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.repository.ts +16 -0
- package/ts-source/browser-code-prod/taon-cms-content/taon-cms-content.validation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-tag/i18n/taon-cms-tag.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.ts +153 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.po +145 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.ts +150 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.po +145 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.ts +153 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.po +145 -136
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.ts +92 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.po +85 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.ts +90 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.po +85 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.ts +92 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.po +85 -69
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.html +50 -5
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.scss +5 -0
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.ts +169 -14
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post/taon-cms-thumbnail.ts +1 -0
- package/ts-source/browser-code-prod/taon-posts/taon-cms-post-details-page/i18n/taon-cms-post-details-page.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.translation.ts +1 -1
- package/ts-source/browser-code-prod/taon-spreadsheet-table/index.split-namespaces.json +4 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/index.ts +15 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.html +377 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.scss +188 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.split-namespaces.json +4 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.ts +621 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.split-namespaces.json +4 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.ts +24 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.split-namespaces.json +4 -0
- package/ts-source/browser-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.ts +399 -0
- package/ts-source/websql-code/build-info._auto-generated_.ts +1 -1
- package/ts-source/websql-code/index._auto-generated_.ts +3 -0
- package/ts-source/websql-code/index.ts +1 -0
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.ts +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.po +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.ts +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.po +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.ts +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.po +1 -1
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.ts +32 -10
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.po +20 -10
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.ts +28 -10
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.po +20 -10
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.ts +32 -10
- package/ts-source/websql-code/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.po +20 -10
- package/ts-source/websql-code/taon-cms-content/taon-cms-content-api.service.ts +40 -42
- package/ts-source/websql-code/taon-cms-content/taon-cms-content-storage.controller.ts +15 -1
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.config.service.ts +7 -0
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.constants.ts +1 -0
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.controller.ts +53 -0
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.models.ts +2 -1
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.provider.ts +1 -0
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.repository.ts +17 -1
- package/ts-source/websql-code/taon-cms-content/taon-cms-content.validation.ts +1 -1
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.ts +153 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.po +145 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.ts +150 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.po +145 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.ts +153 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.po +145 -136
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.ts +92 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.po +85 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.ts +90 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.po +85 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.ts +92 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.po +85 -69
- package/ts-source/websql-code/taon-posts/taon-cms-post/taon-cms-post.component.html +50 -5
- package/ts-source/websql-code/taon-posts/taon-cms-post/taon-cms-post.component.scss +5 -0
- package/ts-source/websql-code/taon-posts/taon-cms-post/taon-cms-post.component.ts +169 -14
- package/ts-source/websql-code/taon-posts/taon-cms-post/taon-cms-thumbnail.ts +1 -0
- package/ts-source/websql-code/taon-spreadsheet-table/index.ts +15 -0
- package/ts-source/websql-code/taon-spreadsheet-table/taon-spreadsheet-table.component.html +377 -0
- package/ts-source/websql-code/taon-spreadsheet-table/taon-spreadsheet-table.component.scss +188 -0
- package/ts-source/websql-code/taon-spreadsheet-table/taon-spreadsheet-table.component.ts +621 -0
- package/ts-source/websql-code/taon-spreadsheet-table/taon-spreadsheet-table.models.ts +24 -0
- package/ts-source/websql-code/taon-spreadsheet-table/taon-spreadsheet-table.utils.ts +399 -0
- package/ts-source/websql-code-prod/build-info._auto-generated_.ts +1 -1
- package/ts-source/websql-code-prod/index._auto-generated_.ts +3 -0
- package/ts-source/websql-code-prod/index.ts +1 -0
- package/ts-source/websql-code-prod/taon-cms-assets-backoffice/i18n/taon-cms-assets-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-backoffice/i18n/taon-cms-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-backoffice/taon-cms-website-features/i18n/taon-cms-website-features.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-backoffice/taon-cms-website-features/taon-cms-website-feature/i18n/taon-cms-website-feature.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-category/i18n/taon-cms-category.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-comment/i18n/taon-cms-comment.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-comment/taon-cms-comments-backoffice/i18n/taon-cms-comments-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-all-preview-backoffice/i18n/taon-cms-comments-all-preview-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-blocked-users/i18n/taon-cms-comments-blocked-users.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-comment/taon-cms-comments-backoffice/taon-cms-comments-filters-settings/i18n/taon-cms-comments-filters-settings.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.i18n.data.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.de-DE.po +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.i18n.data.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.en-US.po +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.i18n.data.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content-storage.controller.ts.pl-PL.po +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.i18n.data.ts +32 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.de-DE.po +20 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.i18n.data.ts +28 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.en-US.po +20 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.i18n.data.ts +32 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.controller.ts.pl-PL.po +20 -10
- package/ts-source/websql-code-prod/taon-cms-content/i18n/taon-cms-content.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content-api.service.ts +40 -42
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content-storage.controller.ts +15 -1
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.config.service.ts +7 -0
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.constants.ts +1 -0
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.controller.ts +53 -0
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.models.ts +2 -1
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.provider.ts +1 -0
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.repository.ts +17 -1
- package/ts-source/websql-code-prod/taon-cms-content/taon-cms-content.validation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-tag/i18n/taon-cms-tag.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-cms-taxonomy-backoffice/i18n/taon-cms-taxonomy-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-cms-course-backoffice/i18n/taon-cms-course-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.i18n.data.ts +153 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.de-DE.po +145 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.i18n.data.ts +150 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.en-US.po +145 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.i18n.data.ts +153 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.html.pl-PL.po +145 -136
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.i18n.data.ts +92 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.de-DE.po +85 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.i18n.data.ts +90 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.en-US.po +85 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.i18n.data.ts +92 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.component.ts.pl-PL.po +85 -69
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/i18n/taon-cms-post.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.html +50 -5
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.scss +5 -0
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/taon-cms-post.component.ts +169 -14
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post/taon-cms-thumbnail.ts +1 -0
- package/ts-source/websql-code-prod/taon-posts/taon-cms-post-details-page/i18n/taon-cms-post-details-page.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-cms-posts-backoffice/i18n/taon-cms-posts-backoffice.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-cms-revision-chooser/i18n/taon-cms-revision-chooser.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-posts/taon-related-post-chooser/i18n/taon-related-post-chooser.translation.ts +1 -1
- package/ts-source/websql-code-prod/taon-spreadsheet-table/index.split-namespaces.json +4 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/index.ts +15 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.html +377 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.scss +188 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.split-namespaces.json +4 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.component.ts +621 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.split-namespaces.json +4 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.models.ts +24 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.split-namespaces.json +4 -0
- package/ts-source/websql-code-prod/taon-spreadsheet-table/taon-spreadsheet-table.utils.ts +399 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.html.de-DE.i18n.data-C5Nxovtl.mjs → cms-websql-taon-cms-post.component.html.de-DE.i18n.data-BdxSK5vd.mjs} +152 -137
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.de-DE.i18n.data-BdxSK5vd.mjs.map +1 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.html.en-US.i18n.data-BTWsCDp3.mjs → cms-websql-taon-cms-post.component.html.en-US.i18n.data-YiL6-4Aq.mjs} +149 -137
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.en-US.i18n.data-YiL6-4Aq.mjs.map +1 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.html.pl-PL.i18n.data-CZhAYAQh.mjs → cms-websql-taon-cms-post.component.html.pl-PL.i18n.data-BzEKPeB_.mjs} +152 -137
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.pl-PL.i18n.data-BzEKPeB_.mjs.map +1 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.ts.de-DE.i18n.data-D10xOAxh.mjs → cms-websql-taon-cms-post.component.ts.de-DE.i18n.data-B_w2dmsP.mjs} +89 -70
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.de-DE.i18n.data-B_w2dmsP.mjs.map +1 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.ts.en-US.i18n.data-D1Fh0Nd6.mjs → cms-websql-taon-cms-post.component.ts.en-US.i18n.data-B8Vs_fm6.mjs} +87 -70
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.en-US.i18n.data-B8Vs_fm6.mjs.map +1 -0
- package/websql/fesm2022/{cms-websql-taon-cms-post.component.ts.pl-PL.i18n.data-C-sVs8_U.mjs → cms-websql-taon-cms-post.component.ts.pl-PL.i18n.data-BaPFft3W.mjs} +89 -70
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.pl-PL.i18n.data-BaPFft3W.mjs.map +1 -0
- package/websql/fesm2022/cms-websql.mjs +1459 -158
- package/websql/fesm2022/cms-websql.mjs.map +1 -1
- package/websql/package.json +1 -1
- package/websql/types/cms-websql.d.ts +124 -9
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-C8qKRpYs.mjs → cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-BC9-axt9.mjs} +152 -137
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-BC9-axt9.mjs.map +1 -0
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-ClarunPV.mjs → cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-DfkiEwvY.mjs} +149 -137
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-DfkiEwvY.mjs.map +1 -0
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-YtHY1P_8.mjs → cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-DX25wx9y.mjs} +152 -137
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-DX25wx9y.mjs.map +1 -0
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-Dv-cf9Kp.mjs → cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-mk6gw3C2.mjs} +89 -70
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-mk6gw3C2.mjs.map +1 -0
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-CHBqahB1.mjs → cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-BOO0IBXz.mjs} +87 -70
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-BOO0IBXz.mjs.map +1 -0
- package/websql-prod/fesm2022/{cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-btgJU4-b.mjs → cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-BcYSlBzW.mjs} +89 -70
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-BcYSlBzW.mjs.map +1 -0
- package/websql-prod/fesm2022/cms-websql-prod.mjs +1459 -158
- package/websql-prod/fesm2022/cms-websql-prod.mjs.map +1 -1
- package/websql-prod/package.json +1 -1
- package/websql-prod/types/cms-websql-prod.d.ts +124 -9
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.de-DE.i18n.data-Bv4175Nc.mjs.map +0 -1
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.en-US.i18n.data-CY1znnmK.mjs.map +0 -1
- package/browser/fesm2022/cms-browser-taon-cms-post.component.html.pl-PL.i18n.data-BcbuzOtn.mjs.map +0 -1
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.de-DE.i18n.data-DzuYOTSn.mjs.map +0 -1
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.en-US.i18n.data-MgewtvKr.mjs.map +0 -1
- package/browser/fesm2022/cms-browser-taon-cms-post.component.ts.pl-PL.i18n.data-B1ILfsGK.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.de-DE.i18n.data-DpFy5ZSO.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.en-US.i18n.data-DLaPQZZj.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.html.pl-PL.i18n.data-alQxEnOY.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.de-DE.i18n.data-4KAdwPda.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.en-US.i18n.data-CyuTYn77.mjs.map +0 -1
- package/browser-prod/fesm2022/cms-browser-prod-taon-cms-post.component.ts.pl-PL.i18n.data-CgmdVwt_.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.de-DE.i18n.data-C5Nxovtl.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.en-US.i18n.data-BTWsCDp3.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.html.pl-PL.i18n.data-CZhAYAQh.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.de-DE.i18n.data-D10xOAxh.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.en-US.i18n.data-D1Fh0Nd6.mjs.map +0 -1
- package/websql/fesm2022/cms-websql-taon-cms-post.component.ts.pl-PL.i18n.data-C-sVs8_U.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-C8qKRpYs.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-ClarunPV.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-YtHY1P_8.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-Dv-cf9Kp.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-CHBqahB1.mjs.map +0 -1
- package/websql-prod/fesm2022/cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-btgJU4-b.mjs.map +0 -1
|
@@ -6,16 +6,16 @@ import { UtilsJson__NS__getValue, ___NS__deburr, ___NS__kebabCase, ___NS__first
|
|
|
6
6
|
import { BaseCommandLineFeature } from 'tnp-helpers/websql-prod';
|
|
7
7
|
import { Translation } from '@taon-dev/i18n/websql-prod';
|
|
8
8
|
import * as i0 from '@angular/core';
|
|
9
|
-
import { Injectable, inject, DestroyRef, signal, Input, ChangeDetectionStrategy, Component, input, effect, Directive, output, ViewChild, ElementRef, EventEmitter, SecurityContext, HostListener, Output,
|
|
9
|
+
import { Injectable, inject, DestroyRef, signal, Input, ChangeDetectionStrategy, Component, input, effect, model, computed, Directive, output, ViewChild, ElementRef, EventEmitter, SecurityContext, HostListener, Output, viewChild, untracked } from '@angular/core';
|
|
10
10
|
import * as i1 from '@angular/material/button';
|
|
11
11
|
import { MatButtonModule } from '@angular/material/button';
|
|
12
|
-
import {
|
|
12
|
+
import { of, map as map$1, finalize, Observable, take, forkJoin, switchMap, from, firstValueFrom, combineLatest } from 'rxjs';
|
|
13
13
|
import { map, catchError } from 'rxjs/operators';
|
|
14
|
-
import { Resource__NS__methodsInterceptors } from 'ng2-rest/websql-prod';
|
|
15
14
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
16
15
|
import { TaonDatatableComponent, adminLazyRoute as adminLazyRoute$1, TaonConfirmDialogComponent, insertTable, createTableEditor, TaonAdminContentFooterService } from '@taon-dev/ui/websql-prod';
|
|
17
|
-
import { AsyncPipe, DatePipe, NgTemplateOutlet } from '@angular/common';
|
|
16
|
+
import { AsyncPipe, DatePipe, NgTemplateOutlet, JsonPipe } from '@angular/common';
|
|
18
17
|
import { RouterOutlet, ActivatedRoute, Router, RouterLink } from '@angular/router';
|
|
18
|
+
import { Resource__NS__methodsInterceptors } from 'ng2-rest/websql-prod';
|
|
19
19
|
import * as i1$1 from '@angular/forms';
|
|
20
20
|
import { FormsModule } from '@angular/forms';
|
|
21
21
|
import * as i3 from '@angular/material/dialog';
|
|
@@ -26,20 +26,22 @@ import * as i5 from '@angular/material/input';
|
|
|
26
26
|
import { MatInputModule } from '@angular/material/input';
|
|
27
27
|
import * as i5$1 from '@angular/material/select';
|
|
28
28
|
import { MatSelectModule } from '@angular/material/select';
|
|
29
|
+
import * as i17 from '@angular/cdk/drag-drop';
|
|
30
|
+
import { moveItemInArray, CdkDrag, CdkDragHandle, CdkDropList, DragDropModule } from '@angular/cdk/drag-drop';
|
|
31
|
+
import * as i2 from '@angular/material/icon';
|
|
32
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
29
33
|
import * as i3$1 from '@angular/material/checkbox';
|
|
30
34
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
31
|
-
import * as i5$2 from '@angular/material/icon';
|
|
32
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
33
35
|
import * as i3$2 from '@angular/material/radio';
|
|
34
36
|
import { MatRadioModule } from '@angular/material/radio';
|
|
35
37
|
import JSZip from 'jszip';
|
|
36
38
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
37
|
-
import * as i17 from '@angular/cdk/drag-drop';
|
|
38
|
-
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
39
39
|
import * as i3$3 from '@angular/material/card';
|
|
40
40
|
import { MatCardModule } from '@angular/material/card';
|
|
41
41
|
import * as i11 from '@angular/material/chips';
|
|
42
42
|
import { MatChipsModule } from '@angular/material/chips';
|
|
43
|
+
import * as i16 from '@angular/material/divider';
|
|
44
|
+
import { MatDividerModule } from '@angular/material/divider';
|
|
43
45
|
import * as i4$1 from '@angular/material/expansion';
|
|
44
46
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
45
47
|
import * as i8 from '@angular/material/menu';
|
|
@@ -53,8 +55,6 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|
|
53
55
|
import * as i10 from 'ngx-editor';
|
|
54
56
|
import { toHTML, NgxEditorModule } from 'ngx-editor';
|
|
55
57
|
import { addRowBefore, addRowAfter, deleteRow, addColumnBefore, addColumnAfter, deleteColumn, deleteTable } from 'prosemirror-tables';
|
|
56
|
-
import * as i16 from '@angular/material/divider';
|
|
57
|
-
import { MatDividerModule } from '@angular/material/divider';
|
|
58
58
|
|
|
59
59
|
let TaonCmsApplicationHookEntity = class TaonCmsApplicationHookEntity extends TaonBaseEntity {
|
|
60
60
|
};
|
|
@@ -88,6 +88,7 @@ var TaonCmsContentType;
|
|
|
88
88
|
TaonCmsContentType["Video"] = "video";
|
|
89
89
|
TaonCmsContentType["Audio"] = "audio";
|
|
90
90
|
TaonCmsContentType["Attachment"] = "attachment";
|
|
91
|
+
TaonCmsContentType["JsonTable"] = "json-table";
|
|
91
92
|
})(TaonCmsContentType || (TaonCmsContentType = {}));
|
|
92
93
|
var TaonCmsContentState;
|
|
93
94
|
(function (TaonCmsContentState) {
|
|
@@ -426,7 +427,7 @@ const CURRENT_PACKAGE_TAON_VERSION = 'v21';
|
|
|
426
427
|
/**
|
|
427
428
|
* Autogenerated by current cli tool. Use *tnp release* to bump version.
|
|
428
429
|
*/
|
|
429
|
-
const CURRENT_PACKAGE_VERSION = '21.0.
|
|
430
|
+
const CURRENT_PACKAGE_VERSION = '21.0.27';
|
|
430
431
|
// THIS FILE IS GENERATED - DO NOT MODIFY
|
|
431
432
|
|
|
432
433
|
//#region tnp-helpers cli template
|
|
@@ -538,7 +539,7 @@ function validateContentInput(input, creating = false) {
|
|
|
538
539
|
}
|
|
539
540
|
if (input.type !== undefined &&
|
|
540
541
|
!Object.values(TaonCmsContentType).includes(input.type)) {
|
|
541
|
-
contentError('type must be normal, video, audio, or
|
|
542
|
+
contentError('type must be normal, video, audio, attachment, or json-table.');
|
|
542
543
|
}
|
|
543
544
|
for (const field of ['title', 'slug']) {
|
|
544
545
|
if (creating || input[field] !== undefined) {
|
|
@@ -1536,6 +1537,389 @@ TaonCmsContentAssetEntity = __decorate([
|
|
|
1536
1537
|
})
|
|
1537
1538
|
], TaonCmsContentAssetEntity);
|
|
1538
1539
|
|
|
1540
|
+
const dangerousKeys = new Set(['__proto__', 'prototype', 'constructor']);
|
|
1541
|
+
const supportedTypes = new Set([
|
|
1542
|
+
'string',
|
|
1543
|
+
'number',
|
|
1544
|
+
'boolean',
|
|
1545
|
+
'date',
|
|
1546
|
+
'json',
|
|
1547
|
+
]);
|
|
1548
|
+
const maximumColumns = 200;
|
|
1549
|
+
const maximumRows = 10_000;
|
|
1550
|
+
const TAON_SPREADSHEET_MAX_BYTES = 10_000_000;
|
|
1551
|
+
function normalizeSpreadsheetKey(header) {
|
|
1552
|
+
const words = header
|
|
1553
|
+
.normalize('NFKD')
|
|
1554
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
1555
|
+
.match(/[A-Za-z0-9]+/g);
|
|
1556
|
+
if (!words?.length) {
|
|
1557
|
+
return '';
|
|
1558
|
+
}
|
|
1559
|
+
return words
|
|
1560
|
+
.map((word, index) => index === 0
|
|
1561
|
+
? word.charAt(0).toLowerCase() + word.slice(1)
|
|
1562
|
+
: word.charAt(0).toUpperCase() + word.slice(1))
|
|
1563
|
+
.join('');
|
|
1564
|
+
}
|
|
1565
|
+
function validateSpreadsheetData(value) {
|
|
1566
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1567
|
+
throw new Error('Table data must be an object.');
|
|
1568
|
+
}
|
|
1569
|
+
const source = value;
|
|
1570
|
+
if (source.version !== 1 ||
|
|
1571
|
+
!Array.isArray(source.columns) ||
|
|
1572
|
+
!Array.isArray(source.rows)) {
|
|
1573
|
+
throw new Error('Table data must use the supported version 1 format.');
|
|
1574
|
+
}
|
|
1575
|
+
if (source.columns.length > maximumColumns) {
|
|
1576
|
+
throw new Error(`A table cannot have more than ${maximumColumns} columns.`);
|
|
1577
|
+
}
|
|
1578
|
+
if (source.rows.length > maximumRows) {
|
|
1579
|
+
throw new Error(`A table cannot have more than ${maximumRows} rows.`);
|
|
1580
|
+
}
|
|
1581
|
+
const keys = new Set();
|
|
1582
|
+
const columns = source.columns.map((column, index) => {
|
|
1583
|
+
if (!column || typeof column !== 'object') {
|
|
1584
|
+
throw new Error(`Column ${index + 1} is invalid.`);
|
|
1585
|
+
}
|
|
1586
|
+
const candidate = column;
|
|
1587
|
+
if (typeof candidate.key !== 'string' ||
|
|
1588
|
+
!/^[a-z][A-Za-z0-9]*$/.test(candidate.key) ||
|
|
1589
|
+
dangerousKeys.has(candidate.key)) {
|
|
1590
|
+
throw new Error(`Column ${index + 1} has an invalid key.`);
|
|
1591
|
+
}
|
|
1592
|
+
if (keys.has(candidate.key)) {
|
|
1593
|
+
throw new Error(`The column key "${candidate.key}" is duplicated.`);
|
|
1594
|
+
}
|
|
1595
|
+
if (!supportedTypes.has(candidate.type)) {
|
|
1596
|
+
throw new Error(`Column "${candidate.key}" has an unsupported type.`);
|
|
1597
|
+
}
|
|
1598
|
+
if (candidate.label !== undefined && typeof candidate.label !== 'string') {
|
|
1599
|
+
throw new Error(`Column "${candidate.key}" has an invalid label.`);
|
|
1600
|
+
}
|
|
1601
|
+
if (candidate.width !== undefined &&
|
|
1602
|
+
(!Number.isFinite(candidate.width) ||
|
|
1603
|
+
candidate.width < 40 ||
|
|
1604
|
+
candidate.width > 1000)) {
|
|
1605
|
+
throw new Error(`Column "${candidate.key}" has an invalid width.`);
|
|
1606
|
+
}
|
|
1607
|
+
keys.add(candidate.key);
|
|
1608
|
+
return {
|
|
1609
|
+
key: candidate.key,
|
|
1610
|
+
...(candidate.label !== undefined ? { label: candidate.label } : {}),
|
|
1611
|
+
type: candidate.type,
|
|
1612
|
+
...(candidate.width !== undefined ? { width: candidate.width } : {}),
|
|
1613
|
+
};
|
|
1614
|
+
});
|
|
1615
|
+
const rows = source.rows.map((row, rowIndex) => {
|
|
1616
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
1617
|
+
throw new Error(`Row ${rowIndex + 1} must be an object.`);
|
|
1618
|
+
}
|
|
1619
|
+
const result = {};
|
|
1620
|
+
for (const key of Object.keys(row)) {
|
|
1621
|
+
if (dangerousKeys.has(key)) {
|
|
1622
|
+
throw new Error(`Row ${rowIndex + 1} contains a dangerous key.`);
|
|
1623
|
+
}
|
|
1624
|
+
if (!keys.has(key)) {
|
|
1625
|
+
throw new Error(`Row ${rowIndex + 1} contains unknown column "${key}".`);
|
|
1626
|
+
}
|
|
1627
|
+
const cell = row[key];
|
|
1628
|
+
const column = columns.find(candidate => candidate.key === key);
|
|
1629
|
+
if (cell !== null && cell !== undefined) {
|
|
1630
|
+
const validType = column.type === 'string'
|
|
1631
|
+
? typeof cell === 'string'
|
|
1632
|
+
: column.type === 'number'
|
|
1633
|
+
? typeof cell === 'number' && Number.isFinite(cell)
|
|
1634
|
+
: column.type === 'boolean'
|
|
1635
|
+
? typeof cell === 'boolean'
|
|
1636
|
+
: column.type === 'date'
|
|
1637
|
+
? typeof cell === 'string' && isValidDateOnly(cell)
|
|
1638
|
+
: true;
|
|
1639
|
+
if (!validType) {
|
|
1640
|
+
throw new Error(`Row ${rowIndex + 1}, column "${key}" does not match type "${column.type}".`);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (column.type === 'json' && cell !== null && cell !== undefined) {
|
|
1644
|
+
assertSafeJson(cell, `Row ${rowIndex + 1}, column "${key}"`);
|
|
1645
|
+
}
|
|
1646
|
+
result[key] = cell;
|
|
1647
|
+
}
|
|
1648
|
+
return result;
|
|
1649
|
+
});
|
|
1650
|
+
return { version: 1, columns, rows };
|
|
1651
|
+
}
|
|
1652
|
+
function parseSpreadsheetDocument(body) {
|
|
1653
|
+
if (!body?.trim()) {
|
|
1654
|
+
return { version: 1, columns: [], rows: [] };
|
|
1655
|
+
}
|
|
1656
|
+
let parsed;
|
|
1657
|
+
try {
|
|
1658
|
+
parsed = JSON.parse(body);
|
|
1659
|
+
}
|
|
1660
|
+
catch {
|
|
1661
|
+
throw new Error('The saved JSON Table content is not valid JSON.');
|
|
1662
|
+
}
|
|
1663
|
+
if (parsed &&
|
|
1664
|
+
typeof parsed === 'object' &&
|
|
1665
|
+
!Array.isArray(parsed) &&
|
|
1666
|
+
parsed.version === 1) {
|
|
1667
|
+
return validateSpreadsheetData(parsed);
|
|
1668
|
+
}
|
|
1669
|
+
if (!Array.isArray(parsed)) {
|
|
1670
|
+
throw new Error('JSON Table content must be a versioned table or an array.');
|
|
1671
|
+
}
|
|
1672
|
+
return inferSpreadsheetData(parsed);
|
|
1673
|
+
}
|
|
1674
|
+
function importSpreadsheetJson(text) {
|
|
1675
|
+
let parsed;
|
|
1676
|
+
try {
|
|
1677
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ''));
|
|
1678
|
+
}
|
|
1679
|
+
catch {
|
|
1680
|
+
throw new Error('The selected JSON file is not valid JSON.');
|
|
1681
|
+
}
|
|
1682
|
+
if (parsed &&
|
|
1683
|
+
typeof parsed === 'object' &&
|
|
1684
|
+
!Array.isArray(parsed) &&
|
|
1685
|
+
parsed.version === 1) {
|
|
1686
|
+
return validateSpreadsheetData(parsed);
|
|
1687
|
+
}
|
|
1688
|
+
if (!Array.isArray(parsed)) {
|
|
1689
|
+
throw new Error('JSON import must contain an array of row objects.');
|
|
1690
|
+
}
|
|
1691
|
+
return inferSpreadsheetData(parsed);
|
|
1692
|
+
}
|
|
1693
|
+
function inferSpreadsheetData(rows) {
|
|
1694
|
+
if (rows.length > maximumRows) {
|
|
1695
|
+
throw new Error(`A table cannot have more than ${maximumRows} rows.`);
|
|
1696
|
+
}
|
|
1697
|
+
if (rows.some(row => !row || typeof row !== 'object' || Array.isArray(row))) {
|
|
1698
|
+
throw new Error('Every imported row must be an object.');
|
|
1699
|
+
}
|
|
1700
|
+
const headers = [...new Set(rows.flatMap(row => Object.keys(row)))];
|
|
1701
|
+
if (headers.length > maximumColumns) {
|
|
1702
|
+
throw new Error(`A table cannot have more than ${maximumColumns} columns.`);
|
|
1703
|
+
}
|
|
1704
|
+
const usedKeys = new Set();
|
|
1705
|
+
const columns = headers.map(header => {
|
|
1706
|
+
let key = normalizeSpreadsheetKey(header);
|
|
1707
|
+
if (!key || !/^[A-Za-z]/.test(key)) {
|
|
1708
|
+
key = `column${key ? key.charAt(0).toUpperCase() + key.slice(1) : ''}`;
|
|
1709
|
+
}
|
|
1710
|
+
const base = key;
|
|
1711
|
+
let suffix = 2;
|
|
1712
|
+
while (usedKeys.has(key) || dangerousKeys.has(key)) {
|
|
1713
|
+
key = `${base}${suffix++}`;
|
|
1714
|
+
}
|
|
1715
|
+
usedKeys.add(key);
|
|
1716
|
+
const values = rows.map(row => row[header]);
|
|
1717
|
+
return {
|
|
1718
|
+
key,
|
|
1719
|
+
...(header !== key ? { label: header } : {}),
|
|
1720
|
+
type: inferType(values),
|
|
1721
|
+
};
|
|
1722
|
+
});
|
|
1723
|
+
const keyByHeader = new Map(headers.map((header, index) => [header, columns[index].key]));
|
|
1724
|
+
const normalizedRows = rows.map(row => {
|
|
1725
|
+
const result = {};
|
|
1726
|
+
for (const header of headers) {
|
|
1727
|
+
const key = keyByHeader.get(header);
|
|
1728
|
+
result[key] = row[header] ?? null;
|
|
1729
|
+
}
|
|
1730
|
+
return result;
|
|
1731
|
+
});
|
|
1732
|
+
return validateSpreadsheetData({
|
|
1733
|
+
version: 1,
|
|
1734
|
+
columns,
|
|
1735
|
+
rows: normalizedRows,
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
function parseSpreadsheetCsv(text, delimiter) {
|
|
1739
|
+
const source = text.replace(/^\uFEFF/, '');
|
|
1740
|
+
const separator = delimiter ?? detectDelimiter(source);
|
|
1741
|
+
const records = [];
|
|
1742
|
+
let record = [];
|
|
1743
|
+
let field = '';
|
|
1744
|
+
let quoted = false;
|
|
1745
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1746
|
+
const char = source[index];
|
|
1747
|
+
if (quoted) {
|
|
1748
|
+
if (char === '"' && source[index + 1] === '"') {
|
|
1749
|
+
field += '"';
|
|
1750
|
+
index += 1;
|
|
1751
|
+
}
|
|
1752
|
+
else if (char === '"') {
|
|
1753
|
+
quoted = false;
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
field += char;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
else if (char === '"' && field.length === 0) {
|
|
1760
|
+
quoted = true;
|
|
1761
|
+
}
|
|
1762
|
+
else if (char === separator) {
|
|
1763
|
+
record.push(field);
|
|
1764
|
+
field = '';
|
|
1765
|
+
}
|
|
1766
|
+
else if (char === '\r' || char === '\n') {
|
|
1767
|
+
record.push(field);
|
|
1768
|
+
records.push(record);
|
|
1769
|
+
record = [];
|
|
1770
|
+
field = '';
|
|
1771
|
+
if (char === '\r' && source[index + 1] === '\n') {
|
|
1772
|
+
index += 1;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
else {
|
|
1776
|
+
field += char;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (quoted) {
|
|
1780
|
+
throw new Error('The CSV file contains an unterminated quoted value.');
|
|
1781
|
+
}
|
|
1782
|
+
if (field.length || record.length) {
|
|
1783
|
+
record.push(field);
|
|
1784
|
+
records.push(record);
|
|
1785
|
+
}
|
|
1786
|
+
const nonEmptyRecords = records.filter(row => row.some(cell => cell !== ''));
|
|
1787
|
+
if (!nonEmptyRecords.length) {
|
|
1788
|
+
return { version: 1, columns: [], rows: [] };
|
|
1789
|
+
}
|
|
1790
|
+
const headers = nonEmptyRecords[0].map(header => header.trim());
|
|
1791
|
+
if (!headers.length || headers.some(header => !header)) {
|
|
1792
|
+
throw new Error('CSV header cells must not be empty.');
|
|
1793
|
+
}
|
|
1794
|
+
if (new Set(headers).size !== headers.length) {
|
|
1795
|
+
throw new Error('CSV headers must be unique before import.');
|
|
1796
|
+
}
|
|
1797
|
+
const rows = nonEmptyRecords.slice(1).map((cells, rowIndex) => {
|
|
1798
|
+
if (cells.length > headers.length) {
|
|
1799
|
+
throw new Error(`CSV row ${rowIndex + 2} has more cells than the header.`);
|
|
1800
|
+
}
|
|
1801
|
+
const result = {};
|
|
1802
|
+
headers.forEach((header, index) => {
|
|
1803
|
+
result[header] = parseCsvValue(cells[index] ?? '');
|
|
1804
|
+
});
|
|
1805
|
+
return result;
|
|
1806
|
+
});
|
|
1807
|
+
const inferred = inferSpreadsheetData(rows.length
|
|
1808
|
+
? rows
|
|
1809
|
+
: [Object.fromEntries(headers.map(header => [header, '']))]);
|
|
1810
|
+
return {
|
|
1811
|
+
version: 1,
|
|
1812
|
+
columns: inferred.columns,
|
|
1813
|
+
rows: rows.length ? inferred.rows : [],
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
function serializeSpreadsheetCsv(data) {
|
|
1817
|
+
const escape = (value) => {
|
|
1818
|
+
let text = value === null || value === undefined
|
|
1819
|
+
? ''
|
|
1820
|
+
: typeof value === 'string'
|
|
1821
|
+
? value
|
|
1822
|
+
: JSON.stringify(value);
|
|
1823
|
+
if (/^[\t\r\n ]*[=+\-@]/.test(text)) {
|
|
1824
|
+
text = `'${text}`;
|
|
1825
|
+
}
|
|
1826
|
+
return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
1827
|
+
};
|
|
1828
|
+
const header = data.columns.map(column => escape(column.label || column.key));
|
|
1829
|
+
const rows = data.rows.map(row => data.columns.map(column => escape(row[column.key] ?? '')).join(','));
|
|
1830
|
+
return [header.join(','), ...rows].join('\r\n');
|
|
1831
|
+
}
|
|
1832
|
+
function inferType(values) {
|
|
1833
|
+
const present = values.filter(value => value !== null && value !== undefined && value !== '');
|
|
1834
|
+
if (present.length &&
|
|
1835
|
+
present.every(value => typeof value === 'string' && isValidDateOnly(value))) {
|
|
1836
|
+
return 'date';
|
|
1837
|
+
}
|
|
1838
|
+
if (present.length && present.every(value => typeof value === 'boolean')) {
|
|
1839
|
+
return 'boolean';
|
|
1840
|
+
}
|
|
1841
|
+
if (present.length &&
|
|
1842
|
+
present.every(value => typeof value === 'number' && Number.isFinite(value))) {
|
|
1843
|
+
return 'number';
|
|
1844
|
+
}
|
|
1845
|
+
if (present.length && present.every(value => typeof value === 'object')) {
|
|
1846
|
+
return 'json';
|
|
1847
|
+
}
|
|
1848
|
+
return 'string';
|
|
1849
|
+
}
|
|
1850
|
+
function detectDelimiter(text) {
|
|
1851
|
+
const firstLine = text.split(/\r\n|\n|\r/, 1)[0] ?? '';
|
|
1852
|
+
const candidates = [',', ';', '\t'];
|
|
1853
|
+
return candidates.reduce((selected, candidate) => countUnquoted(firstLine, candidate) > countUnquoted(firstLine, selected)
|
|
1854
|
+
? candidate
|
|
1855
|
+
: selected);
|
|
1856
|
+
}
|
|
1857
|
+
function countUnquoted(line, delimiter) {
|
|
1858
|
+
let quoted = false;
|
|
1859
|
+
let count = 0;
|
|
1860
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
1861
|
+
if (line[index] === '"' && line[index + 1] === '"' && quoted) {
|
|
1862
|
+
index += 1;
|
|
1863
|
+
}
|
|
1864
|
+
else if (line[index] === '"') {
|
|
1865
|
+
quoted = !quoted;
|
|
1866
|
+
}
|
|
1867
|
+
else if (line[index] === delimiter && !quoted) {
|
|
1868
|
+
count += 1;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return count;
|
|
1872
|
+
}
|
|
1873
|
+
function parseCsvValue(value) {
|
|
1874
|
+
if (!value) {
|
|
1875
|
+
return null;
|
|
1876
|
+
}
|
|
1877
|
+
if (/^(true|false)$/i.test(value)) {
|
|
1878
|
+
return value.toLowerCase() === 'true';
|
|
1879
|
+
}
|
|
1880
|
+
if (/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(value)) {
|
|
1881
|
+
const number = Number(value);
|
|
1882
|
+
if (Number.isFinite(number)) {
|
|
1883
|
+
return number;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
return value;
|
|
1887
|
+
}
|
|
1888
|
+
function isValidDateOnly(value) {
|
|
1889
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1890
|
+
return false;
|
|
1891
|
+
}
|
|
1892
|
+
const date = new Date(`${value}T00:00:00.000Z`);
|
|
1893
|
+
return (!Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value);
|
|
1894
|
+
}
|
|
1895
|
+
function assertSafeJson(value, path) {
|
|
1896
|
+
if (value === null ||
|
|
1897
|
+
typeof value === 'string' ||
|
|
1898
|
+
typeof value === 'boolean') {
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
if (typeof value === 'number') {
|
|
1902
|
+
if (!Number.isFinite(value)) {
|
|
1903
|
+
throw new Error(`${path} contains a non-finite number.`);
|
|
1904
|
+
}
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (Array.isArray(value)) {
|
|
1908
|
+
value.forEach((item, index) => assertSafeJson(item, `${path}[${index}]`));
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
if (typeof value === 'object') {
|
|
1912
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
1913
|
+
if (dangerousKeys.has(key)) {
|
|
1914
|
+
throw new Error(`${path} contains a dangerous JSON key.`);
|
|
1915
|
+
}
|
|
1916
|
+
assertSafeJson(nestedValue, `${path}.${key}`);
|
|
1917
|
+
}
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
throw new Error(`${path} contains a value that cannot be serialized as JSON.`);
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1539
1923
|
//#endregion
|
|
1540
1924
|
let TaonCmsContentRepository = class TaonCmsContentRepository extends TaonBaseRepository {
|
|
1541
1925
|
constructor() {
|
|
@@ -2055,7 +2439,7 @@ let TaonCmsContentRepository = class TaonCmsContentRepository extends TaonBaseRe
|
|
|
2055
2439
|
if (content.status !== 'archived') {
|
|
2056
2440
|
contentError('Only archived posts can be permanently deleted.', 409);
|
|
2057
2441
|
}
|
|
2058
|
-
const repository = manager.getRepository(
|
|
2442
|
+
const repository = manager.getRepository(TaonCmsContentEntity);
|
|
2059
2443
|
const assetRepository = manager.getRepository(TaonCmsContentAssetEntity);
|
|
2060
2444
|
const assetKeys = [
|
|
2061
2445
|
content.videoKey,
|
|
@@ -2660,6 +3044,21 @@ let TaonCmsContentRepository = class TaonCmsContentRepository extends TaonBaseRe
|
|
|
2660
3044
|
if (title === undefined || slug === undefined) {
|
|
2661
3045
|
contentError('title and slug are required.');
|
|
2662
3046
|
}
|
|
3047
|
+
if (type === TaonCmsContentType.JsonTable) {
|
|
3048
|
+
try {
|
|
3049
|
+
const body = input.body === undefined ? (current?.body ?? null) : input.body;
|
|
3050
|
+
if (typeof body === 'string' && body.length > TAON_SPREADSHEET_MAX_BYTES) {
|
|
3051
|
+
contentError('JSON Table content exceeds the 10 MB limit.');
|
|
3052
|
+
}
|
|
3053
|
+
parseSpreadsheetDocument(body);
|
|
3054
|
+
}
|
|
3055
|
+
catch (error) {
|
|
3056
|
+
if (error instanceof Error) {
|
|
3057
|
+
contentError(`JSON Table content is invalid: ${error.message}`);
|
|
3058
|
+
}
|
|
3059
|
+
throw error;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
2663
3062
|
const publishedAt = input.publishedAt === undefined
|
|
2664
3063
|
? (current?.publishedAt ?? null)
|
|
2665
3064
|
: input.publishedAt;
|
|
@@ -3996,6 +4395,10 @@ let TaonCmsContentStorageController = class TaonCmsContentStorageController exte
|
|
|
3996
4395
|
/* */
|
|
3997
4396
|
/* */
|
|
3998
4397
|
/* */
|
|
4398
|
+
/* */
|
|
4399
|
+
/* */
|
|
4400
|
+
/* */
|
|
4401
|
+
/* */
|
|
3999
4402
|
return (void 0);
|
|
4000
4403
|
}
|
|
4001
4404
|
async handleUploadFiles(files, queryParams, req, res) {
|
|
@@ -4036,6 +4439,50 @@ let TaonCmsContentStorageController = class TaonCmsContentStorageController exte
|
|
|
4036
4439
|
/* */
|
|
4037
4440
|
return (void 0);
|
|
4038
4441
|
}
|
|
4442
|
+
/**
|
|
4443
|
+
* Access is checked in beforeEachRequest (CMS managers only).
|
|
4444
|
+
* The declared size is fixed at start and enforced for every part.
|
|
4445
|
+
*/
|
|
4446
|
+
async authorizeMultipartUpload(options) {
|
|
4447
|
+
/* */
|
|
4448
|
+
/* */
|
|
4449
|
+
/* */
|
|
4450
|
+
/* */
|
|
4451
|
+
/* */
|
|
4452
|
+
/* */
|
|
4453
|
+
/* */
|
|
4454
|
+
/* */
|
|
4455
|
+
/* */
|
|
4456
|
+
/* */
|
|
4457
|
+
/* */
|
|
4458
|
+
return (void 0);
|
|
4459
|
+
}
|
|
4460
|
+
async handleCompletedMultipartUpload(object, queryParams, req) {
|
|
4461
|
+
/* */
|
|
4462
|
+
/* */
|
|
4463
|
+
/* */
|
|
4464
|
+
/* */
|
|
4465
|
+
/* */
|
|
4466
|
+
return (void 0);
|
|
4467
|
+
}
|
|
4468
|
+
async withDownloadUrl(object, req) {
|
|
4469
|
+
/* */
|
|
4470
|
+
/* */
|
|
4471
|
+
/* */
|
|
4472
|
+
/* */
|
|
4473
|
+
/* */
|
|
4474
|
+
/* */
|
|
4475
|
+
/* */
|
|
4476
|
+
/* */
|
|
4477
|
+
/* */
|
|
4478
|
+
/* */
|
|
4479
|
+
/* */
|
|
4480
|
+
/* */
|
|
4481
|
+
/* */
|
|
4482
|
+
/* */
|
|
4483
|
+
/* */
|
|
4484
|
+
return (void 0);
|
|
4485
|
+
}
|
|
4039
4486
|
afterFileUploadHook() { }
|
|
4040
4487
|
async isMediaObjectAvailable(key) {
|
|
4041
4488
|
/* */
|
|
@@ -4199,6 +4646,7 @@ function defaultThumbnailUrl(key) {
|
|
|
4199
4646
|
video: '<rect x="248" y="132" width="144" height="100" rx="16"/><path d="m308 158 40 24-40 24z"/>',
|
|
4200
4647
|
audio: '<path d="M280 164v40m20-60v80m20-98v116m20-98v80m20-60v40"/>',
|
|
4201
4648
|
attachment: '<path d="M276 124h60l32 32v84h-92z M336 124v36h32m-70 30h48m-48 22h36"/>',
|
|
4649
|
+
'json-table': '<path d="M252 130h136v100H252z M252 164h136m-92-34v100m46-100v100m-90-66h136m-136 33h136"/>',
|
|
4202
4650
|
};
|
|
4203
4651
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="360" viewBox="0 0 640 360"><defs><linearGradient id="bg" x2="1" y2="1"><stop stop-color="#eaf0ff"/><stop offset="1" stop-color="#e4f5f0"/></linearGradient></defs><rect width="640" height="360" rx="24" fill="url(#bg)"/><circle cx="320" cy="180" r="112" fill="#fff" opacity=".65"/><g fill="none" stroke="#52669a" stroke-width="7" stroke-linecap="round" stroke-linejoin="round">${shapes[type]}</g></svg>`;
|
|
4204
4652
|
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
@@ -4373,6 +4821,8 @@ let TaonCmsContentController = class TaonCmsContentController extends TaonBaseCr
|
|
|
4373
4821
|
/* */
|
|
4374
4822
|
/* */
|
|
4375
4823
|
/* */
|
|
4824
|
+
/* */
|
|
4825
|
+
/* */
|
|
4376
4826
|
return (void 0);
|
|
4377
4827
|
}
|
|
4378
4828
|
//#endregion
|
|
@@ -4524,6 +4974,57 @@ let TaonCmsContentController = class TaonCmsContentController extends TaonBaseCr
|
|
|
4524
4974
|
//#endregion
|
|
4525
4975
|
}
|
|
4526
4976
|
//#endregion
|
|
4977
|
+
getJsonTableBySlug(slug) {
|
|
4978
|
+
//#region @websqlFunc
|
|
4979
|
+
return async (req, _res, { t }) => {
|
|
4980
|
+
const post = await this.requireJsonTablePost(slug, req, t, false);
|
|
4981
|
+
return this.jsonTableRows(post.body, t);
|
|
4982
|
+
};
|
|
4983
|
+
//#endregion
|
|
4984
|
+
}
|
|
4985
|
+
getPublicJsonTableBySlug(slug) {
|
|
4986
|
+
//#region @websqlFunc
|
|
4987
|
+
return async (req, _res, { t }) => {
|
|
4988
|
+
const post = await this.requireJsonTablePost(slug, req, t, true);
|
|
4989
|
+
return this.jsonTableRows(post.body, t);
|
|
4990
|
+
};
|
|
4991
|
+
//#endregion
|
|
4992
|
+
}
|
|
4993
|
+
async requireJsonTablePost(slug, req, t, publicOnly) {
|
|
4994
|
+
//#region @websqlFunc
|
|
4995
|
+
const normalizedSlug = typeof slug === 'string' ? slug.trim() : '';
|
|
4996
|
+
if (!normalizedSlug || normalizedSlug.length > 255) {
|
|
4997
|
+
contentError(t.gettext('The post slug is invalid.'), 400);
|
|
4998
|
+
}
|
|
4999
|
+
const post = await this.taonCmsContentRepository.getContentBySlugForMedia(normalizedSlug);
|
|
5000
|
+
if (!post) {
|
|
5001
|
+
contentError(t.gettext('The post was not found.'), 404);
|
|
5002
|
+
}
|
|
5003
|
+
if (post.type !== TaonCmsContentType.JsonTable) {
|
|
5004
|
+
contentError(t.gettext('The post is not a JSON Table.'), 400);
|
|
5005
|
+
}
|
|
5006
|
+
await this.requireReadAccess(post, publicOnly, req, t);
|
|
5007
|
+
return post;
|
|
5008
|
+
//#endregion
|
|
5009
|
+
}
|
|
5010
|
+
jsonTableRows(body, t) {
|
|
5011
|
+
//#region @websqlFunc
|
|
5012
|
+
if (body && body.length > TAON_SPREADSHEET_MAX_BYTES) {
|
|
5013
|
+
contentError('JSON Table content exceeds the 10 MB limit.', 400);
|
|
5014
|
+
}
|
|
5015
|
+
try {
|
|
5016
|
+
return parseSpreadsheetDocument(body).rows;
|
|
5017
|
+
}
|
|
5018
|
+
catch (error) {
|
|
5019
|
+
if (error instanceof Error) {
|
|
5020
|
+
contentError(t.gettext('JSON Table content is invalid: [[ error ]]', {
|
|
5021
|
+
error: error.message,
|
|
5022
|
+
}), 400);
|
|
5023
|
+
}
|
|
5024
|
+
throw error;
|
|
5025
|
+
}
|
|
5026
|
+
//#endregion
|
|
5027
|
+
}
|
|
4527
5028
|
getPublicBySlug(slug) {
|
|
4528
5029
|
//#region @websqlFunc
|
|
4529
5030
|
return async (req, _res, { t }) => {
|
|
@@ -4858,6 +5359,20 @@ __decorate([
|
|
|
4858
5359
|
__metadata("design:paramtypes", [String]),
|
|
4859
5360
|
__metadata("design:returntype", Object)
|
|
4860
5361
|
], TaonCmsContentController.prototype, "getBySlug", null);
|
|
5362
|
+
__decorate([
|
|
5363
|
+
GET(),
|
|
5364
|
+
__param(0, Query('slug')),
|
|
5365
|
+
__metadata("design:type", Function),
|
|
5366
|
+
__metadata("design:paramtypes", [String]),
|
|
5367
|
+
__metadata("design:returntype", Object)
|
|
5368
|
+
], TaonCmsContentController.prototype, "getJsonTableBySlug", null);
|
|
5369
|
+
__decorate([
|
|
5370
|
+
GET(),
|
|
5371
|
+
__param(0, Query('slug')),
|
|
5372
|
+
__metadata("design:type", Function),
|
|
5373
|
+
__metadata("design:paramtypes", [String]),
|
|
5374
|
+
__metadata("design:returntype", Object)
|
|
5375
|
+
], TaonCmsContentController.prototype, "getPublicJsonTableBySlug", null);
|
|
4861
5376
|
__decorate([
|
|
4862
5377
|
GET(),
|
|
4863
5378
|
__param(0, Query('slug')),
|
|
@@ -5040,6 +5555,8 @@ TaonCmsContentController = __decorate([
|
|
|
5040
5555
|
allowedMethods: [
|
|
5041
5556
|
'getAll',
|
|
5042
5557
|
'getBy',
|
|
5558
|
+
'getJsonTableBySlug',
|
|
5559
|
+
'getPublicJsonTableBySlug',
|
|
5043
5560
|
'getPublicBySlug',
|
|
5044
5561
|
'getPublicVideoChildrenBySlug',
|
|
5045
5562
|
'createContent',
|
|
@@ -5090,6 +5607,13 @@ TaonCmsContentMiddleware = __decorate([
|
|
|
5090
5607
|
|
|
5091
5608
|
//#endregion
|
|
5092
5609
|
let TaonCmsContentProvider = class TaonCmsContentProvider extends TaonBaseProvider {
|
|
5610
|
+
constructor() {
|
|
5611
|
+
super(...arguments);
|
|
5612
|
+
/**
|
|
5613
|
+
* Slugs offered in the post editor "Required Slugs" selector.
|
|
5614
|
+
*/
|
|
5615
|
+
this.requiredSlugs = [];
|
|
5616
|
+
}
|
|
5093
5617
|
async _() {
|
|
5094
5618
|
if (!this.siteName) {
|
|
5095
5619
|
throw new Error(`Please provide siteName in TaonCmsContentProvider`);
|
|
@@ -5450,80 +5974,20 @@ const TaonCmsAbstractContext = createContext(() => ({
|
|
|
5450
5974
|
}));
|
|
5451
5975
|
// THIS FILE IS GENERATED - DO NOT MODIFY
|
|
5452
5976
|
|
|
5453
|
-
const
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
return next.handle(req);
|
|
5468
|
-
}
|
|
5469
|
-
return new Observable(subscriber => {
|
|
5470
|
-
const xhr = new XMLHttpRequest();
|
|
5471
|
-
xhr.open(req.method || 'POST', req.url);
|
|
5472
|
-
xhr.withCredentials = req.credentials === 'include';
|
|
5473
|
-
new Headers(req.headers).forEach((value, name) => {
|
|
5474
|
-
// The browser must supply the multipart boundary.
|
|
5475
|
-
if (name.toLowerCase() !== 'content-type') {
|
|
5476
|
-
xhr.setRequestHeader(name, value);
|
|
5477
|
-
}
|
|
5478
|
-
});
|
|
5479
|
-
xhr.upload.onprogress = event => {
|
|
5480
|
-
if (event.lengthComputable && event.total > 0) {
|
|
5481
|
-
callback(Math.min(100, Math.floor((event.loaded * 100) / event.total)));
|
|
5482
|
-
}
|
|
5483
|
-
};
|
|
5484
|
-
xhr.onload = () => {
|
|
5485
|
-
const headers = new Headers();
|
|
5486
|
-
for (const line of xhr
|
|
5487
|
-
.getAllResponseHeaders()
|
|
5488
|
-
.trim()
|
|
5489
|
-
.split(/[\r\n]+/)) {
|
|
5490
|
-
const separator = line.indexOf(':');
|
|
5491
|
-
if (separator > 0) {
|
|
5492
|
-
headers.append(line.slice(0, separator), line.slice(separator + 1).trim());
|
|
5493
|
-
}
|
|
5494
|
-
}
|
|
5495
|
-
subscriber.next(new Response([204, 205, 304].includes(xhr.status) ? null : xhr.responseText, {
|
|
5496
|
-
status: xhr.status,
|
|
5497
|
-
statusText: xhr.statusText,
|
|
5498
|
-
headers,
|
|
5499
|
-
}));
|
|
5500
|
-
subscriber.complete();
|
|
5501
|
-
};
|
|
5502
|
-
xhr.onerror = () => subscriber.error(new Error('Media upload network error.'));
|
|
5503
|
-
xhr.ontimeout = () => subscriber.error(new Error('Media upload timed out.'));
|
|
5504
|
-
xhr.onabort = () => subscriber.error(new Error('Media upload was cancelled.'));
|
|
5505
|
-
const abort = () => xhr.abort();
|
|
5506
|
-
req.signal?.addEventListener('abort', abort, { once: true });
|
|
5507
|
-
if (req.signal?.aborted) {
|
|
5508
|
-
subscriber.error(new Error('Media upload was cancelled.'));
|
|
5509
|
-
}
|
|
5510
|
-
else {
|
|
5511
|
-
callback(0);
|
|
5512
|
-
xhr.send(uploadBody);
|
|
5513
|
-
}
|
|
5514
|
-
return () => {
|
|
5515
|
-
req.signal?.removeEventListener('abort', abort);
|
|
5516
|
-
if (xhr.readyState !== XMLHttpRequest.DONE) {
|
|
5517
|
-
xhr.abort();
|
|
5518
|
-
}
|
|
5519
|
-
};
|
|
5520
|
-
});
|
|
5521
|
-
},
|
|
5522
|
-
});
|
|
5523
|
-
}
|
|
5524
|
-
progressCallbacks.set(body, onProgress);
|
|
5525
|
-
return () => progressCallbacks.delete(body);
|
|
5526
|
-
}
|
|
5977
|
+
const TaonCmsContentDefaultsValues = {
|
|
5978
|
+
description: '',
|
|
5979
|
+
version: 0,
|
|
5980
|
+
id: void 0,
|
|
5981
|
+
type: TaonCmsContentType.Normal,
|
|
5982
|
+
videoKey: null,
|
|
5983
|
+
audioKey: null,
|
|
5984
|
+
attachmentKey: null,
|
|
5985
|
+
thumbnailKey: null,
|
|
5986
|
+
};
|
|
5987
|
+
/**
|
|
5988
|
+
* Strict maximum size of a single CMS media upload (1 GB).
|
|
5989
|
+
*/
|
|
5990
|
+
const TAON_CMS_MAX_UPLOAD_BYTES = 1_000_000_000;
|
|
5527
5991
|
|
|
5528
5992
|
//#region imports
|
|
5529
5993
|
//#endregion
|
|
@@ -5556,23 +6020,33 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5556
6020
|
}
|
|
5557
6021
|
//#endregion
|
|
5558
6022
|
async getPublicBySlug(slug) {
|
|
5559
|
-
const response = await this.taonCmsContentController
|
|
5560
|
-
.getPublicBySlug(slug)
|
|
5561
|
-
.request();
|
|
6023
|
+
const response = await this.taonCmsContentController.getPublicBySlug(slug).request();
|
|
5562
6024
|
if (response.statusCode >= 400 || !response.body?.json) {
|
|
5563
6025
|
throw new Error(response.responseText || 'The post could not be loaded.');
|
|
5564
6026
|
}
|
|
5565
6027
|
return response.body.json;
|
|
5566
6028
|
}
|
|
5567
|
-
async
|
|
5568
|
-
const response = await this.taonCmsContentController
|
|
5569
|
-
.getPublicVideoChildrenBySlug(slug)
|
|
5570
|
-
.request();
|
|
6029
|
+
async getJsonTableBySlug(slug) {
|
|
6030
|
+
const response = await this.taonCmsContentController.getJsonTableBySlug(slug).request();
|
|
5571
6031
|
if (response.statusCode >= 400 || !Array.isArray(response.body?.json)) {
|
|
5572
|
-
throw new Error(response.responseText || '
|
|
6032
|
+
throw new Error(response.responseText || 'The JSON Table could not be loaded.');
|
|
5573
6033
|
}
|
|
5574
6034
|
return response.body.json;
|
|
5575
6035
|
}
|
|
6036
|
+
getPublicJsonTableBySlug(slug) {
|
|
6037
|
+
return this.taonCmsContentController.getPublicJsonTableBySlug(slug)
|
|
6038
|
+
.request().observable.pipe(map(c => c.body.json), catchError(error => {
|
|
6039
|
+
console.error(`Failed to load JSON table "${slug}":`, error);
|
|
6040
|
+
return of([]);
|
|
6041
|
+
}));
|
|
6042
|
+
}
|
|
6043
|
+
getPublicVideoChildrenBySlug(slug) {
|
|
6044
|
+
return this.taonCmsContentController.getPublicVideoChildrenBySlug(slug)
|
|
6045
|
+
.request().observable.pipe(map(c => c.body.json), catchError(error => {
|
|
6046
|
+
console.error(`Failed to load public video children for "${slug}":`, error);
|
|
6047
|
+
return of([]);
|
|
6048
|
+
}));
|
|
6049
|
+
}
|
|
5576
6050
|
//#region API / get video link by slug
|
|
5577
6051
|
/**
|
|
5578
6052
|
* Get link to private video
|
|
@@ -5635,7 +6109,7 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5635
6109
|
//#region API / get public content by hook
|
|
5636
6110
|
getPublicContentByHook(hook) {
|
|
5637
6111
|
return this.taonCmsContentController.getPublicContentByHook(hook)
|
|
5638
|
-
.request().observable.pipe(map(res => res.body.text), catchError(() => of('---')));
|
|
6112
|
+
.request().observable.pipe(map(res => (res.body.text || '')?.replace(/\\n/g, '\n')), catchError(() => of('---')));
|
|
5639
6113
|
}
|
|
5640
6114
|
//#endregion
|
|
5641
6115
|
//#region API / list application hooks
|
|
@@ -5645,26 +6119,21 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5645
6119
|
}
|
|
5646
6120
|
//#endregion
|
|
5647
6121
|
//#region API / upload media
|
|
5648
|
-
async uploadMedia(file, onProgress) {
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
:
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
throw new Error('The upload did not return a media key.');
|
|
5661
|
-
}
|
|
5662
|
-
onProgress?.(100);
|
|
5663
|
-
return objects[0];
|
|
5664
|
-
}
|
|
5665
|
-
finally {
|
|
5666
|
-
stopTracking?.();
|
|
6122
|
+
async uploadMedia(file, onProgress, signal) {
|
|
6123
|
+
if (file.size > TAON_CMS_MAX_UPLOAD_BYTES) {
|
|
6124
|
+
throw new Error(`The file "${file.name}" is too large. The maximum upload size is 1 GB.`);
|
|
6125
|
+
}
|
|
6126
|
+
const objects = await this.taonCmsContentStorageController.uploadFileInChunks(file, {
|
|
6127
|
+
fileName: file.name,
|
|
6128
|
+
contentType: file.type || 'application/octet-stream',
|
|
6129
|
+
signal,
|
|
6130
|
+
onProgress,
|
|
6131
|
+
});
|
|
6132
|
+
if (!Array.isArray(objects) || objects.length !== 1 || !objects[0]?.key) {
|
|
6133
|
+
throw new Error('The upload did not return a media key.');
|
|
5667
6134
|
}
|
|
6135
|
+
onProgress?.(100);
|
|
6136
|
+
return objects[0];
|
|
5668
6137
|
}
|
|
5669
6138
|
//#endregion
|
|
5670
6139
|
async deleteUnattachedMedia(key) {
|
|
@@ -5733,10 +6202,7 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5733
6202
|
}
|
|
5734
6203
|
//#endregion
|
|
5735
6204
|
permanentlyDeleteContent(id, expectedVersion) {
|
|
5736
|
-
return this.taonCmsContentController
|
|
5737
|
-
.permanentlyDeleteContent(id, expectedVersion)
|
|
5738
|
-
.request()
|
|
5739
|
-
.observable.pipe(map(res => res.body.json));
|
|
6205
|
+
return this.taonCmsContentController.permanentlyDeleteContent(id, expectedVersion).request().observable.pipe(map(res => res.body.json));
|
|
5740
6206
|
}
|
|
5741
6207
|
//#region API / restore content
|
|
5742
6208
|
restoreContent(id, input) {
|
|
@@ -5761,7 +6227,8 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5761
6227
|
}
|
|
5762
6228
|
//#endregion
|
|
5763
6229
|
async getParentPost(id) {
|
|
5764
|
-
return (await this.taonCmsContentController.getParentPost(id).request())
|
|
6230
|
+
return (await this.taonCmsContentController.getParentPost(id).request())
|
|
6231
|
+
.body.json;
|
|
5765
6232
|
}
|
|
5766
6233
|
async getVisibilityConfiguration(id) {
|
|
5767
6234
|
return (await this.taonCmsContentController
|
|
@@ -5769,7 +6236,8 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5769
6236
|
.request()).body.json;
|
|
5770
6237
|
}
|
|
5771
6238
|
async getPostVersions(id) {
|
|
5772
|
-
return (await this.taonCmsContentController.getPostVersions(id).request())
|
|
6239
|
+
return (await this.taonCmsContentController.getPostVersions(id).request())
|
|
6240
|
+
.body.json;
|
|
5773
6241
|
}
|
|
5774
6242
|
//#region API / list permissions
|
|
5775
6243
|
listPermissions(id) {
|
|
@@ -5794,16 +6262,10 @@ class TaonCmsContentApiService extends TaonBaseAngularService {
|
|
|
5794
6262
|
}
|
|
5795
6263
|
//#endregion
|
|
5796
6264
|
linkVersionPost(id, versionPostId, kind, expectedVersion) {
|
|
5797
|
-
return this.taonCmsContentController
|
|
5798
|
-
.linkVersionPost(id, versionPostId, kind, expectedVersion)
|
|
5799
|
-
.request()
|
|
5800
|
-
.observable.pipe(map(res => res.body.json));
|
|
6265
|
+
return this.taonCmsContentController.linkVersionPost(id, versionPostId, kind, expectedVersion).request().observable.pipe(map(res => res.body.json));
|
|
5801
6266
|
}
|
|
5802
6267
|
removeVersionPost(id, kind, expectedVersion) {
|
|
5803
|
-
return this.taonCmsContentController
|
|
5804
|
-
.removeVersionPost(id, kind, expectedVersion)
|
|
5805
|
-
.request()
|
|
5806
|
-
.observable.pipe(map(res => res.body.json));
|
|
6268
|
+
return this.taonCmsContentController.removeVersionPost(id, kind, expectedVersion).request().observable.pipe(map(res => res.body.json));
|
|
5807
6269
|
}
|
|
5808
6270
|
//#region API / get all entites
|
|
5809
6271
|
/**
|
|
@@ -6366,6 +6828,13 @@ class TaonCmsContentConfigService extends TaonBaseAngularService {
|
|
|
6366
6828
|
get siteName() {
|
|
6367
6829
|
return this.taonCmsContentProvider.siteName;
|
|
6368
6830
|
}
|
|
6831
|
+
get requiredSlugs() {
|
|
6832
|
+
return [
|
|
6833
|
+
...new Set((this.taonCmsContentProvider.requiredSlugs ?? [])
|
|
6834
|
+
.map(slug => String(slug ?? '').trim())
|
|
6835
|
+
.filter(Boolean)),
|
|
6836
|
+
];
|
|
6837
|
+
}
|
|
6369
6838
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsContentConfigService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6370
6839
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsContentConfigService }); }
|
|
6371
6840
|
}
|
|
@@ -6373,17 +6842,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
6373
6842
|
type: Injectable
|
|
6374
6843
|
}] });
|
|
6375
6844
|
|
|
6376
|
-
const TaonCmsContentDefaultsValues = {
|
|
6377
|
-
description: '',
|
|
6378
|
-
version: 0,
|
|
6379
|
-
id: void 0,
|
|
6380
|
-
type: TaonCmsContentType.Normal,
|
|
6381
|
-
videoKey: null,
|
|
6382
|
-
audioKey: null,
|
|
6383
|
-
attachmentKey: null,
|
|
6384
|
-
thumbnailKey: null,
|
|
6385
|
-
};
|
|
6386
|
-
|
|
6387
6845
|
/**
|
|
6388
6846
|
* Resolves allowlisted values as HTML text/attribute data, not markup.
|
|
6389
6847
|
* The returned HTML must still pass through the renderer's HTML sanitizer.
|
|
@@ -6412,6 +6870,81 @@ function resolveTemplateVariables(content, context) {
|
|
|
6412
6870
|
});
|
|
6413
6871
|
}
|
|
6414
6872
|
|
|
6873
|
+
const progressCallbacks = new WeakMap();
|
|
6874
|
+
// Keep Taon's middleware, retries and response decoding; replace only the
|
|
6875
|
+
// final Fetch transport for uploads that request browser upload events.
|
|
6876
|
+
function trackCmsUpload(url, body, onProgress) {
|
|
6877
|
+
const key = `cms-upload-progress-POST-${new URL(url).pathname}`;
|
|
6878
|
+
if (!Resource__NS__methodsInterceptors.has(key)) {
|
|
6879
|
+
Resource__NS__methodsInterceptors.set(key, {
|
|
6880
|
+
intercept: ({ req, next }) => {
|
|
6881
|
+
if (!(req.body instanceof FormData)) {
|
|
6882
|
+
return next.handle(req);
|
|
6883
|
+
}
|
|
6884
|
+
const uploadBody = req.body;
|
|
6885
|
+
const callback = progressCallbacks.get(uploadBody);
|
|
6886
|
+
if (!callback) {
|
|
6887
|
+
return next.handle(req);
|
|
6888
|
+
}
|
|
6889
|
+
return new Observable(subscriber => {
|
|
6890
|
+
const xhr = new XMLHttpRequest();
|
|
6891
|
+
xhr.open(req.method || 'POST', req.url);
|
|
6892
|
+
xhr.withCredentials = req.credentials === 'include';
|
|
6893
|
+
new Headers(req.headers).forEach((value, name) => {
|
|
6894
|
+
// The browser must supply the multipart boundary.
|
|
6895
|
+
if (name.toLowerCase() !== 'content-type') {
|
|
6896
|
+
xhr.setRequestHeader(name, value);
|
|
6897
|
+
}
|
|
6898
|
+
});
|
|
6899
|
+
xhr.upload.onprogress = event => {
|
|
6900
|
+
if (event.lengthComputable && event.total > 0) {
|
|
6901
|
+
callback(Math.min(100, Math.floor((event.loaded * 100) / event.total)));
|
|
6902
|
+
}
|
|
6903
|
+
};
|
|
6904
|
+
xhr.onload = () => {
|
|
6905
|
+
const headers = new Headers();
|
|
6906
|
+
for (const line of xhr
|
|
6907
|
+
.getAllResponseHeaders()
|
|
6908
|
+
.trim()
|
|
6909
|
+
.split(/[\r\n]+/)) {
|
|
6910
|
+
const separator = line.indexOf(':');
|
|
6911
|
+
if (separator > 0) {
|
|
6912
|
+
headers.append(line.slice(0, separator), line.slice(separator + 1).trim());
|
|
6913
|
+
}
|
|
6914
|
+
}
|
|
6915
|
+
subscriber.next(new Response([204, 205, 304].includes(xhr.status) ? null : xhr.responseText, {
|
|
6916
|
+
status: xhr.status,
|
|
6917
|
+
statusText: xhr.statusText,
|
|
6918
|
+
headers,
|
|
6919
|
+
}));
|
|
6920
|
+
subscriber.complete();
|
|
6921
|
+
};
|
|
6922
|
+
xhr.onerror = () => subscriber.error(new Error('Media upload network error.'));
|
|
6923
|
+
xhr.ontimeout = () => subscriber.error(new Error('Media upload timed out.'));
|
|
6924
|
+
xhr.onabort = () => subscriber.error(new Error('Media upload was cancelled.'));
|
|
6925
|
+
const abort = () => xhr.abort();
|
|
6926
|
+
req.signal?.addEventListener('abort', abort, { once: true });
|
|
6927
|
+
if (req.signal?.aborted) {
|
|
6928
|
+
subscriber.error(new Error('Media upload was cancelled.'));
|
|
6929
|
+
}
|
|
6930
|
+
else {
|
|
6931
|
+
callback(0);
|
|
6932
|
+
xhr.send(uploadBody);
|
|
6933
|
+
}
|
|
6934
|
+
return () => {
|
|
6935
|
+
req.signal?.removeEventListener('abort', abort);
|
|
6936
|
+
if (xhr.readyState !== XMLHttpRequest.DONE) {
|
|
6937
|
+
xhr.abort();
|
|
6938
|
+
}
|
|
6939
|
+
};
|
|
6940
|
+
});
|
|
6941
|
+
},
|
|
6942
|
+
});
|
|
6943
|
+
}
|
|
6944
|
+
progressCallbacks.set(body, onProgress);
|
|
6945
|
+
return () => progressCallbacks.delete(body);
|
|
6946
|
+
}
|
|
6947
|
+
|
|
6415
6948
|
class TaonCmsContentAssetApiService extends TaonBaseAngularService {
|
|
6416
6949
|
constructor() {
|
|
6417
6950
|
super(...arguments);
|
|
@@ -6990,6 +7523,606 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
6990
7523
|
}]
|
|
6991
7524
|
}] });
|
|
6992
7525
|
|
|
7526
|
+
class TaonSpreadsheetTableComponent {
|
|
7527
|
+
constructor() {
|
|
7528
|
+
this.data = model({
|
|
7529
|
+
version: 1,
|
|
7530
|
+
columns: [],
|
|
7531
|
+
rows: [],
|
|
7532
|
+
}, ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
7533
|
+
this.readOnly = input(false, ...(ngDevMode ? [{ debugName: "readOnly" }] : []));
|
|
7534
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
7535
|
+
this.validationError = model('', ...(ngDevMode ? [{ debugName: "validationError" }] : []));
|
|
7536
|
+
this.view = signal('table', ...(ngDevMode ? [{ debugName: "view" }] : []));
|
|
7537
|
+
this.activeCell = signal(null, ...(ngDevMode ? [{ debugName: "activeCell" }] : []));
|
|
7538
|
+
this.pendingImport = signal(null, ...(ngDevMode ? [{ debugName: "pendingImport" }] : []));
|
|
7539
|
+
this.importFormat = signal('', ...(ngDevMode ? [{ debugName: "importFormat" }] : []));
|
|
7540
|
+
this.importMode = signal('replace', ...(ngDevMode ? [{ debugName: "importMode" }] : []));
|
|
7541
|
+
this.columnTypes = [
|
|
7542
|
+
'string',
|
|
7543
|
+
'number',
|
|
7544
|
+
'boolean',
|
|
7545
|
+
'date',
|
|
7546
|
+
'json',
|
|
7547
|
+
];
|
|
7548
|
+
this.jsonPreview = computed(() => JSON.stringify(this.data().rows, null, 2), ...(ngDevMode ? [{ debugName: "jsonPreview" }] : []));
|
|
7549
|
+
this.undoStack = [];
|
|
7550
|
+
this.redoStack = [];
|
|
7551
|
+
}
|
|
7552
|
+
addColumn() {
|
|
7553
|
+
if (!this.canEdit()) {
|
|
7554
|
+
return;
|
|
7555
|
+
}
|
|
7556
|
+
const data = this.data();
|
|
7557
|
+
let number = data.columns.length + 1;
|
|
7558
|
+
let key = `column${number}`;
|
|
7559
|
+
while (data.columns.some(column => column.key === key)) {
|
|
7560
|
+
number += 1;
|
|
7561
|
+
key = `column${number}`;
|
|
7562
|
+
}
|
|
7563
|
+
this.commit({
|
|
7564
|
+
...data,
|
|
7565
|
+
columns: [
|
|
7566
|
+
...data.columns,
|
|
7567
|
+
{ key, label: `Column ${number}`, type: 'string' },
|
|
7568
|
+
],
|
|
7569
|
+
rows: data.rows.map(row => ({ ...row, [key]: null })),
|
|
7570
|
+
});
|
|
7571
|
+
}
|
|
7572
|
+
addRow() {
|
|
7573
|
+
if (!this.canEdit()) {
|
|
7574
|
+
return;
|
|
7575
|
+
}
|
|
7576
|
+
const row = {};
|
|
7577
|
+
for (const column of this.data().columns) {
|
|
7578
|
+
row[column.key] = null;
|
|
7579
|
+
}
|
|
7580
|
+
this.commit({ ...this.data(), rows: [...this.data().rows, row] });
|
|
7581
|
+
}
|
|
7582
|
+
deleteRow(rowIndex) {
|
|
7583
|
+
if (!this.canEdit() ||
|
|
7584
|
+
!globalThis.confirm('Delete this row? This cannot be undone.')) {
|
|
7585
|
+
return;
|
|
7586
|
+
}
|
|
7587
|
+
this.commit({
|
|
7588
|
+
...this.data(),
|
|
7589
|
+
rows: this.data().rows.filter((_, index) => index !== rowIndex),
|
|
7590
|
+
});
|
|
7591
|
+
}
|
|
7592
|
+
duplicateRow(rowIndex) {
|
|
7593
|
+
if (!this.canEdit()) {
|
|
7594
|
+
return;
|
|
7595
|
+
}
|
|
7596
|
+
const rows = [...this.data().rows];
|
|
7597
|
+
rows.splice(rowIndex + 1, 0, { ...rows[rowIndex] });
|
|
7598
|
+
this.commit({ ...this.data(), rows });
|
|
7599
|
+
}
|
|
7600
|
+
deleteColumn(columnIndex) {
|
|
7601
|
+
if (!this.canEdit() ||
|
|
7602
|
+
!globalThis.confirm('Delete this column and its cell values? This cannot be undone.')) {
|
|
7603
|
+
return;
|
|
7604
|
+
}
|
|
7605
|
+
const column = this.data().columns[columnIndex];
|
|
7606
|
+
const rows = this.data().rows.map(row => {
|
|
7607
|
+
const { [column.key]: _removed, ...remaining } = row;
|
|
7608
|
+
return remaining;
|
|
7609
|
+
});
|
|
7610
|
+
this.commit({
|
|
7611
|
+
...this.data(),
|
|
7612
|
+
columns: this.data().columns.filter((_, index) => index !== columnIndex),
|
|
7613
|
+
rows,
|
|
7614
|
+
});
|
|
7615
|
+
}
|
|
7616
|
+
renameColumn(columnIndex, field, value) {
|
|
7617
|
+
if (!this.canEdit()) {
|
|
7618
|
+
return;
|
|
7619
|
+
}
|
|
7620
|
+
const columns = this.data().columns.map(column => ({ ...column }));
|
|
7621
|
+
const previousKey = columns[columnIndex].key;
|
|
7622
|
+
const nextValue = field === 'key' ? value.trim() : value;
|
|
7623
|
+
const nextKey = field === 'key' ? nextValue : previousKey;
|
|
7624
|
+
if (field === 'key' &&
|
|
7625
|
+
(!/^[a-z][A-Za-z0-9]*$/.test(nextKey) ||
|
|
7626
|
+
['__proto__', 'prototype', 'constructor'].includes(nextKey) ||
|
|
7627
|
+
columns.some((column, index) => index !== columnIndex && column.key === nextKey))) {
|
|
7628
|
+
this.validationError.set('Column keys must be unique, safe camelCase identifiers.');
|
|
7629
|
+
return;
|
|
7630
|
+
}
|
|
7631
|
+
columns[columnIndex] = { ...columns[columnIndex], [field]: nextValue };
|
|
7632
|
+
const rows = field === 'key' && previousKey !== nextKey
|
|
7633
|
+
? this.data().rows.map(row => {
|
|
7634
|
+
const { [previousKey]: value, ...remaining } = row;
|
|
7635
|
+
return { ...remaining, [nextKey]: value };
|
|
7636
|
+
})
|
|
7637
|
+
: this.data().rows;
|
|
7638
|
+
this.commit({ ...this.data(), columns, rows });
|
|
7639
|
+
}
|
|
7640
|
+
setColumnType(columnIndex, type) {
|
|
7641
|
+
if (!this.canEdit()) {
|
|
7642
|
+
return;
|
|
7643
|
+
}
|
|
7644
|
+
const columns = this.data().columns.map(column => ({ ...column }));
|
|
7645
|
+
columns[columnIndex] = { ...columns[columnIndex], type };
|
|
7646
|
+
this.commit({ ...this.data(), columns });
|
|
7647
|
+
}
|
|
7648
|
+
setCell(rowIndex, column, event) {
|
|
7649
|
+
if (!this.canEdit()) {
|
|
7650
|
+
return;
|
|
7651
|
+
}
|
|
7652
|
+
const target = event.target;
|
|
7653
|
+
let value = target.value;
|
|
7654
|
+
if (column.type === 'number') {
|
|
7655
|
+
value = target.value === '' ? null : Number(target.value);
|
|
7656
|
+
if (value !== null && !Number.isFinite(value)) {
|
|
7657
|
+
this.validationError.set(`Row ${rowIndex + 1}, ${column.label || column.key}: enter a valid number.`);
|
|
7658
|
+
return;
|
|
7659
|
+
}
|
|
7660
|
+
}
|
|
7661
|
+
else if (column.type === 'boolean') {
|
|
7662
|
+
value = target.checked;
|
|
7663
|
+
}
|
|
7664
|
+
else if (column.type === 'date') {
|
|
7665
|
+
value = target.value
|
|
7666
|
+
? new Date(`${target.value}T00:00:00.000Z`).toISOString().slice(0, 10)
|
|
7667
|
+
: null;
|
|
7668
|
+
}
|
|
7669
|
+
else if (column.type === 'json') {
|
|
7670
|
+
if (!target.value.trim()) {
|
|
7671
|
+
value = null;
|
|
7672
|
+
}
|
|
7673
|
+
else {
|
|
7674
|
+
try {
|
|
7675
|
+
value = JSON.parse(target.value);
|
|
7676
|
+
}
|
|
7677
|
+
catch {
|
|
7678
|
+
this.validationError.set(`Row ${rowIndex + 1}, ${column.label || column.key}: enter valid JSON.`);
|
|
7679
|
+
return;
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
const rows = this.data().rows.map((row, index) => index === rowIndex ? { ...row, [column.key]: value } : row);
|
|
7684
|
+
this.commit({ ...this.data(), rows });
|
|
7685
|
+
}
|
|
7686
|
+
onCellKeydown(event, row, column) {
|
|
7687
|
+
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
|
|
7688
|
+
const target = event.target;
|
|
7689
|
+
if (target.tagName === 'TEXTAREA') {
|
|
7690
|
+
return;
|
|
7691
|
+
}
|
|
7692
|
+
if (target instanceof HTMLInputElement &&
|
|
7693
|
+
target.type !== 'checkbox' &&
|
|
7694
|
+
(event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
|
7695
|
+
const atBoundary = event.key === 'ArrowLeft'
|
|
7696
|
+
? target.selectionStart === 0
|
|
7697
|
+
: target.selectionStart === target.value.length;
|
|
7698
|
+
if (!atBoundary) {
|
|
7699
|
+
return;
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
event.preventDefault();
|
|
7703
|
+
const next = {
|
|
7704
|
+
row: row +
|
|
7705
|
+
(event.key === 'ArrowUp' ? -1 : event.key === 'ArrowDown' ? 1 : 0),
|
|
7706
|
+
column: column +
|
|
7707
|
+
(event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0),
|
|
7708
|
+
};
|
|
7709
|
+
this.focusCell(next.row, next.column);
|
|
7710
|
+
return;
|
|
7711
|
+
}
|
|
7712
|
+
if (event.target.tagName === 'TEXTAREA' &&
|
|
7713
|
+
event.key === 'Enter' &&
|
|
7714
|
+
!(event.ctrlKey || event.metaKey)) {
|
|
7715
|
+
return;
|
|
7716
|
+
}
|
|
7717
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'z') {
|
|
7718
|
+
event.preventDefault();
|
|
7719
|
+
event.shiftKey ? this.redo() : this.undo();
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'y') {
|
|
7723
|
+
event.preventDefault();
|
|
7724
|
+
this.redo();
|
|
7725
|
+
return;
|
|
7726
|
+
}
|
|
7727
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c') {
|
|
7728
|
+
event.preventDefault();
|
|
7729
|
+
const value = this.cellValue(row, this.data().columns[column]);
|
|
7730
|
+
void (navigator.clipboard?.writeText(value) ??
|
|
7731
|
+
Promise.reject(new Error('Clipboard access is unavailable.'))).catch(() => {
|
|
7732
|
+
this.validationError.set('Could not access the clipboard.');
|
|
7733
|
+
});
|
|
7734
|
+
return;
|
|
7735
|
+
}
|
|
7736
|
+
if (event.key === 'Escape') {
|
|
7737
|
+
event.target.blur();
|
|
7738
|
+
return;
|
|
7739
|
+
}
|
|
7740
|
+
if (event.key === 'Tab' || event.key === 'Enter') {
|
|
7741
|
+
event.preventDefault();
|
|
7742
|
+
const direction = event.shiftKey ? -1 : 1;
|
|
7743
|
+
let next = { row, column };
|
|
7744
|
+
if (event.key === 'Tab') {
|
|
7745
|
+
let nextColumn = column + direction;
|
|
7746
|
+
let nextRow = row;
|
|
7747
|
+
if (nextColumn >= this.data().columns.length) {
|
|
7748
|
+
nextColumn = 0;
|
|
7749
|
+
nextRow += 1;
|
|
7750
|
+
}
|
|
7751
|
+
else if (nextColumn < 0) {
|
|
7752
|
+
nextColumn = this.data().columns.length - 1;
|
|
7753
|
+
nextRow -= 1;
|
|
7754
|
+
}
|
|
7755
|
+
next = { row: nextRow, column: nextColumn };
|
|
7756
|
+
}
|
|
7757
|
+
else {
|
|
7758
|
+
next = { row: row + direction, column };
|
|
7759
|
+
}
|
|
7760
|
+
if (event.key === 'Tab' &&
|
|
7761
|
+
!event.shiftKey &&
|
|
7762
|
+
next.row === this.data().rows.length) {
|
|
7763
|
+
this.addRow();
|
|
7764
|
+
}
|
|
7765
|
+
this.activeCell.set(next);
|
|
7766
|
+
this.focusCell(next.row, next.column);
|
|
7767
|
+
}
|
|
7768
|
+
else if (event.key === 'Backspace' || event.key === 'Delete') {
|
|
7769
|
+
if (!['INPUT', 'TEXTAREA'].includes(event.target.tagName)) {
|
|
7770
|
+
event.preventDefault();
|
|
7771
|
+
this.clearCell(row, column);
|
|
7772
|
+
}
|
|
7773
|
+
}
|
|
7774
|
+
}
|
|
7775
|
+
onCellPaste(event, rowIndex, columnIndex) {
|
|
7776
|
+
if (!this.canEdit()) {
|
|
7777
|
+
return;
|
|
7778
|
+
}
|
|
7779
|
+
const text = event.clipboardData?.getData('text/plain');
|
|
7780
|
+
if (!text || (!text.includes('\t') && !/[\r\n]/.test(text))) {
|
|
7781
|
+
return;
|
|
7782
|
+
}
|
|
7783
|
+
event.preventDefault();
|
|
7784
|
+
try {
|
|
7785
|
+
const matrix = parseClipboardMatrix(text);
|
|
7786
|
+
const columns = [...this.data().columns];
|
|
7787
|
+
const pastedWidth = Math.max(...matrix.map(row => row.length));
|
|
7788
|
+
while (columns.length < columnIndex + pastedWidth &&
|
|
7789
|
+
columns.length < 200) {
|
|
7790
|
+
let number = columns.length + 1;
|
|
7791
|
+
let key = `column${number}`;
|
|
7792
|
+
while (columns.some(column => column.key === key)) {
|
|
7793
|
+
key = `column${++number}`;
|
|
7794
|
+
}
|
|
7795
|
+
columns.push({ key, label: `Column ${number}`, type: 'string' });
|
|
7796
|
+
}
|
|
7797
|
+
if (columns.length < columnIndex + pastedWidth) {
|
|
7798
|
+
throw new Error('Pasted data exceeds the 200-column table limit.');
|
|
7799
|
+
}
|
|
7800
|
+
const rows = [...this.data().rows];
|
|
7801
|
+
while (rows.length < rowIndex + matrix.length && rows.length < 10_000) {
|
|
7802
|
+
const newRow = {};
|
|
7803
|
+
for (const column of columns) {
|
|
7804
|
+
newRow[column.key] = null;
|
|
7805
|
+
}
|
|
7806
|
+
rows.push(newRow);
|
|
7807
|
+
}
|
|
7808
|
+
if (rows.length < rowIndex + matrix.length) {
|
|
7809
|
+
throw new Error('Pasted data exceeds the 10,000-row table limit.');
|
|
7810
|
+
}
|
|
7811
|
+
matrix.forEach((values, pastedRowIndex) => {
|
|
7812
|
+
values.forEach((rawValue, pastedColumnIndex) => {
|
|
7813
|
+
const targetColumn = columnIndex + pastedColumnIndex;
|
|
7814
|
+
const column = columns[targetColumn];
|
|
7815
|
+
rows[rowIndex + pastedRowIndex] = {
|
|
7816
|
+
...rows[rowIndex + pastedRowIndex],
|
|
7817
|
+
[column.key]: parsePastedCell(rawValue, column),
|
|
7818
|
+
};
|
|
7819
|
+
});
|
|
7820
|
+
});
|
|
7821
|
+
this.commit({ version: 1, columns, rows });
|
|
7822
|
+
}
|
|
7823
|
+
catch (error) {
|
|
7824
|
+
this.validationError.set(error instanceof Error ? error.message : 'The pasted data is invalid.');
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7827
|
+
onRowDrop(event) {
|
|
7828
|
+
if (!this.canEdit() || event.previousIndex === event.currentIndex) {
|
|
7829
|
+
return;
|
|
7830
|
+
}
|
|
7831
|
+
const rows = [...this.data().rows];
|
|
7832
|
+
moveItemInArray(rows, event.previousIndex, event.currentIndex);
|
|
7833
|
+
this.commit({ ...this.data(), rows });
|
|
7834
|
+
}
|
|
7835
|
+
onColumnDrop(event) {
|
|
7836
|
+
if (!this.canEdit() || event.previousIndex === event.currentIndex) {
|
|
7837
|
+
return;
|
|
7838
|
+
}
|
|
7839
|
+
const columns = [...this.data().columns];
|
|
7840
|
+
moveItemInArray(columns, event.previousIndex, event.currentIndex);
|
|
7841
|
+
this.commit({ ...this.data(), columns });
|
|
7842
|
+
}
|
|
7843
|
+
undo() {
|
|
7844
|
+
if (!this.canEdit()) {
|
|
7845
|
+
return;
|
|
7846
|
+
}
|
|
7847
|
+
const previous = this.undoStack.pop();
|
|
7848
|
+
if (!previous) {
|
|
7849
|
+
return;
|
|
7850
|
+
}
|
|
7851
|
+
this.redoStack.push(this.data());
|
|
7852
|
+
this.data.set(previous);
|
|
7853
|
+
this.validationError.set('');
|
|
7854
|
+
}
|
|
7855
|
+
redo() {
|
|
7856
|
+
if (!this.canEdit()) {
|
|
7857
|
+
return;
|
|
7858
|
+
}
|
|
7859
|
+
const next = this.redoStack.pop();
|
|
7860
|
+
if (!next) {
|
|
7861
|
+
return;
|
|
7862
|
+
}
|
|
7863
|
+
this.undoStack.push(this.data());
|
|
7864
|
+
this.data.set(next);
|
|
7865
|
+
this.validationError.set('');
|
|
7866
|
+
}
|
|
7867
|
+
async importFile(event) {
|
|
7868
|
+
const inputElement = event.target;
|
|
7869
|
+
const file = inputElement.files?.[0];
|
|
7870
|
+
inputElement.value = '';
|
|
7871
|
+
if (!file || !this.canEdit()) {
|
|
7872
|
+
return;
|
|
7873
|
+
}
|
|
7874
|
+
try {
|
|
7875
|
+
if (file.size > TAON_SPREADSHEET_MAX_BYTES) {
|
|
7876
|
+
throw new Error('Import files must be 10 MB or smaller.');
|
|
7877
|
+
}
|
|
7878
|
+
const text = await file.text();
|
|
7879
|
+
const imported = /\.json$/i.test(file.name)
|
|
7880
|
+
? importSpreadsheetJson(text)
|
|
7881
|
+
: /\.csv$/i.test(file.name)
|
|
7882
|
+
? parseSpreadsheetCsv(text)
|
|
7883
|
+
: (() => {
|
|
7884
|
+
throw new Error('Only JSON and CSV files are supported.');
|
|
7885
|
+
})();
|
|
7886
|
+
this.importFormat.set(/\.json$/i.test(file.name) ? 'JSON' : 'CSV');
|
|
7887
|
+
this.importMode.set('replace');
|
|
7888
|
+
this.pendingImport.set(imported);
|
|
7889
|
+
}
|
|
7890
|
+
catch (error) {
|
|
7891
|
+
this.validationError.set(error instanceof Error
|
|
7892
|
+
? error.message
|
|
7893
|
+
: 'The selected file could not be imported.');
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
applyImport() {
|
|
7897
|
+
const imported = this.pendingImport();
|
|
7898
|
+
if (!imported || !this.canEdit()) {
|
|
7899
|
+
return;
|
|
7900
|
+
}
|
|
7901
|
+
if (this.importMode() === 'replace' &&
|
|
7902
|
+
(this.data().rows.length > 0 || this.data().columns.length > 0) &&
|
|
7903
|
+
!globalThis.confirm('Replace the current table with the imported data?')) {
|
|
7904
|
+
return;
|
|
7905
|
+
}
|
|
7906
|
+
if (this.importMode() === 'append') {
|
|
7907
|
+
const currentColumns = this.data().columns;
|
|
7908
|
+
const compatible = currentColumns.length === imported.columns.length &&
|
|
7909
|
+
currentColumns.every((column, index) => column.key === imported.columns[index].key &&
|
|
7910
|
+
column.type === imported.columns[index].type);
|
|
7911
|
+
if (!compatible) {
|
|
7912
|
+
this.validationError.set('Append requires imported columns with the same keys, types, and order as this table.');
|
|
7913
|
+
return;
|
|
7914
|
+
}
|
|
7915
|
+
this.commit({
|
|
7916
|
+
...this.data(),
|
|
7917
|
+
rows: [...this.data().rows, ...imported.rows],
|
|
7918
|
+
});
|
|
7919
|
+
}
|
|
7920
|
+
else {
|
|
7921
|
+
this.commit(imported);
|
|
7922
|
+
}
|
|
7923
|
+
this.pendingImport.set(null);
|
|
7924
|
+
}
|
|
7925
|
+
cancelImport() {
|
|
7926
|
+
this.pendingImport.set(null);
|
|
7927
|
+
}
|
|
7928
|
+
previewCell(row, column) {
|
|
7929
|
+
const value = row[column.key];
|
|
7930
|
+
return value === undefined
|
|
7931
|
+
? ''
|
|
7932
|
+
: typeof value === 'string'
|
|
7933
|
+
? value
|
|
7934
|
+
: JSON.stringify(value);
|
|
7935
|
+
}
|
|
7936
|
+
downloadJson(fullTable = false) {
|
|
7937
|
+
const content = JSON.stringify(fullTable ? this.data() : this.data().rows, null, 2);
|
|
7938
|
+
this.download(content, fullTable ? 'json-table.json' : 'table-data.json', 'application/json');
|
|
7939
|
+
}
|
|
7940
|
+
downloadCsv() {
|
|
7941
|
+
this.download(serializeSpreadsheetCsv(this.data()), 'table-data.csv', 'text/csv');
|
|
7942
|
+
}
|
|
7943
|
+
async copyJson() {
|
|
7944
|
+
try {
|
|
7945
|
+
if (!navigator.clipboard) {
|
|
7946
|
+
throw new Error('Clipboard access is unavailable.');
|
|
7947
|
+
}
|
|
7948
|
+
await navigator.clipboard.writeText(this.jsonPreview());
|
|
7949
|
+
this.validationError.set('');
|
|
7950
|
+
}
|
|
7951
|
+
catch {
|
|
7952
|
+
this.validationError.set('Could not access the clipboard. Copy the preview manually.');
|
|
7953
|
+
}
|
|
7954
|
+
}
|
|
7955
|
+
keyFromLabel(label) {
|
|
7956
|
+
return normalizeSpreadsheetKey(label);
|
|
7957
|
+
}
|
|
7958
|
+
setColumnWidth(columnIndex, value) {
|
|
7959
|
+
if (!this.canEdit()) {
|
|
7960
|
+
return;
|
|
7961
|
+
}
|
|
7962
|
+
const width = Number(value);
|
|
7963
|
+
if (!Number.isFinite(width) || width < 40 || width > 1000) {
|
|
7964
|
+
this.validationError.set('Column width must be between 40 and 1000 pixels.');
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
const columns = this.data().columns.map(column => ({ ...column }));
|
|
7968
|
+
columns[columnIndex] = { ...columns[columnIndex], width };
|
|
7969
|
+
this.commit({ ...this.data(), columns });
|
|
7970
|
+
}
|
|
7971
|
+
cellValue(rowIndex, column) {
|
|
7972
|
+
const value = this.data().rows[rowIndex]?.[column.key];
|
|
7973
|
+
if (column.type === 'json' && value !== null && value !== undefined) {
|
|
7974
|
+
return JSON.stringify(value);
|
|
7975
|
+
}
|
|
7976
|
+
return value === null || value === undefined ? '' : String(value);
|
|
7977
|
+
}
|
|
7978
|
+
trackColumn(_index, column) {
|
|
7979
|
+
return column.key;
|
|
7980
|
+
}
|
|
7981
|
+
clearCell(rowIndex, columnIndex) {
|
|
7982
|
+
const column = this.data().columns[columnIndex];
|
|
7983
|
+
const rows = this.data().rows.map((row, index) => index === rowIndex ? { ...row, [column.key]: null } : row);
|
|
7984
|
+
this.commit({ ...this.data(), rows });
|
|
7985
|
+
}
|
|
7986
|
+
focusCell(row, column) {
|
|
7987
|
+
if (row < 0 ||
|
|
7988
|
+
row >= this.data().rows.length ||
|
|
7989
|
+
column < 0 ||
|
|
7990
|
+
column >= this.data().columns.length) {
|
|
7991
|
+
return;
|
|
7992
|
+
}
|
|
7993
|
+
this.activeCell.set({ row, column });
|
|
7994
|
+
setTimeout(() => {
|
|
7995
|
+
document
|
|
7996
|
+
.querySelector(`[data-cell="${row}:${column}"]`)
|
|
7997
|
+
?.focus();
|
|
7998
|
+
});
|
|
7999
|
+
}
|
|
8000
|
+
commit(value) {
|
|
8001
|
+
try {
|
|
8002
|
+
const validated = validateSpreadsheetData(value);
|
|
8003
|
+
this.undoStack.push(this.data());
|
|
8004
|
+
if (this.undoStack.length > 100) {
|
|
8005
|
+
this.undoStack.shift();
|
|
8006
|
+
}
|
|
8007
|
+
this.redoStack.length = 0;
|
|
8008
|
+
this.data.set(validated);
|
|
8009
|
+
this.validationError.set('');
|
|
8010
|
+
}
|
|
8011
|
+
catch (error) {
|
|
8012
|
+
this.validationError.set(error instanceof Error ? error.message : 'Table data is invalid.');
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
canEdit() {
|
|
8016
|
+
return !this.readOnly() && !this.disabled();
|
|
8017
|
+
}
|
|
8018
|
+
download(content, fileName, contentType) {
|
|
8019
|
+
const url = URL.createObjectURL(new Blob([content], { type: contentType }));
|
|
8020
|
+
const anchor = document.createElement('a');
|
|
8021
|
+
anchor.href = url;
|
|
8022
|
+
anchor.download = fileName;
|
|
8023
|
+
anchor.click();
|
|
8024
|
+
URL.revokeObjectURL(url);
|
|
8025
|
+
}
|
|
8026
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonSpreadsheetTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8027
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonSpreadsheetTableComponent, isStandalone: true, selector: "taon-spreadsheet-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, validationError: { classPropertyName: "validationError", publicName: "validationError", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { data: "dataChange", validationError: "validationErrorChange" }, ngImport: i0, template: "<section\n class=\"spreadsheet\"\n [class.read-only]=\"readOnly() || disabled()\">\n <header class=\"toolbar\">\n @if (!readOnly()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"addRow()\">\n <mat-icon>add</mat-icon> Add row\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"addColumn()\">\n <mat-icon>view_column</mat-icon> Add column\n </button>\n <label class=\"import-button\">\n <mat-icon>upload_file</mat-icon> Import JSON/CSV\n <input\n type=\"file\"\n accept=\".json,.csv,application/json,text/csv\"\n [disabled]=\"disabled()\"\n (change)=\"importFile($event)\" />\n </label>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"undo()\">\n Undo\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"redo()\">\n Redo\n </button>\n }\n <div class=\"w-full\">\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('table')\">\n Table\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('json')\">\n JSON Preview\n </button>\n </div>\n @if (!readOnly()) {\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('settings')\">\n Settings\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadJson()\">\n Export JSON\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadJson(true)\">\n Full JSON\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadCsv()\">\n Export CSV\n </button>\n }\n </header>\n\n @if (validationError()) {\n <p\n class=\"validation-error\"\n role=\"alert\">\n {{ validationError() }}\n </p>\n }\n\n @if (pendingImport(); as imported) {\n <section\n class=\"import-preview\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"import-preview-title\">\n <h2 id=\"import-preview-title\">Preview {{ importFormat() }} import</h2>\n <p>\n {{ imported.rows.length }} rows \u00B7 {{ imported.columns.length }} columns\n </p>\n @if (data().rows.length || data().columns.length) {\n <label>\n Import mode\n <mat-select\n [value]=\"importMode()\"\n (selectionChange)=\"importMode.set($event.value)\">\n <mat-option value=\"replace\">Replace table</mat-option>\n <mat-option value=\"append\">Append rows</mat-option>\n </mat-select>\n </label>\n }\n <div class=\"table-scroll\">\n <table>\n <thead>\n <tr>\n @for (column of imported.columns; track column.key) {\n <th>\n {{ column.label || column.key }}\n <small>{{ column.key }} \u00B7 {{ column.type }}</small>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of imported.rows.slice(0, 5); track $index) {\n <tr>\n @for (column of imported.columns; track column.key) {\n <td>{{ previewCell(row, column) }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n <footer>\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelImport()\">\n Cancel\n </button>\n <button\n mat-flat-button\n type=\"button\"\n (click)=\"applyImport()\">\n Apply import\n </button>\n </footer>\n </section>\n }\n\n @if (view() === 'table') {\n @if (!data().columns.length) {\n <div class=\"empty-state\">\n <p>No columns yet. Add a column or import a JSON/CSV file.</p>\n @if (!readOnly()) {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"addColumn()\">\n Add first column\n </button>\n }\n </div>\n } @else {\n <div class=\"table-scroll\">\n <table>\n <thead\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [cdkDropListDisabled]=\"readOnly() || disabled()\"\n (cdkDropListDropped)=\"onColumnDrop($event)\">\n <tr>\n <th class=\"row-number\">#</th>\n @for (\n column of data().columns;\n track trackColumn($index, column)\n ) {\n <th\n cdkDrag\n cdkDragHandle\n [cdkDragDisabled]=\"readOnly() || disabled()\"\n [style.minWidth.px]=\"column.width || 140\">\n {{ column.label || column.key }}\n @if (!readOnly()) {\n <small>{{ column.type }}</small>\n }\n </th>\n }\n @if (!readOnly()) {\n <th class=\"row-actions\">Actions</th>\n }\n </tr>\n </thead>\n <tbody\n cdkDropList\n [cdkDropListDisabled]=\"readOnly() || disabled()\"\n (cdkDropListDropped)=\"onRowDrop($event)\">\n @for (row of data().rows; track $index; let rowIndex = $index) {\n <tr\n cdkDrag\n [cdkDragDisabled]=\"readOnly() || disabled()\">\n <th\n class=\"row-number\"\n cdkDragHandle>\n {{ rowIndex + 1 }}\n </th>\n @for (\n column of data().columns;\n track trackColumn($index, column);\n let columnIndex = $index\n ) {\n <td\n [class.active]=\"\n activeCell()?.row === rowIndex &&\n activeCell()?.column === columnIndex\n \">\n @if (column.type === 'boolean') {\n <input\n type=\"checkbox\"\n [checked]=\"row[column.key] === true\"\n [disabled]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (change)=\"setCell(rowIndex, column, $event)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \" />\n } @else if (column.type === 'json') {\n <textarea\n [value]=\"cellValue(rowIndex, column)\"\n [readonly]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (input)=\"setCell(rowIndex, column, $event)\"\n (paste)=\"onCellPaste($event, rowIndex, columnIndex)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \"></textarea>\n } @else {\n <input\n [type]=\"\n column.type === 'number'\n ? 'number'\n : column.type === 'date'\n ? 'date'\n : 'text'\n \"\n [value]=\"cellValue(rowIndex, column)\"\n [readonly]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (input)=\"setCell(rowIndex, column, $event)\"\n (paste)=\"onCellPaste($event, rowIndex, columnIndex)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \" />\n }\n </td>\n }\n @if (!readOnly()) {\n <td class=\"row-actions\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Duplicate row\"\n (click)=\"duplicateRow(rowIndex)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Delete row\"\n (click)=\"deleteRow(rowIndex)\">\n <mat-icon>delete</mat-icon>\n </button>\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (view() === 'json') {\n <div class=\"json-preview\">\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"copyJson()\">\n Copy JSON\n </button>\n <pre>{{ jsonPreview() }}</pre>\n </div>\n } @else {\n <div class=\"settings\">\n @for (\n column of data().columns;\n track trackColumn($index, column);\n let columnIndex = $index\n ) {\n <fieldset>\n <legend>Column {{ columnIndex + 1 }}</legend>\n <label>\n Label\n <input\n [value]=\"column.label || ''\"\n [disabled]=\"disabled()\"\n (change)=\"\n renameColumn(columnIndex, 'label', $any($event.target).value)\n \" />\n </label>\n <label>\n Key\n <input\n [value]=\"column.key\"\n [disabled]=\"disabled()\"\n (change)=\"\n renameColumn(columnIndex, 'key', $any($event.target).value)\n \" />\n </label>\n <label>\n Type\n <mat-select\n [value]=\"column.type\"\n [disabled]=\"disabled()\"\n (selectionChange)=\"setColumnType(columnIndex, $event.value)\">\n @for (type of columnTypes; track type) {\n <mat-option [value]=\"type\">{{ type }}</mat-option>\n }\n </mat-select>\n </label>\n <label>\n Width (px)\n <input\n type=\"number\"\n min=\"40\"\n max=\"1000\"\n [value]=\"column.width || 140\"\n [disabled]=\"disabled()\"\n (change)=\"\n setColumnWidth(columnIndex, $any($event.target).value)\n \" />\n </label>\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"deleteColumn(columnIndex)\">\n Delete column\n </button>\n </fieldset>\n }\n </div>\n }\n</section>\n", styles: [":host{display:block;min-width:0}.spreadsheet{border:1px solid rgba(136,136,136,.3333333333);border-radius:6px;min-width:0}.toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;padding:.5rem;border-bottom:1px solid rgba(136,136,136,.3333333333)}.import-button{display:inline-flex;align-items:center;gap:.35rem;padding:0 .75rem;cursor:pointer;font:inherit}.import-button input{display:none}.table-scroll{max-width:100%;max-height:60vh;overflow:auto}table{width:max-content;min-width:100%;border-collapse:separate;border-spacing:0}th,td{padding:.35rem;border-right:1px solid rgba(136,136,136,.2);border-bottom:1px solid rgba(136,136,136,.2);text-align:left}thead th{position:sticky;top:0;z-index:1;background:var(--mat-sys-surface, white)}thead small{display:block;opacity:.65}.row-number{position:sticky;left:0;z-index:2;width:3rem;background:var(--mat-sys-surface, white);text-align:center}td input:not([type=checkbox]),td textarea{box-sizing:border-box;width:100%;min-width:7rem;padding:.4rem;border:1px solid transparent;color:inherit;background:transparent;font:inherit}td textarea{min-height:2.5rem;resize:vertical}td input:focus,td textarea:focus,td.active{outline:2px solid #3f51b5;outline-offset:-2px}.row-actions{white-space:nowrap}.empty-state,.json-preview,.settings{padding:1rem}.json-preview pre{max-height:60vh;overflow:auto;padding:1rem;background:#8881;font-family:monospace;white-space:pre-wrap;overflow-wrap:anywhere}.settings{display:grid;gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr))}.settings fieldset{display:grid;gap:.75rem;min-width:0}.settings label{display:grid;gap:.25rem}.settings input{min-width:0;padding:.5rem}.validation-error{margin:0;padding:.75rem 1rem;color:#b3261e}.import-preview{position:fixed;inset:10vh max(1rem,(100vw - 56rem) / 2);z-index:1000;display:grid;align-content:start;gap:.75rem;overflow:auto;padding:1.25rem;border:1px solid rgba(136,136,136,.4666666667);border-radius:8px;background:var(--mat-sys-surface, white);box-shadow:0 1rem 4rem #0005}.import-preview h2,.import-preview p{margin:0}.import-preview footer{display:flex;justify-content:flex-end;gap:.5rem}.read-only td input,.read-only td textarea{cursor:default}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8028
|
+
}
|
|
8029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonSpreadsheetTableComponent, decorators: [{
|
|
8030
|
+
type: Component,
|
|
8031
|
+
args: [{ selector: 'taon-spreadsheet-table', standalone: true, imports: [
|
|
8032
|
+
CdkDrag,
|
|
8033
|
+
CdkDragHandle,
|
|
8034
|
+
CdkDropList,
|
|
8035
|
+
MatButtonModule,
|
|
8036
|
+
MatIconModule,
|
|
8037
|
+
MatSelectModule,
|
|
8038
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n class=\"spreadsheet\"\n [class.read-only]=\"readOnly() || disabled()\">\n <header class=\"toolbar\">\n @if (!readOnly()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"addRow()\">\n <mat-icon>add</mat-icon> Add row\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"addColumn()\">\n <mat-icon>view_column</mat-icon> Add column\n </button>\n <label class=\"import-button\">\n <mat-icon>upload_file</mat-icon> Import JSON/CSV\n <input\n type=\"file\"\n accept=\".json,.csv,application/json,text/csv\"\n [disabled]=\"disabled()\"\n (change)=\"importFile($event)\" />\n </label>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"undo()\">\n Undo\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"redo()\">\n Redo\n </button>\n }\n <div class=\"w-full\">\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('table')\">\n Table\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('json')\">\n JSON Preview\n </button>\n </div>\n @if (!readOnly()) {\n <button\n mat-button\n type=\"button\"\n (click)=\"view.set('settings')\">\n Settings\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadJson()\">\n Export JSON\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadJson(true)\">\n Full JSON\n </button>\n <button\n mat-button\n type=\"button\"\n (click)=\"downloadCsv()\">\n Export CSV\n </button>\n }\n </header>\n\n @if (validationError()) {\n <p\n class=\"validation-error\"\n role=\"alert\">\n {{ validationError() }}\n </p>\n }\n\n @if (pendingImport(); as imported) {\n <section\n class=\"import-preview\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"import-preview-title\">\n <h2 id=\"import-preview-title\">Preview {{ importFormat() }} import</h2>\n <p>\n {{ imported.rows.length }} rows \u00B7 {{ imported.columns.length }} columns\n </p>\n @if (data().rows.length || data().columns.length) {\n <label>\n Import mode\n <mat-select\n [value]=\"importMode()\"\n (selectionChange)=\"importMode.set($event.value)\">\n <mat-option value=\"replace\">Replace table</mat-option>\n <mat-option value=\"append\">Append rows</mat-option>\n </mat-select>\n </label>\n }\n <div class=\"table-scroll\">\n <table>\n <thead>\n <tr>\n @for (column of imported.columns; track column.key) {\n <th>\n {{ column.label || column.key }}\n <small>{{ column.key }} \u00B7 {{ column.type }}</small>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of imported.rows.slice(0, 5); track $index) {\n <tr>\n @for (column of imported.columns; track column.key) {\n <td>{{ previewCell(row, column) }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n <footer>\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelImport()\">\n Cancel\n </button>\n <button\n mat-flat-button\n type=\"button\"\n (click)=\"applyImport()\">\n Apply import\n </button>\n </footer>\n </section>\n }\n\n @if (view() === 'table') {\n @if (!data().columns.length) {\n <div class=\"empty-state\">\n <p>No columns yet. Add a column or import a JSON/CSV file.</p>\n @if (!readOnly()) {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"addColumn()\">\n Add first column\n </button>\n }\n </div>\n } @else {\n <div class=\"table-scroll\">\n <table>\n <thead\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [cdkDropListDisabled]=\"readOnly() || disabled()\"\n (cdkDropListDropped)=\"onColumnDrop($event)\">\n <tr>\n <th class=\"row-number\">#</th>\n @for (\n column of data().columns;\n track trackColumn($index, column)\n ) {\n <th\n cdkDrag\n cdkDragHandle\n [cdkDragDisabled]=\"readOnly() || disabled()\"\n [style.minWidth.px]=\"column.width || 140\">\n {{ column.label || column.key }}\n @if (!readOnly()) {\n <small>{{ column.type }}</small>\n }\n </th>\n }\n @if (!readOnly()) {\n <th class=\"row-actions\">Actions</th>\n }\n </tr>\n </thead>\n <tbody\n cdkDropList\n [cdkDropListDisabled]=\"readOnly() || disabled()\"\n (cdkDropListDropped)=\"onRowDrop($event)\">\n @for (row of data().rows; track $index; let rowIndex = $index) {\n <tr\n cdkDrag\n [cdkDragDisabled]=\"readOnly() || disabled()\">\n <th\n class=\"row-number\"\n cdkDragHandle>\n {{ rowIndex + 1 }}\n </th>\n @for (\n column of data().columns;\n track trackColumn($index, column);\n let columnIndex = $index\n ) {\n <td\n [class.active]=\"\n activeCell()?.row === rowIndex &&\n activeCell()?.column === columnIndex\n \">\n @if (column.type === 'boolean') {\n <input\n type=\"checkbox\"\n [checked]=\"row[column.key] === true\"\n [disabled]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (change)=\"setCell(rowIndex, column, $event)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \" />\n } @else if (column.type === 'json') {\n <textarea\n [value]=\"cellValue(rowIndex, column)\"\n [readonly]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (input)=\"setCell(rowIndex, column, $event)\"\n (paste)=\"onCellPaste($event, rowIndex, columnIndex)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \"></textarea>\n } @else {\n <input\n [type]=\"\n column.type === 'number'\n ? 'number'\n : column.type === 'date'\n ? 'date'\n : 'text'\n \"\n [value]=\"cellValue(rowIndex, column)\"\n [readonly]=\"readOnly() || disabled()\"\n [attr.data-cell]=\"rowIndex + ':' + columnIndex\"\n (focus)=\"\n activeCell.set({ row: rowIndex, column: columnIndex })\n \"\n (input)=\"setCell(rowIndex, column, $event)\"\n (paste)=\"onCellPaste($event, rowIndex, columnIndex)\"\n (keydown)=\"\n onCellKeydown($event, rowIndex, columnIndex)\n \" />\n }\n </td>\n }\n @if (!readOnly()) {\n <td class=\"row-actions\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Duplicate row\"\n (click)=\"duplicateRow(rowIndex)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Delete row\"\n (click)=\"deleteRow(rowIndex)\">\n <mat-icon>delete</mat-icon>\n </button>\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (view() === 'json') {\n <div class=\"json-preview\">\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"copyJson()\">\n Copy JSON\n </button>\n <pre>{{ jsonPreview() }}</pre>\n </div>\n } @else {\n <div class=\"settings\">\n @for (\n column of data().columns;\n track trackColumn($index, column);\n let columnIndex = $index\n ) {\n <fieldset>\n <legend>Column {{ columnIndex + 1 }}</legend>\n <label>\n Label\n <input\n [value]=\"column.label || ''\"\n [disabled]=\"disabled()\"\n (change)=\"\n renameColumn(columnIndex, 'label', $any($event.target).value)\n \" />\n </label>\n <label>\n Key\n <input\n [value]=\"column.key\"\n [disabled]=\"disabled()\"\n (change)=\"\n renameColumn(columnIndex, 'key', $any($event.target).value)\n \" />\n </label>\n <label>\n Type\n <mat-select\n [value]=\"column.type\"\n [disabled]=\"disabled()\"\n (selectionChange)=\"setColumnType(columnIndex, $event.value)\">\n @for (type of columnTypes; track type) {\n <mat-option [value]=\"type\">{{ type }}</mat-option>\n }\n </mat-select>\n </label>\n <label>\n Width (px)\n <input\n type=\"number\"\n min=\"40\"\n max=\"1000\"\n [value]=\"column.width || 140\"\n [disabled]=\"disabled()\"\n (change)=\"\n setColumnWidth(columnIndex, $any($event.target).value)\n \" />\n </label>\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"disabled()\"\n (click)=\"deleteColumn(columnIndex)\">\n Delete column\n </button>\n </fieldset>\n }\n </div>\n }\n</section>\n", styles: [":host{display:block;min-width:0}.spreadsheet{border:1px solid rgba(136,136,136,.3333333333);border-radius:6px;min-width:0}.toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;padding:.5rem;border-bottom:1px solid rgba(136,136,136,.3333333333)}.import-button{display:inline-flex;align-items:center;gap:.35rem;padding:0 .75rem;cursor:pointer;font:inherit}.import-button input{display:none}.table-scroll{max-width:100%;max-height:60vh;overflow:auto}table{width:max-content;min-width:100%;border-collapse:separate;border-spacing:0}th,td{padding:.35rem;border-right:1px solid rgba(136,136,136,.2);border-bottom:1px solid rgba(136,136,136,.2);text-align:left}thead th{position:sticky;top:0;z-index:1;background:var(--mat-sys-surface, white)}thead small{display:block;opacity:.65}.row-number{position:sticky;left:0;z-index:2;width:3rem;background:var(--mat-sys-surface, white);text-align:center}td input:not([type=checkbox]),td textarea{box-sizing:border-box;width:100%;min-width:7rem;padding:.4rem;border:1px solid transparent;color:inherit;background:transparent;font:inherit}td textarea{min-height:2.5rem;resize:vertical}td input:focus,td textarea:focus,td.active{outline:2px solid #3f51b5;outline-offset:-2px}.row-actions{white-space:nowrap}.empty-state,.json-preview,.settings{padding:1rem}.json-preview pre{max-height:60vh;overflow:auto;padding:1rem;background:#8881;font-family:monospace;white-space:pre-wrap;overflow-wrap:anywhere}.settings{display:grid;gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr))}.settings fieldset{display:grid;gap:.75rem;min-width:0}.settings label{display:grid;gap:.25rem}.settings input{min-width:0;padding:.5rem}.validation-error{margin:0;padding:.75rem 1rem;color:#b3261e}.import-preview{position:fixed;inset:10vh max(1rem,(100vw - 56rem) / 2);z-index:1000;display:grid;align-content:start;gap:.75rem;overflow:auto;padding:1.25rem;border:1px solid rgba(136,136,136,.4666666667);border-radius:8px;background:var(--mat-sys-surface, white);box-shadow:0 1rem 4rem #0005}.import-preview h2,.import-preview p{margin:0}.import-preview footer{display:flex;justify-content:flex-end;gap:.5rem}.read-only td input,.read-only td textarea{cursor:default}\n"] }]
|
|
8039
|
+
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }, { type: i0.Output, args: ["dataChange"] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], validationError: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationError", required: false }] }, { type: i0.Output, args: ["validationErrorChange"] }] } });
|
|
8040
|
+
function parseClipboardMatrix(value) {
|
|
8041
|
+
const rows = [];
|
|
8042
|
+
let row = [];
|
|
8043
|
+
let cell = '';
|
|
8044
|
+
let quoted = false;
|
|
8045
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
8046
|
+
const character = value[index];
|
|
8047
|
+
if (quoted) {
|
|
8048
|
+
if (character === '"' && value[index + 1] === '"') {
|
|
8049
|
+
cell += '"';
|
|
8050
|
+
index += 1;
|
|
8051
|
+
}
|
|
8052
|
+
else if (character === '"') {
|
|
8053
|
+
quoted = false;
|
|
8054
|
+
}
|
|
8055
|
+
else {
|
|
8056
|
+
cell += character;
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
else if (character === '"' && !cell) {
|
|
8060
|
+
quoted = true;
|
|
8061
|
+
}
|
|
8062
|
+
else if (character === '\t') {
|
|
8063
|
+
row.push(cell);
|
|
8064
|
+
cell = '';
|
|
8065
|
+
}
|
|
8066
|
+
else if (character === '\r' || character === '\n') {
|
|
8067
|
+
row.push(cell);
|
|
8068
|
+
rows.push(row);
|
|
8069
|
+
row = [];
|
|
8070
|
+
cell = '';
|
|
8071
|
+
if (character === '\r' && value[index + 1] === '\n') {
|
|
8072
|
+
index += 1;
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
else {
|
|
8076
|
+
cell += character;
|
|
8077
|
+
}
|
|
8078
|
+
}
|
|
8079
|
+
if (quoted) {
|
|
8080
|
+
throw new Error('Pasted data contains an unterminated quoted value.');
|
|
8081
|
+
}
|
|
8082
|
+
if (cell.length || row.length) {
|
|
8083
|
+
row.push(cell);
|
|
8084
|
+
rows.push(row);
|
|
8085
|
+
}
|
|
8086
|
+
return rows;
|
|
8087
|
+
}
|
|
8088
|
+
function parsePastedCell(value, column) {
|
|
8089
|
+
if (value === '') {
|
|
8090
|
+
return null;
|
|
8091
|
+
}
|
|
8092
|
+
if (column.type === 'number') {
|
|
8093
|
+
const parsed = Number(value);
|
|
8094
|
+
if (!Number.isFinite(parsed)) {
|
|
8095
|
+
throw new Error(`"${value}" is not a valid number for ${column.label || column.key}.`);
|
|
8096
|
+
}
|
|
8097
|
+
return parsed;
|
|
8098
|
+
}
|
|
8099
|
+
if (column.type === 'boolean') {
|
|
8100
|
+
if (/^(true|1|yes)$/i.test(value)) {
|
|
8101
|
+
return true;
|
|
8102
|
+
}
|
|
8103
|
+
if (/^(false|0|no)$/i.test(value)) {
|
|
8104
|
+
return false;
|
|
8105
|
+
}
|
|
8106
|
+
throw new Error(`"${value}" is not a valid boolean for ${column.label || column.key}.`);
|
|
8107
|
+
}
|
|
8108
|
+
if (column.type === 'date') {
|
|
8109
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) ||
|
|
8110
|
+
Number.isNaN(Date.parse(`${value}T00:00:00.000Z`))) {
|
|
8111
|
+
throw new Error(`"${value}" is not a valid date for ${column.label || column.key}.`);
|
|
8112
|
+
}
|
|
8113
|
+
return value;
|
|
8114
|
+
}
|
|
8115
|
+
if (column.type === 'json') {
|
|
8116
|
+
try {
|
|
8117
|
+
return JSON.parse(value);
|
|
8118
|
+
}
|
|
8119
|
+
catch {
|
|
8120
|
+
throw new Error(`"${value}" is not valid JSON for ${column.label || column.key}.`);
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
return value;
|
|
8124
|
+
}
|
|
8125
|
+
|
|
6993
8126
|
//#region imports
|
|
6994
8127
|
//#endregion
|
|
6995
8128
|
class TaonCmsWebsiteFeatureApiService extends TaonBaseAngularService {
|
|
@@ -7153,7 +8286,7 @@ class TaonCmsWebsiteFeaturesComponent {
|
|
|
7153
8286
|
}
|
|
7154
8287
|
}
|
|
7155
8288
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsWebsiteFeaturesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7156
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonCmsWebsiteFeaturesComponent, isStandalone: true, selector: "app-taon-cms-website-features", providers: [TaonCmsWebsiteFeatureApiService], ngImport: i0, template: "<h1>{{ t.gettext('Website Features') }}</h1>\n@if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n <button mat-button type=\"button\" [disabled]=\"busy()\" (click)=\"reload()\">\n {{ t.gettext('Reload features') }}\n </button>\n}\n<p role=\"status\" aria-live=\"polite\">{{ feedback() }}</p>\n<ul [attr.aria-busy]=\"busy()\">\n @for (feature of features(); track feature.key) {\n <li>\n <mat-checkbox\n [checked]=\"feature.enabled\"\n [disabled]=\"busy()\"\n [attr.aria-label]=\"feature.name\"\n (change)=\"toggle(feature, $event.checked)\" />\n <div class=\"feature-name\">\n <span>{{ feature.name }}</span>\n <small>{{ feature.key }}</small>\n </div>\n <button mat-icon-button type=\"button\"\n [attr.aria-label]=\"t.gettext('Copy feature key')\"\n (click)=\"copyKey(feature.key)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" [disabled]=\"busy()\"\n [attr.aria-label]=\"t.gettext('Delete feature')\"\n (click)=\"deleteFeature(feature)\">\n <mat-icon>delete</mat-icon>\n </button>\n </li>\n }\n</ul>\n<form (ngSubmit)=\"addFeature()\">\n <mat-form-field class=\"flex-grow\">\n <mat-label>{{ t.gettext('Enter feature name') }}</mat-label>\n <input matInput name=\"featureName\" [(ngModel)]=\"name\"\n [disabled]=\"busy() || !loaded()\"\n [placeholder]=\"t.gettext('Enter feature name')\" />\n </mat-form-field>\n <button mat-flat-button type=\"submit\" class=\"flex\"\n [disabled]=\"busy() || !loaded() || !name.trim()\">\n {{ t.gettext('Add feature') }}\n </button>\n</form>\n", styles: [":host{display:block}ul{padding:0;list-style:none}li,form{display:flex;align-items:center;gap:.75rem;margin-block:.75rem}.feature-name{flex:1;min-width:0;overflow-wrap:anywhere}small{display:block;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type:
|
|
8289
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonCmsWebsiteFeaturesComponent, isStandalone: true, selector: "app-taon-cms-website-features", providers: [TaonCmsWebsiteFeatureApiService], ngImport: i0, template: "<h1>{{ t.gettext('Website Features') }}</h1>\n@if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n <button mat-button type=\"button\" [disabled]=\"busy()\" (click)=\"reload()\">\n {{ t.gettext('Reload features') }}\n </button>\n}\n<p role=\"status\" aria-live=\"polite\">{{ feedback() }}</p>\n<ul [attr.aria-busy]=\"busy()\">\n @for (feature of features(); track feature.key) {\n <li>\n <mat-checkbox\n [checked]=\"feature.enabled\"\n [disabled]=\"busy()\"\n [attr.aria-label]=\"feature.name\"\n (change)=\"toggle(feature, $event.checked)\" />\n <div class=\"feature-name\">\n <span>{{ feature.name }}</span>\n <small>{{ feature.key }}</small>\n </div>\n <button mat-icon-button type=\"button\"\n [attr.aria-label]=\"t.gettext('Copy feature key')\"\n (click)=\"copyKey(feature.key)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n <button mat-icon-button type=\"button\" [disabled]=\"busy()\"\n [attr.aria-label]=\"t.gettext('Delete feature')\"\n (click)=\"deleteFeature(feature)\">\n <mat-icon>delete</mat-icon>\n </button>\n </li>\n }\n</ul>\n<form (ngSubmit)=\"addFeature()\">\n <mat-form-field class=\"flex-grow\">\n <mat-label>{{ t.gettext('Enter feature name') }}</mat-label>\n <input matInput name=\"featureName\" [(ngModel)]=\"name\"\n [disabled]=\"busy() || !loaded()\"\n [placeholder]=\"t.gettext('Enter feature name')\" />\n </mat-form-field>\n <button mat-flat-button type=\"submit\" class=\"flex\"\n [disabled]=\"busy() || !loaded() || !name.trim()\">\n {{ t.gettext('Add feature') }}\n </button>\n</form>\n", styles: [":host{display:block}ul{padding:0;list-style:none}li,form{display:flex;align-items:center;gap:.75rem;margin-block:.75rem}.feature-name{flex:1;min-width:0;overflow-wrap:anywhere}small{display:block;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7157
8290
|
}
|
|
7158
8291
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsWebsiteFeaturesComponent, decorators: [{
|
|
7159
8292
|
type: Component,
|
|
@@ -7263,7 +8396,7 @@ const TaonCmsCommentsBackofficeRoutes = [
|
|
|
7263
8396
|
*/
|
|
7264
8397
|
// export default TaonCmsCommentsBackofficeRoutes;
|
|
7265
8398
|
|
|
7266
|
-
const t$f = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-
|
|
8399
|
+
const t$f = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-DfkiEwvY.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-DX25wx9y.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-BC9-axt9.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-BOO0IBXz.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-BcYSlBzW.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-mk6gw3C2.mjs')).default } });
|
|
7267
8400
|
class TaonCmsPostChoiceDialogComponent {
|
|
7268
8401
|
constructor() {
|
|
7269
8402
|
this.t = t$f.for(this);
|
|
@@ -7666,7 +8799,7 @@ class TaonCmsCourseBackofficeComponent extends TaonCmsPostTableActions {
|
|
|
7666
8799
|
});
|
|
7667
8800
|
}
|
|
7668
8801
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsCourseBackofficeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
7669
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonCmsCourseBackofficeComponent, isStandalone: true, selector: "taon-cms-course-backoffice", providers: [TaonCmsContentApiService], viewQueries: [{ propertyName: "datatable", first: true, predicate: TaonDatatableComponent, descendants: true }, { propertyName: "coverTemplate", first: true, predicate: ["coverTemplate"], descendants: true, static: true }, { propertyName: "visibilityTemplate", first: true, predicate: ["visibilityTemplate"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<h1>{{ t.gettext('Courses') }}</h1>\n<h2 style=\"color: goldenrod;\">{{ t.gettext('A course is a post with related posts and a thumbnail. Any post that is not archived can be a course.') }}</h2>\n<button mat-button type=\"button\" [disabled]=\"loading()\" (click)=\"reload()\">\n {{ t.gettext('Reload courses') }}\n</button>\n@if (loading()) {\n <p role=\"status\">{{ t.gettext('Loading courses...') }}</p>\n}\n@if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n}\n<taon-datatable\n [data]=\"courses()\"\n [columns]=\"columns\"\n [allowedColumns]=\"['id', 'title', 'status']\"\n tableId=\"cms-courses\"\n storeTableFilters=\"query-params\"\n [filtersTemplate]=\"filtersTemplate\"\n [hideAddButton]=\"true\"\n searchMode=\"advanced\"\n [expansionTemplate]=\"expansionTemplate\">\n</taon-datatable>\n<ng-template #coverTemplate let-post>\n <taon-cms-course-cover [post]=\"post\"></taon-cms-course-cover>\n</ng-template>\n<ng-template #visibilityTemplate let-post>\n <span class=\"visibility\">\n <mat-icon aria-hidden=\"true\">\n {{ post.status === 'published' ? 'lock_open' : 'lock' }}\n </mat-icon>\n {{ post.status === 'published' ? t.gettext('Public') : t.gettext('Private') }}\n </span>\n</ng-template>\n<ng-template #expansionTemplate let-course>\n <taon-cms-course-contents\n [course]=\"course\"\n [actionsColumn]=\"actionsColumn\"\n (editPostRequested)=\"openDetails($event)\">\n </taon-cms-course-contents>\n</ng-template>\n<ng-template\n #filtersTemplate\n let-filters=\"filters\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('ID') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"filters.id\"\n (keyup.enter)=\"datatable.applyAdvancedFilters()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Title') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"filters.title\"\n (keyup.enter)=\"datatable.applyAdvancedFilters()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Visibility') }}</mat-label>\n <mat-select [(ngModel)]=\"filters.status\">\n <mat-option value=\"published\">{{ t.gettext('Public') }}</mat-option>\n <mat-option value=\"draft\">{{ t.gettext('Private') }}</mat-option>\n </mat-select>\n </mat-form-field>\n</ng-template>\n", styles: [":host{display:block}.visibility{display:inline-flex;align-items:center;gap:.4rem}:host ::ng-deep td.cms-course-public{background-color:#2e7d3214}:host ::ng-deep td.cms-course-private{background-color:#c6282812}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type:
|
|
8802
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonCmsCourseBackofficeComponent, isStandalone: true, selector: "taon-cms-course-backoffice", providers: [TaonCmsContentApiService], viewQueries: [{ propertyName: "datatable", first: true, predicate: TaonDatatableComponent, descendants: true }, { propertyName: "coverTemplate", first: true, predicate: ["coverTemplate"], descendants: true, static: true }, { propertyName: "visibilityTemplate", first: true, predicate: ["visibilityTemplate"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<h1>{{ t.gettext('Courses') }}</h1>\n<h2 style=\"color: goldenrod;\">{{ t.gettext('A course is a post with related posts and a thumbnail. Any post that is not archived can be a course.') }}</h2>\n<button mat-button type=\"button\" [disabled]=\"loading()\" (click)=\"reload()\">\n {{ t.gettext('Reload courses') }}\n</button>\n@if (loading()) {\n <p role=\"status\">{{ t.gettext('Loading courses...') }}</p>\n}\n@if (error()) {\n <p role=\"alert\">{{ error() }}</p>\n}\n<taon-datatable\n [data]=\"courses()\"\n [columns]=\"columns\"\n [allowedColumns]=\"['id', 'title', 'status']\"\n tableId=\"cms-courses\"\n storeTableFilters=\"query-params\"\n [filtersTemplate]=\"filtersTemplate\"\n [hideAddButton]=\"true\"\n searchMode=\"advanced\"\n [expansionTemplate]=\"expansionTemplate\">\n</taon-datatable>\n<ng-template #coverTemplate let-post>\n <taon-cms-course-cover [post]=\"post\"></taon-cms-course-cover>\n</ng-template>\n<ng-template #visibilityTemplate let-post>\n <span class=\"visibility\">\n <mat-icon aria-hidden=\"true\">\n {{ post.status === 'published' ? 'lock_open' : 'lock' }}\n </mat-icon>\n {{ post.status === 'published' ? t.gettext('Public') : t.gettext('Private') }}\n </span>\n</ng-template>\n<ng-template #expansionTemplate let-course>\n <taon-cms-course-contents\n [course]=\"course\"\n [actionsColumn]=\"actionsColumn\"\n (editPostRequested)=\"openDetails($event)\">\n </taon-cms-course-contents>\n</ng-template>\n<ng-template\n #filtersTemplate\n let-filters=\"filters\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('ID') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"filters.id\"\n (keyup.enter)=\"datatable.applyAdvancedFilters()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Title') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"filters.title\"\n (keyup.enter)=\"datatable.applyAdvancedFilters()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Visibility') }}</mat-label>\n <mat-select [(ngModel)]=\"filters.status\">\n <mat-option value=\"published\">{{ t.gettext('Public') }}</mat-option>\n <mat-option value=\"draft\">{{ t.gettext('Private') }}</mat-option>\n </mat-select>\n </mat-form-field>\n</ng-template>\n", styles: [":host{display:block}.visibility{display:inline-flex;align-items:center;gap:.4rem}:host ::ng-deep td.cms-course-public{background-color:#2e7d3214}:host ::ng-deep td.cms-course-private{background-color:#c6282812}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: TaonDatatableComponent, selector: "taon-datatable", inputs: ["pageNumber", "pageSize", "pageSizeOptions", "allowedColumns", "expansionTemplate", "entityCrudController", "columns", "hideSearch", "hideAddButton", "safe", "searchMode", "filtersTemplate", "storeTablePagination", "storeTableFilters", "tableId", "callQueryMethod", "data", "showPaginator"], outputs: ["expansionChange", "addingItem"] }, { kind: "component", type: TaonCmsCourseContentsComponent, selector: "taon-cms-course-contents", inputs: ["course", "actionsColumn"], outputs: ["editPostRequested"] }, { kind: "component", type: TaonCmsCourseCoverComponent, selector: "taon-cms-course-cover", inputs: ["post"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7670
8803
|
}
|
|
7671
8804
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsCourseBackofficeComponent, decorators: [{
|
|
7672
8805
|
type: Component,
|
|
@@ -8323,7 +9456,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
8323
9456
|
args: ['paste', ['$event']]
|
|
8324
9457
|
}] } });
|
|
8325
9458
|
|
|
8326
|
-
const t$a = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-
|
|
9459
|
+
const t$a = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-DfkiEwvY.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-DX25wx9y.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-BC9-axt9.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-BOO0IBXz.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-BcYSlBzW.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-mk6gw3C2.mjs')).default } });
|
|
8327
9460
|
class TaonCmsPostThumbnailComponent {
|
|
8328
9461
|
constructor() {
|
|
8329
9462
|
this.t = t$a.for(this);
|
|
@@ -8529,8 +9662,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
8529
9662
|
|
|
8530
9663
|
//#region imports
|
|
8531
9664
|
//#endregion
|
|
8532
|
-
const t$7 = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-
|
|
9665
|
+
const t$7 = Translation.for('src/lib/taon-posts/taon-cms-post/taon-cms-post.component.ts', { "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.en-US.i18n.data-BmZmONdL.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.pl-PL.i18n.data-CUcC-r1T.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.html.de-DE.i18n.data-BngVW1ST.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-choice-dialog.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.en-US.i18n.data-Ccy7Dcvv.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.pl-PL.i18n.data-CjnsarRp.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-choice-dialog.component.ts.de-DE.i18n.data-DdQZA_Pt.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post-thumbnail.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.en-US.i18n.data-CuYgfCFH.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.pl-PL.i18n.data-BWTN9DLn.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post-thumbnail.component.ts.de-DE.i18n.data-CBcBZFVK.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.html": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.en-US.i18n.data-DfkiEwvY.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.pl-PL.i18n.data-DX25wx9y.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.html.de-DE.i18n.data-BC9-axt9.mjs')).default }, "src/lib/taon-posts/taon-cms-post/taon-cms-post.component.ts": { "en-US": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.en-US.i18n.data-BOO0IBXz.mjs')).default, "pl-PL": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.pl-PL.i18n.data-BcYSlBzW.mjs')).default, "de-DE": async () => (await import('./cms-websql-prod-taon-cms-post.component.ts.de-DE.i18n.data-mk6gw3C2.mjs')).default } });
|
|
8533
9666
|
class TaonCmsPostComponent {
|
|
9667
|
+
get requiredSlugs() {
|
|
9668
|
+
return this.contentConfig.requiredSlugs;
|
|
9669
|
+
}
|
|
8534
9670
|
insertTable(event) {
|
|
8535
9671
|
event.preventDefault();
|
|
8536
9672
|
const editor = this.contentEditor();
|
|
@@ -8551,6 +9687,7 @@ class TaonCmsPostComponent {
|
|
|
8551
9687
|
constructor() {
|
|
8552
9688
|
//#region fields and getters
|
|
8553
9689
|
this.t = t$7.for(this);
|
|
9690
|
+
this.contentType = TaonCmsContentType;
|
|
8554
9691
|
this.post = input(null, ...(ngDevMode ? [{ debugName: "post" }] : []));
|
|
8555
9692
|
this.mode = model('view', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
8556
9693
|
this.changed = output();
|
|
@@ -8577,6 +9714,12 @@ class TaonCmsPostComponent {
|
|
|
8577
9714
|
categoryId: null,
|
|
8578
9715
|
tagIds: [],
|
|
8579
9716
|
}, ...(ngDevMode ? [{ debugName: "draft" }] : []));
|
|
9717
|
+
this.jsonTableData = signal({
|
|
9718
|
+
version: 1,
|
|
9719
|
+
columns: [],
|
|
9720
|
+
rows: [],
|
|
9721
|
+
}, ...(ngDevMode ? [{ debugName: "jsonTableData" }] : []));
|
|
9722
|
+
this.jsonTableValidationError = signal('', ...(ngDevMode ? [{ debugName: "jsonTableValidationError" }] : []));
|
|
8580
9723
|
this.postTypes = [
|
|
8581
9724
|
{
|
|
8582
9725
|
value: TaonCmsContentType.Normal,
|
|
@@ -8588,6 +9731,10 @@ class TaonCmsPostComponent {
|
|
|
8588
9731
|
value: TaonCmsContentType.Attachment,
|
|
8589
9732
|
label: this.t.gettext('Attachment post'),
|
|
8590
9733
|
},
|
|
9734
|
+
{
|
|
9735
|
+
value: TaonCmsContentType.JsonTable,
|
|
9736
|
+
label: this.t.gettext('JSON Table post'),
|
|
9737
|
+
},
|
|
8591
9738
|
];
|
|
8592
9739
|
this.currentPostType = computed(() => this.postTypes.find(type => type.value === this.draft().type) ??
|
|
8593
9740
|
this.postTypes[0], ...(ngDevMode ? [{ debugName: "currentPostType" }] : []));
|
|
@@ -8599,6 +9746,8 @@ class TaonCmsPostComponent {
|
|
|
8599
9746
|
return 'audiotrack';
|
|
8600
9747
|
case TaonCmsContentType.Attachment:
|
|
8601
9748
|
return 'attach_file';
|
|
9749
|
+
case TaonCmsContentType.JsonTable:
|
|
9750
|
+
return 'table_chart';
|
|
8602
9751
|
default:
|
|
8603
9752
|
return 'article';
|
|
8604
9753
|
}
|
|
@@ -8659,6 +9808,9 @@ class TaonCmsPostComponent {
|
|
|
8659
9808
|
this.stagedThumbnail = signal(null, ...(ngDevMode ? [{ debugName: "stagedThumbnail" }] : []));
|
|
8660
9809
|
this.uploading = signal(false, ...(ngDevMode ? [{ debugName: "uploading" }] : []));
|
|
8661
9810
|
this.uploadProgress = signal(0, ...(ngDevMode ? [{ debugName: "uploadProgress" }] : []));
|
|
9811
|
+
this.mediaUploadAbort = null;
|
|
9812
|
+
this.childUploadAbort = null;
|
|
9813
|
+
this.childUploadRunning = signal(false, ...(ngDevMode ? [{ debugName: "childUploadRunning" }] : []));
|
|
8662
9814
|
this.mediaPreviewUrl = signal('', ...(ngDevMode ? [{ debugName: "mediaPreviewUrl" }] : []));
|
|
8663
9815
|
this.mediaPreviewLoading = signal(false, ...(ngDevMode ? [{ debugName: "mediaPreviewLoading" }] : []));
|
|
8664
9816
|
this.mediaPreviewError = signal('', ...(ngDevMode ? [{ debugName: "mediaPreviewError" }] : []));
|
|
@@ -8771,7 +9923,9 @@ class TaonCmsPostComponent {
|
|
|
8771
9923
|
!this.permissionsLoading() &&
|
|
8772
9924
|
!this.permissionsError() &&
|
|
8773
9925
|
!this.taxonomyLoading() &&
|
|
8774
|
-
!this.taxonomyError()
|
|
9926
|
+
!this.taxonomyError() &&
|
|
9927
|
+
(this.draft().type !== TaonCmsContentType.JsonTable ||
|
|
9928
|
+
!this.jsonTableValidationError()), ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
8775
9929
|
this.statusLabel = computed(() => {
|
|
8776
9930
|
const status = this.mode() === 'add'
|
|
8777
9931
|
? this.draftStatus()
|
|
@@ -8793,6 +9947,10 @@ class TaonCmsPostComponent {
|
|
|
8793
9947
|
(this.mode() === 'edit' && !this.canSave()), ...(ngDevMode ? [{ debugName: "statusControlDisabled" }] : []));
|
|
8794
9948
|
this.api = inject(TaonCmsContentApiService);
|
|
8795
9949
|
this.contentConfig = inject(TaonCmsContentConfigService);
|
|
9950
|
+
this.selectedRequiredSlug = computed(() => {
|
|
9951
|
+
const slug = this.draft().slug;
|
|
9952
|
+
return this.requiredSlugs.includes(slug) ? slug : null;
|
|
9953
|
+
}, ...(ngDevMode ? [{ debugName: "selectedRequiredSlug" }] : []));
|
|
8796
9954
|
this.contentTab = signal(0, ...(ngDevMode ? [{ debugName: "contentTab" }] : []));
|
|
8797
9955
|
this.contentEditor = signal(null, ...(ngDevMode ? [{ debugName: "contentEditor" }] : []));
|
|
8798
9956
|
this.rawContent = viewChild('rawContent', ...(ngDevMode ? [{ debugName: "rawContent" }] : []));
|
|
@@ -9003,7 +10161,10 @@ class TaonCmsPostComponent {
|
|
|
9003
10161
|
});
|
|
9004
10162
|
}
|
|
9005
10163
|
//#endregion
|
|
9006
|
-
ngOnDestroy() {
|
|
10164
|
+
ngOnDestroy() {
|
|
10165
|
+
this.mediaUploadAbort?.abort();
|
|
10166
|
+
this.childUploadAbort?.abort();
|
|
10167
|
+
}
|
|
9007
10168
|
//#region methods / load related posts
|
|
9008
10169
|
loadTaxonomy(preserveSelectedTags = false) {
|
|
9009
10170
|
this.taxonomyLoad?.unsubscribe();
|
|
@@ -9123,6 +10284,10 @@ class TaonCmsPostComponent {
|
|
|
9123
10284
|
this.error.set(this.t.gettext('Please select an image for the thumbnail.'));
|
|
9124
10285
|
return;
|
|
9125
10286
|
}
|
|
10287
|
+
if (file.size > TAON_CMS_MAX_UPLOAD_BYTES) {
|
|
10288
|
+
this.error.set(this.fileTooLargeMessage(file));
|
|
10289
|
+
return;
|
|
10290
|
+
}
|
|
9126
10291
|
this.busy.set(true);
|
|
9127
10292
|
this.error.set('');
|
|
9128
10293
|
try {
|
|
@@ -9706,6 +10871,33 @@ class TaonCmsPostComponent {
|
|
|
9706
10871
|
//#endregion
|
|
9707
10872
|
//#region methods / update draft
|
|
9708
10873
|
updateDraft(field, value) {
|
|
10874
|
+
if (field === 'type' &&
|
|
10875
|
+
value === TaonCmsContentType.JsonTable &&
|
|
10876
|
+
this.draft().type !== TaonCmsContentType.JsonTable) {
|
|
10877
|
+
let tableData = {
|
|
10878
|
+
version: 1,
|
|
10879
|
+
columns: [],
|
|
10880
|
+
rows: [],
|
|
10881
|
+
};
|
|
10882
|
+
if (this.draft().body.trim()) {
|
|
10883
|
+
try {
|
|
10884
|
+
tableData = parseSpreadsheetDocument(this.draft().body);
|
|
10885
|
+
}
|
|
10886
|
+
catch {
|
|
10887
|
+
if (!globalThis.confirm('Changing the post to a JSON Table will replace its current body. Continue?')) {
|
|
10888
|
+
return;
|
|
10889
|
+
}
|
|
10890
|
+
}
|
|
10891
|
+
}
|
|
10892
|
+
this.jsonTableData.set(tableData);
|
|
10893
|
+
this.jsonTableValidationError.set('');
|
|
10894
|
+
this.draft.update(draft => ({
|
|
10895
|
+
...draft,
|
|
10896
|
+
type: value,
|
|
10897
|
+
body: JSON.stringify(tableData),
|
|
10898
|
+
}));
|
|
10899
|
+
return;
|
|
10900
|
+
}
|
|
9709
10901
|
if (field === 'title' && this.mode() === 'add') {
|
|
9710
10902
|
const title = String(value);
|
|
9711
10903
|
this.draft.update(draft => ({
|
|
@@ -9739,10 +10931,13 @@ class TaonCmsPostComponent {
|
|
|
9739
10931
|
if (!this.currentPost() || this.busy() || !files.length) {
|
|
9740
10932
|
return;
|
|
9741
10933
|
}
|
|
9742
|
-
|
|
10934
|
+
const oversized = files.filter(file => file.size > TAON_CMS_MAX_UPLOAD_BYTES);
|
|
10935
|
+
this.childBatchError.set(oversized.map(file => this.fileTooLargeMessage(file)).join(' '));
|
|
9743
10936
|
this.childFiles.update(selected => [
|
|
9744
10937
|
...selected,
|
|
9745
|
-
...files
|
|
10938
|
+
...files
|
|
10939
|
+
.filter(file => file.size <= TAON_CMS_MAX_UPLOAD_BYTES)
|
|
10940
|
+
.map(file => ({
|
|
9746
10941
|
id: ++this.childFileSequence,
|
|
9747
10942
|
file,
|
|
9748
10943
|
status: 'pending',
|
|
@@ -9773,10 +10968,16 @@ class TaonCmsPostComponent {
|
|
|
9773
10968
|
const pendingFiles = this.childFiles()
|
|
9774
10969
|
.map((file, index) => ({ file, index }))
|
|
9775
10970
|
.filter(({ file }) => file.status !== 'success');
|
|
10971
|
+
const abort = new AbortController();
|
|
10972
|
+
this.childUploadAbort = abort;
|
|
10973
|
+
this.childUploadRunning.set(true);
|
|
9776
10974
|
this.busy.set(true);
|
|
9777
10975
|
this.childBatchError.set('');
|
|
9778
10976
|
try {
|
|
9779
10977
|
for (const { file: selectedFile, index } of pendingFiles) {
|
|
10978
|
+
if (abort.signal.aborted) {
|
|
10979
|
+
break;
|
|
10980
|
+
}
|
|
9780
10981
|
const expectedTitle = this.childTitlePreviews()[index];
|
|
9781
10982
|
this.updateRelatedChildFile(selectedFile.id, {
|
|
9782
10983
|
status: 'processing',
|
|
@@ -9792,22 +10993,24 @@ class TaonCmsPostComponent {
|
|
|
9792
10993
|
}
|
|
9793
10994
|
let uploads = this.childFiles().find(file => file.id === selectedFile.id)?.uploads;
|
|
9794
10995
|
if (!uploads?.source) {
|
|
10996
|
+
abort.signal.throwIfAborted();
|
|
9795
10997
|
const source = await this.api.uploadMedia(selectedFile.file, percentage => this.updateRelatedChildFile(selectedFile.id, {
|
|
9796
10998
|
progress: type === TaonCmsContentType.Video
|
|
9797
10999
|
? Math.round(percentage * 0.65)
|
|
9798
11000
|
: Math.round(percentage * 0.8),
|
|
9799
|
-
}));
|
|
11001
|
+
}), abort.signal);
|
|
9800
11002
|
uploads = { ...uploads, source };
|
|
9801
11003
|
this.updateRelatedChildFile(selectedFile.id, { uploads });
|
|
9802
11004
|
}
|
|
9803
11005
|
if (type === TaonCmsContentType.Video && !uploads.thumbnail) {
|
|
9804
11006
|
const thumbnailBlob = await generateVideoThumbnail(selectedFile.file);
|
|
9805
11007
|
const extension = thumbnailBlob.type === 'image/webp' ? 'webp' : 'png';
|
|
11008
|
+
abort.signal.throwIfAborted();
|
|
9806
11009
|
const thumbnail = await this.api.uploadMedia(new File([thumbnailBlob], `thumbnail.${extension}`, {
|
|
9807
11010
|
type: thumbnailBlob.type,
|
|
9808
11011
|
}), percentage => this.updateRelatedChildFile(selectedFile.id, {
|
|
9809
11012
|
progress: 65 + Math.round(percentage * 0.25),
|
|
9810
|
-
}));
|
|
11013
|
+
}), abort.signal);
|
|
9811
11014
|
uploads = { ...uploads, thumbnail };
|
|
9812
11015
|
this.updateRelatedChildFile(selectedFile.id, { uploads });
|
|
9813
11016
|
}
|
|
@@ -9824,6 +11027,8 @@ class TaonCmsPostComponent {
|
|
|
9824
11027
|
if (!sourceUpload) {
|
|
9825
11028
|
throw new Error('The uploaded source file is unavailable.');
|
|
9826
11029
|
}
|
|
11030
|
+
// Cancellation is honoured only before the child post is created.
|
|
11031
|
+
abort.signal.throwIfAborted();
|
|
9827
11032
|
const result = await this.api.createRelatedChild(parent.id, {
|
|
9828
11033
|
titlePrefix,
|
|
9829
11034
|
expectedTitle,
|
|
@@ -9877,7 +11082,10 @@ class TaonCmsPostComponent {
|
|
|
9877
11082
|
? await this.cleanupRelatedChildUploads(selectedFile.id, uploads)
|
|
9878
11083
|
: []
|
|
9879
11084
|
: [];
|
|
9880
|
-
const reason =
|
|
11085
|
+
const reason = abort.signal.aborted
|
|
11086
|
+
? this.t.gettext('Upload cancelled. No child post was created.')
|
|
11087
|
+
: error?.body?.json?.message ||
|
|
11088
|
+
(error instanceof Error ? error.message : String(error));
|
|
9881
11089
|
const verificationError = relatedPosts
|
|
9882
11090
|
? ''
|
|
9883
11091
|
: `${this.t.gettext('The creation result could not be verified; retrying will not duplicate an existing child.')} `;
|
|
@@ -9891,6 +11099,10 @@ class TaonCmsPostComponent {
|
|
|
9891
11099
|
}
|
|
9892
11100
|
}
|
|
9893
11101
|
finally {
|
|
11102
|
+
if (this.childUploadAbort === abort) {
|
|
11103
|
+
this.childUploadAbort = null;
|
|
11104
|
+
}
|
|
11105
|
+
this.childUploadRunning.set(false);
|
|
9894
11106
|
this.busy.set(false);
|
|
9895
11107
|
this.loadRelatedPosts();
|
|
9896
11108
|
}
|
|
@@ -10028,7 +11240,14 @@ class TaonCmsPostComponent {
|
|
|
10028
11240
|
input.value = '';
|
|
10029
11241
|
return;
|
|
10030
11242
|
}
|
|
11243
|
+
if (file.size > TAON_CMS_MAX_UPLOAD_BYTES) {
|
|
11244
|
+
input.value = '';
|
|
11245
|
+
this.error.set(this.fileTooLargeMessage(file));
|
|
11246
|
+
return;
|
|
11247
|
+
}
|
|
10031
11248
|
const session = this.editorSession;
|
|
11249
|
+
const abort = new AbortController();
|
|
11250
|
+
this.mediaUploadAbort = abort;
|
|
10032
11251
|
this.busy.set(true);
|
|
10033
11252
|
this.uploading.set(true);
|
|
10034
11253
|
this.uploadProgress.set(0);
|
|
@@ -10038,7 +11257,7 @@ class TaonCmsPostComponent {
|
|
|
10038
11257
|
if (!this.destroyRef.destroyed) {
|
|
10039
11258
|
this.uploadProgress.set(percentage);
|
|
10040
11259
|
}
|
|
10041
|
-
});
|
|
11260
|
+
}, abort.signal);
|
|
10042
11261
|
if (!this.destroyRef.destroyed && this.editorSession === session) {
|
|
10043
11262
|
const fileName = object.fileName || file.name;
|
|
10044
11263
|
this.mediaFileNames.update(names => ({
|
|
@@ -10067,10 +11286,20 @@ class TaonCmsPostComponent {
|
|
|
10067
11286
|
}
|
|
10068
11287
|
}
|
|
10069
11288
|
catch (error) {
|
|
10070
|
-
|
|
10071
|
-
|
|
11289
|
+
if (abort.signal.aborted) {
|
|
11290
|
+
if (!this.destroyRef.destroyed) {
|
|
11291
|
+
this.error.set(this.t.gettext('The media upload was cancelled.'));
|
|
11292
|
+
}
|
|
11293
|
+
}
|
|
11294
|
+
else {
|
|
11295
|
+
console.error('[taon-cms-post] Unable to upload media', error);
|
|
11296
|
+
this.error.set(`${this.t.gettext('The media could not be uploaded. Please try again.')}${this.uploadErrorDetails(error)}`);
|
|
11297
|
+
}
|
|
10072
11298
|
}
|
|
10073
11299
|
finally {
|
|
11300
|
+
if (this.mediaUploadAbort === abort) {
|
|
11301
|
+
this.mediaUploadAbort = null;
|
|
11302
|
+
}
|
|
10074
11303
|
this.busy.set(false);
|
|
10075
11304
|
this.uploading.set(false);
|
|
10076
11305
|
this.uploadProgress.set(0);
|
|
@@ -10078,6 +11307,26 @@ class TaonCmsPostComponent {
|
|
|
10078
11307
|
}
|
|
10079
11308
|
}
|
|
10080
11309
|
//#endregion
|
|
11310
|
+
//#region methods / cancel media upload
|
|
11311
|
+
cancelMediaUpload() {
|
|
11312
|
+
this.mediaUploadAbort?.abort();
|
|
11313
|
+
}
|
|
11314
|
+
//#endregion
|
|
11315
|
+
//#region methods / cancel related child uploads
|
|
11316
|
+
cancelRelatedChildUploads() {
|
|
11317
|
+
this.childUploadAbort?.abort();
|
|
11318
|
+
}
|
|
11319
|
+
//#endregion
|
|
11320
|
+
//#region methods / upload error helpers
|
|
11321
|
+
fileTooLargeMessage(file) {
|
|
11322
|
+
return this.t.gettext('The file "[[ fileName ]]" is too large. The maximum upload size is 1 GB.', { fileName: file.name });
|
|
11323
|
+
}
|
|
11324
|
+
uploadErrorDetails(error) {
|
|
11325
|
+
const message = error?.body?.json?.message ||
|
|
11326
|
+
(error instanceof Error ? error.message : '');
|
|
11327
|
+
return message ? ` (${message})` : '';
|
|
11328
|
+
}
|
|
11329
|
+
//#endregion
|
|
10081
11330
|
//#region methods / remove media
|
|
10082
11331
|
removeMedia() {
|
|
10083
11332
|
const media = this.mediaAttachment();
|
|
@@ -10151,6 +11400,11 @@ class TaonCmsPostComponent {
|
|
|
10151
11400
|
this.updateDraft('slug', event.target.value);
|
|
10152
11401
|
}
|
|
10153
11402
|
}
|
|
11403
|
+
selectRequiredSlug(slug) {
|
|
11404
|
+
if (slug && this.requiredSlugs.includes(slug)) {
|
|
11405
|
+
this.updateDraft('slug', slug);
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
10154
11408
|
//#endregion
|
|
10155
11409
|
//#region methods / edit
|
|
10156
11410
|
edit() {
|
|
@@ -10541,6 +11795,13 @@ class TaonCmsPostComponent {
|
|
|
10541
11795
|
categoryId: post?.categoryId ?? null,
|
|
10542
11796
|
tagIds: [],
|
|
10543
11797
|
});
|
|
11798
|
+
if ((post?.type ?? TaonCmsContentType.Normal) === TaonCmsContentType.JsonTable) {
|
|
11799
|
+
this.loadJsonTableData(post?.body ?? null);
|
|
11800
|
+
}
|
|
11801
|
+
else {
|
|
11802
|
+
this.jsonTableData.set({ version: 1, columns: [], rows: [] });
|
|
11803
|
+
this.jsonTableValidationError.set('');
|
|
11804
|
+
}
|
|
10544
11805
|
if (post &&
|
|
10545
11806
|
post.relatedPosts === undefined &&
|
|
10546
11807
|
this.mode() !== 'view-clean') {
|
|
@@ -10554,6 +11815,34 @@ class TaonCmsPostComponent {
|
|
|
10554
11815
|
}
|
|
10555
11816
|
}
|
|
10556
11817
|
//#endregion
|
|
11818
|
+
setJsonTableData(value) {
|
|
11819
|
+
try {
|
|
11820
|
+
const data = validateSpreadsheetData(value);
|
|
11821
|
+
this.jsonTableData.set(data);
|
|
11822
|
+
this.draft.update(draft => ({
|
|
11823
|
+
...draft,
|
|
11824
|
+
body: JSON.stringify(data),
|
|
11825
|
+
}));
|
|
11826
|
+
}
|
|
11827
|
+
catch (error) {
|
|
11828
|
+
this.jsonTableValidationError.set(error instanceof Error ? error.message : 'JSON Table data is invalid.');
|
|
11829
|
+
}
|
|
11830
|
+
}
|
|
11831
|
+
loadJsonTableData(body) {
|
|
11832
|
+
try {
|
|
11833
|
+
const data = parseSpreadsheetDocument(body);
|
|
11834
|
+
this.jsonTableData.set(data);
|
|
11835
|
+
this.draft.update(draft => ({
|
|
11836
|
+
...draft,
|
|
11837
|
+
body: JSON.stringify(data),
|
|
11838
|
+
}));
|
|
11839
|
+
this.jsonTableValidationError.set('');
|
|
11840
|
+
}
|
|
11841
|
+
catch (error) {
|
|
11842
|
+
this.jsonTableData.set({ version: 1, columns: [], rows: [] });
|
|
11843
|
+
this.jsonTableValidationError.set(error instanceof Error ? error.message : 'JSON Table data is invalid.');
|
|
11844
|
+
}
|
|
11845
|
+
}
|
|
10557
11846
|
//#region methods / set application hook selection
|
|
10558
11847
|
setApplicationHookSelection(post, hooks) {
|
|
10559
11848
|
const assignedHook = post?.applicationHook !== undefined
|
|
@@ -10601,7 +11890,7 @@ class TaonCmsPostComponent {
|
|
|
10601
11890
|
TaonPermissionApiService,
|
|
10602
11891
|
TaonCmsCategoryApiService,
|
|
10603
11892
|
TaonCmsTagApiService,
|
|
10604
|
-
], viewQueries: [{ propertyName: "rawContent", first: true, predicate: ["rawContent"], descendants: true, isSignal: true }, { propertyName: "actionButtonsTemplateRef", first: true, predicate: ["actionButtonsTemplate"], descendants: true }], ngImport: i0, template: "@if (mode() !== 'view-clean') {\n <ng-template #actionButtonsTemplate>\n <div class=\"buttons\">\n @if (editing()) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !canSave()\"\n (click)=\"save()\">\n {{ mode() === 'add' ? t.gettext('Add') : t.gettext('Save') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"cancel()\">\n {{ t.gettext('Cancel') }}\n </button>\n } @else if (currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"edit()\">\n <mat-icon>edit</mat-icon> {{ t.gettext('Edit') }}\n </button>\n }\n @if (mode() !== 'add' && currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"revert()\">\n <mat-icon>history</mat-icon> {{ t.gettext('Revert') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"delete()\">\n <mat-icon>delete</mat-icon> {{ t.gettext('Delete') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"duplicate()\">\n <mat-icon>content_copy</mat-icon> {{ t.gettext('Duplicate') }}\n </button>\n }\n </div>\n </ng-template>\n <header class=\"post-actions\">\n @if (visibilitySource(); as source) {\n <span\n class=\"post-status\"\n tabindex=\"0\"\n [matTooltip]=\"visibilityInheritedTooltip()\"\n [attr.aria-label]=\"visibilityInheritedTooltip()\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"true\">\n {{ statusLabel() }}\n </button>\n </span>\n } @else {\n <button\n class=\"post-status\"\n mat-button\n type=\"button\"\n [disabled]=\"statusControlDisabled()\"\n (click)=\"selectStatus()\">\n {{ statusLabel() }}\n </button>\n }\n <ng-container [ngTemplateOutlet]=\"actionButtonsTemplate\" />\n @if (busy()) {\n <span role=\"status\">{{\n uploading()\n ? t.gettext('Uploading... ') + uploadProgress() + '%'\n : t.gettext('Saving...')\n }}</span>\n }\n </header>\n @if (error()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ error() }}\n </p>\n }\n}\n\n<article\n class=\"post-article\"\n [attr.aria-busy]=\"busy()\">\n @if (editing()) {\n <mat-tab-group animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('CONTENT')\">\n <mat-accordion class=\"post-type-accordion\">\n <mat-expansion-panel #postTypePanel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>{{ currentPostTypeIcon() }}</mat-icon>\n {{\n t.gettext('Current post type: [[ postType ]]', {\n postType: currentPostType().label,\n })\n }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <mat-radio-group\n class=\"post-type-selector\"\n [attr.aria-label]=\"t.gettext('Post type')\"\n [value]=\"draft().type\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"updateDraft('type', $event.value)\">\n @for (type of postTypes; track type.value) {\n <mat-radio-button\n [value]=\"type.value\"\n (click)=\"postTypePanel.close()\"\n >{{ type.label }}</mat-radio-button\n >\n }\n </mat-radio-group>\n </mat-expansion-panel>\n </mat-accordion>\n <div\n class=\"post-title editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post title')\"\n aria-required=\"true\"\n [attr.data-placeholder]=\"placeholderTitle()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditableSingleLine]=\"true\"\n [taonCmsEditable]=\"draft().title\"\n (taonCmsEditableChange)=\"updateDraft('title', $event)\"></div>\n @if (subtitleEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('excerpt')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <div\n class=\"post-excerpt editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post excerpt')\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholderShortIntro()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditable]=\"draft().excerpt\"\n (taonCmsEditableChange)=\"updateDraft('excerpt', $event)\"></div>\n @if (descriptionEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('body')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <section class=\"post-content-editor\">\n <mat-tab-group\n [selectedIndex]=\"contentTab()\"\n (selectedIndexChange)=\"setContentTab($event)\"\n [preserveContent]=\"true\"\n animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('Editor')\">\n @if (contentEditor(); as editor) {\n <ng-template #customMenu>\n <button\n type=\"button\"\n class=\"NgxEditor__MenuItem\"\n title=\"Table\"\n aria-label=\"Table actions\"\n [disabled]=\"busy() || dialogOpen()\"\n [matMenuTriggerFor]=\"tableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\"\n (mousedown)=\"$event.preventDefault()\">\n <mat-icon>table_chart</mat-icon>\n </button>\n\n <mat-menu #tableMenu=\"matMenu\">\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"insertTable($event)\">\n <mat-icon>add</mat-icon>\n <span>Insert table</span>\n </button>\n\n <mat-divider />\n\n @for (item of tableCommands; track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"!canRunTableCommand(item.command)\"\n (click)=\"runTableCommand($event, item.command)\">\n {{ item.label }}\n </button>\n }\n </mat-menu>\n </ng-template>\n\n <ngx-editor-menu\n [editor]=\"editor\"\n [customMenuRef]=\"customMenu\"\n [toolbar]=\"contentToolbar\"\n [disabled]=\"busy() || dialogOpen()\" />\n <ngx-editor\n [editor]=\"editor\"\n [placeholder]=\"placeholderTellYourStory()\" />\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Raw')\">\n <textarea\n #rawContent\n class=\"raw-content\"\n [attr.aria-label]=\"t.gettext('Post body HTML')\"\n [value]=\"draft().body\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateRawContent($event)\"\n spellcheck=\"false\"></textarea>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Preview')\">\n <div\n class=\"post-body content-preview\"\n [innerHTML]=\"resolvedBody()\"></div>\n </mat-tab>\n </mat-tab-group>\n @if (contentTab() !== 2) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !contentEditor()\"\n [matMenuTriggerFor]=\"variableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\">\n {{ t.gettext('Insert variable') }}\n </button>\n }\n <mat-menu #variableMenu=\"matMenu\">\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"siteVariableMenu\">\n {{ t.gettext('Site') }}\n </button>\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"postVariableMenu\">\n {{ t.gettext('Post') }}\n </button>\n </mat-menu>\n <mat-menu #siteVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('frontendUrl')\">\n {{ t.gettext('Frontend URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('site.name')\">\n {{ t.gettext('Site name') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('currentYear')\">\n {{ t.gettext('Current year') }}\n </button>\n </mat-menu>\n <mat-menu #postVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('post.title')\">\n {{ t.gettext('Post title') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.url')\">\n {{ t.gettext('Post URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.id')\">\n {{ t.gettext('Post ID') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.thumbnailUrl')\">\n {{ t.gettext('Post Thumbnail URL') }}\n </button>\n </mat-menu>\n </section>\n <label class=\"slug-field\">\n <span>{{ t.gettext('URL slug') }}</span>\n <input\n [attr.aria-label]=\"t.gettext('URL slug')\"\n [value]=\"draft().slug\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateSlug($event)\"\n [placeholder]=\"t.gettext('my-post')\" />\n </label>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('SETTINGS')\">\n <div class=\"post-settings\">\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-child-files\"\n aria-labelledby=\"related-child-files-heading\">\n <h2 id=\"related-child-files-heading\">\n {{ t.gettext('Create related children from files') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Please save the post before creating children.'\n )\n }}\n </p>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Child title prefix') }}</mat-label>\n <input\n matInput\n [value]=\"childTitlePrefix()\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (input)=\"updateChildTitlePrefix($event)\" />\n </mat-form-field>\n <input\n #relatedChildFilesInput\n hidden\n type=\"file\"\n multiple\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (change)=\"relatedChildFilesSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"relatedChildFilesInput.click()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Choose files') }}\n </button>\n @if (childBatchError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ childBatchError() }}\n </p>\n }\n @if (childFiles().length) {\n <div class=\"related-child-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"childOverallProgress()\"\n [attr.aria-label]=\"\n t.gettext('Overall child creation progress')\n \" />\n <span>\n {{ childOverallProgress() }}%\n {{ t.gettext('overall progress') }}\n </span>\n </div>\n <ul>\n @for (file of childFiles(); track file.id; let index =\n $index) {\n <li>\n <div class=\"related-child-file\">\n <div class=\"related-child-file-heading\">\n <span>{{ file.file.name }}</span>\n <span>{{ childTitlePreviews()[index] }}</span>\n </div>\n <div class=\"related-child-file-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"file.progress\"\n [attr.aria-label]=\"\n t.gettext('Progress for [[ fileName ]]', {\n fileName: file.file.name,\n })\n \" />\n <span>{{ file.progress }}%</span>\n </div>\n @if (file.status === 'processing') {\n <p role=\"status\">{{ t.gettext('Processing...') }}</p>\n } @else if (file.status === 'success') {\n <p role=\"status\">{{ t.gettext('Child created.') }}</p>\n @if (file.error) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error }}\n </p>\n }\n } @else if (file.status === 'error') {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error }}\n </p>\n } @else {\n <p>{{ t.gettext('Waiting to upload.') }}</p>\n }\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove file [[ fileName ]]', {\n fileName: file.file.name,\n })\n \"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"removeRelatedChildFile(file.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!canUploadRelatedChildren()\"\n (click)=\"uploadAndCreateChildren()\">\n {{ t.gettext('Upload and Create Children') }}\n </button>\n </section>\n </mat-card>\n @if (mediaAttachment(); as media) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"media-attachment\"\n aria-labelledby=\"media-attachment-heading\">\n <h2 id=\"media-attachment-heading\">{{ media.heading }}</h2>\n @if (uploading()) {\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadProgress()\"\n [attr.aria-label]=\"t.gettext('Media upload progress')\" />\n }\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading media preview...') }}\n </p>\n }\n @if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n }\n @if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support video playback.'\n )\n }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support audio playback.'\n )\n }}\n </audio>\n } @else if (\n media.type === 'attachment' && mediaPreviewUrl()\n ) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ t.gettext('Replace') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeMedia()\">\n {{ t.gettext('Remove') }}\n </button>\n } @else {\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ media.uploadLabel }}\n </button>\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-thumbnail\"\n aria-labelledby=\"thumbnail-heading\">\n <h2 id=\"thumbnail-heading\">{{ t.gettext('Thumbnail') }}</h2>\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n } @else {\n <p>{{ t.gettext('No thumbnail selected.') }}</p>\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <input\n #thumbnailInput\n hidden\n type=\"file\"\n accept=\"image/*\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"thumbnailSelected($event)\" />\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"thumbnailInput.click()\">\n {{ t.gettext('Upload Thumbnail') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"applyDefaultThumbnail()\">\n {{ t.gettext('Apply Default Placeholder') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-taxonomy\"\n aria-labelledby=\"category-heading\">\n <h2 id=\"category-heading\">{{ t.gettext('Category / Tags') }}</h2>\n @if (taxonomyLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading categories and tags...') }}\n </p>\n }\n @if (taxonomyError()) {\n <p role=\"alert\">{{ taxonomyError() }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy()\"\n (click)=\"loadTaxonomy()\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Category') }}</mat-label>\n <mat-select\n [value]=\"draft().categoryId\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"updateDraft('categoryId', $event.value)\">\n <mat-option [value]=\"null\">{{\n t.gettext('None')\n }}</mat-option>\n @for (category of categories(); track category.id) {\n <mat-option [value]=\"category.id\">{{\n category.name\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <h3>{{ t.gettext('Tags') }}</h3>\n <mat-chip-set [attr.aria-label]=\"t.gettext('Selected tags')\">\n @for (tag of selectedTags(); track tag.id) {\n <mat-chip\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (removed)=\"removeTag(tag.id)\">\n {{ tag.name }}\n <button\n matChipRemove\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n [attr.aria-label]=\"\n t.gettext('Remove tag [[ name ]]', { name: tag.name })\n \">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip>\n }\n </mat-chip-set>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Search tags') }}</mat-label>\n <input\n matInput\n [value]=\"tagSearch()\"\n (input)=\"filterTags($event)\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Add tag') }}</mat-label>\n <mat-select\n [value]=\"null\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"addTag($event.value)\">\n @for (tag of filteredTags(); track tag.id) {\n <mat-option [value]=\"tag.id\">{{ tag.name }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('category')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add category') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('tag')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add tag') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"parent-post\"\n aria-labelledby=\"parent-post-heading\">\n <h2 id=\"parent-post-heading\">{{ t.gettext('Parent Post') }}</h2>\n @if (parentPostLoading()) {\n <p role=\"status\">{{ t.gettext('Loading parent post...') }}</p>\n } @else if (parentPostError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ parentPostError() }}\n </p>\n } @else if (parentPost(); as parent) {\n <p>\n <taon-cms-post-thumbnail [post]=\"parent\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, parent)\">\n {{ parent.title }}\n </a>\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeParentPost()\">\n <mat-icon>link_off</mat-icon>\n {{ t.gettext('Remove from parent') }}\n </button>\n } @else {\n <p>{{ t.gettext('No parent added') }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !currentPost()\"\n (click)=\"addAsChildOf()\">\n <mat-icon>account_tree</mat-icon>\n @if (currentPost()) {\n {{ t.gettext('Add as a child') }}\n } @else {\n {{\n t.gettext(\n 'Add as a child (disabled \u2014 please save the post first)'\n )\n }}\n }\n </button>\n }\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-posts\"\n aria-labelledby=\"related-posts-heading\">\n <h2 id=\"related-posts-heading\">\n {{ t.gettext('Related posts') }}\n </h2>\n @if (relatedPostsLoading()) {\n <p role=\"status\">{{ t.gettext('Loading related posts...') }}</p>\n } @else if (relatedPostsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ relatedPostsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadRelatedPosts()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul\n cdkDropList\n [cdkDropListData]=\"relatedPosts()\"\n [cdkDropListDisabled]=\"busy() || dialogOpen()\"\n (cdkDropListDropped)=\"reorderRelatedPosts($event)\">\n @for (post of relatedPosts(); track post.id) {\n <li cdkDrag [cdkDragData]=\"post\">\n <button\n mat-icon-button\n cdkDragHandle\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n [attr.aria-label]=\"\n t.gettext('Reorder related post [[ title ]]', {\n title: post.title,\n })\n \">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n <taon-cms-post-thumbnail [post]=\"post\" />\n <span class=\"related-post-title\">\n #{{ post.id }} -\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, post)\">\n {{ post.title }}\n </a>\n </span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove related post [[ title ]]', {\n title: post.title,\n })\n \"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeRelatedPost(post.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No related posts yet.') }}</li>\n }\n </ul>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"addRelatedPost()\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add related post') }}\n </button>\n }\n <mat-checkbox\n [checked]=\"applyVisibilityToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyVisibilityToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply visibility to related posts') }}\n </mat-checkbox>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-version-links\"\n aria-labelledby=\"post-version-links-heading\">\n <h2 id=\"post-version-links-heading\">\n {{ t.gettext('Post versions') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext('Save the post first to manage version links.')\n }}\n </p>\n }\n @if (postVersionLinksLoading()) {\n <p role=\"status\">{{ t.gettext('Loading post versions...') }}</p>\n }\n @if (postVersionLinksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ postVersionLinksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPostVersionLinks(currentPost())\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Public post version') }}</h3>\n @if (postVersionLinks().publicVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPublicVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('public')\">\n {{ t.gettext('Replace public version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('public')\">\n {{ t.gettext('Remove public version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No public post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPublicVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPublicVersion()\n \"\n (click)=\"selectVersionPost('public')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add public post version') }}\n </button>\n }\n </div>\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Private post version') }}</h3>\n @if (postVersionLinks().privateVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPrivateVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('private')\">\n {{ t.gettext('Replace private version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('private')\">\n {{ t.gettext('Remove private version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No private post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPrivateVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPrivateVersion()\n \"\n (click)=\"selectVersionPost('private')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add private post version') }}\n </button>\n }\n </div>\n </section>\n </mat-card>\n\n @if (!mediaAttachment()) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"application-hook\"\n aria-labelledby=\"application-hook-heading\">\n <h2 id=\"application-hook-heading\">\n {{ t.gettext('Hooks') }}\n </h2>\n @if (applicationHooksLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading application hooks...') }}\n </p>\n } @else if (applicationHooksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ applicationHooksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadApplicationHooks()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <mat-checkbox\n [checked]=\"useAsApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"setUseAsApplicationHook($event.checked)\">\n {{ t.gettext('Use as application hook') }}\n </mat-checkbox>\n @if (useAsApplicationHook()) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Application Hook') }}</mat-label>\n <mat-select\n [value]=\"selectedApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"\n selectedApplicationHook.set($event.value)\n \">\n @for (\n applicationHook of applicationHooks();\n track applicationHook.id\n ) {\n <mat-option\n [value]=\"applicationHook.hook\"\n [disabled]=\"\n applicationHookInUseByAnotherPost(applicationHook)\n \">\n {{ applicationHookLabel(applicationHook.hook) }}\n @if (\n applicationHookInUseByAnotherPost(applicationHook)\n ) {\n {{\n t.gettext('(in use by post # [[ postId ]])', {\n postId: applicationHook.contentId,\n })\n }}\n }\n </mat-option>\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No application hooks configured')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-permissions\"\n aria-labelledby=\"post-permissions-heading\">\n <h2 id=\"post-permissions-heading\">\n {{ t.gettext('Permissions') }}\n </h2>\n @if (permissionSource(); as source) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Permissions are inherited from # [[ postId ]] - [[ title ]]. Edit the source post to change them.',\n { postId: source.id, title: source.title }\n )\n }}\n </p>\n }\n @if (permissionsLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading assigned permissions...') }}\n </p>\n } @else if (permissionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPermissions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul>\n @for (permission of permissions(); track permission.id) {\n <li>\n <span>{{ permission.name }} ({{ permission.code }})</span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove permission [[ name ]]', {\n name: permission.name,\n })\n \"\n [disabled]=\"\n busy() || dialogOpen() || !!permissionSource()\n \"\n (click)=\"removePermission(permission.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No permissions assigned.') }}</li>\n }\n </ul>\n @if (permissionOptionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionOptionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || permissionOptionsLoading()\n \"\n (click)=\"loadPermissionOptions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <div class=\"permission-assignment\">\n <mat-form-field\n class=\"w-full\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Permission') }}</mat-label>\n <mat-select\n [value]=\"selectedPermissionId()\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n permissionOptionsLoading() ||\n !!permissionSource()\n \"\n (selectionChange)=\"\n selectedPermissionId.set($event.value)\n \">\n @for (\n permission of availablePermissions();\n track permission.id\n ) {\n <mat-option [value]=\"permission.id\"\n >{{ permission.name }} ({{\n permission.code\n }})</mat-option\n >\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No permissions available')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n !selectedPermissionId() ||\n !!permissionSource()\n \"\n (click)=\"addPermission()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Assign permission') }}\n </button>\n </div>\n }\n }\n @if (!permissionSource()) {\n <mat-checkbox\n [checked]=\"applyPermissionsToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyPermissionsToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply permissions to related posts') }}\n </mat-checkbox>\n }\n </section>\n </mat-card>\n </div>\n </mat-tab>\n </mat-tab-group>\n } @else if (currentPost(); as post) {\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <h1 class=\"post-title\">{{ post.title }}</h1>\n @if (post.excerpt) {\n <p class=\"post-excerpt\">{{ post.excerpt }}</p>\n }\n <div\n class=\"post-body\"\n [innerHTML]=\"resolvedBody()\"></div>\n @if (mediaAttachment(); as media) {\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">{{ t.gettext('Loading media...') }}</p>\n } @else if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n } @else if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [poster]=\"thumbnailPreviewUrl() || null\"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support video playback.') }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support audio playback.') }}\n </audio>\n } @else if (media.type === 'attachment' && mediaPreviewUrl()) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n }\n }\n @if (mode() === 'view-clean') {\n <taon-cms-comments\n [contentId]=\"post.id\"\n class=\"w-full\" />\n }\n } @else {\n <p role=\"status\">{{ t.gettext('No post is loaded.') }}</p>\n }\n</article>\n", styles: [":host{display:block}:host ::ng-deep .NgxEditor .ProseMirror table{width:100%;border-collapse:collapse;table-layout:fixed;margin:12px 0}:host ::ng-deep .NgxEditor .ProseMirror td,:host ::ng-deep .NgxEditor .ProseMirror th{position:relative;min-width:60px;border:1px solid #aaa;padding:8px;vertical-align:top}:host ::ng-deep .NgxEditor .ProseMirror td>p,:host ::ng-deep .NgxEditor .ProseMirror th>p{margin:0}:host ::ng-deep .NgxEditor .ProseMirror .selectedCell{background:#3f51b51f}mat-expansion-panel{border-radius:0!important}.post-actions{display:flex;align-items:center;flex-wrap:wrap;gap:1rem;padding:1rem;border-bottom:1px solid rgba(136,136,136,.2)}.post-status{margin-right:auto;text-transform:capitalize;opacity:.7}.buttons{display:flex;flex-wrap:wrap;gap:.5rem}.post-article{max-width:760px;margin:0 auto;padding:clamp(1.5rem,5vw,4rem) 1.5rem;overflow-wrap:anywhere}.post-settings{display:grid;gap:1rem;margin-top:1rem}.post-setting-card{display:block;padding:20px}.post-title{margin:0 0 1.5rem;font-size:clamp(2rem,5vw,3.5rem);font-weight:700;line-height:1.15;letter-spacing:-.03em}.post-excerpt{margin:0 0 2rem;font-size:1.35rem;line-height:1.6;opacity:.7;white-space:pre-wrap}.post-body{font-family:Georgia,Times New Roman,serif;font-size:1.2rem;line-height:1.8;white-space:pre-wrap}.editable{min-height:1.2em;outline:none}.editable:focus{box-shadow:-3px 0 #8885}.editable:empty:before{content:attr(data-placeholder);opacity:.4;pointer-events:none}.post-content-editor{border:1px solid rgba(136,136,136,.3333333333);border-radius:4px;overflow:hidden}.raw-content{display:block;box-sizing:border-box;width:100%;min-height:20rem;padding:1rem;resize:vertical;border:0;color:inherit;background:transparent;font-family:monospace}ngx-editor{display:block;min-height:20rem}.content-preview{min-height:12rem;padding:1rem}.slug-field{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;margin-top:3rem;padding-top:1rem;border-top:1px solid rgba(136,136,136,.2);font-size:.875rem}.slug-field input{flex:1;min-width:160px;padding:.5rem;color:inherit;background:transparent;border:1px solid rgba(136,136,136,.3333333333);border-radius:4px}.error{padding:1rem;color:#b3261e}.post-type-selector{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.5rem}.post-type-accordion{display:block;margin-bottom:1rem}.post-type-accordion mat-panel-title{align-items:center;gap:.5rem}.media-attachment,.post-thumbnail,.post-taxonomy,.parent-post,.related-posts,.post-version-links,.application-hook,.post-permissions{margin-top:1.5rem;font-size:.875rem}.media-attachment h2,.post-thumbnail h2,.post-taxonomy h2,.parent-post h2,.related-posts h2,.post-version-links h2,.application-hook h2,.post-permissions h2{font-size:1rem}.media-attachment .thumbnail-preview,.post-thumbnail .thumbnail-preview,.post-taxonomy .thumbnail-preview,.parent-post .thumbnail-preview,.related-posts .thumbnail-preview,.post-version-links .thumbnail-preview,.application-hook .thumbnail-preview,.post-permissions .thumbnail-preview{display:block;width:min(100%,480px);max-height:320px;object-fit:contain;border-radius:12px;margin-bottom:.75rem}.media-attachment .post-taxonomy mat-form-field,.post-thumbnail .post-taxonomy mat-form-field,.post-taxonomy .post-taxonomy mat-form-field,.parent-post .post-taxonomy mat-form-field,.related-posts .post-taxonomy mat-form-field,.post-version-links .post-taxonomy mat-form-field,.application-hook .post-taxonomy mat-form-field,.post-permissions .post-taxonomy mat-form-field{display:block;margin-top:.75rem}.related-posts ul,.related-child-files ul,.post-permissions ul{padding:0;list-style:none}.related-posts mat-form-field,.related-child-files mat-form-field,.post-permissions mat-form-field{display:block;width:min(100%,28rem);margin-top:.75rem}.related-posts .application-hook,.related-child-files .application-hook,.post-permissions .application-hook{display:grid;align-items:start;gap:.75rem}.related-posts .application-hook mat-form-field,.related-child-files .application-hook mat-form-field,.post-permissions .application-hook mat-form-field{width:min(100%,28rem)}.related-posts li,.related-child-files li,.post-permissions li{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.related-child-files{margin-top:1.5rem;font-size:.875rem}.related-child-files li{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem 0;border-bottom:1px solid rgba(136,136,136,.2)}.related-post-title{flex:1}[cdkDragHandle]{cursor:grab}.related-child-file{flex:1;min-width:0}.related-child-file-heading,.related-child-file-progress,.related-child-progress{display:flex;align-items:center;gap:.75rem}.related-child-file-heading{justify-content:space-between;overflow-wrap:anywhere}.related-child-file-progress{margin-top:.5rem}.related-child-file-progress mat-progress-bar{flex:1}.related-child-progress{margin:.75rem 0}.related-child-progress mat-progress-bar{flex:1}.post-version-link{margin-top:1rem;padding-top:.75rem;border-top:1px solid rgba(136,136,136,.2)}.post-version-link h3{font-size:.95rem}video{max-width:100%;max-height:400px!important}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i4$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i8.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i9.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i9.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: NgxEditorModule }, { kind: "component", type: i10.NgxEditorComponent, selector: "ngx-editor", inputs: ["editor", "outputFormat", "placeholder"], outputs: ["focusOut", "focusIn"] }, { kind: "component", type: i10.MenuComponent, selector: "ngx-editor-menu", inputs: ["toolbar", "colorPresets", "disabled", "editor", "customMenuRef", "dropdownPlacement"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i11.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: i11.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i11.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i12.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i15.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: TaonCmsEditableDirective, selector: "[taonCmsEditable]", inputs: ["taonCmsEditableFormat", "taonCmsEditableSingleLine", "taonCmsEditable"], outputs: ["taonCmsEditableChange"] }, { kind: "component", type: TaonCmsCommentsComponent, selector: "taon-cms-comments", inputs: ["contentId"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i16.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i17.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i17.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i17.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: TaonCmsPostThumbnailComponent, selector: "taon-cms-post-thumbnail", inputs: ["post"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11893
|
+
], viewQueries: [{ propertyName: "rawContent", first: true, predicate: ["rawContent"], descendants: true, isSignal: true }, { propertyName: "actionButtonsTemplateRef", first: true, predicate: ["actionButtonsTemplate"], descendants: true }], ngImport: i0, template: "@if (mode() !== 'view-clean') {\n <ng-template #actionButtonsTemplate>\n <div class=\"buttons\">\n @if (editing()) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !canSave()\"\n (click)=\"save()\">\n {{ mode() === 'add' ? t.gettext('Add') : t.gettext('Save') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"cancel()\">\n {{ t.gettext('Cancel') }}\n </button>\n } @else if (currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"edit()\">\n <mat-icon>edit</mat-icon> {{ t.gettext('Edit') }}\n </button>\n }\n @if (mode() !== 'add' && currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"revert()\">\n <mat-icon>history</mat-icon> {{ t.gettext('Revert') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"delete()\">\n <mat-icon>delete</mat-icon> {{ t.gettext('Delete') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"duplicate()\">\n <mat-icon>content_copy</mat-icon> {{ t.gettext('Duplicate') }}\n </button>\n }\n </div>\n </ng-template>\n <header class=\"post-actions\">\n @if (visibilitySource(); as source) {\n <span\n class=\"post-status\"\n tabindex=\"0\"\n [matTooltip]=\"visibilityInheritedTooltip()\"\n [attr.aria-label]=\"visibilityInheritedTooltip()\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"true\">\n {{ statusLabel() }}\n </button>\n </span>\n } @else {\n <button\n class=\"post-status\"\n mat-button\n type=\"button\"\n [disabled]=\"statusControlDisabled()\"\n (click)=\"selectStatus()\">\n {{ statusLabel() }}\n </button>\n }\n <ng-container [ngTemplateOutlet]=\"actionButtonsTemplate\" />\n @if (busy()) {\n <span role=\"status\">{{\n uploading()\n ? t.gettext('Uploading... ') + uploadProgress() + '%'\n : t.gettext('Saving...')\n }}</span>\n }\n </header>\n @if (error()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ error() }}\n </p>\n }\n}\n\n<article\n class=\"post-article\"\n [attr.aria-busy]=\"busy()\">\n @if (editing()) {\n <mat-tab-group animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('CONTENT')\">\n <mat-accordion class=\"post-type-accordion\">\n <mat-expansion-panel #postTypePanel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>{{ currentPostTypeIcon() }}</mat-icon>\n {{\n t.gettext('Current post type: [[ postType ]]', {\n postType: currentPostType().label,\n })\n }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <mat-radio-group\n class=\"post-type-selector\"\n [attr.aria-label]=\"t.gettext('Post type')\"\n [value]=\"draft().type\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"updateDraft('type', $event.value)\">\n @for (type of postTypes; track type.value) {\n <mat-radio-button\n [value]=\"type.value\"\n (click)=\"postTypePanel.close()\"\n >{{ type.label }}</mat-radio-button\n >\n }\n </mat-radio-group>\n </mat-expansion-panel>\n </mat-accordion>\n <div\n class=\"post-title editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post title')\"\n aria-required=\"true\"\n [attr.data-placeholder]=\"placeholderTitle()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditableSingleLine]=\"true\"\n [taonCmsEditable]=\"draft().title\"\n (taonCmsEditableChange)=\"updateDraft('title', $event)\"></div>\n @if (subtitleEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('excerpt')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <div\n class=\"post-excerpt editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post excerpt')\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholderShortIntro()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditable]=\"draft().excerpt\"\n (taonCmsEditableChange)=\"updateDraft('excerpt', $event)\"></div>\n @if (descriptionEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('body')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <section class=\"post-content-editor\">\n @if (draft().type === contentType.JsonTable) {\n <taon-spreadsheet-table\n [data]=\"jsonTableData()\"\n [disabled]=\"busy() || dialogOpen()\"\n [validationError]=\"jsonTableValidationError()\"\n (dataChange)=\"setJsonTableData($event)\"\n (validationErrorChange)=\"jsonTableValidationError.set($event)\" />\n } @else {\n <mat-tab-group\n [selectedIndex]=\"contentTab()\"\n (selectedIndexChange)=\"setContentTab($event)\"\n [preserveContent]=\"true\"\n animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('Editor')\">\n @if (contentEditor(); as editor) {\n <ng-template #customMenu>\n <button\n type=\"button\"\n class=\"NgxEditor__MenuItem\"\n title=\"Table\"\n aria-label=\"Table actions\"\n [disabled]=\"busy() || dialogOpen()\"\n [matMenuTriggerFor]=\"tableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\"\n (mousedown)=\"$event.preventDefault()\">\n <mat-icon>table_chart</mat-icon>\n </button>\n\n <mat-menu #tableMenu=\"matMenu\">\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"insertTable($event)\">\n <mat-icon>add</mat-icon>\n <span>Insert table</span>\n </button>\n\n <mat-divider />\n\n @for (item of tableCommands; track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"!canRunTableCommand(item.command)\"\n (click)=\"runTableCommand($event, item.command)\">\n {{ item.label }}\n </button>\n }\n </mat-menu>\n </ng-template>\n\n <ngx-editor-menu\n [editor]=\"editor\"\n [customMenuRef]=\"customMenu\"\n [toolbar]=\"contentToolbar\"\n [disabled]=\"busy() || dialogOpen()\" />\n <ngx-editor\n [editor]=\"editor\"\n [placeholder]=\"placeholderTellYourStory()\" />\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Raw')\">\n <textarea\n #rawContent\n class=\"raw-content\"\n [attr.aria-label]=\"t.gettext('Post body HTML')\"\n [value]=\"draft().body\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateRawContent($event)\"\n spellcheck=\"false\"></textarea>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Preview')\">\n <div\n class=\"post-body content-preview\"\n [innerHTML]=\"resolvedBody()\"></div>\n </mat-tab>\n </mat-tab-group>\n @if (contentTab() !== 2) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !contentEditor()\"\n [matMenuTriggerFor]=\"variableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\">\n {{ t.gettext('Insert variable') }}\n </button>\n }\n <mat-menu #variableMenu=\"matMenu\">\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"siteVariableMenu\">\n {{ t.gettext('Site') }}\n </button>\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"postVariableMenu\">\n {{ t.gettext('Post') }}\n </button>\n </mat-menu>\n <mat-menu #siteVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('frontendUrl')\">\n {{ t.gettext('Frontend URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('site.name')\">\n {{ t.gettext('Site name') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('currentYear')\">\n {{ t.gettext('Current year') }}\n </button>\n </mat-menu>\n <mat-menu #postVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('post.title')\">\n {{ t.gettext('Post title') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.url')\">\n {{ t.gettext('Post URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.id')\">\n {{ t.gettext('Post ID') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.thumbnailUrl')\">\n {{ t.gettext('Post Thumbnail URL') }}\n </button>\n </mat-menu>\n }\n </section>\n <label class=\"slug-field\">\n <span>{{ t.gettext('URL slug') }}</span>\n <input\n [attr.aria-label]=\"t.gettext('URL slug')\"\n [value]=\"draft().slug\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateSlug($event)\"\n [placeholder]=\"t.gettext('my-post')\" />\n </label>\n @if (requiredSlugs.length) {\n <mat-form-field\n class=\"required-slugs-field\"\n appearance=\"outline\">\n <mat-label>{{ t.gettext('Required Slugs') }}</mat-label>\n <mat-select\n [value]=\"selectedRequiredSlug()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"selectRequiredSlug($event.value)\">\n @for (slug of requiredSlugs; track slug) {\n <mat-option [value]=\"slug\">{{ slug }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('SETTINGS')\">\n <div class=\"post-settings\">\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-child-files\"\n aria-labelledby=\"related-child-files-heading\">\n <h2 id=\"related-child-files-heading\">\n {{ t.gettext('Create related children from files') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Please save the post before creating children.'\n )\n }}\n </p>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Child title prefix') }}</mat-label>\n <input\n matInput\n [value]=\"childTitlePrefix()\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (input)=\"updateChildTitlePrefix($event)\" />\n </mat-form-field>\n <input\n #relatedChildFilesInput\n hidden\n type=\"file\"\n multiple\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (change)=\"relatedChildFilesSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"relatedChildFilesInput.click()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Choose files') }}\n </button>\n @if (childBatchError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ childBatchError() }}\n </p>\n }\n @if (childFiles().length) {\n <div class=\"related-child-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"childOverallProgress()\"\n [attr.aria-label]=\"\n t.gettext('Overall child creation progress')\n \" />\n <span>\n {{ childOverallProgress() }}%\n {{ t.gettext('overall progress') }}\n </span>\n </div>\n <ul>\n @for (file of childFiles(); track file.id; let index =\n $index) {\n <li>\n <div class=\"related-child-file\">\n <div class=\"related-child-file-heading\">\n <span>{{ file.file.name }}</span>\n <span>{{ childTitlePreviews()[index] }}</span>\n </div>\n <div class=\"related-child-file-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"file.progress\"\n [attr.aria-label]=\"\n t.gettext('Progress for [[ fileName ]]', {\n fileName: file.file.name,\n })\n \" />\n <span>{{ file.progress }}%</span>\n </div>\n @if (file.status === 'processing') {\n <p role=\"status\">{{ t.gettext('Processing...') }}</p>\n } @else if (file.status === 'success') {\n <p role=\"status\">{{ t.gettext('Child created.') }}</p>\n @if (file.error) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error | json }}\n </p>\n }\n } @else if (file.status === 'error') {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error | json }}\n </p>\n } @else {\n <p>{{ t.gettext('Waiting to upload.') }}</p>\n }\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove file [[ fileName ]]', {\n fileName: file.file.name,\n })\n \"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"removeRelatedChildFile(file.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!canUploadRelatedChildren()\"\n (click)=\"uploadAndCreateChildren()\">\n {{ t.gettext('Upload and Create Children') }}\n </button>\n @if (childUploadRunning()) {\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelRelatedChildUploads()\">\n {{ t.gettext('Cancel upload') }}\n </button>\n }\n </section>\n </mat-card>\n @if (mediaAttachment(); as media) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"media-attachment\"\n aria-labelledby=\"media-attachment-heading\">\n <h2 id=\"media-attachment-heading\">{{ media.heading }}</h2>\n @if (uploading()) {\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadProgress()\"\n [attr.aria-label]=\"t.gettext('Media upload progress')\" />\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelMediaUpload()\">\n {{ t.gettext('Cancel upload') }}\n </button>\n }\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading media preview...') }}\n </p>\n }\n @if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n }\n @if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support video playback.'\n )\n }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support audio playback.'\n )\n }}\n </audio>\n } @else if (\n media.type === 'attachment' && mediaPreviewUrl()\n ) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ t.gettext('Replace') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeMedia()\">\n {{ t.gettext('Remove') }}\n </button>\n } @else {\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ media.uploadLabel }}\n </button>\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-thumbnail\"\n aria-labelledby=\"thumbnail-heading\">\n <h2 id=\"thumbnail-heading\">{{ t.gettext('Thumbnail') }}</h2>\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n } @else {\n <p>{{ t.gettext('No thumbnail selected.') }}</p>\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <input\n #thumbnailInput\n hidden\n type=\"file\"\n accept=\"image/*\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"thumbnailSelected($event)\" />\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"thumbnailInput.click()\">\n {{ t.gettext('Upload Thumbnail') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"applyDefaultThumbnail()\">\n {{ t.gettext('Apply Default Placeholder') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-taxonomy\"\n aria-labelledby=\"category-heading\">\n <h2 id=\"category-heading\">{{ t.gettext('Category / Tags') }}</h2>\n @if (taxonomyLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading categories and tags...') }}\n </p>\n }\n @if (taxonomyError()) {\n <p role=\"alert\">{{ taxonomyError() }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy()\"\n (click)=\"loadTaxonomy()\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Category') }}</mat-label>\n <mat-select\n [value]=\"draft().categoryId\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"updateDraft('categoryId', $event.value)\">\n <mat-option [value]=\"null\">{{\n t.gettext('None')\n }}</mat-option>\n @for (category of categories(); track category.id) {\n <mat-option [value]=\"category.id\">{{\n category.name\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <h3>{{ t.gettext('Tags') }}</h3>\n <mat-chip-set [attr.aria-label]=\"t.gettext('Selected tags')\">\n @for (tag of selectedTags(); track tag.id) {\n <mat-chip\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (removed)=\"removeTag(tag.id)\">\n {{ tag.name }}\n <button\n matChipRemove\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n [attr.aria-label]=\"\n t.gettext('Remove tag [[ name ]]', { name: tag.name })\n \">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip>\n }\n </mat-chip-set>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Search tags') }}</mat-label>\n <input\n matInput\n [value]=\"tagSearch()\"\n (input)=\"filterTags($event)\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Add tag') }}</mat-label>\n <mat-select\n [value]=\"null\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"addTag($event.value)\">\n @for (tag of filteredTags(); track tag.id) {\n <mat-option [value]=\"tag.id\">{{ tag.name }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('category')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add category') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('tag')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add tag') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"parent-post\"\n aria-labelledby=\"parent-post-heading\">\n <h2 id=\"parent-post-heading\">{{ t.gettext('Parent Post') }}</h2>\n @if (parentPostLoading()) {\n <p role=\"status\">{{ t.gettext('Loading parent post...') }}</p>\n } @else if (parentPostError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ parentPostError() }}\n </p>\n } @else if (parentPost(); as parent) {\n <p>\n <taon-cms-post-thumbnail [post]=\"parent\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, parent)\">\n {{ parent.title }}\n </a>\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeParentPost()\">\n <mat-icon>link_off</mat-icon>\n {{ t.gettext('Remove from parent') }}\n </button>\n } @else {\n <p>{{ t.gettext('No parent added') }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !currentPost()\"\n (click)=\"addAsChildOf()\">\n <mat-icon>account_tree</mat-icon>\n @if (currentPost()) {\n {{ t.gettext('Add as a child') }}\n } @else {\n {{\n t.gettext(\n 'Add as a child (disabled \u2014 please save the post first)'\n )\n }}\n }\n </button>\n }\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-posts\"\n aria-labelledby=\"related-posts-heading\">\n <h2 id=\"related-posts-heading\">\n {{ t.gettext('Related posts') }}\n </h2>\n @if (relatedPostsLoading()) {\n <p role=\"status\">{{ t.gettext('Loading related posts...') }}</p>\n } @else if (relatedPostsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ relatedPostsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadRelatedPosts()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul\n cdkDropList\n [cdkDropListData]=\"relatedPosts()\"\n [cdkDropListDisabled]=\"busy() || dialogOpen()\"\n (cdkDropListDropped)=\"reorderRelatedPosts($event)\">\n @for (post of relatedPosts(); track post.id) {\n <li cdkDrag [cdkDragData]=\"post\">\n <button\n mat-icon-button\n cdkDragHandle\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n [attr.aria-label]=\"\n t.gettext('Reorder related post [[ title ]]', {\n title: post.title,\n })\n \">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n <taon-cms-post-thumbnail [post]=\"post\" />\n <span class=\"related-post-title\">\n #{{ post.id }} -\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, post)\">\n {{ post.title }}\n </a>\n </span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove related post [[ title ]]', {\n title: post.title,\n })\n \"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeRelatedPost(post.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No related posts yet.') }}</li>\n }\n </ul>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"addRelatedPost()\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add related post') }}\n </button>\n }\n <mat-checkbox\n [checked]=\"applyVisibilityToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyVisibilityToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply visibility to related posts') }}\n </mat-checkbox>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-version-links\"\n aria-labelledby=\"post-version-links-heading\">\n <h2 id=\"post-version-links-heading\">\n {{ t.gettext('Post versions') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext('Save the post first to manage version links.')\n }}\n </p>\n }\n @if (postVersionLinksLoading()) {\n <p role=\"status\">{{ t.gettext('Loading post versions...') }}</p>\n }\n @if (postVersionLinksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ postVersionLinksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPostVersionLinks(currentPost())\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Public post version') }}</h3>\n @if (postVersionLinks().publicVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPublicVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('public')\">\n {{ t.gettext('Replace public version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('public')\">\n {{ t.gettext('Remove public version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No public post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPublicVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPublicVersion()\n \"\n (click)=\"selectVersionPost('public')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add public post version') }}\n </button>\n }\n </div>\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Private post version') }}</h3>\n @if (postVersionLinks().privateVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPrivateVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('private')\">\n {{ t.gettext('Replace private version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('private')\">\n {{ t.gettext('Remove private version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No private post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPrivateVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPrivateVersion()\n \"\n (click)=\"selectVersionPost('private')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add private post version') }}\n </button>\n }\n </div>\n </section>\n </mat-card>\n\n @if (!mediaAttachment()) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"application-hook\"\n aria-labelledby=\"application-hook-heading\">\n <h2 id=\"application-hook-heading\">\n {{ t.gettext('Hooks') }}\n </h2>\n @if (applicationHooksLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading application hooks...') }}\n </p>\n } @else if (applicationHooksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ applicationHooksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadApplicationHooks()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <mat-checkbox\n [checked]=\"useAsApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"setUseAsApplicationHook($event.checked)\">\n {{ t.gettext('Use as application hook') }}\n </mat-checkbox>\n @if (useAsApplicationHook()) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Application Hook') }}</mat-label>\n <mat-select\n [value]=\"selectedApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"\n selectedApplicationHook.set($event.value)\n \">\n @for (\n applicationHook of applicationHooks();\n track applicationHook.id\n ) {\n <mat-option\n [value]=\"applicationHook.hook\"\n [disabled]=\"\n applicationHookInUseByAnotherPost(applicationHook)\n \">\n {{ applicationHookLabel(applicationHook.hook) }}\n @if (\n applicationHookInUseByAnotherPost(applicationHook)\n ) {\n {{\n t.gettext('(in use by post # [[ postId ]])', {\n postId: applicationHook.contentId,\n })\n }}\n }\n </mat-option>\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No application hooks configured')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-permissions\"\n aria-labelledby=\"post-permissions-heading\">\n <h2 id=\"post-permissions-heading\">\n {{ t.gettext('Permissions') }}\n </h2>\n @if (permissionSource(); as source) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Permissions are inherited from # [[ postId ]] - [[ title ]]. Edit the source post to change them.',\n { postId: source.id, title: source.title }\n )\n }}\n </p>\n }\n @if (permissionsLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading assigned permissions...') }}\n </p>\n } @else if (permissionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPermissions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul>\n @for (permission of permissions(); track permission.id) {\n <li>\n <span>{{ permission.name }} ({{ permission.code }})</span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove permission [[ name ]]', {\n name: permission.name,\n })\n \"\n [disabled]=\"\n busy() || dialogOpen() || !!permissionSource()\n \"\n (click)=\"removePermission(permission.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No permissions assigned.') }}</li>\n }\n </ul>\n @if (permissionOptionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionOptionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || permissionOptionsLoading()\n \"\n (click)=\"loadPermissionOptions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <div class=\"permission-assignment\">\n <mat-form-field\n class=\"w-full\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Permission') }}</mat-label>\n <mat-select\n [value]=\"selectedPermissionId()\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n permissionOptionsLoading() ||\n !!permissionSource()\n \"\n (selectionChange)=\"\n selectedPermissionId.set($event.value)\n \">\n @for (\n permission of availablePermissions();\n track permission.id\n ) {\n <mat-option [value]=\"permission.id\"\n >{{ permission.name }} ({{\n permission.code\n }})</mat-option\n >\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No permissions available')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n !selectedPermissionId() ||\n !!permissionSource()\n \"\n (click)=\"addPermission()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Assign permission') }}\n </button>\n </div>\n }\n }\n @if (!permissionSource()) {\n <mat-checkbox\n [checked]=\"applyPermissionsToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyPermissionsToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply permissions to related posts') }}\n </mat-checkbox>\n }\n </section>\n </mat-card>\n </div>\n </mat-tab>\n </mat-tab-group>\n } @else if (currentPost(); as post) {\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <h1 class=\"post-title\">{{ post.title }}</h1>\n @if (post.excerpt) {\n <p class=\"post-excerpt\">{{ post.excerpt }}</p>\n }\n @if (post.type === contentType.JsonTable) {\n <taon-spreadsheet-table\n [data]=\"jsonTableData()\"\n [validationError]=\"jsonTableValidationError()\"\n [readOnly]=\"true\" />\n } @else {\n <div\n class=\"post-body\"\n [innerHTML]=\"resolvedBody()\"></div>\n }\n @if (mediaAttachment(); as media) {\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">{{ t.gettext('Loading media...') }}</p>\n } @else if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n } @else if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [poster]=\"thumbnailPreviewUrl() || null\"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support video playback.') }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support audio playback.') }}\n </audio>\n } @else if (media.type === 'attachment' && mediaPreviewUrl()) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n }\n }\n @if (mode() === 'view-clean') {\n <taon-cms-comments\n [contentId]=\"post.id\"\n class=\"w-full\" />\n }\n } @else {\n <p role=\"status\">{{ t.gettext('No post is loaded.') }}</p>\n }\n</article>\n", styles: [":host{display:block}:host ::ng-deep .NgxEditor .ProseMirror table{width:100%;border-collapse:collapse;table-layout:fixed;margin:12px 0}:host ::ng-deep .NgxEditor .ProseMirror td,:host ::ng-deep .NgxEditor .ProseMirror th{position:relative;min-width:60px;border:1px solid #aaa;padding:8px;vertical-align:top}:host ::ng-deep .NgxEditor .ProseMirror td>p,:host ::ng-deep .NgxEditor .ProseMirror th>p{margin:0}:host ::ng-deep .NgxEditor .ProseMirror .selectedCell{background:#3f51b51f}mat-expansion-panel{border-radius:0!important}.post-actions{display:flex;align-items:center;flex-wrap:wrap;gap:1rem;padding:1rem;border-bottom:1px solid rgba(136,136,136,.2)}.post-status{margin-right:auto;text-transform:capitalize;opacity:.7}.buttons{display:flex;flex-wrap:wrap;gap:.5rem}.post-article{max-width:760px;margin:0 auto;padding:clamp(1.5rem,5vw,4rem) 1.5rem;overflow-wrap:anywhere}.post-settings{display:grid;gap:1rem;margin-top:1rem}.post-setting-card{display:block;padding:20px}.post-title{margin:0 0 1.5rem;font-size:clamp(2rem,5vw,3.5rem);font-weight:700;line-height:1.15;letter-spacing:-.03em}.post-excerpt{margin:0 0 2rem;font-size:1.35rem;line-height:1.6;opacity:.7;white-space:pre-wrap}.post-body{font-family:Georgia,Times New Roman,serif;font-size:1.2rem;line-height:1.8;white-space:pre-wrap}.editable{min-height:1.2em;outline:none}.editable:focus{box-shadow:-3px 0 #8885}.editable:empty:before{content:attr(data-placeholder);opacity:.4;pointer-events:none}.post-content-editor{border:1px solid rgba(136,136,136,.3333333333);border-radius:4px;overflow:hidden}.raw-content{display:block;box-sizing:border-box;width:100%;min-height:20rem;padding:1rem;resize:vertical;border:0;color:inherit;background:transparent;font-family:monospace}ngx-editor{display:block;min-height:20rem}.content-preview{min-height:12rem;padding:1rem}.slug-field{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;margin-top:3rem;padding-top:1rem;border-top:1px solid rgba(136,136,136,.2);font-size:.875rem}.slug-field input{flex:1;min-width:160px;padding:.5rem;color:inherit;background:transparent;border:1px solid rgba(136,136,136,.3333333333);border-radius:4px}.error{padding:1rem;color:#b3261e}.post-type-selector{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.5rem}.post-type-accordion{display:block;margin-bottom:1rem}.post-type-accordion mat-panel-title{align-items:center;gap:.5rem}.media-attachment,.post-thumbnail,.post-taxonomy,.parent-post,.related-posts,.post-version-links,.application-hook,.post-permissions{margin-top:1.5rem;font-size:.875rem}.media-attachment h2,.post-thumbnail h2,.post-taxonomy h2,.parent-post h2,.related-posts h2,.post-version-links h2,.application-hook h2,.post-permissions h2{font-size:1rem}.media-attachment .thumbnail-preview,.post-thumbnail .thumbnail-preview,.post-taxonomy .thumbnail-preview,.parent-post .thumbnail-preview,.related-posts .thumbnail-preview,.post-version-links .thumbnail-preview,.application-hook .thumbnail-preview,.post-permissions .thumbnail-preview{display:block;width:min(100%,480px);max-height:320px;object-fit:contain;border-radius:12px;margin-bottom:.75rem}.media-attachment .post-taxonomy mat-form-field,.post-thumbnail .post-taxonomy mat-form-field,.post-taxonomy .post-taxonomy mat-form-field,.parent-post .post-taxonomy mat-form-field,.related-posts .post-taxonomy mat-form-field,.post-version-links .post-taxonomy mat-form-field,.application-hook .post-taxonomy mat-form-field,.post-permissions .post-taxonomy mat-form-field{display:block;margin-top:.75rem}.related-posts ul,.related-child-files ul,.post-permissions ul{padding:0;list-style:none}.related-posts mat-form-field,.related-child-files mat-form-field,.post-permissions mat-form-field{display:block;width:min(100%,28rem);margin-top:.75rem}.related-posts .application-hook,.related-child-files .application-hook,.post-permissions .application-hook{display:grid;align-items:start;gap:.75rem}.related-posts .application-hook mat-form-field,.related-child-files .application-hook mat-form-field,.post-permissions .application-hook mat-form-field{width:min(100%,28rem)}.related-posts li,.related-child-files li,.post-permissions li{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.related-child-files{margin-top:1.5rem;font-size:.875rem}.related-child-files li{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem 0;border-bottom:1px solid rgba(136,136,136,.2)}.related-post-title{flex:1}[cdkDragHandle]{cursor:grab}.related-child-file{flex:1;min-width:0}.related-child-file-heading,.related-child-file-progress,.related-child-progress{display:flex;align-items:center;gap:.75rem}.related-child-file-heading{justify-content:space-between;overflow-wrap:anywhere}.related-child-file-progress{margin-top:.5rem}.related-child-file-progress mat-progress-bar{flex:1}.related-child-progress{margin:.75rem 0}.related-child-progress mat-progress-bar{flex:1}.post-version-link{margin-top:1rem;padding-top:.75rem;border-top:1px solid rgba(136,136,136,.2)}.post-version-link h3{font-size:.95rem}video{max-width:100%;max-height:400px!important}.required-slugs-field{width:100%;margin-top:1rem}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i4$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i8.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i9.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i9.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: NgxEditorModule }, { kind: "component", type: i10.NgxEditorComponent, selector: "ngx-editor", inputs: ["editor", "outputFormat", "placeholder"], outputs: ["focusOut", "focusIn"] }, { kind: "component", type: i10.MenuComponent, selector: "ngx-editor-menu", inputs: ["toolbar", "colorPresets", "disabled", "editor", "customMenuRef", "dropdownPlacement"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i11.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: i11.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i11.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i12.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i15.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: TaonCmsEditableDirective, selector: "[taonCmsEditable]", inputs: ["taonCmsEditableFormat", "taonCmsEditableSingleLine", "taonCmsEditable"], outputs: ["taonCmsEditableChange"] }, { kind: "component", type: TaonCmsCommentsComponent, selector: "taon-cms-comments", inputs: ["contentId"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i16.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i17.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i17.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i17.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: TaonCmsPostThumbnailComponent, selector: "taon-cms-post-thumbnail", inputs: ["post"] }, { kind: "component", type: TaonSpreadsheetTableComponent, selector: "taon-spreadsheet-table", inputs: ["data", "readOnly", "disabled", "validationError"], outputs: ["dataChange", "validationErrorChange"] }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10605
11894
|
}
|
|
10606
11895
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonCmsPostComponent, decorators: [{
|
|
10607
11896
|
type: Component,
|
|
@@ -10627,13 +11916,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
10627
11916
|
MatDividerModule,
|
|
10628
11917
|
DragDropModule,
|
|
10629
11918
|
TaonCmsPostThumbnailComponent,
|
|
11919
|
+
TaonSpreadsheetTableComponent,
|
|
11920
|
+
JsonPipe,
|
|
10630
11921
|
], providers: [
|
|
10631
11922
|
TaonCmsContentApiService,
|
|
10632
11923
|
TaonCmsContentConfigService,
|
|
10633
11924
|
TaonPermissionApiService,
|
|
10634
11925
|
TaonCmsCategoryApiService,
|
|
10635
11926
|
TaonCmsTagApiService,
|
|
10636
|
-
], template: "@if (mode() !== 'view-clean') {\n <ng-template #actionButtonsTemplate>\n <div class=\"buttons\">\n @if (editing()) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !canSave()\"\n (click)=\"save()\">\n {{ mode() === 'add' ? t.gettext('Add') : t.gettext('Save') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"cancel()\">\n {{ t.gettext('Cancel') }}\n </button>\n } @else if (currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"edit()\">\n <mat-icon>edit</mat-icon> {{ t.gettext('Edit') }}\n </button>\n }\n @if (mode() !== 'add' && currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"revert()\">\n <mat-icon>history</mat-icon> {{ t.gettext('Revert') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"delete()\">\n <mat-icon>delete</mat-icon> {{ t.gettext('Delete') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"duplicate()\">\n <mat-icon>content_copy</mat-icon> {{ t.gettext('Duplicate') }}\n </button>\n }\n </div>\n </ng-template>\n <header class=\"post-actions\">\n @if (visibilitySource(); as source) {\n <span\n class=\"post-status\"\n tabindex=\"0\"\n [matTooltip]=\"visibilityInheritedTooltip()\"\n [attr.aria-label]=\"visibilityInheritedTooltip()\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"true\">\n {{ statusLabel() }}\n </button>\n </span>\n } @else {\n <button\n class=\"post-status\"\n mat-button\n type=\"button\"\n [disabled]=\"statusControlDisabled()\"\n (click)=\"selectStatus()\">\n {{ statusLabel() }}\n </button>\n }\n <ng-container [ngTemplateOutlet]=\"actionButtonsTemplate\" />\n @if (busy()) {\n <span role=\"status\">{{\n uploading()\n ? t.gettext('Uploading... ') + uploadProgress() + '%'\n : t.gettext('Saving...')\n }}</span>\n }\n </header>\n @if (error()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ error() }}\n </p>\n }\n}\n\n<article\n class=\"post-article\"\n [attr.aria-busy]=\"busy()\">\n @if (editing()) {\n <mat-tab-group animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('CONTENT')\">\n <mat-accordion class=\"post-type-accordion\">\n <mat-expansion-panel #postTypePanel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>{{ currentPostTypeIcon() }}</mat-icon>\n {{\n t.gettext('Current post type: [[ postType ]]', {\n postType: currentPostType().label,\n })\n }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <mat-radio-group\n class=\"post-type-selector\"\n [attr.aria-label]=\"t.gettext('Post type')\"\n [value]=\"draft().type\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"updateDraft('type', $event.value)\">\n @for (type of postTypes; track type.value) {\n <mat-radio-button\n [value]=\"type.value\"\n (click)=\"postTypePanel.close()\"\n >{{ type.label }}</mat-radio-button\n >\n }\n </mat-radio-group>\n </mat-expansion-panel>\n </mat-accordion>\n <div\n class=\"post-title editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post title')\"\n aria-required=\"true\"\n [attr.data-placeholder]=\"placeholderTitle()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditableSingleLine]=\"true\"\n [taonCmsEditable]=\"draft().title\"\n (taonCmsEditableChange)=\"updateDraft('title', $event)\"></div>\n @if (subtitleEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('excerpt')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <div\n class=\"post-excerpt editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post excerpt')\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholderShortIntro()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditable]=\"draft().excerpt\"\n (taonCmsEditableChange)=\"updateDraft('excerpt', $event)\"></div>\n @if (descriptionEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('body')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <section class=\"post-content-editor\">\n <mat-tab-group\n [selectedIndex]=\"contentTab()\"\n (selectedIndexChange)=\"setContentTab($event)\"\n [preserveContent]=\"true\"\n animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('Editor')\">\n @if (contentEditor(); as editor) {\n <ng-template #customMenu>\n <button\n type=\"button\"\n class=\"NgxEditor__MenuItem\"\n title=\"Table\"\n aria-label=\"Table actions\"\n [disabled]=\"busy() || dialogOpen()\"\n [matMenuTriggerFor]=\"tableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\"\n (mousedown)=\"$event.preventDefault()\">\n <mat-icon>table_chart</mat-icon>\n </button>\n\n <mat-menu #tableMenu=\"matMenu\">\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"insertTable($event)\">\n <mat-icon>add</mat-icon>\n <span>Insert table</span>\n </button>\n\n <mat-divider />\n\n @for (item of tableCommands; track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"!canRunTableCommand(item.command)\"\n (click)=\"runTableCommand($event, item.command)\">\n {{ item.label }}\n </button>\n }\n </mat-menu>\n </ng-template>\n\n <ngx-editor-menu\n [editor]=\"editor\"\n [customMenuRef]=\"customMenu\"\n [toolbar]=\"contentToolbar\"\n [disabled]=\"busy() || dialogOpen()\" />\n <ngx-editor\n [editor]=\"editor\"\n [placeholder]=\"placeholderTellYourStory()\" />\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Raw')\">\n <textarea\n #rawContent\n class=\"raw-content\"\n [attr.aria-label]=\"t.gettext('Post body HTML')\"\n [value]=\"draft().body\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateRawContent($event)\"\n spellcheck=\"false\"></textarea>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Preview')\">\n <div\n class=\"post-body content-preview\"\n [innerHTML]=\"resolvedBody()\"></div>\n </mat-tab>\n </mat-tab-group>\n @if (contentTab() !== 2) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !contentEditor()\"\n [matMenuTriggerFor]=\"variableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\">\n {{ t.gettext('Insert variable') }}\n </button>\n }\n <mat-menu #variableMenu=\"matMenu\">\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"siteVariableMenu\">\n {{ t.gettext('Site') }}\n </button>\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"postVariableMenu\">\n {{ t.gettext('Post') }}\n </button>\n </mat-menu>\n <mat-menu #siteVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('frontendUrl')\">\n {{ t.gettext('Frontend URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('site.name')\">\n {{ t.gettext('Site name') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('currentYear')\">\n {{ t.gettext('Current year') }}\n </button>\n </mat-menu>\n <mat-menu #postVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('post.title')\">\n {{ t.gettext('Post title') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.url')\">\n {{ t.gettext('Post URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.id')\">\n {{ t.gettext('Post ID') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.thumbnailUrl')\">\n {{ t.gettext('Post Thumbnail URL') }}\n </button>\n </mat-menu>\n </section>\n <label class=\"slug-field\">\n <span>{{ t.gettext('URL slug') }}</span>\n <input\n [attr.aria-label]=\"t.gettext('URL slug')\"\n [value]=\"draft().slug\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateSlug($event)\"\n [placeholder]=\"t.gettext('my-post')\" />\n </label>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('SETTINGS')\">\n <div class=\"post-settings\">\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-child-files\"\n aria-labelledby=\"related-child-files-heading\">\n <h2 id=\"related-child-files-heading\">\n {{ t.gettext('Create related children from files') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Please save the post before creating children.'\n )\n }}\n </p>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Child title prefix') }}</mat-label>\n <input\n matInput\n [value]=\"childTitlePrefix()\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (input)=\"updateChildTitlePrefix($event)\" />\n </mat-form-field>\n <input\n #relatedChildFilesInput\n hidden\n type=\"file\"\n multiple\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (change)=\"relatedChildFilesSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"relatedChildFilesInput.click()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Choose files') }}\n </button>\n @if (childBatchError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ childBatchError() }}\n </p>\n }\n @if (childFiles().length) {\n <div class=\"related-child-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"childOverallProgress()\"\n [attr.aria-label]=\"\n t.gettext('Overall child creation progress')\n \" />\n <span>\n {{ childOverallProgress() }}%\n {{ t.gettext('overall progress') }}\n </span>\n </div>\n <ul>\n @for (file of childFiles(); track file.id; let index =\n $index) {\n <li>\n <div class=\"related-child-file\">\n <div class=\"related-child-file-heading\">\n <span>{{ file.file.name }}</span>\n <span>{{ childTitlePreviews()[index] }}</span>\n </div>\n <div class=\"related-child-file-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"file.progress\"\n [attr.aria-label]=\"\n t.gettext('Progress for [[ fileName ]]', {\n fileName: file.file.name,\n })\n \" />\n <span>{{ file.progress }}%</span>\n </div>\n @if (file.status === 'processing') {\n <p role=\"status\">{{ t.gettext('Processing...') }}</p>\n } @else if (file.status === 'success') {\n <p role=\"status\">{{ t.gettext('Child created.') }}</p>\n @if (file.error) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error }}\n </p>\n }\n } @else if (file.status === 'error') {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error }}\n </p>\n } @else {\n <p>{{ t.gettext('Waiting to upload.') }}</p>\n }\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove file [[ fileName ]]', {\n fileName: file.file.name,\n })\n \"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"removeRelatedChildFile(file.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!canUploadRelatedChildren()\"\n (click)=\"uploadAndCreateChildren()\">\n {{ t.gettext('Upload and Create Children') }}\n </button>\n </section>\n </mat-card>\n @if (mediaAttachment(); as media) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"media-attachment\"\n aria-labelledby=\"media-attachment-heading\">\n <h2 id=\"media-attachment-heading\">{{ media.heading }}</h2>\n @if (uploading()) {\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadProgress()\"\n [attr.aria-label]=\"t.gettext('Media upload progress')\" />\n }\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading media preview...') }}\n </p>\n }\n @if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n }\n @if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support video playback.'\n )\n }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support audio playback.'\n )\n }}\n </audio>\n } @else if (\n media.type === 'attachment' && mediaPreviewUrl()\n ) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ t.gettext('Replace') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeMedia()\">\n {{ t.gettext('Remove') }}\n </button>\n } @else {\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ media.uploadLabel }}\n </button>\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-thumbnail\"\n aria-labelledby=\"thumbnail-heading\">\n <h2 id=\"thumbnail-heading\">{{ t.gettext('Thumbnail') }}</h2>\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n } @else {\n <p>{{ t.gettext('No thumbnail selected.') }}</p>\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <input\n #thumbnailInput\n hidden\n type=\"file\"\n accept=\"image/*\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"thumbnailSelected($event)\" />\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"thumbnailInput.click()\">\n {{ t.gettext('Upload Thumbnail') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"applyDefaultThumbnail()\">\n {{ t.gettext('Apply Default Placeholder') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-taxonomy\"\n aria-labelledby=\"category-heading\">\n <h2 id=\"category-heading\">{{ t.gettext('Category / Tags') }}</h2>\n @if (taxonomyLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading categories and tags...') }}\n </p>\n }\n @if (taxonomyError()) {\n <p role=\"alert\">{{ taxonomyError() }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy()\"\n (click)=\"loadTaxonomy()\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Category') }}</mat-label>\n <mat-select\n [value]=\"draft().categoryId\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"updateDraft('categoryId', $event.value)\">\n <mat-option [value]=\"null\">{{\n t.gettext('None')\n }}</mat-option>\n @for (category of categories(); track category.id) {\n <mat-option [value]=\"category.id\">{{\n category.name\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <h3>{{ t.gettext('Tags') }}</h3>\n <mat-chip-set [attr.aria-label]=\"t.gettext('Selected tags')\">\n @for (tag of selectedTags(); track tag.id) {\n <mat-chip\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (removed)=\"removeTag(tag.id)\">\n {{ tag.name }}\n <button\n matChipRemove\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n [attr.aria-label]=\"\n t.gettext('Remove tag [[ name ]]', { name: tag.name })\n \">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip>\n }\n </mat-chip-set>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Search tags') }}</mat-label>\n <input\n matInput\n [value]=\"tagSearch()\"\n (input)=\"filterTags($event)\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Add tag') }}</mat-label>\n <mat-select\n [value]=\"null\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"addTag($event.value)\">\n @for (tag of filteredTags(); track tag.id) {\n <mat-option [value]=\"tag.id\">{{ tag.name }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('category')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add category') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('tag')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add tag') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"parent-post\"\n aria-labelledby=\"parent-post-heading\">\n <h2 id=\"parent-post-heading\">{{ t.gettext('Parent Post') }}</h2>\n @if (parentPostLoading()) {\n <p role=\"status\">{{ t.gettext('Loading parent post...') }}</p>\n } @else if (parentPostError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ parentPostError() }}\n </p>\n } @else if (parentPost(); as parent) {\n <p>\n <taon-cms-post-thumbnail [post]=\"parent\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, parent)\">\n {{ parent.title }}\n </a>\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeParentPost()\">\n <mat-icon>link_off</mat-icon>\n {{ t.gettext('Remove from parent') }}\n </button>\n } @else {\n <p>{{ t.gettext('No parent added') }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !currentPost()\"\n (click)=\"addAsChildOf()\">\n <mat-icon>account_tree</mat-icon>\n @if (currentPost()) {\n {{ t.gettext('Add as a child') }}\n } @else {\n {{\n t.gettext(\n 'Add as a child (disabled \u2014 please save the post first)'\n )\n }}\n }\n </button>\n }\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-posts\"\n aria-labelledby=\"related-posts-heading\">\n <h2 id=\"related-posts-heading\">\n {{ t.gettext('Related posts') }}\n </h2>\n @if (relatedPostsLoading()) {\n <p role=\"status\">{{ t.gettext('Loading related posts...') }}</p>\n } @else if (relatedPostsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ relatedPostsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadRelatedPosts()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul\n cdkDropList\n [cdkDropListData]=\"relatedPosts()\"\n [cdkDropListDisabled]=\"busy() || dialogOpen()\"\n (cdkDropListDropped)=\"reorderRelatedPosts($event)\">\n @for (post of relatedPosts(); track post.id) {\n <li cdkDrag [cdkDragData]=\"post\">\n <button\n mat-icon-button\n cdkDragHandle\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n [attr.aria-label]=\"\n t.gettext('Reorder related post [[ title ]]', {\n title: post.title,\n })\n \">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n <taon-cms-post-thumbnail [post]=\"post\" />\n <span class=\"related-post-title\">\n #{{ post.id }} -\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, post)\">\n {{ post.title }}\n </a>\n </span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove related post [[ title ]]', {\n title: post.title,\n })\n \"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeRelatedPost(post.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No related posts yet.') }}</li>\n }\n </ul>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"addRelatedPost()\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add related post') }}\n </button>\n }\n <mat-checkbox\n [checked]=\"applyVisibilityToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyVisibilityToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply visibility to related posts') }}\n </mat-checkbox>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-version-links\"\n aria-labelledby=\"post-version-links-heading\">\n <h2 id=\"post-version-links-heading\">\n {{ t.gettext('Post versions') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext('Save the post first to manage version links.')\n }}\n </p>\n }\n @if (postVersionLinksLoading()) {\n <p role=\"status\">{{ t.gettext('Loading post versions...') }}</p>\n }\n @if (postVersionLinksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ postVersionLinksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPostVersionLinks(currentPost())\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Public post version') }}</h3>\n @if (postVersionLinks().publicVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPublicVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('public')\">\n {{ t.gettext('Replace public version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('public')\">\n {{ t.gettext('Remove public version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No public post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPublicVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPublicVersion()\n \"\n (click)=\"selectVersionPost('public')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add public post version') }}\n </button>\n }\n </div>\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Private post version') }}</h3>\n @if (postVersionLinks().privateVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPrivateVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('private')\">\n {{ t.gettext('Replace private version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('private')\">\n {{ t.gettext('Remove private version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No private post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPrivateVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPrivateVersion()\n \"\n (click)=\"selectVersionPost('private')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add private post version') }}\n </button>\n }\n </div>\n </section>\n </mat-card>\n\n @if (!mediaAttachment()) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"application-hook\"\n aria-labelledby=\"application-hook-heading\">\n <h2 id=\"application-hook-heading\">\n {{ t.gettext('Hooks') }}\n </h2>\n @if (applicationHooksLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading application hooks...') }}\n </p>\n } @else if (applicationHooksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ applicationHooksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadApplicationHooks()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <mat-checkbox\n [checked]=\"useAsApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"setUseAsApplicationHook($event.checked)\">\n {{ t.gettext('Use as application hook') }}\n </mat-checkbox>\n @if (useAsApplicationHook()) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Application Hook') }}</mat-label>\n <mat-select\n [value]=\"selectedApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"\n selectedApplicationHook.set($event.value)\n \">\n @for (\n applicationHook of applicationHooks();\n track applicationHook.id\n ) {\n <mat-option\n [value]=\"applicationHook.hook\"\n [disabled]=\"\n applicationHookInUseByAnotherPost(applicationHook)\n \">\n {{ applicationHookLabel(applicationHook.hook) }}\n @if (\n applicationHookInUseByAnotherPost(applicationHook)\n ) {\n {{\n t.gettext('(in use by post # [[ postId ]])', {\n postId: applicationHook.contentId,\n })\n }}\n }\n </mat-option>\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No application hooks configured')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-permissions\"\n aria-labelledby=\"post-permissions-heading\">\n <h2 id=\"post-permissions-heading\">\n {{ t.gettext('Permissions') }}\n </h2>\n @if (permissionSource(); as source) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Permissions are inherited from # [[ postId ]] - [[ title ]]. Edit the source post to change them.',\n { postId: source.id, title: source.title }\n )\n }}\n </p>\n }\n @if (permissionsLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading assigned permissions...') }}\n </p>\n } @else if (permissionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPermissions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul>\n @for (permission of permissions(); track permission.id) {\n <li>\n <span>{{ permission.name }} ({{ permission.code }})</span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove permission [[ name ]]', {\n name: permission.name,\n })\n \"\n [disabled]=\"\n busy() || dialogOpen() || !!permissionSource()\n \"\n (click)=\"removePermission(permission.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No permissions assigned.') }}</li>\n }\n </ul>\n @if (permissionOptionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionOptionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || permissionOptionsLoading()\n \"\n (click)=\"loadPermissionOptions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <div class=\"permission-assignment\">\n <mat-form-field\n class=\"w-full\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Permission') }}</mat-label>\n <mat-select\n [value]=\"selectedPermissionId()\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n permissionOptionsLoading() ||\n !!permissionSource()\n \"\n (selectionChange)=\"\n selectedPermissionId.set($event.value)\n \">\n @for (\n permission of availablePermissions();\n track permission.id\n ) {\n <mat-option [value]=\"permission.id\"\n >{{ permission.name }} ({{\n permission.code\n }})</mat-option\n >\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No permissions available')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n !selectedPermissionId() ||\n !!permissionSource()\n \"\n (click)=\"addPermission()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Assign permission') }}\n </button>\n </div>\n }\n }\n @if (!permissionSource()) {\n <mat-checkbox\n [checked]=\"applyPermissionsToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyPermissionsToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply permissions to related posts') }}\n </mat-checkbox>\n }\n </section>\n </mat-card>\n </div>\n </mat-tab>\n </mat-tab-group>\n } @else if (currentPost(); as post) {\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <h1 class=\"post-title\">{{ post.title }}</h1>\n @if (post.excerpt) {\n <p class=\"post-excerpt\">{{ post.excerpt }}</p>\n }\n <div\n class=\"post-body\"\n [innerHTML]=\"resolvedBody()\"></div>\n @if (mediaAttachment(); as media) {\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">{{ t.gettext('Loading media...') }}</p>\n } @else if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n } @else if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [poster]=\"thumbnailPreviewUrl() || null\"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support video playback.') }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support audio playback.') }}\n </audio>\n } @else if (media.type === 'attachment' && mediaPreviewUrl()) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n }\n }\n @if (mode() === 'view-clean') {\n <taon-cms-comments\n [contentId]=\"post.id\"\n class=\"w-full\" />\n }\n } @else {\n <p role=\"status\">{{ t.gettext('No post is loaded.') }}</p>\n }\n</article>\n", styles: [":host{display:block}:host ::ng-deep .NgxEditor .ProseMirror table{width:100%;border-collapse:collapse;table-layout:fixed;margin:12px 0}:host ::ng-deep .NgxEditor .ProseMirror td,:host ::ng-deep .NgxEditor .ProseMirror th{position:relative;min-width:60px;border:1px solid #aaa;padding:8px;vertical-align:top}:host ::ng-deep .NgxEditor .ProseMirror td>p,:host ::ng-deep .NgxEditor .ProseMirror th>p{margin:0}:host ::ng-deep .NgxEditor .ProseMirror .selectedCell{background:#3f51b51f}mat-expansion-panel{border-radius:0!important}.post-actions{display:flex;align-items:center;flex-wrap:wrap;gap:1rem;padding:1rem;border-bottom:1px solid rgba(136,136,136,.2)}.post-status{margin-right:auto;text-transform:capitalize;opacity:.7}.buttons{display:flex;flex-wrap:wrap;gap:.5rem}.post-article{max-width:760px;margin:0 auto;padding:clamp(1.5rem,5vw,4rem) 1.5rem;overflow-wrap:anywhere}.post-settings{display:grid;gap:1rem;margin-top:1rem}.post-setting-card{display:block;padding:20px}.post-title{margin:0 0 1.5rem;font-size:clamp(2rem,5vw,3.5rem);font-weight:700;line-height:1.15;letter-spacing:-.03em}.post-excerpt{margin:0 0 2rem;font-size:1.35rem;line-height:1.6;opacity:.7;white-space:pre-wrap}.post-body{font-family:Georgia,Times New Roman,serif;font-size:1.2rem;line-height:1.8;white-space:pre-wrap}.editable{min-height:1.2em;outline:none}.editable:focus{box-shadow:-3px 0 #8885}.editable:empty:before{content:attr(data-placeholder);opacity:.4;pointer-events:none}.post-content-editor{border:1px solid rgba(136,136,136,.3333333333);border-radius:4px;overflow:hidden}.raw-content{display:block;box-sizing:border-box;width:100%;min-height:20rem;padding:1rem;resize:vertical;border:0;color:inherit;background:transparent;font-family:monospace}ngx-editor{display:block;min-height:20rem}.content-preview{min-height:12rem;padding:1rem}.slug-field{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;margin-top:3rem;padding-top:1rem;border-top:1px solid rgba(136,136,136,.2);font-size:.875rem}.slug-field input{flex:1;min-width:160px;padding:.5rem;color:inherit;background:transparent;border:1px solid rgba(136,136,136,.3333333333);border-radius:4px}.error{padding:1rem;color:#b3261e}.post-type-selector{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.5rem}.post-type-accordion{display:block;margin-bottom:1rem}.post-type-accordion mat-panel-title{align-items:center;gap:.5rem}.media-attachment,.post-thumbnail,.post-taxonomy,.parent-post,.related-posts,.post-version-links,.application-hook,.post-permissions{margin-top:1.5rem;font-size:.875rem}.media-attachment h2,.post-thumbnail h2,.post-taxonomy h2,.parent-post h2,.related-posts h2,.post-version-links h2,.application-hook h2,.post-permissions h2{font-size:1rem}.media-attachment .thumbnail-preview,.post-thumbnail .thumbnail-preview,.post-taxonomy .thumbnail-preview,.parent-post .thumbnail-preview,.related-posts .thumbnail-preview,.post-version-links .thumbnail-preview,.application-hook .thumbnail-preview,.post-permissions .thumbnail-preview{display:block;width:min(100%,480px);max-height:320px;object-fit:contain;border-radius:12px;margin-bottom:.75rem}.media-attachment .post-taxonomy mat-form-field,.post-thumbnail .post-taxonomy mat-form-field,.post-taxonomy .post-taxonomy mat-form-field,.parent-post .post-taxonomy mat-form-field,.related-posts .post-taxonomy mat-form-field,.post-version-links .post-taxonomy mat-form-field,.application-hook .post-taxonomy mat-form-field,.post-permissions .post-taxonomy mat-form-field{display:block;margin-top:.75rem}.related-posts ul,.related-child-files ul,.post-permissions ul{padding:0;list-style:none}.related-posts mat-form-field,.related-child-files mat-form-field,.post-permissions mat-form-field{display:block;width:min(100%,28rem);margin-top:.75rem}.related-posts .application-hook,.related-child-files .application-hook,.post-permissions .application-hook{display:grid;align-items:start;gap:.75rem}.related-posts .application-hook mat-form-field,.related-child-files .application-hook mat-form-field,.post-permissions .application-hook mat-form-field{width:min(100%,28rem)}.related-posts li,.related-child-files li,.post-permissions li{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.related-child-files{margin-top:1.5rem;font-size:.875rem}.related-child-files li{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem 0;border-bottom:1px solid rgba(136,136,136,.2)}.related-post-title{flex:1}[cdkDragHandle]{cursor:grab}.related-child-file{flex:1;min-width:0}.related-child-file-heading,.related-child-file-progress,.related-child-progress{display:flex;align-items:center;gap:.75rem}.related-child-file-heading{justify-content:space-between;overflow-wrap:anywhere}.related-child-file-progress{margin-top:.5rem}.related-child-file-progress mat-progress-bar{flex:1}.related-child-progress{margin:.75rem 0}.related-child-progress mat-progress-bar{flex:1}.post-version-link{margin-top:1rem;padding-top:.75rem;border-top:1px solid rgba(136,136,136,.2)}.post-version-link h3{font-size:.95rem}video{max-width:100%;max-height:400px!important}\n"] }]
|
|
11927
|
+
], template: "@if (mode() !== 'view-clean') {\n <ng-template #actionButtonsTemplate>\n <div class=\"buttons\">\n @if (editing()) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !canSave()\"\n (click)=\"save()\">\n {{ mode() === 'add' ? t.gettext('Add') : t.gettext('Save') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"cancel()\">\n {{ t.gettext('Cancel') }}\n </button>\n } @else if (currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"edit()\">\n <mat-icon>edit</mat-icon> {{ t.gettext('Edit') }}\n </button>\n }\n @if (mode() !== 'add' && currentPost()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"revert()\">\n <mat-icon>history</mat-icon> {{ t.gettext('Revert') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"delete()\">\n <mat-icon>delete</mat-icon> {{ t.gettext('Delete') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"duplicate()\">\n <mat-icon>content_copy</mat-icon> {{ t.gettext('Duplicate') }}\n </button>\n }\n </div>\n </ng-template>\n <header class=\"post-actions\">\n @if (visibilitySource(); as source) {\n <span\n class=\"post-status\"\n tabindex=\"0\"\n [matTooltip]=\"visibilityInheritedTooltip()\"\n [attr.aria-label]=\"visibilityInheritedTooltip()\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"true\">\n {{ statusLabel() }}\n </button>\n </span>\n } @else {\n <button\n class=\"post-status\"\n mat-button\n type=\"button\"\n [disabled]=\"statusControlDisabled()\"\n (click)=\"selectStatus()\">\n {{ statusLabel() }}\n </button>\n }\n <ng-container [ngTemplateOutlet]=\"actionButtonsTemplate\" />\n @if (busy()) {\n <span role=\"status\">{{\n uploading()\n ? t.gettext('Uploading... ') + uploadProgress() + '%'\n : t.gettext('Saving...')\n }}</span>\n }\n </header>\n @if (error()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ error() }}\n </p>\n }\n}\n\n<article\n class=\"post-article\"\n [attr.aria-busy]=\"busy()\">\n @if (editing()) {\n <mat-tab-group animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('CONTENT')\">\n <mat-accordion class=\"post-type-accordion\">\n <mat-expansion-panel #postTypePanel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>{{ currentPostTypeIcon() }}</mat-icon>\n {{\n t.gettext('Current post type: [[ postType ]]', {\n postType: currentPostType().label,\n })\n }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <mat-radio-group\n class=\"post-type-selector\"\n [attr.aria-label]=\"t.gettext('Post type')\"\n [value]=\"draft().type\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"updateDraft('type', $event.value)\">\n @for (type of postTypes; track type.value) {\n <mat-radio-button\n [value]=\"type.value\"\n (click)=\"postTypePanel.close()\"\n >{{ type.label }}</mat-radio-button\n >\n }\n </mat-radio-group>\n </mat-expansion-panel>\n </mat-accordion>\n <div\n class=\"post-title editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post title')\"\n aria-required=\"true\"\n [attr.data-placeholder]=\"placeholderTitle()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditableSingleLine]=\"true\"\n [taonCmsEditable]=\"draft().title\"\n (taonCmsEditableChange)=\"updateDraft('title', $event)\"></div>\n @if (subtitleEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('excerpt')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <div\n class=\"post-excerpt editable\"\n contenteditable=\"true\"\n role=\"textbox\"\n [attr.aria-label]=\"t.gettext('Post excerpt')\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholderShortIntro()\"\n [attr.contenteditable]=\"!busy() && !dialogOpen()\"\n [taonCmsEditable]=\"draft().excerpt\"\n (taonCmsEditableChange)=\"updateDraft('excerpt', $event)\"></div>\n @if (descriptionEmpty()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"generateLoremIpsum('body')\">\n {{ t.gettext('Generate Lorem Ipsum') }}\n </button>\n }\n <section class=\"post-content-editor\">\n @if (draft().type === contentType.JsonTable) {\n <taon-spreadsheet-table\n [data]=\"jsonTableData()\"\n [disabled]=\"busy() || dialogOpen()\"\n [validationError]=\"jsonTableValidationError()\"\n (dataChange)=\"setJsonTableData($event)\"\n (validationErrorChange)=\"jsonTableValidationError.set($event)\" />\n } @else {\n <mat-tab-group\n [selectedIndex]=\"contentTab()\"\n (selectedIndexChange)=\"setContentTab($event)\"\n [preserveContent]=\"true\"\n animationDuration=\"0ms\">\n <mat-tab [label]=\"t.gettext('Editor')\">\n @if (contentEditor(); as editor) {\n <ng-template #customMenu>\n <button\n type=\"button\"\n class=\"NgxEditor__MenuItem\"\n title=\"Table\"\n aria-label=\"Table actions\"\n [disabled]=\"busy() || dialogOpen()\"\n [matMenuTriggerFor]=\"tableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\"\n (mousedown)=\"$event.preventDefault()\">\n <mat-icon>table_chart</mat-icon>\n </button>\n\n <mat-menu #tableMenu=\"matMenu\">\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"insertTable($event)\">\n <mat-icon>add</mat-icon>\n <span>Insert table</span>\n </button>\n\n <mat-divider />\n\n @for (item of tableCommands; track item.label) {\n <button\n mat-menu-item\n type=\"button\"\n [disabled]=\"!canRunTableCommand(item.command)\"\n (click)=\"runTableCommand($event, item.command)\">\n {{ item.label }}\n </button>\n }\n </mat-menu>\n </ng-template>\n\n <ngx-editor-menu\n [editor]=\"editor\"\n [customMenuRef]=\"customMenu\"\n [toolbar]=\"contentToolbar\"\n [disabled]=\"busy() || dialogOpen()\" />\n <ngx-editor\n [editor]=\"editor\"\n [placeholder]=\"placeholderTellYourStory()\" />\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Raw')\">\n <textarea\n #rawContent\n class=\"raw-content\"\n [attr.aria-label]=\"t.gettext('Post body HTML')\"\n [value]=\"draft().body\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateRawContent($event)\"\n spellcheck=\"false\"></textarea>\n </mat-tab>\n <mat-tab [label]=\"t.gettext('Preview')\">\n <div\n class=\"post-body content-preview\"\n [innerHTML]=\"resolvedBody()\"></div>\n </mat-tab>\n </mat-tab-group>\n @if (contentTab() !== 2) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !contentEditor()\"\n [matMenuTriggerFor]=\"variableMenu\"\n [matMenuTriggerRestoreFocus]=\"false\">\n {{ t.gettext('Insert variable') }}\n </button>\n }\n <mat-menu #variableMenu=\"matMenu\">\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"siteVariableMenu\">\n {{ t.gettext('Site') }}\n </button>\n <button\n mat-menu-item\n [matMenuTriggerFor]=\"postVariableMenu\">\n {{ t.gettext('Post') }}\n </button>\n </mat-menu>\n <mat-menu #siteVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('frontendUrl')\">\n {{ t.gettext('Frontend URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('site.name')\">\n {{ t.gettext('Site name') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('currentYear')\">\n {{ t.gettext('Current year') }}\n </button>\n </mat-menu>\n <mat-menu #postVariableMenu=\"matMenu\">\n <button\n mat-menu-item\n (click)=\"insertVariable('post.title')\">\n {{ t.gettext('Post title') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.url')\">\n {{ t.gettext('Post URL') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.id')\">\n {{ t.gettext('Post ID') }}\n </button>\n <button\n mat-menu-item\n (click)=\"insertVariable('post.thumbnailUrl')\">\n {{ t.gettext('Post Thumbnail URL') }}\n </button>\n </mat-menu>\n }\n </section>\n <label class=\"slug-field\">\n <span>{{ t.gettext('URL slug') }}</span>\n <input\n [attr.aria-label]=\"t.gettext('URL slug')\"\n [value]=\"draft().slug\"\n [disabled]=\"busy() || dialogOpen()\"\n (input)=\"updateSlug($event)\"\n [placeholder]=\"t.gettext('my-post')\" />\n </label>\n @if (requiredSlugs.length) {\n <mat-form-field\n class=\"required-slugs-field\"\n appearance=\"outline\">\n <mat-label>{{ t.gettext('Required Slugs') }}</mat-label>\n <mat-select\n [value]=\"selectedRequiredSlug()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"selectRequiredSlug($event.value)\">\n @for (slug of requiredSlugs; track slug) {\n <mat-option [value]=\"slug\">{{ slug }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </mat-tab>\n <mat-tab [label]=\"t.gettext('SETTINGS')\">\n <div class=\"post-settings\">\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-child-files\"\n aria-labelledby=\"related-child-files-heading\">\n <h2 id=\"related-child-files-heading\">\n {{ t.gettext('Create related children from files') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Please save the post before creating children.'\n )\n }}\n </p>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Child title prefix') }}</mat-label>\n <input\n matInput\n [value]=\"childTitlePrefix()\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (input)=\"updateChildTitlePrefix($event)\" />\n </mat-form-field>\n <input\n #relatedChildFilesInput\n hidden\n type=\"file\"\n multiple\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (change)=\"relatedChildFilesSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"relatedChildFilesInput.click()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Choose files') }}\n </button>\n @if (childBatchError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ childBatchError() }}\n </p>\n }\n @if (childFiles().length) {\n <div class=\"related-child-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"childOverallProgress()\"\n [attr.aria-label]=\"\n t.gettext('Overall child creation progress')\n \" />\n <span>\n {{ childOverallProgress() }}%\n {{ t.gettext('overall progress') }}\n </span>\n </div>\n <ul>\n @for (file of childFiles(); track file.id; let index =\n $index) {\n <li>\n <div class=\"related-child-file\">\n <div class=\"related-child-file-heading\">\n <span>{{ file.file.name }}</span>\n <span>{{ childTitlePreviews()[index] }}</span>\n </div>\n <div class=\"related-child-file-progress\">\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"file.progress\"\n [attr.aria-label]=\"\n t.gettext('Progress for [[ fileName ]]', {\n fileName: file.file.name,\n })\n \" />\n <span>{{ file.progress }}%</span>\n </div>\n @if (file.status === 'processing') {\n <p role=\"status\">{{ t.gettext('Processing...') }}</p>\n } @else if (file.status === 'success') {\n <p role=\"status\">{{ t.gettext('Child created.') }}</p>\n @if (file.error) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error | json }}\n </p>\n }\n } @else if (file.status === 'error') {\n <p\n class=\"error\"\n role=\"alert\">\n {{ file.error | json }}\n </p>\n } @else {\n <p>{{ t.gettext('Waiting to upload.') }}</p>\n }\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove file [[ fileName ]]', {\n fileName: file.file.name,\n })\n \"\n [disabled]=\"!currentPost() || busy() || dialogOpen()\"\n (click)=\"removeRelatedChildFile(file.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n }\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!canUploadRelatedChildren()\"\n (click)=\"uploadAndCreateChildren()\">\n {{ t.gettext('Upload and Create Children') }}\n </button>\n @if (childUploadRunning()) {\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelRelatedChildUploads()\">\n {{ t.gettext('Cancel upload') }}\n </button>\n }\n </section>\n </mat-card>\n @if (mediaAttachment(); as media) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"media-attachment\"\n aria-labelledby=\"media-attachment-heading\">\n <h2 id=\"media-attachment-heading\">{{ media.heading }}</h2>\n @if (uploading()) {\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadProgress()\"\n [attr.aria-label]=\"t.gettext('Media upload progress')\" />\n <button\n mat-button\n type=\"button\"\n (click)=\"cancelMediaUpload()\">\n {{ t.gettext('Cancel upload') }}\n </button>\n }\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading media preview...') }}\n </p>\n }\n @if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n }\n @if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support video playback.'\n )\n }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [src]=\"mediaPreviewUrl()\">\n {{\n t.gettext(\n 'Your browser does not support audio playback.'\n )\n }}\n </audio>\n } @else if (\n media.type === 'attachment' && mediaPreviewUrl()\n ) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ t.gettext('Replace') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeMedia()\">\n {{ t.gettext('Remove') }}\n </button>\n } @else {\n <input\n #mediaInput\n hidden\n type=\"file\"\n [accept]=\"media.accept\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"fileSelected($event)\" />\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"mediaInput.click()\">\n {{ media.uploadLabel }}\n </button>\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-thumbnail\"\n aria-labelledby=\"thumbnail-heading\">\n <h2 id=\"thumbnail-heading\">{{ t.gettext('Thumbnail') }}</h2>\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n } @else {\n <p>{{ t.gettext('No thumbnail selected.') }}</p>\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <input\n #thumbnailInput\n hidden\n type=\"file\"\n accept=\"image/*\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"thumbnailSelected($event)\" />\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"thumbnailInput.click()\">\n {{ t.gettext('Upload Thumbnail') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"applyDefaultThumbnail()\">\n {{ t.gettext('Apply Default Placeholder') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-taxonomy\"\n aria-labelledby=\"category-heading\">\n <h2 id=\"category-heading\">{{ t.gettext('Category / Tags') }}</h2>\n @if (taxonomyLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading categories and tags...') }}\n </p>\n }\n @if (taxonomyError()) {\n <p role=\"alert\">{{ taxonomyError() }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy()\"\n (click)=\"loadTaxonomy()\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Category') }}</mat-label>\n <mat-select\n [value]=\"draft().categoryId\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"updateDraft('categoryId', $event.value)\">\n <mat-option [value]=\"null\">{{\n t.gettext('None')\n }}</mat-option>\n @for (category of categories(); track category.id) {\n <mat-option [value]=\"category.id\">{{\n category.name\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <h3>{{ t.gettext('Tags') }}</h3>\n <mat-chip-set [attr.aria-label]=\"t.gettext('Selected tags')\">\n @for (tag of selectedTags(); track tag.id) {\n <mat-chip\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (removed)=\"removeTag(tag.id)\">\n {{ tag.name }}\n <button\n matChipRemove\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n [attr.aria-label]=\"\n t.gettext('Remove tag [[ name ]]', { name: tag.name })\n \">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip>\n }\n </mat-chip-set>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Search tags') }}</mat-label>\n <input\n matInput\n [value]=\"tagSearch()\"\n (input)=\"filterTags($event)\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ t.gettext('Add tag') }}</mat-label>\n <mat-select\n [value]=\"null\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n taxonomyLoading() ||\n !!taxonomyError()\n \"\n (selectionChange)=\"addTag($event.value)\">\n @for (tag of filteredTags(); track tag.id) {\n <mat-option [value]=\"tag.id\">{{ tag.name }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"buttons\">\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('category')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add category') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || taxonomyLoading()\"\n (click)=\"createTaxonomy('tag')\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add tag') }}\n </button>\n </div>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"parent-post\"\n aria-labelledby=\"parent-post-heading\">\n <h2 id=\"parent-post-heading\">{{ t.gettext('Parent Post') }}</h2>\n @if (parentPostLoading()) {\n <p role=\"status\">{{ t.gettext('Loading parent post...') }}</p>\n } @else if (parentPostError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ parentPostError() }}\n </p>\n } @else if (parentPost(); as parent) {\n <p>\n <taon-cms-post-thumbnail [post]=\"parent\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, parent)\">\n {{ parent.title }}\n </a>\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeParentPost()\">\n <mat-icon>link_off</mat-icon>\n {{ t.gettext('Remove from parent') }}\n </button>\n } @else {\n <p>{{ t.gettext('No parent added') }}</p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen() || !currentPost()\"\n (click)=\"addAsChildOf()\">\n <mat-icon>account_tree</mat-icon>\n @if (currentPost()) {\n {{ t.gettext('Add as a child') }}\n } @else {\n {{\n t.gettext(\n 'Add as a child (disabled \u2014 please save the post first)'\n )\n }}\n }\n </button>\n }\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"related-posts\"\n aria-labelledby=\"related-posts-heading\">\n <h2 id=\"related-posts-heading\">\n {{ t.gettext('Related posts') }}\n </h2>\n @if (relatedPostsLoading()) {\n <p role=\"status\">{{ t.gettext('Loading related posts...') }}</p>\n } @else if (relatedPostsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ relatedPostsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadRelatedPosts()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul\n cdkDropList\n [cdkDropListData]=\"relatedPosts()\"\n [cdkDropListDisabled]=\"busy() || dialogOpen()\"\n (cdkDropListDropped)=\"reorderRelatedPosts($event)\">\n @for (post of relatedPosts(); track post.id) {\n <li cdkDrag [cdkDragData]=\"post\">\n <button\n mat-icon-button\n cdkDragHandle\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n [attr.aria-label]=\"\n t.gettext('Reorder related post [[ title ]]', {\n title: post.title,\n })\n \">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n <taon-cms-post-thumbnail [post]=\"post\" />\n <span class=\"related-post-title\">\n #{{ post.id }} -\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, post)\">\n {{ post.title }}\n </a>\n </span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove related post [[ title ]]', {\n title: post.title,\n })\n \"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeRelatedPost(post.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No related posts yet.') }}</li>\n }\n </ul>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"addRelatedPost()\">\n <mat-icon>add</mat-icon> {{ t.gettext('Add related post') }}\n </button>\n }\n <mat-checkbox\n [checked]=\"applyVisibilityToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyVisibilityToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply visibility to related posts') }}\n </mat-checkbox>\n </section>\n </mat-card>\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-version-links\"\n aria-labelledby=\"post-version-links-heading\">\n <h2 id=\"post-version-links-heading\">\n {{ t.gettext('Post versions') }}\n </h2>\n @if (!currentPost()) {\n <p role=\"note\">\n {{\n t.gettext('Save the post first to manage version links.')\n }}\n </p>\n }\n @if (postVersionLinksLoading()) {\n <p role=\"status\">{{ t.gettext('Loading post versions...') }}</p>\n }\n @if (postVersionLinksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ postVersionLinksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPostVersionLinks(currentPost())\">\n {{ t.gettext('Try again') }}\n </button>\n }\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Public post version') }}</h3>\n @if (postVersionLinks().publicVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPublicVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('public')\">\n {{ t.gettext('Replace public version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('public')\">\n {{ t.gettext('Remove public version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No public post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPublicVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPublicVersion()\n \"\n (click)=\"selectVersionPost('public')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add public post version') }}\n </button>\n }\n </div>\n <div class=\"post-version-link\">\n <h3>{{ t.gettext('Private post version') }}</h3>\n @if (postVersionLinks().privateVersionPost; as versionPost) {\n <p>\n <taon-cms-post-thumbnail [post]=\"versionPost\" />\n <a\n href=\"#\"\n (click)=\"requestPostEditor($event, versionPost)\">\n {{ versionPost.title }}\n </a>\n [{{ versionPost.slug }}]\n </p>\n @if (canEditPrivateVersion()) {\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"selectVersionPost('private')\">\n {{ t.gettext('Replace private version') }}\n </button>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"removeVersionPost('private')\">\n {{ t.gettext('Remove private version') }}\n </button>\n } @else {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n } @else {\n @if (!postVersionLinksLoading() && !postVersionLinksError()) {\n <p>{{ t.gettext('No private post version linked.') }}</p>\n }\n @if (\n currentPost() &&\n !postVersionLinksLoading() &&\n !postVersionLinksError() &&\n !canEditPrivateVersion()\n ) {\n <p>\n {{\n t.gettext(\n 'Version editing is disabled for the current post status.'\n )\n }}\n </p>\n }\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || !canEditPrivateVersion()\n \"\n (click)=\"selectVersionPost('private')\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Add private post version') }}\n </button>\n }\n </div>\n </section>\n </mat-card>\n\n @if (!mediaAttachment()) {\n <mat-card class=\"post-setting-card\">\n <section\n class=\"application-hook\"\n aria-labelledby=\"application-hook-heading\">\n <h2 id=\"application-hook-heading\">\n {{ t.gettext('Hooks') }}\n </h2>\n @if (applicationHooksLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading application hooks...') }}\n </p>\n } @else if (applicationHooksError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ applicationHooksError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadApplicationHooks()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <mat-checkbox\n [checked]=\"useAsApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"setUseAsApplicationHook($event.checked)\">\n {{ t.gettext('Use as application hook') }}\n </mat-checkbox>\n @if (useAsApplicationHook()) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Application Hook') }}</mat-label>\n <mat-select\n [value]=\"selectedApplicationHook()\"\n [disabled]=\"busy() || dialogOpen()\"\n (selectionChange)=\"\n selectedApplicationHook.set($event.value)\n \">\n @for (\n applicationHook of applicationHooks();\n track applicationHook.id\n ) {\n <mat-option\n [value]=\"applicationHook.hook\"\n [disabled]=\"\n applicationHookInUseByAnotherPost(applicationHook)\n \">\n {{ applicationHookLabel(applicationHook.hook) }}\n @if (\n applicationHookInUseByAnotherPost(applicationHook)\n ) {\n {{\n t.gettext('(in use by post # [[ postId ]])', {\n postId: applicationHook.contentId,\n })\n }}\n }\n </mat-option>\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No application hooks configured')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n }\n </section>\n </mat-card>\n }\n <mat-card class=\"post-setting-card\">\n <section\n class=\"post-permissions\"\n aria-labelledby=\"post-permissions-heading\">\n <h2 id=\"post-permissions-heading\">\n {{ t.gettext('Permissions') }}\n </h2>\n @if (permissionSource(); as source) {\n <p role=\"note\">\n {{\n t.gettext(\n 'Permissions are inherited from # [[ postId ]] - [[ title ]]. Edit the source post to change them.',\n { postId: source.id, title: source.title }\n )\n }}\n </p>\n }\n @if (permissionsLoading()) {\n <p role=\"status\">\n {{ t.gettext('Loading assigned permissions...') }}\n </p>\n } @else if (permissionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"busy() || dialogOpen()\"\n (click)=\"loadPermissions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <ul>\n @for (permission of permissions(); track permission.id) {\n <li>\n <span>{{ permission.name }} ({{ permission.code }})</span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"\n t.gettext('Remove permission [[ name ]]', {\n name: permission.name,\n })\n \"\n [disabled]=\"\n busy() || dialogOpen() || !!permissionSource()\n \"\n (click)=\"removePermission(permission.id)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n } @empty {\n <li>{{ t.gettext('No permissions assigned.') }}</li>\n }\n </ul>\n @if (permissionOptionsError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ permissionOptionsError() }}\n </p>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() || dialogOpen() || permissionOptionsLoading()\n \"\n (click)=\"loadPermissionOptions()\">\n {{ t.gettext('Try again') }}\n </button>\n } @else {\n <div class=\"permission-assignment\">\n <mat-form-field\n class=\"w-full\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\">\n <mat-label>{{ t.gettext('Permission') }}</mat-label>\n <mat-select\n [value]=\"selectedPermissionId()\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n permissionOptionsLoading() ||\n !!permissionSource()\n \"\n (selectionChange)=\"\n selectedPermissionId.set($event.value)\n \">\n @for (\n permission of availablePermissions();\n track permission.id\n ) {\n <mat-option [value]=\"permission.id\"\n >{{ permission.name }} ({{\n permission.code\n }})</mat-option\n >\n } @empty {\n <mat-option [disabled]=\"true\">{{\n t.gettext('No permissions available')\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <button\n mat-button\n type=\"button\"\n [disabled]=\"\n busy() ||\n dialogOpen() ||\n !selectedPermissionId() ||\n !!permissionSource()\n \"\n (click)=\"addPermission()\">\n <mat-icon>add</mat-icon>\n {{ t.gettext('Assign permission') }}\n </button>\n </div>\n }\n }\n @if (!permissionSource()) {\n <mat-checkbox\n [checked]=\"applyPermissionsToRelatedPosts()\"\n [disabled]=\"busy() || dialogOpen()\"\n (change)=\"applyPermissionsToRelatedPosts.set($event.checked)\">\n {{ t.gettext('Apply permissions to related posts') }}\n </mat-checkbox>\n }\n </section>\n </mat-card>\n </div>\n </mat-tab>\n </mat-tab-group>\n } @else if (currentPost(); as post) {\n @if (thumbnailPreviewUrl()) {\n <img\n class=\"thumbnail-preview\"\n [src]=\"thumbnailPreviewUrl()\"\n [alt]=\"t.gettext('Post thumbnail')\" />\n }\n @if (thumbnailError()) {\n <p role=\"alert\">{{ thumbnailError() }}</p>\n }\n <h1 class=\"post-title\">{{ post.title }}</h1>\n @if (post.excerpt) {\n <p class=\"post-excerpt\">{{ post.excerpt }}</p>\n }\n @if (post.type === contentType.JsonTable) {\n <taon-spreadsheet-table\n [data]=\"jsonTableData()\"\n [validationError]=\"jsonTableValidationError()\"\n [readOnly]=\"true\" />\n } @else {\n <div\n class=\"post-body\"\n [innerHTML]=\"resolvedBody()\"></div>\n }\n @if (mediaAttachment(); as media) {\n @if (draft()[media.key]; as key) {\n @if (mediaPreviewLoading()) {\n <p role=\"status\">{{ t.gettext('Loading media...') }}</p>\n } @else if (mediaPreviewError()) {\n <p\n class=\"error\"\n role=\"alert\">\n {{ mediaPreviewError() }}\n </p>\n } @else if (media.type === 'video' && mediaPreviewUrl()) {\n <video\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [poster]=\"thumbnailPreviewUrl() || null\"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support video playback.') }}\n </video>\n } @else if (media.type === 'audio' && mediaPreviewUrl()) {\n <audio\n controls\n preload=\"metadata\"\n [attr.controlsList]=\"\n currentPost()?.status === 'archived' ||\n currentPost()?.status === 'draft'\n ? 'nodownload noremoteplayback'\n : null\n \"\n [src]=\"mediaPreviewUrl()\">\n {{ t.gettext('Your browser does not support audio playback.') }}\n </audio>\n } @else if (media.type === 'attachment' && mediaPreviewUrl()) {\n <p>\n {{\n t.gettext('Attached file: [[ fileName ]]', {\n fileName: mediaFileName(key),\n })\n }}\n <a\n [href]=\"mediaPreviewUrl()\"\n target=\"_blank\"\n rel=\"noopener\"\n >{{ t.gettext('Download or open') }}</a\n >\n </p>\n }\n }\n }\n @if (mode() === 'view-clean') {\n <taon-cms-comments\n [contentId]=\"post.id\"\n class=\"w-full\" />\n }\n } @else {\n <p role=\"status\">{{ t.gettext('No post is loaded.') }}</p>\n }\n</article>\n", styles: [":host{display:block}:host ::ng-deep .NgxEditor .ProseMirror table{width:100%;border-collapse:collapse;table-layout:fixed;margin:12px 0}:host ::ng-deep .NgxEditor .ProseMirror td,:host ::ng-deep .NgxEditor .ProseMirror th{position:relative;min-width:60px;border:1px solid #aaa;padding:8px;vertical-align:top}:host ::ng-deep .NgxEditor .ProseMirror td>p,:host ::ng-deep .NgxEditor .ProseMirror th>p{margin:0}:host ::ng-deep .NgxEditor .ProseMirror .selectedCell{background:#3f51b51f}mat-expansion-panel{border-radius:0!important}.post-actions{display:flex;align-items:center;flex-wrap:wrap;gap:1rem;padding:1rem;border-bottom:1px solid rgba(136,136,136,.2)}.post-status{margin-right:auto;text-transform:capitalize;opacity:.7}.buttons{display:flex;flex-wrap:wrap;gap:.5rem}.post-article{max-width:760px;margin:0 auto;padding:clamp(1.5rem,5vw,4rem) 1.5rem;overflow-wrap:anywhere}.post-settings{display:grid;gap:1rem;margin-top:1rem}.post-setting-card{display:block;padding:20px}.post-title{margin:0 0 1.5rem;font-size:clamp(2rem,5vw,3.5rem);font-weight:700;line-height:1.15;letter-spacing:-.03em}.post-excerpt{margin:0 0 2rem;font-size:1.35rem;line-height:1.6;opacity:.7;white-space:pre-wrap}.post-body{font-family:Georgia,Times New Roman,serif;font-size:1.2rem;line-height:1.8;white-space:pre-wrap}.editable{min-height:1.2em;outline:none}.editable:focus{box-shadow:-3px 0 #8885}.editable:empty:before{content:attr(data-placeholder);opacity:.4;pointer-events:none}.post-content-editor{border:1px solid rgba(136,136,136,.3333333333);border-radius:4px;overflow:hidden}.raw-content{display:block;box-sizing:border-box;width:100%;min-height:20rem;padding:1rem;resize:vertical;border:0;color:inherit;background:transparent;font-family:monospace}ngx-editor{display:block;min-height:20rem}.content-preview{min-height:12rem;padding:1rem}.slug-field{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;margin-top:3rem;padding-top:1rem;border-top:1px solid rgba(136,136,136,.2);font-size:.875rem}.slug-field input{flex:1;min-width:160px;padding:.5rem;color:inherit;background:transparent;border:1px solid rgba(136,136,136,.3333333333);border-radius:4px}.error{padding:1rem;color:#b3261e}.post-type-selector{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.5rem}.post-type-accordion{display:block;margin-bottom:1rem}.post-type-accordion mat-panel-title{align-items:center;gap:.5rem}.media-attachment,.post-thumbnail,.post-taxonomy,.parent-post,.related-posts,.post-version-links,.application-hook,.post-permissions{margin-top:1.5rem;font-size:.875rem}.media-attachment h2,.post-thumbnail h2,.post-taxonomy h2,.parent-post h2,.related-posts h2,.post-version-links h2,.application-hook h2,.post-permissions h2{font-size:1rem}.media-attachment .thumbnail-preview,.post-thumbnail .thumbnail-preview,.post-taxonomy .thumbnail-preview,.parent-post .thumbnail-preview,.related-posts .thumbnail-preview,.post-version-links .thumbnail-preview,.application-hook .thumbnail-preview,.post-permissions .thumbnail-preview{display:block;width:min(100%,480px);max-height:320px;object-fit:contain;border-radius:12px;margin-bottom:.75rem}.media-attachment .post-taxonomy mat-form-field,.post-thumbnail .post-taxonomy mat-form-field,.post-taxonomy .post-taxonomy mat-form-field,.parent-post .post-taxonomy mat-form-field,.related-posts .post-taxonomy mat-form-field,.post-version-links .post-taxonomy mat-form-field,.application-hook .post-taxonomy mat-form-field,.post-permissions .post-taxonomy mat-form-field{display:block;margin-top:.75rem}.related-posts ul,.related-child-files ul,.post-permissions ul{padding:0;list-style:none}.related-posts mat-form-field,.related-child-files mat-form-field,.post-permissions mat-form-field{display:block;width:min(100%,28rem);margin-top:.75rem}.related-posts .application-hook,.related-child-files .application-hook,.post-permissions .application-hook{display:grid;align-items:start;gap:.75rem}.related-posts .application-hook mat-form-field,.related-child-files .application-hook mat-form-field,.post-permissions .application-hook mat-form-field{width:min(100%,28rem)}.related-posts li,.related-child-files li,.post-permissions li{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.related-child-files{margin-top:1.5rem;font-size:.875rem}.related-child-files li{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem 0;border-bottom:1px solid rgba(136,136,136,.2)}.related-post-title{flex:1}[cdkDragHandle]{cursor:grab}.related-child-file{flex:1;min-width:0}.related-child-file-heading,.related-child-file-progress,.related-child-progress{display:flex;align-items:center;gap:.75rem}.related-child-file-heading{justify-content:space-between;overflow-wrap:anywhere}.related-child-file-progress{margin-top:.5rem}.related-child-file-progress mat-progress-bar{flex:1}.related-child-progress{margin:.75rem 0}.related-child-progress mat-progress-bar{flex:1}.post-version-link{margin-top:1rem;padding-top:.75rem;border-top:1px solid rgba(136,136,136,.2)}.post-version-link h3{font-size:.95rem}video{max-width:100%;max-height:400px!important}.required-slugs-field{width:100%;margin-top:1rem}\n"] }]
|
|
10637
11928
|
}], ctorParameters: () => [], propDecorators: { post: [{ type: i0.Input, args: [{ isSignal: true, alias: "post", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }, { type: i0.Output, args: ["modeChange"] }], changed: [{ type: i0.Output, args: ["changed"] }], deleted: [{ type: i0.Output, args: ["deleted"] }], editPostRequested: [{ type: i0.Output, args: ["editPostRequested"] }], duplicated: [{ type: i0.Output, args: ["duplicated"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], actionButtonsTemplate: [{ type: i0.Output, args: ["actionButtonsTemplate"] }], actionButtonsTemplateRef: [{
|
|
10638
11929
|
type: ViewChild,
|
|
10639
11930
|
args: ['actionButtonsTemplate']
|
|
@@ -11290,6 +12581,8 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
11290
12581
|
get CmsContext_1793664000000_postVersionLinks () { return CmsContext_1793664000000_postVersionLinks; },
|
|
11291
12582
|
MIGRATIONS_CLASSES_FOR_CmsContext: MIGRATIONS_CLASSES_FOR_CmsContext,
|
|
11292
12583
|
PROJECT_NPM_NAME: PROJECT_NPM_NAME,
|
|
12584
|
+
TAON_CMS_MAX_UPLOAD_BYTES: TAON_CMS_MAX_UPLOAD_BYTES,
|
|
12585
|
+
TAON_SPREADSHEET_MAX_BYTES: TAON_SPREADSHEET_MAX_BYTES,
|
|
11293
12586
|
TaonCmsAbstractContext: TaonCmsAbstractContext,
|
|
11294
12587
|
TaonCmsApplicationHook: TaonCmsApplicationHook,
|
|
11295
12588
|
get TaonCmsApplicationHookEntity () { return TaonCmsApplicationHookEntity; },
|
|
@@ -11437,6 +12730,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
11437
12730
|
TaonCmsWebsiteFeaturesComponent: TaonCmsWebsiteFeaturesComponent,
|
|
11438
12731
|
TaonCmsWebsiteFeaturesRoutes: TaonCmsWebsiteFeaturesRoutes,
|
|
11439
12732
|
TaonRelatedPostChooserComponent: TaonRelatedPostChooserComponent,
|
|
12733
|
+
TaonSpreadsheetTableComponent: TaonSpreadsheetTableComponent,
|
|
11440
12734
|
TaonTaonCmsWebsiteFeatureConfig: TaonTaonCmsWebsiteFeatureConfig,
|
|
11441
12735
|
contentError: contentError,
|
|
11442
12736
|
default: index,
|
|
@@ -11444,13 +12738,20 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
11444
12738
|
defaultThumbnailUrl: defaultThumbnailUrl,
|
|
11445
12739
|
extractDocumentHtml: extractDocumentHtml,
|
|
11446
12740
|
generateVideoThumbnail: generateVideoThumbnail,
|
|
12741
|
+
importSpreadsheetJson: importSpreadsheetJson,
|
|
12742
|
+
inferSpreadsheetData: inferSpreadsheetData,
|
|
12743
|
+
normalizeSpreadsheetKey: normalizeSpreadsheetKey,
|
|
12744
|
+
parseSpreadsheetCsv: parseSpreadsheetCsv,
|
|
12745
|
+
parseSpreadsheetDocument: parseSpreadsheetDocument,
|
|
11447
12746
|
requireContentId: requireContentId,
|
|
11448
12747
|
requireContentVersion: requireContentVersion,
|
|
11449
12748
|
resolveTemplateVariables: resolveTemplateVariables,
|
|
12749
|
+
serializeSpreadsheetCsv: serializeSpreadsheetCsv,
|
|
11450
12750
|
startCli: startCli,
|
|
11451
12751
|
trackCmsUpload: trackCmsUpload,
|
|
11452
12752
|
validateCategoryHierarchy: validateCategoryHierarchy,
|
|
11453
12753
|
validateContentInput: validateContentInput,
|
|
12754
|
+
validateSpreadsheetData: validateSpreadsheetData,
|
|
11454
12755
|
validateTaxonomy: validateTaxonomy,
|
|
11455
12756
|
visiblePixelRatio: visiblePixelRatio
|
|
11456
12757
|
});
|
|
@@ -11459,5 +12760,5 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
11459
12760
|
* Generated bundle index. Do not edit.
|
|
11460
12761
|
*/
|
|
11461
12762
|
|
|
11462
|
-
export { APP_ID, BUILD_BASE_HREF, BUILD_FRAMEWORK_CLI_NAME, CURRENT_PACKAGE_TAON_VERSION, CURRENT_PACKAGE_VERSION, CmsContext_1792506757808_applicaitonHooks, CmsContext_1793664000000_postVersionLinks, MIGRATIONS_CLASSES_FOR_CmsContext, PROJECT_NPM_NAME, TaonCmsAbstractContext, TaonCmsApplicationHook, TaonCmsApplicationHookEntity, TaonCmsAssetPreviewComponent, TaonCmsAssetsBackofficeComponent, TaonCmsAssetsBackofficeRoutes, TaonCmsBackofficeComponent, TaonCmsBackofficeRoutes, TaonCmsBlockedUsersBackofficeComponent, TaonCmsCategoriesBackofficeRoutes, TaonCmsCategoryAbstractContext, TaonCmsCategoryApiService, TaonCmsCategoryController, TaonCmsCategoryDefaultsValues, TaonCmsCategoryEntity, TaonCmsCategoryMiddleware, TaonCmsCategoryProvider, TaonCmsCategoryRepository, TaonCmsCategoryState, TaonCmsCategorySubscriber, TaonCmsCategoryUtils__NS__isActive, TaonCmsCommentAbstractContext, TaonCmsCommentApiService, TaonCmsCommentBlockedPhraseEntity, TaonCmsCommentBlockedUserEntity, TaonCmsCommentController, TaonCmsCommentDefaultsValues, TaonCmsCommentEntity, TaonCmsCommentMiddleware, TaonCmsCommentProvider, TaonCmsCommentRepository, TaonCmsCommentSpamReportEntity, TaonCmsCommentState, TaonCmsCommentSubscriber, TaonCmsCommentUtils__NS__isActive, TaonCmsCommentVoteEntity, TaonCmsCommentsAllPreviewBackofficeComponent, TaonCmsCommentsAllPreviewBackofficeRoutes, TaonCmsCommentsBackofficeComponent, TaonCmsCommentsBackofficeRoutes, TaonCmsCommentsBlockedUsersRoutes, TaonCmsCommentsComponent, TaonCmsCommentsFiltersSettingsComponent, TaonCmsCommentsFiltersSettingsRoutes, TaonCmsContentAbstractContext, TaonCmsContentApiService, TaonCmsContentAssetAbstractContext, TaonCmsContentAssetApiService, TaonCmsContentAssetController, TaonCmsContentAssetDefaultsValues, TaonCmsContentAssetEntity, TaonCmsContentAssetMiddleware, TaonCmsContentAssetProvider, TaonCmsContentAssetRepository, TaonCmsContentAssetState, TaonCmsContentAssetSubscriber, TaonCmsContentAssetUtils__NS__isActive, TaonCmsContentConfigService, TaonCmsContentController, TaonCmsContentDefaultsValues, TaonCmsContentEntity, TaonCmsContentMiddleware, TaonCmsContentPermissionAbstractContext, TaonCmsContentPermissionApiService, TaonCmsContentPermissionController, TaonCmsContentPermissionDefaultsValues, TaonCmsContentPermissionEntity, TaonCmsContentPermissionMiddleware, TaonCmsContentPermissionProvider, TaonCmsContentPermissionRepository, TaonCmsContentPermissionState, TaonCmsContentPermissionSubscriber, TaonCmsContentPermissionUtils__NS__isActive, TaonCmsContentProvider, TaonCmsContentRepository, TaonCmsContentRevisionAbstractContext, TaonCmsContentRevisionApiService, TaonCmsContentRevisionController, TaonCmsContentRevisionDefaultsValues, TaonCmsContentRevisionEntity, TaonCmsContentRevisionMiddleware, TaonCmsContentRevisionProvider, TaonCmsContentRevisionRepository, TaonCmsContentRevisionState, TaonCmsContentRevisionSubscriber, TaonCmsContentRevisionUtils__NS__isActive, TaonCmsContentState, TaonCmsContentStorageController, TaonCmsContentSubscriber, TaonCmsContentTagAbstractContext, TaonCmsContentTagApiService, TaonCmsContentTagController, TaonCmsContentTagDefaultsValues, TaonCmsContentTagEntity, TaonCmsContentTagMiddleware, TaonCmsContentTagProvider, TaonCmsContentTagRepository, TaonCmsContentTagState, TaonCmsContentTagSubscriber, TaonCmsContentTagUtils__NS__isActive, TaonCmsContentType, TaonCmsContentUtils__NS__isActive, TaonCmsContentUtils__NS__orderRelatedPosts, TaonCmsCourseBackofficeComponent, TaonCmsCourseBackofficeRoutes, TaonCmsCourseContentsComponent, TaonCmsCourseCoverComponent, TaonCmsEditableDirective, TaonCmsPostChoiceDialogComponent, TaonCmsPostComponent, TaonCmsPostDetailsPageComponent, TaonCmsPostRoutes, TaonCmsPostTableActions, TaonCmsPostThumbnailComponent, TaonCmsPostsBackofficeComponent, TaonCmsPostsBackofficeRoutes, TaonCmsRevisionChooserComponent, TaonCmsTagAbstractContext, TaonCmsTagApiService, TaonCmsTagController, TaonCmsTagDefaultsValues, TaonCmsTagEntity, TaonCmsTagMiddleware, TaonCmsTagProvider, TaonCmsTagRepository, TaonCmsTagState, TaonCmsTagSubscriber, TaonCmsTagUtils__NS__isActive, TaonCmsTagsBackofficeRoutes, TaonCmsTaxonomyBackofficeComponent, TaonCmsTaxonomyQuickCreateDialogComponent, TaonCmsWebsiteFeatureAbstractContext, TaonCmsWebsiteFeatureApiService, TaonCmsWebsiteFeatureConfigService, TaonCmsWebsiteFeatureController, TaonCmsWebsiteFeatureDefaultsValues, TaonCmsWebsiteFeatureErrors, TaonCmsWebsiteFeatureKvRepository, TaonCmsWebsiteFeatureModels__NS__TaonCmsWebsiteFeatureState, TaonCmsWebsiteFeatureProvider, TaonCmsWebsiteFeatureTranslationErorsMap, TaonCmsWebsiteFeatureUtils__NS__isActive, TaonCmsWebsiteFeatureUtils__NS__keyFromName, TaonCmsWebsiteFeatureValidators__NS__passwordMatchValidator, TaonCmsWebsiteFeaturesComponent, TaonCmsWebsiteFeaturesRoutes, TaonRelatedPostChooserComponent, TaonTaonCmsWebsiteFeatureConfig, contentError, defaultThumbnailKey, defaultThumbnailUrl, extractDocumentHtml, generateVideoThumbnail, requireContentId, requireContentVersion, resolveTemplateVariables, startCli, trackCmsUpload, validateCategoryHierarchy, validateContentInput, validateTaxonomy, visiblePixelRatio };
|
|
12763
|
+
export { APP_ID, BUILD_BASE_HREF, BUILD_FRAMEWORK_CLI_NAME, CURRENT_PACKAGE_TAON_VERSION, CURRENT_PACKAGE_VERSION, CmsContext_1792506757808_applicaitonHooks, CmsContext_1793664000000_postVersionLinks, MIGRATIONS_CLASSES_FOR_CmsContext, PROJECT_NPM_NAME, TAON_CMS_MAX_UPLOAD_BYTES, TAON_SPREADSHEET_MAX_BYTES, TaonCmsAbstractContext, TaonCmsApplicationHook, TaonCmsApplicationHookEntity, TaonCmsAssetPreviewComponent, TaonCmsAssetsBackofficeComponent, TaonCmsAssetsBackofficeRoutes, TaonCmsBackofficeComponent, TaonCmsBackofficeRoutes, TaonCmsBlockedUsersBackofficeComponent, TaonCmsCategoriesBackofficeRoutes, TaonCmsCategoryAbstractContext, TaonCmsCategoryApiService, TaonCmsCategoryController, TaonCmsCategoryDefaultsValues, TaonCmsCategoryEntity, TaonCmsCategoryMiddleware, TaonCmsCategoryProvider, TaonCmsCategoryRepository, TaonCmsCategoryState, TaonCmsCategorySubscriber, TaonCmsCategoryUtils__NS__isActive, TaonCmsCommentAbstractContext, TaonCmsCommentApiService, TaonCmsCommentBlockedPhraseEntity, TaonCmsCommentBlockedUserEntity, TaonCmsCommentController, TaonCmsCommentDefaultsValues, TaonCmsCommentEntity, TaonCmsCommentMiddleware, TaonCmsCommentProvider, TaonCmsCommentRepository, TaonCmsCommentSpamReportEntity, TaonCmsCommentState, TaonCmsCommentSubscriber, TaonCmsCommentUtils__NS__isActive, TaonCmsCommentVoteEntity, TaonCmsCommentsAllPreviewBackofficeComponent, TaonCmsCommentsAllPreviewBackofficeRoutes, TaonCmsCommentsBackofficeComponent, TaonCmsCommentsBackofficeRoutes, TaonCmsCommentsBlockedUsersRoutes, TaonCmsCommentsComponent, TaonCmsCommentsFiltersSettingsComponent, TaonCmsCommentsFiltersSettingsRoutes, TaonCmsContentAbstractContext, TaonCmsContentApiService, TaonCmsContentAssetAbstractContext, TaonCmsContentAssetApiService, TaonCmsContentAssetController, TaonCmsContentAssetDefaultsValues, TaonCmsContentAssetEntity, TaonCmsContentAssetMiddleware, TaonCmsContentAssetProvider, TaonCmsContentAssetRepository, TaonCmsContentAssetState, TaonCmsContentAssetSubscriber, TaonCmsContentAssetUtils__NS__isActive, TaonCmsContentConfigService, TaonCmsContentController, TaonCmsContentDefaultsValues, TaonCmsContentEntity, TaonCmsContentMiddleware, TaonCmsContentPermissionAbstractContext, TaonCmsContentPermissionApiService, TaonCmsContentPermissionController, TaonCmsContentPermissionDefaultsValues, TaonCmsContentPermissionEntity, TaonCmsContentPermissionMiddleware, TaonCmsContentPermissionProvider, TaonCmsContentPermissionRepository, TaonCmsContentPermissionState, TaonCmsContentPermissionSubscriber, TaonCmsContentPermissionUtils__NS__isActive, TaonCmsContentProvider, TaonCmsContentRepository, TaonCmsContentRevisionAbstractContext, TaonCmsContentRevisionApiService, TaonCmsContentRevisionController, TaonCmsContentRevisionDefaultsValues, TaonCmsContentRevisionEntity, TaonCmsContentRevisionMiddleware, TaonCmsContentRevisionProvider, TaonCmsContentRevisionRepository, TaonCmsContentRevisionState, TaonCmsContentRevisionSubscriber, TaonCmsContentRevisionUtils__NS__isActive, TaonCmsContentState, TaonCmsContentStorageController, TaonCmsContentSubscriber, TaonCmsContentTagAbstractContext, TaonCmsContentTagApiService, TaonCmsContentTagController, TaonCmsContentTagDefaultsValues, TaonCmsContentTagEntity, TaonCmsContentTagMiddleware, TaonCmsContentTagProvider, TaonCmsContentTagRepository, TaonCmsContentTagState, TaonCmsContentTagSubscriber, TaonCmsContentTagUtils__NS__isActive, TaonCmsContentType, TaonCmsContentUtils__NS__isActive, TaonCmsContentUtils__NS__orderRelatedPosts, TaonCmsCourseBackofficeComponent, TaonCmsCourseBackofficeRoutes, TaonCmsCourseContentsComponent, TaonCmsCourseCoverComponent, TaonCmsEditableDirective, TaonCmsPostChoiceDialogComponent, TaonCmsPostComponent, TaonCmsPostDetailsPageComponent, TaonCmsPostRoutes, TaonCmsPostTableActions, TaonCmsPostThumbnailComponent, TaonCmsPostsBackofficeComponent, TaonCmsPostsBackofficeRoutes, TaonCmsRevisionChooserComponent, TaonCmsTagAbstractContext, TaonCmsTagApiService, TaonCmsTagController, TaonCmsTagDefaultsValues, TaonCmsTagEntity, TaonCmsTagMiddleware, TaonCmsTagProvider, TaonCmsTagRepository, TaonCmsTagState, TaonCmsTagSubscriber, TaonCmsTagUtils__NS__isActive, TaonCmsTagsBackofficeRoutes, TaonCmsTaxonomyBackofficeComponent, TaonCmsTaxonomyQuickCreateDialogComponent, TaonCmsWebsiteFeatureAbstractContext, TaonCmsWebsiteFeatureApiService, TaonCmsWebsiteFeatureConfigService, TaonCmsWebsiteFeatureController, TaonCmsWebsiteFeatureDefaultsValues, TaonCmsWebsiteFeatureErrors, TaonCmsWebsiteFeatureKvRepository, TaonCmsWebsiteFeatureModels__NS__TaonCmsWebsiteFeatureState, TaonCmsWebsiteFeatureProvider, TaonCmsWebsiteFeatureTranslationErorsMap, TaonCmsWebsiteFeatureUtils__NS__isActive, TaonCmsWebsiteFeatureUtils__NS__keyFromName, TaonCmsWebsiteFeatureValidators__NS__passwordMatchValidator, TaonCmsWebsiteFeaturesComponent, TaonCmsWebsiteFeaturesRoutes, TaonRelatedPostChooserComponent, TaonSpreadsheetTableComponent, TaonTaonCmsWebsiteFeatureConfig, contentError, defaultThumbnailKey, defaultThumbnailUrl, extractDocumentHtml, generateVideoThumbnail, importSpreadsheetJson, inferSpreadsheetData, normalizeSpreadsheetKey, parseSpreadsheetCsv, parseSpreadsheetDocument, requireContentId, requireContentVersion, resolveTemplateVariables, serializeSpreadsheetCsv, startCli, trackCmsUpload, validateCategoryHierarchy, validateContentInput, validateSpreadsheetData, validateTaxonomy, visiblePixelRatio };
|
|
11463
12764
|
//# sourceMappingURL=cms-websql-prod.mjs.map
|