HomeFeatures

←→

Exemples de ce que Pathou ajoute à une trace — une alternative, un balcon, un passage difficile — posés autour de la question de l’accueil, avec les composants mêmes de la vue trace.

stableÀ valider
// Titre : Balcon (BalconyExample)
import { BalconyExample } from '@pathou/components/HomeFeatures';

export default function Exemple() {
  return (
    <div style={{ width: 340 }}>
      <BalconyExample />
    </div>
  );
}

Installation

Copie le fichier dans ton projet, ainsi que les tokens qu’il utilise.

src/components/HomeFeatures.tsx
import { WAY_COLOR, gradeColor } from '../lib/colors';
import { WAY_CLASS_LABEL } from '../lib/osm/classify';
import BalconyFigure, { FIG_H } from './BalconyFigure';
import { WalkIcon } from './DetourBlock';
import { colStyle } from './techStyle';
import DetourGlyph from './DetourGlyph';

/** Exemples de ce que Pathou ajoute à la trace, posés autour de la question de l'accueil (composants de la vue trace). */
export function DetourExample() {
  return (
    <aside className="hp-feat hp-feat-detour" aria-label="Exemple d’alternative à un passage trop dur">
      <div className="detour-top">
        <DetourGlyph kind="praticable" />
        <div className="detour-head">
          Alternative praticable
          <small>contourne la descente technique</small>
        </div>
      </div>
      <div className="detour-stats">
        <div>
          <b>+540 m</b>
          <span>distance</span>
        </div>
        <div>
          <b>+25 m</b>
          <span>D+</span>
        </div>
        <span className="detour-chip">
          <i style={{ background: WAY_COLOR.piste_roulante }} />
          {WAY_CLASS_LABEL.piste_roulante}
        </span>
      </div>
    </aside>
  );
}

/** Pente illustrative (pas un relevé de la trace d'exemple) : un balcon franc, sous le seuil « exposé ». */
const BALCONY_SLOPE = 84;

export function BalconyExample() {
  const deg = Math.round((Math.atan(BALCONY_SLOPE / 100) * 180) / Math.PI);
  return (
    <aside className="hp-feat hp-feat-balcony" aria-label="Exemple de sentier en balcon">
      <span className="alert-head">
        <span className="alert-name">Sentier en balcon</span>
        <span className="alert-len">180 m</span>
      </span>
      <span className="alert-balcony">
        <BalconyFigure slope={BALCONY_SLOPE} side="droit" />
        <span className="alert-stats">
          <span>
            <b>{BALCONY_SLOPE} % ({deg}°)</b>
            <small>sous le sentier, vide à droite</small>
          </span>
        </span>
      </span>
    </aside>
  );
}

/**
 * Profil illustratif d'un point d'attention (pas un relevé de la trace d'exemple) : abords en gris, passage coloré par la pente,
 * comme dans la vue trace. Abscisses en part de la largeur (0–100), altitudes en mètres.
 */
const PASSAGE_PROFILE: [number, number][] = [
  [0, 0], [8, 2], [16, 5], [24, 7], [30, 9], [36, 15], [42, 23], [48, 31], [54, 39], [60, 46], [66, 51], [70, 53], [78, 55], [86, 56], [100, 57],
];
const PASSAGE_X0 = 30;
const PASSAGE_X1 = 70;
/** Pente de chaque segment du passage (%), pour le dégradé : 190 m pour ~44 m de dénivelé. */
const PASSAGE_GRADES = [12, 18, 22, 21, 19, 14, 9];

