package gallery import ( "fmt" "strings" "amadan.net/rastrillo/fiesta/example" ) // An example's page and its frame documents. frameFiles names the // documents and example writes them, under exactly those names, which // is how the tree gate, the frame count and the accessibility scan // learn of a new document the day a later task adds one here. // frameFiles is the paths, under the theme and locale directory, of // one example's frame documents: one per state, and for a grouped // example the row document its one widget frames as well. The // per-state documents of a grouped example are still written, because // the accessibility gate scans one state at a time. func (s *site) frameFiles(e example.Entry) []string { var out []string for _, st := range e.States { out = append(out, "frames/"+e.Name+"/"+st.ID+".html") } if grouped[e.Name] { out = append(out, "frames/"+e.Name+"/row.html") } return out } // stateView is one state on an example's page. type stateView struct { ID string Label string Note string Warning *warning Preview previewView Routes string } // groupNote is one state under a grouped widget: its id, its label on // a line of its own, and its note where the prose has one. The two // are separate elements rather than one string joined with ": ", // because that joiner is copy no review saw, and an ASCII colon is // wrong punctuation in ja, zh-Hans and yue. type groupNote struct { ID, State, Note string } // exampleBody is one example's page: its family as a kicker, its own // sentence, the two sentences every example page says, then every // state as a frame of its own document with the note on the state // under its label, the warning where the prose has one, the widget, // and the routes it stands for; or, for a grouped example, one widget // and a block per state under it. The three primitives that are also // pages of their own end with the link to that page, and a layout's // page with the button that opens the shell's document, each in a tab // of its own. const exampleBody = `{{define "ds-body-example"}}

{{.Family.Title}} {{.Family.Blurb}}

{{if .Blurb}}

{{.Blurb}}

{{end}}

{{say "gallery.example.live"}} {{say "gallery.example.html"}}

{{with .Group}}
{{template "ds-view" .}} {{range $.Notes}}
{{.State}} {{if .Note}}

{{.Note}}

{{end}}
{{end}}
{{else}}{{range .States}}

{{.Label}}

{{if .Note}}

{{.Note}}

{{end}}{{with .Warning}}
{{.Title}}

{{.Body}}

{{end}}{{template "ds-view" .Preview}} {{if .Routes}}

{{.Routes}}

{{end}}
{{end}}{{end}}{{with .Live}}

{{.Label}} ({{say "gallery.newtab"}})

{{end}}{{if .Shell}}

{{say "gallery.shell.open" "shell" .Shell}} ({{say "gallery.newtab"}})

{{end}}{{end}}` // demoIdiom is one primitive that is also a page of the tree: the // words its link wears, by copy id, and the address it goes to. type demoIdiom struct { Label string Href func(s *site, theme, locale string) string } // demoIdioms are the three primitives whose preview is not the last // word on them. The two shells are whole page frames, better seen at // the size of a window than of a paragraph, so each links its layout's // document; the modal's whole claim is that it is a URL, so the // demonstration is not complete until you have been to it. Rastrillo's // three, under Rastrillo's name. var demoIdioms = map[string]demoIdiom{ "shell-topbar": {"gallery.primitives.topbar.live", func(s *site, theme, locale string) string { return s.layoutHref(theme, locale, "layout-topbar") }}, "shell-sidebar": {"gallery.primitives.sidebar.live", func(s *site, theme, locale string) string { return s.layoutHref(theme, locale, "layout-sidebar") }}, "modal": {"gallery.primitives.modal.live", (*site).modalHref}, } // layoutHref is a layout example's document opened whole: its default // state's frame document. func (s *site) layoutHref(theme, locale, name string) string { state, ok := s.m.Default(name) if !ok { // The layouts are frozen in examples/ and named in code; a // manifest without one is a programming error, as a kind // fileOf does not know is. panic("gallery: the manifest has no " + name + " example") } return s.href(theme, locale, "frames/"+name+"/"+state+".html") } // shellOf is the shell a layout example documents, by the name its // open button says: column, topbar, sidebar, console or stage. The // -page members of the sidebar and console pairs are the same shell's // content page and get no button; a component is no shell at all. func shellOf(e example.Entry) string { if !e.Document || strings.HasSuffix(e.Name, "-page") { return "" } return strings.TrimPrefix(e.Name, "layout-") } // example renders one example's page body and its frame documents, // keyed by the paths frameFiles names. func (s *site) example(p page, e example.Entry) (body []byte, frames map[string][]byte, err error) { ex := s.prose.Examples[e.Name] frames = map[string][]byte{} data := struct { page Family family Entry example.Entry Blurb string States []stateView Group *previewView Notes []groupNote Shell string ShellHref string Live *link }{page: p, Entry: e, Blurb: s.proseIn(p.Locale, "examples."+e.Name+".blurb", ex.Blurb)} f, _ := s.familyByKey(e.Family) data.Family = s.familyIn(p.Locale, f) for _, st := range e.States { fr, code, err := s.localSample(p.Theme, p.Locale, e, st) if err != nil { return nil, nil, fmt.Errorf("%s/%s: %w", e.Name, st.ID, err) } rel := "frames/" + e.Name + "/" + st.ID + ".html" frames[rel] = []byte(fr) note := s.proseIn(p.Locale, "examples."+e.Name+".states."+st.ID, ex.States[st.ID]) if grouped[e.Name] { data.Notes = append(data.Notes, groupNote{ID: st.ID, State: s.label(p.Locale, e, st), Note: note}) continue } view, err := s.preview(p, e, st, s.href(p.Theme, p.Locale, rel), code) if err != nil { return nil, nil, fmt.Errorf("%s/%s: %w", e.Name, st.ID, err) } sv := stateView{ID: st.ID, Label: s.label(p.Locale, e, st), Note: note, Preview: view} if len(st.SampleURLs) > 0 { sv.Routes = s.say(p.Locale, "gallery.routes", "routes", strings.Join(st.SampleURLs, ", ")) } if w, ok := ex.Warnings[st.ID]; ok { slot := "examples." + e.Name + ".warnings." + st.ID sv.Warning = &warning{ Title: s.proseIn(p.Locale, slot+".title", w.Title), Body: s.proseIn(p.Locale, slot+".body", w.Body), } } data.States = append(data.States, sv) } if grouped[e.Name] { view, rowDoc, err := s.groupedPreview(p, e) if err != nil { return nil, nil, err } fr, err := s.frame(rowDoc, p.Theme, p.Locale, e, "row") if err != nil { return nil, nil, fmt.Errorf("%s/row: %w", e.Name, err) } frames["frames/"+e.Name+"/row.html"] = []byte(fr) data.Group = &view } if d, ok := demoIdioms[e.Name]; ok { data.Live = &link{Href: d.Href(s, p.Theme, p.Locale), Label: s.say(p.Locale, d.Label)} } if data.Shell = shellOf(e); data.Shell != "" { data.ShellHref = s.href(p.Theme, p.Locale, "frames/"+e.Name+"/"+e.Default+".html") } out, err := s.body(p.Locale, "ds-body-example", data) if err != nil { return nil, nil, err } return []byte(out), frames, nil } func init() { registerTemplate(exampleBody) }