@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,229 +1,229 @@
|
|
|
1
|
-
// How a carousel's items (Carousel.tsx) are laid out in Material's contained layouts that change the items' widths as
|
|
2
|
-
// they scroll (m3.material.io/components/carousel): 'multi-browse' (large items from the start, then a medium and a
|
|
3
|
-
// small one) and 'hero' (a large one and a small one after it, or, aligned to the centre, between two small ones). As
|
|
4
|
-
// Material's CarouselLayoutManager does it: every item is in the row as wide as a large one (so swiping and scroll-snap
|
|
5
|
-
// work as in any row), and its mask (.carousel-item-mask) is moved and clipped to the size its place gives it. The
|
|
6
|
-
// places are keylines: where each item stands and how wide it is seen, with an item that is just leaving or coming in
|
|
7
|
-
// outside at each end. Scrolled to the start, the large ones are at the start (also in a hero aligned to the centre);
|
|
8
|
-
// scrolled to the end, at the end; between, the layout's own; and from one to the other the keylines move by
|
|
9
|
-
// themselves, so that every item grows and shrinks as it passes. Widths from the theme (--carousel-large-item-width,
|
|
10
|
-
// --carousel-small-item-min-width and -max-width, the gap and the height, in ui.css); what is masked of an item is on it
|
|
11
|
-
// as --carousel-mask (0 to 1), for what is on it to fade. The site's carousel.script.ts and, in the editor, Carousel.tsx
|
|
12
|
-
// run it; it gives back what stops it. The other layouts ('uncontained', 'full-screen') are only ui.css.
|
|
13
|
-
export type CarouselLayout = 'multi-browse' | 'uncontained' | 'hero' | 'full-screen';
|
|
14
|
-
export type CarouselAlignment = 'start' | 'center';
|
|
15
|
-
|
|
16
|
-
/** The layouts laid out here, with their items' widths changing as they scroll. */
|
|
17
|
-
export const keylineLayouts: CarouselLayout[] = ['multi-browse', 'hero'];
|
|
18
|
-
|
|
19
|
-
type Keyline = { loc: number; size: number };
|
|
20
|
-
type State = { lines: Keyline[]; offsets: number[]; focal: number };
|
|
21
|
-
|
|
22
|
-
/** A length the theme gives in a custom property (in px or rem), else `fallback` px. */
|
|
23
|
-
function lengthOf(el: HTMLElement, name: string, fallback: number) {
|
|
24
|
-
const value = getComputedStyle(el).getPropertyValue(name).trim();
|
|
25
|
-
const n = parseFloat(value);
|
|
26
|
-
if (Number.isNaN(n)) return fallback;
|
|
27
|
-
if (value.endsWith('rem')) return n * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
28
|
-
return n;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** The sizes the items are seen at in the layout's own arrangement, from its start, filling `width` with `gap`s. */
|
|
32
|
-
function arrangement(
|
|
33
|
-
layout: CarouselLayout,
|
|
34
|
-
alignment: CarouselAlignment,
|
|
35
|
-
count: number,
|
|
36
|
-
width: number,
|
|
37
|
-
gap: number,
|
|
38
|
-
{ large, smallMin, smallMax, height }: { large: number; smallMin: number; smallMax: number; height: number },
|
|
39
|
-
): number[] {
|
|
40
|
-
// As many large ones as there are items, side by side, when they all fit.
|
|
41
|
-
const even = (n: number) => Array<number>(n).fill((width - (n - 1) * gap) / n);
|
|
42
|
-
if (layout === 'hero') {
|
|
43
|
-
// A large one at most twice as wide as it is high: more large ones on a wide screen.
|
|
44
|
-
const smalls = alignment === 'center' ? 2 : 1;
|
|
45
|
-
if (count <= smalls) return even(count);
|
|
46
|
-
let n = 1;
|
|
47
|
-
const sizeOf = (k: number) => (width - smalls * smallMax - (k + smalls - 1) * gap) / k;
|
|
48
|
-
while (sizeOf(n) > 2 * height && n + smalls < count) n++;
|
|
49
|
-
const l = sizeOf(n);
|
|
50
|
-
const larges = Array<number>(n).fill(l);
|
|
51
|
-
return alignment === 'center' ? [smallMax, ...larges, smallMax] : [...larges, smallMax];
|
|
52
|
-
}
|
|
53
|
-
// Multi-browse: large items, a medium one and a small one (without the medium one only where it does not fit), the
|
|
54
|
-
// large ones as near the theme's width as they can be.
|
|
55
|
-
let best: { sizes: number[]; cost: number } | undefined;
|
|
56
|
-
for (const medium of [1, 0]) {
|
|
57
|
-
for (let n = 1; n + medium + 1 <= count; n++)
|
|
58
|
-
for (const small of [smallMax, smallMin]) {
|
|
59
|
-
// n large (l), evt. a medium one halfway between a large and the small one, and the small one.
|
|
60
|
-
const l = (width - small - (n + medium) * gap - (medium * small) / 2) / (n + medium / 2);
|
|
61
|
-
if (l < small * 2) continue;
|
|
62
|
-
const cost = Math.abs(l - large);
|
|
63
|
-
if (!best || cost < best.cost) best = { sizes: [...Array<number>(n).fill(l), ...(medium ? [(l + small) / 2] : []), small], cost };
|
|
64
|
-
}
|
|
65
|
-
if (best) break;
|
|
66
|
-
}
|
|
67
|
-
return best?.sizes ?? even(Math.max(1, count));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** The keylines of an arrangement, with one outside at each end, and the unmasked centres of the items that stand at
|
|
71
|
-
* them: an item as wide as a large one (`large`) one step (`large` and a gap) after the other, from the first large one
|
|
72
|
-
* (`anchor` 'first') or back from the last (`anchor` 'last'). */
|
|
73
|
-
function stateOf(sizes: number[], width: number, gap: number, large: number, anchor: 'first' | 'last'): State {
|
|
74
|
-
const small = Math.min(...sizes);
|
|
75
|
-
const lines: Keyline[] = [{ loc: -gap - small / 2, size: small }];
|
|
76
|
-
let x = 0;
|
|
77
|
-
for (const size of sizes) {
|
|
78
|
-
lines.push({ loc: x + size / 2, size });
|
|
79
|
-
x += size + gap;
|
|
80
|
-
}
|
|
81
|
-
lines.push({ loc: width + gap + small / 2, size: small });
|
|
82
|
-
const at = (i: number) => Math.abs(lines[i].size - large) < 0.5;
|
|
83
|
-
const focal = anchor === 'first' ? lines.findIndex((_, i) => at(i)) : lines.findLastIndex((_, i) => at(i));
|
|
84
|
-
const step = large + gap;
|
|
85
|
-
return { lines, offsets: lines.map((_, i) => lines[focal].loc + (i - focal) * step), focal };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
|
89
|
-
const mix = (a: State, b: State, t: number): State => ({
|
|
90
|
-
lines: a.lines.map((l, i) => ({ loc: lerp(l.loc, b.lines[i].loc, t), size: lerp(l.size, b.lines[i].size, t) })),
|
|
91
|
-
offsets: a.offsets.map((o, i) => lerp(o, b.offsets[i], t)),
|
|
92
|
-
focal: a.focal,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
/** Lays out the carousel `root` (its [data-carousel-items] and their masks) in its layout (data-carousel-layout) and
|
|
96
|
-
* alignment (data-carousel-alignment) as it scrolls and is resized; gives back what stops it. */
|
|
97
|
-
export function carouselLayout(root: HTMLElement): () => void {
|
|
98
|
-
const layout = (root.dataset.carouselLayout ?? 'uncontained') as CarouselLayout;
|
|
99
|
-
const alignment = (root.dataset.carouselAlignment ?? 'start') as CarouselAlignment;
|
|
100
|
-
const row = root.querySelector<HTMLElement>('[data-carousel-items]');
|
|
101
|
-
if (!row || !keylineLayouts.includes(layout)) return () => {};
|
|
102
|
-
const items = [...row.querySelectorAll<HTMLElement>('[data-carousel-item]')];
|
|
103
|
-
const masks = items.map((item) => item.querySelector<HTMLElement>('[data-carousel-mask]'));
|
|
104
|
-
let start: State, normal: State, end: State, large: number;
|
|
105
|
-
let lead = 0;
|
|
106
|
-
let toStart = 0;
|
|
107
|
-
let toEnd = 0;
|
|
108
|
-
|
|
109
|
-
function measure() {
|
|
110
|
-
const width = row!.clientWidth;
|
|
111
|
-
const gap = parseFloat(getComputedStyle(row!).columnGap) || 0;
|
|
112
|
-
const sizes = arrangement(layout, alignment, items.length, width, gap, {
|
|
113
|
-
large: lengthOf(root, '--carousel-large-item-width', 200),
|
|
114
|
-
smallMin: lengthOf(root, '--carousel-small-item-min-width', 40),
|
|
115
|
-
smallMax: lengthOf(root, '--carousel-small-item-max-width', 56),
|
|
116
|
-
height: row!.clientHeight,
|
|
117
|
-
});
|
|
118
|
-
large = Math.max(...sizes);
|
|
119
|
-
row!.style.setProperty('--carousel-item-width', `${large}px`);
|
|
120
|
-
// Scrolled to the start, the large ones first; to the end, last; between, the layout's own.
|
|
121
|
-
start = stateOf(
|
|
122
|
-
[...sizes].sort((a, b) => b - a),
|
|
123
|
-
width,
|
|
124
|
-
gap,
|
|
125
|
-
large,
|
|
126
|
-
'first',
|
|
127
|
-
);
|
|
128
|
-
normal = stateOf(sizes, width, gap, large, 'first');
|
|
129
|
-
end = stateOf(
|
|
130
|
-
[...sizes].sort((a, b) => a - b),
|
|
131
|
-
width,
|
|
132
|
-
gap,
|
|
133
|
-
large,
|
|
134
|
-
'last',
|
|
135
|
-
);
|
|
136
|
-
// Where an item rests: its start this far before the first large keyline's (scroll-padding, for scroll-snap).
|
|
137
|
-
lead = normal.lines[normal.focal].loc - large / 2;
|
|
138
|
-
row!.style.scrollPaddingInlineStart = `${lead}px`;
|
|
139
|
-
toStart = start.offsets[0] - normal.offsets[0];
|
|
140
|
-
toEnd = normal.offsets[0] - end.offsets[0];
|
|
141
|
-
update();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function update() {
|
|
145
|
-
const s = row!.scrollLeft;
|
|
146
|
-
const max = row!.scrollWidth - row!.clientWidth;
|
|
147
|
-
let state = start;
|
|
148
|
-
if (max > 0) {
|
|
149
|
-
// When the row is too short for both, each shift takes its share of it.
|
|
150
|
-
const k = Math.min(1, max / (toStart + toEnd || 1));
|
|
151
|
-
const a = toStart * k;
|
|
152
|
-
const b = toEnd * k;
|
|
153
|
-
state = s < a ? mix(start, normal, s / a) : s > max - b ? mix(normal, end, (s - (max - b)) / b) : normal;
|
|
154
|
-
}
|
|
155
|
-
const { lines, offsets } = state;
|
|
156
|
-
const first = items[0]?.offsetLeft ?? 0;
|
|
157
|
-
items.forEach((item, i) => {
|
|
158
|
-
const mask = masks[i];
|
|
159
|
-
if (!mask) return;
|
|
160
|
-
// Where its centre is in the row's window, and which two keylines it is between.
|
|
161
|
-
const c = item.offsetLeft - first + large / 2 - s;
|
|
162
|
-
let loc: number;
|
|
163
|
-
let size: number;
|
|
164
|
-
const last = lines.length - 1;
|
|
165
|
-
if (c <= offsets[0]) [loc, size] = [lines[0].loc + c - offsets[0], lines[0].size];
|
|
166
|
-
else if (c >= offsets[last]) [loc, size] = [lines[last].loc + c - offsets[last], lines[last].size];
|
|
167
|
-
else {
|
|
168
|
-
const j = offsets.findIndex((o, n) => c >= o && c <= offsets[n + 1]);
|
|
169
|
-
const t = (c - offsets[j]) / (offsets[j + 1] - offsets[j]);
|
|
170
|
-
loc = lerp(lines[j].loc, lines[j + 1].loc, t);
|
|
171
|
-
size = lerp(lines[j].size, lines[j + 1].size, t);
|
|
172
|
-
}
|
|
173
|
-
mask.style.transform = `translateX(${loc - c}px)`;
|
|
174
|
-
mask.style.setProperty('--carousel-mask-inset', `${Math.max(0, (large - size) / 2)}px`);
|
|
175
|
-
item.style.setProperty('--carousel-mask', String(Math.max(0, Math.min(1, 1 - size / large))));
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
let frame = 0;
|
|
180
|
-
const onScroll = () => {
|
|
181
|
-
cancelAnimationFrame(frame);
|
|
182
|
-
frame = requestAnimationFrame(update);
|
|
183
|
-
};
|
|
184
|
-
row.addEventListener('scroll', onScroll, { passive: true });
|
|
185
|
-
const resized = new ResizeObserver(() => measure());
|
|
186
|
-
resized.observe(row);
|
|
187
|
-
measure();
|
|
188
|
-
|
|
189
|
-
return () => {
|
|
190
|
-
cancelAnimationFrame(frame);
|
|
191
|
-
row.removeEventListener('scroll', onScroll);
|
|
192
|
-
resized.disconnect();
|
|
193
|
-
row.style.removeProperty('--carousel-item-width');
|
|
194
|
-
row.style.scrollPaddingInlineStart = '';
|
|
195
|
-
items.forEach((item, i) => {
|
|
196
|
-
item.style.removeProperty('--carousel-mask');
|
|
197
|
-
masks[i]?.style.removeProperty('transform');
|
|
198
|
-
masks[i]?.style.removeProperty('--carousel-mask-inset');
|
|
199
|
-
});
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// How much further than its padding an item rests from the row's start (its scroll-padding: a hero's small item before
|
|
204
|
-
// it), and the step from one item to the next.
|
|
205
|
-
function restOf(row: HTMLElement) {
|
|
206
|
-
const style = getComputedStyle(row);
|
|
207
|
-
const items = [...row.querySelectorAll<HTMLElement>('[data-carousel-item]')];
|
|
208
|
-
return {
|
|
209
|
-
items,
|
|
210
|
-
lead: (parseFloat(style.scrollPaddingInlineStart) || 0) - (parseFloat(style.paddingInlineStart) || 0),
|
|
211
|
-
step: (items[1]?.offsetLeft ?? 0) - (items[0]?.offsetLeft ?? 0) || 1,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** Where a carousel's row is scrolled to show item `i` where it rests. */
|
|
216
|
-
export function scrollFor(row: HTMLElement, i: number) {
|
|
217
|
-
const { items, lead } = restOf(row);
|
|
218
|
-
const target = items[Math.max(0, Math.min(items.length - 1, i))];
|
|
219
|
-
return target ? target.offsetLeft - items[0].offsetLeft - lead : 0;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** The item a carousel's row shows where it rests, for its page indicator. */
|
|
223
|
-
export function shownItem(row: HTMLElement) {
|
|
224
|
-
const { items, lead, step } = restOf(row);
|
|
225
|
-
if (items.length < 2) return 0;
|
|
226
|
-
const end = row.scrollLeft >= row.scrollWidth - row.clientWidth - 2;
|
|
227
|
-
// Several items can be in view at once: at the end, the last one is the chosen one.
|
|
228
|
-
return end ? items.length - 1 : Math.max(0, Math.min(items.length - 1, Math.round((row.scrollLeft + lead) / step)));
|
|
229
|
-
}
|
|
1
|
+
// How a carousel's items (Carousel.tsx) are laid out in Material's contained layouts that change the items' widths as
|
|
2
|
+
// they scroll (m3.material.io/components/carousel): 'multi-browse' (large items from the start, then a medium and a
|
|
3
|
+
// small one) and 'hero' (a large one and a small one after it, or, aligned to the centre, between two small ones). As
|
|
4
|
+
// Material's CarouselLayoutManager does it: every item is in the row as wide as a large one (so swiping and scroll-snap
|
|
5
|
+
// work as in any row), and its mask (.carousel-item-mask) is moved and clipped to the size its place gives it. The
|
|
6
|
+
// places are keylines: where each item stands and how wide it is seen, with an item that is just leaving or coming in
|
|
7
|
+
// outside at each end. Scrolled to the start, the large ones are at the start (also in a hero aligned to the centre);
|
|
8
|
+
// scrolled to the end, at the end; between, the layout's own; and from one to the other the keylines move by
|
|
9
|
+
// themselves, so that every item grows and shrinks as it passes. Widths from the theme (--carousel-large-item-width,
|
|
10
|
+
// --carousel-small-item-min-width and -max-width, the gap and the height, in ui.css); what is masked of an item is on it
|
|
11
|
+
// as --carousel-mask (0 to 1), for what is on it to fade. The site's carousel.script.ts and, in the editor, Carousel.tsx
|
|
12
|
+
// run it; it gives back what stops it. The other layouts ('uncontained', 'full-screen') are only ui.css.
|
|
13
|
+
export type CarouselLayout = 'multi-browse' | 'uncontained' | 'hero' | 'full-screen';
|
|
14
|
+
export type CarouselAlignment = 'start' | 'center';
|
|
15
|
+
|
|
16
|
+
/** The layouts laid out here, with their items' widths changing as they scroll. */
|
|
17
|
+
export const keylineLayouts: CarouselLayout[] = ['multi-browse', 'hero'];
|
|
18
|
+
|
|
19
|
+
type Keyline = { loc: number; size: number };
|
|
20
|
+
type State = { lines: Keyline[]; offsets: number[]; focal: number };
|
|
21
|
+
|
|
22
|
+
/** A length the theme gives in a custom property (in px or rem), else `fallback` px. */
|
|
23
|
+
function lengthOf(el: HTMLElement, name: string, fallback: number) {
|
|
24
|
+
const value = getComputedStyle(el).getPropertyValue(name).trim();
|
|
25
|
+
const n = parseFloat(value);
|
|
26
|
+
if (Number.isNaN(n)) return fallback;
|
|
27
|
+
if (value.endsWith('rem')) return n * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
28
|
+
return n;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The sizes the items are seen at in the layout's own arrangement, from its start, filling `width` with `gap`s. */
|
|
32
|
+
function arrangement(
|
|
33
|
+
layout: CarouselLayout,
|
|
34
|
+
alignment: CarouselAlignment,
|
|
35
|
+
count: number,
|
|
36
|
+
width: number,
|
|
37
|
+
gap: number,
|
|
38
|
+
{ large, smallMin, smallMax, height }: { large: number; smallMin: number; smallMax: number; height: number },
|
|
39
|
+
): number[] {
|
|
40
|
+
// As many large ones as there are items, side by side, when they all fit.
|
|
41
|
+
const even = (n: number) => Array<number>(n).fill((width - (n - 1) * gap) / n);
|
|
42
|
+
if (layout === 'hero') {
|
|
43
|
+
// A large one at most twice as wide as it is high: more large ones on a wide screen.
|
|
44
|
+
const smalls = alignment === 'center' ? 2 : 1;
|
|
45
|
+
if (count <= smalls) return even(count);
|
|
46
|
+
let n = 1;
|
|
47
|
+
const sizeOf = (k: number) => (width - smalls * smallMax - (k + smalls - 1) * gap) / k;
|
|
48
|
+
while (sizeOf(n) > 2 * height && n + smalls < count) n++;
|
|
49
|
+
const l = sizeOf(n);
|
|
50
|
+
const larges = Array<number>(n).fill(l);
|
|
51
|
+
return alignment === 'center' ? [smallMax, ...larges, smallMax] : [...larges, smallMax];
|
|
52
|
+
}
|
|
53
|
+
// Multi-browse: large items, a medium one and a small one (without the medium one only where it does not fit), the
|
|
54
|
+
// large ones as near the theme's width as they can be.
|
|
55
|
+
let best: { sizes: number[]; cost: number } | undefined;
|
|
56
|
+
for (const medium of [1, 0]) {
|
|
57
|
+
for (let n = 1; n + medium + 1 <= count; n++)
|
|
58
|
+
for (const small of [smallMax, smallMin]) {
|
|
59
|
+
// n large (l), evt. a medium one halfway between a large and the small one, and the small one.
|
|
60
|
+
const l = (width - small - (n + medium) * gap - (medium * small) / 2) / (n + medium / 2);
|
|
61
|
+
if (l < small * 2) continue;
|
|
62
|
+
const cost = Math.abs(l - large);
|
|
63
|
+
if (!best || cost < best.cost) best = { sizes: [...Array<number>(n).fill(l), ...(medium ? [(l + small) / 2] : []), small], cost };
|
|
64
|
+
}
|
|
65
|
+
if (best) break;
|
|
66
|
+
}
|
|
67
|
+
return best?.sizes ?? even(Math.max(1, count));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The keylines of an arrangement, with one outside at each end, and the unmasked centres of the items that stand at
|
|
71
|
+
* them: an item as wide as a large one (`large`) one step (`large` and a gap) after the other, from the first large one
|
|
72
|
+
* (`anchor` 'first') or back from the last (`anchor` 'last'). */
|
|
73
|
+
function stateOf(sizes: number[], width: number, gap: number, large: number, anchor: 'first' | 'last'): State {
|
|
74
|
+
const small = Math.min(...sizes);
|
|
75
|
+
const lines: Keyline[] = [{ loc: -gap - small / 2, size: small }];
|
|
76
|
+
let x = 0;
|
|
77
|
+
for (const size of sizes) {
|
|
78
|
+
lines.push({ loc: x + size / 2, size });
|
|
79
|
+
x += size + gap;
|
|
80
|
+
}
|
|
81
|
+
lines.push({ loc: width + gap + small / 2, size: small });
|
|
82
|
+
const at = (i: number) => Math.abs(lines[i].size - large) < 0.5;
|
|
83
|
+
const focal = anchor === 'first' ? lines.findIndex((_, i) => at(i)) : lines.findLastIndex((_, i) => at(i));
|
|
84
|
+
const step = large + gap;
|
|
85
|
+
return { lines, offsets: lines.map((_, i) => lines[focal].loc + (i - focal) * step), focal };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
|
89
|
+
const mix = (a: State, b: State, t: number): State => ({
|
|
90
|
+
lines: a.lines.map((l, i) => ({ loc: lerp(l.loc, b.lines[i].loc, t), size: lerp(l.size, b.lines[i].size, t) })),
|
|
91
|
+
offsets: a.offsets.map((o, i) => lerp(o, b.offsets[i], t)),
|
|
92
|
+
focal: a.focal,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/** Lays out the carousel `root` (its [data-carousel-items] and their masks) in its layout (data-carousel-layout) and
|
|
96
|
+
* alignment (data-carousel-alignment) as it scrolls and is resized; gives back what stops it. */
|
|
97
|
+
export function carouselLayout(root: HTMLElement): () => void {
|
|
98
|
+
const layout = (root.dataset.carouselLayout ?? 'uncontained') as CarouselLayout;
|
|
99
|
+
const alignment = (root.dataset.carouselAlignment ?? 'start') as CarouselAlignment;
|
|
100
|
+
const row = root.querySelector<HTMLElement>('[data-carousel-items]');
|
|
101
|
+
if (!row || !keylineLayouts.includes(layout)) return () => {};
|
|
102
|
+
const items = [...row.querySelectorAll<HTMLElement>('[data-carousel-item]')];
|
|
103
|
+
const masks = items.map((item) => item.querySelector<HTMLElement>('[data-carousel-mask]'));
|
|
104
|
+
let start: State, normal: State, end: State, large: number;
|
|
105
|
+
let lead = 0;
|
|
106
|
+
let toStart = 0;
|
|
107
|
+
let toEnd = 0;
|
|
108
|
+
|
|
109
|
+
function measure() {
|
|
110
|
+
const width = row!.clientWidth;
|
|
111
|
+
const gap = parseFloat(getComputedStyle(row!).columnGap) || 0;
|
|
112
|
+
const sizes = arrangement(layout, alignment, items.length, width, gap, {
|
|
113
|
+
large: lengthOf(root, '--carousel-large-item-width', 200),
|
|
114
|
+
smallMin: lengthOf(root, '--carousel-small-item-min-width', 40),
|
|
115
|
+
smallMax: lengthOf(root, '--carousel-small-item-max-width', 56),
|
|
116
|
+
height: row!.clientHeight,
|
|
117
|
+
});
|
|
118
|
+
large = Math.max(...sizes);
|
|
119
|
+
row!.style.setProperty('--carousel-item-width', `${large}px`);
|
|
120
|
+
// Scrolled to the start, the large ones first; to the end, last; between, the layout's own.
|
|
121
|
+
start = stateOf(
|
|
122
|
+
[...sizes].sort((a, b) => b - a),
|
|
123
|
+
width,
|
|
124
|
+
gap,
|
|
125
|
+
large,
|
|
126
|
+
'first',
|
|
127
|
+
);
|
|
128
|
+
normal = stateOf(sizes, width, gap, large, 'first');
|
|
129
|
+
end = stateOf(
|
|
130
|
+
[...sizes].sort((a, b) => a - b),
|
|
131
|
+
width,
|
|
132
|
+
gap,
|
|
133
|
+
large,
|
|
134
|
+
'last',
|
|
135
|
+
);
|
|
136
|
+
// Where an item rests: its start this far before the first large keyline's (scroll-padding, for scroll-snap).
|
|
137
|
+
lead = normal.lines[normal.focal].loc - large / 2;
|
|
138
|
+
row!.style.scrollPaddingInlineStart = `${lead}px`;
|
|
139
|
+
toStart = start.offsets[0] - normal.offsets[0];
|
|
140
|
+
toEnd = normal.offsets[0] - end.offsets[0];
|
|
141
|
+
update();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function update() {
|
|
145
|
+
const s = row!.scrollLeft;
|
|
146
|
+
const max = row!.scrollWidth - row!.clientWidth;
|
|
147
|
+
let state = start;
|
|
148
|
+
if (max > 0) {
|
|
149
|
+
// When the row is too short for both, each shift takes its share of it.
|
|
150
|
+
const k = Math.min(1, max / (toStart + toEnd || 1));
|
|
151
|
+
const a = toStart * k;
|
|
152
|
+
const b = toEnd * k;
|
|
153
|
+
state = s < a ? mix(start, normal, s / a) : s > max - b ? mix(normal, end, (s - (max - b)) / b) : normal;
|
|
154
|
+
}
|
|
155
|
+
const { lines, offsets } = state;
|
|
156
|
+
const first = items[0]?.offsetLeft ?? 0;
|
|
157
|
+
items.forEach((item, i) => {
|
|
158
|
+
const mask = masks[i];
|
|
159
|
+
if (!mask) return;
|
|
160
|
+
// Where its centre is in the row's window, and which two keylines it is between.
|
|
161
|
+
const c = item.offsetLeft - first + large / 2 - s;
|
|
162
|
+
let loc: number;
|
|
163
|
+
let size: number;
|
|
164
|
+
const last = lines.length - 1;
|
|
165
|
+
if (c <= offsets[0]) [loc, size] = [lines[0].loc + c - offsets[0], lines[0].size];
|
|
166
|
+
else if (c >= offsets[last]) [loc, size] = [lines[last].loc + c - offsets[last], lines[last].size];
|
|
167
|
+
else {
|
|
168
|
+
const j = offsets.findIndex((o, n) => c >= o && c <= offsets[n + 1]);
|
|
169
|
+
const t = (c - offsets[j]) / (offsets[j + 1] - offsets[j]);
|
|
170
|
+
loc = lerp(lines[j].loc, lines[j + 1].loc, t);
|
|
171
|
+
size = lerp(lines[j].size, lines[j + 1].size, t);
|
|
172
|
+
}
|
|
173
|
+
mask.style.transform = `translateX(${loc - c}px)`;
|
|
174
|
+
mask.style.setProperty('--carousel-mask-inset', `${Math.max(0, (large - size) / 2)}px`);
|
|
175
|
+
item.style.setProperty('--carousel-mask', String(Math.max(0, Math.min(1, 1 - size / large))));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let frame = 0;
|
|
180
|
+
const onScroll = () => {
|
|
181
|
+
cancelAnimationFrame(frame);
|
|
182
|
+
frame = requestAnimationFrame(update);
|
|
183
|
+
};
|
|
184
|
+
row.addEventListener('scroll', onScroll, { passive: true });
|
|
185
|
+
const resized = new ResizeObserver(() => measure());
|
|
186
|
+
resized.observe(row);
|
|
187
|
+
measure();
|
|
188
|
+
|
|
189
|
+
return () => {
|
|
190
|
+
cancelAnimationFrame(frame);
|
|
191
|
+
row.removeEventListener('scroll', onScroll);
|
|
192
|
+
resized.disconnect();
|
|
193
|
+
row.style.removeProperty('--carousel-item-width');
|
|
194
|
+
row.style.scrollPaddingInlineStart = '';
|
|
195
|
+
items.forEach((item, i) => {
|
|
196
|
+
item.style.removeProperty('--carousel-mask');
|
|
197
|
+
masks[i]?.style.removeProperty('transform');
|
|
198
|
+
masks[i]?.style.removeProperty('--carousel-mask-inset');
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// How much further than its padding an item rests from the row's start (its scroll-padding: a hero's small item before
|
|
204
|
+
// it), and the step from one item to the next.
|
|
205
|
+
function restOf(row: HTMLElement) {
|
|
206
|
+
const style = getComputedStyle(row);
|
|
207
|
+
const items = [...row.querySelectorAll<HTMLElement>('[data-carousel-item]')];
|
|
208
|
+
return {
|
|
209
|
+
items,
|
|
210
|
+
lead: (parseFloat(style.scrollPaddingInlineStart) || 0) - (parseFloat(style.paddingInlineStart) || 0),
|
|
211
|
+
step: (items[1]?.offsetLeft ?? 0) - (items[0]?.offsetLeft ?? 0) || 1,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Where a carousel's row is scrolled to show item `i` where it rests. */
|
|
216
|
+
export function scrollFor(row: HTMLElement, i: number) {
|
|
217
|
+
const { items, lead } = restOf(row);
|
|
218
|
+
const target = items[Math.max(0, Math.min(items.length - 1, i))];
|
|
219
|
+
return target ? target.offsetLeft - items[0].offsetLeft - lead : 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** The item a carousel's row shows where it rests, for its page indicator. */
|
|
223
|
+
export function shownItem(row: HTMLElement) {
|
|
224
|
+
const { items, lead, step } = restOf(row);
|
|
225
|
+
if (items.length < 2) return 0;
|
|
226
|
+
const end = row.scrollLeft >= row.scrollWidth - row.clientWidth - 2;
|
|
227
|
+
// Several items can be in view at once: at the end, the last one is the chosen one.
|
|
228
|
+
return end ? items.length - 1 : Math.max(0, Math.min(items.length - 1, Math.round((row.scrollLeft + lead) / step)));
|
|
229
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// What a checkbox (Checkbox.tsx) does on the site: one that is indeterminate (data-indeterminate) is shown so, as the
|
|
2
|
-
// browser only has that as a property, until it is clicked. Run on every page by the theme's Base.astro (blockScripts,
|
|
3
|
-
// packages/editor/site/blockScripts.ts); without such a checkbox on the page it does nothing.
|
|
4
|
-
export default function checkbox() {
|
|
5
|
-
document.querySelectorAll<HTMLInputElement>('input[data-indeterminate]').forEach((input) => {
|
|
6
|
-
input.indeterminate = true;
|
|
7
|
-
});
|
|
8
|
-
}
|
|
1
|
+
// What a checkbox (Checkbox.tsx) does on the site: one that is indeterminate (data-indeterminate) is shown so, as the
|
|
2
|
+
// browser only has that as a property, until it is clicked. Run on every page by the theme's Base.astro (blockScripts,
|
|
3
|
+
// packages/editor/site/blockScripts.ts); without such a checkbox on the page it does nothing.
|
|
4
|
+
export default function checkbox() {
|
|
5
|
+
document.querySelectorAll<HTMLInputElement>('input[data-indeterminate]').forEach((input) => {
|
|
6
|
+
input.indeterminate = true;
|
|
7
|
+
});
|
|
8
|
+
}
|