From: early Date: Wed, 11 Sep 2024 04:50:33 +0000 (-0600) Subject: fix template behavior with slotted components X-Git-Url: https://git.earlybird.gay/?a=commitdiff_plain;h=55d49a64189f39e9996b5e65223ab7fadeb0d79a;p=today fix template behavior with slotted components --- diff --git a/internal/compile/template.go b/internal/compile/template.go index 808c3fd..a1e0e2b 100644 --- a/internal/compile/template.go +++ b/internal/compile/template.go @@ -270,11 +270,11 @@ func replaceTemplateFields(n *html.Node) []string { if token == tokenCloseTemplate { inTemplate = false } - // If we're in a template and found a function identifier, replace it + // If we're in a template and found a template value identifier, replace it // with a namespaced version if inTemplate && isField(token) || inAttrs && isDataAttr[i] { - raised = append(raised, strings.Split(token, ".")[1]) + raised = append(raised, token[1:]) token = ".parent" + token } @@ -314,8 +314,18 @@ func insertTemplateSource(subSource TemplateSource, context *html.Node) (*comput // ===== CONTEXT PREPROCESSING ===== // Raise any fields pre-existing in the context to .parent.field. raiseFields := make([]string, 0) + raiseDepth := 0 htmltree.Walk(context.FirstChild, func(n *html.Node) (bool, error) { - raiseFields = append(raiseFields, replaceTemplateFields(n)...) + if raiseDepth == 0 { + raiseFields = append(raiseFields, replaceTemplateFields(n)...) + } + if n.Type == html.TextNode { + if scopesUp(n.Data) { + raiseDepth++ + } else if scopesDown(n.Data) { + raiseDepth-- + } + } return false, nil }) // Remove :data attributes from the root of context, and prepare them @@ -365,26 +375,67 @@ func insertTemplateSource(subSource TemplateSource, context *html.Node) (*comput // Mix stuff from the context into the template using slots... n := context.FirstChild + // slot context is the nodes currently considered candidates for slotting. + // Being in the slotCtx doesn't mean a node *will* be slotted, just that it + // might be, and if it is it needs to have its order preserved with every + // other node in the ctx. + slotCtx := make([]*html.Node, 0) + // nonslotCtx is a copy of slotCtx, but elements with no slot go here instead. + nonslotCtx := make([]*html.Node, 0) + var slotUsed, nonslotUsed bool + var targetSlot *html.Node + // slotCtxDepth is the depth of any template scope biz that surrounds a + // slotted node. + slotCtxDepth := 0 for n != nil { next := n.NextSibling context.RemoveChild(n) - // Do not slot any non-slottable nodes, or comments, because I do not - // like them - if n.Type == html.CommentNode { - n = next - continue - } - slotName := htmltree.GetAttr(n, "slot") - slot := slots[slotName] - if slot != nil { - if !isShadow { - slot.Parent.InsertBefore(n, slot) + switch n.Type { + case html.TextNode: + // Raise/lower the slot context depth if scope goes up or down. + if scopesUp(n.Data) { + slotCtxDepth++ + } else if scopesDown(n.Data) { + slotCtxDepth-- + } + // Add the node to slotCtx, and a copy to nonslotCtx + slotCtx = append(slotCtx, n) + nonslotCtx = append(nonslotCtx, &html.Node{ + Type: n.Type, + Data: n.Data, + }) + case html.ElementNode: + slotName := htmltree.GetAttr(n, "slot") + slot, ok := slots[slotName] + if slotName != "" && ok { + // Add this element to the current slottable context. + slotCtx = append(slotCtx, n) + slotUsed = true + targetSlot = slot + // Mark the slot as used in usedSlot so its default content + // is removed. + usedSlot[slotName] = true } else { - context.AppendChild(n) + // Add it to the nonslottable context. + nonslotCtx = append(nonslotCtx, n) + nonslotUsed = true + } + } + if slotCtxDepth == 0 { + if slotUsed { + for _, slotCtxNode := range slotCtx { + targetSlot.Parent.InsertBefore(slotCtxNode, targetSlot) + } + slotCtx = make([]*html.Node, 0) + slotUsed = false + } + if nonslotUsed { + for _, nonslotCtxNode := range nonslotCtx { + root.AppendChild(nonslotCtxNode) + } + nonslotCtx = make([]*html.Node, 0) + nonslotUsed = false } - usedSlot[slotName] = true - } else { - root.AppendChild(n) } n = next }