Zestaw UI
@openchamber/sdk/ui to zestaw gotowych kontrolek, które używają kolorów, czcionek i odstępów aplikacji, więc Twoje rozszerzenie wygląda jak reszta OpenChamber bez kopiowania jej styli. Każda kontrolka to funkcja: dajesz jej swój element i ustawienia, ona rysuje się w nim i zwraca { update, dispose }. Ty składasz kontrolki w ekran i trzymasz dane. Zestaw nigdy nie wywołuje Twojego dostawcy ani OpenChamber; host.request, host.attach i resztę nadal wywołujesz samodzielnie. Własny HTML i CSS możesz napisać dla wszystkiego, czego w zestawie brakuje, ale najpierw sięgaj po zestaw: jego kontrolki same podążają za motywem, odstępami i nawykami klawiaturowymi użytkownika, więc zbudowany z nich panel wygląda jak część OpenChamber, a nie strona internetowa w środku.
Najpierw wywołaj applyHostReady
Kontrolki czytają kolory ze zmiennych CSS, które applyHostReady zapisuje na stronie. Ustawia też czcionkę i kolor tekstu aplikacji na korzeniu strony, więc zwykły HTML, który dodasz samodzielnie, też wygląda poprawnie. Wywołaj je w host.onReady przed pierwszym montowaniem, a kolory będą podążać za motywem aplikacji, gdy użytkownik go zmieni.
import { applyHostReady } from "@openchamber/sdk/ui";
let mounted = false;host.onReady((ctx) => { applyHostReady(ctx, document.documentElement); if (mounted) return; mounted = true; // Mount controls and register listeners once here.});Zestaw używa kolorów tekstu obliczonych przez hosta w delikatnie zabarwionych przyciskach, plakietkach, tytułach powiadomień, komunikatach walidacji i linkach. Nie trzeba samodzielnie obliczać kontrastu.
Do własnego CSS applyHostReady udostępnia --primary-text, --success-text, --warning-text, --error-text i --info-text oraz odpowiadające im aliasy --oc-*-text. Używaj ich do tekstu na neutralnych lub lekko zabarwionych tłach. Dla wypełnień zachowaj kolory bazowe, a dla tekstu na pełnym wypełnieniu --oc-primary używaj --oc-primary-fg. Stosuj każdy stan z onReady, aby kolory były aktualne.
onReady może uruchamiać się wielokrotnie, gdy panel jest otwarty, na przykład po zmianie kontekstu sesji. Stosuj motyw za każdym razem, ale montuj kontrolki i rejestruj listenery tylko raz. Przechowuj szkice poza funkcjami renderującymi. Przy zmianie zakładki ukrywaj istniejące panele lub odtwarzaj ich wartości ze stanu.
Co zmienia się po aktualizacji OpenChamber
Kolory, obramowania, zaokrąglenia rogów oraz stany najechania, zaznaczenia i fokusu kontrolek zestawu pochodzą z wersji OpenChamber, w której działa rozszerzenie, więc po aktualizacji podążają za aplikacją bez przebudowy. Rozmiary i odstępy zostają takie, z jakimi zbudowano paczkę, więc układ się nie przesuwa; zmieniają się, gdy przebudujesz rozszerzenie z nowszym SDK. OpenChamber dodaje swoje style po twoim dokumencie, więc żeby zmienić wygląd kontrolki zestawu, użyj selektora bardziej szczegółowego niż selektor zestawu, na przykład #root .oc-sdk-btn.
Synchronizacja wyboru i pól
mountTabs, mountSelect, mountCheckbox i mountSwitch zgłaszają zmiany przez onChange, ale same nie zmieniają activeId, value ani checked. Zapisz wartość i przekaż ją z powrotem przez update. Pola tekstowe i wyszukiwania pokazują wpisany tekst od razu, ale też wymagają update({ value }), aby późniejsza aktualizacja nie przywróciła starej wartości. onClick nie zmienia wariantu przycisku. Do wyboru trybu użyj mountTabs.
Zamontuj te kontrolki raz w istniejącym root panelu:
import { mountSelect, mountTabs } from "@openchamber/sdk/ui";
let activeId = "convert";const tabs = mountTabs(root, { items: [{ id: "convert", label: "Convert" }, { id: "format", label: "Format" }], activeId, onChange: (next) => { activeId = next; tabs.update({ activeId }); // Show the matching panel without discarding its draft values. },});
let format = "json";const select = mountSelect(root, { options: [{ id: "json", label: "JSON" }, { id: "yaml", label: "YAML" }], value: format, onChange: (next) => { format = next; select.update({ value: format }); },});Aby zamienić formaty, zamień obie wartości w stanie i wywołaj update({ value }) na obu istniejących selectach. Nie montuj nowych kontrolek do zmiany wartości. Przy usuwaniu kontrolki wywołaj dispose().
Co możesz zamontować
| Funkcja | Co rysuje |
|---|---|
mountButton | Przycisk. variant to default, secondary, outline, ghost lub destructive; size to default, sm lub xs. loading pokazuje spinner i blokuje kliknięcia. |
mountTextField | Pole z etykietą albo textarea z multiline. Opcjonalny tekst helper lub error, maskowanie password i mono dla tokenów i identyfikatorów. |
mountSearchField | Pole wyszukiwania z lupą i przyciskiem czyszczenia. Escape je czyści. |
mountSelect | Lista rozwijana. searchable dodaje pole filtra na górze listy. Strzałki przesuwają, Enter wybiera, Escape zamyka. |
mountCheckbox, mountSwitch | Pole wyboru albo przełącznik z etykietą i opcjonalnym description. |
mountTabs | Zakładki w formie pigułek, każda z opcjonalnym licznikiem. Strzałki w lewo i w prawo przechodzą między nimi. |
mountBadge | Mała pigułka. tone to neutral, primary, success, warning, error lub info. |
mountList | Lista przyjazna klawiaturze. Każdy wiersz może mieć klucz z przodu, tytuł, podtytuł, tekst meta wyrównany do prawej i odznakę. |
mountEmpty | Wyśrodkowany stan pusty z tytułem, linią treści i opcjonalnym przyciskiem. |
mountSpinner | Pierścień ładowania z opcjonalną etykietą. |
mountBanner | Kolorowy komunikat z tytułem, treścią i opcjonalną akcją. |
mountSeparator | Cienka linia, opcjonalnie z etykietą pośrodku. |
mountProgress | Pasek postępu od 0 do 100. |
mountMenu | Przycisk, który otwiera listę akcji. Pozycje mogą być destrukcyjne, wyłączone albo być separatorem. |
mountText | Tekst od Twojego dostawcy. Obrazy i linki http(s) w stylu Markdown stają się prawdziwymi obrazami i linkami; cała reszta pozostaje zwykłym tekstem, więc niezaufaną treść można bezpiecznie pokazać. |
Obok kontrolek dostajesz trzy proste funkcje pomocnicze. filterSelectOptions(options, query) to dopasowanie, którego używa mountSelect. splitTextMedia(text) to to, co mountText robi przed rysowaniem. moveListSelection to krok klawiatury wspólny dla listy, selecta i menu, na wypadek gdybyś budował własną listę.
Lista z wyszukiwaniem
import { applyHostReady, mountEmpty, mountList, mountSearchField } from "@openchamber/sdk/ui";
let mounted = false;host.onReady((ctx) => { applyHostReady(ctx, document.documentElement); if (mounted) return; mounted = true; const root = document.querySelector("#root")!; const searchRoot = root.appendChild(document.createElement("div")); const listRoot = root.appendChild(document.createElement("div")); const emptyRoot = root.appendChild(document.createElement("div"));
let query = ""; let empty: { dispose: () => void } | null = null;
const list = mountList(listRoot, { items: [], onSelect: (id) => { const task = tasks.find((item) => item.id === id); if (task) void host.attach({ providerId: "acme-hello", id, title: task.title, url: task.url }); }, });
const paint = () => { const rows = tasks .filter((task) => task.title.toLowerCase().includes(query.toLowerCase())) .map((task) => ({ id: task.id, leading: task.key, title: task.title, meta: task.updated })); list.update({ items: rows }); empty?.dispose(); empty = rows.length === 0 ? mountEmpty(emptyRoot, { title: "No tasks match", body: "Try a shorter search." }) : null; };
const search = mountSearchField(searchRoot, { value: query, placeholder: "Search tasks", onChange: (next) => { query = next; search.update({ value: next }); paint(); }, }); paint();});update przyjmuje tylko te ustawienia, które się zmieniły. Lista zachowuje podświetlony wiersz między aktualizacjami, dopóki ten wiersz nadal istnieje.
Linki w tekście od dostawcy
Twoja strona działa w sandboxie i nie może sama otworzyć nowej karty. Gdy używasz mountText, przekaż onOpenUrl i oddaj link OpenChamber:
import { mountText } from "@openchamber/sdk/ui";
mountText(root, { text: comment.body, onOpenUrl: (url) => void host.openUrl(url),});Zobacz też
- Tworzenie rozszerzenia o folderze, manifeście i instalacji
- API hosta o
connectHost, attach i kodach błędów