Skip to main content
Frontendové komponenty jsou React komponenty, které se vykreslují přímo v uživatelském rozhraní Twenty. Běží v izolovaném Web Workeru s využitím Remote DOM — váš kód se spouští uvnitř sandboxovaného iframe s nejasným původem (opaque-origin), ale jeho UI se stále vykresluje nativně na stránce, místo aby bylo omezené na tento iframe.
Komponenty Front jsou stále aktivně vyvíjeny. Váš kód běží nad částečným DOMem, nikoli nad skutečnou stránkou prohlížeče, takže pokročilé způsoby použití mohou selhávat, často bez zjevných chyb. Viz Aktuální omezení.

Kde lze použít frontendové komponenty

Frontendové komponenty se mohou vykreslovat na třech místech v rámci Twenty:
  • Postranní panel — Frontendové komponenty, které nejsou headless, se otevírají v pravém postranním panelu. Toto je výchozí chování, když je frontendová komponenta vyvolána z příkazového menu.
  • Widgety (nástěnky a stránky záznamů) — front komponenty lze vkládat jako widgety do rozložení stránky. Při konfiguraci nástěnky nebo rozložení stránky záznamu mohou uživatelé přidat widget frontendové komponenty.
  • Nastavení aplikace — Definovaná pomocí defineSettingsFrontComponent(), frontendová komponenta se vykreslí jako sekce na kartě Settings (Nastavení) aplikace, místo výchozího uživatelského rozhraní pro konfiguraci proměnných.
Samotná frontendová komponenta není z uživatelského rozhraní dostupná — je potřeba ji zpřístupnit. Tři způsoby, jak to udělat, jsou:
  • Spárujte ji s položkou příkazové nabídky — zaregistruje ji v příkazové nabídce (Cmd+K) a volitelně také jako připnutou rychlou akci.
  • Vložte ji jako widget do rozložení stránky — umístí ji na detailní stránku záznamu nebo na nástěnku.
  • Definujte ji pomocí defineSettingsFrontComponent() — vykreslí ji jako sekci na kartě Settings (Nastavení) aplikace, místo výchozího uživatelského rozhraní pro konfiguraci proměnných.

Základní příklad

Nejrychlejší způsob, jak vidět front komponentu v akci, je spárovat ji s defineCommandMenuItem, aby se objevila jako tlačítko rychlé akce v pravém horním rohu stránky:
src/front-components/hello-world.tsx
src/command-menu-items/hello-world.command-menu-item.ts
Po synchronizaci pomocí yarn twenty dev (nebo po jednorázovém spuštění yarn twenty apply) se rychlá akce zobrazí v pravém horním rohu stránky:
Tlačítko rychlé akce v pravém horním rohu
Kliknutím na něj vykreslíte komponentu přímo ve stránce.

Konfigurační pole

Umístění frontendové komponenty na stránku

Mimo příkazy můžete frontendovou komponentu vložit přímo na stránku záznamu přidáním jako widget v rozvržení stránky. Podrobnosti viz Rozložení stránek.

Vlastní komponenta nastavení

Chcete-li nahradit automaticky generované uživatelské rozhraní pro konfiguraci proměnných na kartě Settings (Nastavení) vaší aplikace vlastní komponentou, definujte ji pomocí defineSettingsFrontComponent místo defineFrontComponent. Používá stejná konfigurační pole (kromě isHeadless, který není podporován, protože komponenta nastavení vždy vykresluje viditelné uživatelské rozhraní) a zároveň označuje komponentu jako uživatelské rozhraní nastavení aplikace. Komponenta se vykreslí jako sekce uvnitř karty Settings, nikoli jako náhrada celé karty. Systémem spravované sekce Twenty — automatická aktualizace, App URL a připojení — se vždy zobrazují nad ní a aplikace je nemůže přebít.
src/front-components/app-settings.tsx
Na jednu aplikaci je povolena pouze jedna frontová komponenta nastavení; deklarace více než jedné způsobí selhání sestavení. Je-li přítomna, karta Settings aplikace vykreslí tuto komponentu místo výchozího uživatelského rozhraní pro konfiguraci proměnných.

Headless vs. ne-headless

