Factor out vocabulary.ts.

pull/59/head
Atul Varma 2021-03-27 08:29:42 -04:00
rodzic a19a560b8f
commit 51f68656ad
9 zmienionych plików z 73 dodań i 78 usunięć

Wyświetl plik

@ -6,6 +6,7 @@ import {
} from "./creature-symbol";
import { AttachmentPointType } from "./specs";
import { SvgSymbolData } from "./svg-symbol";
import { Vocabulary } from "./vocabulary";
type AttachmentIndices = {
left?: boolean;
@ -71,7 +72,7 @@ type SimpleCreatureSymbolFC = React.FC<SimpleCreatureSymbolProps> & {
* render a `<CreatureSymbol>`.
*/
export function createCreatureSymbolFactory(
getSymbol: (name: string) => SvgSymbolData
vocabulary: Vocabulary<SvgSymbolData>
) {
/**
* Returns a React component that renders a `<CreatureSymbol>`, using the symbol
@ -80,7 +81,7 @@ export function createCreatureSymbolFactory(
return function createCreatureSymbol(
name: string
): React.FC<SimpleCreatureSymbolProps> {
const data = getSymbol(name);
const data = vocabulary.get(name);
const Component: SimpleCreatureSymbolFC = (props) => {
const symbol = getCreatureSymbol(data, props);
return <CreatureSymbol {...symbol} />;

Wyświetl plik

@ -25,7 +25,7 @@ import { NumericSlider } from "../numeric-slider";
import { DEFAULT_BG_COLOR } from "../colors";
/** Symbols that can be the "root" (i.e., main body) of a creature. */
const ROOT_SYMBOLS = SvgVocabulary.filter(
const ROOT_SYMBOLS = SvgVocabulary.items.filter(
(data) => data.meta?.always_be_nested !== true
);
@ -41,7 +41,7 @@ const ATTACHMENT_SYMBOLS: AttachmentSymbolMap = (() => {
const result = {} as AttachmentSymbolMap;
for (let type of ATTACHMENT_POINT_TYPES) {
result[type] = SvgVocabulary.filter((data) => {
result[type] = SvgVocabulary.items.filter((data) => {
const { meta } = data;
// If we have no metadata whatsoever, it can attach anywhere.
@ -66,7 +66,7 @@ const ATTACHMENT_SYMBOLS: AttachmentSymbolMap = (() => {
})();
/** Symbols that can be nested within any part of a creature. */
const NESTED_SYMBOLS = SvgVocabulary.filter(
const NESTED_SYMBOLS = SvgVocabulary.items.filter(
// Since we don't currently support recursive nesting, ignore anything that
// wants nested children.
(data) =>

Wyświetl plik

@ -5,10 +5,10 @@ import { createCreatureSymbolFactory } from "../creature-symbol-factory";
import { HoverDebugHelper } from "../hover-debug-helper";
import { createSvgSymbolContext } from "../svg-symbol";
import { svgScale, SvgTransforms } from "../svg-transform";
import { getSvgSymbol } from "../svg-vocabulary";
import { SvgVocabulary } from "../svg-vocabulary";
import { SymbolContextWidget } from "../symbol-context-widget";
const symbol = createCreatureSymbolFactory(getSvgSymbol);
const symbol = createCreatureSymbolFactory(SvgVocabulary);
const Eye = symbol("eye");

Wyświetl plik

@ -13,18 +13,18 @@ import {
SvgSymbolContext,
SvgSymbolData,
} from "../svg-symbol";
import { SvgSymbolWidget } from "../svg-symbol-widget";
import { VocabularyWidget } from "../vocabulary-widget";
import {
svgRotate,
svgScale,
SvgTransforms,
svgTranslate,
} from "../svg-transform";
import { getSvgSymbol, SvgVocabulary } from "../svg-vocabulary";
import { SvgVocabulary } from "../svg-vocabulary";
import { SymbolContextWidget } from "../symbol-context-widget";
import { range } from "../util";
const EYE = getSvgSymbol("eye");
const EYE = SvgVocabulary.get("eye");
const MandalaCircle: React.FC<
{
@ -72,7 +72,7 @@ export const MandalaPage: React.FC<{}> = () => {
<ColorWidget label="Background" value={bgColor} onChange={setBgColor} />{" "}
</SymbolContextWidget>
<p>
<SvgSymbolWidget
<VocabularyWidget
label="Symbol"
value={symbol}
onChange={setSymbol}

Wyświetl plik

@ -44,7 +44,7 @@ export const VocabularyPage: React.FC<{}> = () => {
<h1>Mystic Symbolic Vocabulary</h1>
<SymbolContextWidget ctx={ctx} onChange={setCtx} />
<HoverDebugHelper>
{SvgVocabulary.map((symbolData) => (
{SvgVocabulary.items.map((symbolData) => (
<div
key={symbolData.name}
style={{

Wyświetl plik

@ -1,47 +0,0 @@
import React, { useMemo } from "react";
import { SvgSymbolData } from "./svg-symbol";
import { slugify } from "./util";
export type SvgSymbolWidgetProps = {
id?: string;
label: string;
value: SvgSymbolData;
onChange: (value: SvgSymbolData) => void;
choices: SvgSymbolData[];
};
export const SvgSymbolWidget: React.FC<SvgSymbolWidgetProps> = ({
id,
label,
value,
onChange,
choices,
}) => {
id = id || slugify(label);
const symbolMap = useMemo(
() => new Map(choices.map((symbol) => [symbol.name, symbol])),
[choices]
);
const handleChange = (value: string) => {
const symbol = symbolMap.get(value);
if (!symbol) throw new Error(`Unable to find "${value}"`);
onChange(symbol);
};
return (
<>
<label htmlFor={id}>{label}: </label>
<select
id={id}
onChange={(e) => handleChange(e.target.value)}
value={value.name}
>
{choices.map((choice) => (
<option key={choice.name} value={choice.name}>
{choice.name}
</option>
))}
</select>
</>
);
};

Wyświetl plik

@ -1,23 +1,7 @@
import type { SvgSymbolData } from "./svg-symbol";
import { Vocabulary } from "./vocabulary";
import _SvgVocabulary from "./_svg-vocabulary.json";
export const SvgVocabulary: SvgSymbolData[] = _SvgVocabulary as any;
/**
* Mapping from symbol names to symbol data, for quick and easy access.
*/
const SYMBOL_MAP = new Map(
SvgVocabulary.map((symbol) => [symbol.name, symbol])
export const SvgVocabulary = new Vocabulary<SvgSymbolData>(
_SvgVocabulary as any
);
/**
* Returns the data for the given symbol, throwing an error
* if it doesn't exist.
*/
export function getSvgSymbol(name: string): SvgSymbolData {
const symbol = SYMBOL_MAP.get(name);
if (!symbol) {
throw new Error(`Unable to find the symbol "${name}"!`);
}
return symbol;
}

Wyświetl plik

@ -0,0 +1,38 @@
import React from "react";
import { slugify } from "./util";
import { Vocabulary, VocabularyType } from "./vocabulary";
export type VocabularyWidgetProps<T extends VocabularyType> = {
id?: string;
label: string;
value: T;
onChange: (value: T) => void;
choices: Vocabulary<T>;
};
export function VocabularyWidget<T extends VocabularyType>({
id,
label,
value,
onChange,
choices,
}: VocabularyWidgetProps<T>) {
id = id || slugify(label);
return (
<>
<label htmlFor={id}>{label}: </label>
<select
id={id}
onChange={(e) => onChange(choices.get(e.target.value))}
value={value.name}
>
{choices.items.map((choice) => (
<option key={choice.name} value={choice.name}>
{choice.name}
</option>
))}
</select>
</>
);
}

19
lib/vocabulary.ts 100644
Wyświetl plik

@ -0,0 +1,19 @@
export type VocabularyType = {
name: string;
};
export class Vocabulary<T extends VocabularyType> {
readonly itemMap: Map<string, T>;
constructor(readonly items: T[]) {
this.itemMap = new Map(items.map((item) => [item.name, item]));
}
get(name: string): T {
const item = this.itemMap.get(name);
if (!item) {
throw new Error(`Unable to find the item "${name}"!`);
}
return item;
}
}