In this example, the data-ref="foo" wants to be managed by the data-component="my-component", but data-component="some-wrapper" is in the way (unless ignoreGuard is passed).
Proposal
Being able to "mark" a component as wrapper, so its "children" are resolved to its parent. Basically it's removing (parts of) itself from the resolving equation.
The currently resolving works as follows:
A components defines a refElement or refComponent
A querySelector is done to find candidates
Each candidate looks up in the tree for its parent data-component element.
If ignoreGuard is passed, it will just return as valid
if the "owner" is found, it will return as valid
Otherwise it will be invalid.
To make this wrapper work, it should do an additional step after number 5, where it continues looking upwards if it encounters a wrapper component.
indicating something as wrapper
In order to make the above work, the resolving code needs some additional information. It needs to know which data-component to skip (similar to ignoreGuard, but more granular). However, the wrapper component also need to "protect" it's own components, and those don't belong to the parent.
So besides a boundary on the parent side, it also needs a boundary on the children side. Everything those boundaries is marked internal, but everything past the children boundary belongs to the parent again.
Since we already have the data-component as attribute on the "root tag" of each component, that can serve as parent boundary. Then we only need a new attribute for the child boundary. When that attribute exists somewhere in the component, the whole component becomes a "wrapper component".
If the wrapper component exists only of a single div (e.g. because all its wrapper logic is done on JS, and not in the template), the boundary attribute can be placed on that single element.
If a wrapper component has multiple places where it can render child content, multiple boundary attributes could exist on different elements.
The name of this attribute is TBD, but let's call it data-wrapper-boundary for now.
In the example, the data-ref="toggle" and data-ref="toggle-content" refs belong to the some-wrapper component, but the data-ref="foo" element that's inside the (or conceptually outside) the data-wrapper-boundary could now belong to my-component again.
Updated resolve logic
Once we have found the closest parent data-component, we need to know if we are inside or outside the data-wrapper-boundary. If we only look for the data-wrapper-boundary element, it can be positioned in three locations; between the data-component and the target element; further down the target element, or further up the `data-component element.
In this case we only care about looking up, but we still need to know if it's inside or above the data-component element. We can do this by also looking upwards from the the data-wrapper-boundary to find the closest data-component, and see if it's the same. If it is, we can ignore that component, and continue looking upwards.
So in short:
find closest parent data-wrapper-boundary element
from that element, find closest data-component element
if equal to the current found data-component, ignore and continue
otherwise, return null
With this in place, it should be able to "skip" multiple wrappers as well:
In the resolve logic, all the 3 wrappers can be skipped, so the data-ref="foo" can be owned by my-component.
What can wrappers own?
If wrappers have a child boundary, should they be able to access those elements without using ignoreGuard? Since all logic is passed from parents, it should never have direct access to it. So we need to build this in the resolving logic.
This means that step number 5 above (if owner is found), needs another condition. It needs to make sure there is NO data-wrapper-boundary in between. So we can do the same logic as before, and return null if this is the case (instead of ignoring it).
So the complete resolve logic should be as follows:
A components defines a refElement or refComponent
A querySelector is done to find candidates
Each candidate looks up in the tree for its parent data-component element.
If ignoreGuard is passed, it will just return as valid
find closest parent data-wrapper-boundary
find the "owner" of the data-wrapper-boundary
if the owner of the boundary is the same as the owner of the ref, return null
if the owner of the boundary is the same as the closest parent (but is not the ref owner), ignore and find the next parent, and continue from 4
Partly relates to https://github.com/mubanjs/muban/projects/1#card-68019505
Wrapper Components can be described as components that render content that is passed down from above, and is owned by a parent.
Examples are:
Current Issue
Currently, any
data-ref
ordata-component
is owned and instantiated by its direct parent.Let's look at what the HTML could look like:
In this example, the
data-ref="foo"
wants to be managed by thedata-component="my-component"
, butdata-component="some-wrapper"
is in the way (unlessignoreGuard
is passed).Proposal
Being able to "mark" a component as wrapper, so its "children" are resolved to its parent. Basically it's removing (parts of) itself from the resolving equation.
The currently resolving works as follows:
refElement
orrefComponent
querySelector
is done to find candidatesdata-component
element.ignoreGuard
is passed, it will just return as validTo make this wrapper work, it should do an additional step after number 5, where it continues looking upwards if it encounters a wrapper component.
indicating something as wrapper
In order to make the above work, the resolving code needs some additional information. It needs to know which
data-component
to skip (similar toignoreGuard
, but more granular). However, the wrapper component also need to "protect" it's own components, and those don't belong to the parent.So besides a boundary on the parent side, it also needs a boundary on the children side. Everything those boundaries is marked internal, but everything past the children boundary belongs to the parent again.
Since we already have the
data-component
as attribute on the "root tag" of each component, that can serve as parent boundary. Then we only need a new attribute for the child boundary. When that attribute exists somewhere in the component, the whole component becomes a "wrapper component".If the wrapper component exists only of a single div (e.g. because all its wrapper logic is done on JS, and not in the template), the boundary attribute can be placed on that single element.
If a wrapper component has multiple places where it can render child content, multiple boundary attributes could exist on different elements.
The name of this attribute is TBD, but let's call it
data-wrapper-boundary
for now.The earlier example would then look like:
A less simple example, where the wrapper as a button ref, would look like this:
In the example, the
data-ref="toggle"
anddata-ref="toggle-content"
refs belong to thesome-wrapper
component, but thedata-ref="foo"
element that's inside the (or conceptually outside) thedata-wrapper-boundary
could now belong tomy-component
again.Updated resolve logic
Once we have found the closest parent
data-component
, we need to know if we are inside or outside thedata-wrapper-boundary
. If we only look for thedata-wrapper-boundary
element, it can be positioned in three locations; between thedata-component
and the target element; further down the target element, or further up the `data-component element.In this case we only care about looking up, but we still need to know if it's inside or above the
data-component
element. We can do this by also looking upwards from the thedata-wrapper-boundary
to find the closestdata-component
, and see if it's the same. If it is, we can ignore that component, and continue looking upwards.So in short:
data-wrapper-boundary
elementdata-component
elementdata-component
, ignore and continuenull
With this in place, it should be able to "skip" multiple wrappers as well:
In the resolve logic, all the 3 wrappers can be skipped, so the
data-ref="foo"
can be owned bymy-component
.What can wrappers own?
If wrappers have a child boundary, should they be able to access those elements without using
ignoreGuard
? Since all logic is passed from parents, it should never have direct access to it. So we need to build this in the resolving logic.This means that step number 5 above (if owner is found), needs another condition. It needs to make sure there is NO
data-wrapper-boundary
in between. So we can do the same logic as before, and returnnull
if this is the case (instead of ignoring it).So the complete resolve logic should be as follows:
refElement
orrefComponent
querySelector
is done to find candidatesdata-component
element.ignoreGuard
is passed, it will just return as validdata-wrapper-boundary
data-wrapper-boundary
null