İçindekiler

Tier 1 kontrolleri, native HTML form elemanlarına doğrudan eşlenir. github.com/zatrano/goui/forms içinde yaşarlar ve iki gömülü yardımcı türü paylaşırlar:

import "github.com/zatrano/goui/forms"
  • forms.CommonAttrs — neredeyse her kontrolün kabul ettiği öznitelikler: ID, Class, Title, TabIndex *int, Spellcheck *bool, Draggable *bool, AriaLabel, AriaDescribedBy, Autocomplete, Disabled, ReadOnly, Required, Autofocus, Name.
  • forms.FieldValidationRules []validation.Rule ve Errors []string; tam doğrulama hikayesi için 06-validation.md'ye bakın. Bunu gömmek, alan üzerinde Validate() bool verir.

Her Tier 1 alanı, forms.ValidateAll'un ve genel form-bağlama kodunun dayandığı, paylaşılan forms.FieldValue sözleşmesini de (Name() string, RawValue() string, SetRawValue(string)) uygular.

Aşağıdaki tüm örnekler şunu varsayar:

import (
    "github.com/zatrano/goui/core"
    "github.com/zatrano/goui/forms"
)

TextInput

type=text|password|email|search|tel|url'i kapsar.

type TextInput struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Type          string
    Value         string
    Placeholder   string
    MinLength     int
    MaxLength     int
    Pattern       string
    Size          int
    Multiple      bool // email
    List          string
    EventName     string // g-change / g-input olay adı (varsayılan Name)
    DebounceMS    int
    OnChange      func(newValue string)
    ShowCharCount bool
    ShowStrength  bool   // parola gücü göstergesi (sunucu tarafı)
    HelperText    string // alanın altında ipucu
}
email := forms.TextInput{
    CommonAttrs: forms.CommonAttrs{Name: "email", ID: "email", Required: true},
    Type:        "email",
    Placeholder: "[email protected]",
}
html, err := email.Render()
<input id="email" name="email" required type="email"
       value="" placeholder="[email protected]"
       g-change="email" g-input="email">

ShowCharCount/ShowStrength, girdinin altında ekstra <p>/<div> meta verisi render eder (bkz. forms.fieldMetaHTML); ShowStrength yalnızca Type == "password" olduğunda etkili olur.


NumericInput

type=number|range'i kapsar.

type NumericInput struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Type      string
    Value     string
    Min       string
    Max       string
    Step      string
    EventName string
    OnChange  func(newValue string)
}
qty := forms.NumericInput{
    CommonAttrs: forms.CommonAttrs{Name: "qty", ID: "qty"},
    Type:        "number",
    Min:         "0",
    Max:         "10",
    Step:        "1",
}
<input id="qty" name="qty" type="number" value="" min="0" max="10" step="1"
       g-change="qty" g-input="qty">

DateTimeInput

type=date|time|datetime-local|month|week'i kapsar.

type DateTimeInput struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Type      string
    Value     string
    Min       string
    Max       string
    Step      string
    EventName string
    OnChange  func(newValue string)
}
birthday := forms.DateTimeInput{
    CommonAttrs: forms.CommonAttrs{Name: "birthday", ID: "birthday"},
    Type:        "date",
}
<input id="birthday" name="birthday" type="date" value="" g-change="birthday">

ChoiceInput (checkbox / radio)

type=checkbox|radio'yu kapsar. CheckboxInput ve RadioInput, aynı struct'ın tür takma adlarıdır (type alias) — çağrı noktasında sadece okunabilirlik için.

type ChoiceInput struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Type      string // checkbox | radio
    Value     string // işaretlendiğinde gönderilen değer
    Checked   bool
    EventName string
    LabelText string // isteğe bağlı bitişik etiket metni
    OnChange  func(checked bool, value string)
}

type CheckboxInput = ChoiceInput
type RadioInput = ChoiceInput
subscribe := forms.ChoiceInput{
    CommonAttrs: forms.CommonAttrs{Name: "subscribe", ID: "subscribe"},
    Type:        "checkbox",
    Value:       "yes",
    LabelText:   "Subscribe to newsletter",
}
<input id="subscribe" name="subscribe" type="checkbox" value="yes" g-change="subscribe">
<span>Subscribe to newsletter</span>

FileInput

type=file'ı kapsar. Sadece son seçilen dosya adı/adlarını istemci tarafında takip eder; gerçek ikili yükleme ayrı bir akıştır (bkz. 07-forms-tier2.md'deki forms.DragDropUpload / forms.AvatarUpload ve upload paketi).

