--- // 小平市の公園・周辺の公園・公共施設の一覧(/kodaira-data/parks/)を表示する。 // データは src/data/parks-rows.ts で組み立てる。 // DOM を抑えるため、設備は1セルにまとめ、アイコンは CSS の擬似要素で描く。 // 絞り込み・並べ替え・カード表示は利用者の端末で実行する。 import { parks, mappedNames, type Park } from "@/data/parks-rows"; const EMPTY = "—"; // 種別の表示(色付きの丸+区分名。正式名は title / aria-label で補う)。 const TYPE_LABEL: Record = { 街区: "街区公園", 近隣: "近隣公園", 運動: "運動公園", 広域: "広域公園", 広場: "広場", 緑地: "緑地", 団地内公園: "団地内公園", 児童遊園: "児童遊園", 市外: "市外の近隣公園", 地域センター: "地域センター", 公民館: "公民館", 児童館: "児童館", 図書館: "図書館", 市役所支所: "市役所支所", 文化施設: "文化施設", }; const TYPE_CLASS: Record = { 街区: "parks-type--block", 近隣: "parks-type--neighbor", 運動: "parks-type--sports", 広域: "parks-type--wide", 広場: "parks-type--open", 緑地: "parks-type--green", 団地内公園: "parks-type--danchi", 児童遊園: "parks-type--jidou", 市外: "parks-type--green", 地域センター: "parks-type--facility", 公民館: "parks-type--facility", 児童館: "parks-type--facility", 図書館: "parks-type--facility", 市役所支所: "parks-type--facility", 文化施設: "parks-type--facility", }; // 種別は列幅を小さくするため、漢字1文字の記号で表示する。 const TYPE_MARK: Record = { 街区: "街", 近隣: "近", 運動: "運", 広域: "広", 広場: "場", 緑地: "緑", 団地内公園: "団", 児童遊園: "児", 市外: "外", 地域センター: "📍", 公民館: "🏛", 児童館: "🧒", 図書館: "📚", 市役所支所: "🏢", 文化施設: "🎭", }; // 市立以外の公園に付けるバッジ(設置者が空欄なら市立として扱う)。 function ownerLabel(owner: string): string { if (owner.includes("都営")) return "都営"; if (owner === "東京都") return "都立"; if (owner.includes("UR")) return "UR"; if (owner.includes("民間")) return "民間"; return owner; } function ownerClass(owner: string): string { if (owner.includes("都営")) return "parks-owner--toei"; if (owner === "東京都") return "parks-owner--tokyo"; if (owner === "未確認") return "parks-owner--unknown"; return "parks-owner--other"; } // 設備バッジ(アイコンは CSS の ::before で描く)。 type Badge = { kind: string; label: string; title: string }; function facilityBadges(p: Park): Badge[] { const badges: Badge[] = []; if (p["トイレ"]) { badges.push({ kind: "toilet", label: "トイレ", title: `トイレ: ${p["トイレ"]}` }); } if (p["水飲み場"]) { badges.push({ kind: "water", label: "水飲み場", title: `水飲み場: ${p["水飲み場"]}` }); } if (p["給水"]) { badges.push({ kind: "water", label: "給水", title: `給水: ${p["給水"]}` }); } if (p["遊具"]) { badges.push({ kind: "toy", label: "遊具", title: `遊具: ${p["遊具"]}` }); } const ball = p["ボール遊び"]; if (ball && ball !== "禁止") { const label = ball.includes("一部") ? "ボール遊び一部可" : "ボール遊び可"; badges.push({ kind: "ball", label, title: `ボール遊び: ${ball}` }); } if (p["南西部指定管理"]) { badges.push({ kind: "cm", label: "指定管理", title: "南西部地域の指定管理公園" }); } return badges; } // 設備フィルタ用のトークン(data-fac)。 function facTokens(p: Park): string { const tokens: string[] = []; if (p["トイレ"]) tokens.push("toilet"); if (p["水飲み場"]) tokens.push("water"); if (p["給水"]) tokens.push("water"); if (p["遊具"]) tokens.push("toy"); const play = p["遊具"] || ""; if (play.includes("砂場")) tokens.push("sand"); if (play.includes("ブランコ")) tokens.push("swing"); if (play.includes("シーソー")) tokens.push("seesaw"); if (play.includes("スプリング")) tokens.push("spring"); if (play.includes("ジャングル")) tokens.push("jungle"); if (play.includes("ヒコーキ") || play.includes("飛行機")) tokens.push("plane"); if (play.includes("滑り台") || play.includes("滑台")) tokens.push("slide"); if (play.includes("複合遊具")) tokens.push("complex"); if (play.includes("ロープウェイ")) tokens.push("rope"); const ball = p["ボール遊び"]; if (ball && ball !== "禁止") tokens.push("ball"); const clock = (p["時計"] || "").trim(); if (clock === "○" || clock === "あり" || clock === "有") tokens.push("clock"); if (p["南西部指定管理"]) tokens.push("cm"); // ピクニック広場・芝生広場・テーブルのある公園。 const picnicText = `${p["公園名"] || ""} ${p["種別"] || ""} ${p["その他設備"] || ""} ${p["備考"] || ""}`; if ( (p["テーブル"] || "").trim() || p["種別"] === "広場" || picnicText.includes("芝生") || picnicText.includes("ピクニック") || picnicText.includes("広場") ) { tokens.push("picnic"); } return tokens.join(" "); } // 設備セルの補足テキスト(バッジで示した項目の詳細と、その他の項目)。 // ラベル文字は使わず、アイコンで項目を示す。 const DETAIL_FIELDS: { key: string; icon: string }[] = [ { key: "ベンチ・椅子", icon: "🪑" }, { key: "テーブル", icon: "🍽" }, { key: "シェルター等", icon: "⛱" }, { key: "防災用具", icon: "🧯" }, { key: "遊具", icon: "🎠" }, { key: "時計", icon: "⏰" }, { key: "駐車場", icon: "🅿" }, { key: "その他設備", icon: "🔧" }, { key: "備考", icon: "" }, ]; function detailText(p: Park): string { const parts: string[] = []; for (const { key, icon } of DETAIL_FIELDS) { const value = p[key]; if (!value) continue; parts.push(icon ? `${icon} ${value}` : value); } const ball = p["ボール遊び"]; if (ball && ball !== "禁止" && ball !== "可") parts.push(`⚽ ${ball}`); return parts.join(" / "); } // 面積(数値) function areaNumber(value: string): number { const n = Number(value); return Number.isFinite(n) ? n : 0; } function areaText(value: string): string { const n = areaNumber(value); return n ? n.toLocaleString("ja-JP") : value; } // 面積の目安(小・中・大)。しきい値はページの注記に合わせる。 function sizeLabel(area: number): string { if (area < 1000) return "小"; if (area < 10000) return "中"; return "大"; } function sizeTitle(area: number): string { if (area < 1000) return "目安: 1,000㎡未満(小さめ)"; if (area < 10000) return "目安: 1,000〜10,000㎡(ふつう)"; return "目安: 10,000㎡以上(広め)"; } // 公園名で検索する。公園名だけだと別の自治体の同名公園に飛ぶので「小平市」を必ず付ける。 // 公園名がない場合だけ住所、それもなければ座標を使う。 function mapsUrl(p: Park): string { if (p["地図URL"]) return p["地図URL"]; const name = (p["公園名"] || "").trim(); const query = name ? `小平市${name}` : p["住所"] ? `小平市${p["住所"]}` : `${p["緯度"]},${p["経度"]}`; return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`; } // 検索対象(公園名・住所・町名・種別・設備・備考)。 function searchIndex(p: Park): string { return [ p["公園名"], p["住所"], p["町名"], p["種別"], TYPE_LABEL[p["種別"]], p["設置者"], p["状態"], p["トイレ"], p["水飲み場"], p["給水"], p["ボール遊び"], ...DETAIL_FIELDS.map((f) => p[f.key]), ] .join(" ") .toLowerCase(); } ---
{parks.map((park) => ( ))}
住所 種別 施設・備考
{mappedNames.has(park["公園名"]) ? ( ) : park["公式URL"] ? ( {park["公園名"]} ) : ( park["公園名"] )} {park["状態"] === "閉鎖" ? ( 閉鎖 ) : null} {park["設置者"] && park["設置者"] !== "小平市" ? ( {ownerLabel(park["設置者"])} ) : null} {park["住所"] || EMPTY} {park["面積㎡"] ? `${areaText(park["面積㎡"])}㎡` : EMPTY} {park["種別"] ? ( ) : ( EMPTY )} {facilityBadges(park).map((b) => ( ))} {detailText(park) ? {detailText(park)} : null} {facilityBadges(park).length === 0 && !detailText(park) ? EMPTY : null}