@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.
- 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
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "@vnejs/contracts.views.settings";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "@vnejs/contracts.views.settings";
|
|
2
|
+
import { regPlugin } from "@vnejs/shared";
|
|
3
|
+
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.settings";
|
|
4
|
+
import { SettingsViewController } from "./modules/controller.js";
|
|
5
|
+
import { SettingsView } from "./modules/view.js";
|
|
6
|
+
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsView]);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
3
|
+
import type { SettingsViewPluginState, SettingsViewTabContent, SettingsViewTabPayload } from "../utils/settings.js";
|
|
4
|
+
export declare class SettingsViewController extends ModuleController<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams, SettingsViewPluginState> {
|
|
5
|
+
name: string;
|
|
6
|
+
updateEvent: "vne:settings_view:update";
|
|
7
|
+
controlsIndex: number;
|
|
8
|
+
tabKeyOf: ({ tabKey, tab }?: SettingsViewTabPayload) => string;
|
|
9
|
+
tabContentOf: (tab: SettingsViewTabPayload["tab"], locs?: Record<string, string>) => SettingsViewTabContent;
|
|
10
|
+
applyTab: (payload?: SettingsViewTabPayload) => {
|
|
11
|
+
tab: import("@vnejs/contracts.views.settings").SettingsTab | undefined;
|
|
12
|
+
tabKey: string;
|
|
13
|
+
tabsContent: {
|
|
14
|
+
[k: string]: SettingsViewTabContent;
|
|
15
|
+
};
|
|
16
|
+
fields: import("@vnejs/contracts.views.settings").SettingsField[];
|
|
17
|
+
locs: Record<string, string> | undefined;
|
|
18
|
+
showTextExample: boolean;
|
|
19
|
+
currentItemInner: number;
|
|
20
|
+
};
|
|
21
|
+
onShowFields: (payload?: SettingsViewTabPayload) => Promise<void>;
|
|
22
|
+
onTabSet: (payload?: SettingsViewTabPayload) => Promise<void | unknown[]>;
|
|
23
|
+
onArrowLeft: () => false | Promise<unknown[]> | undefined;
|
|
24
|
+
onArrowRight: () => false | Promise<unknown[]> | undefined;
|
|
25
|
+
onInteract: () => void;
|
|
26
|
+
controls: {
|
|
27
|
+
abstract_interact: () => undefined;
|
|
28
|
+
abstract_arrow_left: () => undefined;
|
|
29
|
+
abstract_arrow_right: () => undefined;
|
|
30
|
+
abstract_arrow_up: () => Promise<void>;
|
|
31
|
+
abstract_arrow_bottom: () => Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
controlsCheckNext: boolean;
|
|
34
|
+
subscribe: () => void;
|
|
35
|
+
beforeShow: (payload?: SettingsViewTabPayload) => Promise<void>;
|
|
36
|
+
afterHide: () => void;
|
|
37
|
+
onSettingChange: () => false | Promise<unknown[]> | undefined;
|
|
38
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
export class SettingsViewController extends ModuleController {
|
|
3
|
+
name = "settings.fields.controller";
|
|
4
|
+
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
5
|
+
controlsIndex = this.PARAMS.SETTINGS_VIEW.ZINDEX;
|
|
6
|
+
tabKeyOf = ({ tabKey, tab } = {}) => tabKey || tab?.titleLocKey || "default";
|
|
7
|
+
tabContentOf = (tab, locs) => ({
|
|
8
|
+
fields: tab?.fields ?? [],
|
|
9
|
+
showTextExample: Boolean(tab?.showTextExample),
|
|
10
|
+
locs,
|
|
11
|
+
});
|
|
12
|
+
applyTab = (payload = {}) => {
|
|
13
|
+
const { tab, locs } = payload;
|
|
14
|
+
const fields = tab?.fields ?? [];
|
|
15
|
+
const key = this.tabKeyOf(payload);
|
|
16
|
+
this.maxCurrentItem = Math.max((fields.length ?? 1) - 1, 0);
|
|
17
|
+
const tabsContent = payload.tabs
|
|
18
|
+
? Object.fromEntries(Object.entries(payload.tabs).map(([tabKey, settingsTab]) => [tabKey, this.tabContentOf(settingsTab, locs)]))
|
|
19
|
+
: { ...(this.state.tabsContent ?? {}), [key]: this.tabContentOf(tab, locs) };
|
|
20
|
+
return {
|
|
21
|
+
tab,
|
|
22
|
+
tabKey: key,
|
|
23
|
+
tabsContent,
|
|
24
|
+
fields,
|
|
25
|
+
locs,
|
|
26
|
+
showTextExample: Boolean(tab?.showTextExample),
|
|
27
|
+
currentItemInner: 0,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
onShowFields = (payload = {}) => this.onShow(payload);
|
|
31
|
+
onTabSet = async (payload = {}) => {
|
|
32
|
+
if (!this.state.isShow)
|
|
33
|
+
return this.onShow(payload);
|
|
34
|
+
const isForce = Boolean(payload.isForce || this.shared.viewForceAnimationSources?.isNotEmpty());
|
|
35
|
+
this.currentItem = null;
|
|
36
|
+
const next = { ...this.applyTab(payload), currentItem: null };
|
|
37
|
+
if (isForce)
|
|
38
|
+
return this.updateStateAndViewForce(next);
|
|
39
|
+
const key = next.tabKey;
|
|
40
|
+
const alreadyMounted = Boolean(this.state.tabsContent?.[key]);
|
|
41
|
+
// Mount the incoming tab at opacity 0 first so CSS can crossfade both layers together.
|
|
42
|
+
if (!alreadyMounted) {
|
|
43
|
+
await this.updateStateAndViewFast({ tabsContent: next.tabsContent });
|
|
44
|
+
await this.waitRerender();
|
|
45
|
+
}
|
|
46
|
+
return this.updateStateAndView(next);
|
|
47
|
+
};
|
|
48
|
+
onArrowLeft = () => {
|
|
49
|
+
const { currentItemInner = 0, currentItem, fields = [] } = this.state;
|
|
50
|
+
if (currentItem == null || !fields.length)
|
|
51
|
+
return;
|
|
52
|
+
const { settingKey = "", step = 0, min = 0, type = "", options = [] } = fields[currentItem];
|
|
53
|
+
if (!settingKey)
|
|
54
|
+
return;
|
|
55
|
+
if (options?.length) {
|
|
56
|
+
const newValue = (currentItemInner === 0 ? options.length : (currentItemInner ?? 0)) - 1;
|
|
57
|
+
return this.updateStateAndViewFast({ currentItemInner: newValue });
|
|
58
|
+
}
|
|
59
|
+
if (type === "toggle") {
|
|
60
|
+
return this.shared.settings[settingKey] === true && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: false });
|
|
61
|
+
}
|
|
62
|
+
if (type === "range") {
|
|
63
|
+
const newValue = Number(Number(Number(this.shared.settings[settingKey]) - step).toFixed(4));
|
|
64
|
+
return newValue >= min && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
onArrowRight = () => {
|
|
68
|
+
const { currentItemInner = 0, currentItem, fields = [] } = this.state;
|
|
69
|
+
if (currentItem == null || !fields.length)
|
|
70
|
+
return;
|
|
71
|
+
const { settingKey = "", step = 0, max = 0, type = "", options = [] } = fields[currentItem];
|
|
72
|
+
if (!settingKey)
|
|
73
|
+
return;
|
|
74
|
+
if (options?.length) {
|
|
75
|
+
return this.updateStateAndViewFast({ currentItemInner: ((currentItemInner ?? 0) + 1) % options.length });
|
|
76
|
+
}
|
|
77
|
+
if (type === "toggle") {
|
|
78
|
+
return this.shared.settings[settingKey] === false && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: true });
|
|
79
|
+
}
|
|
80
|
+
if (type === "range") {
|
|
81
|
+
const newValue = Number(Number(Number(this.shared.settings[settingKey]) + step).toFixed(4));
|
|
82
|
+
return newValue <= max && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
onInteract = () => {
|
|
86
|
+
const { currentItemInner = 0, currentItem, fields = [], locs = {} } = this.state;
|
|
87
|
+
if (currentItem == null || !fields.length)
|
|
88
|
+
return;
|
|
89
|
+
const { settingKey = "", type = "", options = [] } = fields[currentItem];
|
|
90
|
+
if (!settingKey)
|
|
91
|
+
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
|
+
if (type === "toggle")
|
|
97
|
+
return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: !this.shared.settings[settingKey] });
|
|
98
|
+
};
|
|
99
|
+
controls = {
|
|
100
|
+
[this.CONST.CONTROLS.BUTTONS.INTERACT]: () => void this.onInteract(),
|
|
101
|
+
[this.CONST.CONTROLS.BUTTONS.ARROW_LEFT]: () => void this.onArrowLeft(),
|
|
102
|
+
[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
|
+
};
|
|
106
|
+
controlsCheckNext = true;
|
|
107
|
+
subscribe = () => {
|
|
108
|
+
this.on(this.EVENTS.SETTINGS_VIEW.SHOW, this.onShowFields);
|
|
109
|
+
this.on(this.EVENTS.SETTINGS_VIEW.HIDE, this.onHide);
|
|
110
|
+
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
111
|
+
this.on(this.EVENTS.SETTINGS.CHANGED, this.onSettingChange);
|
|
112
|
+
this.on(this.EVENTS.COMPONENTS.EXTERNAL_UPDATE, this.onSettingChange);
|
|
113
|
+
};
|
|
114
|
+
beforeShow = async (payload = {}) => this.updateState(this.applyTab(payload));
|
|
115
|
+
afterHide = () => this.setDefaultState();
|
|
116
|
+
onSettingChange = () => this.state.isShow && this.updateViewFast();
|
|
117
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
3
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
4
|
+
export declare class SettingsView extends ModuleView<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams, SettingsViewPluginState> {
|
|
5
|
+
name: string;
|
|
6
|
+
locLabel: string;
|
|
7
|
+
animationTime: number;
|
|
8
|
+
updateEvent: "vne:settings_view:update";
|
|
9
|
+
renderFunc: import("@vnejs/module.components").ViewRenderFunc<SettingsViewPluginState>;
|
|
10
|
+
updateHandler: (state?: SettingsViewPluginState | undefined) => Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
+
import { render } from "../view/index.js";
|
|
3
|
+
export class SettingsView extends ModuleView {
|
|
4
|
+
name = "settings.fields.view";
|
|
5
|
+
locLabel = this.PARAMS.SETTINGS_VIEW.LOC_LABEL;
|
|
6
|
+
animationTime = this.PARAMS.SETTINGS_VIEW.TRANSITION;
|
|
7
|
+
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
8
|
+
renderFunc = render;
|
|
9
|
+
updateHandler = this.onUpdateStoreComponent;
|
|
10
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export type SettingsViewPluginEvents = ModuleComponentsEvents & Record<PluginName, SubscribeEvents> & Record<SettingsPluginName, SettingsSubscribeEvents>;
|
|
8
|
+
export type SettingsViewPluginConstants = ModuleComponentsConstants & Record<ControlsPluginName, ControlsConstants>;
|
|
9
|
+
export type SettingsViewPluginSettings = ModuleComponentsSettings & Record<TextSmoothPluginName, TextSmoothSettingsKeys> & Record<TextsizePluginName, TextsizeSettingsKeys>;
|
|
10
|
+
export type SettingsViewPluginParams = ModuleComponentsParams & Record<PluginName, Params> & Record<TextSmoothPluginName, TextSmoothParams>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SettingsField, SettingsTab } from "@vnejs/contracts.views.settings";
|
|
2
|
+
export type SettingsViewTabContent = {
|
|
3
|
+
fields?: SettingsField[];
|
|
4
|
+
showTextExample?: boolean;
|
|
5
|
+
locs?: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
export type SettingsViewPluginState = {
|
|
8
|
+
isShow: boolean;
|
|
9
|
+
isForce: boolean;
|
|
10
|
+
tab?: SettingsTab;
|
|
11
|
+
tabKey?: string;
|
|
12
|
+
tabsContent?: Record<string, SettingsViewTabContent>;
|
|
13
|
+
fields?: SettingsField[];
|
|
14
|
+
locs?: Record<string, string>;
|
|
15
|
+
showTextExample?: boolean;
|
|
16
|
+
currentItem?: number | null;
|
|
17
|
+
currentItemInner?: number | null;
|
|
18
|
+
};
|
|
19
|
+
export type SettingsViewTabPayload = {
|
|
20
|
+
tab?: SettingsTab;
|
|
21
|
+
tabs?: Record<string, SettingsTab>;
|
|
22
|
+
tabKey?: string;
|
|
23
|
+
locs?: Record<string, string>;
|
|
24
|
+
isForce?: boolean;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactComponentProps } from "@vnejs/uis.react";
|
|
2
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
3
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
4
|
+
type SettingsExampleTextProps = ReactComponentProps<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams, SettingsViewPluginState> & {
|
|
5
|
+
isShow?: boolean;
|
|
6
|
+
locs?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
export declare const SettingsExampleText: ({ isShow, locs, PARAMS, shared, SETTINGS }: SettingsExampleTextProps) => import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { tokenizeText } from "@vnejs/helpers";
|
|
3
|
+
import { SmoothText, useEffect, useMemo, useState } from "@vnejs/uis.react";
|
|
4
|
+
export const SettingsExampleText = ({ isShow = false, locs = {}, PARAMS, shared, SETTINGS }) => {
|
|
5
|
+
const [tokens, setTokens] = useState([]);
|
|
6
|
+
const isForce = shared.textProcessSkipSources.isNotEmpty();
|
|
7
|
+
const text = useMemo(() => locs["fields.text.example"] ?? "", [locs]);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (isShow)
|
|
10
|
+
setTokens(tokenizeText(text));
|
|
11
|
+
}, [isShow, text]);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setTokens((prev) => (prev.length ? [...prev] : prev));
|
|
14
|
+
}, [isForce]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (isShow || isForce)
|
|
17
|
+
return;
|
|
18
|
+
const tm = setTimeout(() => setTokens([]), PARAMS.SETTINGS_VIEW.TRANSITION);
|
|
19
|
+
return () => clearTimeout(tm);
|
|
20
|
+
}, [isShow, isForce, PARAMS.SETTINGS_VIEW.TRANSITION]);
|
|
21
|
+
const charDelay = isForce
|
|
22
|
+
? 0
|
|
23
|
+
: PARAMS.TEXT_SMOOTH.TOKEN_RENDER_DELAY / Number(shared.settings[SETTINGS.TEXT_SMOOTH.TOKEN_RENDER_SPEED]);
|
|
24
|
+
return (_jsx(SmoothText, { tokens: tokens, isForce: isForce, charDelay: charDelay, tokensVisibleForce: isForce ? tokens.length : null, transition: PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.transition, size: PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.size, color: PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.color, height: PARAMS.SETTINGS_VIEW.VIEW_PROPS.exampleText.height, scrollBarTextSize: Number(shared.settings[SETTINGS.TEXTSIZE.VALUE]) }));
|
|
25
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SettingsField } from "@vnejs/contracts.views.settings";
|
|
2
|
+
import type { ReactComponentProps } from "@vnejs/uis.react";
|
|
3
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
4
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
5
|
+
type SettingsTabContentProps = ReactComponentProps<SettingsViewPluginEvents, SettingsViewPluginConstants, SettingsViewPluginSettings, SettingsViewPluginParams, SettingsViewPluginState> & {
|
|
6
|
+
isShow?: boolean;
|
|
7
|
+
isForce?: boolean;
|
|
8
|
+
locs?: Record<string, string>;
|
|
9
|
+
fields?: SettingsField[];
|
|
10
|
+
showTextExample?: boolean;
|
|
11
|
+
currentItem?: number | null;
|
|
12
|
+
currentItemInner?: number | null;
|
|
13
|
+
};
|
|
14
|
+
export declare const SettingsTabContent: ({ isShow, isForce, locs, fields, showTextExample, currentItem, currentItemInner, PARAMS, emit, EVENTS, shared, SETTINGS, store, onMount, CONST, }: SettingsTabContentProps) => import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Flex, PositionBox, Settings, useCallback } from "@vnejs/uis.react";
|
|
3
|
+
import { SettingsExampleText } from "./SettingsExampleText.js";
|
|
4
|
+
export const SettingsTabContent = ({ isShow = false, isForce = false, locs = {}, fields = [], showTextExample = false, currentItem = null, currentItemInner = null, PARAMS, emit, EVENTS, shared, SETTINGS, store, onMount, CONST, }) => {
|
|
5
|
+
const mapFields = useCallback(({ locKey = "", type = "", settingKey = "", options = [], titleToggledLocKey = "", titleUntoggledLocKey = "", valueMap = "", ...fieldProps } = {}) => {
|
|
6
|
+
const onChange = (value) => void emit(EVENTS.SETTINGS.SET, { name: settingKey, value });
|
|
7
|
+
const titles = { title: locs[locKey], titleToggled: locs[titleToggledLocKey], titleUntoggled: locs[titleUntoggledLocKey] };
|
|
8
|
+
const valueMapReal = valueMap ? PARAMS.SETTINGS_VIEW.VALUE_MAPS[valueMap] : null;
|
|
9
|
+
const optionsReal = options.map((option) => {
|
|
10
|
+
const result = { ...option };
|
|
11
|
+
if (option.locKey) {
|
|
12
|
+
delete result.locKey;
|
|
13
|
+
result.text = locs[option.locKey];
|
|
14
|
+
}
|
|
15
|
+
if (option.action) {
|
|
16
|
+
delete result.action;
|
|
17
|
+
result.onClick = () => PARAMS.SETTINGS_VIEW.ACTIONS[option.action]({ emit, emitOne: emit, emitReal: emit }, locs);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
});
|
|
21
|
+
return { ...titles, value: shared.settings[settingKey], type, onChange, props: fieldProps, options: optionsReal, valueMap: valueMapReal };
|
|
22
|
+
}, [EVENTS.SETTINGS.SET, PARAMS.SETTINGS_VIEW.ACTIONS, PARAMS.SETTINGS_VIEW.VALUE_MAPS, emit, locs, shared.settings]);
|
|
23
|
+
// Do not memoize mapped settings: shared.settings is mutated in place on SETTINGS.CHANGED.
|
|
24
|
+
const propsSettings = {
|
|
25
|
+
...PARAMS.SETTINGS_VIEW.VIEW_PROPS.fieldProps,
|
|
26
|
+
transition: isForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION,
|
|
27
|
+
settings: fields.map(mapFields),
|
|
28
|
+
selectedIndex: isShow ? (currentItem ?? undefined) : -1,
|
|
29
|
+
selectedIndexInner: isShow ? (currentItemInner ?? undefined) : -1,
|
|
30
|
+
};
|
|
31
|
+
return (_jsx(PositionBox, { isCentered: true, opacity: isShow ? 1 : 0, transition: isForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION, children: _jsxs(Flex, { direction: Flex.DIRECTION.COLUMN, gap: PARAMS.SETTINGS_VIEW.VIEW_PROPS.fieldsGap, children: [_jsx(Settings, { ...propsSettings }), showTextExample && (_jsx(SettingsExampleText, { store: store, onMount: onMount, PARAMS: PARAMS, emit: emit, EVENTS: EVENTS, shared: shared, CONST: CONST, SETTINGS: SETTINGS, locs: locs, isShow: isShow }))] }) }));
|
|
32
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createElement as _createElement } from "react";
|
|
3
|
+
import { View, createRenderFunc, useIsForceHook, useMemo, useStoreState } from "@vnejs/uis.react";
|
|
4
|
+
import { SettingsTabContent } from "./SettingsTabContent.js";
|
|
5
|
+
const SettingsViewComponent = ({ store, onMount, PARAMS, emit, EVENTS, shared, CONST, SETTINGS }) => {
|
|
6
|
+
const { isShow = false, isForce = false, locs = {}, tabsContent = {}, tabKey = "", currentItem = null, currentItemInner = null, } = useStoreState(store, onMount);
|
|
7
|
+
const isRealForce = useIsForceHook(isForce);
|
|
8
|
+
const propsByView = PARAMS.SETTINGS_VIEW.VIEW_PROPS;
|
|
9
|
+
const propsView = useMemo(() => ({
|
|
10
|
+
...propsByView.view,
|
|
11
|
+
isShow,
|
|
12
|
+
isForce: isRealForce,
|
|
13
|
+
transition: isRealForce ? 0 : PARAMS.SETTINGS_VIEW.TRANSITION,
|
|
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, currentItem, currentItemInner, locs]);
|
|
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
|
+
};
|
|
18
|
+
export const render = createRenderFunc(SettingsViewComponent);
|
package/package.json
CHANGED
|
@@ -1,17 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vnejs/plugins.views.settings",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"tsconfig.json"
|
|
19
|
+
],
|
|
5
20
|
"scripts": {
|
|
6
|
-
"test": "
|
|
21
|
+
"test": "npx @vnejs/monorepo test",
|
|
22
|
+
"build": "npx @vnejs/monorepo package",
|
|
7
23
|
"publish:major:plugin": "npm run publish:major",
|
|
8
24
|
"publish:minor:plugin": "npm run publish:minor",
|
|
9
25
|
"publish:patch:plugin": "npm run publish:patch",
|
|
10
|
-
"publish:major": "
|
|
11
|
-
"publish:minor": "
|
|
12
|
-
"publish:patch": "
|
|
26
|
+
"publish:major": "npx @vnejs/monorepo publish major --access public",
|
|
27
|
+
"publish:minor": "npx @vnejs/monorepo publish minor --access public",
|
|
28
|
+
"publish:patch": "npx @vnejs/monorepo publish patch --access public"
|
|
13
29
|
},
|
|
14
30
|
"author": "",
|
|
15
31
|
"license": "ISC",
|
|
16
|
-
"
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@vnejs/contracts.views.settings": "~0.2.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@vnejs/helpers": "~0.2.0",
|
|
37
|
+
"@vnejs/module.core": "~0.2.0",
|
|
38
|
+
"@vnejs/module.components": "~0.2.0",
|
|
39
|
+
"@vnejs/contracts.controls": "~0.2.0",
|
|
40
|
+
"@vnejs/contracts.core.settings": "~0.2.0",
|
|
41
|
+
"@vnejs/contracts.settings.textsize": "~0.2.0",
|
|
42
|
+
"@vnejs/contracts.text.smooth": "~0.2.0",
|
|
43
|
+
"@vnejs/shared": "~0.2.0",
|
|
44
|
+
"@vnejs/uis.react": "~0.2.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@vnejs/configs.ts-common": "~0.2.0",
|
|
48
|
+
"@vnejs/configs.vitest": "~0.2.0",
|
|
49
|
+
"@vnejs/test-utils": "~0.2.0",
|
|
50
|
+
"@vnejs/contracts.controls": "~0.2.0",
|
|
51
|
+
"@vnejs/contracts.core.settings": "~0.2.0",
|
|
52
|
+
"@vnejs/contracts.settings.textsize": "~0.2.0",
|
|
53
|
+
"@vnejs/contracts.text.smooth": "~0.2.0",
|
|
54
|
+
"@vnejs/contracts.views.settings": "~0.2.0",
|
|
55
|
+
"@vnejs/helpers": "~0.2.0",
|
|
56
|
+
"@vnejs/module.core": "~0.2.0",
|
|
57
|
+
"@vnejs/module.components": "~0.2.0",
|
|
58
|
+
"@vnejs/shared": "~0.2.0",
|
|
59
|
+
"@vnejs/sources-set": "~0.2.0",
|
|
60
|
+
"@vnejs/uis.react": "~0.2.0"
|
|
61
|
+
}
|
|
17
62
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "@vnejs/contracts.views.settings";
|
|
2
|
+
|
|
3
|
+
import { regPlugin } from "@vnejs/shared";
|
|
4
|
+
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.settings";
|
|
5
|
+
|
|
6
|
+
import { SettingsViewController } from "./modules/controller.js";
|
|
7
|
+
import { SettingsView } from "./modules/view.js";
|
|
8
|
+
|
|
9
|
+
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [SettingsViewController, SettingsView]);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ModuleController } from "@vnejs/module.components";
|
|
2
|
+
|
|
3
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
4
|
+
import type { SettingsViewPluginState, SettingsViewTabContent, SettingsViewTabPayload } from "../utils/settings.js";
|
|
5
|
+
|
|
6
|
+
export class SettingsViewController extends ModuleController<
|
|
7
|
+
SettingsViewPluginEvents,
|
|
8
|
+
SettingsViewPluginConstants,
|
|
9
|
+
SettingsViewPluginSettings,
|
|
10
|
+
SettingsViewPluginParams,
|
|
11
|
+
SettingsViewPluginState
|
|
12
|
+
> {
|
|
13
|
+
name = "settings.fields.controller";
|
|
14
|
+
|
|
15
|
+
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
16
|
+
controlsIndex = this.PARAMS.SETTINGS_VIEW.ZINDEX;
|
|
17
|
+
|
|
18
|
+
tabKeyOf = ({ tabKey, tab }: SettingsViewTabPayload = {}) => tabKey || tab?.titleLocKey || "default";
|
|
19
|
+
|
|
20
|
+
tabContentOf = (tab: SettingsViewTabPayload["tab"], locs?: Record<string, string>): SettingsViewTabContent => ({
|
|
21
|
+
fields: tab?.fields ?? [],
|
|
22
|
+
showTextExample: Boolean(tab?.showTextExample),
|
|
23
|
+
locs,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
applyTab = (payload: SettingsViewTabPayload = {}) => {
|
|
27
|
+
const { tab, locs } = payload;
|
|
28
|
+
const fields = tab?.fields ?? [];
|
|
29
|
+
const key = this.tabKeyOf(payload);
|
|
30
|
+
|
|
31
|
+
this.maxCurrentItem = Math.max((fields.length ?? 1) - 1, 0);
|
|
32
|
+
|
|
33
|
+
const tabsContent = payload.tabs
|
|
34
|
+
? Object.fromEntries(
|
|
35
|
+
Object.entries(payload.tabs).map(([tabKey, settingsTab]) => [tabKey, this.tabContentOf(settingsTab, locs)]),
|
|
36
|
+
)
|
|
37
|
+
: { ...(this.state.tabsContent ?? {}), [key]: this.tabContentOf(tab, locs) };
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
tab,
|
|
41
|
+
tabKey: key,
|
|
42
|
+
tabsContent,
|
|
43
|
+
fields,
|
|
44
|
+
locs,
|
|
45
|
+
showTextExample: Boolean(tab?.showTextExample),
|
|
46
|
+
currentItemInner: 0,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
onShowFields = (payload: SettingsViewTabPayload = {}) => this.onShow(payload);
|
|
51
|
+
|
|
52
|
+
onTabSet = async (payload: SettingsViewTabPayload = {}) => {
|
|
53
|
+
if (!this.state.isShow) return this.onShow(payload);
|
|
54
|
+
|
|
55
|
+
const isForce = Boolean(payload.isForce || this.shared.viewForceAnimationSources?.isNotEmpty());
|
|
56
|
+
|
|
57
|
+
this.currentItem = null;
|
|
58
|
+
|
|
59
|
+
const next = { ...this.applyTab(payload), currentItem: null as null };
|
|
60
|
+
|
|
61
|
+
if (isForce) return this.updateStateAndViewForce(next);
|
|
62
|
+
|
|
63
|
+
const key = next.tabKey!;
|
|
64
|
+
const alreadyMounted = Boolean(this.state.tabsContent?.[key]);
|
|
65
|
+
|
|
66
|
+
// Mount the incoming tab at opacity 0 first so CSS can crossfade both layers together.
|
|
67
|
+
if (!alreadyMounted) {
|
|
68
|
+
await this.updateStateAndViewFast({ tabsContent: next.tabsContent });
|
|
69
|
+
await this.waitRerender();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return this.updateStateAndView(next);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
onArrowLeft = () => {
|
|
76
|
+
const { currentItemInner = 0, currentItem, fields = [] } = this.state;
|
|
77
|
+
|
|
78
|
+
if (currentItem == null || !fields.length) return;
|
|
79
|
+
|
|
80
|
+
const { settingKey = "", step = 0, min = 0, type = "", options = [] } = fields[currentItem];
|
|
81
|
+
if (!settingKey) return;
|
|
82
|
+
|
|
83
|
+
if (options?.length) {
|
|
84
|
+
const newValue = (currentItemInner === 0 ? options.length : (currentItemInner ?? 0)) - 1;
|
|
85
|
+
|
|
86
|
+
return this.updateStateAndViewFast({ currentItemInner: newValue });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (type === "toggle") {
|
|
90
|
+
return this.shared.settings[settingKey] === true && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: false });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (type === "range") {
|
|
94
|
+
const newValue = Number(Number(Number(this.shared.settings[settingKey]) - step).toFixed(4));
|
|
95
|
+
|
|
96
|
+
return newValue >= min && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
onArrowRight = () => {
|
|
101
|
+
const { currentItemInner = 0, currentItem, fields = [] } = this.state;
|
|
102
|
+
|
|
103
|
+
if (currentItem == null || !fields.length) return;
|
|
104
|
+
|
|
105
|
+
const { settingKey = "", step = 0, max = 0, type = "", options = [] } = fields[currentItem];
|
|
106
|
+
if (!settingKey) return;
|
|
107
|
+
|
|
108
|
+
if (options?.length) {
|
|
109
|
+
return this.updateStateAndViewFast({ currentItemInner: ((currentItemInner ?? 0) + 1) % options.length });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (type === "toggle") {
|
|
113
|
+
return this.shared.settings[settingKey] === false && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: true });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (type === "range") {
|
|
117
|
+
const newValue = Number(Number(Number(this.shared.settings[settingKey]) + step).toFixed(4));
|
|
118
|
+
|
|
119
|
+
return newValue <= max && void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: newValue });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
onInteract = () => {
|
|
124
|
+
const { currentItemInner = 0, currentItem, fields = [], locs = {} } = this.state;
|
|
125
|
+
|
|
126
|
+
if (currentItem == null || !fields.length) return;
|
|
127
|
+
|
|
128
|
+
const { settingKey = "", type = "", options = [] } = fields[currentItem];
|
|
129
|
+
if (!settingKey) return;
|
|
130
|
+
|
|
131
|
+
if (options?.length)
|
|
132
|
+
return options[currentItemInner ?? 0].action
|
|
133
|
+
? this.PARAMS.SETTINGS_VIEW.ACTIONS[options[currentItemInner ?? 0].action!](this, locs)
|
|
134
|
+
: void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: options[currentItemInner ?? 0].value });
|
|
135
|
+
|
|
136
|
+
if (type === "toggle") return void this.emit(this.EVENTS.SETTINGS.SET, { name: settingKey, value: !this.shared.settings[settingKey] });
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
controls = {
|
|
140
|
+
[this.CONST.CONTROLS.BUTTONS.INTERACT]: () => void this.onInteract(),
|
|
141
|
+
[this.CONST.CONTROLS.BUTTONS.ARROW_LEFT]: () => void this.onArrowLeft(),
|
|
142
|
+
[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
|
+
};
|
|
146
|
+
controlsCheckNext = true;
|
|
147
|
+
|
|
148
|
+
subscribe = () => {
|
|
149
|
+
this.on(this.EVENTS.SETTINGS_VIEW.SHOW, this.onShowFields);
|
|
150
|
+
this.on(this.EVENTS.SETTINGS_VIEW.HIDE, this.onHide);
|
|
151
|
+
this.on(this.EVENTS.SETTINGS_VIEW.TAB_SET, this.onTabSet);
|
|
152
|
+
|
|
153
|
+
this.on(this.EVENTS.SETTINGS.CHANGED, this.onSettingChange);
|
|
154
|
+
this.on(this.EVENTS.COMPONENTS.EXTERNAL_UPDATE, this.onSettingChange);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
beforeShow = async (payload: SettingsViewTabPayload = {}) => this.updateState(this.applyTab(payload));
|
|
158
|
+
afterHide = () => this.setDefaultState();
|
|
159
|
+
|
|
160
|
+
onSettingChange = () => this.state.isShow && this.updateViewFast();
|
|
161
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
+
|
|
3
|
+
import { render } from "../view/index.js";
|
|
4
|
+
import type { SettingsViewPluginConstants, SettingsViewPluginEvents, SettingsViewPluginParams, SettingsViewPluginSettings } from "../types.js";
|
|
5
|
+
import type { SettingsViewPluginState } from "../utils/settings.js";
|
|
6
|
+
|
|
7
|
+
export class SettingsView extends ModuleView<
|
|
8
|
+
SettingsViewPluginEvents,
|
|
9
|
+
SettingsViewPluginConstants,
|
|
10
|
+
SettingsViewPluginSettings,
|
|
11
|
+
SettingsViewPluginParams,
|
|
12
|
+
SettingsViewPluginState
|
|
13
|
+
> {
|
|
14
|
+
name = "settings.fields.view";
|
|
15
|
+
|
|
16
|
+
locLabel = this.PARAMS.SETTINGS_VIEW.LOC_LABEL;
|
|
17
|
+
animationTime = this.PARAMS.SETTINGS_VIEW.TRANSITION;
|
|
18
|
+
updateEvent = this.EVENTS.SETTINGS_VIEW.UPDATE;
|
|
19
|
+
|
|
20
|
+
renderFunc = render;
|
|
21
|
+
updateHandler = this.onUpdateStoreComponent;
|
|
22
|
+
}
|