@vnejs/plugins.views.settings 0.1.3 → 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.
Files changed (39) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +6 -0
  3. package/dist/modules/controller.d.ts +38 -0
  4. package/dist/modules/controller.js +117 -0
  5. package/dist/modules/view.d.ts +11 -0
  6. package/dist/modules/view.js +10 -0
  7. package/dist/types.d.ts +10 -0
  8. package/dist/types.js +1 -0
  9. package/dist/utils/settings.d.ts +25 -0
  10. package/dist/utils/settings.js +1 -0
  11. package/dist/view/SettingsExampleText.d.ts +9 -0
  12. package/dist/view/SettingsExampleText.js +25 -0
  13. package/dist/view/SettingsTabContent.d.ts +15 -0
  14. package/dist/view/SettingsTabContent.js +32 -0
  15. package/dist/view/index.d.ts +3 -0
  16. package/dist/view/index.js +18 -0
  17. package/package.json +52 -7
  18. package/src/index.ts +9 -0
  19. package/src/modules/controller.ts +161 -0
  20. package/src/modules/view.ts +22 -0
  21. package/src/tests/controller.test.ts +93 -0
  22. package/src/tests/setup.ts +44 -0
  23. package/src/types.ts +20 -0
  24. package/src/utils/settings.ts +28 -0
  25. package/src/view/SettingsExampleText.tsx +61 -0
  26. package/src/view/SettingsTabContent.tsx +118 -0
  27. package/src/view/index.tsx +63 -0
  28. package/tsconfig.json +10 -0
  29. package/const/events.js +0 -7
  30. package/const/params.js +0 -43
  31. package/index.js +0 -9
  32. package/modules/controller.js +0 -132
  33. package/modules/view.js +0 -14
  34. package/view/components/SettingsControls.jsx +0 -22
  35. package/view/components/SettingsCross.jsx +0 -13
  36. package/view/components/SettingsExampleText.jsx +0 -40
  37. package/view/components/SettingsTabContent.jsx +0 -66
  38. package/view/components/index.js +0 -4
  39. 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
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@vnejs/configs.ts-common/tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist",
6
+ "jsx": "react-jsx"
7
+ },
8
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
9
+ "exclude": ["dist", "node_modules", "src/tests"]
10
+ }
package/const/events.js DELETED
@@ -1,7 +0,0 @@
1
- export const SUBSCRIBE_EVENTS = {
2
- SHOW: "vne:settings_view:show",
3
- HIDE: "vne:settings_view:hide",
4
- UPDATE: "vne:settings_view:update",
5
- PRELOAD: "vne:settings_view:preload",
6
- TAB_SET: "vne:settings_view:tab_set",
7
- };
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 = "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]);
@@ -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
- };