Skip to content

Commit 112968e

Browse files
committed
add flexsearch for bzdb editor
1 parent 7e7779a commit 112968e

File tree

7 files changed

+129
-55
lines changed

7 files changed

+129
-55
lines changed

package-lock.json

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"dayjs": "^1.10.6",
1616
"dedent": "^0.7.0",
1717
"file-saver": "^2.0.5",
18+
"flexsearch": "^0.8.205",
1819
"immer": "^9.0.6",
1920
"js-yaml": "^4.1.0",
2021
"markdown-it": "^13.0.0",

src/Components/Modals/BZDBSettingsModal.module.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.modalBody {
22
display: flex;
3+
flex-direction: column;
4+
gap: var(--spacing-normal);
35
}
46

57
.tabList {

src/Components/Modals/BZDBSettingsModal.tsx

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
1+
import { Document } from 'flexsearch';
12
import produce from 'immer';
2-
import React, {
3-
useCallback,
4-
useEffect,
5-
useMemo,
6-
useReducer,
7-
useState,
8-
} from 'react';
3+
import React, { useCallback, useEffect, useReducer, useState } from 'react';
94
import { useDialogState } from 'reakit';
105
import { useRecoilState } from 'recoil';
116

127
import { IOptions } from '../../Document/Obstacles/Option';
138
import { BZDBSettingsModalOpenEventName } from '../../Events/IBZDBSettingsModalOpenEvent';
9+
import bzdbDocumentation, { BZDBDocType } from '../../Utilities/BZDBDocumentor';
1410
import { documentState } from '../../atoms';
1511
import { BZDBType } from '../../data/bzdb-types';
12+
import { useDocumentSearch } from '../../hooks/useFlexSearch';
1613
import Button from '../Button';
1714
import BZDBEquationField from '../Form/BZDBEquationField';
1815
import CheckboxField from '../Form/CheckboxField';
16+
import TextField from '../Form/TextField';
1917
import ListenerModal from '../ListenerModal';
2018
import Markdown from '../Markdown';
2119
import { Tab, TabList } from '../TabList';
2220

23-
import BZDBDocs from '../../data/bzdb-documention.json';
2421
import generalStyles from '../../sass/general.module.scss';
2522
import styles from './BZDBSettingsModal.module.scss';
2623

27-
type BZDBVariableType = typeof BZDBDocs.variables[number];
28-
2924
interface SettingEditorProps {
3025
onChange: (setting: string, value: any) => void;
31-
variable: BZDBVariableType;
26+
variable: BZDBDocType;
3227
}
3328

3429
// https://github.com/BZFlag-Dev/bzflag/blob/a249151/src/common/StateDatabase.cxx#L252-L256
@@ -87,7 +82,7 @@ const SettingEditor = ({ onChange, variable }: SettingEditorProps) => {
8782
<div>{renderEditor(variable.type ?? 'string')}</div>
8883
</div>
8984
<div className={generalStyles.descriptionLike}>
90-
<Markdown content={variable.desc} inline />
85+
<Markdown content={variable.description} inline />
9186
</div>
9287
</div>
9388
);
@@ -123,37 +118,28 @@ function bzdbReducer(state: BZDBStore, action: ReducerAction) {
123118
});
124119
}
125120

