iOS:add ListItem actions api

This commit is contained in:
pengfei.zhou 2021-04-23 19:05:33 +08:00 committed by osborn
parent cc7df5d2ca
commit e18b7e781e
18 changed files with 167 additions and 36 deletions

View File

@ -1,4 +1,4 @@
import { Group, Panel, List, text, gravity, Color, LayoutSpec, list, listItem, log, vlayout, Gravity, hlayout, Text, refreshable, Refreshable, ListItem, layoutConfig, ViewHolder, ViewModel, VMPanel, loge } from "doric"; import { Group, Panel, List, text, gravity, Color, LayoutSpec, list, listItem, log, vlayout, Gravity, hlayout, Text, refreshable, Refreshable, ListItem, layoutConfig, ViewHolder, ViewModel, VMPanel, loge, modal } from "doric";
interface ItemModel { interface ItemModel {
text: string text: string
@ -80,6 +80,21 @@ class ListVM extends ViewModel<ListModel, ListVH> {
widthSpec: LayoutSpec.MOST, widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT, heightSpec: LayoutSpec.FIT,
} }
}).apply({
actions: [
{
title: "First",
callback: () => {
modal(context).alert("First action")
}
},
{
title: "Second",
callback: () => {
modal(context).alert("Second action")
}
}
]
}) })
}, },
onLoadMore: async () => { onLoadMore: async () => {

View File

@ -34,4 +34,11 @@ - (void)initWithSuperNode:(DoricSuperNode *)superNode {
[super initWithSuperNode:superNode]; [super initWithSuperNode:superNode];
self.reusable = YES; self.reusable = YES;
} }
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
if ([@"identifier" isEqualToString:name] || [@"actions" isEqualToString:name]) {
} else {
[super blendView:view forPropName:name propValue:prop];
}
}
@end @end

View File