type FileInput struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Accept    string
    Capture   string
    Multiple  bool
    EventName string
    OnChange  func(fileNames string)
    Value     string // görüntüleme/durum için son seçilen dosya adı/adları
}
avatar := forms.FileInput{
    CommonAttrs: forms.CommonAttrs{Name: "avatar", ID: "avatar"},
    Accept:      "image/*",
}
<input id="avatar" name="avatar" type="file" accept="image/*" g-change="avatar">

ColorInput

type=color'ı kapsar. Doğrulanmaz (FieldValidation gömülü değildir); Value boş olduğunda #000000'a varsayılan olur.

type ColorInput struct {
    core.BaseComponent
    CommonAttrs
    Value     string
    EventName string
    OnChange  func(newValue string)
}
brand := forms.ColorInput{
    CommonAttrs: forms.CommonAttrs{Name: "brand", ID: "brand"},
    Value:       "#2563eb",
}
<input id="brand" name="brand" type="color" value="#2563eb" g-change="brand">

HiddenInput

type=hidden'ı kapsar. Doğrulama yok, olay yok (HandleEvent no-op'tur — gizli alanlar kullanıcı girdisinden değil, sunucu mantığından programatik olarak ayarlanır).

type HiddenInput struct {
    core.BaseComponent
    CommonAttrs
    Value string
}
csrf := forms.HiddenInput{
    CommonAttrs: forms.CommonAttrs{Name: "csrf_token"},
    Value:       "abc123",
}
<input name="csrf_token" type="hidden" value="abc123">

Textarea

type Textarea struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Value         string
    Placeholder   string
    Rows          int
    Cols          int
    Wrap          string
    MinLength     int
    MaxLength     int
    EventName     string
    DebounceMS    int
    OnChange      func(newValue string)
    ShowCharCount bool
    HelperText    string
}
message := forms.Textarea{
    CommonAttrs:   forms.CommonAttrs{Name: "message", ID: "message"},
    Rows:          4,
    MaxLength:     500,
    ShowCharCount: true,
}
<textarea id="message" name="message" rows="4" maxlength="500"
          g-change="message" g-input="message" g-debounce="100"></textarea>
<p class="goui-char-count text-sm">0 / 500</p>

Select, Option, Optgroup

type Option struct {
    Value    string
    Label    string
    Selected bool
    Disabled bool
}

type Optgroup struct {
    Label    string
    Disabled bool
    Options  []Option
}

type Select struct {
    core.BaseComponent
    CommonAttrs
    FieldValidation
    Value     string
    Multiple  bool
    Size      int
    Options   []Option
    Groups    []Optgroup
    EventName string
    OnChange  func(newValue string)
}
country := forms.Select{
    CommonAttrs: forms.CommonAttrs{Name: "country", ID: "country"},
    Options: []forms.Option{
        {Value: "", Label: "Select a country"},
        {Value: "tr", Label: "Türkiye"},
        {Value: "us", Label: "United States"},
    },
}
<select id="country" name="country" g-change="country">
  <option value="">Select a country</option>
  <option value="tr">Türkiye</option>
  <option value="us">United States</option>
</select>

Optgroup, üst seviye Options'tan sonra iç içe bir <optgroup label="..."> bloğu olarak render edilir. HandleEvent, gelen değerden her Option.Selected'ı (hem Options hem de her Optgroup.Options içinde) yeniden türetir, böylece struct sonraki render'lar için tutarlı kalır.


Button

type=submit|button|reset|image'i kapsar.

type Button struct {
    core.BaseComponent
    CommonAttrs
    Type      string
    Value     string
    Text      string
    Src       string // type=image
    Alt       string
    EventName string // g-click olayı
}
save := forms.Button{Type: "button", Text: "Save", EventName: "save"}
<button type="button" class="goui-button ..." g-click="save">Save</button>

Type == "image", bir <button> elemanı yerine <input type="image" src="..." alt="..."> render eder.


Form, Fieldset, Legend, Label

Kendine ait sunucu tarafı durumu olmayan yapısal konteynerler — öznitelik artı çağıranın sağladığı iç HTML'i (forms.JoinHTML ile birleştirilmiş) render ederler.

type Form struct {
    core.BaseComponent
    CommonAttrs
    Action    string
    Method    string
    EncType   string
    InnerHTML string
    OnSubmit  string // g-submit olay adı
}

type Fieldset struct {
    core.BaseComponent
    CommonAttrs
    InnerHTML string
}

type Legend struct {
    core.BaseComponent
    CommonAttrs
    Text string
}

type Label struct {
    core.BaseComponent
    CommonAttrs
    For  string
    Text string
}
nameLabel, _ := (&forms.Label{For: "name", Text: "Name"}).Render()
nameInput, _ := (&forms.TextInput{CommonAttrs: forms.CommonAttrs{Name: "name", ID: "name"}}).Render()

