İçeriğe geç
Docs

Alanlar

Tecof Studio alan başvurusu — yerleşik tipler, çok dilli metin, zengin metin, medya, bağlantı, renk, kod, ikon, tekrar eden satırlar, harici/API kaynakları, CMS ve e-ticaret seçicileri; saklanan değer biçimleri.

Son güncelleme:

Alanlar Inspector’da düzenlenen prop’ları tanımlar. Yerleşik tipler doğrudan { type: "…" } olarak yazılır; gelişmiş alanlar create*Field() fabrikalarıyla üretilir. Fabrikalar { type: "custom", _fieldType, label, render } döndürür, yani Puck uyumlu config’e doğrudan konur.

Yerleşik tipler#

typeDeğerÖnemli seçenekler
text, textareastringbindable (CMS bağlama düğmesi, varsayılan true), readOnly
select, radioseçilen valueoptions: [{ label, value }]
numbernumber—
boolean, togglebooleanonLabel, offLabel (varsayılan Açık / Kapalı)
rangenumbermin (0), max (100), step (1), unit (ör. "px")
arrayobject[]arrayFields
objectobjectobjectFields
slot(zone)orientation, repeatSource, itemSchema — bkz. Slotlar

Her alan label, defaultValue ve readOnly alabilir. Dinamik salt okunurluk resolveData().readOnly ile gelir; ikisi VEYA’lanır.

Ortak seçenekler#

Tüm fabrikalar şunları destekler:

SeçenekTipAçıklama
labelstringInspector etiketi
labelIconReactElementEtiketin yanındaki ikon (ör. <Globe size={16} />)
visiblebooleanAlanın panelde görünüp görünmediği

LanguageField — çok dilli metin#

Merchant dillerini merchant-info’dan alır ve dil başına değer tutar. Studio içinde üst çubuktaki global dil kullanılır ve alan içindeki dil sekmeleri gizlenir; Studio dışında sekmeli moda döner.

TSX
import { createLanguageField } from "@tecof/theme-editor";

fields: {
  title: createLanguageField({ label: "Başlık" }),
  description: createLanguageField({ label: "Açıklama", isTextarea: true, textareaRows: 4 }),
  html: createLanguageField({ label: "HTML içerik", isHtml: true }),
}
SeçenekVarsayılanAçıklama
isTextareafalseÇok satırlı giriş
textareaRows3Satır sayısı
placeholder''Yer tutucu
isHtmlfalseÇeviride HTML etiketlerini korur

Değer: [{ code: "tr", value: "Merhaba" }, { code: "en", value: "Hello" }]. Temada aktif dilin değerini seçin; değer yoksa varsayılan dile düşmek temanın sorumluluğundadır:

TypeScript
const pick = (value: Array<{ code: string; value: string }> | string | undefined, locale: string, fallback = "tr") =>
  typeof value === "string" ? value : (value?.find((v) => v.code === locale)?.value || value?.find((v) => v.code === fallback)?.value || "");

Dil araçları: Birden fazla dil varken alan üstünde Hızlı Doldur (aktif sekmedeki metni tüm dillere kopyalar) ve Çevir (API üzerinden diğer dillere çevirir; isHtml ile etiketler korunur) düğmeleri görünür. Aynı çubuk EditorField, LinkField (yalnız Hızlı Doldur, yalnız boş diller) ve LocalizedUploadField’da da vardır.

Kendi çok dilli alanınızda aynı araçları kullanabilirsiniz:

TSX
import {
  LanguageToolsBar, useLanguageToolsStatus, normalizeLocalizedValues, fillLanguages,
  translateLanguages, isEmptyText, LANGUAGE_TOOL_MESSAGES,
} from "@tecof/theme-editor";

const { status, translating, setTranslating, flash } = useLanguageToolsStatus();
const values = normalizeLocalizedValues<string>(value, languages, () => "");

const fill = () => {
  const res = fillLanguages(values, languages, activeTab, { isEmpty: isEmptyText });
  if (!res) return; // kaynak boş ya da doldurulacak dil yok
  onChange(res.values);
  flash({ text: LANGUAGE_TOOL_MESSAGES.filledAll, type: "success" }, 2000);
};

