Default types and templates
This commit is contained in:
parent
8fb7494464
commit
a7337bd33a
@ -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]);
|
||||||
|
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
@ -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 })
|
||||||
|
70
project-warstone/src/constants/TemplateTypes.ts
Normal file
70
project-warstone/src/constants/TemplateTypes.ts
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -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: {}}
|
||||||
});
|
});
|
@ -19,7 +19,7 @@ export const GameSystemsService = {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
status: 404,
|
status: 404,
|
||||||
json: async () => ({name:'', types: {}})
|
json: async () => ({name:'', types: {}, templates: {}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user