Default types and templates

This commit is contained in:
Emma 2023-06-12 18:02:19 -06:00
parent 8fb7494464
commit a7337bd33a
10 changed files with 179 additions and 18 deletions

View File

@ -1,8 +1,6 @@
import { FC, useCallback, useEffect } from 'react' import { FC, useCallback, useEffect } from 'react'
import { FieldType, FieldTypes, fieldTypeOptions, fieldTypesWithValues } from '../../types/schema' import { FieldType, FieldTypes, fieldTypeOptions, fieldTypesWithValues } from '../../types/schema'
import { useObjectStateWrapper } from '../../hooks/useObjectState'; import { useObjectStateWrapper } from '../../hooks/useObjectState';
import { FieldTypeInput } from './field-type-input';
import { InputBinder } from '../../types/inputBinder';
import { ValueField } from './value-field'; import { ValueField } from './value-field';
import { HelpPopper } from '../Poppables/help'; import { HelpPopper } from '../Poppables/help';
@ -13,7 +11,7 @@ interface IProps {
} }
export const FieldEditor: FC<IProps> = ({ update, field, fieldName }) => { export const FieldEditor: FC<IProps> = ({ update, field, fieldName }) => {
const { update: updateField, bindProperty, bindPropertyCheck } = useObjectStateWrapper(field, (e) => update(typeof e === 'function' ? e(field) : e)) const { bindProperty, bindPropertyCheck } = useObjectStateWrapper(field, (e) => update(typeof e === 'function' ? e(field) : e))
const shouldShowValueField = useCallback(() => fieldTypesWithValues.includes(field.type) || field.isConstant, [field.isConstant, field.type]); const shouldShowValueField = useCallback(() => fieldTypesWithValues.includes(field.type) || field.isConstant, [field.isConstant, field.type]);

View File

@ -2,6 +2,7 @@ import { useRecoilValue } from 'recoil';
import { SchemaEditAtom } from '../../recoil/atoms/schema'; import { SchemaEditAtom } from '../../recoil/atoms/schema';
import { FCC } from '../../types' import { FCC } from '../../types'
import { InputBinder } from '../../types/inputBinder' import { InputBinder } from '../../types/inputBinder'
import { TEMPLATE_TYPES } from '../../constants/TemplateTypes';
interface IProps { interface IProps {
bind: InputBinder bind: InputBinder
@ -11,13 +12,17 @@ export const FieldTypeInput: FCC<IProps> = ({ bind }) => {
const Schema = useRecoilValue(SchemaEditAtom); const Schema = useRecoilValue(SchemaEditAtom);
return ( return (
<> <label className="w-min">
Type:
<input type="text" {...bind} list="type-editor-type-list" /> <input type="text" {...bind} list="type-editor-type-list" />
<optgroup id="type-editor-type-list"> <datalist id="type-editor-type-list">
{Object.keys(TEMPLATE_TYPES).map(k => (
<option className="capitalize" value={k}>{k}</option>
))}
{Object.keys(Schema.types).map(k => ( {Object.keys(Schema.types).map(k => (
<option className="capitalize" value={k}>{k}</option> <option className="capitalize" value={k}>{k}</option>
))} ))}
</optgroup> </datalist>
</> </label>
) )
} }

View File

@ -2,12 +2,13 @@ import { FC, useCallback, useEffect, useState } from 'react'
import AnimatedPageContainer from '../AnimatedPageContainer'; import AnimatedPageContainer from '../AnimatedPageContainer';
import { TypeEditor } from './type-editor'; import { TypeEditor } from './type-editor';
import { useObjectState, useObjectStateWrapper } from '../../hooks/useObjectState'; import { useObjectState, useObjectStateWrapper } from '../../hooks/useObjectState';
import { Schema, TypeType } from '../../types/schema'; import { FieldTypes, Schema, Template, TypeType } from '../../types/schema';
import { useInput } from '../../hooks/useInput'; import { useInput } from '../../hooks/useInput';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { SchemaEditAtom } from '../../recoil/atoms/schema'; import { SchemaEditAtom } from '../../recoil/atoms/schema';
import { GameSystemsService } from '../../services/game-systems'; import { GameSystemsService } from '../../services/game-systems';
import { SchemaViewer } from './schema-viewer'; import { SchemaViewer } from './schema-viewer';
import { TemplateEditor } from './template-editor';
export const SchemaBuilder: FC = () => { export const SchemaBuilder: FC = () => {
@ -25,6 +26,7 @@ export const SchemaBuilder: FC = () => {
if (result.status !== 200) return; if (result.status !== 200) return;
const fetchedSchema = await result.json(); const fetchedSchema = await result.json();
// if (fetchedSchema.name === schema.name) return; // if (fetchedSchema.name === schema.name) return;
if (!fetchedSchema.templates) fetchedSchema.templates = {}
setSchema(fetchedSchema); setSchema(fetchedSchema);
setLastSaved(fetchedSchema); setLastSaved(fetchedSchema);
}, [setSchema]) }, [setSchema])
@ -58,12 +60,43 @@ export const SchemaBuilder: FC = () => {
setPageNumber(1); setPageNumber(1);
}, []) }, [])
const { value: templateName, bind: bindTemplateName, reset: resetTemplateName } = useInput('');
const addTemplate = useCallback(() => {
updateSchema(s => ({
templates: {
...s.templates,
[templateName]: {
publishable: false,
type: FieldTypes.any
}
}
}));
resetTemplateName();
}, [resetTemplateName, templateName, updateSchema])
const updateTemplate = useCallback((key: string, template: Template) => {
updateSchema(s => ({
templates: {
...s.templates,
[key]: template
}
}))
}, [updateSchema])
return ( return (
<div className="container flex gap-4 p-8"> <div className="container flex gap-4 p-8">
<div className="panel w-2/3 h-full"> <div className="panel w-2/3 h-full">
<p className="subheader">Add a template</p>
<input type="text" {...bindTemplateName} />
<button onClick={addTemplate} disabled={!templateName}>Add</button>
<ul>
{Object.entries(schema.templates).map(([templateKey, template]) => (
<TemplateEditor templateKey={templateKey} template={template} update={updateTemplate} />
))}
</ul>
<AnimatedPageContainer currentPage={pageNumber}> <AnimatedPageContainer currentPage={pageNumber}>
<div> <div>
<p className="subheader">Add A Type</p> <p className="subheader">Add a type</p>
<input type="text" {...bindTypeName} /> <input type="text" {...bindTypeName} />
<button className="interactive" disabled={!typeName} onClick={() => setPageNumber(1)}>Configure</button> <button className="interactive" disabled={!typeName} onClick={() => setPageNumber(1)}>Configure</button>
</div> </div>

View File

@ -14,8 +14,8 @@ export const SchemaViewer: FC<IProps> = ({ schema, onTypeClick }) => {
return 'Constant value:' return 'Constant value:'
} }
switch (field.type) { switch (field.type) {
case FieldTypes.type: return 'Type' case FieldTypes.type: return 'Type:'
case FieldTypes.dice: return 'Dice' case FieldTypes.dice: return 'Dice:'
default: return ''; default: return '';
} }
}, []) }, [])
@ -25,6 +25,19 @@ export const SchemaViewer: FC<IProps> = ({ schema, onTypeClick }) => {
{/* <div className="whitespace-pre-wrap">{JSON.stringify(schema, null, 2)}</div> */} {/* <div className="whitespace-pre-wrap">{JSON.stringify(schema, null, 2)}</div> */}
<div> <div>
<p className="font-bold text-lg">{schema.name}</p> <p className="font-bold text-lg">{schema.name}</p>
<hr />
<p className="font-bold italic">Templates</p>
<ul>
{Object.entries(schema.templates).map(([templateKey, template]) => (
<li>
<p className="font-bold">{templateKey}</p>
<p className="font-thin text-xs">{template.type}</p>
{template.publishable && <p className="font-thin text-xs">This template can create publications</p>}
</li>
))}
</ul>
<hr />
<p className="font-bold italic">Types</p>
<ul className="rounded-lg overflow-hidden"> <ul className="rounded-lg overflow-hidden">
{Object.entries(schema.types).map(([typeKey, type]) => ( {Object.entries(schema.types).map(([typeKey, type]) => (
<li <li

View File

@ -0,0 +1,34 @@
import { FC, useCallback } from 'react'
import { Template } from '../../types/schema';
import { useObjectStateWrapper } from '../../hooks/useObjectState';
interface IProps {
templateKey: string;
update: (arg0: string, arg1: Template) => void;
template: Template
}
export const TemplateEditor: FC<IProps> = ({ templateKey, update, template }) => {
const updateTemplate = useCallback((t: Template | ((arg: Template) => Template)) => {
update(templateKey, typeof t === 'function' ? t(template) : t)
}, [templateKey, update, template])
const { bindProperty, bindPropertyCheck } = useObjectStateWrapper(template, updateTemplate)
return (
<li>
<p className="font-bold">{templateKey}</p>
<div className="flex gap-4">
<label className="w-min">
Type
<input type="text" {...bindProperty('type')} />
</label>
<label>
<input type="checkbox" {...bindPropertyCheck('publishable')} />
Can create publications
</label>
</div>
</li>
)
}

View File

@ -31,8 +31,9 @@ export const TypeEditor: FCC<IProps> = ({ saveType, name, type: passedType }) =>
isConstant: false, isConstant: false,
limit: 1, limit: 1,
} }
}) });
}, [propertyName, updateType]); resetPropertyName();
}, [propertyName, updateType, resetPropertyName]);
const updateField = useCallback((k: keyof typeof type) => (field: FieldType) => { const updateField = useCallback((k: keyof typeof type) => (field: FieldType) => {
updateType({ [k]: field }) updateType({ [k]: field })

View File

@ -0,0 +1,70 @@
import { FieldTypes, TypeType } from '../types/schema';
export const TEMPLATE_TYPES: Record<string, TypeType> = {
section: {
name: {
isConstant: false,
limit: 1,
type: FieldTypes.text,
value: ''
},
body: {
isConstant: false,
limit: 0,
type: FieldTypes['long text'],
value: ''
},
},
steps: {
steps: {
isConstant: false,
limit: 0,
type: FieldTypes.type,
value: 'section'
}
},
image: {
name: {
isConstant: false,
limit: 1,
type: FieldTypes.text,
value: ''
},
link: {
isConstant: false,
limit: 1,
type: FieldTypes.text,
value: ''
},
},
list: {
items: {
isConstant: false,
limit: 0,
type: FieldTypes['long text'],
value: ''
}
},
tableRow: {
columns: {
isConstant: false,
limit: 0,
type: FieldTypes.any,
value: ''
}
},
table: {
rows: {
isConstant: false,
limit: 0,
type: FieldTypes.type,
value: 'tableRow'
},
header: {
isConstant: false,
limit: 1,
type: FieldTypes.type,
value: 'tableRow'
}
}
};

View File

@ -3,5 +3,5 @@ import { Schema } from '../../types/schema';
export const SchemaEditAtom = atom<Schema>({ export const SchemaEditAtom = atom<Schema>({
key: 'schema-edit', key: 'schema-edit',
default: {name: '', types: {}} default: {name: '', types: {}, templates: {}}
}); });

View File

@ -19,7 +19,7 @@ export const GameSystemsService = {
return { return {
status: 404, status: 404,
json: async () => ({name:'', types: {}}) json: async () => ({name:'', types: {}, templates: {}})
} }
} }
} }

View File

@ -1,5 +1,5 @@
export type MetadataType = { export type MetadataType = {
[key: string]: string [key: string]: string;
} }
export type FieldType = { export type FieldType = {
@ -11,9 +11,15 @@ export type FieldType = {
export type TypeType = Record<string, FieldType>; export type TypeType = Record<string, FieldType>;
export type Template = {
type: string;
publishable: boolean;
}
export type Schema = { export type Schema = {
name: string; name: string;
types: Record<string, TypeType> templates: Record<string, Template>;
types: Record<string, TypeType>;
} }
export enum FieldTypes { export enum FieldTypes {
@ -22,7 +28,8 @@ export enum FieldTypes {
'long text' = 'long text', 'long text' = 'long text',
checkbox = 'checkbox', checkbox = 'checkbox',
type = '@type', type = '@type',
dice = 'dice' dice = 'dice',
any = '@select'
} }
export const fieldTypeOptions: (keyof typeof FieldTypes)[] = ['number', 'text', 'long text', 'checkbox', 'type', 'dice'] export const fieldTypeOptions: (keyof typeof FieldTypes)[] = ['number', 'text', 'long text', 'checkbox', 'type', 'dice']