Skip to main content

Estructura del projecte

El creador genera un projecte petit amb tres parts, sigui quin sigui el framework:

  1. El plugin (plugin.ts): el que es construeix i es publica. Exporta initialize i dispose.
  2. Les seves vistes: els components que el plugin injecta a les regions del shell.
  3. El sandbox (src/sandbox.ts): inicia el shell de demostració amb el teu plugin. Només es fa servir en desenvolupament i no forma part del plugin.
my-plugin/
├── index.html ← development page
├── src/
│ ├── plugin.ts ← initialize and dispose
│ ├── sandbox.ts ← starts the shell (dev only)
│ └── views/
│ ├── main-view.tsx ← a React component
│ └── main-view.css
├── vite.config.ts ← npm run dev / npm run build
├── tsconfig.json
└── package.json

El plugin: plugin.ts​

És el punt d'entrada del plugin, l'únic fitxer que coneix el shell. El shell crida initialize quan carrega el plugin i dispose quan el descarrega, cada vegada amb una API pròpia (consulta el shell de demostració).

src/plugin.ts
import { wrapReactViewFactory } from "@uxland/harmonix-adapters";
import type { DemoShellApi } from "@uxland/harmonix-demo-shell";
import { MainView } from "./views/main-view";
import styles from "./views/main-view.css?inline";

export const initialize = async (api: DemoShellApi) => {
// A view in the `main` region: a React component rendered inside a web component.
await api.regionManager.registerMainView({
id: "main",
factory: () => wrapReactViewFactory(MainView, styles, { fullHeight: true })({ api }),
});
// An item in the side menu that activates that view.
await api.regionManager.registerNavigationItem({ id: "menu", label: "My plugin", mainViewId: "main" });
};

export const dispose = async (api: DemoShellApi) => {
const { regions } = api.regionManager;
await api.regionManager.removeView(regions.sideMenu, "menu");
await api.regionManager.removeView(regions.main, "main");
};

Què fa:

  • Una vista és un objecte amb un id i una factory: una funció asíncrona que retorna l'element HTML que s'ha de mostrar. El shell crida la factoria quan cal renderitzar la vista. Sigui quin sigui el framework, el shell només veu un element HTML.
  • registerMainView registra la vista a la regió main, i registerNavigationItem afegeix al menú lateral un element que l'activa. Totes dues són dreceres del shell sobre el genèric registerView(region, view).
  • Els ids ("main", "menu") només han de ser únics dins del plugin: el shell desa les vistes com a pluginId::viewId, així que dos plugins poden fer servir els mateixos ids.
  • dispose desfà el que ha fet initialize. El shell pot descarregar i tornar a carregar plugins sense recarregar la pàgina, així que un plugin no ha de deixar res enrere (consulta les bones pràctiques).

La vista​

La vista principal rep l'API des de plugin.ts, com qualsevol altre input, i la fa servir per parlar amb altres plugins a través del broker: el botó publica un esdeveniment my-plugin:greeting i la vista, que hi està subscrita, compta les salutacions que rep.

src/views/main-view.tsx
const GREETING = "my-plugin:greeting";

export const MainView = ({ api }: { api: DemoShellApi }) => {
const [greetings, setGreetings] = useState(0);

useEffect(() => {
const subscription = api.broker.subscribe(GREETING, () => setGreetings((count) => count + 1));
return () => subscription.dispose();
}, [api]);

return (
<section className="view">
{/* … */}
<button type="button" onClick={() => api.broker.publish(GREETING, { from: api.pluginInfo.pluginId })}>
Publish a greeting
</button>
<p className="result">Greetings received: {greetings}</p>
</section>
);
};

Els estils de la vista fan servir els colors del shell a través de variables CSS, com ara var(--hx-primary-text, #00756a), amb un valor alternatiu per si un altre shell carrega el plugin.

El sandbox: src/sandbox.ts​

Inicia el shell de demostració i carrega el teu plugin des del codi font, de manera que cada canvi que desis es recarrega. És l'únic lloc on s'estableix l'id del plugin:

src/sandbox.ts
import { bootstrapPlugins, initializeShell } from "@uxland/harmonix-demo-shell";

initializeShell(document.body, { title: "my-plugin" });
await bootstrapPlugins([{ pluginId: "my-plugin", importer: () => import("./plugin") }]);

bootstrapPlugins rep una llista: per provar el teu plugin juntament amb d'altres, afegeix-los-hi. Una aplicació real fa el mateix, però obté la llista de plugins del Plugin Store.

La configuració​

FitxerQuè fa
package.jsonEls scripts (dev, build, typecheck) i les dependències. name i version identifiquen el plugin quan el publiques, i module apunta al fitxer construït.
vite.config.tsReact i Lit: npm run dev serveix index.html, i npm run build construeix només src/plugin.ts, deixant el framework i el shell fora del bundle. Angular: el segon pas de la construcció (consulta Construir i publicar).
angular.jsonNomés Angular: l'aplicació sandbox, que serveix npm run dev, i la llibreria my-plugin.
tsconfig.jsonTypeScript. Lit hi activa els decoradors que necessita.