main.js — code complet du sélecteur des modes de livraison et de retrait en Vue
Composants natifs, état local, gestion du focus itinérant des boutons radio et tous les imports de styles requis.
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");
- Utilisez <div id="root"></div> dans index.html.


