@vnejs/plugins.views.settings 0.2.1 → 0.2.3
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.
- package/dist/index.js +2 -1
- package/dist/modules/arrows.d.ts +18 -0
- package/dist/modules/arrows.js +34 -0
- package/dist/modules/controller.d.ts +9 -5
- package/dist/modules/controller.js +66 -25
- package/dist/utils/settings.d.ts +1 -1
- package/dist/view/index.js +2 -2
- package/package.json +3 -1
- package/src/index.ts +2 -1
- package/src/modules/arrows.ts +54 -0
- package/src/modules/controller.ts +79 -30
- package/src/tests/arrows.test.ts +97 -0
- package/src/tests/controller.test.ts +146 -6
- package/src/tests/setup.ts +4 -2
- package/src/utils/settings.ts +1 -1
- package/src/view/index.tsx +3 -3
package/dist/index.js
CHANGED
|
@@ -2,5 +2,6 @@ import "@vnejs/contracts.views.settings";
|
|
|
2
2
|
import { regPlugin } from "@vnejs/shared";
|
|
3
3
|
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.settings";
|
|
4
4
|
import { SettingsViewController } from "./modules/controller.js";
|
|
5
|
+
import { SettingsViewArrows } from "./modules/arrows.js";
|
|
5
6
|
import { SettingsView } from "./modules/view.js";
|
|
6
|
-
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsView]);
|
|
7
|
+
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsViewArrows, SettingsView]);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
2
|
+
import { ModuleControllerArrowsVertical } from "@vnejs/module.controller.arrows.vertical";
|
|
3
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
4
|
+
import type { SettingsViewTabPayload } from "../utils/settings.js";
|
|
5
|
+
export declare class SettingsViewArrows extends ModuleControllerArrowsVertical<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams> {
|
|
6
|
+
name: string;
|
|
7
|
+
EVENT_SHOW: "vne:settings_view:show";
|
|
8
|
+
EVENT_HIDE: "vne:settings_view:hide";
|
|
9
|
+
EVENT_STATE_UPDATE: "vne:settings_view:state_update";
|
|
10
|
+
EVENT_UPDATE_FAST: "vne:settings_view:update_fast";
|
|
11
|
+
CONTROLS_ZINDEX: number;
|
|
12
|
+
fields: SettingsField[];
|
|
13
|
+
subscribe: () => void;
|
|
14
|
+
onShow: (payload?: SettingsViewTabPayload) => Promise<unknown[]> | undefined;
|
|
15
|
+
onTabSet: (payload?: SettingsViewTabPayload) => void;
|
|
16
|
+
getMaxCurrentItem: () => number;
|
|
17
|
+
syncFields: (payload?: SettingsViewTabPayload) => void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ModuleControllerArrowsVertical } from "@vnejs/module.controller.arrows.vertical";
|
|
2
|
+
export class SettingsViewArrows extends ModuleControllerArrowsVertical {
|
|
3
|
+
name = "settings.fields.controller.arrows";
|
|
4
|
+
EVENT_SHOW = this.EVENTS.SETTINGS_VIEW.SHOW;
|
|
5
|
+
EVENT_HIDE = this.EVENTS.SETTINGS_VIEW.HIDE;
|
|
6
|
+
EVENT_STATE_UPDATE = this.EVENTS.SETTINGS_VIEW.STATE_UPDATE;
|
|
7
|
+
EVENT_UPDATE_FAST = this.EVENTS.SETTINGS_VIEW.UPDATE_FAST;
|
|
8
|
+
CONTROLS_ZINDEX = this.PARAMS.SETTINGS_VIEW.ZINDEX;
|
|
9
|
+
fields = [];
|
|
10
|
+
subscribe = () => {
|
|
11
|
+
this.on(this.EVENT_SHOW, this.onShow);
|
|
12
|
+
this.on(this.EVENT_HIDE, this.onHide);
|
|
13
|
+
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
14
|
+
};
|
|
15
|
+
onShow = (payload = {}) => {
|
|
16
|
+
this.syncFields(payload);
|
|
17
|
+
this.currentItem = null;
|
|
18
|
+
return this.emit(this.EVENTS.CONTROLS.PUSH, {
|
|
19
|
+
key: this.name,
|
|
20
|
+
controls: this.controls,
|
|
21
|
+
index: this.CONTROLS_ZINDEX + 1,
|
|
22
|
+
checkNext: true,
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
onTabSet = (payload = {}) => {
|
|
26
|
+
this.syncFields(payload);
|
|
27
|
+
this.currentItem = null;
|
|
28
|
+
this.emitCurrentItem();
|
|
29
|
+
};
|
|
30
|
+
getMaxCurrentItem = () => Math.max((this.fields.length ?? 1) - 1, 0);
|
|
31
|
+
syncFields = (payload = {}) => {
|
|
32
|
+
this.fields = payload.tab?.fields ?? [];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
2
3
|
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
3
4
|
import type { SettingsViewPluginState, SettingsViewTabContent, SettingsViewTabPayload } from "../utils/settings.js";
|
|
4
5
|
export declare class SettingsViewController extends ModuleController<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams, SettingsViewPluginState> {
|
|
@@ -13,22 +14,25 @@ export declare class SettingsViewController extends ModuleController<SettingsVie
|
|
|
13
14
|
tabsContent: {
|
|
14
15
|
[k: string]: SettingsViewTabContent;
|
|
15
16
|
};
|
|
16
|
-
fields:
|
|
17
|
+
fields: SettingsField[];
|
|
17
18
|
locs: Record<string, string> | undefined;
|
|
18
19
|
showTextExample: boolean;
|
|
19
20
|
currentItemInner: number;
|
|
20
21
|
};
|
|
21
22
|
onShowFields: (payload?: SettingsViewTabPayload) => Promise<void>;
|
|
22
23
|
onTabSet: (payload?: SettingsViewTabPayload) => Promise<void | unknown[]>;
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
onStateUpdate: (payload?: Partial<SettingsViewPluginState>) => void;
|
|
25
|
+
getFocusedField: () => SettingsField | undefined;
|
|
26
|
+
innerIndexOfField: (field?: SettingsField) => number;
|
|
27
|
+
optionIndexOf: (length: number, index?: number) => number;
|
|
28
|
+
shiftOptions: (direction: -1 | 1) => void;
|
|
29
|
+
onArrowLeft: () => void | false;
|
|
30
|
+
onArrowRight: () => void | false;
|
|
25
31
|
onInteract: () => void;
|
|
26
32
|
controls: {
|
|
27
33
|
abstract_interact: () => undefined;
|
|
28
34
|
abstract_arrow_left: () => undefined;
|
|
29
35
|
abstract_arrow_right: () => undefined;
|
|
30
|
-
abstract_arrow_up: () => Promise<void>;
|
|
31
|
-
abstract_arrow_bottom: () => Promise<void>;
|
|
32
36
|
};
|
|
33
37
|
controlsCheckNext: boolean;
|
|
34
38
|
subscribe: () => void;
|
|
@@ -13,7 +13,6 @@ export class SettingsViewController extends ModuleController {
|
|
|
13
13
|
const { tab, locs } = payload;
|
|
14
14
|
const fields = tab?.fields ?? [];
|
|
15
15
|
const key = this.tabKeyOf(payload);
|
|
16
|
-
this.maxCurrentItem = Math.max((fields.length ?? 1) - 1, 0);
|
|
17
16
|
const tabsContent = payload.tabs
|
|
18
17
|
? Object.fromEntries(Object.entries(payload.tabs).map(([tabKey, settingsTab]) => [tabKey, this.tabContentOf(settingsTab, locs)]))
|
|
19
18
|
: { ...(this.state.tabsContent ?? {}), [key]: this.tabContentOf(tab, locs) };
|
|
@@ -32,8 +31,7 @@ export class SettingsViewController extends ModuleController {
|
|
|
32
31
|
if (!this.state.isShow)
|
|
33
32
|
return this.onShow(payload);
|
|
34
33
|
const isForce = Boolean(payload.isForce || this.shared.viewForceAnimationSources?.isNotEmpty());
|
|
35
|
-
this.
|
|
36
|
-
const next = { ...this.applyTab(payload), currentItem: null };
|
|
34
|
+
const next = { ...this.applyTab(payload), currentArrowVerticalItem: null };
|
|
37
35
|
if (isForce)
|
|
38
36
|
return this.updateStateAndViewForce(next);
|
|
39
37
|
const key = next.tabKey;
|
|
@@ -45,17 +43,54 @@ export class SettingsViewController extends ModuleController {
|
|
|
45
43
|
}
|
|
46
44
|
return this.updateStateAndView(next);
|
|
47
45
|
};
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
46
|
+
onStateUpdate = (payload = {}) => {
|
|
47
|
+
const currentArrowVerticalItem = payload.currentArrowVerticalItem;
|
|
48
|
+
const field = currentArrowVerticalItem == null ? undefined : this.state.fields?.[currentArrowVerticalItem];
|
|
49
|
+
this.updateState({ ...payload, currentItemInner: this.innerIndexOfField(field) });
|
|
50
|
+
};
|
|
51
|
+
getFocusedField = () => {
|
|
52
|
+
const { currentArrowVerticalItem, fields = [] } = this.state;
|
|
53
|
+
if (currentArrowVerticalItem == null || !fields.length)
|
|
51
54
|
return;
|
|
52
|
-
|
|
55
|
+
return fields[currentArrowVerticalItem];
|
|
56
|
+
};
|
|
57
|
+
innerIndexOfField = (field) => {
|
|
58
|
+
const options = field?.options ?? [];
|
|
59
|
+
if (!options.length)
|
|
60
|
+
return 0;
|
|
61
|
+
const settingKey = field?.settingKey;
|
|
53
62
|
if (!settingKey)
|
|
63
|
+
return 0;
|
|
64
|
+
const index = options.findIndex((option) => option.value === this.shared.settings[settingKey]);
|
|
65
|
+
return index >= 0 ? index : 0;
|
|
66
|
+
};
|
|
67
|
+
optionIndexOf = (length, index = 0) => {
|
|
68
|
+
if (!length)
|
|
69
|
+
return 0;
|
|
70
|
+
return ((index % length) + length) % length;
|
|
71
|
+
};
|
|
72
|
+
shiftOptions = (direction) => {
|
|
73
|
+
const field = this.getFocusedField();
|
|
74
|
+
const options = field?.options ?? [];
|
|
75
|
+
if (!options.length)
|
|
54
76
|
return;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
const currentItemInner = this.optionIndexOf(options.length, (this.state.currentItemInner ?? 0) + direction);
|
|
78
|
+
const option = options[currentItemInner];
|
|
79
|
+
const settingKey = field?.settingKey;
|
|
80
|
+
void this.updateStateAndViewFast({ currentItemInner });
|
|
81
|
+
if (settingKey && option && !option.action) {
|
|
82
|
+
void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: option.value });
|
|
58
83
|
}
|
|
84
|
+
};
|
|
85
|
+
onArrowLeft = () => {
|
|
86
|
+
const field = this.getFocusedField();
|
|
87
|
+
if (!field)
|
|
88
|
+
return;
|
|
89
|
+
if (field.options?.length)
|
|
90
|
+
return this.shiftOptions(-1);
|
|
91
|
+
const { settingKey = "", step = 0, min = 0, type = "" } = field;
|
|
92
|
+
if (!settingKey)
|
|
93
|
+
return;
|
|
59
94
|
if (type === "toggle") {
|
|
60
95
|
return this.shared.settings[settingKey] === true && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: false });
|
|
61
96
|
}
|
|
@@ -65,15 +100,14 @@ export class SettingsViewController extends ModuleController {
|
|
|
65
100
|
}
|
|
66
101
|
};
|
|
67
102
|
onArrowRight = () => {
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
103
|
+
const field = this.getFocusedField();
|
|
104
|
+
if (!field)
|
|
70
105
|
return;
|
|
71
|
-
|
|
106
|
+
if (field.options?.length)
|
|
107
|
+
return this.shiftOptions(1);
|
|
108
|
+
const { settingKey = "", step = 0, max = 0, type = "" } = field;
|
|
72
109
|
if (!settingKey)
|
|
73
110
|
return;
|
|
74
|
-
if (options?.length) {
|
|
75
|
-
return this.updateStateAndViewFast({ currentItemInner: ((currentItemInner ?? 0) + 1) % options.length });
|
|
76
|
-
}
|
|
77
111
|
if (type === "toggle") {
|
|
78
112
|
return this.shared.settings[settingKey] === false && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: true });
|
|
79
113
|
}
|
|
@@ -83,16 +117,23 @@ export class SettingsViewController extends ModuleController {
|
|
|
83
117
|
}
|
|
84
118
|
};
|
|
85
119
|
onInteract = () => {
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
120
|
+
const field = this.getFocusedField();
|
|
121
|
+
if (!field)
|
|
122
|
+
return;
|
|
123
|
+
const { settingKey = "", type = "", options = [] } = field;
|
|
124
|
+
const { locs = {} } = this.state;
|
|
125
|
+
if (options?.length) {
|
|
126
|
+
const option = options[this.optionIndexOf(options.length, this.state.currentItemInner ?? 0)];
|
|
127
|
+
if (!option)
|
|
128
|
+
return;
|
|
129
|
+
if (option.action)
|
|
130
|
+
return this.PARAMS.SETTINGS_VIEW.ACTIONS[option.action](this, locs);
|
|
131
|
+
if (settingKey)
|
|
132
|
+
return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: option.value });
|
|
88
133
|
return;
|
|
89
|
-
|
|
134
|
+
}
|
|
90
135
|
if (!settingKey)
|
|
91
136
|
return;
|
|
92
|
-
if (options?.length)
|
|
93
|
-
return options[currentItemInner ?? 0].action
|
|
94
|
-
? this.PARAMS.SETTINGS_VIEW.ACTIONS[options[currentItemInner ?? 0].action](this, locs)
|
|
95
|
-
: void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: options[currentItemInner ?? 0].value });
|
|
96
137
|
if (type === "toggle")
|
|
97
138
|
return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: !this.shared.settings[settingKey] });
|
|
98
139
|
};
|
|
@@ -100,14 +141,14 @@ export class SettingsViewController extends ModuleController {
|
|
|
100
141
|
[this.CONST.CONTROLS.BUTTONS.INTERACT]: () => void this.onInteract(),
|
|
101
142
|
[this.CONST.CONTROLS.BUTTONS.ARROW_LEFT]: () => void this.onArrowLeft(),
|
|
102
143
|
[this.CONST.CONTROLS.BUTTONS.ARROW_RIGHT]: () => void this.onArrowRight(),
|
|
103
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_UP]: this.decCurrentItem,
|
|
104
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_BOTTOM]: this.incCurrentItem,
|
|
105
144
|
};
|
|
106
145
|
controlsCheckNext = true;
|
|
107
146
|
subscribe = () => {
|
|
108
147
|
this.on(this.EVENTS.SETTINGS_VIEW.SHOW, this.onShowFields);
|
|
109
148
|
this.on(this.EVENTS.SETTINGS_VIEW.HIDE, this.onHide);
|
|
110
149
|
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
150
|
+
this.on(this.EVENTS.SETTINGS_VIEW.STATE_UPDATE, this.onStateUpdate);
|
|
151
|
+
this.on(this.EVENTS.SETTINGS_VIEW.UPDATE_FAST, this.updateViewFast);
|
|
111
152
|
this.on(this.EVENTS.SETTINGS.CHANGED, this.onSettingChange);
|
|
112
153
|
this.on(this.EVENTS.COMPONENTS.EXTERNAL_UPDATE, this.onSettingChange);
|
|
113
154
|
};
|
package/dist/utils/settings.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type SettingsViewPluginState = {
|
|
|
13
13
|
fields?: SettingsField[];
|
|
14
14
|
locs?: Record<string, string>;
|
|
15
15
|
showTextExample?: boolean;
|
|
16
|
-
|
|
16
|
+
currentArrowVerticalItem?: number | null;
|
|
17
17
|
currentItemInner?: number | null;
|
|
18
18
|
};
|
|
19
19
|
export type SettingsViewTabPayload = {
|
package/dist/view/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createElement as _createElement } from "react";
|
|
|
3
3
|
import { View, createRenderFunc, useIsForceHook, useMemo, useStoreState } from "@vnejs/uis.react";
|
|
4
4
|
import { SettingsTabContent } from "./SettingsTabContent.js";
|
|
5
5
|
const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS }) => {
|
|
6
|
-
const { isShow = false, isForce = false, locs = {}, tabsContent = {}, tabKey = "",
|
|
6
|
+
const { isShow = false, isForce = false, locs = {}, tabsContent = {}, tabKey = "", currentArrowVerticalItem = null, currentItemInner = null, } = useStoreState(store, onMount);
|
|
7
7
|
const isRealForce = useIsForceHook(isForce);
|
|
8
8
|
const propsByView = PARAMS.SETTINGS_VIEW.VIEW_PROPS;
|
|
9
9
|
const propsView = useMemo(() => ({
|
|
@@ -12,7 +12,7 @@ const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, C
|
|
|
12
12
|
isForce: isRealForce,
|
|
13
13
|
transition: isRealForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION,
|
|
14
14
|
}), [isShow, isRealForce, propsByView.view, PARAMS.SETTINGS_VIEW.TRANSITION]);
|
|
15
|
-
const propsTab = useMemo(() => ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isForce: isRealForce, currentItem, currentItemInner, locs }), [store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isRealForce,
|
|
15
|
+
const propsTab = useMemo(() => ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isForce: isRealForce, currentItem: currentArrowVerticalItem, currentItemInner, locs }), [store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isRealForce, currentArrowVerticalItem, currentItemInner, locs]);
|
|
16
16
|
return (_jsx(View, { ...propsView, children: Object.entries(tabsContent).map(([key, content]) => (_createElement(SettingsTabContent, { ...propsTab, key: key, isShow: tabKey === key, fields: content.fields, showTextExample: content.showTextExample, locs: content.locs ?? locs }))) }));
|
|
17
17
|
};
|
|
18
18
|
export const render = createRenderFunc(SettingsViewComponent);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vnejs/plugins.views.settings",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@vnejs/helpers": "~0.2.0",
|
|
37
37
|
"@vnejs/module.core": "~0.2.0",
|
|
38
38
|
"@vnejs/module.components": "~0.2.0",
|
|
39
|
+
"@vnejs/module.controller.arrows.vertical": "~0.2.0",
|
|
39
40
|
"@vnejs/contracts.controls": "~0.2.0",
|
|
40
41
|
"@vnejs/contracts.core.settings": "~0.2.0",
|
|
41
42
|
"@vnejs/contracts.settings.textsize": "~0.2.0",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"@vnejs/helpers": "~0.2.0",
|
|
56
57
|
"@vnejs/module.core": "~0.2.0",
|
|
57
58
|
"@vnejs/module.components": "~0.2.0",
|
|
59
|
+
"@vnejs/module.controller.arrows.vertical": "~0.2.0",
|
|
58
60
|
"@vnejs/shared": "~0.2.0",
|
|
59
61
|
"@vnejs/sources-set": "~0.2.0",
|
|
60
62
|
"@vnejs/uis.react": "~0.2.0"
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { regPlugin } from "@vnejs/shared";
|
|
|
4
4
|
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.settings";
|
|
5
5
|
|
|
6
6
|
import { SettingsViewController } from "./modules/controller.js";
|
|
7
|
+
import { SettingsViewArrows } from "./modules/arrows.js";
|
|
7
8
|
import { SettingsView } from "./modules/view.js";
|
|
8
9
|
|
|
9
|
-
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsView]);
|
|
10
|
+
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsViewArrows, SettingsView]);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ControlsPushPayload } from "@vnejs/contracts.controls";
|
|
2
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
3
|
+
import { ModuleControllerArrowsVertical } from "@vnejs/module.controller.arrows.vertical";
|
|
4
|
+
|
|
5
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
6
|
+
import type { SettingsViewTabPayload } from "../utils/settings.js";
|
|
7
|
+
|
|
8
|
+
export class SettingsViewArrows extends ModuleControllerArrowsVertical<
|
|
9
|
+
SettingsViewPluginEvents,
|
|
10
|
+
SettingsViewPluginConstants,
|
|
11
|
+
SettingsViewPluginSettings,
|
|
12
|
+
SettingsViewPluginParams
|
|
13
|
+
> {
|
|
14
|
+
name = "settings.fields.controller.arrows";
|
|
15
|
+
|
|
16
|
+
EVENT_SHOW = this.EVENTS.SETTINGS_VIEW.SHOW;
|
|
17
|
+
EVENT_HIDE = this.EVENTS.SETTINGS_VIEW.HIDE;
|
|
18
|
+
EVENT_STATE_UPDATE = this.EVENTS.SETTINGS_VIEW.STATE_UPDATE;
|
|
19
|
+
EVENT_UPDATE_FAST = this.EVENTS.SETTINGS_VIEW.UPDATE_FAST;
|
|
20
|
+
|
|
21
|
+
CONTROLS_ZINDEX = this.PARAMS.SETTINGS_VIEW.ZINDEX;
|
|
22
|
+
|
|
23
|
+
fields: SettingsField[] = [];
|
|
24
|
+
|
|
25
|
+
subscribe = () => {
|
|
26
|
+
this.on(this.EVENT_SHOW, this.onShow);
|
|
27
|
+
this.on(this.EVENT_HIDE, this.onHide);
|
|
28
|
+
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
onShow = (payload: SettingsViewTabPayload = {}) => {
|
|
32
|
+
this.syncFields(payload);
|
|
33
|
+
this.currentItem = null;
|
|
34
|
+
|
|
35
|
+
return this.emit(this.EVENTS.CONTROLS.PUSH, {
|
|
36
|
+
key: this.name,
|
|
37
|
+
controls: this.controls,
|
|
38
|
+
index: this.CONTROLS_ZINDEX + 1,
|
|
39
|
+
checkNext: true,
|
|
40
|
+
} satisfies ControlsPushPayload);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
onTabSet = (payload: SettingsViewTabPayload = {}) => {
|
|
44
|
+
this.syncFields(payload);
|
|
45
|
+
this.currentItem = null;
|
|
46
|
+
this.emitCurrentItem();
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
getMaxCurrentItem = () => Math.max((this.fields.length ?? 1) - 1, 0);
|
|
50
|
+
|
|
51
|
+
syncFields = (payload: SettingsViewTabPayload = {}) => {
|
|
52
|
+
this.fields = payload.tab?.fields ?? [];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
2
3
|
|
|
3
4
|
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
4
5
|
import type { SettingsViewPluginState, SettingsViewTabContent, SettingsViewTabPayload } from "../utils/settings.js";
|
|
@@ -28,8 +29,6 @@ export class SettingsViewController extends ModuleController<
|
|
|
28
29
|
const fields = tab?.fields ?? [];
|
|
29
30
|
const key = this.tabKeyOf(payload);
|
|
30
31
|
|
|
31
|
-
this.maxCurrentItem = Math.max((fields.length ?? 1) - 1, 0);
|
|
32
|
-
|
|
33
32
|
const tabsContent = payload.tabs
|
|
34
33
|
? Object.fromEntries(
|
|
35
34
|
Object.entries(payload.tabs).map(([tabKey, settingsTab]) => [tabKey, this.tabContentOf(settingsTab, locs)]),
|
|
@@ -54,9 +53,7 @@ export class SettingsViewController extends ModuleController<
|
|
|
54
53
|
|
|
55
54
|
const isForce = Boolean(payload.isForce || this.shared.viewForceAnimationSources?.isNotEmpty());
|
|
56
55
|
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
const next = { ...this.applyTab(payload), currentItem: null as null };
|
|
56
|
+
const next = { ...this.applyTab(payload), currentArrowVerticalItem: null as null };
|
|
60
57
|
|
|
61
58
|
if (isForce) return this.updateStateAndViewForce(next);
|
|
62
59
|
|
|
@@ -72,19 +69,65 @@ export class SettingsViewController extends ModuleController<
|
|
|
72
69
|
return this.updateStateAndView(next);
|
|
73
70
|
};
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
const
|
|
72
|
+
onStateUpdate = (payload: Partial<SettingsViewPluginState> = {}) => {
|
|
73
|
+
const currentArrowVerticalItem = payload.currentArrowVerticalItem;
|
|
74
|
+
const field = currentArrowVerticalItem == null ? undefined : this.state.fields?.[currentArrowVerticalItem];
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
this.updateState({ ...payload, currentItemInner: this.innerIndexOfField(field) });
|
|
77
|
+
};
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
getFocusedField = () => {
|
|
80
|
+
const { currentArrowVerticalItem, fields = [] } = this.state;
|
|
82
81
|
|
|
83
|
-
if (
|
|
84
|
-
|
|
82
|
+
if (currentArrowVerticalItem == null || !fields.length) return;
|
|
83
|
+
|
|
84
|
+
return fields[currentArrowVerticalItem];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
innerIndexOfField = (field?: SettingsField) => {
|
|
88
|
+
const options = field?.options ?? [];
|
|
89
|
+
if (!options.length) return 0;
|
|
90
|
+
|
|
91
|
+
const settingKey = field?.settingKey;
|
|
92
|
+
if (!settingKey) return 0;
|
|
93
|
+
|
|
94
|
+
const index = options.findIndex((option) => option.value === this.shared.settings[settingKey]);
|
|
95
|
+
|
|
96
|
+
return index >= 0 ? index : 0;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
optionIndexOf = (length: number, index = 0) => {
|
|
100
|
+
if (!length) return 0;
|
|
101
|
+
|
|
102
|
+
return ((index % length) + length) % length;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
shiftOptions = (direction: -1 | 1) => {
|
|
106
|
+
const field = this.getFocusedField();
|
|
107
|
+
const options = field?.options ?? [];
|
|
108
|
+
|
|
109
|
+
if (!options.length) return;
|
|
110
|
+
|
|
111
|
+
const currentItemInner = this.optionIndexOf(options.length, (this.state.currentItemInner ?? 0) + direction);
|
|
112
|
+
const option = options[currentItemInner];
|
|
113
|
+
const settingKey = field?.settingKey;
|
|
114
|
+
|
|
115
|
+
void this.updateStateAndViewFast({ currentItemInner });
|
|
85
116
|
|
|
86
|
-
|
|
117
|
+
if (settingKey && option && !option.action) {
|
|
118
|
+
void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: option.value });
|
|
87
119
|
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
onArrowLeft = () => {
|
|
123
|
+
const field = this.getFocusedField();
|
|
124
|
+
|
|
125
|
+
if (!field) return;
|
|
126
|
+
|
|
127
|
+
if (field.options?.length) return this.shiftOptions(-1);
|
|
128
|
+
|
|
129
|
+
const { settingKey = "", step = 0, min = 0, type = "" } = field;
|
|
130
|
+
if (!settingKey) return;
|
|
88
131
|
|
|
89
132
|
if (type === "toggle") {
|
|
90
133
|
return this.shared.settings[settingKey] === true && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: false });
|
|
@@ -98,16 +141,14 @@ export class SettingsViewController extends ModuleController<
|
|
|
98
141
|
};
|
|
99
142
|
|
|
100
143
|
onArrowRight = () => {
|
|
101
|
-
const
|
|
144
|
+
const field = this.getFocusedField();
|
|
102
145
|
|
|
103
|
-
if (
|
|
146
|
+
if (!field) return;
|
|
104
147
|
|
|
105
|
-
|
|
106
|
-
if (!settingKey) return;
|
|
148
|
+
if (field.options?.length) return this.shiftOptions(1);
|
|
107
149
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
150
|
+
const { settingKey = "", step = 0, max = 0, type = "" } = field;
|
|
151
|
+
if (!settingKey) return;
|
|
111
152
|
|
|
112
153
|
if (type === "toggle") {
|
|
113
154
|
return this.shared.settings[settingKey] === false && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: true });
|
|
@@ -121,17 +162,25 @@ export class SettingsViewController extends ModuleController<
|
|
|
121
162
|
};
|
|
122
163
|
|
|
123
164
|
onInteract = () => {
|
|
124
|
-
const
|
|
165
|
+
const field = this.getFocusedField();
|
|
125
166
|
|
|
126
|
-
if (
|
|
167
|
+
if (!field) return;
|
|
127
168
|
|
|
128
|
-
const { settingKey = "", type = "", options = [] } =
|
|
129
|
-
|
|
169
|
+
const { settingKey = "", type = "", options = [] } = field;
|
|
170
|
+
const { locs = {} } = this.state;
|
|
171
|
+
|
|
172
|
+
if (options?.length) {
|
|
173
|
+
const option = options[this.optionIndexOf(options.length, this.state.currentItemInner ?? 0)];
|
|
174
|
+
|
|
175
|
+
if (!option) return;
|
|
130
176
|
|
|
131
|
-
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
177
|
+
if (option.action) return this.PARAMS.SETTINGS_VIEW.ACTIONS[option.action](this, locs);
|
|
178
|
+
if (settingKey) return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: option.value });
|
|
179
|
+
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!settingKey) return;
|
|
135
184
|
|
|
136
185
|
if (type === "toggle") return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: !this.shared.settings[settingKey] });
|
|
137
186
|
};
|
|
@@ -140,8 +189,6 @@ export class SettingsViewController extends ModuleController<
|
|
|
140
189
|
[this.CONST.CONTROLS.BUTTONS.INTERACT]: () => void this.onInteract(),
|
|
141
190
|
[this.CONST.CONTROLS.BUTTONS.ARROW_LEFT]: () => void this.onArrowLeft(),
|
|
142
191
|
[this.CONST.CONTROLS.BUTTONS.ARROW_RIGHT]: () => void this.onArrowRight(),
|
|
143
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_UP]: this.decCurrentItem,
|
|
144
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_BOTTOM]: this.incCurrentItem,
|
|
145
192
|
};
|
|
146
193
|
controlsCheckNext = true;
|
|
147
194
|
|
|
@@ -149,6 +196,8 @@ export class SettingsViewController extends ModuleController<
|
|
|
149
196
|
this.on(this.EVENTS.SETTINGS_VIEW.SHOW, this.onShowFields);
|
|
150
197
|
this.on(this.EVENTS.SETTINGS_VIEW.HIDE, this.onHide);
|
|
151
198
|
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
199
|
+
this.on(this.EVENTS.SETTINGS_VIEW.STATE_UPDATE, this.onStateUpdate);
|
|
200
|
+
this.on(this.EVENTS.SETTINGS_VIEW.UPDATE_FAST, this.updateViewFast);
|
|
152
201
|
|
|
153
202
|
this.on(this.EVENTS.SETTINGS.CHANGED, this.onSettingChange);
|
|
154
203
|
this.on(this.EVENTS.COMPONENTS.EXTERNAL_UPDATE, this.onSettingChange);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { spyEvent } from "@vnejs/test-utils";
|
|
4
|
+
|
|
5
|
+
import { SettingsViewArrows } from "../modules/arrows.js";
|
|
6
|
+
import { CONTROLS_EVENTS, createSettingsViewTestModule, registerSettingsViewPluginVne, SUBSCRIBE_EVENTS } from "./setup.js";
|
|
7
|
+
|
|
8
|
+
const generalTab = {
|
|
9
|
+
titleLocKey: "general",
|
|
10
|
+
fields: [{ settingKey: "a", type: "toggle" }, { settingKey: "b", type: "toggle" }],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("SettingsViewArrows", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
registerSettingsViewPluginVne();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("SHOW pushes arrow controls above the controller layer", async () => {
|
|
19
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewArrows);
|
|
20
|
+
const arrows = module as SettingsViewArrows;
|
|
21
|
+
const push = spyEvent(observer, CONTROLS_EVENTS.PUSH);
|
|
22
|
+
|
|
23
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, { tab: generalTab });
|
|
24
|
+
|
|
25
|
+
expect(arrows.currentItem).toBeNull();
|
|
26
|
+
expect(arrows.fields).toHaveLength(2);
|
|
27
|
+
expect(push).toHaveBeenCalledExactlyOnceWith(
|
|
28
|
+
expect.objectContaining({
|
|
29
|
+
key: "settings.fields.controller.arrows",
|
|
30
|
+
checkNext: true,
|
|
31
|
+
index: arrows.PARAMS.SETTINGS_VIEW.ZINDEX + 1,
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("HIDE pops arrow controls", async () => {
|
|
37
|
+
const { observer } = createSettingsViewTestModule(SettingsViewArrows);
|
|
38
|
+
const pop = spyEvent(observer, CONTROLS_EVENTS.POP);
|
|
39
|
+
|
|
40
|
+
await observer.emit(SUBSCRIBE_EVENTS.HIDE);
|
|
41
|
+
|
|
42
|
+
expect(pop).toHaveBeenCalledExactlyOnceWith({ key: "settings.fields.controller.arrows" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("first ARROW_BOTTOM selects 0 and emits STATE_UPDATE plus UPDATE_FAST", async () => {
|
|
46
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewArrows);
|
|
47
|
+
const arrows = module as SettingsViewArrows;
|
|
48
|
+
const stateUpdate = spyEvent(observer, SUBSCRIBE_EVENTS.STATE_UPDATE);
|
|
49
|
+
const updateFast = spyEvent(observer, SUBSCRIBE_EVENTS.UPDATE_FAST);
|
|
50
|
+
|
|
51
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, { tab: generalTab });
|
|
52
|
+
await arrows.incCurrentItem();
|
|
53
|
+
|
|
54
|
+
expect(arrows.currentItem).toBe(0);
|
|
55
|
+
expect(stateUpdate).toHaveBeenCalledExactlyOnceWith({ currentArrowVerticalItem: 0 });
|
|
56
|
+
expect(updateFast).toHaveBeenCalledOnce();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("arrow wrap cycles fields", async () => {
|
|
60
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewArrows);
|
|
61
|
+
const arrows = module as SettingsViewArrows;
|
|
62
|
+
const stateUpdate = spyEvent(observer, SUBSCRIBE_EVENTS.STATE_UPDATE);
|
|
63
|
+
|
|
64
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, { tab: generalTab });
|
|
65
|
+
await arrows.incCurrentItem();
|
|
66
|
+
await arrows.incCurrentItem();
|
|
67
|
+
expect(arrows.currentItem).toBe(1);
|
|
68
|
+
|
|
69
|
+
await arrows.incCurrentItem();
|
|
70
|
+
expect(arrows.currentItem).toBe(0);
|
|
71
|
+
expect(stateUpdate).toHaveBeenLastCalledWith({ currentArrowVerticalItem: 0 });
|
|
72
|
+
|
|
73
|
+
await arrows.decCurrentItem();
|
|
74
|
+
expect(arrows.currentItem).toBe(1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("TAB_SET resets currentItem and syncs fields", async () => {
|
|
78
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewArrows);
|
|
79
|
+
const arrows = module as SettingsViewArrows;
|
|
80
|
+
const stateUpdate = spyEvent(observer, SUBSCRIBE_EVENTS.STATE_UPDATE);
|
|
81
|
+
|
|
82
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, { tab: generalTab });
|
|
83
|
+
await arrows.incCurrentItem();
|
|
84
|
+
expect(arrows.currentItem).toBe(0);
|
|
85
|
+
|
|
86
|
+
await observer.emit(SUBSCRIBE_EVENTS.TAB_SET, {
|
|
87
|
+
tab: {
|
|
88
|
+
titleLocKey: "audio",
|
|
89
|
+
fields: [{ settingKey: "c", type: "toggle" }, { settingKey: "d", type: "toggle" }, { settingKey: "e", type: "toggle" }],
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(arrows.currentItem).toBeNull();
|
|
94
|
+
expect(arrows.fields).toHaveLength(3);
|
|
95
|
+
expect(stateUpdate).toHaveBeenLastCalledWith({ currentArrowVerticalItem: null });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { spyEvent } from "@vnejs/test-utils";
|
|
4
4
|
import { SUBSCRIBE_EVENTS as SETTINGS_EVENTS } from "@vnejs/contracts.core.settings";
|
|
@@ -11,7 +11,7 @@ describe("SettingsViewController", () => {
|
|
|
11
11
|
registerSettingsViewPluginVne();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
it("SHOW stores fields from tab
|
|
14
|
+
it("SHOW stores fields from tab", async () => {
|
|
15
15
|
const { module, observer } = createSettingsViewTestModule(SettingsViewController);
|
|
16
16
|
|
|
17
17
|
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
@@ -26,7 +26,6 @@ describe("SettingsViewController", () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
expect((module as SettingsViewController).state.fields).toHaveLength(2);
|
|
29
|
-
expect((module as SettingsViewController).maxCurrentItem).toBe(1);
|
|
30
29
|
expect((module as SettingsViewController).state.isShow).toBe(true);
|
|
31
30
|
expect((module as SettingsViewController).state.tabKey).toBe("general");
|
|
32
31
|
expect(Object.keys((module as SettingsViewController).state.tabsContent ?? {})).toEqual(["general"]);
|
|
@@ -41,8 +40,7 @@ describe("SettingsViewController", () => {
|
|
|
41
40
|
tab: { titleLocKey: "general", fields: [{ settingKey: "test.toggle", type: "toggle" }] },
|
|
42
41
|
});
|
|
43
42
|
|
|
44
|
-
(module as SettingsViewController).
|
|
45
|
-
(module as SettingsViewController).updateState({ currentItem: 0 });
|
|
43
|
+
(module as SettingsViewController).updateState({ currentArrowVerticalItem: 0 });
|
|
46
44
|
|
|
47
45
|
const settingsSet = spyEvent(observer, SETTINGS_EVENTS.SET);
|
|
48
46
|
|
|
@@ -85,9 +83,151 @@ describe("SettingsViewController", () => {
|
|
|
85
83
|
|
|
86
84
|
expect((module as SettingsViewController).state.fields).toHaveLength(2);
|
|
87
85
|
expect((module as SettingsViewController).state.showTextExample).toBe(true);
|
|
88
|
-
expect((module as SettingsViewController).maxCurrentItem).toBe(1);
|
|
89
86
|
expect((module as SettingsViewController).state.currentItemInner).toBe(0);
|
|
87
|
+
expect((module as SettingsViewController).state.currentArrowVerticalItem).toBeNull();
|
|
90
88
|
expect((module as SettingsViewController).state.tabKey).toBe("audio");
|
|
91
89
|
expect(Object.keys((module as SettingsViewController).state.tabsContent ?? {}).sort()).toEqual(["audio", "general"]);
|
|
92
90
|
});
|
|
91
|
+
|
|
92
|
+
it("ARROW_RIGHT on selector_texts cycles inner and SET", async () => {
|
|
93
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController, {
|
|
94
|
+
shared: { settings: { "test.lang": "ru" } },
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
98
|
+
tab: {
|
|
99
|
+
titleLocKey: "general",
|
|
100
|
+
fields: [
|
|
101
|
+
{
|
|
102
|
+
settingKey: "test.lang",
|
|
103
|
+
type: "selector_texts",
|
|
104
|
+
options: [
|
|
105
|
+
{ text: "ru", value: "ru" },
|
|
106
|
+
{ text: "en", value: "en" },
|
|
107
|
+
{ text: "de", value: "de" },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const controller = module as SettingsViewController;
|
|
115
|
+
|
|
116
|
+
controller.updateState({ currentArrowVerticalItem: 0, currentItemInner: 0 });
|
|
117
|
+
|
|
118
|
+
const settingsSet = spyEvent(observer, SETTINGS_EVENTS.SET);
|
|
119
|
+
|
|
120
|
+
controller.onArrowRight();
|
|
121
|
+
|
|
122
|
+
expect(controller.state.currentItemInner).toBe(1);
|
|
123
|
+
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ name: "test.lang", value: "en" });
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("ARROW_LEFT on selector_texts wraps inner and SET", async () => {
|
|
127
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController, {
|
|
128
|
+
shared: { settings: { "test.lang": "ru" } },
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
132
|
+
tab: {
|
|
133
|
+
titleLocKey: "general",
|
|
134
|
+
fields: [
|
|
135
|
+
{
|
|
136
|
+
settingKey: "test.lang",
|
|
137
|
+
type: "selector_texts",
|
|
138
|
+
options: [
|
|
139
|
+
{ text: "ru", value: "ru" },
|
|
140
|
+
{ text: "en", value: "en" },
|
|
141
|
+
{ text: "de", value: "de" },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const controller = module as SettingsViewController;
|
|
149
|
+
|
|
150
|
+
controller.updateState({ currentArrowVerticalItem: 0, currentItemInner: 0 });
|
|
151
|
+
|
|
152
|
+
const settingsSet = spyEvent(observer, SETTINGS_EVENTS.SET);
|
|
153
|
+
|
|
154
|
+
controller.onArrowLeft();
|
|
155
|
+
|
|
156
|
+
expect(controller.state.currentItemInner).toBe(2);
|
|
157
|
+
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ name: "test.lang", value: "de" });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("ARROW_* on actions without settingKey only moves inner", async () => {
|
|
161
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController);
|
|
162
|
+
const controller = module as SettingsViewController;
|
|
163
|
+
const doA = vi.fn();
|
|
164
|
+
const doB = vi.fn();
|
|
165
|
+
|
|
166
|
+
controller.PARAMS.SETTINGS_VIEW.ACTIONS.do_a = doA;
|
|
167
|
+
controller.PARAMS.SETTINGS_VIEW.ACTIONS.do_b = doB;
|
|
168
|
+
|
|
169
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
170
|
+
tab: {
|
|
171
|
+
titleLocKey: "general",
|
|
172
|
+
fields: [
|
|
173
|
+
{
|
|
174
|
+
type: "actions",
|
|
175
|
+
options: [
|
|
176
|
+
{ locKey: "a", action: "do_a" },
|
|
177
|
+
{ locKey: "b", action: "do_b" },
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
controller.updateState({ currentArrowVerticalItem: 0, currentItemInner: 0 });
|
|
185
|
+
|
|
186
|
+
const settingsSet = spyEvent(observer, SETTINGS_EVENTS.SET);
|
|
187
|
+
|
|
188
|
+
controller.onArrowLeft();
|
|
189
|
+
|
|
190
|
+
expect(controller.state.currentItemInner).toBe(1);
|
|
191
|
+
expect(doA).not.toHaveBeenCalled();
|
|
192
|
+
expect(doB).not.toHaveBeenCalled();
|
|
193
|
+
expect(settingsSet).not.toHaveBeenCalled();
|
|
194
|
+
|
|
195
|
+
controller.onInteract();
|
|
196
|
+
|
|
197
|
+
expect(doB).toHaveBeenCalledOnce();
|
|
198
|
+
expect(doA).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("STATE_UPDATE syncs currentItemInner to the current option", async () => {
|
|
202
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController, {
|
|
203
|
+
shared: { settings: { "test.lang": "en" } },
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
207
|
+
tab: {
|
|
208
|
+
titleLocKey: "general",
|
|
209
|
+
fields: [
|
|
210
|
+
{ settingKey: "a", type: "toggle" },
|
|
211
|
+
{
|
|
212
|
+
settingKey: "test.lang",
|
|
213
|
+
type: "selector_texts",
|
|
214
|
+
options: [
|
|
215
|
+
{ text: "ru", value: "ru" },
|
|
216
|
+
{ text: "en", value: "en" },
|
|
217
|
+
{ text: "de", value: "de" },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const controller = module as SettingsViewController;
|
|
225
|
+
|
|
226
|
+
controller.updateState({ currentArrowVerticalItem: 0, currentItemInner: 99 });
|
|
227
|
+
|
|
228
|
+
await observer.emit(SUBSCRIBE_EVENTS.STATE_UPDATE, { currentArrowVerticalItem: 1 });
|
|
229
|
+
|
|
230
|
+
expect(controller.state.currentArrowVerticalItem).toBe(1);
|
|
231
|
+
expect(controller.state.currentItemInner).toBe(1);
|
|
232
|
+
});
|
|
93
233
|
});
|
package/src/tests/setup.ts
CHANGED
|
@@ -23,7 +23,7 @@ export const createSettingsViewTestModule = <T extends Parameters<typeof baseCre
|
|
|
23
23
|
const result = baseCreateTestModule(ModuleClass, {
|
|
24
24
|
...rest,
|
|
25
25
|
state: {
|
|
26
|
-
"settings.fields.controller": { isShow: false, fields: [],
|
|
26
|
+
"settings.fields.controller": { isShow: false, fields: [], currentArrowVerticalItem: null, currentItemInner: 0 },
|
|
27
27
|
...(stateOption ?? {}),
|
|
28
28
|
},
|
|
29
29
|
shared: {
|
|
@@ -36,9 +36,11 @@ export const createSettingsViewTestModule = <T extends Parameters<typeof baseCre
|
|
|
36
36
|
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.SHOW);
|
|
37
37
|
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.HIDE);
|
|
38
38
|
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.UPDATE);
|
|
39
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.UPDATE_FAST);
|
|
40
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.STATE_UPDATE);
|
|
39
41
|
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.TAB_SET);
|
|
40
42
|
|
|
41
43
|
return result;
|
|
42
44
|
};
|
|
43
45
|
|
|
44
|
-
export { SUBSCRIBE_EVENTS, PARAMS, PLUGIN_NAME };
|
|
46
|
+
export { SUBSCRIBE_EVENTS, PARAMS, PLUGIN_NAME, CONTROLS_EVENTS };
|
package/src/utils/settings.ts
CHANGED
package/src/view/index.tsx
CHANGED
|
@@ -21,7 +21,7 @@ const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, C
|
|
|
21
21
|
locs = {},
|
|
22
22
|
tabsContent = {},
|
|
23
23
|
tabKey = "",
|
|
24
|
-
|
|
24
|
+
currentArrowVerticalItem = null,
|
|
25
25
|
currentItemInner = null,
|
|
26
26
|
} = useStoreState<SettingsViewPluginState>(store, onMount);
|
|
27
27
|
|
|
@@ -40,8 +40,8 @@ const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, C
|
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
const propsTab = useMemo(
|
|
43
|
-
() => ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isForce: isRealForce, currentItem, currentItemInner, locs }),
|
|
44
|
-
[store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isRealForce,
|
|
43
|
+
() => ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isForce: isRealForce, currentItem: currentArrowVerticalItem, currentItemInner, locs }),
|
|
44
|
+
[store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS, isRealForce, currentArrowVerticalItem, currentItemInner, locs],
|
|
45
45
|
);
|
|
46
46
|
|
|
47
47
|
return (
|