From 254a8eb2e616659611d95f0ad29f1e946244491e Mon Sep 17 00:00:00 2001 From: Sil Date: Sat, 17 Oct 2026 11:59:47 +0200 Subject: [PATCH 1/2] Add native Liquid component sources and Shopify runtime --- .prettierignore | 8 + AGENTS.md | 2 + DESIGN.md | 6 +- eslint.config.js | 9 +- knip.jsonc | 13 +- liquid/LICENSE-DATA-SLOT.md | 21 + liquid/LICENSE-FULLDEV.txt | 21 + liquid/LICENSE-SHADCN.md | 21 + liquid/README.md | 17 + liquid/components/accordion.liquid | 5 + liquid/components/alert-dialog.liquid | 116 ++ liquid/components/alert.liquid | 45 + liquid/components/aspect-ratio.liquid | 28 + liquid/components/attachment.liquid | 134 ++ liquid/components/avatar.liquid | 52 + liquid/components/badge.liquid | 86 ++ liquid/components/banner.liquid | 40 + liquid/components/base.liquid | 102 ++ liquid/components/breadcrumb.liquid | 46 + liquid/components/button-group.liquid | 56 + liquid/components/button.liquid | 232 ++++ liquid/components/card.liquid | 5 + liquid/components/carousel.liquid | 5 + liquid/components/checkbox.liquid | 54 + liquid/components/collapsible.liquid | 3 + liquid/components/combobox.liquid | 87 ++ liquid/components/command.liquid | 86 ++ liquid/components/dialog.liquid | 80 ++ liquid/components/drawer.liquid | 97 ++ liquid/components/dropdown-menu.liquid | 3 + liquid/components/empty.liquid | 72 ++ liquid/components/field.liquid | 5 + liquid/components/form.liquid | 5 + liquid/components/header.liquid | 33 + liquid/components/hover-card.liquid | 3 + liquid/components/icon.liquid | 5 + liquid/components/input-group.liquid | 53 + liquid/components/input.liquid | 74 ++ liquid/components/invalid-controls.liquid | 18 + liquid/components/item.liquid | 114 ++ liquid/components/kbd.liquid | 20 + liquid/components/logo.liquid | 33 + liquid/components/marquee.liquid | 5 + liquid/components/navigation-menu.liquid | 5 + liquid/components/pagination.liquid | 32 + liquid/components/popover.liquid | 3 + liquid/components/popup.liquid | 189 +++ liquid/components/price.liquid | 5 + liquid/components/radio-group.liquid | 53 + liquid/components/rating.liquid | 5 + liquid/components/section.liquid | 5 + liquid/components/select.liquid | 51 + liquid/components/separator.liquid | 19 + liquid/components/sheet.liquid | 68 + liquid/components/skeleton.liquid | 17 + liquid/components/slider.liquid | 50 + liquid/components/spinner.liquid | 28 + liquid/components/switch.liquid | 54 + liquid/components/table.liquid | 64 + liquid/components/tabs.liquid | 88 ++ liquid/components/toast.liquid | 5 + liquid/components/toc.liquid | 45 + liquid/components/toggle-group.liquid | 77 ++ liquid/components/toggle.liquid | 61 + liquid/components/tooltip.liquid | 45 + liquid/components/triggers.liquid | 20 + liquid/components/typography.liquid | 5 + liquid/components/video.liquid | 5 + liquid/contracts.json | 428 +++++++ liquid/licenses.json | 14 + liquid/manifest.json | 110 ++ liquid/registry.json | 1295 ++++++++++++++++++++ liquid/runtime/ui-carousel.js | 22 + liquid/runtime/ui-controls.js | 195 +++ liquid/runtime/ui-lifecycle.js | 107 ++ liquid/runtime/ui-motion.js | 45 + liquid/runtime/ui-start.js | 37 + liquid/snippets/init.liquid | 6 + liquid/snippets/runtime.liquid | 9 + liquid/snippets/ui.liquid | 68 + liquid/styles/accordion.css | 66 + liquid/styles/card.css | 114 ++ liquid/styles/carousel.css | 58 + liquid/styles/field.css | 66 + liquid/styles/form.css | 14 + liquid/styles/icon.css | 32 + liquid/styles/marquee.css | 65 + liquid/styles/navigation-menu.css | 235 ++++ liquid/styles/price.css | 41 + liquid/styles/primitives.css | 53 + liquid/styles/rating.css | 23 + liquid/styles/section.css | 77 ++ liquid/styles/toast.css | 125 ++ liquid/styles/tokens.css | 118 ++ liquid/styles/typography.css | 670 ++++++++++ liquid/styles/video.css | 10 + liquid/vendor.json | 180 +++ liquid/vendor/data-slot-accordion.js | 1 + liquid/vendor/data-slot-alert-dialog.js | 1 + liquid/vendor/data-slot-carousel.js | 1 + liquid/vendor/data-slot-collapsible.js | 1 + liquid/vendor/data-slot-combobox.js | 1 + liquid/vendor/data-slot-command.js | 1 + liquid/vendor/data-slot-core.js | 1 + liquid/vendor/data-slot-dialog.js | 1 + liquid/vendor/data-slot-drawer.js | 1 + liquid/vendor/data-slot-dropdown-menu.js | 1 + liquid/vendor/data-slot-hover-card.js | 1 + liquid/vendor/data-slot-navigation-menu.js | 1 + liquid/vendor/data-slot-popover.js | 1 + liquid/vendor/data-slot-radio-group.js | 1 + liquid/vendor/data-slot-select.js | 1 + liquid/vendor/data-slot-slider.js | 1 + liquid/vendor/data-slot-switch.js | 1 + liquid/vendor/data-slot-tabs.js | 1 + liquid/vendor/data-slot-toast.js | 8 + liquid/vendor/data-slot-toggle-group.js | 1 + liquid/vendor/data-slot-toggle.js | 1 + liquid/vendor/data-slot-tooltip.js | 1 + package.json | 1 + pnpm-lock.yaml | 67 +- prettier.config.cjs | 16 +- tsconfig.json | 5 +- 123 files changed, 7312 insertions(+), 8 deletions(-) create mode 100644 liquid/LICENSE-DATA-SLOT.md create mode 100644 liquid/LICENSE-FULLDEV.txt create mode 100644 liquid/LICENSE-SHADCN.md create mode 100644 liquid/README.md create mode 100644 liquid/components/accordion.liquid create mode 100644 liquid/components/alert-dialog.liquid create mode 100644 liquid/components/alert.liquid create mode 100644 liquid/components/aspect-ratio.liquid create mode 100644 liquid/components/attachment.liquid create mode 100644 liquid/components/avatar.liquid create mode 100644 liquid/components/badge.liquid create mode 100644 liquid/components/banner.liquid create mode 100644 liquid/components/base.liquid create mode 100644 liquid/components/breadcrumb.liquid create mode 100644 liquid/components/button-group.liquid create mode 100644 liquid/components/button.liquid create mode 100644 liquid/components/card.liquid create mode 100644 liquid/components/carousel.liquid create mode 100644 liquid/components/checkbox.liquid create mode 100644 liquid/components/collapsible.liquid create mode 100644 liquid/components/combobox.liquid create mode 100644 liquid/components/command.liquid create mode 100644 liquid/components/dialog.liquid create mode 100644 liquid/components/drawer.liquid create mode 100644 liquid/components/dropdown-menu.liquid create mode 100644 liquid/components/empty.liquid create mode 100644 liquid/components/field.liquid create mode 100644 liquid/components/form.liquid create mode 100644 liquid/components/header.liquid create mode 100644 liquid/components/hover-card.liquid create mode 100644 liquid/components/icon.liquid create mode 100644 liquid/components/input-group.liquid create mode 100644 liquid/components/input.liquid create mode 100644 liquid/components/invalid-controls.liquid create mode 100644 liquid/components/item.liquid create mode 100644 liquid/components/kbd.liquid create mode 100644 liquid/components/logo.liquid create mode 100644 liquid/components/marquee.liquid create mode 100644 liquid/components/navigation-menu.liquid create mode 100644 liquid/components/pagination.liquid create mode 100644 liquid/components/popover.liquid create mode 100644 liquid/components/popup.liquid create mode 100644 liquid/components/price.liquid create mode 100644 liquid/components/radio-group.liquid create mode 100644 liquid/components/rating.liquid create mode 100644 liquid/components/section.liquid create mode 100644 liquid/components/select.liquid create mode 100644 liquid/components/separator.liquid create mode 100644 liquid/components/sheet.liquid create mode 100644 liquid/components/skeleton.liquid create mode 100644 liquid/components/slider.liquid create mode 100644 liquid/components/spinner.liquid create mode 100644 liquid/components/switch.liquid create mode 100644 liquid/components/table.liquid create mode 100644 liquid/components/tabs.liquid create mode 100644 liquid/components/toast.liquid create mode 100644 liquid/components/toc.liquid create mode 100644 liquid/components/toggle-group.liquid create mode 100644 liquid/components/toggle.liquid create mode 100644 liquid/components/tooltip.liquid create mode 100644 liquid/components/triggers.liquid create mode 100644 liquid/components/typography.liquid create mode 100644 liquid/components/video.liquid create mode 100644 liquid/contracts.json create mode 100644 liquid/licenses.json create mode 100644 liquid/manifest.json create mode 100644 liquid/registry.json create mode 100644 liquid/runtime/ui-carousel.js create mode 100644 liquid/runtime/ui-controls.js create mode 100644 liquid/runtime/ui-lifecycle.js create mode 100644 liquid/runtime/ui-motion.js create mode 100644 liquid/runtime/ui-start.js create mode 100644 liquid/snippets/init.liquid create mode 100644 liquid/snippets/runtime.liquid create mode 100644 liquid/snippets/ui.liquid create mode 100644 liquid/styles/accordion.css create mode 100644 liquid/styles/card.css create mode 100644 liquid/styles/carousel.css create mode 100644 liquid/styles/field.css create mode 100644 liquid/styles/form.css create mode 100644 liquid/styles/icon.css create mode 100644 liquid/styles/marquee.css create mode 100644 liquid/styles/navigation-menu.css create mode 100644 liquid/styles/price.css create mode 100644 liquid/styles/primitives.css create mode 100644 liquid/styles/rating.css create mode 100644 liquid/styles/section.css create mode 100644 liquid/styles/toast.css create mode 100644 liquid/styles/tokens.css create mode 100644 liquid/styles/typography.css create mode 100644 liquid/styles/video.css create mode 100644 liquid/vendor.json create mode 100644 liquid/vendor/data-slot-accordion.js create mode 100644 liquid/vendor/data-slot-alert-dialog.js create mode 100644 liquid/vendor/data-slot-carousel.js create mode 100644 liquid/vendor/data-slot-collapsible.js create mode 100644 liquid/vendor/data-slot-combobox.js create mode 100644 liquid/vendor/data-slot-command.js create mode 100644 liquid/vendor/data-slot-core.js create mode 100644 liquid/vendor/data-slot-dialog.js create mode 100644 liquid/vendor/data-slot-drawer.js create mode 100644 liquid/vendor/data-slot-dropdown-menu.js create mode 100644 liquid/vendor/data-slot-hover-card.js create mode 100644 liquid/vendor/data-slot-navigation-menu.js create mode 100644 liquid/vendor/data-slot-popover.js create mode 100644 liquid/vendor/data-slot-radio-group.js create mode 100644 liquid/vendor/data-slot-select.js create mode 100644 liquid/vendor/data-slot-slider.js create mode 100644 liquid/vendor/data-slot-switch.js create mode 100644 liquid/vendor/data-slot-tabs.js create mode 100644 liquid/vendor/data-slot-toast.js create mode 100644 liquid/vendor/data-slot-toggle-group.js create mode 100644 liquid/vendor/data-slot-toggle.js create mode 100644 liquid/vendor/data-slot-tooltip.js diff --git a/.prettierignore b/.prettierignore index ae803367e..f3e40fd95 100644 --- a/.prettierignore +++ b/.prettierignore @@ -25,3 +25,11 @@ registry/styles/shadcn/ # Generated from the copies; formatting would reorder classes and change how # conflicting classes merge, so docs previews and installs would differ. registry/styles/adapted/ + +# Vendored assets and generated Liquid metadata retain their exact bytes. +liquid/vendor/ +liquid/LICENSE* +liquid/styles/tokens.css +liquid/contracts.json +liquid/manifest.json +liquid/licenses.json diff --git a/AGENTS.md b/AGENTS.md index 0b42ca665..825b14682 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,8 @@ This repository is the Fulldev UI registry source, docs site and reference imple ## Registry +- Native Liquid lives in `liquid/`, with its own `liquid/registry.json`. Follow `liquid/README.md`; its handwritten CSS does not use the Tailwind style generator. Keep shared tokens and part contracts aligned with the other platforms. Shopify settings, commerce adapters and merchant content stay outside the UI registry. + - `registry.json` is the source of truth for installable items. Update it when you add or remove an item or change its files or dependencies. Item metadata is regenerated by `pnpm registry:meta` (part of `pnpm registry:build`); read `scripts/registry-meta.mjs` for which values it overwrites before editing them by hand. - Docs pages document registry items, but they do not make something installable. - `public/r` is generated registry output; do not hand-edit it. Run `pnpm registry:build` when registry inputs change and commit the result: `pnpm check` rebuilds the registry and fails on a difference. diff --git a/DESIGN.md b/DESIGN.md index 52d260971..6cf92ddff 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -17,6 +17,10 @@ A general homepage for both frameworks, with its content in the frontmatter of ` - Then a selection of components, with the extras React gets from Fulldev UI marked; the block categories with their counts from the registry; the features block; the presets; the FAQ block; and a closing call to action on the primary color. Sections follow the 6xl container; the blocks render as they would on a site, without Preview or Code frames. - The homepage, like Create, belongs to both frameworks: its links lead to the framework chosen last. +## Liquid + +Liquid is the third framework in the shared switcher. It has its own navigation, component pages, native Liquid examples, installation commands and Markdown endpoints under `/liquid/`. Previews render the actual Liquid and CSS in isolated frames, with the shared theme tokens. Native styles stay Vega; Tailwind style presets do not transform Liquid. Shopify blocks and commerce stay theme-owned. + ## Presets The site follows one preset everywhere: theme, style, fonts and menu color, previews included. @@ -34,6 +38,6 @@ The site follows one preset everywhere: theme, style, fonts and menu color, prev ## Rejected - The inset app shell with a collapsible sidebar and breadcrumbs (`Sidebar1`): too busy for docs. -- Per-page Astro/React tabs and a style menu next to every preview: both are global choices in the switcher and on Create. +- Per-page Astro/React/Liquid tabs and a style menu next to every preview: both are global choices in the switcher and on Create. - Docs without a left navigation: too hard to browse 60+ components. - On the homepage: blocks in Preview and Code frames, a hero that leads with Astro only, a split hero, install tabs in the hero, and the bordered call to action card (`Cta8`). diff --git a/eslint.config.js b/eslint.config.js index b7b507fd2..725bbc268 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,14 @@ import globals from "globals" import tseslint from "typescript-eslint" export default defineConfig([ - globalIgnores(["dist", ".astro", ".netlify", ".dev", "public/r"]), + globalIgnores([ + "dist", + ".astro", + ".netlify", + ".dev", + "public/r", + "liquid/vendor", + ]), { files: ["**/*.{js,mjs,ts}"], extends: [js.configs.recommended, tseslint.configs.recommended], diff --git a/knip.jsonc b/knip.jsonc index a2cb200b5..6455ba4c7 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -18,9 +18,14 @@ "src/components/live-code.astro", // Read by the html-validate CLI in the build. "html-validate.config.mjs", + "liquid/runtime/*.js", ], // Read as text by src/lib/styles.ts and the registry scripts. - "ignoreFiles": ["registry/styles/**/*.css"], + "ignoreFiles": [ + "registry/styles/**/*.css", + "liquid/vendor/**", + "liquid/styles/*.css", + ], "ignoreIssues": { // Part of the installed price component, shipped unchanged. "src/components/ui/price/format-price-value.ts": ["exports"], @@ -28,6 +33,12 @@ "ignoreDependencies": [ // Read from node_modules by the Icon component. "lucide-static", + // Native Liquid resolves these through its Shopify import map. + "@data-slot/carousel", + "@fulldev/lifecycle", + "@fulldev/controls", + "@fulldev/carousel", + "@fulldev/motion", ], }, "react": { diff --git a/liquid/LICENSE-DATA-SLOT.md b/liquid/LICENSE-DATA-SLOT.md new file mode 100644 index 000000000..d11af2261 --- /dev/null +++ b/liquid/LICENSE-DATA-SLOT.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Bejamas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/liquid/LICENSE-FULLDEV.txt b/liquid/LICENSE-FULLDEV.txt new file mode 100644 index 000000000..cbd58f91f --- /dev/null +++ b/liquid/LICENSE-FULLDEV.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Fulldev, Sil Veltman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/liquid/LICENSE-SHADCN.md b/liquid/LICENSE-SHADCN.md new file mode 100644 index 000000000..fad4d887a --- /dev/null +++ b/liquid/LICENSE-SHADCN.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 shadcn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/liquid/README.md b/liquid/README.md new file mode 100644 index 000000000..71e37c5a6 --- /dev/null +++ b/liquid/README.md @@ -0,0 +1,17 @@ +# Fulldev UI for Liquid + +Native Shopify Liquid, readable CSS and ES modules. The consumer needs no Tailwind, framework, package manifest or build step. See the [installation and API guide](https://ui.full.dev/liquid/). + +`components/` owns each component's styles and optional markup. Most use Shopify's `{% stylesheet %}` tag. Components with global classes, such as typography's `typeset`, keep their CSS in `styles/`, so Shopify Theme Check can resolve those classes across snippets. Shopify bundles the stylesheet tags; the source stays small and separate. Card parts remain native HTML, styled through `data-slot`. + +`runtime/` owns initialization, native form fallbacks, asynchronous imports, cleanup, editor section events and block selection. It uses unmodified data-slot controllers in `vendor/`. `snippets/ui.liquid` loads the complete component set once. A theme with commerce adapters uses `runtime: false` and imports the lifecycle and control adapters into its existing initializer. + +`registry.json` is the install contract. Every item is universal, every file has an explicit `~/assets/` or `~/snippets/` target, and dependencies use full Liquid registry URLs. Individual items install their prerequisites, including shared popup styles and runtime where needed. `components` is the complete set. `init` is tokens and base styles only. + +Only shared neutral palette tokens, the part inventory, licenses and file hashes are generated by `scripts/registry-liquid.mjs`. CSS and Liquid are maintained by hand. The palette comes from `registry/styles/shadcn/themes.json`; `styles/primitives.css` holds native sizes, radii, shadows and media roles. Theme colors, settings, content, Shopify forms and layouts remain theme-owned. A consumer loads its scheme overrides after `ui`, and `custom.css` after `content_for_header`. + +Run `vp run registry:liquid` after a source change. Commit `public/r/liquid`, `contracts.json`, `styles/tokens.css`, `licenses.json` and `manifest.json`. `vp run test:liquid` verifies the real CLI, the dependency closures, token parity and repeat installs. `vp env exec -- node --test tests/browser/liquid.test.mjs` verifies a CLI-installed copy in Chromium, Firefox and WebKit, including native forms and editor lifecycle. The local renderer is LiquidJS with explicit Shopify filter substitutes. It does not verify a store's commerce objects or Shopify's stylesheet subsetting. + +This first port follows Vega and the complete reusable storefront layer from `fulldotdev/liquid-template` at `e13fc25`. Sidebar, Resizable, Menubar, Scroll Area and React-only app components are outside this native storefront set. Shopify blocks, commerce adapters, translation keys and settings are not installed by the UI registry. The existing storefront media and card variants are additive to the shared component contract. Other Tailwind presets are not translated automatically. + +Vendored modules stay byte-identical to data-slot 1.1.1. Update them together with `vendor.json` and verify the recorded SHA-256 hashes. Every installation includes `assets/ui-licenses.json`, including the original shadcn/ui and data-slot notices. The complete install includes `assets/ui-manifest.json`; consumers can check those hashes to detect drift. Store customizations belong in their own files. diff --git a/liquid/components/accordion.liquid b/liquid/components/accordion.liquid new file mode 100644 index 000000000..fa061466d --- /dev/null +++ b/liquid/components/accordion.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI accordion styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-accordion.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/alert-dialog.liquid b/liquid/components/alert-dialog.liquid new file mode 100644 index 000000000..8783d154e --- /dev/null +++ b/liquid/components/alert-dialog.liquid @@ -0,0 +1,116 @@ +{% doc %} + Fulldev UI alert-dialog styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A confirmation over the page, as Vega's alert dialog: a centered stack on phones and in the small + size; from 640px the default size aligns text left with the media beside the title and description. */ + [data-slot='alert-dialog-overlay'] { + position: fixed; + inset: 0; + z-index: calc(var(--alert-dialog-overlay-stack-index, 0) * 2 + 100); + background: var(--scrim); + transition: opacity 100ms; + } + @supports (backdrop-filter: blur(4px)) { + [data-slot='alert-dialog-overlay'] { + backdrop-filter: blur(4px); + } + } + [data-slot='alert-dialog-content'] { + position: fixed; + top: 50%; + left: 50%; + z-index: calc(var(--alert-dialog-content-stack-index, 0) * 2 + 101); + display: grid; + gap: calc(var(--spacing) * 6); + width: calc(100% - 2rem); + max-width: 20rem; + padding: calc(var(--spacing) * 6); + border-radius: var(--radius-xl); + background: var(--popover); + color: var(--popover-foreground); + font-size: 0.875rem; + box-shadow: 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent); + outline: none; + translate: -50% -50%; + transition: + opacity 100ms, + scale 100ms; + } + [data-slot='alert-dialog-overlay']:is([data-starting-style], [data-ending-style]) { + opacity: 0; + } + [data-slot='alert-dialog-content']:is([data-starting-style], [data-ending-style]) { + opacity: 0; + scale: 0.95; + } + [data-slot='alert-dialog-header'] { + display: grid; + grid-template-rows: auto 1fr; + place-items: center; + gap: calc(var(--spacing) * 1.5); + text-align: center; + } + [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media']) { + grid-template-rows: auto auto 1fr; + column-gap: calc(var(--spacing) * 6); + } + [data-slot='alert-dialog-media'] { + display: inline-flex; + align-items: center; + justify-content: center; + width: calc(var(--spacing) * 16); + height: calc(var(--spacing) * 16); + margin-bottom: calc(var(--spacing) * 2); + border-radius: var(--radius-md); + background: var(--muted); + } + [data-slot='alert-dialog-media'] > .icon { + width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + } + [data-slot='alert-dialog-title'] { + font-size: 1.125rem; + font-weight: 500; + letter-spacing: var(--tracking-normal); + } + [data-slot='alert-dialog-description'] { + color: var(--muted-foreground); + text-wrap: balance; + } + [data-slot='alert-dialog-footer'] { + display: flex; + flex-direction: column-reverse; + gap: calc(var(--spacing) * 2); + } + [data-slot='alert-dialog-content'][data-size='sm'] [data-slot='alert-dialog-footer'] { + display: grid; + grid-template-columns: 1fr 1fr; + } + @media (min-width: 640px) { + [data-slot='alert-dialog-content']:not([data-size='sm']) { + max-width: 32rem; + } + [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-header'] { + place-items: start; + text-align: left; + } + [data-slot='alert-dialog-content']:not([data-size='sm']) + [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media']) { + grid-template-rows: auto 1fr; + } + [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-media'] { + grid-row: span 2; + } + [data-slot='alert-dialog-content']:not([data-size='sm']) + [data-slot='alert-dialog-header']:has([data-slot='alert-dialog-media']) + > [data-slot='alert-dialog-title'] { + grid-column-start: 2; + } + [data-slot='alert-dialog-content']:not([data-size='sm']) [data-slot='alert-dialog-footer'] { + flex-direction: row; + justify-content: flex-end; + } + } +{% endstylesheet %} diff --git a/liquid/components/alert.liquid b/liquid/components/alert.liquid new file mode 100644 index 000000000..bac754a5a --- /dev/null +++ b/liquid/components/alert.liquid @@ -0,0 +1,45 @@ +{% doc %} + Fulldev UI alert styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='alert'] { + display: grid; + gap: calc(var(--spacing) * 0.5); + padding: calc(var(--spacing) * 3) calc(var(--spacing) * 4); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--card); + color: var(--card-foreground); + font-size: 0.875rem; + text-align: left; + } + [data-slot='alert']:has(> .icon) { + grid-template-columns: auto 1fr; + column-gap: calc(var(--spacing) * 2.5); + } + [data-slot='alert'] > .icon { + grid-row: span 2; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + translate: 0 calc(var(--spacing) * 0.5); + color: currentcolor; + } + [data-slot='alert'][data-variant='destructive'] { + color: var(--destructive); + } + [data-slot='alert-title'] { + font-weight: 500; + } + [data-slot='alert-description'] { + color: var(--muted-foreground); + text-wrap: balance; + } + [data-slot='alert'][data-variant='destructive'] [data-slot='alert-description'] { + color: color-mix(in oklch, var(--destructive) 90%, transparent); + } + [data-slot='alert'] ul { + margin: 0; + padding-left: calc(var(--spacing) * 4.5); + } +{% endstylesheet %} diff --git a/liquid/components/aspect-ratio.liquid b/liquid/components/aspect-ratio.liquid new file mode 100644 index 000000000..2984534ac --- /dev/null +++ b/liquid/components/aspect-ratio.liquid @@ -0,0 +1,28 @@ +{% doc %} + Fulldev UI aspect-ratio styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Media at a fixed ratio, covered by its image on a muted fill until it loads. The caller rounds it + as its Fulldev UI block does; framed adds the rounded border of Fulldev UI's video. */ + [data-slot='aspect-ratio'] { + position: relative; + overflow: hidden; + aspect-ratio: var(--ratio, 4 / 3); + background: var(--muted); + } + [data-slot='aspect-ratio'][data-variant='framed'] { + border: 1px solid var(--border); + border-radius: var(--radius-xl); + } + [data-slot='aspect-ratio'] > :is(img, svg, video, iframe, picture) { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + [data-slot='aspect-ratio'] > svg { + fill: color-mix(in oklch, var(--muted-foreground) 35%, transparent); + } +{% endstylesheet %} diff --git a/liquid/components/attachment.liquid b/liquid/components/attachment.liquid new file mode 100644 index 000000000..2f4a6b7a9 --- /dev/null +++ b/liquid/components/attachment.liquid @@ -0,0 +1,134 @@ +{% doc %} + Fulldev UI attachment styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A file with its icon or preview, name and actions, as Fulldev UI's attachment. The caller sets + data-state: idle (dashed), uploading or processing (pulsing name), error, or done. */ + [data-slot='attachment'] { + position: relative; + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + gap: calc(var(--spacing) * 2); + width: fit-content; + min-width: 10rem; + max-width: 100%; + border: 1px solid var(--border); + border-radius: var(--radius-xl); + background: var(--card); + color: var(--card-foreground); + font-size: 0.875rem; + transition: background-color 150ms; + } + [data-slot='attachment']:has([data-slot='attachment-content']) { + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5); + } + [data-slot='attachment']:focus-within { + box-shadow: 0 0 0 1px color-mix(in oklch, var(--ring) 50%, transparent); + } + [data-slot='attachment']:has([data-slot='attachment-trigger']):hover { + background: color-mix(in oklch, var(--muted) 50%, transparent); + } + [data-slot='attachment'][data-state='idle'] { + border-style: dashed; + } + [data-slot='attachment'][data-state='error'] { + border-color: color-mix(in oklch, var(--destructive) 30%, transparent); + } + [data-slot='attachment'][data-size='sm'] { + gap: calc(var(--spacing) * 2.5); + font-size: 0.75rem; + } + [data-slot='attachment'][data-size='sm']:has([data-slot='attachment-content']) { + padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2); + } + [data-slot='attachment-media'] { + position: relative; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: calc(var(--spacing) * 10); + aspect-ratio: 1; + overflow: hidden; + border-radius: var(--radius-lg); + background: var(--muted); + color: var(--foreground); + } + [data-slot='attachment-media'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='attachment'][data-size='sm'] [data-slot='attachment-media'] { + width: calc(var(--spacing) * 8); + } + [data-slot='attachment'][data-state='error'] [data-slot='attachment-media'] { + background: color-mix(in oklch, var(--destructive) 10%, transparent); + color: var(--destructive); + } + [data-slot='attachment-content'] { + display: flex; + flex: 1; + flex-direction: column; + min-width: 0; + line-height: 1.25; + } + [data-slot='attachment-title'], + [data-slot='attachment-description'] { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + [data-slot='attachment-title'] { + font-weight: 500; + } + /* The skeleton's pulse reads at about 3.6:1 at 0.5; the name pulses to 0.65 to keep 4.5:1. */ + [data-slot='attachment'][data-state='uploading'] [data-slot='attachment-title'] { + animation: attachment-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + } + @keyframes attachment-pulse { + 50% { + opacity: 0.65; + } + } + [data-slot='attachment-description'] { + margin-top: calc(var(--spacing) * 0.5); + color: var(--muted-foreground); + font-size: 0.75rem; + } + [data-slot='attachment'][data-state='error'] [data-slot='attachment-description'] { + color: var(--destructive); + } + [data-slot='attachment-actions'] { + position: relative; + z-index: 2; + display: flex; + flex-shrink: 0; + align-items: center; + } + /* A link or button that covers the whole attachment, under its actions. */ + [data-slot='attachment-trigger'] { + position: absolute; + inset: 0; + z-index: 1; + border-radius: inherit; + outline: none; + } + [data-slot='attachment-group'] { + display: flex; + gap: calc(var(--spacing) * 3); + min-width: 0; + overflow-x: auto; + overscroll-behavior-x: contain; + padding-block: var(--spacing); + scroll-snap-type: x mandatory; + } + [data-slot='attachment-group'] > [data-slot='attachment'] { + flex: none; + scroll-snap-align: start; + } +{% endstylesheet %} diff --git a/liquid/components/avatar.liquid b/liquid/components/avatar.liquid new file mode 100644 index 000000000..8d40844cf --- /dev/null +++ b/liquid/components/avatar.liquid @@ -0,0 +1,52 @@ +{% doc %} + Fulldev UI avatar styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='avatar'] { + position: relative; + display: grid; + flex: none; + place-items: center; + width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + overflow: hidden; + border-radius: 50%; + font-size: 0.75rem; + font-weight: 500; + } + [data-slot='avatar'][data-size='sm'] { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + [data-slot='avatar'][data-size='lg'] { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + font-size: 0.875rem; + } + [data-slot='avatar-image'] { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + } + /* Overlapping avatars, each ringed with the background. */ + [data-slot='avatar-group'] { + display: flex; + } + [data-slot='avatar-group'] > [data-slot='avatar'] { + box-shadow: 0 0 0 2px var(--background); + } + [data-slot='avatar-group'] > [data-slot='avatar'] + [data-slot='avatar'] { + margin-inline-start: calc(var(--spacing) * -2); + } + [data-slot='avatar-fallback'] { + display: grid; + place-items: center; + width: 100%; + height: 100%; + border-radius: 50%; + background: var(--muted); + color: var(--muted-foreground); + } +{% endstylesheet %} diff --git a/liquid/components/badge.liquid b/liquid/components/badge.liquid new file mode 100644 index 000000000..37ffce629 --- /dev/null +++ b/liquid/components/badge.liquid @@ -0,0 +1,86 @@ +{% doc %} + Fulldev UI badge styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A label wider than its container wraps, as a button does; one line keeps Vega's 20px. */ + [data-slot='badge'] { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + gap: var(--spacing); + width: fit-content; + max-width: 100%; + min-height: calc(var(--spacing) * 5); + padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 2); + overflow: hidden; + border: 1px solid transparent; + border-radius: var(--radius-4xl); + background: var(--primary); + color: var(--primary-foreground); + font-size: 0.75rem; + font-weight: 500; + line-height: calc(var(--spacing) * 3.5); + text-align: center; + overflow-wrap: anywhere; + text-underline-offset: 4px; + transition: + background-color 150ms, + color 150ms; + } + [data-slot='badge'] > .icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + a[data-slot='badge']:hover { + background: color-mix(in oklch, var(--primary) 80%, transparent); + } + a[data-slot='badge']:focus-visible { + border-color: var(--ring); + outline: none; + box-shadow: var(--focus-ring); + } + [data-slot='badge'][data-variant='secondary'] { + background: var(--secondary); + color: var(--secondary-foreground); + } + a[data-slot='badge'][data-variant='secondary']:hover { + background: color-mix(in oklch, var(--secondary) 80%, transparent); + } + [data-slot='badge'][data-variant='outline'] { + border-color: var(--border); + background: transparent; + color: var(--foreground); + } + a[data-slot='badge'][data-variant='outline']:hover { + background: var(--muted); + color: var(--muted-foreground); + } + [data-slot='badge'][data-variant='destructive'] { + background: light-dark( + color-mix(in oklch, var(--destructive) 10%, transparent), + color-mix(in oklch, var(--destructive) 20%, transparent) + ); + color: var(--destructive); + } + a[data-slot='badge'][data-variant='destructive']:hover { + background: color-mix(in oklch, var(--destructive) 20%, transparent); + } + [data-slot='badge'][data-variant='ghost'] { + background: transparent; + color: var(--foreground); + } + a[data-slot='badge'][data-variant='ghost']:hover { + background: light-dark(var(--muted), color-mix(in oklch, var(--muted) 50%, transparent)); + color: var(--muted-foreground); + } + [data-slot='badge'][data-variant='link'] { + background: transparent; + color: var(--primary); + } + a[data-slot='badge'][data-variant='link']:hover { + background: transparent; + text-decoration: underline; + } +{% endstylesheet %} diff --git a/liquid/components/banner.liquid b/liquid/components/banner.liquid new file mode 100644 index 000000000..a1442496f --- /dev/null +++ b/liquid/components/banner.liquid @@ -0,0 +1,40 @@ +{% doc %} + Fulldev UI banner styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Fulldev UI's banner: inverted across the page, or a floating framed panel inset by the gutter. */ + [data-slot='banner'] { + position: relative; + display: flex; + align-items: center; + width: 100%; + padding-block: 8px; + background: var(--foreground); + color: var(--background); + } + [data-slot='banner'][data-variant='floating'] { + width: calc(100% - 2 * var(--gutter)); + max-width: calc(var(--container) - 2 * var(--gutter)); + margin: 8px auto; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--background); + color: var(--foreground); + box-shadow: var(--shadow-sm); + } + [data-slot='banner-container'] { + position: relative; + display: flex; + gap: 16px; + width: 100%; + max-width: var(--container); + margin-inline: auto; + padding-inline: var(--gutter); + } + [data-slot='banner'] a:not([data-slot]) { + font-weight: 500; + text-decoration: underline; + text-underline-offset: 4px; + } +{% endstylesheet %} diff --git a/liquid/components/base.liquid b/liquid/components/base.liquid new file mode 100644 index 000000000..acd1d0eba --- /dev/null +++ b/liquid/components/base.liquid @@ -0,0 +1,102 @@ +{% doc %} + Fulldev UI base styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + *, + *::before, + *::after { + box-sizing: border-box; + border-color: var(--border); + } + body { + margin: 0; + min-height: 100svh; + display: flex; + flex-direction: column; + background: var(--background); + color: var(--foreground); + font: 16px/1.5 var(--font-sans); + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; + } + main { + flex: 1; + } + img, + video, + svg, + iframe { + max-width: 100%; + } + img, + video, + svg { + display: block; + height: auto; + } + a { + color: inherit; + text-decoration: none; + text-underline-offset: 4px; + } + button, + input, + select, + textarea { + font: inherit; + color: inherit; + } + button, + summary { + cursor: pointer; + } + button { + border: 0; + background: none; + padding: 0; + border-radius: 0; + } + :focus-visible { + outline: 2px solid color-mix(in oklch, var(--ring) 50%, transparent); + outline-offset: 2px; + } + [hidden] { + display: none !important; + } + h1, + h2, + h3, + h4, + p, + figure, + blockquote, + fieldset, + dl, + dd { + margin: 0; + } + fieldset { + border: 0; + padding: 0; + min-width: 0; + } + /* Lists that a part or layout styles with a class; merchant rich text keeps its markers. */ + :where(ul, ol)[class] { + margin: 0; + padding: 0; + list-style: none; + } + legend { + padding: 0; + } + p, + h1, + h2, + h3, + h4, + a, + li { + overflow-wrap: anywhere; + } +{% endstylesheet %} diff --git a/liquid/components/breadcrumb.liquid b/liquid/components/breadcrumb.liquid new file mode 100644 index 000000000..feae6ebd3 --- /dev/null +++ b/liquid/components/breadcrumb.liquid @@ -0,0 +1,46 @@ +{% doc %} + Fulldev UI breadcrumb styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* The breadcrumb nav; long page titles wrap inside it. */ + [data-slot='breadcrumb'] { + min-width: 0; + overflow-wrap: anywhere; + } + [data-slot='breadcrumb-list'] { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: calc(var(--spacing) * 1.5); + margin: 0; + padding: 0; + list-style: none; + font-size: 0.875rem; + color: var(--muted-foreground); + overflow-wrap: anywhere; + } + @media (min-width: 640px) { + [data-slot='breadcrumb-list'] { + gap: calc(var(--spacing) * 2.5); + } + } + [data-slot='breadcrumb-item'] { + display: inline-flex; + align-items: center; + gap: calc(var(--spacing) * 1.5); + } + [data-slot='breadcrumb-link'] { + transition: color 150ms; + } + [data-slot='breadcrumb-link']:hover { + color: var(--foreground); + } + [data-slot='breadcrumb-page'] { + color: var(--foreground); + } + [data-slot='breadcrumb-separator'] > .icon { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } +{% endstylesheet %} diff --git a/liquid/components/button-group.liquid b/liquid/components/button-group.liquid new file mode 100644 index 000000000..3214b5e04 --- /dev/null +++ b/liquid/components/button-group.liquid @@ -0,0 +1,56 @@ +{% doc %} + Fulldev UI button-group styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='button-group'] { + display: flex; + align-items: stretch; + width: fit-content; + } + [data-slot='button-group'] > :focus-visible { + position: relative; + z-index: 1; + } + [data-slot='button-group'] > input { + flex: 1; + } + [data-slot='button-group']:not([data-orientation='vertical']) > [data-slot]:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + [data-slot='button-group']:not([data-orientation='vertical']) > [data-slot] ~ [data-slot] { + border-left-width: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + [data-slot='button-group'][data-orientation='vertical'] { + flex-direction: column; + } + [data-slot='button-group'][data-orientation='vertical'] > [data-slot]:not(:last-child) { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + [data-slot='button-group'][data-orientation='vertical'] > [data-slot] ~ [data-slot] { + border-top-width: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + [data-slot='button-group-text'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + padding-inline: calc(var(--spacing) * 2.5); + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--muted); + font-size: 0.875rem; + font-weight: 500; + box-shadow: var(--shadow-xs); + } + [data-slot='button-group-separator'] { + align-self: stretch; + width: 1px; + background: var(--input); + } +{% endstylesheet %} diff --git a/liquid/components/button.liquid b/liquid/components/button.liquid new file mode 100644 index 000000000..7ae916a9c --- /dev/null +++ b/liquid/components/button.liquid @@ -0,0 +1,232 @@ +{% doc %} + A button or link. Omitting label registers its styles for native data-slot markup. + + @param {string} [label] - Plain text label, escaped. + @param {string} [href] - Link destination. + @param {string} [variant] - Visual variant. + @param {string} [size] - Button size. + @param {string} [type] - Native button type, defaults to button. + @param {boolean} [disabled] - Disable the button. +{% enddoc %} + +{% if label != blank %} + {% if href != blank %} + + {{- label | escape -}} + + {% else %} + + {% endif %} +{% endif %} + +{% stylesheet %} + /* A part that keeps a behavioral data-slot of its own, such as dialog-close or carousel-next, + takes the button look with data-button and the same data-variant and data-size values. */ + /* Merchant labels can be long: a label wider than its container wraps inside the button, which + then grows from its fixed height. One line keeps the Vega height in every size. */ + :is([data-slot='button'], [data-button]) { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + gap: calc(var(--spacing) * 1.5); + max-width: 100%; + min-height: calc(var(--spacing) * 9); + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5); + border: 1px solid transparent; + border-radius: var(--radius-md); + background: var(--primary); + background-clip: padding-box; + color: var(--primary-foreground); + font-size: 0.875rem; + font-weight: 500; + line-height: 1.25; + text-align: center; + overflow-wrap: anywhere; + text-decoration: none; + text-underline-offset: 4px; + cursor: pointer; + user-select: none; + outline: none; + transition: + background-color 150ms, + color 150ms, + border-color 150ms, + box-shadow 150ms, + translate 150ms; + } + :is([data-slot='button'], [data-button]) > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + pointer-events: none; + } + :is([data-slot='button'], [data-button]):where(:has(> .icon:last-child:not(:first-child))) { + padding-right: calc(var(--spacing) * 2); + } + :is([data-slot='button'], [data-button]):where(:has(> .icon:first-child:not(:last-child))) { + padding-left: calc(var(--spacing) * 2); + } + :is([data-slot='button'], [data-button]):hover { + background-color: color-mix(in oklch, var(--primary) 80%, transparent); + } + :is([data-slot='button'], [data-button]):focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + :is([data-slot='button'], [data-button])[aria-invalid='true'] { + border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent)); + box-shadow: 0 0 0 3px + light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 40%, transparent) + ); + } + :is([data-slot='button'], [data-button]):active:not([aria-haspopup]) { + translate: 0 1px; + } + :is([data-slot='button'], [data-button]):disabled, + :is([data-slot='button'], [data-button])[aria-disabled='true'] { + opacity: 0.5; + pointer-events: none; + } + :is([data-slot='button'], [data-button])[data-variant='outline'] { + border-color: light-dark(var(--border), var(--input)); + background-color: light-dark(var(--background), color-mix(in oklch, var(--input) 30%, transparent)); + color: var(--foreground); + box-shadow: var(--shadow-xs); + } + :is([data-slot='button'], [data-button])[data-variant='outline']:hover { + background-color: light-dark(var(--muted), color-mix(in oklch, var(--input) 50%, transparent)); + color: var(--foreground); + } + :is([data-slot='button'], [data-button])[data-variant='outline'][aria-expanded='true'] { + background-color: var(--muted); + } + :is([data-slot='button'], [data-button])[data-variant='outline']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + :is([data-slot='button'], [data-button])[data-variant='secondary'] { + background-color: var(--secondary); + color: var(--secondary-foreground); + } + :is([data-slot='button'], [data-button])[data-variant='secondary']:hover { + background-color: color-mix(in oklch, var(--secondary), var(--foreground) 5%); + } + :is([data-slot='button'], [data-button])[data-variant='ghost'] { + background-color: transparent; + color: inherit; + } + :is([data-slot='button'], [data-button])[data-variant='ghost']:hover { + background-color: light-dark(var(--muted), color-mix(in oklch, var(--muted) 50%, transparent)); + color: var(--foreground); + } + :is([data-slot='button'], [data-button])[data-variant='ghost'][aria-expanded='true'] { + background-color: var(--muted); + color: var(--foreground); + } + :is([data-slot='button'], [data-button])[data-variant='destructive'] { + background-color: light-dark( + color-mix(in oklch, var(--destructive) 10%, transparent), + color-mix(in oklch, var(--destructive) 20%, transparent) + ); + /* Vega's destructive text on its own tint is about 4:1; mixing in the foreground keeps 4.5:1. */ + color: color-mix(in oklch, var(--destructive) 65%, var(--foreground)); + } + :is([data-slot='button'], [data-button])[data-variant='destructive']:hover { + background-color: light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 30%, transparent) + ); + } + :is([data-slot='button'], [data-button])[data-variant='destructive']:focus-visible { + border-color: color-mix(in oklch, var(--destructive) 40%, transparent); + box-shadow: 0 0 0 3px + light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 40%, transparent) + ); + } + :is([data-slot='button'], [data-button])[data-variant='link'] { + background: none; + color: var(--primary); + } + :is([data-slot='button'], [data-button])[data-variant='link']:hover { + text-decoration: underline; + } + /* A fixed white button on photos and video. */ + :is([data-slot='button'], [data-button])[data-variant='media'] { + background-color: var(--media-button); + color: var(--media-button-foreground); + } + :is([data-slot='button'], [data-button])[data-variant='media']:hover { + background-color: var(--media-button-hover); + } + /* The secondary button on photos: a translucent white fill. */ + :is([data-slot='button'], [data-button])[data-variant='media-secondary'] { + background-color: var(--media-secondary); + color: var(--on-media); + } + :is([data-slot='button'], [data-button])[data-variant='media-secondary']:hover { + background-color: var(--media-secondary-hover); + } + :is([data-slot='button'], [data-button])[data-size='xs'] { + gap: var(--spacing); + min-height: calc(var(--spacing) * 6); + padding: calc(var(--spacing) * 0.75) calc(var(--spacing) * 2); + border-radius: min(var(--radius-md), 8px); + font-size: 0.75rem; + } + :is([data-slot='button'], [data-button])[data-size='xs'] > .icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + :is([data-slot='button'], [data-button])[data-size='sm'] { + gap: var(--spacing); + min-height: calc(var(--spacing) * 8); + padding-block: calc(var(--spacing) * 1.5); + border-radius: min(var(--radius-md), 10px); + } + :is([data-slot='button'], [data-button])[data-size='lg'] { + min-height: calc(var(--spacing) * 10); + } + :is([data-slot='button'], [data-button])[data-size^='icon'] { + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + min-height: 0; + padding: 0; + } + :is([data-slot='button'], [data-button])[data-size='icon-xs'] { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + border-radius: min(var(--radius-md), 8px); + } + :is([data-slot='button'], [data-button])[data-size='icon-xs'] > .icon { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + :is([data-slot='button'], [data-button])[data-size='icon-sm'] { + width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + border-radius: min(var(--radius-md), 10px); + } + :is([data-slot='button'], [data-button])[data-size='icon-lg'] { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + } +{% endstylesheet %} diff --git a/liquid/components/card.liquid b/liquid/components/card.liquid new file mode 100644 index 000000000..450f04338 --- /dev/null +++ b/liquid/components/card.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI card styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-card.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/carousel.liquid b/liquid/components/carousel.liquid new file mode 100644 index 000000000..396ff721d --- /dev/null +++ b/liquid/components/carousel.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI carousel styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-carousel.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/checkbox.liquid b/liquid/components/checkbox.liquid new file mode 100644 index 000000000..e73ab31c3 --- /dev/null +++ b/liquid/components/checkbox.liquid @@ -0,0 +1,54 @@ +{% doc %} + Fulldev UI checkbox styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Fulldev UI's checkbox: a native checkbox with Vega's box and a check mark. */ + [data-slot='checkbox'] { + position: relative; + flex-shrink: 0; + appearance: none; + border: 1px solid var(--input); + border-radius: 4px; + background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent)); + color: var(--primary-foreground); + box-shadow: var(--shadow-xs); + outline: none; + transition: box-shadow 150ms; + } + [data-slot='checkbox']::before { + content: ''; + position: absolute; + inset: 0; + margin: auto; + width: 14px; + height: 14px; + background-color: currentcolor; + opacity: 0; + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E") + center / 14px no-repeat; + } + [data-slot='checkbox']:checked { + border-color: var(--primary); + background-color: var(--primary); + } + [data-slot='checkbox']:checked::before { + opacity: 1; + } + [data-slot='checkbox']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='checkbox'][aria-invalid='true'] { + border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent)); + box-shadow: 0 0 0 3px + light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 40%, transparent) + ); + } + /* As Fulldev UI's compiled checkbox: checked keeps the primary border, except in dark mode. */ + [data-slot='checkbox'][aria-invalid='true']:checked { + border-color: light-dark(var(--primary), color-mix(in oklch, var(--destructive) 50%, transparent)); + } +{% endstylesheet %} diff --git a/liquid/components/collapsible.liquid b/liquid/components/collapsible.liquid new file mode 100644 index 000000000..2856755fd --- /dev/null +++ b/liquid/components/collapsible.liquid @@ -0,0 +1,3 @@ +{% doc %} + collapsible uses its shared popup or trigger styles and native data-slot controller. +{% enddoc %} diff --git a/liquid/components/combobox.liquid b/liquid/components/combobox.liquid new file mode 100644 index 000000000..0bef2ce8c --- /dev/null +++ b/liquid/components/combobox.liquid @@ -0,0 +1,87 @@ +{% doc %} + Fulldev UI combobox styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* The list of a data-slot combobox, after Vega's combobox. The positioner and the content are + portaled to the body, so the caller puts its scheme class on combobox-portal. */ + [data-slot='combobox-positioner'] { + z-index: 1000; + isolation: isolate; + } + [data-slot='combobox-content'] { + width: var(--anchor-width); + min-width: 9rem; + max-width: var(--available-width); + max-height: min(18rem, var(--available-height)); + overflow: hidden; + border-radius: var(--radius-md); + background: var(--popover); + color: var(--popover-foreground); + box-shadow: + 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent), + var(--shadow-md); + opacity: 1; + transform: scale(1); + transform-origin: var(--transform-origin); + transition: + opacity 100ms, + transform 100ms; + } + [data-slot='combobox-content'][data-starting-style], + [data-slot='combobox-content'][data-ending-style] { + opacity: 0; + transform: scale(0.95); + } + [data-slot='combobox-list'] { + max-height: min(18rem, var(--available-height)); + overflow-y: auto; + overscroll-behavior: contain; + padding: 4px; + scroll-padding-block: 4px; + scrollbar-width: none; + } + [data-slot='combobox-list'][data-empty] { + padding: 0; + } + [data-slot='combobox-item'] { + position: relative; + display: flex; + align-items: center; + gap: 8px; + padding: 6px 32px 6px 8px; + border-radius: var(--radius-sm); + font-size: 0.875rem; + cursor: default; + user-select: none; + outline: none; + } + [data-slot='combobox-item'][data-highlighted] { + background: var(--accent); + color: var(--accent-foreground); + } + [data-slot='combobox-item'][data-disabled] { + opacity: 0.5; + pointer-events: none; + } + [data-slot='combobox-item-indicator'] { + position: absolute; + right: 8px; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + pointer-events: none; + } + [data-slot='combobox-item-indicator'] .icon { + width: 16px; + height: 16px; + } + [data-slot='combobox-empty'] { + padding: 8px; + text-align: center; + font-size: 0.875rem; + color: var(--muted-foreground); + } +{% endstylesheet %} diff --git a/liquid/components/command.liquid b/liquid/components/command.liquid new file mode 100644 index 000000000..cc1d85a1f --- /dev/null +++ b/liquid/components/command.liquid @@ -0,0 +1,86 @@ +{% doc %} + Fulldev UI command styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='command'] { + display: flex; + flex-direction: column; + overflow: hidden; + } + + [data-slot='command-input-wrapper'] { + padding: 4px 4px 0; + } + + [data-slot='command-input'] { + flex: 1; + height: 100%; + padding: 0 4px; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + font-size: 0.875rem; + } + + [data-slot='command-input']:focus-visible { + box-shadow: none; + outline: 0; + } + + [data-slot='command-input']::-webkit-search-cancel-button { + appearance: none; + } + + [data-slot='command-list'] { + height: var(--search-results-height, auto); + max-height: 18rem; + overflow: hidden auto; + scroll-padding-block: 4px; + scrollbar-width: none; + transition: height 150ms ease-out; + } + + [data-slot='command-group'] { + padding: 4px; + } + + [data-slot='command-group'] + [data-slot='command-group'] { + border-top: 1px solid var(--border); + } + + [data-slot='command-group-heading'] { + padding: 6px 8px; + font-size: 0.75rem; + font-weight: 500; + color: var(--muted-foreground); + } + + [data-slot='command-item'] { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 6px 8px; + border-radius: var(--radius-lg); + cursor: default; + user-select: none; + } + + [data-slot='command-item'][data-selected] { + background: var(--muted); + color: var(--foreground); + } + + [data-slot='command-item'] > .icon { + width: 16px; + height: 16px; + color: var(--muted-foreground); + } + + [data-slot='command-item'][data-selected] > .icon { + color: var(--foreground); + } +{% endstylesheet %} diff --git a/liquid/components/dialog.liquid b/liquid/components/dialog.liquid new file mode 100644 index 000000000..2517158ac --- /dev/null +++ b/liquid/components/dialog.liquid @@ -0,0 +1,80 @@ +{% doc %} + Fulldev UI dialog styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Dialogs, sheets and search, after shadcn/ui's base-vega dialog, sheet and command styles. */ + [data-slot='dialog-overlay'] { + position: fixed; + inset: 0; + z-index: calc(var(--dialog-overlay-stack-index, 0) * 2 + 100); + background: var(--scrim); + opacity: 1; + transition: opacity 150ms; + } + @supports (backdrop-filter: blur(4px)) { + [data-slot='dialog-overlay'] { + backdrop-filter: blur(4px); + } + } + [data-slot='dialog-overlay'][data-starting-style], + [data-slot='dialog-overlay'][data-ending-style] { + opacity: 0; + } + + /* A centered panel, as Vega's DialogContent. Sheets and the search dialog adjust it below. */ + [data-slot='dialog-content'] { + position: fixed; + top: 50%; + left: 50%; + z-index: calc(var(--dialog-content-stack-index, 0) * 2 + 101); + display: grid; + gap: calc(var(--spacing) * 6); + width: 100%; + max-width: calc(100% - 2rem); + max-height: calc(100% - 2rem); + overflow-y: auto; + padding: calc(var(--spacing) * 6); + border-radius: var(--radius-xl); + background: var(--popover); + color: var(--popover-foreground); + font-size: 0.875rem; + box-shadow: 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent); + outline: none; + opacity: 1; + transform: translate(-50%, -50%) scale(1); + transition: + opacity 100ms, + transform 100ms; + } + [data-slot='dialog-content'][data-starting-style], + [data-slot='dialog-content'][data-ending-style] { + opacity: 0; + transform: translate(-50%, -50%) scale(0.95); + } + [data-slot='dialog-header'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + } + [data-slot='dialog-title'] { + font-size: 1rem; + font-weight: 500; + letter-spacing: var(--tracking-normal); + line-height: 1; + } + [data-slot='dialog-description'] { + color: var(--muted-foreground); + } + /* The close button in a panel's corner, as Vega's dialog. */ + [data-slot='dialog-content']:not([data-side]) > [data-slot='dialog-close'] { + position: absolute; + top: calc(var(--spacing) * 4); + right: calc(var(--spacing) * 4); + } + @media (min-width: 640px) { + [data-slot='dialog-content'] { + max-width: 28rem; + } + } +{% endstylesheet %} diff --git a/liquid/components/drawer.liquid b/liquid/components/drawer.liquid new file mode 100644 index 000000000..03accaa11 --- /dev/null +++ b/liquid/components/drawer.liquid @@ -0,0 +1,97 @@ +{% doc %} + Fulldev UI drawer styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A bottom drawer, as Vega's drawer. The controller sets the snap and swipe offsets. */ + [data-slot='drawer-backdrop'] { + position: fixed; + inset: 0; + z-index: calc(var(--drawer-overlay-stack-index, 0) * 2 + 100); + background: var(--scrim); + opacity: calc(1 - var(--drawer-swipe-progress, 0)); + transition: opacity 450ms cubic-bezier(0.32, 0.72, 0, 1); + } + @supports (backdrop-filter: blur(4px)) { + [data-slot='drawer-backdrop'] { + backdrop-filter: blur(4px); + } + } + [data-slot='drawer-backdrop']:is([data-starting-style], [data-ending-style]) { + opacity: 0; + } + [data-slot='drawer-backdrop'][data-swiping] { + transition: none; + } + [data-slot='drawer-popup'] { + position: fixed; + inset-inline: 0; + bottom: 0; + z-index: calc(var(--drawer-content-stack-index, 0) * 2 + 101); + display: flex; + flex-direction: column; + width: 100%; + max-height: 80vh; + border-top: 1px solid var(--border); + border-radius: var(--radius-xl) var(--radius-xl) 0 0; + background: var(--popover); + color: var(--popover-foreground); + font-size: 0.875rem; + box-shadow: var(--shadow-lg); + outline: none; + transform: translateY(calc(var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-amount-y, 0px))); + transition: transform 450ms cubic-bezier(0.32, 0.72, 0, 1); + } + [data-slot='drawer-popup']:is([data-starting-style], [data-ending-style]) { + transform: translateY(100%); + } + [data-slot='drawer-popup'][data-swiping] { + transition: none; + } + [data-slot='drawer-handle'] { + flex-shrink: 0; + width: 100px; + height: 6px; + margin: calc(var(--spacing) * 4) auto 0; + border-radius: 9999px; + background: var(--muted); + } + [data-slot='drawer-content'] { + display: flex; + flex-direction: column; + width: 100%; + max-width: 32rem; + min-height: 0; + margin-inline: auto; + overflow-y: auto; + } + [data-slot='drawer-header'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 0.5); + padding: calc(var(--spacing) * 4); + text-align: center; + } + [data-slot='drawer-title'] { + color: var(--foreground); + font-size: 1rem; + font-weight: 500; + letter-spacing: var(--tracking-normal); + } + [data-slot='drawer-description'] { + color: var(--muted-foreground); + } + [data-slot='drawer-footer'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + margin-top: auto; + padding: calc(var(--spacing) * 4); + } + @media (min-width: 768px) { + [data-slot='drawer-header'] { + gap: calc(var(--spacing) * 1.5); + text-align: left; + } + } +{% endstylesheet %} diff --git a/liquid/components/dropdown-menu.liquid b/liquid/components/dropdown-menu.liquid new file mode 100644 index 000000000..9a0a32782 --- /dev/null +++ b/liquid/components/dropdown-menu.liquid @@ -0,0 +1,3 @@ +{% doc %} + dropdown-menu uses its shared popup or trigger styles and native data-slot controller. +{% enddoc %} diff --git a/liquid/components/empty.liquid b/liquid/components/empty.liquid new file mode 100644 index 000000000..b975b09ad --- /dev/null +++ b/liquid/components/empty.liquid @@ -0,0 +1,72 @@ +{% doc %} + Fulldev UI empty styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='empty'] { + display: flex; + flex: 1; + flex-direction: column; + align-items: center; + justify-content: center; + gap: calc(var(--spacing) * 4); + width: 100%; + min-width: 0; + padding: calc(var(--spacing) * 12); + border-radius: var(--radius-lg); + text-align: center; + text-wrap: balance; + } + [data-slot='empty'][data-variant='outline'] { + border: 1px dashed var(--border); + } + [data-slot='empty-header'] { + display: flex; + flex-direction: column; + align-items: center; + gap: calc(var(--spacing) * 2); + max-width: 24rem; + } + [data-slot='empty-icon'] { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + margin-bottom: calc(var(--spacing) * 2); + } + [data-slot='empty-icon'][data-variant='icon'] { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + border-radius: var(--radius-lg); + background: var(--muted); + color: var(--foreground); + } + [data-slot='empty-icon'][data-variant='icon'] > .icon { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + [data-slot='empty-title'] { + font-size: 1.125rem; + font-weight: 500; + line-height: 1.75rem; + letter-spacing: var(--tracking-tight); + } + [data-slot='empty-description'] { + font-size: 0.875rem; + line-height: 1.625; + color: var(--muted-foreground); + } + [data-slot='empty-description'] a { + text-decoration: underline; + text-underline-offset: 4px; + } + [data-slot='empty-content'] { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: calc(var(--spacing) * 3); + width: 100%; + max-width: 24rem; + font-size: 0.875rem; + } +{% endstylesheet %} diff --git a/liquid/components/field.liquid b/liquid/components/field.liquid new file mode 100644 index 000000000..6ac5ef2b4 --- /dev/null +++ b/liquid/components/field.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI field styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-field.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/form.liquid b/liquid/components/form.liquid new file mode 100644 index 000000000..3d8eb67da --- /dev/null +++ b/liquid/components/form.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI form styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-form.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/header.liquid b/liquid/components/header.liquid new file mode 100644 index 000000000..86ebba901 --- /dev/null +++ b/liquid/components/header.liquid @@ -0,0 +1,33 @@ +{% doc %} + Fulldev UI header styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Fulldev UI's header: a full-width bar, or a floating framed bar inset by the gutter. */ + [data-slot='header'] { + position: relative; + z-index: 50; + display: flex; + align-items: center; + gap: 16px; + width: 100%; + padding-block: 8px; + background: var(--background); + color: var(--foreground); + } + [data-slot='header'][data-variant='floating'] { + width: calc(100% - 2 * var(--gutter)); + max-width: calc(var(--container) - 2 * var(--gutter)); + margin: 8px auto; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + } + + [data-slot='header-group'] { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + } +{% endstylesheet %} diff --git a/liquid/components/hover-card.liquid b/liquid/components/hover-card.liquid new file mode 100644 index 000000000..ad72b7823 --- /dev/null +++ b/liquid/components/hover-card.liquid @@ -0,0 +1,3 @@ +{% doc %} + hover-card uses its shared popup or trigger styles and native data-slot controller. +{% enddoc %} diff --git a/liquid/components/icon.liquid b/liquid/components/icon.liquid new file mode 100644 index 000000000..935230bff --- /dev/null +++ b/liquid/components/icon.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI icon styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-icon.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/input-group.liquid b/liquid/components/input-group.liquid new file mode 100644 index 000000000..26000a661 --- /dev/null +++ b/liquid/components/input-group.liquid @@ -0,0 +1,53 @@ +{% doc %} + Fulldev UI input-group styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* An input with addons inside one border, as Fulldev UI's input group. The input keeps the slot of + its own component, such as combobox-input. */ + [data-slot='input-group'] { + display: flex; + align-items: center; + width: 100%; + min-width: 0; + height: calc(var(--spacing) * 9); + border: 1px solid var(--input); + border-radius: var(--radius-md); + background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent)); + box-shadow: var(--shadow-xs); + transition: + border-color 150ms, + box-shadow 150ms; + } + [data-slot='input-group'][data-size='sm'] { + height: calc(var(--spacing) * 8); + } + [data-slot='input-group']:has(> input:focus-visible) { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='input-group'] > input { + flex: 1; + height: 100%; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + } + [data-slot='input-group'] > input:focus-visible { + box-shadow: none; + } + [data-slot='input-group-addon'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + padding-right: calc(var(--spacing) * 2); + color: var(--muted-foreground); + font-size: 0.875rem; + font-weight: 500; + } + [data-slot='input-group-addon'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } +{% endstylesheet %} diff --git a/liquid/components/input.liquid b/liquid/components/input.liquid new file mode 100644 index 000000000..46d539161 --- /dev/null +++ b/liquid/components/input.liquid @@ -0,0 +1,74 @@ +{% doc %} + Fulldev UI input styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])), + [data-slot='input'], + [data-slot='native-select'], + select, + [data-slot='textarea'], + textarea { + width: 100%; + min-width: 0; + height: calc(var(--spacing) * 9); + padding: var(--spacing) calc(var(--spacing) * 2.5); + border: 1px solid var(--input); + border-radius: var(--radius-md); + background-color: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent)); + font-size: 1rem; + line-height: 1.5; + box-shadow: var(--shadow-xs); + outline: none; + transition: + color 150ms, + border-color 150ms, + box-shadow 150ms; + } + input::placeholder, + textarea::placeholder { + color: var(--muted-foreground); + } + input:where(:not([type='hidden'], [type='checkbox'], [type='radio'])):focus-visible, + select:focus-visible, + textarea:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + :is(input, select, textarea)[aria-invalid='true'] { + border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent)); + box-shadow: 0 0 0 3px + light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 40%, transparent) + ); + } + :is(input, select, textarea):disabled { + cursor: not-allowed; + opacity: 0.5; + } + textarea { + height: auto; + min-height: calc(var(--spacing) * 16); + padding-block: calc(var(--spacing) * 2); + resize: vertical; + field-sizing: content; + } + select { + appearance: none; + padding-right: calc(var(--spacing) * 8); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + } + :is(input, select)[data-size='sm'] { + height: calc(var(--spacing) * 8); + } + input[type='checkbox'], + input[type='radio'] { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + margin: 0; + accent-color: var(--primary); + } +{% endstylesheet %} diff --git a/liquid/components/invalid-controls.liquid b/liquid/components/invalid-controls.liquid new file mode 100644 index 000000000..09b06a139 --- /dev/null +++ b/liquid/components/invalid-controls.liquid @@ -0,0 +1,18 @@ +{% doc %} + Fulldev UI invalid-controls styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Vega's invalid border and ring, after the focus rules so they win. The select controller sets + aria-invalid on its trigger. The radio group and switch controllers validate the native input + they add right after each item and switch, which matches :user-invalid after a submit attempt. */ + [data-slot='select-trigger'][aria-invalid='true'], + :is([data-slot='radio-group-item'], [data-slot='switch']):is([aria-invalid='true'], :has(+ input:user-invalid)) { + border-color: light-dark(var(--destructive), color-mix(in oklch, var(--destructive) 50%, transparent)); + box-shadow: 0 0 0 3px + light-dark( + color-mix(in oklch, var(--destructive) 20%, transparent), + color-mix(in oklch, var(--destructive) 40%, transparent) + ); + } +{% endstylesheet %} diff --git a/liquid/components/item.liquid b/liquid/components/item.liquid new file mode 100644 index 000000000..fe0aba6fe --- /dev/null +++ b/liquid/components/item.liquid @@ -0,0 +1,114 @@ +{% doc %} + Fulldev UI item styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A row of media, content and actions, as Vega's item. */ + [data-slot='item'] { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: calc(var(--spacing) * 3.5); + width: 100%; + padding: calc(var(--spacing) * 3.5) calc(var(--spacing) * 4); + border: 1px solid transparent; + border-radius: var(--radius-md); + font-size: 0.875rem; + outline: none; + transition: background-color 100ms; + } + a[data-slot='item']:hover { + background: var(--muted); + } + [data-slot='item']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='item'][data-variant='outline'] { + border-color: var(--border); + } + [data-slot='item'][data-variant='muted'] { + background: color-mix(in oklch, var(--muted) 50%, transparent); + } + [data-slot='item'][data-size='sm'] { + gap: calc(var(--spacing) * 2.5); + padding: calc(var(--spacing) * 2.5) calc(var(--spacing) * 3); + } + [data-slot='item'][data-size='xs'] { + gap: calc(var(--spacing) * 2); + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2.5); + } + [data-slot='item-media'] { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + gap: calc(var(--spacing) * 2); + } + [data-slot='item']:has([data-slot='item-description']) > [data-slot='item-media'] { + align-self: flex-start; + translate: 0 2px; + } + [data-slot='item-media'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='item-media'][data-variant='image'] { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + overflow: hidden; + border-radius: var(--radius-sm); + } + [data-slot='item-media'][data-variant='image'] img { + width: 100%; + height: 100%; + object-fit: cover; + } + [data-slot='item-content'] { + display: flex; + flex: 1; + flex-direction: column; + gap: var(--spacing); + min-width: 0; + } + [data-slot='item-title'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + font-weight: 500; + line-height: 1.375; + } + [data-slot='item-description'] { + color: var(--muted-foreground); + line-height: 1.5; + } + [data-slot='item'][data-size='xs'] [data-slot='item-content'] { + gap: 0; + } + [data-slot='item'][data-size='xs'] [data-slot='item-description'] { + font-size: 0.75rem; + } + [data-slot='item-actions'], + [data-slot='item-header'], + [data-slot='item-footer'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + } + [data-slot='item-header'], + [data-slot='item-footer'] { + flex-basis: 100%; + justify-content: space-between; + } + [data-slot='item-group'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 4); + margin: 0; + padding: 0; + list-style: none; + } + [data-slot='item-group']:has([data-slot='item'][data-size='sm']) { + gap: calc(var(--spacing) * 2.5); + } +{% endstylesheet %} diff --git a/liquid/components/kbd.liquid b/liquid/components/kbd.liquid new file mode 100644 index 000000000..cc084de57 --- /dev/null +++ b/liquid/components/kbd.liquid @@ -0,0 +1,20 @@ +{% doc %} + Fulldev UI kbd styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='kbd'] { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing); + width: fit-content; + min-width: calc(var(--spacing) * 5); + height: calc(var(--spacing) * 5); + padding-inline: var(--spacing); + border-radius: var(--radius-sm); + background: var(--muted); + color: var(--muted-foreground); + font: 500 0.75rem/1 var(--font-sans); + } +{% endstylesheet %} diff --git a/liquid/components/logo.liquid b/liquid/components/logo.liquid new file mode 100644 index 000000000..a5b2ae813 --- /dev/null +++ b/liquid/components/logo.liquid @@ -0,0 +1,33 @@ +{% doc %} + Fulldev UI logo styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A wordmark stays on one line and ends in an ellipsis when it is wider than its container; + the full name stays in the text. */ + [data-slot='logo'] { + display: flex; + align-items: center; + gap: 8px; + max-width: 100%; + height: 40px; + font-family: var(--font-heading); + font-size: 1rem; + font-weight: 600; + letter-spacing: var(--tracking-tight); + white-space: nowrap; + } + [data-slot='logo-text'] { + overflow: hidden; + font-size: 1.125rem; + text-overflow: ellipsis; + } + [data-slot='logo-image'] { + flex: none; + width: var(--logo-width, auto); + height: auto; + max-height: 40px; + object-fit: contain; + object-position: left; + } +{% endstylesheet %} diff --git a/liquid/components/marquee.liquid b/liquid/components/marquee.liquid new file mode 100644 index 000000000..78cca7f2b --- /dev/null +++ b/liquid/components/marquee.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI marquee styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-marquee.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/navigation-menu.liquid b/liquid/components/navigation-menu.liquid new file mode 100644 index 000000000..0b4d90315 --- /dev/null +++ b/liquid/components/navigation-menu.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI navigation-menu styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-navigation-menu.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/pagination.liquid b/liquid/components/pagination.liquid new file mode 100644 index 000000000..4decd1ae7 --- /dev/null +++ b/liquid/components/pagination.liquid @@ -0,0 +1,32 @@ +{% doc %} + Fulldev UI pagination styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Links take the button look: ghost, and outline for the current page. */ + [data-slot='pagination'] { + display: flex; + justify-content: center; + width: 100%; + } + [data-slot='pagination-content'] { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--spacing); + margin: 0; + padding: 0; + list-style: none; + } + [data-slot='pagination-link'] { + min-width: calc(var(--spacing) * 9); + } + [data-slot='pagination-ellipsis'] { + display: inline-flex; + align-items: center; + justify-content: center; + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + color: var(--muted-foreground); + } +{% endstylesheet %} diff --git a/liquid/components/popover.liquid b/liquid/components/popover.liquid new file mode 100644 index 000000000..11ba6e972 --- /dev/null +++ b/liquid/components/popover.liquid @@ -0,0 +1,3 @@ +{% doc %} + popover uses its shared popup or trigger styles and native data-slot controller. +{% enddoc %} diff --git a/liquid/components/popup.liquid b/liquid/components/popup.liquid new file mode 100644 index 000000000..b00f9aa4b --- /dev/null +++ b/liquid/components/popup.liquid @@ -0,0 +1,189 @@ +{% doc %} + Fulldev UI popup styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Shared surface of the anchored popups, as Vega's popover, select, dropdown menu and hover card + content: the popover colors, a hairline ring, a medium shadow and a 100ms fade and zoom. */ + :is( + [data-slot='select-positioner'], + [data-slot='popover-positioner'], + [data-slot='hover-card-positioner'], + [data-slot='dropdown-menu-positioner'], + [data-slot='tooltip-positioner'] + ) { + z-index: 1000; + isolation: isolate; + } + [data-slot='select-content'], + [data-slot='popover-content'], + [data-slot='hover-card-content'], + [data-slot='dropdown-menu-content'] { + border-radius: var(--radius-md); + background: var(--popover); + color: var(--popover-foreground); + font-size: 0.875rem; + box-shadow: + 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent), + var(--shadow-md); + outline: none; + transform-origin: var(--transform-origin, center); + transition: + opacity 100ms, + scale 100ms; + } + :is( + [data-slot='select-content'], + [data-slot='popover-content'], + [data-slot='hover-card-content'], + [data-slot='dropdown-menu-content'], + [data-slot='tooltip-content'] + ):is([data-starting-style], [data-ending-style]) { + opacity: 0; + scale: 0.95; + } + :is([data-slot='hover-card-content'], [data-slot='tooltip-content'])[data-instant] { + transition: none; + } + [data-slot='select-content'] { + min-width: 9rem; + max-height: var(--available-height, 24rem); + overflow: hidden auto; + } + [data-slot='select-viewport'] { + padding: var(--spacing); + } + [data-slot='select-label'], + [data-slot='dropdown-menu-label'] { + padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2); + color: var(--muted-foreground); + font-size: 0.75rem; + } + [data-slot='dropdown-menu-label'] { + font-weight: 500; + } + [data-slot='select-item'], + [data-slot='dropdown-menu-item'], + [data-slot='dropdown-menu-checkbox-item'], + [data-slot='dropdown-menu-radio-item'] { + position: relative; + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + width: 100%; + padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2); + border-radius: var(--radius-sm); + font-size: 0.875rem; + text-align: left; + text-decoration: none; + cursor: default; + user-select: none; + outline: none; + } + [data-slot='select-item'], + [data-slot='dropdown-menu-checkbox-item'], + [data-slot='dropdown-menu-radio-item'] { + padding-right: calc(var(--spacing) * 8); + } + :is( + [data-slot='select-item'], + [data-slot='dropdown-menu-item'], + [data-slot='dropdown-menu-checkbox-item'], + [data-slot='dropdown-menu-radio-item'] + )[data-highlighted] { + background: var(--accent); + color: var(--accent-foreground); + } + :is( + [data-slot='select-item'], + [data-slot='dropdown-menu-item'], + [data-slot='dropdown-menu-checkbox-item'], + [data-slot='dropdown-menu-radio-item'] + )[data-disabled] { + opacity: 0.5; + pointer-events: none; + } + :is( + [data-slot='select-item'], + [data-slot='dropdown-menu-item'], + [data-slot='dropdown-menu-checkbox-item'], + [data-slot='dropdown-menu-radio-item'] + ) + > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='dropdown-menu-item'][data-variant='destructive'] { + color: var(--destructive); + } + [data-slot='dropdown-menu-item'][data-variant='destructive'][data-highlighted] { + background: light-dark( + color-mix(in oklch, var(--destructive) 10%, transparent), + color-mix(in oklch, var(--destructive) 20%, transparent) + ); + color: var(--destructive); + } + /* The check of a selected option sits at the end of its row. */ + [data-slot='select-item-indicator'], + [data-slot='dropdown-menu-item-indicator'] { + position: absolute; + right: calc(var(--spacing) * 2); + display: flex; + align-items: center; + justify-content: center; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + visibility: hidden; + pointer-events: none; + } + [data-slot='select-item-indicator'] > .icon, + [data-slot='dropdown-menu-item-indicator'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-selected] > [data-slot='select-item-indicator'], + [data-checked] > [data-slot='dropdown-menu-item-indicator'] { + visibility: visible; + } + [data-slot='select-separator'], + [data-slot='dropdown-menu-separator'] { + height: 1px; + margin: var(--spacing) calc(var(--spacing) * -1); + background: var(--border); + } + [data-slot='dropdown-menu-content'] { + min-width: 8rem; + padding: var(--spacing); + } + [data-slot='dropdown-menu-shortcut'] { + margin-left: auto; + color: var(--muted-foreground); + font-size: 0.75rem; + letter-spacing: 0.1em; + } + [data-slot='popover-content'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 4); + width: min(18rem, var(--available-width, 18rem)); + padding: calc(var(--spacing) * 4); + } + [data-slot='popover-header'] { + display: flex; + flex-direction: column; + gap: var(--spacing); + } + [data-slot='popover-title'] { + font-size: 0.875rem; + font-weight: 500; + letter-spacing: var(--tracking-normal); + } + [data-slot='popover-description'] { + color: var(--muted-foreground); + } + [data-slot='hover-card-content'] { + width: min(16rem, var(--available-width, 16rem)); + padding: calc(var(--spacing) * 4); + border-radius: var(--radius-lg); + } +{% endstylesheet %} diff --git a/liquid/components/price.liquid b/liquid/components/price.liquid new file mode 100644 index 000000000..915cd1e09 --- /dev/null +++ b/liquid/components/price.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI price styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-price.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/radio-group.liquid b/liquid/components/radio-group.liquid new file mode 100644 index 000000000..8d3ba4d5c --- /dev/null +++ b/liquid/components/radio-group.liquid @@ -0,0 +1,53 @@ +{% doc %} + Fulldev UI radio-group styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* The items are spans; the controller adds a hidden native radio for each, with the group's name. */ + [data-slot='radio-group'] { + display: grid; + gap: calc(var(--spacing) * 3); + } + [data-slot='radio-group-item'] { + position: relative; + display: flex; + flex-shrink: 0; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + border: 1px solid var(--input); + border-radius: 50%; + background: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent)); + box-shadow: var(--shadow-xs); + outline: none; + transition: box-shadow 150ms; + } + [data-slot='radio-group-item'][data-checked] { + border-color: var(--primary); + background: var(--primary); + } + [data-slot='radio-group-item']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='radio-group-item'][data-disabled] { + cursor: not-allowed; + opacity: 0.5; + } + [data-slot='radio-group-indicator'] { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + opacity: 0; + } + [data-slot='radio-group-indicator']::after { + content: ''; + width: calc(var(--spacing) * 2); + height: calc(var(--spacing) * 2); + border-radius: 50%; + background: var(--primary-foreground); + } + [data-slot='radio-group-indicator'][data-checked] { + opacity: 1; + } +{% endstylesheet %} diff --git a/liquid/components/rating.liquid b/liquid/components/rating.liquid new file mode 100644 index 000000000..cac8cf35b --- /dev/null +++ b/liquid/components/rating.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI rating styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-rating.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/section.liquid b/liquid/components/section.liquid new file mode 100644 index 000000000..e968b1d17 --- /dev/null +++ b/liquid/components/section.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI section styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-section.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/select.liquid b/liquid/components/select.liquid new file mode 100644 index 000000000..91dcb077a --- /dev/null +++ b/liquid/components/select.liquid @@ -0,0 +1,51 @@ +{% doc %} + Fulldev UI select styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* The data-slot select: a trigger like the native select and a listbox popup, as Vega's select. */ + [data-slot='select-trigger'] { + display: flex; + align-items: center; + justify-content: space-between; + gap: calc(var(--spacing) * 1.5); + width: 100%; + height: calc(var(--spacing) * 9); + padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2) calc(var(--spacing) * 2) calc(var(--spacing) * 2.5); + border: 1px solid var(--input); + border-radius: var(--radius-md); + background: light-dark(transparent, color-mix(in oklch, var(--input) 30%, transparent)); + font-size: 0.875rem; + white-space: nowrap; + box-shadow: var(--shadow-xs); + outline: none; + transition: + color 150ms, + box-shadow 150ms; + } + [data-slot='select-trigger'][data-placeholder] { + color: var(--muted-foreground); + } + [data-slot='select-trigger']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='select-trigger']:disabled { + cursor: not-allowed; + opacity: 0.5; + } + [data-slot='select-trigger'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + color: var(--muted-foreground); + } + [data-slot='select'][data-size='sm'] [data-slot='select-trigger'] { + height: calc(var(--spacing) * 8); + } + [data-slot='select-value'] { + flex: 1; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; + } +{% endstylesheet %} diff --git a/liquid/components/separator.liquid b/liquid/components/separator.liquid new file mode 100644 index 000000000..620187557 --- /dev/null +++ b/liquid/components/separator.liquid @@ -0,0 +1,19 @@ +{% doc %} + Fulldev UI separator styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='separator'] { + flex-shrink: 0; + width: 100%; + height: 1px; + margin: 0; + border: 0; + background: var(--border); + } + [data-slot='separator'][data-orientation='vertical'] { + width: 1px; + height: auto; + align-self: stretch; + } +{% endstylesheet %} diff --git a/liquid/components/sheet.liquid b/liquid/components/sheet.liquid new file mode 100644 index 000000000..7a181bad1 --- /dev/null +++ b/liquid/components/sheet.liquid @@ -0,0 +1,68 @@ +{% doc %} + Fulldev UI sheet styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Dialog content with data-side='right' is a sheet, as shadcn's SheetContent. */ + [data-slot='dialog-content'][data-side='right'] { + position: fixed; + inset: 0 0 0 auto; + z-index: calc(var(--dialog-content-stack-index, 0) * 2 + 101); + display: flex; + flex-direction: column; + gap: 0; + width: 75%; + max-width: 24rem; + height: 100%; + max-height: none; + overflow-y: auto; + padding: 0; + border-left: 1px solid var(--border); + border-radius: 0; + background: var(--popover); + color: var(--popover-foreground); + font-size: 0.875rem; + box-shadow: var(--shadow-lg); + opacity: 1; + transform: translateX(0); + transition: + opacity 200ms ease-in-out, + transform 200ms ease-in-out; + } + [data-slot='dialog-content'][data-side='right'][data-starting-style], + [data-slot='dialog-content'][data-side='right'][data-ending-style] { + opacity: 0; + transform: translateX(2.5rem); + } + [data-slot='sheet-header'] { + position: sticky; + top: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: calc(var(--spacing) * 3); + padding: calc(var(--spacing) * 4); + background: var(--popover); + } + [data-slot='sheet-header'] [data-slot='dialog-title'] { + font-size: 1rem; + font-weight: 500; + letter-spacing: var(--tracking-normal); + line-height: 1.5; + } + [data-slot='sheet-header'] [data-slot='dialog-close'] { + color: var(--muted-foreground); + } + [data-slot='sheet-footer'] { + position: sticky; + bottom: 0; + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + margin-top: auto; + padding: calc(var(--spacing) * 4); + border-top: 1px solid var(--border); + background: var(--popover); + } +{% endstylesheet %} diff --git a/liquid/components/skeleton.liquid b/liquid/components/skeleton.liquid new file mode 100644 index 000000000..f88501d94 --- /dev/null +++ b/liquid/components/skeleton.liquid @@ -0,0 +1,17 @@ +{% doc %} + Fulldev UI skeleton styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A placeholder while content loads, as Fulldev UI's skeleton. */ + [data-slot='skeleton'] { + border-radius: var(--radius-md); + background: var(--muted); + animation: skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + } + @keyframes skeleton-pulse { + 50% { + opacity: 0.5; + } + } +{% endstylesheet %} diff --git a/liquid/components/slider.liquid b/liquid/components/slider.liquid new file mode 100644 index 000000000..437c50fbb --- /dev/null +++ b/liquid/components/slider.liquid @@ -0,0 +1,50 @@ +{% doc %} + Fulldev UI slider styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* The controller positions the range and thumbs; these rules only draw them. */ + [data-slot='slider'] { + width: 100%; + } + [data-slot='slider-control'] { + position: relative; + display: flex; + align-items: center; + width: 100%; + min-height: calc(var(--spacing) * 4); + touch-action: none; + user-select: none; + } + [data-slot='slider-control'][data-disabled] { + opacity: 0.5; + } + [data-slot='slider-track'] { + position: relative; + flex-grow: 1; + height: 6px; + overflow: hidden; + border-radius: 9999px; + background: var(--muted); + } + [data-slot='slider-range'] { + height: 100%; + background: var(--primary); + } + [data-slot='slider-thumb'] { + display: block; + flex-shrink: 0; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + border: 1px solid var(--primary); + border-radius: 50%; + background: var(--background); + box-shadow: var(--shadow-sm); + outline: none; + transition: box-shadow 150ms; + } + [data-slot='slider-thumb']:hover, + [data-slot='slider-thumb']:focus-visible { + box-shadow: 0 0 0 4px color-mix(in oklch, var(--ring) 50%, transparent); + } +{% endstylesheet %} diff --git a/liquid/components/spinner.liquid b/liquid/components/spinner.liquid new file mode 100644 index 000000000..39c6e9178 --- /dev/null +++ b/liquid/components/spinner.liquid @@ -0,0 +1,28 @@ +{% doc %} + Fulldev UI spinner styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Fulldev UI's spinner: a turning loader icon in a role=status element with a translated label. */ + [data-slot='spinner'] { + display: inline-flex; + flex-shrink: 0; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + animation: spinner-turn 1s linear infinite; + } + [data-slot='spinner'] > .icon { + width: 100%; + height: 100%; + } + @keyframes spinner-turn { + to { + rotate: 360deg; + } + } + @media (prefers-reduced-motion: reduce) { + [data-slot='spinner'] { + animation-duration: 3s; + } + } +{% endstylesheet %} diff --git a/liquid/components/switch.liquid b/liquid/components/switch.liquid new file mode 100644 index 000000000..734020635 --- /dev/null +++ b/liquid/components/switch.liquid @@ -0,0 +1,54 @@ +{% doc %} + Fulldev UI switch styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + [data-slot='switch'] { + display: inline-flex; + flex-shrink: 0; + align-items: center; + width: 32px; + height: 18.4px; + border: 1px solid transparent; + border-radius: 9999px; + background: light-dark(var(--input), color-mix(in oklch, var(--input) 80%, transparent)); + box-shadow: var(--shadow-xs); + cursor: pointer; + outline: none; + transition: + background-color 150ms, + box-shadow 150ms; + } + [data-slot='switch'][data-checked] { + background: var(--primary); + } + [data-slot='switch']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='switch'][data-disabled] { + cursor: not-allowed; + opacity: 0.5; + } + [data-slot='switch-thumb'] { + display: block; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + border-radius: 50%; + background: light-dark(var(--background), var(--foreground)); + pointer-events: none; + transition: translate 150ms; + } + [data-slot='switch-thumb'][data-checked] { + background: light-dark(var(--background), var(--primary-foreground)); + translate: calc(100% - 2px) 0; + } + [data-slot='switch'][data-size='sm'] { + width: 24px; + height: 14px; + } + [data-slot='switch'][data-size='sm'] [data-slot='switch-thumb'] { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } +{% endstylesheet %} diff --git a/liquid/components/table.liquid b/liquid/components/table.liquid new file mode 100644 index 000000000..797b93b4f --- /dev/null +++ b/liquid/components/table.liquid @@ -0,0 +1,64 @@ +{% doc %} + Fulldev UI table styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Vega's table: rows between rules, a muted footer and the caption below. */ + [data-slot='table-container'] { + position: relative; + width: 100%; + overflow-x: auto; + } + [data-slot='table'] { + width: 100%; + border-collapse: collapse; + caption-side: bottom; + font-size: 0.875rem; + } + [data-slot='table-header'] [data-slot='table-row'] { + border-bottom: 1px solid var(--border); + } + [data-slot='table-row'] { + border-bottom: 1px solid var(--border); + transition: background-color 150ms; + } + [data-slot='table-body'] [data-slot='table-row']:last-child { + border-bottom: 0; + } + [data-slot='table-body'] [data-slot='table-row']:hover { + background: color-mix(in oklch, var(--muted) 50%, transparent); + } + [data-slot='table-row'][data-state='selected'] { + background: var(--muted); + } + [data-slot='table-footer'] { + border-top: 1px solid var(--border); + background: color-mix(in oklch, var(--muted) 50%, transparent); + font-weight: 500; + } + [data-slot='table-footer'] [data-slot='table-row']:last-child { + border-bottom: 0; + } + [data-slot='table-head'] { + height: calc(var(--spacing) * 10); + padding-inline: calc(var(--spacing) * 2); + color: var(--foreground); + font-weight: 500; + text-align: left; + vertical-align: middle; + white-space: nowrap; + } + [data-slot='table-cell'] { + padding: calc(var(--spacing) * 2); + vertical-align: middle; + white-space: nowrap; + } + :is([data-slot='table-head'], [data-slot='table-cell']):has([data-slot='checkbox']) { + padding-right: 0; + } + [data-slot='table-caption'] { + margin-top: calc(var(--spacing) * 4); + color: var(--muted-foreground); + font-size: 0.875rem; + } +{% endstylesheet %} diff --git a/liquid/components/tabs.liquid b/liquid/components/tabs.liquid new file mode 100644 index 000000000..38135817b --- /dev/null +++ b/liquid/components/tabs.liquid @@ -0,0 +1,88 @@ +{% doc %} + Fulldev UI tabs styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Vega's tabs. The controller sets data-state='active' on the selected trigger and its content. */ + [data-slot='tabs'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 2); + } + [data-slot='tabs-list'] { + display: inline-flex; + align-items: center; + justify-content: center; + width: fit-content; + height: calc(var(--spacing) * 9); + padding: 3px; + border-radius: var(--radius-lg); + background: var(--muted); + color: var(--muted-foreground); + } + [data-slot='tabs-trigger'] { + position: relative; + display: inline-flex; + flex: 1; + align-items: center; + justify-content: center; + gap: calc(var(--spacing) * 1.5); + height: calc(100% - 1px); + padding: var(--spacing) calc(var(--spacing) * 2); + border: 1px solid transparent; + border-radius: var(--radius-md); + color: color-mix(in oklch, var(--foreground) 60%, transparent); + font-size: 0.875rem; + font-weight: 500; + white-space: nowrap; + outline: none; + transition: + color 150ms, + background-color 150ms, + box-shadow 150ms; + } + [data-slot='tabs-trigger']:hover { + color: var(--foreground); + } + [data-slot='tabs-trigger']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='tabs-trigger'][data-state='active'] { + border-color: light-dark(transparent, var(--input)); + background: light-dark(var(--background), color-mix(in oklch, var(--input) 30%, transparent)); + color: var(--foreground); + box-shadow: var(--shadow-sm); + } + [data-slot='tabs-trigger']:disabled { + opacity: 0.5; + pointer-events: none; + } + [data-slot='tabs-trigger'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='tabs-list'][data-variant='line'] { + gap: var(--spacing); + border-radius: 0; + background: transparent; + } + [data-slot='tabs-list'][data-variant='line'] [data-slot='tabs-trigger'][data-state='active'] { + border-color: transparent; + background: transparent; + box-shadow: none; + } + [data-slot='tabs-list'][data-variant='line'] [data-slot='tabs-trigger'][data-state='active']::after { + content: ''; + position: absolute; + inset-inline: 0; + bottom: -5px; + height: 2px; + background: var(--foreground); + } + [data-slot='tabs-content'] { + flex: 1; + font-size: 0.875rem; + outline: none; + } +{% endstylesheet %} diff --git a/liquid/components/toast.liquid b/liquid/components/toast.liquid new file mode 100644 index 000000000..ac51deb19 --- /dev/null +++ b/liquid/components/toast.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI toast styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-toast.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/toc.liquid b/liquid/components/toc.liquid new file mode 100644 index 000000000..087fdce77 --- /dev/null +++ b/liquid/components/toc.liquid @@ -0,0 +1,45 @@ +{% doc %} + Fulldev UI toc styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Fulldev UI's toc: links to the headings of a page, the current one in the foreground. */ + [data-slot='toc'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 3); + } + [data-slot='toc-title'] { + font-size: 0.875rem; + font-weight: 500; + letter-spacing: var(--tracking-tight); + } + [data-slot='toc-menu'] { + display: flex; + flex-direction: column; + gap: var(--spacing); + margin: 0; + padding: 0; + list-style: none; + } + [data-slot='toc-menu-item'] { + position: relative; + } + [data-slot='toc-menu-link'] { + display: block; + color: var(--muted-foreground); + font-size: 0.875rem; + line-height: 1.5rem; + transition: color 150ms; + } + [data-slot='toc-menu-link']:hover, + [data-slot='toc-menu-link'][aria-current] { + color: var(--foreground); + } + [data-slot='toc-menu-link'][data-depth='3'] { + padding-inline-start: calc(var(--spacing) * 4); + } + [data-slot='toc-menu-link'][data-depth='4'] { + padding-inline-start: calc(var(--spacing) * 8); + } +{% endstylesheet %} diff --git a/liquid/components/toggle-group.liquid b/liquid/components/toggle-group.liquid new file mode 100644 index 000000000..cf56a56fe --- /dev/null +++ b/liquid/components/toggle-group.liquid @@ -0,0 +1,77 @@ +{% doc %} + Fulldev UI toggle-group styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Joined toggles, as Vega's toggle group without spacing. The group sets the variant and size. */ + [data-slot='toggle-group'] { + display: flex; + align-items: center; + width: fit-content; + border-radius: var(--radius-md); + } + [data-slot='toggle-group'][data-variant='outline'] { + box-shadow: var(--shadow-xs); + } + [data-slot='toggle-group-item'] { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing); + min-width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + padding-inline: calc(var(--spacing) * 2); + border: 1px solid transparent; + border-radius: 0; + background: transparent; + font-size: 0.875rem; + font-weight: 500; + white-space: nowrap; + outline: none; + transition: + background-color 150ms, + color 150ms, + box-shadow 150ms; + } + [data-slot='toggle-group-item']:first-child { + border-top-left-radius: var(--radius-md); + border-bottom-left-radius: var(--radius-md); + } + [data-slot='toggle-group-item']:last-child { + border-top-right-radius: var(--radius-md); + border-bottom-right-radius: var(--radius-md); + } + [data-slot='toggle-group-item']:hover, + [data-slot='toggle-group-item'][data-state='on'] { + background: var(--muted); + color: var(--foreground); + } + [data-slot='toggle-group-item']:focus-visible { + position: relative; + z-index: 1; + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='toggle-group-item']:disabled { + opacity: 0.5; + pointer-events: none; + } + [data-slot='toggle-group-item'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='toggle-group'][data-variant='outline'] [data-slot='toggle-group-item'] { + border-color: var(--input); + } + [data-slot='toggle-group'][data-variant='outline'] [data-slot='toggle-group-item'] + [data-slot='toggle-group-item'] { + border-left-width: 0; + } + [data-slot='toggle-group'][data-size='sm'] [data-slot='toggle-group-item'] { + min-width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + } + [data-slot='toggle-group'][data-size='lg'] [data-slot='toggle-group-item'] { + min-width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + } +{% endstylesheet %} diff --git a/liquid/components/toggle.liquid b/liquid/components/toggle.liquid new file mode 100644 index 000000000..98a459a2e --- /dev/null +++ b/liquid/components/toggle.liquid @@ -0,0 +1,61 @@ +{% doc %} + Fulldev UI toggle styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Option values and filter values: links or labels with the toggle look. The selected value + also takes a primary border, so it stands out beyond the fill. */ + [data-slot='toggle'] { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing); + min-width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + padding-inline: calc(var(--spacing) * 2.5); + border: 1px solid transparent; + border-radius: var(--radius-md); + background: transparent; + font-size: 0.875rem; + font-weight: 500; + white-space: nowrap; + text-decoration: none; + outline: none; + transition: + background-color 150ms, + border-color 150ms, + color 150ms, + box-shadow 150ms; + } + [data-slot='toggle'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + [data-slot='toggle']:hover { + background: var(--muted); + color: var(--foreground); + } + [data-slot='toggle']:focus-visible, + [data-slot='toggle']:has(:focus-visible) { + border-color: var(--ring); + box-shadow: var(--focus-ring); + } + [data-slot='toggle']:is([aria-current], [aria-pressed='true'], :has(:checked)) { + background: var(--muted); + } + [data-slot='toggle'][data-variant='outline'] { + border-color: var(--input); + box-shadow: var(--shadow-xs); + } + [data-slot='toggle'][data-variant='outline']:is([aria-current], [aria-pressed='true'], :has(:checked)) { + border-color: var(--primary); + } + [data-slot='toggle'][data-size='sm'] { + min-width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + } + [data-slot='toggle'][data-size='lg'] { + min-width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + } +{% endstylesheet %} diff --git a/liquid/components/tooltip.liquid b/liquid/components/tooltip.liquid new file mode 100644 index 000000000..a4178af0d --- /dev/null +++ b/liquid/components/tooltip.liquid @@ -0,0 +1,45 @@ +{% doc %} + Fulldev UI tooltip styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* Inverted colors: the scheme's foreground as the fill, as Fulldev UI's tooltip. */ + [data-slot='tooltip-content'] { + position: relative; + display: inline-flex; + align-items: center; + gap: calc(var(--spacing) * 1.5); + width: fit-content; + max-width: 20rem; + padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 3); + border-radius: var(--radius-md); + background: var(--foreground); + color: var(--background); + font-size: 0.75rem; + line-height: 1.33; + transform-origin: var(--transform-origin, center); + transition: + opacity 100ms, + scale 100ms; + } + [data-slot='tooltip-arrow'] { + position: absolute; + width: 10px; + height: 10px; + border-radius: 2px; + background: var(--foreground); + rotate: 45deg; + } + [data-slot='tooltip-arrow'][data-side='bottom'] { + top: -4px; + } + [data-slot='tooltip-arrow'][data-side='top'] { + bottom: -4px; + } + [data-slot='tooltip-arrow'][data-side='left'] { + right: -4px; + } + [data-slot='tooltip-arrow'][data-side='right'] { + left: -4px; + } +{% endstylesheet %} diff --git a/liquid/components/triggers.liquid b/liquid/components/triggers.liquid new file mode 100644 index 000000000..e7af8f577 --- /dev/null +++ b/liquid/components/triggers.liquid @@ -0,0 +1,20 @@ +{% doc %} + Fulldev UI triggers styles. Use native markup with its data-slot parts. +{% enddoc %} + +{% stylesheet %} + /* A control that only works with its controller, such as a popup trigger, a toggle or a tab list, + shows once the controller has started, which theme.js marks with data-enhanced. Without it, the + page keeps the content available: collapsible and tab panels stay open. */ + [data-slot='popover']:not([data-enhanced]) > [data-slot='popover-trigger'], + [data-slot='dropdown-menu']:not([data-enhanced]) > [data-slot='dropdown-menu-trigger'], + [data-slot='alert-dialog']:not([data-enhanced]) > [data-slot='alert-dialog-trigger'], + [data-slot='drawer']:not([data-enhanced]) > [data-slot='drawer-trigger'], + [data-slot='toast']:not([data-enhanced]) [data-toast-title], + [data-slot='collapsible']:not([data-enhanced]) > [data-slot='collapsible-trigger'], + [data-slot='tabs']:not([data-enhanced]) > [data-slot='tabs-list'], + [data-slot='toggle-group']:not([data-enhanced]), + button[data-slot='toggle']:not([data-enhanced]) { + display: none; + } +{% endstylesheet %} diff --git a/liquid/components/typography.liquid b/liquid/components/typography.liquid new file mode 100644 index 000000000..5cee43205 --- /dev/null +++ b/liquid/components/typography.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI typography styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-typography.css' | asset_url | stylesheet_tag }} diff --git a/liquid/components/video.liquid b/liquid/components/video.liquid new file mode 100644 index 000000000..96a52059a --- /dev/null +++ b/liquid/components/video.liquid @@ -0,0 +1,5 @@ +{% doc %} + Fulldev UI video styles. Use native markup with its data-slot parts. +{% enddoc %} + +{{ 'ui-video.css' | asset_url | stylesheet_tag }} diff --git a/liquid/contracts.json b/liquid/contracts.json new file mode 100644 index 000000000..11d4bea52 --- /dev/null +++ b/liquid/contracts.json @@ -0,0 +1,428 @@ +{ + "runtime": [ + "${name}", + "${n}", + "${r}", + "${t}", + "accordion", + "accordion-content", + "accordion-item", + "accordion-trigger", + "alert-dialog-action", + "alert-dialog-cancel", + "carousel", + "carousel-content", + "carousel-indicator", + "carousel-item", + "carousel-next", + "carousel-previous", + "combobox-empty", + "combobox-group", + "combobox-item", + "combobox-item-indicator", + "combobox-label", + "combobox-list", + "combobox-separator", + "command", + "command-empty", + "command-group", + "command-item", + "command-separator", + "dialog-close", + "drawer", + "drawer-${t}", + "drawer-indent", + "drawer-indent-background", + "drawer-popup", + "drawer-provider", + "drawer-virtual-keyboard-provider", + "dropdown-menu-checkbox-item", + "dropdown-menu-item", + "dropdown-menu-radio-item", + "navigation-menu-content", + "navigation-menu-indicator", + "navigation-menu-item", + "navigation-menu-trigger", + "radio-group-indicator", + "select-group", + "select-item", + "select-item-text", + "select-label", + "select-trigger", + "select-viewport", + "tabs-trigger", + "toast-action", + "toast-close", + "toast-description", + "toast-item", + "toast-title", + "toggle", + "toggle-group-item", + "tooltip-arrow" + ], + "banner": [ + "banner", + "banner-container" + ], + "tabs": [ + "tabs", + "tabs-content", + "tabs-list", + "tabs-trigger" + ], + "form": [ + "form" + ], + "select": [ + "select", + "select-trigger", + "select-value" + ], + "field": [ + "field", + "field-description", + "field-error", + "field-group", + "field-label", + "field-legend", + "field-set", + "label" + ], + "item": [ + "item", + "item-actions", + "item-content", + "item-description", + "item-footer", + "item-group", + "item-header", + "item-media", + "item-title" + ], + "spinner": [ + "spinner" + ], + "video": [ + "video" + ], + "button": [ + "button" + ], + "toggle": [ + "toggle" + ], + "logo": [ + "logo", + "logo-image", + "logo-text" + ], + "kbd": [ + "kbd" + ], + "rating": [ + "rating" + ], + "tooltip": [ + "tooltip-arrow", + "tooltip-content" + ], + "avatar": [ + "avatar", + "avatar-fallback", + "avatar-group", + "avatar-image" + ], + "empty": [ + "empty", + "empty-content", + "empty-description", + "empty-header", + "empty-icon", + "empty-title" + ], + "alert-dialog": [ + "alert-dialog-content", + "alert-dialog-description", + "alert-dialog-footer", + "alert-dialog-header", + "alert-dialog-media", + "alert-dialog-overlay", + "alert-dialog-title" + ], + "navigation-menu": [ + "navigation-menu-content", + "navigation-menu-link", + "navigation-menu-list", + "navigation-menu-popup", + "navigation-menu-positioner", + "navigation-menu-trigger", + "navigation-menu-viewport" + ], + "typography": [ + "typography", + "typography-a", + "typography-blockquote", + "typography-h1", + "typography-h2", + "typography-h3", + "typography-h4", + "typography-inline-code", + "typography-large", + "typography-lead", + "typography-list", + "typography-muted", + "typography-p", + "typography-small", + "typography-table", + "typography-table-cell", + "typography-table-container", + "typography-table-head", + "typography-table-row" + ], + "toc": [ + "toc", + "toc-menu", + "toc-menu-item", + "toc-menu-link", + "toc-title" + ], + "icon": [ + "icon" + ], + "combobox": [ + "combobox-content", + "combobox-empty", + "combobox-item", + "combobox-item-indicator", + "combobox-list", + "combobox-positioner" + ], + "toast": [ + "drawer-backdrop", + "drawer-popup", + "toast-action", + "toast-close", + "toast-content", + "toast-description", + "toast-item", + "toast-title", + "toast-viewport" + ], + "header": [ + "header", + "header-group" + ], + "command": [ + "command", + "command-group", + "command-group-heading", + "command-input", + "command-input-wrapper", + "command-item", + "command-list" + ], + "accordion": [ + "accordion", + "accordion-content", + "accordion-content-inner", + "accordion-item", + "accordion-trigger" + ], + "slider": [ + "slider", + "slider-control", + "slider-range", + "slider-thumb", + "slider-track" + ], + "input": [ + "input", + "native-select", + "textarea" + ], + "input-group": [ + "input-group", + "input-group-addon" + ], + "triggers": [ + "alert-dialog", + "alert-dialog-trigger", + "collapsible", + "collapsible-trigger", + "drawer", + "drawer-trigger", + "dropdown-menu", + "dropdown-menu-trigger", + "popover", + "popover-trigger", + "tabs", + "tabs-list", + "toast", + "toggle", + "toggle-group" + ], + "toggle-group": [ + "toggle-group", + "toggle-group-item" + ], + "dialog": [ + "dialog-close", + "dialog-content", + "dialog-description", + "dialog-header", + "dialog-overlay", + "dialog-title" + ], + "drawer": [ + "drawer-backdrop", + "drawer-content", + "drawer-description", + "drawer-footer", + "drawer-handle", + "drawer-header", + "drawer-popup", + "drawer-title" + ], + "badge": [ + "badge" + ], + "alert": [ + "alert", + "alert-description", + "alert-title" + ], + "table": [ + "checkbox", + "table", + "table-body", + "table-caption", + "table-cell", + "table-container", + "table-footer", + "table-head", + "table-header", + "table-row" + ], + "breadcrumb": [ + "breadcrumb", + "breadcrumb-item", + "breadcrumb-link", + "breadcrumb-list", + "breadcrumb-page", + "breadcrumb-separator" + ], + "aspect-ratio": [ + "aspect-ratio" + ], + "price": [ + "price", + "price-unit", + "price-value" + ], + "radio-group": [ + "radio-group", + "radio-group-indicator", + "radio-group-item" + ], + "checkbox": [ + "checkbox" + ], + "switch": [ + "switch", + "switch-thumb" + ], + "skeleton": [ + "skeleton" + ], + "separator": [ + "separator" + ], + "card": [ + "aspect-ratio", + "card", + "card-action", + "card-content", + "card-description", + "card-footer", + "card-header", + "card-title" + ], + "pagination": [ + "pagination", + "pagination-content", + "pagination-ellipsis", + "pagination-link" + ], + "popup": [ + "dropdown-menu-checkbox-item", + "dropdown-menu-content", + "dropdown-menu-item", + "dropdown-menu-item-indicator", + "dropdown-menu-label", + "dropdown-menu-positioner", + "dropdown-menu-radio-item", + "dropdown-menu-separator", + "dropdown-menu-shortcut", + "hover-card-content", + "hover-card-positioner", + "popover-content", + "popover-description", + "popover-header", + "popover-positioner", + "popover-title", + "select-content", + "select-item", + "select-item-indicator", + "select-label", + "select-positioner", + "select-separator", + "select-viewport", + "tooltip-content", + "tooltip-positioner" + ], + "sheet": [ + "dialog-close", + "dialog-content", + "dialog-title", + "sheet-footer", + "sheet-header" + ], + "carousel": [ + "carousel", + "carousel-content", + "carousel-item" + ], + "attachment": [ + "attachment", + "attachment-actions", + "attachment-content", + "attachment-description", + "attachment-group", + "attachment-media", + "attachment-title", + "attachment-trigger" + ], + "marquee": [ + "marquee", + "marquee-content", + "marquee-item", + "marquee-toggle" + ], + "invalid-controls": [ + "radio-group-item", + "select-trigger", + "switch" + ], + "section": [ + "header-container", + "section", + "section-container", + "section-description", + "section-title" + ], + "button-group": [ + "button-group", + "button-group-separator", + "button-group-text" + ] +} diff --git a/liquid/licenses.json b/liquid/licenses.json new file mode 100644 index 000000000..0b6d6d3c7 --- /dev/null +++ b/liquid/licenses.json @@ -0,0 +1,14 @@ +[ + { + "name": "Fulldev UI", + "text": "MIT License\n\nCopyright (c) 2024 Fulldev, Sil Veltman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + { + "name": "shadcn/ui (derived Vega styles)", + "text": "MIT License\n\nCopyright (c) 2023 shadcn\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + { + "name": "data-slot 1.1.1", + "text": "MIT License\n\nCopyright (c) 2026 Bejamas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + } +] diff --git a/liquid/manifest.json b/liquid/manifest.json new file mode 100644 index 000000000..7d609d4a9 --- /dev/null +++ b/liquid/manifest.json @@ -0,0 +1,110 @@ +{ + "registry": "https://ui.full.dev/r/liquid/", + "files": { + "assets/ui-tokens.css": "55a8f2810ab4a1498ca3dd2f6e7dc89009c60e2402ddfcb95783b6eba996c374", + "snippets/ui-base.liquid": "1a39ed17214f86fa6fa844cb1d26e7e4a386c2c8a9d4492b2fd03efa940b9955", + "snippets/ui-init.liquid": "98f56067cf51e4cb2cd3e10ede7c00304c73ce57fe5b5a0270c0d8a9e448f7af", + "assets/ui-licenses.json": "6eaca08e24468055e5471a9141a3453381b86ced4bdce85cb874f73b550baa09", + "assets/ui-carousel.js": "95655cf60a4c1cacdad87295acb2126032dc8d59ca117c6d738cf534be648e12", + "assets/ui-controls.js": "cf51f6d8b23c93e30d3355e993ff21a1aee79f38facfa413bed64e9815df255d", + "assets/ui-lifecycle.js": "78c975473620171c01b1289bf5cdce56a5626c847437fa5168fb91a4300260d6", + "assets/ui-start.js": "738d7e171b5d7993bfd0a169e4382b2db08340f52a87059b94d27e1fe3606500", + "assets/data-slot-core.js": "d2879cc3a2f05a7867763b937bad3bd4a369f54670989ce535367cb690fb66e6", + "assets/data-slot-dialog.js": "a5201f452700d04145b5156cc46880d2b5bdf7521b03ff9521c3f7693ee1b5d5", + "assets/data-slot-command.js": "f7944120617676b14dfda2ea3f3e8a15767e0766d5312b4ed9519affe9b7444a", + "assets/data-slot-navigation-menu.js": "f9b42a9bceca8e1103718d43aa5f0d141ab63d38278cb121e790a2c80e827b68", + "assets/data-slot-accordion.js": "fe6b0ac2c87a2e31582e5e88c509e0b508f41d0065d0a420407999ad210e6a27", + "assets/data-slot-tabs.js": "1f187134bba755cfda9d35cf9f16174c1a183862d32d100f270f4177033058c7", + "assets/data-slot-combobox.js": "b5f2ed9d65a43d836c59c0d4dc33c16ab12ef9fa05d510078b5d62517d854db6", + "assets/data-slot-carousel.js": "d83ef938d3b62dc3652b123428ca2261e03fb2ba1133992b0f5545a974238d03", + "assets/data-slot-alert-dialog.js": "1caee9a86bb2a816ea32bc1a48b82f3cc588ec3c563ebc443f836ab1dae2f618", + "assets/data-slot-collapsible.js": "c42f76e3dfeff4570eb2f5104434fbffd3ef55710833e888965c590ce59f4f9f", + "assets/data-slot-drawer.js": "c9343b26ea27193cc1d2da3e41bf452005bc2e534151fa1a762b2b6535de28d0", + "assets/data-slot-dropdown-menu.js": "543c5717c9ba8363bbab4c0a11005980d75d4baaed79cf7123c7bcfe3ec8720b", + "assets/data-slot-hover-card.js": "86e1bfa5d4822f180f3501ca7cc51bf044d4e917a7ebdb7a10b70006dba6467f", + "assets/data-slot-popover.js": "526e30d663381329a7a6394eae886224024034d0abf96f888c932bb4b8c1b1cb", + "assets/data-slot-radio-group.js": "7a9b90166de80d79ee42aa2df73e4d8179e9d291cb30b3095c7d8332375b0499", + "assets/data-slot-select.js": "13d1a3fbe90f4b3b830d987b6a28267897faa3589bf00e64edca4938acbf406b", + "assets/data-slot-slider.js": "0d10131aa95450aa1269234103ead8d805d6abdf9e0683510f4fcf58ab0f1efa", + "assets/data-slot-switch.js": "0d5b09a89429880f61aeb540be5f87383ba5c6beb261ad468e9a6cf18e45b6a0", + "assets/data-slot-toast.js": "acfd86824d0ff37fbd62d0c48672e527d7391412780730e9a3a8d61268b3aaf2", + "assets/data-slot-toggle.js": "7a651d578cb83be842c8e59fe1a2cd124594b1fd6c942faf0a33cf9e57326947", + "assets/data-slot-toggle-group.js": "c1f2fc9712974e63e4e44c97ab2a1ec8bc3c3cb73c1afb4578b085fed11977f1", + "assets/data-slot-tooltip.js": "5a9e51a54669153620eb3512e74487eac12ababe3331aff7dfed6c717b8b93c0", + "snippets/ui-runtime.liquid": "62ae98f4ad16dc0ca406d96bbabb373cd30a63dd385e3614da0dcd2e63d0f17c", + "assets/ui-motion.js": "2252a6794ddb162b73ee1640af212292b8c520e98ae09ea862b479078f81d3d1", + "snippets/ui-banner.liquid": "9965b12e9a2036aca6e35f1327da8a240c894a6fd91d99f37e77107818d271a3", + "snippets/ui-tabs.liquid": "5b57bb3d6400e44ef9343f7b45d9baf2d8581d678ccc64764be60164160687eb", + "snippets/ui-form.liquid": "1c002d21c441e217a8f3d64268d429c97e4785af118eb7f7abb5ff1c3d10a3b6", + "assets/ui-form.css": "ab0dd70553818fe0799e393f6811e949884eca9f44d9ceff4c2b333c0bf724b4", + "snippets/ui-select.liquid": "2d8c99759161a024285d1b54c0ca166dc7b13c97596497351253e945b5e52ee2", + "snippets/ui-field.liquid": "cf73aaaf3e27bc9d121dd089317610fbd08b9550a1d5fb0738337c6817879309", + "assets/ui-field.css": "1436dff789c8811aae13025ca862ac1bfbf64c57e895c87474f45bfcd8b3f665", + "snippets/ui-item.liquid": "fe03c1a3e6df09192c3ac7a08520b0fc1c629a597a029d193ff91655cb7ceac0", + "snippets/ui-spinner.liquid": "84d48693f10cb8af4549cc0d71cd847a85da0e111f2ad64dae309c128c2a8c4a", + "snippets/ui-video.liquid": "14acea7c523198269a9165270decac21a43a5d69bf12bd248a76aceb2e9b26ed", + "assets/ui-video.css": "c1572853f48474f6291a70ccdb1085512700fc3dcbbdaebd71374b838ec67fee", + "snippets/ui-button.liquid": "5dd981b8e5de94d5dac3fd367a3d31c0553a2ea2f72aefda6ee2e07fde876af8", + "snippets/ui-toggle.liquid": "c86100cf7c163c0d5bc81976fd794788f68fab2ebc5c0887d8042958ce020912", + "snippets/ui-logo.liquid": "5d6de7be3f7aab9e05c9533afef39ea0e9dce1b4ee12a13bf7b82780c973b126", + "snippets/ui-kbd.liquid": "024f34ed2932d183671f7fa44ff724a70ec17b766cf7e0d3f565f46f33ab2cf2", + "snippets/ui-rating.liquid": "aa57adc5a5508b368da78023b242694e6ce924b6e9e5407c141a5030a414bf1d", + "assets/ui-rating.css": "e5efea2a9edaea78522f20ebd9e9d01abe8a2fcd310c193239adf80ec33b4975", + "snippets/ui-tooltip.liquid": "d2863a0da3090c73dda10d8615b84fc50fab64373a466fbf4e4e743cfb083c96", + "snippets/ui-avatar.liquid": "9bfb5d5f535857cc45f31afbc91c855e94972bab07ad3b4fe2dc0d241106385d", + "snippets/ui-empty.liquid": "1a6b8a006d67700225a9f646fdb74308904319cc1cd8ea83a637e2d9d5fbc3d9", + "snippets/ui-alert-dialog.liquid": "f739374035ffa0f3bca2c4b6d946710751c8268c8f1dde535251be9c2c7b0cd0", + "snippets/ui-navigation-menu.liquid": "52aecbb36b0031810a85a99f5d4439e852dc1ba3a872b9525dd75c8df8b5a84e", + "assets/ui-navigation-menu.css": "29a47ce0eb549e58cc8195dcb656bd2cef56360178c76acfc2d481a110463ca6", + "snippets/ui-typography.liquid": "b03dc93c4de6636164a206c95979caa6fe9ab7cf8d1c24d75611416259da7979", + "assets/ui-typography.css": "4363ea0774e3c76a0154b1c586a6270a4fbd31b7794fe9549977f41563f54d78", + "snippets/ui-toc.liquid": "04038e0870112b2335b80b4092814915105b31d08cdd36ecf1f4de1761f66acf", + "snippets/ui-icon.liquid": "ca75a9485eb33cdb31a3a4e2e5ca03ce74d4995dbd1696d964832a87602bf3ca", + "assets/ui-icon.css": "cfb1a47905cbd9c25b635c96926a77f8eed983a4aca62fdce758345dcc792add", + "snippets/ui-combobox.liquid": "bacd605b44237512a74d34119d01432440b76ae60218dae6a6f37f98b32e3e00", + "snippets/ui-toast.liquid": "bb032d90d6084fb15e12da22777635b80d09a06402513c46c89e853e3ede0405", + "assets/ui-toast.css": "c322fab210a202fa434695025550495dbdc93e688ed7ae39303fea9830864d0b", + "snippets/ui-header.liquid": "cc14effb4e3b6c4ac397e306e4ebd270a31b3415cc12ffbeb6cd38f411eaba80", + "snippets/ui-command.liquid": "4a37f59dc0039fa1505de764472212d6cd7c4e5c52b737a1700e928db286f233", + "snippets/ui-accordion.liquid": "4b58c83ae13e14e03a3129a2dccbd02eeafa90c1e48edbbf9af2dc412bf3aabf", + "assets/ui-accordion.css": "430924440bd0f1d49ceb72baffc77404c9411c31c24b940d75861daba2649e83", + "snippets/ui-slider.liquid": "f2de1fb9099de77bf291455447bc86b2578781e7cf0efdf12117fb4575e0b0e6", + "snippets/ui-input.liquid": "1f101e309dc15bae127766427ef619d190eb9e341fc34808b26f56ea354312e7", + "snippets/ui-input-group.liquid": "b862a0c63e23dc7ae4ffe3a8e057a87f38fa8e6c8842ddf3c86b826de0283d70", + "snippets/ui-triggers.liquid": "ba9c7b802fb0a2aba0e9cc145a9dd44134964afb53e5363cf9e0ae138b906cc9", + "snippets/ui-toggle-group.liquid": "2d61e0b9bc746e05834157f5a75d23123f02bc2df737412e4f6099844afc4f49", + "snippets/ui-dialog.liquid": "779bbcd609a69ed29a984df1f26f9ed80fd24d71c4ee3ead5f4daa055b28e9fb", + "snippets/ui-drawer.liquid": "16120fdd70fac393470c3936223e78ad5f81164e2b7616fd075e8e82098a17d5", + "snippets/ui-badge.liquid": "13d8b46cd5432783a9b986de76365c9fcc0e7fb30ad4f956ec386c1ae549ce99", + "snippets/ui-alert.liquid": "7530d324ac1fb4645bba0a23780fbeacc67544f8bad3ed9e367debd88c0365b5", + "snippets/ui-table.liquid": "6599d8b3bd193761571c2b71e499f78546ba5c2768d4e0a0a60cd0e348632e5b", + "snippets/ui-breadcrumb.liquid": "dafdeaf03286b386c90f8e72777de1946fba4718ee568bd85ca7bc768483a3ca", + "snippets/ui-aspect-ratio.liquid": "c8aae05fcd76af7ae768aedafd7e758533e2b52135b00ff72940896ad793f177", + "snippets/ui-price.liquid": "96156754aee3a04aa3ae7084a14470000213e8be14468c4937e9f3a87e348234", + "assets/ui-price.css": "95c947e219621102b704164e48a83e381e6e2b27b99311a294e076745b39a36f", + "snippets/ui-radio-group.liquid": "af9978e18716cc86d03ed21dece8304992e74a4fab88e12069b0648cfd944492", + "snippets/ui-checkbox.liquid": "0e4dd1738b355c5a93533e0b813e2e6c72f7491c289d65aa7b6435be1c599006", + "snippets/ui-switch.liquid": "84ed60c777e77daec8b5fe6f2ee434877d51224235dc1bc2e17927cd182fe646", + "snippets/ui-skeleton.liquid": "1e30245f6577f73d28f4badbc293934656882a29d5dec1d2f74ef95f3bcede2c", + "snippets/ui-separator.liquid": "90b1d2795666b6c51f35645ca8d07659f2c4868fe9553e233cddb4e962095f6e", + "snippets/ui-card.liquid": "581a46843f1280ebace0423ee84289b40fbd46142e6a9ab3940f63eca0e4c5f2", + "assets/ui-card.css": "ee7e4192ec814066d0c9838f810ae4ca267b9f071b7fa945c43226d46b0f8dc9", + "snippets/ui-pagination.liquid": "30943c00686e1a23d92865465b38c964e059b14ae3a20fb7fe151824a73773eb", + "snippets/ui-popup.liquid": "840f938aa2a331e4e7d55c659f25e841dcfb1bf5c5cadace6263243352ff8359", + "snippets/ui-sheet.liquid": "0de705bed685bf78fa3af4517c947b38d62a1aa56bf29356c0a201b64db17614", + "snippets/ui-carousel.liquid": "59dc0c06b2a9f2f779ca22b3ff4b005071d4909aba052d6f8f0a1aa19e84c2ce", + "assets/ui-carousel.css": "5317c251b3d3599ba330947fc9709ea50acfb858a5d7e08573b5d34ada2f3bf4", + "snippets/ui-attachment.liquid": "6881df7cdb1b27462d8cce7c17c0b4ed4dd860d62535e1daa81378a57bb69ddf", + "snippets/ui-marquee.liquid": "47d5df8f1ff46a2cd48a132986c2ba199c9e19b485e62fc3e0d19091d4baaab1", + "assets/ui-marquee.css": "ef13b83f3dbcd6685fcee3dba14238386d0f34bc2266a5326c0b6f2d40a23fce", + "snippets/ui-invalid-controls.liquid": "a169e8244cc86f5431f902f7a73b37188051ba8f2ef3e323830462dae5aed066", + "snippets/ui-section.liquid": "da358cd87447457f3675b035b48556103d86cd11e0b2240ae2800e4c9ea54bdc", + "assets/ui-section.css": "0d5df020f70d38b3c049430204d469d681f56f6c2c4905b925ca0c81e61ff16f", + "snippets/ui-button-group.liquid": "a2790f8da0f8b37acc1ed558ba1548ea250a5b93e1b2dfbd11eb150f3f02783a", + "snippets/ui-collapsible.liquid": "e6107a03d00b8cc75bb03206e64b9aa7f2989d982c0408b398b62067c6df450a", + "snippets/ui-dropdown-menu.liquid": "25b31e09e3e7bfee9a397ca4de6e310754844c55e71639eadba917b0f4e6d2e5", + "snippets/ui-popover.liquid": "44ff93d72eaad409f56f8a84ce25715e2c3633f2ac49ba7a414767c6dbe2ee47", + "snippets/ui-hover-card.liquid": "ba4133d352fa14296743138c99f3694015bafe953751b8e42155f047c4beb9f0", + "snippets/ui.liquid": "30679056b2eafefbaeef5ecd271604951676893a65a8eccb0f970c787a67b900" + } +} diff --git a/liquid/registry.json b/liquid/registry.json new file mode 100644 index 000000000..ed07bfa17 --- /dev/null +++ b/liquid/registry.json @@ -0,0 +1,1295 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry.json", + "name": "@fulldev/liquid", + "homepage": "https://ui.full.dev/liquid/", + "items": [ + { + "name": "init", + "type": "registry:item", + "title": "Init", + "description": "Shared neutral tokens, native base styles and licenses. No framework or build required.", + "registryDependencies": [], + "files": [ + { + "path": "styles/tokens.css", + "type": "registry:file", + "target": "~/assets/ui-tokens.css" + }, + { + "path": "components/base.liquid", + "type": "registry:file", + "target": "~/snippets/ui-base.liquid" + }, + { + "path": "snippets/init.liquid", + "type": "registry:file", + "target": "~/snippets/ui-init.liquid" + }, + { + "path": "licenses.json", + "type": "registry:file", + "target": "~/assets/ui-licenses.json" + } + ] + }, + { + "name": "runtime", + "type": "registry:item", + "title": "Runtime", + "description": "Native ES modules, lazy data-slot controllers and Shopify editor lifecycle. Render once before other modules, or extend your own import map and use the exported lifecycle.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "runtime/ui-carousel.js", + "type": "registry:file", + "target": "~/assets/ui-carousel.js" + }, + { + "path": "runtime/ui-controls.js", + "type": "registry:file", + "target": "~/assets/ui-controls.js" + }, + { + "path": "runtime/ui-lifecycle.js", + "type": "registry:file", + "target": "~/assets/ui-lifecycle.js" + }, + { + "path": "runtime/ui-start.js", + "type": "registry:file", + "target": "~/assets/ui-start.js" + }, + { + "path": "vendor/data-slot-core.js", + "type": "registry:file", + "target": "~/assets/data-slot-core.js" + }, + { + "path": "vendor/data-slot-dialog.js", + "type": "registry:file", + "target": "~/assets/data-slot-dialog.js" + }, + { + "path": "vendor/data-slot-command.js", + "type": "registry:file", + "target": "~/assets/data-slot-command.js" + }, + { + "path": "vendor/data-slot-navigation-menu.js", + "type": "registry:file", + "target": "~/assets/data-slot-navigation-menu.js" + }, + { + "path": "vendor/data-slot-accordion.js", + "type": "registry:file", + "target": "~/assets/data-slot-accordion.js" + }, + { + "path": "vendor/data-slot-tabs.js", + "type": "registry:file", + "target": "~/assets/data-slot-tabs.js" + }, + { + "path": "vendor/data-slot-combobox.js", + "type": "registry:file", + "target": "~/assets/data-slot-combobox.js" + }, + { + "path": "vendor/data-slot-carousel.js", + "type": "registry:file", + "target": "~/assets/data-slot-carousel.js" + }, + { + "path": "vendor/data-slot-alert-dialog.js", + "type": "registry:file", + "target": "~/assets/data-slot-alert-dialog.js" + }, + { + "path": "vendor/data-slot-collapsible.js", + "type": "registry:file", + "target": "~/assets/data-slot-collapsible.js" + }, + { + "path": "vendor/data-slot-drawer.js", + "type": "registry:file", + "target": "~/assets/data-slot-drawer.js" + }, + { + "path": "vendor/data-slot-dropdown-menu.js", + "type": "registry:file", + "target": "~/assets/data-slot-dropdown-menu.js" + }, + { + "path": "vendor/data-slot-hover-card.js", + "type": "registry:file", + "target": "~/assets/data-slot-hover-card.js" + }, + { + "path": "vendor/data-slot-popover.js", + "type": "registry:file", + "target": "~/assets/data-slot-popover.js" + }, + { + "path": "vendor/data-slot-radio-group.js", + "type": "registry:file", + "target": "~/assets/data-slot-radio-group.js" + }, + { + "path": "vendor/data-slot-select.js", + "type": "registry:file", + "target": "~/assets/data-slot-select.js" + }, + { + "path": "vendor/data-slot-slider.js", + "type": "registry:file", + "target": "~/assets/data-slot-slider.js" + }, + { + "path": "vendor/data-slot-switch.js", + "type": "registry:file", + "target": "~/assets/data-slot-switch.js" + }, + { + "path": "vendor/data-slot-toast.js", + "type": "registry:file", + "target": "~/assets/data-slot-toast.js" + }, + { + "path": "vendor/data-slot-toggle.js", + "type": "registry:file", + "target": "~/assets/data-slot-toggle.js" + }, + { + "path": "vendor/data-slot-toggle-group.js", + "type": "registry:file", + "target": "~/assets/data-slot-toggle-group.js" + }, + { + "path": "vendor/data-slot-tooltip.js", + "type": "registry:file", + "target": "~/assets/data-slot-tooltip.js" + }, + { + "path": "snippets/runtime.liquid", + "type": "registry:file", + "target": "~/snippets/ui-runtime.liquid" + }, + { + "path": "runtime/ui-motion.js", + "type": "registry:file", + "target": "~/assets/ui-motion.js" + } + ] + }, + { + "name": "banner", + "type": "registry:item", + "title": "Banner", + "description": "Native Liquid banner with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/banner.liquid", + "type": "registry:file", + "target": "~/snippets/ui-banner.liquid" + } + ] + }, + { + "name": "tabs", + "type": "registry:item", + "title": "Tabs", + "description": "Native Liquid tabs with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tabs.liquid", + "type": "registry:file", + "target": "~/snippets/ui-tabs.liquid" + } + ] + }, + { + "name": "form", + "type": "registry:item", + "title": "Form", + "description": "Native Liquid form with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/field.json" + ], + "files": [ + { + "path": "components/form.liquid", + "type": "registry:file", + "target": "~/snippets/ui-form.liquid" + }, + { + "path": "styles/form.css", + "type": "registry:file", + "target": "~/assets/ui-form.css" + } + ] + }, + { + "name": "select", + "type": "registry:item", + "title": "Select", + "description": "Native Liquid select with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/select.liquid", + "type": "registry:file", + "target": "~/snippets/ui-select.liquid" + } + ] + }, + { + "name": "field", + "type": "registry:item", + "title": "Field", + "description": "Native Liquid field with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/field.liquid", + "type": "registry:file", + "target": "~/snippets/ui-field.liquid" + }, + { + "path": "styles/field.css", + "type": "registry:file", + "target": "~/assets/ui-field.css" + } + ] + }, + { + "name": "item", + "type": "registry:item", + "title": "Item", + "description": "Native Liquid item with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/item.liquid", + "type": "registry:file", + "target": "~/snippets/ui-item.liquid" + } + ] + }, + { + "name": "spinner", + "type": "registry:item", + "title": "Spinner", + "description": "Native Liquid spinner with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/spinner.liquid", + "type": "registry:file", + "target": "~/snippets/ui-spinner.liquid" + } + ] + }, + { + "name": "video", + "type": "registry:item", + "title": "Video", + "description": "Native Liquid video with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/video.liquid", + "type": "registry:file", + "target": "~/snippets/ui-video.liquid" + }, + { + "path": "styles/video.css", + "type": "registry:file", + "target": "~/assets/ui-video.css" + } + ] + }, + { + "name": "button", + "type": "registry:item", + "title": "Button", + "description": "Native Liquid button with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/button.liquid", + "type": "registry:file", + "target": "~/snippets/ui-button.liquid" + } + ] + }, + { + "name": "toggle", + "type": "registry:item", + "title": "Toggle", + "description": "Native Liquid toggle with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toggle.liquid" + } + ] + }, + { + "name": "logo", + "type": "registry:item", + "title": "Logo", + "description": "Native Liquid logo with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/logo.liquid", + "type": "registry:file", + "target": "~/snippets/ui-logo.liquid" + } + ] + }, + { + "name": "kbd", + "type": "registry:item", + "title": "Kbd", + "description": "Native Liquid kbd with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/kbd.liquid", + "type": "registry:file", + "target": "~/snippets/ui-kbd.liquid" + } + ] + }, + { + "name": "rating", + "type": "registry:item", + "title": "Rating", + "description": "Native Liquid rating with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/rating.liquid", + "type": "registry:file", + "target": "~/snippets/ui-rating.liquid" + }, + { + "path": "styles/rating.css", + "type": "registry:file", + "target": "~/assets/ui-rating.css" + } + ] + }, + { + "name": "tooltip", + "type": "registry:item", + "title": "Tooltip", + "description": "Native Liquid tooltip with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/tooltip.liquid", + "type": "registry:file", + "target": "~/snippets/ui-tooltip.liquid" + } + ] + }, + { + "name": "avatar", + "type": "registry:item", + "title": "Avatar", + "description": "Native Liquid avatar with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/avatar.liquid", + "type": "registry:file", + "target": "~/snippets/ui-avatar.liquid" + } + ] + }, + { + "name": "empty", + "type": "registry:item", + "title": "Empty", + "description": "Native Liquid empty with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/empty.liquid", + "type": "registry:file", + "target": "~/snippets/ui-empty.liquid" + } + ] + }, + { + "name": "alert-dialog", + "type": "registry:item", + "title": "Alert Dialog", + "description": "Native Liquid alert-dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/alert-dialog.liquid", + "type": "registry:file", + "target": "~/snippets/ui-alert-dialog.liquid" + } + ] + }, + { + "name": "navigation-menu", + "type": "registry:item", + "title": "Navigation Menu", + "description": "Native Liquid navigation-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/navigation-menu.liquid", + "type": "registry:file", + "target": "~/snippets/ui-navigation-menu.liquid" + }, + { + "path": "styles/navigation-menu.css", + "type": "registry:file", + "target": "~/assets/ui-navigation-menu.css" + } + ] + }, + { + "name": "typography", + "type": "registry:item", + "title": "Typography", + "description": "Native Liquid typography with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/typography.liquid", + "type": "registry:file", + "target": "~/snippets/ui-typography.liquid" + }, + { + "path": "styles/typography.css", + "type": "registry:file", + "target": "~/assets/ui-typography.css" + } + ] + }, + { + "name": "toc", + "type": "registry:item", + "title": "Toc", + "description": "Native Liquid toc with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/toc.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toc.liquid" + } + ] + }, + { + "name": "icon", + "type": "registry:item", + "title": "Icon", + "description": "Native Liquid icon with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/icon.liquid", + "type": "registry:file", + "target": "~/snippets/ui-icon.liquid" + }, + { + "path": "styles/icon.css", + "type": "registry:file", + "target": "~/assets/ui-icon.css" + } + ] + }, + { + "name": "combobox", + "type": "registry:item", + "title": "Combobox", + "description": "Native Liquid combobox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/combobox.liquid", + "type": "registry:file", + "target": "~/snippets/ui-combobox.liquid" + } + ] + }, + { + "name": "toast", + "type": "registry:item", + "title": "Toast", + "description": "Native Liquid toast with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toast.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toast.liquid" + }, + { + "path": "styles/toast.css", + "type": "registry:file", + "target": "~/assets/ui-toast.css" + } + ] + }, + { + "name": "header", + "type": "registry:item", + "title": "Header", + "description": "Native Liquid header with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/section.json" + ], + "files": [ + { + "path": "components/header.liquid", + "type": "registry:file", + "target": "~/snippets/ui-header.liquid" + } + ] + }, + { + "name": "command", + "type": "registry:item", + "title": "Command", + "description": "Native Liquid command with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/command.liquid", + "type": "registry:file", + "target": "~/snippets/ui-command.liquid" + } + ] + }, + { + "name": "accordion", + "type": "registry:item", + "title": "Accordion", + "description": "Native Liquid accordion with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/accordion.liquid", + "type": "registry:file", + "target": "~/snippets/ui-accordion.liquid" + }, + { + "path": "styles/accordion.css", + "type": "registry:file", + "target": "~/assets/ui-accordion.css" + } + ] + }, + { + "name": "slider", + "type": "registry:item", + "title": "Slider", + "description": "Native Liquid slider with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/slider.liquid", + "type": "registry:file", + "target": "~/snippets/ui-slider.liquid" + } + ] + }, + { + "name": "input", + "type": "registry:item", + "title": "Input", + "description": "Native Liquid input with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/input.liquid", + "type": "registry:file", + "target": "~/snippets/ui-input.liquid" + } + ] + }, + { + "name": "input-group", + "type": "registry:item", + "title": "Input Group", + "description": "Native Liquid input-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/input-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-input-group.liquid" + } + ] + }, + { + "name": "triggers", + "type": "registry:item", + "title": "Triggers", + "description": "Native Liquid triggers with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/triggers.liquid", + "type": "registry:file", + "target": "~/snippets/ui-triggers.liquid" + } + ] + }, + { + "name": "toggle-group", + "type": "registry:item", + "title": "Toggle Group", + "description": "Native Liquid toggle-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/toggle-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-toggle-group.liquid" + } + ] + }, + { + "name": "dialog", + "type": "registry:item", + "title": "Dialog", + "description": "Native Liquid dialog with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dialog.liquid", + "type": "registry:file", + "target": "~/snippets/ui-dialog.liquid" + } + ] + }, + { + "name": "drawer", + "type": "registry:item", + "title": "Drawer", + "description": "Native Liquid drawer with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/drawer.liquid", + "type": "registry:file", + "target": "~/snippets/ui-drawer.liquid" + } + ] + }, + { + "name": "badge", + "type": "registry:item", + "title": "Badge", + "description": "Native Liquid badge with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/badge.liquid", + "type": "registry:file", + "target": "~/snippets/ui-badge.liquid" + } + ] + }, + { + "name": "alert", + "type": "registry:item", + "title": "Alert", + "description": "Native Liquid alert with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/alert.liquid", + "type": "registry:file", + "target": "~/snippets/ui-alert.liquid" + } + ] + }, + { + "name": "table", + "type": "registry:item", + "title": "Table", + "description": "Native Liquid table with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/table.liquid", + "type": "registry:file", + "target": "~/snippets/ui-table.liquid" + } + ] + }, + { + "name": "breadcrumb", + "type": "registry:item", + "title": "Breadcrumb", + "description": "Native Liquid breadcrumb with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/breadcrumb.liquid", + "type": "registry:file", + "target": "~/snippets/ui-breadcrumb.liquid" + } + ] + }, + { + "name": "aspect-ratio", + "type": "registry:item", + "title": "Aspect Ratio", + "description": "Native Liquid aspect-ratio with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/aspect-ratio.liquid", + "type": "registry:file", + "target": "~/snippets/ui-aspect-ratio.liquid" + } + ] + }, + { + "name": "price", + "type": "registry:item", + "title": "Price", + "description": "Native Liquid price with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/price.liquid", + "type": "registry:file", + "target": "~/snippets/ui-price.liquid" + }, + { + "path": "styles/price.css", + "type": "registry:file", + "target": "~/assets/ui-price.css" + } + ] + }, + { + "name": "radio-group", + "type": "registry:item", + "title": "Radio Group", + "description": "Native Liquid radio-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/radio-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-radio-group.liquid" + } + ] + }, + { + "name": "checkbox", + "type": "registry:item", + "title": "Checkbox", + "description": "Native Liquid checkbox with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/input.json" + ], + "files": [ + { + "path": "components/checkbox.liquid", + "type": "registry:file", + "target": "~/snippets/ui-checkbox.liquid" + } + ] + }, + { + "name": "switch", + "type": "registry:item", + "title": "Switch", + "description": "Native Liquid switch with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/switch.liquid", + "type": "registry:file", + "target": "~/snippets/ui-switch.liquid" + } + ] + }, + { + "name": "skeleton", + "type": "registry:item", + "title": "Skeleton", + "description": "Native Liquid skeleton with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/skeleton.liquid", + "type": "registry:file", + "target": "~/snippets/ui-skeleton.liquid" + } + ] + }, + { + "name": "separator", + "type": "registry:item", + "title": "Separator", + "description": "Native Liquid separator with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/separator.liquid", + "type": "registry:file", + "target": "~/snippets/ui-separator.liquid" + } + ] + }, + { + "name": "card", + "type": "registry:item", + "title": "Card", + "description": "Native Liquid card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json" + ], + "files": [ + { + "path": "components/card.liquid", + "type": "registry:file", + "target": "~/snippets/ui-card.liquid" + }, + { + "path": "styles/card.css", + "type": "registry:file", + "target": "~/assets/ui-card.css" + } + ] + }, + { + "name": "pagination", + "type": "registry:item", + "title": "Pagination", + "description": "Native Liquid pagination with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/pagination.liquid", + "type": "registry:file", + "target": "~/snippets/ui-pagination.liquid" + } + ] + }, + { + "name": "popup", + "type": "registry:item", + "title": "Popup", + "description": "Native Liquid popup with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/popup.liquid", + "type": "registry:file", + "target": "~/snippets/ui-popup.liquid" + } + ] + }, + { + "name": "sheet", + "type": "registry:item", + "title": "Sheet", + "description": "Native Liquid sheet with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/sheet.liquid", + "type": "registry:file", + "target": "~/snippets/ui-sheet.liquid" + } + ] + }, + { + "name": "carousel", + "type": "registry:item", + "title": "Carousel", + "description": "Native Liquid carousel with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/carousel.liquid", + "type": "registry:file", + "target": "~/snippets/ui-carousel.liquid" + }, + { + "path": "styles/carousel.css", + "type": "registry:file", + "target": "~/assets/ui-carousel.css" + } + ] + }, + { + "name": "attachment", + "type": "registry:item", + "title": "Attachment", + "description": "Native Liquid attachment with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/icon.json" + ], + "files": [ + { + "path": "components/attachment.liquid", + "type": "registry:file", + "target": "~/snippets/ui-attachment.liquid" + } + ] + }, + { + "name": "marquee", + "type": "registry:item", + "title": "Marquee", + "description": "Native Liquid marquee with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/marquee.liquid", + "type": "registry:file", + "target": "~/snippets/ui-marquee.liquid" + }, + { + "path": "styles/marquee.css", + "type": "registry:file", + "target": "~/assets/ui-marquee.css" + } + ] + }, + { + "name": "invalid-controls", + "type": "registry:item", + "title": "Invalid Controls", + "description": "Native Liquid invalid-controls with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/invalid-controls.liquid", + "type": "registry:file", + "target": "~/snippets/ui-invalid-controls.liquid" + } + ] + }, + { + "name": "section", + "type": "registry:item", + "title": "Section", + "description": "Native Liquid section with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/init.json"], + "files": [ + { + "path": "components/section.liquid", + "type": "registry:file", + "target": "~/snippets/ui-section.liquid" + }, + { + "path": "styles/section.css", + "type": "registry:file", + "target": "~/assets/ui-section.css" + } + ] + }, + { + "name": "button-group", + "type": "registry:item", + "title": "Button Group", + "description": "Native Liquid button-group with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/button.json" + ], + "files": [ + { + "path": "components/button-group.liquid", + "type": "registry:file", + "target": "~/snippets/ui-button-group.liquid" + } + ] + }, + { + "name": "collapsible", + "type": "registry:item", + "title": "Collapsible", + "description": "Native Liquid collapsible with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/collapsible.liquid", + "type": "registry:file", + "target": "~/snippets/ui-collapsible.liquid" + } + ] + }, + { + "name": "dropdown-menu", + "type": "registry:item", + "title": "Dropdown Menu", + "description": "Native Liquid dropdown-menu with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/dropdown-menu.liquid", + "type": "registry:file", + "target": "~/snippets/ui-dropdown-menu.liquid" + } + ] + }, + { + "name": "popover", + "type": "registry:item", + "title": "Popover", + "description": "Native Liquid popover with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/popover.liquid", + "type": "registry:file", + "target": "~/snippets/ui-popover.liquid" + } + ] + }, + { + "name": "hover-card", + "type": "registry:item", + "title": "Hover Card", + "description": "Native Liquid hover-card with readable CSS and data-slot parts.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/runtime.json" + ], + "files": [ + { + "path": "components/hover-card.liquid", + "type": "registry:file", + "target": "~/snippets/ui-hover-card.liquid" + } + ] + }, + { + "name": "textarea", + "type": "registry:item", + "title": "Textarea", + "description": "Native Liquid textarea with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/input.json"], + "files": [] + }, + { + "name": "native-select", + "type": "registry:item", + "title": "Native Select", + "description": "Native Liquid native-select with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/input.json"], + "files": [] + }, + { + "name": "label", + "type": "registry:item", + "title": "Label", + "description": "Native Liquid label with readable CSS and data-slot parts.", + "registryDependencies": ["https://ui.full.dev/r/liquid/field.json"], + "files": [] + }, + { + "name": "components", + "type": "registry:item", + "title": "Components", + "description": "All native Liquid styles, controllers and a single layout integration snippet.", + "registryDependencies": [ + "https://ui.full.dev/r/liquid/init.json", + "https://ui.full.dev/r/liquid/runtime.json", + "https://ui.full.dev/r/liquid/banner.json", + "https://ui.full.dev/r/liquid/tabs.json", + "https://ui.full.dev/r/liquid/form.json", + "https://ui.full.dev/r/liquid/select.json", + "https://ui.full.dev/r/liquid/field.json", + "https://ui.full.dev/r/liquid/item.json", + "https://ui.full.dev/r/liquid/spinner.json", + "https://ui.full.dev/r/liquid/video.json", + "https://ui.full.dev/r/liquid/button.json", + "https://ui.full.dev/r/liquid/toggle.json", + "https://ui.full.dev/r/liquid/logo.json", + "https://ui.full.dev/r/liquid/kbd.json", + "https://ui.full.dev/r/liquid/rating.json", + "https://ui.full.dev/r/liquid/tooltip.json", + "https://ui.full.dev/r/liquid/avatar.json", + "https://ui.full.dev/r/liquid/empty.json", + "https://ui.full.dev/r/liquid/alert-dialog.json", + "https://ui.full.dev/r/liquid/navigation-menu.json", + "https://ui.full.dev/r/liquid/typography.json", + "https://ui.full.dev/r/liquid/toc.json", + "https://ui.full.dev/r/liquid/icon.json", + "https://ui.full.dev/r/liquid/combobox.json", + "https://ui.full.dev/r/liquid/toast.json", + "https://ui.full.dev/r/liquid/header.json", + "https://ui.full.dev/r/liquid/command.json", + "https://ui.full.dev/r/liquid/accordion.json", + "https://ui.full.dev/r/liquid/slider.json", + "https://ui.full.dev/r/liquid/input.json", + "https://ui.full.dev/r/liquid/input-group.json", + "https://ui.full.dev/r/liquid/triggers.json", + "https://ui.full.dev/r/liquid/toggle-group.json", + "https://ui.full.dev/r/liquid/dialog.json", + "https://ui.full.dev/r/liquid/drawer.json", + "https://ui.full.dev/r/liquid/badge.json", + "https://ui.full.dev/r/liquid/alert.json", + "https://ui.full.dev/r/liquid/table.json", + "https://ui.full.dev/r/liquid/breadcrumb.json", + "https://ui.full.dev/r/liquid/aspect-ratio.json", + "https://ui.full.dev/r/liquid/price.json", + "https://ui.full.dev/r/liquid/radio-group.json", + "https://ui.full.dev/r/liquid/checkbox.json", + "https://ui.full.dev/r/liquid/switch.json", + "https://ui.full.dev/r/liquid/skeleton.json", + "https://ui.full.dev/r/liquid/separator.json", + "https://ui.full.dev/r/liquid/card.json", + "https://ui.full.dev/r/liquid/pagination.json", + "https://ui.full.dev/r/liquid/popup.json", + "https://ui.full.dev/r/liquid/sheet.json", + "https://ui.full.dev/r/liquid/carousel.json", + "https://ui.full.dev/r/liquid/attachment.json", + "https://ui.full.dev/r/liquid/marquee.json", + "https://ui.full.dev/r/liquid/invalid-controls.json", + "https://ui.full.dev/r/liquid/section.json", + "https://ui.full.dev/r/liquid/button-group.json", + "https://ui.full.dev/r/liquid/collapsible.json", + "https://ui.full.dev/r/liquid/dropdown-menu.json", + "https://ui.full.dev/r/liquid/popover.json", + "https://ui.full.dev/r/liquid/hover-card.json" + ], + "files": [ + { + "path": "snippets/ui.liquid", + "type": "registry:file", + "target": "~/snippets/ui.liquid" + }, + { + "path": "manifest.json", + "type": "registry:file", + "target": "~/assets/ui-manifest.json" + } + ] + } + ] +} diff --git a/liquid/runtime/ui-carousel.js b/liquid/runtime/ui-carousel.js new file mode 100644 index 000000000..6fe9c0a90 --- /dev/null +++ b/liquid/runtime/ui-carousel.js @@ -0,0 +1,22 @@ +import { createCarousel } from '@data-slot/carousel'; + +// @data-slot/carousel owns scrolling, snapping, dragging, the arrow keys and the previous and next +// buttons, and keeps every visible card interactive. It writes English ARIA descriptions and slide +// labels, so the translated ones from Liquid are put back after it binds. +export function initializeCarousel(root) { + const content = root.querySelector('[data-slot="carousel-content"]'); + const items = [...content.children].filter((item) => item.matches('[data-slot="carousel-item"]')); + const descriptions = [root, ...items].map((element) => [element, element.getAttribute('aria-roledescription')]); + const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false }); + for (const [element, description] of descriptions) + if (description) element.setAttribute('aria-roledescription', description); + // Markup without a translated label template keeps the controller's labels. + const template = root.dataset.slideLabel; + if (template) + items.forEach((item, index) => { + item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', items.length)); + }); + for (const button of root.querySelectorAll('[data-slot="carousel-previous"], [data-slot="carousel-next"]')) + button.hidden = false; + return carousel; +} diff --git a/liquid/runtime/ui-controls.js b/liquid/runtime/ui-controls.js new file mode 100644 index 000000000..78a765184 --- /dev/null +++ b/liquid/runtime/ui-controls.js @@ -0,0 +1,195 @@ +// The data-slot controllers that few pages use, imported when theme.js finds their markup. Each +// entry starts one root and returns its controller with destroy(). Behavior is the vendored +// controller's; the adapters here only connect it to native forms and theme state. + +// A composite input renders hidden after its native fallback, such as a select or radio inputs, +// which the form submits until the controller runs. Then the fallback leaves the document, so +// labels and the form see only the field the controller generates, and the fallback's id moves to +// the labelled element, so a label[for] still reaches the control. start() creates the controller +// with the fallback's reset default and the value a shopper chose while it loaded; its destroy() +// writes the current value back, and the fallback returns with its own reset default. +function replaceFallback(root, labelled, start) { + const fallback = root.previousElementSibling?.matches('[data-fallback]') ? root.previousElementSibling : null; + const attributes = ['id', 'aria-labelledby'].map((name) => [name, labelled.getAttribute(name)]); + const restore = () => { + root.hidden = true; + for (const [name, value] of attributes) { + if (value === null) labelled.removeAttribute(name); + else labelled.setAttribute(name, value); + } + if (fallback) root.before(fallback); + }; + fallback?.remove(); + if (fallback?.id) labelled.id = fallback.id; + root.hidden = false; + let controller; + try { + controller = start(fallback); + } catch (error) { + restore(); + throw error; + } + return { + destroy() { + controller.destroy(); + restore(); + }, + }; +} + +function initializeSelect({ createSelect }, root) { + return replaceFallback(root, root.querySelector('[data-slot="select-trigger"]'), (select) => { + const options = [...(select?.options ?? [])]; + const initial = options.find((option) => option.defaultSelected) ?? options.find((option) => !option.disabled); + const controller = createSelect(root, { mountStrategy: 'eager', defaultValue: initial?.value || undefined }); + const value = select?.value || null; + if (select && value !== controller.value) root.dispatchEvent(new CustomEvent('select:set', { detail: { value } })); + return { + destroy() { + if (select) select.value = controller.value ?? ''; + controller.destroy(); + }, + }; + }); +} + +function initializeRadioGroup({ createRadioGroup }, root) { + return replaceFallback(root, root, (fallback) => { + const inputs = [...(fallback?.querySelectorAll('input[type="radio"]') ?? [])]; + const initial = inputs.find((input) => input.defaultChecked)?.value ?? ''; + const controller = createRadioGroup(root, fallback ? { defaultValue: initial } : {}); + const value = inputs.find((input) => input.checked)?.value ?? null; + if (fallback && value !== controller.value) { + if (value === null) controller.clear(); + else controller.select(value); + } + return { + destroy() { + for (const input of inputs) input.checked = input.value === controller.value; + controller.destroy(); + }, + }; + }); +} + +function initializeSwitch({ createSwitch }, root) { + return replaceFallback(root, root, (checkbox) => { + const controller = createSwitch(root, checkbox ? { defaultChecked: checkbox.defaultChecked } : {}); + if (checkbox) controller.setChecked(checkbox.checked); + return { + destroy() { + if (checkbox) checkbox.checked = controller.checked; + controller.destroy(); + }, + }; + }); +} + +// The slider has no form field of its own, so its native range inputs stay in the form, hidden, +// and follow its value. A form reset moves the slider back to the inputs' reset values. +function initializeSlider({ createSlider }, root) { + const fallback = root.previousElementSibling?.matches('[data-fallback]') ? root.previousElementSibling : null; + const inputs = fallback ? [...fallback.querySelectorAll('input[type="range"]')] : []; + const read = () => (inputs.length > 1 ? inputs.map((input) => Number(input.value)) : Number(inputs[0]?.value)); + const write = (value) => { + [value].flat().forEach((number, index) => { + if (!inputs[index] || inputs[index].value === String(number)) return; + inputs[index].value = String(number); + inputs[index].dispatchEvent(new Event('input', { bubbles: true })); + }); + }; + const slider = createSlider(root, { + ...(inputs.length ? { defaultValue: read() } : {}), + onValueChange: write, + onValueCommit: () => inputs.forEach((input) => input.dispatchEvent(new Event('change', { bubbles: true }))), + }); + const events = new AbortController(); + inputs[0]?.form?.addEventListener('reset', () => setTimeout(() => slider.setValue(read())), { + signal: events.signal, + }); + if (fallback) fallback.hidden = true; + root.hidden = false; + return { + destroy() { + events.abort(); + slider.destroy(); + root.hidden = true; + if (fallback) fallback.hidden = false; + }, + }; +} + +// The action of an alert dialog is a native link or submit or reset button, and the controller +// leaves closing to the caller. A link closes the dialog on click; a submit or reset button once its +// form submits or resets, so an invalid form keeps its native validation and the dialog stays open. +function initializeAlertDialog({ createAlertDialog }, root) { + const dialog = createAlertDialog(root); + const events = new AbortController(); + for (const action of root.querySelectorAll('[data-slot="alert-dialog-action"]')) { + action.addEventListener( + 'click', + () => { + const form = ['submit', 'reset'].includes(action.type) ? action.form : null; + if (form) form.addEventListener(action.type, () => dialog.close(), { once: true, signal: events.signal }); + else dialog.close(); + }, + { signal: events.signal }, + ); + } + return { + destroy() { + events.abort(); + dialog.destroy(); + }, + }; +} + +// Buttons inside a toaster show a toast with their translated title and description. Like other +// triggers that only open a popup, they show once the component has started (data-enhanced). +function initializeToaster({ createToast }, root) { + const toaster = createToast(root); + const events = new AbortController(); + for (const button of root.querySelectorAll('[data-toast-title]')) { + button.addEventListener( + 'click', + () => toaster.show({ title: button.dataset.toastTitle, description: button.dataset.toastDescription }), + { signal: events.signal }, + ); + } + return { + destroy() { + events.abort(); + toaster.destroy(); + }, + }; +} + +// Each entry loads its data-slot package, then starts one root with it and returns a controller +// with destroy(). theme.js starts the root only if it is still current once the package has +// loaded. Select, tooltip and hover card keep their closed content connected (eager), so nested +// components and the lifecycle in theme.js never see it leave the document. +export const controls = { + select: [() => import('@data-slot/select'), initializeSelect], + 'radio-group': [() => import('@data-slot/radio-group'), initializeRadioGroup], + switch: [() => import('@data-slot/switch'), initializeSwitch], + slider: [() => import('@data-slot/slider'), initializeSlider], + toggle: [() => import('@data-slot/toggle'), ({ createToggle }, root) => createToggle(root)], + 'toggle-group': [() => import('@data-slot/toggle-group'), ({ createToggleGroup }, root) => createToggleGroup(root)], + collapsible: [() => import('@data-slot/collapsible'), ({ createCollapsible }, root) => createCollapsible(root)], + popover: [() => import('@data-slot/popover'), ({ createPopover }, root) => createPopover(root)], + 'hover-card': [ + () => import('@data-slot/hover-card'), + ({ createHoverCard }, root) => createHoverCard(root, { mountStrategy: 'eager' }), + ], + tooltip: [ + () => import('@data-slot/tooltip'), + ({ createTooltip }, root) => createTooltip(root, { mountStrategy: 'eager' }), + ], + 'dropdown-menu': [ + () => import('@data-slot/dropdown-menu'), + ({ createDropdownMenu }, root) => createDropdownMenu(root), + ], + 'alert-dialog': [() => import('@data-slot/alert-dialog'), initializeAlertDialog], + drawer: [() => import('@data-slot/drawer'), ({ createDrawer }, root) => createDrawer(root)], + toast: [() => import('@data-slot/toast'), initializeToaster], +}; diff --git a/liquid/runtime/ui-lifecycle.js b/liquid/runtime/ui-lifecycle.js new file mode 100644 index 000000000..45862d33a --- /dev/null +++ b/liquid/runtime/ui-lifecycle.js @@ -0,0 +1,107 @@ +// One owner per root, including asynchronous imports and Shopify editor replacements. +export function createLifecycle(factories, scope = document) { + const components = new Map(); + const events = new AbortController(); + + function initialize(parent) { + for (const [selector, factory] of factories) { + const roots = [...parent.querySelectorAll(selector)]; + if (parent.matches?.(selector)) roots.unshift(parent); + for (const root of roots) { + if (components.has(root) || !root.isConnected) continue; + try { + const controller = factory(root); + components.set(root, controller); + if (!controller.ready) root.dataset.enhanced = ''; + controller.ready?.then( + () => { + if (components.get(root) === controller) root.dataset.enhanced = ''; + }, + (error) => { + console.error(error); + if (components.get(root) === controller) components.delete(root); + }, + ); + } catch (error) { + console.error(error); + } + } + } + } + + function destroy(predicate) { + for (const [root, controller] of components) { + if (!predicate(root)) continue; + // Drop ownership before a fallback is restored and the observer sees it. + components.delete(root); + controller.destroy?.(); + delete root.dataset.enhanced; + } + } + + function reveal(root, method, value) { + const owner = components.get(root); + const apply = (controller) => { + if (owner && components.get(root) === owner) controller?.[method]?.(value); + }; + // A selected block can arrive while its controller is still loading. + if (owner?.ready) + owner.ready.then( + (controller) => apply(controller ?? owner), + () => {}, + ); + else apply(owner); + } + + initialize(scope); + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE && node.isConnected) initialize(node); + } + } + if (mutations.some((mutation) => mutation.removedNodes.length)) destroy((root) => !root.isConnected); + }); + observer.observe(scope === document ? document.body : scope, { childList: true, subtree: true }); + document.addEventListener('shopify:section:load', (event) => initialize(event.target), { signal: events.signal }); + document.addEventListener('shopify:section:unload', (event) => destroy((root) => event.target.contains(root)), { + signal: events.signal, + }); + document.addEventListener( + 'shopify:block:select', + (event) => { + const item = event.target.closest('[data-slot="accordion-item"]'); + if (item) reveal(item.closest('[data-slot="accordion"]'), 'expand', item.dataset.value); + const slide = event.target.closest('[data-slot="carousel-item"]'); + if (slide) + reveal(slide.closest('[data-slot="carousel"]'), 'goTo', [...slide.parentElement.children].indexOf(slide)); + }, + { signal: events.signal }, + ); + return { + components, + initialize, + destroy() { + observer.disconnect(); + events.abort(); + destroy(() => true); + }, + }; +} + +export function deferred(load, start) { + return (root) => { + let controller; + let destroyed = false; + const ready = load().then((module) => { + if (!destroyed && root.isConnected) return (controller = start(module, root)); + }); + return { + ready, + destroy() { + destroyed = true; + controller?.destroy(); + }, + }; + }; +} diff --git a/liquid/runtime/ui-motion.js b/liquid/runtime/ui-motion.js new file mode 100644 index 000000000..fd2ce9fc7 --- /dev/null +++ b/liquid/runtime/ui-motion.js @@ -0,0 +1,45 @@ +const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)'); + +// A pause button for a CSS animation that runs on its own, such as a marquee. Its label stays Pause +// and aria-pressed carries the state: pressed while the animation is paused. +export function initializeMotionPause(button) { + const root = button.closest('[data-motion]'); + const events = new AbortController(); + // CSS animates the root only from here on, so it never moves without a working pause button. + root.dataset.motionReady = ''; + const sync = () => { + button.hidden = reducedMotion.matches; + }; + button.addEventListener( + 'click', + () => { + const paused = button.getAttribute('aria-pressed') !== 'true'; + button.setAttribute('aria-pressed', String(paused)); + root.toggleAttribute('data-paused', paused); + }, + { signal: events.signal }, + ); + reducedMotion.addEventListener('change', sync, { signal: events.signal }); + sync(); + return { + destroy() { + events.abort(); + delete root.dataset.motionReady; + }, + }; +} + +// Muted autoplay videos stay still for visitors who prefer reduced motion; they keep their controls. +export function initializeAutoplayVideo(video) { + const events = new AbortController(); + const sync = () => { + if (reducedMotion.matches) video.pause(); + }; + reducedMotion.addEventListener('change', sync, { signal: events.signal }); + sync(); + return { + destroy() { + events.abort(); + }, + }; +} diff --git a/liquid/runtime/ui-start.js b/liquid/runtime/ui-start.js new file mode 100644 index 000000000..d90e606cc --- /dev/null +++ b/liquid/runtime/ui-start.js @@ -0,0 +1,37 @@ +import { controls } from '@fulldev/controls'; +import { createLifecycle, deferred } from '@fulldev/lifecycle'; +import { initializeAutoplayVideo, initializeMotionPause } from '@fulldev/motion'; + +const simple = [ + ['accordion', () => import('@data-slot/accordion'), 'createAccordion'], + ['tabs', () => import('@data-slot/tabs'), 'createTabs'], + ['dialog', () => import('@data-slot/dialog'), 'createDialog'], + ['combobox', () => import('@data-slot/combobox'), 'createCombobox'], + ['command', () => import('@data-slot/command'), 'createCommand'], + ['navigation-menu', () => import('@data-slot/navigation-menu'), 'createNavigationMenu'], +]; + +export const lifecycle = createLifecycle([ + ['[data-motion-pause]', initializeMotionPause], + ['video[autoplay]', initializeAutoplayVideo], + ...simple.map(([name, load, factory]) => [ + `[data-slot="${name}"]`, + deferred(load, (module, root) => + module[factory](root, { + mountStrategy: 'eager', + ...(name === 'accordion' ? { multiple: root.dataset.multiple !== 'false' } : {}), + }), + ), + ]), + [ + '[data-slot="carousel"]', + deferred( + () => import('@fulldev/carousel'), + (module, root) => module.initializeCarousel(root), + ), + ], + ...Object.entries(controls).map(([name, [load, start]]) => [ + name === 'toggle' ? 'button[data-slot="toggle"]' : `[data-slot="${name}"]`, + deferred(load, start), + ]), +]); diff --git a/liquid/snippets/init.liquid b/liquid/snippets/init.liquid new file mode 100644 index 000000000..685d1d30b --- /dev/null +++ b/liquid/snippets/init.liquid @@ -0,0 +1,6 @@ +{% doc %} + Fulldev UI tokens and native base. Render once in the head before theme overrides. +{% enddoc %} + +{{ 'ui-tokens.css' | asset_url | stylesheet_tag: preload: true }} +{% render 'ui-base' %} diff --git a/liquid/snippets/runtime.liquid b/liquid/snippets/runtime.liquid new file mode 100644 index 000000000..a9b7f1c14 --- /dev/null +++ b/liquid/snippets/runtime.liquid @@ -0,0 +1,9 @@ +{% doc %} + Default Fulldev UI runtime. Render once in the head, before other module scripts. + Use your own import map and createLifecycle when the theme supplies Shopify adapters. +{% enddoc %} + + + diff --git a/liquid/snippets/ui.liquid b/liquid/snippets/ui.liquid new file mode 100644 index 000000000..bea8bebd4 --- /dev/null +++ b/liquid/snippets/ui.liquid @@ -0,0 +1,68 @@ +{% doc %} + All Fulldev UI styles. Render once in the head before theme styles. + + @param {boolean} [runtime] - False when the theme owns its import map and initialization. +{% enddoc %} + +{% render 'ui-init' %} +{% render 'ui-banner' %} +{% render 'ui-tabs' %} +{% render 'ui-form' %} +{% render 'ui-select' %} +{% render 'ui-field' %} +{% render 'ui-item' %} +{% render 'ui-spinner' %} +{% render 'ui-video' %} +{% render 'ui-button' %} +{% render 'ui-toggle' %} +{% render 'ui-logo' %} +{% render 'ui-kbd' %} +{% render 'ui-rating' %} +{% render 'ui-tooltip' %} +{% render 'ui-avatar' %} +{% render 'ui-empty' %} +{% render 'ui-alert-dialog' %} +{% render 'ui-navigation-menu' %} +{% render 'ui-typography' %} +{% render 'ui-toc' %} +{% render 'ui-icon' %} +{% render 'ui-combobox' %} +{% render 'ui-toast' %} +{% render 'ui-header' %} +{% render 'ui-command' %} +{% render 'ui-accordion' %} +{% render 'ui-slider' %} +{% render 'ui-input' %} +{% render 'ui-input-group' %} +{% render 'ui-triggers' %} +{% render 'ui-toggle-group' %} +{% render 'ui-dialog' %} +{% render 'ui-drawer' %} +{% render 'ui-badge' %} +{% render 'ui-alert' %} +{% render 'ui-table' %} +{% render 'ui-breadcrumb' %} +{% render 'ui-aspect-ratio' %} +{% render 'ui-price' %} +{% render 'ui-radio-group' %} +{% render 'ui-checkbox' %} +{% render 'ui-switch' %} +{% render 'ui-skeleton' %} +{% render 'ui-separator' %} +{% render 'ui-card' %} +{% render 'ui-pagination' %} +{% render 'ui-popup' %} +{% render 'ui-sheet' %} +{% render 'ui-carousel' %} +{% render 'ui-attachment' %} +{% render 'ui-marquee' %} +{% render 'ui-invalid-controls' %} +{% render 'ui-section' %} +{% render 'ui-button-group' %} +{% render 'ui-collapsible' %} +{% render 'ui-dropdown-menu' %} +{% render 'ui-popover' %} +{% render 'ui-hover-card' %} +{% if runtime != false %} + {% render 'ui-runtime' %} +{% endif %} diff --git a/liquid/styles/accordion.css b/liquid/styles/accordion.css new file mode 100644 index 000000000..9e4a2ebf6 --- /dev/null +++ b/liquid/styles/accordion.css @@ -0,0 +1,66 @@ +/* The data-slot accordion, and the same parts on native
and , which open + without JavaScript. */ +[data-slot='accordion'] { + display: flex; + flex-direction: column; + width: 100%; +} +[data-slot='accordion-item']:not(:last-child) { + border-bottom: 1px solid var(--border); +} +.accordion__heading { + font: inherit; + letter-spacing: var(--tracking-normal); +} +[data-slot='accordion-trigger'] { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: calc(var(--spacing) * 4); + width: 100%; + padding-block: calc(var(--spacing) * 4); + border: 1px solid transparent; + border-radius: var(--radius-md); + font-size: 0.875rem; + font-weight: 500; + line-height: 1.43; + text-align: left; + list-style: none; + outline: none; +} +summary[data-slot='accordion-trigger']::-webkit-details-marker { + display: none; +} +[data-slot='accordion-trigger']:hover { + text-decoration: underline; +} +[data-slot='accordion-trigger']:focus-visible { + border-color: var(--ring); + box-shadow: var(--focus-ring); +} +[data-slot='accordion-trigger'] > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + margin-left: auto; + color: var(--muted-foreground); + transition: rotate 200ms; +} +[data-slot='accordion-item'][data-state='open'] [data-slot='accordion-trigger'] > .icon, +details[open] > [data-slot='accordion-trigger'] > .icon { + rotate: 180deg; +} +[data-slot='accordion-content'] { + overflow: hidden; + font-size: 0.875rem; +} +[data-slot='accordion'] [data-slot='accordion-content'] { + height: var(--accordion-panel-height); + transition: height 200ms ease; +} +[data-slot='accordion-content'][data-starting-style], +[data-slot='accordion-content'][data-ending-style] { + height: 0; +} +[data-slot='accordion-content-inner'] { + padding-bottom: calc(var(--spacing) * 4); +} diff --git a/liquid/styles/card.css b/liquid/styles/card.css new file mode 100644 index 000000000..ad436e55e --- /dev/null +++ b/liquid/styles/card.css @@ -0,0 +1,114 @@ +/* The card surface and its parts, as in Vega. Header, content and footer carry the inline + padding, so media placed first spans the full width. Fulldev UI's blocks restyle the card with + classes; the theme names the two they use, outline (a border on the background) and muted (a + muted fill), as variants. A card that is one link holds no buttons or forms. */ +[data-slot='card'] { + --card-spacing: calc(var(--spacing) * 6); + display: flex; + flex-direction: column; + gap: var(--card-spacing); + min-width: 0; + padding-block: var(--card-spacing); + overflow: hidden; + border-radius: var(--radius-xl); + background: var(--card); + color: var(--card-foreground); + font-size: 0.875rem; + text-decoration: none; + box-shadow: + var(--shadow-xs), + 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent); + outline: none; + transition: + background-color 150ms, + box-shadow 150ms; +} +[data-slot='card'][data-size='sm'] { + --card-spacing: calc(var(--spacing) * 4); +} +[data-slot='card']:has(> :is(img, [data-slot='aspect-ratio']):first-child) { + padding-top: 0; +} +[data-slot='card-header'] { + display: grid; + grid-auto-rows: min-content; + align-items: start; + gap: var(--spacing); + padding-inline: var(--card-spacing); +} +[data-slot='card-header']:has(> [data-slot='card-action']) { + grid-template-columns: 1fr auto; +} +[data-slot='card-action'] { + grid-row: 1 / span 2; + grid-column: 2; + place-self: start end; +} +[data-slot='card-title'] { + font-family: inherit; + font-size: 1rem; + font-weight: 500; + line-height: 1.5; + letter-spacing: var(--tracking-normal); +} +[data-slot='card'][data-size='sm'] [data-slot='card-title'] { + font-size: 0.875rem; +} +[data-slot='card-description'] { + color: var(--muted-foreground); + font-size: 0.875rem; + line-height: 1.625; +} +[data-slot='card-content'] { + padding-inline: var(--card-spacing); +} +[data-slot='card-footer'] { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: calc(var(--spacing) * 2); + margin-top: auto; + padding-inline: var(--card-spacing); +} +[data-slot='card'][data-variant='outline'] { + border: 1px solid var(--border); + background: var(--background); + color: var(--foreground); + box-shadow: none; +} +[data-slot='card'][data-variant='muted'] { + background: color-mix(in oklch, var(--muted) 50%, transparent); + color: var(--foreground); + box-shadow: none; +} +a[data-slot='card']:not([data-variant='muted']):hover { + background-color: color-mix(in oklch, var(--muted) 40%, transparent); +} +a[data-slot='card'][data-variant='muted']:hover { + background-color: color-mix(in oklch, var(--muted) 70%, transparent); +} +/* After the variants, which drop the card's shadow, so every linked card shows the ring. */ +[data-slot='card']:focus-visible { + box-shadow: var(--focus-ring); +} +:is([data-slot='card'][data-variant='outline'], [data-slot='card'][data-variant='muted']):focus-visible { + border-color: var(--ring); +} +/* The arrow link at the foot of a content card; inside a linked card it is text. */ +.card-link { + display: inline-flex; + align-items: center; + gap: var(--spacing); + color: var(--primary); + font-size: 0.875rem; + font-weight: 500; + text-underline-offset: 4px; +} +.card-link > .icon { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); +} +a:hover .card-link, +a.card-link:hover { + text-decoration: underline; +} diff --git a/liquid/styles/carousel.css b/liquid/styles/carousel.css new file mode 100644 index 000000000..5743f2723 --- /dev/null +++ b/liquid/styles/carousel.css @@ -0,0 +1,58 @@ +[data-slot='carousel'] { + position: relative; + display: flex; + flex-direction: column; + gap: 24px; +} +/* The strip bleeds into the gutters. Each item's box starts at a scroll position and insets its + content by the gutter, so the controller's positions and the snap points line up with the + section's columns; the end padding keeps the last item off the edge. It is the containing block + of absolutely positioned content in the cards, such as screen reader labels, so its overflow + clips them and they cannot widen the page. */ +[data-slot='carousel-content'] { + position: relative; + display: flex; + margin-inline: calc(var(--gutter) * -1); + padding-inline-end: var(--gutter); + overflow-x: auto; + scroll-snap-type: x mandatory; + scrollbar-width: none; +} +[data-slot='carousel-content']::-webkit-scrollbar { + display: none; +} +[data-slot='carousel'][data-dragging='true'] [data-slot='carousel-content'] { + cursor: grabbing; +} +[data-slot='carousel-item'] { + flex: 0 0 calc(100% / var(--carousel-columns, 1)); + min-width: 0; + padding-left: var(--gutter); + scroll-snap-align: start; +} +.carousel__controls { + display: flex; + gap: 8px; +} +.carousel__controls[data-align='end'] { + justify-content: flex-end; +} +.carousel__controls[data-align='center'] { + justify-content: center; +} +.carousel__controls:not(:has(button:not([hidden]))) { + display: none; +} +.carousel__button[data-button] { + border-radius: 50%; +} +@media (max-width: 1023px) { + [data-slot='carousel-item'] { + flex-basis: calc(100% / var(--carousel-columns-tablet, 1)); + } +} +@media (max-width: 767px) { + [data-slot='carousel-item'] { + flex-basis: calc(100% / var(--carousel-columns-mobile, 1)); + } +} diff --git a/liquid/styles/field.css b/liquid/styles/field.css new file mode 100644 index 000000000..64fa0bdf5 --- /dev/null +++ b/liquid/styles/field.css @@ -0,0 +1,66 @@ +[data-slot='label'], +[data-slot='field-label'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + font-size: 0.875rem; + font-weight: 500; + line-height: 1; +} +/* Fulldev UI's field set: a fieldset of fields or options under a legend. A legend sits outside its + fieldset's flex layout, so it keeps the field gap as its margin. */ +[data-slot='field-set'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 6); + min-width: 0; + margin: 0; + padding: 0; + border: 0; +} +[data-slot='field-legend'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 2); + margin-bottom: calc(var(--spacing) * 3); + padding: 0; + font-size: 1rem; + font-weight: 500; + line-height: 1; +} +[data-slot='field-legend'][data-variant='label'] { + font-size: 0.875rem; +} +[data-slot='field'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 3); + width: 100%; +} +/* An input and its button side by side, as in newsletter and search forms. */ +[data-slot='field'][data-orientation='horizontal'] { + flex-direction: row; + align-items: center; +} +[data-slot='field'][data-orientation='horizontal'] > :is(input, select, textarea) { + flex: 1; +} +[data-slot='field-group'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 7); +} +/* Two fields side by side from 640px. */ +.field-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: calc(var(--spacing) * 4); +} +[data-slot='field-description'] { + font-size: 0.875rem; + color: var(--muted-foreground); +} +[data-slot='field-error'] { + font-size: 0.875rem; + color: var(--destructive); +} diff --git a/liquid/styles/form.css b/liquid/styles/form.css new file mode 100644 index 000000000..5a2794441 --- /dev/null +++ b/liquid/styles/form.css @@ -0,0 +1,14 @@ +/* Fulldev UI's form. Shopify form tags take a class, so their forms use .form. */ +.form, +[data-slot='form'] { + display: flex; + flex-direction: column; + gap: calc(var(--spacing) * 6); +} +.form-error { + font-size: 0.875rem; + color: var(--destructive); +} +.form-error:empty { + display: none; +} diff --git a/liquid/styles/icon.css b/liquid/styles/icon.css new file mode 100644 index 000000000..33cc1e590 --- /dev/null +++ b/liquid/styles/icon.css @@ -0,0 +1,32 @@ +/* snippets/icon.liquid: decorative Lucide icons, 20px unless a part sizes them. */ +[data-slot='icon'] { + width: calc(var(--spacing) * 5); + height: calc(var(--spacing) * 5); + flex: none; +} +.icon-box { + display: inline-flex; + flex: none; + align-items: center; + justify-content: center; + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + border-radius: var(--radius-lg); + background: color-mix(in oklch, var(--primary) 10%, transparent); + color: var(--primary); +} +.icon-box > .icon { + width: calc(var(--spacing) * 5); + height: calc(var(--spacing) * 5); +} +/* A 36px secondary tile with a primary icon, as Fulldev UI's contact details. */ +.icon-box--secondary { + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + border-radius: var(--radius-md); + background: var(--secondary); +} +.icon-box--secondary > .icon { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); +} diff --git a/liquid/styles/marquee.css b/liquid/styles/marquee.css new file mode 100644 index 000000000..2d3a7c7dc --- /dev/null +++ b/liquid/styles/marquee.css @@ -0,0 +1,65 @@ +/* Fulldev UI's marquee parts on a CSS animation. Each marquee-content row holds its items once in a + track and again in an inert duplicate. Rows stay static and wrapped without JavaScript and with + reduced motion; motion.js sets data-motion-ready once the marquee-toggle pause button works. + data-reverse runs a row the other way. Hover, focus and the toggle pause every row. */ +[data-slot='marquee'] { + --marquee-gap: calc(var(--spacing) * 4); + position: relative; +} +[data-slot='marquee-content'] { + display: flex; + overflow: hidden; +} +.marquee-track { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + min-width: 100%; + margin-inline-start: calc(var(--marquee-gap) * -1); +} +.marquee-track[aria-hidden='true'] { + display: none; +} +[data-slot='marquee-item'] { + flex: none; + min-width: 0; + padding-inline-start: var(--marquee-gap); +} +@media (prefers-reduced-motion: no-preference) { + [data-slot='marquee'][data-motion-ready] .marquee-track { + flex: none; + flex-wrap: nowrap; + justify-content: flex-start; + margin-inline-start: 0; + animation: marquee var(--marquee-duration, 40s) linear infinite; + } + [data-slot='marquee'][data-motion-ready] .marquee-track[aria-hidden='true'] { + display: flex; + } + [data-slot='marquee'][data-motion-ready] [data-reverse] .marquee-track { + animation-direction: reverse; + } +} +[data-slot='marquee']:hover .marquee-track, +[data-slot='marquee']:focus-within .marquee-track, +[data-slot='marquee'][data-paused] .marquee-track { + animation-play-state: paused; +} +@keyframes marquee { + to { + transform: translateX(-100%); + } +} +/* The marquee's pause button, an outline icon-sm button. */ +[data-slot='marquee-toggle'] { + z-index: 1; +} +/* The pause buttons of marquees swap their icon once pressed. */ +.motion-pause .icon--play, +.motion-pause[aria-pressed='true'] .icon--pause { + display: none; +} +.motion-pause[aria-pressed='true'] .icon--play { + display: block; +} diff --git a/liquid/styles/navigation-menu.css b/liquid/styles/navigation-menu.css new file mode 100644 index 000000000..14c9ed086 --- /dev/null +++ b/liquid/styles/navigation-menu.css @@ -0,0 +1,235 @@ +.navigation-menu { + position: relative; + justify-self: start; +} +.navigation-menu [data-slot='navigation-menu-list'] { + display: flex; + align-items: center; + gap: 2px; + margin: 0; + padding: 0; + list-style: none; +} +/* A long merchant label wraps and grows the link from its 36px height. */ +.navigation-menu [data-slot='navigation-menu-list'] > li > a, +.navigation-menu [data-slot='navigation-menu-trigger'] { + display: inline-flex; + align-items: center; + gap: 4px; + min-height: 36px; + padding: 6px 16px; + border-radius: var(--radius-md); + background: transparent; + font-size: 0.875rem; + font-weight: 500; + overflow-wrap: anywhere; + text-decoration: none; + transition: + background-color 150ms, + color 150ms; +} +.navigation-menu [data-slot='navigation-menu-trigger'] .icon { + flex: none; + width: 12px; + height: 12px; + margin-left: 4px; + transition: rotate 300ms; +} +.navigation-menu [data-slot='navigation-menu-trigger']:hover, +.navigation-menu [data-slot='navigation-menu-trigger']:focus-visible, +.navigation-menu [data-slot='navigation-menu-list'] > li > a:hover { + background: var(--muted); +} +.navigation-menu [data-slot='navigation-menu-list'] > li > a[aria-current] { + background: color-mix(in oklch, var(--muted) 50%, transparent); +} +.navigation-menu [data-slot='navigation-menu-trigger'][data-open] { + background: color-mix(in oklch, var(--muted) 50%, transparent); +} +.navigation-menu [data-slot='navigation-menu-trigger'][data-open] .icon { + rotate: 180deg; +} +.navigation-menu [data-slot='navigation-menu-trigger']:focus-visible, +.navigation-menu [data-slot='navigation-menu-list'] > li > a:focus-visible { + outline: 1px solid var(--ring); + outline-offset: 0; + box-shadow: var(--focus-ring); +} +[data-slot='navigation-menu-positioner'] { + z-index: 40; + box-sizing: border-box; + width: var(--positioner-width); + height: var(--positioner-height); + max-width: var(--available-width); + top: 0; + left: 0; + transition: + top 0.3s cubic-bezier(0.22, 1, 0.36, 1), + left 0.3s cubic-bezier(0.22, 1, 0.36, 1); +} +[data-slot='navigation-menu-positioner'][data-instant] { + transition: none; +} +[data-slot='navigation-menu-popup'] { + position: relative; + box-sizing: border-box; + width: var(--popup-width); + height: var(--popup-height); + transform-origin: var(--transform-origin); + overflow: hidden; + border-radius: var(--radius-lg); + background: var(--popover); + color: var(--popover-foreground); + box-shadow: + 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent), + var(--shadow-md); + transition: + transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), + width 0.3s cubic-bezier(0.22, 1, 0.36, 1), + height 0.3s cubic-bezier(0.22, 1, 0.36, 1), + opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1); +} +[data-slot='navigation-menu-popup'][data-ending-style] { + transition-duration: 150ms; + transition-timing-function: ease; +} +[data-slot='navigation-menu-popup'][data-instant] { + transition: + transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), + opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1); +} +[data-slot='navigation-menu-popup'][data-starting-style], +[data-slot='navigation-menu-popup'][data-ending-style] { + opacity: 0; + transform: scale(0.9); +} +[data-slot='navigation-menu-popup'][data-closed]:not([data-ending-style]) { + pointer-events: none; +} +[data-slot='navigation-menu-viewport'] { + position: relative; + overflow: hidden; + width: 100%; + height: 100%; +} +[data-slot='navigation-menu-content'] { + position: relative; + transform: translateX(0); + transition: + transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), + opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1); + will-change: transform, opacity; +} +[data-slot='navigation-menu-content'][data-closed]:not([data-ending-style]) { + opacity: 0; + pointer-events: none; +} +[data-slot='navigation-menu-content'][data-starting-style], +[data-slot='navigation-menu-content'][data-ending-style] { + opacity: 0; +} +[data-slot='navigation-menu-content'][data-starting-style][data-activation-direction='right'] { + transform: translateX(50%); +} +[data-slot='navigation-menu-content'][data-starting-style][data-activation-direction='left'] { + transform: translateX(-50%); +} +[data-slot='navigation-menu-content'][data-ending-style][data-activation-direction='right'] { + transform: translateX(-50%); +} +[data-slot='navigation-menu-content'][data-ending-style][data-activation-direction='left'] { + transform: translateX(50%); +} +/* Fulldev UI's mega panel: the overview card beside the child links. */ +.navigation-panel { + display: grid; + gap: 8px; + width: min(46rem, calc(100vw - 32px)); + padding: 4px; +} +.navigation-panel[data-layout='wide'] { + width: min(52rem, calc(100vw - 32px)); + padding: 8px; +} +[data-slot='navigation-menu-link'] { + display: flex; + align-items: center; + gap: 12px; + padding: 8px; + border-radius: var(--radius-md); + font-size: 0.875rem; + outline: none; + transition: background-color 150ms; +} +.navigation-panel [data-slot='navigation-menu-link']:is(:hover, :focus-visible) { + background: var(--muted); +} +.navigation-panel [data-slot='navigation-menu-link']:focus-visible { + box-shadow: var(--focus-ring); +} +.navigation-panel [data-slot='navigation-menu-link'][aria-current] { + background: color-mix(in oklch, var(--muted) 50%, transparent); +} +.navigation-panel__overview[data-slot='navigation-menu-link'] { + flex-direction: column; + align-items: flex-start; + justify-content: flex-end; + gap: 12px; + min-height: 160px; + padding: 16px; + background: color-mix(in oklch, var(--muted) 50%, transparent); +} +.navigation-panel[data-layout='wide'] .navigation-panel__overview[data-slot='navigation-menu-link'] { + justify-content: flex-start; + padding: 12px; +} +.navigation-panel__overview img { + width: 100%; + margin-bottom: auto; + aspect-ratio: 16 / 9; + border-radius: var(--radius-md); + background: var(--muted); + object-fit: cover; +} +.navigation-panel__overview-title { + font-size: 1rem; + font-weight: 500; +} +.navigation-panel__description { + color: var(--muted-foreground); + font-size: 0.875rem; + line-height: 1.625; +} +.navigation-panel__links { + display: grid; + align-content: start; + gap: 4px; +} +.navigation-panel__link { + padding: 12px; + font-weight: 500; +} +.navigation-panel__sublinks { + display: grid; + gap: 2px; + margin-inline-start: 12px; +} +.navigation-panel__link--sub { + padding-block: 6px; + color: var(--muted-foreground); + font-weight: 400; +} +.navigation-panel__link--sub:hover { + color: var(--foreground); +} +@media (min-width: 1024px) { + .navigation-panel { + grid-template-columns: 1fr 1.1fr; + } + .navigation-panel[data-layout='wide'] { + grid-template-columns: 1fr 1.2fr; + } + .navigation-panel[data-layout='wide'] .navigation-panel__links { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} diff --git a/liquid/styles/price.css b/liquid/styles/price.css new file mode 100644 index 000000000..610b62538 --- /dev/null +++ b/liquid/styles/price.css @@ -0,0 +1,41 @@ +/* Fulldev UI's price: the current value, the higher compare-at price struck through, a savings + badge, and the unit price on its own line. */ +[data-slot='price'] { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--spacing) calc(var(--spacing) * 2); + font-size: 0.875rem; +} +[data-slot='price-value'] { + display: inline-flex; + color: var(--foreground); + flex-wrap: wrap; + align-items: center; + gap: var(--spacing) calc(var(--spacing) * 2); + line-height: 1.25; +} +[data-slot='price-value'] del { + color: var(--muted-foreground); + font-size: 0.875rem; +} +.price__badge { + padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 2); + border-radius: 9999px; + background: var(--muted); + color: var(--foreground); + font-size: 0.75rem; + font-weight: 500; + line-height: 1rem; +} +[data-slot='price'] [data-slot='price-unit'] { + width: 100%; +} +[data-slot='price-unit'] { + font-size: 0.75rem; + color: var(--muted-foreground); +} +.price--lg { + font-size: 1.25rem; + font-weight: 500; +} diff --git a/liquid/styles/primitives.css b/liquid/styles/primitives.css new file mode 100644 index 000000000..dfd7bdb97 --- /dev/null +++ b/liquid/styles/primitives.css @@ -0,0 +1,53 @@ +:root { + --heading-weight: 600; + --container: 1280px; + --section-gap: 64px; + --header-height: 64px; + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --radius: 0.625rem; + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); + --shadow-x: 0; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0; + --shadow-opacity: 0.1; + --shadow-color: oklch(0 0 0); + --shadow-2xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0 1px 3px 0 hsl(0 0% 0% / 0.25); + --font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif; + --font-heading: var(--font-sans); + --font-mono: ui-monospace, sfmono-regular, menlo, monaco, consolas, monospace; + --tracking-tight: -0.025em; + --tracking-normal: 0; + --spacing: 0.25rem; + --gutter: calc(var(--spacing) * 4); + --section-padding: calc(var(--spacing) * 16); + /* Fixed colors for content placed on photos and video, independent of the color scheme. */ + --on-media: #fff; + --on-media-muted: rgb(255 255 255 / 0.85); + --media-button: #fff; + --media-button-foreground: #171717; + --media-button-hover: #e5e5e5; + --media-secondary: rgb(255 255 255 / 0.15); + --media-secondary-hover: rgb(255 255 255 / 0.2); + --media-overlay: rgb(0 0 0 / 0.7); + --media-base: rgb(0 0 0); + --media-fade: linear-gradient(to top, rgb(0 0 0 / 0.8), rgb(0 0 0 / 0.6)); + --scrim: rgb(0 0 0 / 0.1); +} diff --git a/liquid/styles/rating.css b/liquid/styles/rating.css new file mode 100644 index 000000000..5d2154078 --- /dev/null +++ b/liquid/styles/rating.css @@ -0,0 +1,23 @@ +[data-slot='rating'] { + display: inline-flex; + align-items: center; + gap: var(--spacing); + color: var(--primary); +} +.rating__star { + position: relative; + display: inline-flex; + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + color: color-mix(in oklch, var(--muted-foreground) 25%, transparent); +} +.rating__star svg { + width: 100%; + height: 100%; +} +.rating__fill { + position: absolute; + inset: 0; + overflow: hidden; + color: var(--primary); +} diff --git a/liquid/styles/section.css b/liquid/styles/section.css new file mode 100644 index 000000000..168e4c8d3 --- /dev/null +++ b/liquid/styles/section.css @@ -0,0 +1,77 @@ +[data-slot='section'] { + position: relative; + display: flex; + flex-direction: column; + gap: var(--section-gap); + padding-block: var(--section-padding); + background: var(--background); + color: var(--foreground); + scroll-margin-top: 48px; +} +[data-slot='section'][data-variant='bordered'] { + padding-block: 32px; + border-block: 1px solid var(--border); +} +/* The first section of a template page, below the header. */ +[data-slot='section'][data-variant='page'] { + padding-top: 32px; +} +/* A framed panel inset by the gutter, as Fulldev UI's floating section. */ +[data-slot='section'][data-variant='floating'] { + width: calc(100% - 2 * var(--gutter)); + max-width: calc(var(--container) - 2 * var(--gutter)); + margin: 64px auto; + overflow: hidden; + border: 1px solid var(--border); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-md); +} +/* The container only sets the width and gutters; each section lays out its own children. */ +[data-slot='section-container'], +[data-slot='header-container'] { + position: relative; + width: 100%; + min-width: 0; + max-width: var(--container); + margin-inline: auto; + padding-inline: var(--gutter); +} +[data-slot='header-container'] { + display: flex; + gap: 16px; +} +[data-slot='section-title'] { + font-size: 1.875rem; + line-height: 2.25rem; + text-wrap: balance; +} +[data-slot='section-description'] { + font-size: 1.125rem; + line-height: 1.625; + color: var(--muted-foreground); + text-wrap: balance; +} +/* Display headline of centered heroes and banners. */ +.display-title { + max-width: 56rem; + font-size: 2.25rem; + line-height: 1.1; + text-wrap: balance; +} + +/* The actions of a section intro, wrapping when they do not fit beside each other. */ +.section-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; +} +/* A section title set tight, as in Fulldev UI's call to action and content blocks. */ +[data-slot='section-title'].tight-title { + line-height: 1.1; +} +/* The display headline of split heroes: 36px, 48px from 640px. */ +.hero-title { + font-size: 2.25rem; + line-height: 1.1; + text-wrap: balance; +} diff --git a/liquid/styles/toast.css b/liquid/styles/toast.css new file mode 100644 index 000000000..2efa816d8 --- /dev/null +++ b/liquid/styles/toast.css @@ -0,0 +1,125 @@ +/* Stacked notifications, after Fulldev UI's toaster. The controller measures the stack and writes + the offsets; collapsed toasts behind the front one show only their edge. */ +[data-slot='toast-viewport'] { + --toast-gap: 0.75rem; + --toast-collapsed-peek: 0.75rem; + + position: fixed; + right: 1rem; + bottom: 1rem; + z-index: 1100; + width: min(24rem, calc(100vw - 2rem)); + height: var(--toast-stack-size, 0); + pointer-events: none; + outline: none; +} +[data-slot='toast-item'] { + position: absolute; + inset-inline: 0; + bottom: 0; + z-index: calc(1000 - var(--toast-index, 0)); + border-radius: var(--radius-2xl); + background: var(--popover); + color: var(--popover-foreground); + box-shadow: + 0 0 0 1px color-mix(in oklch, var(--foreground) 10%, transparent), + var(--shadow-lg); + opacity: 0; + transform: translate3d(0, calc(var(--toast-lift, -1) * -100%), 0); + transform-origin: bottom; + pointer-events: auto; + outline: none; + user-select: none; + transition: + transform 300ms cubic-bezier(0.22, 1, 0.36, 1), + opacity 300ms cubic-bezier(0.22, 1, 0.36, 1), + height 300ms cubic-bezier(0.22, 1, 0.36, 1); +} +/* Keeps the pointer on the stack between toasts, so hovering it keeps it expanded. */ +[data-slot='toast-item']::after { + content: ''; + position: absolute; + bottom: 100%; + left: 0; + width: 100%; + height: calc(var(--toast-gap, 0px) + 1px); +} +[data-slot='toast-item']:focus-visible { + box-shadow: var(--focus-ring); +} +[data-slot='toast-item'][data-mounted='true'] { + opacity: 1; + transform: translate3d(0, 0, 0); +} +[data-slot='toast-item'][data-mounted='true'][data-expanded='true'] { + height: var(--toast-initial-height); + transform: translate3d(0, calc(var(--toast-offset, 0px) * var(--toast-lift, -1)), 0); +} +[data-slot='toast-item'][data-mounted='true'][data-expanded='false'][data-front='false'] { + height: var(--toast-front-height); + transform: translate3d(0, calc(var(--toast-collapsed-offset-y, 0px) * var(--toast-lift, -1)), 0) + scale(calc(1 - var(--toast-index, 0) * 0.05)); +} +[data-slot='toast-item'][data-expanded='false'][data-front='false'][data-open] > * { + opacity: 0; +} +[data-slot='toast-item'][data-visible='false'] { + opacity: 0; + pointer-events: none; +} +[data-slot='toast-item'][data-removed='true'] { + opacity: 0; +} +[data-slot='toast-item'][data-removed='true'][data-front='true'] { + transform: translate3d(0, calc(var(--toast-lift, -1) * -100%), 0); +} +[data-slot='toast-item'][data-swiping='true'] { + transition: none; + transform: translate3d(var(--toast-swipe-amount-x, 0), var(--toast-swipe-amount-y, 0), 0); +} +[data-slot='toast-item'][data-removed='true'][data-swipe-out='true'] { + transform: translate3d(var(--toast-swipe-end-x, 0), var(--toast-swipe-end-y, 0), 0); +} +[data-slot='toast-content'] { + display: flex; + align-items: center; + gap: calc(var(--spacing) * 3); + min-height: calc(var(--spacing) * 16); + padding: calc(var(--spacing) * 4); + overflow: hidden; + transition: opacity 200ms; +} +/* The title and description beside the toast's icon. */ +.toast-text { + display: flex; + flex: 1; + flex-direction: column; + gap: var(--spacing); + min-width: 0; +} +[data-slot='toast-title'] { + font-size: 0.875rem; + font-weight: 500; +} +[data-slot='toast-description'] { + color: var(--muted-foreground); + font-size: 0.875rem; +} +[data-slot='toast-description']:empty, +[data-slot='toast-action']:empty { + display: none; +} +[data-slot='toast-close'] { + flex-shrink: 0; + color: var(--muted-foreground); +} +[data-slot='toast-close']:hover { + color: var(--foreground); +} +@media (prefers-reduced-motion: reduce) { + [data-slot='toast-item'], + [data-slot='drawer-popup'], + [data-slot='drawer-backdrop'] { + transition-duration: 0ms; + } +} diff --git a/liquid/styles/tokens.css b/liquid/styles/tokens.css new file mode 100644 index 000000000..073d0fc93 --- /dev/null +++ b/liquid/styles/tokens.css @@ -0,0 +1,118 @@ +/* Generated from the shared neutral palette and Liquid primitives. */ +:root { + color-scheme: light; + + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + --heading-weight: 600; + --container: 1280px; + --section-gap: 64px; + --header-height: 64px; + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); + --shadow-x: 0; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0; + --shadow-opacity: 0.1; + --shadow-color: oklch(0 0 0); + --shadow-2xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0 hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: 0 1px 3px 0 hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0 1px 3px 0 hsl(0 0% 0% / 0.25); + --font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif; + --font-heading: var(--font-sans); + --font-mono: ui-monospace, sfmono-regular, menlo, monaco, consolas, monospace; + --tracking-tight: -0.025em; + --tracking-normal: 0; + --spacing: 0.25rem; + --gutter: calc(var(--spacing) * 4); + --section-padding: calc(var(--spacing) * 16); + --on-media: #fff; + --on-media-muted: rgb(255 255 255 / 0.85); + --media-button: #fff; + --media-button-foreground: #171717; + --media-button-hover: #e5e5e5; + --media-secondary: rgb(255 255 255 / 0.15); + --media-secondary-hover: rgb(255 255 255 / 0.2); + --media-overlay: rgb(0 0 0 / 0.7); + --media-base: rgb(0 0 0); + --media-fade: linear-gradient(to top, rgb(0 0 0 / 0.8), rgb(0 0 0 / 0.6)); + --scrim: rgb(0 0 0 / 0.1); + --focus-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent); +} +.dark { + color-scheme: dark; + + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + --focus-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent); +} diff --git a/liquid/styles/typography.css b/liquid/styles/typography.css new file mode 100644 index 000000000..5de9b9c1a --- /dev/null +++ b/liquid/styles/typography.css @@ -0,0 +1,670 @@ +h1, +h2, +h3, +h4 { + font-family: var(--font-heading); + font-weight: var(--heading-weight); + letter-spacing: var(--tracking-tight); + scroll-margin-top: 80px; +} +h1 { + font-size: 2.25rem; + line-height: 2.5rem; + text-wrap: balance; +} +h2 { + font-size: 1.875rem; + line-height: 2.25rem; +} +h3 { + font-size: 1.5rem; + line-height: 2rem; +} +h4 { + font-size: 1.25rem; + line-height: 1.75rem; +} +[data-size='sm']:is(h1) { + font-size: 1.875rem; + line-height: 2.25rem; +} +[data-size='sm']:is(h2) { + font-size: 1.5rem; + line-height: 2rem; +} +[data-size='sm']:is(h3) { + font-size: 1.25rem; + line-height: 1.75rem; +} +[data-size='sm']:is(h4) { + font-size: 1.125rem; + line-height: 1.75rem; +} +[data-size='lg']:is(h1) { + font-size: 3rem; + line-height: 1; +} +.muted { + color: var(--muted-foreground); +} +.link { + font-weight: 500; + text-decoration: underline; +} +.link:hover { + text-decoration-thickness: 2px; +} + +/* Rich text from the editor or Shopify content: Fulldev UI's typography, shadcn/ui's typeset. + Text is 18px below 768px and 16px from there (sm 14px, lg 18px), headings scale in em, and the + flow sets the space between blocks. Theme tokens set the colors and fonts, images and videos + keep square corners, and a wide table scrolls. [data-not-typeset] keeps a part out. + The typeset class gives merchant rich text inside another part, such as a section or card + description, the same flow, headings, lists and links at that part's size, leading and color. */ +:is([data-slot='typography'], .typeset) { + --typeset-font-heading: var(--font-heading); + --typeset-font-mono: var(--font-mono); + --typeset-flow: 1.25em; + --typeset-heading: currentcolor; + --typeset-muted: currentcolor; + --typeset-rule: var(--border); + + overflow-wrap: break-word; +} +[data-slot='typography'] { + --typeset-font-body: inherit; + --typeset-size: 1rem; + --typeset-leading: 1.75; + --typeset-heading: var(--foreground); + --typeset-muted: var(--muted-foreground); + + font-family: var(--typeset-font-body); + font-size: calc(var(--typeset-size) * 1.125); + line-height: var(--typeset-leading); + color: var(--foreground); +} +@media (min-width: 768px), print { + [data-slot='typography'] { + font-size: var(--typeset-size); + } +} +[data-slot='typography'][data-size='sm'] { + --typeset-size: 0.875rem; + --typeset-leading: 1.7; + --typeset-flow: 1.125em; +} +[data-slot='typography'][data-size='lg'] { + --typeset-size: 1.125rem; + --typeset-leading: 1.8; + --typeset-flow: 1.5em; +} +[data-slot='typography'][data-variant='muted'] { + color: var(--muted-foreground); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(p) { + margin-block: var(--typeset-flow) 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1), +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h2), +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h3), +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h4), +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h5), +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h6) { + color: var(--typeset-heading); + font-family: var(--typeset-font-heading); + font-weight: var(--heading-weight); + letter-spacing: var(--tracking-normal); + margin-block-end: 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1) { + font-size: 1.75em; + line-height: 1.3; + margin-block-start: var(--typeset-flow); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h2) { + font-size: 1.25em; + line-height: 1.4; + margin-block-start: calc(var(--typeset-flow) * 1.4); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h3) { + font-size: 1.125em; + line-height: 1.45; + margin-block-start: var(--typeset-flow); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h4) { + font-size: 1em; + line-height: 1.5; + margin-block-start: var(--typeset-flow); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h5) { + font-size: 0.875em; + line-height: 1.5; + font-weight: 500; + color: var(--typeset-muted); + margin-block-start: calc(var(--typeset-flow) / 0.875); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h6) { + font-size: 0.8125em; + line-height: 1.5; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--typeset-muted); + margin-block-start: calc(var(--typeset-flow) / 0.8125); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where([id]) { + scroll-margin-block-start: var(--typeset-flow); +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1 + *, h2 + *, h3 + *, h4 + *, h5 + *, h6 + *) { + margin-block-start: 1em; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(h1, h2, h3, h4) :is(code)) { + font-size: 0.9em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(a) { + color: inherit; + font-weight: 500; + text-decoration-line: underline; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(a:hover) { + text-decoration-thickness: 2px; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(h1, h2, h3, h4, h5, h6) :is(a)) { + font-weight: inherit; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(strong, b) { + font-weight: 600; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(mark) { + padding: 0.05em 0.15em; + border-radius: 0.2em; + background-color: color-mix(in oklab, var(--primary) 15%, transparent); + color: inherit; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(abbr[title]) { + text-decoration-line: underline; + text-decoration-style: dotted; + text-underline-offset: 0.2em; + cursor: help; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(del, s) { + color: var(--typeset-muted); + text-decoration-line: line-through; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup, sub) { + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup) { + top: -0.5em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sub) { + bottom: -0.25em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(sup a) { + text-decoration-line: none; + font-weight: 500; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul) { + list-style-type: disc; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol) { + list-style-type: decimal; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul ul) { + list-style-type: circle; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul ul ul) { + list-style-type: square; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='a' i]) { + list-style-type: lower-alpha; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='i' i]) { + list-style-type: lower-roman; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='A' s]) { + list-style-type: upper-alpha; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol[type='I' s]) { + list-style-type: upper-roman; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul, ol) { + margin-block: var(--typeset-flow) 0; + padding-inline-start: 1.5em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(li) { + margin-block-start: 0.5em; + padding-inline-start: 0.4em; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > p, li > ul, li > ol) { + margin-block-start: 0.5em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ul > li)::marker, +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(ol > li)::marker { + color: var(--typeset-muted); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(details) { + margin-block: var(--typeset-flow) 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(summary) { + cursor: pointer; + font-weight: 500; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(summary)::marker { + color: var(--typeset-muted); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(kbd) { + padding: 0.0625em 0.35em; + border: 1px solid var(--typeset-rule); + border-block-end-width: 2px; + border-radius: min(calc(var(--radius) * 0.6), 0.35em); + font-family: inherit; + font-size: 0.85em; + font-weight: 500; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dl) { + margin-block: var(--typeset-flow) 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dt) { + margin-block-start: 1em; + font-weight: 500; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dt + dt) { + margin-block-start: 0.25em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(dd) { + margin-block-start: 0.25em; + margin-inline-start: 0; + padding-inline-start: 1em; + color: var(--typeset-muted); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(:not(pre) > code) { + padding: 0.125em 0.3em; + border-radius: min(calc(var(--radius) * 0.6), 0.35em); + background-color: var(--muted); + font-family: var(--typeset-font-mono); + font-size: 0.85em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre) { + margin-block: calc(var(--typeset-flow) / 0.875) 0; + overflow-x: auto; + padding: 0.75em 1em; + border-radius: var(--radius); + background-color: var(--muted); + font-family: var(--typeset-font-mono); + font-size: 0.875em; + line-height: 1.5; + tab-size: 2; + direction: ltr; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre code) { + padding: 0; + border-radius: 0; + background-color: transparent; + font-family: inherit; + font-size: inherit; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(blockquote) { + margin-block: var(--typeset-flow) 0; + margin-inline: 0; + padding-inline-start: 1em; + border-inline-start: 2px solid var(--typeset-rule); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(hr) { + margin-block: calc(var(--typeset-flow) * 2.4) 0; + border: 0; + border-block-start: 1px solid var(--typeset-rule); +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(hr + h1, hr + h2, hr + h3, hr + h4) { + margin-block-start: var(--typeset-flow); +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(img, video) { + max-width: 100%; + height: auto; + margin-block: var(--typeset-flow) 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(iframe) { + max-width: 100%; + margin-block: var(--typeset-flow) 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(p img) { + margin-block-start: 0; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(figure) { + margin-block: var(--typeset-flow) 0; + margin-inline: 0; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(figcaption, caption) { + margin-block-start: calc(0.75em / 0.875); + color: var(--typeset-muted); + font-size: 0.875em; + text-align: center; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(caption) { + caption-side: bottom; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(table) { + display: block; + width: max-content; + max-width: 100%; + margin-block: var(--typeset-flow) 0; + overflow-x: auto; + border-block-end: 1px solid var(--typeset-rule); + border-collapse: separate; + border-spacing: 0; + font-size: 1em; + line-height: 1.5; + font-variant-numeric: tabular-nums; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(thead th) { + padding: 0.65em 1em; + font-weight: 500; + text-align: start; + white-space: nowrap; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(:is(tbody, tfoot) :is(td, th)) { + padding: 0.75em 1em; + border-block-start: 1px solid var(--typeset-rule); + text-align: start; + vertical-align: top; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(tbody th, tfoot th) { + font-weight: 500; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(th:first-child, td:first-child) { + padding-inline-start: 0; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(th[align='center'], td[align='center']) { + text-align: center; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(th[align='right'], td[align='right']) { + text-align: end; +} +:is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > blockquote, li > table, li > figure) { + margin-block-start: 0.5em; +} +:is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(li > pre) { + margin-block-start: calc(0.5em / 0.875); +} +@media print { + :is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(pre, table, blockquote, figure) { + break-inside: avoid; + } + :is([data-slot='typography'], .typeset) :not(:where([data-not-typeset], [data-not-typeset] *)):where(h1, h2, h3, h4) { + break-after: avoid; + } +} +@media (forced-colors: active) { + :is([data-slot='typography'], .typeset) + :not(:where([data-not-typeset], [data-not-typeset] *)):where(:not(pre) > code) { + border: 1px solid; + } +} +:is([data-slot='typography'], .typeset) > :where(:first-child):not(:where([data-not-typeset], [data-not-typeset] *)), +:is([data-slot='typography'], .typeset) + > :where(:first-child) + > :where(:first-child):not(:where([data-not-typeset], [data-not-typeset] *)), +:is([data-slot='typography'], .typeset) + :where( + li > :first-child, + blockquote > :first-child, + td > :first-child, + th > :first-child, + dd > :first-child, + figure > :first-child, + figure > picture > img + ):not(:where([data-not-typeset], [data-not-typeset] *)) { + margin-block-start: 0; +} + +/* Typography parts, as Fulldev UI's typography components: the same roles outside rich text, + each with the sm and lg sizes. Large, small and muted are shadcn/ui's typography roles. */ +[data-slot='typography-h1'], +[data-slot='typography-h2'], +[data-slot='typography-h3'], +[data-slot='typography-h4'] { + font-family: var(--font-heading); + font-weight: var(--heading-weight); + letter-spacing: var(--tracking-tight); + scroll-margin-top: 80px; +} +[data-slot='typography-h1'] { + text-wrap: balance; + font-size: 2.25rem; + line-height: 2.5rem; +} +[data-slot='typography-h1'][data-size='sm'] { + font-size: 1.875rem; + line-height: 2.25rem; +} +[data-slot='typography-h1'][data-size='lg'] { + font-size: 3rem; + line-height: 1; +} +[data-slot='typography-h2'] { + font-size: 1.875rem; + line-height: 2.25rem; +} +[data-slot='typography-h2'][data-size='sm'] { + font-size: 1.5rem; + line-height: 2rem; +} +[data-slot='typography-h2'][data-size='lg'] { + font-size: 2.25rem; + line-height: 2.5rem; +} +[data-slot='typography-h3'] { + font-size: 1.5rem; + line-height: 2rem; +} +[data-slot='typography-h3'][data-size='sm'] { + font-size: 1.25rem; + line-height: 1.75rem; +} +[data-slot='typography-h3'][data-size='lg'] { + font-size: 1.875rem; + line-height: 2.25rem; +} +[data-slot='typography-h4'] { + font-size: 1.25rem; + line-height: 1.75rem; +} +[data-slot='typography-h4'][data-size='sm'] { + font-size: 1.125rem; + line-height: 1.75rem; +} +[data-slot='typography-h4'][data-size='lg'] { + font-size: 1.5rem; + line-height: 2rem; +} +[data-slot='typography-p'], +[data-slot='typography-blockquote'], +[data-slot='typography-list'] { + font-size: 1rem; + line-height: 1.75rem; +} +[data-slot='typography-p']:not(:first-child) { + margin-top: 1.5rem; +} +[data-slot='typography-lead'] { + font-size: 1.125rem; + line-height: 1.75rem; + color: var(--muted-foreground); +} +[data-slot='typography-lead'][data-size='sm'] { + font-size: 1rem; + line-height: 1.5rem; +} +[data-slot='typography-lead'][data-size='lg'] { + font-size: 1.25rem; + line-height: 1.75rem; +} +[data-slot='typography-large'] { + font-size: 1.125rem; + font-weight: 600; + line-height: 1.75rem; +} +[data-slot='typography-small'] { + font-size: 0.875rem; + font-weight: 500; + line-height: 1; +} +[data-slot='typography-muted'] { + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--muted-foreground); +} +[data-slot='typography-blockquote'] { + margin-top: 1.5rem; + padding-inline-start: 1.5rem; + border-inline-start: 2px solid var(--border); + font-style: italic; +} +[data-slot='typography-list'] { + margin-block: 1.5rem; + padding-inline-start: 1.5rem; +} +[data-slot='typography-list'] > li + li { + margin-top: 0.5rem; +} +[data-slot='typography-p'][data-size='sm'] { + font-size: 0.875rem; + line-height: 1.5rem; +} +[data-slot='typography-p'][data-size='lg'] { + font-size: 1.125rem; + line-height: 1.875rem; +} +[data-slot='typography-blockquote'][data-size='sm'] { + font-size: 0.875rem; + line-height: 1.5rem; +} +[data-slot='typography-blockquote'][data-size='lg'] { + font-size: 1.125rem; + line-height: 1.875rem; +} +[data-slot='typography-list'][data-size='sm'] { + font-size: 0.875rem; + line-height: 1.5rem; +} +[data-slot='typography-list'][data-size='lg'] { + font-size: 1.125rem; + line-height: 1.875rem; +} +[data-slot='typography-inline-code'] { + padding: 0.2rem 0.3rem; + border-radius: var(--radius-sm); + background: var(--muted); + font-family: var(--font-mono); + font-size: 0.875rem; + font-weight: 600; +} +[data-slot='typography-inline-code'][data-size='sm'] { + font-size: 0.75rem; +} +[data-slot='typography-inline-code'][data-size='lg'] { + font-size: 1rem; +} +[data-slot='typography-a'] { + color: var(--primary); + font-size: 1rem; + font-weight: 500; + line-height: 1.5rem; + text-decoration: underline; + text-underline-offset: 4px; +} +[data-slot='typography-a'][data-size='sm'] { + font-size: 0.875rem; + line-height: 1.25rem; +} +[data-slot='typography-a'][data-size='lg'] { + font-size: 1.125rem; + line-height: 1.75rem; +} +[data-slot='typography-a']:hover { + text-decoration-thickness: 2px; +} + +/* A table in running text, as Fulldev UI's typography table: framed, with striped rows. */ +[data-slot='typography-table-container'] { + width: 100%; + margin-block: 1.5rem; + overflow-x: auto; + border: 1px solid var(--border); + border-radius: var(--radius-xl); +} +[data-slot='typography-table'] { + width: 100%; + border-collapse: collapse; + font-size: 1rem; + line-height: 1.5rem; +} +[data-slot='typography-table'][data-size='sm'] { + font-size: 0.875rem; + line-height: 1.25rem; +} +[data-slot='typography-table'][data-size='lg'] { + font-size: 1.125rem; + line-height: 1.75rem; +} +[data-slot='typography-table-row']:nth-child(even) { + background: var(--muted); +} +[data-slot='typography-table-head'], +[data-slot='typography-table-cell'] { + padding: 0.75rem 1rem; + text-align: start; +} +[data-slot='typography-table-head'] { + border-bottom: 1px solid var(--border); + font-weight: 600; +} +[data-slot='typography-table-cell'] { + border-top: 1px solid var(--border); +} +:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='left'] { + text-align: left; +} +:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='center'] { + text-align: center; +} +:is([data-slot='typography-table-head'], [data-slot='typography-table-cell'])[align='right'] { + text-align: right; +} + +.check-list { + display: flex; + flex-direction: column; + gap: 12px; + margin: 0; + padding: 0; + list-style: none; + font-size: 0.875rem; +} +.check-list > li { + display: flex; + align-items: flex-start; + gap: 12px; +} +.check-list .icon { + width: 16px; + height: 16px; + margin-top: 2px; + color: var(--primary); +} diff --git a/liquid/styles/video.css b/liquid/styles/video.css new file mode 100644 index 000000000..c8094340c --- /dev/null +++ b/liquid/styles/video.css @@ -0,0 +1,10 @@ +/* The playing media of a video: a Shopify video, an embed, or the click-to-load cover that becomes + one. It fills its aspect-ratio frame, which carries Fulldev UI's border and radius. */ +[data-slot='video'] { + display: block; + width: 100%; + height: 100%; + border: 0; + background: var(--muted); + object-fit: cover; +} diff --git a/liquid/vendor.json b/liquid/vendor.json new file mode 100644 index 000000000..4a83c823d --- /dev/null +++ b/liquid/vendor.json @@ -0,0 +1,180 @@ +{ + "files": [ + { + "path": "assets/data-slot-core.js", + "package": "@data-slot/core", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "d2879cc3a2f05a7867763b937bad3bd4a369f54670989ce535367cb690fb66e6" + }, + { + "path": "assets/data-slot-dialog.js", + "package": "@data-slot/dialog", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "a5201f452700d04145b5156cc46880d2b5bdf7521b03ff9521c3f7693ee1b5d5" + }, + { + "path": "assets/data-slot-command.js", + "package": "@data-slot/command", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "f7944120617676b14dfda2ea3f3e8a15767e0766d5312b4ed9519affe9b7444a" + }, + { + "path": "assets/data-slot-navigation-menu.js", + "package": "@data-slot/navigation-menu", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "f9b42a9bceca8e1103718d43aa5f0d141ab63d38278cb121e790a2c80e827b68" + }, + { + "path": "assets/data-slot-accordion.js", + "package": "@data-slot/accordion", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "fe6b0ac2c87a2e31582e5e88c509e0b508f41d0065d0a420407999ad210e6a27" + }, + { + "path": "assets/data-slot-tabs.js", + "package": "@data-slot/tabs", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "1f187134bba755cfda9d35cf9f16174c1a183862d32d100f270f4177033058c7" + }, + { + "path": "assets/data-slot-combobox.js", + "package": "@data-slot/combobox", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "b5f2ed9d65a43d836c59c0d4dc33c16ab12ef9fa05d510078b5d62517d854db6" + }, + { + "path": "assets/data-slot-carousel.js", + "package": "@data-slot/carousel", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "d83ef938d3b62dc3652b123428ca2261e03fb2ba1133992b0f5545a974238d03" + }, + { + "path": "assets/data-slot-alert-dialog.js", + "package": "@data-slot/alert-dialog", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "1caee9a86bb2a816ea32bc1a48b82f3cc588ec3c563ebc443f836ab1dae2f618" + }, + { + "path": "assets/data-slot-collapsible.js", + "package": "@data-slot/collapsible", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "c42f76e3dfeff4570eb2f5104434fbffd3ef55710833e888965c590ce59f4f9f" + }, + { + "path": "assets/data-slot-drawer.js", + "package": "@data-slot/drawer", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "c9343b26ea27193cc1d2da3e41bf452005bc2e534151fa1a762b2b6535de28d0" + }, + { + "path": "assets/data-slot-dropdown-menu.js", + "package": "@data-slot/dropdown-menu", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "543c5717c9ba8363bbab4c0a11005980d75d4baaed79cf7123c7bcfe3ec8720b" + }, + { + "path": "assets/data-slot-hover-card.js", + "package": "@data-slot/hover-card", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "86e1bfa5d4822f180f3501ca7cc51bf044d4e917a7ebdb7a10b70006dba6467f" + }, + { + "path": "assets/data-slot-popover.js", + "package": "@data-slot/popover", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "526e30d663381329a7a6394eae886224024034d0abf96f888c932bb4b8c1b1cb" + }, + { + "path": "assets/data-slot-radio-group.js", + "package": "@data-slot/radio-group", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "7a9b90166de80d79ee42aa2df73e4d8179e9d291cb30b3095c7d8332375b0499" + }, + { + "path": "assets/data-slot-select.js", + "package": "@data-slot/select", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "13d1a3fbe90f4b3b830d987b6a28267897faa3589bf00e64edca4938acbf406b" + }, + { + "path": "assets/data-slot-slider.js", + "package": "@data-slot/slider", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "0d10131aa95450aa1269234103ead8d805d6abdf9e0683510f4fcf58ab0f1efa" + }, + { + "path": "assets/data-slot-switch.js", + "package": "@data-slot/switch", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "0d5b09a89429880f61aeb540be5f87383ba5c6beb261ad468e9a6cf18e45b6a0" + }, + { + "path": "assets/data-slot-toast.js", + "package": "@data-slot/toast", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "acfd86824d0ff37fbd62d0c48672e527d7391412780730e9a3a8d61268b3aaf2" + }, + { + "path": "assets/data-slot-toggle.js", + "package": "@data-slot/toggle", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "7a651d578cb83be842c8e59fe1a2cd124594b1fd6c942faf0a33cf9e57326947" + }, + { + "path": "assets/data-slot-toggle-group.js", + "package": "@data-slot/toggle-group", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "c1f2fc9712974e63e4e44c97ab2a1ec8bc3c3cb73c1afb4578b085fed11977f1" + }, + { + "path": "assets/data-slot-tooltip.js", + "package": "@data-slot/tooltip", + "version": "1.1.1", + "source": "dist/index.js", + "license": "LICENSE-DATA-SLOT.md", + "sha256": "5a9e51a54669153620eb3512e74487eac12ababe3331aff7dfed6c717b8b93c0" + } + ] +} diff --git a/liquid/vendor/data-slot-accordion.js b/liquid/vendor/data-slot-accordion.js new file mode 100644 index 000000000..63ff2c74a --- /dev/null +++ b/liquid/vendor/data-slot-accordion.js @@ -0,0 +1 @@ +import{clearRootBinding as e,createPresenceLifecycle as t,emit as n,ensureId as r,getDataBool as i,getDataEnum as a,getDataString as o,getOwnedElements as s,getParts as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as ee}from"@data-slot/core";const te=[`horizontal`,`vertical`],h=new Set([`all`,`height`,`width`,`block-size`,`inline-size`]),ne=new Set([`Enter`,` `]),g=e=>!!e&&(e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`),_=(e,t,n)=>{n?e.setAttribute(t,``):e.removeAttribute(t)},v=(e,t)=>{e.setAttribute(`data-state`,t?`open`:`closed`),t?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`))},y=e=>{let t=e.trim();return t?t.endsWith(`ms`)?Number.parseFloat(t.slice(0,-2))||0:t.endsWith(`s`)?(Number.parseFloat(t.slice(0,-1))||0)*1e3:Number.parseFloat(t)||0:0},b=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=b(e.transitionDuration,e.transitionDelay),n=b(e.animationDuration,e.animationDelay);return Math.max(t,n)},ie=e=>b(e.animationDuration,e.animationDelay)<=0?!1:e.animationName.split(`,`).map(e=>e.trim()).some(e=>e!==``&&e!==`none`),x=(e,t)=>{let n=e.transitionProperty.split(`,`).map(e=>e.trim()),r=e.transitionDuration.split(`,`),i=e.transitionDelay.split(`,`),a=Math.max(n.length,r.length,i.length),o=0;for(let e=0;ex(e,e=>h.has(e)),ae=e=>{if(e===void 0)return;let t=e.trim();if(!t.startsWith(`[`)||!t.endsWith(`]`))return e;try{let n=JSON.parse(t);return Array.isArray(n)?n.filter(e=>typeof e==`string`):e}catch{return e}},C=`@data-slot/accordion`;function w(l,u={}){let y=p(l,C,`[@data-slot/accordion] createAccordion() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=l,x=c(l,`accordion-item`);if(x.length===0)throw Error(`Accordion requires at least one accordion-item`);let w=l.ownerDocument?.defaultView??window,T=u.multiple??i(l,`multiple`)??!1,oe=u.onValueChange,E=u.disabled??i(l,`disabled`)??g(l),D=u.orientation??a(l,`orientation`,te)??`vertical`,O=u.loopFocus??i(l,`loopFocus`)??!0,k=u.hiddenUntilFound??i(l,`hiddenUntilFound`)??!1,se=u.collapsible??i(l,`collapsible`)??!0,A=[],j=[],M=(e,t,n)=>{e.content.style.setProperty(`--accordion-panel-height`,t),e.content.style.setProperty(`--accordion-panel-width`,n),e.content.style.setProperty(`--radix-accordion-content-height`,t),e.content.style.setProperty(`--radix-accordion-content-width`,n)},N=(e,t,n)=>{M(e,`${t}px`,`${n}px`)},P=e=>{M(e,`auto`,`auto`)},F=e=>{N(e,0,0)},I=e=>{N(e,e.content.scrollHeight,e.content.scrollWidth)},L=e=>{let t=e.content.style.getPropertyValue(`--accordion-panel-height`).trim(),n=e.content.style.getPropertyValue(`--accordion-panel-width`).trim();return t===`auto`&&n===`auto`},R=e=>{e.suppressClick=!1,e.suppressClickTimeoutId!==null&&(w.clearTimeout(e.suppressClickTimeoutId),e.suppressClickTimeoutId=null)},z=e=>{e.openSettleRafId!==null&&(w.cancelAnimationFrame(e.openSettleRafId),e.openSettleRafId=null),e.openSettleTimeoutId!==null&&(w.clearTimeout(e.openSettleTimeoutId),e.openSettleTimeoutId=null),e.openSettleCleanups.forEach(e=>e()),e.openSettleCleanups=[]},B=e=>{e.closeZeroRafId!==null&&(w.cancelAnimationFrame(e.closeZeroRafId),e.closeZeroRafId=null)},V=e=>{z(e),B(e)},H=e=>S(e)>0?`css-transition`:ie(e)?`css-animation`:`none`,U=(e,t)=>{let n=e.content.style.getPropertyValue(`animation-name`);e.content.style.setProperty(`animation-name`,`none`);try{P(e),I(e),t?.()}finally{n?e.content.style.setProperty(`animation-name`,n):e.content.style.removeProperty(`animation-name`)}},ce=e=>{e.idleAnimationSuppressed||(e.idleAnimationName=e.content.style.getPropertyValue(`animation-name`)||null,e.idleAnimationSuppressed=!0,e.content.style.setProperty(`animation-name`,`none`))},le=e=>{e.idleAnimationSuppressed&&=(e.idleAnimationName?e.content.style.setProperty(`animation-name`,e.idleAnimationName):e.content.style.removeProperty(`animation-name`),e.idleAnimationName=null,!1)},W=e=>{e.content.removeAttribute(`hidden`)},G=e=>{k?e.content.setAttribute(`hidden`,`until-found`):e.content.hidden=!0,F(e)},K=new Set,q=(e,t)=>{z(e),!(!K.has(e.value)||e.presence.isExiting)&&(P(e),t===`css-animation`&&ce(e))},J=(e,t,n)=>{z(e);let r=re(n),i=S(n),a=i||r;if(a>0){let n=typeof w.performance?.now==`function`?w.performance.now():Date.now(),r=e=>(typeof w.performance?.now==`function`?w.performance.now():Date.now())-n>=Math.max(0,e-5),o=n=>{if(n.target!==e.content)return;let o=`propertyName`in n?String(n.propertyName):``;if(i>0){if(!h.has(o)||!r(i))return}else if(!r(a))return;q(e,t)},s=n=>{n.target===e.content&&(i>0||r(a)&&q(e,t))};e.content.addEventListener(`transitionend`,o),e.content.addEventListener(`animationend`,s),e.openSettleCleanups.push(()=>e.content.removeEventListener(`transitionend`,o)),e.openSettleCleanups.push(()=>e.content.removeEventListener(`animationend`,s)),e.openSettleTimeoutId=w.setTimeout(()=>{e.openSettleTimeoutId=null,q(e,t)},Math.ceil(a)+50);return}e.openSettleRafId=w.requestAnimationFrame(()=>{e.openSettleRafId=null,q(e,t)})},ue=e=>{B(e),e.closeZeroRafId=w.requestAnimationFrame(()=>{e.closeZeroRafId=null,!K.has(e.value)&&e.presence.isExiting&&F(e)})},de=e=>{e.el.setAttribute(`data-index`,String(e.index)),e.content.setAttribute(`data-index`,String(e.index)),e.content.setAttribute(`data-orientation`,D),_(e.el,`data-disabled`,e.disabled),_(e.trigger,`data-disabled`,e.disabled),_(e.content,`data-disabled`,e.disabled),e.disabled?(e.trigger.setAttribute(`aria-disabled`,`true`),e.trigger instanceof HTMLButtonElement&&(e.trigger.disabled=!0)):(e.trigger.removeAttribute(`aria-disabled`),e.trigger instanceof HTMLButtonElement&&(e.trigger.disabled=!1))},Y=(e,t)=>{e.trigger.setAttribute(`data-state`,t?`open`:`closed`),_(e.trigger,`data-panel-open`,t)},fe=e=>{let t=K.has(e.value);if(de(e),m(e.trigger,`expanded`,t),v(e.el,t),v(e.content,t),Y(e,t),e.content.removeAttribute(`data-starting-style`),e.content.removeAttribute(`data-ending-style`),t){W(e);let t=getComputedStyle(e.content),n=H(t);n===`css-animation`?U(e):I(e),J(e,n,t)}else G(e)},pe=(e,t)=>{let n=K.has(e.value),r=e.trigger.getAttribute(`aria-expanded`)===`true`;if(!(n===r&&(n?L(e):!e.presence.isExiting))){if(m(e.trigger,`expanded`,n),v(e.el,n),v(e.content,n),Y(e,n),n){B(e),W(e);let t=getComputedStyle(e.content),n=H(t);n===`css-animation`?U(e,()=>{r||e.presence.enter()}):(I(e),r||e.presence.enter()),J(e,n,t);return}if(r){z(e),H(getComputedStyle(e.content))===`css-animation`?U(e,()=>e.presence.exit()):(N(e,...t),e.presence.exit(),ue(e));return}V(e),e.content.removeAttribute(`data-starting-style`),e.content.removeAttribute(`data-ending-style`),G(e)}},me=e=>{let t=[],n=new Set;for(let r of e)if(!(n.has(r)||!x.some(e=>e.dataset.value===r))&&(n.add(r),t.push(r),!T&&t.length===1))break;return t},he=e=>e.size===K.size?[...e].some(e=>!K.has(e)):!0,ge=()=>{let e=[...K];n(l,`accordion:change`,{value:e}),oe?.(e)},X=e=>{let t=me(e);if(!T&&!se&&t.length===0&&K.size>0)return!1;let n=new Set(t);if(!he(n))return!1;let r=j.filter(e=>K.has(e.value)!==n.has(e.value)),i=new Map;for(let e of r)K.has(e.value)&&i.set(e,[e.content.scrollHeight,e.content.scrollWidth]);return r.forEach(le),K=n,j.forEach(e=>pe(e,i.get(e))),ge(),!0},_e=e=>(e.getAttribute(`dir`)??b.getAttribute(`dir`))===`rtl`||(getComputedStyle(e).direction||getComputedStyle(b).direction||l.ownerDocument?.documentElement.getAttribute(`dir`)||``)===`rtl`?`rtl`:`ltr`;b.setAttribute(`data-orientation`,D),_(b,`data-disabled`,!!E),x.forEach((e,n)=>{let i=e.dataset.value;if(!i)return;let a=s(l,e,`[data-slot="accordion-trigger"]`)[0]??null,o=s(l,e,`[data-slot="accordion-content"]`)[0]??null;if(!a||!o)return;let c=r(o,`accordion-content`),u=r(a,`accordion-trigger`);a.setAttribute(`aria-controls`,c),o.setAttribute(`aria-labelledby`,u),o.setAttribute(`role`,`region`);let f=!!E||g(e)||g(a),p;p={el:e,value:i,index:n,disabled:f,trigger:a,content:o,presence:t({element:o,onExitComplete:()=>{B(p),G(p)}}),sizeObserver:null,idleAnimationName:null,idleAnimationSuppressed:!1,openSettleRafId:null,openSettleTimeoutId:null,closeZeroRafId:null,openSettleCleanups:[],suppressClick:!1,suppressClickTimeoutId:null},typeof ResizeObserver<`u`&&(p.sizeObserver=new ResizeObserver(()=>{!K.has(p.value)||p.presence.isExiting||L(p)||I(p)}),p.sizeObserver.observe(o)),A.push(d(a,`click`,()=>{if(p.suppressClick){R(p);return}p.disabled||(K.has(p.value)?X([...K].filter(e=>e!==p.value)):X(T?[...K,p.value]:[p.value]))})),A.push(d(a,`keydown`,e=>{if(ne.has(e.key)){if(p.disabled){e.preventDefault();return}e.preventDefault(),R(p),p.suppressClick=!0,p.suppressClickTimeoutId=w.setTimeout(()=>{p.suppressClick=!1,p.suppressClickTimeoutId=null},0),K.has(p.value)?X([...K].filter(e=>e!==p.value)):X(T?[...K,p.value]:[p.value])}})),k&&A.push(d(o,`beforematch`,()=>{X(T?[...K,p.value]:[p.value])})),j.push(p)});let Z=new Set(j.map(e=>e.value)),Q=u.defaultValue??ae(o(l,`defaultValue`)),ve=me((Q?Array.isArray(Q)?Q:[Q]:[]).filter(e=>Z.has(e)));K=new Set(ve),j.forEach(fe),A.push(d(b,`keydown`,e=>{let t=e.target;if(!t)return;let n=j.find(e=>e.trigger===t);if(!n)return;let r=j.filter(e=>!e.disabled),i=r.findIndex(e=>e.trigger===t);if(i===-1)return;let a=r.length-1,o=-1,s=()=>{o=O?i+1>a?0:i+1:Math.min(i+1,a)},c=()=>{o=O?i===0?a:i-1:Math.max(i-1,0)};switch(e.key){case`ArrowDown`:D===`vertical`&&s();break;case`ArrowUp`:D===`vertical`&&c();break;case`ArrowRight`:D===`horizontal`&&(_e(n.trigger)===`rtl`?c():s());break;case`ArrowLeft`:D===`horizontal`&&(_e(n.trigger)===`rtl`?s():c());break;case`Home`:o=0;break;case`End`:o=a;break;default:return}o<0||(e.preventDefault(),r[o]?.trigger.focus())})),A.push(f(l,`accordion:set`,e=>{let t=e.detail?.value;t!==void 0&&X(Array.isArray(t)?t:[t])}));let $={expand:e=>{!Z.has(e)||K.has(e)||X(T?[...K,e]:[e])},collapse:e=>{!Z.has(e)||!K.has(e)||X([...K].filter(t=>t!==e))},toggle:e=>{Z.has(e)&&(K.has(e)?$.collapse(e):$.expand(e))},get value(){return[...K]},destroy:()=>{j.forEach(e=>{R(e),e.presence.cleanup(),V(e),e.sizeObserver?.disconnect(),e.sizeObserver=null}),A.forEach(e=>e()),A.length=0,e(l,C,$)}};return ee(l,C,$),$}function T(e=document){let t=[];for(let n of l(e,`accordion`))u(n,C)||t.push(w(n));return t}export{T as create,w as createAccordion}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-alert-dialog.js b/liquid/vendor/data-slot-alert-dialog.js new file mode 100644 index 000000000..bbd9304ff --- /dev/null +++ b/liquid/vendor/data-slot-alert-dialog.js @@ -0,0 +1 @@ +import{clearRootBinding as e,createDismissLayer as t,createModalStackItem as n,createPortalLifecycle as r,createPresenceLifecycle as i,createTerminalLifecycle as a,emit as ee,ensureId as te,focusElement as o,getAutofocusOrFirstFocusable as s,getDataBool as c,getPart as l,getRoots as u,getTabbables as ne,hasRootBinding as d,linkLabelledBy as f,lockScroll as p,on as m,onRoot as h,registerModalTerminalResources as re,reuseRootBinding as ie,setAria as g,setRootBinding as _,unlockScroll as v}from"@data-slot/core";const y=`@data-slot/alert-dialog`;function b(u,d={}){let b=ie(u,y,`[@data-slot/alert-dialog] createAlertDialog() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(b)return b;let x=d.defaultOpen??c(u,`defaultOpen`)??!1,S=d.onOpenChange,C=d.closeOnClickOutside??c(u,`closeOnClickOutside`)??!1,w=d.closeOnEscape??c(u,`closeOnEscape`)??!0,T=d.lockScroll??c(u,`lockScroll`)??!0,E=l(u,`alert-dialog-trigger`),D=l(u,`alert-dialog-portal`),O=l(u,`alert-dialog-overlay`),k=l(u,`alert-dialog-content`),A=l(u,`alert-dialog-title`),ae=l(u,`alert-dialog-description`);if(!k)throw Error(`Alert dialog requires alert-dialog-content slot`);if(!O)throw Error(`Alert dialog requires alert-dialog-overlay slot`);let j=!1,M=a(),N=null,P=[],F=D?r({content:D,root:u}):null,I=!1,L=()=>{M.trackFinalRaf(()=>{N&&(document.contains(N)?o(N):E&&document.contains(E)&&o(E)),N=null})};te(k,`alert-dialog-content`),k.setAttribute(`role`,`alertdialog`),g(k,`modal`,!0),f(k,A,ae),O.setAttribute(`role`,`presentation`),O.setAttribute(`aria-hidden`,`true`),O.tabIndex=-1,E&&(E.setAttribute(`aria-haspopup`,`dialog`),E.setAttribute(`aria-controls`,k.id),g(E,`expanded`,!1));let R=!1,z=()=>{k.hasAttribute(`tabindex`)||(k.tabIndex=-1,R=!0)},B=()=>{R&&=(k.removeAttribute(`tabindex`),!1)},V=()=>{let e=s(k);if(e)return e.focus();z(),k.focus()},H=()=>{M.trackRaf(()=>{N&&document.contains(N)&&typeof N.focus==`function`?o(N):E&&document.contains(E)&&o(E),N=null})},U=e=>{if(u.setAttribute(`data-state`,e),D?.setAttribute(`data-state`,e),O.setAttribute(`data-state`,e),k.setAttribute(`data-state`,e),e===`open`){u.setAttribute(`data-open`,``),D?.setAttribute(`data-open`,``),O.setAttribute(`data-open`,``),k.setAttribute(`data-open`,``),u.removeAttribute(`data-closed`),D?.removeAttribute(`data-closed`),O.removeAttribute(`data-closed`),k.removeAttribute(`data-closed`);return}u.setAttribute(`data-closed`,``),D?.setAttribute(`data-closed`,``),O.setAttribute(`data-closed`,``),k.setAttribute(`data-closed`,``),u.removeAttribute(`data-open`),D?.removeAttribute(`data-open`),O.removeAttribute(`data-open`),k.removeAttribute(`data-open`)},W=0,G=0,K=0,q=0,J=(e,t)=>{M.isDestroyed||j||t!==W||(e.hidden=!0,G=Math.max(0,G-1),G===0&&(B(),H()))},Y=i({element:O,onExitComplete:()=>J(O,K)}),X=i({element:k,onExitComplete:()=>J(k,q)}),Z=n({content:k,overlay:O,onTabKeydown:e=>{if(e.key!==`Tab`)return;let t=ne(k);if(t.length===0){e.preventDefault(),z(),k.focus();return}let n=t[0],r=t[t.length-1],i=k.ownerDocument.activeElement;if(!t.includes(i)){e.preventDefault(),(e.shiftKey?r:n).focus();return}if(n===r){e.preventDefault();return}e.shiftKey?i===n&&(e.preventDefault(),r.focus()):i===r&&(e.preventDefault(),n.focus())},cssVarPrefix:`alert-dialog`}),Q=(e,t=!1)=>{M.isDestroyed||j===e&&!t||(e?(W+=1,G=0,F?.mount(),N=document.activeElement,Z.open(),T&&!I&&(p(),I=!0)):(W+=1,G=2,K=W,q=W,Z.close(),I&&=(v(),!1)),j=e,E&&g(E,`expanded`,j),e?(O.hidden=!1,k.hidden=!1,U(`open`),Y.enter(),X.enter()):(U(`closed`),Y.exit(),X.exit()),ee(u,`alert-dialog:change`,{open:j}),S?.(j),e&&M.trackRaf(()=>V()))};k.hidden=!0,O.hidden=!0,U(`closed`),E&&P.push(m(E,`click`,()=>Q(!j))),P.push(m(k,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest?.(`[data-slot="alert-dialog-cancel"]`);n&&k.contains(n)&&Q(!1)})),C&&P.push(m(O,`click`,e=>{e.button===0&&e.target===O&&j&&Q(!1)})),P.push(t({root:u,isOpen:()=>j,onDismiss:()=>Q(!1),closeOnClickOutside:!1,closeOnEscape:w}));let $={open:()=>{M.isDestroyed||Q(!0)},close:()=>{M.isDestroyed||Q(!1)},toggle:()=>{M.isDestroyed||Q(!j)},get isOpen(){return j},destroy:()=>{M.destroy()}};return re(M,{cleanups:P,modalStack:Z,presence:[Y,X],portal:F,beforeDestroy:L,reset:()=>{W+=1,G=0,j=!1,U(`closed`),O.hidden=!0,k.hidden=!0,E&&g(E,`expanded`,!1)},releaseScrollLock:()=>{I&&=(v(),!1)},cleanup:B,unbind:()=>e(u,y,$)}),P.push(h(u,`alert-dialog:set`,e=>{let t=e.detail;typeof t?.open==`boolean`&&Q(t.open)})),_(u,y,$),x&&Q(!0),$}function x(e=document){let t=[];for(let n of u(e,`alert-dialog`))d(n,y)||t.push(b(n));return t}export{x as create,b as createAlertDialog}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-carousel.js b/liquid/vendor/data-slot-carousel.js new file mode 100644 index 000000000..c466da820 --- /dev/null +++ b/liquid/vendor/data-slot-carousel.js @@ -0,0 +1 @@ +import{clearRootBinding as e,createSwipeGesture as t,drainCleanups as n,emit as r,ensureId as i,getDataBool as a,getDataEnum as o,getDataNumber as s,getPart as ee,getParts as c,getRoots as l,hasRootBinding as u,on as d,reuseRootBinding as te,setAria as f,setRootBinding as ne}from"@data-slot/core";const re=[`single`,`multiple`],ie=[`horizontal`,`vertical`],ae={horizontal:{scroll:`scrollLeft`,edge:`left`,extent:`scrollWidth`,viewport:`clientWidth`,prevKey:`ArrowLeft`,nextKey:`ArrowRight`,touchAction:`pan-y`,swipe:`x`},vertical:{scroll:`scrollTop`,edge:`top`,extent:`scrollHeight`,viewport:`clientHeight`,prevKey:`ArrowUp`,nextKey:`ArrowDown`,touchAction:`pan-x`,swipe:`y`}},oe=`Carousel requires carousel-content and at least one carousel-item`,p=`@data-slot/carousel`,m=(e,t,n)=>{if(t<=0)return 0;let r=Number.isFinite(e)?Math.trunc(e):0;return n?(r%t+t)%t:Math.min(t-1,Math.max(0,r))},se=e=>e instanceof Element&&e.closest(`input, textarea, select, [contenteditable]:not([contenteditable="false"])`)!==null,h=(e,t)=>{`disabled`in e&&(e.disabled=t),f(e,`disabled`,t)};function g(l,u={}){let g=te(l,p,`[@data-slot/carousel] createCarousel() was called on a root that is already bound. Returning the existing controller.`);if(g)return g;let _=ee(l,`carousel-content`);if(!_)throw Error(oe);let ce=()=>Array.from(_.children).filter(e=>e instanceof HTMLElement&&e.getAttribute(`data-slot`)===`carousel-item`),v=ce();if(v.length===0)throw Error(oe);let y=u.orientation??o(l,`orientation`,ie)??`horizontal`,b=u.loop??a(l,`loop`)??!1,x=u.slides??o(l,`slides`,re)??`single`,S=u.snap??(l.getAttribute(`data-snap`)===`none`?!1:void 0),le=u.drag??a(l,`drag`)??!1,C=u.defaultIndex??s(l,`defaultIndex`)??0,ue=u.onIndexChange,w=ae[y],T=c(l,`carousel-previous`),E=c(l,`carousel-next`),D=[],O=l.ownerDocument?.defaultView??window,de=O.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1?`auto`:`smooth`,k=m(C,v.length,b),A=[],j=[],M,N=!1,P,F=()=>{P&&=(_.style.scrollSnapType=P.style,void 0)},fe=_.style.scrollSnapType;S!==void 0&&(_.style.scrollSnapType=S?`${w.swipe} mandatory`:`none`);let I=()=>P?.enabled??S??O.getComputedStyle(_).scrollSnapType!==`none`,pe=()=>{P??={style:_.style.scrollSnapType,enabled:I()},_.style.scrollSnapType=`none`},L=null,R=null,me=e=>e.getBoundingClientRect()[w.edge]-_.getBoundingClientRect()[w.edge]+_[w.scroll],z=e=>{let t=0,n=1/0;for(let r=0;rA.length<=1?!1:b||(I()?k>0:_[w.scroll]>(A[0]??0)+1),V=()=>A.length<=1?!1:b||(I()?k{l.setAttribute(`role`,`region`),l.setAttribute(`aria-roledescription`,`carousel`),l.setAttribute(`data-orientation`,y);for(let e=0;e{let e=!B(),t=!V();for(let t of T)h(t,e);for(let e of E)h(e,t)},W=()=>{let e=_.getBoundingClientRect(),t=e.left+_.clientLeft,n=e.top+_.clientTop,r=t+_.clientWidth,i=n+_.clientHeight;for(let e=0;et&&o.leftn&&o.top{_.getAttribute(`tabindex`)===`-1`&&_.removeAttribute(`tabindex`)})),_.focus({preventScroll:!0})),a.setAttribute(`data-state`,s?`active`:`inactive`),x===`multiple`&&s?a.removeAttribute(`aria-hidden`):f(a,`hidden`,!s),a.toggleAttribute(`inert`,!s)}},he=()=>{for(let e of c(l,`carousel-indicators`)){for(;e.children.length>A.length;)e.lastElementChild?.remove();for(;e.children.lengthe.setAttribute(`data-index`,String(t)))}for(let e of c(l,`carousel-indicator`)){let t=s(e,`index`),n=t!==void 0&&Number.isInteger(t)&&t>=0&&t{l.setAttribute(`data-index`,String(k)),W(),U(),he(),e&&(r(l,`carousel:change`,{index:k}),ue?.(k))},K=(e,t=`auto`)=>{let n={behavior:t};n[w.edge]=A[e]??0,_.scrollTo(n)},q=e=>{let t=e!==k;k=e,G(t)},J=()=>{let e=Math.max(0,_[w.extent]-_[w.viewport]);A=[],j=[];for(let t of v){let n=Math.min(e,Math.max(0,me(t)));A.some(e=>Math.abs(e-n)<1)||(A.push(n),j.push(t))}},Y=e=>{let t=_[w.scroll];if(J(),v.length===0){k=0,G(!1);return}let n=I(),r=n?e(t):z(t);n&&!N&&K(r),q(r)},ge=()=>{if(!(typeof ResizeObserver>`u`)){L?.disconnect(),L=new ResizeObserver(()=>Y(z)),L.observe(_);for(let e of v)L.observe(e)}},_e=()=>{let e=j[k]??null;v=ce(),H(),Y(()=>{let t=e?j.indexOf(e):-1;return t>=0?t:m(k,A.length,b)}),ge()},X=(e,t=de)=>{let n=m(e,A.length,b);K(n,t),q(n)},Z=()=>{B()&&X(I()?k-1:A.findLastIndex(e=>e<_[w.scroll]-1))},Q=()=>{if(!V())return;let e=I()?k+1:A.findIndex(e=>e>_[w.scroll]+1);X(e<0?A.length:e)},ve=()=>{O.clearTimeout(M),M=void 0,!N&&(F(),q(z(_[w.scroll])))},ye=()=>{x===`multiple`&&W(),I()||U(),!N&&(O.clearTimeout(M),M=O.setTimeout(ve,150))};J(),k=m(C,A.length,b),H(),K(k),q(k),D.push(d(_,`scroll`,ye)),D.push(d(_,`scrollend`,ve)),D.push(d(l,`keydown`,e=>{if(!(e.defaultPrevented||se(e.target))){switch(e.key){case w.prevKey:Z();break;case w.nextKey:Q();break;case`Home`:X(0);break;case`End`:X(A.length-1);break;default:return}e.preventDefault()}})),D.push(d(l,`carousel:set`,e=>{let t=e.detail;if(!(!t||typeof t!=`object`)){if(typeof t.index==`number`){X(t.index);return}t.action===`next`?Q():t.action===`prev`&&Z()}})),D.push(d(l,`click`,e=>{let t=e.target instanceof Element?e.target.closest(`[data-slot="carousel-indicator"]`):null;if(!t||t.closest(`[data-slot="carousel"]`)!==l)return;let n=s(t,`index`);n===void 0||!Number.isInteger(n)||n<0||n>=A.length||X(n)})),le&&D.push((()=>{let e=_.style.touchAction,n=0;_.style.touchAction=w.touchAction;let r=()=>{N&&(N=!1,l.removeAttribute(`data-dragging`))},i=()=>{if(!I()){F(),q(z(_[w.scroll]));return}let e=z(_[w.scroll]);X(e),Math.abs(_[w.scroll]-(A[e]??0))<1?F():ye()},a=t({element:_,axes:[w.swipe],lockThreshold:12,start:(e,t)=>t.closest(`a[href], button, input, select, textarea, summary, [contenteditable]:not([contenteditable="false"]), [role="button"], [role="link"], [role="tab"], [role="checkbox"], [role="radio"], [role="switch"], [role="textbox"]`)?null:_,lock(){return n=_[w.scroll],N=!0,l.setAttribute(`data-dragging`,`true`),O.clearTimeout(M),pe(),_.scrollTo({[w.edge]:n,behavior:`instant`}),!0},move(e,{deltaX:t,deltaY:r}){_[w.scroll]=n-(w.swipe===`x`?t:r)},release(){r(),i()},reset(e,t){r(),t&&i()}});return()=>{a.destroy(),F(),_.style.touchAction=e}})());for(let[e,t]of[[T,Z],[E,Q]])for(let n of e)n.tagName===`BUTTON`&&!n.hasAttribute(`type`)&&(n.type=`button`),D.push(d(n,`click`,()=>t()));ge(),typeof MutationObserver<`u`&&(R=new MutationObserver(_e),R.observe(_,{childList:!0}));let $={prev:Z,next:Q,goTo(e){X(e)},get index(){return k},get count(){return A.length},get canScrollPrev(){return B()},get canScrollNext(){return V()},destroy(){O.clearTimeout(M),L?.disconnect(),R?.disconnect(),n(D),S!==void 0&&(_.style.scrollSnapType=fe),e(l,p,$)}};return ne(l,p,$),$}function _(e=document){let t=[];for(let n of l(e,`carousel`))u(n,p)||t.push(g(n));return t}export{_ as create,g as createCarousel}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-collapsible.js b/liquid/vendor/data-slot-collapsible.js new file mode 100644 index 000000000..9b2993869 --- /dev/null +++ b/liquid/vendor/data-slot-collapsible.js @@ -0,0 +1 @@ +import{clearRootBinding as e,containsWithPortals as t,createDismissLayer as n,createPresenceLifecycle as r,emit as i,ensureId as a,focusElement as o,getDataBool as s,getPart as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as h}from"@data-slot/core";const g=`@data-slot/collapsible`,_=e=>{let t=e.trim();return t?t.endsWith(`ms`)?Number.parseFloat(t.slice(0,-2))||0:t.endsWith(`s`)?(Number.parseFloat(t.slice(0,-1))||0)*1e3:Number.parseFloat(t)||0:0},v=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=getComputedStyle(e),n=v(t.transitionDuration,t.transitionDelay),r=v(t.animationDuration,t.animationDelay);return Math.max(n,r)};function b(l,u={}){let _=p(l,g,`[@data-slot/collapsible] createCollapsible() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(_)return _;let v=u.defaultOpen??s(l,`defaultOpen`)??!1,b=u.hiddenUntilFound??s(l,`hiddenUntilFound`)??!1,x=u.closeOnEscape??s(l,`closeOnEscape`)??!1,S=u.closeOnClickOutside??s(l,`closeOnClickOutside`)??!1,C=u.onOpenChange,w=c(l,`collapsible-trigger`),T=c(l,`collapsible-content`);if(!w||!T)throw Error(`Collapsible requires trigger and content slots`);let E=l.ownerDocument?.defaultView??window,D=v,O=[],k=null,A=null,j=null,M=null,N=[],P=a(T,`collapsible-content`),F=a(w,`collapsible-trigger`);w.setAttribute(`aria-controls`,P),T.setAttribute(`role`,`region`),T.setAttribute(`aria-labelledby`,F);let I=e=>{l.setAttribute(`data-state`,e),T.setAttribute(`data-state`,e)},L=(e,t)=>{T.style.setProperty(`--collapsible-panel-height`,e),T.style.setProperty(`--collapsible-panel-width`,t)},R=(e,t)=>{L(`${e}px`,`${t}px`)},z=()=>{R(0,0)},B=()=>{R(T.scrollHeight,T.scrollWidth)},V=()=>{let e=T.style.getPropertyValue(`--collapsible-panel-height`).trim(),t=T.style.getPropertyValue(`--collapsible-panel-width`).trim();return e===`auto`&&t===`auto`},H=()=>{A!==null&&(E.cancelAnimationFrame(A),A=null),j!==null&&(E.clearTimeout(j),j=null),N.forEach(e=>e()),N=[]},U=()=>{M!==null&&(E.cancelAnimationFrame(M),M=null)},W=()=>{T.removeAttribute(`hidden`)},G=()=>{b?T.setAttribute(`hidden`,`until-found`):T.hidden=!0,z()},K=()=>{H(),!(!D||Y.isExiting)&&L(`auto`,`auto`)},q=()=>{H();let e=y(T);if(e>0){let t=e=>{e.target===T&&K()};T.addEventListener(`transitionend`,t),T.addEventListener(`animationend`,t),N.push(()=>T.removeEventListener(`transitionend`,t)),N.push(()=>T.removeEventListener(`animationend`,t)),j=E.setTimeout(()=>{j=null,K()},Math.ceil(e)+50);return}A=E.requestAnimationFrame(()=>{A=null,K()})},J=()=>{U(),M=E.requestAnimationFrame(()=>{M=null,!D&&Y.isExiting&&z()})},Y=r({element:T,onExitComplete:()=>{U(),G()}}),X=e=>{D!==e&&(D=e,D||B(),m(w,`expanded`,D),I(D?`open`:`closed`),D?(U(),W(),B(),Y.enter(),q()):(H(),Y.exit(),J()),i(l,`collapsible:change`,{open:D}),C?.(D))};m(w,`expanded`,D),D?(W(),B(),q()):G(),I(D?`open`:`closed`),typeof ResizeObserver<`u`&&(k=new ResizeObserver(()=>{!D||Y.isExiting||V()||B()}),k.observe(T));let Z=()=>w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`;O.push(d(w,`click`,()=>{Z()||X(!D)})),(x||S)&&O.push(n({root:l,isOpen:()=>D,onDismiss:({reason:e})=>{if(Z())return;let n=e===`escape-key`&&t(l,l.ownerDocument.activeElement);X(!1),n&&o(w)},closeOnEscape:x,closeOnClickOutside:S})),b&&O.push(d(T,`beforematch`,()=>{D||X(!0)})),O.push(f(l,`collapsible:set`,e=>{if(Z())return;let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&X(n)}));let Q={open:()=>X(!0),close:()=>X(!1),toggle:()=>X(!D),get isOpen(){return D},destroy:()=>{Y.cleanup(),H(),U(),k?.disconnect(),k=null,O.forEach(e=>e()),O.length=0,e(l,g,Q)}};return h(l,g,Q),Q}function x(e=document){let t=[];for(let n of l(e,`collapsible`))u(n,g)||t.push(b(n));return t}export{x as create,b as createCollapsible}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-combobox.js b/liquid/vendor/data-slot-combobox.js new file mode 100644 index 000000000..60079cc59 --- /dev/null +++ b/liquid/vendor/data-slot-combobox.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,containsWithPortals as r,createDismissLayer as i,createFormFieldAdapter as a,createPortalLifecycle as o,createPositionSync as s,createPresenceLifecycle as c,createTerminalLifecycle as l,emit as u,ensureId as d,ensureItemVisibleInContainer as f,getDataBool as p,getDataEnum as m,getDataNumber as h,getDataString as g,getOwnedElements as _,getPart as v,getRoots as y,hasRootBinding as b,measurePopupContentRect as ee,on as x,onRoot as te,registerFloatingTerminalResources as ne,reuseRootBinding as S,setAria as C,setRootBinding as w}from"@data-slot/core";const T=[`top`,`bottom`],E=[`start`,`center`,`end`];function re(e,t,n,r,i){let a=(t,i)=>m(n,t,i)??(r?m(r,t,i):void 0)??m(e,t,i),o=t=>h(n,t)??(r?h(r,t):void 0)??h(e,t);return{defaultValue:i.defaultValue??g(e,`defaultValue`)??null,defaultOpen:i.defaultOpen??p(e,`defaultOpen`)??!1,placeholder:i.placeholder??g(e,`placeholder`)??``,disabled:i.disabled??p(e,`disabled`)??!1,required:i.required??p(e,`required`)??!1,name:i.name??g(e,`name`)??t.getAttribute(`name`)??null,openOnFocus:i.openOnFocus??p(e,`openOnFocus`)??!0,autoHighlight:i.autoHighlight??p(e,`autoHighlight`)??!1,customFilter:i.filter??null,onValueChange:i.onValueChange,onOpenChange:i.onOpenChange,onInputValueChange:i.onInputValueChange,preferredSide:i.side??a(`side`,T)??`bottom`,preferredAlign:i.align??a(`align`,E)??`start`,sideOffset:i.sideOffset??o(`sideOffset`)??4,alignOffset:i.alignOffset??o(`alignOffset`)??0,avoidCollisions:i.avoidCollisions??(t=>p(n,t)??(r?p(r,t):void 0)??p(e,t))(`avoidCollisions`)??!0,collisionPadding:i.collisionPadding??o(`collisionPadding`)??8}}function ie({root:e,container:t,input:n,emptySlot:r,filter:i,itemToStringValue:a}){let o=v(e,`combobox-content`)??t,s=[],c=[],l=[],u=[],p=[],m=new Map,h=-1,g=a,y=(e,t)=>{e.hidden!==t&&(e.hidden=t)},b=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,ee=e=>{if(e.dataset.label)return e.dataset.label;let t=``;for(let n of e.childNodes)n.nodeType===Node.TEXT_NODE&&(t+=n.textContent);return t.trim()||e.textContent?.trim()||``},x=e=>e.hasAttribute(`data-value`)?e.getAttribute(`data-value`):void 0,te=n=>n===null?null:_(e,t,`[data-slot="combobox-item"]`).find(e=>x(e)===n)??null,ne=e=>{let t=te(e);return g?g(t,e):t?ee(t):``},S=(n,r)=>{for(let e of n){let t=x(e)===r;C(e,`selected`,t),e.toggleAttribute(`data-selected`,t)}for(let n of _(e,t,`[data-slot="combobox-item-indicator"]`)){let e=n.closest(`[data-slot="combobox-item"]`);e&&(n.hidden=!e.hasAttribute(`data-selected`))}},w=()=>{p[h]?.removeAttribute(`data-highlighted`),h=-1,n.removeAttribute(`aria-activedescendant`)},T=()=>{w(),p=s.filter(e=>!e.hidden&&!b(e)),m=new Map(p.map((e,t)=>[e,t]))},E=()=>{if(u.length===0)return;for(let e of u)e.hidden=!0;let e=Array.from(t.children).filter(e=>e instanceof HTMLElement);for(let t=0;t{let r=n===``||i(n,x(e)??``,c[t]);y(e,!r),r&&a++});for(let t of l)y(t,_(e,t,`[data-slot="combobox-item"]`).every(e=>e.hidden));E(),r&&y(r,a>0),o.toggleAttribute(`data-empty`,a===0),T()},select(n){S(s.length>0?s:_(e,t,`[data-slot="combobox-item"]`),n)},highlight(e){let r=p[e];w(),r&&(r.setAttribute(`data-highlighted`,``),n.setAttribute(`aria-activedescendant`,r.id),f(r,t.contains(r)&&t.scrollHeight>t.clientHeight?t:o),h=e)},clearHighlight:w,setItemToStringValue(e){g=e},get enabled(){return p},get highlightedIndex(){return h},indexOf(e){return m.get(e)},isDisabled:b,valueOf:x,labelFor:ne}}function ae(e,t){return Array.from(y(e,`combobox`)).filter(e=>!b(e,`@data-slot/combobox`)).map(t)}const oe=`@data-slot/combobox`,se=[`top`,`bottom`];function D(f,p={}){let m=S(f,oe,`[@data-slot/combobox] createCombobox() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(m)return m;let h=v(f,`combobox-input`),g=v(f,`combobox-content`),y=v(f,`combobox-list`)??_(f,g??f,`[data-slot="combobox-list"]`)[0]??null,b=v(f,`combobox-trigger`),T=v(f,`combobox-clear`),E=v(f,`combobox-value`),ae=_(f,y??g??f,`[data-slot="combobox-empty"]`)[0]??null,D=v(f,`combobox-positioner`),O=D&&g&&D.contains(g)?D:null,ce=v(f,`combobox-portal`),le=ce&&O&&ce.contains(O)?ce:null;if(!h||!g)throw Error(`Combobox requires combobox-input and combobox-content slots`);let k=g.contains(h),ue=E?.textContent?.trim()??``,{defaultValue:de,defaultOpen:fe,placeholder:A,disabled:j,required:pe,name:me,openOnFocus:he,autoHighlight:ge,customFilter:_e,onValueChange:ve,onOpenChange:ye,onInputValueChange:be,preferredSide:xe,preferredAlign:Se,sideOffset:M,alignOffset:Ce,avoidCollisions:we,collisionPadding:N}=re(f,h,g,O,p),P=p.itemToStringValue??null,F=!1,I=de,L=!1,R=null,z=[],B=f.ownerDocument??document,V=B.defaultView??window,Te=f,Ee=-1/0,H=!1,U=!1,De=null,W=o({content:g,root:f,wrapperSlot:O?void 0:`combobox-positioner`,container:O??void 0,mountTarget:O?le??O:void 0}),G=l(),Oe=e=>!!V.matchMedia?.(e).matches,K=Oe(`(pointer: coarse)`)||V.navigator.maxTouchPoints>0&&Oe(`(hover: none)`),ke=d(h,`combobox-input`),Ae=y??g,je=d(Ae,`combobox-list`);h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-autocomplete`,`list`),h.setAttribute(`autocomplete`,`off`),h.setAttribute(`aria-controls`,je),Ae.setAttribute(`role`,`listbox`),b&&(b.hasAttribute(`type`)||b.setAttribute(`type`,`button`),b.hasAttribute(`tabindex`)||(b.tabIndex=-1),b.setAttribute(`aria-label`,`Toggle`)),T instanceof HTMLButtonElement&&!T.hasAttribute(`type`)&&T.setAttribute(`type`,`button`),T&&!T.hasAttribute(`tabindex`)&&(T.tabIndex=-1);let Me=document.querySelector(`label[for="${CSS.escape(ke)}"]`);if(Me){let e=d(Me,`combobox-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),Ae.setAttribute(`aria-labelledby`,e)}j&&(h.setAttribute(`aria-disabled`,`true`),h.disabled=!0,b&&(b.setAttribute(`aria-disabled`,`true`),b.setAttribute(`data-disabled`,``))),pe&&(h.setAttribute(`aria-required`,`true`),h.required=!0);let Ne=()=>{pe&&h.setCustomValidity(I===null?`Please select a value`:``)};A&&(h.placeholder=A),E&&(E.textContent=ue||A,E.textContent.trim().length>0&&(E.setAttribute(`data-placeholder`,``),b?.setAttribute(`data-placeholder`,``)));let q=ie({root:f,container:y??g,input:h,emptySlot:ae,filter:_e??((e,t,n)=>n.toLowerCase().includes(e.toLowerCase())),itemToStringValue:P}),Pe=()=>{let e=Te.getBoundingClientRect();return g.style.minWidth=`${e.width}px`,e},J=(e=Pe())=>{let r=W.container,i=K?`bottom`:R??xe,a=K?!1:we,o=t({anchorRect:e,contentRect:ee(g),side:i,align:Se,sideOffset:M,alignOffset:Ce,avoidCollisions:a,collisionPadding:N,allowedSides:se}),s=n({side:o.side,align:o.align,anchorRect:e,popupX:o.x,popupY:o.y}),c=`translate3d(${o.x+V.scrollX}px, ${o.y+V.scrollY}px, 0)`,l=V.visualViewport,u=l?.offsetTop??0,d=l?.width??V.innerWidth,f=l?.height??V.innerHeight,p=Math.max(0,d-N*2),m=o.side===`top`?Math.max(0,e.top-u-N-M):Math.max(0,u+f-e.bottom-N-M),h=V.devicePixelRatio||1,_=(Math.round((e.x+e.width)*h)-Math.round(e.x*h))/h,v=(Math.round((e.y+e.height)*h)-Math.round(e.y*h))/h;r.style.transform=c,r.style.setProperty(`--transform-origin`,s);for(let e of r===g?[g]:[g,r])e.style.setProperty(`--available-width`,`${p}px`),e.style.setProperty(`--available-height`,`${m}px`),e.style.setProperty(`--anchor-width`,`${_}px`),e.style.setProperty(`--anchor-height`,`${v}px`),e.setAttribute(`data-side`,o.side),e.setAttribute(`data-align`,o.align);!K&&a&&(R=o.side)},Y=s({observedElements:[f,g],isActive:()=>F,ancestorScroll:!0,onUpdate:J,ignoreScrollTarget:e=>e instanceof Node&&g.contains(e)}),X=e=>{for(let t of[f,g,b])t&&(t.setAttribute(`data-state`,e),t.toggleAttribute(`data-open`,e===`open`),t.toggleAttribute(`data-closed`,e===`closed`))},Fe=c({element:g,onExitComplete:()=>{G.isDestroyed||(W.restore(),g.hidden=!0)}}),Ie=()=>{L=!1,k&&(h.value=``),q.filter(h.value),q.highlight(q.enabled.findIndex(e=>q.valueOf(e)===I))},Z=e=>{if(!G.isDestroyed&&F!==e&&!(j&&e)){if(e){let e=Pe();F=!0,R=null,C(h,`expanded`,!0),W.mount();let t=W.container.style;t.position=`absolute`,t.top=`0px`,t.left=`0px`,t.willChange=`transform`,t.margin=`0`,g.hidden=!1,X(`open`),Fe.enter(),q.cache(I),Ie(),Y.start(),J(e),G.trackRaf(()=>{G.isDestroyed||!F||J()})}else F=!1,R=null,C(h,`expanded`,!1),X(`closed`),q.clearHighlight(),L=!1,Y.stop(),Fe.exit(),k?h.value=``:h.value=q.labelFor(I);u(f,`combobox:open-change`,{open:F}),ye?.(F)}},Q=(e,t=!1)=>{if(I===e&&!t)return;let n=I;I=e,Ne(),De?.setValue(e),e===null?f.removeAttribute(`data-value`):f.setAttribute(`data-value`,e),q.select(e);let r=q.labelFor(e);k||(h.value=r),E?e===null?(E.textContent=ue||A,(E.textContent??``).trim().length>0?(E.setAttribute(`data-placeholder`,``),b?.setAttribute(`data-placeholder`,``)):(E.removeAttribute(`data-placeholder`),b?.removeAttribute(`data-placeholder`))):(E.textContent=r,E.removeAttribute(`data-placeholder`),b?.removeAttribute(`data-placeholder`)):b&&(e===null?b.setAttribute(`data-placeholder`,``):b.removeAttribute(`data-placeholder`)),!t&&n!==e&&(u(f,`combobox:change`,{value:e}),ve?.(e))},Le=e=>{if(q.isDisabled(e))return;let t=q.valueOf(e);t!==void 0&&(Q(t),Z(!1))};C(h,`expanded`,!1),g.hidden=!0,X(`closed`),Q(I,!0),De=a({root:f,name:me,defaultValue:de,control:h,disabled:j,onReset:e=>{Q(e,!0),F&&(Ie(),Y.update())}}),z.push(x(B,`keydown`,e=>{e.key===`Tab`&&(Ee=Date.now())},{capture:!0}),x(h,`pointerdown`,()=>{H=!0}),x(h,`input`,()=>{let e=h.value,t=e.trim()!==``;u(f,`combobox:input-change`,{inputValue:e}),be?.(e),F?(q.filter(e),Y.update()):Z(!0),q.highlight(ge&&t?0:-1)}),x(h,`keydown`,e=>{if(!j)switch(e.key){case`ArrowDown`:{if(e.preventDefault(),!F){Z(!0),ge&&q.highlight(0);return}L=!0;let t=q.enabled.length;if(t===0)return;q.highlight(q.highlightedIndex===-1?0:(q.highlightedIndex+1)%t);break}case`ArrowUp`:{if(e.preventDefault(),!F){Z(!0),ge&&q.highlight(q.enabled.length-1);return}L=!0;let t=q.enabled.length;if(t===0)return;q.highlight(q.highlightedIndex===-1?t-1:(q.highlightedIndex-1+t)%t);break}case`Home`:if(!F)return;e.preventDefault(),L=!0,q.highlight(0);break;case`End`:if(!F)return;e.preventDefault(),L=!0,q.highlight(q.enabled.length-1);break;case`Enter`:{if(!F)return;e.preventDefault();let t=q.enabled[q.highlightedIndex];t&&Le(t);break}case`Escape`:F?(e.preventDefault(),Z(!1)):I!==null&&(e.preventDefault(),Q(null));break;case`Tab`:F&&Z(!1);break}}),x(h,`focus`,()=>{if(j)return;if(U){U=!1,H=!1;return}K||h.select();let e=H||Date.now()-Ee<=750;H=!1,he&&!F&&e&&Z(!0)})),b&&z.push(x(b,`click`,()=>{j||(F?Z(!1):(Z(!0),h.focus()))})),T&&z.push(x(T,`mousedown`,e=>{e.preventDefault()}),x(T,`click`,()=>{if(j||h.readOnly||T&&(T.hasAttribute(`disabled`)||T.getAttribute(`aria-disabled`)===`true`))return;Q(null),h.value=``,q.clearHighlight(),F&&(q.filter(h.value),Y.update());let e=B.activeElement!==h;U=e,h.focus(),e||(U=!1)})),z.push(x(g,`click`,e=>{let t=e.target.closest?.(`[data-slot="combobox-item"]`);t&&!t.hidden&&_(f,y??g,`[data-slot="combobox-item"]`).includes(t)&&Le(t)}),x(g,`pointermove`,e=>{let t=e.target.closest?.(`[data-slot="combobox-item"]`);if(!(L&&(L=!1,t&&q.indexOf(t)===q.highlightedIndex)))if(t&&!t.hidden){let e=q.indexOf(t);e!==void 0&&e!==q.highlightedIndex&&q.highlight(e)}else q.clearHighlight()}),x(g,`pointerleave`,()=>{L||q.clearHighlight()}),x(g,`mousedown`,e=>{e.preventDefault()})),z.push(i({root:f,isOpen:()=>F,onDismiss:()=>Z(!1),closeOnClickOutside:!K,closeOnEscape:!1})),K&&z.push(x(B,`click`,e=>{if(!F)return;let t=e.target;r(f,t)||Z(!1)},{capture:!0})),z.push(te(f,`combobox:set`,e=>{let t=e.detail;t?.value!==void 0&&Q(t.value),t?.open!==void 0&&Z(t.open),t?.inputValue!==void 0&&(h.value=t.inputValue),t?.itemToStringValue!==void 0&&(P=t.itemToStringValue,q.setItemToStringValue(P),Q(I,!0))}));let $={get value(){return I},get inputValue(){return h.value},get isOpen(){return F},select:e=>{G.isDestroyed||Q(e)},clear:()=>{G.isDestroyed||Q(null)},open:()=>{G.isDestroyed||Z(!0)},close:()=>{G.isDestroyed||Z(!1)},setItemToStringValue:e=>{G.isDestroyed||(P=e,q.setItemToStringValue(P),Q(I,!0))},destroy:()=>{G.destroy()&&(F=!1,C(h,`expanded`,!1),X(`closed`),g.hidden=!0,De?.destroy())}};return ne(G,{cleanups:z,positionSync:Y,presence:Fe,portal:W,unbind:()=>e(f,oe,$)}),w(f,oe,$),fe&&Z(!0),$}function O(e=document){return ae(e,D)}export{O as create,D as createCombobox}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-command.js b/liquid/vendor/data-slot-command.js new file mode 100644 index 000000000..2d5c14e91 --- /dev/null +++ b/liquid/vendor/data-slot-command.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,ensureItemVisibleInContainer as r,getDataBool as i,getDataString as a,getOwnedElements as o,getPart as s,getRoots as c,hasRootBinding as l,on as u,onRoot as d,reuseRootBinding as ee,setAria as f,setRootBinding as te}from"@data-slot/core";const p=.1,m=.999,h=/[\\/_+.#"@\[\(\{&]/,g=/[\\/_+.#"@\[\(\{&]/g,_=/[\s-]/,v=/[\s-]/g;function y(e,t,n,r,i,a,o){if(a===t.length)return i===e.length?1:.99;let s=`${i},${a}`,c=o[s];if(c!==void 0)return c;let l=r.charAt(a),u=n.indexOf(l,i),d=0;for(;u>=0;){let s=y(e,t,n,r,u+1,a+1,o);if(s>d){if(u===i)s*=1;else if(h.test(e.charAt(u-1))){s*=.8;let t=e.slice(i,u-1).match(g);t&&i>0&&(s*=m**+t.length)}else if(_.test(e.charAt(u-1))){s*=.9;let t=e.slice(i,u-1).match(v);t&&i>0&&(s*=m**+t.length)}else s*=.17,i>0&&(s*=m**+(u-i));e.charAt(u)!==t.charAt(a)&&(s*=.9999)}if(ss&&(s=i*p)}s>d&&(d=s),u=n.indexOf(l,u+1)}return o[s]=d,d}function b(e){return e.toLowerCase().replace(v,` `)}function ne(e,t,n=[]){let r=n.length>0?`${e} ${n.join(` `)}`:e;return y(r,t,b(r),b(t),0,0,{})}const x=Symbol(`data-slot.command.authored-value`),S=Symbol(`data-slot.command.inferred-value`),C=e=>e==null?null:e.trim(),re=e=>e?e.split(/[,\n]/).map(e=>e.trim()).filter(Boolean):[],ie=(e,t)=>Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.getAttribute(`data-slot`)===t),ae=e=>{let t=e=>e.nodeType===Node.TEXT_NODE?e.textContent??``:!(e instanceof HTMLElement)||e.getAttribute(`data-slot`)===`command-shortcut`?``:Array.from(e.childNodes,t).join(``);return t(e).replace(/\s+/g,` `).trim()},oe=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,se=(e,t)=>{let n=e,r=e.hasAttribute(`data-value`);if(n[x]===void 0&&(n[x]=r),r?n[x]||(n[x]=C(e.getAttribute(`data-value`))!==n[S]):n[x]=!1,n[x])return{authored:!0,value:C(e.getAttribute(`data-value`))};let i=t();return n[S]=i,i===null?e.removeAttribute(`data-value`):e.setAttribute(`data-value`,i),{authored:!1,value:i}},w=`@data-slot/command`,T=`[data-slot="command-item"]`,E=`[data-slot="command-group"]`,ce=`[data-slot="command-separator"]`,le=[`input:not([type="hidden"])`,`textarea`,`select`,`button`,`a[href]`,`summary`,`audio[controls]`,`video[controls]`,`[contenteditable=""]`,`[contenteditable="true"]`,`[contenteditable="plaintext-only"]`,`[tabindex]:not([tabindex="-1"])`].join(`, `),ue=(e,t,n)=>t.trim()?ne(e,t,n??[]):1,de=(e,t)=>{let n=e;for(;n&&n.parentElement&&n.parentElement!==t;)n=n.parentElement;return n?.parentElement===t?n:null},D=e=>Array.from(e.children).filter(e=>e instanceof HTMLElement);function O(c,l={}){let p=ee(c,w,`[@data-slot/command] createCommand() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(p)return p;let m=c,h=s(c,`command-input`),g=s(c,`command-list`);if(!h||!g)throw Error(`Command requires command-input and command-list slots`);let _=o(c,g,`[data-slot="command-empty"]`)[0]??null,v=l.label??a(m,`label`)??`Command Menu`,y=l.shouldFilter??i(m,`shouldFilter`)??!0,b=l.loop??i(m,`loop`)??!1,ne=l.disablePointerSelection??i(m,`disablePointerSelection`)??!1,x=l.vimBindings??i(m,`vimBindings`)??!0,S=l.filter??ue,O=l.onValueChange,fe=l.onSearchChange,pe=l.onSelect,k=C(l.defaultValue??a(m,`defaultValue`)),A=l.defaultSearch??a(m,`defaultSearch`)??``,j=[],M=[],N=[],P=new Map,F=new Map,I=0,L=[],R=null,z=null,B=!1,V=!1,me=!1,H=[],U=new Map,W=!1,he=n(h,`command-input`),ge=n(g,`command-list`);m.hasAttribute(`tabindex`)||(m.tabIndex=-1,me=!0),h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-autocomplete`,`list`),h.setAttribute(`aria-expanded`,`true`),h.setAttribute(`aria-controls`,ge),h.setAttribute(`autocomplete`,`off`),h.setAttribute(`autocorrect`,`off`),h.spellcheck=!1,g.setAttribute(`role`,`listbox`),g.tabIndex=-1;let _e=document.querySelector(`label[for="${CSS.escape(he)}"]`);if(_e){let e=n(_e,`command-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),g.setAttribute(`aria-labelledby`,e)}else !h.hasAttribute(`aria-label`)&&!h.hasAttribute(`aria-labelledby`)&&(h.setAttribute(`aria-label`,v),g.setAttribute(`aria-label`,`Suggestions`));let ve=()=>{document.activeElement!==h&&h.focus({preventScroll:!0})},ye=e=>{if(!(e instanceof HTMLElement)||!m.contains(e))return null;let t=e.closest(le);return t instanceof HTMLElement&&m.contains(t)?t:null},be=e=>{R?.disconnect();try{return e()}finally{He()}},xe=()=>{g.style.setProperty(`--command-list-height`,`${g.scrollHeight.toFixed(1)}px`)},Se=(e,t)=>{let n=e.filter(e=>t.includes(e));for(let e of t){if(n.includes(e))continue;let r=t.indexOf(e),i=!1;for(let a=r-1;a>=0;--a){let r=t[a];if(!r)continue;let o=n.indexOf(r);if(o!==-1){n.splice(o+1,0,e),i=!0;break}}if(!i){for(let a=r+1;a{H=D(g),U=new Map(M.map(e=>[e.el,D(e.el)]))},we=()=>{H=Se(H,D(g)),U=new Map(M.map(e=>[e.el,Se(U.get(e.el)??[],D(e.el))]))},G=(e,t)=>{let n=new Set(t.filter(t=>t.parentElement===e)),r=e.firstElementChild;for(let t of[...n,...D(e).filter(e=>!n.has(e))])t===r?r=r.nextElementSibling:e.insertBefore(t,r)},Te=e=>new Map(e.map((e,t)=>[e,t])),Ee=()=>{for(let e of M)G(e.el,U.get(e.el)??[]);G(g,H)},K=()=>k===null?null:j.find(e=>e.value===k&&!e.el.hidden&&!(e.group?.el.hidden??!1))?.el??null,De=()=>{for(let e of j){let t=e.value===k&&k!==null;e.el.hasAttribute(`data-selected`)!==t&&(f(e.el,`selected`,t),t?e.el.setAttribute(`data-selected`,`true`):e.el.removeAttribute(`data-selected`))}let e=K();e?(h.setAttribute(`aria-activedescendant`,e.id),g.setAttribute(`aria-activedescendant`,e.id)):(h.removeAttribute(`aria-activedescendant`),g.removeAttribute(`aria-activedescendant`))},Oe=()=>{k===null?m.removeAttribute(`data-value`):m.setAttribute(`data-value`,k),A?m.setAttribute(`data-search`,A):m.removeAttribute(`data-search`)},ke=()=>[...g.querySelectorAll(T)].map(e=>P.get(e)??null).filter(e=>e!==null&&!e.el.hidden&&!(e.group?.el.hidden??!1)),q=()=>ke().filter(e=>!e.disabled),Ae=(e,n,r)=>{!r||e===n||(t(m,`command:change`,{value:n}),O?.(n))},je=()=>{let e=K();e&&r(e,g)},J=(e,t={})=>{let n=C(e),r=k,i=t.emit??!0,a=t.ensureVisible??!1;return k=n,De(),Oe(),a&&je(),(document.activeElement===h||document.activeElement===m)&&ve(),Ae(r,k,i),r!==k},Me=()=>{j=[],M=[],P=new Map,F=new Map;for(let e of o(m,g,E)){let t=ie(e,`command-group-heading`)[0]??null,r={el:e,heading:t,value:se(e,()=>C(t?.textContent)??n(e,`command-group`)).value??n(e,`command-group`),forceMount:i(e,`forceMount`)??!1,maxRank:0};if(F.set(e,r),M.push(r),e.setAttribute(`role`,`group`),t){let r=n(t,`command-group-heading`);e.setAttribute(`aria-labelledby`,r)}else e.removeAttribute(`aria-labelledby`)}for(let e of o(m,g,T)){let t=e.closest(E),r=t instanceof HTMLElement&&t.closest(`[data-slot="command"]`)===m?F.get(t)??null:null,a={el:e,value:se(e,()=>C(e.getAttribute(`data-label`))??C(ae(e))).value,keywords:re(e.getAttribute(`data-keywords`)??void 0),disabled:oe(e),forceMount:i(e,`forceMount`)??!1,rank:0,group:r};P.set(e,a),j.push(a),n(e,`command-item`),e.setAttribute(`role`,`option`),f(e,`selected`,!1),f(e,`disabled`,a.disabled||null),e.removeAttribute(`data-selected`)}N=o(m,g,ce);for(let e of N)e.setAttribute(`role`,`separator`)},Ne=()=>{let e=A.length>0,t=y&&e;I=0;for(let e of M)e.maxRank=0;for(let e of j)e.rank=t&&e.value!==null?S(e.value,A,e.keywords):1,e.rank>0&&(I+=1),e.el.hidden=!(e.forceMount||e.group?.forceMount||e.rank>0),e.group&&e.rank>e.group.maxRank&&(e.group.maxRank=e.rank);for(let e of M)e.el.hidden=t&&!e.forceMount&&e.maxRank<=0;for(let t of N)t.hidden=e&&!(i(t,`alwaysRender`)??!1);_&&(_.hidden=I>0)},Pe=()=>{if(!y||!A)return;let e=H.filter(e=>e.parentElement===g),t=Te(e),n=new Map,r=new Map;for(let e of j){if(e.group!==null)continue;let t=de(e.el,g);t&&(n.set(t,Math.max(n.get(t)??0,e.rank)),e.el.hidden||r.set(t,Math.max(r.get(t)??0,e.rank)))}let i=new Set([...n.keys(),...M.map(e=>e.el)]),a=[...Array.from(r,([e,t])=>({el:e,rank:t})),...M.filter(e=>!e.el.hidden).map(e=>({el:e.el,rank:e.maxRank}))].sort((e,n)=>n.rank===e.rank?(t.get(e.el)??2**53-1)-(t.get(n.el)??2**53-1):n.rank-e.rank),o=new Set(a.map(e=>e.el)),s=D(g).filter(e=>i.has(e)&&!o.has(e)),c=[...a.map(e=>e.el),...s],l=0;G(g,e.map(e=>i.has(e)?c[l++]??e:e));for(let e of M){let t=U.get(e.el)?.filter(t=>t.parentElement===e.el)??[],n=Te(t),r=new Set,i=new Map;for(let t of j){if(t.group!==e)continue;let n=de(t.el,e.el);n&&(r.add(n),t.el.hidden||i.set(n,Math.max(i.get(n)??0,t.rank)))}let a=Array.from(i,([e,t])=>({el:e,rank:t})).sort((e,t)=>t.rank===e.rank?(n.get(e.el)??2**53-1)-(n.get(t.el)??2**53-1):t.rank-e.rank),o=new Set(a.map(e=>e.el)),s=D(e.el).filter(e=>r.has(e)&&!o.has(e)),c=[...a.map(e=>e.el),...s],l=0,u=t.map(e=>r.has(e)?c[l++]??e:e);G(e.el,u)}},Fe=()=>{be(()=>{y&&A?(Ne(),Pe(),W=!0):(W&&Ee(),Ne(),W=!1),De(),Oe()}),z||xe()},Y=()=>{if(be(Me),W?we():Ce(),Fe(),!(typeof ResizeObserver>`u`)){z??=new ResizeObserver(xe),z.disconnect();for(let e of[g,...o(m,g,`${T}, ${E}, ${ce}, [data-slot="command-empty"]`)])z.observe(e)}},Ie=(e=!0,t=!1)=>{J(q()[0]?.value??null,{emit:e,ensureVisible:t})},X=(e,n=!0,r=!0)=>{A!==e&&(A=e,h.value=e,Fe(),r&&Ie(n,!0),n&&(t(m,`command:search-change`,{search:A}),fe?.(A)))},Le=e=>{e.value===null||e.disabled||(J(e.value,{emit:!0}),t(m,`command:select`,{value:e.value}),pe?.(e.value))},Re=e=>{let t=P.get(e)??null;return t||(Y(),t=P.get(e)??null,t)},Z=e=>{let t=q()[e];t&&J(t.value,{emit:!0,ensureVisible:!0})},Q=e=>{let t=q();if(t.length===0)return;let n=t.findIndex(e=>e.value===k),r=t[n+e];b&&(r=n+e<0?t[t.length-1]:n+e===t.length?t[0]:t[n+e]),r&&J(r.value,{emit:!0,ensureVisible:!0})},ze=e=>{let t=K()?.closest(E);for(;t;){let n=e>0?t.nextElementSibling:t.previousElementSibling,r=null;for(;n;){if(n instanceof HTMLElement&&n.getAttribute(`data-slot`)===`command-group`&&!n.hidden){r=n;break}n=e>0?n.nextElementSibling:n.previousElementSibling}if(!r)break;let i=[...r.querySelectorAll(T)].map(e=>P.get(e)??null).find(e=>e!==null&&!e.disabled&&!e.el.hidden);if(i){J(i.value,{emit:!0,ensureVisible:!0});return}t=r}Q(e)},Be=e=>{if(e.defaultPrevented)return;let t=ye(e.target);if(t&&t!==h)return;let n=e.key.toLowerCase();if(e.isComposing||e.keyCode===229)return;let r=()=>{e.preventDefault(),e.metaKey?Z(q().length-1):e.altKey?ze(1):Q(1)},i=()=>{e.preventDefault(),e.metaKey?Z(0):e.altKey?ze(-1):Q(-1)};switch(n){case`arrowdown`:r();break;case`arrowup`:i();break;case`home`:e.preventDefault(),Z(0);break;case`end`:e.preventDefault(),Z(q().length-1);break;case`enter`:{let t=K(),n=t?P.get(t):null;if(!n||n.value===null)return;e.preventDefault(),Le(n);break}case`j`:case`n`:x&&e.ctrlKey&&r();break;case`k`:case`p`:x&&e.ctrlKey&&i();break}},Ve=()=>{B||V||(B=!0,queueMicrotask(()=>{if(B=!1,V)return;let e=K();Y();let t=e?P.get(e)??null:null;if(t&&t.value!==null&&!t.disabled&&!t.el.hidden&&!(t.group?.el.hidden??!1)){J(t.value,{emit:!0});return}if(k!==null){let e=q().find(e=>e.value===k)??null;if(e){J(e.value,{emit:!0});return}}J(q()[0]?.value??null,{emit:!0})}))},He=()=>{R&&R.observe(g,{subtree:!0,childList:!0,characterData:!0,attributes:!0,attributeFilter:[`data-slot`,`data-value`,`data-label`,`data-keywords`,`data-disabled`,`disabled`,`aria-disabled`,`data-force-mount`,`data-always-render`]})};typeof MutationObserver<`u`&&(R=new MutationObserver(()=>{Ve()}),He()),h.value=A,Y(),(A||k===null)&&Ie(!1),L.push(u(h,`input`,()=>{X(h.value,!0,!0)})),L.push(u(m,`keydown`,e=>{Be(e)})),L.push(u(g,`pointermove`,e=>{if(ne)return;let t=e.target,n=t instanceof HTMLElement?t.closest(T):null;if(!(n instanceof HTMLElement))return;let r=Re(n);!r||r.disabled||n.hidden||r.group?.el.hidden||J(r.value,{emit:!0})})),L.push(u(g,`click`,e=>{let t=e.target,n=ye(t),r=t instanceof HTMLElement?t.closest(T):null;if(n&&n!==r||!(r instanceof HTMLElement))return;let i=Re(r);!i||i.disabled||i.value===null||r.hidden||i.group?.el.hidden||Le(i)})),L.push(d(m,`command:set`,e=>{let t=e.detail;t&&(t.search!==void 0&&X(String(t.search),!0,t.value===void 0),t.value!==void 0&&J(t.value,{emit:!0,ensureVisible:!0}))}));let $={get value(){return k},get search(){return A},select(e){J(e,{emit:!0,ensureVisible:!0})},setSearch(e){X(e,!0,!0)},destroy(){V=!0,R?.disconnect(),z?.disconnect(),L.forEach(e=>e()),L=[],me&&m.removeAttribute(`tabindex`),e(m,w,$)}};return te(m,w,$),$}function fe(e=document){let t=[];for(let n of c(e,`command`))l(n,w)||t.push(O(n));return t}export{fe as create,O as createCommand}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-core.js b/liquid/vendor/data-slot-core.js new file mode 100644 index 000000000..76c42aff4 --- /dev/null +++ b/liquid/vendor/data-slot-core.js @@ -0,0 +1 @@ +const e=Symbol.for(`data-slot.retained-content`),t=`data-retained-content`,n=[];function r(t){return t[e]??n}function i(n){let{root:r,target:i}=n,a=r,o=(n.strategy??`lazy`)===`lazy`,s=o?r.ownerDocument.createComment(`data-slot content`):null,c=!1,l=!1;s&&i.before(s);let u=()=>{let n=a[e];n?.delete(i),!n?.size&&(delete a[e],a.removeAttribute(t))};return{mount:()=>{c||!l||(s?.parentNode?.insertBefore(i,s.nextSibling),u(),l=!1)},unmount(){c||!o||l||(i.remove(),l=!0,(a[e]??=new Set).add(i),a.setAttribute(t,``))},cleanup(){c||=(s?.parentNode&&s.parentNode.insertBefore(i,s.nextSibling),s?.remove(),u(),!0)}}}const a=(e,t,n)=>{let r=e.getAttribute(`data-slot`);return[...t.querySelectorAll(n)].filter(n=>{let i=r&&n.parentElement?.closest(`[data-slot="${r}"]`);return!i||i===e||!t.contains(i)})},o=(e,t)=>s(e,t)[0]??null,s=(e,t)=>a(e,e,`[data-slot="${t}"]`),c=(e,n)=>{let i=new Set,a=new Set,o=e=>{if(a.has(e))return;a.add(e);for(let t of e.querySelectorAll(`[data-slot="${n}"]`))i.add(t);let s=e.querySelectorAll(`[${t}]`);for(let t of e instanceof Element?[e,...s]:s)for(let e of r(t))t.contains(e)||(e.getAttribute(`data-slot`)===n&&i.add(e),o(e))};return o(e),[...i]},l=Symbol.for(`data-slot.root-bindings`),u=Symbol.for(`data-slot.root-binding-warnings`),d=(e,t=!1)=>{let n=e,r=n[l];return!r&&t&&(r=new Map,n[l]=r),r},f=(e,t=!1)=>{let n=e,r=n[u];return!r&&t&&(r=new Set,n[u]=r),r};function p(e,t){return d(e)?.get(t)}function m(e,t){return d(e)?.has(t)??!1}function h(e,t,n){return d(e,!0).set(t,n),n}function g(e,t,n){let r=d(e);return!r?.has(t)||arguments.length>=3&&r.get(t)!==n?!1:(r.delete(t),r.size===0&&delete e[l],!0)}function _(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let r=f(e,!0);r.has(t)||(r.add(t),console.warn(n))}function v(e,t,n){let r=p(e,t);return r!==void 0&&_(e,t,n),r}const y=new WeakMap;function b(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let r=y.get(e);r||(r=new Set,y.set(e,r)),!r.has(t)&&(r.add(t),console.warn(`[@data-slot] ${n}`))}function x(e){let t=`data-${e.replace(/([A-Z])/g,`-$1`).toLowerCase()}`,n=`data-${e}`;return t===n?[t]:[t,n]}function S(e,t){for(let n of x(t))if(e.hasAttribute(n))return e.getAttribute(n);return null}function C(e,t){return x(t).some(t=>e.hasAttribute(t))}const ee=new Set([``,`true`,`1`,`yes`]),te=new Set([`false`,`0`,`no`]);function ne(e,t){if(!C(e,t))return;let n=S(e,t);if(n===null)return;let r=n.toLowerCase();if(ee.has(r))return!0;if(te.has(r))return!1;b(e,t,`Invalid boolean value "${n}" for data-${t}. Expected: true/false/1/0/yes/no or empty.`)}function re(e,t){let n=S(e,t);if(n===null||n===``)return;let r=Number(n);if(Number.isNaN(r)||!Number.isFinite(r)){b(e,t,`Invalid number value "${n}" for data-${t}.`);return}return r}function ie(e,t){if(C(e,t))return S(e,t)??void 0}function ae(e,t,n){let r=S(e,t);if(r!==null){if(n.includes(r))return r;b(e,t,`Invalid value "${r}" for data-${t}. Expected one of: ${n.join(`, `)}.`)}}const w=new WeakMap,T=Symbol.for(`data-slot.portal-owner`),oe=e=>e[T]??w.get(e),se=(e,t)=>{w.set(e,t),e[T]=t},ce=e=>{w.delete(e),delete e[T]};function E(e,t){return D(e,t,new Set)}function D(e,t,n){if(!t)return!1;let r=t instanceof Element?t:t.parentElement;if(!r)return!1;if(e.contains(r))return!0;let i=r;for(;i;){let t=oe(i);if(t&&!n.has(t)&&(n.add(t),D(e,t,n)))return!0;i=i.parentElement}return!1}function O(e,t,n){if(n.portaled)return;let r=(t.ownerDocument??document)?.body;r&&(n.originalParent=e.parentNode,n.originalNextSibling=e.nextSibling,se(e,t),r.appendChild(e),n.portaled=!0)}function k(e,t){if(!t.portaled)return;ce(e);let n=t.originalParent,r=t.originalNextSibling;n&&n.isConnected?r&&r.parentNode===n?n.insertBefore(e,r):n.appendChild(e):e.remove(),t.portaled=!1,t.originalParent=null,t.originalNextSibling=null}let le=0;const A=(e,t)=>e.id||=`${t}-${++le}`,ue=(e,t,n)=>{n===null?e.removeAttribute(`aria-${t}`):e.setAttribute(`aria-${t}`,String(n))},de=(e,t,n)=>{t&&e.setAttribute(`aria-labelledby`,A(t,`title`)),n&&e.setAttribute(`aria-describedby`,A(n,`desc`))};function j(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}const fe=(e,t,n)=>j(e,t,t=>{t.target===e&&n(t)}),pe=(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n})),me=(...e)=>t=>{for(let n of e){if(t.defaultPrevented)break;n?.(t)}};function he(e,t,n,r){let i=e.ownerDocument.createElement(`input`);i.type=`hidden`,i.name=`data-slot-form-field-${crypto.getRandomValues(new Uint32Array(4)).join(`-`)}`;let a=n.getAttribute(`form`);a!==null&&i.setAttribute(`form`,a),n.after(i);let o=new Map,s=new WeakSet,c=e=>{if(e.target!==n.form||s.has(e))return;s.add(e),l();let a=e.formData,o=Array.from(a.entries()),c=o.findIndex(([e])=>e===i.name);if(c<0)return;let u=!n.disabled,d=r||t.matches(`:disabled`);n.disabled=d;let f=c-(u?1:0);o.splice(f,u?2:1,...d?[]:[[n.name,n.value]]);for(let e of new Set(a.keys()))a.delete(e);for(let[e,t]of o)a.append(e,t)},l=()=>{for(let t of[e.ownerDocument,e.getRootNode(),n.form])!t||o.has(t)||o.set(t,j(t,`formdata`,c,{capture:!0}))};return l(),{observe:l,destroy(){for(let e of o.values())e();o.clear(),i.remove()}}}function ge(e,t){e.required=!0,e.tabIndex=-1,e.setAttribute(`aria-hidden`,`true`),Object.assign(e.style,{position:`absolute`,width:`1px`,height:`1px`,padding:`0`,margin:`-1px`,overflow:`hidden`,clip:`rect(0, 0, 0, 0)`,whiteSpace:`nowrap`,border:`0`});let n=e.ownerDocument.defaultView,r,i=()=>{r!==void 0&&n?.cancelAnimationFrame(r),r=void 0},a=()=>{i(),e.willValidate&&!e.validity.valid?t?.setAttribute(`aria-invalid`,`true`):t?.removeAttribute(`aria-invalid`)},o=()=>!e.willValidate||e.validity.valid?!1:e.form?Array.from(e.form.elements).find(e=>{let t=e;return t.willValidate&&!t.validity.valid})===e:!0,s=j(e,`invalid`,i=>{a();let s=i.defaultPrevented;if(i.preventDefault(),s||!t||!o())return;t.focus();let c=e.form;r=n?.requestAnimationFrame(()=>{r=void 0,e.form===c&&o()&&t.focus()})});return a(),{sync:a,destroy(){i(),s(),t?.removeAttribute(`aria-invalid`)}}}function M({root:e,getForm:t,onReset:n,onResetDispatched:r}){let i=new Map,a=new Map,o=e=>{if(e.target!==t()||a.has(e))return;s(),r?.(e);let i=setTimeout(()=>{a.delete(e),!(e.defaultPrevented||e.target!==t())&&n(e)},0);a.set(e,i)},s=()=>{for(let n of[e.ownerDocument,e.getRootNode(),t()])!n||i.has(n)||i.set(n,j(n,`reset`,o,{capture:!0}))};return s(),{observe:s,destroy(){for(let e of a.values())clearTimeout(e);a.clear();for(let e of i.values())e();i.clear()}}}function _e({root:e,name:t,defaultValue:n,control:r,disabled:i=!1,required:a=!1,validationTarget:o,onReset:s}){let c=r?.getAttribute(`name`)??null,l=r?.getAttribute(`form`)??null,u=null,d=null,f=null,p=e=>{f&&(f.value=e??``),u&&(u.value=e??``)};if(t){if(r?.removeAttribute(`name`),a)d=e.ownerDocument.createElement(`select`),f=e.ownerDocument.createElement(`option`),f.defaultSelected=!0,d.appendChild(f),u=d;else{let t=e.ownerDocument.createElement(`input`);t.type=`hidden`,t.defaultValue=n??``,u=t}u.name=t,p(n),u.disabled=i||(r?.disabled??!1),l!==null&&u.setAttribute(`form`,l),u.setAttribute(`data-form-field-generated`,``),e.appendChild(u)}let m=d?ge(d,o):null,h=r&&u?he(e,r,u,i):null,g=()=>u?u.form:r?.hasAttribute(`form`)||r?.form?r.form:e.closest(`form`),_=n,v=0,y=new WeakMap,b=M({root:e,getForm:g,onResetDispatched:e=>{y.set(e,v)},onReset:e=>{if(y.get(e)!==v){p(_),m?.sync();return}p(n),m?.sync(),_=n,s(n)}});return{setValue(e){e!==_&&(_=e,v++),p(e),m?.sync(),h?.observe(),b.observe()},destroy(){b.destroy(),m?.destroy(),h?.destroy(),u?.remove(),r&&t&&(c===null?r.removeAttribute(`name`):r.setAttribute(`name`,c))}}}let N=0,P=``,F=``;function ve(){if(N===0){let e=document.documentElement;P=e.style.overflow,F=e.style.scrollbarGutter,e.style.overflow=`hidden`,e.style.scrollbarGutter=`stable`}N++}function ye(){if(N=Math.max(0,N-1),N===0){let e=document.documentElement;e.style.overflow=P,e.style.scrollbarGutter=F}}const I=[`a[href]`,`area[href]`,`button`,`input:not([type="hidden"])`,`select`,`textarea`,`iframe`,`audio[controls]`,`video[controls]`].join(`, `),be=`${I}, summary, [contenteditable], [tabindex]`;function xe(e){for(let t=e;t;t=t.parentElement)if(t.hasAttribute(`inert`)||t.inert)return!0;return!1}function Se(e){let t=e.ownerDocument.defaultView?.getComputedStyle(e).visibility;if(t===`hidden`||t===`collapse`)return!1;for(let t=e;t;t=t.parentElement)if(t.hidden||t.ownerDocument.defaultView?.getComputedStyle(t)?.display===`none`)return!1;return!0}function Ce(e){for(let t=e.parentElement;t;t=t.parentElement)if(!(t.tagName!==`DETAILS`||t.hasAttribute(`open`))&&!Array.from(t.children).find(e=>e.tagName===`SUMMARY`)?.contains(e))return!0;return!1}function we(e){if(!e.matches(`button, input, select, textarea, option, optgroup`))return!1;let t=e.closest(`fieldset[disabled]`);return t?!Array.from(t.children).find(e=>e.tagName===`LEGEND`)?.contains(e):!1}function L(e){return!Te(e)||e.matches(`:disabled`)||we(e)?!1:Se(e)&&!xe(e)&&!Ce(e)}function R(e){for(let t=e;t;t=t.parentElement){let e=t.getAttribute(`contenteditable`)?.toLowerCase();if(e===`false`)return!1;if(e===``||e===`true`||e===`plaintext-only`)return!0}return!1}function Te(e){if(e.matches(`input[type="hidden"]`))return!1;let t=e.getAttribute(`tabindex`);return t!==null&&!Number.isNaN(Number.parseInt(t,10))||e.matches(I)||e.tagName===`SUMMARY`&&e.parentElement?.tagName===`DETAILS`&&Array.from(e.parentElement.children).find(e=>e.tagName===`SUMMARY`)===e?!0:R(e)&&!R(e.parentElement)}function z(e){let t=e.getRootNode();if(t!==e)return t;let n=e;for(;n.parentNode;)n=n.parentNode;return n}function Ee(e){let t=new Map;for(let n of e.querySelectorAll(`input[type="radio"]:checked`)){if(!n.name||z(n)!==e||!L(n))continue;let r=t.get(n.form);r||(r=new Map,t.set(n.form,r)),r.has(n.name)||r.set(n.name,n)}return t}function B(e){return Array.from(e.querySelectorAll(be)).filter(L)}function De(e){let t=B(e);return t.find(e=>e.hasAttribute(`autofocus`))??t[0]}function Oe(e){let t=new Map;return B(e).filter(e=>{let n=e.getAttribute(`tabindex`);if(n!==null&&Number.parseInt(n,10)<0)return!1;let r=e.ownerDocument.defaultView?.HTMLInputElement;if(!r||!(e instanceof r)||e.type!==`radio`||!e.name)return!0;let i=z(e),a=t.get(i);a||(a=Ee(i),t.set(i,a));let o=a.get(e.form)?.get(e.name);return!o||o===e})}const V=(e,t)=>e===`inline-start`?t===`rtl`?`right`:`left`:e===`inline-end`?t===`rtl`?`left`:`right`:e,ke=e=>{switch(e){case`top`:return[`top`,`right`,`bottom`,`left`];case`bottom`:return[`bottom`,`top`,`right`,`left`];case`left`:return[`left`,`top`,`right`,`bottom`];case`right`:return[`right`,`top`,`bottom`,`left`];case`inline-start`:return[`inline-start`,`inline-end`,`top`,`bottom`];case`inline-end`:return[`inline-end`,`inline-start`,`top`,`bottom`]}},Ae=e=>{let t=window.visualViewport,n=e.viewportWidth??t?.width,r=e.viewportHeight??t?.height;if(n==null||r==null)throw Error(`computeFloatingPosition requires window.visualViewport when viewport dimensions are not provided`);return{x:t?.offsetLeft??0,y:t?.offsetTop??0,width:n,height:r}},H=(e,t,n,r,i,a)=>{let o=0,s=0;return e===`top`?s=n.top-r.height-i:e===`bottom`?s=n.bottom+i:o=e===`left`?n.left-r.width-i:n.right+i,e===`top`||e===`bottom`?o=t===`start`?n.left+a:t===`center`?n.left+n.width/2-r.width/2+a:n.right-r.width-a:s=t===`start`?n.top+a:t===`center`?n.top+n.height/2-r.height/2+a:n.bottom-r.height-a,{x:o,y:s}},je=(e,t)=>t===`start`?{x:e.left,y:e.top}:t===`end`?{x:e.right,y:e.bottom}:{x:e.left+e.width/2,y:e.top+e.height/2},U=(e,t,n,r=`ltr`)=>{let i=V(e,r),a=je(n,t);return i===`top`?{x:a.x,y:n.top}:i===`bottom`?{x:a.x,y:n.bottom}:i===`left`?{x:n.left,y:a.y}:{x:n.right,y:a.y}},Me=e=>{let t=U(e.side,e.align,e.anchorRect,e.direction);return`${t.x-e.popupX}px ${t.y-e.popupY}px`};function Ne(e){let t=e.getBoundingClientRect(),n=e.offsetWidth>0?e.offsetWidth:t.width,r=e.offsetHeight>0?e.offsetHeight:t.height;return{top:t.top,left:t.left,right:t.left+n,bottom:t.top+r,width:n,height:r}}const Pe=e=>{if(e)try{e.focus({preventScroll:!0})}catch{e.focus()}},Fe=(e,t,n,r,i)=>{let a=r.x+i,o=r.x+r.width-i,s=r.y+i,c=r.y+r.height-i;return e===`top`?Math.max(0,s-t.y):e===`bottom`?Math.max(0,t.y+n.height-c):e===`left`?Math.max(0,a-t.x):Math.max(0,t.x+n.width-o)},W=(e,t,n)=>nt>n&&ee===`top`||e===`bottom`;function Le(e){let t=Ae(e),n=e.direction??`ltr`,r=e.allowedSides?.length?[...new Set(e.allowedSides)]:[...ke(e.side)],i=r.includes(e.side)?e.side:r[0],a=i,o=V(i,n),s=H(o,e.align,e.anchorRect,e.contentRect,e.sideOffset,e.alignOffset);if(e.avoidCollisions){let c=t.x+e.collisionPadding,l=t.y+e.collisionPadding,u=G(e.anchorRect.left,e.anchorRect.right,t.x,t.x+t.width),d=G(e.anchorRect.top,e.anchorRect.bottom,t.y,t.y+t.height);if(Ie(o)?d:u){let c=[i,...r.filter(e=>e!==i)],l=a,u=s,d=1/0,f=o;for(let r of c){let i=V(r,n),a=H(i,e.align,e.anchorRect,e.contentRect,e.sideOffset,e.alignOffset),o=Fe(i,a,e.contentRect,t,e.collisionPadding);if(o<=0){l=r,u=a,d=o,f=i;break}ol&&(u=s-t.clientHeight+n),u=Math.min(Math.max(u,0),r),u!==t.scrollTop&&(t.scrollTop=u)}const K=new WeakMap,ze=(e,t)=>{let n=String(t);e.overlay&&(e.overlay.setAttribute(`data-stack-index`,n),e.overlay.style.setProperty(`--${e.cssVarPrefix}-stack-index`,n),e.overlay.style.setProperty(`--${e.cssVarPrefix}-overlay-stack-index`,n)),e.content.setAttribute(`data-stack-index`,n),e.content.style.setProperty(`--${e.cssVarPrefix}-stack-index`,n),e.content.style.setProperty(`--${e.cssVarPrefix}-content-stack-index`,n)},Be=e=>{e.overlay&&(e.overlay.removeAttribute(`data-stack-index`),e.overlay.style.removeProperty(`--${e.cssVarPrefix}-stack-index`),e.overlay.style.removeProperty(`--${e.cssVarPrefix}-overlay-stack-index`)),e.content.removeAttribute(`data-stack-index`),e.content.style.removeProperty(`--${e.cssVarPrefix}-stack-index`),e.content.style.removeProperty(`--${e.cssVarPrefix}-content-stack-index`)},q=e=>{e.entries.forEach((e,t)=>ze(e,t)),e.updateIsolation()},Ve=e=>{let t=e.defaultView,n=new Map,r,i=()=>{for(let[e,t]of n)e.toggleAttribute(`inert`,t.inert),t.aria===null?e.removeAttribute(`aria-hidden`):e.setAttribute(`aria-hidden`,t.aria);n.clear()},a={entries:[],cleanup:()=>{},updateIsolation(){i();let o=a.entries.at(-1);if(!o||!a.entries.some(e=>e.isolateOutside)){r?.disconnect(),r=void 0;return}r||(r=new t.MutationObserver(()=>a.updateIsolation()),r.observe(e.body,{childList:!0,subtree:!0}));let s=new Set([o.content]);o.overlay&&s.add(o.overlay);for(let t of e.body.querySelectorAll(`*`))E(o.content,t)&&s.add(t);let c=new Set;for(let e of s)for(let t=e.parentElement;t&&!c.has(t);t=t.parentElement)c.add(t);let l=e=>{for(let t of e.children)if(!s.has(t)){if(c.has(t)){l(t);continue}n.set(t,{inert:t.hasAttribute(`inert`),aria:t.getAttribute(`aria-hidden`)}),t.setAttribute(`inert`,``),t.setAttribute(`aria-hidden`,`true`)}};l(e.body)}},o=j(e,`keydown`,e=>{if(e.key!==`Tab`)return;let t=a.entries[a.entries.length-1];t&&t.onTabKeydown?.(e)});return a.cleanup=()=>{o(),r?.disconnect(),i(),a.entries.length=0},a},He=e=>{let t=K.get(e);if(t)return t;let n=Ve(e);return K.set(e,n),n};function Ue(e){let t=e.content.ownerDocument??document,n={content:e.content,overlay:e.overlay??null,onTabKeydown:e.onTabKeydown,cssVarPrefix:e.cssVarPrefix,isolateOutside:e.isolateOutside},r=!1,i=()=>{let e=K.get(t);if(!e)return;let r=e.entries.indexOf(n);r!==-1&&(e.entries.splice(r,1),Be(n),q(e),e.entries.length===0&&(e.cleanup(),K.delete(t)))};return{open:()=>{if(r)return;let e=He(t);e.entries.includes(n)||(e.entries.push(n),q(e))},close:i,get isTopmost(){return K.get(t)?.entries.at(-1)===n},destroy:()=>{r||(r=!0,i())}}}const J=e=>({x:e.x,y:e.y,width:e.width,height:e.height}),Y=(e,t)=>e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height,We=e=>{let t=getComputedStyle(e),n=`${t.overflow}${t.overflowX}${t.overflowY}`;return/(auto|scroll|overlay)/.test(n)},Ge=(e,t)=>{let n=new Set([e]),r=e.parentNode;for(;r;){if(r instanceof Element){We(r)&&n.add(r),r=r.parentNode;continue}if(r instanceof Document){r.scrollingElement&&n.add(r.scrollingElement),n.add(r),r.defaultView&&(n.add(r.defaultView),r.defaultView.visualViewport&&n.add(r.defaultView.visualViewport));break}r=null}return n.add(t),t.visualViewport&&n.add(t.visualViewport),[...n]},Ke=(e,t,n)=>{let r=null,i=null,a=()=>{i!==null&&(n.clearTimeout(i),i=null),r?.disconnect(),r=null},o=(s=!1,c=1)=>{a();let l=e.getBoundingClientRect();if(s||t(),!l.width||!l.height)return;let u=e.ownerDocument.documentElement,d=Math.floor(l.top),f=Math.floor(u.clientWidth-(l.left+l.width)),p=Math.floor(u.clientHeight-(l.top+l.height)),m=Math.floor(l.left),h=`${-d}px ${-f}px ${-p}px ${-m}px`,g=Math.max(0,Math.min(1,c))||1,_=!0;r=new IntersectionObserver(t=>{let r=t[0]?.intersectionRatio??1;if(r!==g){if(!_){o();return}r?o(!1,r):i=n.setTimeout(()=>{o(!1,1e-7)},1e3)}if(r===1&&!Y(J(l),J(e.getBoundingClientRect()))){o();return}_=!1},{rootMargin:h,threshold:g}),r.observe(e)};return o(!0),a};function qe(e){let t=e.win??window,n=e.isActive??(()=>!0),r=e.observedElements??[],i=e.ancestorScroll??!0,a=e.syncOnScroll??!1,o=e.ancestorResize??!0,s=e.elementResize??typeof ResizeObserver<`u`,c=e.layoutShift??!1,l=e.animationFrame??!1,u=null,d=null,f=!1,p=null,m=null,h=[],g=()=>{u===null&&(u=t.requestAnimationFrame(()=>{u=null,n()&&e.onUpdate()}))},_=()=>{u!==null&&(t.cancelAnimationFrame(u),u=null)},v=()=>g(),y=t=>{if(!e.ignoreScrollTarget?.(t.target)){if(a){_(),n()&&e.onUpdate();return}g()}},b=()=>{let e=new Set([t]);t.visualViewport&&e.add(t.visualViewport);for(let n of r)for(let r of Ge(n,t))e.add(r);return[...e]};return{start:()=>{if(f)return;if(f=!0,i)for(let e of b())e.addEventListener(`scroll`,y,{passive:!0}),h.push(()=>e.removeEventListener(`scroll`,y));if(o){let e=[t];t.visualViewport&&e.push(t.visualViewport);for(let t of e)t.addEventListener(`resize`,v),h.push(()=>t.removeEventListener(`resize`,v))}if(s&&typeof ResizeObserver<`u`){p=new ResizeObserver(v);for(let e of r)p.observe(e)}let e=r[0]??null;if(c&&e&&typeof IntersectionObserver<`u`&&(m=Ke(e,g,t)),l&&e){let n=J(e.getBoundingClientRect()),r=()=>{if(!f)return;let i=J(e.getBoundingClientRect());Y(n,i)||g(),n=i,d=t.requestAnimationFrame(r)};d=t.requestAnimationFrame(r)}},stop:()=>{f&&(f=!1,u!==null&&_(),d!==null&&(t.cancelAnimationFrame(d),d=null),h.forEach(e=>e()),h=[],p?.disconnect(),p=null,m?.(),m=null)},update:g}}const X=new WeakMap,Je=(e,t)=>{let n=t.isOpen();return n&&!t.wasOpen&&(e.openSequence+=1,t.openOrder=e.openSequence),t.wasOpen=n,n},Z=(e,t)=>{let n=null;for(let r of e.layers)Je(e,r)&&(t&&!t(r)||(!n||r.openOrder>n.openOrder)&&(n=r));return n},Ye=e=>{let t=e.defaultView??window,n={layers:[],openSequence:0,pendingTouchOutside:!1,pendingIframeBlur:null,cleanup:()=>{}},r=()=>{n.pendingIframeBlur!=null&&(t.clearTimeout(n.pendingIframeBlur),n.pendingIframeBlur=null)},i=j(e,`pointerdown`,e=>{let t=e.target,r=Z(n);if(!r||!r.closeOnClickOutside||r.isInside(t)){n.pendingTouchOutside=!1;return}if(e.pointerType===`touch`){n.pendingTouchOutside=!0;return}n.pendingTouchOutside=!1,r.onDismiss({reason:`outside-press`,originalEvent:e})}),a=j(e,`click`,e=>{if(!n.pendingTouchOutside)return;n.pendingTouchOutside=!1;let t=e.target,r=Z(n);!r||!r.closeOnClickOutside||r.isInside(t)||r.onDismiss({reason:`outside-press`,originalEvent:e})}),o=j(e,`pointercancel`,()=>{n.pendingTouchOutside=!1}),s=j(t,`blur`,i=>{Z(n,e=>e.closeOnClickOutside)&&(n.pendingTouchOutside=!1,r(),n.pendingIframeBlur=t.setTimeout(()=>{n.pendingIframeBlur=null;let r=Z(n,e=>e.closeOnClickOutside);if(!r)return;let a=t.HTMLIFrameElement,o=e.activeElement;!a||!(o instanceof a)||r.isInside(o)||r.onDismiss({reason:`focus-out`,originalEvent:i})},0))}),c=j(e,`keydown`,e=>{if(e.key!==`Escape`||e.defaultPrevented)return;let t=e.target,r=Z(n,e=>e.closeOnEscape&&e.isInside(t))??Z(n,e=>e.closeOnEscape);r&&(r.preventEscapeDefault&&e.preventDefault(),r.onDismiss({reason:`escape-key`,originalEvent:e}))});return n.cleanup=()=>{i(),a(),o(),s(),c(),r(),n.pendingTouchOutside=!1,n.layers.length=0},n},Xe=e=>{let t=X.get(e);if(t)return t;let n=Ye(e);return X.set(e,n),n};function Ze(e){let t=e.root.ownerDocument??document,n=Xe(t),r={isOpen:e.isOpen,onDismiss:e.onDismiss,isInside:e.isInside??(t=>E(e.root,t)),closeOnClickOutside:e.closeOnClickOutside??!0,closeOnEscape:e.closeOnEscape??!0,preventEscapeDefault:e.preventEscapeDefault??!0,wasOpen:!1,openOrder:0};return Je(n,r)&&(r.wasOpen=!0),n.layers.push(r),()=>{let e=n.layers.indexOf(r);e!==-1&&n.layers.splice(e,1),n.layers.length===0&&(n.cleanup(),X.delete(t))}}function Qe(){let e=!1,t=!1,n=[],r=[],i=new Set,a=new Set;return{get isDestroyed(){return e},onBeforeDestroy:r=>{e||(t?r():n.push(r))},onDestroy:t=>{e?t():r.push(t)},trackRaf:n=>{if(e||t)return null;let r=0;return r=requestAnimationFrame(t=>{i.delete(r),e||n(t)}),i.add(r),r},cancelRaf:e=>{e===null||!i.delete(e)||cancelAnimationFrame(e)},cancelTimeout:e=>{e===null||!a.delete(e)||clearTimeout(e)},trackFinalRaf:n=>!t||e?null:requestAnimationFrame(n),trackTimeout:(n,r)=>{if(e||t)return null;let i=setTimeout(()=>{a.delete(i),e||n()},r);return a.add(i),i},destroy(){if(e||t)return!1;t=!0;for(let e of i)cancelAnimationFrame(e);i.clear();for(let e of a)clearTimeout(e);a.clear();for(let e of n.splice(0))e();e=!0,t=!1;for(let e of r.splice(0))e();return!0}}}function $e(e,t){e.onDestroy(()=>{t.positionSync.stop(),t.presence.cleanup(),t.portal.cleanup(),Q(t.cleanups),t.unbind()})}function et(e,t){t.beforeDestroy&&e.onBeforeDestroy(t.beforeDestroy),e.onDestroy(()=>{t.modalStack.destroy();for(let e of t.presence)e.cleanup();t.reset(),t.releaseScrollLock(),t.cleanup(),t.portal?.cleanup(),Q(t.cleanups),t.unbind()})}function Q(e){for(let t of e.splice(0))t()}function tt(e){let t=e.enabled??!0,n=e.wrapperSlot,r=e.container,i=e.mountTarget??r??e.content,a=e.state??{originalParent:null,originalNextSibling:null,portaled:!1},o=e.root.ownerDocument??document,s=null,c=()=>{if(s)return s;let e=o.createElement(`div`);return n&&(e.setAttribute(`data-slot`,n),e.style.isolation=`isolate`,e.style.zIndex=`50`),s=e,e},l=()=>{if(a.portaled)return;let t=e.content.parentNode;if(!t)return;let n=c();t.insertBefore(n,e.content),n.appendChild(e.content),O(n,e.root,a)},u=()=>{a.portaled||i.isConnected&&O(i,e.root,a)},d=()=>{if(!a.portaled)return;let t=s;if(!t)return;k(t,a);let n=t.parentNode;n&&n.isConnected?(n.insertBefore(e.content,t),t.remove()):e.content.remove()},f=()=>{a.portaled&&k(i,a)};return{state:a,get container(){return r||(t&&n&&a.portaled&&s?s:e.content)},mount:()=>{if(t){if(r||e.mountTarget){u();return}n?l():O(e.content,e.root,a)}},restore:()=>{if(t){if(r||e.mountTarget){f();return}n?d():k(e.content,a)}},cleanup:()=>{if(t){if(r||e.mountTarget){f();return}n?d():k(e.content,a)}}}}const nt=e=>{let t=e.trim().toLowerCase();if(!t)return 0;if(t.endsWith(`ms`)){let e=Number(t.slice(0,-2).trim());return Number.isFinite(e)?e:0}if(t.endsWith(`s`)){let e=Number(t.slice(0,-1).trim());return Number.isFinite(e)?e*1e3:0}let n=Number(t);return Number.isFinite(n)?n:0},$=(e,t)=>{let n=e.split(`,`),r=t.split(`,`),i=Math.max(n.length,r.length),a=0;for(let e=0;e{let t=getComputedStyle(e),n=$(t.transitionDuration,t.transitionDelay),r=$(t.animationDuration,t.animationDelay);return Math.max(n,r)};function it(e){let t=e.win??window,n=!1,r=null,i=null,a=null,o=null,s=[],c=()=>{a!==null&&(t.cancelAnimationFrame(a),a=null),o!==null&&(t.clearTimeout(o),o=null),s.forEach(e=>e()),s=[]},l=()=>{c(),n=!1,e.element.removeAttribute(`data-ending-style`)},u=()=>{r!==null&&(t.cancelAnimationFrame(r),r=null),i!==null&&(t.cancelAnimationFrame(i),i=null),e.element.removeAttribute(`data-starting-style`)},d=()=>{n&&(c(),n=!1,e.element.removeAttribute(`data-ending-style`),e.onExitComplete())};return{get isExiting(){return n},enter:()=>{l(),u(),e.element.setAttribute(`data-starting-style`,``),r=t.requestAnimationFrame(()=>{r=null,i=t.requestAnimationFrame(()=>{i=null,e.element.removeAttribute(`data-starting-style`)})})},exit:()=>{l(),u(),n=!0,e.element.setAttribute(`data-ending-style`,``);let r=t.performance.now(),i=rt(e.element);if(i>0){let n=n=>{if(n.target===e.element){if(typeof e.element.getAnimations==`function`){if(e.element.getAnimations().some(e=>e.playState===`running`||e.pending))return}else if(t.performance.now()-re.element.removeEventListener(`transitionend`,n)),s.push(()=>e.element.removeEventListener(`animationend`,n)),o=t.setTimeout(()=>{o=null,d()},Math.ceil(i)+50)}else a=t.requestAnimationFrame(()=>{a=null,d()})},cleanup:()=>{l(),u()}}}function at(e){let{element:t,axes:n}=e,r=t.ownerDocument,i=e.lockThreshold??8,a=null,o=!1,s,c=()=>{let e=a;if(a=null,e?.captured)try{e.captured.releasePointerCapture?.(e.pointerId)}catch{}return e},l=t=>{let n=c();n?.axis&&e.reset(n.target,t)},u=t=>!e.active||e.active(t.target),d=t=>{if(t.defaultPrevented||t.button!==0)return;let n=t.target;if(!(n instanceof Element))return;let r=e.start(t,n);r!==null&&(l(t),a={target:r,pressTarget:n,pointerId:t.pointerId,startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,timeStamp:t.timeStamp,axis:null,captured:null})},f=(r,o=r)=>{if(!a||a.pointerId!==r.pointerId)return;if(!u(a)){l(o);return}a.lastX=r.clientX,a.lastY=r.clientY;let s=r.clientX-a.startX,c=r.clientY-a.startY;if(!a.axis){let l=Math.abs(s),u=Math.abs(c);if(Math.max(l,u)u?`x`:`y`,f={axis:d,deltaX:s,deltaY:c,event:o};if(!n.includes(d)||e.lock&&!e.lock(a.target,f,a.pressTarget)){a=null;return}a.axis=d,a.captured=e.capture?.(a.target)??t;try{a.captured.setPointerCapture?.(r.pointerId)}catch{}}o.cancelable&&o.preventDefault(),e.move(a.target,{axis:a.axis,deltaX:s,deltaY:c,event:o})},p=(t,n=t)=>{if(!a||a.pointerId!==t.pointerId)return;if(!u(a)){l(n);return}let r=c();r.axis&&(o=!0,clearTimeout(s),s=setTimeout(()=>{o=!1},400),e.release(r.target,{axis:r.axis,deltaX:r.lastX-r.startX,deltaY:r.lastY-r.startY,event:n,duration:t.timeStamp-r.timeStamp}))},m=()=>{o=!1,clearTimeout(s)},h=(e,t)=>({target:e.target,pointerId:t.identifier,clientX:t.clientX,clientY:t.clientY,button:0,timeStamp:e.timeStamp,defaultPrevented:e.defaultPrevented,cancelable:e.cancelable,preventDefault:()=>e.preventDefault()}),g=e=>Array.from(e).find(e=>e.identifier===a?.pointerId),_=[j(r,`pointerdown`,m,{capture:!0}),j(r,`touchstart`,m,{capture:!0,passive:!0}),j(t,`pointerdown`,e=>{e.pointerType!==`touch`&&d(e)}),j(r,`pointermove`,e=>{e.pointerType!==`touch`&&f(e)},{passive:!1}),j(r,`pointerup`,e=>{e.pointerType!==`touch`&&p(e)}),j(r,`pointercancel`,e=>{e.pointerType!==`touch`&&a?.pointerId===e.pointerId&&l(e)}),j(t,`touchstart`,e=>{if(e.touches.length!==1){l(e);return}d(h(e,e.touches[0]))},{passive:!0}),j(r,`touchmove`,e=>{let t=g(e.touches);t&&f(h(e,t),e)},{passive:!1}),j(r,`touchend`,e=>{let t=g(e.changedTouches);t&&p(h(e,t),e)}),j(r,`touchcancel`,e=>l(e)),j(r,`click`,e=>{!o||e.detail===0||(o=!1,e.preventDefault(),e.stopImmediatePropagation())},{capture:!0})];return{cancel(e){a&&(e===void 0||a.target===e)&&l()},destroy(){_.forEach(e=>e()),clearTimeout(s),l()}}}function ot(e={}){let t=e.resetDelayMs??500,n=e.win??window,r=``,i=null,a=()=>{i!==null&&(n.clearTimeout(i),i=null),r=``};return{match:(e,a,o)=>{i!==null&&n.clearTimeout(i),i=n.setTimeout(()=>{r=``,i=null},t);let s=e.toLowerCase();r+=s;let c=a.map(e=>e.toLowerCase()),l=c.findIndex(e=>e.startsWith(r));if(l===-1&&r.length===1){let e=o+1;for(let t=0;t{A.trackFinalRaf(()=>{j&&(document.contains(j)?a(j):T&&document.contains(T)&&a(T)),j=null})};ne(O,`dialog-content`),O.setAttribute(`role`,w?`alertdialog`:`dialog`),h(O,`modal`,!0),f(O,se,ce),D.setAttribute(`role`,`presentation`),D.setAttribute(`aria-hidden`,`true`),D.tabIndex=-1,T&&(T.setAttribute(`aria-haspopup`,`dialog`),T.setAttribute(`aria-controls`,O.id),h(T,`expanded`,!1));let I=!1,L=()=>{O.hasAttribute(`tabindex`)||(O.tabIndex=-1,I=!0)},R=()=>{I&&=(O.removeAttribute(`tabindex`),!1)},z=()=>{let e=o(O);if(e)return e.focus();L(),O.focus()},B=()=>{N?.mount()},V=()=>{A.trackRaf(()=>{j&&document.contains(j)&&typeof j.focus==`function`?a(j):T&&document.contains(T)&&a(T),j=null})},H=e=>{if(l.setAttribute(`data-state`,e),E&&E.setAttribute(`data-state`,e),D.setAttribute(`data-state`,e),O.setAttribute(`data-state`,e),e===`open`){l.setAttribute(`data-open`,``),E?.setAttribute(`data-open`,``),D.setAttribute(`data-open`,``),O.setAttribute(`data-open`,``),l.removeAttribute(`data-closed`),E?.removeAttribute(`data-closed`),D.removeAttribute(`data-closed`),O.removeAttribute(`data-closed`);return}l.setAttribute(`data-closed`,``),E?.setAttribute(`data-closed`,``),D.setAttribute(`data-closed`,``),O.setAttribute(`data-closed`,``),l.removeAttribute(`data-open`),E?.removeAttribute(`data-open`),D.removeAttribute(`data-open`),O.removeAttribute(`data-open`)},U=0,W=0,G=0,K=0,q=(e,t)=>{A.isDestroyed||k||t!==U||(e.hidden=!0,W=Math.max(0,W-1),W===0&&(R(),V()))},J=i({element:D,onExitComplete:()=>q(D,G)}),Y=i({element:O,onExitComplete:()=>q(O,K)}),X=(e,t=!1)=>{A.isDestroyed||k===e&&!t||(e?(U+=1,W=0,B(),j=document.activeElement,Q.open(),C&&!P&&(p(),P=!0)):(U+=1,W=2,G=U,K=U,Q.close(),P&&=(g(),!1)),k=e,T&&h(T,`expanded`,k),e?(D.hidden=!1,O.hidden=!1,H(`open`),J.enter(),Y.enter()):(H(`closed`),J.exit(),Y.exit()),te(l,`dialog:change`,{open:k}),b?.(k),e&&A.trackRaf(()=>z()))},Z=e=>{if(e.key!==`Tab`)return;let t=u(O);if(t.length===0){e.preventDefault(),L(),O.focus();return}let n=t[0],r=t[t.length-1],i=O.ownerDocument.activeElement;if(!t.includes(i)){e.preventDefault(),(e.shiftKey?r:n).focus();return}if(n===r){e.preventDefault();return}e.shiftKey?i===n&&(e.preventDefault(),r.focus()):i===r&&(e.preventDefault(),n.focus())},Q=n({content:O,overlay:D,onTabKeydown:Z,cssVarPrefix:`dialog`});O.hidden=!0,D.hidden=!0,H(`closed`),T&&M.push(m(T,`click`,()=>X(!k))),M.push(m(O,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest?.(`[data-slot="dialog-close"]`);n&&O.contains(n)&&X(!1)})),x&&M.push(m(D,`click`,e=>{e.button===0&&e.target===D&&k&&X(!1)})),M.push(t({root:l,isOpen:()=>k,onDismiss:()=>X(!1),closeOnClickOutside:!1,closeOnEscape:S}));let $={open:()=>{A.isDestroyed||X(!0)},close:()=>{A.isDestroyed||X(!1)},toggle:()=>{A.isDestroyed||X(!k)},get isOpen(){return k},destroy:()=>{A.destroy()},_handleKeydown:Z,_content:O,_overlay:D};return ie(A,{cleanups:M,modalStack:Q,presence:[J,Y],portal:N,beforeDestroy:F,reset:()=>{U+=1,W=0,k=!1,H(`closed`),D.hidden=!0,O.hidden=!0,T&&h(T,`expanded`,!1)},releaseScrollLock:()=>{P&&=(g(),!1)},cleanup:R,unbind:()=>e(l,_,$)}),M.push(re(l,`dialog:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&X(n)})),oe(l,_,$),y&&X(!0),$}function y(e=document){let t=[];for(let n of l(e,`dialog`))d(n,_)||t.push(v(n));return t}export{y as create,v as createDialog}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-drawer.js b/liquid/vendor/data-slot-drawer.js new file mode 100644 index 000000000..a241bf710 --- /dev/null +++ b/liquid/vendor/data-slot-drawer.js @@ -0,0 +1 @@ +import{clearRootBinding as e,containsWithPortals as t,createDismissLayer as n,createModalStackItem as r,createPortalLifecycle as i,createPresenceLifecycle as a,createSwipeGesture as o,emit as s,ensureId as c,focusElement as l,getAutofocusOrFirstFocusable as u,getDataBool as d,getDataString as f,getRoots as p,getTabbables as ee,hasRootBinding as m,linkLabelledBy as te,lockScroll as ne,on as h,reuseRootBinding as re,setAria as g,setRootBinding as ie,unlockScroll as ae}from"@data-slot/core";function oe(e){if(typeof e==`number`)return Number.isFinite(e)&&e>=0?e:null;if(typeof e!=`string`||!e.trim())return null;let t=e.trim();if(/^\d*\.?\d+(px|rem)$/.test(t))return t;let n=Number(t);return Number.isFinite(n)&&n>=0?n:null}function se(e,t,n){return typeof e==`number`?e<=1?e*t:e:parseFloat(e)*(e.endsWith(`rem`)?n:1)}function _(e,t,n,r){for(let i=e;i&&t.contains(i);i=i.parentElement){let e=i,t=e.ownerDocument.defaultView.getComputedStyle(e),a=n?t.overflowX:t.overflowY;if(!/(auto|scroll)/.test(a))continue;let o=n?e.scrollLeft:e.scrollTop,s=n?e.scrollWidth-e.clientWidth:e.scrollHeight-e.clientHeight;if(s>1&&(r>0?o>0:or?e.deltaX:e.deltaY;return o({element:t,axes:[r?`x`:`y`],start:(n,r)=>!e.enabled()||r.closest(`[data-swipe-ignore],input,textarea,select,button,a,[contenteditable="true"]`)?null:r.closest(`[data-slot="drawer-popup"]`)===t?t:null,lock:(e,n,i)=>!_(i,t,r,a(n)),active:e.enabled,move:(t,n)=>e.move(a(n)*i,n.event),release:(t,n)=>{let r=a(n)*i;e.release(r,r/Math.max(1,n.duration),n.event),e.reset()},reset:(t,n)=>e.reset(n)}).destroy}function le(e){let t=e.ownerDocument,n=t.defaultView,r=n.visualViewport,i=e.style.getPropertyValue(`--drawer-keyboard-inset`),a=e.hasAttribute(`data-keyboard-open`),o=0,s=null,c=()=>{if(!s)return;let{element:e,paddingBottom:t,scrollPaddingBottom:n,overflowAnchor:r}=s;Object.assign(e.style,{paddingBottom:t,scrollPaddingBottom:n,overflowAnchor:r}),s=null},l=()=>{o=0;let i=t.activeElement,a=i?.matches(`textarea, input:not([type]), input[type="text"], input[type="email"], input[type="number"], input[type="password"], input[type="search"], input[type="tel"], input[type="url"], [contenteditable]:not([contenteditable="false"])`),l=!!r&&(r.scale===void 0||r.scale===1)&&n.innerHeight-r.height>60&&!!a&&e.contains(i),u=r?Math.min(n.innerHeight,r.height+r.offsetTop):n.innerHeight;if(e.style.setProperty(`--drawer-keyboard-inset`,`${l?Math.max(0,n.innerHeight-u):0}px`),e.toggleAttribute(`data-keyboard-open`,l),!l||!i){c();return}let d=i.parentElement;for(;d&&e.contains(d)&&!/(auto|scroll)/.test(n.getComputedStyle(d).overflowY);)d=d.parentElement;if(!d||!e.contains(d)){c();return}s?.element!==d&&c();let f=i.getBoundingClientRect(),p=d.getBoundingClientRect(),ee=Math.min(u,p.bottom)-16,m=Math.max(r?.offsetTop??0,p.top)+16,te=f.bottom-ee;te>0?(s||={element:d,paddingBottom:d.style.paddingBottom,scrollPaddingBottom:d.style.scrollPaddingBottom,overflowAnchor:d.style.overflowAnchor,basePadding:parseFloat(n.getComputedStyle(d).paddingBottom)||0},d.style.overflowAnchor=`none`,d.style.paddingBottom=`${s.basePadding+Math.max(0,p.bottom-u)+f.height+48}px`,d.style.scrollPaddingBottom=`16px`,d.scrollTop+=te):f.top{o||=n.requestAnimationFrame(l)};l();let d=[h(t,`focusin`,u),h(t,`focusout`,u),h(n,`resize`,u)];return r&&d.push(h(r,`resize`,u),h(r,`scroll`,u)),()=>{d.forEach(e=>e()),n.cancelAnimationFrame(o),c(),i?e.style.setProperty(`--drawer-keyboard-inset`,i):e.style.removeProperty(`--drawer-keyboard-inset`),e.toggleAttribute(`data-keyboard-open`,a)}}const v=new WeakMap;function y(e){let t=null;for(let n of e)n.open&&(!t||n.order>t.order)&&(t=n);return t}function b(e){let{popup:t,swipe:n}=e,r=e.front??e;t.toggleAttribute(`data-nested-swiping`,e.descendants.some(e=>e.open&&e.swipe!==null));for(let i of[t,e.parts.backdrop,e.parts.viewport])i&&(i.toggleAttribute(`data-swiping`,n!==null),i.style.setProperty(`--drawer-swipe-progress`,String((i===t?r.swipe:n)?.progress??0)));t.style.setProperty(`--drawer-swipe-amount-x`,`${n?.x??0}px`),t.style.setProperty(`--drawer-swipe-amount-y`,`${n?.y??0}px`)}function x(e){e.popup.toggleAttribute(`data-nested`,!!e.parent),e.popup.toggleAttribute(`data-nested-drawer-open`,e.front!==null),e.popup.style.setProperty(`--nested-drawers`,String(e.descendants.filter(e=>e.open).length)),e.popup.style.setProperty(`--drawer-front-height`,`${(e.front??e).height}px`),b(e)}function S(e){let{front:t}=e;for(let n of e.indents)n.toggleAttribute(`data-active`,!!t),n.toggleAttribute(`data-inactive`,!t),n.style.setProperty(`--drawer-swipe-progress`,String(t?.swipe?.progress??0)),n.style.setProperty(`--drawer-height`,`${t?.height??0}px`)}function ue(e){e.front=y(e.entries),e.indents=Array.from(e.element.querySelectorAll(`[data-slot="drawer-indent"],[data-slot="drawer-indent-background"]`)).filter(t=>t.closest(`[data-slot="drawer-provider"]`)===e.element),S(e)}function de(e){for(let t of e.entries.values())t.ancestors=[],t.descendants=[];for(let t of e.entries.values())for(let n=t.parent&&e.entries.get(t.parent);n;n=n.parent&&e.entries.get(n.parent))t.ancestors.push(n),n.descendants.push(t);for(let t of e.entries.values())t.front=y(t.descendants),x(t);for(let[t,n]of e.providers)ue(n),n.entries.size||e.providers.delete(t)}function fe(e,n,r={}){let i=e.ownerDocument,a=v.get(i);a||(a={entries:new Map,providers:new Map,openOrder:0},v.set(i,a));let o=null;for(let n of a.entries.values())t(n.popup,e)&&(!o||t(o.popup,n.root))&&(o=n);let s=e.closest(`[data-slot="drawer-provider"]`)??o?.provider?.element,c=null;s&&(c=a.providers.get(s)??{element:s,entries:new Set,indents:[],front:null},a.providers.set(s,c));let l={root:e,popup:n,parts:r,parent:e.parentElement?.closest(`[data-slot="drawer"]`)??o?.root??null,ancestors:[],descendants:[],provider:c,open:!1,swipe:null,order:0,height:n.getBoundingClientRect().height,front:null};a.entries.set(e,l),c?.entries.add(l),de(a);let u=()=>{x(l),l.ancestors.forEach(x)},d=!1;return{get parent(){return l.parent},setOpen(e){if(!(d||e===l.open)){e&&(l.order=++a.openOrder),l.open=e,e||(l.swipe=null);for(let e of l.ancestors)e.front=y(e.descendants);u(),c&&ue(c)}},setSwipe(e){if(!d&&(l.swipe=e,b(l),l.ancestors.forEach(b),c?.front===l))for(let t of c.indents)t.style.setProperty(`--drawer-swipe-progress`,String(e?.progress??0))},refresh(e){d||l.height===e||(l.height=e,u(),c?.front===l&&S(c))},destroy(){d||(d=!0,a.entries.delete(e),c?.entries.delete(l),de(a),a.entries.size||v.delete(i))}}}const C=`@data-slot/drawer`,pe=e=>e.matches(`:disabled`)||d(e,`disabled`)===!0||e.getAttribute(`aria-disabled`)===`true`,w=(e,t)=>Array.from(e.querySelectorAll(`[data-slot="drawer-${t}"]`)).filter(t=>t.closest(`[data-slot="drawer"]`)===e),me=(e,t)=>{try{return e.querySelector(t)}catch{return null}},he=(e,t)=>{let n=f(e,t);return n===`false`?!1:n===`true`||n===``?!0:n},ge=e=>{let t=e?.getAttribute(`data-payload`);if(t!=null)try{return JSON.parse(t)}catch{return t}};function T(o,p={}){let m=re(o,C,`[@data-slot/drawer] Drawer already initialized; returning its controller.`);if(m)return m;let _=w(o,`popup`)[0];if(!_)throw Error(`Drawer requires drawer-popup slot`);let v=o.ownerDocument,y=v.defaultView,b=w(o,`portal`)[0],x=w(o,`backdrop`)[0],S=w(o,`viewport`)[0],ue=w(o,`title`)[0],de=w(o,`description`)[0],T=w(o,`trigger`);if(o.id)for(let e of v.querySelectorAll(`[data-drawer-target]`))e.getAttribute(`data-drawer-target`)?.replace(/^#/,``)===o.id&&e.dataset.slot===`drawer-trigger`&&!T.includes(e)&&T.push(e);let _e=f(o,`modal`),E=p.modal??(_e===`trap-focus`?`trap-focus`:_e!==`false`),ve=!(p.disablePointerDismissal??d(o,`disablePointerDismissal`)??!1),ye=p.closeOnEscape??d(o,`closeOnEscape`)??!0,be=p.swipeDirection??f(o,`swipeDirection`),D=[`up`,`down`,`left`,`right`].includes(be??``)?be:`down`,O=D===`left`||D===`right`,xe=D===`left`||D===`up`?-1:1,k=oe(p.snapPoint===void 0?p.defaultSnapPoint===void 0?f(o,`snapPoint`)??f(o,`defaultSnapPoint`):p.defaultSnapPoint:p.snapPoint),Se=p.initialFocus??he(_,`initialFocus`),A=p.finalFocus??he(_,`finalFocus`),Ce=p.keepMounted??(b?d(b,`keepMounted`):void 0)??!1,j=p.container??(b?f(b,`container`):void 0),we=typeof j==`string`?me(v,j):j,Te=b??S??_,M=i({content:Te,root:o}),N=[],P=!1,F=!1,Ee=!1,De=!1,I=!1,L,Oe=S&&(o.closest(`[data-slot="drawer-virtual-keyboard-provider"]`)||w(o,`virtual-keyboard-provider`).length),R=null,ke=p.triggerId??p.defaultTriggerId??f(o,`triggerId`)??f(o,`defaultTriggerId`),z=T.find(e=>e.id===ke)??null,B=0,Ae=-1,je=0,V=new Set,Me=e=>{let t=y.requestAnimationFrame(()=>{V.delete(t),F||e()});V.add(t)},Ne=[o,b,x,S,_].filter(e=>!!e),Pe=_.getAttribute(`tabindex`);Pe===null&&(_.tabIndex=-1),_.setAttribute(`role`,`dialog`),E===!0?g(_,`modal`,!0):_.removeAttribute(`aria-modal`),c(_,`drawer-popup`),te(_,ue??null,de??null),x&&(x.setAttribute(`aria-hidden`,`true`),x.setAttribute(`role`,`presentation`));for(let e of[...T,...w(o,`close`)])e.tagName===`BUTTON`&&!e.hasAttribute(`type`)&&e.setAttribute(`type`,`button`);for(let e of T)c(e,`drawer-trigger`),e.setAttribute(`aria-haspopup`,`dialog`),e.setAttribute(`aria-controls`,_.id),g(e,`expanded`,!1);let H=fe(o,_,{backdrop:x,viewport:S}),U=()=>{for(let e of Ne)e.setAttribute(`data-state`,P?`open`:`closed`),e.toggleAttribute(`data-open`,P),e.toggleAttribute(`data-closed`,!P),e.setAttribute(`data-swipe-direction`,D);for(let e of T)g(e,`expanded`,P&&e===z),e.toggleAttribute(`data-popup-open`,P&&e===z);H.setOpen(P)},Fe=()=>O?_.getBoundingClientRect().width||y.innerWidth:_.getBoundingClientRect().height||y.innerHeight,W=()=>k===null?Fe():Math.min(Fe(),se(k,O?S?.clientWidth||v.documentElement.clientWidth||y.innerWidth:S?.clientHeight||v.documentElement.clientHeight||y.innerHeight,parseFloat(y.getComputedStyle(v.documentElement).fontSize)||16)),Ie=()=>Math.max(0,Fe()-W()),G=()=>{let e=_.getBoundingClientRect();_.style.setProperty(`--drawer-height`,`${e.height}px`),_.style.setProperty(`--drawer-width`,`${e.width}px`),_.style.setProperty(`--drawer-snap-point-offset`,`${Ie()*xe}px`),k===null?o.removeAttribute(`data-snap-point`):o.setAttribute(`data-snap-point`,String(k)),H.refresh(e.height)},K=e=>t(_,e),Le=e=>typeof e==`string`?me(v,e):typeof e==`object`?e:null,Re=()=>{!P||!Y.isTopmost||Se===!1||l(Le(Se)??(Se===!0?u(_):null)??_)},ze=()=>{let e=Le(A)??z??R;R=null,A!==!1&&e?.isConnected&&l(e)},Be=e=>{s(o,`drawer:change-complete`,{open:e}),p.onOpenChangeComplete?.(e)},Ve=e=>{if(!P||B!==e||Ae===e)return;let t=Ue.flatMap(e=>typeof e.getAnimations==`function`?e.getAnimations():[]),n=()=>{!F&&P&&B===e&&Ae!==e&&(Ae=e,Be(!0))};t.length?Promise.allSettled(t.map(e=>e.finished)).then(n):n()},q=()=>{if(!(P||I||F)){_.hidden=!0,x&&(x.hidden=!0),S&&(S.hidden=!0),b&&(b.hidden=!0);for(let e of Ue)e.removeAttribute(`data-ending-style`);Ce||(M.restore(),Ee=!1)}},He=()=>{P||F||--je>0||(q(),Be(!1))},Ue=[_,x].filter(e=>!!e),J=Ue.map(e=>a({element:e,win:y,onExitComplete:()=>{e.setAttribute(`data-ending-style`,``),He()}})),We=(e,t=`imperative-action`,n)=>{let r=oe(e);if(F||e!==null&&r===null||r===k)return;let i=!1,a={snapPoint:r,reason:t,originalEvent:n,cancel:()=>{i=!0}},c=new CustomEvent(`drawer:beforesnapchange`,{bubbles:!0,cancelable:!0,detail:a});o.dispatchEvent(c),!(c.defaultPrevented||i||F)&&(p.onSnapPointChange?.(r,a),!(i||F)&&(k=r,G(),s(o,`drawer:snapchange`,a)))},Y=r({content:_,overlay:x,cssVarPrefix:`drawer`,isolateOutside:E===!0,onTabKeydown:e=>{if(E===!1||!P)return;let t=ee(_),n=v.activeElement;if(K(n)&&!_.contains(n))return;if(!t.length){e.preventDefault(),l(_);return}let r=t[0],i=t.at(-1);(!t.includes(n)||!e.shiftKey&&n===i||e.shiftKey&&n===r)&&(e.preventDefault(),l(e.shiftKey?i:r))}}),Ge=({next:e,reason:t,originalEvent:n,trigger:r})=>{if(F)return;let i=P&&e&&r!==z;if(P===e&&!i)return;let a=!1,c=!1,l={open:e,reason:t,trigger:r,payload:ge(r),originalEvent:n,cancel:()=>{a=!0},preventUnmountOnClose:()=>{c=!0}},u=new CustomEvent(`drawer:beforechange`,{bubbles:!0,cancelable:!0,detail:l});if(o.dispatchEvent(u),u.defaultPrevented||a||F||(p.onOpenChange?.(e,l),a||F))return;if(i){z=r,U(),s(o,`drawer:change`,l);return}let d=++B;if(z=r,P=e,I=!e&&c,e){R=v.activeElement,J.forEach(e=>e.enter()),Ee||=(M.mount(),we&&we.appendChild(Te),!0);for(let e of[b,S,x,_])e&&(e.hidden=!1);Y.open(),E===!0&&(ne(),De=!0),Oe&&S&&(L=le(S)),U(),G(),Me(Re),Me(()=>Me(()=>Ve(d)))}else{Y.close(),L?.(),L=void 0,De&&=(ae(),!1),U(),je=J.length,J.forEach(e=>e.exit());let e=n?.target;A===void 0&&(t===`focus-out`||t===`outside-press`&&E===!1&&e?.closest?.(`button,input,textarea,select,a[href],[tabindex]`))?R=null:ze()}s(o,`drawer:change`,l)},X=null,Z=null,Q=(e,t=`imperative-action`,n,r=z)=>{if(!F){if(X?.next===e&&X.trigger===r){Z=null;return}if(Z={next:e,reason:t,originalEvent:n,trigger:r},!X)try{for(;Z&&!F;)X=Z,Z=null,Ge(X)}finally{X=null,Z=null}}};N.push(n({root:o,isOpen:()=>P,closeOnEscape:ye,closeOnClickOutside:ve,isInside:e=>K(e)||T.some(t=>t.contains(e)),onDismiss:({reason:e,originalEvent:t})=>{e===`outside-press`&&t.button!==0||Q(!1,e,t)}}));for(let e of T)N.push(h(e,`click`,t=>{t.defaultPrevented||pe(e)||Q(!P||z!==e,`trigger-press`,t,e)}));for(let e of w(o,`close`))N.push(h(e,`click`,t=>{!t.defaultPrevented&&!pe(e)&&Q(!1,`close-press`,t)}));N.push(h(v,`focusin`,e=>{!P||!Y.isTopmost||K(e.target)||(E===!1?ve&&!T.includes(e.target)&&Q(!1,`focus-out`,e):l(u(_)??_))}));let Ke=()=>H.setSwipe(null);if(N.push(ce({popup:_,direction:D,enabled:()=>P&&Y.isTopmost,move:e=>{let t=Math.max(-Ie(),Math.min(W(),e)),n=Math.min(1,Math.max(0,t/Math.max(1,W())));H.setSwipe({progress:n,x:O?t*xe:0,y:O?0:t*xe})},release:(e,t,n)=>{_.style.setProperty(`--drawer-swipe-strength`,String(Math.max(.1,Math.min(1,1/Math.max(1,Math.abs(t)))))),(e>W()*.35||t>.5&&e>12)&&Q(!1,`swipe`,n)},reset:Ke})),N.push(h(y,`resize`,G)),y.ResizeObserver!==void 0){let e=new y.ResizeObserver(G);e.observe(_),N.push(()=>e.disconnect())}let $=(e,t)=>N.push(h(o,e,e=>{e.target===o&&t(e)}));$(`drawer:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n=typeof t.triggerId==`string`?T.find(e=>e.id===t.triggerId)??z:z;`snapPoint`in t&&We(t.snapPoint,`imperative-action`,e),typeof t.open==`boolean`&&Q(t.open,`imperative-action`,e,n)}),$(`drawer:open`,e=>Q(!0,`imperative-action`,e)),$(`drawer:close`,e=>Q(!1,`imperative-action`,e)),$(`drawer:toggle`,e=>Q(!P,`imperative-action`,e)),$(`drawer:unmount`,()=>{I=!1,q()}),_.style.setProperty(`--drawer-swipe-strength`,`1`),U(),G(),Ke(),q();let qe={open:e=>Q(!0,`imperative-action`,void 0,T.find(t=>t.id===e)??z),close:()=>Q(!1),toggle:()=>Q(!P),setSnapPoint:e=>We(e),unmount:()=>{I=!1,q()},get isOpen(){return P},get snapPoint(){return k},get triggerId(){return z?.id??null},destroy(){if(F)return;let t=P&&K(v.activeElement);F=!0,P=!1,B++,V.forEach(e=>y.cancelAnimationFrame(e)),V.clear(),J.forEach(e=>e.cleanup()),Y.destroy(),L?.(),De&&=(ae(),!1),N.forEach(e=>e()),U(),H.destroy();for(let e of[_,x,S,b])e&&(e.hidden=!0);M.cleanup(),Pe===null&&_.removeAttribute(`tabindex`),t&&ze(),e(o,C,qe)}};return ie(o,C,qe),(p.open??p.defaultOpen??d(o,`defaultOpen`)??!1)&&Q(!0,`none`),qe}function _e(e=document){return p(e,`drawer`).filter(e=>!m(e,C)).map(e=>T(e))}export{_e as create,T as createDrawer}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-dropdown-menu.js b/liquid/vendor/data-slot-dropdown-menu.js new file mode 100644 index 000000000..9c4a2f6d2 --- /dev/null +++ b/liquid/vendor/data-slot-dropdown-menu.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createDismissLayer as r,createPortalLifecycle as i,createPositionSync as a,createPresenceLifecycle as o,createTerminalLifecycle as s,createTypeahead as c,emit as l,ensureId as u,ensureItemVisibleInContainer as d,focusElement as f,getDataBool as p,getDataEnum as m,getDataNumber as h,getDataString as g,getOwnedElements as _,getPart as v,getRoots as y,hasRootBinding as b,lockScroll as ee,measurePopupContentRect as te,on as x,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setAria as S,setRootBinding as ae,unlockScroll as oe}from"@data-slot/core";const C=[`top`,`right`,`bottom`,`left`],w=[`start`,`center`,`end`],T=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),se=e=>{try{let t=JSON.parse(e?.trim()||`[]`);return Array.isArray(t)?t.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(Boolean):[]}catch{return[]}},ce=(e,t,n,r)=>{let i=(r,i)=>m(t,r,i)??(n?m(n,r,i):void 0)??m(e,r,i),a=r=>h(t,r)??(n?h(n,r):void 0)??h(e,r),o=r=>p(t,r)??(n?p(n,r):void 0)??p(e,r),s=T(r,`defaultValue`),c=T(r,`defaultValues`),l=e.hasAttribute(`data-default-value`),u=e.hasAttribute(`data-default-values`);return{defaultOpen:r.defaultOpen??p(e,`defaultOpen`)??!1,closeOnClickOutside:r.closeOnClickOutside??p(e,`closeOnClickOutside`)??!0,closeOnEscape:r.closeOnEscape??p(e,`closeOnEscape`)??!0,closeOnSelect:r.closeOnSelect??p(e,`closeOnSelect`)??!0,preferredSide:r.side??i(`side`,C)??`bottom`,preferredAlign:r.align??i(`align`,w)??`start`,sideOffset:r.sideOffset??a(`sideOffset`)??4,alignOffset:r.alignOffset??a(`alignOffset`)??0,avoidCollisions:r.avoidCollisions??o(`avoidCollisions`)??!0,collisionPadding:r.collisionPadding??a(`collisionPadding`)??8,lockScroll:r.lockScroll??p(e,`lockScroll`)??!0,highlightItemOnHover:r.highlightItemOnHover??p(e,`highlightItemOnHover`)??!0,optionsHasDefaultValue:s,optionsHasDefaultValues:c,rootHasDefaultValue:l,rootHasDefaultValues:u,requestedDefaultValue:s?r.defaultValue??null:l?g(e,`defaultValue`)??null:null,requestedDefaultValues:c?r.defaultValues??[]:u?se(g(e,`defaultValues`)):[]}},E=`[data-slot="dropdown-menu-item"], [data-slot="dropdown-menu-radio-item"], [data-slot="dropdown-menu-checkbox-item"]`,le=e=>{let t=e.getAttribute(`data-slot`);return t===`dropdown-menu-radio-item`?`radio`:t===`dropdown-menu-checkbox-item`?`checkbox`:`item`},D=e=>e.el.hasAttribute(`disabled`)||e.el.hasAttribute(`data-disabled`)||e.el.getAttribute(`aria-disabled`)===`true`||e.type!==`item`&&e.value===null,ue=(e,t)=>{let n=[],r=[],i=new Map,a=new WeakSet,o=()=>n.filter(e=>e.type===`radio`),s=()=>n.filter(e=>e.type===`checkbox`),c=e=>n.find(t=>t.el===e)??null,l=e=>c(e instanceof Element?e.closest(E):null),u=(e,t=n)=>t.find(t=>t.type===`radio`&&t.value===e)??null,d=e=>e?e.type===`item`?e.value||e.el.textContent?.trim()||``:e.value:null,f=e=>r.find(t=>d(t)===e)??null,p=(e,t)=>{let n=s().filter(e=>e.value!==null);if(n.length===0)return null;if(e.length===0)return[];let r=new Set(e.filter(e=>typeof e==`string`).map(e=>e.trim()).filter(Boolean));if(r.size===0)return t===`init`?[]:null;let i=n.flatMap(e=>e.value&&r.has(e.value)?[e.value]:[]);return i.length>0||t===`init`?i:null},m=(e,t,r=n)=>{let i=new Set(e),a=new Set(t),o=null,s=null,c=null;for(let e of r){if(e.type!==`checkbox`||!e.value)continue;let t=i.has(e.value),n=a.has(e.value);if(t!==n){if(o!==null)return{changedValue:null,checked:null,item:null};o=e.value,s=n,c=e.el}}return{changedValue:o,checked:s,item:c}},h=(t,r)=>{for(let e of n){if(e.type===`item`)continue;let n=e.value!==null&&(e.type===`radio`?t===e.value:r.includes(e.value));e.el.hasAttribute(`data-checked`)!==n&&(e.el.toggleAttribute(`data-checked`,n),S(e.el,`checked`,n))}t!==null&&o().length>0?e.setAttribute(`data-value`,t):e.removeAttribute(`data-value`)},g=(e,t)=>{e!==t&&(e?.removeAttribute(`data-highlighted`),t?.setAttribute(`data-highlighted`,``))};return{refresh:o=>{let s=n;n=_(e,t,E).map(e=>({el:e,type:le(e),value:e.dataset.value?.trim()||null})),r=[],i=new Map;for(let e of n){a.has(e.el)||(a.add(e.el),e.el.setAttribute(`role`,e.type===`radio`?`menuitemradio`:e.type===`checkbox`?`menuitemcheckbox`:`menuitem`),e.el.tabIndex=-1,e.el.removeAttribute(`data-checked`),S(e.el,`checked`,e.type!==`item`&&e.value!==null?!1:null));let t=D(e);S(e.el,`disabled`,t||null),t||i.set(e.el,r.push(e)-1)}let c=o.value!==null&&u(o.value)?o.value:null,l=o.values.length?p(o.values,`init`)??[]:[],d=o.highlightedItem&&i.has(o.highlightedItem)?o.highlightedItem:null;return h(c,l),g(o.highlightedItem,d),{value:c,values:l,highlightedItem:d,previousItems:s}},synchronizeSelection:h,highlight:g,recordFor:c,fromTarget:l,radios:o,checkboxes:s,radioFor:u,valueFor:d,itemForValue:f,checkboxValues:p,checkboxDiff:m,isDisabled:D,get enabled(){return r},enabledIndex:e=>i.get(e),isEnabled:e=>i.has(e)}},O=`@data-slot/dropdown-menu`,de=(e,t)=>e.length===t.length&&e.every((e,n)=>e===t[n]),fe=(e,t,n,r=!1)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:r,detail:n}));function k(m,h={}){let g=ie(m,O,`[@data-slot/dropdown-menu] createDropdownMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let _=v(m,`dropdown-menu-trigger`),y=v(m,`dropdown-menu-content`),b=v(m,`dropdown-menu-positioner`),C=b&&y&&b.contains(y)?b:null,w=v(m,`dropdown-menu-portal`),T=w&&C&&w.contains(C)?w:null;if(!_||!y)throw Error(`DropdownMenu requires trigger and content slots`);let{defaultOpen:se,closeOnClickOutside:E,closeOnEscape:le,closeOnSelect:D,preferredSide:k,preferredAlign:pe,sideOffset:me,alignOffset:he,avoidCollisions:ge,collisionPadding:_e,lockScroll:A,highlightItemOnHover:j,optionsHasDefaultValue:ve,optionsHasDefaultValues:ye,rootHasDefaultValue:be,rootHasDefaultValues:xe,requestedDefaultValue:Se,requestedDefaultValues:Ce}=ce(m,y,C,h),we=h.onOpenChange,Te=h.onSelect,Ee=h.onValueChange,De=h.onValuesChange,M=!1,N=null,P=[],F=null,I=null,L=c(),R=!1,z=!1,B=s();B.onDestroy(()=>{z&&=(oe(),!1)});let V=[],H=i({content:y,root:m,wrapperSlot:C?void 0:`dropdown-menu-positioner`,container:C??void 0,mountTarget:C?T??C:void 0}),U=ue(m,y),Oe=e=>e.pointerType!==`touch`,W=()=>U.synchronizeSelection(N,P),G=({source:e=`programmatic`,emitSelectionInvalidation:t=!1}={})=>{let n=N,r=[...P],i=U.refresh({value:n,values:r,highlightedItem:F}),{previousItems:a}=i;if(N=i.value,P=i.values,F=i.highlightedItem,t&&(n!==N&&Ae({value:N,previousValue:n,item:N===null?null:U.radioFor(N)?.el??null,previousItem:n===null?null:U.radioFor(n,a)?.el??null,source:e}),!de(r,P))){let t=U.checkboxDiff(r,P,a);je({values:[...P],previousValues:r,changedValue:t.changedValue,checked:t.checked,item:t.item,source:e})}},ke=e=>{l(m,`dropdown-menu:open-change`,e),l(m,`dropdown-menu:change`,e),we?.(e.open)},Ae=e=>{l(m,`dropdown-menu:value-change`,e),Ee?.(e.value)},je=e=>{l(m,`dropdown-menu:values-change`,e),De?.([...e.values])},Me=()=>{let e=H.container,r=m.ownerDocument.defaultView??window,i=_.getBoundingClientRect(),a=t({anchorRect:i,contentRect:te(y),side:k,align:pe,sideOffset:me,alignOffset:he,avoidCollisions:ge,collisionPadding:_e}),o=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.transform=A?`translate3d(${a.x}px, ${a.y}px, 0)`:`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.position=A?`fixed`:`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.setProperty(`--transform-origin`,o),e.style.willChange=`transform`,e.style.margin=`0`,y.setAttribute(`data-side`,a.side),y.setAttribute(`data-align`,a.align),e!==y&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},K=a({observedElements:[_,y],isActive:()=>M,ancestorScroll:A,onUpdate:Me}),q=!1,Ne=()=>{q=!1,I&&document.contains(I)?f(I):document.contains(_)&&f(_),I=null},Pe=()=>{q=!0,B.trackRaf(Ne)};B.onBeforeDestroy(()=>{q&&B.trackFinalRaf(Ne)});let J=o({element:y,onExitComplete:()=>{B.isDestroyed||(H.restore(),y.hidden=!0,Pe())}}),Y=e=>{for(let t of[m,y])t.setAttribute(`data-state`,e),t.toggleAttribute(`data-open`,e===`open`),t.toggleAttribute(`data-closed`,e===`closed`)},X=(e,{source:t,focus:n=!0,focusContentOnClear:r=!1})=>{if(e&&!U.isEnabled(e))return!1;let i=F;return i===e?(e&&n?(d(e,y),f(e)):!e&&r&&f(y),!1):(F=e,U.highlight(i,e),e?(d(e,y),n&&f(e)):r&&f(y),l(m,`dropdown-menu:highlight-change`,{value:U.valueFor(U.recordFor(e)),previousValue:U.valueFor(U.recordFor(i)),item:e,previousItem:i,source:t}),!0)},Z=(e,t,n=!0)=>{if(G({source:t,emitSelectionInvalidation:n}),U.radios().length===0)return!1;let r=e===null?null:U.radioFor(e);if(e!==null&&!r||N===e)return!1;let i=N,a=i===null?null:U.radioFor(i);return N=e,W(),n&&Ae({value:N,previousValue:i,item:r?.el??null,previousItem:a?.el??null,source:t}),!0},Fe=(e,t,n=!0)=>{G({source:t,emitSelectionInvalidation:n});let r=U.checkboxValues(e,n?`set`:`init`);if(r===null||de(P,r))return!1;let i=[...P],a=U.checkboxDiff(i,r);return P=r,W(),n&&je({values:[...P],previousValues:i,changedValue:a.changedValue,checked:a.checked,item:a.item,source:t}),!0},Ie=()=>{if(G(),ve||be)Se===null?N=null:Z(Se,`programmatic`,!1);else for(let e of U.radios())if(e.value!==null&&p(e.el,`defaultChecked`)){N=e.value;break}if(ye||xe)P=U.checkboxValues(Ce,`init`)??[];else{let e=U.checkboxes().filter(e=>e.value!==null&&p(e.el,`defaultChecked`)).map(e=>e.value);P=U.checkboxValues(e,`init`)??[]}W()},Q=(e,{source:t,reason:n})=>{if(B.isDestroyed||M===e)return;let r=M;e?(I=document.activeElement,M=!0,S(_,`expanded`,!0),H.mount(),y.hidden=!1,Y(`open`),J.enter(),A&&!z&&(ee(),z=!0),G({source:t===`restore`?`restore`:`programmatic`,emitSelectionInvalidation:t!==`init`}),R=!1,L.reset(),K.start(),Me(),f(y)):(M=!1,S(_,`expanded`,!1),Y(`closed`),X(null,{source:t===`init`?`programmatic`:t,focus:!1}),L.reset(),R=!1,z&&=(oe(),!1),K.stop(),J.exit()),ke({open:M,previousOpen:r,source:t,reason:n})},Le=(e,t)=>{if(U.isDisabled(e))return;let n=U.valueFor(e);if(n===null)return;let r;if(e.type===`radio`?r=!0:e.type===`checkbox`&&e.value!==null&&(r=!P.includes(e.value)),fe(m,`dropdown-menu:select`,{value:n,item:e.el,itemType:e.type,source:t,checked:r},!0)){if(Te?.(n),e.type===`radio`)Z(e.value,t,!0);else if(e.type===`checkbox`&&e.value!==null){let n=new Set(P);n.has(e.value)?n.delete(e.value):n.add(e.value),Fe([...n],t,!0)}D&&Q(!1,{source:t,reason:`select`})}},Re=e=>{let t=F?U.enabledIndex(F)??-1:-1,n=L.match(e,U.enabled.map(e=>e.el.textContent?.trim()??``),t);n!==-1&&(R=!0,X(U.enabled[n]?.el??null,{source:`keyboard`,focus:!0}))},ze=e=>{let t=e.source??`programmatic`;if(e.value!==void 0&&Z(e.value,t,!0),e.values!==void 0&&Fe(e.values,t,!0),e.open!==void 0&&Q(e.open,{source:t,reason:`programmatic`}),e.highlightedValue!==void 0){if(!M)return;if(e.highlightedValue===null)X(null,{source:t,focus:!1,focusContentOnClear:!0});else{let n=U.itemForValue(e.highlightedValue);n&&X(n.el,{source:t,focus:!0})}}},Be=u(_,`dropdown-menu-trigger`),Ve=u(y,`dropdown-menu-content`);_.setAttribute(`aria-haspopup`,`menu`),_.setAttribute(`aria-controls`,Ve),y.setAttribute(`role`,`menu`),y.setAttribute(`aria-labelledby`,Be),y.tabIndex=-1,S(_,`expanded`,!1),y.hidden=!0,Y(`closed`),Ie(),V.push(x(_,`click`,()=>{Q(!M,{source:`pointer`,reason:`trigger`})}),x(_,`keydown`,e=>{(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)&&!M&&(e.preventDefault(),Q(!0,{source:`keyboard`,reason:`trigger`}))})),V.push(x(y,`keydown`,e=>{if(e.key===`Tab`){Q(!1,{source:`keyboard`,reason:`tab`});return}let t=U.enabled.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),R=!0,X(U.enabled[F?((U.enabledIndex(F)??-1)+1)%t:0]?.el??null,{source:`keyboard`,focus:!0});break;case`ArrowUp`:e.preventDefault(),R=!0,X(U.enabled[F?(U.enabledIndex(F)-1+t)%t:t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Home`:e.preventDefault(),R=!0,X(U.enabled[0]?.el??null,{source:`keyboard`,focus:!0});break;case`End`:e.preventDefault(),R=!0,X(U.enabled[t-1]?.el??null,{source:`keyboard`,focus:!0});break;case`Enter`:case` `:if(e.preventDefault(),F){let e=U.recordFor(F);e&&Le(e,`keyboard`)}break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Re(e.key.toLowerCase()))}}),x(y,`click`,e=>{let t=U.fromTarget(e.target);t&&Le(t,`pointer`)}),x(y,`pointermove`,e=>{if(!j||!Oe(e))return;let t=U.fromTarget(e.target)?.el??null;R&&(R=!1,t&&t===F)||(t&&U.isEnabled(t)?X(t,{source:`pointer`,focus:!0}):F&&X(null,{source:`pointer`,focus:!1,focusContentOnClear:!0}))}),x(y,`pointerleave`,e=>{!j||!Oe(e)||R||!F||X(null,{source:`pointer`,focus:!1,focusContentOnClear:!0})})),V.push(r({root:m,isOpen:()=>M,onDismiss:({reason:e})=>{Q(!1,e===`escape-key`?{source:`keyboard`,reason:`escape`}:{source:`pointer`,reason:`outside`})},closeOnClickOutside:E,closeOnEscape:le})),V.push(ne(m,`dropdown-menu:set`,e=>{let t=e.detail;if(!t||typeof t!=`object`)return;let n={source:t.source===`restore`||t.source===`programmatic`?t.source:void 0};t.open!==void 0&&(n.open=t.open),t.values!==void 0&&(n.values=Array.isArray(t.values)?t.values.filter(e=>typeof e==`string`):void 0),t.highlightedValue!==void 0&&(n.highlightedValue=t.highlightedValue===null||typeof t.highlightedValue==`string`?t.highlightedValue:void 0),t.value!==void 0&&(typeof t.value==`boolean`&&t.open===void 0?n.open=t.value:(t.value===null||typeof t.value==`string`)&&(n.value=t.value)),ze(n)}));let $={open:()=>!B.isDestroyed&&Q(!0,{source:`programmatic`,reason:`programmatic`}),close:()=>!B.isDestroyed&&Q(!1,{source:`programmatic`,reason:`programmatic`}),toggle:()=>!B.isDestroyed&&Q(!M,{source:`programmatic`,reason:`programmatic`}),set:e=>{B.isDestroyed||ze(e)},get isOpen(){return M},get value(){return N},get values(){return[...P]},get highlightedValue(){return U.valueFor(U.recordFor(F))},destroy:()=>{B.destroy()&&(L.destroy(),M=!1,S(_,`expanded`,!1),Y(`closed`),y.hidden=!0)}};return re(B,{cleanups:V,positionSync:K,presence:J,portal:H,unbind:()=>e(m,O,$)}),ae(m,O,$),se&&Q(!0,{source:`init`,reason:`init`}),$}function pe(e=document){let t=[];for(let n of y(e,`dropdown-menu`))b(n,O)||t.push(k(n));return t}export{pe as create,k as createDropdownMenu}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-hover-card.js b/liquid/vendor/data-slot-hover-card.js new file mode 100644 index 000000000..c9be3f671 --- /dev/null +++ b/liquid/vendor/data-slot-hover-card.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as ee,createPresenceLifecycle as te,createTerminalLifecycle as ne,emit as re,ensureId as ie,getDataBool as o,getDataEnum as s,getDataNumber as c,getPart as l,getRoots as u,hasRootBinding as d,measurePopupContentRect as ae,on as f,onRoot as oe,registerFloatingTerminalResources as se,reuseRootBinding as ce,setAria as p,setRootBinding as le}from"@data-slot/core";const ue=[`top`,`right`,`bottom`,`left`],de=[`start`,`center`,`end`];let fe=0;const m=new Set,h=new Set,g=(e,t)=>{if(!e)return!1;for(let n of m)if(n!==t&&n.contains(e))return!0;return!1},_=(e,t)=>{for(let n of h)n(e,t)},v=`@data-slot/hover-card`;function y(u,d={}){let y=ce(u,v,`[@data-slot/hover-card] createHoverCard() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=l(u,`hover-card-trigger`),x=l(u,`hover-card-content`),S=l(u,`hover-card-positioner`),C=S&&x&&S.contains(x)?S:null,pe=l(u,`hover-card-portal`),me=pe&&C&&pe.contains(C)?pe:null;if(!b||!x)throw Error(`Hover-card requires trigger and content slots`);let he=d.open!==void 0,ge=d.defaultOpen??o(u,`defaultOpen`)??!1,_e=d.delay??c(u,`delay`)??700,w=d.skipDelayDuration??c(u,`skipDelayDuration`)??300,ve=d.closeDelay??c(u,`closeDelay`)??300,ye=d.onOpenChange,be=d.closeOnClickOutside??o(u,`closeOnClickOutside`)??!0,xe=d.closeOnEscape??o(u,`closeOnEscape`)??!0,Se=d.portal??o(x,`portal`)??o(u,`portal`)??!0,Ce=(e,t)=>s(x,e,t)??(C?s(C,e,t):void 0)??s(u,e,t),T=e=>c(x,e)??(C?c(C,e):void 0)??c(u,e),we=e=>o(x,e)??(C?o(C,e):void 0)??o(u,e),Te=d.side??Ce(`side`,ue)??`bottom`,Ee=d.align??Ce(`align`,de)??`center`,De=d.sideOffset??T(`sideOffset`)??4,Oe=d.alignOffset??T(`alignOffset`)??0,ke=d.avoidCollisions??we(`avoidCollisions`)??!0,Ae=d.collisionPadding??T(`collisionPadding`)??8,E=d.open??ge,D=!1,O=ne(),k=!1,A=!1,j=!1,M=null,N=null,P=-1/0,F=-1/0,I=[],L=a({content:x,root:u,enabled:Se,wrapperSlot:C?void 0:`hover-card-positioner`,container:C??void 0,mountTarget:C?me??C:void 0}),R=r({root:u,target:me??C??x,strategy:d.mountStrategy??s(u,`mountStrategy`,[`lazy`,`eager`])??`lazy`});I.push(()=>R.cleanup());let je=ie(x,`hover-card-content`);b.setAttribute(`aria-haspopup`,`dialog`),E?b.setAttribute(`aria-controls`,je):b.removeAttribute(`aria-controls`),x.setAttribute(`data-side`,Te),x.setAttribute(`data-align`,Ee);let z=()=>b.hasAttribute(`disabled`)||b.getAttribute(`aria-disabled`)===`true`,B=()=>{M&&=(O.cancelTimeout(M),null)},V=()=>{N&&=(O.cancelTimeout(N),null)},H=()=>{B(),V()},U=()=>{H(),k=!1,A=!1,j=!1},Me=(e,t)=>{re(u,`hover-card:change`,{open:e,reason:t,trigger:b,content:x}),ye?.(e)},W=()=>{let e=L.container,r=u.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ae(x),side:Te,align:Ee,sideOffset:De,alignOffset:Oe,avoidCollisions:ke,collisionPadding:Ae}),ee=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,ee),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align))},G=e=>{let t=L.container;if(u.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),t!==x&&t.setAttribute(`data-state`,e),e===`open`){u.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),t!==x&&t.setAttribute(`data-open`,``),D?(u.setAttribute(`data-instant`,``),x.setAttribute(`data-instant`,``),t!==x&&t.setAttribute(`data-instant`,``)):(u.removeAttribute(`data-instant`),x.removeAttribute(`data-instant`),t!==x&&t.removeAttribute(`data-instant`)),u.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`),t!==x&&t.removeAttribute(`data-closed`);return}u.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),t!==x&&t.setAttribute(`data-closed`,``),D?(u.setAttribute(`data-instant`,``),x.setAttribute(`data-instant`,``),t!==x&&t.setAttribute(`data-instant`,``)):(u.removeAttribute(`data-instant`),x.removeAttribute(`data-instant`),t!==x&&t.removeAttribute(`data-instant`)),u.removeAttribute(`data-open`),x.removeAttribute(`data-open`),t!==x&&t.removeAttribute(`data-open`)},K=te({element:x,onExitComplete:()=>{O.isDestroyed||(x.contains(u.ownerDocument.activeElement)&&(P=-1/0,b.focus()),L.restore(),x.hidden=!0,R.unmount())}}),q=ee({observedElements:[b,x],isActive:()=>E,ancestorScroll:!1,onUpdate:W}),Ne=(e,t,n=!1)=>{O.isDestroyed||E!==e&&(!e&&E&&w>0&&(fe=Date.now()+w),D=n,E=e,p(b,`expanded`,E),e?(R.mount(),L.mount(),b.setAttribute(`aria-controls`,je),x.hidden=!1,G(`open`),K.enter(),W(),q.start()):(b.removeAttribute(`aria-controls`),G(`closed`),K.exit(),q.stop()),Me(E,t))},J=(e,t,n=!1)=>{if(E!==e){if(he){Me(e,t);return}Ne(e,t,n)}},Y=(e,t,n=!1)=>{Ne(e,t,n)},X=(e,t=!1)=>{U(),J(!1,e,t)},Pe=(e,t=!1)=>{U(),Y(!1,e,t)},Fe=(e,t)=>{e===b||!E||X(t,!0)};m.add(b),h.add(Fe),I.push(()=>{h.delete(Fe),m.delete(b)});let Z=e=>{if(V(),B(),w>0&&Date.now(){M=null,!O.isDestroyed&&J(!0,e)},_e)},Ie=e=>{if(B(),V(),ve<=0){J(!1,e);return}N=O.trackTimeout(()=>{N=null,!O.isDestroyed&&J(!1,e)},ve)},Q=e=>{k||A||j||Ie(e)};p(b,`expanded`,E),G(E?`open`:`closed`),x.hidden=!E,E&&(L.mount(),K.enter(),x.hidden=!1,W(),q.start()),I.push(f(u.ownerDocument,`keydown`,e=>{e.key===`Tab`&&(P=Date.now())},{capture:!0}),f(u.ownerDocument,`pointerdown`,()=>{P=-1/0,F=-1/0},{capture:!0}),f(u.ownerDocument,`pointermove`,e=>{e.pointerType!==`touch`&&(F=Date.now())},{capture:!0}),f(b,`pointerenter`,e=>{e.pointerType!==`touch`&&(k=!0,!z()&&(Date.now()-F>250||(_(b,`pointer`),Z(`pointer`))))}),f(b,`pointermove`,e=>{e.pointerType!==`touch`&&(!k||z()||E||M||(_(b,`pointer`),Z(`pointer`)))}),f(b,`pointerleave`,e=>{if(e.pointerType===`touch`)return;k=!1;let t=e.relatedTarget;if(!(t&&x.contains(t))){if(g(t,b)){X(`pointer`,!0);return}Q(`pointer`)}})),I.push(f(x,`pointerenter`,e=>{e.pointerType!==`touch`&&(A=!0,V())}),f(x,`pointerleave`,e=>{if(e.pointerType===`touch`)return;A=!1;let t=e.relatedTarget;if(!(t&&b.contains(t))){if(g(t,b)){X(`pointer`,!0);return}Q(`pointer`)}})),I.push(f(b,`focusin`,()=>{z()||Date.now()-P>750||(j=!0,_(b,`focus`),Z(`focus`))}),f(b,`focusout`,e=>{let t=e.relatedTarget;if(!(t&&(b.contains(t)||x.contains(t)))){if(j=!1,g(t,b)){X(`blur`,!0);return}Q(`blur`)}}),f(x,`focusin`,()=>{j=!0,V()}),f(x,`focusout`,e=>{let t=e.relatedTarget;if(!(t&&(b.contains(t)||x.contains(t)))){if(j=!1,g(t,b)){X(`blur`,!0);return}Q(`blur`)}})),I.push(i({root:u,isOpen:()=>E,onDismiss:()=>X(`dismiss`),closeOnClickOutside:be,closeOnEscape:xe})),I.push(oe(u,`hover-card:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&(n?(H(),Y(!0,`api`)):Pe(`api`))}));let $={open:()=>{O.isDestroyed||z()||(H(),J(!0,`api`))},close:()=>{O.isDestroyed||X(`api`)},toggle:()=>{O.isDestroyed||!E&&z()||(E?X(`api`):(H(),J(!0,`api`)))},setOpen:e=>{O.isDestroyed||(e?(H(),Y(!0,`api`)):Pe(`api`))},get isOpen(){return E},destroy:()=>{O.destroy()&&(U(),E=!1,p(b,`expanded`,!1),b.removeAttribute(`aria-controls`),G(`closed`),x.hidden=!0)}};return se(O,{cleanups:I,positionSync:q,presence:K,portal:L,unbind:()=>e(u,v,$)}),E||R.unmount(),le(u,v,$),$}function b(e=document){let t=[];for(let n of u(e,`hover-card`))d(n,v)||t.push(y(n));return t}export{b as create,y as createHoverCard}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-navigation-menu.js b/liquid/vendor/data-slot-navigation-menu.js new file mode 100644 index 000000000..436911e95 --- /dev/null +++ b/liquid/vendor/data-slot-navigation-menu.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,containsWithPortals as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as o,createPresenceLifecycle as s,createTerminalLifecycle as c,drainCleanups as l,emit as u,ensureId as d,getDataBool as f,getDataEnum as p,getDataNumber as m,getOwnedElements as h,getPart as g,getParts as ee,getRoots as _,getTabbables as v,hasRootBinding as te,on as y,onRoot as b,reuseRootBinding as x,setAria as S,setRootBinding as C}from"@data-slot/core";function w(e,t,n,r){let i=c(),a=null,o=null,s=()=>{o!==null&&(i.cancelRaf(o),o=null)},l=r=>{if(!e)return;if(a=r,!r){s(),e.removeAttribute(`data-instant`),e.setAttribute(`data-state`,`hidden`);return}e.getAttribute(`data-state`)!==`visible`&&(s(),e.setAttribute(`data-instant`,``),o=i.trackRaf(()=>{o=i.trackRaf(()=>{e?.removeAttribute(`data-instant`),o=null})}));let c=t.getBoundingClientRect(),l=r.getBoundingClientRect();e.style.setProperty(`--indicator-left`,`${l.left-c.left}px`),e.style.setProperty(`--indicator-width`,`${l.width}px`),e.style.setProperty(`--indicator-top`,`${l.top-c.top}px`);let u=n&&parseFloat(getComputedStyle(n).marginTop)||0;e.style.setProperty(`--indicator-height`,`${l.height-(u<1?1:0)}px`),e.setAttribute(`data-state`,`visible`)};return{show:l,sync:(e=null)=>l(r()??e),get hovered(){return a},destroy(){i.destroy(),s(),a=null}}}const T=`a[href], button:not([disabled]), [role="link"], [role="button"], [tabindex]:not([tabindex="-1"])`,E=e=>e.replace(/[^a-z0-9\-_:.]/gi,`-`);function ne(e,t,n){let r=new Map,i=[],a=0;for(let t of n){let n=t.dataset.value;if(!n)continue;let o=h(e,t,`[data-slot="navigation-menu-trigger"]`)[0]??null,s=h(e,t,`[data-slot="navigation-menu-content"]`)[0]??null;if(!o||!s)continue;let c={value:n,item:t,trigger:o,content:s,index:a++};i.push(c),r.set(n,c);let l=E(n),u=d(o,`nav-menu-trigger-${l}`);d(s,`nav-menu-content-${l}`),o.setAttribute(`aria-haspopup`,`true`),o.removeAttribute(`aria-controls`),s.setAttribute(`aria-labelledby`,u)}let o=e=>{let t=e?.closest(`[data-slot="navigation-menu-item"]`);if(!t)return null;let n=t.dataset.value,i=n?r.get(n):null;return i?.item===t?i:null},s=t=>{if(t.matches(T))return t;for(let n of h(e,t,T))if(!n.closest(`[data-slot="navigation-menu-content"]`)&&!n.hidden&&!n.closest(`[hidden]`))return n;return null},c=[],l=new Map;for(let e of n){let t=o(e),n=t?{kind:`submenu`,element:t.trigger,item:t.item,value:t.value,trigger:t.trigger}:(()=>{let t=s(e);return t?{kind:`plain`,element:t}:null})();n&&(c.push(n),l.set(n.element,n),n.kind===`submenu`&&l.set(n.item,n))}return{allItems:i,itemMap:r,navigables:c,navigableByTarget:e=>{let n=e instanceof HTMLElement?e:null;for(;n&&n!==t;){let e=l.get(n);if(e)return e;n=n.parentElement}return null},isNonSubmenuListTarget(e){if(!(e instanceof Node)||!t.contains(e))return!1;let n=e instanceof HTMLElement?e:e.parentElement;return!n||n.closest(`[data-slot="navigation-menu-indicator"]`)||o(n)?!1:!!n.closest(`[data-slot="navigation-menu-item"], a[href], button, [role="link"], [role="button"]`)}}}const D=e=>{let t=getComputedStyle(e),n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;if(e instanceof HTMLElement){let t=e.offsetWidth||n,i=e.offsetHeight||r;(Math.round(n)!==t||Math.round(r)!==i)&&(n=t,r=i)}return{width:n,height:r}};function O(e){let t=new WeakMap,n=null,r=null,i={committed:{width:0,height:0},live:{width:0,height:0}},o=()=>n?.popup??x().popup,c=()=>n?.positioner??x().positioner,l=()=>{let{viewport:t,root:r}=e;if(!t)return!1;if(n)return!0;let i=x(),o=i.popup,c=i.positioner,l=i.portal,u=!1,d=!1,f=!1;(!o||!o.contains(t))&&(o=S(t,`navigation-menu-popup`),u=!0),(!c||!c.contains(o))&&(c=S(o,`navigation-menu-positioner`),d=!0),(!l||!l.contains(c))&&(l=S(c,`navigation-menu-portal`),f=!0);let p=a({content:o,root:r,enabled:!0,container:c,mountTarget:l}),m,h=s({element:t,onExitComplete:()=>{m.viewportExitComplete=!0,t.setAttribute(`data-ending-style`,``),y(m)}}),g=s({element:o,onExitComplete:()=>{m.popupExitComplete=!0,o.setAttribute(`data-ending-style`,``),y(m)}});return m={popup:o,positioner:c,portal:l,popupPortal:p,popupPresence:g,viewportPresence:h,generatedPopup:u,generatedPositioner:d,generatedPortal:f,isClosing:!1,popupExitComplete:!1,viewportExitComplete:!1},n=m,w(o),!0},u=()=>{if(!l())return;let e=n;e.popupPortal.mount(),e.isClosing=!1,e.popupExitComplete=!1,e.viewportExitComplete=!1},d=t=>{let r=n,i=e.viewport;!r||!i||(r.popup.hidden=!1,r.popup.style.pointerEvents=`auto`,i.hidden=!1,t&&(r.popupPresence.enter(),r.viewportPresence.enter()))},f=e=>{let t=n;t&&(e.width>0&&e.height>0&&(ne(t.popup,e),_(t.positioner,e),ee(e.width,e.height)),t.isClosing=!0,t.popupExitComplete=!1,t.viewportExitComplete=!1,t.popupPresence.exit(),t.viewportPresence.exit())},p=()=>{let e=o();if(!e)return{...i.committed};let t=D(e),n={width:e.offsetWidth||t.width||i.live.width||i.committed.width,height:e.offsetHeight||t.height||i.live.height||i.committed.height};return E(n),n},m=()=>{let e=i.committed;if(e.width<=0||e.height<=0)return!1;let t=p();return Math.abs(t.width-e.width)>1||Math.abs(t.height-e.height)>1},h=(e,t)=>{if(!l())return t;let r=n,a={...i.committed},o=e===`sync-current`?p():O(r),s={width:e===`sync-current`?a.width||o.width||t.width:o.width||t.width,height:e===`sync-current`?a.height||o.height||t.height:o.height||t.height};return E(o.width>0&&o.height>0?o:s),e!==`sync-current`&&T(s),s},g=(e,t)=>{let n=o();if(n){if(e!==`sync-current`){ne(n,t);return}(!n.style.getPropertyValue(`--popup-width`).trim()||!n.style.getPropertyValue(`--popup-height`).trim())&&ne(n,{width:i.committed.width||t.width,height:i.committed.height||t.height})}},ee=(t,n)=>{let r=e.viewport;r&&(r.style.setProperty(`--viewport-width`,`${t}px`),r.style.setProperty(`--viewport-height`,`${n}px`))},_=(e,t)=>{e.style.setProperty(`--positioner-width`,t===`max-content`?`max-content`:`${t.width}px`),e.style.setProperty(`--positioner-height`,t===`max-content`?`max-content`:`${t.height}px`)},v=e=>{let n=o();if(!n)return;if(e===null){k(n);return}let r=t.get(n)??{applied:!1,bottom:``,left:``,position:``,right:``,top:``};r.applied||Object.assign(r,{position:n.style.position,top:n.style.top,right:n.style.right,bottom:n.style.bottom,left:n.style.left}),r.applied=!0,t.set(n,r),n.style.position=`absolute`,e===`top`?Object.assign(n.style,{top:``,right:``,bottom:`0px`,left:`0px`}):Object.assign(n.style,{top:`0px`,right:`0px`,bottom:``,left:``})},te=()=>{b(!0)},y=t=>{e.isDestroyed()||n!==t||!t.isClosing||!t.popupExitComplete||!t.viewportExitComplete||e.hasExitingContent?.()||b(!1)},b=t=>{let a=n,o=e.viewport;if(!a||!o||!t&&e.isDestroyed())return;r?.disconnect(),r=null,e.beforeRestore(),A(a),i={committed:{width:0,height:0},live:{width:0,height:0}},o.hidden=!0,o.style.pointerEvents=`none`,a.popup.hidden=!0,a.popup.style.pointerEvents=`none`;let{originalParent:s,originalNextSibling:c}=a.popupPortal.state;a.popupPortal.restore(),s&&!s.isConnected&&s.insertBefore(a.portal,c?.parentNode===s?c:null),a.generatedPopup&&C(a.popup),a.generatedPositioner&&C(a.positioner),a.generatedPortal&&C(a.portal),a.popupPresence.cleanup(),a.viewportPresence.cleanup(),n=null},x=()=>{let t=e.viewport,n=e.root;if(!t)return{popup:null,positioner:null,portal:null};let r=null,i=null,a=null,o=t.parentElement,s=`popup`;for(;o&&o!==n;){let e=o.getAttribute(`data-slot`);if(e===`navigation-menu-portal`){a=o;break}if(s===`popup`&&e===`navigation-menu-popup`){r=o,s=`positioner`,o=o.parentElement;continue}if((s===`popup`||s===`positioner`)&&(e===`navigation-menu-positioner`||e===`navigation-menu-viewport-positioner`)){i=o,s=`portal`,o=o.parentElement;continue}o=o.parentElement}return{popup:r,positioner:i,portal:a}},S=(t,n)=>{let r=(e.root.ownerDocument??document).createElement(`div`);r.setAttribute(`data-slot`,n),n===`navigation-menu-positioner`&&(r.style.isolation=`isolate`,r.style.zIndex=`50`);let i=t.parentNode;if(!i)throw Error(`NavigationMenu expected popup stack child to have a parent node`);return i.insertBefore(r,t),r.appendChild(t),r},C=e=>{let t=e.parentNode;if(t){for(;e.firstChild;)t.insertBefore(e.firstChild,e);e.remove()}},w=e=>{r?.disconnect(),r=null,typeof ResizeObserver==`function`&&(r=new ResizeObserver(t=>{let n=t[0],r=Array.isArray(n?.borderBoxSize)?n.borderBoxSize[0]:void 0;E({width:Math.ceil(r?.inlineSize??e.offsetWidth),height:Math.ceil(r?.blockSize??e.offsetHeight)})}),r.observe(e))},T=e=>{e.width>0&&(i.committed.width=e.width),e.height>0&&(i.committed.height=e.height)},E=e=>{e.width>0&&(i.live.width=e.width),e.height>0&&(i.live.height=e.height)},ne=(e,t)=>{e.style.setProperty(`--popup-width`,`${t.width}px`),e.style.setProperty(`--popup-height`,`${t.height}px`)},O=e=>{let t=(e,t)=>{let n=new Map;for(let[r,i]of Object.entries(t))n.set(r,e.style.getPropertyValue(r)),e.style.setProperty(r,i);return()=>{for(let[t,r]of n)r?e.style.setProperty(t,r):e.style.removeProperty(t)}},n=t(e.popup,{"--popup-width":`auto`,"--popup-height":`auto`,position:`static`,transform:`none`,scale:`1`,top:``,right:``,bottom:``,left:``}),r=t(e.positioner,{"--positioner-width":`max-content`,"--positioner-height":`max-content`,"--available-width":`none`,"--available-height":`none`}),i=D(e.popup);return r(),n(),i},k=e=>{let n=t.get(e);if(n?.applied){for(let t of[`position`,`top`,`right`,`bottom`,`left`])n[t]?e.style.setProperty(t,n[t]):e.style.removeProperty(t);n.applied=!1}},A=t=>{let n=t.positioner;Object.assign(n.style,{position:``,top:``,left:``,margin:``,willChange:``,pointerEvents:``,transform:``});for(let e of[`--transform-origin`,`--positioner-width`,`--positioner-height`,`--available-width`,`--available-height`])n.style.removeProperty(e);let r=t.popup;Object.assign(r.style,{willChange:``,pointerEvents:``}),k(r),r.style.removeProperty(`scale`),r.style.removeProperty(`--transform-origin`),r.style.removeProperty(`--popup-width`),r.style.removeProperty(`--popup-height`);let i=e.viewport;if(i){Object.assign(i.style,{top:``,left:``,willChange:``});for(let e of[`--transform-origin`,`--viewport-width`,`--viewport-height`])i.style.removeProperty(e)}};return{get popup(){return o()},get positioner(){return c()},get closing(){return!!n?.isClosing},ensure:l,prepareOpen:u,reveal:d,close:f,checkExitComplete:()=>{n&&y(n)},baseline:p,isSizeTransitioning:m,measure:h,commitSize:g,setViewportSize:ee,setPositionerSize:_,setRuntimePosition:v,destroy:te}}function k(e){let{root:t,viewport:n,popup:r,activePanel:i,onBridgeEnter:a,onBridgeLeave:o}=e,s=e.safeTriangle??f(t,`safeTriangle`)??!1,c=e.debugSafeTriangle??f(t,`debugSafeTriangle`)??!1,l=s||c,u=[],d=null,p=null,m=()=>n?r.positioner?r.positioner:r.popup?r.popup:n.parentElement instanceof HTMLElement?n.parentElement:n:null,h=()=>{let e=m();return d||(d=t.ownerDocument.createElement(`div`),d.setAttribute(`data-slot`,`navigation-menu-bridge`),d.style.cssText=`position: absolute; pointer-events: auto; z-index: 0; display: none;`,u.push(y(d,`pointerenter`,a),y(d,`pointerleave`,e=>o(e.relatedTarget)))),e&&d.parentElement!==e&&e.insertBefore(d,e.firstChild),d},g=()=>{d&&Object.assign(d.style,{height:`0`,width:`0`,top:`0px`,left:`0px`,right:`0px`,bottom:`auto`,transform:`none`,clipPath:`none`,display:`none`})},ee=()=>!c||!t.ownerDocument.body?null:(p||(p=t.ownerDocument.createElement(`div`),p.setAttribute(`data-slot`,`navigation-menu-safe-triangle`),p.style.cssText=`position: fixed; pointer-events: none; display: none; z-index: 2147483647; background: rgba(255, 0, 0, 0.18); border: 1px solid rgba(255, 0, 0, 0.45)`),p.parentElement!==t.ownerDocument.body&&t.ownerDocument.body.appendChild(p),p),_=()=>{p&&Object.assign(p.style,{width:`0`,height:`0`,clipPath:`none`,display:`none`})},v=()=>{c||_()},te=e=>{let t=ee();if(!t)return;let{apex:n,edgeA:r,edgeB:i}=e,a=Math.min(n.x,r.x,i.x),o=Math.min(n.y,r.y,i.y),s=Math.max(1,Math.max(n.x,r.x,i.x)-a),c=Math.max(1,Math.max(n.y,r.y,i.y)-o),l=e=>`${(e.x-a)/s*100}% ${(e.y-o)/c*100}%`;Object.assign(t.style,{display:`block`,left:`${a}px`,top:`${o}px`,width:`${s}px`,height:`${c}px`,clipPath:`polygon(${l(n)}, ${l(r)}, ${l(i)})`})},b=(e,t,n)=>(e.x-n.x)*(t.y-n.y)-(t.x-n.x)*(e.y-n.y),x=(e,t,n,r)=>{let i=b(e,t,n),a=b(e,n,r),o=b(e,r,t);return!((i<0||a<0||o<0)&&(i>0||a>0||o>0))},S=(e,t,n)=>{let r=.5;return n.top>=t.bottom-r?[{x:n.left,y:n.top},{x:n.right,y:n.top}]:n.bottom<=t.top+r?[{x:n.left,y:n.bottom},{x:n.right,y:n.bottom}]:n.left>=t.right-r?[{x:n.left,y:n.top},{x:n.left,y:n.bottom}]:n.right<=t.left+r?[{x:n.right,y:n.top},{x:n.right,y:n.bottom}]:[[{x:n.left,y:n.top},{x:n.right,y:n.top}],[{x:n.right,y:n.top},{x:n.right,y:n.bottom}],[{x:n.left,y:n.bottom},{x:n.right,y:n.bottom}],[{x:n.left,y:n.top},{x:n.left,y:n.bottom}]].reduce((t,n)=>Math.abs(n[0].y===n[1].y?e.y-n[0].y:e.x-n[0].x){if(n.width<=0||n.height<=0)return null;let r={x:e.left+e.width/2,y:e.top+e.height*.62},[i,a]=S(r,t,n),o=i.x===a.x?Math.abs(a.y-i.y):Math.abs(a.x-i.x),s=o<=28?0:Math.min(10,(o-28)/2);if(i.x===a.x){let e=Math.min(i.y,a.y),t=Math.max(i.y,a.y);i={x:i.x,y:e+s},a={x:a.x,y:t-s}}else{let e=Math.min(i.x,a.x),t=Math.max(i.x,a.x);i={x:e+s,y:i.y},a={x:t-s,y:a.y}}return{apex:r,edgeA:i,edgeB:a}},w=()=>{let e=i();if(!l||!n||!e)return null;let r=n.getBoundingClientRect(),a=e.content.getBoundingClientRect();return C(e.trigger.getBoundingClientRect(),t.getBoundingClientRect(),r.width>0&&r.height>0?r:a)},T=e=>{let t=w();return!!t&&x({x:e.clientX,y:e.clientY},t.apex,t.edgeA,t.edgeB)},E=e=>{let t=Array.from(new Map(e.map(e=>[`${e.x.toFixed(3)}:${e.y.toFixed(3)}`,e])).values()).sort((e,t)=>e.x===t.x?e.y-t.y:e.x-t.x);if(t.length<=2)return t;let n=(e,t,n)=>(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x),r=e=>{let t=[];for(let r of e){for(;t.length>=2&&n(t[t.length-2],t[t.length-1],r)<=0;)t.pop();t.push(r)}return t},i=r(t),a=r([...t].reverse());return i.pop(),a.pop(),i.concat(a)},ne=(e,t)=>{if(e.length<3)return g();let n=h(),i=E(n.parentElement===r.positioner?e.map(e=>({x:e.x-t.x,y:e.y-t.y})):e);if(i.length<3)return g();let a=Math.min(...i.map(e=>e.x)),o=Math.min(...i.map(e=>e.y)),s=Math.max(...i.map(e=>e.x)),c=Math.max(...i.map(e=>e.y)),l=Math.max(1,s-a),u=Math.max(1,c-o);Object.assign(n.style,{display:`block`,transform:`none`,bottom:`auto`,right:`auto`,left:`${a}px`,top:`${o}px`,width:`${l}px`,height:`${u}px`,clipPath:`polygon(${i.map(e=>`${(e.x-a)/l*100}% ${(e.y-o)/u*100}%`).join(`, `)})`})},D=e=>{let t=Math.max(0,e.viewportRect.top-e.rootRect.bottom),n=Math.max(0,e.rootRect.top-e.viewportRect.bottom),r=Math.max(0,e.viewportRect.left-e.rootRect.right),i=Math.max(0,e.rootRect.left-e.viewportRect.right),a=Math.max(t,n,e.viewportMarginTop),o=Math.max(r,i),s=l?C(e.triggerRect,e.rootRect,e.viewportRect):null,c=[];if(a>=o&&a>0){let r=Math.max(t,n,e.viewportMarginTop),i=e.offset.x,a=e.contentSize.width,o=n>t&&n>=e.viewportMarginTop?e.offset.y+e.contentSize.height:e.offset.y-r;if(s){let t=Math.min(s.edgeA.x,s.edgeB.x)-e.rootRect.left,n=Math.max(s.edgeA.x,s.edgeB.x)-e.rootRect.left;i=t-8,a=n-t+16}c.push({x:i,y:o},{x:i,y:o},{x:i+a,y:o},{x:i+a,y:o+r},{x:i,y:o+r})}else if(o>0){let t=Math.max(r,i),n=e.offset.y,a=e.contentSize.height,o=i>r?e.offset.x+e.contentSize.width:e.offset.x-t;if(s){let t=Math.min(s.edgeA.y,s.edgeB.y)-e.rootRect.top,r=Math.max(s.edgeA.y,s.edgeB.y)-e.rootRect.top;n=t-8,a=r-t+16}c.push({x:o,y:n},{x:o+t,y:n},{x:o+t,y:n+a},{x:o,y:n+a})}s&&c.push({x:s.apex.x-e.rootRect.left,y:s.apex.y-e.rootRect.top},{x:s.edgeA.x-e.rootRect.left,y:s.edgeA.y-e.rootRect.top},{x:s.edgeB.x-e.rootRect.left,y:s.edgeB.y-e.rootRect.top}),ne(c,e.offset),O()},O=()=>{if(!c)return;let e=w();e?te(e):_()};return{update:D,contains:e=>!!e&&(!!i()?.content.contains(e)||!!n?.contains(e)||!!r.popup?.contains(e)||!!d?.contains(e)||!!r.positioner?.contains(e)),inCorridor:T,clear:v,hideBridge:g,refreshDebug:O,destroy:()=>{g(),_(),p?.remove(),p=null,u.splice(0).forEach(e=>e())}}}const A=[`start`,`center`,`end`],j=[`top`,`right`,`bottom`,`left`],M=(e,t)=>t===`start`?{x:e.left,y:e.top}:t===`end`?{x:e.right,y:e.bottom}:{x:e.left+e.width/2,y:e.top+e.height/2},N=(e,t,n)=>{let r=M(n,t);return e===`top`?{x:r.x,y:n.top}:e===`bottom`?{x:r.x,y:n.bottom}:e===`left`?{x:n.left,y:r.y}:{x:n.right,y:r.y}};function re(e){let{root:n,viewport:r,triggers:i,popup:a,placement:s,readSelection:l,onLayout:u}=e,d=s.side??p(n,`side`,j)??`bottom`,f=s.align??p(n,`align`,A)??`start`,h=s.sideOffset??m(n,`sideOffset`)??0,g=s.alignOffset??m(n,`alignOffset`)??0,ee=s.positionMethod??p(n,`positionMethod`,[`absolute`,`fixed`])??`absolute`,_=new Map,v=new Map,te={x:0,y:0},y=!1,b=0,x=!1,S=null,C=null,w=null,T=null,E=c(),ne=e=>({side:s.side??p(e.content,`side`,j)??p(e.item,`side`,j)??d,align:s.align??p(e.content,`align`,A)??p(e.item,`align`,A)??f,sideOffset:s.sideOffset??m(e.content,`sideOffset`)??m(e.item,`sideOffset`)??h,alignOffset:s.alignOffset??m(e.content,`alignOffset`)??m(e.item,`alignOffset`)??g}),D=()=>{C!==null&&E.cancelRaf(C),C=null},O=()=>{S!==null&&E.cancelRaf(S),S=null},k=()=>{let e=y||x;for(let t of[r,a.popup,a.positioner])t&&(e?t.setAttribute(`data-instant`,``):t.removeAttribute(`data-instant`))},M=()=>{O(),S=E.trackRaf(()=>{if(S=null,y&&b>0){--b,M();return}y=!1,x=!1,k()})},re=(e,t)=>{let n=_.get(e)??{applied:!1,left:``,position:``,top:``};if(t){n.applied||Object.assign(n,{position:e.style.position,top:e.style.top,left:e.style.left}),n.applied=!0,_.set(e,n),Object.assign(e.style,{position:`absolute`,top:`0px`,left:`0px`});return}if(n.applied){for(let t of[`position`,`top`,`left`])n[t]?e.style.setProperty(t,n[t]):e.style.removeProperty(t);n.applied=!1}},ie=e=>{if(!r)return;let t=v.get(e)??{originalParent:null,originalNextSibling:null,mountedInViewport:!1};t.mountedInViewport||(t.originalParent=e.parentNode,t.originalNextSibling=e.nextSibling,t.mountedInViewport=!0,v.set(e,t)),e.parentNode!==r&&r.appendChild(e)},P=e=>{let t=v.get(e);t?.mountedInViewport&&(t.originalParent?t.originalNextSibling?.parentNode===t.originalParent?t.originalParent.insertBefore(e,t.originalNextSibling):t.originalParent.appendChild(e):e.remove(),t.mountedInViewport=!1,t.originalParent=null,t.originalNextSibling=null)},ae=(e,i)=>{if(!r||!a.ensure()||a.closing||r.getAttribute(`data-state`)!==`open`||e.content.getAttribute(`data-state`)!==`active`)return;let o=a.popup,s=a.positioner;if(!o||!s)return;re(e.content,!1);let c=e.content.firstElementChild,l=e.content.lastElementChild,d=c&&parseFloat(getComputedStyle(c).marginTop)||0,f=l&&parseFloat(getComputedStyle(l).marginBottom)||0,p=e.content.getBoundingClientRect(),m={width:Math.max(p.width,e.content.scrollWidth,e.content.offsetWidth,e.content.clientWidth),height:Math.max(p.height,e.content.scrollHeight,e.content.offsetHeight,e.content.clientHeight)+d+f},h=a.measure(i,m);if(h.width<=0||h.height<=0)return;let g=ne(e),_=n.getBoundingClientRect(),v=e.trigger.getBoundingClientRect(),y=t({anchorRect:v,contentRect:{top:p.top,left:p.left,width:h.width,height:h.height,right:p.left+h.width,bottom:p.top+h.height},side:g.side,align:g.align,sideOffset:g.sideOffset,alignOffset:g.alignOffset,avoidCollisions:!1,collisionPadding:0,allowedSides:j});te={x:y.x-_.left,y:y.y-_.top};let b=y.side,x=y.align,S=N(b,x,v),C=`${S.x-y.x}px ${S.y-y.y}px`,w=`${S.x-_.left}px ${S.y-_.top}px`,T=n.ownerDocument.defaultView??window,E=ee===`fixed`,D=T.visualViewport,O=D?.offsetLeft??0,k=D?.offsetTop??0,A=D?.width??T.innerWidth,M=D?.height??T.innerHeight,ie=b===`left`?Math.max(0,v.left-O-g.sideOffset):b===`right`?Math.max(0,O+A-v.right-g.sideOffset):Math.max(0,A),P=b===`top`?Math.max(0,v.top-k-g.sideOffset):b===`bottom`?Math.max(0,k+M-v.bottom-g.sideOffset):Math.max(0,M);Object.assign(s.style,{position:ee,top:`${y.y+(E?0:T.scrollY)}px`,left:`${y.x+(E?0:T.scrollX)}px`,margin:`0`,willChange:`top,left`,pointerEvents:`none`}),o.style.willChange=`width,height`,o.style.pointerEvents=`auto`,a.setRuntimePosition(b===`top`?`top`:b===`left`?`left`:null),o.style.setProperty(`--transform-origin`,C),Object.assign(r.style,{top:`0px`,left:`0px`,willChange:`transform,width,height`}),r.style.setProperty(`--transform-origin`,C),e.content.style.setProperty(`--transform-origin`,C);for(let t of[o,r,e.content,s])t.setAttribute(`data-side`,b),t.setAttribute(`data-align`,x);s.style.setProperty(`--transform-origin`,w),a.setPositionerSize(s,h),s.style.setProperty(`--available-width`,`${ie}px`),s.style.setProperty(`--available-height`,`${P}px`),a.setViewportSize(h.width,h.height),a.commitSize(i,h),u({rootRect:_,triggerRect:v,viewportRect:new DOMRect(y.x,y.y,h.width,h.height),offset:{...te},contentSize:h,viewportMarginTop:parseFloat(getComputedStyle(r).marginTop)||0})},F=(e=l().panel,t={})=>{if(!e)return;let n=()=>ae(e,t.mode??`measure-target`);t.defer===!1?n():E.trackRaf(()=>{E.isDestroyed||n()})},oe=()=>{D(),C=E.trackRaf(()=>{C=null;let e=l();!e.panel||!a.ensure()||a.closing||(y&&R(),F(e.panel,{defer:!1,mode:a.popup?.hasAttribute(`data-starting-style`)||a.isSizeTransitioning()?`sync-current`:`measure-target`}))})},I=e=>{w?.disconnect(),w=null,T?.disconnect(),T=null,D(),!(!r||!e)&&(typeof ResizeObserver==`function`&&(w=new ResizeObserver(oe),w.observe(e.content)),typeof MutationObserver==`function`&&(T=new MutationObserver(oe),T.observe(e.content,{childList:!0,subtree:!0,characterData:!0})))},L=o({observedElements:[n,...i],isActive:()=>l().open,ancestorScroll:!0,syncOnScroll:!0,ancestorResize:!0,elementResize:!0,layoutShift:!0,onUpdate:()=>{x=!0,k(),F(void 0,{defer:!1,mode:`sync-current`}),M()}}),R=()=>{y=!0,b=Math.max(b,4),k(),M()},z=()=>{O(),y=!1,b=0,x=!1,k()},B=({open:e,initial:t,switching:n})=>{(!e||n)&&(O(),x=!1),y=t,b=t?Math.max(b,4):0,k()},V=()=>{D(),z(),te={x:0,y:0}},H=()=>{L.stop(),I(null),V()};return{mount:ie,setAbsolute:re,restore:P,sync:F,observe:I,start:()=>L.start(),stop:()=>L.stop(),beginInitialOpen:R,clearInstant:z,commitInstant:B,reset:V,dispose:H,destroy:()=>{if(E.destroy()){H();for(let e of v.keys())P(e),re(e,!1),e.hidden=!0,e.style.pointerEvents=`none`;v.clear(),_.clear()}}}}const ie=`@data-slot/navigation-menu`;function P(t,a={}){let o=x(t,ie,`[@data-slot/navigation-menu] createNavigationMenu() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(o)return o;let d=a.delayOpen??m(t,`delayOpen`)??0,h=a.delayClose??m(t,`delayClose`)??0,_=a.openOnFocus??f(t,`openOnFocus`)??!1,te=a.onValueChange,T=a.mountStrategy??p(t,`mountStrategy`,[`lazy`,`eager`])??`lazy`,E=(e,t)=>{`inert`in e&&(e.inert=t)},D=g(t,`navigation-menu-list`),A=ee(t,`navigation-menu-item`),j=g(t,`navigation-menu-viewport`),M=g(t,`navigation-menu-indicator`);if(!D||A.length===0)throw Error(`NavigationMenu requires navigation-menu-list and at least one navigation-menu-item`);let N=null,P=null,ae=-1,F=null,oe=null,I=null,L=!1,R=null,z=null,B=!1,V=c();V.onDestroy(()=>l(U));let H=null,se=()=>{H!==null&&(V.cancelRaf(H),H=null)},U=[],ce=new Map,le=(e,t)=>{e&&(t?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`)))},W=(e,t)=>{e&&(e.setAttribute(`data-state`,t?`open`:`closed`),le(e,t))},ue=(e,t)=>{e.setAttribute(`data-state`,t?`active`:`inactive`),le(e,t)},de=(e,t)=>{t?e.setAttribute(`data-activation-direction`,t):e.removeAttribute(`data-activation-direction`)},fe=()=>{},G=O({root:t,viewport:j,isDestroyed:()=>V.isDestroyed,beforeRestore:()=>fe(),hasExitingContent:()=>[...ce.values()].some(e=>e.isExiting)}),pe=()=>G.popup,me=()=>G.positioner,he=ne(t,D,A),{allItems:ge,itemMap:K,navigables:q,navigableByTarget:_e}=he,ve=new Map;for(let{content:e}of ge)ve.set(e,r({root:t,target:e,strategy:T})),ce.set(e,s({element:e,onExitComplete:()=>{V.isDestroyed||(ue(e,!1),de(e,null),e.removeAttribute(`data-motion`),Z.setAbsolute(e,!1),Z.restore(e),e.hidden=!0,e.style.pointerEvents=`none`,ve.get(e)?.unmount(),G.checkExitComplete())}}));let ye=Array.from(K.values()).map(e=>e.trigger),be=_e,xe=e=>{let t=be(e);return t?.kind===`plain`?t:null},Se=e=>{let t=be(e);return t?.kind===`submenu`?t:null},Ce=()=>{let e=t.ownerDocument.defaultView;if(!e?.matchMedia)return!0;try{return e.matchMedia(`(any-hover: hover)`).matches}catch{return!0}},we=e=>{try{return e.matches(`:hover`)}catch{return!1}},Te=()=>{for(let e of q){if(e.kind===`submenu`){if(we(e.item)||we(e.trigger))return e;continue}if(we(e.element))return e}return null},Ee=(e,n={})=>{let r=t.ownerDocument,i=n.preserveOpenOnPlain??!1,a=e.kind===`submenu`?e.trigger:e.element;e.kind===`plain`&&N!==null&&!i&&$();let o=r.activeElement===a;return a.focus(),r.activeElement===a?(o&&Ve(a),!0):!1},De=(e,t,n={})=>{for(let r=e+t;r>=0&&r{let r=q.indexOf(e);return r===-1?!1:De(r,t,n)},ke=(e,t,n={})=>{let r=q.findIndex(t=>t.kind===`submenu`&&t.trigger===e);return r===-1?!1:De(r,t,n)},Ae=e=>he.isNonSubmenuListTarget(e),je=v,Me=e=>{if(t.contains(e)||j?.contains(e)||pe()?.contains(e)||me()?.contains(e))return!0;for(let{content:t}of K.values())if(t.contains(e))return!0;return!1},Ne=()=>{let e=t.ownerDocument,n=v(e);for(let r of n)if(!Me(r)&&(t.compareDocumentPosition(r)&Node.DOCUMENT_POSITION_FOLLOWING)!==0&&(r.focus(),e.activeElement===r))return!0;return!1},Pe=e=>{V.trackRaf(()=>{if(N!==e)return;let t=K.get(e);if(!t)return;let n=je(t.content)[0];n?n.focus():t.content.focus()})},J=()=>{F&&=(V.cancelTimeout(F),null),oe&&=(V.cancelTimeout(oe),null)},Fe=()=>{J(),P=null},Ie=()=>{R=null,z=null},Le=()=>N?K.get(N)??null:null,Y=k({root:t,viewport:j,popup:G,safeTriangle:a.safeTriangle,debugSafeTriangle:a.debugSafeTriangle,activePanel:Le,onBridgeEnter:J,onBridgeLeave:e=>{L||N===null||Y.contains(e)||e&&n(t,e)||(Q(null),X(null))}});U.push(()=>Y.destroy());let Re=e=>Y.update(e),ze=e=>ae===-1||e>ae?`right`:`left`,Be=w(M,D,j,()=>N?K.get(N)?.trigger??null:null);U.push(()=>Be.destroy());let X=e=>{e||se(),I=e,Be.show(e)},Ve=(e=null)=>{Be.sync(e),I=Be.hovered},Z=re({root:t,viewport:j,triggers:ye,popup:G,placement:a,readSelection:()=>({open:N!==null,panel:Le()}),onLayout:Re});fe=Z.reset,U.push(()=>{Z.dispose(),ce.forEach(e=>e.cleanup()),ce.clear(),Z.destroy(),G.destroy(),ve.forEach(e=>e.cleanup()),ve.clear()});let Q=(e,r=!1)=>{if(V.isDestroyed)return;if(Y.clear(),e===N){e===null?Fe():J();return}if(!r&&e!==null&&e===P)return;J(),P=e===null?null:e;let i=()=>{let r=N,i=e?K.get(e):null,a=e!==null,o=i?null:G.baseline(),s=r===null&&a,c=r!==null&&e!==null&&r!==e,l=c&&i?ze(i.index):null,d=document.activeElement;if(e===null&&d&&r){let e=K.get(r);e&&n(e.content,d)&&e.trigger.focus()}let f=r?K.get(r):null;if(f){let{trigger:e,content:t,item:n}=f;S(e,`expanded`,!1),e.removeAttribute(`aria-controls`),e.setAttribute(`data-state`,`closed`),n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),E(t,!0),t.style.pointerEvents=`none`,de(t,l),l?t.setAttribute(`data-motion`,l===`right`?`to-left`:`to-right`):t.removeAttribute(`data-motion`),Z.setAbsolute(t,!0),ce.get(t)?.exit()}if(i){if(i.trigger.setAttribute(`data-state`,`open`),i.item.setAttribute(`data-state`,`open`),ve.get(i.content)?.mount(),G.prepareOpen(),j&&s&&(W(t,!0),W(me(),!0),W(pe(),!0),W(j,!0),j.style.pointerEvents=`auto`,Z.beginInitialOpen()),G.reveal(r===null),Z.mount(i.content),i.trigger.setAttribute(`aria-controls`,i.content.id),S(i.trigger,`expanded`,!0),ce.get(i.content)?.enter(),Z.start(),l){de(i.content,l);let e=l===`right`?`from-right`:`from-left`;i.content.setAttribute(`data-motion`,e)}else de(i.content,null),i.content.removeAttribute(`data-motion`);ue(i.content,!0),Z.setAbsolute(i.content,!1),i.content.removeAttribute(`aria-hidden`),E(i.content,!1),i.content.hidden=!1,i.content.style.pointerEvents=`auto`,ae=i.index,c&&Z.clearInstant(),Z.sync(i,{defer:s||c?!1:void 0,mode:s?`initial-open`:`measure-target`}),Z.observe(i),X(i.trigger)}else se(),X(null),Z.stop(),Y.hideBridge(),Y.clear(),G.close(o),Z.observe(null);if(W(t,a),l?t.setAttribute(`data-motion`,l===`right`?`from-right`:`from-left`):t.removeAttribute(`data-motion`),j){let e=pe();W(me(),a),W(e,a),W(j,a),j.style.pointerEvents=a?`auto`:`none`,Z.commitInstant({open:a,initial:s,switching:c}),l?j.style.setProperty(`--motion-direction`,l===`right`?`1`:`-1`):j.style.removeProperty(`--motion-direction`)}N=e,P=null,e===null&&X(null),Y.refreshDebug(),u(t,`navigation-menu:change`,{value:e}),te?.(e)};r?i():e!==null&&N===null?F=V.trackTimeout(i,d):e!==null&&N!==null?i():oe=V.trackTimeout(i,h)},He={popup:G.popup,positioner:G.positioner};W(t,!1),j&&(W(He.positioner,!1),W(He.popup,!1),W(j,!1),j.hidden=!0,j.style.pointerEvents=`none`,He.popup&&(He.popup.hidden=!0,He.popup.style.pointerEvents=`none`)),M&&M.setAttribute(`data-state`,`hidden`),K.forEach(({trigger:e,content:t,item:n})=>{e.tagName===`BUTTON`&&!e.hasAttribute(`type`)&&(e.type=`button`),S(e,`expanded`,!1),e.setAttribute(`data-state`,`closed`),e.tabIndex=0,n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),t.tabIndex=-1,E(t,!0),t.hidden=!0,t.style.pointerEvents=`none`,ve.get(t)?.unmount()}),K.forEach(({item:e,trigger:r},i)=>{U.push(y(r,`pointerenter`,e=>{if(!L){if(N!==i&&Y.inCorridor(e))return;X(r)}})),U.push(y(e,`pointerenter`,e=>{if(!L){if(N!==i&&Y.inCorridor(e))return;Q(i)}})),U.push(y(e,`pointerleave`,e=>{if(P===i&&N===null&&Fe(),N===i&&!L){let r=e.relatedTarget;if(Y.contains(r))return;(!r||!n(t,r))&&(Q(null),X(null))}})),U.push(y(r,`focus`,()=>{if(R===r){R=null;return}_&&Q(i,!0),Ve(r)})),U.push(y(r,`pointerdown`,()=>{R=r,z=r}),y(r,`keydown`,()=>{z=null})),U.push(y(r,`click`,()=>{let e=z===r;z=null,R=null,Fe(),N===i&&L?(L=!1,Q(null,!0),X(null)):N===i&&!L?(L=!0,X(r),e||Pe(i)):(L=!0,Q(i,!0),e||Pe(i))}))}),U.push(y(D,`pointerover`,e=>{let t=e;if(t.pointerType===`touch`)return;let n=Se(t.target);if(n){if(B=!0,L||N!==n.value&&Y.inCorridor(t))return;X(n.trigger),Q(n.value);return}let r=xe(t.target);if(r){if(B=!0,N!==null){if(L)return;$()}X(r.element);return}N!==null&&(L||Ae(t.target)&&$())}),y(D,`click`,e=>{N!==null&&Ae(e.target)&&$()})),U.push(y(D,`focusin`,e=>{let t=xe(e.target);if(t){if(N!==null){Ve();return}X(t.element)}}),y(t,`pointerenter`,()=>{B=!0}),y(t,`pointerleave`,e=>{let t=e.relatedTarget;if(!Y.contains(t)&&(B=!1,!L)){if(Y.inCorridor(e)){J();return}Q(null),X(null)}}),y(t,`pointerdown`,()=>{Y.clear(),Fe()}));let Ue=()=>{if(!Ce()||N!==null||P!==null||L)return;let e=Te();if(B=e!==null||t instanceof HTMLElement&&we(t),e){if(e.kind===`submenu`){Q(e.value,!0);return}X(e.element)}};j&&U.push(y(j,`pointerenter`,()=>{Y.clear(),J()}),y(j,`transitionend`,e=>{if(e.target!==j)return;let t=N?K.get(N):null;t&&Z.sync(t,{defer:!1,mode:`sync-current`})})),K.forEach(({content:e})=>{U.push(y(e,`pointerenter`,()=>{Y.clear(),J()}),y(e,`pointerleave`,e=>{if(L)return;let r=e.relatedTarget;Y.contains(r)||n(t,r)||(Q(null),X(null))}))}),U.push(y(D,`keydown`,e=>{let t=be(e.target);if(!t)return;let n=q.indexOf(t);if(n===-1)return;let r=n;switch(e.key){case`Tab`:if(e.shiftKey||N===null)return;(Oe(t,1,{preserveOpenOnPlain:!0})||Ne())&&e.preventDefault();return;case`ArrowLeft`:r=n-1,r<0&&(r=q.length-1);break;case`ArrowRight`:r=n+1,r>=q.length&&(r=0);break;case`ArrowDown`:if(t.kind===`submenu`){e.preventDefault();let n=t.value;L=!0,Q(n,!0),Pe(n)}return;case`Home`:r=0;break;case`End`:r=q.length-1;break;case`Escape`:L=!1,Q(null,!0),X(null);return;default:return}e.preventDefault();let i=q[r];i&&Ee(i,{preserveOpenOnPlain:!0})})),K.forEach(({content:e,trigger:t})=>{U.push(y(e,`keydown`,n=>{if(!/^(Tab|Arrow(Up|Down|Left|Right)|Escape)$/.test(n.key))return;let r=n.target,i=je(e),a=i.indexOf(r);if(a!==-1)switch(n.key){case`Tab`:if(!n.shiftKey&&a===i.length-1){(ke(t,1,{preserveOpenOnPlain:!0})||Ne())&&n.preventDefault();return}if(n.shiftKey&&a===0){n.preventDefault(),t.focus();return}return;case`ArrowDown`:case`ArrowRight`:{n.preventDefault();let e=a+1;en(t,document.activeElement)||B||L,$=()=>{Y.clear(),Fe(),Ie(),L=!1,Q(null,!0),X(null),Y.refreshDebug()};U.push(y(document,`focusin`,e=>{let r=e.target;if(!n(t,r)){if(N!==null){$();return}X(null)}})),U.push(i({root:t,isOpen:()=>N!==null&&We(),onDismiss:$,closeOnClickOutside:!0,closeOnEscape:!0,preventEscapeDefault:!1,isInside:e=>!!e&&n(t,e)})),U.push(y(window,`resize`,()=>{(N||I)&&(se(),H=V.trackRaf(()=>{H=null,V.isDestroyed||Ve(I)}))}),y(D,`scroll`,()=>{(N||I)&&(se(),H=V.trackRaf(()=>{H=null,V.isDestroyed||Ve(I)}))})),U.push(b(t,`navigation-menu:set`,e=>{let t=e.detail;if(t?.value!==void 0){if(t.value===null)$();else if(K.has(t.value)){L=!0,Q(t.value,!0);let e=K.get(t.value);e&&X(e.trigger)}}}));let Ge={get value(){return N},open:e=>{V.isDestroyed||Q(e,!0)},close:()=>{V.isDestroyed||$()},destroy:()=>{V.destroy()&&(Fe(),Ie(),se(),X(null),K.forEach(({trigger:e,content:t,item:n})=>{S(e,`expanded`,!1),e.removeAttribute(`aria-controls`),e.setAttribute(`data-state`,`closed`),n.setAttribute(`data-state`,`closed`),ue(t,!1),t.setAttribute(`aria-hidden`,`true`),E(t,!0),t.hidden=!0,t.style.pointerEvents=`none`}),N=null,e(t,ie,Ge))}};return Ue(),C(t,ie,Ge),Ge}function ae(e=document){let t=[];for(let n of _(e,`navigation-menu`))te(n,ie)||t.push(P(n));return t}function F(e,t={}){return P(e,t)}export{ae as create,F as createNavigationMenu}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-popover.js b/liquid/vendor/data-slot-popover.js new file mode 100644 index 000000000..49c93f70b --- /dev/null +++ b/liquid/vendor/data-slot-popover.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createDismissLayer as r,createPortalLifecycle as i,createPositionSync as a,createPresenceLifecycle as o,createTerminalLifecycle as ee,emit as te,ensureId as ne,focusElement as s,getAutofocusOrFirstFocusable as re,getDataBool as c,getDataEnum as l,getDataNumber as u,getPart as d,getRoots as f,hasRootBinding as p,measurePopupContentRect as ie,on as m,onRoot as ae,registerFloatingTerminalResources as oe,reuseRootBinding as se,setAria as h,setRootBinding as g}from"@data-slot/core";const _=[`top`,`right`,`bottom`,`left`],ce=[`start`,`center`,`end`],v=`@data-slot/popover`;function y(f,p={}){let y=se(f,v,`[@data-slot/popover] createPopover() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(y)return y;let b=d(f,`popover-trigger`),x=d(f,`popover-content`),S=d(f,`popover-close`),C=d(f,`popover-positioner`),w=C&&x&&C.contains(x)?C:null,T=d(f,`popover-portal`),le=T&&w&&T.contains(w)?T:null;if(!b||!x)throw Error(`Popover requires trigger and content slots`);let E=p.defaultOpen??c(f,`defaultOpen`)??!1,D=p.onOpenChange,ue=p.closeOnClickOutside??c(f,`closeOnClickOutside`)??!0,O=p.closeOnEscape??c(f,`closeOnEscape`)??!0,k=p.portal??c(x,`portal`)??c(f,`portal`)??!0,A=(e,t)=>l(x,e,t)??(w?l(w,e,t):void 0)??l(f,e,t),j=e=>u(x,e)??(w?u(w,e):void 0)??u(f,e),de=e=>c(x,e)??(w?c(w,e):void 0)??c(f,e),fe=p.position??A(`position`,_),M=p.side??A(`side`,_)??fe??`bottom`,N=p.align??A(`align`,ce)??`center`,pe=p.sideOffset??j(`sideOffset`)??4,P=p.alignOffset??j(`alignOffset`)??0,F=p.avoidCollisions??de(`avoidCollisions`)??!0,I=p.collisionPadding??j(`collisionPadding`)??8,L=E,R=[],z=i({content:x,root:f,enabled:k,wrapperSlot:w?void 0:`popover-positioner`,container:w??void 0,mountTarget:w?le??w:void 0}),B=ee(),V=null,H=!1,U=()=>{H&&=(x.removeAttribute(`tabindex`),!1)},W=()=>{if(B.isDestroyed)return;let e=re(x);if(e)return e.focus();x.getAttribute(`tabindex`)||(x.setAttribute(`tabindex`,`-1`),H=!0),x.focus()},me=ne(x,`popover-content`);b.setAttribute(`aria-haspopup`,`dialog`),b.setAttribute(`aria-controls`,me),x.setAttribute(`data-side`,M),x.setAttribute(`data-align`,N),x.setAttribute(`data-position`,M);let G=()=>{let e=z.container,r=f.ownerDocument.defaultView??window,i=b.getBoundingClientRect(),a=t({anchorRect:i,contentRect:ie(x),side:M,align:N,sideOffset:pe,alignOffset:P,avoidCollisions:F,collisionPadding:I}),o=n({side:a.side,align:a.align,anchorRect:i,popupX:a.x,popupY:a.y});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${a.x+r.scrollX}px, ${a.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,o),e.style.willChange=`transform`,e.style.margin=`0`,x.setAttribute(`data-side`,a.side),x.setAttribute(`data-align`,a.align),e!==x&&(e.setAttribute(`data-side`,a.side),e.setAttribute(`data-align`,a.align)),x.setAttribute(`data-position`,a.side)},K=e=>{let t=z.container;f.setAttribute(`data-state`,e),x.setAttribute(`data-state`,e),t!==x&&t.setAttribute(`data-state`,e),e===`open`?(f.setAttribute(`data-open`,``),x.setAttribute(`data-open`,``),t!==x&&t.setAttribute(`data-open`,``),f.removeAttribute(`data-closed`),x.removeAttribute(`data-closed`),t!==x&&t.removeAttribute(`data-closed`)):(f.setAttribute(`data-closed`,``),x.setAttribute(`data-closed`,``),t!==x&&t.setAttribute(`data-closed`,``),f.removeAttribute(`data-open`),x.removeAttribute(`data-open`),t!==x&&t.removeAttribute(`data-open`))},q=!1,J=()=>{q=!1,V&&V.isConnected?s(V):s(b),V=null},Y=()=>{q=!0,B.trackRaf(J)};B.onBeforeDestroy(()=>{q&&B.trackFinalRaf(J)});let X=o({element:x,onExitComplete:()=>{B.isDestroyed||(z.restore(),x.hidden=!0,U(),Y())}}),Z=a({observedElements:[b,x],isActive:()=>L,ancestorScroll:!1,onUpdate:G}),Q=e=>{B.isDestroyed||L!==e&&(e&&(V=document.activeElement),L=e,h(b,`expanded`,L),e?(z.mount(),x.hidden=!1,K(`open`),X.enter(),G(),Z.start(),B.trackRaf(W)):(K(`closed`),X.exit(),Z.stop()),te(f,`popover:change`,{open:L}),D?.(L))};h(b,`expanded`,L),K(L?`open`:`closed`),x.hidden=!L,E&&(z.mount(),X.enter(),x.hidden=!1,G(),Z.start(),B.trackRaf(W)),R.push(m(b,`click`,()=>Q(!L))),S&&R.push(m(S,`click`,()=>Q(!1))),R.push(r({root:f,isOpen:()=>L,onDismiss:()=>Q(!1),closeOnClickOutside:ue,closeOnEscape:O})),R.push(ae(f,`popover:set`,e=>{let t=e.detail,n;t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`&&Q(n)}));let $={open:()=>{B.isDestroyed||Q(!0)},close:()=>{B.isDestroyed||Q(!1)},toggle:()=>{B.isDestroyed||Q(!L)},get isOpen(){return L},destroy:()=>{B.destroy()&&(L=!1,h(b,`expanded`,!1),K(`closed`),x.hidden=!0,U())}};return oe(B,{cleanups:R,positionSync:Z,presence:X,portal:z,unbind:()=>e(f,v,$)}),g(f,v,$),$}function b(e=document){let t=[];for(let n of f(e,`popover`))p(n,v)||t.push(y(n));return t}export{b as create,y as createPopover}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-radio-group.js b/liquid/vendor/data-slot-radio-group.js new file mode 100644 index 000000000..8b608463f --- /dev/null +++ b/liquid/vendor/data-slot-radio-group.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataString as i,getOwnedElements as a,getParts as o,getRoots as s,hasRootBinding as c,observeFormReset as l,on as u,onRoot as d,reuseRootBinding as f,setAria as p,setRootBinding as m}from"@data-slot/core";const h=`@data-slot/radio-group`,g=[`position:absolute`,`width:1px`,`height:1px`,`padding:0`,`margin:-1px`,`overflow:hidden`,`clip:rect(0, 0, 0, 0)`,`white-space:nowrap`,`border:0`,`pointer-events:none`].join(`;`);function _(e,t,n){n?e.setAttribute(t,``):e.removeAttribute(t)}function v(e,t){_(e,`data-checked`,t),_(e,`data-unchecked`,!t)}function y(e,t,n,r){_(e,`data-disabled`,t),_(e,`data-readonly`,n),_(e,`data-required`,r)}function b(e,t){let n=new Set;if(e)for(let t of e.split(/\s+/))t&&n.add(t);for(let e of t)e&&n.add(e);return n.size>0?[...n].join(` `):null}function x(e,t){let n=e.parentNode;if(!n){e.appendChild(t);return}n.insertBefore(t,e.nextSibling)}function S(e){return e.tagName===`BUTTON`}function C(e){let t=[],n=e.closest(`label`);if(n instanceof HTMLLabelElement&&t.push(n),!e.id)return t;let r=e.ownerDocument??document,i=`label[for="${CSS.escape(e.id)}"]`;for(let e of r.querySelectorAll(i))t.includes(e)||t.push(e);return t}function w(s,c={}){let _=f(s,h,`[@data-slot/radio-group] createRadioGroup() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(_)return _;let w=s,T=o(w,`radio-group-item`);if(T.length===0)throw Error(`RadioGroup requires at least one radio-group-item`);let E=c.disabled??r(w,`disabled`)??(w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`),D=c.readOnly??r(w,`readOnly`)??w.getAttribute(`aria-readonly`)===`true`,O=c.required??r(w,`required`)??w.getAttribute(`aria-required`)===`true`,k=c.name??i(w,`name`),A=c.onValueChange,j=[],M=[],N=new Map,P=[];for(let e of T){let t=(e.dataset.value||``).trim();if(!t){j.push(e);continue}let n=(w.ownerDocument??document).createElement(`input`);n.type=`radio`,n.tabIndex=-1,n.setAttribute(`aria-hidden`,`true`),n.setAttribute(`data-radio-group-generated`,`input`),n.style.cssText=g,x(e,n);let r={el:e,value:t,authoredDisabled:e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,indicators:a(w,e,`[data-slot="radio-group-indicator"]`),hiddenInput:n};M.push(r),N.has(t)||N.set(t,r)}for(let e of j)e.tabIndex=-1,e.setAttribute(`aria-disabled`,`true`);if(M.length===0)throw Error(`RadioGroup requires at least one radio-group-item with a data-value attribute`);let F=(c.defaultValue??i(w,`defaultValue`)??``).trim(),I=N.get(F)??null,L=null,R=e=>E||e.authoredDisabled,z=()=>M.filter(e=>!R(e)),B=()=>I?.value??null,V=(e,t)=>{e.hiddenInput.checked=t,p(e.el,`checked`,t),v(e.el,t);for(let n of e.indicators)v(n,t)},H=()=>{let e=(I&&!R(I)?I:null)??z()[0]??null;e!==L&&(L&&(L.el.tabIndex=-1),e&&(e.el.tabIndex=0),L=e)},U=()=>{H();let e=B();e?w.setAttribute(`data-value`,e):w.removeAttribute(`data-value`)},W=(e,n=!0)=>{I!==e&&(I&&V(I,!1),I=e,e&&V(e,!0),U(),n&&(t(w,`radio-group:change`,{value:B()}),A?.(B())))},G=e=>{e&&e.el.focus()},K=(e,t)=>{let n=z();if(n.length===0)return null;let r=n.findIndex(t=>t===e);return r===-1?n[0]??null:n[(r+t+n.length)%n.length]??null},q=e=>{let t=z();return t.length===0?null:e===`first`?t[0]??null:t[t.length-1]??null};w.setAttribute(`role`,`radiogroup`),p(w,`disabled`,E?!0:null),p(w,`readonly`,D?!0:null),p(w,`required`,O?!0:null);for(let e of M){let t=R(e),r=e===I;S(e.el)&&(e.el.hasAttribute(`type`)||(e.el.type=`button`),e.el.disabled=t),e.el.setAttribute(`role`,`radio`),e.el.tabIndex=-1,p(e.el,`disabled`,t?!0:null),p(e.el,`readonly`,D?!0:null),p(e.el,`required`,O?!0:null),y(e.el,t,D,O);for(let n of e.indicators)y(n,t,D,O);e.hiddenInput.disabled=t,e.hiddenInput.required=!!(O&&k),e.hiddenInput.value=e.value,k&&(e.hiddenInput.name=k),e.hiddenInput.defaultChecked=r,V(e,r);let i=C(e.el);if(i.length>0){let t=i.map(e=>n(e,`radio-group-label`)),r=b(e.el.getAttribute(`aria-labelledby`),t);r&&e.el.setAttribute(`aria-labelledby`,r)}P.push(u(e.hiddenInput,`focus`,()=>{document.activeElement!==e.el&&e.el.focus()})),P.push(u(e.hiddenInput,`click`,t=>{(E||D||e.authoredDisabled)&&t.preventDefault()})),P.push(u(e.hiddenInput,`change`,()=>{E||D||e.authoredDisabled||W(e)})),P.push(u(e.el,`click`,t=>{if(t.target!==e.hiddenInput){if(E||D||e.authoredDisabled){t.preventDefault();return}if(I===e){t.preventDefault();return}t.preventDefault(),G(e),e.hiddenInput.click()}})),P.push(u(e.el,`keydown`,t=>{if(E||D||e.authoredDisabled)return;let n=t;if(!n.repeat)switch(n.key){case`ArrowRight`:case`ArrowDown`:{n.preventDefault();let t=K(e,1);G(t),t&&W(t);return}case`ArrowLeft`:case`ArrowUp`:{n.preventDefault();let t=K(e,-1);G(t),t&&W(t);return}case`Home`:{n.preventDefault();let e=q(`first`);G(e),e&&W(e);return}case`End`:{n.preventDefault();let e=q(`last`);G(e),e&&W(e);return}case` `:case`Spacebar`:case`Enter`:n.preventDefault(),I!==e&&e.hiddenInput.click();return;default:return}}));for(let t of i)t.contains(e.el)||P.push(u(t,`click`,t=>{t.preventDefault(),!(E||D||e.authoredDisabled)&&(G(e),I!==e&&e.hiddenInput.click())}))}U();let J=l({root:w,getForm:()=>M.find(e=>e.hiddenInput.form)?.hiddenInput.form??w.closest(`form`),onReset:()=>{W(M.find(e=>e.hiddenInput.checked)??null,!1)}});P.push(()=>J.destroy()),P.push(d(w,`radio-group:set`,e=>{if(E||D)return;let t=e.detail,n=t?.value===null||typeof t?.value==`string`?t.value:void 0;n!==void 0&&W(n===null?null:N.get(n)??null)}));let Y={get value(){return B()},select:e=>{let t=N.get(e);t&&W(t)},clear:()=>W(null),destroy:()=>{P.forEach(e=>e()),P.length=0;for(let e of M)e.hiddenInput.remove();e(w,h,Y)}};return m(w,h,Y),Y}function T(e=document){let t=[];for(let n of s(e,`radio-group`))c(n,h)||t.push(w(n));return t}export{T as create,w as createRadioGroup}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-select.js b/liquid/vendor/data-slot-select.js new file mode 100644 index 000000000..de935fb6d --- /dev/null +++ b/liquid/vendor/data-slot-select.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createFormFieldAdapter as a,createPortalLifecycle as o,createPositionSync as s,createPresenceLifecycle as c,createTerminalLifecycle as l,createTypeahead as u,emit as d,ensureId as f,ensureItemVisibleInContainer as p,focusElement as m,getDataBool as h,getDataEnum as g,getDataNumber as _,getDataString as v,getOwnedElements as y,getPart as b,getRoots as x,hasRootBinding as S,lockScroll as ee,measurePopupContentRect as te,on as C,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setAria as w,setRootBinding as ae,unlockScroll as T}from"@data-slot/core";const E=[`top`,`bottom`],oe=[`start`,`center`,`end`],se=[`item-aligned`,`popper`];function ce(e,t,n,r,i){let a=(n,i)=>g(t,n,i)??(r?g(r,n,i):void 0)??g(e,n,i),o=n=>_(t,n)??(r?_(r,n):void 0)??_(e,n);return{defaultValue:i.defaultValue??v(e,`defaultValue`)??null,defaultOpen:i.defaultOpen??h(e,`defaultOpen`)??!1,mountStrategy:i.mountStrategy??g(e,`mountStrategy`,[`lazy`,`eager`])??`lazy`,placeholder:i.placeholder??v(e,`placeholder`)??(n?v(n,`placeholder`):void 0)??``,disabled:i.disabled??h(e,`disabled`)??!1,required:i.required??h(e,`required`)??!1,name:i.name??v(e,`name`)??null,onValueChange:i.onValueChange,onOpenChange:i.onOpenChange,position:i.position??a(`position`,se)??`item-aligned`,preferredSide:i.side??a(`side`,E)??`bottom`,preferredAlign:i.align??a(`align`,oe)??`start`,sideOffset:i.sideOffset??o(`sideOffset`)??4,alignOffset:i.alignOffset??o(`alignOffset`)??0,avoidCollisions:i.avoidCollisions??(n=>h(t,n)??(r?h(r,n):void 0)??h(e,n))(`avoidCollisions`)??!0,collisionPadding:i.collisionPadding??o(`collisionPadding`)??8,lockScrollOption:i.lockScroll??h(e,`lockScroll`)??!0,highlightItemOnHover:i.highlightItemOnHover??h(e,`highlightItemOnHover`)??!0}}function D(e,t){return Array.from(x(e,`select`)).filter(e=>!S(e,`@data-slot/select`)).map(t)}const O=[`top`,`bottom`];function le(e){let{root:r,trigger:i,content:a,valueSlot:o,getPositioner:c,viewport:l,isOpen:u,getCollection:d,position:f,preferredSide:p,preferredAlign:m,sideOffset:h,alignOffset:g,avoidCollisions:_,collisionPadding:v,lockScroll:y}=e,b=l??a,x=e=>e.querySelector(`[data-slot="select-item-text"]`),S=e=>{if(!e)return null;let t=e.getBoundingClientRect();return t.width>0||t.height>0?t:null},ee=(e=f===`item-aligned`)=>{a.setAttribute(`data-position`,f),a.setAttribute(`data-align-trigger`,e?`true`:`false`),l?.setAttribute(`data-position`,f)},C=()=>{let e=i.getBoundingClientRect();return a.style.minWidth=`${e.width}px`,e},ne=e=>S(o)??e,re=(e,t,n,r,i)=>{let a=i===`top`?`offsetTop`:`offsetLeft`,o=i===`top`?`clientTop`:`clientLeft`,s=i===`top`?`top`:`left`,c=0,l=e;for(;l&&l!==t;){c+=l[a];let e=l.offsetParent;if(!(e instanceof HTMLElement)){c=NaN;break}e!==t&&(c+=e[o]),l=e}return l===t&&Number.isFinite(c)?c:e.getBoundingClientRect()[s]-n[s]-t[o]+r},ie=(e,t)=>re(e,a,t,b.scrollTop,`top`)+a.clientTop,w=e=>{let t=x(e);return S(t)?t:e},ae=(e,t)=>{let{enabledItems:n,highlightedIndex:r,selectedItem:i}=d(),a=i??n[r]??n[0],s=ne(e),c=S(o),l=e.left,u,f=0,p=s.height;if(a){let e=x(a),n=!!(c&&S(e)),r=n&&e?e:w(a),i=S(r)??r.getBoundingClientRect();f=ie(r,t),p=i.height||r.offsetHeight||a.getBoundingClientRect().height||a.offsetHeight||s.height,n&&c&&(l=c.left-(i.left-t.left)),u=s.top+s.height/2-f-p/2}else u=e.top;return{x:l,y:u,alignItem:a,anchorTopInContent:f,anchorHeight:p,triggerAlignmentRect:s}},T=()=>{let e=c(),i=r.ownerDocument.defaultView??window,o=C(),s=te(a),l,u=`bottom`,d,x=f===`item-aligned`;if(f===`item-aligned`){let e=Number.parseFloat(i.getComputedStyle(a).minHeight)||0,r=Math.max(0,i.innerHeight-v*2),c=(o.width>0||o.height>0)&&(o.topi.innerHeight-v-20),f=s.height>0&&(b.scrollHeight<=b.clientHeight&&s.height>r+.5||e>0&&r+.5_?n0){let t=t=>Math.min(Math.max(e.anchorTopInContent+e.anchorHeight/2-(n-t),0),c),i=t=>r(n-(e.anchorTopInContent-t+e.anchorHeight/2),v,a),o=t(i(t(r(n-s.height/2,v,a))));b.scrollTop=o,l.y=i(o)}else b.scrollTop=0,l.y=r(e.y,v,a);u=l.ye instanceof Node&&a.contains(e)});return{update:T,measureTrigger:C,start:()=>{let e=c().style;e.position=y?`fixed`:`absolute`,e.top=`0px`,e.left=`0px`,e.willChange=`transform`,e.margin=`0`,E.start()},stop:()=>E.stop(),syncResolvedPositionAttributes:ee}}const ue=`@data-slot/select`;function de(t,n={}){let s=ie(t,ue,`[@data-slot/select] createSelect() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(s)return s;let h=b(t,`select-trigger`),g=b(t,`select-content`),_=b(t,`select-value`),v=b(t,`select-positioner`),x=v&&g&&v.contains(g)?v:null,S=b(t,`select-portal`),te=S&&x&&S.contains(x)?S:null;if(!h||!g)throw Error(`Select requires trigger and content slots`);let{defaultValue:E,defaultOpen:oe,placeholder:se,disabled:D,required:O,name:de,onValueChange:fe,onOpenChange:pe,position:me,preferredSide:he,preferredAlign:ge,sideOffset:_e,alignOffset:ve,avoidCollisions:ye,collisionPadding:be,lockScrollOption:xe,highlightItemOnHover:Se,mountStrategy:Ce}=ce(t,g,_,x,n),k=!1,A=E,j=null,M=-1,N=u(),P=!1,F=0,I=0,we=``,L=!1,R=[],z=[],B=[],V=new Map,Te=null,H=!1,U=o({content:g,root:t,wrapperSlot:x?void 0:`select-positioner`,container:x??void 0,mountTarget:x?te??x:void 0}),W=r({root:t,target:te??x??g,strategy:Ce});R.push(()=>W.cleanup());let G=l();G.onDestroy(()=>{H&&=(T(),!1)});let Ee=!0,K=e=>e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`,De=e=>e.pointerType!==`touch`,Oe=f(h,`select-trigger`),ke=f(g,`select-content`);h.setAttribute(`role`,`combobox`),h.setAttribute(`aria-haspopup`,`listbox`),h.removeAttribute(`aria-controls`),h.hasAttribute(`type`)||h.setAttribute(`type`,`button`),g.setAttribute(`role`,`listbox`),g.setAttribute(`aria-labelledby`,Oe),g.tabIndex=-1;let Ae=document.querySelector(`label[for="${CSS.escape(Oe)}"]`);if(Ae){let e=f(Ae,`select-label`),t=h.getAttribute(`aria-labelledby`);h.setAttribute(`aria-labelledby`,t?`${t} ${e}`:e),R.push(C(Ae,`click`,e=>{e.preventDefault(),D||Q(!k)}))}D&&(h.setAttribute(`aria-disabled`,`true`),h.setAttribute(`data-disabled`,``),h instanceof HTMLButtonElement&&(h.disabled=!0)),O&&h.setAttribute(`aria-required`,`true`);let q=null,je=()=>{let e=z.find(e=>e.dataset.value===A)??null;e!==q&&(q&&(w(q,`selected`,!1),q.removeAttribute(`data-selected`)),e&&(w(e,`selected`,!0),e.setAttribute(`data-selected`,``)),q=e)},Me=()=>{let e=new Set(z);z=y(t,g,`[data-slot="select-item"]`);for(let t of z)t.hasAttribute(`data-disabled`)||t.hasAttribute(`disabled`)?t.setAttribute(`aria-disabled`,`true`):t.removeAttribute(`aria-disabled`),!e.has(t)&&(t.setAttribute(`role`,`option`),t.tabIndex=-1,w(t,`selected`,!1),t.removeAttribute(`data-selected`));B=z.filter(e=>!K(e)),V=new Map(B.map((e,t)=>[e,t])),je();let n=y(t,g,`[data-slot="select-group"]`);for(let e of n){e.setAttribute(`role`,`group`);let n=y(t,e,`[data-slot="select-label"]`)[0]??null;if(n){let t=f(n,`select-label`);e.setAttribute(`aria-labelledby`,t)}}},Ne=y(t,g,`[data-slot="select-viewport"]`)[0]??null,Pe=Ne??g,Fe=e=>y(t,e,`[data-slot="select-item-text"]`)[0]??null,Ie=e=>e?.textContent?.trim()||void 0,Le=(e,t=``)=>e?(e.dataset.label?.trim()||void 0)??Ie(Fe(e))??Ie(e)??t:t,J=le({root:t,trigger:h,content:g,valueSlot:_,getPositioner:()=>U.container,viewport:Ne,isOpen:()=>k,getCollection:()=>({enabledItems:B,highlightedIndex:M,selectedItem:q}),position:me,preferredSide:he,preferredAlign:ge,sideOffset:_e,alignOffset:ve,avoidCollisions:ye,collisionPadding:be,lockScroll:xe}),Y=(e,t=!0,n=!0)=>{e!==M&&(B[M]?.removeAttribute(`data-highlighted`),B[e]?.setAttribute(`data-highlighted`,``),M=e);let r=B[e];r&&(n&&p(r,Pe),t&&r.focus())},Re=()=>Y(-1),ze=()=>Y(q?B.indexOf(q):-1,!1,!1),Be=()=>{Re(),m(g)},Ve=e=>{t.setAttribute(`data-state`,e),h.setAttribute(`data-state`,e),g.setAttribute(`data-state`,e),e===`open`?(t.setAttribute(`data-open`,``),h.setAttribute(`data-open`,``),g.setAttribute(`data-open`,``),t.removeAttribute(`data-closed`),h.removeAttribute(`data-closed`),g.removeAttribute(`data-closed`)):(t.setAttribute(`data-closed`,``),h.setAttribute(`data-closed`,``),g.setAttribute(`data-closed`,``),t.removeAttribute(`data-open`),h.removeAttribute(`data-open`),g.removeAttribute(`data-open`))},X=!1,He=null,Ue=()=>{X=!1,He=null,j&&document.contains(j)?m(j):h&&document.contains(h)&&m(h),j=null},We=()=>{X=!0,He=G.trackRaf(Ue)};G.onBeforeDestroy(()=>{X&&G.trackFinalRaf(Ue)});let Ge=()=>{G.isDestroyed||(U.restore(),g.hidden=!0,W.unmount(),Ee?We():j=null)},Z=c({element:g,onExitComplete:Ge}),Ke=()=>{_&&(A===null?(_.textContent=se,h.setAttribute(`data-placeholder`,``)):(_.textContent=Le(q,A),h.removeAttribute(`data-placeholder`)))},Q=(e,n={})=>{if(G.isDestroyed)return;let{skipFocusRestore:r=!1,immediate:i=!1}=n;if(k!==e&&!(D&&e)){if(e){let e=L;L=!1,Ee=!0;let t=document.activeElement;(!X||t!==document.body&&!g.contains(t))&&(j=t),G.cancelRaf(He),He=null,X=!1,k=!0,J.measureTrigger(),W.mount(),U.mount(),h.setAttribute(`aria-controls`,ke),w(h,`expanded`,!0),g.hidden=!1,Ve(`open`),Z.enter(),xe&&!H&&(ee(),H=!0),Me(),P=!1,ze(),J.start(),J.update(),G.trackRaf(()=>{if(!(G.isDestroyed||!k)&&(J.update(),e&&Se&&we!==`touch`&&(F!==0||I!==0))){let e=document.elementFromPoint(F,I)?.closest?.(`[data-slot="select-item"]`);if(e&&!K(e)&&g.contains(e)){let t=V.get(e);t!==void 0&&Y(t,!0,!1)}}}),m(g)}else k=!1,L=!1,F=0,I=0,we=``,w(h,`expanded`,!1),h.removeAttribute(`aria-controls`),Ve(`closed`),Re(),N.reset(),P=!1,Ee=!r,H&&=(T(),!1),J.stop(),i?(Z.cleanup(),Ge()):Z.exit();d(t,`select:open-change`,{open:k}),pe?.(k)}},$=(e,n=!1)=>{if(G.isDestroyed||A===e&&!n)return;let r=A;A=e,Te?.setValue(e),e===null?t.removeAttribute(`data-value`):t.setAttribute(`data-value`,e),je(),Ke(),!n&&r!==e&&(d(t,`select:change`,{value:e}),fe?.(e))},qe=e=>{if(K(e))return;let t=e.dataset.value;t!==void 0&&($(t),Q(!1,{immediate:!0}))},Je=e=>{let t=B.length;if(t!==0)switch(e.key){case`ArrowDown`:e.preventDefault(),P=!0,Y(M===-1?0:Math.min(M+1,t-1));break;case`ArrowUp`:e.preventDefault(),P=!0,Y(M===-1?t-1:Math.max(M-1,0));break;case`Home`:e.preventDefault(),P=!0,Y(0);break;case`End`:e.preventDefault(),P=!0,Y(t-1);break;case`Enter`:case` `:e.preventDefault(),M>=0&&qe(B[M]);break;case`Tab`:Q(!1,{skipFocusRestore:!0});break;case`Escape`:e.preventDefault(),Q(!1);break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(e.preventDefault(),Ye(e.key.toLowerCase()))}},Ye=e=>{let t=N.match(e,B.map(e=>Le(e)),M);t!==-1&&(P=!0,Y(t))};w(h,`expanded`,!1),g.hidden=!0,J.syncResolvedPositionAttributes(),Ve(`closed`),Me(),$(A,!0),Te=a({root:t,name:de,defaultValue:E,disabled:D,required:O,validationTarget:h,onReset:e=>{$(e,!0),k&&(N.reset(),ze())}}),R.push(C(h,`pointerdown`,e=>{F=e.clientX,I=e.clientY,we=e.pointerType,L=!0}),C(h,`click`,()=>{D||Q(!k)}),C(h,`keydown`,e=>{if(!D)switch(e.key){case`Enter`:case` `:case`ArrowDown`:case`ArrowUp`:e.preventDefault(),L=!1,Q(!0);break}})),R.push(C(g,`keydown`,Je),C(g,`click`,e=>{let n=e.target.closest?.(`[data-slot="select-item"]`);n&&y(t,g,`[data-slot="select-item"]`).includes(n)&&qe(n)}),C(g,`pointermove`,e=>{if(!Se||!De(e))return;let t=e.target.closest?.(`[data-slot="select-item"]`);if(!(P&&(P=!1,t&&V.get(t)===M)))if(t&&!K(t)){let e=V.get(t);e!==void 0&&e!==M&&Y(e,!0)}else Be()}),C(g,`pointerleave`,e=>{!Se||!De(e)||P||Be()})),R.push(i({root:t,isOpen:()=>k,onDismiss:()=>Q(!1),closeOnClickOutside:!0,closeOnEscape:!1})),R.push(ne(t,`select:set`,e=>{let t=e.detail;t?.value!==void 0&&$(t.value),t?.open!==void 0&&Q(t.open)}));let Xe={get value(){return A},get isOpen(){return k},select:e=>{G.isDestroyed||$(e)},open:()=>{G.isDestroyed||Q(!0)},close:()=>{G.isDestroyed||Q(!1)},destroy:()=>{G.destroy()&&(N.destroy(),k=!1,w(h,`expanded`,!1),h.removeAttribute(`aria-controls`),Ve(`closed`),g.hidden=!0,Te?.destroy())}};return re(G,{cleanups:R,positionSync:J,presence:Z,portal:U,unbind:()=>e(t,ue,Xe)}),ae(t,ue,Xe),oe&&Q(!0),k||W.unmount(),Xe}function fe(e=document){return D(e,de)}export{fe as create,de as createSelect}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-slider.js b/liquid/vendor/data-slot-slider.js new file mode 100644 index 000000000..c34f6bd91 --- /dev/null +++ b/liquid/vendor/data-slot-slider.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataEnum as i,getDataNumber as a,getDataString as o,getPart as s,getParts as c,getRoots as l,hasRootBinding as u,on as d,onRoot as f,reuseRootBinding as p,setAria as m,setRootBinding as ee}from"@data-slot/core";const te=[`horizontal`,`vertical`],ne=[`center`,`edge`,`edge-client-only`];function re(e){if(!e)return;let t=e.split(`,`).map(e=>parseFloat(e.trim()));if(!t.some(e=>isNaN(e))){if(t.length===2)return[t[0],t[1]];if(t.length===1)return t[0]}}function h(e){return Array.isArray(e)}function g(e,t,n,r){let i=Math.round((e-t)/r)*r+t,a=r.toString().split(`.`)[1]?.length??0,o=parseFloat(i.toFixed(a));return Math.min(n,Math.max(t,o))}function _(e,t,n){return n===t?0:(e-t)/(n-t)*100}function ie(e,t,n){return e/100*(n-t)+t}function v(e){return Math.max(0,Math.min(100,e))}const y=`@data-slot/slider`;function b(l,u={}){let b=p(l,y,`[@data-slot/slider] createSlider() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(b)return b;let x=l,S=s(l,`slider-track`),C=c(l,`slider-thumb`),w=s(l,`slider-range`),ae=s(l,`slider-control`);if(!S||C.length===0)throw Error(`Slider requires slider-track and at least one slider-thumb`);let T=ae??(S.parentElement instanceof HTMLElement?S.parentElement:null)??x,E=u.min??a(l,`min`)??0,D=u.max??a(l,`max`)??100;E>D&&([E,D]=[D,E]);let O=u.step??a(l,`step`)??1;O<=0&&(O=1);let k=u.largeStep??a(l,`largeStep`)??O*10,A=u.orientation??i(l,`orientation`,te)??`horizontal`,oe=u.thumbAlignment??i(l,`thumbAlignment`,ne)??`center`,j=u.disabled??r(l,`disabled`)??!1,se=u.onValueChange,M=u.onValueCommit,ce=re(o(l,`defaultValue`)),N=u.defaultValue??ce??E,P=C.length>=2;P&&!h(N)?N=[E,N]:!P&&h(N)&&(N=N[1]);let F=h(N)?[g(N[0],E,D,O),g(N[1],E,D,O)]:g(N,E,D,O),I=[],L=null,R=0,z=null,B=null,V=0,H=null,le=Array.from(new Set([x,T,S,w,...C].filter(e=>e instanceof HTMLElement)));(()=>{for(let e of le)e.setAttribute(`data-orientation`,A)})(),(e=>{for(let t of le)e?t.setAttribute(`data-disabled`,``):t.removeAttribute(`data-disabled`);for(let t of C)m(t,`disabled`,e),t.tabIndex=e?-1:0})(j);let ue=(e,t)=>{e.setAttribute(`role`,`slider`),e.tabIndex=j?-1:0,n(e,`slider-thumb`),m(e,`orientation`,A),P?e.setAttribute(`data-index`,String(t)):e.removeAttribute(`data-index`);let r=e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`),i=e.dataset.label;i?m(e,`label`,i):!r&&P&&m(e,`label`,t===0?`Minimum`:`Maximum`)};for(let e=0;ee instanceof HTMLElement))),fe=()=>{S.style.position=`relative`},G=(e,t)=>{e.style.position=`absolute`,e.style.setProperty(`--position`,`${t}%`),U?(e.style.setProperty(`inset-inline-start`,`var(--position)`),e.style.top=`50%`,e.style.bottom=``,e.style.left=``,e.style.setProperty(`translate`,`-50% -50%`)):(e.style.removeProperty(`inset-inline-start`),e.style.bottom=`var(--position)`,e.style.left=`50%`,e.style.top=``,e.style.setProperty(`translate`,`-50% 50%`))},pe=(e,t,n)=>{if(!w)return;let r=n?e:t;w.style.setProperty(`--start-position`,`${r}%`),w.style.position=U?`relative`:`absolute`,U?(w.style.setProperty(`inset-inline-start`,n?`var(--start-position)`:`0%`),w.style.width=n?`var(--relative-size)`:`var(--start-position)`,w.style.height=`inherit`,w.style.bottom=``,w.style.left=``):(w.style.removeProperty(`inset-inline-start`),w.style.bottom=n?`var(--start-position)`:`0%`,w.style.height=n?`var(--relative-size)`:`var(--start-position)`,w.style.width=`inherit`,w.style.left=``),n?w.style.setProperty(`--relative-size`,`${t}%`):w.style.removeProperty(`--relative-size`)};fe();let K=e=>U?e.width:e.height,me=(e,t)=>U?t.left-e.left:e.bottom-t.bottom,he=e=>e.offsetParent instanceof HTMLElement?e.offsetParent:T,q=(e,t,n)=>{if(!Number.isFinite(t)||!Number.isFinite(n)||t<=0)return;let r=Math.min(t,n),i=Math.max(0,t-r),a=v((r/2+i*e/100)/t*100);return Number.isFinite(a)?a:void 0},J=(e,t)=>{let n={thumbPercent:t,trackPercent:t};if(!e||!W)return n;let r=S.getBoundingClientRect(),i=e.getBoundingClientRect(),a=he(e).getBoundingClientRect(),o=K(r),s=K(i),c=K(a);if(!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(c)||o<=0||c<=0)return n;let l=q(t,o,s);if(l===void 0)return n;let u=Math.min(o,s),d=Math.max(0,o-u),f=u/2+d*t/100,p=v((me(a,r)+f)/c*100);return!Number.isFinite(l)||!Number.isFinite(p)?n:{thumbPercent:p,trackPercent:l}},ge=(e,t)=>{let n={startPercent:e,sizePercent:Math.max(0,t-e)};if(!W)return n;let r=K(S.getBoundingClientRect());if(!Number.isFinite(r)||r<=0)return n;let i=C.slice(0,2).map(e=>e instanceof HTMLElement?K(e.getBoundingClientRect()):NaN);if(i.length<2||i.some(e=>!Number.isFinite(e)||e<=0))return n;let a=Math.max(...i),o=q(e,r,a),s=q(t,r,a);return o===void 0||s===void 0?n:{startPercent:o,sizePercent:Math.max(0,s-o)}},Y=()=>{if(h(F)){let[e,t]=F,n=_(e,E,D),r=_(t,E,D),i=J(C[0],n),a=J(C[1],r),o=ge(n,r);C[0]&&(m(C[0],`valuenow`,String(e)),m(C[0],`valuemin`,String(E)),m(C[0],`valuemax`,String(t)),G(C[0],i.thumbPercent)),C[1]&&(m(C[1],`valuenow`,String(t)),m(C[1],`valuemin`,String(e)),m(C[1],`valuemax`,String(D)),G(C[1],a.thumbPercent)),pe(o.startPercent,o.sizePercent,!0)}else{let e=_(F,E,D),t=J(C[0],e);C[0]&&(m(C[0],`valuenow`,String(F)),m(C[0],`valuemin`,String(E)),m(C[0],`valuemax`,String(D)),G(C[0],t.thumbPercent)),pe(0,t.trackPercent,!1)}h(F)?l.setAttribute(`data-value`,`${F[0]},${F[1]}`):l.setAttribute(`data-value`,String(F))},_e=(e,t)=>h(e)&&h(t)?e[0]===t[0]&&e[1]===t[1]:e===t,X=(e,n=!0)=>{let r;if(h(e)){let[t,n]=e;t=g(t,E,D,O),n=g(n,E,D,O),t>n&&([t,n]=[n,t]),r=[t,n]}else r=g(e,E,D,O);return _e(r,F)?!1:(F=r,Y(),n&&(t(l,`slider:change`,{value:F}),se?.(F)),!0)};Y(),(()=>{if(H?.disconnect(),H=null,!(!W||typeof ResizeObserver!=`function`)){H=new ResizeObserver(()=>{Y()});for(let e of de)H.observe(e)}})(),I.push(()=>{H?.disconnect(),H=null});let Z=(e,t=null)=>{let n=S.getBoundingClientRect(),r=K(n);if(r===0)return null;let i=(U?e.clientX:e.clientY)-V,a=U?i-n.left:n.bottom-i,o=a/r*100;if(W&&t!==null){let e=C[t]?.getBoundingClientRect(),n=e?K(e):0,i=r-n;Number.isFinite(n)&&n>0&&i>0&&(o=(a-n/2)/i*100)}return o=v(o),ie(o,E,D)},ve=(e,t)=>{if(!W||e===null)return 0;let n=C[e],r=n?.getBoundingClientRect(),i=r?K(r):0;if(!n||!r||!Number.isFinite(i)||i<=0)return 0;let a=U?r.left+r.width/2:r.top+r.height/2;return(U?t.clientX:t.clientY)-a},ye=e=>{if(!e||!(e instanceof HTMLElement))return null;let t=C.indexOf(e);if(t!==-1)return t;for(let t=0;t{if(!h(F))return 0;let[t,n]=F,r=Math.abs(e-t),i=Math.abs(e-n);return r===i?R:r{if(h(F)){let[n,r]=F;return X(e===0?[g(t,E,r,O),r]:[n,g(t,n,D,O)])}else return X(t)},xe=e=>{if(j)return;e.preventDefault();let t=ye(e.target);V=ve(t,e);let n=Z(e,t);if(n===null){V=0;return}if(t===null){t=be(n);let r=Z(e,t);r!==null&&(n=r)}L=t,R=t,l.setAttribute(`data-dragging`,``),C[t]?.setAttribute(`data-dragging`,``),C[t]?.focus(),B=T.style.touchAction,T.style.touchAction=`none`,Q(t,n),T.setPointerCapture(e.pointerId)},Se=e=>{if(L===null||j)return;e.preventDefault();let t=Z(e,L);t!==null&&Q(L,t)},Ce=e=>{if(L!==null){l.removeAttribute(`data-dragging`);for(let e of C)e.removeAttribute(`data-dragging`);T.style.touchAction=B??``,B=null,t(l,`slider:commit`,{value:F}),M?.(F),L=null,V=0;try{T.releasePointerCapture(e.pointerId)}catch{}}};I.push(d(T,`pointerdown`,xe)),I.push(d(T,`pointermove`,Se)),I.push(d(T,`pointerup`,Ce)),I.push(d(T,`pointercancel`,Ce));let we=e=>{if(j)return;let t=e.target,n=C.indexOf(t);if(n===-1)return;let r=0,i=null;switch(e.key){case`ArrowRight`:if(!U)return;r=O;break;case`ArrowLeft`:if(!U)return;r=-O;break;case`ArrowUp`:if(U)return;r=O;break;case`ArrowDown`:if(U)return;r=-O;break;case`PageUp`:r=k;break;case`PageDown`:r=-k;break;case`Home`:i=E;break;case`End`:i=D;break;default:return}e.shiftKey&&e.key.startsWith(`Arrow`)&&(r=r>0?k:r<0?-k:0),e.preventDefault(),R=n,z===null&&(z=h(F)?[F[0],F[1]]:F);let a=h(F)?F[n]??F[0]:F;Q(n,i===null?a+r:i)},Te=()=>{z!==null&&(_e(z,F)||(t(l,`slider:commit`,{value:F}),M?.(F)),z=null)};for(let e of C)I.push(d(e,`keydown`,we)),I.push(d(e,`blur`,Te));I.push(f(l,`slider:set`,e=>{if(j)return;let n=e.detail,r;typeof n==`number`||Array.isArray(n)?r=n:n&&typeof n==`object`&&`value`in n&&(r=n.value),r!==void 0&&X(r)&&(t(l,`slider:commit`,{value:F}),M?.(F))}));let $={setValue:e=>{X(e)},get value(){return F},get min(){return E},get max(){return D},get disabled(){return j},destroy:()=>{V=0,I.forEach(e=>e()),I.length=0,e(l,y,$)}};return ee(l,y,$),$}function x(e=document){let t=[];for(let n of l(e,`slider`))u(n,y)||t.push(b(n));return t}export{x as create,b as createSlider}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-switch.js b/liquid/vendor/data-slot-switch.js new file mode 100644 index 000000000..2aa40017d --- /dev/null +++ b/liquid/vendor/data-slot-switch.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataString as i,getParts as a,getRoots as o,hasRootBinding as s,observeFormReset as c,on as l,onRoot as u,reuseRootBinding as d,setAria as f,setRootBinding as p}from"@data-slot/core";const m=`@data-slot/switch`,h=[`position:absolute`,`width:1px`,`height:1px`,`padding:0`,`margin:-1px`,`overflow:hidden`,`clip:rect(0, 0, 0, 0)`,`white-space:nowrap`,`border:0`,`pointer-events:none`].join(`;`);function g(e,t,n){n?e.setAttribute(t,``):e.removeAttribute(t)}function _(e,t){g(e,`data-checked`,t),g(e,`data-unchecked`,!t)}function v(e,t,n,r){g(e,`data-disabled`,t),g(e,`data-readonly`,n),g(e,`data-required`,r)}function y(e,t){let n=new Set;if(e)for(let t of e.split(/\s+/))t&&n.add(t);for(let e of t)e&&n.add(e);return n.size>0?[...n].join(` `):null}function b(e,t){let n=e.parentNode;if(!n){e.appendChild(t);return}n.insertBefore(t,e.nextSibling)}function x(e){return e.tagName===`BUTTON`}function S(e){let t=e.tagName;return t===`BUTTON`||t===`INPUT`||t===`SELECT`||t===`TEXTAREA`?!0:t===`A`?e.hasAttribute(`href`):!1}function C(e){let t=[],n=e.closest(`label`);if(n instanceof HTMLLabelElement&&t.push(n),!e.id)return t;let r=e.ownerDocument??document,i=`label[for="${CSS.escape(e.id)}"]`;for(let e of r.querySelectorAll(i))t.includes(e)||t.push(e);return t}function w(o,s={}){let g=d(o,m,`[@data-slot/switch] createSwitch() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let w=o,T=s.disabled??r(w,`disabled`)??(w.hasAttribute(`disabled`)||w.getAttribute(`aria-disabled`)===`true`),E=s.readOnly??r(w,`readOnly`)??w.getAttribute(`aria-readonly`)===`true`,D=s.required??r(w,`required`)??w.getAttribute(`aria-required`)===`true`,O=s.defaultChecked??r(w,`defaultChecked`)??w.getAttribute(`aria-checked`)===`true`,k=s.name??i(w,`name`),A=s.value??i(w,`value`),j=s.uncheckedValue??i(w,`uncheckedValue`),M=s.onCheckedChange,N=[],P=o.ownerDocument??document,F=P.createElement(`input`);F.type=`checkbox`,F.tabIndex=-1,F.setAttribute(`aria-hidden`,`true`),F.setAttribute(`data-switch-generated`,`input`),F.style.cssText=h,b(w,F);let I=null,L=!!O;F.defaultChecked=L;let R=a(w,`switch-thumb`),z=()=>{F.checked=L,f(w,`checked`,L),_(w,L);for(let e of R)_(e,L);!T&&!L&&k!==void 0&&j!==void 0?I||(I=P.createElement(`input`),I.type=`hidden`,I.setAttribute(`data-switch-generated`,`unchecked`),I.name=k,I.value=j,b(F,I)):(I?.remove(),I=null)},B=(e,n=!0)=>{L!==e&&(L=e,z(),n&&(t(w,`switch:change`,{checked:L}),M?.(L)))},V=()=>{T||E||F.click()},H=C(w);if(H.length>0){let e=H.map(e=>n(e,`switch-label`)),t=y(w.getAttribute(`aria-labelledby`),e);t&&w.setAttribute(`aria-labelledby`,t)}x(w)?(w.hasAttribute(`type`)||w.setAttribute(`type`,`button`),w.disabled=T):S(w)||(T?w.tabIndex=-1:w.hasAttribute(`tabindex`)||(w.tabIndex=0)),w.setAttribute(`role`,`switch`),f(w,`disabled`,T?!0:null),f(w,`readonly`,E?!0:null),f(w,`required`,D?!0:null),v(w,T,E,D);for(let e of R)v(e,T,E,D);F.disabled=T,F.required=D,k&&(F.name=k),A!==void 0&&(F.value=A),z();let U=c({root:w,getForm:()=>F.form??w.closest(`form`),onReset:()=>B(F.checked,!1)});N.push(()=>U.destroy()),N.push(l(F,`click`,e=>{(T||E)&&e.preventDefault()})),N.push(l(F,`change`,()=>{B(F.checked)})),N.push(l(w,`click`,e=>{if(!(e.target===F||e.target===I)){if(T||E){e.preventDefault();return}e.preventDefault(),F.click()}})),x(w)||N.push(l(w,`keydown`,e=>{let t=e;t.repeat||t.key!==`Enter`&&t.key!==` `&&t.key!==`Spacebar`||(t.preventDefault(),V())}));for(let e of H)e.contains(w)||N.push(l(e,`click`,e=>{e.preventDefault(),V()}));N.push(u(w,`switch:set`,e=>{let t=e.detail,n=typeof t==`boolean`?t:typeof t?.checked==`boolean`?t.checked:typeof t?.value==`boolean`?t.value:void 0;typeof n==`boolean`&&B(n)}));let W={get checked(){return L},toggle:()=>B(!L),check:()=>B(!0),uncheck:()=>B(!1),setChecked:e=>B(!!e),destroy:()=>{N.forEach(e=>e()),N.length=0,F.remove(),I?.remove(),e(w,m,W)}};return p(w,m,W),W}function T(e=document){let t=[];for(let n of o(e,`switch`))s(n,m)||t.push(w(n));return t}export{T as create,w as createSwitch}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-tabs.js b/liquid/vendor/data-slot-tabs.js new file mode 100644 index 000000000..54b809f67 --- /dev/null +++ b/liquid/vendor/data-slot-tabs.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,getDataEnum as r,getDataString as i,getFocusable as a,getPart as o,getParts as s,getRoots as c,hasRootBinding as l,on as u,onRoot as d,reuseRootBinding as f,setAria as p,setRootBinding as m}from"@data-slot/core";const h=[`horizontal`,`vertical`],g=[`auto`,`manual`],_=`@data-slot/tabs`;function v(c,l={}){let v=f(c,_,`[@data-slot/tabs] createTabs() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(v)return v;let y=o(c,`tabs-list`),b=s(c,`tabs-trigger`),x=s(c,`tabs-content`),S=o(c,`tabs-indicator`);if(!y||b.length===0)throw Error(`Tabs requires tabs-list and at least one tabs-trigger`);let C=l.onValueChange,w=l.orientation??r(c,`orientation`,h)??`horizontal`,T=l.activationMode??r(c,`activationMode`,g)??`auto`,E=new Map;for(let e of x){let t=(e.dataset.value||``).trim();t&&E.set(t,e)}let D=[],O=new Map,k=new Map;for(let e of b){let t=(e.dataset.value||``).trim();if(!t)continue;let n={el:e,value:t,disabled:e.hasAttribute(`disabled`)||e.dataset.disabled!==void 0||e.getAttribute(`aria-disabled`)===`true`,panel:E.get(t)};D.push(n),O.set(t,n),k.set(e,n)}let A=D.filter(e=>!e.disabled),j=new Map;A.forEach((e,t)=>j.set(e.value,t));let M=A[0]?.value||``,N=(l.defaultValue??i(c,`defaultValue`)??``).trim(),P=O.get(N),F=P&&!P.disabled?N:M,I=[],L=null,R=null;y.setAttribute(`role`,`tablist`),w===`vertical`&&p(y,`orientation`,`vertical`);for(let e of D){let{el:t,disabled:r,panel:i}=e;t.setAttribute(`role`,`tab`);let a=n(t,`tab`);if(t.tagName===`BUTTON`&&!t.hasAttribute(`type`)&&(t.type=`button`),r&&(t.setAttribute(`aria-disabled`,`true`),t.tagName===`BUTTON`&&(t.disabled=!0)),i){i.setAttribute(`role`,`tabpanel`),i.tabIndex=-1;let e=n(i,`tabpanel`);t.setAttribute(`aria-controls`,e),i.setAttribute(`aria-labelledby`,a)}}let z=e=>{for(let[t,n]of Object.entries(e))S.style.setProperty(`--active-tab-${t}`,`${n}px`)},B=e=>{let t=0,n=0,r=e;for(;r&&r!==y;){t+=r.offsetLeft,n+=r.offsetTop;let e=r.offsetParent;if(!(e instanceof HTMLElement))return null;e!==y&&(t-=e.scrollLeft,n-=e.scrollTop),r=e}if(r!==y)return null;let i={left:t-y.clientLeft,top:n-y.clientTop,width:e.offsetWidth,height:e.offsetHeight},a=V(e);return a&&(w===`horizontal`?i.top=Math.floor(a.top):i.left=Math.floor(a.left)),i},V=e=>{let t=y.getBoundingClientRect(),n=e.getBoundingClientRect();if(t.width===0&&t.height===0||n.width===0&&n.height===0)return null;let r=(e,t)=>{if(t<=0)return 1;let n=e/t;return Math.abs(n-1)<.05?1:n},i=r(n.width,e.offsetWidth),a=r(n.height,e.offsetHeight);return{left:(n.left-t.left-y.clientLeft+y.scrollLeft)/i,top:(n.top-t.top-y.clientTop+y.scrollTop)/a,width:e.offsetWidth,height:e.offsetHeight}},H=()=>{if(!S)return;let e=O.get(F);z(e&&(B(e.el)??V(e.el))||{left:0,top:0,width:0,height:0})},U=()=>{!S||L!==null||(L=requestAnimationFrame(()=>{L=null,H()}))},W=(e,t)=>{let n=D.findIndex(t=>t.value===e),r=D.findIndex(e=>e.value===t);return n<0||r<0||n===r?null:w===`vertical`?r>n?`down`:`up`:r>n?`right`:`left`},G=(e,n=!1)=>{if(e=e.trim(),F===e&&!n)return;let r=O.get(e);if(!r||r.disabled)if(n){if(e=M,!e)return}else return;let i=F,a=n?null:W(i,e),o=n||a!==R;R=a,F=e;for(let t of D){let r=t.value===e;!n&&!r&&t.value!==i||(p(t.el,`selected`,r),t.el.tabIndex=r&&!t.disabled?0:-1,t.el.dataset.state=r?`active`:`inactive`)}for(let t of x){let r=(t.dataset.value||``).trim();if(!r)continue;let s=r===e;(n||s||r===i)&&(t.hidden=!s,t.dataset.state=s?`active`:`inactive`),o&&(a?t.dataset.activationDirection=a:delete t.dataset.activationDirection)}c.setAttribute(`data-value`,e),U(),n||(t(c,`tabs:change`,{value:e}),C?.(e))};if(G(F,!0),S){let e=()=>U();I.push(u(window,`resize`,e)),I.push(u(y,`scroll`,e));let t=new ResizeObserver(e);t.observe(y),I.push(()=>t.disconnect()),I.push(()=>{L!==null&&(cancelAnimationFrame(L),L=null)})}I.push(u(y,`click`,e=>{let t=e.target.closest?.(`[data-slot="tabs-trigger"]`);if(!t)return;let n=k.get(t);n&&!n.disabled&&G(n.value)}));let K=w===`horizontal`,q=K?`ArrowLeft`:`ArrowUp`,J=K?`ArrowRight`:`ArrowDown`;I.push(u(y,`keydown`,e=>{let t=e.target.closest?.(`[data-slot="tabs-trigger"]`);if(!t)return;let n=k.get(t);if(!n||A.length===0)return;if(e.key===`Enter`||e.key===` `){e.preventDefault(),n.disabled||G(n.value);return}if(K&&e.key===`ArrowDown`&&n.value===F){let t=n.panel;if(t){e.preventDefault(),(a(t)[0]||t).focus();return}}let r=j.get(n.value)??-1;r===-1&&(r=j.get(F)??0);let i=r;switch(e.key){case q:i=r-1,i<0&&(i=A.length-1);break;case J:i=r+1,i>=A.length&&(i=0);break;case`Home`:i=0;break;case`End`:i=A.length-1;break;default:return}e.preventDefault();let o=A[i];o&&(o.el.focus(),T===`auto`&&G(o.value))}));let Y=e=>{let t=e,n=e.currentTarget,r=t.detail,i=(typeof r==`string`?r:r?.value??n?.dataset?.value)?.trim();i&&G(i)};I.push(d(c,`tabs:set`,Y)),I.push(d(c,`tabs:select`,Y));let X={select:e=>G(e),get value(){return F},updateIndicator:H,destroy:()=>{I.forEach(e=>e()),I.length=0,e(c,_,X)}};return m(c,_,X),X}function y(e=document){let t=[];for(let n of c(e,`tabs`))l(n,_)||t.push(v(n));return t}export{y as create,v as createTabs}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-toast.js b/liquid/vendor/data-slot-toast.js new file mode 100644 index 000000000..48f7be116 --- /dev/null +++ b/liquid/vendor/data-slot-toast.js @@ -0,0 +1,8 @@ +import{clearRootBinding as e,createPortalLifecycle as t,createPresenceLifecycle as n,createSwipeGesture as r,createTerminalLifecycle as i,emit as a,focusElement as o,getDataBool as s,getDataEnum as c,getDataNumber as l,getFocusable as u,getPart as d,getRoots as f,hasRootBinding as p,isFocusable as m,on as h,reuseRootBinding as g,setRootBinding as _}from"@data-slot/core";const v=[`top-left`,`top-center`,`top-right`,`bottom-left`,`bottom-center`,`bottom-right`],y=[`default`,`success`,`error`,`warning`,`info`,`loading`],b=e=>!!e&&typeof e==`object`,x=e=>typeof e==`string`,S=e=>typeof e==`string`&&e.trim()!==``,C=e=>typeof e==`boolean`,w=e=>typeof e==`number`&&Number.isFinite(e),T=e=>typeof e==`string`&&y.includes(e),E={title:S,description:x,type:T,duration:w,action:e=>b(e)&&S(e.label)&&(e.onClick===void 0||typeof e.onClick==`function`)&&(e.value===void 0||x(e.value)),dismissible:C,closeButtonAriaLabel:x,testId:x},ee=Object.keys(E),te=new Set([`description`,`action`,`closeButtonAriaLabel`,`testId`]),ne=e=>w(e)?Math.max(1,Math.trunc(e)):3,re=(e,t)=>w(e)?e<=0?0:Math.max(1,Math.trunc(e)):t,ie=(e,t)=>({id:e,title:``,type:`default`,duration:t,dismissible:!0}),D=(e,t)=>t===void 0?e:t??void 0,ae=(e,t)=>{let n=t.title??e.title;if(!S(n))throw Error(`Toast requires a non-empty title`);return{id:e.id,title:n,description:D(e.description,t.description),type:T(t.type)?t.type:e.type,duration:t.duration===void 0?e.duration:re(t.duration,e.duration),action:D(e.action,t.action),dismissible:t.dismissible??e.dismissible,closeButtonAriaLabel:D(e.closeButtonAriaLabel,t.closeButtonAriaLabel),testId:D(e.testId,t.testId)}},O=e=>{let t={};for(let n of ee){let r=e[n];(E[n](r)||r===null&&te.has(n))&&Object.assign(t,{[n]:r})}return t},oe=e=>{if(!b(e))return null;let t=O(e);return t.title?{id:S(e.id)?e.id:void 0,patch:t}:null},se=e=>{if(!b(e)||!S(e.id))return null;let t=O(e);return Object.keys(t).length>0?{id:e.id,patch:t}:null},ce=e=>S(e)?e:b(e)&&S(e.id)?e.id:null,le=e=>typeof e==`string`?{title:e}:e??{},ue=(e,t)=>le(typeof e==`function`?e(t):e),k=(e,t)=>{let{message:n,...r}=e;return{...r,title:S(e.title)?e.title:S(n)?n:t.title,description:e.description===void 0?t.description:e.description,type:e.type??t.type,duration:e.duration??t.duration}},de=e=>S(e)?e:e instanceof Error&&S(e.message)?e.message:`Error`,A=e=>e instanceof HTMLTemplateElement,j=e=>!!e.content.querySelector(`[data-slot="toast-item"]`),M=e=>{let t=e.createElement(`template`);return t.innerHTML=` +
  • + + + + +
  • + `,t},N=(e,t)=>{e.setAttribute(`data-state`,t),t===`open`?(e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`)):(e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`))},P=(e,t)=>{t===`error`||t===`warning`?(e.setAttribute(`role`,`alert`),e.setAttribute(`aria-live`,`assertive`)):(e.setAttribute(`role`,`status`),e.setAttribute(`aria-live`,`polite`)),e.setAttribute(`aria-atomic`,`true`)};function fe(e,t){let{viewport:r}=t,a=(A(t.template)&&j(t.template)?t.template:M(r.ownerDocument)).content.cloneNode(!0),o=a.querySelector(`[data-slot="toast-item"]`),s=new WeakMap,c=i(),l=e,u=!1,d=t.paused,f=null,p=l.duration,m=0,h=(e,t)=>{let n=e.querySelector(`[data-slot="toast-title"]`),r=e.querySelector(`[data-slot="toast-description"]`),i=e.querySelector(`[data-slot="toast-action"]`),a=e.querySelector(`[data-slot="toast-close"]`);if(n&&(n.textContent=t.title,n.hidden=!1),r&&(t.description&&t.description.trim()!==``?(r.textContent=t.description,r.hidden=!1):(r.textContent=``,r.hidden=!0)),i&&(t.action?.label&&t.action.label.trim()!==``?(i.textContent=t.action.label,i.hidden=!1):(i.textContent=``,i.hidden=!0)),e.setAttribute(`data-type`,t.type),t.dismissible?e.removeAttribute(`data-dismissible`):e.setAttribute(`data-dismissible`,`false`),t.testId&&t.testId.trim()!==``?e.setAttribute(`data-testid`,t.testId):e.removeAttribute(`data-testid`),a){if(s.has(a)||s.set(a,a.getAttribute(`aria-label`)),t.closeButtonAriaLabel&&t.closeButtonAriaLabel.trim()!==``)a.setAttribute(`aria-label`,t.closeButtonAriaLabel);else{let e=s.get(a);e&&e.trim()!==``?a.setAttribute(`aria-label`,e):a.setAttribute(`aria-label`,`Close`)}a.getAttribute(`aria-label`)||a.setAttribute(`aria-label`,`Close`)}P(e,t.type)},g=()=>{c.cancelTimeout(f),f=null},_=()=>{if(!(!y.active||d||l.duration<=0||f!==null)){if(p<=0){t.onTimeout(y);return}m=Date.now(),f=c.trackTimeout(()=>{f=null,t.onTimeout(y)},p)}},v=n({element:o,win:r.ownerDocument.defaultView??window,onExitComplete:()=>{c.isDestroyed||t.onExitComplete(y)}});c.onDestroy(()=>{v.cleanup(),o.remove()});let y={get id(){return l.id},element:o,get toast(){return l},get active(){return!u&&!c.isDestroyed},get exiting(){return u},mount(){r.appendChild(a),c.trackRaf(()=>{c.trackRaf(()=>{y.active&&o.setAttribute(`data-mounted`,`true`)})}),_()},update(e){y.active&&(l=ae(l,e),h(o,l),e.duration!==void 0&&(g(),p=l.duration,_()))},setPaused(e){d===e||!y.active||(d=e,d?(f!==null&&(p=Math.max(0,p-(Date.now()-m))),g()):_())},dismiss(){y.active&&(u=!0,g(),o.setAttribute(`data-removed`,`true`),o.style.zIndex=`0`,o.style.pointerEvents=`none`,N(o,`closed`),v.exit())},destroy(){c.destroy()}};o.setAttribute(`data-id`,l.id),h(o,l);for(let e of[`mounted`,`removed`,`front`,`visible`,`swiping`,`swipe-out`])o.setAttribute(`data-${e}`,`false`);o.setAttribute(`data-expanded`,String(t.expanded));for(let e of[`amount-x`,`amount-y`,`end-x`,`end-y`])o.style.setProperty(`--toast-swipe-${e}`,`0px`);return N(o,`open`),y}const F=`data-toast-prev-tabindex`,I=`__none__`,pe=e=>{let t=getComputedStyle(e).getPropertyValue(`--toast-gap`).trim();if(!t)return 8;let n=Number.parseFloat(t);return Number.isFinite(n)?n:8},L=e=>{let t=getComputedStyle(e).getPropertyValue(`--toast-collapsed-peek`).trim();if(!t)return 14;let n=Number.parseFloat(t);return Number.isFinite(n)?Math.max(0,n):14},R=e=>{let t=e.cloneNode(!0);t.setAttribute(`aria-hidden`,`true`),t.setAttribute(`inert`,``),t.style.width=getComputedStyle(e).width,t.style.height=`auto`,t.style.maxHeight=`none`,t.style.position=`absolute`,t.style.inset=`0 auto auto 0`,t.style.visibility=`hidden`,t.style.pointerEvents=`none`,t.style.transform=`none`,t.style.transition=`none`,t.style.animation=`none`,e.parentElement.append(t);try{return t.offsetHeight||t.getBoundingClientRect().height||e.offsetHeight||e.getBoundingClientRect().height}finally{t.remove()}},me=e=>{let t=u(e);return m(e)&&t.unshift(e),t.filter(e=>e.getAttribute(`aria-hidden`)!==`true`)},z=e=>{let t=[...e.querySelectorAll(`[${F}]`)];return e.hasAttribute(F)&&t.unshift(e),t},B=(e,t)=>{if(t){e.removeAttribute(`aria-hidden`),e.removeAttribute(`inert`);for(let t of z(e)){let e=t.getAttribute(F);t.removeAttribute(F),!e||e===I?t.removeAttribute(`tabindex`):t.setAttribute(`tabindex`,e)}return}for(let t of me(e)){if(!t.hasAttribute(F)){let e=t.getAttribute(`tabindex`);t.setAttribute(F,e===null?I:e)}t.setAttribute(`tabindex`,`-1`)}e.setAttribute(`aria-hidden`,`true`),e.setAttribute(`inert`,``)};function he({viewport:e,limit:t,stackDirection:n,getItems:r,onLayout:i}){let a=e.ownerDocument,o=new Set,s=new WeakMap,c=!1;e.style.setProperty(`--toast-lift`,String(n));let l=t=>{t?e.setAttribute(`data-expanded`,``):e.removeAttribute(`data-expanded`);let n=t?e.style.getPropertyValue(`--toast-expanded-stack-size`):e.style.getPropertyValue(`--toast-collapsed-stack-size`);e.style.setProperty(`--toast-stack-size`,n.trim()===``?`0px`:n);for(let e of o)e.setAttribute(`data-expanded`,String(t))},u=()=>{if(c)return;let n=r(),o=pe(e),l=L(e),u=n.length,d=0,f=0,p=0,m=0;for(let e=0;e0&&s.set(r,h),e===0&&(m=h);let g=e*l;if(r.style.setProperty(`--toast-index`,String(e)),r.style.setProperty(`--toast-initial-height`,`${h}px`),r.style.setProperty(`--toast-offset`,`${d}px`),r.style.setProperty(`--toast-collapsed-offset-y`,`${g}px`),r.setAttribute(`data-front`,String(e===0)),r.setAttribute(`data-visible`,String(i)),r.setAttribute(`data-removed`,`false`),r.style.zIndex=String(u-e+1),r.style.pointerEvents=i?``:`none`,!i&&c){let e=a.activeElement;e instanceof HTMLElement&&r.contains(e)&&e.blur()}B(r,i),d+=h+o,i&&(p+=1,f+=h+o)}let h=p>0?Math.max(0,f-o):0,g=p>0?Math.max(0,m+l*(p-1)):0;e.style.setProperty(`--toast-count`,String(u)),e.style.setProperty(`--toast-front-height`,`${m}px`),e.style.setProperty(`--toast-expanded-stack-size`,`${h}px`),e.style.setProperty(`--toast-collapsed-stack-size`,`${g}px`),i()},d=typeof ResizeObserver<`u`?new ResizeObserver(u):null;return{update:u,setExpanded:l,observe(e){o.add(e),d?.observe(e)},unobserve(e){o.delete(e),d?.unobserve(e)},destroy(){c=!0,d?.disconnect(),o.clear(),e.removeAttribute(`data-expanded`)}}}const V=e=>{e.element.setAttribute(`data-swiping`,`false`),e.element.setAttribute(`data-swipe-out`,`false`);for(let t of[`amount-x`,`amount-y`,`end-x`,`end-y`])e.element.style.removeProperty(`--toast-swipe-${t}`)};function ge({viewport:e,position:t,getEntry:n,dismiss:i}){let a={x:t.endsWith(`left`)?-1:t.endsWith(`right`)?1:0,y:t.startsWith(`top`)?-1:1},o=a.x===0?[`y`]:[`x`,`y`],s=(e,t)=>e*t<0?e*.2:e,c=e=>e.active&&e.toast.dismissible;return r({element:e,axes:o,lockThreshold:12,capture:e=>e.element,start(e,t){if(t.closest(`[data-slot="toast-action"], [data-slot="toast-close"]`))return null;let r=n(t);return r&&c(r)?r:null},active:c,move(e,{axis:t,deltaX:n,deltaY:r}){let i=t===`x`?s(n,a.x):0,o=t===`y`?s(r,a.y):0;e.element.setAttribute(`data-swiping`,`true`),e.element.style.setProperty(`--toast-swipe-amount-x`,`${i}px`),e.element.style.setProperty(`--toast-swipe-amount-y`,`${o}px`)},release(e,{axis:t,deltaX:n,deltaY:r}){let o=t===`x`?n:r;if(o*a[t]<40){V(e);return}let s=t===`x`?e.element.offsetWidth:e.element.offsetHeight,c=a[t]*Math.max(Math.abs(o),s+16);e.element.setAttribute(`data-swiping`,`false`),e.element.setAttribute(`data-swipe-out`,`true`),e.element.style.setProperty(`--toast-swipe-end-x`,`${t===`x`?c:0}px`),e.element.style.setProperty(`--toast-swipe-end-y`,`${t===`y`?c:0}px`),i(e)},reset:V})}const H=new Set([`hover`,`focus`]);function _e({hasExitingEntries:e,setExpanded:t,setPaused:n}){let r=new Set,i=!1,a=!1,o=()=>[...r].some(e=>H.has(e)),s=()=>{i&&(o()||!e())&&(i=!1),t(o()||i);let s=r.size>0;s!==a&&(a=s,n(a))};return{get expanded(){return o()||i},get paused(){return a},set(t,n){n?r.add(t):r.delete(t)&&H.has(t)&&!o()&&e()&&(i=!0),s()},sync:s}}const U=`@data-slot/toast`;function W(n,r={}){let u=g(n,U,`[@data-slot/toast] createToast() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(u)return u;let f=d(n,`toast-viewport`);if(!f)throw Error(`Toast requires a toast-viewport slot`);let p=n.ownerDocument??document,m=d(n,`toast-template`),y=ne(r.limit??l(n,`limit`)),b=re(r.duration??l(n,`duration`),5e3),x=r.position??c(n,`position`,v)??`bottom-right`,C=r.pauseOnHover??s(n,`pauseOnHover`)??!0,w=r.pauseOnFocus??s(n,`pauseOnFocus`)??!0,T=r.portal??s(n,`portal`)??!1,E=r.onShow,ee=r.onDismiss,te=r.onAction,D=x.startsWith(`top`)?1:-1;n.setAttribute(`data-position`,x),f.setAttribute(`data-position`,x),f.hasAttribute(`role`)||f.setAttribute(`role`,`region`),f.hasAttribute(`aria-label`)||f.setAttribute(`aria-label`,`Notifications`);let O=t({content:f,root:n,enabled:T});T&&O.mount();let A=new Map,j=i(),M=[],N=0,P=null,F=()=>[...A.values()].filter(e=>e.active),I=e=>A.get(e.id)===e,pe=()=>[...A.values()].some(e=>e.exiting),L=he({viewport:f,limit:y,stackDirection:D,getItems:()=>F().reverse().map(e=>e.element),onLayout:()=>R.set(`focus`,w&&B())}),R=_e({hasExitingEntries:pe,setExpanded:e=>L.setExpanded(e),setPaused:e=>{for(let t of F())t.setPaused(e)}});p.visibilityState===`hidden`&&R.set(`document`,!0);let z=e=>{if(!(e instanceof Node)||!f.contains(e))return!1;if(!(e instanceof Element))return!0;let t=e.closest(`[data-slot="toast-item"]`);return!t||!f.contains(t)?!0:t.getAttribute(`data-visible`)===`true`&&t.getAttribute(`data-state`)===`open`},B=()=>z(p.activeElement),V=()=>{for(let e of F().reverse()){if(e.element.getAttribute(`data-visible`)!==`true`)continue;let t=me(e.element)[0];if(t)return o(t),!0}return!1},H=()=>P&&P.isConnected?(o(P),!0):!1,W=e=>{let t=p.activeElement;!(t instanceof HTMLElement)||!f.contains(t)||e&&!e.contains(t)||V()||H()||t.blur()},G=e=>{a(n,`toast:change`,{id:e,action:`dismiss`}),ee?.(e)},K=e=>{I(e)&&(Q.cancel(e),L.unobserve(e.element),e.destroy(),A.delete(e.id),L.update())},q=(e,t=!0)=>{!I(e)||!e.active||(Q.cancel(e),e.dismiss(),L.update(),t&&W(e.element),G(e.id))},ve=e=>{let t=A.get(e);t&&q(t)},J=()=>{let e=``;do N+=1,e=`toast-${N}`;while(A.has(e));return e},Y=(e,t)=>{let r=S(e)?e:J(),i=ae(ie(r,b),t),o=A.get(r),s=o?.active;o&&K(o),s&&G(r);let c=fe(i,{viewport:f,template:m,expanded:R.expanded,paused:R.paused,onTimeout:q,onExitComplete:K});if(j.isDestroyed)return c.destroy(),c;let l=A.get(r);return l&&K(l),A.set(r,c),c.mount(),L.observe(c.element),L.update(),I(c)&&c.active&&(a(n,`toast:change`,{id:r,action:`show`}),E?.(r)),c},ye=e=>j.isDestroyed?e.id??J():Y(e.id,e).id,X=(e,t)=>{!I(e)||!e.active||(e.update(t),e.toast.dismissible||Q.cancel(e),L.update())},be=(e,t)=>{let n=A.get(e);n&&X(n,t)},xe=(e,t)=>{let n=k(le(t.loading),{title:`Loading...`,type:`loading`,duration:0,description:t.description}),r=j.isDestroyed?void 0:Y(void 0,n),i=r?.id??J(),a=Promise.resolve().then(()=>typeof e==`function`?e():e).then(e=>{let n=k(ue(t.success,e),{title:`Success`,type:`success`,duration:b,description:t.description});return r&&X(r,n),e}).catch(e=>{let n=k(ue(t.error,e),{title:de(e),type:`error`,duration:b,description:t.description});throw r&&X(r,n),e});return a.catch(()=>{}),{id:i,unwrap:()=>a}},Se=()=>{let e=p.activeElement instanceof HTMLElement&&f.contains(p.activeElement);for(let e of F())q(e,!1);e&&W()},Ce=()=>{let e=!1;return{get defaultPrevented(){return e},preventDefault:()=>{e=!0}}},we=e=>{let t=A.get(e);if(!t||t.exiting)return;let r=t.toast.action?.value,i=Ce();t.toast.action?.onClick?.(i),a(n,`toast:action`,{id:e,value:r}),te?.(e,r),i.defaultPrevented||q(t)},Z=e=>{if(!(e instanceof Element))return;let t=e.closest(`[data-slot="toast-item"]`);if(!t||!f.contains(t))return;let n=A.get(t.getAttribute(`data-id`)??``);return n?.element===t?n:void 0},Q=ge({viewport:f,position:x,getEntry:Z,dismiss:e=>q(e)});M.push(h(f,`click`,e=>{let t=e.target;if(!t)return;let n=t.closest(`[data-slot="toast-close"]`);if(n){let e=Z(n);e&&q(e);return}let r=t.closest(`[data-slot="toast-action"]`);if(r){let e=Z(r);e&&we(e.id)}}));let Te=e=>z(e)||B();M.push(h(f,`pointerenter`,()=>{C&&R.set(`hover`,!0)}),h(f,`pointerleave`,()=>{C&&R.set(`hover`,!1)}),h(f,`focusin`,e=>{if(!w)return;let{relatedTarget:t,target:n}=e;t instanceof HTMLElement&&!f.contains(t)&&(P=t),R.set(`focus`,Te(n))}),h(f,`focusout`,e=>{w&&R.set(`focus`,Te(e.relatedTarget))}));let Ee=p.defaultView??window;M.push(h(Ee,`blur`,()=>R.set(`window`,!0)),h(Ee,`focus`,()=>R.set(`window`,!1)),h(p,`visibilitychange`,()=>R.set(`document`,p.visibilityState===`hidden`))),M.push(h(n,`toast:show`,e=>{let t=oe(e.detail);!t||j.isDestroyed||Y(t.id,t.patch)}),h(n,`toast:update`,e=>{let t=se(e.detail);t&&be(t.id,t.patch)}),h(n,`toast:dismiss`,e=>{let t=ce(e.detail);t&&ve(t)}),h(n,`toast:clear`,()=>{Se()})),j.onDestroy(()=>{M.forEach(e=>e()),Q.destroy(),L.destroy();for(let e of A.values())e.destroy();A.clear(),P=null,O.cleanup(),e(n,U,$)}),L.update();let $={show:ye,update:be,promise:xe,dismiss:ve,dismissAll:Se,get count(){return F().length},destroy:()=>{j.destroy()}};return _(n,U,$),$}function G(e=document){let t=[];for(let n of f(e,`toast`))p(n,U)||t.push(W(n));return t}export{G as create,W as createToast}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-toggle-group.js b/liquid/vendor/data-slot-toggle-group.js new file mode 100644 index 000000000..36bc56620 --- /dev/null +++ b/liquid/vendor/data-slot-toggle-group.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,ensureId as n,getDataBool as r,getDataEnum as i,getDataString as a,getParts as o,getRoots as s,hasRootBinding as c,on as l,onRoot as u,reuseRootBinding as d,setAria as f,setRootBinding as p}from"@data-slot/core";const m=[`horizontal`,`vertical`],h=`@data-slot/toggle-group`;function g(s,c={}){let g=d(s,h,`[@data-slot/toggle-group] createToggleGroup() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(g)return g;let _=o(s,`toggle-group-item`);if(_.length===0)throw Error(`ToggleGroup requires at least one toggle-group-item`);let v=c.multiple??r(s,`multiple`)??!1,y=c.orientation??i(s,`orientation`,m)??`horizontal`,b=c.loop??r(s,`loop`)??!0,x=c.disabled??r(s,`disabled`)??!1,S=c.onValueChange,C=(()=>{if(c.defaultValue!==void 0)return Array.isArray(c.defaultValue)?c.defaultValue:c.defaultValue.split(/\s+/).filter(Boolean);let e=a(s,`defaultValue`);return e?e.split(/\s+/).filter(Boolean):[]})(),w=[],T=new Map,E=new Map,D=[];for(let e of _){let t=(e.dataset.value||``).trim();if(!t){D.push(e);continue}let n={el:e,value:t,disabled:e.hasAttribute(`disabled`)||e.hasAttribute(`data-disabled`)||e.getAttribute(`aria-disabled`)===`true`};w.push(n),T.set(t,n),E.set(e,n)}for(let e of D)e.tabIndex=-1,e.setAttribute(`aria-disabled`,`true`);if(w.length===0)throw Error(`ToggleGroup requires at least one toggle-group-item with a data-value attribute`);let O=new Set;for(let e of C)if(T.has(e)&&(O.add(e),!v))break;let k=[],A=()=>s.hasAttribute(`disabled`)||s.hasAttribute(`data-disabled`)||s.getAttribute(`aria-disabled`)===`true`,j=e=>e.el.hasAttribute(`disabled`)||e.el.hasAttribute(`data-disabled`)||e.el.getAttribute(`aria-disabled`)===`true`,M=()=>w.filter(e=>!j(e));s.setAttribute(`role`,`group`),x&&s.setAttribute(`aria-disabled`,`true`),y===`vertical`&&f(s,`orientation`,`vertical`),v&&(s.dataset.multiple=``);for(let e of w){let{el:t,disabled:r}=e;n(t,`toggle-group-item`),t.tabIndex=-1,t.tagName===`BUTTON`&&!t.hasAttribute(`type`)&&(t.type=`button`),r&&(t.setAttribute(`aria-disabled`,`true`),t.tagName===`BUTTON`&&(t.disabled=!0))}let N=(e,n=!1)=>{let r=!1;for(let t of w){let i=e.has(t.value);!n&&i===O.has(t.value)||(r=!0,f(t.el,`pressed`,i),t.el.dataset.state=i?`on`:`off`)}O=e,I();let i=[...O];s.setAttribute(`data-value`,i.join(` `)),r&&!n&&(t(s,`toggle-group:change`,{value:i}),S?.(i))},P,F=e=>{e!==P&&(P&&(P.el.tabIndex=-1),e&&(e.el.tabIndex=0),P=e)},I=()=>{let e=M();F(e.find(e=>O.has(e.value))??e[0])};N(O,!0);let L=e=>{let t=new Set(O);v?t.has(e)?t.delete(e):t.add(e):t.has(e)?t.clear():(t.clear(),t.add(e)),N(t)},R=e=>{let t=Array.isArray(e)?e:e.split(/\s+/).filter(Boolean),n=new Set;for(let e of t)if(T.has(e)&&(n.add(e),!v))break;N(n)};k.push(l(s,`click`,e=>{if(A())return;let t=e.target.closest?.(`[data-slot="toggle-group-item"]`);if(!t)return;let n=E.get(t);!n||j(n)||L(n.value)}));let z=y===`horizontal`,B=z?`ArrowLeft`:`ArrowUp`,V=z?`ArrowRight`:`ArrowDown`;k.push(l(s,`keydown`,e=>{if(A())return;let t=e.target.closest?.(`[data-slot="toggle-group-item"]`);if(!t)return;let n=E.get(t);if(!n)return;if(e.key===`Enter`||e.key===` `){j(n)&&e.preventDefault();return}let r=M();if(r.length===0)return;let i=r.findIndex(e=>e.el===t);i===-1&&(i=0);let a=i;switch(e.key){case B:a=i-1,a<0&&(a=b?r.length-1:0);break;case V:a=i+1,a>=r.length&&(a=b?0:r.length-1);break;case`Home`:a=0;break;case`End`:a=r.length-1;break;default:return}e.preventDefault();let o=r[a];o&&(F(o),o.el.focus())})),k.push(u(s,`toggle-group:set`,e=>{if(A())return;let t=e.detail,n;typeof t==`string`||Array.isArray(t)?n=t:t&&typeof t==`object`&&`value`in t&&(n=t.value),n!==void 0&&R(n)}));let H={setValue:e=>R(e),toggle:e=>L(e),get value(){return[...O]},destroy:()=>{k.forEach(e=>e()),k.length=0,e(s,h,H)}};return p(s,h,H),H}function _(e=document){let t=[];for(let n of s(e,`toggle-group`))c(n,h)||t.push(g(n));return t}export{_ as create,g as createToggleGroup}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-toggle.js b/liquid/vendor/data-slot-toggle.js new file mode 100644 index 000000000..3195e7a4d --- /dev/null +++ b/liquid/vendor/data-slot-toggle.js @@ -0,0 +1 @@ +import{clearRootBinding as e,emit as t,getDataBool as n,getRoots as r,hasRootBinding as i,on as a,onRoot as o,reuseRootBinding as s,setAria as c,setRootBinding as l}from"@data-slot/core";const u=`@data-slot/toggle`;function d(r,i={}){let d=s(r,u,`[@data-slot/toggle] createToggle() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(d)return d;let f=i.defaultPressed??n(r,`defaultPressed`)??!1,p=i.disabled??n(r,`disabled`)??!1,m=i.onPressedChange,h=f,g=[],_=()=>r.hasAttribute(`disabled`)||r.getAttribute(`aria-disabled`)===`true`,v=(e,n=!1)=>{h===e&&!n||(h=e,c(r,`pressed`,h),r.dataset.state=h?`on`:`off`,n||(t(r,`toggle:change`,{pressed:h}),m?.(h)))};p&&(r.tagName===`BUTTON`&&r.setAttribute(`disabled`,``),r.setAttribute(`aria-disabled`,`true`)),r.tagName===`BUTTON`&&!r.hasAttribute(`type`)&&r.setAttribute(`type`,`button`),v(h,!0),g.push(a(r,`click`,()=>{_()||v(!h)})),g.push(o(r,`toggle:set`,e=>{if(_())return;let t=e.detail,n;typeof t==`boolean`?n=t:t?.value===void 0?t?.pressed!==void 0&&(n=t.pressed):n=t.value,typeof n==`boolean`&&v(n)}));let y={toggle:()=>v(!h),press:()=>v(!0),release:()=>v(!1),get pressed(){return h},destroy:()=>{g.forEach(e=>e()),g.length=0,e(r,u,y)}};return l(r,u,y),y}function f(e=document){let t=[];for(let n of r(e,`toggle`))i(n,u)||t.push(d(n));return t}export{f as create,d as createToggle}; \ No newline at end of file diff --git a/liquid/vendor/data-slot-tooltip.js b/liquid/vendor/data-slot-tooltip.js new file mode 100644 index 000000000..9dacaeb6b --- /dev/null +++ b/liquid/vendor/data-slot-tooltip.js @@ -0,0 +1 @@ +import{clearRootBinding as e,computeFloatingPosition as t,computeFloatingTransformOrigin as n,createContentMount as r,createDismissLayer as i,createPortalLifecycle as a,createPositionSync as o,createPresenceLifecycle as s,createTerminalLifecycle as c,emit as l,ensureId as u,getDataBool as d,getDataEnum as f,getDataNumber as p,getOwnedElements as ee,getPart as m,getRoots as h,hasRootBinding as g,measurePopupContentRect as te,on as _,onRoot as ne,registerFloatingTerminalResources as re,reuseRootBinding as ie,setRootBinding as ae}from"@data-slot/core";const v=`@data-slot/tooltip`;let y=0;const b=new Set,x=new Set,S=(e,t)=>{if(!e)return!1;for(let n of b)if(n!==t&&n.contains(e))return!0;return!1},oe=(e,t)=>{for(let n of x)n(e,t)},se=[`top`,`right`,`bottom`,`left`,`inline-start`,`inline-end`],ce=[`start`,`center`,`end`],le=(e,t)=>e===`inline-start`?t===`rtl`?`right`:`left`:e===`inline-end`?t===`rtl`?`left`:`right`:e,C=(...e)=>[...new Set(e.filter(e=>e!=null))];function w(h,g={}){let w=ie(h,v,`[@data-slot/tooltip] createTooltip() called more than once for the same root. Returning the existing controller. Destroy it before rebinding with new options.`);if(w)return w;let T=m(h,`tooltip-trigger`),E=m(h,`tooltip-content`),D=E?ee(h,E,`[data-slot="tooltip-arrow"]`)[0]??null:null,O=m(h,`tooltip-positioner`),k=O&&E&&O.contains(E)?O:null,A=m(h,`tooltip-portal`),j=A&&k&&A.contains(k)?A:null;if(!T||!E)throw Error(`Tooltip requires trigger and content slots`);D&&(D.setAttribute(`aria-hidden`,`true`),D.style.position=`absolute`);let ue=g.delay??p(h,`delay`)??300,de=g.skipDelayDuration??p(h,`skipDelayDuration`)??300,fe=g.onOpenChange,pe=g.portal??d(E,`portal`)??d(h,`portal`)??!0,me=(e,t)=>f(E,e,t)??(k?f(k,e,t):void 0)??f(h,e,t),M=e=>p(E,e)??(k?p(k,e):void 0)??p(h,e),he=e=>d(E,e)??(k?d(k,e):void 0)??d(h,e),N=g.side??me(`side`,se)??`top`,P=g.align??me(`align`,ce)??`center`,ge=g.sideOffset??M(`sideOffset`)??4,_e=g.alignOffset??M(`alignOffset`)??0,ve=g.avoidCollisions??he(`avoidCollisions`)??!0,ye=g.collisionPadding??M(`collisionPadding`)??8,F=!1,I=null,L=!1,R=c(),z=null,B=[],V=a({content:E,root:h,enabled:pe,wrapperSlot:k?void 0:`tooltip-positioner`,container:k??void 0,mountTarget:k?j??k:void 0}),be=g.mountStrategy??f(h,`mountStrategy`,[`lazy`,`eager`])??`lazy`,H=r({root:h,target:j??k??E,strategy:be});B.push(()=>H.cleanup());let U=u(E,`tooltip-content`),W=!1,xe=!1,G=()=>T.getAttribute(`aria-describedby`)?.split(/\s+/).filter(Boolean)??[],Se=()=>{let e=G();e.includes(U)||(T.setAttribute(`aria-describedby`,[...e,U].join(` `)),W=!0)},K=()=>{if(!W)return;let e=G().filter(e=>e!==U);e.length?T.setAttribute(`aria-describedby`,e.join(` `)):T.removeAttribute(`aria-describedby`),W=!1},Ce=()=>{be===`lazy`&&G().includes(U)&&(xe||=!W,W=!0,K()),H.unmount()};E.setAttribute(`role`,`tooltip`);let we=()=>{let e=h instanceof HTMLElement?h:null;return(e?.getAttribute(`dir`)??T.getAttribute(`dir`))===`rtl`||(getComputedStyle(T).direction||(e?getComputedStyle(e).direction:``)||h.ownerDocument.documentElement.getAttribute(`dir`)||``)===`rtl`?`rtl`:`ltr`},Te=(e,t)=>{let n=V.container;for(let r of C(E,n,D))r.setAttribute(`data-side`,e),r.setAttribute(`data-align`,t)},Ee=e=>{let t=V.container;for(let n of C(h,E,t,D))e?n.setAttribute(`data-instant`,e):n.removeAttribute(`data-instant`)},De=(e,t,n,r)=>{if(!D)return;D.style.position=`absolute`;let i=D.getBoundingClientRect(),a=D.offsetWidth>0?D.offsetWidth:i.width,o=D.offsetHeight>0?D.offsetHeight:i.height;if(a<=0||o<=0){D.style.removeProperty(`left`),D.style.removeProperty(`top`),D.removeAttribute(`data-uncentered`);return}let s=le(e,t);if(s===`top`||s===`bottom`){let e=n.left+n.width/2-r.left-a/2,t=Math.max(5,r.width-a-5),i=Math.min(Math.max(e,5),t);D.style.left=`${i}px`,D.style.removeProperty(`top`),Math.abs(i-e)>.5?D.setAttribute(`data-uncentered`,``):D.removeAttribute(`data-uncentered`);return}let c=n.top+n.height/2-r.top-o/2,l=Math.max(5,r.height-o-5),u=Math.min(Math.max(c,5),l);D.style.top=`${u}px`,D.style.removeProperty(`left`),Math.abs(u-c)>.5?D.setAttribute(`data-uncentered`,``):D.removeAttribute(`data-uncentered`)},q=e=>{let t=V.container;if(h.setAttribute(`data-state`,e),E.setAttribute(`data-state`,e),Ee(I),e===`open`){for(let e of C(h,E,t,D))e.setAttribute(`data-open`,``),e.removeAttribute(`data-closed`);return}for(let e of C(h,E,t,D))e.setAttribute(`data-closed`,``),e.removeAttribute(`data-open`)},Oe=()=>{let e=V.container,r=h.ownerDocument.defaultView??window,i=we(),a=T.getBoundingClientRect(),o=te(E),s=t({anchorRect:a,contentRect:o,side:N,align:P,sideOffset:ge,alignOffset:_e,avoidCollisions:ve,collisionPadding:ye,direction:i}),c=n({side:s.side,align:s.align,anchorRect:a,popupX:s.x,popupY:s.y,direction:i});e.style.position=`absolute`,e.style.top=`0px`,e.style.left=`0px`,e.style.transform=`translate3d(${s.x+r.scrollX}px, ${s.y+r.scrollY}px, 0)`,e.style.setProperty(`--transform-origin`,c),e.style.willChange=`transform`,e.style.margin=`0`,Te(s.side,s.align),De(s.side,i,a,{top:s.y,left:s.x,right:s.x+o.width,bottom:s.y+o.height,width:o.width,height:o.height})},J=s({element:E,onExitComplete:()=>{R.isDestroyed||(V.restore(),E.hidden=!0,Ce())}}),Y=o({observedElements:[T,E],isActive:()=>F,ancestorScroll:!1,onUpdate:Oe}),X=()=>T.hasAttribute(`disabled`)||T.getAttribute(`aria-disabled`)===`true`,Z=(e,t,n=null)=>{R.isDestroyed||F!==e&&(!e&&F&&de>0&&(y=Date.now()+de),I=n,F=e,F?(H.mount(),V.mount(),Se(),E.setAttribute(`aria-hidden`,`false`),E.hidden=!1,q(`open`),J.enter(),Oe(),Y.start()):(q(`closed`),K(),E.setAttribute(`aria-hidden`,`true`),J.exit(),Y.stop()),l(h,`tooltip:change`,{open:F,trigger:T,content:E,reason:t}),fe?.(F))},ke=e=>{if(z&&=(R.cancelTimeout(z),null),Date.now(){R.isDestroyed||(Z(!0,e,e===`focus`?`focus`:null),z=null)},ue)},Q=(e,t=null)=>{z&&=(R.cancelTimeout(z),null),Z(!1,e,t)},Ae=(e,t)=>{e===T||!F||(L=!1,Q(t,`delay`))};b.add(T),x.add(Ae),B.push(()=>{x.delete(Ae),b.delete(T)}),E.hidden=!0,E.setAttribute(`aria-hidden`,`true`),Te(N,P),q(`closed`),B.push(_(T,`pointerenter`,e=>{e.pointerType!==`touch`&&(X()||(oe(T,`pointer`),ke(`pointer`)))}),_(T,`pointerleave`,e=>{if(e.pointerType===`touch`||L)return;let t=e.relatedTarget;if(!(t&&E.contains(t))){if(S(t,T)){Q(`pointer`,`delay`);return}Q(`pointer`)}}),_(T,`click`,()=>{if(!X()){if(z){R.cancelTimeout(z),z=null;return}F&&Q(`pointer`,`dismiss`)}}),_(T,`focus`,()=>{L=!0,!X()&&(oe(T,`focus`),ke(`focus`))}),_(T,`blur`,e=>{L=!1;let t=e.relatedTarget;if(S(t,T)){Q(`blur`,`delay`);return}Q(`blur`)})),B.push(_(E,`pointerleave`,e=>{if(e.pointerType===`touch`||L)return;let t=e.relatedTarget;if(!(t&&T.contains(t))){if(S(t,T)){Q(`pointer`,`delay`);return}Q(`pointer`)}})),B.push(ne(h,`tooltip:set`,e=>{let t=e.detail,n;if(t?.open===void 0?t?.value!==void 0&&(n=t.value):n=t.open,typeof n==`boolean`)if(n){if(X())return;z&&=(R.cancelTimeout(z),null),Z(!0,`api`)}else Q(`api`)})),B.push(i({root:h,isOpen:()=>F,onDismiss:()=>Q(`escape`,`dismiss`),closeOnClickOutside:!1,closeOnEscape:!0,preventEscapeDefault:!1}));let $={show:()=>{R.isDestroyed||X()||(z&&=(R.cancelTimeout(z),null),Z(!0,`api`))},hide:()=>{R.isDestroyed||Q(`api`)},get isOpen(){return F},destroy:()=>{R.destroy()&&(z&&R.cancelTimeout(z),z=null,F=!1,q(`closed`),K(),xe&&Se(),E.setAttribute(`aria-hidden`,`true`),E.hidden=!0)}};return re(R,{cleanups:B,positionSync:Y,presence:J,portal:V,unbind:()=>e(h,v,$)}),Ce(),ae(h,v,$),$}function T(e=document){let t=[];for(let n of h(e,`tooltip`))g(n,v)||t.push(w(n));return t}export{T as create,w as createTooltip}; \ No newline at end of file diff --git a/package.json b/package.json index 11a3d6a4c..6af14988c 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "@changesets/cli": "^3.0.3", "@eslint/js": "^10.0.1", "@ianvs/prettier-plugin-sort-imports": "^4.7.1", + "@shopify/prettier-plugin-liquid": "1.11.2", "@tailwindcss/vite": "^4.3.3", "@types/node": "^24.19.0", "astro": "^7.3.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6da92c70..159d520cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,6 +295,9 @@ importers: '@ianvs/prettier-plugin-sort-imports': specifier: ^4.7.1 version: 4.7.1(prettier@3.9.9) + '@shopify/prettier-plugin-liquid': + specifier: 1.11.2 + version: 1.11.2(prettier@3.9.9) '@tailwindcss/vite': specifier: ^4.3.3 version: 4.3.3(vite@8.3.2(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.1)) @@ -351,7 +354,7 @@ importers: version: 1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9) prettier-plugin-tailwindcss: specifier: ^0.8.1 - version: 0.8.1(@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9))(prettier-plugin-astro@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9))(prettier@3.9.9) + version: 0.8.1(@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9))(@shopify/prettier-plugin-liquid@1.11.2(prettier@3.9.9))(prettier-plugin-astro@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9))(prettier@3.9.9) sharp: specifier: ^0.35.5 version: 0.35.5(@types/node@24.19.0) @@ -2106,6 +2109,14 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@shopify/liquid-html-parser@2.10.2': + resolution: {integrity: sha512-/qBtTT3tbzSFgFVMNTPhX2pPN14ShMSYTu9nzkCB1vorjNvm1G9mvgYW9jKod0mrki+TUVfMYi5d1H+QKrLnCA==} + + '@shopify/prettier-plugin-liquid@1.11.2': + resolution: {integrity: sha512-KTWczMeDBHSOl7eJY1pCrCJ7x5xj4o/F7212J2mOrzB6BmbjtvroARVhPgBgQ7lWF+GqSxva+uLOwR4kd4UFXg==} + peerDependencies: + prettier: ^2.0.0 || ^3.0.0 + '@sidvind/better-ajv-errors@7.0.0': resolution: {integrity: sha512-imVsp5D3KxJ8+uUmu2dsLKnFg3qdX952v/L1gZoCa0NO2ivhQWHMpYM2KmpFrRXHWFjlqkNZ/935nxiTqXEi1A==} engines: {node: ^22.12 || >= 24.0} @@ -2698,6 +2709,10 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -3516,6 +3531,9 @@ packages: html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + html-styles@1.0.0: + resolution: {integrity: sha512-cDl5dcj73oI4Hy0DSUNh54CAwslNLJRCCoO+RNkVo+sBrjA/0+7E/xzvj3zH/GxbbBLGJhE0hBe1eg+0FINC6w==} + html-validate@11.16.1: resolution: {integrity: sha512-PklC0d9tLeXpvC6INePZcTBlXplZOmBkR0IQTYoJ9TSrtqxBKR74lMdKPXcnOImD4mAdp/L5/QJR/x6WrvDYvA==} engines: {node: ^22.22.0 || >= 24.8.0} @@ -3794,6 +3812,9 @@ packages: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3804,6 +3825,10 @@ packages: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -4040,6 +4065,9 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + line-column@1.0.2: + resolution: {integrity: sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4048,6 +4076,11 @@ packages: engines: {node: '>=22'} hasBin: true + liquidjs@10.25.1: + resolution: {integrity: sha512-D+jsJvkGigFn8qNUgh8U6XNHhGFBp+p8Dk26ea/Hl+XrjFVSg9OXlN31hGAfS3MYQ3Kr8Xi9sEVBVQa/VTVSmg==} + engines: {node: '>=16'} + hasBin: true + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -7371,6 +7404,16 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@shopify/liquid-html-parser@2.10.2': + dependencies: + line-column: 1.0.2 + + '@shopify/prettier-plugin-liquid@1.11.2(prettier@3.9.9)': + dependencies: + '@shopify/liquid-html-parser': 2.10.2 + html-styles: 1.0.0 + prettier: 3.9.9 + '@sidvind/better-ajv-errors@7.0.0(ajv@8.20.0)': dependencies: ajv: 8.20.0 @@ -8063,6 +8106,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} + commander@11.1.0: {} commander@14.0.3: {} @@ -9122,6 +9167,8 @@ snapshots: html-escaper@3.0.3: {} + html-styles@1.0.0: {} + html-validate@11.16.1: dependencies: '@html-validate/stylish': 6.0.0 @@ -9350,12 +9397,18 @@ snapshots: dependencies: is-inside-container: 1.0.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} isexe@3.1.5: {} + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + jiti@2.7.0: {} jju@1.4.0: {} @@ -9539,6 +9592,11 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + line-column@1.0.2: + dependencies: + isarray: 1.0.0 + isobject: 2.1.0 + lines-and-columns@1.2.4: {} linkinator@8.1.0: @@ -9555,6 +9613,10 @@ snapshots: srcset: 5.0.3 undici: 8.11.2 + liquidjs@10.25.1: + dependencies: + commander: 10.0.1 + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -10452,11 +10514,12 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - prettier-plugin-tailwindcss@0.8.1(@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9))(prettier-plugin-astro@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9))(prettier@3.9.9): + prettier-plugin-tailwindcss@0.8.1(@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9))(@shopify/prettier-plugin-liquid@1.11.2(prettier@3.9.9))(prettier-plugin-astro@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9))(prettier@3.9.9): dependencies: prettier: 3.9.9 optionalDependencies: '@ianvs/prettier-plugin-sort-imports': 4.7.1(prettier@3.9.9) + '@shopify/prettier-plugin-liquid': 1.11.2(prettier@3.9.9) prettier-plugin-astro: 1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(prettier@3.9.9) prettier@3.9.9: {} diff --git a/prettier.config.cjs b/prettier.config.cjs index d3bc79ad6..b6047f486 100644 --- a/prettier.config.cjs +++ b/prettier.config.cjs @@ -36,11 +36,25 @@ module.exports = { ], importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], plugins: [ + "@shopify/prettier-plugin-liquid", "prettier-plugin-astro", "@ianvs/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss", ], tailwindStylesheet: "src/styles/global.css", tailwindFunctions: ["cn", "cva"], - overrides: [{ files: "*.astro", options: { parser: "astro" } }], + overrides: [ + { files: "*.astro", options: { parser: "astro" } }, + { + files: "liquid/**/*.{liquid,css,js}", + options: { + semi: true, + singleQuote: true, + liquidSingleQuote: true, + embeddedSingleQuote: true, + printWidth: 120, + trailingComma: "all", + }, + }, + ], } diff --git a/tsconfig.json b/tsconfig.json index 045708930..71849baae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,11 @@ { "extends": "astro/tsconfigs/strict", "include": [".astro/types.d.ts", "**/*"], - "exclude": ["dist", "react"], + "exclude": ["dist", "react", "liquid/vendor"], "compilerOptions": { "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@liquid/*": ["./liquid/*"] } } } From a7009f49818fa921666a830a4ee4fe42f2f54dab Mon Sep 17 00:00:00 2001 From: Sil Date: Sat, 17 Oct 2026 12:19:23 +0200 Subject: [PATCH 2/2] fix(liquid): preserve responsive styles and translated slide updates --- liquid/README.md | 4 ++-- liquid/components/base.liquid | 10 ++++++++ liquid/components/empty.liquid | 5 ++++ liquid/components/input.liquid | 10 ++++++++ liquid/manifest.json | 12 +++++----- liquid/runtime/ui-carousel.js | 44 +++++++++++++++++++++++++--------- liquid/styles/field.css | 6 ++++- liquid/styles/section.css | 15 ++++++++++++ 8 files changed, 86 insertions(+), 20 deletions(-) diff --git a/liquid/README.md b/liquid/README.md index 71e37c5a6..fa5b58990 100644 --- a/liquid/README.md +++ b/liquid/README.md @@ -8,9 +8,9 @@ Native Shopify Liquid, readable CSS and ES modules. The consumer needs no Tailwi `registry.json` is the install contract. Every item is universal, every file has an explicit `~/assets/` or `~/snippets/` target, and dependencies use full Liquid registry URLs. Individual items install their prerequisites, including shared popup styles and runtime where needed. `components` is the complete set. `init` is tokens and base styles only. -Only shared neutral palette tokens, the part inventory, licenses and file hashes are generated by `scripts/registry-liquid.mjs`. CSS and Liquid are maintained by hand. The palette comes from `registry/styles/shadcn/themes.json`; `styles/primitives.css` holds native sizes, radii, shadows and media roles. Theme colors, settings, content, Shopify forms and layouts remain theme-owned. A consumer loads its scheme overrides after `ui`, and `custom.css` after `content_for_header`. +CSS and Liquid are maintained by hand. The shared neutral palette comes from `registry/styles/shadcn/themes.json`; `styles/primitives.css` holds native sizes, radii, shadows and media roles. Theme colors, settings, content, Shopify forms and layouts remain theme-owned. A consumer loads its scheme overrides after `ui`, and `custom.css` after `content_for_header`. -Run `vp run registry:liquid` after a source change. Commit `public/r/liquid`, `contracts.json`, `styles/tokens.css`, `licenses.json` and `manifest.json`. `vp run test:liquid` verifies the real CLI, the dependency closures, token parity and repeat installs. `vp env exec -- node --test tests/browser/liquid.test.mjs` verifies a CLI-installed copy in Chromium, Firefox and WebKit, including native forms and editor lifecycle. The local renderer is LiquidJS with explicit Shopify filter substitutes. It does not verify a store's commerce objects or Shopify's stylesheet subsetting. +Registry generation and installation tests are added separately from this source layer. Local LiquidJS fixtures do not verify a store's commerce objects or Shopify's stylesheet subsetting. This first port follows Vega and the complete reusable storefront layer from `fulldotdev/liquid-template` at `e13fc25`. Sidebar, Resizable, Menubar, Scroll Area and React-only app components are outside this native storefront set. Shopify blocks, commerce adapters, translation keys and settings are not installed by the UI registry. The existing storefront media and card variants are additive to the shared component contract. Other Tailwind presets are not translated automatically. diff --git a/liquid/components/base.liquid b/liquid/components/base.liquid index acd1d0eba..69f827c87 100644 --- a/liquid/components/base.liquid +++ b/liquid/components/base.liquid @@ -99,4 +99,14 @@ li { overflow-wrap: anywhere; } + @media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + } {% endstylesheet %} diff --git a/liquid/components/empty.liquid b/liquid/components/empty.liquid index b975b09ad..a15553e02 100644 --- a/liquid/components/empty.liquid +++ b/liquid/components/empty.liquid @@ -69,4 +69,9 @@ max-width: 24rem; font-size: 0.875rem; } + @media (max-width: 639px) { + [data-slot='empty'] { + padding: 32px 16px; + } + } {% endstylesheet %} diff --git a/liquid/components/input.liquid b/liquid/components/input.liquid index 46d539161..4c11100d5 100644 --- a/liquid/components/input.liquid +++ b/liquid/components/input.liquid @@ -71,4 +71,14 @@ margin: 0; accent-color: var(--primary); } + @media (min-width: 768px) { + input:where(:not([type='hidden'], [type='checkbox'], [type='radio'], [type='range'], [type='submit'])), + [data-slot='input'], + [data-slot='native-select'], + select, + [data-slot='textarea'], + textarea { + font-size: 0.875rem; + } + } {% endstylesheet %} diff --git a/liquid/manifest.json b/liquid/manifest.json index 7d609d4a9..6b92b31fe 100644 --- a/liquid/manifest.json +++ b/liquid/manifest.json @@ -2,10 +2,10 @@ "registry": "https://ui.full.dev/r/liquid/", "files": { "assets/ui-tokens.css": "55a8f2810ab4a1498ca3dd2f6e7dc89009c60e2402ddfcb95783b6eba996c374", - "snippets/ui-base.liquid": "1a39ed17214f86fa6fa844cb1d26e7e4a386c2c8a9d4492b2fd03efa940b9955", + "snippets/ui-base.liquid": "bc55195b5b6c82e63deeeed1255fd19b434c47866815f82b95b9202421256d34", "snippets/ui-init.liquid": "98f56067cf51e4cb2cd3e10ede7c00304c73ce57fe5b5a0270c0d8a9e448f7af", "assets/ui-licenses.json": "6eaca08e24468055e5471a9141a3453381b86ced4bdce85cb874f73b550baa09", - "assets/ui-carousel.js": "95655cf60a4c1cacdad87295acb2126032dc8d59ca117c6d738cf534be648e12", + "assets/ui-carousel.js": "3337447a9e0051cebbc1726bca7c7966de1035819fb8e0b676383b85f1917632", "assets/ui-controls.js": "cf51f6d8b23c93e30d3355e993ff21a1aee79f38facfa413bed64e9815df255d", "assets/ui-lifecycle.js": "78c975473620171c01b1289bf5cdce56a5626c847437fa5168fb91a4300260d6", "assets/ui-start.js": "738d7e171b5d7993bfd0a169e4382b2db08340f52a87059b94d27e1fe3606500", @@ -39,7 +39,7 @@ "assets/ui-form.css": "ab0dd70553818fe0799e393f6811e949884eca9f44d9ceff4c2b333c0bf724b4", "snippets/ui-select.liquid": "2d8c99759161a024285d1b54c0ca166dc7b13c97596497351253e945b5e52ee2", "snippets/ui-field.liquid": "cf73aaaf3e27bc9d121dd089317610fbd08b9550a1d5fb0738337c6817879309", - "assets/ui-field.css": "1436dff789c8811aae13025ca862ac1bfbf64c57e895c87474f45bfcd8b3f665", + "assets/ui-field.css": "96f846a52ab0b832a9eca70ef430e5fd14664a3367d04842de320a82e622722b", "snippets/ui-item.liquid": "fe03c1a3e6df09192c3ac7a08520b0fc1c629a597a029d193ff91655cb7ceac0", "snippets/ui-spinner.liquid": "84d48693f10cb8af4549cc0d71cd847a85da0e111f2ad64dae309c128c2a8c4a", "snippets/ui-video.liquid": "14acea7c523198269a9165270decac21a43a5d69bf12bd248a76aceb2e9b26ed", @@ -52,7 +52,7 @@ "assets/ui-rating.css": "e5efea2a9edaea78522f20ebd9e9d01abe8a2fcd310c193239adf80ec33b4975", "snippets/ui-tooltip.liquid": "d2863a0da3090c73dda10d8615b84fc50fab64373a466fbf4e4e743cfb083c96", "snippets/ui-avatar.liquid": "9bfb5d5f535857cc45f31afbc91c855e94972bab07ad3b4fe2dc0d241106385d", - "snippets/ui-empty.liquid": "1a6b8a006d67700225a9f646fdb74308904319cc1cd8ea83a637e2d9d5fbc3d9", + "snippets/ui-empty.liquid": "0e7fb5b68d50abcf7213304034762b4dd535c791dd6f4dc517100145e04b0cde", "snippets/ui-alert-dialog.liquid": "f739374035ffa0f3bca2c4b6d946710751c8268c8f1dde535251be9c2c7b0cd0", "snippets/ui-navigation-menu.liquid": "52aecbb36b0031810a85a99f5d4439e852dc1ba3a872b9525dd75c8df8b5a84e", "assets/ui-navigation-menu.css": "29a47ce0eb549e58cc8195dcb656bd2cef56360178c76acfc2d481a110463ca6", @@ -69,7 +69,7 @@ "snippets/ui-accordion.liquid": "4b58c83ae13e14e03a3129a2dccbd02eeafa90c1e48edbbf9af2dc412bf3aabf", "assets/ui-accordion.css": "430924440bd0f1d49ceb72baffc77404c9411c31c24b940d75861daba2649e83", "snippets/ui-slider.liquid": "f2de1fb9099de77bf291455447bc86b2578781e7cf0efdf12117fb4575e0b0e6", - "snippets/ui-input.liquid": "1f101e309dc15bae127766427ef619d190eb9e341fc34808b26f56ea354312e7", + "snippets/ui-input.liquid": "d29e280ce1d08c7450902e69a73a0b7aa125f1cb662c50dbfc80a4a4fef0a85e", "snippets/ui-input-group.liquid": "b862a0c63e23dc7ae4ffe3a8e057a87f38fa8e6c8842ddf3c86b826de0283d70", "snippets/ui-triggers.liquid": "ba9c7b802fb0a2aba0e9cc145a9dd44134964afb53e5363cf9e0ae138b906cc9", "snippets/ui-toggle-group.liquid": "2d61e0b9bc746e05834157f5a75d23123f02bc2df737412e4f6099844afc4f49", @@ -99,7 +99,7 @@ "assets/ui-marquee.css": "ef13b83f3dbcd6685fcee3dba14238386d0f34bc2266a5326c0b6f2d40a23fce", "snippets/ui-invalid-controls.liquid": "a169e8244cc86f5431f902f7a73b37188051ba8f2ef3e323830462dae5aed066", "snippets/ui-section.liquid": "da358cd87447457f3675b035b48556103d86cd11e0b2240ae2800e4c9ea54bdc", - "assets/ui-section.css": "0d5df020f70d38b3c049430204d469d681f56f6c2c4905b925ca0c81e61ff16f", + "assets/ui-section.css": "f4936a02aaf1927ccced898886857855e4b5e95fafe5c2a52748ecbbe87b6621", "snippets/ui-button-group.liquid": "a2790f8da0f8b37acc1ed558ba1548ea250a5b93e1b2dfbd11eb150f3f02783a", "snippets/ui-collapsible.liquid": "e6107a03d00b8cc75bb03206e64b9aa7f2989d982c0408b398b62067c6df450a", "snippets/ui-dropdown-menu.liquid": "25b31e09e3e7bfee9a397ca4de6e310754844c55e71639eadba917b0f4e6d2e5", diff --git a/liquid/runtime/ui-carousel.js b/liquid/runtime/ui-carousel.js index 6fe9c0a90..3c524eb92 100644 --- a/liquid/runtime/ui-carousel.js +++ b/liquid/runtime/ui-carousel.js @@ -2,21 +2,43 @@ import { createCarousel } from '@data-slot/carousel'; // @data-slot/carousel owns scrolling, snapping, dragging, the arrow keys and the previous and next // buttons, and keeps every visible card interactive. It writes English ARIA descriptions and slide -// labels, so the translated ones from Liquid are put back after it binds. +// labels, so the translated ones from Liquid are put back after binding and slide updates. export function initializeCarousel(root) { const content = root.querySelector('[data-slot="carousel-content"]'); - const items = [...content.children].filter((item) => item.matches('[data-slot="carousel-item"]')); - const descriptions = [root, ...items].map((element) => [element, element.getAttribute('aria-roledescription')]); - const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false }); - for (const [element, description] of descriptions) - if (description) element.setAttribute('aria-roledescription', description); - // Markup without a translated label template keeps the controller's labels. - const template = root.dataset.slideLabel; - if (template) - items.forEach((item, index) => { - item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', items.length)); + const items = () => [...content.children].filter((item) => item.matches('[data-slot="carousel-item"]')); + const rootDescription = root.getAttribute('aria-roledescription'); + const descriptions = new WeakMap(items().map((item) => [item, item.getAttribute('aria-roledescription')])); + let destroyed = false; + const translate = () => { + if (destroyed) return; + if (rootDescription) root.setAttribute('aria-roledescription', rootDescription); + const slides = items(); + const template = root.dataset.slideLabel; + slides.forEach((item, index) => { + const description = descriptions.get(item); + if (description) item.setAttribute('aria-roledescription', description); + if (template) + item.setAttribute('aria-label', template.replace('[index]', index + 1).replace('[total]', slides.length)); }); + }; + // Observe first to capture authored descriptions before data-slot rewrites them. + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) + for (const node of mutation.addedNodes) + if (node instanceof Element && node.matches('[data-slot="carousel-item"]')) + descriptions.set(node, node.getAttribute('aria-roledescription')); + queueMicrotask(translate); + }); + observer.observe(content, { childList: true }); + const carousel = createCarousel(root, { slides: 'multiple', drag: true, loop: false }); + translate(); for (const button of root.querySelectorAll('[data-slot="carousel-previous"], [data-slot="carousel-next"]')) button.hidden = false; + const destroy = carousel.destroy.bind(carousel); + carousel.destroy = () => { + destroyed = true; + observer.disconnect(); + destroy(); + }; return carousel; } diff --git a/liquid/styles/field.css b/liquid/styles/field.css index 64fa0bdf5..ddba76732 100644 --- a/liquid/styles/field.css +++ b/liquid/styles/field.css @@ -53,9 +53,13 @@ /* Two fields side by side from 640px. */ .field-row { display: grid; - grid-template-columns: 1fr 1fr; gap: calc(var(--spacing) * 4); } +@media (min-width: 640px) { + .field-row { + grid-template-columns: 1fr 1fr; + } +} [data-slot='field-description'] { font-size: 0.875rem; color: var(--muted-foreground); diff --git a/liquid/styles/section.css b/liquid/styles/section.css index 168e4c8d3..c2aec375d 100644 --- a/liquid/styles/section.css +++ b/liquid/styles/section.css @@ -75,3 +75,18 @@ line-height: 1.1; text-wrap: balance; } +@media (min-width: 640px) { + [data-slot='section-title'] { + font-size: 2.25rem; + line-height: 2.5rem; + } + .display-title, + .hero-title { + font-size: 3rem; + } +} +@media (min-width: 1024px) { + .display-title { + font-size: 3.75rem; + } +}