121+
const bzdbSearchIndex = new Document({
122+
id: 'name',
123+
index: [
124+
{
125+
field: 'name',
126+
tokenize: 'reverse',
127+
},
128+
'description',
129+
],
130+
store: ['name', 'description', 'default', 'category'],
131+
});
132+
133+
bzdbDocumentation.forEach((variable) => {
134+
bzdbSearchIndex.add({ ...variable });
135+
});
136+
126137
const BZDBSettingsModal = () => {
127138
const [world, setBZWDocument] = useRecoilState(documentState);
128139
const [bzdbStore, bzdbStoreDispatch] = useReducer(bzdbReducer, {});
129140
const dialog = useDialogState();
130-
131-
const {
132-
bzdbCategories,
133-
bzdbDefinitionsByCategory,
134-
bzdbDefinitionsByVariable,
135-
} = useMemo(() => {
136-
const groupedByCat: Record<string, BZDBVariableType[]> = {};
137-
const mappedByVariable: Partial<Record<BZDBType, BZDBVariableType>> = {};
138-
139-
BZDBDocs.variables.forEach((variable) => {
140-
const cat = variable.category ?? 'Miscellaneous';
141-
142-
if (!groupedByCat.hasOwnProperty(cat)) {
143-
groupedByCat[cat] = [];
144-
}
145-
146-
groupedByCat[cat].push(variable);
147-
mappedByVariable[variable.name as BZDBType] = variable;
148-
});
149-
150-
return {
151-
bzdbDefinitionsByCategory: groupedByCat,
152-
bzdbCategories: Object.keys(groupedByCat).sort(),
153-
bzdbDefinitionsByVariable: mappedByVariable,
154-
bzdbVariables: Object.keys(mappedByVariable).sort(),
155-
};
156-
}, []);
141+
const [searchQuery, setSearchQuery] = useState<string>('');
142+
const results = useDocumentSearch(searchQuery, bzdbSearchIndex);
157143

158144
const syncStateToWorld = useCallback(() => {
159145
bzdbStoreDispatch({
@@ -163,7 +149,7 @@ const BZDBSettingsModal = () => {
163149
}, [world?._options]);
164150

165151
const handleOnChange = (variable: string, value: string) => {
166-
const definition = bzdbDefinitionsByVariable[variable as BZDBType];
152+
const definition = bzdbDocumentation.store[variable as BZDBType];
167153

168154
if (value === definition?.default) {
169155
bzdbStoreDispatch({ type: 'delete', variable });
@@ -184,6 +170,8 @@ const BZDBSettingsModal = () => {
184170
dialog.hide();
185171
};
186172

173+
console.log({ results });
174+
187175
return (
188176
<ListenerModal
189177
event={BZDBSettingsModalOpenEventName}
@@ -200,10 +188,17 @@ const BZDBSettingsModal = () => {
200188
hideOnEsc={false}
201189
hideOnClickOutside={false}
202190
>
191+
<div>
192+
<TextField
193+
label="Search"
194+
onChange={setSearchQuery}
195+
value={searchQuery}
196+
/>
197+
</div>
203198
<TabList aria-label="BZDB Settings" className={styles.tabList} vertical>
204-
{bzdbCategories.map((category) => (
199+
{bzdbDocumentation.categories.map((category) => (
205200
<Tab title={category} key={category}>
206-
{bzdbDefinitionsByCategory[category].map((variable) => (
201+
{bzdbDocumentation.mapByCategory(category, (variable) => (
207202
<SettingEditor
208203
key={variable.name}
209204
onChange={handleOnChange}

src/Components/Modals/FlagSettingsModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const FlagSettingsModal = () => {
6464
<div className="col-md-6">
6565
<NumberField
6666
label="Flag Altitude"
67-
description={bzdbDocumentation.getDescription('_flagAltitude')}
67+
description={bzdbDocumentation.store['_flagAltitude'].description}
6868
allowChange={positiveOnly}
6969
onChange={setFlagAltitude}
7070
value={flagAltitude}
@@ -73,7 +73,7 @@ const FlagSettingsModal = () => {
7373
<div className="col-md-6">
7474
<NumberField
7575
label="Flag Height"
76-
description={bzdbDocumentation.getDescription('_flagHeight')}
76+
description={bzdbDocumentation.store['_flagHeight'].description}
7777
allowChange={positiveOnly}
7878
onChange={setFlagHeight}
7979
value={flagHeight}

src/Utilities/BZDBDocumentor.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,67 @@ import data from '../data/bzdb-documention.json';
44

55
type DocsType = typeof data;
66

7-
interface BZDBDocType {
7+
export interface BZDBDocType {
88
name: string;
99
description: string;
10-
defaultValue: string;
10+
default: string;
11+
type: typeof data['variables'][number]['type'];
1112
}
1213

1314
export class BZDBDocumentor {
15+
private readonly grpByCat: Record<string, Record<string, BZDBDocType>> = {};
1416
private readonly storage: Record<string, BZDBDocType> = {};
15-
private readonly fields: BZDBType[] = [];
17+
private readonly _fields: BZDBType[] = [];
1618

1719
constructor(data: DocsType) {
1820
for (const variable of data.variables) {
19-
this.fields.push(variable.name as BZDBType);
21+
this._fields.push(variable.name as BZDBType);
2022
this.storage[variable.name] = {
2123
name: variable.name,
2224
description: variable.desc ?? '',
23-
defaultValue: variable.default,
25+
default: variable.default,
26+
type: variable.type,
2427
};
28+
29+
const cat = variable.category ?? 'Miscellaneous';
30+
31+
this.grpByCat = this.grpByCat ?? {};
32+
this.grpByCat[cat] = this.grpByCat[cat] ?? {};
33+
this.grpByCat[cat][variable.name] = this.storage[variable.name];
2534
}
2635
}
2736

28-
getDescription(bzdb: BZDBType): string {
29-
return this.storage[bzdb].description;
37+
forEach(
38+
callback: (doc: BZDBDocType, index: number, array: BZDBDocType[]) => void,
39+
): void {
40+
const array = this._fields.map((f) => this.storage[f]);
41+
42+
this._fields.forEach((field, index) => {
43+
callback(this.storage[field], index, array);
44+
});
45+
}
46+
47+
mapByCategory<T>(
48+
category: string,
49+
callback: (doc: BZDBDocType, index: number, array: BZDBDocType[]) => T,
50+
): T[] {
51+
if (!this.grpByCat.hasOwnProperty(category)) {
52+
return [];
53+
}
54+
55+
return Object.values(this.grpByCat[category]).map(callback);
56+
}
57+
58+
get categories(): string[] {
59+
return Object.keys(this.grpByCat).sort();
3060
}
3161

32-
getDefaultValue(bzdb: BZDBType): string {
33-
return this.storage[bzdb].defaultValue;
62+
get fields(): BZDBType[] {
63+
return [...this._fields];
3464
}
3565

36-
getSettings(): Iterable<BZDBType> {
37-
return this.fields;
66+
get store(): Readonly<Record<string, BZDBDocType>> {
67+
return { ...this.storage };
3868
}
3969
}
4070

src/hooks/useFlexSearch.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Document } from 'flexsearch';
2+
import { useMemo } from 'react';
3+
4+
export function useDocumentSearch(query: string, index: Document) {
5+
return useMemo(() => {
6+
return index.search(query, { enrich: true });
7+
}, [query, index]);
8+
}

0 commit comments

Comments
 (0)