const translate = async () => {
  setTranslating(true);
  try {
    const out = await translateLanguages({
      values: () => valuesRef.current, // getter: yanıt gelene kadar yazılanlar korunur
      languages,
      sourceCode: activeTab,
      translate: apiClient.translate.bind(apiClient),
    });
    if (out.ok) onChange(out.values);
    flash(out.ok ? { text: LANGUAGE_TOOL_MESSAGES.translated, type: "success" } : { text: out.message, type: "error" }, 3000);
  } finally {
    setTranslating(false);
  }
};

<LanguageToolsBar onFill={fill} onTranslate={translate} translating={translating} status={status} />;

fillLanguages(values, languages, sourceCode, { onlyEmpty, isEmpty, clone }) → { values, filled } | null. onlyEmpty: true dolu dilleri ezmez ve listede olmayan dil kodlarını korur. Boşluk tanımları: isEmptyText, isEmptyHtml (medya etiketi dolu sayılır), isEmptyLink.

EditorField — zengin metin#

TipTap tabanlı, çok dilli WYSIWYG editör (kalın, italik, bağlantı, liste, başlık…). Hızlı Doldur boş <p></p> içerikte pasiftir; Çevir yalnız TecofProvider varken görünür. Araçlar aktif dilin içeriğini değiştirmediği için imleç korunur.

TSX
fields: { content: createEditorField({ label: "İçerik" }) }

UploadField — medya#

Seçili medya kare önizleme kutuları olarak görünür; “ekle” kutusu sekmeli bir medya penceresi açar: Kütüphane (sunucudaki dosyalar), Yükle (FilePond), Referans (CMS değişkeni, {{ data.alan }}). Doka görsel düzenleyici (kırp, döndür, ayarlar), istemci tarafı WebP sıkıştırma ve odak noktası seçimi içerir.

TSX
fields: {
  gallery: createUploadField({
    label: "Ürün galerisi",
    allowMultiple: true,
    maxFiles: 10,
    maxFileSize: "50MB",
    acceptedTypes: ["image/*"],
    folder: "/products",
  }),
  document: createUploadField({ label: "Katalog", allowMultiple: false, acceptedTypes: ["application/pdf"] }),
}
SeçenekVarsayılanAçıklama
allowMultipletrueÇoklu dosya
maxFiles100En çok dosya
maxFileSize"100MB"Tek dosya sınırı
maxTotalFileSize"200MB"Toplam sınır
acceptedTypestümüMIME türleri (["image/*"], ["application/pdf"])
folder"/"Yükleme klasörü
allowReordertrueSürükleyerek sıralama
imageCompressionEnabledtrueYüklemeden önce sıkıştırma
imageCompressionOptions{ maxSizeMB: 1, maxWidthOrHeight: 1920 }Sıkıştırma ayarları (fileType de verilebilir)
imagePreviewHeight—FilePond önizleme yüksekliği
showUploadedFilesfalseKullanımdan kalktı; yeni arayüzde etkisiz

Değer: UploadedFile[] — { _id, name, size, type, mimeType?, url?, folder?, provider?, focalPoint?, meta? }. Görseli temada TecofPicture ile basın (Yayın render’ı).

Odak noktası: görsel kutusundaki nişangâh düğmesi odak penceresini açar (tıkla/sürükle, ok tuşlarıyla 1’er, Shift ile 10’ar adım; 16:9, 1:1, 9:16 kırpma önizlemeleri). Seçim dosyanın içine focalPoint: { x, y } (yüzde, 50/50 merkez) olarak yazılır; TecofPicture bunu object-position olarak uygular. Backend’de şema değişikliği gerekmez.

LocalizedUploadField — dile göre medya#

UploadField’ın dil sekmeli sürümü; seçenekleri aynıdır. Her dil için ayrı dosya listesi tutar.

TSX
fields: {
  banner: createLocalizedUploadField({ label: "Kampanya görseli", allowMultiple: false }),
}
defaultProps: { banner: [] }

