Skip to content

Commit baf3387

Browse files
authored
refactor: introduce EnumRecord utility methods (#60)
1 parent 74ca112 commit baf3387

File tree

12 files changed

+9134
-11863
lines changed

12 files changed

+9134
-11863
lines changed

babel.config.cjs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
presets: [
3+
['@babel/preset-env', { targets: { node: 'current' }}],
4+
'@babel/preset-typescript',
5+
],
6+
};

jest.config.cjs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ module.exports = {
66
coverageDirectory: './coverage/',
77
moduleFileExtensions: ['js', 'ts'],
88
testMatch: [
9-
'**/spec/**/*.spec.js',
9+
'**/*.test.js',
10+
'**/*.test.ts',
1011
],
1112
verbose: true,
1213
};

package-lock.json

+9,033-11,826
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@
3232
"fengari": "0.1.4"
3333
},
3434
"devDependencies": {
35+
"@babel/preset-env": "7.24.4",
36+
"@babel/preset-typescript": "7.24.1",
3537
"@commitlint/cli": "18.4.4",
3638
"@commitlint/config-conventional": "18.4.4",
39+
"@types/jest": "29.5.12",
3740
"@typescript-eslint/eslint-plugin": "6.19.0",
3841
"eslint": "8.56.0",
3942
"eslint-plugin-import": "2.29.1",
4043
"husky": "^4.3.6",
41-
"jest": "26.0.1",
44+
"jest": "29.7.0",
4245
"lint-staged": "15.2.0",
4346
"typescript": "^5.2.2",
4447
"vite": "^5.0.13"

spec/.eslintrc

-5
This file was deleted.

spec/index.spec.js

-1
This file was deleted.

src/ui/components/UIRoot.ts

+2-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import DrawLayerType from '../DrawLayerType';
22
import Screen from '../../gfx/Screen';
33
import ScreenLayer from '../../gfx/ScreenLayer';
44
import { EdgeRect } from '../../math';
5-
import { EnumRecord, LinkedList, NDCtoDDCWidth, NDCtoDDCHeight } from '../../utils';
5+
import { EnumRecord, LinkedList, NDCtoDDCWidth, NDCtoDDCHeight, enumRecordFor } from '../../utils';
66

77
import Frame, { FrameFlag } from './simple/Frame';
88
import FramePointType from './abstract/FramePointType';
@@ -31,17 +31,7 @@ class UIRoot extends LayoutFrame {
3131
anchor: FramePointType.TOPLEFT,
3232
};
3333

34-
this.strata = [
35-
new FrameStrata(FrameStrataType.WORLD),
36-
new FrameStrata(FrameStrataType.BACKGROUND),
37-
new FrameStrata(FrameStrataType.LOW),
38-
new FrameStrata(FrameStrataType.MEDIUM),
39-
new FrameStrata(FrameStrataType.HIGH),
40-
new FrameStrata(FrameStrataType.DIALOG),
41-
new FrameStrata(FrameStrataType.FULLSCREEN),
42-
new FrameStrata(FrameStrataType.FULLSCREEN_DIALOG),
43-
new FrameStrata(FrameStrataType.TOOLTIP),
44-
];
34+
this.strata = enumRecordFor(FrameStrataType, (type) => new FrameStrata(type));
4535

4636
this.frames = LinkedList.using('framesLink');
4737
this.destroyedFrames = LinkedList.using('destroyedLink');

src/ui/components/abstract/FrameStrataLevel.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import DrawLayerType from '../../DrawLayerType';
22
import Frame from '../simple/Frame';
33
import RenderBatch from '../../rendering/RenderBatch';
4-
import { EnumRecord, LinkedList } from '../../../utils';
4+
import { EnumRecord, LinkedList, enumRecordFor } from '../../../utils';
55

66
class FrameStrataLevel {
77
index: number;
@@ -19,13 +19,7 @@ class FrameStrataLevel {
1919
this.pendingFrames = LinkedList.using('strataLink');
2020
this.frames = LinkedList.using('strataLink');
2121

22-
this.batches = [
23-
new RenderBatch(DrawLayerType.BACKGROUND),
24-
new RenderBatch(DrawLayerType.BORDER),
25-
new RenderBatch(DrawLayerType.ARTWORK),
26-
new RenderBatch(DrawLayerType.OVERLAY),
27-
new RenderBatch(DrawLayerType.HIGHLIGHT),
28-
];
22+
this.batches = enumRecordFor(DrawLayerType, (type) => new RenderBatch(type));
2923

3024
this.batchDirty = 0;
3125

src/ui/components/simple/Frame.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
LinkedListLink,
1515
LinkedListNode,
1616
Status,
17+
enumRecordFor,
1718
stringToBoolean,
1819
} from '../../../utils';
1920
import { EPSILON1, Rect, areClose } from '../../../math';
@@ -80,13 +81,7 @@ class Frame extends ScriptRegion {
8081
this.level = 0;
8182
this.frameScale = 1.0;
8283

83-
this.layersEnabled = [
84-
true,
85-
true,
86-
true,
87-
true,
88-
false,
89-
];
84+
this.layersEnabled = enumRecordFor(DrawLayerType, (type) => type !== DrawLayerType.HIGHLIGHT);
9085
this.backdrop = null;
9186

9287
this.regions = LinkedList.using('regionLink');

src/ui/components/simple/Texture.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Client from '../../../Client';
22
import Device from '../../../gfx/Device';
33
import DrawLayerType from '../../DrawLayerType';
4+
import FramePointType from '../../components/abstract/FramePointType';
45
import GfxTexture from '../../../gfx/Texture';
56
import Region from './Region';
67
import RenderBatch from '../../rendering/RenderBatch';
@@ -15,6 +16,7 @@ import {
1516
NDCtoDDCHeight,
1617
NDCtoDDCWidth,
1718
Status,
19+
enumSizeFor,
1820
maxAspectCompensation,
1921
stringToBoolean,
2022
stringToFloat,
@@ -255,12 +257,13 @@ class Texture extends Region {
255257
postLoadXML(_node: XMLNode) {
256258
if (this._parent) {
257259
let i = 0;
260+
const length = enumSizeFor(FramePointType);
258261
for (const point of this.points) {
259262
if (point && !(point.flags & 0x8)) {
260263
break;
261264
}
262265

263-
if (i + 1 === this.points.length) {
266+
if (i + 1 === length) {
264267
this.setAllPoints(this._parent, true);
265268
break;
266269
}

src/utils/types.test.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { enumRecordFor, enumSizeFor, enumValuesFor } from './types';
2+
3+
enum Currency {
4+
USD = 'US Dollar',
5+
EUR = 'Euro'
6+
}
7+
8+
enum Ranking {
9+
FIRST = 1,
10+
SECOND = 2,
11+
THIRD = 3
12+
}
13+
14+
describe('enumValuesFor', () => {
15+
it('returns an array of string enum values', () => {
16+
expect(enumValuesFor(Currency)).toEqual([Currency.USD, Currency.EUR]);
17+
});
18+
19+
it('returns an array of number enum values', () => {
20+
expect(enumValuesFor(Ranking)).toEqual([Ranking.FIRST, Ranking.SECOND, Ranking.THIRD]);
21+
});
22+
});
23+
24+
describe('enumRecordFor', () => {
25+
it('constructs an iterable string enum record', () => {
26+
const record = enumRecordFor(Currency, (value) => value.length);
27+
expect(record).toEqual({
28+
[Currency.USD]: 9,
29+
[Currency.EUR]: 4,
30+
});
31+
expect(Array.from(record)).toEqual([9, 4]);
32+
});
33+
34+
it('constructs an iterable number enum record', () => {
35+
const record = enumRecordFor(Ranking, (value) => value * 3);
36+
expect(record).toEqual({
37+
[Ranking.FIRST]: 3,
38+
[Ranking.SECOND]: 6,
39+
[Ranking.THIRD]: 9,
40+
});
41+
expect(Array.from(record)).toEqual([3, 6, 9]);
42+
});
43+
});
44+
45+
describe('enumSizeFor', () => {
46+
it('returns size for string enum', () => {
47+
expect(enumSizeFor(Currency)).toEqual(2);
48+
});
49+
50+
it('returns size for number enum', () => {
51+
expect(enumSizeFor(Ranking)).toEqual(3);
52+
});
53+
});

src/utils/types.ts

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
1-
export type EnumRecord<E extends string | number | symbol, V> = Record<E, V> & Iterable<V> & { length: number };
1+
export type EnumRecord<E extends string | number, V> = Record<E, V> & Iterable<V>;
2+
3+
// Loosely adapted from: https://github.com/microsoft/TypeScript/issues/4753#issuecomment-694557208
4+
type EnumType<T> = { [name: string]: T }
5+
6+
export function enumValuesFor<T extends string>(enumeration: EnumType<T>): T[];
7+
export function enumValuesFor<T extends string | number>(enumeration: EnumType<T>): Exclude<T, string>[];
8+
export function enumValuesFor<T>(enumeration: EnumType<T>): T[] {
9+
const isStringEnum = Object.values(enumeration).every((value) => typeof value === 'string');
10+
return Object.values(enumeration).filter((value) => isStringEnum || typeof value === 'number');
11+
}
12+
13+
export function enumRecordFor<T extends string, V extends (value: T) => unknown>(enumeration: EnumType<T>, mapFv: V): EnumRecord<T, ReturnType<V>>;
14+
export function enumRecordFor<T extends string | number, V extends (value: Exclude<T, string>) => unknown>(enumeration: EnumType<T>, mapFn: V): EnumRecord<Exclude<T, string>, ReturnType<V>>;
15+
export function enumRecordFor<T extends string | number, V extends (value: T) => unknown>(enumeration: EnumType<T>, mapFn: V): EnumRecord<T, ReturnType<V>> {
16+
const values = enumValuesFor(enumeration);
17+
const record = Object.assign({}, ...values.map((value) => ({ [value]: mapFn(value) })));
18+
Object.defineProperty(record, Symbol.iterator, {
19+
value: () => Object.values(record).values()
20+
});
21+
return record;
22+
}
23+
24+
export function enumSizeFor<T extends string | number>(enumeration: EnumType<T>): number {
25+
return enumValuesFor(enumeration).length;
26+
}
227

328
// See: https://github.com/microsoft/TypeScript/issues/5863#issuecomment-1336204919
429
export type ThisConstructor<

0 commit comments

Comments
 (0)