Front-endové komponenty existují ve dvou režimech vykreslování řízených volbou isHeadless: Ne-headless (výchozí) — Komponenta vykreslí viditelné uživatelské rozhraní. Po vyvolání z menu příkazů se otevře v postranním panelu. Toto je výchozí chování, když je isHeadless false nebo když tato volba není uvedena. Headless (isHeadless: true) — Komponenta se neviditelně inicializuje na pozadí. Neotevírá postranní panel. Headless komponenty jsou určené pro akce, které provedou logiku a poté se odpojí — například spuštění asynchronního úkolu, navigaci na stránku nebo zobrazení potvrzovacího modálního okna. Přirozeně se hodí ke komponentám SDK Command popsaným níže.
src/front-components/sync-tracker.tsx
Protože komponenta vrací null, Twenty přeskočí vykreslení kontejneru — v rozvržení se neobjeví žádné prázdné místo. Komponenta má však stále přístup ke všem hookům a API komunikace s hostitelem.

Komponenty SDK Command

Balíček twenty-sdk poskytuje čtyři pomocné komponenty Command navržené pro headless front-endové komponenty. Každá komponenta při připojení provede akci, chyby zpracuje zobrazením oznámení ve snackbaru a po dokončení automaticky odpojí front-endovou komponentu. Importujte je z twenty-sdk/front-component:
  • Command — Spustí asynchronní callback přes prop execute.
  • CommandLink — Naviguje na cestu v aplikaci. Props: to, params, queryParams, options.
  • CommandModal — Otevře potvrzovací modální okno. Pokud uživatel potvrdí, provede callback execute. Props: title, subtitle, execute, confirmButtonText, confirmButtonAccent.
  • CommandOpenSidePanelPage — Otevře stránku postranního panelu. Props závisí na page — např. ViewRecord bere recordId + objectNameSingular (plus volitelné id tab pro otevření záznamu na konkrétní záložce), ostatní stránky berou pageTitle + pageIcon.
Zde je kompletní příklad headless front-endové komponenty, která pomocí Command spouští akci z menu příkazů:
src/front-components/run-action.tsx
src/command-menu-items/run-action.command-menu-item.ts
A příklad s použitím CommandModal k vyžádání potvrzení před provedením:
src/front-components/delete-draft.tsx
A zde je příklad použití CommandOpenSidePanelPage k otevření aktuálního záznamu v postranním panelu na konkrétní záložce. tab je ID záložky rozvržení stránky (výchozí rozvržení používají ID jako company-tab-emails nebo company-tab-timeline; vlastní rozvržení používají vlastní ID záložky). Pokud ID v rozvržení záznamu neexistuje, místo toho se otevře výchozí záložka:
src/front-components/open-company-emails.tsx

Volání logické funkce

Front komponenty běží v prohlížeči v sandboxovaném Web Workeru uvnitř iframe s nejasným původem (opaque-origin), zatímco logické funkce běží na serveru. Neexistuje mezi nimi žádné přímé volání v rámci jednoho procesu — místo toho se front komponenta k logické funkci připojuje přes HTTP. Logická funkce deklarovaná pomocí httpRouteTriggerSettings je přes HTTP dostupná na své cestě (route path). RestApiClient považuje cesty začínající na /s/ za aplikační trasy, převede je na URL, ze které jsou vaše funkce poskytovány, a autentizuje je pomocí TWENTY_APP_ACCESS_TOKEN.
V Twenty Cloud jsou logické funkce spouštěné přes HTTP poskytovány na vyhrazené doméně pro každý workspace na adrese https://\<your-workspace-subdomain>.withtwenty.com\<path>. Pro externí volající zkopírujte přesnou URL z nastavení funkce HTTP trigger nebo z karty Settings aplikace.
Headless front komponenta může volání spustit při mountu přes komponentu Command a poté se automaticky odmountovat:
src/front-components/sync-prs.tsx
Cesta předaná RestApiClient je httpRouteTriggerSettings.path logické funkce s předponou /s. Ponechte isAuthRequired: true; TWENTY_APP_ACCESS_TOKEN, který Twenty vygeneruje pro vaši komponentu, požadavek autentizuje:
src/logic-functions/fetch-prs.logic-function.ts
TWENTY_APP_ACCESS_TOKEN je vložen automaticky — viz Proměnné aplikace. Protože tajné proměnné aplikace nejsou nikdy vystaveny front komponentám, ponechte API klíče a další citlivou logiku v logické funkci, ne ve front komponentě.

