Just to clarity, I obviously haven't had a time to discuss this with my 
colleagues so I don't know which one (or something else entirely) we (Apple) 
end up endorsing/opposing at the end.

> On Apr 25, 2015, at 12:14 AM, Ryosuke Niwa <rn...@apple.com> wrote:
> 
> Hi all,
> 
> In today's F2F, I've got an action item to come up with a concrete workable 
> proposal for imperative API.  I had a great chat about this afterwards with 
> various people who attended F2F and here's a summary.  I'll continue to work 
> with Dimitri & Erik to work out details in the coming months (our deadline is 
> July 13th).
> 
> https://gist.github.com/rniwa/2f14588926e1a11c65d3 
> <https://gist.github.com/rniwa/2f14588926e1a11c65d3>
> 
> Imperative API for Node Distribution in Shadow DOM
> 
> There are two approaches to the problem depending on whether we want to 
> natively support redistribution or not.
> 
> To recap, a redistribution of a node (N_1) happens when it's distributed to 
> an insertion point (I_1) inside a shadow root (S_1), and I_1's parent also 
> has a shadow root which contains an insertion point which ends picking up 
> N_1. e.g. the original tree may look like:
> 
> (host of S_1) - S_1
>   + N_1         + (host of S_2) - S_2
>                    + I_1           + I_2
> Here, (host of S_1) has N_1 as a child, and (host of S_2) is a child of S_1 
> and has I_1 as a child. S_2 has I_2 as a child. The composed tree, then, may 
> look like:
> 
> (host of S_1)
>  + (host of S_2)
>    + I_2
>      + N_1
>  
> <https://gist.github.com/rniwa/2f14588926e1a11c65d3#redistribution-is-implemented-by-authors>Redistribution
>  is implemented by authors
> 
> In this model, we can add insertAt and remove on content element and expose 
> distributedNodes defined as follows:
> 
> insertAt(Node nodeToDistribute, long index) - Inserts nodeToDistribute to the 
> list of the distributed nodes at index. It throws if nodeToDistribute is not 
> a descendent (or a direct child if wanted to keep this constraint) of the 
> shadow host of the ancestor shadow root of containt or if index is larger 
> than the length of distributedNodes.
> remove(Node distributedNode) - Remove distributedNode from the list 
> distributed nodes. Throws if distributedNodes doesn't contain this node.
> distributedNodes - Returns an array of nodes that are distributed into this 
> insertion point in the order they appear.
> In addition, content fires a synchrnous distributionchanged event when 
> distributedNodeschanges (in response to calls to insertAt or remove). 
> 
>  <https://gist.github.com/rniwa/2f14588926e1a11c65d3#pros>Pros
> 
> Very simple / very primitive looking.
> Defers the exact mechanism/algorithm of re-distributions to component authors.
> We can support distributing any descendent, not just direct children, to any 
> insertion points. This was not possible with select attribute especially with 
> the presence of multiple generations of shadow DOM due to perfomance problems.
> Allows distributed nodes to be re-ordered (select doesn't allow this).
>  <https://gist.github.com/rniwa/2f14588926e1a11c65d3#cons>Cons
> 
> Each component needs to manually implement re-distributions by recursively 
> traversing through distributedNodes of content elements inside 
> distributedNodes of the content element if it didn't want to re-distribute 
> everything. This is particularly challenging because you need to listen to 
> distributionchanged event on every such content element. We might need 
> something aking to MutationObserver's subtree option to monitor this if we're 
> going this route.
> It seems hard to support re-distribution natively in v2.
>  
> <https://gist.github.com/rniwa/2f14588926e1a11c65d3#redistribution-is-implemented-by-uas>Redistribution
>  is implemented by UAs
> 
> In this model, the browser is responsible for taking care of redistributions. 
> Namely, we would like to expose distributionPool on the shadow root which 
> contains the ordered list of nodes that could be distributed (because they're 
> direct children of the host) or re-distributed. Conceptually, you could think 
> of it as a depth first traversal of distributedNodes of every content 
> element. Because this list contains every candidate for (re)distribution, 
> it's impractical to include every descendent node especially if we wanted to 
> do synchronous updates so we're back to supporting only direct children for 
> distribution.
> 
> In this proposal, we add a new callback distributeCallback(NodeList 
> distributionPool) as an arguemnt (probably inside a dictionary) to 
> createShadowRoot. e.g.
> 
> var shadowRoot = element.createShadowRoot({
>   distributedCallback: function (distributionPool) {
>     ... // code to distribute nodes
>   }
> });
> Unfortunately, we can't really use insertAt and remove in model because 
> distributionPoolmaybe changed under the foot by (outer) insertion points in 
> the light DOM if this shadow root to attached to a host inside another shadow 
> DOM unless we manually listen to distributionchangedevent on every content 
> (which may recursively appear in distributedNodes of those content).
> 
> One way to work around this problem is let UA also propagate changes to 
> distributionPool to each nested shadow DOM. That is, when distributionPool of 
> a shadow root gets modified due to changes to distributionPools of direct 
> children (of the shadow host) that are content elements themselves, UA will 
> automatically invoke distributedCallback to trigger a distribution.
> 
> We also expose distribute() on ShadowRoot to allow arbitrary execution (e.g. 
> when its internal state changes) of this distribution propagation mechanism. 
> Components will use this function to listen to changes in DOM.
> 
> We could also trigger this propagation mechanism at the end of micro task 
> (via MutationObserver) when direct children of a shadow host is mutated.
> 
> In terms of actual distribution, we only need to expose add(Node) on content 
> element. Because all candidates are distributed each time, we can clear 
> distributed nodes from every insertion point in the shadow DOM. (Leaving them 
> in tact doesn't make sense because some of the nodes that have been 
> distributed in the past may no longer be available).
> 
> There is an alternative approach to add something like done() or redistribute 
> to specifically trigger redistribution but some authors may forget to make 
> this extra function call because it's not required in normal cases.
> 
>  <https://gist.github.com/rniwa/2f14588926e1a11c65d3#pros-1>Pros
> 
> Components don't have to implement complicated redistribution algorithms 
> themselves.
> Allows distributed nodes to be re-ordered (select doesn't allow this).
>  <https://gist.github.com/rniwa/2f14588926e1a11c65d3#cons-1>Cons
> 
> Redistribution algorithm is not simple
> At a slightly higher abstraction level
> 
> 
> - R. Niwa
> 

Reply via email to