Skip to content

Commit ceead10

Browse files
committed
ref: better feature info types
1 parent eebe1c8 commit ceead10

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

src/features/index.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { t, tp } from "../translations";
22
import { getPreferredLocale } from "../utils";
33
import langMapping from "../../assets/locales.json" assert { type: "json" };
44
import { remSongMinPlayTime } from "./behavior";
5+
import { FeatureInfo } from "../types";
56

67
export * from "./layout";
78
export * from "./behavior";
@@ -10,14 +11,6 @@ export * from "./lyrics";
1011
export * from "./songLists";
1112
export * from "./versionCheck";
1213

13-
//#MARKER types
14-
15-
/** Union of all feature keys */
16-
export type FeatInfoKey = keyof typeof featInfo;
17-
18-
/** Union of all feature categories */
19-
export type FeatureCategory = typeof featInfo[FeatInfoKey]["category"];
20-
2114
type SelectOption = { value: number | string, label: string };
2215

2316
//#MARKER feature dependencies
@@ -38,9 +31,9 @@ const localeOptions = Object.entries(langMapping).reduce((a, [locale, { name }])
3831
* **Required props:**
3932
* | Property | Description |
4033
* | :-- | :-- |
41-
* | `type` | type of the feature - see below for possible values |
42-
* | `category` | category of the feature - see what `FeatureCategory` above expands to for possible values |
43-
* | `default` | default value of the feature - type of the value depends on the `type` property |
34+
* | `type` | type of the feature configuration element - use autocomplete or check `FeatureTypeProps` in `src/types.ts` |
35+
* | `category` | category of the feature - use autocomplete or check `FeatureCategory` in `src/types.ts` |
36+
* | `default` | default value of the feature - type of the value depends on the given `type` |
4437
* | `enable(value: any)` | function that will be called when the feature is enabled / initialized for the first time |
4538
*
4639
* **Optional props:**
@@ -53,7 +46,7 @@ const localeOptions = Object.entries(langMapping).reduce((a, [locale, { name }])
5346
* | `min` | Only if type is `number` or `slider` - Overwrites the default of the `min` property of the HTML input element |
5447
* | `max` | Only if type is `number` or `slider` - Overwrites the default of the `max` property of the HTML input element |
5548
* | `step` | Only if type is `number` or `slider` - Overwrites the default of the `step` property of the HTML input element |
56-
* | `unit` | Only if type is `number` or `slider` - The unit text that is displayed next to the input element |
49+
* | `unit` | Only if type is `number` or `slider` - The unit text that is displayed next to the input element, i.e. "px" |
5750
*
5851
* **Notes:**
5952
* - If no `disable()` or `change()` function is present, the page needs to be reloaded for the changes to take effect
@@ -285,4 +278,4 @@ export const featInfo = {
285278
default: 1,
286279
enable: () => void "TODO",
287280
},
288-
} as const;
281+
} as const satisfies FeatureInfo;

src/menu/menu_old.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { compress, decompress, debounce, isScrollable } from "@sv443-network/userutils";
22
import { defaultConfig, getFeatures, migrations, saveFeatures, setDefaultFeatures } from "../config";
33
import { host, scriptInfo } from "../constants";
4-
import { FeatureCategory, FeatInfoKey, featInfo, disableBeforeUnload } from "../features/index";
4+
import { featInfo, disableBeforeUnload } from "../features/index";
55
import { error, getResourceUrl, info, log, resourceToHTMLString, warn } from "../utils";
66
import { formatVersion } from "../config";
77
import { emitSiteEvent, siteEvents } from "../siteEvents";
88
import { getLocale, hasKey, initTranslations, setLocale, t } from "../translations";
9-
import { FeatureConfig, HotkeyObj } from "../types";
9+
import { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj } from "../types";
1010
import changelog from "../../changelog.md";
1111
import "./menu_old.css";
1212
import { createHotkeyInput } from "./hotkeyInput";
@@ -245,11 +245,11 @@ export async function addCfgMenu() {
245245
.reduce(
246246
(acc, [key, { category }]) => {
247247
if(!acc[category])
248-
acc[category] = {} as Record<FeatInfoKey, unknown>;
249-
acc[category][key as FeatInfoKey] = featureCfg[key as FeatInfoKey];
248+
acc[category] = {} as Record<FeatureKey, unknown>;
249+
acc[category][key as FeatureKey] = featureCfg[key as FeatureKey];
250250
return acc;
251251
},
252-
{} as Record<FeatureCategory, Record<FeatInfoKey, unknown>>,
252+
{} as Record<FeatureCategory, Record<FeatureKey, unknown>>,
253253
);
254254

255255
const fmtVal = (v: unknown) => String(v).trim();
@@ -310,7 +310,7 @@ export async function addCfgMenu() {
310310
e.preventDefault();
311311
e.stopPropagation();
312312

313-
openHelpDialog(featKey as FeatInfoKey);
313+
openHelpDialog(featKey as FeatureKey);
314314
});
315315
}
316316
else {
@@ -632,10 +632,10 @@ function checkToggleScrollIndicator() {
632632

633633
let isHelpDialogOpen = false;
634634
/** Key of the feature currently loaded in the help dialog */
635-
let helpDialogCurFeature: FeatInfoKey | undefined;
635+
let helpDialogCurFeature: FeatureKey | undefined;
636636

637637
/** Opens the feature help dialog for the given feature */
638-
async function openHelpDialog(featureKey: FeatInfoKey) {
638+
async function openHelpDialog(featureKey: FeatureKey) {
639639
if(isHelpDialogOpen)
640640
return;
641641
isHelpDialogOpen = true;

src/types.ts

+72
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,78 @@ declare global {
9898
}
9999
}
100100

101+
export type FeatureKey = keyof FeatureConfig;
102+
103+
export type FeatureCategory =
104+
| "layout"
105+
| "songLists"
106+
| "behavior"
107+
| "input"
108+
| "lyrics"
109+
| "general";
110+
111+
type SelectOption = {
112+
value: string | number,
113+
label: string,
114+
};
115+
116+
type FeatureTypeProps =
117+
| {
118+
type: "toggle",
119+
default: boolean,
120+
}
121+
| {
122+
type: "number",
123+
default: number,
124+
min: number,
125+
max: number,
126+
step?: number,
127+
unit?: string,
128+
}
129+
| {
130+
type: "select",
131+
default: string | number,
132+
options: SelectOption[] | (() => SelectOption[]),
133+
}
134+
| {
135+
type: "slider",
136+
default: number,
137+
min: number,
138+
max: number,
139+
step?: number,
140+
unit?: string,
141+
}
142+
| {
143+
type: "hotkey",
144+
default: HotkeyObj,
145+
};
146+
147+
type FeatureFuncProps = {
148+
enable: () => void,
149+
} & (
150+
{
151+
disable?: () => void,
152+
}
153+
| {
154+
change?: () => void,
155+
}
156+
)
157+
158+
/**
159+
* The feature info object that contains all properties necessary to construct the config menu and the feature config object.
160+
* Values are loosely typed so try to only use this with the `satisfies` keyword.
161+
* Use `typeof featInfo` (from `src/features/index.ts`) instead for full type safety.
162+
*/
163+
export type FeatureInfo = Record<
164+
keyof FeatureConfig,
165+
{
166+
category: FeatureCategory;
167+
helpText?: string | (() => string);
168+
}
169+
& FeatureTypeProps
170+
& FeatureFuncProps
171+
>;
172+
101173
/** Feature configuration */
102174
export interface FeatureConfig {
103175
//#SECTION layout

0 commit comments

Comments
 (0)