Compare commits
	
		
			5 Commits
		
	
	
		
			589bbca889
			...
			9495849dc9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9495849dc9 | ||
|   | f6071909b6 | ||
|   | 2b3cd3120c | ||
|   | 369dc51779 | ||
|   | 148ced050b | 
							
								
								
									
										4
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								TODO.md
									
									
									
									
									
								
							| @ -34,8 +34,8 @@ For example: | |||||||
| - [x] wvcc bug: Nested static view classes are not seen by compiler | - [x] wvcc bug: Nested static view classes are not seen by compiler | ||||||
|   - This required tweaking how the configurations are passed around. Ultimately, we should strive for less complexity |   - This required tweaking how the configurations are passed around. Ultimately, we should strive for less complexity | ||||||
|     in this regard. |     in this regard. | ||||||
| - [ ] `OutletContainer` trait or interface for components which can contain an `<Outlet />` child. | - [x] `OutletContainer` trait or interface for components which can contain an `<Outlet />` child. | ||||||
| - [ ] `Context` should have methods for simply finding an ancestor of a certain type without the need for a predicate. | - [x] `Context` should have methods for simply finding an ancestor of a certain type without the need for a predicate. | ||||||
| 
 | 
 | ||||||
| ## 0.1.2 | ## 0.1.2 | ||||||
| - [x] `Outlet` component for rendering children like so: | - [x] `Outlet` component for rendering children like so: | ||||||
|  | |||||||
| @ -99,6 +99,23 @@ public interface ComponentContext { | |||||||
|         return ancestorClass.cast(this.findNearestAncestor(matching.and(ancestorClass::isInstance))); |         return ancestorClass.cast(this.findNearestAncestor(matching.and(ancestorClass::isInstance))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     default <T extends ViewComponent> @Nullable T findNearestAncestor(Class<T> ancestorClass) { | ||||||
|  |         return ancestorClass.cast(this.findNearestAncestor(ancestorClass::isInstance)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     boolean hasAncestor(Predicate<? super ViewComponent> matching); | ||||||
|  | 
 | ||||||
|  |     default <T extends ViewComponent> boolean hasAncestor( | ||||||
|  |             Class<T> ancestorClass, | ||||||
|  |             Predicate<? super ViewComponent> matching | ||||||
|  |     ) { | ||||||
|  |         return this.hasAncestor(matching.and(ancestorClass::isInstance)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     default <T extends ViewComponent> boolean hasAncestor(Class<T> ancestorClass) { | ||||||
|  |         return this.hasAncestor(ancestorClass::isInstance); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     List<ViewComponent> getAllAncestors(); |     List<ViewComponent> getAllAncestors(); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -70,7 +70,10 @@ public class DefaultComponentContext implements ComponentContext { | |||||||
|     public @Nullable ViewComponent findNearestAncestor(Predicate<? super ViewComponent> matching) { |     public @Nullable ViewComponent findNearestAncestor(Predicate<? super ViewComponent> matching) { | ||||||
|         final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); |         final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); | ||||||
|         if (componentStack.size() > 1) { |         if (componentStack.size() > 1) { | ||||||
|             for (final var ancestor : componentStack.subList(1, componentStack.size() -1)) { |             // 1/27/25: earlier this was originally componentStack.size() - 1 as the second argument to sublist(). | ||||||
|  |             // On examination today, it didn't make sense, because it would be chopping off the farthest ancestor from | ||||||
|  |             // search. So I removed it, in accordance with the implementation in hasAncestor(). | ||||||
|  |             for (final var ancestor : componentStack.subList(1, componentStack.size())) { | ||||||
|                 if (matching.test(ancestor)) { |                 if (matching.test(ancestor)) { | ||||||
|                     return ancestor; |                     return ancestor; | ||||||
|                 } |                 } | ||||||
| @ -79,6 +82,19 @@ public class DefaultComponentContext implements ComponentContext { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean hasAncestor(Predicate<? super ViewComponent> matching) { | ||||||
|  |         final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); | ||||||
|  |         if (componentStack.size() > 1) { | ||||||
|  |             for (final var ancestor : componentStack.subList(1, componentStack.size())) { | ||||||
|  |                 if (matching.test(ancestor)) { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<ViewComponent> getAllAncestors() { |     public List<ViewComponent> getAllAncestors() { | ||||||
|         final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); |         final List<ViewComponent> componentStack = this.getRenderContext().getComponentStack(); | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import groowt.view.component.ComponentRenderException | |||||||
| import groowt.view.component.context.ComponentContext | import groowt.view.component.context.ComponentContext | ||||||
| import groowt.view.component.context.ComponentScope.TypeAndFactory | import groowt.view.component.context.ComponentScope.TypeAndFactory | ||||||
| import groowt.view.component.factory.ComponentFactory | import groowt.view.component.factory.ComponentFactory | ||||||
| import groowt.view.component.web.WithHtml |  | ||||||
| 
 | 
 | ||||||
| class IntrinsicHtml extends DelegatingWebViewComponent implements WithHtml { | class IntrinsicHtml extends DelegatingWebViewComponent implements WithHtml { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| package groowt.view.component.web.lib | package groowt.view.component.web.lib | ||||||
| 
 | 
 | ||||||
| import groowt.view.View | import groowt.view.View | ||||||
|  | import groowt.view.component.ComponentRenderException | ||||||
| import groowt.view.component.runtime.DefaultComponentWriter | import groowt.view.component.runtime.DefaultComponentWriter | ||||||
| 
 | 
 | ||||||
| class Outlet extends DelegatingWebViewComponent { | class Outlet extends DelegatingWebViewComponent { | ||||||
| @ -14,6 +15,11 @@ class Outlet extends DelegatingWebViewComponent { | |||||||
|     @Override |     @Override | ||||||
|     protected View getDelegate() { |     protected View getDelegate() { | ||||||
|         return { Writer w -> |         return { Writer w -> | ||||||
|  |             if (!context.hasAncestor(OutletContainer)) { | ||||||
|  |                 throw new ComponentRenderException( | ||||||
|  |                         "<Outlet> is being used outside of a component implementing OutletContainer." | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|             def cw = new DefaultComponentWriter(w, context.renderContext, context) |             def cw = new DefaultComponentWriter(w, context.renderContext, context) | ||||||
|             givenChildren.each { cw << it } |             givenChildren.each { cw << it } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -0,0 +1,5 @@ | |||||||
|  | package groowt.view.component.web.lib | ||||||
|  | 
 | ||||||
|  | import groowt.view.component.web.WebViewComponent | ||||||
|  | 
 | ||||||
|  | interface OutletContainer extends WebViewComponent {} | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package groowt.view.component.web | package groowt.view.component.web.lib | ||||||
| 
 | 
 | ||||||
| trait WithHtml { | trait WithHtml { | ||||||
| 
 | 
 | ||||||
| @ -1,17 +1,35 @@ | |||||||
| package groowt.view.component.web.lib | package groowt.view.component.web.lib | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | import groowt.view.component.web.WebViewComponentContext | ||||||
|  | import groowt.view.component.web.WebViewComponentScope | ||||||
| import org.junit.jupiter.api.Test | import org.junit.jupiter.api.Test | ||||||
| 
 | 
 | ||||||
| class OutletTests extends AbstractWebViewComponentTests { | class OutletTests extends AbstractWebViewComponentTests { | ||||||
| 
 | 
 | ||||||
|  |     static final class DummyOutletContainer extends Echo implements OutletContainer { | ||||||
|  | 
 | ||||||
|  |         DummyOutletContainer() { | ||||||
|  |             super([:]) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     void configureContext(WebViewComponentContext context) { | ||||||
|  |         context.configureRootScope(WebViewComponentScope) { | ||||||
|  |             addWithNoArgConstructor(DummyOutletContainer) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Test |     @Test | ||||||
|     void smokeScreen() { |     void smokeScreen() { | ||||||
|         doTest('<Outlet />', '') |         doTest('<OutletTests.DummyOutletContainer><Outlet /></OutletTests.DummyOutletContainer>', '') | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     void withChildren() { |     void withChildren() { | ||||||
|         doTest('<Echo items={[0, 1, 2]}><Outlet children={items} /></Echo>', '012') |         doTest('<OutletTests.DummyOutletContainer><Echo items={[0, 1, 2]}><Outlet children={items} /></Echo></OutletTests.DummyOutletContainer>', '012') | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user