export function PassageExample() {
  const top = Math.max(...PASSAGE_PROFILE.map(([, z]) => z));
  const y = (z: number) => 1.5 + (FIG_H - 3) * (1 - z / top);
  const line = (pts: [number, number][]) => pts.map(([x, z], k) => `${k ? 'L' : 'M'}${x} ${y(z)}`).join('');
  const inside = PASSAGE_PROFILE.filter(([x]) => x >= PASSAGE_X0 && x <= PASSAGE_X1);
  const passage = line(inside);
  return (
    <aside className="hp-feat hp-feat-passage" aria-label="Exemple de point d’attention : poussage probable">
      <span className="alert-head">
        <span className="alert-km">km 18,4</span>
        <span className="alert-name">Poussage probable</span>
        <span className="alert-len">190 m</span>
      </span>
      <span className="alert-figure">
        <svg className="alert-shape" viewBox={`0 0 100 ${FIG_H}`} preserveAspectRatio="none" style={{ height: FIG_H }} aria-hidden="true">
          <defs>
            <linearGradient id="hp-passage-grad" x1="0" x2="1" y1="0" y2="0">
              {PASSAGE_GRADES.map((g, k) => (
                <stop key={k} offset={k / (PASSAGE_GRADES.length - 1)} stopColor={gradeColor(g)} />
              ))}
            </linearGradient>
          </defs>
          <path className="alert-context" d={`${line(PASSAGE_PROFILE)}L100 ${FIG_H}L0 ${FIG_H}Z`} />
          <path className="alert-context-line" d={line(PASSAGE_PROFILE)} vectorEffect="non-scaling-stroke" />
          <path d={`${passage}L${PASSAGE_X1} ${FIG_H}L${PASSAGE_X0} ${FIG_H}Z`} fill="url(#hp-passage-grad)" fillOpacity="0.3" />
          <path d={passage} fill="none" stroke="url(#hp-passage-grad)" strokeWidth="2.5" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
          <line className="alert-bound" x1={PASSAGE_X0} x2={PASSAGE_X0} y1="0" y2={FIG_H} vectorEffect="non-scaling-stroke" />
          <line className="alert-bound" x1={PASSAGE_X1} x2={PASSAGE_X1} y1="0" y2={FIG_H} vectorEffect="non-scaling-stroke" />
        </svg>
        <span className="climb-push" aria-hidden="true">
          <i style={{ left: '38%', width: '24%' }} />
        </span>
      </span>
      <span className="alert-stats">
        <span>
          <b>
            <i style={{ background: gradeColor(22) }} />
            22 %
          </b>
          <small>pente max</small>
        </span>
        <span>
          <b>26 %</b>
          <small>rampe sur 50 m</small>
        </span>
        <span>
          <b>
            <WalkIcon />
            ~120 m
          </b>
          <small>à pied</small>
        </span>
      </span>
      <span className="alert-chips">
        <span className="pill-tech tech-pill" style={colStyle(1)}>
          S1
        </span>
        <span className="detour-chip">
          <i style={{ background: WAY_COLOR.sentier }} />
          {WAY_CLASS_LABEL.sentier}
        </span>
      </span>
    </aside>
  );
}

Tokens : --panel --text --muted --detour --detour-ink --line --accent --chip --exigeant --ease-out --tech-black --tech-black-ink

Utilisation

import { BalconyExample, DetourExample, PassageExample } from './components/HomeFeatures';

<DetourExample />

Exemples

Alternative (DetourExample)

// Titre : Alternative (DetourExample)
import { DetourExample } from '@pathou/components/HomeFeatures';

export default function Exemple() {
  return (
    <div style={{ width: 340 }}>
      <DetourExample />
    </div>
  );
}

Passage difficile (PassageExample)

// Titre : Passage difficile (PassageExample)
import { PassageExample } from '@pathou/components/HomeFeatures';

export default function Exemple() {
  return (
    <div style={{ width: 340 }}>
      <PassageExample />
    </div>
  );
}

Guidelines

  • MUST
    Construire ces exemples avec les composants de la vue trace (DetourBlock, BalconyFigure…), pour que l’accueil montre ce que l’on verra vraiment.
  • SHOULD
    Signaler dans le code une valeur illustrative (pente du balcon) qui n’est pas un relevé.

Référence de l’API

DetourExample

Aucune prop.

BalconyExample

Aucune prop.

PassageExample

Aucune prop.