Refactor inputs
This commit is contained in:
parent
7f18a66a8b
commit
f81d8674c0
|
|
@ -4,7 +4,7 @@
|
|||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "bun build/server.ts",
|
||||
"start": "bun --hot build/server.ts",
|
||||
"bake": "bun build/build.ts",
|
||||
"test": "bun test"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,6 @@
|
|||
import { extractString } from "@common/utils";
|
||||
import { useCallback } from "preact/hooks";
|
||||
|
||||
export function useInputCallback<T>(callback: (value: string) => T, deps: any[]): ((value: string | Event) => T) {
|
||||
return useCallback((e: Event | string) => {
|
||||
if (typeof e === 'string') {
|
||||
return callback(e);
|
||||
} else {
|
||||
const { target } = e;
|
||||
if (target && 'value' in target && typeof target.value === 'string') {
|
||||
return callback(target.value);
|
||||
}
|
||||
}
|
||||
|
||||
return callback('');
|
||||
}, deps);
|
||||
}
|
||||
export const useInputCallback = <T>(callback: (value: string) => T, deps: any[]): ((value: string | Event) => T) => (
|
||||
useCallback((e: Event | string) => callback(extractString(e)), deps)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,11 @@
|
|||
import { extractString } from "@common/utils";
|
||||
import { useCallback, useState } from "preact/hooks";
|
||||
|
||||
export const useInputState = (defaultValue = ''): [string, (e: Event | string) => void] => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
|
||||
const handleInput = useCallback((e: Event | string) => {
|
||||
if (typeof e === 'string') {
|
||||
setValue(e);
|
||||
} else {
|
||||
const { target } = e;
|
||||
if (target && 'value' in target && typeof target.value === 'string') {
|
||||
setValue(target.value);
|
||||
} else if (target instanceof HTMLElement) {
|
||||
setValue(target.innerHTML);
|
||||
}
|
||||
}
|
||||
setValue(extractString(e));
|
||||
}, []);
|
||||
|
||||
return [value, handleInput];
|
||||
|
|
|
|||
|
|
@ -160,3 +160,17 @@ export const formatTime = (seconds: number): string => {
|
|||
parts.push(timeStr);
|
||||
return parts.join(' ');
|
||||
};
|
||||
|
||||
export const extractString = (e: Event | string): string => {
|
||||
if (typeof e === 'string') {
|
||||
return e;
|
||||
} else {
|
||||
const { target } = e;
|
||||
if (target && 'value' in target && typeof target.value === 'string') {
|
||||
return target.value;
|
||||
} else if (target instanceof HTMLElement) {
|
||||
return target.innerHTML;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { useState } from "preact/hooks";
|
|||
import styles from '../../assets/character-editor.module.css';
|
||||
import { CharacterRole, useAppState, type Character } from "../../contexts/state";
|
||||
import LLM from "../../utils/llm";
|
||||
import { extractString } from "@common/utils";
|
||||
|
||||
export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
||||
const { currentWorld, currentStory, mergedCharacters, dispatch, connection, model } = useAppState();
|
||||
|
|
@ -38,7 +39,10 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleEditCharacter = (characterId: string, field: keyof Character, value: any) => {
|
||||
const handleEditCharacter = (characterId: string, field: keyof Character, value: string[] | string | Event) => {
|
||||
if (!Array.isArray(value)) {
|
||||
value = extractString(value);
|
||||
}
|
||||
dispatch({
|
||||
type: 'EDIT_CHARACTER',
|
||||
worldId,
|
||||
|
|
@ -71,14 +75,14 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
setNewRelation({ ...newRelation, [characterId]: { name: '', relation: '' } });
|
||||
};
|
||||
|
||||
const handleEditRelation = (characterId: string, targetName: string, field: 'name' | 'relation', value: string) => {
|
||||
const handleEditRelation = (characterId: string, targetName: string, field: 'name' | 'relation', value: string | Event) => {
|
||||
dispatch({
|
||||
type: 'EDIT_CHARACTER_RELATION',
|
||||
worldId,
|
||||
storyId,
|
||||
characterId,
|
||||
targetName,
|
||||
updates: { [field]: value },
|
||||
updates: { [field]: extractString(value) },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +114,8 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleNewRelationChange = (characterId: string, field: 'name' | 'relation', value: string) => {
|
||||
const handleNewRelationChange = (characterId: string, field: 'name' | 'relation', value: string | Event) => {
|
||||
value = extractString(value);
|
||||
const current = newRelation[characterId] || { name: '', relation: '' };
|
||||
setNewRelation({ ...newRelation, [characterId]: { ...current, [field]: value } });
|
||||
};
|
||||
|
|
@ -153,7 +158,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.nameInput}
|
||||
value={character.name}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'name', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'name', e)}
|
||||
onFocus={(e) => e.currentTarget.select()}
|
||||
placeholder="Character name"
|
||||
/>
|
||||
|
|
@ -191,7 +196,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
<select
|
||||
class={styles.select}
|
||||
value={character.role}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'role', e.currentTarget.value as CharacterRole)}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'role', e)}
|
||||
>
|
||||
{Object.entries(CharacterRole)
|
||||
.filter(([, value]) => typeof value === 'string')
|
||||
|
|
@ -209,7 +214,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
autoLines
|
||||
class={styles.textarea}
|
||||
value={character.description}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'description', e.currentTarget.textContent)}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'description', e)}
|
||||
placeholder="Full character description..."
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -228,7 +233,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
<textarea
|
||||
class={styles.textarea}
|
||||
value={character.shortDescription}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'shortDescription', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditCharacter(character.id, 'shortDescription', e)}
|
||||
placeholder="Brief description (one line)..."
|
||||
rows={1}
|
||||
/>
|
||||
|
|
@ -242,7 +247,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.addInputField}
|
||||
value={newNickname[character.id] || ''}
|
||||
onInput={(e) => setNewNickname({ ...newNickname, [character.id]: e.currentTarget.value })}
|
||||
onInput={(e) => setNewNickname({ ...newNickname, [character.id]: extractString(e) })}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
|
|
@ -284,7 +289,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
<select
|
||||
class={styles.addInputField}
|
||||
value={(newRelation[character.id]?.name) || ''}
|
||||
onInput={(e) => handleNewRelationChange(character.id, 'name', e.currentTarget.value)}
|
||||
onInput={(e) => handleNewRelationChange(character.id, 'name', e)}
|
||||
>
|
||||
<option value="" disabled>Select character</option>
|
||||
{mergedCharacters
|
||||
|
|
@ -297,7 +302,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.addInputField}
|
||||
value={(newRelation[character.id]?.relation) || ''}
|
||||
onInput={(e) => handleNewRelationChange(character.id, 'relation', e.currentTarget.value)}
|
||||
onInput={(e) => handleNewRelationChange(character.id, 'relation', e)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
|
|
@ -323,7 +328,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
<select
|
||||
class={styles.relationInput}
|
||||
value={rel.name}
|
||||
onInput={(e) => handleEditRelation(character.id, rel.name, 'name', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditRelation(character.id, rel.name, 'name', e)}
|
||||
>
|
||||
{mergedCharacters
|
||||
.filter(c => c.id !== character.id)
|
||||
|
|
@ -335,7 +340,7 @@ export const CharacterEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.relationInput}
|
||||
value={rel.relation}
|
||||
onInput={(e) => handleEditRelation(character.id, rel.name, 'relation', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditRelation(character.id, rel.name, 'relation', e)}
|
||||
placeholder="Relationship"
|
||||
/>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ContentEditable } from "@common/components/ContentEditable";
|
||||
import { extractString } from "@common/utils";
|
||||
import { useState } from "preact/hooks";
|
||||
import styles from '../../assets/location-editor.module.css';
|
||||
import { LocationScale, useAppState, type Location } from "../../contexts/state";
|
||||
|
|
@ -33,13 +34,13 @@ export const LocationEditor = ({ visible }: { visible: boolean }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleEditLocation = (locationId: string, field: keyof Location, value: any) => {
|
||||
const handleEditLocation = (locationId: string, field: keyof Location, value: string | Event) => {
|
||||
dispatch({
|
||||
type: 'EDIT_LOCATION',
|
||||
worldId,
|
||||
storyId,
|
||||
locationId,
|
||||
updates: { [field]: value },
|
||||
updates: { [field]: extractString(value) },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ export const LocationEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.nameInput}
|
||||
value={location.name}
|
||||
onInput={(e) => handleEditLocation(location.id, 'name', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditLocation(location.id, 'name', e)}
|
||||
onFocus={(e) => e.currentTarget.select()}
|
||||
placeholder="Location name"
|
||||
/>
|
||||
|
|
@ -128,7 +129,7 @@ export const LocationEditor = ({ visible }: { visible: boolean }) => {
|
|||
<select
|
||||
class={styles.select}
|
||||
value={location.scale}
|
||||
onInput={(e) => handleEditLocation(location.id, 'scale', e.currentTarget.value as LocationScale)}
|
||||
onInput={(e) => handleEditLocation(location.id, 'scale', e)}
|
||||
>
|
||||
{Object.values(LocationScale).map((option) => (
|
||||
<option key={option} value={option}>
|
||||
|
|
@ -144,7 +145,7 @@ export const LocationEditor = ({ visible }: { visible: boolean }) => {
|
|||
autoLines
|
||||
class={styles.textarea}
|
||||
value={location.description}
|
||||
onInput={(e) => handleEditLocation(location.id, 'description', e.currentTarget.textContent)}
|
||||
onInput={(e) => handleEditLocation(location.id, 'description', e)}
|
||||
placeholder="Full location description..."
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -163,7 +164,7 @@ export const LocationEditor = ({ visible }: { visible: boolean }) => {
|
|||
<textarea
|
||||
class={styles.textarea}
|
||||
value={location.shortDescription}
|
||||
onInput={(e) => handleEditLocation(location.id, 'shortDescription', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditLocation(location.id, 'shortDescription', e)}
|
||||
placeholder="Brief description (one line)..."
|
||||
rows={1}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { ContentEditable } from "@common/components/ContentEditable";
|
||||
import { useInputState } from "@common/hooks/useInputState";
|
||||
import { extractString } from "@common/utils";
|
||||
import { useState } from "preact/hooks";
|
||||
import styles from '../../assets/lore-editor.module.css';
|
||||
import { useAppState, type LoreEntry } from "../../contexts/state";
|
||||
|
|
@ -6,7 +8,7 @@ import { useAppState, type LoreEntry } from "../../contexts/state";
|
|||
export const LoreEditor = ({ visible }: { visible: boolean }) => {
|
||||
const { currentWorld, currentStory, dispatch } = useAppState();
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [newTitle, setNewTitle] = useState('');
|
||||
const [newTitle, setNewTitle] = useInputState('');
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null);
|
||||
|
||||
if (!currentWorld || !visible) {
|
||||
|
|
@ -35,13 +37,13 @@ export const LoreEditor = ({ visible }: { visible: boolean }) => {
|
|||
setEditingId(null);
|
||||
};
|
||||
|
||||
const handleEditEntry = (entryId: string, field: keyof LoreEntry, value: string) => {
|
||||
const handleEditEntry = (entryId: string, field: keyof LoreEntry, value: string | Event) => {
|
||||
dispatch({
|
||||
type: 'EDIT_LORE_ENTRY',
|
||||
worldId,
|
||||
storyId,
|
||||
entryId,
|
||||
updates: { [field]: value },
|
||||
updates: { [field]: extractString(value) },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -87,7 +89,7 @@ export const LoreEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.titleInput}
|
||||
value={newTitle}
|
||||
onInput={(e) => setNewTitle(e.currentTarget.value)}
|
||||
onInput={setNewTitle}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
|
|
@ -116,7 +118,7 @@ export const LoreEditor = ({ visible }: { visible: boolean }) => {
|
|||
type="text"
|
||||
class={styles.titleEditInput}
|
||||
value={entry.title}
|
||||
onInput={(e) => handleEditEntry(entry.id, 'title', e.currentTarget.value)}
|
||||
onInput={(e) => handleEditEntry(entry.id, 'title', e)}
|
||||
onBlur={() => setEditingId(null)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
|
|
@ -191,7 +193,7 @@ export const LoreEditor = ({ visible }: { visible: boolean }) => {
|
|||
autoLines
|
||||
class={styles.textarea}
|
||||
value={entry.text}
|
||||
onInput={(e) => handleEditEntry(entry.id, 'text', e.currentTarget.textContent || '')}
|
||||
onInput={(e) => handleEditEntry(entry.id, 'text', e)}
|
||||
placeholder="Enter lore content..."
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue