Merge branch 'feature/slider' into 'master'
Feature/slider See merge request !15
This commit is contained in:
commit
3093fe0923
@ -28,6 +28,8 @@ import pub.doric.shader.StackNode;
|
|||||||
import pub.doric.shader.TextNode;
|
import pub.doric.shader.TextNode;
|
||||||
import pub.doric.shader.VLayoutNode;
|
import pub.doric.shader.VLayoutNode;
|
||||||
import pub.doric.shader.ViewNode;
|
import pub.doric.shader.ViewNode;
|
||||||
|
import pub.doric.shader.slider.SlideItemNode;
|
||||||
|
import pub.doric.shader.slider.SliderNode;
|
||||||
import pub.doric.utils.DoricMetaInfo;
|
import pub.doric.utils.DoricMetaInfo;
|
||||||
import pub.doric.plugin.DoricJavaPlugin;
|
import pub.doric.plugin.DoricJavaPlugin;
|
||||||
import pub.doric.plugin.ModalPlugin;
|
import pub.doric.plugin.ModalPlugin;
|
||||||
@ -72,6 +74,8 @@ public class DoricRegistry {
|
|||||||
this.registerViewNode(ListNode.class);
|
this.registerViewNode(ListNode.class);
|
||||||
this.registerViewNode(ListItemNode.class);
|
this.registerViewNode(ListItemNode.class);
|
||||||
this.registerViewNode(ScrollerNode.class);
|
this.registerViewNode(ScrollerNode.class);
|
||||||
|
this.registerViewNode(SliderNode.class);
|
||||||
|
this.registerViewNode(SlideItemNode.class);
|
||||||
initRegistry(this);
|
initRegistry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,4 +148,33 @@ public abstract class SuperNode<V extends View> extends ViewNode<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean viewIdIsEqual(JSObject src, JSObject target) {
|
||||||
|
String srcId = src.asObject().getProperty("id").asString().value();
|
||||||
|
String targetId = target.asObject().getProperty("id").asString().value();
|
||||||
|
return srcId.equals(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void recursiveMixin(JSObject src, JSObject target) {
|
||||||
|
JSObject srcProps = src.getProperty("props").asObject();
|
||||||
|
JSObject targetProps = target.getProperty("props").asObject();
|
||||||
|
JSValue oriSubviews = targetProps.getProperty("subviews");
|
||||||
|
for (String key : srcProps.propertySet()) {
|
||||||
|
JSValue jsValue = srcProps.getProperty(key);
|
||||||
|
if ("subviews".equals(key) && jsValue.isArray()) {
|
||||||
|
JSValue[] subviews = jsValue.asArray().toArray();
|
||||||
|
for (JSValue subview : subviews) {
|
||||||
|
if (oriSubviews.isArray()) {
|
||||||
|
for (JSValue targetSubview : oriSubviews.asArray().toArray()) {
|
||||||
|
if (viewIdIsEqual(subview.asObject(), targetSubview.asObject())) {
|
||||||
|
recursiveMixin(subview.asObject(), targetSubview.asObject());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
targetProps.asObject().setProperty(key, jsValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ import pub.doric.shader.ViewNode;
|
|||||||
* @Author: pengfei.zhou
|
* @Author: pengfei.zhou
|
||||||
* @CreateDate: 2019-11-12
|
* @CreateDate: 2019-11-12
|
||||||
*/
|
*/
|
||||||
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
|
class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
|
||||||
|
|
||||||
private final ListNode listNode;
|
private final ListNode listNode;
|
||||||
String renderItemFuncId;
|
String renderItemFuncId;
|
||||||
|
@ -44,7 +44,17 @@ public class ListNode extends SuperNode<RecyclerView> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blendSubNode(JSObject subProperties) {
|
protected void blendSubNode(JSObject subProperties) {
|
||||||
listAdapter.blendSubNode(subProperties);
|
String viewId = subProperties.getProperty("id").asString().value();
|
||||||
|
ViewNode node = getSubNodeById(viewId);
|
||||||
|
if (node != null) {
|
||||||
|
node.blend(subProperties.getProperty("props").asObject());
|
||||||
|
} else {
|
||||||
|
JSObject oldModel = getSubModel(viewId);
|
||||||
|
if (oldModel != null) {
|
||||||
|
recursiveMixin(subProperties, oldModel);
|
||||||
|
}
|
||||||
|
listAdapter.blendSubNode(subProperties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,7 +91,7 @@ public class ListNode extends SuperNode<RecyclerView> {
|
|||||||
clearSubModel();
|
clearSubModel();
|
||||||
break;
|
break;
|
||||||
case "batchCount":
|
case "batchCount":
|
||||||
this.listAdapter.batchCount = 15;
|
this.listAdapter.batchCount = prop.asNumber().toInt();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.blend(view, name, prop);
|
super.blend(view, name, prop);
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.slider;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSArray;
|
||||||
|
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||||
|
import com.github.pengfeizhou.jscore.JSNull;
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.async.AsyncResult;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
class SlideAdapter extends RecyclerView.Adapter<SlideAdapter.DoricViewHolder> {
|
||||||
|
|
||||||
|
private final SliderNode sliderNode;
|
||||||
|
int itemCount = 0;
|
||||||
|
int batchCount = 3;
|
||||||
|
SparseArray<String> itemValues = new SparseArray<>();
|
||||||
|
|
||||||
|
SlideAdapter(SliderNode sliderNode) {
|
||||||
|
this.sliderNode = sliderNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
SlideItemNode node = (SlideItemNode) ViewNode.create(sliderNode.getDoricContext(), "SlideItem");
|
||||||
|
node.init(sliderNode);
|
||||||
|
return new DoricViewHolder(node, node.getDoricLayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||||
|
JSValue jsValue = getItemModel(position);
|
||||||
|
if (jsValue.isObject()) {
|
||||||
|
JSObject jsObject = jsValue.asObject();
|
||||||
|
holder.slideItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||||
|
holder.slideItemNode.blend(jsObject.getProperty("props").asObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
JSValue value = getItemModel(position);
|
||||||
|
if (value.isObject()) {
|
||||||
|
if (value.asObject().getProperty("identifier").isString()) {
|
||||||
|
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getItemViewType(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSValue getItemModel(final int position) {
|
||||||
|
String id = itemValues.get(position);
|
||||||
|
if (TextUtils.isEmpty(id)) {
|
||||||
|
AsyncResult<JSDecoder> asyncResult = sliderNode.callJSResponse(
|
||||||
|
"renderBunchedItems",
|
||||||
|
position,
|
||||||
|
batchCount);
|
||||||
|
try {
|
||||||
|
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||||
|
JSValue result = jsDecoder.decode();
|
||||||
|
if (result.isArray()) {
|
||||||
|
JSArray jsArray = result.asArray();
|
||||||
|
for (int i = 0; i < jsArray.size(); i++) {
|
||||||
|
JSObject itemModel = jsArray.get(i).asObject();
|
||||||
|
String itemId = itemModel.getProperty("id").asString().value();
|
||||||
|
itemValues.put(i + position, itemId);
|
||||||
|
sliderNode.setSubModel(itemId, itemModel);
|
||||||
|
}
|
||||||
|
return sliderNode.getSubModel(itemValues.get(position));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
JSObject childModel = sliderNode.getSubModel(id);
|
||||||
|
if (childModel == null) {
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
return childModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void blendSubNode(JSObject subProperties) {
|
||||||
|
for (int i = 0; i < itemValues.size(); i++) {
|
||||||
|
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||||
|
notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
SlideItemNode slideItemNode;
|
||||||
|
|
||||||
|
DoricViewHolder(SlideItemNode node, @NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
slideItemNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.slider;
|
||||||
|
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.StackNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "SlideItem")
|
||||||
|
public class SlideItemNode extends StackNode {
|
||||||
|
public String identifier = "";
|
||||||
|
|
||||||
|
public SlideItemNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.mReusable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||||
|
if ("identifier".equals(name)) {
|
||||||
|
this.identifier = prop.asString().value();
|
||||||
|
} else {
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
|
super.blend(jsObject);
|
||||||
|
getDoricLayer().getLayoutParams().width = getLayoutParams().width;
|
||||||
|
getDoricLayer().getLayoutParams().height = getLayoutParams().height;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.slider;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.PagerSnapHelper;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.SuperNode;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: pub.doric.shader
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-19
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "Slider")
|
||||||
|
public class SliderNode extends SuperNode<RecyclerView> {
|
||||||
|
private final SlideAdapter slideAdapter;
|
||||||
|
|
||||||
|
public SliderNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.slideAdapter = new SlideAdapter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView build() {
|
||||||
|
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||||
|
|
||||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||||
|
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||||
|
recyclerView.setLayoutManager(layoutManager);
|
||||||
|
PagerSnapHelper mPagerSnapHelper = new PagerSnapHelper();
|
||||||
|
mPagerSnapHelper.attachToRecyclerView(recyclerView);
|
||||||
|
recyclerView.setAdapter(this.slideAdapter);
|
||||||
|
return recyclerView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewNode getSubNodeById(String id) {
|
||||||
|
RecyclerView.LayoutManager manager = mView.getLayoutManager();
|
||||||
|
if (manager == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < manager.getChildCount(); i++) {
|
||||||
|
View view = manager.getChildAt(i);
|
||||||
|
if (view == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SlideAdapter.DoricViewHolder viewHolder = (SlideAdapter.DoricViewHolder) mView.getChildViewHolder(view);
|
||||||
|
if (id.equals(viewHolder.slideItemNode.getId())) {
|
||||||
|
return viewHolder.slideItemNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blendSubNode(JSObject subProperties) {
|
||||||
|
String viewId = subProperties.getProperty("id").asString().value();
|
||||||
|
ViewNode node = getSubNodeById(viewId);
|
||||||
|
if (node != null) {
|
||||||
|
node.blend(subProperties.getProperty("props").asObject());
|
||||||
|
} else {
|
||||||
|
JSObject oldModel = getSubModel(viewId);
|
||||||
|
if (oldModel != null) {
|
||||||
|
recursiveMixin(subProperties, oldModel);
|
||||||
|
}
|
||||||
|
slideAdapter.blendSubNode(subProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
|
super.blend(jsObject);
|
||||||
|
if (mView != null) {
|
||||||
|
mView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
slideAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||||
|
switch (name) {
|
||||||
|
case "itemCount":
|
||||||
|
this.slideAdapter.itemCount = prop.asNumber().toInt();
|
||||||
|
break;
|
||||||
|
case "renderItem":
|
||||||
|
// If reset renderItem,should reset native cache.
|
||||||
|
this.slideAdapter.itemValues.clear();
|
||||||
|
clearSubModel();
|
||||||
|
break;
|
||||||
|
case "batchCount":
|
||||||
|
this.slideAdapter.batchCount = prop.asNumber().toInt();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,5 @@ export default [
|
|||||||
'src/Snake',
|
'src/Snake',
|
||||||
'src/ListDemo',
|
'src/ListDemo',
|
||||||
'src/ScrollerDemo',
|
'src/ScrollerDemo',
|
||||||
|
'src/SliderDemo',
|
||||||
]
|
]
|
@ -54,6 +54,12 @@ class ListPanel extends Panel {
|
|||||||
textSize: 20,
|
textSize: 20,
|
||||||
height: 50,
|
height: 50,
|
||||||
bgColor: Color.parse('#00ffff'),
|
bgColor: Color.parse('#00ffff'),
|
||||||
|
}).also(it => {
|
||||||
|
let start = 0
|
||||||
|
it.onClick = () => {
|
||||||
|
log(`clicked text:${start}`)
|
||||||
|
it.text = `${start++}`
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]).also(it => {
|
]).also(it => {
|
||||||
it.layoutConfig = {
|
it.layoutConfig = {
|
||||||
|
60
demo/src/SliderDemo.ts
Normal file
60
demo/src/SliderDemo.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, slider, slideItem } from "doric";
|
||||||
|
const colors = [
|
||||||
|
"#f0932b",
|
||||||
|
"#eb4d4b",
|
||||||
|
"#6ab04c",
|
||||||
|
"#e056fd",
|
||||||
|
"#686de0",
|
||||||
|
"#30336b",
|
||||||
|
]
|
||||||
|
@Entry
|
||||||
|
class SliderPanel extends Panel {
|
||||||
|
build(rootView: Group): void {
|
||||||
|
rootView.addChild(vlayout([
|
||||||
|
text({
|
||||||
|
text: "SliderDemo",
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
|
},
|
||||||
|
textSize: 30,
|
||||||
|
textColor: Color.parse("#535c68"),
|
||||||
|
bgColor: Color.parse("#dff9fb"),
|
||||||
|
textAlignment: gravity().center(),
|
||||||
|
height: 50,
|
||||||
|
}),
|
||||||
|
slider({
|
||||||
|
itemCount: 100,
|
||||||
|
renderPage: (idx) => {
|
||||||
|
return slideItem(text({
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
|
alignment: gravity().center(),
|
||||||
|
},
|
||||||
|
text: `Page At Line ${idx}`,
|
||||||
|
textAlignment: gravity().center(),
|
||||||
|
textColor: Color.parse("#ffffff"),
|
||||||
|
textSize: 20,
|
||||||
|
height: 300,
|
||||||
|
bgColor: Color.parse(colors[idx % colors.length]),
|
||||||
|
}).also(it => {
|
||||||
|
let start = idx
|
||||||
|
it.onClick = () => {
|
||||||
|
it.bgColor = Color.parse(colors[++start % colors.length])
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]).also(it => {
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.AT_MOST,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
- (void)connectDevKit:(NSString *)url;
|
- (void)connectDevKit:(NSString *)url;
|
||||||
|
|
||||||
- (void)disconnectDevKit;
|
- (void)disconnectDevKit;
|
||||||
|
|
||||||
|
- (void)ensureSyncInMainQueue:(dispatch_block_t)block;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -183,4 +183,11 @@ - (void)disconnectDevKit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)ensureSyncInMainQueue:(dispatch_block_t)block {
|
||||||
|
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {
|
||||||
|
block();
|
||||||
|
} else {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), block);
|
||||||
|
}
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#import "DoricListNode.h"
|
#import "DoricListNode.h"
|
||||||
#import "DoricListItemNode.h"
|
#import "DoricListItemNode.h"
|
||||||
#import "DoricScrollerNode.h"
|
#import "DoricScrollerNode.h"
|
||||||
|
#import "DoricSliderNode.h"
|
||||||
|
#import "DoricSlideItemNode.h"
|
||||||
|
|
||||||
@interface DoricRegistry ()
|
@interface DoricRegistry ()
|
||||||
|
|
||||||
@ -64,6 +66,8 @@ - (void)innerRegister {
|
|||||||
[self registerViewNode:DoricListNode.class withName:@"List"];
|
[self registerViewNode:DoricListNode.class withName:@"List"];
|
||||||
[self registerViewNode:DoricListItemNode.class withName:@"ListItem"];
|
[self registerViewNode:DoricListItemNode.class withName:@"ListItem"];
|
||||||
[self registerViewNode:DoricScrollerNode.class withName:@"Scroller"];
|
[self registerViewNode:DoricScrollerNode.class withName:@"Scroller"];
|
||||||
|
[self registerViewNode:DoricSliderNode.class withName:@"Slider"];
|
||||||
|
[self registerViewNode:DoricSlideItemNode.class withName:@"SlideItem"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name {
|
- (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name {
|
||||||
|
@ -29,6 +29,23 @@ @interface DoricTableViewCell : UITableViewCell
|
|||||||
@implementation DoricTableViewCell
|
@implementation DoricTableViewCell
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface DoricTableView : UITableView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricTableView
|
||||||
|
- (CGSize)sizeThatFits:(CGSize)size {
|
||||||
|
if (self.subviews.count > 0) {
|
||||||
|
CGFloat width = size.width;
|
||||||
|
for (UIView *child in self.subviews) {
|
||||||
|
width = MAX(child.width, width);
|
||||||
|
}
|
||||||
|
return CGSizeMake(width, size.width);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface DoricListNode () <UITableViewDataSource, UITableViewDelegate>
|
@interface DoricListNode () <UITableViewDataSource, UITableViewDelegate>
|
||||||
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSString *> *itemViewIds;
|
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSString *> *itemViewIds;
|
||||||
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSNumber *> *itemHeights;
|
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSNumber *> *itemHeights;
|
||||||
@ -47,7 +64,7 @@ - (instancetype)initWithContext:(DoricContext *)doricContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (UITableView *)build {
|
- (UITableView *)build {
|
||||||
return [[UITableView new] also:^(UITableView *it) {
|
return [[DoricTableView new] also:^(UITableView *it) {
|
||||||
it.dataSource = self;
|
it.dataSource = self;
|
||||||
it.delegate = self;
|
it.delegate = self;
|
||||||
it.separatorStyle = UITableViewCellSeparatorStyleNone;
|
it.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||||
@ -129,15 +146,23 @@ - (NSDictionary *)itemModelAt:(NSUInteger)position {
|
|||||||
|
|
||||||
- (void)blendSubNode:(NSDictionary *)subModel {
|
- (void)blendSubNode:(NSDictionary *)subModel {
|
||||||
NSString *viewId = subModel[@"id"];
|
NSString *viewId = subModel[@"id"];
|
||||||
[self.itemViewIds enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) {
|
DoricViewNode *viewNode = [self subNodeWithViewId:viewId];
|
||||||
if ([viewId isEqualToString:obj]) {
|
if (viewNode) {
|
||||||
*stop = YES;
|
[viewNode blend:subModel[@"props"]];
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[key integerValue] inSection:0];
|
} else {
|
||||||
[UIView performWithoutAnimation:^{
|
NSMutableDictionary *model = [[self subModelOf:viewId] mutableCopy];
|
||||||
[self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
[self recursiveMixin:subModel to:model];
|
||||||
}];
|
[self setSubModel:model in:viewId];
|
||||||
}
|
[self.itemViewIds enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) {
|
||||||
}];
|
if ([viewId isEqualToString:obj]) {
|
||||||
|
*stop = YES;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[key integerValue] inSection:0];
|
||||||
|
[UIView performWithoutAnimation:^{
|
||||||
|
[self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)callItem:(NSUInteger)position height:(CGFloat)height {
|
- (void)callItem:(NSUInteger)position height:(CGFloat)height {
|
||||||
@ -154,7 +179,7 @@ - (void)callItem:(NSUInteger)position height:(CGFloat)height {
|
|||||||
|
|
||||||
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
|
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
|
||||||
__block DoricViewNode *ret = nil;
|
__block DoricViewNode *ret = nil;
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
[self.doricContext.driver ensureSyncInMainQueue:^{
|
||||||
for (UITableViewCell *tableViewCell in self.view.visibleCells) {
|
for (UITableViewCell *tableViewCell in self.view.visibleCells) {
|
||||||
if ([tableViewCell isKindOfClass:[DoricTableViewCell class]]) {
|
if ([tableViewCell isKindOfClass:[DoricTableViewCell class]]) {
|
||||||
DoricListItemNode *node = ((DoricTableViewCell *) tableViewCell).doricListItemNode;
|
DoricListItemNode *node = ((DoricTableViewCell *) tableViewCell).doricListItemNode;
|
||||||
@ -164,7 +189,7 @@ - (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}];
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
//
|
//
|
||||||
// Created by pengfei.zhou on 2019/11/19.
|
// DoricScrollerNode.h
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "DoricSuperNode.h"
|
#import "DoricSuperNode.h"
|
||||||
|
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
//
|
//
|
||||||
// Created by pengfei.zhou on 2019/11/19.
|
// DoricScrollerNode.m
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "DoricScrollerNode.h"
|
#import "DoricScrollerNode.h"
|
||||||
#import "DoricExtensions.h"
|
#import "DoricExtensions.h"
|
||||||
|
|
||||||
|
26
iOS/Pod/Classes/Shader/DoricSlideItemNode.h
Normal file
26
iOS/Pod/Classes/Shader/DoricSlideItemNode.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// DoricSlideItemNode.h
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
|
//
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "DoricStackNode.h"
|
||||||
|
|
||||||
|
@interface DoricSlideItemNode : DoricStackNode
|
||||||
|
@end
|
47
iOS/Pod/Classes/Shader/DoricSlideItemNode.m
Normal file
47
iOS/Pod/Classes/Shader/DoricSlideItemNode.m
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// DoricSlideItemNode.m
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
|
//
|
||||||
|
#import "DoricSlideItemNode.h"
|
||||||
|
|
||||||
|
@interface DoricSlideItemView : DoricStackView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricSlideItemView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricSlideItemNode
|
||||||
|
|
||||||
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
|
if (self = [super initWithContext:doricContext]) {
|
||||||
|
self.reusable = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initWithSuperNode:(DoricSuperNode *)superNode {
|
||||||
|
[super initWithSuperNode:superNode];
|
||||||
|
self.reusable = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DoricStackView *)build {
|
||||||
|
return [DoricSlideItemView new];
|
||||||
|
}
|
||||||
|
@end
|
27
iOS/Pod/Classes/Shader/DoricSliderNode.h
Normal file
27
iOS/Pod/Classes/Shader/DoricSliderNode.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// DoricSliderNode.h
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
|
//
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "DoricSuperNode.h"
|
||||||
|
|
||||||
|
@interface DoricSliderNode : DoricSuperNode<UICollectionView *>
|
||||||
|
@end
|
190
iOS/Pod/Classes/Shader/DoricSliderNode.m
Normal file
190
iOS/Pod/Classes/Shader/DoricSliderNode.m
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// DoricSliderNode.m
|
||||||
|
// Doric
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/19.
|
||||||
|
//
|
||||||
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
|
#import "DoricSliderNode.h"
|
||||||
|
#import "Doric.h"
|
||||||
|
#import "DoricSlideItemNode.h"
|
||||||
|
|
||||||
|
@interface DoricCollectionViewCell : UICollectionViewCell
|
||||||
|
@property(nonatomic, strong) DoricSlideItemNode *doricSlideItemNode;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricCollectionViewCell
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface DoricSliderNode () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||||
|
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSString *> *itemViewIds;
|
||||||
|
@property(nonatomic, assign) NSUInteger itemCount;
|
||||||
|
@property(nonatomic, assign) NSUInteger batchCount;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface DoricCollectionView : UICollectionView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricCollectionView
|
||||||
|
- (CGSize)sizeThatFits:(CGSize)size {
|
||||||
|
if (self.subviews.count > 0) {
|
||||||
|
CGFloat height = size.height;
|
||||||
|
for (UIView *child in self.subviews) {
|
||||||
|
height = MAX(child.height, height);
|
||||||
|
}
|
||||||
|
return CGSizeMake(height, size.height);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricSliderNode
|
||||||
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
|
if (self = [super initWithContext:doricContext]) {
|
||||||
|
_itemViewIds = [NSMutableDictionary new];
|
||||||
|
_batchCount = 15;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UICollectionView *)build {
|
||||||
|
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||||
|
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
|
||||||
|
|
||||||
|
return [[[DoricCollectionView alloc] initWithFrame:CGRectZero
|
||||||
|
collectionViewLayout:flowLayout]
|
||||||
|
also:^(UICollectionView *it) {
|
||||||
|
it.backgroundColor = [UIColor whiteColor];
|
||||||
|
it.pagingEnabled = YES;
|
||||||
|
it.delegate = self;
|
||||||
|
it.dataSource = self;
|
||||||
|
[it registerClass:[DoricCollectionViewCell class] forCellWithReuseIdentifier:@"doricCell"];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValue:(id)prop {
|
||||||
|
if ([@"itemCount" isEqualToString:name]) {
|
||||||
|
self.itemCount = [prop unsignedIntegerValue];
|
||||||
|
} else if ([@"renderPage" isEqualToString:name]) {
|
||||||
|
[self.itemViewIds removeAllObjects];
|
||||||
|
[self clearSubModel];
|
||||||
|
} else if ([@"batchCount" isEqualToString:name]) {
|
||||||
|
self.batchCount = [prop unsignedIntegerValue];
|
||||||
|
} else {
|
||||||
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||||
|
return self.itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
return CGSizeMake(self.view.width, self.view.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
NSUInteger position = (NSUInteger) indexPath.row;
|
||||||
|
NSDictionary *model = [self itemModelAt:position];
|
||||||
|
NSDictionary *props = model[@"props"];
|
||||||
|
DoricCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"doricCell" forIndexPath:indexPath];
|
||||||
|
if (!cell.doricSlideItemNode) {
|
||||||
|
DoricSlideItemNode *slideItemNode = [[DoricSlideItemNode alloc] initWithContext:self.doricContext];
|
||||||
|
[slideItemNode initWithSuperNode:self];
|
||||||
|
cell.doricSlideItemNode = slideItemNode;
|
||||||
|
[cell.contentView addSubview:slideItemNode.view];
|
||||||
|
}
|
||||||
|
DoricSlideItemNode *node = cell.doricSlideItemNode;
|
||||||
|
node.viewId = model[@"id"];
|
||||||
|
[node blend:props];
|
||||||
|
[node.view setNeedsLayout];
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)itemModelAt:(NSUInteger)position {
|
||||||
|
NSString *viewId = self.itemViewIds[@(position)];
|
||||||
|
if (viewId && viewId.length > 0) {
|
||||||
|
return [self subModelOf:viewId];
|
||||||
|
} else {
|
||||||
|
DoricAsyncResult *result = [self callJSResponse:@"renderBunchedItems", @(position), @(self.batchCount), nil];
|
||||||
|
JSValue *models = [result waitUntilResult];
|
||||||
|
NSArray *array = [models toArray];
|
||||||
|
[array enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
|
||||||
|
NSString *thisViewId = obj[@"id"];
|
||||||
|
[self setSubModel:obj in:thisViewId];
|
||||||
|
NSUInteger pos = position + idx;
|
||||||
|
self.itemViewIds[@(pos)] = thisViewId;
|
||||||
|
}];
|
||||||
|
return array[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
|
||||||
|
__block DoricViewNode *ret = nil;
|
||||||
|
[self.doricContext.driver ensureSyncInMainQueue:^{
|
||||||
|
for (UICollectionViewCell *collectionViewCell in self.view.visibleCells) {
|
||||||
|
if ([collectionViewCell isKindOfClass:[DoricCollectionViewCell class]]) {
|
||||||
|
DoricSlideItemNode *node = ((DoricCollectionViewCell *) collectionViewCell).doricSlideItemNode;
|
||||||
|
if ([viewId isEqualToString:node.viewId]) {
|
||||||
|
ret = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendSubNode:(NSDictionary *)subModel {
|
||||||
|
NSString *viewId = subModel[@"id"];
|
||||||
|
DoricViewNode *viewNode = [self subNodeWithViewId:viewId];
|
||||||
|
if (viewNode) {
|
||||||
|
[viewNode blend:subModel[@"props"]];
|
||||||
|
} else {
|
||||||
|
NSMutableDictionary *model = [[self subModelOf:viewId] mutableCopy];
|
||||||
|
[self recursiveMixin:subModel to:model];
|
||||||
|
[self setSubModel:model in:viewId];
|
||||||
|
[self.itemViewIds enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) {
|
||||||
|
if ([viewId isEqualToString:obj]) {
|
||||||
|
*stop = YES;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[key integerValue] inSection:0];
|
||||||
|
[UIView performWithoutAnimation:^{
|
||||||
|
[self.view reloadItemsAtIndexPaths:@[indexPath]];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
||||||
|
NSUInteger pageIndex = (NSUInteger) (scrollView.contentOffset.x / scrollView.width);
|
||||||
|
scrollView.contentOffset = CGPointMake(pageIndex * scrollView.width, scrollView.contentOffset.y);
|
||||||
|
}
|
||||||
|
@end
|
@ -36,4 +36,6 @@
|
|||||||
- (void)clearSubModel;
|
- (void)clearSubModel;
|
||||||
|
|
||||||
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId;
|
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId;
|
||||||
|
|
||||||
|
- (void)recursiveMixin:(NSDictionary *)srcModel to:(NSMutableDictionary *)targetModel;
|
||||||
@end
|
@end
|
@ -65,6 +65,36 @@ - (void)mixin:(NSDictionary *)srcModel to:(NSMutableDictionary *)targetModel {
|
|||||||
targetModel[@"props"] = [targetProp copy];
|
targetModel[@"props"] = [targetProp copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)recursiveMixin:(NSDictionary *)srcModel to:(NSMutableDictionary *)targetModel {
|
||||||
|
NSDictionary *srcProp = srcModel[@"props"];
|
||||||
|
NSMutableDictionary *targetProp = [targetModel[@"props"] mutableCopy];
|
||||||
|
NSArray *targetOri = targetProp[@"subviews"];
|
||||||
|
|
||||||
|
[srcProp enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||||
|
if ([@"subviews" isEqualToString:key]) {
|
||||||
|
NSArray *subviews = obj;
|
||||||
|
NSMutableArray *targetSubviews = [targetOri mutableCopy];
|
||||||
|
if (subviews) {
|
||||||
|
for (NSDictionary *subview in subviews) {
|
||||||
|
NSString *viewId = subview[@"id"];
|
||||||
|
[targetSubviews enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
|
||||||
|
if ([viewId isEqualToString:obj[@"id"]]) {
|
||||||
|
NSMutableDictionary *mutableDictionary = [obj mutableCopy];
|
||||||
|
[self recursiveMixin:subview to:mutableDictionary];
|
||||||
|
targetSubviews[idx] = [mutableDictionary copy];
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
targetProp[@"subviews"] = [targetSubviews copy];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetProp[key] = obj;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
targetModel[@"props"] = [targetProp copy];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)blendSubNode:(DoricViewNode *)subNode layoutConfig:(NSDictionary *)layoutConfig {
|
- (void)blendSubNode:(DoricViewNode *)subNode layoutConfig:(NSDictionary *)layoutConfig {
|
||||||
DoricLayoutConfig *params = subNode.layoutConfig;
|
DoricLayoutConfig *params = subNode.layoutConfig;
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
export * from "./src/ui/view"
|
export * from "./src/ui/view"
|
||||||
export * from "./src/ui/layout"
|
export * from "./src/ui/layout"
|
||||||
export * from "./src/ui/listview"
|
export * from "./src/ui/list"
|
||||||
|
export * from "./src/ui/slider"
|
||||||
export * from "./src/ui/scroller"
|
export * from "./src/ui/scroller"
|
||||||
export * from "./src/ui/widgets"
|
export * from "./src/ui/widgets"
|
||||||
export * from "./src/ui/panel"
|
export * from "./src/ui/panel"
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
import { View, LayoutSpec } from './view'
|
import { View, LayoutSpec } from './view'
|
||||||
import { Stack, HLayout, VLayout } from './layout'
|
import { Stack, HLayout, VLayout } from './layout'
|
||||||
import { IText, IImage, Text, Image } from './widgets'
|
import { IText, IImage, Text, Image } from './widgets'
|
||||||
import { IList, List } from './listview'
|
import { IList, List } from './list'
|
||||||
|
import { ISlider, Slider } from './slider'
|
||||||
|
|
||||||
export function text(config: IText) {
|
export function text(config: IText) {
|
||||||
const ret = new Text
|
const ret = new Text
|
||||||
@ -85,3 +86,11 @@ export function list(config: IList) {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function slider(config: ISlider) {
|
||||||
|
const ret = new Slider
|
||||||
|
for (let key in config) {
|
||||||
|
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
72
js-framework/src/ui/slider.ts
Normal file
72
js-framework/src/ui/slider.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Superview, View, LayoutSpec, Property, IView } from "./view";
|
||||||
|
import { Stack } from "./layout";
|
||||||
|
|
||||||
|
export function slideItem(item: View) {
|
||||||
|
return (new SlideItem).also((it) => {
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
}
|
||||||
|
it.addChild(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SlideItem extends Stack {
|
||||||
|
/**
|
||||||
|
* Set to reuse native view
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
identifier?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISlider extends IView {
|
||||||
|
renderPage: (index: number) => SlideItem
|
||||||
|
itemCount: number
|
||||||
|
batchCount?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Slider extends Superview implements ISlider {
|
||||||
|
private cachedViews: Map<string, SlideItem> = new Map
|
||||||
|
|
||||||
|
private ignoreDirtyCallOnce = false
|
||||||
|
|
||||||
|
allSubviews() {
|
||||||
|
return this.cachedViews.values()
|
||||||
|
}
|
||||||
|
@Property
|
||||||
|
itemCount = 0
|
||||||
|
|
||||||
|
@Property
|
||||||
|
renderPage!: (index: number) => SlideItem
|
||||||
|
|
||||||
|
@Property
|
||||||
|
batchCount = 3
|
||||||
|
|
||||||
|
|
||||||
|
private getItem(itemIdx: number) {
|
||||||
|
let view = this.cachedViews.get(`${itemIdx}`)
|
||||||
|
if (view === undefined) {
|
||||||
|
view = this.renderPage(itemIdx)
|
||||||
|
view.superview = this
|
||||||
|
this.cachedViews.set(`${itemIdx}`, view)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty() {
|
||||||
|
if (this.ignoreDirtyCallOnce) {
|
||||||
|
this.ignoreDirtyCallOnce = false
|
||||||
|
//Ignore the dirty call once.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return super.isDirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderBunchedItems(start: number, length: number) {
|
||||||
|
this.ignoreDirtyCallOnce = true;
|
||||||
|
return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => {
|
||||||
|
const slideItem = this.getItem(start + idx)
|
||||||
|
return slideItem.toModel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,19 @@ import { Modeling } from "./types";
|
|||||||
* Store color as format AARRGGBB or RRGGBB
|
* Store color as format AARRGGBB or RRGGBB
|
||||||
*/
|
*/
|
||||||
export class Color implements Modeling {
|
export class Color implements Modeling {
|
||||||
|
static BLACK = new Color(0xFF000000)
|
||||||
|
static DKGRAY = new Color(0xFF444444)
|
||||||
|
static GRAY = new Color(0xFF888888)
|
||||||
|
static LTGRAY = new Color(0xFFCCCCCC)
|
||||||
|
static WHITE = new Color(0xFFFFFFFF)
|
||||||
|
static RED = new Color(0xFFFF0000)
|
||||||
|
static GREEN = new Color(0xFF00FF00)
|
||||||
|
static BLUE = new Color(0xFF0000FF)
|
||||||
|
static YELLOW = new Color(0xFFFFFF00)
|
||||||
|
static CYAN = new Color(0xFF00FFFF)
|
||||||
|
static MAGENTA = new Color(0xFFFF00FF)
|
||||||
static TRANSPARENT = new Color(0)
|
static TRANSPARENT = new Color(0)
|
||||||
|
|
||||||
_value: number = 0
|
_value: number = 0
|
||||||
|
|
||||||
constructor(v: number) {
|
constructor(v: number) {
|
||||||
|
Reference in New Issue
Block a user