diff --git a/demo/index.ts b/demo/index.ts index 62eca345..0ee06023 100644 --- a/demo/index.ts +++ b/demo/index.ts @@ -14,4 +14,5 @@ export default [ 'src/NavbarDemo', 'src/RefreshableDemo', 'src/FlowLayoutDemo', + 'src/PopoverDemo' ] \ No newline at end of file diff --git a/demo/src/PopoverDemo.ts b/demo/src/PopoverDemo.ts new file mode 100644 index 00000000..ca95c458 --- /dev/null +++ b/demo/src/PopoverDemo.ts @@ -0,0 +1,46 @@ +import { Group, Panel, popover, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, Text, scroller, layoutConfig, image, IView, IVLayout, ScaleType, modal, IText, network } from "doric"; +import { title, label, colors } from "./utils"; + +@Entry +class PopoverDemo extends Panel { + build(rootView: Group): void { + scroller(vlayout([ + title("Popover Demo"), + label('Popover').apply({ + width: 200, + height: 50, + bgColor: colors[0], + textSize: 30, + textColor: Color.WHITE, + layoutConfig: layoutConfig().exactly(), + onClick: () => { + popover(context).show(text({ + width: 200, + height: 50, + bgColor: colors[0], + textColor: Color.WHITE, + layoutConfig: layoutConfig().exactly().a(Gravity.Center), + text: "This is PopOver Window", + onClick: () => { + modal(context).toast('Dismissed after 3 seconds') + setTimeout(() => { + popover(context).dismiss() + }, 3000) + }, + }).also(v => { + let idx = 0 + v.onClick = () => { + v.bgColor = colors[(++idx) % colors.length] + } + })) + } + } as IText), + ]).apply({ + layoutConfig: layoutConfig().atmost().h(LayoutSpec.WRAP_CONTENT), + gravity: gravity().center(), + space: 10, + } as IVLayout)).apply({ + layoutConfig: layoutConfig().atmost(), + }).in(rootView) + } +} \ No newline at end of file diff --git a/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m index 6f428047..433e9bab 100644 --- a/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m +++ b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m @@ -7,7 +7,7 @@ #import "Doric.h" @interface DoricPopoverPlugin () -@property(nonatomic, strong) DoricRootNode *popoverNode; +@property(nonatomic, strong) DoricViewNode *popoverNode; @property(nonatomic, strong) UIView *fullScreenView; @end @@ -20,19 +20,26 @@ - (void)show:(NSDictionary *)params withPromise:(DoricPromise *)promise { it.width = superView.width; it.height = superView.height; it.top = it.left = 0; - [superView addSubview:self.fullScreenView]; + [superView addSubview:it]; UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissPopover)]; [it addGestureRecognizer:gestureRecognizer]; }]; } [superView bringSubviewToFront:self.fullScreenView]; - self.fullScreenView.hidden = NO; - if (!self.popoverNode) { - self.popoverNode = [[DoricRootNode alloc] initWithContext:self.doricContext]; - DoricStackView *view = [[DoricStackView alloc] initWithFrame:self.fullScreenView.frame]; - [self.popoverNode setupRootView:view]; + if (self.popoverNode) { + [self dismissPopover]; } - [self.popoverNode render:params[@"props"]]; + self.fullScreenView.hidden = NO; + NSString *viewId = params[@"id"]; + NSString *type = params[@"type"]; + self.popoverNode = [[DoricViewNode create:self.doricContext withType:type] also:^(DoricViewNode *it) { + it.viewId = viewId; + [it initWithSuperNode:nil]; + it.view.layoutConfig = [DoricLayoutConfig new]; + [it blend:params[@"props"]]; + [self.fullScreenView addSubview:it.view]; + [self.doricContext.headNodes addObject:it]; + }]; [promise resolve:nil]; }); } @@ -45,10 +52,12 @@ - (void)dismiss:(NSDictionary *)params withPromise:(DoricPromise *)promise { } - (void)dismissPopover { + [self.doricContext.headNodes removeObject:self.popoverNode]; self.popoverNode.view.hidden = YES; self.fullScreenView.hidden = YES; [self.popoverNode.view.subviews forEach:^(__kindof UIView *obj) { [obj removeFromSuperview]; }]; + self.popoverNode = nil; } -@end \ No newline at end of file +@end diff --git a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m index 02e510d7..ebe2b784 100644 --- a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m +++ b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m @@ -36,10 +36,15 @@ - (void)render:(NSDictionary *)argument { __weak typeof(self) _self = self; dispatch_async(dispatch_get_main_queue(), ^{ __strong typeof(_self) self = _self; - [self.doricContext.rootNode also:^(DoricRootNode *it) { - it.viewId = argument[@"id"]; - [it render:argument[@"props"]]; - }]; + + NSString *viewId = argument[@"id"]; + if ([self.doricContext.rootNode.viewId isEqualToString:viewId]) { + [self.doricContext.rootNode render:argument[@"props"]]; + } else { + DoricViewNode *viewNode = [self headViewNodeByViewId:viewId]; + [viewNode blend:argument[@"props"]]; + } + }); } @@ -49,6 +54,7 @@ - (DoricViewNode *)headViewNodeByViewId:(NSString *)viewId { return node; } } + self.doricContext.rootNode.viewId = viewId; return self.doricContext.rootNode; } diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.h b/iOS/Pod/Classes/Shader/DoricViewNode.h index 88b2c73e..faad1da5 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.h +++ b/iOS/Pod/Classes/Shader/DoricViewNode.h @@ -24,7 +24,6 @@ #import "DoricLayouts.h" #import "UIView+Doric.h" -NS_ASSUME_NONNULL_BEGIN @class DoricSuperNode; @interface DoricViewNode : DoricContextHolder @@ -55,5 +54,3 @@ NS_ASSUME_NONNULL_BEGIN - (void)requestLayout; @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index 3a432cbb..db7f3140 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -126,6 +126,8 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop } else if ([name isEqualToString:@"layoutConfig"]) { if (self.superNode && [prop isKindOfClass:[NSDictionary class]]) { [self.superNode blendSubNode:self layoutConfig:prop]; + } else { + [self blendLayoutConfig:prop]; } } else if ([name isEqualToString:@"onClick"]) { self.callbackIds[@"onClick"] = prop; @@ -246,4 +248,36 @@ - (NSNumber *)getRotation { return @(degree); } +- (void)blendLayoutConfig:(NSDictionary *)params { + [params[@"widthSpec"] also:^(NSNumber *it) { + if (it) { + self.layoutConfig.widthSpec = (DoricLayoutSpec) [it integerValue]; + } + }]; + + [params[@"heightSpec"] also:^(NSNumber *it) { + if (it) { + self.layoutConfig.heightSpec = (DoricLayoutSpec) [it integerValue]; + } + }]; + + NSDictionary *margin = params[@"margin"]; + if (margin) { + self.layoutConfig.margin = DoricMarginMake( + [(NSNumber *) margin[@"left"] floatValue], + [(NSNumber *) margin[@"top"] floatValue], + [(NSNumber *) margin[@"right"] floatValue], + [(NSNumber *) margin[@"bottom"] floatValue]); + } + + NSNumber *alignment = params[@"alignment"]; + if (alignment) { + self.layoutConfig.alignment = (DoricGravity) [alignment integerValue]; + } + NSNumber *weight = params[@"weight"]; + if (weight) { + self.layoutConfig.weight = (DoricGravity) [weight integerValue]; + } +} + @end diff --git a/js-framework/src/native/index.native.ts b/js-framework/src/native/index.native.ts index 372f9d1b..be12d2eb 100644 --- a/js-framework/src/native/index.native.ts +++ b/js-framework/src/native/index.native.ts @@ -18,3 +18,4 @@ export * from './navbar' export * from './navigator' export * from './network' export * from './storage' +export * from './popover' diff --git a/js-framework/src/native/popover.ts b/js-framework/src/native/popover.ts index 3d5f6986..802534ed 100644 --- a/js-framework/src/native/popover.ts +++ b/js-framework/src/native/popover.ts @@ -15,13 +15,25 @@ */ import { BridgeContext } from "../runtime/global" import { View } from "../ui/view" +import { Panel } from "../ui/panel" export function popover(context: BridgeContext) { + const entity = context.entity + let panel: Panel | undefined = undefined + if (entity instanceof Panel) { + panel = entity + } return { show: (view: View) => { - return context.popover.show(view) + if (panel) { + panel.addHeadView(view) + } + return context.popover.show(view.toModel()) }, - dismiss: () => { + dismiss: (view: View | undefined = undefined) => { + if (panel && view) { + panel.removeHeadView(view) + } return context.popover.dismiss() }, } diff --git a/js-framework/src/ui/panel.ts b/js-framework/src/ui/panel.ts index 30508907..2f6987db 100644 --- a/js-framework/src/ui/panel.ts +++ b/js-framework/src/ui/panel.ts @@ -137,15 +137,19 @@ export abstract class Panel { } private hookBeforeNativeCall() { - this.__root__.clean() + for (let v of this.headviews.values()) { + v.clean() + } } private hookAfterNativeCall() { //Here insert a native call to ensure the promise is resolved done. nativeEmpty() - if (this.__root__.isDirty()) { - const model = this.__root__.toModel() - this.nativeRender(model) + for (let v of this.headviews.values()) { + if (v.isDirty()) { + const model = v.toModel() + this.nativeRender(model) + } } }