diff --git a/Android/doric/src/main/java/pub/doric/DoricContext.java b/Android/doric/src/main/java/pub/doric/DoricContext.java index fd1971e0..ab791f14 100644 --- a/Android/doric/src/main/java/pub/doric/DoricContext.java +++ b/Android/doric/src/main/java/pub/doric/DoricContext.java @@ -22,6 +22,7 @@ import com.github.pengfeizhou.jscore.JSONBuilder; import org.json.JSONObject; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -30,6 +31,7 @@ import pub.doric.navbar.IDoricNavBar; import pub.doric.navigator.IDoricNavigator; import pub.doric.plugin.DoricJavaPlugin; import pub.doric.shader.RootNode; +import pub.doric.shader.ViewNode; import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricMetaInfo; @@ -47,6 +49,26 @@ public class DoricContext { private String script; private JSONObject initParams; private IDoricDriver doricDriver; + private final Map mHeadNodes = new HashMap<>(); + + public Collection allHeadNodes() { + return mHeadNodes.values(); + } + + public void addHeadNode(ViewNode viewNode) { + mHeadNodes.put(viewNode.getId(), viewNode); + } + + public void removeHeadNode(ViewNode viewNode) { + mHeadNodes.remove(viewNode.getId()); + } + + public ViewNode targetViewNode(String id) { + if (id.equals(mRootNode.getId())) { + return mRootNode; + } + return mHeadNodes.get(id); + } protected DoricContext(Context context, String contextId, String source) { this.mContext = context; diff --git a/Android/doric/src/main/java/pub/doric/DoricRegistry.java b/Android/doric/src/main/java/pub/doric/DoricRegistry.java index b15f70fa..39a1ca5f 100644 --- a/Android/doric/src/main/java/pub/doric/DoricRegistry.java +++ b/Android/doric/src/main/java/pub/doric/DoricRegistry.java @@ -23,6 +23,7 @@ import pub.doric.loader.IDoricJSLoader; import pub.doric.plugin.NavBarPlugin; import pub.doric.plugin.NavigatorPlugin; import pub.doric.plugin.NetworkPlugin; +import pub.doric.plugin.PopoverPlugin; import pub.doric.plugin.ShaderPlugin; import pub.doric.plugin.StoragePlugin; import pub.doric.refresh.RefreshableNode; @@ -87,6 +88,7 @@ public class DoricRegistry { this.registerNativePlugin(StoragePlugin.class); this.registerNativePlugin(NavigatorPlugin.class); this.registerNativePlugin(NavBarPlugin.class); + this.registerNativePlugin(PopoverPlugin.class); this.registerViewNode(RootNode.class); this.registerViewNode(TextNode.class); diff --git a/Android/doric/src/main/java/pub/doric/extension/bridge/DoricBridgeExtension.java b/Android/doric/src/main/java/pub/doric/extension/bridge/DoricBridgeExtension.java index 49464ca8..e968a96d 100644 --- a/Android/doric/src/main/java/pub/doric/extension/bridge/DoricBridgeExtension.java +++ b/Android/doric/src/main/java/pub/doric/extension/bridge/DoricBridgeExtension.java @@ -58,6 +58,10 @@ public class DoricBridgeExtension { return new JavaValue(false); } DoricMethod doricMethod = method.getAnnotation(DoricMethod.class); + if (doricMethod == null) { + DoricLog.e("Cannot find DoricMethod annotation in class:%s,method:%s", module, methodName); + return new JavaValue(false); + } Callable callable = new Callable() { @Override public JavaValue call() throws Exception { diff --git a/Android/doric/src/main/java/pub/doric/plugin/ModalPlugin.java b/Android/doric/src/main/java/pub/doric/plugin/ModalPlugin.java index 18dcd2f9..845a387f 100644 --- a/Android/doric/src/main/java/pub/doric/plugin/ModalPlugin.java +++ b/Android/doric/src/main/java/pub/doric/plugin/ModalPlugin.java @@ -50,9 +50,8 @@ public class ModalPlugin extends DoricJavaPlugin { } @DoricMethod(thread = ThreadMode.UI) - public void toast(JSDecoder decoder) { + public void toast(JSObject jsObject) { try { - JSObject jsObject = decoder.decode().asObject(); String msg = jsObject.getProperty("msg").asString().value(); JSValue gravityVal = jsObject.getProperty("gravity"); int gravity = Gravity.BOTTOM; @@ -77,9 +76,8 @@ public class ModalPlugin extends DoricJavaPlugin { } @DoricMethod(thread = ThreadMode.UI) - public void alert(JSDecoder decoder, final DoricPromise promise) { + public void alert(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue titleVal = jsObject.getProperty("title"); JSValue msgVal = jsObject.getProperty("msg"); JSValue okBtn = jsObject.getProperty("okLabel"); @@ -109,9 +107,8 @@ public class ModalPlugin extends DoricJavaPlugin { @DoricMethod(thread = ThreadMode.UI) - public void confirm(JSDecoder decoder, final DoricPromise promise) { + public void confirm(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue titleVal = jsObject.getProperty("title"); JSValue msgVal = jsObject.getProperty("msg"); JSValue okBtn = jsObject.getProperty("okLabel"); @@ -152,9 +149,8 @@ public class ModalPlugin extends DoricJavaPlugin { @DoricMethod(thread = ThreadMode.UI) - public void prompt(JSDecoder decoder, final DoricPromise promise) { + public void prompt(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue titleVal = jsObject.getProperty("title"); JSValue msgVal = jsObject.getProperty("msg"); JSValue okBtn = jsObject.getProperty("okLabel"); diff --git a/Android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java b/Android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java index 45fdc272..ec36fb34 100644 --- a/Android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java +++ b/Android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java @@ -58,9 +58,8 @@ public class NetworkPlugin extends DoricJavaPlugin { } @DoricMethod - public void request(JSDecoder decoder, final DoricPromise promise) { + public void request(JSObject requestVal, final DoricPromise promise) { try { - JSObject requestVal = decoder.decode().asObject(); String url = requestVal.getProperty("url").asString().value(); String method = requestVal.getProperty("method").asString().value(); JSValue headerVal = requestVal.getProperty("headers"); diff --git a/Android/doric/src/main/java/pub/doric/plugin/PopoverPlugin.java b/Android/doric/src/main/java/pub/doric/plugin/PopoverPlugin.java new file mode 100644 index 00000000..457b168b --- /dev/null +++ b/Android/doric/src/main/java/pub/doric/plugin/PopoverPlugin.java @@ -0,0 +1,139 @@ +package pub.doric.plugin; + +import android.app.Activity; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.github.pengfeizhou.jscore.JSDecoder; +import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; +import com.github.pengfeizhou.jscore.JavaValue; + +import java.util.concurrent.Callable; + +import pub.doric.DoricContext; +import pub.doric.async.AsyncCall; +import pub.doric.async.AsyncResult; +import pub.doric.extension.bridge.DoricMethod; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; +import pub.doric.shader.ViewNode; +import pub.doric.utils.ThreadMode; + +/** + * @Description: pub.doric.plugin + * @Author: pengfei.zhou + * @CreateDate: 2019-11-29 + */ +@DoricPlugin(name = "popover") +public class PopoverPlugin extends DoricJavaPlugin { + private FrameLayout mFullScreenView; + + public PopoverPlugin(DoricContext doricContext) { + super(doricContext); + } + + @DoricMethod + public void show(JSDecoder decoder, final DoricPromise promise) { + try { + final JSObject jsObject = decoder.decode().asObject(); + getDoricContext().getDriver().asyncCall(new Callable() { + @Override + public Object call() throws Exception { + if (mFullScreenView == null) { + mFullScreenView = new FrameLayout(getDoricContext().getContext()); + ViewGroup decorView = (ViewGroup) getDoricContext().getRootNode().getNodeView().getRootView(); + decorView.addView(mFullScreenView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + } + mFullScreenView.setVisibility(View.VISIBLE); + mFullScreenView.bringToFront(); + String viewId = jsObject.getProperty("id").asString().value(); + String type = jsObject.getProperty("type").asString().value(); + ViewNode node = ViewNode.create(getDoricContext(), type); + node.setId(viewId); + node.init(new FrameLayout.LayoutParams(0, 0)); + node.blend(jsObject.getProperty("props").asObject()); + mFullScreenView.addView(node.getNodeView()); + getDoricContext().addHeadNode(node); + return null; + } + }, ThreadMode.UI).setCallback(new AsyncResult.Callback() { + @Override + public void onResult(Object result) { + promise.resolve(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + promise.reject(new JavaValue(t.getLocalizedMessage())); + } + + @Override + public void onFinish() { + + } + }); + } catch (Exception e) { + e.printStackTrace(); + promise.reject(new JavaValue(e.getLocalizedMessage())); + } + } + + @DoricMethod + public void dismiss(final JSValue value, final DoricPromise promise) { + try { + getDoricContext().getDriver().asyncCall(new Callable() { + @Override + public Object call() throws Exception { + if (value.isObject()) { + String viewId = value.asObject().getProperty("id").asString().value(); + ViewNode node = getDoricContext().targetViewNode(viewId); + dismissViewNode(node); + } else { + dismissPopover(); + } + return null; + } + }, ThreadMode.UI).setCallback(new AsyncResult.Callback() { + @Override + public void onResult(Object result) { + promise.resolve(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + promise.reject(new JavaValue(t.getLocalizedMessage())); + } + + @Override + public void onFinish() { + + } + }); + + + } catch (Exception e) { + e.printStackTrace(); + promise.reject(new JavaValue(e.getLocalizedMessage())); + } + } + + private void dismissViewNode(ViewNode node) { + getDoricContext().removeHeadNode(node); + mFullScreenView.removeView(node.getNodeView()); + if (getDoricContext().allHeadNodes().isEmpty()) { + mFullScreenView.setVisibility(View.GONE); + } + } + + private void dismissPopover() { + for (ViewNode node : getDoricContext().allHeadNodes()) { + dismissViewNode(node); + } + } +} diff --git a/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java b/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java index fb1954ce..0dc81e23 100644 --- a/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java +++ b/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java @@ -15,6 +15,8 @@ */ package pub.doric.plugin; +import android.text.TextUtils; + import pub.doric.DoricContext; import pub.doric.async.AsyncResult; import pub.doric.extension.bridge.DoricMethod; @@ -55,9 +57,17 @@ public class ShaderPlugin extends DoricJavaPlugin { getDoricContext().getDriver().asyncCall(new Callable() { @Override public Object call() throws Exception { + String viewId = jsObject.getProperty("id").asString().value(); RootNode rootNode = getDoricContext().getRootNode(); - rootNode.setId(jsObject.getProperty("id").asString().value()); - rootNode.render(jsObject.getProperty("props").asObject()); + if (TextUtils.isEmpty(rootNode.getId())) { + rootNode.setId(viewId); + rootNode.blend(jsObject.getProperty("props").asObject()); + } else { + ViewNode viewNode = getDoricContext().targetViewNode(viewId); + if (viewNode != null) { + viewNode.blend(jsObject.getProperty("props").asObject()); + } + } return null; } }, ThreadMode.UI).setCallback(new AsyncResult.Callback() { @@ -93,7 +103,7 @@ public class ShaderPlugin extends DoricJavaPlugin { ViewNode viewNode = null; for (JSValue value : viewIds) { if (viewNode == null) { - viewNode = getDoricContext().getRootNode(); + viewNode = getDoricContext().targetViewNode(value.asString().value()); } else { if (value.isString() && viewNode instanceof SuperNode) { String viewId = value.asString().value(); diff --git a/Android/doric/src/main/java/pub/doric/plugin/StoragePlugin.java b/Android/doric/src/main/java/pub/doric/plugin/StoragePlugin.java index f5293235..746ffd29 100644 --- a/Android/doric/src/main/java/pub/doric/plugin/StoragePlugin.java +++ b/Android/doric/src/main/java/pub/doric/plugin/StoragePlugin.java @@ -41,9 +41,8 @@ public class StoragePlugin extends DoricJavaPlugin { } @DoricMethod - public void setItem(JSDecoder decoder, final DoricPromise promise) { + public void setItem(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue zone = jsObject.getProperty("zone"); String key = jsObject.getProperty("key").asString().value(); String value = jsObject.getProperty("value").asString().value(); @@ -59,9 +58,8 @@ public class StoragePlugin extends DoricJavaPlugin { } @DoricMethod - public void getItem(JSDecoder decoder, final DoricPromise promise) { + public void getItem(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue zone = jsObject.getProperty("zone"); String key = jsObject.getProperty("key").asString().value(); String prefName = zone.isString() ? PREF_NAME + "_" + zone.asString() : PREF_NAME; @@ -76,9 +74,8 @@ public class StoragePlugin extends DoricJavaPlugin { } @DoricMethod - public void remove(JSDecoder decoder, final DoricPromise promise) { + public void remove(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue zone = jsObject.getProperty("zone"); String key = jsObject.getProperty("key").asString().value(); String prefName = zone.isString() ? PREF_NAME + "_" + zone.asString() : PREF_NAME; @@ -93,9 +90,8 @@ public class StoragePlugin extends DoricJavaPlugin { } @DoricMethod - public void clear(JSDecoder decoder, final DoricPromise promise) { + public void clear(JSObject jsObject, final DoricPromise promise) { try { - JSObject jsObject = decoder.decode().asObject(); JSValue zone = jsObject.getProperty("zone"); if (zone.isString()) { String prefName = PREF_NAME + "_" + zone.asString(); diff --git a/Android/doric/src/main/java/pub/doric/shader/RootNode.java b/Android/doric/src/main/java/pub/doric/shader/RootNode.java index 75f58444..ec8d1539 100644 --- a/Android/doric/src/main/java/pub/doric/shader/RootNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/RootNode.java @@ -53,8 +53,4 @@ public class RootNode extends StackNode { public ViewGroup.LayoutParams getLayoutParams() { return mView.getLayoutParams(); } - - public void render(JSObject props) { - blend(props); - } } diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index 39decdcf..204291be 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -66,6 +66,12 @@ public abstract class ViewNode extends DoricContextHolder { this.mView.setLayoutParams(mLayoutParams); } + public void init(ViewGroup.LayoutParams layoutParams) { + this.mLayoutParams = layoutParams; + this.mView = build(); + this.mView.setLayoutParams(layoutParams); + } + public void setId(String id) { this.mId = id; } @@ -265,6 +271,61 @@ public abstract class ViewNode extends DoricContextHolder { protected void setLayoutConfig(JSObject layoutConfig) { if (mSuperNode != null) { mSuperNode.blendSubLayoutConfig(this, layoutConfig); + } else { + blendLayoutConfig(layoutConfig); + } + } + + private void blendLayoutConfig(JSObject jsObject) { + JSValue margin = jsObject.getProperty("margin"); + JSValue widthSpec = jsObject.getProperty("widthSpec"); + JSValue heightSpec = jsObject.getProperty("heightSpec"); + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if (widthSpec.isNumber()) { + switch (widthSpec.asNumber().toInt()) { + case 1: + layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; + break; + case 2: + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + break; + default: + break; + } + } + if (heightSpec.isNumber()) { + switch (heightSpec.asNumber().toInt()) { + case 1: + layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + break; + case 2: + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + break; + default: + break; + } + } + if (margin.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) { + JSValue topVal = margin.asObject().getProperty("top"); + if (topVal.isNumber()) { + ((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(topVal.asNumber().toFloat()); + } + JSValue leftVal = margin.asObject().getProperty("left"); + if (leftVal.isNumber()) { + ((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(leftVal.asNumber().toFloat()); + } + JSValue rightVal = margin.asObject().getProperty("right"); + if (rightVal.isNumber()) { + ((ViewGroup.MarginLayoutParams) layoutParams).rightMargin = DoricUtils.dp2px(rightVal.asNumber().toFloat()); + } + JSValue bottomVal = margin.asObject().getProperty("bottom"); + if (bottomVal.isNumber()) { + ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = DoricUtils.dp2px(bottomVal.asNumber().toFloat()); + } + } + JSValue jsValue = jsObject.getProperty("alignment"); + if (jsValue.isNumber() && layoutParams instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) layoutParams).gravity = jsValue.asNumber().toInt(); } } 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..b78a5c63 --- /dev/null +++ b/demo/src/PopoverDemo.ts @@ -0,0 +1,44 @@ +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", + }).also(v => { + let idx = 0 + v.onClick = () => { + v.bgColor = colors[(++idx) % colors.length] + } + modal(context).toast('Dismissed after 3 seconds') + setTimeout(() => { + popover(context).dismiss() + }, 3000) + })) + } + } 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/DoricContext.h b/iOS/Pod/Classes/DoricContext.h index af6e814d..b0bf324f 100644 --- a/iOS/Pod/Classes/DoricContext.h +++ b/iOS/Pod/Classes/DoricContext.h @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN +@class DoricViewNode; @class DoricRootNode; @interface DoricContext : NSObject @@ -35,10 +36,11 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) NSString *contextId; @property(nonatomic, strong) DoricDriver *driver; @property(nonatomic, strong) NSMutableDictionary *pluginInstanceMap; -@property(nonatomic, strong) DoricRootNode *rootNode; @property(nonatomic, strong) NSString *source; @property(nonatomic, strong) NSString *script;; @property(nonatomic, strong) NSMutableDictionary *initialParams; +@property(nonatomic, strong) DoricRootNode *rootNode; +@property(nonatomic, strong) NSMutableDictionary *headNodes; - (instancetype)initWithScript:(NSString *)script source:(NSString *)source; @@ -56,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)onHidden; +- (DoricViewNode *)targetViewNode:(NSString *)viewId; @end NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/DoricContext.m b/iOS/Pod/Classes/DoricContext.m index fa07d1df..09e946b6 100644 --- a/iOS/Pod/Classes/DoricContext.m +++ b/iOS/Pod/Classes/DoricContext.m @@ -31,9 +31,11 @@ @implementation DoricContext - (instancetype)initWithScript:(NSString *)script source:(NSString *)source { if (self = [super init]) { _driver = [DoricDriver instance]; - _pluginInstanceMap = [[NSMutableDictionary alloc] init]; + _pluginInstanceMap = [NSMutableDictionary new]; [[DoricContextManager instance] createContext:self script:script source:source]; - _rootNode = [[DoricRootNode alloc] initWithContext:self]; + _headNodes = [NSMutableDictionary new]; + DoricRootNode *rootNode = [[DoricRootNode alloc] initWithContext:self]; + _rootNode = rootNode; _script = script; _source = source; _initialParams = [@{@"width": @(0), @"height": @(0)} mutableCopy]; @@ -42,6 +44,14 @@ - (instancetype)initWithScript:(NSString *)script source:(NSString *)source { return self; } +- (DoricViewNode *)targetViewNode:(NSString *)viewId { + if ([self.rootNode.viewId isEqualToString:viewId]) { + return self.rootNode; + } else { + return self.headNodes[viewId]; + } +} + - (void)dealloc { [self callEntity:DORIC_ENTITY_DESTROY, nil]; [[DoricContextManager instance] destroyContext:self]; diff --git a/iOS/Pod/Classes/DoricRegistry.m b/iOS/Pod/Classes/DoricRegistry.m index 48f6ac46..86f1f800 100644 --- a/iOS/Pod/Classes/DoricRegistry.m +++ b/iOS/Pod/Classes/DoricRegistry.m @@ -40,6 +40,7 @@ #import "DoricRefreshableNode.h" #import "DoricFlowLayoutItemNode.h" #import "DoricFlowLayoutNode.h" +#import "DoricPopoverPlugin.h" @interface DoricRegistry () @@ -68,6 +69,7 @@ - (void)innerRegister { [self registerNativePlugin:DoricStoragePlugin.class withName:@"storage"]; [self registerNativePlugin:DoricNavigatorPlugin.class withName:@"navigator"]; [self registerNativePlugin:DoricNavBarPlugin.class withName:@"navbar"]; + [self registerNativePlugin:DoricPopoverPlugin.class withName:@"popover"]; [self registerViewNode:DoricStackNode.class withName:@"Stack"]; [self registerViewNode:DoricVLayoutNode.class withName:@"VLayout"]; diff --git a/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.h b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.h new file mode 100644 index 00000000..c9eaaf8f --- /dev/null +++ b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.h @@ -0,0 +1,9 @@ +// +// Created by pengfei.zhou on 2019/11/28. +// + +#import +#import "DoricNativePlugin.h" + +@interface DoricPopoverPlugin : DoricNativePlugin +@end \ No newline at end of file diff --git a/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m new file mode 100644 index 00000000..43125f51 --- /dev/null +++ b/iOS/Pod/Classes/Plugin/DoricPopoverPlugin.m @@ -0,0 +1,70 @@ +// +// Created by pengfei.zhou on 2019/11/28. +// + +#import "DoricPopoverPlugin.h" +#import "DoricRootNode.h" +#import "Doric.h" + +@interface DoricPopoverPlugin () +@property(nonatomic, strong) UIView *fullScreenView; +@end + +@implementation DoricPopoverPlugin +- (void)show:(NSDictionary *)params withPromise:(DoricPromise *)promise { + dispatch_async(dispatch_get_main_queue(), ^{ + UIView *superView = [UIApplication sharedApplication].windows.lastObject; + if (!self.fullScreenView) { + self.fullScreenView = [[DoricStackView new] also:^(UIView *it) { + it.width = superView.width; + it.height = superView.height; + it.top = it.left = 0; + [superView addSubview:it]; + }]; + } + [superView bringSubviewToFront:self.fullScreenView]; + self.fullScreenView.hidden = NO; + NSString *viewId = params[@"id"]; + NSString *type = params[@"type"]; + DoricViewNode *viewNode = [self.doricContext targetViewNode:viewId]; + if (!viewNode) { + viewNode = [[DoricViewNode create:self.doricContext withType:type] also:^(DoricViewNode *it) { + it.viewId = viewId; + [it initWithSuperNode:nil]; + it.view.layoutConfig = [DoricLayoutConfig new]; + [self.fullScreenView addSubview:it.view]; + self.doricContext.headNodes[viewId] = it; + }]; + } + [viewNode blend:params[@"props"]]; + [promise resolve:nil]; + }); +} + +- (void)dismiss:(NSDictionary *)params withPromise:(DoricPromise *)promise { + NSString *viewId = params[@"id"]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (viewId) { + DoricViewNode *viewNode = [self.doricContext targetViewNode:viewId]; + [self dismissViewNode:viewNode]; + } else { + [self dismissPopover]; + } + [promise resolve:nil]; + }); +} + +- (void)dismissViewNode:(DoricViewNode *)node { + [self.doricContext.headNodes removeObjectForKey:node.viewId]; + [node.view removeFromSuperview]; + if (self.doricContext.headNodes.count == 0) { + self.fullScreenView.hidden = YES; + } +} + +- (void)dismissPopover { + for (DoricViewNode *node in self.doricContext.headNodes.allValues) { + [self dismissViewNode:node]; + } +} +@end diff --git a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m index 94ff77e7..22f989e2 100644 --- a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m +++ b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m @@ -36,10 +36,16 @@ - (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 == nil) { + self.doricContext.rootNode.viewId = viewId; + [self.doricContext.rootNode blend:argument[@"props"]]; + } else { + DoricViewNode *viewNode = [self.doricContext targetViewNode:viewId]; + [viewNode blend:argument[@"props"]]; + } }); } @@ -50,7 +56,7 @@ - (id)command:(NSDictionary *)argument withPromise:(DoricPromise *)promise { DoricViewNode *viewNode = nil; for (NSString *viewId in viewIds) { if (!viewNode) { - viewNode = self.doricContext.rootNode; + viewNode = [self.doricContext targetViewNode:viewId]; } else { if ([viewNode isKindOfClass:[DoricSuperNode class]]) { viewNode = [((DoricSuperNode *) viewNode) subNodeWithViewId:viewId]; diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.h b/iOS/Pod/Classes/Shader/DoricRootNode.h index 58bff00a..32abe898 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.h +++ b/iOS/Pod/Classes/Shader/DoricRootNode.h @@ -28,7 +28,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)setupRootView:(DoricStackView *)view; -- (void)render:(NSDictionary *)props; @end NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.m b/iOS/Pod/Classes/Shader/DoricRootNode.m index 7272a937..669c6bbb 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.m +++ b/iOS/Pod/Classes/Shader/DoricRootNode.m @@ -27,9 +27,6 @@ - (void)setupRootView:(DoricStackView *)view { self.view = view; } -- (void)render:(NSDictionary *)props { - [self blend:props]; -} - (void)requestLayout { [self.view setNeedsLayout]; } 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/modal.ts b/js-framework/src/native/modal.ts index 5d56541d..b50bcc0f 100644 --- a/js-framework/src/native/modal.ts +++ b/js-framework/src/native/modal.ts @@ -1,4 +1,3 @@ - /* * Copyright [2019] [Doric.Pub] * diff --git a/js-framework/src/native/popover.ts b/js-framework/src/native/popover.ts new file mode 100644 index 00000000..ff6834da --- /dev/null +++ b/js-framework/src/native/popover.ts @@ -0,0 +1,44 @@ +/* + * Copyright [2019] [Doric.Pub] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +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) => { + if (panel) { + panel.addHeadView(view) + } + return context.popover.show(view.toModel()) + }, + dismiss: (view: View | undefined = undefined) => { + if (panel) { + if (view) { + panel.removeHeadView(view) + } else { + panel.clearHeadViews() + } + } + return context.popover.dismiss(view ? { id: view.viewId } : undefined) + }, + } +} \ No newline at end of file diff --git a/js-framework/src/ui/panel.ts b/js-framework/src/ui/panel.ts index 30508907..7c488b1b 100644 --- a/js-framework/src/ui/panel.ts +++ b/js-framework/src/ui/panel.ts @@ -58,6 +58,10 @@ export abstract class Panel { } } + clearHeadViews() { + this.headviews.clear() + } + getRootView() { return this.__root__ } @@ -65,9 +69,6 @@ export abstract class Panel { getInitData() { return this.__data__ } - constructor() { - this.addHeadView(this.__root__) - } @NativeCall private __init__(frame: Frame, data: any) { @@ -120,6 +121,9 @@ export abstract class Panel { private retrospectView(ids: string[]): View | undefined { return ids.reduce((acc: View | undefined, cur) => { if (acc === undefined) { + if (cur === this.__root__.viewId) { + return this.__root__ + } return this.headviews.get(cur) } else { if (Reflect.has(acc, "subviewById")) { @@ -138,6 +142,9 @@ export abstract class Panel { private hookBeforeNativeCall() { this.__root__.clean() + for (let v of this.headviews.values()) { + v.clean() + } } private hookAfterNativeCall() { @@ -147,6 +154,12 @@ export abstract class Panel { 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) + } + } } } \ No newline at end of file