main.js — vollständiger Vue-Fulfillment-Auswahldialog
Native Komponenten, lokaler Zustand, roving Radiobutton-Fokus und alle erforderlichen Style-Imports.
import { createApp, h, ref } from "vue";
import CdrButton from "@rei/cedar/CdrButton";
import CdrFulfillmentTile from "@rei/cedar/CdrFulfillmentTile";
import "@rei/cedar/dist/cdr-reset.css";
import "@rei/cedar/dist/style/cdr-button.css";
import "@rei/cedar/dist/style/cdr-fulfillment-title.css";
import "@rei/cedar/dist/style/cdr-surface-selection.css";
import "@rei/cedar/dist/style/cdr-layout.css";
import "@rei/cdr-tokens/css";
import "@rei/cedar/dist/style/cdr-surface.css";
import "@rei/cedar/dist/style/cdr-body.css";
import "@rei/cedar/dist/style/cdr-subheading-sans.css";
import "./cedar.css";
const choices = [
{
id: "ship",
label: "Ship to home",
detail: "Sample delivery: 3–5 days",
price: "No delivery charge",
available: true,
},
{
id: "pickup",
label: "Pick up nearby",
detail: "Sample location: North Trail",
price: "Ready tomorrow",
available: true,
},
{
id: "express",
label: "Express delivery",
detail: "Unavailable for this sample item",
price: "Choose another method",
available: false,
},
];
createApp({
setup() {
const selected = ref("ship"),
result = ref("");
function choose(id) {
selected.value = id;
result.value = "";
}
function keyboard(e, id) {
if (
[
"ArrowRight",
"ArrowDown",
"ArrowLeft",
"ArrowUp",
"Home",
"End",
].includes(e.key)
) {
e.preventDefault();
const ids = choices.filter((x) => x.available).map((x) => x.id);
let next =
e.key === "Home"
? ids[0]
: e.key === "End"
? ids.at(-1)
: ids[
(ids.indexOf(id) +
(e.key === "ArrowRight" || e.key === "ArrowDown"
? 1
: ids.length - 1)) %
ids.length
];
choose(next);
document.getElementById("choice-" + next).focus();
}
}
return () =>
h("main", [
h("header", [
h("p", { class: "eyebrow" }, "OUTDOOR GEAR / ORIGINAL SPECIMEN"),
h("h1", "Ready for the next trail"),
h("p", "Choose a fulfillment method for this fictional daypack."),
]),
h("div", { class: "composition" }, [
h("section", { class: "product", "aria-label": "Sample product" }, [
h("div", { class: "art", "aria-hidden": "true" }, [
h("div", { class: "pack" }, [h("i"), h("span"), h("b")]),
]),
h("div", [
h("h2", "Ridge daypack · 24 L"),
h("p", "A lightweight everyday pack for short outings."),
h(
"p",
{ class: "note" },
"Illustrated sample product · no inventory or purchase connection",
),
]),
]),
h("section", { class: "options" }, [
h(
"h2",
{ id: "fulfillment-label" },
"How would you like to receive it?",
),
h(
"div",
{
class: "tiles",
role: "radiogroup",
"aria-labelledby": "fulfillment-label",
},
choices.map((c) =>
h(
CdrFulfillmentTile,
{
id: "choice-" + c.id,
role: "radio",
tabindex: c.available && selected.value === c.id ? 0 : -1,
checked: selected.value === c.id,
disabled: !c.available,
"aria-label": c.label,
onClick: () => c.available && choose(c.id),
onKeydown: (e) => keyboard(e, c.id),
},
{
header: () => c.label,
"icon-right": () =>
selected.value === c.id
? h(
"span",
{
"aria-hidden": "true",
style: "font-size:2rem;color:#1f513f",
},
"✓",
)
: null,
body: () => c.detail,
footer: () => c.price,
},
),
),
),
h("div", { class: "review" }, [
h(
CdrButton,
{
onClick: () => {
result.value = `Local selection: ${choices.find((c) => c.id === selected.value).label}. No order has been placed.`;
},
},
() => "Review selection",
),
h("p", { role: "status", class: "result" }, result.value),
]),
]),
]),
h(
"footer",
"Native Cedar 17.1.0 fulfillment tiles and button. Original composition with system-font fallback; no REI proprietary fonts or photography.",
),
]);
},
}).mount("#root");
- Verwende <div id="root"></div> in index.html.


