@vnejs/plugins.views.settings 0.1.2 → 0.2.1
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.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/modules/controller.d.ts +38 -0
- package/dist/modules/controller.js +117 -0
- package/dist/modules/view.d.ts +11 -0
- package/dist/modules/view.js +10 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/dist/utils/settings.d.ts +25 -0
- package/dist/utils/settings.js +1 -0
- package/dist/view/SettingsExampleText.d.ts +9 -0
- package/dist/view/SettingsExampleText.js +25 -0
- package/dist/view/SettingsTabContent.d.ts +15 -0
- package/dist/view/SettingsTabContent.js +32 -0
- package/dist/view/index.d.ts +3 -0
- package/dist/view/index.js +18 -0
- package/package.json +52 -7
- package/src/index.ts +9 -0
- package/src/modules/controller.ts +161 -0
- package/src/modules/view.ts +22 -0
- package/src/tests/controller.test.ts +93 -0
- package/src/tests/setup.ts +44 -0
- package/src/types.ts +20 -0
- package/src/utils/settings.ts +28 -0
- package/src/view/SettingsExampleText.tsx +61 -0
- package/src/view/SettingsTabContent.tsx +118 -0
- package/src/view/index.tsx +63 -0
- package/tsconfig.json +10 -0
- package/const/events.js +0 -7
- package/const/params.js +0 -43
- package/index.js +0 -9
- package/modules/controller.js +0 -132
- package/modules/view.js +0 -14
- package/view/components/SettingsControls.jsx +0 -22
- package/view/components/SettingsCross.jsx +0 -13
- package/view/components/SettingsExampleText.jsx +0 -40
- package/view/components/SettingsTabContent.jsx +0 -66
- package/view/components/index.js +0 -4
- package/view/index.jsx +0 -42
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { spyEvent } from "@vnejs/test-utils";
|
|
4
|
+
import { SUBSCRIBE_EVENTS as SETTINGS_EVENTS } from "@vnejs/contracts.core.settings";
|
|
5
|
+
|
|
6
|
+
import { SettingsViewController } from "../modules/controller.js";
|
|
7
|
+
import { createSettingsViewTestModule, registerSettingsViewPluginVne, SUBSCRIBE_EVENTS } from "./setup.js";
|
|
8
|
+
|
|
9
|
+
describe("SettingsViewController", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
registerSettingsViewPluginVne();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("SHOW stores fields from tab and maxCurrentItem", async () => {
|
|
15
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController);
|
|
16
|
+
|
|
17
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
18
|
+
tabKey: "general",
|
|
19
|
+
tab: {
|
|
20
|
+
titleLocKey: "general",
|
|
21
|
+
fields: [
|
|
22
|
+
{ settingKey: "a", type: "toggle" },
|
|
23
|
+
{ settingKey: "b", type: "toggle" },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect((module as SettingsViewController).state.fields).toHaveLength(2);
|
|
29
|
+
expect((module as SettingsViewController).maxCurrentItem).toBe(1);
|
|
30
|
+
expect((module as SettingsViewController).state.isShow).toBe(true);
|
|
31
|
+
expect((module as SettingsViewController).state.tabKey).toBe("general");
|
|
32
|
+
expect(Object.keys((module as SettingsViewController).state.tabsContent ?? {})).toEqual(["general"]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("ARROW_RIGHT toggles setting when focused", async () => {
|
|
36
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController, {
|
|
37
|
+
shared: { settings: { "test.toggle": false } },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
41
|
+
tab: { titleLocKey: "general", fields: [{ settingKey: "test.toggle", type: "toggle" }] },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
(module as SettingsViewController).currentItem = 0;
|
|
45
|
+
(module as SettingsViewController).updateState({ currentItem: 0 });
|
|
46
|
+
|
|
47
|
+
const settingsSet = spyEvent(observer, SETTINGS_EVENTS.SET);
|
|
48
|
+
|
|
49
|
+
(module as SettingsViewController).onArrowRight();
|
|
50
|
+
|
|
51
|
+
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ name: "test.toggle", value: true });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("TAB_SET while visible replaces fields with fade", async () => {
|
|
55
|
+
const { module, observer } = createSettingsViewTestModule(SettingsViewController);
|
|
56
|
+
|
|
57
|
+
await observer.emit(SUBSCRIBE_EVENTS.SHOW, {
|
|
58
|
+
tabKey: "general",
|
|
59
|
+
tabs: {
|
|
60
|
+
general: { titleLocKey: "general", fields: [{ settingKey: "a", type: "toggle" }] },
|
|
61
|
+
audio: {
|
|
62
|
+
titleLocKey: "audio",
|
|
63
|
+
showTextExample: true,
|
|
64
|
+
fields: [
|
|
65
|
+
{ settingKey: "b", type: "toggle" },
|
|
66
|
+
{ settingKey: "c", type: "toggle" },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
tab: { titleLocKey: "general", fields: [{ settingKey: "a", type: "toggle" }] },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await observer.emit(SUBSCRIBE_EVENTS.TAB_SET, {
|
|
74
|
+
isForce: true,
|
|
75
|
+
tabKey: "audio",
|
|
76
|
+
tab: {
|
|
77
|
+
titleLocKey: "audio",
|
|
78
|
+
showTextExample: true,
|
|
79
|
+
fields: [
|
|
80
|
+
{ settingKey: "b", type: "toggle" },
|
|
81
|
+
{ settingKey: "c", type: "toggle" },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect((module as SettingsViewController).state.fields).toHaveLength(2);
|
|
87
|
+
expect((module as SettingsViewController).state.showTextExample).toBe(true);
|
|
88
|
+
expect((module as SettingsViewController).maxCurrentItem).toBe(1);
|
|
89
|
+
expect((module as SettingsViewController).state.currentItemInner).toBe(0);
|
|
90
|
+
expect((module as SettingsViewController).state.tabKey).toBe("audio");
|
|
91
|
+
expect(Object.keys((module as SettingsViewController).state.tabsContent ?? {}).sort()).toEqual(["audio", "general"]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CONSTANTS as CONTROLS_CONST, PLUGIN_NAME as CONTROLS, SUBSCRIBE_EVENTS as CONTROLS_EVENTS } from "@vnejs/contracts.controls";
|
|
2
|
+
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.settings";
|
|
3
|
+
import { SourcesSet } from "@vnejs/sources-set";
|
|
4
|
+
import {
|
|
5
|
+
createTestModule as baseCreateTestModule,
|
|
6
|
+
registerCoreVne,
|
|
7
|
+
registerVnePlugin,
|
|
8
|
+
stubSilentEvent,
|
|
9
|
+
} from "@vnejs/test-utils";
|
|
10
|
+
|
|
11
|
+
export const registerSettingsViewPluginVne = () => {
|
|
12
|
+
registerCoreVne();
|
|
13
|
+
registerVnePlugin(CONTROLS, { constants: CONTROLS_CONST, events: CONTROLS_EVENTS });
|
|
14
|
+
registerVnePlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const createSettingsViewTestModule = <T extends Parameters<typeof baseCreateTestModule>[0]>(
|
|
18
|
+
ModuleClass: T,
|
|
19
|
+
options: Parameters<typeof baseCreateTestModule>[1] = {},
|
|
20
|
+
) => {
|
|
21
|
+
const { shared: sharedOption, state: stateOption, ...rest } = options;
|
|
22
|
+
|
|
23
|
+
const result = baseCreateTestModule(ModuleClass, {
|
|
24
|
+
...rest,
|
|
25
|
+
state: {
|
|
26
|
+
"settings.fields.controller": { isShow: false, fields: [], currentItem: null, currentItemInner: 0 },
|
|
27
|
+
...(stateOption ?? {}),
|
|
28
|
+
},
|
|
29
|
+
shared: {
|
|
30
|
+
...(sharedOption ?? {}),
|
|
31
|
+
settings: { ...(sharedOption?.settings ?? {}) },
|
|
32
|
+
viewForceAnimationSources: new SourcesSet(),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.SHOW);
|
|
37
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.HIDE);
|
|
38
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.UPDATE);
|
|
39
|
+
stubSilentEvent(result.observer, SUBSCRIBE_EVENTS.TAB_SET);
|
|
40
|
+
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export { SUBSCRIBE_EVENTS, PARAMS, PLUGIN_NAME };
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ModuleComponentsConstants, ModuleComponentsEvents, ModuleComponentsParams, ModuleComponentsSettings } from "@vnejs/module.components";
|
|
2
|
+
import type { Constants as ControlsConstants, PluginName as ControlsPluginName } from "@vnejs/contracts.controls";
|
|
3
|
+
import type { PluginName as SettingsPluginName, SubscribeEvents as SettingsSubscribeEvents } from "@vnejs/contracts.core.settings";
|
|
4
|
+
import type { SettingsKeys as TextsizeSettingsKeys, PluginName as TextsizePluginName } from "@vnejs/contracts.settings.textsize";
|
|
5
|
+
import type { Params as TextSmoothParams, PluginName as TextSmoothPluginName, SettingsKeys as TextSmoothSettingsKeys } from "@vnejs/contracts.text.smooth";
|
|
6
|
+
import type { Params, PluginName, SubscribeEvents } from "@vnejs/contracts.views.settings";
|
|
7
|
+
|
|
8
|
+
export type SettingsViewPluginEvents = ModuleComponentsEvents &
|
|
9
|
+
Record<PluginName, SubscribeEvents> &
|
|
10
|
+
Record<SettingsPluginName, SettingsSubscribeEvents>;
|
|
11
|
+
|
|
12
|
+
export type SettingsViewPluginConstants = ModuleComponentsConstants & Record<ControlsPluginName, ControlsConstants>;
|
|
13
|
+
|
|
14
|
+
export type SettingsViewPluginSettings = ModuleComponentsSettings &
|
|
15
|
+
Record<TextSmoothPluginName, TextSmoothSettingsKeys> &
|
|
16
|
+
Record<TextsizePluginName, TextsizeSettingsKeys>;
|
|
17
|
+
|
|
18
|
+
export type SettingsViewPluginParams = ModuleComponentsParams &
|
|
19
|
+
Record<PluginName, Params> &
|
|
20
|
+
Record<TextSmoothPluginName, TextSmoothParams>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SettingsField, SettingsTab } from "@vnejs/contracts.views.settings";
|
|
2
|
+
|
|
3
|
+
export type SettingsViewTabContent = {
|
|
4
|
+
fields?: SettingsField[];
|
|
5
|
+
showTextExample?: boolean;
|
|
6
|
+
locs?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type SettingsViewPluginState = {
|
|
10
|
+
isShow: boolean;
|
|
11
|
+
isForce: boolean;
|
|
12
|
+
tab?: SettingsTab;
|
|
13
|
+
tabKey?: string;
|
|
14
|
+
tabsContent?: Record<string, SettingsViewTabContent>;
|
|
15
|
+
fields?: SettingsField[];
|
|
16
|
+
locs?: Record<string, string>;
|
|
17
|
+
showTextExample?: boolean;
|
|
18
|
+
currentItem?: number | null;
|
|
19
|
+
currentItemInner?: number | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type SettingsViewTabPayload = {
|
|
23
|
+
tab?: SettingsTab;
|
|
24
|
+
tabs?: Record<string, SettingsTab>;
|
|
25
|
+
tabKey?: string;
|
|
26
|
+
locs?: Record<string, string>;
|
|
27
|
+
isForce?: boolean;
|
|
28
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { tokenizeText } from "@vnejs/helpers";
|
|
2
|
+
import type { ReactComponentProps } from "@vnejs/uis.react";
|
|
3
|
+
import { SmoothText, useEffect, useMemo, useState } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
6
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
7
|
+
|
|
8
|
+
type SettingsExampleTextProps = ReactComponentProps<
|
|
9
|
+
SettingsViewPluginEvents,
|
|
10
|
+
SettingsViewPluginConstants,
|
|
11
|
+
SettingsViewPluginSettings,
|
|
12
|
+
SettingsViewPluginParams,
|
|
13
|
+
SettingsViewPluginState
|
|
14
|
+
> & {
|
|
15
|
+
isShow?: boolean;
|
|
16
|
+
locs?: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type SmoothTextTokens = NonNullable<Parameters<typeof SmoothText>[0]["tokens"]>;
|
|
20
|
+
|
|
21
|
+
export const SettingsExampleText = ({ isShow = false, locs = {}, PARAMS, shared, SETTINGS }: SettingsExampleTextProps) => {
|
|
22
|
+
const [tokens, setTokens] = useState<SmoothTextTokens>([]);
|
|
23
|
+
|
|
24
|
+
const isForce = shared.textProcessSkipSources.isNotEmpty();
|
|
25
|
+
|
|
26
|
+
const text = useMemo(() => locs["fields.text.example"] ?? "", [locs]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (isShow) setTokens(tokenizeText(text) as SmoothTextTokens);
|
|
30
|
+
}, [isShow, text]);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setTokens((prev) => (prev.length ? [...prev] : prev));
|
|
34
|
+
}, [isForce]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (isShow || isForce) return;
|
|
38
|
+
|
|
39
|
+
const tm = setTimeout(() => setTokens([]), PARAMS.SETTINGS_VIEW.TRANSITION);
|
|
40
|
+
|
|
41
|
+
return () => clearTimeout(tm);
|
|
42
|
+
}, [isShow, isForce, PARAMS.SETTINGS_VIEW.TRANSITION]);
|
|
43
|
+
|
|
44
|
+
const charDelay = isForce
|
|
45
|
+
? 0
|
|
46
|
+
: PARAMS.TEXT_SMOOTH.TOKEN_RENDER_DELAY / Number(shared.settings[SETTINGS.TEXT_SMOOTH.TOKEN_RENDER_SPEED]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<SmoothText
|
|
50
|
+
tokens={tokens}
|
|
51
|
+
isForce={isForce}
|
|
52
|
+
charDelay={charDelay}
|
|
53
|
+
tokensVisibleForce={isForce ? tokens.length : null}
|
|
54
|
+
transition={PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.transition}
|
|
55
|
+
size={PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.size}
|
|
56
|
+
color={PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.color}
|
|
57
|
+
height={PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.height}
|
|
58
|
+
scrollBarTextSize={Number(shared.settings[SETTINGS.TEXTSIZE.VALUE])}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
2
|
+
import type { ReactComponentProps } from "@vnejs/uis.react";
|
|
3
|
+
import { Flex, PositionBox, Settings, useCallback } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
6
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
7
|
+
import { SettingsExampleText } from "./SettingsExampleText.js";
|
|
8
|
+
|
|
9
|
+
type SettingsTabContentProps = ReactComponentProps<
|
|
10
|
+
SettingsViewPluginEvents,
|
|
11
|
+
SettingsViewPluginConstants,
|
|
12
|
+
SettingsViewPluginSettings,
|
|
13
|
+
SettingsViewPluginParams,
|
|
14
|
+
SettingsViewPluginState
|
|
15
|
+
> & {
|
|
16
|
+
isShow?: boolean;
|
|
17
|
+
isForce?: boolean;
|
|
18
|
+
locs?: Record<string, string>;
|
|
19
|
+
fields?: SettingsField[];
|
|
20
|
+
showTextExample?: boolean;
|
|
21
|
+
currentItem?: number | null;
|
|
22
|
+
currentItemInner?: number | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type SettingsItem = NonNullable<Parameters<typeof Settings>[0]["settings"]>[number];
|
|
26
|
+
|
|
27
|
+
export const SettingsTabContent = ({
|
|
28
|
+
isShow = false,
|
|
29
|
+
isForce = false,
|
|
30
|
+
locs = {},
|
|
31
|
+
fields = [],
|
|
32
|
+
showTextExample = false,
|
|
33
|
+
currentItem = null,
|
|
34
|
+
currentItemInner = null,
|
|
35
|
+
PARAMS,
|
|
36
|
+
emit,
|
|
37
|
+
EVENTS,
|
|
38
|
+
shared,
|
|
39
|
+
SETTINGS,
|
|
40
|
+
store,
|
|
41
|
+
onMount,
|
|
42
|
+
CONST,
|
|
43
|
+
}: SettingsTabContentProps) => {
|
|
44
|
+
const mapFields = useCallback(
|
|
45
|
+
({
|
|
46
|
+
locKey = "",
|
|
47
|
+
type = "",
|
|
48
|
+
settingKey = "",
|
|
49
|
+
options = [],
|
|
50
|
+
titleToggledLocKey = "",
|
|
51
|
+
titleUntoggledLocKey = "",
|
|
52
|
+
valueMap = "",
|
|
53
|
+
...fieldProps
|
|
54
|
+
}: SettingsField = {}) => {
|
|
55
|
+
const onChange = (value: unknown) => void emit(EVENTS.SETTINGS.SET, { name: settingKey, value });
|
|
56
|
+
|
|
57
|
+
const titles = { title: locs[locKey], titleToggled: locs[titleToggledLocKey], titleUntoggled: locs[titleUntoggledLocKey] };
|
|
58
|
+
const valueMapReal = valueMap ? PARAMS.SETTINGS_VIEW.VALUE_MAPS[valueMap] : null;
|
|
59
|
+
|
|
60
|
+
const optionsReal = options.map((option) => {
|
|
61
|
+
const result: Record<string, unknown> = { ...option };
|
|
62
|
+
|
|
63
|
+
if (option.locKey) {
|
|
64
|
+
delete result.locKey;
|
|
65
|
+
result.text = locs[option.locKey];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (option.action) {
|
|
69
|
+
delete result.action;
|
|
70
|
+
result.onClick = () => PARAMS.SETTINGS_VIEW.ACTIONS[option.action!]({ emit, emitOne: emit, emitReal: emit }, locs);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return { ...titles, value: shared.settings[settingKey], type, onChange, props: fieldProps, options: optionsReal, valueMap: valueMapReal };
|
|
77
|
+
},
|
|
78
|
+
[EVENTS.SETTINGS.SET, PARAMS.SETTINGS_VIEW.ACTIONS, PARAMS.SETTINGS_VIEW.VALUE_MAPS, emit, locs, shared.settings],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Do not memoize mapped settings: shared.settings is mutated in place on SETTINGS.CHANGED.
|
|
82
|
+
const propsSettings = {
|
|
83
|
+
...PARAMS.SETTINGS_VIEW.VIEW_PROPS.fieldProps,
|
|
84
|
+
transition: isForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION,
|
|
85
|
+
settings: fields.map(mapFields) as SettingsItem[],
|
|
86
|
+
selectedIndex: isShow ? (currentItem ?? undefined) : -1,
|
|
87
|
+
selectedIndexInner: isShow ? (currentItemInner ?? undefined) : -1,
|
|
88
|
+
} as Parameters<typeof Settings>[0];
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<PositionBox
|
|
92
|
+
isCentered={true}
|
|
93
|
+
opacity={isShow ? 1 : 0}
|
|
94
|
+
transition={isForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION}
|
|
95
|
+
>
|
|
96
|
+
<Flex
|
|
97
|
+
direction={Flex.DIRECTION.COLUMN}
|
|
98
|
+
gap={PARAMS.SETTINGS_VIEW.VIEW_PROPS.fieldsGap}
|
|
99
|
+
>
|
|
100
|
+
<Settings {...propsSettings} />
|
|
101
|
+
{showTextExample && (
|
|
102
|
+
<SettingsExampleText
|
|
103
|
+
store={store}
|
|
104
|
+
onMount={onMount}
|
|
105
|
+
PARAMS={PARAMS}
|
|
106
|
+
emit={emit}
|
|
107
|
+
EVENTS={EVENTS}
|
|
108
|
+
shared={shared}
|
|
109
|
+
CONST={CONST}
|
|
110
|
+
SETTINGS={SETTINGS}
|
|
111
|
+
locs={locs}
|
|
112
|
+
isShow={isShow}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</Flex>
|
|
116
|
+
</PositionBox>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ViewRenderFunc } from "@vnejs/module.components";
|
|
2
|
+
import type { ReactComponentProps } from "@vnejs/uis.react";
|
|
3
|
+
import { View, createRenderFunc, useIsForceHook, useMemo, useStoreState } from "@vnejs/uis.react";
|
|
4
|
+
|
|
5
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
6
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
7
|
+
import { SettingsTabContent } from "./SettingsTabContent.js";
|
|
8
|
+
|
|
9
|
+
type SettingsComponentProps = ReactComponentProps<
|
|
10
|
+
SettingsViewPluginEvents,
|
|
11
|
+
SettingsViewPluginConstants,
|
|
12
|
+
SettingsViewPluginSettings,
|
|
13
|
+
SettingsViewPluginParams,
|
|
14
|
+
SettingsViewPluginState
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS }: SettingsComponentProps) => {
|
|
18
|
+
const {
|
|
19
|
+
isShow = false,
|
|
20
|
+
isForce = false,
|
|
21
|
+
locs = {},
|
|
22
|
+
tabsContent = {},
|
|
23
|
+
tabKey = "",
|
|
24
|
+
currentItem = null,
|
|
25
|
+
currentItemInner = null,
|
|
26
|
+
} = useStoreState<SettingsViewPluginState>(store, onMount);
|
|
27
|
+
|
|
28
|
+
const isRealForce = useIsForceHook(isForce);
|
|
29
|
+
|
|
30
|
+
const propsByView = PARAMS.SETTINGS_VIEW.VIEW_PROPS;
|
|
31
|
+
|
|
32
|
+
const propsView = useMemo(
|
|
33
|
+
() => ({
|
|
34
|
+
...propsByView.view,
|
|
35
|
+
isShow,
|
|
36
|
+
isForce: isRealForce,
|
|
37
|
+
transition: isRealForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION,
|
|
38
|
+
}),
|
|
39
|
+
[isShow, isRealForce, propsByView.view, PARAMS.SETTINGS_VIEW.TRANSITION],
|
|
40
|
+
);
|
|
41
|
+
|
|
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, currentItem, currentItemInner, locs],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View {...propsView}>
|
|
49
|
+
{Object.entries(tabsContent).map(([key, content]) => (
|
|
50
|
+
<SettingsTabContent
|
|
51
|
+
{...propsTab}
|
|
52
|
+
key={key}
|
|
53
|
+
isShow={tabKey === key}
|
|
54
|
+
fields={content.fields}
|
|
55
|
+
showTextExample={content.showTextExample}
|
|
56
|
+
locs={content.locs ?? locs}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const render: ViewRenderFunc<SettingsViewPluginState> = createRenderFunc(SettingsViewComponent);
|
package/tsconfig.json
ADDED
package/const/events.js
DELETED
package/const/params.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export const TRANSITION = 500;
|
|
2
|
-
export const ZINDEX = 4000;
|
|
3
|
-
export const LOC_LABEL = "settings";
|
|
4
|
-
|
|
5
|
-
export const BACKGROUND = "Menu/save";
|
|
6
|
-
|
|
7
|
-
export const TABS = {};
|
|
8
|
-
|
|
9
|
-
export const VALUE_MAPS = {};
|
|
10
|
-
|
|
11
|
-
export const VIEW_PROPS = {
|
|
12
|
-
screen: { withBackgroundDark: true, withBackgroundBlur: false, transition: TRANSITION, zIndex: ZINDEX },
|
|
13
|
-
|
|
14
|
-
background: { withDark: true, withBlur: false },
|
|
15
|
-
|
|
16
|
-
cross: { position: { bottom: 90, right: 120 }, svg: { size: 60 } },
|
|
17
|
-
|
|
18
|
-
exampleText: { size: 60, height: 300, color: "var(--vne-color-active)", transition: 300 },
|
|
19
|
-
|
|
20
|
-
fieldsGap: 48,
|
|
21
|
-
|
|
22
|
-
fieldProps: {
|
|
23
|
-
widthTitle: 1440,
|
|
24
|
-
width: 3120,
|
|
25
|
-
|
|
26
|
-
sizeTitle: 96,
|
|
27
|
-
sizeText: 72,
|
|
28
|
-
|
|
29
|
-
gapBetweenItems: 48,
|
|
30
|
-
gapBetweenTitleAndContent: 120,
|
|
31
|
-
gapBetweenInnerItems: 120,
|
|
32
|
-
|
|
33
|
-
rangeHeight: 24,
|
|
34
|
-
|
|
35
|
-
widthInnerLeft: 960,
|
|
36
|
-
widthInnerRight: 240,
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
controls: {
|
|
40
|
-
position: { top: 90, left: 120 },
|
|
41
|
-
props: { textWidth: 660, wrapWidth: 3600, flexGap: 36, flexWithWrap: false, textSize: 96, textAlign: "center" },
|
|
42
|
-
},
|
|
43
|
-
};
|
package/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { regPlugin } from "@vnejs/shared";
|
|
2
|
-
|
|
3
|
-
import { SUBSCRIBE_EVENTS } from "./const/events";
|
|
4
|
-
import * as params from "./const/params";
|
|
5
|
-
|
|
6
|
-
import { SettingsController } from "./modules/controller";
|
|
7
|
-
import { SettingsView } from "./modules/view";
|
|
8
|
-
|
|
9
|
-
regPlugin("SETTINGS_VIEW", { events: SUBSCRIBE_EVENTS, params }, [SettingsController, SettingsView]);
|
package/modules/controller.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
-
|
|
3
|
-
const filterTabsToShow = ({ showInSettings = false }) => Boolean(showInSettings);
|
|
4
|
-
|
|
5
|
-
export class SettingsController extends ModuleController {
|
|
6
|
-
name = "settings.controller";
|
|
7
|
-
|
|
8
|
-
emitHide = () => this.emit(this.EVENTS.SETTINGS_VIEW.HIDE);
|
|
9
|
-
|
|
10
|
-
onArrowLeft = () => {
|
|
11
|
-
const { currentTab, currentItemInner, currentItem } = this.state;
|
|
12
|
-
|
|
13
|
-
if (currentItem === null) return;
|
|
14
|
-
|
|
15
|
-
const { settingKey = "", step = 0, min = 0, type = "", options = [] } = this.tabs[currentTab].fields[currentItem];
|
|
16
|
-
|
|
17
|
-
if (options?.length) {
|
|
18
|
-
const newValue = (currentItemInner === 0 ? options.length : currentItemInner) - 1;
|
|
19
|
-
|
|
20
|
-
return this.updateStateAndViewFast({ currentItemInner: newValue });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (type === "toggle") {
|
|
24
|
-
return this.shared.settings[settingKey] === true && this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: false });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (type === "range") {
|
|
28
|
-
const newValue = Number(Number(this.shared.settings[settingKey] - step).toFixed(4));
|
|
29
|
-
|
|
30
|
-
return newValue >= min && this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
onArrowRight = () => {
|
|
35
|
-
const { currentTab, currentItemInner, currentItem } = this.state;
|
|
36
|
-
|
|
37
|
-
if (currentItem === null) return;
|
|
38
|
-
|
|
39
|
-
const { settingKey = "", step = 0, max = 0, type = "", options = [] } = this.tabs[currentTab].fields[currentItem];
|
|
40
|
-
|
|
41
|
-
if (options?.length) {
|
|
42
|
-
return this.updateStateAndViewFast({ currentItemInner: (currentItemInner + 1) % options.length });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (type === "toggle") {
|
|
46
|
-
return this.shared.settings[settingKey] === false && this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: true });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (type === "range") {
|
|
50
|
-
const newValue = Number(Number(this.shared.settings[settingKey] + step).toFixed(4));
|
|
51
|
-
|
|
52
|
-
return newValue <= max && this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
onPrevTab = () => this.emit(this.EVENTS.SETTINGS_VIEW.TAB_SET, { tab: (this.state.currentTab === 0 ? this.tabs.length : this.state.currentTab) - 1 });
|
|
57
|
-
onNextTab = () => this.emit(this.EVENTS.SETTINGS_VIEW.TAB_SET, { tab: (this.state.currentTab + 1) % this.tabs.length });
|
|
58
|
-
|
|
59
|
-
onInteract = () => {
|
|
60
|
-
const { currentTab, currentItemInner, currentItem } = this.state;
|
|
61
|
-
|
|
62
|
-
if (currentItem === null) return;
|
|
63
|
-
|
|
64
|
-
const { settingKey = "", type = "", options = [] } = this.tabs[currentTab].fields[currentItem];
|
|
65
|
-
|
|
66
|
-
if (options?.length)
|
|
67
|
-
return options[currentItemInner].action
|
|
68
|
-
? this.PARAMS.SETTINGS_VIEW.ACTIONS[options[currentItemInner].action](this, this.state.locs)
|
|
69
|
-
: this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: options[currentItemInner].value });
|
|
70
|
-
|
|
71
|
-
if (type === "toggle") return this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: !this.shared.settings[settingKey] });
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
bgName = this.PARAMS.SETTINGS_VIEW.BACKGROUND;
|
|
75
|
-
|
|
76
|
-
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
77
|
-
controls = {
|
|
78
|
-
[this.CONST.CONTROLS.BUTTONS.BACK]: this.emitHide,
|
|
79
|
-
[this.CONST.CONTROLS.BUTTONS.MENU]: this.emitHide,
|
|
80
|
-
[this.CONST.CONTROLS.BUTTONS.GAMEMENU]: this.emitHide,
|
|
81
|
-
[this.CONST.CONTROLS.BUTTONS.INTERACT]: this.onInteract,
|
|
82
|
-
[this.CONST.CONTROLS.BUTTONS.ACCEPT]: this.onInteract,
|
|
83
|
-
[this.CONST.CONTROLS.BUTTONS.TAB_PREV]: this.onPrevTab,
|
|
84
|
-
[this.CONST.CONTROLS.BUTTONS.TAB_NEXT]: this.onNextTab,
|
|
85
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_LEFT]: this.onArrowLeft,
|
|
86
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_RIGHT]: this.onArrowRight,
|
|
87
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_UP]: this.decCurrentItem,
|
|
88
|
-
[this.CONST.CONTROLS.BUTTONS.ARROW_BOTTOM]: this.incCurrentItem,
|
|
89
|
-
};
|
|
90
|
-
controlsIndex = this.PARAMS.SETTINGS_VIEW.ZINDEX;
|
|
91
|
-
|
|
92
|
-
subscribe = () => {
|
|
93
|
-
this.on(this.EVENTS.SETTINGS_VIEW.SHOW, this.onShow);
|
|
94
|
-
this.on(this.EVENTS.SETTINGS_VIEW.HIDE, this.onHide);
|
|
95
|
-
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
96
|
-
this.on(this.EVENTS.SETTINGS_VIEW.PRELOAD, this.loadBgMedia);
|
|
97
|
-
|
|
98
|
-
this.on(this.EVENTS.SETTINGS.CHANGED, this.onSettingChange);
|
|
99
|
-
|
|
100
|
-
this.on(this.EVENTS.LOCS.CHANGED, this.updateViewFast);
|
|
101
|
-
|
|
102
|
-
this.on(this.EVENTS.STATE.SET, this.onHide);
|
|
103
|
-
this.on(this.EVENTS.STATE.CLEAR, this.onHide);
|
|
104
|
-
|
|
105
|
-
this.on(this.EVENTS.MEMORY.CLEARED, this.onMemoryCleared);
|
|
106
|
-
};
|
|
107
|
-
init = () => {
|
|
108
|
-
this.tabs = Object.values(this.PARAMS.SETTINGS_VIEW.TABS).filter(filterTabsToShow);
|
|
109
|
-
|
|
110
|
-
this.setMaxCurrentItemByTabIndex(0);
|
|
111
|
-
|
|
112
|
-
return this.emit(this.EVENTS.SETTINGS.INIT);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
beforeShow = async () => this.updateState({ bgSrc: (await this.loadBgMedia()).src, tabs: this.tabs, currentTab: 0 });
|
|
116
|
-
afterHide = () => this.setDefaultState();
|
|
117
|
-
|
|
118
|
-
onSettingChange = () => this.isReady && this.state.isShow && this.updateViewFast();
|
|
119
|
-
|
|
120
|
-
onTabSet = ({ tab = 0 } = {}) => {
|
|
121
|
-
this.setMaxCurrentItemByTabIndex(tab);
|
|
122
|
-
this.unsetCurrentItem();
|
|
123
|
-
|
|
124
|
-
return this.updateStateAndViewFast({ currentTab: tab, currentItemInner: 0 });
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
onMemoryCleared = async () => this.updateStateAndViewFast({ bgSrc: (await this.loadBgMedia()).src });
|
|
128
|
-
|
|
129
|
-
setMaxCurrentItemByTabIndex = (i) => {
|
|
130
|
-
this.maxCurrentItem = this.tabs[i].fields.length - 1;
|
|
131
|
-
};
|
|
132
|
-
}
|
package/modules/view.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
-
|
|
3
|
-
import { render } from "../view";
|
|
4
|
-
|
|
5
|
-
export class SettingsView extends ModuleView {
|
|
6
|
-
name = "settings.view";
|
|
7
|
-
|
|
8
|
-
locLabel = this.PARAMS.SETTINGS_VIEW.LOC_LABEL;
|
|
9
|
-
animationTime = this.PARAMS.SETTINGS_VIEW.TRANSITION;
|
|
10
|
-
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
11
|
-
|
|
12
|
-
renderFunc = render;
|
|
13
|
-
updateHandler = this.onUpdateStoreComponent;
|
|
14
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
-
|
|
3
|
-
import { PositionBox, TextControls } from "@vnejs/uis.react";
|
|
4
|
-
|
|
5
|
-
export const SettingsControls = ({ tabs = [], locs = {}, currentTab = 0, ...props } = {}) => {
|
|
6
|
-
const [texts, setTexts] = useState();
|
|
7
|
-
|
|
8
|
-
useEffect(() => void setTexts(Object.values(tabs).map(({ titleLocKey }, i) => ({ value: i, text: locs[titleLocKey] }))), [locs, tabs]);
|
|
9
|
-
|
|
10
|
-
const onClick = useCallback((tab) => props.emit(props.EVENTS.SETTINGS_VIEW.TAB_SET, { tab }), []);
|
|
11
|
-
|
|
12
|
-
const propsControls = useMemo(
|
|
13
|
-
() => ({ ...props.PARAMS.SETTINGS_VIEW.VIEW_PROPS.controls.props, selectedIndex: currentTab, texts, onClick }),
|
|
14
|
-
[currentTab, texts],
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<PositionBox {...props.PARAMS.SETTINGS_VIEW.VIEW_PROPS.controls.position}>
|
|
19
|
-
<TextControls {...propsControls} />
|
|
20
|
-
</PositionBox>
|
|
21
|
-
);
|
|
22
|
-
};
|