Değer: [{ code: "tr", value: UploadedFile[] }, { code: "en", value: [] }]. Yayında istenen dil boşsa varsayılan dile, o da boşsa ilk dolu dile düşülür:

TSX
import { resolveLocalizedUploadFile, TecofPicture } from "@tecof/theme-editor";

const file = resolveLocalizedUploadFile(props.banner, locale, "tr"); // UploadedFile | null
return file ? <TecofPicture data={file} alt="" /> : null;

Çoklu dosya için resolveLocalizedUpload(value, locale, defaultLanguage) → UploadedFile[]. Eski düz UploadedFile[] kayıtlar varsayılan dil altında açılır ve ilk düzenlemede yeni biçimde kaydedilir. Hızlı Doldur yalnız boş dillere kopyalar; Çevir yoktur. Ters yön (yerelleştirilmiş alanı tekrar createUploadField’a çevirmek) desteklenmez.

LinkField — sayfa veya URL#

Mağaza sayfalarından seçim (aranabilir pencere, yayın durumu göstergesi) ya da elle URL girişi. Dil sekmelidir; değer dil başına saklanır.

TSX
fields: {
  cta: createLinkField({ label: "CTA bağlantısı", showTarget: true, placeholder: "https://…" }),
}
SeçenekVarsayılanAçıklama
showTargettrueAynı / yeni sekme seçimi
placeholderhttps://...URL yer tutucusu

Değer: [{ code: "tr", value: LinkFieldValue }, …]; LinkFieldValue = { url, label?, target?: "_self" | "_blank", type?: "page" | "custom" | "category" | "brand" | "product" | "cms" }. Render yalnız url ve target okur. Eski düz LinkFieldValue kayıtlar mağaza dillerine göre normalize edilir.

ColorField — renk#

Popover renk seçici: doygunluk/parlaklık alanı, ton ve opaklık kaydırıcıları, HEX/RGB/HSL girdileri, ekrandan renk seçme (EyeDropper), canlı tema paleti, 22 tonluk Tailwind paleti, son kullanılanlar ve WCAG kontrast önizlemesi. Tam klavye erişimi vardır.

TSX
fields: {
  bgColor: createColorField({ label: "Arka plan" }),
  overlay: createColorField({ label: "Kaplama", showOpacity: true, defaultColor: "#00000080" }),
  accent: createColorField({ label: "Vurgu", themeVars: true }),
  brand: createColorField({ label: "Marka", swatches: ["#18181b", "#74b500", "#ffffff"], palette: false }),
}
SeçenekVarsayılanAçıklama
showOpacityfalseOpaklık; değer #rrggbbaa
defaultColor''Sıfırlama hedefi
placeholder'#000000'HEX girdisi yer tutucusu
showResettrueDeğer varsayılandan farklıyken sıfırla düğmesi
swatches[]“Hızlı seçim” satırı
themeColorstrueCanlı tema renkleri (yalnız Studio içinde)
themeVarsfalseTema rengini var(--theme-color-<anahtar>) olarak bağlar; kapalıyken açık paletteki hex kopyalanır
palettetrueTailwind paleti
contrasttrueKontrast çipleri (WCAG AA/AAA)
contrastAgainst—Kontrast zemini (hex)

Değer sözleşmesi: '', #rrggbb, #rrggbbaa (yalnız showOpacity) ya da var(--theme-color-*) (yalnız themeVars). rgb()/hsl()/transparent girdileri hex’e çevrilir; tanınmayan değerler korunur. Hex matematiği yapan tema kodu var() beklememelidir. Saf yardımcılar da dışa aktarılır: parseColor, formatHex, normalizeColorValue, classifyColorValue, contrastRatio, wcagLevel, themeColorVar, parseThemeColorVar, THEME_COLOR_KEYS, toThemeCssKey.

CodeEditorField — kod#

Monaco tabanlı kod editörü (ilk kullanımda tembel yüklenir).

TSX
fields: {
  customHtml: createCodeEditorField({ label: "Özel kod", defaultLanguage: "html" }),
  json: createCodeEditorField({ label: "Yapılandırma", defaultLanguage: "json", height: "240px" }),
}

