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