Volání Twenty REST API

Pro volání aplikačních HTTP tras nebo čtení a zápis záznamů Twenty z front komponenty použijte RestApiClient z twenty-client-sdk/rest. Odesílá cesty /s/... na základní URL funkcí vašeho workspace a všechny ostatní cesty, včetně /rest/..., na TWENTY_API_URL. options přijímá headers, query (záznam parametrů dotazovacího řetězce; hodnoty typu nullish jsou vynechány) a AbortSignal prostřednictvím signal. Objekt body, který není typu FormData, je automaticky serializován do JSON. Při 401 klient jednou obnoví přístupový token prostřednictvím hostitele a požadavek znovu odešle. Základní URL a token jsou ve výchozím nastavení odvozeny z prostředí. Podle potřeby předávejte konstruktoru přepsané hodnoty — například v testech:
Neúspěšné požadavky vyvolají RestApiClientError, který zpřístupňuje status, statusText, url a parsované body:

Přístup k běhovému kontextu

Uvnitř komponenty použijte hooky SDK pro přístup k aktuálnímu uživateli, záznamu a instanci komponenty:
src/front-components/record-info.tsx
Dostupné hooky:

Aplikační proměnné

Aplikační proměnné definované v defineApplication() s isSecret: false jsou k dispozici ve front-endových komponentách prostřednictvím pomocné funkce getApplicationVariable:
src/front-components/greeting.tsx
Tajné proměnné (isSecret: true) nejsou zpřístupněny front-endovým komponentám. Jsou k dispozici pouze v logických funkcích, které běží na straně serveru. Tím se zabrání odesílání citlivých hodnot, jako jsou API klíče, do prohlížeče.
getApplicationVariable vždy vrací string (nebo undefined), bez ohledu na deklarovaný type proměnné. Řetězec je serializován konzistentně podle typu (logické hodnoty jako "true" / "false", čísla jako desetinné řetězce, pole / objekty jako JSON), ve stejném formátu, jaký používá process.env v logických funkcích — zpracujte jej sami (Number(...), JSON.parse(...), === 'true'). Viz Typy proměnných. Následující systémové proměnné jsou vždy dostupné přes process.env:

TWENTY_FUNCTIONS_URL

