@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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// "Ring 12 34 56 78" button (`label`, its text before the number: booking.json, "Blokke" › "Book en tid"), outlined
|
|
2
|
-
// (Button.tsx). In the editor only drawn.
|
|
3
|
-
import Button from '@theme/ui/Button';
|
|
4
|
-
|
|
5
|
-
export default function PhoneButton({ href, label, phone, className }: { href: string; label: string; phone: string; className?: string }) {
|
|
6
|
-
return (
|
|
7
|
-
<Button href={href} kind="outlined" icon="phone" className={className}>
|
|
8
|
-
{label} {phone}
|
|
9
|
-
</Button>
|
|
10
|
-
);
|
|
11
|
-
}
|
|
1
|
+
// "Ring 12 34 56 78" button (`label`, its text before the number: booking.json, "Blokke" › "Book en tid"), outlined
|
|
2
|
+
// (Button.tsx). In the editor only drawn.
|
|
3
|
+
import Button from '@theme/ui/Button';
|
|
4
|
+
|
|
5
|
+
export default function PhoneButton({ href, label, phone, className }: { href: string; label: string; phone: string; className?: string }) {
|
|
6
|
+
return (
|
|
7
|
+
<Button href={href} kind="outlined" icon="phone" className={className}>
|
|
8
|
+
{label} {phone}
|
|
9
|
+
</Button>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Pop-ups (packages/editor/site/popups.ts, "Indhold" › "Promovering" in the editor): those for this page, as a modal dialog in the
|
|
3
|
-
// middle of the screen over its scrim (dialogModalClass, ui/Dialog.tsx) with their box (PopupView.tsx, as in the
|
|
4
|
-
// editor: a picture at the top, a heading, a text and a button).
|
|
5
|
-
// A pop-up of a kind an app adds (`type`, e.g. Mailchimp's) has the app's part under the text instead
|
|
6
|
-
// (src/apps/<app>/popups/View.tsx), which shows the button where it wants; it can tell the script that the visitor is
|
|
7
|
-
// done (the event `popup:done`: not shown again) and that it cannot be shown (an element with `data-popup-off`: an app
|
|
8
|
-
// without its settings). The cross to close it is in the top right corner, over the picture. The script shows the
|
|
9
|
-
// first one the visitor has not closed, `delay` seconds after the page opens, once the page is scrolled `scroll` %
|
|
10
|
-
// down, or as the visitor is about to leave it (`trigger`), and not while the cookie banner is asking or another dialog
|
|
11
|
-
// is open. Closed (the cross, Escape, a tap outside it or its button), it is remembered in the browser and not shown to
|
|
12
|
-
// that visitor again by itself. A button that opens it ("Knapper": "Åbn promovering", `data-open="promotion:<file
|
|
13
|
-
// name>"`) opens it whenever it is tapped, also when it was closed before. Without JavaScript none is shown.
|
|
14
|
-
import PopupView, { type PopupPart } from '@theme/components/PopupView';
|
|
15
|
-
import { loadPopup } from '@theme/components/loadPopup';
|
|
16
|
-
import { dialogModalClass } from '@theme/ui/Dialog';
|
|
17
|
-
import { popupsAt, type Popup } from '@morphemecms/cms/site/popups';
|
|
18
|
-
|
|
19
|
-
// The kinds the apps add (src/apps/<app>/popups/: View.tsx and load.ts), by the app's id: what is under the text of one
|
|
20
|
-
// of that kind (Mailchimp: the signup form), given the button. Also when the app is no longer installed: what was made
|
|
21
|
-
// with it stays on the site. A kind of an app without that part is no pop-up, and shows itself (Calendly's booking
|
|
22
|
-
// sheet, in the app's slots/).
|
|
23
|
-
const byApp = <T,>(found: Record<string, T>) =>
|
|
24
|
-
Object.fromEntries(Object.entries(found).map(([path, m]) => [path.split('/apps/')[1].split('/')[0], m]));
|
|
25
|
-
const parts = byApp(import.meta.glob<{ default: PopupPart }>('/src/apps/*/popups/View.tsx', { eager: true }));
|
|
26
|
-
const loads = byApp(import.meta.glob<{ load: (popup: Popup) => unknown }>('/src/apps/*/popups/load.ts', { eager: true }));
|
|
27
|
-
const shown = await Promise.all(
|
|
28
|
-
(await popupsAt(Astro.url.pathname))
|
|
29
|
-
.filter((p) => p.type === 'standard' || (parts[p.type] && loads[p.type]))
|
|
30
|
-
.map(async (p) => ({ ...p, box: await loadPopup(p), Part: parts[p.type]?.default, part: loads[p.type]?.load(p) })),
|
|
31
|
-
);
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
{shown.map((p) => (
|
|
35
|
-
<dialog
|
|
36
|
-
class={dialogModalClass}
|
|
37
|
-
aria-labelledby={`popup-${p.slug}`}
|
|
38
|
-
data-popup={p.slug}
|
|
39
|
-
data-trigger={p.trigger}
|
|
40
|
-
data-delay={p.delay}
|
|
41
|
-
data-scroll={p.scroll}
|
|
42
|
-
>
|
|
43
|
-
<PopupView popup={p.box} Part={p.Part} part={p.part} />
|
|
44
|
-
</dialog>
|
|
45
|
-
))}
|
|
46
|
-
|
|
47
|
-
<script>
|
|
48
|
-
const KEY = 'popups-closed';
|
|
49
|
-
const closed = (): string[] => {
|
|
50
|
-
try {
|
|
51
|
-
return JSON.parse(localStorage.getItem(KEY) ?? '[]');
|
|
52
|
-
} catch {
|
|
53
|
-
return [];
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const remember = (slug: string) => {
|
|
57
|
-
try {
|
|
58
|
-
localStorage.setItem(KEY, JSON.stringify([...new Set([...closed(), slug])]));
|
|
59
|
-
} catch {}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Those that can be shown (an app's that cannot, without its settings, says so with data-popup-off).
|
|
63
|
-
const all = [...document.querySelectorAll<HTMLDialogElement>('dialog[data-popup]')].filter((d) => !d.querySelector('[data-popup-off]'));
|
|
64
|
-
for (const d of all) {
|
|
65
|
-
const close = () => d.open && d.close();
|
|
66
|
-
d.addEventListener('close', () => remember(d.dataset.popup!));
|
|
67
|
-
// An app's kind says the visitor is done (signed up): not shown again, also before it is closed.
|
|
68
|
-
d.addEventListener('popup:done', () => remember(d.dataset.popup!));
|
|
69
|
-
d.querySelector('[data-popup-close]')?.addEventListener('click', close);
|
|
70
|
-
// A tap on the dimmed backdrop (outside the box) closes it.
|
|
71
|
-
d.addEventListener('click', (e) => e.target === d && close());
|
|
72
|
-
// Its button closes it first, so what it opens (the page, or an app's sheet over it) opens alone.
|
|
73
|
-
d.querySelectorAll('a').forEach((a) => a.addEventListener('click', close));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// A button that opens one of them, whenever it is tapped (not with a key held: a new tab).
|
|
77
|
-
document.addEventListener('click', (e) => {
|
|
78
|
-
const link = (e.target as Element | null)?.closest?.<HTMLElement>('[data-open^="promotion:"]');
|
|
79
|
-
const d = link && all.find((p) => `promotion:${p.dataset.popup}` === link.dataset.open);
|
|
80
|
-
if (!d || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || (e as MouseEvent).button !== 0) return;
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
all.forEach((p) => p.open && p.close());
|
|
83
|
-
d.showModal();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// The one that opens by itself: the first the visitor has not closed.
|
|
87
|
-
const popup = all.find((d) => !closed().includes(d.dataset.popup!));
|
|
88
|
-
if (popup) {
|
|
89
|
-
// Not over the cookie banner (CookieConsent.astro), an app's dialog (a booking sheet) or another pop-up
|
|
90
|
-
// (an app's, src/apps/): it waits until they are answered and closed.
|
|
91
|
-
const busy = () =>
|
|
92
|
-
document.getElementById('cookie-consent')?.hidden === false ||
|
|
93
|
-
[...document.querySelectorAll('dialog')].some((d) => d !== popup && d.open);
|
|
94
|
-
const show = () => {
|
|
95
|
-
if (popup.open) return;
|
|
96
|
-
if (!busy()) return popup.showModal();
|
|
97
|
-
setTimeout(show, 1000);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// How far down the page is scrolled, 0 to 1 (1 for a page too short to scroll).
|
|
101
|
-
const down = () => {
|
|
102
|
-
const max = document.documentElement.scrollHeight - innerHeight;
|
|
103
|
-
return max > 0 ? scrollY / max : 1;
|
|
104
|
-
};
|
|
105
|
-
const trigger = popup.dataset.trigger;
|
|
106
|
-
if (trigger === 'scroll') {
|
|
107
|
-
const at = Math.min(Number(popup.dataset.scroll) / 100, 0.98);
|
|
108
|
-
const check = () => {
|
|
109
|
-
if (down() < at) return;
|
|
110
|
-
removeEventListener('scroll', check);
|
|
111
|
-
show();
|
|
112
|
-
};
|
|
113
|
-
addEventListener('scroll', check, { passive: true });
|
|
114
|
-
// A page too short to scroll (or opened further down) gets it without a scroll.
|
|
115
|
-
setTimeout(check, 1000);
|
|
116
|
-
} else if (trigger === 'exit') {
|
|
117
|
-
if (matchMedia('(hover: hover) and (pointer: fine)').matches) {
|
|
118
|
-
// The mouse leaves the window at the top: towards the tabs, the address bar or the back button.
|
|
119
|
-
const leave = (e: MouseEvent) => {
|
|
120
|
-
if (e.relatedTarget || e.clientY > 0) return;
|
|
121
|
-
document.removeEventListener('mouseout', leave);
|
|
122
|
-
show();
|
|
123
|
-
};
|
|
124
|
-
document.addEventListener('mouseout', leave);
|
|
125
|
-
} else {
|
|
126
|
-
// A phone has no mouse: a quick scroll back up, once the visitor has scrolled a screen down.
|
|
127
|
-
let mark = { y: scrollY, t: performance.now() };
|
|
128
|
-
let deepest = scrollY;
|
|
129
|
-
const flick = () => {
|
|
130
|
-
const now = performance.now();
|
|
131
|
-
deepest = Math.max(deepest, scrollY);
|
|
132
|
-
if (now - mark.t < 150) return;
|
|
133
|
-
const speed = (mark.y - scrollY) / (now - mark.t);
|
|
134
|
-
mark = { y: scrollY, t: now };
|
|
135
|
-
if (deepest < innerHeight || speed < 1.5) return;
|
|
136
|
-
removeEventListener('scroll', flick);
|
|
137
|
-
show();
|
|
138
|
-
};
|
|
139
|
-
addEventListener('scroll', flick, { passive: true });
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
setTimeout(show, Number(popup.dataset.delay) * 1000);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
</script>
|
|
1
|
+
---
|
|
2
|
+
// Pop-ups (packages/editor/site/popups.ts, "Indhold" › "Promovering" in the editor): those for this page, as a modal dialog in the
|
|
3
|
+
// middle of the screen over its scrim (dialogModalClass, ui/Dialog.tsx) with their box (PopupView.tsx, as in the
|
|
4
|
+
// editor: a picture at the top, a heading, a text and a button).
|
|
5
|
+
// A pop-up of a kind an app adds (`type`, e.g. Mailchimp's) has the app's part under the text instead
|
|
6
|
+
// (src/apps/<app>/popups/View.tsx), which shows the button where it wants; it can tell the script that the visitor is
|
|
7
|
+
// done (the event `popup:done`: not shown again) and that it cannot be shown (an element with `data-popup-off`: an app
|
|
8
|
+
// without its settings). The cross to close it is in the top right corner, over the picture. The script shows the
|
|
9
|
+
// first one the visitor has not closed, `delay` seconds after the page opens, once the page is scrolled `scroll` %
|
|
10
|
+
// down, or as the visitor is about to leave it (`trigger`), and not while the cookie banner is asking or another dialog
|
|
11
|
+
// is open. Closed (the cross, Escape, a tap outside it or its button), it is remembered in the browser and not shown to
|
|
12
|
+
// that visitor again by itself. A button that opens it ("Knapper": "Åbn promovering", `data-open="promotion:<file
|
|
13
|
+
// name>"`) opens it whenever it is tapped, also when it was closed before. Without JavaScript none is shown.
|
|
14
|
+
import PopupView, { type PopupPart } from '@theme/components/PopupView';
|
|
15
|
+
import { loadPopup } from '@theme/components/loadPopup';
|
|
16
|
+
import { dialogModalClass } from '@theme/ui/Dialog';
|
|
17
|
+
import { popupsAt, type Popup } from '@morphemecms/cms/site/popups';
|
|
18
|
+
|
|
19
|
+
// The kinds the apps add (src/apps/<app>/popups/: View.tsx and load.ts), by the app's id: what is under the text of one
|
|
20
|
+
// of that kind (Mailchimp: the signup form), given the button. Also when the app is no longer installed: what was made
|
|
21
|
+
// with it stays on the site. A kind of an app without that part is no pop-up, and shows itself (Calendly's booking
|
|
22
|
+
// sheet, in the app's slots/).
|
|
23
|
+
const byApp = <T,>(found: Record<string, T>) =>
|
|
24
|
+
Object.fromEntries(Object.entries(found).map(([path, m]) => [path.split('/apps/')[1].split('/')[0], m]));
|
|
25
|
+
const parts = byApp(import.meta.glob<{ default: PopupPart }>('/src/apps/*/popups/View.tsx', { eager: true }));
|
|
26
|
+
const loads = byApp(import.meta.glob<{ load: (popup: Popup) => unknown }>('/src/apps/*/popups/load.ts', { eager: true }));
|
|
27
|
+
const shown = await Promise.all(
|
|
28
|
+
(await popupsAt(Astro.url.pathname))
|
|
29
|
+
.filter((p) => p.type === 'standard' || (parts[p.type] && loads[p.type]))
|
|
30
|
+
.map(async (p) => ({ ...p, box: await loadPopup(p), Part: parts[p.type]?.default, part: loads[p.type]?.load(p) })),
|
|
31
|
+
);
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
{shown.map((p) => (
|
|
35
|
+
<dialog
|
|
36
|
+
class={dialogModalClass}
|
|
37
|
+
aria-labelledby={`popup-${p.slug}`}
|
|
38
|
+
data-popup={p.slug}
|
|
39
|
+
data-trigger={p.trigger}
|
|
40
|
+
data-delay={p.delay}
|
|
41
|
+
data-scroll={p.scroll}
|
|
42
|
+
>
|
|
43
|
+
<PopupView popup={p.box} Part={p.Part} part={p.part} />
|
|
44
|
+
</dialog>
|
|
45
|
+
))}
|
|
46
|
+
|
|
47
|
+
<script>
|
|
48
|
+
const KEY = 'popups-closed';
|
|
49
|
+
const closed = (): string[] => {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(localStorage.getItem(KEY) ?? '[]');
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const remember = (slug: string) => {
|
|
57
|
+
try {
|
|
58
|
+
localStorage.setItem(KEY, JSON.stringify([...new Set([...closed(), slug])]));
|
|
59
|
+
} catch {}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Those that can be shown (an app's that cannot, without its settings, says so with data-popup-off).
|
|
63
|
+
const all = [...document.querySelectorAll<HTMLDialogElement>('dialog[data-popup]')].filter((d) => !d.querySelector('[data-popup-off]'));
|
|
64
|
+
for (const d of all) {
|
|
65
|
+
const close = () => d.open && d.close();
|
|
66
|
+
d.addEventListener('close', () => remember(d.dataset.popup!));
|
|
67
|
+
// An app's kind says the visitor is done (signed up): not shown again, also before it is closed.
|
|
68
|
+
d.addEventListener('popup:done', () => remember(d.dataset.popup!));
|
|
69
|
+
d.querySelector('[data-popup-close]')?.addEventListener('click', close);
|
|
70
|
+
// A tap on the dimmed backdrop (outside the box) closes it.
|
|
71
|
+
d.addEventListener('click', (e) => e.target === d && close());
|
|
72
|
+
// Its button closes it first, so what it opens (the page, or an app's sheet over it) opens alone.
|
|
73
|
+
d.querySelectorAll('a').forEach((a) => a.addEventListener('click', close));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// A button that opens one of them, whenever it is tapped (not with a key held: a new tab).
|
|
77
|
+
document.addEventListener('click', (e) => {
|
|
78
|
+
const link = (e.target as Element | null)?.closest?.<HTMLElement>('[data-open^="promotion:"]');
|
|
79
|
+
const d = link && all.find((p) => `promotion:${p.dataset.popup}` === link.dataset.open);
|
|
80
|
+
if (!d || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || (e as MouseEvent).button !== 0) return;
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
all.forEach((p) => p.open && p.close());
|
|
83
|
+
d.showModal();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// The one that opens by itself: the first the visitor has not closed.
|
|
87
|
+
const popup = all.find((d) => !closed().includes(d.dataset.popup!));
|
|
88
|
+
if (popup) {
|
|
89
|
+
// Not over the cookie banner (CookieConsent.astro), an app's dialog (a booking sheet) or another pop-up
|
|
90
|
+
// (an app's, src/apps/): it waits until they are answered and closed.
|
|
91
|
+
const busy = () =>
|
|
92
|
+
document.getElementById('cookie-consent')?.hidden === false ||
|
|
93
|
+
[...document.querySelectorAll('dialog')].some((d) => d !== popup && d.open);
|
|
94
|
+
const show = () => {
|
|
95
|
+
if (popup.open) return;
|
|
96
|
+
if (!busy()) return popup.showModal();
|
|
97
|
+
setTimeout(show, 1000);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// How far down the page is scrolled, 0 to 1 (1 for a page too short to scroll).
|
|
101
|
+
const down = () => {
|
|
102
|
+
const max = document.documentElement.scrollHeight - innerHeight;
|
|
103
|
+
return max > 0 ? scrollY / max : 1;
|
|
104
|
+
};
|
|
105
|
+
const trigger = popup.dataset.trigger;
|
|
106
|
+
if (trigger === 'scroll') {
|
|
107
|
+
const at = Math.min(Number(popup.dataset.scroll) / 100, 0.98);
|
|
108
|
+
const check = () => {
|
|
109
|
+
if (down() < at) return;
|
|
110
|
+
removeEventListener('scroll', check);
|
|
111
|
+
show();
|
|
112
|
+
};
|
|
113
|
+
addEventListener('scroll', check, { passive: true });
|
|
114
|
+
// A page too short to scroll (or opened further down) gets it without a scroll.
|
|
115
|
+
setTimeout(check, 1000);
|
|
116
|
+
} else if (trigger === 'exit') {
|
|
117
|
+
if (matchMedia('(hover: hover) and (pointer: fine)').matches) {
|
|
118
|
+
// The mouse leaves the window at the top: towards the tabs, the address bar or the back button.
|
|
119
|
+
const leave = (e: MouseEvent) => {
|
|
120
|
+
if (e.relatedTarget || e.clientY > 0) return;
|
|
121
|
+
document.removeEventListener('mouseout', leave);
|
|
122
|
+
show();
|
|
123
|
+
};
|
|
124
|
+
document.addEventListener('mouseout', leave);
|
|
125
|
+
} else {
|
|
126
|
+
// A phone has no mouse: a quick scroll back up, once the visitor has scrolled a screen down.
|
|
127
|
+
let mark = { y: scrollY, t: performance.now() };
|
|
128
|
+
let deepest = scrollY;
|
|
129
|
+
const flick = () => {
|
|
130
|
+
const now = performance.now();
|
|
131
|
+
deepest = Math.max(deepest, scrollY);
|
|
132
|
+
if (now - mark.t < 150) return;
|
|
133
|
+
const speed = (mark.y - scrollY) / (now - mark.t);
|
|
134
|
+
mark = { y: scrollY, t: now };
|
|
135
|
+
if (deepest < innerHeight || speed < 1.5) return;
|
|
136
|
+
removeEventListener('scroll', flick);
|
|
137
|
+
show();
|
|
138
|
+
};
|
|
139
|
+
addEventListener('scroll', flick, { passive: true });
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
setTimeout(show, Number(popup.dataset.delay) * 1000);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
// The button of a pop-up (PopupView.tsx): the booking sheet, the phone or a page, as the buttons of the blocks
|
|
2
|
-
// (Buttons.tsx), or a PDF to download in the same look. A pop-up of an app's kind (a Mailchimp signup) gets it and shows
|
|
3
|
-
// it where it wants (after the signup).
|
|
4
|
-
import Buttons from '@theme/blocks/buttons/Buttons';
|
|
5
|
-
import type { ButtonView } from '@morphemecms/cms/src/standard/buttons/view';
|
|
6
|
-
import Button from '@theme/ui/Button';
|
|
7
|
-
|
|
8
|
-
/** Its button: a PDF to download (its address and text), or the buttons' view (one). */
|
|
9
|
-
export type PopupButtonView = { file: { href: string; label: string } } | { buttons: ButtonView[] };
|
|
10
|
-
|
|
11
|
-
export default function PopupButton({ button }: { button?: PopupButtonView }) {
|
|
12
|
-
if (!button) return null;
|
|
13
|
-
return (
|
|
14
|
-
<div className="block-buttons mt-6">
|
|
15
|
-
{'file' in button ? (
|
|
16
|
-
<Button href={button.file.href} icon="download-simple" download="" className="w-full sm:w-auto">
|
|
17
|
-
{button.file.label}
|
|
18
|
-
</Button>
|
|
19
|
-
) : (
|
|
20
|
-
<Buttons buttons={button.buttons} className="w-full sm:w-auto" />
|
|
21
|
-
)}
|
|
22
|
-
</div>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
1
|
+
// The button of a pop-up (PopupView.tsx): the booking sheet, the phone or a page, as the buttons of the blocks
|
|
2
|
+
// (Buttons.tsx), or a PDF to download in the same look. A pop-up of an app's kind (a Mailchimp signup) gets it and shows
|
|
3
|
+
// it where it wants (after the signup).
|
|
4
|
+
import Buttons from '@theme/blocks/buttons/Buttons';
|
|
5
|
+
import type { ButtonView } from '@morphemecms/cms/src/standard/buttons/view';
|
|
6
|
+
import Button from '@theme/ui/Button';
|
|
7
|
+
|
|
8
|
+
/** Its button: a PDF to download (its address and text), or the buttons' view (one). */
|
|
9
|
+
export type PopupButtonView = { file: { href: string; label: string } } | { buttons: ButtonView[] };
|
|
10
|
+
|
|
11
|
+
export default function PopupButton({ button }: { button?: PopupButtonView }) {
|
|
12
|
+
if (!button) return null;
|
|
13
|
+
return (
|
|
14
|
+
<div className="block-buttons mt-6">
|
|
15
|
+
{'file' in button ? (
|
|
16
|
+
<Button href={button.file.href} icon="download-simple" download="" className="w-full sm:w-auto">
|
|
17
|
+
{button.file.label}
|
|
18
|
+
</Button>
|
|
19
|
+
) : (
|
|
20
|
+
<Buttons buttons={button.buttons} className="w-full sm:w-auto" />
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
// A pop-up's box, a dialog (Dialog.tsx; Popup.astro shows it modally on the site, the editor draws it beside its
|
|
2
|
-
// form): the cross to close it in the top right corner (a tonal icon button, as it can stand on a picture), over the
|
|
3
|
-
// picture at the top (to the edges in 3:2, as on the cards with a picture), the heading, the text and its button
|
|
4
|
-
// (PopupButton.tsx). A pop-up of a kind an app adds (`type`, e.g. Mailchimp's) has the app's part under the text
|
|
5
|
-
// instead (`Part`, src/apps/<app>/popups/View.tsx, given its view and the button), which shows the button where it
|
|
6
|
-
// wants.
|
|
7
|
-
import type { ComponentType, ReactNode } from 'react';
|
|
8
|
-
import type { Picture } from '@morphemecms/cms/src/standard/view';
|
|
9
|
-
import Dialog from '@theme/ui/Dialog';
|
|
10
|
-
import IconButton from '@theme/ui/IconButton';
|
|
11
|
-
import PopupButton, { type PopupButtonView } from '@theme/components/PopupButton';
|
|
12
|
-
import { siteT } from '@morphemecms/cms/src/i18n';
|
|
13
|
-
|
|
14
|
-
export type PopupBoxView = { slug: string; image?: Picture; heading: string; text: string[]; button?: PopupButtonView };
|
|
15
|
-
|
|
16
|
-
/** An app's part of a pop-up: its view, and the pop-up's own button to show where it wants. */
|
|
17
|
-
export type PopupPart<V = any> = ComponentType<{ view: V; button: ReactNode }>;
|
|
18
|
-
|
|
19
|
-
export default function PopupView({ popup, Part, part }: { popup: PopupBoxView; Part?: PopupPart; part?: unknown }) {
|
|
20
|
-
const button = <PopupButton button={popup.button} />;
|
|
21
|
-
return (
|
|
22
|
-
<Dialog className="relative overflow-hidden">
|
|
23
|
-
<IconButton icon="x" label={siteT.theme.close} kind="tonal" className="absolute top-3 right-3 z-10" data-popup-close="true" />
|
|
24
|
-
{popup.image && (
|
|
25
|
-
<img
|
|
26
|
-
src={popup.image.src}
|
|
27
|
-
srcSet={popup.image.srcset}
|
|
28
|
-
width={popup.image.width}
|
|
29
|
-
height={popup.image.height}
|
|
30
|
-
sizes="(min-width: 480px) 448px, calc(100vw - 2rem)"
|
|
31
|
-
alt=""
|
|
32
|
-
className="block aspect-[3/2] w-full bg-tint object-cover"
|
|
33
|
-
/>
|
|
34
|
-
)}
|
|
35
|
-
<div className="p-6 md:p-8">
|
|
36
|
-
<h2 id={`popup-${popup.slug}`} className={popup.image ? 'type-headline-small' : 'pr-12 type-headline-small'}>
|
|
37
|
-
{popup.heading}
|
|
38
|
-
</h2>
|
|
39
|
-
{popup.text.map((t, i) => (
|
|
40
|
-
<p key={i} className="mt-3 text-muted">
|
|
41
|
-
{t}
|
|
42
|
-
</p>
|
|
43
|
-
))}
|
|
44
|
-
{Part ? <Part view={part} button={button} /> : button}
|
|
45
|
-
</div>
|
|
46
|
-
</Dialog>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
1
|
+
// A pop-up's box, a dialog (Dialog.tsx; Popup.astro shows it modally on the site, the editor draws it beside its
|
|
2
|
+
// form): the cross to close it in the top right corner (a tonal icon button, as it can stand on a picture), over the
|
|
3
|
+
// picture at the top (to the edges in 3:2, as on the cards with a picture), the heading, the text and its button
|
|
4
|
+
// (PopupButton.tsx). A pop-up of a kind an app adds (`type`, e.g. Mailchimp's) has the app's part under the text
|
|
5
|
+
// instead (`Part`, src/apps/<app>/popups/View.tsx, given its view and the button), which shows the button where it
|
|
6
|
+
// wants.
|
|
7
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
8
|
+
import type { Picture } from '@morphemecms/cms/src/standard/view';
|
|
9
|
+
import Dialog from '@theme/ui/Dialog';
|
|
10
|
+
import IconButton from '@theme/ui/IconButton';
|
|
11
|
+
import PopupButton, { type PopupButtonView } from '@theme/components/PopupButton';
|
|
12
|
+
import { siteT } from '@morphemecms/cms/src/i18n';
|
|
13
|
+
|
|
14
|
+
export type PopupBoxView = { slug: string; image?: Picture; heading: string; text: string[]; button?: PopupButtonView };
|
|
15
|
+
|
|
16
|
+
/** An app's part of a pop-up: its view, and the pop-up's own button to show where it wants. */
|
|
17
|
+
export type PopupPart<V = any> = ComponentType<{ view: V; button: ReactNode }>;
|
|
18
|
+
|
|
19
|
+
export default function PopupView({ popup, Part, part }: { popup: PopupBoxView; Part?: PopupPart; part?: unknown }) {
|
|
20
|
+
const button = <PopupButton button={popup.button} />;
|
|
21
|
+
return (
|
|
22
|
+
<Dialog className="relative overflow-hidden">
|
|
23
|
+
<IconButton icon="x" label={siteT.theme.close} kind="tonal" className="absolute top-3 right-3 z-10" data-popup-close="true" />
|
|
24
|
+
{popup.image && (
|
|
25
|
+
<img
|
|
26
|
+
src={popup.image.src}
|
|
27
|
+
srcSet={popup.image.srcset}
|
|
28
|
+
width={popup.image.width}
|
|
29
|
+
height={popup.image.height}
|
|
30
|
+
sizes="(min-width: 480px) 448px, calc(100vw - 2rem)"
|
|
31
|
+
alt=""
|
|
32
|
+
className="block aspect-[3/2] w-full bg-tint object-cover"
|
|
33
|
+
/>
|
|
34
|
+
)}
|
|
35
|
+
<div className="p-6 md:p-8">
|
|
36
|
+
<h2 id={`popup-${popup.slug}`} className={popup.image ? 'type-headline-small' : 'pr-12 type-headline-small'}>
|
|
37
|
+
{popup.heading}
|
|
38
|
+
</h2>
|
|
39
|
+
{popup.text.map((t, i) => (
|
|
40
|
+
<p key={i} className="mt-3 text-muted">
|
|
41
|
+
{t}
|
|
42
|
+
</p>
|
|
43
|
+
))}
|
|
44
|
+
{Part ? <Part view={part} button={button} /> : button}
|
|
45
|
+
</div>
|
|
46
|
+
</Dialog>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
// List of posts: one column on mobile, two on desktop. Thumbnail, date and reading time, title, description. The posts
|
|
2
|
-
// after the newest (BlogPosts.tsx), and the posts of the standard block "Blogindlæg" (blocks/posts/View.tsx). `random`: the list has all the posts it can take from, and on the site a script right after
|
|
3
|
-
// it shows `count` of them on each visit, before the list is drawn (without JavaScript: the first, the newest).
|
|
4
|
-
import Img from '@theme/components/Img';
|
|
5
|
-
import PostMeta from '@theme/components/PostMeta';
|
|
6
|
-
import type { PostView } from '@theme/components/posts';
|
|
7
|
-
import { Link, useOnSite } from '@morphemecms/cms/src/standard/view';
|
|
8
|
-
|
|
9
|
-
// Shows the number in data-random-posts of the posts in the list just before it, picked at random.
|
|
10
|
-
const PICK = `(() => {
|
|
11
|
-
const list = document.currentScript.previousElementSibling;
|
|
12
|
-
const items = [...list.children];
|
|
13
|
-
const shown = new Set();
|
|
14
|
-
while (shown.size < Number(list.dataset.randomPosts)) shown.add(Math.floor(Math.random() * items.length));
|
|
15
|
-
items.forEach((li, i) => (li.hidden = !shown.has(i)));
|
|
16
|
-
})();`;
|
|
17
|
-
|
|
18
|
-
export default function PostList({
|
|
19
|
-
posts,
|
|
20
|
-
count = posts.length,
|
|
21
|
-
random = false,
|
|
22
|
-
className,
|
|
23
|
-
}: {
|
|
24
|
-
posts: PostView[];
|
|
25
|
-
count?: number;
|
|
26
|
-
random?: boolean;
|
|
27
|
-
className?: string;
|
|
28
|
-
}) {
|
|
29
|
-
const onSite = useOnSite();
|
|
30
|
-
return (
|
|
31
|
-
<>
|
|
32
|
-
<ul className={['grid gap-x-12 md:grid-cols-2', className].filter(Boolean).join(' ')} data-random-posts={random ? count : undefined}>
|
|
33
|
-
{posts.map((post, i) => (
|
|
34
|
-
<li key={post.href || post.title} hidden={random && i >= count} className="border-t border-line">
|
|
35
|
-
<Link href={post.href} className="group grid grid-cols-[5.5rem_1fr] gap-4 py-5 sm:grid-cols-[8rem_1fr] sm:gap-5">
|
|
36
|
-
{post.image ? (
|
|
37
|
-
<Img
|
|
38
|
-
image={post.image}
|
|
39
|
-
alt=""
|
|
40
|
-
className="theme-post-picture aspect-square w-full object-cover"
|
|
41
|
-
sizes="(min-width: 640px) 128px, 88px"
|
|
42
|
-
/>
|
|
43
|
-
) : (
|
|
44
|
-
<span className="theme-post-picture aspect-square bg-tint" />
|
|
45
|
-
)}
|
|
46
|
-
<div>
|
|
47
|
-
<PostMeta post={post} />
|
|
48
|
-
<h3 className="mt-1 type-title-medium transition-colors group-hover:text-accent">{post.title}</h3>
|
|
49
|
-
{post.description && <p className="mt-1.5 line-clamp-2 type-body-medium text-muted">{post.description}</p>}
|
|
50
|
-
</div>
|
|
51
|
-
</Link>
|
|
52
|
-
</li>
|
|
53
|
-
))}
|
|
54
|
-
</ul>
|
|
55
|
-
{random && onSite && <script dangerouslySetInnerHTML={{ __html: PICK }} />}
|
|
56
|
-
</>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
1
|
+
// List of posts: one column on mobile, two on desktop. Thumbnail, date and reading time, title, description. The posts
|
|
2
|
+
// after the newest (BlogPosts.tsx), and the posts of the standard block "Blogindlæg" (blocks/posts/View.tsx). `random`: the list has all the posts it can take from, and on the site a script right after
|
|
3
|
+
// it shows `count` of them on each visit, before the list is drawn (without JavaScript: the first, the newest).
|
|
4
|
+
import Img from '@theme/components/Img';
|
|
5
|
+
import PostMeta from '@theme/components/PostMeta';
|
|
6
|
+
import type { PostView } from '@theme/components/posts';
|
|
7
|
+
import { Link, useOnSite } from '@morphemecms/cms/src/standard/view';
|
|
8
|
+
|
|
9
|
+
// Shows the number in data-random-posts of the posts in the list just before it, picked at random.
|
|
10
|
+
const PICK = `(() => {
|
|
11
|
+
const list = document.currentScript.previousElementSibling;
|
|
12
|
+
const items = [...list.children];
|
|
13
|
+
const shown = new Set();
|
|
14
|
+
while (shown.size < Number(list.dataset.randomPosts)) shown.add(Math.floor(Math.random() * items.length));
|
|
15
|
+
items.forEach((li, i) => (li.hidden = !shown.has(i)));
|
|
16
|
+
})();`;
|
|
17
|
+
|
|
18
|
+
export default function PostList({
|
|
19
|
+
posts,
|
|
20
|
+
count = posts.length,
|
|
21
|
+
random = false,
|
|
22
|
+
className,
|
|
23
|
+
}: {
|
|
24
|
+
posts: PostView[];
|
|
25
|
+
count?: number;
|
|
26
|
+
random?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
}) {
|
|
29
|
+
const onSite = useOnSite();
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<ul className={['grid gap-x-12 md:grid-cols-2', className].filter(Boolean).join(' ')} data-random-posts={random ? count : undefined}>
|
|
33
|
+
{posts.map((post, i) => (
|
|
34
|
+
<li key={post.href || post.title} hidden={random && i >= count} className="border-t border-line">
|
|
35
|
+
<Link href={post.href} className="group grid grid-cols-[5.5rem_1fr] gap-4 py-5 sm:grid-cols-[8rem_1fr] sm:gap-5">
|
|
36
|
+
{post.image ? (
|
|
37
|
+
<Img
|
|
38
|
+
image={post.image}
|
|
39
|
+
alt=""
|
|
40
|
+
className="theme-post-picture aspect-square w-full object-cover"
|
|
41
|
+
sizes="(min-width: 640px) 128px, 88px"
|
|
42
|
+
/>
|
|
43
|
+
) : (
|
|
44
|
+
<span className="theme-post-picture aspect-square bg-tint" />
|
|
45
|
+
)}
|
|
46
|
+
<div>
|
|
47
|
+
<PostMeta post={post} />
|
|
48
|
+
<h3 className="mt-1 type-title-medium transition-colors group-hover:text-accent">{post.title}</h3>
|
|
49
|
+
{post.description && <p className="mt-1.5 line-clamp-2 type-body-medium text-muted">{post.description}</p>}
|
|
50
|
+
</div>
|
|
51
|
+
</Link>
|
|
52
|
+
</li>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
{random && onSite && <script dangerouslySetInnerHTML={{ __html: PICK }} />}
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// Date and reading time for a blog post: "12. marts 2024 · 5 min. læsetid" in Danish (posts.ts makes them).
|
|
2
|
-
import { Svg } from '@morphemecms/cms/src/icon';
|
|
3
|
-
import type { PostView } from '@theme/components/posts';
|
|
4
|
-
import { siteT } from '@morphemecms/cms/src/i18n';
|
|
5
|
-
|
|
6
|
-
export default function PostMeta({ post, className }: { post: PostView; className?: string }) {
|
|
7
|
-
return (
|
|
8
|
-
<p className={['flex flex-wrap items-center gap-x-2 type-body-medium text-muted', className].filter(Boolean).join(' ')}>
|
|
9
|
-
<time dateTime={post.date}>{post.dateText}</time>
|
|
10
|
-
<span aria-hidden="true">·</span>
|
|
11
|
-
<span className="inline-flex items-center gap-1">
|
|
12
|
-
<Svg name="clock" size={15} />
|
|
13
|
-
{siteT.theme.readingTime(post.minutes)}
|
|
14
|
-
</span>
|
|
15
|
-
</p>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
1
|
+
// Date and reading time for a blog post: "12. marts 2024 · 5 min. læsetid" in Danish (posts.ts makes them).
|
|
2
|
+
import { Svg } from '@morphemecms/cms/src/icon';
|
|
3
|
+
import type { PostView } from '@theme/components/posts';
|
|
4
|
+
import { siteT } from '@morphemecms/cms/src/i18n';
|
|
5
|
+
|
|
6
|
+
export default function PostMeta({ post, className }: { post: PostView; className?: string }) {
|
|
7
|
+
return (
|
|
8
|
+
<p className={['flex flex-wrap items-center gap-x-2 type-body-medium text-muted', className].filter(Boolean).join(' ')}>
|
|
9
|
+
<time dateTime={post.date}>{post.dateText}</time>
|
|
10
|
+
<span aria-hidden="true">·</span>
|
|
11
|
+
<span className="inline-flex items-center gap-1">
|
|
12
|
+
<Svg name="clock" size={15} />
|
|
13
|
+
{siteT.theme.readingTime(post.minutes)}
|
|
14
|
+
</span>
|
|
15
|
+
</p>
|
|
16
|
+
);
|
|
17
|
+
}
|