form := &forms.Form{
    Method:   "post",
    OnSubmit: "save",
    InnerHTML: forms.JoinHTML(
        `<div class="field">`, nameLabel, nameInput, `</div>`,
    ),
}
html, err := form.Render()
<form class="goui-form ..." method="post" g-submit="save">
  <div class="field">
    <label class="goui-label ..." for="name">Name</label>
    <input id="name" name="name" type="text" value="">
  </div>
</form>

g-submit, native form submit'ini yakalar (preventDefault) ve payload'ı her adlandırılmış form kontrolünden FormData aracılığıyla toplanmış {"fields": {...}} olan bir event frame'i gönderir — bunu uçtan uca nasıl bağlayacağınıza dair tam bir form örneği için 06-validation.md'ye bakın.


Datalist

Bir girdinin list="<id>" özniteliğini, native autocomplete önerileri için destekler.

type DatalistOption struct {
    Value string
    Label string
}

type Datalist struct {
    core.BaseComponent
    CommonAttrs
    Options []DatalistOption
}
cities := forms.Datalist{
    CommonAttrs: forms.CommonAttrs{ID: "cities"},
    Options: []forms.DatalistOption{
        {Value: "ist", Label: "İstanbul"},
        {Value: "ank", Label: "Ankara"},
    },
}
<datalist id="cities">
  <option value="ist" label="İstanbul">İstanbul</option>
  <option value="ank" label="Ankara">Ankara</option>
</datalist>
<input list="cities">

Output

Bir hesaplama sonucunu görüntüler (örn. for aracılığıyla bağlanmış <input>ları olan bir <form>'un sonucu).

type Output struct {
    core.BaseComponent
    CommonAttrs
    For   string
    Form  string
    Value string
    Text  string
}
summary := forms.Output{
    CommonAttrs: forms.CommonAttrs{Name: "summary"},
    Text:        "Name: Ada | Email: [email protected]",
}
<output name="summary">Name: Ada | Email: [email protected]</output>

Meter

Bilinen bir aralık içindeki skaler bir ölçümü temsil eder.

type Meter struct {
    core.BaseComponent
    CommonAttrs
    Value   float64
    Min     float64
    Max     float64
    Low     float64
    High    float64
    Optimum float64
}
disk := forms.Meter{Value: 0.6, Min: 0, Max: 1, High: 0.9, Optimum: 0.3}
<meter value="0.6" min="0" max="1" high="0.9" optimum="0.3"></meter>

Progress

Görev tamamlanmasını temsil eder. Max, sıfır değerinde bırakıldığında 1'e varsayılan olur.

type Progress struct {
    core.BaseComponent
    CommonAttrs
    Value float64
    Max   float64
}
upload := forms.Progress{Value: 0.42}
<progress value="0.42" max="1"></progress>

Hepsini bir araya getirme

Birkaç Tier 1 alanını kompoze eden minimal, kayıtlı bir bileşen:

type ContactForm struct {
    core.BaseComponent
    Name  forms.TextInput
    Email forms.TextInput
}

func NewContactForm() *ContactForm {
    return &ContactForm{
        Name:  forms.TextInput{CommonAttrs: forms.CommonAttrs{Name: "name", ID: "name"}},
        Email: forms.TextInput{CommonAttrs: forms.CommonAttrs{Name: "email", ID: "email"}, Type: "email"},
    }
}

func (c *ContactForm) Mount(_ context.Context) error   { return nil }
func (c *ContactForm) Unmount(_ context.Context) error { return nil }

func (c *ContactForm) HandleEvent(ctx context.Context, event string, payload map[string]any) error {
    switch event {
    case "name":
        return c.Name.HandleEvent(ctx, event, payload)
    case "email":
        return c.Email.HandleEvent(ctx, event, payload)
    }
    return nil
}

func (c *ContactForm) Render() (string, error) {
    nameHTML, _ := c.Name.Render()
    emailHTML, _ := c.Email.Render()
    return forms.JoinHTML(`<div class="field">`, nameHTML, `</div>`,
        `<div class="field">`, emailHTML, `</div>`), nil
}

Her Tier 1 alanının HandleEvent'i, "change"/"input"'a (uygunsa) ve kendi yapılandırılmış olay adına (EventName, varsayılan olarak Name) yanıt verir — dolayısıyla event string'ine göre yukarıdaki gibi doğrudan eşleşen alanın HandleEvent'ine dispatch etmek, ekstra bir bağlantı gerektirmeden çalışır. Doğrulama ve toast'larla tam, çalıştırılabilir bir versiyon için gönderilen examples/contact-form'a bakın.