Twenty také vkládá TWENTY_FUNCTIONS_URL do front komponent a logických funkcí: základní URL, ze které jsou poskytovány vaše logické funkce spouštěné přes HTTP. Existuje proto, že tato URL není vždy samotný server Twenty. V Twenty Cloud jsou aplikační trasy poskytovány na vyhrazené doméně pro každý workspace (https://\<your-workspace-subdomain>.withtwenty.com, nebo hlavní veřejná doména aplikace, pokud je nakonfigurována), aby odpovědi vytvořené aplikací běžely na odděleném původu, a nikoli na původu aplikace Twenty. Self-hostované a lokální instance poskytují aplikační trasy pod předponou /s přímo na serveru a proměnnou nemusí vůbec nastavovat. Protože se základní URL liší podle workspace a instance, váš kód ji nemůže napevno zakódovat — server správnou hodnotu vloží za běhu. Jen zřídka ji potřebujete číst přímo. Volání svých tras provádějte přes RestApiClient s cestou s předponou /s/ a klient za vás URL vyřeší: odstraní předponu /s a zacílí na TWENTY_FUNCTIONS_URL, přičemž pokud proměnná není nastavena, použije jako zálohu \<TWENTY_API_URL>/s. Použijte resolveUrl('/s/\<path>') pro získání absolutní URL bez odeslání požadavku, např. pro odkaz. Proměnnou čtěte přímo pouze při ručním sestavování URL:

API komunikace s hostitelem

Frontendové komponenty mohou pomocí funkcí z twenty-sdk vyvolávat navigaci, modály a oznámení: Zde je příklad, který používá hostitelské API k zobrazení snackbaru a zavření postranního panelu po dokončení akce:
src/front-components/archive-record.tsx

Práce s více záznamy

Použijte useSelectedRecordIds() pro zpracování více vybraných záznamů. To je užitečné pro hromadné operace:
src/front-components/bulk-export.tsx
Zobrazte ji pomocí položky příkazové nabídky omezené na výběry záznamů:
src/command-menu-items/bulk-export.command-menu-item.ts

Veřejné soubory

Frontendové komponenty mohou přistupovat k souborům ze složky aplikace public/ pomocí getPublicAssetUrl:
Podrobnosti viz sekci veřejných souborů.

Stylování

Frontendové komponenty podporují více přístupů ke stylování. Můžete použít:
  • Inline stylystyle={{ color: 'red' }}
  • Komponenty Twenty UI — vlastní knihovna komponent Twenty; podívejte se níže na Používání komponent Twenty UI
  • Emotion — CSS-in-JS s @emotion/react
  • Styled-components — vzory styled.div
  • Tailwind CSS — utilitní třídy
  • Jakákoli CSS-in-JS knihovna kompatibilní s Reactem

Používání komponent Twenty UI

Twenty dodává svou knihovnu komponent jako balíček twenty-ui. Frontendové komponenty jej mohou používat pro tlačítka, tagy, stavové štítky, čipy, avatary, ikony, typografii a tokeny motivu, které se automaticky přizpůsobují světlému a tmavému motivu pracovního prostoru.

Instalace

Přidejte balíček do své aplikace, připnutý k verzi, se kterou je dodána vaše instance Twenty:
twenty-ui je zabalen do vaší frontendové komponenty při sestavení, takže stačí, aby byl závislostí vaší aplikace — za běhu není třeba nic konfigurovat.

Import komponent

Importujte z odpovídající podcesty místo z kořene balíčku, aby se do vašeho bundlu dostaly jen komponenty, které používáte:

Ikony

Importujte jednotlivé ikony z twenty-ui/icon:
Každá pojmenovaná ikona je odstraňována tree-shakingem, takže import několika málo ikon přidá do vašeho bundlu jen minimum navíc. Vyhněte se IconsProvider, useIcons a iconsState — natáhnou celou sadu ikon Tabler (několik MB).

Témování a tokeny motivu

Komponenty Twenty UI se automaticky přizpůsobí světlému a tmavému motivu pracovního prostoru — renderer použije na hostiteli aktivní barevné schéma a komponenty podle něj odvodí své barvy. Chcete-li ve svých vlastních inline stylech používat stejné design tokeny, zavolejte hook useTheme(). Vrací tokeny motivu Twenty (odsazení, barvy, poloměry, písma) napojené na aktivní motiv, aniž by bylo potřeba v komponentě nastavovat ThemeProvider:
Protože useTheme() je hook, čtete tokeny uvnitř těla komponenty, takže hodnoty vždy odrážejí aktuální motiv. Stejná mapa tokenů je také exportována jako konstanta themeCssVariables, ale ve frontendových komponentách preferujte useTheme() — modulová konstanta, která dereferencuje themeCssVariables, může být během extrakce manifestu aplikace nedefinovaná. Chcete-li se explicitně větvit podle aktivního schématu, načtěte jej pomocí useColorScheme() z twenty-sdk/front-component, která vrací 'light' nebo 'dark'.

Aktuální omezení

Komponenty Front jsou aktivně vyvíjeny. Renderování, stylování a obsluha událostí fungují dobře. Cokoli, co sahá mimo samotné renderování (měření prvku, volání metody DOM na refu, vytváření portálu mimo váš strom, práce s úložištěm prohlížeče), dnes chybí nebo je nekompletní a většinou to selhává tiše: bez výjimky a bez chyby TypeScriptu, protože kostra je typovaná proti plnému DOMu prohlížeče. Pokud vás něco z toho blokuje, vytvořte issue, aby to bylo upřednostněno.

Rozvržení a měření

Zatím se nic nemůže samo změřit. Takže recharts ResponsiveContainer, Floating UI / Popper, virtualizace seznamů a táhnutí pro změnu velikosti zatím nefungují. Místo toho dělejte rozvržení v CSS: váš stylesheet se dostane ke skutečné stránce, takže flexbox, grid, aspect-ratio, clamp() i @container se chovají normálně.
requestAnimationFrame, fetch, setTimeout a queueMicrotask fungují bez prefixu window.. Pouze window.requestAnimationFrame(...) a podobné volání vyvolají výjimku.

Přístup k DOMu

ref vám dává sandboxový prvek, ne HTMLElement. Kvůli této mezeře v portálu popovery Radix, Headless UI, MUI a react-select ve výchozím stavu nic nevyrenderují. Většina z nich přijímá prop pro kontejner; nasměrujte ho na prvek, který jste vyrenderovali.

Události

Události myši, ukazatele, dotyku, tažení, klávesnice, fokusu, input/change/submit, scroll/wheel/contextmenu a animationend/transitionend přecházejí do hostitele, plus několik specifických pro prvek: load/error na <img>, schránka a kompozice na <input>/\<textarea>, média na \<video>/\<audio>, toggle na \<details>/\<dialog>. Cokoli dalšího (onAuxClick, onSelect, onInvalid, onReset, onAnimationStart, zachycení ukazatele (pointer capture), onLoad na <img>) je bez varování zahazováno. document.addEventListener() a window.addEventListener() se zaregistrují bez chyby, ale nikdy se nespustí, což je důvod, proč se přetažení zastaví, jakmile ukazatel opustí prvek, na kterém začalo. Ani event.preventDefault() se nepřenáší; odeslání formuláře, dragover/drop a kliknutí na odkazy jsou už za vás ošetřené.

Atributy a stylování

Každý prvek předává své vlastní vlastnosti do hostitelského DOMu (href na \<a>, src/alt na <img>, value/placeholder/disabled na <input> atd.) plus společnou sadu na každém prvku: id, className, style, title, tabIndex, role, draggable a jakýkoli atribut aria-* / data-* (s pomlčkou, takže ariaLabel je zahozena). Cokoli mimo tento rozsah je tiše zahazováno, takže vlastní stav vyjádřete jako data-*. Komponentové CSS, ať už z import './styles.css', CSS-in-JS nebo z prvku \<style>, je do \<head> hostitelské stránky vkládáno bez ohraničení (unscoped). Třídy se tak střetávají s vlastními třídami Twenty (přidávejte jim předpony a nikdy nepište holé div { ... } selektory) a @media se vztahuje k oknu prohlížeče, nikoli k vašemu widgetu (použijte @container s vlastním container-type). Inline propy style nejsou ovlivněny.

Úložiště a síť

localStorage, sessionStorage, IndexedDB, cookies, Cache API a BroadcastChannel nejsou vůbec k dispozici, protože komponenta běží ve workeru na neprůhledném (opaque) původu. Pro uchování stavu zavolejte logickou funkci a použijte její úložiště klíč–hodnota. fetch funguje, s výhradami:
  • Volání na Twenty API a trasy (routes) vaší aplikace jsou proxyována hostitelem, proto upřednostněte RestApiClient. U proxyovaných volání jsou AbortSignal a ostatní volby RequestInit zahazovány a jsou podporována pouze těla typu string a URLSearchParams.
  • Jiné původy opouštějí sandbox s Origin: null, takže API třetí strany odpoví jen tehdy, pokud posílá Access-Control-Allow-Origin: *. Místo toho jej volejte z logické funkce.
  • fetch('/rest/people') se nikdy nespáruje s Twenty API, protože sandbox nemá URL stránky, podle které by vyhodnotil relativní cestu.

Další omezení

  • Obsah souborů. <input type="file"> poskytuje vašemu handleru pouze metadata souboru, nikoli samotné bajty, takže FileReader a nahrávání zatím nejsou možná.
  • Payloady drag-and-drop. Události tažení se spouštějí, ale event.dataTransfer je undefined.
  • Vestavěné moduly Node. fs, path a node:crypto způsobí chybu při sestavení, takže tuto práci přesuňte do logické funkce. Web Crypto, fetch, TextEncoder a URL jsou k dispozici.
  • \<iframe> je vždy znovu zasandboxováno bez allow-same-origin, takže vložený obsah spoléhající se na vlastní relaci se vykreslí jako odhlášený. Nemá ani onLoad.