Seçenekler: defaultLanguage, height, theme.

RepeaterField — tekrar eden satırlar#

Alt alan setini (subFields) satırlar hâlinde düzenler: ekle, sil, çoğalt, sürükleyerek sırala, satırı katla.

TSX
fields: {
  items: createRepeaterField({
    label: "Öğeler",
    subFields: {
      title: createLanguageField({ label: "Başlık" }),
      link: createLinkField({ label: "Bağlantı" }),
    },
    minItems: 1,
    maxItems: 6,
    defaultRow: { title: "", link: null },
  }),
}

Değer: Record<string, unknown>[]. Bir slot’un repeatSource’u olarak kullanılabilir (Repeat zone).

IconField — ikon#

lucide-react setinden aranabilir ikon seçici (arama yoksa ilk 120 ikon). Değer ikonun adıdır ("ShoppingCart"); bileşeni render’da siz çözersiniz:

TSX
import { icons } from "lucide-react";

const Icon = icons[props.icon as keyof typeof icons];
return Icon ? <Icon className="size-5" /> : null;

ExternalField — harici tek kayıt#

Kendi API’nizden aranabilir bir pencerede tek kayıt seçtirir.

TSX
fields: {
  product: createExternalField({
    label: "Ürün",
    fetchList: async ({ query }) => (await fetch(`/api/products?q=${query ?? ""}`)).json(),
    mapProp: (row) => ({ id: row.id, name: row.name }),
    mapRow: (row) => ({ Ad: row.name, SKU: row.sku }),
    getItemSummary: (value) => value?.name ?? "",
    placeholder: "Ürün seçin",
  }),
}
SeçenekAçıklama
fetchList({ query?, filters? })Zorunlu. Satırları getirir
mapProp(row)Prop’a yazılacak değer (varsayılan satırın kendisi)
mapRow(row)Pencere tablosunun sütunları
getItemSummary(value)Seçili değerin etiketi
showSearchArama kutusu (varsayılan true)

ApiListField — API’den liste#

Kendi API’nizden liste akıtır; bir slot’un repeatSource’una bağlanınca öğe şablonu her satır için tekrarlanır.

TSX
fields: {
  source: createApiListField({
    label: "Ürünler",
    fetchList: async ({ query, limit }) => (await fetch(`/api/products?q=${query ?? ""}&limit=${limit ?? 8}`)).json(),
    itemSchema: [{ key: "name", label: "Ad", type: "text" }, { key: "image", label: "Görsel", type: "image" }],
    defaultLimit: 8,
  }),
  card: { type: "slot", orientation: "horizontal", repeatSource: "source" },
}

Değer: { query?: string; limit?: number } — dokümana yalnız sorgu parametreleri yazılır; satırlar render anında çözülür (oturum içi önbellek, eşzamanlı istek tekleme). Seçenekler: showQuery, showLimit (varsayılan true), defaultLimit.

E-ticaret seçicileri#

Mağazanın kendi kayıtlarından seçim yaptırır; slug ya da ObjectId elle yazdırmaz.

TSX
import {
  createCategoryField, createCategoryListField, createProductField, createProductListField,
  createBrandField, createBrandListField, createTagField, createTagListField,
  createAttributeField, createAttributeListField, createVariantTypeField, createVariantField,
  createFlashSaleField, createCampaignField, createDiscountField,
} from "@tecof/theme-editor";

fields: {
  category: createCategoryField({ label: "Kategori" }),
  featured: createProductField({ label: "Öne çıkan ürün" }),
  tags: createTagListField({ label: "Etiketler", max: 6 }),
  coupon: createDiscountField({ label: "Kupon" }),
}

Saklanan değer seçim anında donan bir anlık görüntüdür; yayın ek istek atmadan render edebilir:

AlanDeğer
createCategoryField{ id, slug, name, path }
createProductField{ id, slug, name, image }
createBrandField{ id, slug, name, image }
createTagField{ id, slug, name }
createAttributeField{ id, slug, name, type, isFilterable, options: [{ value, label, colorCode }] }
createVariantTypeField{ id, name, selectionType, values: [{ label, colorCode }] }
createVariantField{ productId, productSlug, productName, variantId, sku, title, price }
createFlashSaleField{ id, name, startDate, endDate, showCountdown, bannerText, highlightColor, discountType, discountValue }
createCampaignField{ id, name, status, startDate, endDate, discountCode }
createDiscountField{ id, code, title, type, value, minCartAmount, maxDiscountAmount, startDate, endDate }
  • *ListField varyantlarında değer bu nesnelerin dizisidir; editördeki sıra korunur. Seçenekler: label, placeholder, visible, listelerde max.
  • Neden hem id hem slug: ürün listeleme ucu markayı ve etiketi ObjectId, kategoriyi slug ile filtreler. Temada filtreyi kimlikle gönderin: getProducts({ brand: props.brand?.id, tag: props.tag?.id }).
  • Veri kaynağı: kategori/ürün/marka vitrin uçlarından (/api/store/ecommerce/*); etiket, özellik, varyant tipi, flaş satış, kampanya ve kupon panel uçlarından (/api/merchant/ecommerce/*) gelir. Panel uçları JWT’ye ek olarak x-merchant-id ister; TecofApiClient bunu merchant-info’dan bir kez çözer. Kimlik çözülemezse liste boş değil hata gösterir.
  • Anlık görüntü donar: markanın adı ya da kampanyanın tarihi panelde sonradan değişirse sayfa eski değeri gösterir; alanı yeniden seçin.
  • Boş durum: liste boşsa “Panelde ürün ekle” gibi bir bağlantı, doluysa başlıkta “Panelde yönet” görünür; adresler panelUrl üzerine kurulur ve yeni sekmede açılır. Panelde kayıt ekledikten sonra seçicideki Yenile listeyi tazeler.
  • Geriye uyum: var olan bir metin alanının yerine koyarken eski prop’u okumaya devam edin: props.brand?.id ?? props.brandIdText ?? "".

CmsCollectionField — koleksiyon bağlama#

Bir bileşeni CMS koleksiyonuna bağlar: koleksiyon, adet ve sıralama seçilir; bileşenin veri yuvaları koleksiyon alanlarına eşlenir.

TSX
fields: {
  source: createCmsCollectionField({
    label: "Veri kaynağı",
    defaultLimit: 6,
    slots: {
      title: { label: "Başlık", fieldTypes: ["text"] },
      image: { label: "Görsel", fieldTypes: ["image"] },
      link: { label: "Bağlantı" },
    },
  }),
}
SeçenekVarsayılanAçıklama
defaultLimit10Varsayılan öğe sayısı
showLimit, showSorttrueAdet ve sıralama (özel / yeni → eski / eski → yeni) kontrolleri
slots—Bileşenin ihtiyaç duyduğu veri yuvaları

Değer: { collectionSlug, collectionName?, limit?, sort?: "newest" | "oldest" | "custom", fieldMap?: Record<yuva, cmsAlanKısaKodu> }. Bir repeat slot’a kaynak olarak bağlanırsa satırlar koleksiyondan otomatik çekilir (ağaçta TecofProvider gerekir).

CMS veri bağlama (metin alanları)#

text ve textarea alanlarının yanında bir { } düğmesi görünür; koleksiyon → alan seçilince alana {{ data.<kısaKod> }} token’ı eklenir. Yayında token, TecofRender’a verilen cmsData kaydıyla çözülür. Kapatmak için bindable: false:

TSX
fields: {
  title: { type: "text", label: "Başlık" },                         // bağlama açık
  staticLabel: { type: "text", label: "Sabit etiket", bindable: false },
}

Repeat zone şablonunun içindeyken aynı düğme Öğe alanları bölümünü de gösterir ({{ item.<alan> }}).

Hata yalıtımı#

Fabrikaların ürettiği alanlar FieldErrorBoundary içinde render edilir; bir alanın hatası Inspector’ı düşürmez. Kendi render alanlarınızda da aynı bileşeni kullanabilirsiniz.