Skip to content

React‑Router Theme Framework – Vadovas ​

Šis dokumentas aprašo tavo theme framework griaučius, paremtus react-router, ir parodo, kaip kurti maršrutus, išdėstymus (layouts), sekcijas ir jų „loaderius“. Viskas pailiustruota realiais tavo kodo pavyzdžiais.


1) Projekto esmė ir struktūra ​

Pagrindinės idėjos

  • Tema yra aprašoma faile `` – čia deklaruojami meta duomenys, plėtiniai (extension points), maršrutai, sekcijos, lokalės ir t. t.
  • Pagrindinis išdėstymas (layout) – . Jis naudoja bendriems UI elementams ir kontekstams.
  • Standartinis puslapio apvalkalas (content wrapper) – ``.
  • `` – papildomi puslapiai/maršrutai (kai kurie skirti premium vartotojams).
  • `` – atskiros kompozicinės sekcijos su savo UI ir (pasirinktinai) serverio loaderiais.

Duomenų srautas trumpai

  1. theme.manifest.js deklaruoja routes, extensionPoints ir sections.
  2. Routeris rendrina layout/theme.tsx → jis viduje įterpia Wrapper ir children.
  3. Puslapiai (iš routes/*) gali turėti loader arba naudoti sekcijų duomenis.
  4. Sekcijos (iš sections/*) gauna duomenis per loader su
    ts
    { request, layout, product, lang }
  5. Komponentuose duomenys pasiekiami su @pikselis/runtime hooks, pvz. useSectionData("productsSection").

2) theme.manifest.js ​

Meta ​

js
export default {
  name: "clothes",
  version: "1.0.0",
  engine: ">=1.0 <2",
  tokens: "tokens.css",
  public: "public",
  config: { settingsId: "86302a6a-e93a-462d-a03d-3049028e2173" },
  locales: "locales",
  supportedLanguages: ["lt", "en"],
  ...
}

Paaiškinimai

  • name/version/engine – temos identitetas ir suderinamumo langas.
  • tokens – kelias į dizaino tokenų CSS (spalvos, tipografija ir pan.).
  • public – statinių failų katalogas.
  • config.settingsId – išorinė nuostata (pvz., nuotoliniams nustatymams gauti).
  • locales/supportedLanguages – i18n resursai ir palaikomos kalbos.

Extension points ​

js
extensionPoints: {
  layout: "layout/theme.tsx",
  header: "sections/Header.tsx",
  footer: "sections/Footer.tsx",
  page:   "layout/Page.tsx"
},
  • layout – šakninis layout (apgaubia visą aplikaciją).
  • header/footer – bendri viršus/apačia, kurie paduodami į Wrapper.
  • page – bazinis puslapio „konteineris“ (padding, grid ir pan.).

Maršrutai (routes) ​

Deklaruojami masyve routes. Minimalus įrašas:

js
{ path: "/", file: "routes/home.tsx", hasLoader: false }

Papildomai:

  • loader: "loader" – nurodo pavadintą eksportą faile (pvz., export const loader = ...).
  • Galima naudoti „wildcards“ ir dinaminius segmentus, pvz.: /user/orders/*, /prekiu-zenklai/:handle.

Root extensions ​

js
rootExtensions: [
  { key: "products", routes: ["^/products/[^/]+$"], file: "rootExtensions/recs.server.ts" }
]

Leidžia prijungti server-side logiką tam tikriems keliams (regex), pvz. rekomendacijoms.

Sekcijos (sections) ​

Sekcijos yra atkartojami UI blokai su konfigūracija:

js
{
  id: "productInfo",
  label: "Product Info",
  type: "productInfo",
  component: "sections/ProductInfo.tsx",
  slots: ["product.main"],
  propsSchema: {
    type: "object",
    properties: {
      showVendor: { type: "boolean", default: true },
      enableTabs: { type: "boolean", default: false }
    },
    additionalProperties: false
  },
  blocks: [
    { type: "price", label: "Price", component: "blocks/Price.tsx", maxRepeat: 1 },
    { type: "inventoryBadge", label: "Inventory Badge", component: "blocks/InventoryBadge.tsx", maxRepeat: 3,
      propsSchema: { type: "object", properties: { lowThreshold: { type: "integer", default: 5, minimum: 0 } }, additionalProperties: false }
    }
  ],
  defaults: { props: { showVendor: true }, blocks: [{ type: "price" }] }
}

Svarbu

  • id – unikalus identifikatorius (naudojamas useSectionData(id)).
  • type – sekcijos tipas (naudojamas loaderių/konfigų logikoje).
  • component – React komponento kelias.
  • slots – kur sekcija gali būti talpinama (pvz., home, category, product, product.main).
  • propsSchema – JSON schema validuoti nustatymus (props) iš layout konfigūracijos.
  • blocks – vidiniai „sub‑blokai“; suteikia smulkesnę kompoziciją.
  • defaults – pradinės reikšmės, jei konfigūroje nieko neaprašyta.

3) Layout ir apvalkalai ​

layout/Page.tsx ​

Minimalus apvalkalas turiniui:

tsx
export default function Page({ children }: React.PropsWithChildren) {
  return <section className="section-padding py-8">{children}</section>;
}

layout/Wrapper.tsx ​

Bendras UI apvalkalas, perduodantis Header, Footer, ThemeContext, tvarkantis mobilų overlay ir pan. Svarbūs akcentai:

  • `` – tiekiamas useTheme iš layouto.
  • Mobilus overlay – reaguoja į redux reikšmę ui.mobileMenuIsOpen ir useBetterMediaQuery.
  • Struktūra – Header → content → Footer.

layout/theme.tsx ​

Pagrindinis layout su meta tag’ais, guest krepšelio token’u ir „last seen products“:

  • `` – grąžina title ir <link rel="stylesheet" ...> tagus (CSS, fontai, AOS ir pan.).
  • Guest token – regenerateCartToken() kviečiamas, jei nėra guestToken nei localStorage, nei Cookies.
  • „Last seen products“ – į Cookies rašomas sąrašas paskutinių 20 productId (30 d. galiojimas).

Karkasas:

tsx
export default function Layout({ header, footer, children, scripts, useTheme }: any) {
  // useLocation, useRouteData("root")
  // efektai: guestToken, lastSeenProductIds
  return (
    <Wrapper header={header} footer={footer} useTheme={useTheme}>
      {children}
    </Wrapper>
  );
}

4) Maršrutų kūrimas ​

  1. Sukurk failą routes/xyz.tsx su React komponentu.
  2. Jei reikia duomenų – pridėk pavadintą eksportą export const loader = async ({ request, params }) => { ... }.
  3. `` įdėk įrašą į routes:
    js
    { path: "/xyz", file: "routes/xyz.tsx", hasLoader: true, loader: "loader" }
  4. (Pasirinktinai) naudok dinamiką: :param, *.

Pavyzdžiai iš temos

  • Krepšelio žingsniai: /krepselis, /krepselis/prisijungimas, /krepselis/adresai, /krepselis/mokejimas/*, /krepselis/perziura.
  • Naudotojo sritis: /user/info, /user/orders/*, /user/addresses/*, /user/wishlist.
  • Turinys: /blog, /blog/:blogSlug, /contacts, /search, /parduotuves.

5) Sekcijų kūrimas ir duomenys ​

Loaderio parašas ​

Sekcijos server loader gauna:

ts
// @DOC - passed request, layout, product, lang
const sectionData = await section.loader({
  request: args.request,
  layout,
  product: baseData?.anyPageData?.product,
  lang: baseData?.lang
});

Loaderio panaudojimas ​

Komponente:

tsx
import { useSectionData } from "@pikselis/runtime";
const data = useSectionData("productsSection");

Pavyzdinis loaderis (produktų blokai) ​

ts
export const loader = (args) => getProductsByType(args);

const getProductsByType = async ({ layout }) => {
  const productsBlockSectionData = {};
  const tasks = [];

  for (const section of layout.sections ?? []) {
    if (!['newProducts','discountedProducts','recommendedProducts','productsSection'].includes(section?.type)) continue;

    const { type, limit } = section.settings;
    let url = '';
    switch (type) {
      case 'discounted':
        url = `/products?filterType=discountedIndexed&selectMerged=1&useIndexedSearch=1&perPage=${limit}`; break;
      case 'recommended':
      case 'new':
      default:
        url = `/products?selectMerged=1&useIndexedSearch=1&orderBy=createdAt&orderWay=desc&perPage=${limit}`; break;
    }

    tasks.push(
      getApiGoNoAuth(url).then((res) => {
        if (res?.success) productsBlockSectionData[section.id] = res.products;
      })
    );
  }

  await Promise.all(tasks);
  return productsBlockSectionData;
};

Pastabos

  • Loaderis vienu kartu gali aptarnauti kelias sekcijas (pagal layout.sections).
  • Grąžinama struktūra sectionId → data, kad kliente būtų galima useSectionData(id).

6) i18n ir lokalės ​

  • `` – katalogas su vertimais.
  • `` – kalbų sąrašas (pvz., lt, en).
  • handle.getMeta naudoja settings?.siteTitle[lang] – meta priklauso nuo kalbos.

7) Dizaino tokenai ir vieši failai ​

  • `` – CSS kintamieji, kuriais paremtas temos dizainas.
  • `` – paveikslėliai, šriftai, papildomi CSS (common.css, styles.css, index.css), trečiųjų šalių CSS (pvz., AOS), Google Fonts preconnect ir pan.

8) Praktiniai receptai ​

A) Nauja sekcija nuo 0 ​

  1. Sukurk sections/MySection.tsx su default export komponentu.
  2. (Nebūtina) pridėk export const loader = async (args) => { ... }.
  3. theme.manifest.js pridėk į sections masyvą:
    js
    {
      id: "mySection",
      label: "My Section",
      type: "mySection",
      component: "sections/MySection.tsx",
      slots: ["home"],
      propsSchema: { type: "object", properties: { title: { type: "string" } } }
    }
  4. Layouto konfigūroje į atitinkamą slot įtrauk mySection su props.
  5. Komponente gauk duomenis su useSectionData("mySection").

B) Papildomas maršrutas su loaderiu ​

js
routes: [
  { path: "/faq", file: "routes/faq.tsx", hasLoader: true, loader: "loader" }
]

Faile routes/faq.tsx:

tsx
export const loader = async ({ request }) => {
  // fetch from API, cookies, whatever
  return { items: [...] };
};
export default function FAQ() {
  const data = useLoaderData();
  return <Page>{/* render data.items */}</Page>;
}

