Skip to content

v0.11.0

Choose a tag to compare

@jackielii jackielii released this 29 Oct 20:48
· 9 commits to main since this release
453fcdc

v0.11.0 — Keyed components: one instance of many

A component rendered once per item, such as a table row or a card, now gets an id per instance, and a request for one instance tells Props which item it wants. (#41)

func Key(ctx context.Context, v any, key string) (string, error)       // "list-row--s3"
func KeyTarget(ctx context.Context, v any, key string) (string, error) // "#list-row--s3"
func (sp *StructPages) Key(v any, key string) (string, error)
func (sp *StructPages) KeyTarget(v any, key string) (string, error)
func KeyOf(t RenderTarget) string // "" when the target isn't keyed

Register the filters in gsx.toml:

key       = "github.com/jackielii/structpages.Key"
keytarget = "github.com/jackielii/structpages.KeyTarget"
<tr id={ Page.Row |> key(row.ID.String()) }>
  <button hx-get={ Page{} |> url } hx-target={ Page.Row |> keytarget(row.ID.String()) } hx-swap="outerHTML">Reload</button>
</tr>
case t.Is(p.Row):
	id, err := uuid.Parse(structpages.KeyOf(t)) // the key is client input: parse it, then authorise it

Like React's key, the key is the item's identity. Unlike React's, Props can read it back.

  • Id format: the component's id, then --, then the key. Bytes outside [A-Za-z0-9-] (_ included) are written as _XX, so the id needs no CSS escaping and KeyOf returns exactly the key you passed.
  • Errors: an empty key, or a plain string in place of a component.
  • Matching: HTMXRenderTarget and HTMXv4RenderTarget match a keyed id exactly, never by the loose name and suffix rules.
    • For one of the served page's components, the target Is that component.
    • For another page's component or a standalone function, the target Is that component and Props renders it with RenderComponent.
    • Either way the target carries the key.
  • Interface unchanged: RenderTarget is the same. A custom target carries a key by adding a Key() string method.

See Keyed components.

Fixed

  • Ids of names with underscores or non-ASCII letters. camelToKebab lowercased every rune with r | 0x20, so Row_Edit got the id row\x7f-edit, with a DEL control character, and Größe became gröÿe. Underscores and hyphens now separate words (Row_Edit becomes row-edit), and letters of any script are lowercased properly. (#42)

Ids that change

  • Names made only of ASCII letters and digits: unchanged. A test checks this against the old function over 100,000 random names.
  • Names with _ or non-ASCII letters: fixed as above. Two names that now kebab-case alike in one tree (RowEdit and Row_Edit) are reported by Mount.
  • Functions whose package path element has stray hyphens: a function in a package whose last import path element has leading, trailing or doubled hyphens, or a hyphen before a capital (My-Lib), drops the extra hyphens. Generated ids never contain --.
  • Hand-written HX-Target ids of keyed form: this covers anything with a valid key after --, BEM-style card--active included. They now match only exactly, so one that used to match a component loosely renders Page.

Upgrading

Replace hand-built instance ids like f`@{Page.Row |> id}-@{row.ID}` with |> key(row.ID). structpages-lint tools/lint/v0.1.19 flags them.

Full Changelog: v0.10.1...v0.11.0