@morphemecms/cms 0.1.0 → 0.2.0-next-20261104150211
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/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +51 -51
- package/astro.mjs +162 -162
- package/bin/morpheme.mjs +38 -38
- package/client.d.ts +10 -10
- package/defaults/blockTexts.ts +33 -33
- package/defaults/collections.ts +774 -774
- package/defaults/colors.ts +125 -125
- package/defaults/company.ts +20 -20
- package/defaults/editor.ts +178 -178
- package/defaults/internalLinks.ts +30 -30
- package/defaults/opens.ts +18 -18
- package/defaults/popup.tsx +93 -93
- package/defaults/privacy.ts +79 -79
- package/defaults/rules.ts +450 -450
- package/defaults/schema.ts +16 -16
- package/defaults/sitedata.ts +129 -129
- package/defaults/standard.ts +106 -106
- package/defaults/standardPages.ts +87 -87
- package/defaults/texts.ts +481 -481
- package/demo/README.md +26 -26
- package/demo/src/content/blog/build-a-page-from-blocks.md +19 -19
- package/demo/src/content/blog/colors-everyone-can-read.md +19 -19
- package/demo/src/content/blog/edit-your-website-from-your-phone.md +21 -21
- package/demo/src/content/blog/every-save-is-a-version.md +21 -21
- package/demo/src/content/blog/schedule-a-post.md +17 -17
- package/demo/src/content/blog/secure-from-day-one.md +17 -17
- package/demo/src/content/blog/seo-without-the-guesswork.md +23 -23
- package/demo/src/content/blog/switch-themes-without-losing-anything.md +23 -23
- package/demo/src/content/blog/the-app-store.md +21 -21
- package/demo/src/content/pages/about.md +22 -22
- package/demo/src/content/pages/app-store.md +30 -30
- package/demo/src/content/pages/blocks.md +98 -98
- package/demo/src/content/pages/blog-page.md +11 -11
- package/demo/src/content/pages/contact.md +16 -16
- package/demo/src/content/pages/get-started.md +20 -20
- package/demo/src/content/pages/home.md +26 -26
- package/demo/src/content/pages/page-not-found.md +11 -11
- package/demo/src/content/pages/privacy-policy.md +24 -24
- package/demo/src/data/blocks.json +25 -25
- package/demo/src/data/booking.json +6 -6
- package/demo/src/data/categories/apps.json +6 -6
- package/demo/src/data/categories/design.json +6 -6
- package/demo/src/data/categories/editing.json +6 -6
- package/demo/src/data/categories/security.json +6 -6
- package/demo/src/data/categories/seo.json +6 -6
- package/demo/src/data/colors.json +75 -75
- package/demo/src/data/contact.json +8 -8
- package/demo/src/data/cookies.json +21 -21
- package/demo/src/data/footer.json +27 -27
- package/demo/src/data/menu.json +35 -35
- package/demo/src/data/mobile-menu.json +25 -25
- package/demo/src/data/popups/booking-bar.json +13 -13
- package/demo/src/data/popups/try-the-editor.json +16 -16
- package/demo/src/data/standard-pages.json +8 -8
- package/demo/src/data/testimonials/sample-bakery.json +10 -10
- package/demo/src/data/testimonials/sample-clinic.json +10 -10
- package/demo/src/data/testimonials/sample-studio.json +10 -10
- package/demo/src/site/colors.ts +11 -11
- package/demo/src/site/config.ts +19 -19
- package/icons/lucide/LICENSE +43 -43
- package/icons/lucide/address-book.svg +1 -1
- package/icons/lucide/app-window.svg +1 -1
- package/icons/lucide/arrow-counter-clockwise.svg +1 -1
- package/icons/lucide/arrow-down.svg +1 -1
- package/icons/lucide/arrow-left.svg +1 -1
- package/icons/lucide/arrow-right.svg +1 -1
- package/icons/lucide/arrow-square-out.svg +1 -1
- package/icons/lucide/arrow-u-up-left.svg +1 -1
- package/icons/lucide/arrow-u-up-right.svg +1 -1
- package/icons/lucide/arrow-up-right.svg +1 -1
- package/icons/lucide/arrow-up.svg +1 -1
- package/icons/lucide/arrows-down-up.svg +1 -1
- package/icons/lucide/arrows-in-line-horizontal.svg +1 -1
- package/icons/lucide/arrows-out-line-horizontal.svg +1 -1
- package/icons/lucide/article.svg +1 -1
- package/icons/lucide/bank.svg +1 -1
- package/icons/lucide/bell.svg +1 -1
- package/icons/lucide/bicycle.svg +1 -1
- package/icons/lucide/binoculars.svg +1 -1
- package/icons/lucide/book-open-text.svg +1 -1
- package/icons/lucide/book-open.svg +1 -1
- package/icons/lucide/browser.svg +1 -1
- package/icons/lucide/buildings.svg +1 -1
- package/icons/lucide/bus.svg +1 -1
- package/icons/lucide/calendar-blank.svg +1 -1
- package/icons/lucide/calendar-check.svg +1 -1
- package/icons/lucide/car.svg +1 -1
- package/icons/lucide/cards.svg +1 -1
- package/icons/lucide/caret-down.svg +1 -1
- package/icons/lucide/caret-left.svg +1 -1
- package/icons/lucide/caret-right.svg +1 -1
- package/icons/lucide/caret-up.svg +1 -1
- package/icons/lucide/certificate.svg +1 -1
- package/icons/lucide/chart-line-up.svg +1 -1
- package/icons/lucide/chats.svg +1 -1
- package/icons/lucide/check-circle.svg +1 -1
- package/icons/lucide/check.svg +1 -1
- package/icons/lucide/circle-half.svg +1 -1
- package/icons/lucide/circle.svg +1 -1
- package/icons/lucide/clock-counter-clockwise.svg +1 -1
- package/icons/lucide/clock.svg +1 -1
- package/icons/lucide/code.svg +1 -1
- package/icons/lucide/coffee.svg +1 -1
- package/icons/lucide/columns.svg +1 -1
- package/icons/lucide/cookie.svg +1 -1
- package/icons/lucide/copy.svg +1 -1
- package/icons/lucide/credit-card.svg +1 -1
- package/icons/lucide/currency-circle-dollar.svg +1 -1
- package/icons/lucide/cursor-click.svg +1 -1
- package/icons/lucide/database.svg +1 -1
- package/icons/lucide/desktop.svg +1 -1
- package/icons/lucide/device-mobile.svg +1 -1
- package/icons/lucide/door-open.svg +1 -1
- package/icons/lucide/download-simple.svg +1 -1
- package/icons/lucide/envelope-simple.svg +1 -1
- package/icons/lucide/eye-slash.svg +1 -1
- package/icons/lucide/eye.svg +1 -1
- package/icons/lucide/file-pdf.svg +1 -1
- package/icons/lucide/file-text.svg +1 -1
- package/icons/lucide/file-video.svg +1 -1
- package/icons/lucide/file-zip.svg +1 -1
- package/icons/lucide/flower-lotus.svg +1 -1
- package/icons/lucide/folder-simple.svg +1 -1
- package/icons/lucide/funnel.svg +1 -1
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear.svg +1 -1
- package/icons/lucide/github-logo.svg +1 -1
- package/icons/lucide/globe.svg +1 -1
- package/icons/lucide/google-logo.svg +1 -1
- package/icons/lucide/graduation-cap.svg +1 -1
- package/icons/lucide/hammer.svg +1 -1
- package/icons/lucide/hand-heart.svg +1 -1
- package/icons/lucide/handshake.svg +1 -1
- package/icons/lucide/hard-drives.svg +1 -1
- package/icons/lucide/heart.svg +1 -1
- package/icons/lucide/house.svg +1 -1
- package/icons/lucide/image-square.svg +1 -1
- package/icons/lucide/image.svg +1 -1
- package/icons/lucide/info.svg +1 -1
- package/icons/lucide/keyboard.svg +1 -1
- package/icons/lucide/leaf.svg +1 -1
- package/icons/lucide/lifebuoy.svg +1 -1
- package/icons/lucide/lightbulb.svg +1 -1
- package/icons/lucide/link-break.svg +1 -1
- package/icons/lucide/link-simple.svg +1 -1
- package/icons/lucide/list-bullets.svg +1 -1
- package/icons/lucide/list-checks.svg +1 -1
- package/icons/lucide/list-numbers.svg +1 -1
- package/icons/lucide/list.svg +1 -1
- package/icons/lucide/lock-simple-open.svg +1 -1
- package/icons/lucide/lock-simple.svg +1 -1
- package/icons/lucide/magnifying-glass.svg +1 -1
- package/icons/lucide/map-pin.svg +1 -1
- package/icons/lucide/map-trifold.svg +1 -1
- package/icons/lucide/medal.svg +1 -1
- package/icons/lucide/megaphone.svg +1 -1
- package/icons/lucide/minus.svg +1 -1
- package/icons/lucide/moon.svg +1 -1
- package/icons/lucide/newspaper.svg +1 -1
- package/icons/lucide/pack.json +23 -23
- package/icons/lucide/paint-brush-broad.svg +1 -1
- package/icons/lucide/palette.svg +1 -1
- package/icons/lucide/paper-plane-tilt.svg +1 -1
- package/icons/lucide/pencil-simple.svg +1 -1
- package/icons/lucide/phone.svg +1 -1
- package/icons/lucide/play-fill.svg +1 -1
- package/icons/lucide/plus.svg +1 -1
- package/icons/lucide/prohibit.svg +1 -1
- package/icons/lucide/puzzle-piece.svg +1 -1
- package/icons/lucide/question.svg +1 -1
- package/icons/lucide/quotes.svg +1 -1
- package/icons/lucide/rows.svg +1 -1
- package/icons/lucide/ruler.svg +1 -1
- package/icons/lucide/scan.svg +1 -1
- package/icons/lucide/shapes.svg +1 -1
- package/icons/lucide/share-network.svg +1 -1
- package/icons/lucide/shield-check.svg +1 -1
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sign-out.svg +1 -1
- package/icons/lucide/signpost.svg +1 -1
- package/icons/lucide/sliders-horizontal.svg +1 -1
- package/icons/lucide/smiley.svg +1 -1
- package/icons/lucide/sparkle.svg +1 -1
- package/icons/lucide/square-half-bottom.svg +1 -1
- package/icons/lucide/square.svg +1 -1
- package/icons/lucide/squares-four.svg +1 -1
- package/icons/lucide/sticker.svg +1 -1
- package/icons/lucide/storefront.svg +1 -1
- package/icons/lucide/sun.svg +1 -1
- package/icons/lucide/swatches.svg +1 -1
- package/icons/lucide/tag.svg +1 -1
- package/icons/lucide/text-aa.svg +1 -1
- package/icons/lucide/text-align-left.svg +1 -1
- package/icons/lucide/text-b.svg +1 -1
- package/icons/lucide/text-h-one.svg +1 -1
- package/icons/lucide/text-h-three.svg +1 -1
- package/icons/lucide/text-h-two.svg +1 -1
- package/icons/lucide/text-italic.svg +1 -1
- package/icons/lucide/text-t.svg +1 -1
- package/icons/lucide/ticket.svg +1 -1
- package/icons/lucide/toggle-right.svg +1 -1
- package/icons/lucide/train.svg +1 -1
- package/icons/lucide/trash.svg +1 -1
- package/icons/lucide/upload-simple.svg +1 -1
- package/icons/lucide/user-circle.svg +1 -1
- package/icons/lucide/user.svg +1 -1
- package/icons/lucide/users-three.svg +1 -1
- package/icons/lucide/users.svg +1 -1
- package/icons/lucide/video-camera.svg +1 -1
- package/icons/lucide/warning-circle.svg +1 -1
- package/icons/lucide/warning.svg +1 -1
- package/icons/lucide/wave-sine.svg +1 -1
- package/icons/lucide/wheelchair.svg +1 -1
- package/icons/lucide/wrench.svg +1 -1
- package/icons/lucide/x.svg +1 -1
- package/icons/material-symbols/LICENSE +201 -201
- package/icons/material-symbols/address-book.svg +1 -1
- package/icons/material-symbols/app-window.svg +1 -1
- package/icons/material-symbols/arrow-counter-clockwise.svg +1 -1
- package/icons/material-symbols/arrow-down.svg +1 -1
- package/icons/material-symbols/arrow-left.svg +1 -1
- package/icons/material-symbols/arrow-right.svg +1 -1
- package/icons/material-symbols/arrow-square-out.svg +1 -1
- package/icons/material-symbols/arrow-u-up-left.svg +1 -1
- package/icons/material-symbols/arrow-u-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up.svg +1 -1
- package/icons/material-symbols/arrows-down-up.svg +1 -1
- package/icons/material-symbols/arrows-in-line-horizontal.svg +1 -1
- package/icons/material-symbols/arrows-out-line-horizontal.svg +1 -1
- package/icons/material-symbols/article.svg +1 -1
- package/icons/material-symbols/bank.svg +1 -1
- package/icons/material-symbols/bell.svg +1 -1
- package/icons/material-symbols/bicycle.svg +1 -1
- package/icons/material-symbols/binoculars.svg +1 -1
- package/icons/material-symbols/book-open-text.svg +1 -1
- package/icons/material-symbols/book-open.svg +1 -1
- package/icons/material-symbols/browser.svg +1 -1
- package/icons/material-symbols/buildings.svg +1 -1
- package/icons/material-symbols/bus.svg +1 -1
- package/icons/material-symbols/calendar-blank.svg +1 -1
- package/icons/material-symbols/calendar-check.svg +1 -1
- package/icons/material-symbols/car.svg +1 -1
- package/icons/material-symbols/cards.svg +1 -1
- package/icons/material-symbols/caret-down.svg +1 -1
- package/icons/material-symbols/caret-left.svg +1 -1
- package/icons/material-symbols/caret-right.svg +1 -1
- package/icons/material-symbols/caret-up.svg +1 -1
- package/icons/material-symbols/certificate.svg +1 -1
- package/icons/material-symbols/chart-line-up.svg +1 -1
- package/icons/material-symbols/chats.svg +1 -1
- package/icons/material-symbols/check-circle.svg +1 -1
- package/icons/material-symbols/check.svg +1 -1
- package/icons/material-symbols/circle-half.svg +1 -1
- package/icons/material-symbols/circle.svg +1 -1
- package/icons/material-symbols/clock-counter-clockwise.svg +1 -1
- package/icons/material-symbols/clock.svg +1 -1
- package/icons/material-symbols/code.svg +1 -1
- package/icons/material-symbols/coffee.svg +1 -1
- package/icons/material-symbols/columns.svg +1 -1
- package/icons/material-symbols/cookie.svg +1 -1
- package/icons/material-symbols/copy.svg +1 -1
- package/icons/material-symbols/credit-card.svg +1 -1
- package/icons/material-symbols/currency-circle-dollar.svg +1 -1
- package/icons/material-symbols/cursor-click.svg +1 -1
- package/icons/material-symbols/database.svg +1 -1
- package/icons/material-symbols/desktop.svg +1 -1
- package/icons/material-symbols/device-mobile.svg +1 -1
- package/icons/material-symbols/door-open.svg +1 -1
- package/icons/material-symbols/download-simple.svg +1 -1
- package/icons/material-symbols/envelope-simple.svg +1 -1
- package/icons/material-symbols/eye-slash.svg +1 -1
- package/icons/material-symbols/eye.svg +1 -1
- package/icons/material-symbols/file-pdf.svg +1 -1
- package/icons/material-symbols/file-text.svg +1 -1
- package/icons/material-symbols/file-video.svg +1 -1
- package/icons/material-symbols/file-zip.svg +1 -1
- package/icons/material-symbols/flower-lotus.svg +1 -1
- package/icons/material-symbols/folder-simple.svg +1 -1
- package/icons/material-symbols/funnel.svg +1 -1
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear.svg +1 -1
- package/icons/material-symbols/github-logo.svg +1 -1
- package/icons/material-symbols/globe.svg +1 -1
- package/icons/material-symbols/google-logo.svg +1 -1
- package/icons/material-symbols/graduation-cap.svg +1 -1
- package/icons/material-symbols/hammer.svg +1 -1
- package/icons/material-symbols/hand-heart.svg +1 -1
- package/icons/material-symbols/handshake.svg +1 -1
- package/icons/material-symbols/hard-drives.svg +1 -1
- package/icons/material-symbols/heart.svg +1 -1
- package/icons/material-symbols/house.svg +1 -1
- package/icons/material-symbols/image-square.svg +1 -1
- package/icons/material-symbols/image.svg +1 -1
- package/icons/material-symbols/info.svg +1 -1
- package/icons/material-symbols/keyboard.svg +1 -1
- package/icons/material-symbols/leaf.svg +1 -1
- package/icons/material-symbols/lifebuoy.svg +1 -1
- package/icons/material-symbols/lightbulb.svg +1 -1
- package/icons/material-symbols/link-break.svg +1 -1
- package/icons/material-symbols/link-simple.svg +1 -1
- package/icons/material-symbols/list-bullets.svg +1 -1
- package/icons/material-symbols/list-checks.svg +1 -1
- package/icons/material-symbols/list-numbers.svg +1 -1
- package/icons/material-symbols/list.svg +1 -1
- package/icons/material-symbols/lock-simple-open.svg +1 -1
- package/icons/material-symbols/lock-simple.svg +1 -1
- package/icons/material-symbols/magnifying-glass.svg +1 -1
- package/icons/material-symbols/map-pin.svg +1 -1
- package/icons/material-symbols/map-trifold.svg +1 -1
- package/icons/material-symbols/medal.svg +1 -1
- package/icons/material-symbols/megaphone.svg +1 -1
- package/icons/material-symbols/minus.svg +1 -1
- package/icons/material-symbols/moon.svg +1 -1
- package/icons/material-symbols/newspaper.svg +1 -1
- package/icons/material-symbols/pack.json +29 -29
- package/icons/material-symbols/paint-brush-broad.svg +1 -1
- package/icons/material-symbols/palette.svg +1 -1
- package/icons/material-symbols/paper-plane-tilt.svg +1 -1
- package/icons/material-symbols/pencil-simple.svg +1 -1
- package/icons/material-symbols/phone.svg +1 -1
- package/icons/material-symbols/play-fill.svg +1 -1
- package/icons/material-symbols/plus.svg +1 -1
- package/icons/material-symbols/prohibit.svg +1 -1
- package/icons/material-symbols/puzzle-piece.svg +1 -1
- package/icons/material-symbols/question.svg +1 -1
- package/icons/material-symbols/quotes.svg +1 -1
- package/icons/material-symbols/rows.svg +1 -1
- package/icons/material-symbols/ruler.svg +1 -1
- package/icons/material-symbols/scan.svg +1 -1
- package/icons/material-symbols/shapes.svg +1 -1
- package/icons/material-symbols/share-network.svg +1 -1
- package/icons/material-symbols/shield-check.svg +1 -1
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sign-out.svg +1 -1
- package/icons/material-symbols/signpost.svg +1 -1
- package/icons/material-symbols/sliders-horizontal.svg +1 -1
- package/icons/material-symbols/smiley.svg +1 -1
- package/icons/material-symbols/sparkle.svg +1 -1
- package/icons/material-symbols/square-half-bottom.svg +1 -1
- package/icons/material-symbols/square.svg +1 -1
- package/icons/material-symbols/squares-four.svg +1 -1
- package/icons/material-symbols/sticker.svg +1 -1
- package/icons/material-symbols/storefront.svg +1 -1
- package/icons/material-symbols/sun.svg +1 -1
- package/icons/material-symbols/swatches.svg +1 -1
- package/icons/material-symbols/tag.svg +1 -1
- package/icons/material-symbols/text-aa.svg +1 -1
- package/icons/material-symbols/text-align-left.svg +1 -1
- package/icons/material-symbols/text-b.svg +1 -1
- package/icons/material-symbols/text-h-one.svg +1 -1
- package/icons/material-symbols/text-h-three.svg +1 -1
- package/icons/material-symbols/text-h-two.svg +1 -1
- package/icons/material-symbols/text-italic.svg +1 -1
- package/icons/material-symbols/text-t.svg +1 -1
- package/icons/material-symbols/ticket.svg +1 -1
- package/icons/material-symbols/toggle-right.svg +1 -1
- package/icons/material-symbols/train.svg +1 -1
- package/icons/material-symbols/trash.svg +1 -1
- package/icons/material-symbols/upload-simple.svg +1 -1
- package/icons/material-symbols/user-circle.svg +1 -1
- package/icons/material-symbols/user.svg +1 -1
- package/icons/material-symbols/users-three.svg +1 -1
- package/icons/material-symbols/users.svg +1 -1
- package/icons/material-symbols/video-camera.svg +1 -1
- package/icons/material-symbols/warning-circle.svg +1 -1
- package/icons/material-symbols/warning.svg +1 -1
- package/icons/material-symbols/wave-sine.svg +1 -1
- package/icons/material-symbols/wheelchair.svg +1 -1
- package/icons/material-symbols/wrench.svg +1 -1
- package/icons/material-symbols/x.svg +1 -1
- package/icons/phosphor/pack.json +8 -8
- package/lib/apps.mjs +142 -139
- package/lib/apps.test.d.mts +1 -0
- package/lib/apps.test.mjs +50 -0
- package/lib/blocks.mjs +90 -90
- package/lib/caseCollisions.d.mts +4 -0
- package/lib/caseCollisions.mjs +62 -0
- package/lib/caseCollisions.test.d.mts +1 -0
- package/lib/caseCollisions.test.mjs +74 -0
- package/lib/colors.ts +86 -86
- package/lib/contactBar.ts +18 -18
- package/lib/contracts.d.mts +15 -0
- package/lib/contracts.mjs +17 -0
- package/lib/cookieCategories.mjs +28 -28
- package/lib/crop.mjs +83 -83
- package/lib/csp.mjs +130 -130
- package/lib/cspSources.mjs +60 -60
- package/lib/dynamic-colors.ts +426 -426
- package/lib/git.mjs +64 -64
- package/lib/hooks.ts +51 -51
- package/lib/inline.ts +20 -20
- package/lib/license-texts/Apache-2.0.txt +190 -190
- package/lib/license-texts/BSD-2-Clause.txt +24 -24
- package/lib/license-texts/BSD-3-Clause.txt +28 -28
- package/lib/license-texts/ISC.txt +15 -15
- package/lib/license-texts/MIT.txt +21 -21
- package/lib/licenses.d.mts +1 -1
- package/lib/licenses.mjs +683 -683
- package/lib/markdown.ts +42 -42
- package/lib/motion-schemes.mjs +5 -5
- package/lib/motion.ts +131 -131
- package/lib/notice.mjs +34 -34
- package/lib/scheduled.mjs +37 -37
- package/lib/snackbar.ts +149 -149
- package/lib/status.mjs +18 -18
- package/lib/storeEntries.d.mts +18 -0
- package/lib/storeEntries.mjs +64 -0
- package/lib/storeEntries.test.d.mts +1 -0
- package/lib/storeEntries.test.mjs +71 -0
- package/lib/svg.mjs +231 -231
- package/lib/text-colors.mjs +43 -43
- package/lib/video-id.mjs +72 -72
- package/lib/video.mjs +91 -91
- package/package.json +136 -131
- package/pages/404.astro +10 -10
- package/pages/[...slug].astro +51 -51
- package/pages/_headers.txt +31 -31
- package/pages/blog/index.astro +10 -10
- package/pages/blog/kategori/[category].astro +18 -18
- package/pages/editor/code-images.json.js +25 -25
- package/pages/editor/index.astro +75 -75
- package/pages/editor/manifest.webmanifest.js +23 -23
- package/pages/editor/sw.js +33 -33
- package/pages/editor/sw.js.js +4 -4
- package/pages/editor/thumbnails.json.js +21 -21
- package/pages/editor/version.json.js +22 -22
- package/pages/headers.js +4 -4
- package/pages/index.astro +10 -10
- package/pages/ny.astro +6 -6
- package/pages/robots.txt.js +3 -3
- package/pages/rss.xml.js +20 -20
- package/scripts/app-screenshots.mjs +164 -164
- package/scripts/app-workers.mjs +6 -6
- package/scripts/bin.mjs +12 -12
- package/scripts/built-site.mjs +134 -99
- package/scripts/check-consent.mjs +113 -113
- package/scripts/check-content.mjs +151 -149
- package/scripts/check-demo.mjs +24 -24
- package/scripts/check-store.mjs +160 -0
- package/scripts/check-themes.mjs +45 -45
- package/scripts/check-worker.mjs +569 -569
- package/scripts/compare-html.mjs +81 -81
- package/scripts/declarations.mjs +10 -0
- package/scripts/declarations.test.mjs +32 -0
- package/scripts/demo-site.mjs +42 -42
- package/scripts/editor-icons.mjs +56 -56
- package/scripts/editor-roundtrip.mjs +134 -134
- package/scripts/editor-screens.mjs +101 -101
- package/scripts/git-env.mjs +9 -9
- package/scripts/icon-packs.mjs +151 -151
- package/scripts/link-counts.mjs +22 -22
- package/scripts/page-colors.mjs +42 -42
- package/scripts/releases.mjs +80 -80
- package/scripts/responsive-images.mjs +94 -94
- package/scripts/scheduled-list.mjs +15 -15
- package/scripts/theme-screenshots.mjs +88 -88
- package/scripts/types.mjs +17 -21
- package/scripts/video-thumbnails.mjs +89 -89
- package/scripts/worker-modules.mjs +34 -34
- package/site/appCookies.mjs +74 -74
- package/site/apps.ts +21 -21
- package/site/autolinks.mjs +200 -200
- package/site/blockScripts.ts +31 -31
- package/site/categories.ts +16 -16
- package/site/consent.ts +26 -26
- package/site/content.config.ts +84 -84
- package/site/content.ts +104 -104
- package/site/cookieList.ts +17 -17
- package/site/defineSite.d.mts +2 -14
- package/site/defineSite.mjs +131 -136
- package/site/directives/interact.mjs +31 -31
- package/site/editor-version.mjs +26 -26
- package/site/fill.ts +13 -13
- package/site/format.ts +11 -11
- package/site/home.ts +66 -66
- package/site/iconPacks.mjs +43 -43
- package/site/icons.mjs +605 -605
- package/site/images.mjs +157 -157
- package/site/motion.ts +13 -13
- package/site/noindex.mjs +21 -21
- package/site/pageColors.ts +110 -110
- package/site/popups.ts +98 -98
- package/site/schema.ts +189 -189
- package/site/sections.ts +3 -3
- package/site/seo.ts +28 -28
- package/site/site.ts +76 -76
- package/site/siteConfig.mjs +8 -8
- package/site/siteCsp.mjs +75 -75
- package/site/siteModules.mjs +27 -27
- package/site/standardPages.ts +59 -59
- package/site/storeTheme.d.mts +6 -0
- package/site/storeTheme.mjs +29 -0
- package/site/storeTheme.test.d.mts +1 -0
- package/site/storeTheme.test.mjs +53 -0
- package/site/testimonials.ts +48 -48
- package/site/text.ts +43 -43
- package/site/theme.mjs +483 -472
- package/site/tracking.client.ts +225 -225
- package/site/tracking.ts +9 -9
- package/src/ActionParts.tsx +205 -205
- package/src/Advanced.tsx +152 -152
- package/src/App.tsx +901 -904
- package/src/AppList.tsx +1362 -1345
- package/src/BlockDialog.tsx +360 -360
- package/src/BlockParts.tsx +652 -652
- package/src/BlockSettings.tsx +533 -533
- package/src/BrandForm.tsx +164 -164
- package/src/Colors.tsx +476 -476
- package/src/CommunicationParts.tsx +199 -199
- package/src/CompanyForm.tsx +209 -209
- package/src/Consents.tsx +175 -175
- package/src/ContainmentParts.tsx +185 -185
- package/src/Crop.tsx +152 -152
- package/src/DataForm.tsx +434 -434
- package/src/Deleted.tsx +177 -177
- package/src/EntryForm.tsx +740 -740
- package/src/FileView.tsx +30 -30
- package/src/FormFrame.tsx +237 -237
- package/src/Keywords.tsx +225 -225
- package/src/Library.tsx +1563 -1563
- package/src/Licenses.tsx +336 -336
- package/src/LinkSheet.tsx +138 -138
- package/src/MarkdownEditor.tsx +1075 -1075
- package/src/Media.tsx +677 -677
- package/src/NavigationParts.tsx +350 -350
- package/src/NavigationRestParts.tsx +247 -247
- package/src/Notifications.tsx +59 -59
- package/src/PagePicker.tsx +136 -136
- package/src/Parts.tsx +991 -991
- package/src/PartsShown.tsx +168 -168
- package/src/PickerParts.tsx +183 -183
- package/src/Redirects.tsx +450 -450
- package/src/Screen.tsx +184 -184
- package/src/SectionsField.tsx +65 -65
- package/src/SelectionParts.tsx +134 -134
- package/src/SeoScores.tsx +184 -184
- package/src/Sheet.tsx +35 -35
- package/src/ThemeStore.tsx +376 -376
- package/src/Tokens.tsx +346 -346
- package/src/Versions.tsx +214 -214
- package/src/addresses.ts +75 -75
- package/src/allIcons.ts +7 -7
- package/src/appFiles.ts +29 -29
- package/src/apps.ts +281 -281
- package/src/ask.tsx +123 -123
- package/src/blocks.ts +251 -251
- package/src/brand.ts +153 -153
- package/src/cache.ts +42 -42
- package/src/check.ts +285 -285
- package/src/checkSite.ts +256 -256
- package/src/{consents.ts → consentData.ts} +26 -26
- package/src/cookieList.ts +70 -70
- package/src/countries.ts +59 -59
- package/src/deploy.ts +155 -155
- package/src/editor.css +602 -602
- package/src/examples.ts +85 -85
- package/src/fields.tsx +942 -942
- package/src/frontmatter.mjs +42 -42
- package/src/github.ts +376 -376
- package/src/i18n/da-DK/advanced.ts +28 -28
- package/src/i18n/da-DK/app.ts +71 -71
- package/src/i18n/da-DK/apps.ts +90 -90
- package/src/i18n/da-DK/blockSettings.ts +38 -38
- package/src/i18n/da-DK/blocks.ts +408 -408
- package/src/i18n/da-DK/check.ts +25 -25
- package/src/i18n/da-DK/colors.ts +64 -64
- package/src/i18n/da-DK/common.ts +118 -118
- package/src/i18n/da-DK/company.ts +24 -24
- package/src/i18n/da-DK/consents.ts +23 -23
- package/src/i18n/da-DK/forms.ts +116 -116
- package/src/i18n/da-DK/history.ts +35 -35
- package/src/i18n/da-DK/index.ts +49 -49
- package/src/i18n/da-DK/keywords.ts +19 -19
- package/src/i18n/da-DK/library.ts +135 -135
- package/src/i18n/da-DK/licenses.ts +25 -25
- package/src/i18n/da-DK/media.ts +79 -79
- package/src/i18n/da-DK/parts.ts +696 -696
- package/src/i18n/da-DK/preview.ts +13 -13
- package/src/i18n/da-DK/redirects.ts +51 -51
- package/src/i18n/da-DK/seo.ts +20 -20
- package/src/i18n/da-DK/text.ts +35 -35
- package/src/i18n/da-DK/theme.ts +41 -41
- package/src/i18n/en-US/advanced.ts +32 -32
- package/src/i18n/en-US/app.ts +84 -84
- package/src/i18n/en-US/apps.ts +134 -134
- package/src/i18n/en-US/blockSettings.ts +48 -48
- package/src/i18n/en-US/blocks.ts +441 -441
- package/src/i18n/en-US/check.ts +31 -31
- package/src/i18n/en-US/colors.ts +82 -82
- package/src/i18n/en-US/common.ts +144 -144
- package/src/i18n/en-US/company.ts +25 -25
- package/src/i18n/en-US/consents.ts +25 -25
- package/src/i18n/en-US/forms.ts +145 -145
- package/src/i18n/en-US/history.ts +43 -43
- package/src/i18n/en-US/index.ts +51 -51
- package/src/i18n/en-US/keywords.ts +24 -24
- package/src/i18n/en-US/library.ts +176 -176
- package/src/i18n/en-US/licenses.ts +36 -36
- package/src/i18n/en-US/media.ts +80 -80
- package/src/i18n/en-US/parts.ts +718 -718
- package/src/i18n/en-US/preview.ts +15 -15
- package/src/i18n/en-US/redirects.ts +66 -66
- package/src/i18n/en-US/seo.ts +26 -26
- package/src/i18n/en-US/text.ts +41 -41
- package/src/i18n/en-US/theme.ts +41 -41
- package/src/i18n/index.ts +124 -124
- package/src/icon.tsx +160 -160
- package/src/iconPacks.ts +89 -89
- package/src/images.ts +163 -163
- package/src/{licenses.ts → licenseData.ts} +79 -79
- package/src/markdown.mjs +204 -204
- package/src/nav.ts +68 -68
- package/src/notFound.ts +28 -28
- package/src/{notifications.ts → notificationData.ts} +223 -223
- package/src/pagePreview.tsx +204 -204
- package/src/r2Caching.ts +55 -55
- package/src/{redirects.ts → redirectData.ts} +149 -149
- package/src/registry.tsx +188 -188
- package/src/schema.ts +417 -417
- package/src/sections.mjs +303 -303
- package/src/seo.ts +333 -333
- package/src/shared.ts +44 -44
- package/src/sidebar.ts +31 -31
- package/src/site.ts +317 -317
- package/src/standard/Block.astro +19 -19
- package/src/standard/BlockSite.astro +18 -18
- package/src/standard/accordion/index.tsx +47 -47
- package/src/standard/accordion/load.ts +23 -23
- package/src/standard/accordion/shared.ts +15 -15
- package/src/standard/accordion/view.ts +32 -32
- package/src/standard/buttons/fields.ts +98 -98
- package/src/standard/buttons/index.tsx +23 -23
- package/src/standard/buttons/load.ts +34 -34
- package/src/standard/buttons/shared.ts +12 -12
- package/src/standard/buttons/view.ts +30 -30
- package/src/standard/cards/index.tsx +71 -71
- package/src/standard/cards/load.ts +17 -17
- package/src/standard/cards/shared.ts +27 -27
- package/src/standard/cards/view.ts +29 -29
- package/src/standard/checklist/index.tsx +27 -27
- package/src/standard/checklist/load.ts +5 -5
- package/src/standard/checklist/view.ts +13 -13
- package/src/standard/cookies/index.tsx +17 -17
- package/src/standard/cookies/list.ts +56 -56
- package/src/standard/cookies/load.ts +11 -11
- package/src/standard/cookies/shared.ts +20 -20
- package/src/standard/cookies/view.ts +28 -28
- package/src/standard/faq/index.tsx +29 -29
- package/src/standard/faq/load.ts +14 -14
- package/src/standard/faq/view.ts +21 -21
- package/src/standard/head.ts +23 -23
- package/src/standard/hero/fields.ts +48 -48
- package/src/standard/hero/index.tsx +57 -57
- package/src/standard/hero/load.ts +23 -23
- package/src/standard/hero/view.ts +38 -38
- package/src/standard/links/index.tsx +41 -41
- package/src/standard/links/load.ts +17 -17
- package/src/standard/links/view.ts +25 -25
- package/src/standard/list/index.tsx +50 -50
- package/src/standard/list/load.ts +5 -5
- package/src/standard/list/view.ts +18 -18
- package/src/standard/load.ts +7 -7
- package/src/standard/notice/index.tsx +30 -30
- package/src/standard/notice/load.ts +16 -16
- package/src/standard/notice/view.ts +10 -10
- package/src/standard/points/index.tsx +42 -42
- package/src/standard/points/load.ts +5 -5
- package/src/standard/points/view.ts +12 -12
- package/src/standard/posts/index.tsx +132 -132
- package/src/standard/posts/load.ts +36 -36
- package/src/standard/posts/shared.ts +21 -21
- package/src/standard/posts/view.ts +63 -63
- package/src/standard/pricecards/index.tsx +57 -57
- package/src/standard/pricecards/load.ts +11 -11
- package/src/standard/pricecards/view.ts +50 -50
- package/src/standard/sharedTexts.ts +6 -6
- package/src/standard/site.ts +124 -124
- package/src/standard/siteLooks.ts +19 -19
- package/src/standard/sites.ts +13 -13
- package/src/standard/steps/index.tsx +28 -28
- package/src/standard/steps/load.ts +5 -5
- package/src/standard/steps/view.ts +11 -11
- package/src/standard/testimonials/index.tsx +87 -87
- package/src/standard/testimonials/list.ts +43 -43
- package/src/standard/testimonials/load.ts +58 -58
- package/src/standard/testimonials/shared.ts +13 -13
- package/src/standard/testimonials/view.ts +76 -76
- package/src/standard/themeBlocks.ts +13 -13
- package/src/standard/video/index.tsx +23 -23
- package/src/standard/video/load.ts +10 -10
- package/src/standard/video/view.ts +42 -42
- package/src/standard/view.tsx +105 -105
- package/src/standard/views.ts +37 -37
- package/src/store.ts +479 -499
- package/src/theme.ts +90 -90
- package/src/themeUi.ts +97 -97
- package/src/themes.ts +91 -91
- package/src/ui.tsx +3402 -3402
- package/src/virtual.d.ts +5 -5
- package/src/when.ts +22 -22
- package/starter/.githooks/pre-push +4 -4
- package/starter/.nvmrc +1 -1
- package/starter/.prettierignore +11 -11
- package/starter/.prettierrc.json +7 -7
- package/starter/README.md +27 -26
- package/starter/astro.config.mjs +5 -5
- package/starter/package.json +67 -67
- package/starter/public/_redirects +1 -1
- package/starter/public/favicon.svg +1 -1
- package/starter/public/logo.svg +1 -1
- package/starter/src/content/pages/blog.md +8 -8
- package/starter/src/content/pages/forside.md +9 -9
- package/starter/src/content/pages/kontakt.md +7 -7
- package/starter/src/content/pages/privatlivspolitik.md +9 -9
- package/starter/src/content/pages/siden-findes-ikke.md +7 -7
- package/starter/src/content.config.ts +2 -2
- package/starter/src/data/apps.json +1 -1
- package/starter/src/data/block-settings.json +3 -3
- package/starter/src/data/blocks.json +1 -1
- package/starter/src/data/booking.json +6 -6
- package/starter/src/data/colors.json +70 -70
- package/starter/src/data/contact.json +8 -8
- package/starter/src/data/cookies.json +21 -21
- package/starter/src/data/csp.json +1 -1
- package/starter/src/data/footer.json +15 -15
- package/starter/src/data/internal-links.json +4 -4
- package/starter/src/data/menu.json +4 -4
- package/starter/src/data/mobile-menu.json +4 -4
- package/starter/src/data/old-addresses.json +1 -1
- package/starter/src/data/redirects.json +1 -1
- package/starter/src/data/standard-pages.json +8 -8
- package/starter/src/data/theme.json +1 -1
- package/starter/src/env.d.ts +2 -2
- package/starter/src/site/config.ts +13 -13
- package/starter/src/site/worker.js +14 -14
- package/starter/tsconfig.json +17 -17
- package/starter/worker/apps.js +6 -6
- package/starter/wrangler.jsonc +39 -39
- package/theme/LICENSE +21 -21
- package/theme/blocks/ArrowLink.tsx +14 -14
- package/theme/blocks/BlockHead.tsx +26 -26
- package/theme/blocks/Panel.tsx +32 -32
- package/theme/blocks/Person.tsx +26 -26
- package/theme/blocks/accordion/PageRow.tsx +25 -25
- package/theme/blocks/accordion/View.tsx +17 -17
- package/theme/blocks/buttons/Buttons.tsx +28 -28
- package/theme/blocks/buttons/View.tsx +12 -12
- package/theme/blocks/cards/LinkCards.tsx +72 -72
- package/theme/blocks/cards/View.tsx +20 -20
- package/theme/blocks/checklist/Checks.tsx +15 -15
- package/theme/blocks/checklist/View.tsx +14 -14
- package/theme/blocks/cookies/View.tsx +36 -36
- package/theme/blocks/faq/Accordion.tsx +34 -34
- package/theme/blocks/faq/View.tsx +16 -16
- package/theme/blocks/faq/script.ts +56 -56
- package/theme/blocks/hero/View.tsx +44 -44
- package/theme/blocks/links/LinkGroups.tsx +24 -24
- package/theme/blocks/links/View.tsx +17 -17
- package/theme/blocks/list/Rows.tsx +24 -24
- package/theme/blocks/list/View.tsx +14 -14
- package/theme/blocks/notice/View.tsx +13 -13
- package/theme/blocks/points/PointList.tsx +34 -34
- package/theme/blocks/points/View.tsx +14 -14
- package/theme/blocks/posts/View.tsx +26 -26
- package/theme/blocks/pricecards/View.tsx +40 -40
- package/theme/blocks/steps/StepList.tsx +23 -23
- package/theme/blocks/steps/View.tsx +14 -14
- package/theme/blocks/testimonials/List.tsx +46 -46
- package/theme/blocks/testimonials/Slider.tsx +44 -44
- package/theme/blocks/testimonials/Testimonial.tsx +124 -124
- package/theme/blocks/testimonials/View.tsx +21 -21
- package/theme/blocks/testimonials/script.ts +52 -52
- package/theme/blocks/video/View.tsx +41 -41
- package/theme/components/ActionBar.astro +63 -63
- package/theme/components/AppSlot.astro +21 -21
- package/theme/components/ArrowLink.tsx +21 -21
- package/theme/components/BlogIndex.astro +48 -48
- package/theme/components/BlogPosts.astro +18 -18
- package/theme/components/BlogPosts.tsx +45 -45
- package/theme/components/BookButton.tsx +22 -22
- package/theme/components/Container.astro +11 -11
- package/theme/components/CookieConsent.astro +121 -121
- package/theme/components/EndBlock.astro +21 -21
- package/theme/components/FontPreload.astro +6 -6
- package/theme/components/Footer.astro +85 -85
- package/theme/components/Header.astro +186 -186
- package/theme/components/Icon.astro +16 -16
- package/theme/components/Img.astro +29 -29
- package/theme/components/Img.tsx +33 -33
- package/theme/components/Inline.astro +11 -11
- package/theme/components/Logo.astro +23 -23
- package/theme/components/LogoMark.astro +10 -10
- package/theme/components/Markdown.astro +17 -17
- package/theme/components/MobileMenu.tsx +224 -224
- package/theme/components/MobileNav.astro +33 -33
- package/theme/components/PageHeader.astro +22 -22
- package/theme/components/PageSection.astro +24 -24
- package/theme/components/PhoneButton.tsx +11 -11
- package/theme/components/Popup.astro +145 -145
- package/theme/components/PopupButton.tsx +24 -24
- package/theme/components/PopupView.tsx +48 -48
- package/theme/components/PostList.tsx +58 -58
- package/theme/components/PostMeta.tsx +17 -17
- package/theme/components/RichText.astro +28 -28
- package/theme/components/Section.astro +29 -29
- package/theme/components/SectionHeading.tsx +8 -8
- package/theme/components/TextSection.astro +16 -16
- package/theme/components/home/PageBlock.astro +19 -19
- package/theme/components/home/Text.astro +13 -13
- package/theme/components/loadPopup.ts +22 -22
- package/theme/components/loadPosts.ts +16 -16
- package/theme/components/posts.ts +15 -15
- package/theme/editor/looks.mjs +7 -7
- package/theme/editor/previews.tsx +56 -56
- package/theme/layouts/Base.astro +273 -273
- package/theme/layouts/PageLayout.astro +126 -126
- package/theme/layouts/PostLayout.astro +137 -137
- package/theme/scripts/motion.ts +29 -29
- package/theme/styles/base.css +320 -320
- package/theme/styles/blocks.css +1808 -1808
- package/theme/styles/components.css +206 -206
- package/theme/styles/global.css +1302 -1302
- package/theme/styles/ui.css +6544 -6544
- package/theme/templates/Blog.astro +38 -38
- package/theme/templates/Category.astro +19 -19
- package/theme/templates/Home.astro +36 -36
- package/theme/templates/New.astro +68 -68
- package/theme/templates/NotFound.astro +117 -117
- package/theme/theme.json +28 -28
- package/theme/ui/Badge.tsx +45 -45
- package/theme/ui/BottomSheet.tsx +148 -148
- package/theme/ui/Button.tsx +79 -79
- package/theme/ui/ButtonGroup.tsx +31 -31
- package/theme/ui/Card.tsx +45 -45
- package/theme/ui/Carousel.tsx +124 -124
- package/theme/ui/Checkbox.tsx +59 -59
- package/theme/ui/Chip.tsx +101 -101
- package/theme/ui/ChipSet.tsx +27 -27
- package/theme/ui/DatePicker.tsx +149 -149
- package/theme/ui/DatePickerContainer.tsx +316 -316
- package/theme/ui/Dialog.tsx +100 -100
- package/theme/ui/Divider.tsx +27 -27
- package/theme/ui/ExposedDropdownMenu.tsx +77 -77
- package/theme/ui/Fab.tsx +88 -88
- package/theme/ui/FabMenu.tsx +67 -67
- package/theme/ui/IconButton.tsx +78 -78
- package/theme/ui/List.tsx +287 -287
- package/theme/ui/LoadingIndicator.tsx +29 -29
- package/theme/ui/Menu.tsx +278 -278
- package/theme/ui/NavigationBar.tsx +62 -62
- package/theme/ui/NavigationDrawer.tsx +80 -80
- package/theme/ui/NavigationRail.tsx +154 -154
- package/theme/ui/ProgressIndicator.tsx +162 -162
- package/theme/ui/RadioButton.tsx +50 -50
- package/theme/ui/Search.tsx +129 -129
- package/theme/ui/SegmentedButton.tsx +49 -49
- package/theme/ui/SideSheet.tsx +54 -54
- package/theme/ui/Slider.tsx +155 -155
- package/theme/ui/Snackbar.tsx +67 -67
- package/theme/ui/SplitButton.tsx +67 -67
- package/theme/ui/Switch.tsx +60 -60
- package/theme/ui/Tabs.tsx +114 -114
- package/theme/ui/TextField.tsx +278 -278
- package/theme/ui/TimePicker.tsx +128 -128
- package/theme/ui/TimePickerContainer.tsx +234 -234
- package/theme/ui/Toolbar.tsx +67 -67
- package/theme/ui/Tooltip.tsx +58 -58
- package/theme/ui/TopAppBar.tsx +77 -77
- package/theme/ui/bottomSheet.script.ts +8 -8
- package/theme/ui/{bottomSheet.ts → bottomSheetControl.ts} +211 -211
- package/theme/ui/carousel.script.ts +28 -28
- package/theme/ui/carouselLayout.ts +229 -229
- package/theme/ui/checkbox.script.ts +8 -8
- package/theme/ui/datePicker.script.ts +302 -302
- package/theme/ui/dates.ts +185 -185
- package/theme/ui/{dialog.ts → dialogControl.ts} +48 -48
- package/theme/ui/dropdownMenu.script.ts +166 -166
- package/theme/ui/fabMenu.script.ts +57 -57
- package/theme/ui/navigationRail.script.ts +47 -47
- package/theme/ui/ripple.script.ts +159 -159
- package/theme/ui/search.script.ts +92 -92
- package/theme/ui/{sideSheet.ts → sideSheetControl.ts} +48 -48
- package/theme/ui/slider.script.ts +12 -12
- package/theme/ui/sliderUpdate.ts +52 -52
- package/theme/ui/snackbar.script.ts +16 -16
- package/theme/ui/splitButton.script.ts +63 -63
- package/theme/ui/tabs.script.ts +48 -48
- package/theme/ui/textField.script.ts +17 -17
- package/theme/ui/textFieldUpdate.ts +44 -44
- package/theme/ui/timePicker.script.ts +248 -248
- package/theme/ui/toolbar.script.ts +43 -43
- package/theme/ui/tooltip.script.ts +68 -68
- package/worker/consent.js +141 -141
- package/worker/editor-github.js +167 -167
- package/worker/editor-host.js +74 -74
- package/worker/files.js +401 -401
- package/worker/github.js +36 -36
- package/worker/index.js +188 -184
- package/worker/not-found.js +97 -97
- package/worker/oauth.js +95 -95
- package/worker/publish.js +52 -52
- package/worker/push.js +270 -270
- package/worker/r2-status.js +50 -50
- package/worker/session.js +58 -58
- package/worker/store.js +46 -0
package/src/FileView.tsx
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
// A file in Filer as it is shown (Media.tsx, and Deleted.tsx when it was deleted): a picture, a video
|
|
2
|
-
// to play, or a PDF to open and a zip file to download, each a button.
|
|
3
|
-
import { ArrowSquareOut, DownloadSimple, FilePdf, FileVideo, FileZip, type Icon } from '@phosphor-icons/react';
|
|
4
|
-
import { LinkButton } from './ui';
|
|
5
|
-
import { fileKindOf, type FileKind } from './images';
|
|
6
|
-
import { t } from './i18n';
|
|
7
|
-
|
|
8
|
-
/** The kind of a file in Filer, by its address: one in the folder of files, or else a picture. */
|
|
9
|
-
export type Kind = 'picture' | FileKind;
|
|
10
|
-
export const KINDS: Kind[] = ['picture', 'pdf', 'video', 'zip'];
|
|
11
|
-
export const kindOf = (src: string): Kind => fileKindOf(src) ?? 'picture';
|
|
12
|
-
|
|
13
|
-
/** The icon of a PDF, a video and a zip file, in the grid. */
|
|
14
|
-
export const fileIcons: Record<FileKind, Icon> = { pdf: FilePdf, video: FileVideo, zip: FileZip };
|
|
15
|
-
|
|
16
|
-
/** src: where the browser gets it (the site, or the bytes in this browser); name: its file name. */
|
|
17
|
-
export function FileView({ src, name }: { src: string; name: string }) {
|
|
18
|
-
const kind = kindOf(name);
|
|
19
|
-
if (kind === 'picture') return <img src={src} alt="" className="max-h-[45dvh] w-full rounded-xl bg-tint object-contain" />;
|
|
20
|
-
if (kind === 'video') return <video src={src} controls preload="metadata" className="max-h-[45dvh] w-full rounded-xl bg-tint" />;
|
|
21
|
-
return kind === 'pdf' ? (
|
|
22
|
-
<LinkButton href={src} target="_blank" rel="noopener" kind="outlined" wide icon={ArrowSquareOut}>
|
|
23
|
-
{t.media.openPdf}
|
|
24
|
-
</LinkButton>
|
|
25
|
-
) : (
|
|
26
|
-
<LinkButton href={src} download={name} kind="outlined" wide icon={DownloadSimple}>
|
|
27
|
-
{t.media.download}
|
|
28
|
-
</LinkButton>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
1
|
+
// A file in Filer as it is shown (Media.tsx, and Deleted.tsx when it was deleted): a picture, a video
|
|
2
|
+
// to play, or a PDF to open and a zip file to download, each a button.
|
|
3
|
+
import { ArrowSquareOut, DownloadSimple, FilePdf, FileVideo, FileZip, type Icon } from '@phosphor-icons/react';
|
|
4
|
+
import { LinkButton } from './ui';
|
|
5
|
+
import { fileKindOf, type FileKind } from './images';
|
|
6
|
+
import { t } from './i18n';
|
|
7
|
+
|
|
8
|
+
/** The kind of a file in Filer, by its address: one in the folder of files, or else a picture. */
|
|
9
|
+
export type Kind = 'picture' | FileKind;
|
|
10
|
+
export const KINDS: Kind[] = ['picture', 'pdf', 'video', 'zip'];
|
|
11
|
+
export const kindOf = (src: string): Kind => fileKindOf(src) ?? 'picture';
|
|
12
|
+
|
|
13
|
+
/** The icon of a PDF, a video and a zip file, in the grid. */
|
|
14
|
+
export const fileIcons: Record<FileKind, Icon> = { pdf: FilePdf, video: FileVideo, zip: FileZip };
|
|
15
|
+
|
|
16
|
+
/** src: where the browser gets it (the site, or the bytes in this browser); name: its file name. */
|
|
17
|
+
export function FileView({ src, name }: { src: string; name: string }) {
|
|
18
|
+
const kind = kindOf(name);
|
|
19
|
+
if (kind === 'picture') return <img src={src} alt="" className="max-h-[45dvh] w-full rounded-xl bg-tint object-contain" />;
|
|
20
|
+
if (kind === 'video') return <video src={src} controls preload="metadata" className="max-h-[45dvh] w-full rounded-xl bg-tint" />;
|
|
21
|
+
return kind === 'pdf' ? (
|
|
22
|
+
<LinkButton href={src} target="_blank" rel="noopener" kind="outlined" wide icon={ArrowSquareOut}>
|
|
23
|
+
{t.media.openPdf}
|
|
24
|
+
</LinkButton>
|
|
25
|
+
) : (
|
|
26
|
+
<LinkButton href={src} download={name} kind="outlined" wide icon={DownloadSimple}>
|
|
27
|
+
{t.media.download}
|
|
28
|
+
</LinkButton>
|
|
29
|
+
);
|
|
30
|
+
}
|
package/src/FormFrame.tsx
CHANGED
|
@@ -1,237 +1,237 @@
|
|
|
1
|
-
// What every form in the editor has around its fields: the top bar with the way back and a link to
|
|
2
|
-
// the page on the site, "Slet" below the fields where entries can be deleted, and a bar at the bottom
|
|
3
|
-
// of the screen with "Gem", which is only there when something has been changed. Messages are shown
|
|
4
|
-
// by the app (Env.notify); the first "Gem" asks whether the editor may also send notifications.
|
|
5
|
-
// Leaving with changes that are not saved asks first, to save them or not (in the editor's dialog;
|
|
6
|
-
// closing or reloading the page asks in the browser's own). "Forhåndsvis" beside "Gem" and in the top bar shows the page with
|
|
7
|
-
// what is in the form, before it is saved (pagePreview.tsx).
|
|
8
|
-
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
9
|
-
import { ArrowSquareOut, Binoculars, Trash } from '@phosphor-icons/react';
|
|
10
|
-
import Screen, { type Back } from './Screen';
|
|
11
|
-
import { Button, Group, IconButton, IconLink, LoadingIndicator, type TabChoice, ListItem } from './ui';
|
|
12
|
-
import { hasUnsaved, setUnsaved } from './deploy';
|
|
13
|
-
import { askToNotify } from './
|
|
14
|
-
import { choose } from './ask';
|
|
15
|
-
import { when } from './when';
|
|
16
|
-
import { useSidebar } from './sidebar';
|
|
17
|
-
import type { Env } from './App';
|
|
18
|
-
import { t } from './i18n';
|
|
19
|
-
|
|
20
|
-
import type { Edit } from './schema';
|
|
21
|
-
import type { EditorType } from './
|
|
22
|
-
|
|
23
|
-
// `kind`: what kind of notification it is (
|
|
24
|
-
// without it, it is only shown at the bottom (something going on, a form that cannot be saved).
|
|
25
|
-
export type Status = { text: string; error?: boolean; edit?: Edit; kind?: EditorType } | null;
|
|
26
|
-
|
|
27
|
-
type Props<T extends string> = {
|
|
28
|
-
title: string;
|
|
29
|
-
/** The way back; leaving asks first while something is unsaved, unless it has a `guard` of its own (a field's page
|
|
30
|
-
* goes back to its form, which keeps what is unsaved). */
|
|
31
|
-
back: Back;
|
|
32
|
-
status?: ReactNode;
|
|
33
|
-
view?: string;
|
|
34
|
-
/** More in the top bar, before the link to the site. */
|
|
35
|
-
actions?: ReactNode;
|
|
36
|
-
/** The icon of the form's navigation drawer, the last in the top bar (Screen). */
|
|
37
|
-
drawer?: ReactNode;
|
|
38
|
-
/** The form's tabs, in the top bar (Screen). */
|
|
39
|
-
tabs?: TabChoice<T>;
|
|
40
|
-
dirty: boolean;
|
|
41
|
-
saving: boolean;
|
|
42
|
-
// Deleting the entry: "Sletter" instead of "Gemmer".
|
|
43
|
-
deleting?: boolean;
|
|
44
|
-
/** Saves the form; true when it was saved (not when it said why not, or the owner chose not to). */
|
|
45
|
-
onSave: () => Promise<unknown>;
|
|
46
|
-
/** "Forhåndsvis" beside "Gem" and in the top bar (pagePreview.tsx). */
|
|
47
|
-
onPreview?: () => void;
|
|
48
|
-
onDelete?: () => void;
|
|
49
|
-
deleteLabel?: string;
|
|
50
|
-
/** The fields beside the text on a wide computer screen (FormColumns in ui.tsx). */
|
|
51
|
-
panel?: boolean;
|
|
52
|
-
/** A list (the menu): as wide as the other lists on a computer. */
|
|
53
|
-
wide?: boolean;
|
|
54
|
-
children: ReactNode;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// When a file was last saved (Env.savedAt), for the top bar: undefined while it is being asked, null
|
|
58
|
-
// when it is not known (a new file). Set it to the time of a save.
|
|
59
|
-
export function useSavedAt(env: Env, path: string) {
|
|
60
|
-
const [savedAt, setSavedAt] = useState<Date | null | undefined>(path ? undefined : null);
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (path)
|
|
63
|
-
env.savedAt([path]).then(
|
|
64
|
-
(t) => setSavedAt(t[path] ?? null),
|
|
65
|
-
() => setSavedAt(null),
|
|
66
|
-
);
|
|
67
|
-
}, []);
|
|
68
|
-
return [savedAt, setSavedAt] as const;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// The top bar's line under the title: deleting, saving, or when the file was last saved.
|
|
72
|
-
export function savedStatus({
|
|
73
|
-
savedAt,
|
|
74
|
-
saving,
|
|
75
|
-
deleting,
|
|
76
|
-
isNew,
|
|
77
|
-
}: {
|
|
78
|
-
savedAt?: Date | null;
|
|
79
|
-
saving: boolean;
|
|
80
|
-
deleting?: boolean;
|
|
81
|
-
isNew?: boolean;
|
|
82
|
-
}) {
|
|
83
|
-
if (deleting) return <LoadingIndicator>{t.app.busy.deleting}</LoadingIndicator>;
|
|
84
|
-
if (saving) return <LoadingIndicator>{t.app.busy.saving}</LoadingIndicator>;
|
|
85
|
-
if (savedAt) return t.app.lastSaved(when(savedAt));
|
|
86
|
-
return isNew ? t.app.notSavedYet : undefined;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// The open form's save (onSave, with the question about notifications), while something is unsaved.
|
|
90
|
-
let saveOpen: (() => Promise<boolean>) | null = null;
|
|
91
|
-
|
|
92
|
-
/** Asks, while something is unsaved ("Gem ændringerne?"), whether to save it first ("Gem", the main one) or leave
|
|
93
|
-
* it ("Kassér"); Esc stays. True when the editor may go on: nothing unsaved, it was saved, or it is left. A save that fails or stops (a field not filled in, an address that is taken) stays. */
|
|
94
|
-
export async function mayLeave() {
|
|
95
|
-
if (!hasUnsaved()) return true;
|
|
96
|
-
const answer = await choose({
|
|
97
|
-
title: t.app.unsavedTitle,
|
|
98
|
-
text: t.app.unsaved,
|
|
99
|
-
choices: [...(saveOpen ? [{ value: 'save', label: t.common.save }] : []), { value: 'discard', label: t.app.discard }],
|
|
100
|
-
// A choice to make: no "Annuller"; Esc stays on the screen (null), so nothing is lost.
|
|
101
|
-
cancel: false,
|
|
102
|
-
});
|
|
103
|
-
if (!answer) return false;
|
|
104
|
-
if (answer.choice === 'save' && !(await saveOpen?.())) return false;
|
|
105
|
-
setUnsaved(false);
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// The addresses of the open form and of its fields' pages of their own (Field.page, CompanyForm.tsx): the editor goes
|
|
110
|
-
// between them without leaving the form (App.tsx), so it keeps what is in it, saved or not, and asks nothing.
|
|
111
|
-
let formPages: string[] = [];
|
|
112
|
-
|
|
113
|
-
/** Keeps the addresses of the open form and of its fields' pages while it is open. */
|
|
114
|
-
export function useFormPages(pages: string[]) {
|
|
115
|
-
const key = pages.join('\n');
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
formPages = key.split('\n');
|
|
118
|
-
return () => {
|
|
119
|
-
formPages = [];
|
|
120
|
-
};
|
|
121
|
-
}, [key]);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Whether going from one address (a route) to another stays in the open form: to one of its fields' pages, or back. */
|
|
125
|
-
export const inForm = (from: string, to: string) => from !== to && formPages.includes(from) && formPages.includes(to);
|
|
126
|
-
|
|
127
|
-
// Goes to another screen of the editor (a hash, or a way there, e.g. nav.ts up), asking first while
|
|
128
|
-
// something is unsaved.
|
|
129
|
-
export function leaveTo(to: string | (() => void)) {
|
|
130
|
-
const go = typeof to === 'string' ? () => (location.hash = to) : to;
|
|
131
|
-
void mayLeave().then((yes) => yes && go());
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export default function FormFrame<T extends string>({
|
|
135
|
-
title,
|
|
136
|
-
back,
|
|
137
|
-
status,
|
|
138
|
-
view,
|
|
139
|
-
actions,
|
|
140
|
-
drawer,
|
|
141
|
-
tabs,
|
|
142
|
-
dirty,
|
|
143
|
-
saving,
|
|
144
|
-
deleting,
|
|
145
|
-
onSave,
|
|
146
|
-
onPreview,
|
|
147
|
-
onDelete,
|
|
148
|
-
deleteLabel,
|
|
149
|
-
panel,
|
|
150
|
-
wide,
|
|
151
|
-
children,
|
|
152
|
-
}: Props<T>) {
|
|
153
|
-
// On a computer the save bar starts where the sidebar (or its rail, sidebar.ts) ends.
|
|
154
|
-
const sidebar = useSidebar();
|
|
155
|
-
const save = useRef(onSave);
|
|
156
|
-
save.current = onSave;
|
|
157
|
-
useEffect(() => {
|
|
158
|
-
if (!dirty) return;
|
|
159
|
-
const warn = (e: BeforeUnloadEvent) => e.preventDefault();
|
|
160
|
-
window.addEventListener('beforeunload', warn);
|
|
161
|
-
// The editor does not reload itself to a newer version meanwhile (deploy.ts keepCurrent).
|
|
162
|
-
setUnsaved(true);
|
|
163
|
-
saveOpen = async () => {
|
|
164
|
-
askToNotify();
|
|
165
|
-
return (await save.current()) === true;
|
|
166
|
-
};
|
|
167
|
-
return () => {
|
|
168
|
-
window.removeEventListener('beforeunload', warn);
|
|
169
|
-
setUnsaved(false);
|
|
170
|
-
saveOpen = null;
|
|
171
|
-
};
|
|
172
|
-
}, [dirty]);
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<Screen
|
|
176
|
-
title={title}
|
|
177
|
-
status={status}
|
|
178
|
-
panel={panel}
|
|
179
|
-
wide={wide}
|
|
180
|
-
drawer={drawer}
|
|
181
|
-
tabs={tabs}
|
|
182
|
-
back={{ guard: leaveTo, ...back }}
|
|
183
|
-
actions={
|
|
184
|
-
<>
|
|
185
|
-
{actions}
|
|
186
|
-
{onPreview && <IconButton icon={Binoculars} label={t.preview.button} aria-haspopup="dialog" onClick={onPreview} />}
|
|
187
|
-
{view && <IconLink href={view} target="_blank" rel="noopener" label={t.app.viewOnSite} icon={ArrowSquareOut} />}
|
|
188
|
-
</>
|
|
189
|
-
}
|
|
190
|
-
>
|
|
191
|
-
<div className="space-y-6">{children}</div>
|
|
192
|
-
{onDelete && (
|
|
193
|
-
<div className="mt-12">
|
|
194
|
-
<Group>
|
|
195
|
-
<ListItem
|
|
196
|
-
tone="delete"
|
|
197
|
-
disabled={saving}
|
|
198
|
-
onClick={onDelete}
|
|
199
|
-
headline={
|
|
200
|
-
<span className="inline-flex items-center gap-3">
|
|
201
|
-
<Trash size={18} />
|
|
202
|
-
{deleting ? <LoadingIndicator>{t.app.busy.deleting}</LoadingIndicator> : (deleteLabel ?? t.common.delete)}
|
|
203
|
-
</span>
|
|
204
|
-
}
|
|
205
|
-
/>
|
|
206
|
-
</Group>
|
|
207
|
-
</div>
|
|
208
|
-
)}
|
|
209
|
-
{(dirty || saving) && (
|
|
210
|
-
<div
|
|
211
|
-
className={`editor-save-bar fixed inset-x-0 bottom-0 z-20 border-t border-line bg-surface/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md ${sidebar ? 'md:left-64' : 'md:left-18'}`}
|
|
212
|
-
>
|
|
213
|
-
<div className="mx-auto max-w-3xl px-4 py-3 md:px-6">
|
|
214
|
-
{/* "Forhåndsvis" beside "Gem". */}
|
|
215
|
-
<div className={onPreview ? 'grid grid-cols-[auto_1fr] gap-2' : ''}>
|
|
216
|
-
{onPreview && (
|
|
217
|
-
<Button kind="outlined" icon={Binoculars} aria-haspopup="dialog" onClick={onPreview}>
|
|
218
|
-
{t.preview.button}
|
|
219
|
-
</Button>
|
|
220
|
-
)}
|
|
221
|
-
<Button
|
|
222
|
-
wide
|
|
223
|
-
disabled={saving}
|
|
224
|
-
onClick={() => {
|
|
225
|
-
askToNotify();
|
|
226
|
-
void onSave();
|
|
227
|
-
}}
|
|
228
|
-
>
|
|
229
|
-
{saving ? <LoadingIndicator>{deleting ? t.app.busy.deleting : t.app.busy.saving}</LoadingIndicator> : t.common.saveChanges}
|
|
230
|
-
</Button>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
)}
|
|
235
|
-
</Screen>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
1
|
+
// What every form in the editor has around its fields: the top bar with the way back and a link to
|
|
2
|
+
// the page on the site, "Slet" below the fields where entries can be deleted, and a bar at the bottom
|
|
3
|
+
// of the screen with "Gem", which is only there when something has been changed. Messages are shown
|
|
4
|
+
// by the app (Env.notify); the first "Gem" asks whether the editor may also send notifications.
|
|
5
|
+
// Leaving with changes that are not saved asks first, to save them or not (in the editor's dialog;
|
|
6
|
+
// closing or reloading the page asks in the browser's own). "Forhåndsvis" beside "Gem" and in the top bar shows the page with
|
|
7
|
+
// what is in the form, before it is saved (pagePreview.tsx).
|
|
8
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
9
|
+
import { ArrowSquareOut, Binoculars, Trash } from '@phosphor-icons/react';
|
|
10
|
+
import Screen, { type Back } from './Screen';
|
|
11
|
+
import { Button, Group, IconButton, IconLink, LoadingIndicator, type TabChoice, ListItem } from './ui';
|
|
12
|
+
import { hasUnsaved, setUnsaved } from './deploy';
|
|
13
|
+
import { askToNotify } from './notificationData';
|
|
14
|
+
import { choose } from './ask';
|
|
15
|
+
import { when } from './when';
|
|
16
|
+
import { useSidebar } from './sidebar';
|
|
17
|
+
import type { Env } from './App';
|
|
18
|
+
import { t } from './i18n';
|
|
19
|
+
|
|
20
|
+
import type { Edit } from './schema';
|
|
21
|
+
import type { EditorType } from './notificationData';
|
|
22
|
+
|
|
23
|
+
// `kind`: what kind of notification it is (notificationData.ts: `site`, `saved`, `failed`), kept under "Notifikationer";
|
|
24
|
+
// without it, it is only shown at the bottom (something going on, a form that cannot be saved).
|
|
25
|
+
export type Status = { text: string; error?: boolean; edit?: Edit; kind?: EditorType } | null;
|
|
26
|
+
|
|
27
|
+
type Props<T extends string> = {
|
|
28
|
+
title: string;
|
|
29
|
+
/** The way back; leaving asks first while something is unsaved, unless it has a `guard` of its own (a field's page
|
|
30
|
+
* goes back to its form, which keeps what is unsaved). */
|
|
31
|
+
back: Back;
|
|
32
|
+
status?: ReactNode;
|
|
33
|
+
view?: string;
|
|
34
|
+
/** More in the top bar, before the link to the site. */
|
|
35
|
+
actions?: ReactNode;
|
|
36
|
+
/** The icon of the form's navigation drawer, the last in the top bar (Screen). */
|
|
37
|
+
drawer?: ReactNode;
|
|
38
|
+
/** The form's tabs, in the top bar (Screen). */
|
|
39
|
+
tabs?: TabChoice<T>;
|
|
40
|
+
dirty: boolean;
|
|
41
|
+
saving: boolean;
|
|
42
|
+
// Deleting the entry: "Sletter" instead of "Gemmer".
|
|
43
|
+
deleting?: boolean;
|
|
44
|
+
/** Saves the form; true when it was saved (not when it said why not, or the owner chose not to). */
|
|
45
|
+
onSave: () => Promise<unknown>;
|
|
46
|
+
/** "Forhåndsvis" beside "Gem" and in the top bar (pagePreview.tsx). */
|
|
47
|
+
onPreview?: () => void;
|
|
48
|
+
onDelete?: () => void;
|
|
49
|
+
deleteLabel?: string;
|
|
50
|
+
/** The fields beside the text on a wide computer screen (FormColumns in ui.tsx). */
|
|
51
|
+
panel?: boolean;
|
|
52
|
+
/** A list (the menu): as wide as the other lists on a computer. */
|
|
53
|
+
wide?: boolean;
|
|
54
|
+
children: ReactNode;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// When a file was last saved (Env.savedAt), for the top bar: undefined while it is being asked, null
|
|
58
|
+
// when it is not known (a new file). Set it to the time of a save.
|
|
59
|
+
export function useSavedAt(env: Env, path: string) {
|
|
60
|
+
const [savedAt, setSavedAt] = useState<Date | null | undefined>(path ? undefined : null);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (path)
|
|
63
|
+
env.savedAt([path]).then(
|
|
64
|
+
(t) => setSavedAt(t[path] ?? null),
|
|
65
|
+
() => setSavedAt(null),
|
|
66
|
+
);
|
|
67
|
+
}, []);
|
|
68
|
+
return [savedAt, setSavedAt] as const;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The top bar's line under the title: deleting, saving, or when the file was last saved.
|
|
72
|
+
export function savedStatus({
|
|
73
|
+
savedAt,
|
|
74
|
+
saving,
|
|
75
|
+
deleting,
|
|
76
|
+
isNew,
|
|
77
|
+
}: {
|
|
78
|
+
savedAt?: Date | null;
|
|
79
|
+
saving: boolean;
|
|
80
|
+
deleting?: boolean;
|
|
81
|
+
isNew?: boolean;
|
|
82
|
+
}) {
|
|
83
|
+
if (deleting) return <LoadingIndicator>{t.app.busy.deleting}</LoadingIndicator>;
|
|
84
|
+
if (saving) return <LoadingIndicator>{t.app.busy.saving}</LoadingIndicator>;
|
|
85
|
+
if (savedAt) return t.app.lastSaved(when(savedAt));
|
|
86
|
+
return isNew ? t.app.notSavedYet : undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The open form's save (onSave, with the question about notifications), while something is unsaved.
|
|
90
|
+
let saveOpen: (() => Promise<boolean>) | null = null;
|
|
91
|
+
|
|
92
|
+
/** Asks, while something is unsaved ("Gem ændringerne?"), whether to save it first ("Gem", the main one) or leave
|
|
93
|
+
* it ("Kassér"); Esc stays. True when the editor may go on: nothing unsaved, it was saved, or it is left. A save that fails or stops (a field not filled in, an address that is taken) stays. */
|
|
94
|
+
export async function mayLeave() {
|
|
95
|
+
if (!hasUnsaved()) return true;
|
|
96
|
+
const answer = await choose({
|
|
97
|
+
title: t.app.unsavedTitle,
|
|
98
|
+
text: t.app.unsaved,
|
|
99
|
+
choices: [...(saveOpen ? [{ value: 'save', label: t.common.save }] : []), { value: 'discard', label: t.app.discard }],
|
|
100
|
+
// A choice to make: no "Annuller"; Esc stays on the screen (null), so nothing is lost.
|
|
101
|
+
cancel: false,
|
|
102
|
+
});
|
|
103
|
+
if (!answer) return false;
|
|
104
|
+
if (answer.choice === 'save' && !(await saveOpen?.())) return false;
|
|
105
|
+
setUnsaved(false);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The addresses of the open form and of its fields' pages of their own (Field.page, CompanyForm.tsx): the editor goes
|
|
110
|
+
// between them without leaving the form (App.tsx), so it keeps what is in it, saved or not, and asks nothing.
|
|
111
|
+
let formPages: string[] = [];
|
|
112
|
+
|
|
113
|
+
/** Keeps the addresses of the open form and of its fields' pages while it is open. */
|
|
114
|
+
export function useFormPages(pages: string[]) {
|
|
115
|
+
const key = pages.join('\n');
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
formPages = key.split('\n');
|
|
118
|
+
return () => {
|
|
119
|
+
formPages = [];
|
|
120
|
+
};
|
|
121
|
+
}, [key]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Whether going from one address (a route) to another stays in the open form: to one of its fields' pages, or back. */
|
|
125
|
+
export const inForm = (from: string, to: string) => from !== to && formPages.includes(from) && formPages.includes(to);
|
|
126
|
+
|
|
127
|
+
// Goes to another screen of the editor (a hash, or a way there, e.g. nav.ts up), asking first while
|
|
128
|
+
// something is unsaved.
|
|
129
|
+
export function leaveTo(to: string | (() => void)) {
|
|
130
|
+
const go = typeof to === 'string' ? () => (location.hash = to) : to;
|
|
131
|
+
void mayLeave().then((yes) => yes && go());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default function FormFrame<T extends string>({
|
|
135
|
+
title,
|
|
136
|
+
back,
|
|
137
|
+
status,
|
|
138
|
+
view,
|
|
139
|
+
actions,
|
|
140
|
+
drawer,
|
|
141
|
+
tabs,
|
|
142
|
+
dirty,
|
|
143
|
+
saving,
|
|
144
|
+
deleting,
|
|
145
|
+
onSave,
|
|
146
|
+
onPreview,
|
|
147
|
+
onDelete,
|
|
148
|
+
deleteLabel,
|
|
149
|
+
panel,
|
|
150
|
+
wide,
|
|
151
|
+
children,
|
|
152
|
+
}: Props<T>) {
|
|
153
|
+
// On a computer the save bar starts where the sidebar (or its rail, sidebar.ts) ends.
|
|
154
|
+
const sidebar = useSidebar();
|
|
155
|
+
const save = useRef(onSave);
|
|
156
|
+
save.current = onSave;
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (!dirty) return;
|
|
159
|
+
const warn = (e: BeforeUnloadEvent) => e.preventDefault();
|
|
160
|
+
window.addEventListener('beforeunload', warn);
|
|
161
|
+
// The editor does not reload itself to a newer version meanwhile (deploy.ts keepCurrent).
|
|
162
|
+
setUnsaved(true);
|
|
163
|
+
saveOpen = async () => {
|
|
164
|
+
askToNotify();
|
|
165
|
+
return (await save.current()) === true;
|
|
166
|
+
};
|
|
167
|
+
return () => {
|
|
168
|
+
window.removeEventListener('beforeunload', warn);
|
|
169
|
+
setUnsaved(false);
|
|
170
|
+
saveOpen = null;
|
|
171
|
+
};
|
|
172
|
+
}, [dirty]);
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<Screen
|
|
176
|
+
title={title}
|
|
177
|
+
status={status}
|
|
178
|
+
panel={panel}
|
|
179
|
+
wide={wide}
|
|
180
|
+
drawer={drawer}
|
|
181
|
+
tabs={tabs}
|
|
182
|
+
back={{ guard: leaveTo, ...back }}
|
|
183
|
+
actions={
|
|
184
|
+
<>
|
|
185
|
+
{actions}
|
|
186
|
+
{onPreview && <IconButton icon={Binoculars} label={t.preview.button} aria-haspopup="dialog" onClick={onPreview} />}
|
|
187
|
+
{view && <IconLink href={view} target="_blank" rel="noopener" label={t.app.viewOnSite} icon={ArrowSquareOut} />}
|
|
188
|
+
</>
|
|
189
|
+
}
|
|
190
|
+
>
|
|
191
|
+
<div className="space-y-6">{children}</div>
|
|
192
|
+
{onDelete && (
|
|
193
|
+
<div className="mt-12">
|
|
194
|
+
<Group>
|
|
195
|
+
<ListItem
|
|
196
|
+
tone="delete"
|
|
197
|
+
disabled={saving}
|
|
198
|
+
onClick={onDelete}
|
|
199
|
+
headline={
|
|
200
|
+
<span className="inline-flex items-center gap-3">
|
|
201
|
+
<Trash size={18} />
|
|
202
|
+
{deleting ? <LoadingIndicator>{t.app.busy.deleting}</LoadingIndicator> : (deleteLabel ?? t.common.delete)}
|
|
203
|
+
</span>
|
|
204
|
+
}
|
|
205
|
+
/>
|
|
206
|
+
</Group>
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
{(dirty || saving) && (
|
|
210
|
+
<div
|
|
211
|
+
className={`editor-save-bar fixed inset-x-0 bottom-0 z-20 border-t border-line bg-surface/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md ${sidebar ? 'md:left-64' : 'md:left-18'}`}
|
|
212
|
+
>
|
|
213
|
+
<div className="mx-auto max-w-3xl px-4 py-3 md:px-6">
|
|
214
|
+
{/* "Forhåndsvis" beside "Gem". */}
|
|
215
|
+
<div className={onPreview ? 'grid grid-cols-[auto_1fr] gap-2' : ''}>
|
|
216
|
+
{onPreview && (
|
|
217
|
+
<Button kind="outlined" icon={Binoculars} aria-haspopup="dialog" onClick={onPreview}>
|
|
218
|
+
{t.preview.button}
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
<Button
|
|
222
|
+
wide
|
|
223
|
+
disabled={saving}
|
|
224
|
+
onClick={() => {
|
|
225
|
+
askToNotify();
|
|
226
|
+
void onSave();
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
{saving ? <LoadingIndicator>{deleting ? t.app.busy.deleting : t.app.busy.saving}</LoadingIndicator> : t.common.saveChanges}
|
|
230
|
+
</Button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
)}
|
|
235
|
+
</Screen>
|
|
236
|
+
);
|
|
237
|
+
}
|