C) Naudotojo sąrašai/detalės su „wildcards“ ​

/user/orders/*, /user/addresses/* leidžia talpinti gilesnius path’us (pvz. /user/orders/123), atskirame faile naudoti params.


9) Gerosios praktikos ​

  • Sekcijų `` stabilumas – nekeisk id, jei nori išlaikyti sudėliotų blokų ryšį su duomenimis.
  • `` – griežtas validavimas padeda saugiai redaguoti per UI.
  • Loader’iai – agreguok kelias sekcijas viename request’e ten, kur prasminga (kaip getProductsByType).
  • SSR/CSR balansas – sekcijų loaderius laikyk server’yje, komponentus – mažus, su memo kur reikia.
  • Cache/limitai – API užklausoms naudok limitus (perPage), pageidautina pridėti AbortController klientui.
  • Atskirk UI nuo duomenų – komponentai turi skaityti per useSectionData(id) ir nepažinti backend URL.

10) Greita atmintinė ​

  • Manifestas = vienintelė tiesa apie maršrutus, sekcijas ir extension points.
  • Layout (theme.tsx) tiekia meta, guest krepšelį, last‑seen produktus.
  • Wrapper – sujungia Header, Footer, kontekstą ir pagrindinę turinio zoną.
  • Sekcijos – deklaruojamos manifeste + turi component ir (pasirinktinai) loader.
  • Duomenys į sekciją → per useSectionData("<sectionId>").

11) Naudingi importai (greita nuoroda) ​

ts
import { useRouteData, useSectionData, useTheme } from "@pikselis/runtime";
import { useRouteLoaderData } from "react-router";
import Cookies from "js-cookie";
import { useSelector } from "react-redux";

Klausimai? ​

Jei reikia, galime pridėti skyrių apie temos konfigūravimą per settings (naudojant settingsId) arba pateikti pilnas sekcijų konfigūravimo JSON pavyzdžius konkretiems slot’ams (pvz., home, category, product).

Pikselis Business dokumentacija