i need to make the library dialog a datagrid.

This commit is contained in:
Jordan 2024-02-29 11:10:04 -08:00
parent ee266ea372
commit 326f3788fa
12 changed files with 113 additions and 58 deletions

View File

@ -54,11 +54,11 @@ function App() {
const promptItems = [ const promptItems = [
{ {
id: uuid4(), items: [ id: uuid4(), items: [
{ id: uuid4(), item: libItems[0] }, { id: uuid4(), item: libItems[0], score: -2 },
{ id: uuid4(), item: libItems[1] }, { id: uuid4(), item: libItems[1], score: 1 },
], op: Op.AND, ], op: Op.AND,
}, },
{ id: uuid4(), item: libItems[2] }, { id: uuid4(), item: libItems[2], score: 0, },
] as Composition; ] as Composition;
$library.set(libItems); $library.set(libItems);

View File

@ -10,7 +10,7 @@ export interface StyleProps {
export function LibraryItem(props: StyleProps) { export function LibraryItem(props: StyleProps) {
const { item, onInsertItem } = props const { item, onInsertItem } = props
return ( return (
<div> <div className={`library-item ${item.category}`}>
<Button onClick={() => onInsertItem(item)} aria-label="Add"> <Button onClick={() => onInsertItem(item)} aria-label="Add">
<AddCircleOutlineOutlinedIcon/> <AddCircleOutlineOutlinedIcon/>
</Button> </Button>

View File

@ -1,4 +1,4 @@
import { Button, FormControl, InputLabel, MenuItem, TextField } from "@material-ui/core"; import { Button, Container, FormControl, InputLabel, MenuItem, Table, TableRow, TextField } from "@material-ui/core";
import Select, { SelectChangeEvent } from '@mui/material/Select'; import Select, { SelectChangeEvent } from '@mui/material/Select';
import { Category, LibraryItem, addItemToLibrary, categoryHasName } from "../lib/prompt"; import { Category, LibraryItem, addItemToLibrary, categoryHasName } from "../lib/prompt";
import { ChangeEvent, useState } from "react"; import { ChangeEvent, useState } from "react";
@ -46,32 +46,28 @@ export function NewLibraryItem(props: NewLibraryItemProps) {
const catChoices = Object.keys(Category); const catChoices = Object.keys(Category);
return ( return (
<div> <Container className="new-item-form">
<FormControl onSubmit={handleCreateItem}> <FormControl onSubmitCapture={handleCreateItem}>
<div> <InputLabel htmlFor="new-prompt-category">Category</InputLabel>
<InputLabel htmlFor="new-prompt-category">Category</InputLabel> <Select
<Select native
native id="new-prompt-category"
id="new-prompt-category" aria-label="Prompt Item Category"
aria-label="Prompt Item Category" value={category}
value={category} onChange={(e) => handleCategoryChange(e)}
onChange={(e) => handleCategoryChange(e)} >
> {catChoices.map(cat => (
{catChoices.map(cat => ( <option value={cat} id={cat} key={cat}>{titleCase(cat)}</option>
<option value={cat} id={cat} key={cat}>{titleCase(cat)}</option> ))}
))} </Select>
</Select> {categoryHasName(category) && <>
</div> <InputLabel htmlFor="name">Name</InputLabel>
<div> <TextField aria-label="Prompt Item Name" value={name} onChange={handleNameChange} id="name" />
{categoryHasName(category) ? (<InputLabel htmlFor="name">Name</InputLabel>) : <></>} </>}
{categoryHasName(category) ? (<TextField aria-label="Prompt Item Name" value={name} onChange={handleNameChange} id="name" />) : <></>} <InputLabel htmlFor="prompt">Prompt</InputLabel>
</div> <TextField aria-label="Prompt Item Text" value={prompt} onChange={handlePromptChange} id="prompt" />
<div> <Button onClick={handleCreateItem} >Create</Button>
<InputLabel htmlFor="prompt">Prompt</InputLabel>
<TextField aria-label="Prompt Item Text" value={prompt} onChange={handlePromptChange} id="prompt" />
<Button onClick={handleCreateItem} >Create</Button>
</div>
</FormControl> </FormControl>
</div> </Container>
) )
} }

View File

@ -1,13 +1,19 @@
.nugget { .nugget.toplevel {
border: 1px solid slategray; border: 1px solid slategray;
border-radius: 10pt;
display: inline-flex; display: inline-flex;
margin: 10pt; }
.operation .nugget {
border: 1px solid white;
border-radius: 2pt;
display: inline-flex;
} }
.nugget > .text, .nugget > .score, .nugget.buttons { .nugget > .text, .nugget > .score, .nugget.buttons {
display: flex;
padding: 4pt; padding: 4pt;
margin-top: 10pt; display: inline-block;
vertical-align: text-bottom; }
.nugget .buttons button {
max-height: 12pt;
} }

View File

@ -11,6 +11,7 @@ import { useStore } from '@nanostores/react';
export interface NuggetProps extends PromptItemProps { export interface NuggetProps extends PromptItemProps {
nugget: NuggetType, nugget: NuggetType,
isTopLevel?: boolean,
} }
export default function Nugget(props: NuggetProps) { export default function Nugget(props: NuggetProps) {
@ -22,6 +23,7 @@ export default function Nugget(props: NuggetProps) {
onDrop, onDrop,
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
isTopLevel,
} = props; } = props;
const scoreDisp = nugget.score > 0 ? "+" + nugget.score : nugget.score; const scoreDisp = nugget.score > 0 ? "+" + nugget.score : nugget.score;
@ -30,6 +32,8 @@ export default function Nugget(props: NuggetProps) {
const composition = useStore($composition) const composition = useStore($composition)
const thisId = `prompt-item-${nugget.id}` const thisId = `prompt-item-${nugget.id}`
const className = isTopLevel ? 'nugget toplevel prompt-item' : 'nugget child prompt-item';
const handleOnDragStart = () => { const handleOnDragStart = () => {
onDragStart ? onDragStart(nugget) : null; onDragStart ? onDragStart(nugget) : null;
} }
@ -66,7 +70,7 @@ export default function Nugget(props: NuggetProps) {
return ( return (
<div <div
className='nugget prompt-item' className={className}
id={thisId} id={thisId}
draggable draggable
onDragStart={handleOnDragStart} onDragStart={handleOnDragStart}

View File

@ -1,16 +1,29 @@
.operation { .operation {
display: inline-block; display: inline-block;
border: 1px solid lightgray;
} }
.operation .title { .operation .title {
padding: 4pt;
margin: 4pt;
text-align: left; text-align: left;
} }
.operation .nuggets { .operation .nuggets {
display: inline-flex; display: inline-flex;
background-color: #ffdddd; border-style: solid;
border: 1px solid coral;
border-radius: 10pt; border-radius: 10pt;
}
.operation.and .nuggets, .operation.joined .nuggets {
background-color: #b983a3;
border-color: #532e44;
}
.operation.swapped .nuggets, .operation.swap .nuggets {
background-color: #8dacbd;
border-color: #4c6978;
}
.operation.blended .nuggets, .operation.blend .nuggets {
background-color: #a1af86;
border-color: #58663d;
} }

View File

@ -92,7 +92,7 @@ function Operation(props: OperationProps) {
onDragOver={handleOnDragOver} onDragOver={handleOnDragOver}
onMouseEnter={handleOnMouseEnter} onMouseEnter={handleOnMouseEnter}
onMouseOut={handleOnMouseLeave} onMouseOut={handleOnMouseLeave}
className="operation" className={`operation ${operation.op} prompt-item`}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
data-promptitem-id={operation.id} data-promptitem-id={operation.id}
> >
@ -100,7 +100,7 @@ function Operation(props: OperationProps) {
<div className="nuggets"> <div className="nuggets">
{ {
operation.items.map(nugget => { operation.items.map(nugget => {
return <Nugget nugget={nugget} /> return <Nugget nugget={nugget} isTopLevel={false} />
}) })
} }
</div> </div>

View File

@ -2,4 +2,10 @@
position: absolute; position: absolute;
right: 10pt; right: 10pt;
top: 10pt; top: 10pt;
}
.prompt-item {
margin: 4pt;
padding: 2pt;
border-radius: 5pt;
} }

View File

@ -72,7 +72,7 @@ export default function PromptComposer(props: PromptComposerProps) {
return ("op" in promptItem ? return ("op" in promptItem ?
<Operation operation={promptItem} key={key} {...callbacks} /> <Operation operation={promptItem} key={key} {...callbacks} />
: <Nugget nugget={promptItem} key={key} {...callbacks} />) : <Nugget nugget={promptItem} key={key} isTopLevel={true} {...callbacks} />)
} }
return ( return (
@ -90,9 +90,9 @@ export default function PromptComposer(props: PromptComposerProps) {
<div> <div>
{ {
slottedComposition.map((itemCol, i) => ( slottedComposition.map((itemCol, i) => (
<Stack> <>
{itemCol.map((promptItem, j) => promptItemFactory(promptItem, `item-${j}-${j}`))} {itemCol.map((promptItem, j) => promptItemFactory(promptItem, `item-${j}-${j}`))}
</Stack> </>
)) ))
} }
</div> </div>

View File

@ -1,3 +1,18 @@
.prompt-library-dialog .categories div { .prompt-library-dialog .categories div {
display: inline; display: inline;
margin: 20pt;
}
.library-item button, .library-item span {
display: inline-block;
vertical-align: sub;
}
.new-item-form {
border: 1px solid blue;
}
.hidden {
display: none;
} }

View File

@ -34,7 +34,7 @@ export function PromptLibrary(props: SimpleDialogProps) {
const [doCreate, setDoCreate] = useState(false); const [doCreate, setDoCreate] = useState(false);
const [visibleCategories, setVisibleCategories] = useState([] as Category[]); const [visibleCategories, setVisibleCategories] = useState(Object.keys(Category) as Category[]);
const handleOnAddItem = (item: LibItemType) => { const handleOnAddItem = (item: LibItemType) => {
// onAddItem(item); // onAddItem(item);
@ -44,12 +44,22 @@ export function PromptLibrary(props: SimpleDialogProps) {
onInsertItem(item); onInsertItem(item);
} }
const filterCat = (catKey: string, v: ChangeEvent<HTMLInputElement>) => { const filterCat = (catKey: string) => {
const isChecked = v.target.value === '1'; document.querySelectorAll(`.library-item`).forEach($el => {
setVisibleCategories((visibleCategories.includes(catKey as Category) && !isChecked) ? visibleCategories.filter(c => c != catKey) : [...visibleCategories, catKey as Category]); console.log("Found %x", $el);
document.querySelectorAll(`.category-${catKey}`).forEach($el => { show($el);
if (isChecked) show($el) });
else hide($el) if (visibleCategories.includes(catKey as Category)) {
setVisibleCategories(visibleCategories.filter((v) => v !== catKey));
} else {
setVisibleCategories([...visibleCategories, catKey as Category]);
}
console.log(visibleCategories);
document.querySelectorAll(`.library-item`).forEach($el => {
Object.keys(Category).forEach(c => {
if (c in visibleCategories) show($el)
else hide($el)
})
}); });
} }
@ -69,8 +79,8 @@ export function PromptLibrary(props: SimpleDialogProps) {
<div className="categories"> <div className="categories">
{categoryChoices.map(catKey => { {categoryChoices.map(catKey => {
return ( return (
<div key={catKey}> <div key={catKey} onMouseDown={() => filterCat(catKey)}>
<Checkbox onChange={v => filterCat(catKey, v)} /> <Checkbox checked={visibleCategories.includes(catKey as Category)} />
<span>{title(catKey)}</span> <span>{title(catKey)}</span>
</div> </div>
) )

View File

@ -3,9 +3,14 @@ import { $textComposition } from "../lib/prompt";
import "./TextPrompt.css"; import "./TextPrompt.css";
import { useStore } from "@nanostores/react"; import { useStore } from "@nanostores/react";
export function TextPrompt () { export function TextPrompt() {
const text = useStore($textComposition); const text = useStore($textComposition);
return ( return (
<TextareaAutosize content={text} className="text-prompt" /> <>
<TextareaAutosize
className="text-prompt"
defaultValue={text}
/>
</>
) )
} }