@ -18,11 +18,14 @@
// //
#import <JavaScriptCore/JavaScriptCore.h> #import <JavaScriptCore/JavaScriptCore.h>
#import <DoricCore/Doric.h>
#import "DoricListNode.h" #import "DoricListNode.h"
#import "DoricExtensions.h" #import "DoricExtensions.h"
#import "DoricListItemNode.h" #import "DoricListItemNode.h"
#import "DoricRefreshableNode.h" #import "DoricRefreshableNode.h"
#import "DoricJSDispatcher.h" #import "DoricJSDispatcher.h"
#import "DoricUtil.h"
#import "DoricExtensions.h"
@interface DoricTableViewCell : UITableViewCell @interface DoricTableViewCell : UITableViewCell
@property(nonatomic, strong) DoricListItemNode *doricListItemNode; @property(nonatomic, strong) DoricListItemNode *doricListItemNode;
@ -44,6 +47,7 @@ - (CGSize)sizeThatFits:(CGSize)size {
@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;
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSArray *> *itemActions;
@property(nonatomic, assign) NSUInteger itemCount; @property(nonatomic, assign) NSUInteger itemCount;
@property(nonatomic, assign) NSUInteger batchCount; @property(nonatomic, assign) NSUInteger batchCount;
@property(nonatomic, copy) NSString *onLoadMoreFuncId; @property(nonatomic, copy) NSString *onLoadMoreFuncId;
@ -62,6 +66,7 @@ - (instancetype)initWithContext:(DoricContext *)doricContext {
if (self = [super initWithContext:doricContext]) { if (self = [super initWithContext:doricContext]) {
_itemViewIds = [NSMutableDictionary new]; _itemViewIds = [NSMutableDictionary new];
_itemHeights = [NSMutableDictionary new]; _itemHeights = [NSMutableDictionary new];
_itemActions = [NSMutableDictionary new];
_batchCount = 15; _batchCount = 15;
} }
return self; return self;
@ -144,6 +149,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
NSDictionary *model = [self itemModelAt:position]; NSDictionary *model = [self itemModelAt:position];
NSDictionary *props = model[@"props"]; NSDictionary *props = model[@"props"];
NSString *reuseId = props[@"identifier"]; NSString *reuseId = props[@"identifier"];
self.itemActions[@(position)] = props[@"actions"];
if (position > 0 && position >= self.itemCount && self.onLoadMoreFuncId) { if (position > 0 && position >= self.itemCount && self.onLoadMoreFuncId) {
reuseId = @"doricLoadMoreCell"; reuseId = @"doricLoadMoreCell";
[self callLoadMore]; [self callLoadMore];
@ -175,7 +181,53 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa
} else { } else {
return 44.f; return 44.f;
} }
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *actions = self.itemActions[@(indexPath.row)];
return actions.count > 0;
}
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *actions = self.itemActions[@(indexPath.row)];
NSMutableArray <UITableViewRowAction *> *array = [NSMutableArray new];
for (NSDictionary *action in actions) {
__weak typeof(self) _self = self;
UITableViewRowAction *tableViewRowAction = [UITableViewRowAction
rowActionWithStyle:UITableViewRowActionStyleNormal
title:action[@"title"]
handler:^(UITableViewRowAction *tableViewRowAction, NSIndexPath *indexPath) {
__strong typeof(_self) self = _self;
[self callJSResponse:action[@"callback"], nil];
}];
[action[@"backgroundColor"] let:^(id it) {
tableViewRowAction.backgroundColor = DoricColor(it);
}];
[array addObject:tableViewRowAction];
}
return array;
}
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)) {
NSArray *actions = self.itemActions[@(indexPath.row)];
NSMutableArray<UIContextualAction *> *array = [NSMutableArray new];
for (NSDictionary *action in actions) {
__weak typeof(self) _self = self;
UIContextualAction *contextualAction = [UIContextualAction
contextualActionWithStyle:UIContextualActionStyleNormal
title:action[@"title"]
handler:^(UIContextualAction *_Nonnull contextualAction, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
__strong typeof(_self) self = _self;
[self callJSResponse:action[@"callback"], nil];
}];
[action[@"backgroundColor"] let:^(id it) {
contextualAction.backgroundColor = DoricColor(it);
}];
[array addObject:contextualAction];
}
UISwipeActionsConfiguration *configuration = [UISwipeActionsConfiguration configurationWithActions:array];
configuration.performsFirstActionWithFullSwipe = NO;
return configuration;
} }
- (NSDictionary *)itemModelAt:(NSUInteger)position { - (NSDictionary *)itemModelAt:(NSUInteger)position {

View File

@ -2,9 +2,12 @@
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
function obj2Model(obj) { function obj2Model(obj, convertor) {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(function (e) { return obj2Model(e); }); return convertor(obj);
}
else if (obj instanceof Array) {
return obj.map(function (e) { return obj2Model(e, convertor); });
} }
else if (obj instanceof Object) { else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
@ -14,7 +17,7 @@ function obj2Model(obj) {
else { else {
for (var key in obj) { for (var key in obj) {
var val = Reflect.get(obj, key); var val = Reflect.get(obj, key);
Reflect.set(obj, key, obj2Model(val)); Reflect.set(obj, key, obj2Model(val, convertor));
} }
return obj; return obj;
} }
@ -298,11 +301,12 @@ var View = /** @class */ (function () {
configurable: true configurable: true
}); });
View.prototype.onPropertyChanged = function (propKey, oldV, newV) { View.prototype.onPropertyChanged = function (propKey, oldV, newV) {
var _this = this;
if (newV instanceof Function) { if (newV instanceof Function) {
newV = this.callback2Id(newV); newV = this.callback2Id(newV);
} }
else { else {
newV = obj2Model(newV); newV = obj2Model(newV, function (v) { return _this.callback2Id(v); });
} }
if (this.__dirty_props__ === undefined) { if (this.__dirty_props__ === undefined) {
this.__dirty_props__ = {}; this.__dirty_props__ = {};
@ -2048,6 +2052,10 @@ var ListItem = /** @class */ (function (_super) {
Property, Property,
__metadata$8("design:type", String) __metadata$8("design:type", String)
], ListItem.prototype, "identifier", void 0); ], ListItem.prototype, "identifier", void 0);
__decorate$8([
Property,
__metadata$8("design:type", Array)
], ListItem.prototype, "actions", void 0);
return ListItem; return ListItem;
}(Stack)); }(Stack));
var List = /** @class */ (function (_super) { var List = /** @class */ (function (_super) {

View File

@ -2,9 +2,12 @@
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
function obj2Model(obj) { function obj2Model(obj, convertor) {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(e => obj2Model(e)); return convertor(obj);
}
else if (obj instanceof Array) {
return obj.map(e => obj2Model(e, convertor));
} }
else if (obj instanceof Object) { else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
@ -14,7 +17,7 @@ function obj2Model(obj) {
else { else {
for (let key in obj) { for (let key in obj) {
const val = Reflect.get(obj, key); const val = Reflect.get(obj, key);
Reflect.set(obj, key, obj2Model(val)); Reflect.set(obj, key, obj2Model(val, convertor));
} }
return obj; return obj;
} }
@ -228,7 +231,7 @@ class View {
newV = this.callback2Id(newV); newV = this.callback2Id(newV);
} }
else { else {
newV = obj2Model(newV); newV = obj2Model(newV, (v) => this.callback2Id(v));
} }
this.__dirty_props__[propKey] = newV; this.__dirty_props__[propKey] = newV;
} }
@ -1548,6 +1551,10 @@ __decorate$8([
Property, Property,
__metadata$8("design:type", String) __metadata$8("design:type", String)
], ListItem.prototype, "identifier", void 0); ], ListItem.prototype, "identifier", void 0);
__decorate$8([
Property,
__metadata$8("design:type", Array)
], ListItem.prototype, "actions", void 0);
class List extends Superview { class List extends Superview {
constructor() { constructor() {
super(...arguments); super(...arguments);

View File

@ -1593,9 +1593,12 @@ var doric = /*#__PURE__*/Object.freeze({
jsCallbackTimer: jsCallbackTimer jsCallbackTimer: jsCallbackTimer
}); });
function obj2Model(obj) { function obj2Model(obj, convertor) {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(e => obj2Model(e)); return convertor(obj);
}
else if (obj instanceof Array) {
return obj.map(e => obj2Model(e, convertor));
} }
else if (obj instanceof Object) { else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
@ -1605,7 +1608,7 @@ function obj2Model(obj) {
else { else {
for (let key in obj) { for (let key in obj) {
const val = Reflect.get(obj, key); const val = Reflect.get(obj, key);
Reflect.set(obj, key, obj2Model(val)); Reflect.set(obj, key, obj2Model(val, convertor));
} }
return obj; return obj;
} }
@ -1749,7 +1752,7 @@ class View {
newV = this.callback2Id(newV); newV = this.callback2Id(newV);
} }
else { else {
newV = obj2Model(newV); newV = obj2Model(newV, (v) => this.callback2Id(v));
} }
this.__dirty_props__[propKey] = newV; this.__dirty_props__[propKey] = newV;
} }
@ -3069,6 +3072,10 @@ __decorate$8([
Property, Property,
__metadata$8("design:type", String) __metadata$8("design:type", String)
], ListItem.prototype, "identifier", void 0); ], ListItem.prototype, "identifier", void 0);
__decorate$8([
Property,
__metadata$8("design:type", Array)
], ListItem.prototype, "actions", void 0);
class List extends Superview { class List extends Superview {
constructor() { constructor() {
super(...arguments); super(...arguments);

8
doric-js/index.d.ts vendored
View File

@ -587,11 +587,17 @@ declare module 'doric/lib/src/widget/list' {
import { View, Superview, NativeViewModel } from "doric/lib/src/ui/view"; import { View, Superview, NativeViewModel } from "doric/lib/src/ui/view";
import { Stack } from "doric/lib/src/widget/layouts"; import { Stack } from "doric/lib/src/widget/layouts";
import { BridgeContext } from "doric/lib/src/runtime/global"; import { BridgeContext } from "doric/lib/src/runtime/global";
import { Color } from "doric/lib/src/util/color";
export class ListItem extends Stack { export class ListItem extends Stack {
/** /**
* Set to reuse native view * Set to reuse native view
*/ */
identifier?: string; identifier?: string;
actions?: {
title: string;
backgroundColor?: Color;
callback: () => void;
}[];
} }
export class List extends Superview { export class List extends Superview {
allSubviews(): IterableIterator<ListItem> | ListItem[]; allSubviews(): IterableIterator<ListItem> | ListItem[];
@ -1187,7 +1193,7 @@ declare module 'doric/lib/src/util/types' {
export interface Modeling { export interface Modeling {
toModel(): Model; toModel(): Model;
} }
export function obj2Model(obj: Model): Model; export function obj2Model(obj: Model, convertor: (v: Function) => string): Model;
type _M = string | number | boolean | Modeling | { type _M = string | number | boolean | Modeling | {
[index: string]: Model; [index: string]: Model;
} | undefined; } | undefined;

View File

@ -113,7 +113,7 @@ export class View {
newV = this.callback2Id(newV); newV = this.callback2Id(newV);
} }
else { else {
newV = obj2Model(newV); newV = obj2Model(newV, (v) => this.callback2Id(v));
} }
this.__dirty_props__[propKey] = newV; this.__dirty_props__[propKey] = newV;
} }

View File

@ -1,7 +1,7 @@
export interface Modeling { export interface Modeling {
toModel(): Model; toModel(): Model;
} }
export declare function obj2Model(obj: Model): Model; export declare function obj2Model(obj: Model, convertor: (v: Function) => string): Model;
declare type _M = string | number | boolean | Modeling | { declare type _M = string | number | boolean | Modeling | {
[index: string]: Model; [index: string]: Model;
} | undefined; } | undefined;

View File

@ -1,6 +1,9 @@
export function obj2Model(obj) { export function obj2Model(obj, convertor) {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(e => obj2Model(e)); return convertor(obj);
}
else if (obj instanceof Array) {
return obj.map(e => obj2Model(e, convertor));
} }
else if (obj instanceof Object) { else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
@ -10,7 +13,7 @@ export function obj2Model(obj) {
else { else {
for (let key in obj) { for (let key in obj) {
const val = Reflect.get(obj, key); const val = Reflect.get(obj, key);
Reflect.set(obj, key, obj2Model(val)); Reflect.set(obj, key, obj2Model(val, convertor));
} }
return obj; return obj;
} }

View File

@ -1,11 +1,17 @@
import { View, Superview, NativeViewModel } from "../ui/view"; import { View, Superview, NativeViewModel } from "../ui/view";
import { Stack } from "./layouts"; import { Stack } from "./layouts";
import { BridgeContext } from "../runtime/global"; import { BridgeContext } from "../runtime/global";
import { Color } from "../util/color";
export declare class ListItem extends Stack { export declare class ListItem extends Stack {
/** /**
* Set to reuse native view * Set to reuse native view
*/ */
identifier?: string; identifier?: string;
actions?: {
title: string;
backgroundColor?: Color;
callback: () => void;
}[];
} }
export declare class List extends Superview { export declare class List extends Superview {
private cachedViews; private cachedViews;

View File

@ -31,6 +31,10 @@ __decorate([
Property, Property,
__metadata("design:type", String) __metadata("design:type", String)
], ListItem.prototype, "identifier", void 0); ], ListItem.prototype, "identifier", void 0);
__decorate([
Property,
__metadata("design:type", Array)
], ListItem.prototype, "actions", void 0);
export class List extends Superview { export class List extends Superview {
constructor() { constructor() {
super(...arguments); super(...arguments);

View File

@ -193,7 +193,7 @@ export abstract class View implements Modeling {
if (newV instanceof Function) { if (newV instanceof Function) {
newV = this.callback2Id(newV) newV = this.callback2Id(newV)
} else { } else {
newV = obj2Model(newV) newV = obj2Model(newV, (v) => this.callback2Id(v))
} }
if (this.__dirty_props__ === undefined) { if (this.__dirty_props__ === undefined) {
this.__dirty_props__ = {} this.__dirty_props__ = {}

View File

@ -199,7 +199,7 @@ export abstract class View implements Modeling {
if (newV instanceof Function) { if (newV instanceof Function) {
newV = this.callback2Id(newV) newV = this.callback2Id(newV)
} else { } else {
newV = obj2Model(newV) newV = obj2Model(newV, (v) => this.callback2Id(v))
} }
this.__dirty_props__[propKey] = newV this.__dirty_props__[propKey] = newV
} }

View File

@ -16,9 +16,11 @@
export interface Modeling { export interface Modeling {
toModel(): Model toModel(): Model
} }
export function obj2Model(obj: Model): Model { export function obj2Model(obj: Model, convertor: (v: Function) => string): Model {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(e => obj2Model(e)) as Model return convertor(obj)
} else if (obj instanceof Array) {
return obj.map(e => obj2Model(e, convertor)) as Model
} else if (obj instanceof Object) { } else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
obj = Reflect.apply(Reflect.get(obj, 'toModel'), obj, []) obj = Reflect.apply(Reflect.get(obj, 'toModel'), obj, [])
@ -26,7 +28,7 @@ export function obj2Model(obj: Model): Model {
} else { } else {
for (let key in obj) { for (let key in obj) {
const val = Reflect.get(obj, key) const val = Reflect.get(obj, key)
Reflect.set(obj, key, obj2Model(val)) Reflect.set(obj, key, obj2Model(val, convertor))
} }
return obj return obj
} }

View File

@ -18,7 +18,7 @@ import { View, Property, Superview, NativeViewModel } from "../ui/view";
import { Stack } from "./layouts"; import { Stack } from "./layouts";
import { layoutConfig } from "../util/layoutconfig"; import { layoutConfig } from "../util/layoutconfig";
import { BridgeContext } from "../runtime/global"; import { BridgeContext } from "../runtime/global";
import { Color } from "../util/color";
export class ListItem extends Stack { export class ListItem extends Stack {
/** /**
@ -26,6 +26,13 @@ export class ListItem extends Stack {
*/ */
@Property @Property
identifier?: string identifier?: string
@Property
actions?: {
title: string,
backgroundColor?: Color,
callback: () => void,
}[]
} }
export class List extends Superview { export class List extends Superview {

View File

@ -1577,9 +1577,12 @@ Reflect.apply(doric.jsRegisterModule,this,["doric",Reflect.apply(function(__modu
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
function obj2Model(obj) { function obj2Model(obj, convertor) {
if (obj instanceof Array) { if (obj instanceof Function) {
return obj.map(e => obj2Model(e)); return convertor(obj);
}
else if (obj instanceof Array) {
return obj.map(e => obj2Model(e, convertor));
} }
else if (obj instanceof Object) { else if (obj instanceof Object) {
if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) {
@ -1589,7 +1592,7 @@ function obj2Model(obj) {
else { else {
for (let key in obj) { for (let key in obj) {
const val = Reflect.get(obj, key); const val = Reflect.get(obj, key);
Reflect.set(obj, key, obj2Model(val)); Reflect.set(obj, key, obj2Model(val, convertor));
} }
return obj; return obj;
} }
@ -1803,7 +1806,7 @@ class View {
newV = this.callback2Id(newV); newV = this.callback2Id(newV);
} }
else { else {
newV = obj2Model(newV); newV = obj2Model(newV, (v) => this.callback2Id(v));
} }
this.__dirty_props__[propKey] = newV; this.__dirty_props__[propKey] = newV;
} }
@ -3123,6 +3126,10 @@ __decorate$8([
Property, Property,
__metadata$8("design:type", String) __metadata$8("design:type", String)
], ListItem.prototype, "identifier", void 0); ], ListItem.prototype, "identifier", void 0);
__decorate$8([
Property,
__metadata$8("design:type", Array)
], ListItem.prototype, "actions", void 0);
class List extends Superview { class List extends Superview {
constructor() { constructor() {
super(...arguments); super(...arguments);

File diff suppressed because one or more lines are too long