jsbox-cview 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Base } from "./base";
|
|
2
|
+
|
|
3
|
+
type MenuItem = {
|
|
4
|
+
title: string;
|
|
5
|
+
symbol: string;
|
|
6
|
+
handler: () => void;
|
|
7
|
+
destructive?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const RegisteredOCClassName: Set<string> = new Set()
|
|
11
|
+
|
|
12
|
+
function defineOCClass({ classname, menuList }: {
|
|
13
|
+
classname: string;
|
|
14
|
+
menuList: { title: string; items: MenuItem[] }[]
|
|
15
|
+
}) {
|
|
16
|
+
if (RegisteredOCClassName.has(classname)) return;
|
|
17
|
+
RegisteredOCClassName.add(classname);
|
|
18
|
+
$define({
|
|
19
|
+
type: classname + " : UIView <UIContextMenuInteractionDelegate>",
|
|
20
|
+
events: {
|
|
21
|
+
"contextMenuInteraction:configurationForMenuAtLocation:": (interacton: any, point: JBPoint) => {
|
|
22
|
+
console.log(interacton.$view().jsValue().info)
|
|
23
|
+
const menuIndex = (interacton.$view().jsValue().info?.menuIndex ?? 0) as number;
|
|
24
|
+
if (menuIndex < 0 || menuIndex >= menuList.length) return
|
|
25
|
+
return createContextMenuConfiguration(menuList[menuIndex]);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createUIAction(item: MenuItem) {
|
|
32
|
+
const action = $objc("UIAction").$actionWithTitle_image_identifier_handler(
|
|
33
|
+
item.title,
|
|
34
|
+
item.symbol,
|
|
35
|
+
null,
|
|
36
|
+
$block("void, UIAction *", () => item.handler())
|
|
37
|
+
);
|
|
38
|
+
if (item.destructive) action.$setAttributes(1 << 1);
|
|
39
|
+
return action;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function createContextMenuConfiguration({ title, items }: { title: string, items: MenuItem[] }) {
|
|
43
|
+
return $objc("UIContextMenuConfiguration").$configurationWithIdentifier_previewProvider_actionProvider(
|
|
44
|
+
null,
|
|
45
|
+
null,
|
|
46
|
+
$block("UIMenu *, NSArray *", () => {
|
|
47
|
+
const actions = items.map(item => createUIAction(item))
|
|
48
|
+
return $objc("UIMenu").$menuWithTitle_children(title, actions);
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createRuntimeView(classname: string, menuList: { title: string; items: MenuItem[] }[]) {
|
|
54
|
+
defineOCClass({ classname, menuList });
|
|
55
|
+
const view = $objc(classname).invoke("alloc.init");
|
|
56
|
+
const interaction = $objc("UIContextMenuInteraction")
|
|
57
|
+
.invoke("alloc")
|
|
58
|
+
.invoke("initWithDelegate", view);
|
|
59
|
+
view.$addInteraction(interaction);
|
|
60
|
+
return view;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 动态上下文菜单视图,此视图是为了弥补JSBox中无法动态调整上下文菜单的缺陷而设计的。
|
|
65
|
+
*
|
|
66
|
+
* 由于OC类的注册是全局性的,所以需要特殊的处理来避免重复注册类:对于使用同一套动态上下文菜单的View,应该注册为同一个类。
|
|
67
|
+
* 用menuList列表来记录所需的动态上下文菜单,通过props.info.menuIndex来指定当前视图需要使用的菜单。
|
|
68
|
+
*
|
|
69
|
+
* 此视图除了一般UIView的props, layout, events, views四个参数外,还有三个必须的特殊参数:
|
|
70
|
+
* 1. ocClassName: string OC类名,用于注册OC类,一个类名只能注册一次,重复注册会被忽略。
|
|
71
|
+
* 这和menuList是配套的,同一套menuList用同一个类名。
|
|
72
|
+
* 2. menuList: { title: string; items: MenuItem[] }[] 菜单列表,每个菜单项包含一个标题和一个MenuItem数组。
|
|
73
|
+
* 3. props.info: { menuIndex: number } 用于指定当前视图的菜单索引,从0开始。info中可以包含其他参数。
|
|
74
|
+
*
|
|
75
|
+
*/
|
|
76
|
+
export class DynamicContextMenuView extends Base<UILabelView, UiTypes.RuntimeOptions> {
|
|
77
|
+
private _menuList: { title: string; items: MenuItem[] }[];
|
|
78
|
+
_defineView: () => UiTypes.RuntimeOptions;
|
|
79
|
+
constructor({ ocClassName, menuList, props, layout, events, views = [] }: {
|
|
80
|
+
ocClassName: string;
|
|
81
|
+
menuList: { title: string; items: MenuItem[] }[];
|
|
82
|
+
props: UiTypes.BaseViewProps;
|
|
83
|
+
layout?: (make: MASConstraintMaker, view: UIView) => void;
|
|
84
|
+
events?: UiTypes.BaseViewEvents;
|
|
85
|
+
views: UiTypes.AllViewOptions[];
|
|
86
|
+
}) {
|
|
87
|
+
super();
|
|
88
|
+
if (!props.info || props.info?.menuIndex === undefined || props.info?.menuIndex === null) {
|
|
89
|
+
throw new Error("props.info.menuIndex is required");
|
|
90
|
+
}
|
|
91
|
+
if (typeof props.info.menuIndex !== "number") {
|
|
92
|
+
throw new Error("props.info.menuIndex must be a number");
|
|
93
|
+
}
|
|
94
|
+
if (props.info.menuIndex < 0 || props.info.menuIndex >= menuList.length) {
|
|
95
|
+
throw new Error("props.info.menuIndex is out of range");
|
|
96
|
+
}
|
|
97
|
+
this._menuList = menuList;
|
|
98
|
+
const runtimeView = createRuntimeView(ocClassName, menuList);
|
|
99
|
+
this._defineView = () => {
|
|
100
|
+
return {
|
|
101
|
+
type: "runtime",
|
|
102
|
+
props: {
|
|
103
|
+
...props,
|
|
104
|
+
id: this.id,
|
|
105
|
+
view: runtimeView
|
|
106
|
+
},
|
|
107
|
+
layout,
|
|
108
|
+
events,
|
|
109
|
+
views
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
set menuIndex(index: number) {
|
|
115
|
+
if (index < 0 || index >= this._menuList.length) {
|
|
116
|
+
throw new Error("menuIndex is out of range");
|
|
117
|
+
}
|
|
118
|
+
// 必须重新赋值info,否则info不会改变
|
|
119
|
+
this.view.info = { ...this.view.info, menuIndex: index };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get menuIndex(): number {
|
|
123
|
+
return this.view.info.menuIndex;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamicContextMenuView = void 0;
|
|
4
|
+
const base_1 = require("./base");
|
|
5
|
+
const RegisteredOCClassName = new Set();
|
|
6
|
+
function defineOCClass({ classname, menuList }) {
|
|
7
|
+
if (RegisteredOCClassName.has(classname))
|
|
8
|
+
return;
|
|
9
|
+
RegisteredOCClassName.add(classname);
|
|
10
|
+
$define({
|
|
11
|
+
type: classname + " : UIView <UIContextMenuInteractionDelegate>",
|
|
12
|
+
events: {
|
|
13
|
+
"contextMenuInteraction:configurationForMenuAtLocation:": (interacton, point) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
console.log(interacton.$view().jsValue().info);
|
|
16
|
+
const menuIndex = ((_b = (_a = interacton.$view().jsValue().info) === null || _a === void 0 ? void 0 : _a.menuIndex) !== null && _b !== void 0 ? _b : 0);
|
|
17
|
+
if (menuIndex < 0 || menuIndex >= menuList.length)
|
|
18
|
+
return;
|
|
19
|
+
return createContextMenuConfiguration(menuList[menuIndex]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function createUIAction(item) {
|
|
25
|
+
const action = $objc("UIAction").$actionWithTitle_image_identifier_handler(item.title, item.symbol, null, $block("void, UIAction *", () => item.handler()));
|
|
26
|
+
if (item.destructive)
|
|
27
|
+
action.$setAttributes(1 << 1);
|
|
28
|
+
return action;
|
|
29
|
+
}
|
|
30
|
+
;
|
|
31
|
+
function createContextMenuConfiguration({ title, items }) {
|
|
32
|
+
return $objc("UIContextMenuConfiguration").$configurationWithIdentifier_previewProvider_actionProvider(null, null, $block("UIMenu *, NSArray *", () => {
|
|
33
|
+
const actions = items.map(item => createUIAction(item));
|
|
34
|
+
return $objc("UIMenu").$menuWithTitle_children(title, actions);
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function createRuntimeView(classname, menuList) {
|
|
38
|
+
defineOCClass({ classname, menuList });
|
|
39
|
+
const view = $objc(classname).invoke("alloc.init");
|
|
40
|
+
const interaction = $objc("UIContextMenuInteraction")
|
|
41
|
+
.invoke("alloc")
|
|
42
|
+
.invoke("initWithDelegate", view);
|
|
43
|
+
view.$addInteraction(interaction);
|
|
44
|
+
return view;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 动态上下文菜单视图,此视图是为了弥补JSBox中无法动态调整上下文菜单的缺陷而设计的。
|
|
48
|
+
*
|
|
49
|
+
* 由于OC类的注册是全局性的,所以需要特殊的处理来避免重复注册类:对于使用同一套动态上下文菜单的View,应该注册为同一个类。
|
|
50
|
+
* 用menuList列表来记录所需的动态上下文菜单,通过props.info.menuIndex来指定当前视图需要使用的菜单。
|
|
51
|
+
*
|
|
52
|
+
* 此视图除了一般UIView的props, layout, events, views四个参数外,还有三个必须的特殊参数:
|
|
53
|
+
* 1. ocClassName: string OC类名,用于注册OC类,一个类名只能注册一次,重复注册会被忽略。
|
|
54
|
+
* 这和menuList是配套的,同一套menuList用同一个类名。
|
|
55
|
+
* 2. menuList: { title: string; items: MenuItem[] }[] 菜单列表,每个菜单项包含一个标题和一个MenuItem数组。
|
|
56
|
+
* 3. props.info: { menuIndex: number } 用于指定当前视图的菜单索引,从0开始。info中可以包含其他参数。
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
class DynamicContextMenuView extends base_1.Base {
|
|
60
|
+
constructor({ ocClassName, menuList, props, layout, events, views = [] }) {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
super();
|
|
63
|
+
if (!props.info || ((_a = props.info) === null || _a === void 0 ? void 0 : _a.menuIndex) === undefined || ((_b = props.info) === null || _b === void 0 ? void 0 : _b.menuIndex) === null) {
|
|
64
|
+
throw new Error("props.info.menuIndex is required");
|
|
65
|
+
}
|
|
66
|
+
if (typeof props.info.menuIndex !== "number") {
|
|
67
|
+
throw new Error("props.info.menuIndex must be a number");
|
|
68
|
+
}
|
|
69
|
+
if (props.info.menuIndex < 0 || props.info.menuIndex >= menuList.length) {
|
|
70
|
+
throw new Error("props.info.menuIndex is out of range");
|
|
71
|
+
}
|
|
72
|
+
this._menuList = menuList;
|
|
73
|
+
const runtimeView = createRuntimeView(ocClassName, menuList);
|
|
74
|
+
this._defineView = () => {
|
|
75
|
+
return {
|
|
76
|
+
type: "runtime",
|
|
77
|
+
props: Object.assign(Object.assign({}, props), { id: this.id, view: runtimeView }),
|
|
78
|
+
layout,
|
|
79
|
+
events,
|
|
80
|
+
views
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
set menuIndex(index) {
|
|
85
|
+
if (index < 0 || index >= this._menuList.length) {
|
|
86
|
+
throw new Error("menuIndex is out of range");
|
|
87
|
+
}
|
|
88
|
+
// 必须重新赋值info,否则info不会改变
|
|
89
|
+
this.view.info = Object.assign(Object.assign({}, this.view.info), { menuIndex: index });
|
|
90
|
+
}
|
|
91
|
+
get menuIndex() {
|
|
92
|
+
return this.view.info.menuIndex;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.DynamicContextMenuView = DynamicContextMenuView;
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./components/base"), exports);
|
|
18
18
|
__exportStar(require("./components/custom-navigation-bar"), exports);
|
|
19
|
+
__exportStar(require("./components/dynamic-contextmenu-view"), exports);
|
|
19
20
|
__exportStar(require("./components/dynamic-itemsize-matrix"), exports);
|
|
20
21
|
__exportStar(require("./components/dynamic-preference-listview"), exports);
|
|
21
22
|
__exportStar(require("./components/dynamic-rowheight-list"), exports);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const dynamic_contextmenu_view_1 = require("../components/dynamic-contextmenu-view");
|
|
4
|
+
const menuList = [
|
|
5
|
+
{
|
|
6
|
+
title: "菜单1",
|
|
7
|
+
items: [
|
|
8
|
+
{
|
|
9
|
+
title: "变成菜单1",
|
|
10
|
+
symbol: "plus",
|
|
11
|
+
handler: () => {
|
|
12
|
+
view.menuIndex = 0;
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
title: "变成菜单2",
|
|
17
|
+
symbol: "plus",
|
|
18
|
+
destructive: true,
|
|
19
|
+
handler: () => {
|
|
20
|
+
view.menuIndex = 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: "菜单2",
|
|
27
|
+
items: [
|
|
28
|
+
{
|
|
29
|
+
title: "变成菜单1",
|
|
30
|
+
symbol: "plus",
|
|
31
|
+
handler: () => {
|
|
32
|
+
view.menuIndex = 0;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: "变成菜单2",
|
|
37
|
+
symbol: "plus",
|
|
38
|
+
handler: () => {
|
|
39
|
+
view.menuIndex = 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
const view = new dynamic_contextmenu_view_1.DynamicContextMenuView({
|
|
46
|
+
ocClassName: "testView",
|
|
47
|
+
menuList,
|
|
48
|
+
props: {
|
|
49
|
+
info: {
|
|
50
|
+
menuIndex: 0
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
layout: (make, view) => {
|
|
54
|
+
make.center.equalTo(view.super);
|
|
55
|
+
make.size.equalTo($size(100, 100));
|
|
56
|
+
},
|
|
57
|
+
views: [{
|
|
58
|
+
type: "label",
|
|
59
|
+
props: {
|
|
60
|
+
text: "长按我",
|
|
61
|
+
textColor: $color("black"),
|
|
62
|
+
align: $align.center
|
|
63
|
+
},
|
|
64
|
+
layout: $layout.center
|
|
65
|
+
}]
|
|
66
|
+
});
|
|
67
|
+
$ui.render({
|
|
68
|
+
views: [view.definition]
|
|
69
|
+
});
|
package/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './components/base';
|
|
2
2
|
export * from './components/custom-navigation-bar';
|
|
3
|
+
export * from './components/dynamic-contextmenu-view';
|
|
3
4
|
export * from './components/dynamic-itemsize-matrix';
|
|
4
5
|
export * from './components/dynamic-preference-listview';
|
|
5
6
|
export * from './components/dynamic-rowheight-list';
|
package/package.json
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { DynamicContextMenuView } from "../components/dynamic-contextmenu-view";
|
|
2
|
+
|
|
3
|
+
const menuList = [
|
|
4
|
+
{
|
|
5
|
+
title: "菜单1",
|
|
6
|
+
items: [
|
|
7
|
+
{
|
|
8
|
+
title: "变成菜单1",
|
|
9
|
+
symbol: "plus",
|
|
10
|
+
handler: () => {
|
|
11
|
+
view.menuIndex = 0;
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
title: "变成菜单2",
|
|
16
|
+
symbol: "plus",
|
|
17
|
+
destructive: true,
|
|
18
|
+
handler: () => {
|
|
19
|
+
view.menuIndex = 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
title: "菜单2",
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
title: "变成菜单1",
|
|
29
|
+
symbol: "plus",
|
|
30
|
+
handler: () => {
|
|
31
|
+
view.menuIndex = 0;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
title: "变成菜单2",
|
|
36
|
+
symbol: "plus",
|
|
37
|
+
handler: () => {
|
|
38
|
+
view.menuIndex = 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
const view = new DynamicContextMenuView({
|
|
46
|
+
ocClassName: "testView",
|
|
47
|
+
menuList,
|
|
48
|
+
props: {
|
|
49
|
+
info: {
|
|
50
|
+
menuIndex: 0
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
layout: (make, view) => {
|
|
54
|
+
make.center.equalTo(view.super);
|
|
55
|
+
make.size.equalTo($size(100, 100));
|
|
56
|
+
},
|
|
57
|
+
views: [{
|
|
58
|
+
type: "label",
|
|
59
|
+
props: {
|
|
60
|
+
text: "长按我",
|
|
61
|
+
textColor: $color("black"),
|
|
62
|
+
align: $align.center
|
|
63
|
+
},
|
|
64
|
+
layout: $layout.center
|
|
65
|
+
}]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
$ui.render({
|
|
69
|
+
views: [view.definition]
|
|
70
|
+
})
|