pretty much have it to a working state now.
This commit is contained in:
parent
6da57d63bb
commit
4af0874b8f
@ -28,7 +28,9 @@
|
|||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-sortablejs": "^6.1.4",
|
||||||
"sortable": "^2.0.0",
|
"sortable": "^2.0.0",
|
||||||
|
"sortablejs": "^1.15.2",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vite": "^5.1.4",
|
"vite": "^5.1.4",
|
||||||
@ -61,6 +63,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"jest-without-globals": "^0.0.3"
|
"jest-without-globals": "^0.0.3"
|
||||||
},
|
},
|
||||||
|
@ -83,9 +83,15 @@ dependencies:
|
|||||||
react-scripts:
|
react-scripts:
|
||||||
specifier: 5.0.1
|
specifier: 5.0.1
|
||||||
version: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.56.0)(react@18.2.0)(typescript@4.9.5)
|
version: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.56.0)(react@18.2.0)(typescript@4.9.5)
|
||||||
|
react-sortablejs:
|
||||||
|
specifier: ^6.1.4
|
||||||
|
version: 6.1.4(@types/sortablejs@1.15.8)(react-dom@18.2.0)(react@18.2.0)(sortablejs@1.15.2)
|
||||||
sortable:
|
sortable:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
sortablejs:
|
||||||
|
specifier: ^1.15.2
|
||||||
|
version: 1.15.2
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^4.9.5
|
specifier: ^4.9.5
|
||||||
version: 4.9.5
|
version: 4.9.5
|
||||||
@ -106,6 +112,9 @@ devDependencies:
|
|||||||
'@types/jest':
|
'@types/jest':
|
||||||
specifier: ^27.5.2
|
specifier: ^27.5.2
|
||||||
version: 27.5.2
|
version: 27.5.2
|
||||||
|
'@types/sortablejs':
|
||||||
|
specifier: ^1.15.8
|
||||||
|
version: 1.15.8
|
||||||
'@types/uuid':
|
'@types/uuid':
|
||||||
specifier: ^9.0.8
|
specifier: ^9.0.8
|
||||||
version: 9.0.8
|
version: 9.0.8
|
||||||
@ -3838,6 +3847,9 @@ packages:
|
|||||||
'@types/node': 16.18.82
|
'@types/node': 16.18.82
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/sortablejs@1.15.8:
|
||||||
|
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
|
||||||
|
|
||||||
/@types/stack-utils@1.0.1:
|
/@types/stack-utils@1.0.1:
|
||||||
resolution: {integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==}
|
resolution: {integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -5130,6 +5142,10 @@ packages:
|
|||||||
static-extend: 0.1.2
|
static-extend: 0.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/classnames@2.3.1:
|
||||||
|
resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/clean-css@5.3.3:
|
/clean-css@5.3.3:
|
||||||
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
|
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
|
||||||
engines: {node: '>= 10.0'}
|
engines: {node: '>= 10.0'}
|
||||||
@ -11299,6 +11315,22 @@ packages:
|
|||||||
- webpack-plugin-serve
|
- webpack-plugin-serve
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-sortablejs@6.1.4(@types/sortablejs@1.15.8)(react-dom@18.2.0)(react@18.2.0)(sortablejs@1.15.2):
|
||||||
|
resolution: {integrity: sha512-fc7cBosfhnbh53Mbm6a45W+F735jwZ1UFIYSrIqcO/gRIFoDyZeMtgKlpV4DdyQfbCzdh5LoALLTDRxhMpTyXQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/sortablejs': '1'
|
||||||
|
react: '>=16.9.0'
|
||||||
|
react-dom: '>=16.9.0'
|
||||||
|
sortablejs: '1'
|
||||||
|
dependencies:
|
||||||
|
'@types/sortablejs': 1.15.8
|
||||||
|
classnames: 2.3.1
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
sortablejs: 1.15.2
|
||||||
|
tiny-invariant: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0):
|
/react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -12064,6 +12096,10 @@ packages:
|
|||||||
observable: 1.3.1
|
observable: 1.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/sortablejs@1.15.2:
|
||||||
|
resolution: {integrity: sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/source-list-map@2.0.1:
|
/source-list-map@2.0.1:
|
||||||
resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
|
resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -12647,6 +12683,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
|
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/tiny-invariant@1.2.0:
|
||||||
|
resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tiny-warning@1.0.3:
|
/tiny-warning@1.0.3:
|
||||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { ChangeEvent, useEffect } from 'react';
|
import React, { ChangeEvent, useEffect } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import PromptComposer from './components/PromptComposer';
|
import PromptComposer from './components/PromptComposer';
|
||||||
import { Box, Tabs, Tab, Typography, Container } from '@material-ui/core';
|
import { Box, Tabs, Tab, Typography, Container, Paper } from '@material-ui/core';
|
||||||
import { $composition, $library, $textComposition, Category, Composition, Library, LibraryItem, addItemToLibrary, insertIntoComposition, lassoNuggets } from './lib/prompt';
|
import { $composition, $library, $textComposition, Category, Composition, Library, LibraryItem, addItemToLibrary, insertIntoComposition, lassoNuggets } from './lib/prompt';
|
||||||
import { TextPrompt } from './components/TextPrompt';
|
import { TextPrompt } from './components/TextPrompt';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
@ -72,7 +72,7 @@ function App() {
|
|||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Paper>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
<Tabs value={value} onChange={handleChange} aria-label="prompt-selection-tabs">
|
<Tabs value={value} onChange={handleChange} aria-label="prompt-selection-tabs">
|
||||||
<Tab label="Prompt Composer" {...a11yProps(0)} aria-label="prompt-composer-tab" />
|
<Tab label="Prompt Composer" {...a11yProps(0)} aria-label="prompt-composer-tab" />
|
||||||
@ -85,7 +85,7 @@ function App() {
|
|||||||
<CustomTabPanel value={value} index={1}>
|
<CustomTabPanel value={value} index={1}>
|
||||||
<TextPrompt />
|
<TextPrompt />
|
||||||
</CustomTabPanel>
|
</CustomTabPanel>
|
||||||
</Container>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ export function NewLibraryItem(props: NewLibraryItemProps) {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
{categoryHasName(category) && (
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<InputLabel id="name-textfield-label">Name</InputLabel>
|
<InputLabel id="name-textfield-label">Name</InputLabel>
|
||||||
<TextField
|
<TextField
|
||||||
@ -71,9 +72,10 @@ export function NewLibraryItem(props: NewLibraryItemProps) {
|
|||||||
id="name-textfield"
|
id="name-textfield"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
hidden={category === Category.subject || category === Category.medium}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
)
|
||||||
|
}
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<InputLabel id="prompt-textfield-label">Prompt</InputLabel>
|
<InputLabel id="prompt-textfield-label">Prompt</InputLabel>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -9,11 +9,15 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nugget > .text, .nugget > .score, .nugget.buttons {
|
.nugget.child > .text, .nugget.child > .score, .nugget.child.buttons {
|
||||||
padding: 4pt;
|
padding: 4pt 2pt;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nugget.toplevel {
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nugget .buttons button {
|
.nugget .buttons button {
|
||||||
max-height: 12pt;
|
max-height: 14pt;
|
||||||
}
|
}
|
@ -14,7 +14,8 @@ const nugget: NuggetType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('renders Nugget component', () => {
|
test('renders Nugget component', () => {
|
||||||
render(<Nugget nugget={nugget} />);
|
render(<Nugget nugget={nugget}
|
||||||
|
onDelete={i => {}} />);
|
||||||
const textElement = screen.getByText(nugget.item.prompt);
|
const textElement = screen.getByText(nugget.item.prompt);
|
||||||
expect(textElement).toBeInTheDocument();
|
expect(textElement).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -25,6 +26,7 @@ test('increases score when button is clicked', () => {
|
|||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
<Nugget
|
<Nugget
|
||||||
nugget={nugget}
|
nugget={nugget}
|
||||||
|
onDelete={i => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const increaseButton = screen.getByLabelText('incScore');
|
const increaseButton = screen.getByLabelText('incScore');
|
||||||
@ -32,6 +34,7 @@ test('increases score when button is clicked', () => {
|
|||||||
rerender(
|
rerender(
|
||||||
<Nugget
|
<Nugget
|
||||||
nugget={{ ...nugget, score: nugget.score + 1 }}
|
nugget={{ ...nugget, score: nugget.score + 1 }}
|
||||||
|
onDelete={i => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
// expect(increaseScore).toHaveBeenCalledTimes(1);
|
// expect(increaseScore).toHaveBeenCalledTimes(1);
|
||||||
@ -44,6 +47,7 @@ test('decreases score when button is clicked', () => {
|
|||||||
const { rerender } = render(
|
const { rerender } = render(
|
||||||
<Nugget
|
<Nugget
|
||||||
nugget={nugget}
|
nugget={nugget}
|
||||||
|
onDelete={i => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const decreaseButton = screen.getByLabelText('decScore');
|
const decreaseButton = screen.getByLabelText('decScore');
|
||||||
@ -51,6 +55,7 @@ test('decreases score when button is clicked', () => {
|
|||||||
rerender(
|
rerender(
|
||||||
<Nugget
|
<Nugget
|
||||||
nugget={{ ...nugget, score: nugget.score - 1 }}
|
nugget={{ ...nugget, score: nugget.score - 1 }}
|
||||||
|
onDelete={i => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
// expect(decreaseScore).toHaveBeenCalledTimes(1);
|
// expect(decreaseScore).toHaveBeenCalledTimes(1);
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import { Button, ButtonGroup, Chip, Divider } from '@material-ui/core';
|
import { Button, ButtonGroup, Chip, Divider } from '@material-ui/core';
|
||||||
import React, { Component, DragEvent, useEffect, useState } from 'react';
|
import React, { Component, DragEvent, useEffect, useState } from 'react';
|
||||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
import {KeyboardArrowUp, KeyboardArrowDown} from '@mui/icons-material';
|
||||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
||||||
import { $composition, Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore, togglePromptItemMute } from '../lib/prompt';
|
import { $composition, Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore, togglePromptItemMute } from '../lib/prompt';
|
||||||
import "./Nugget.css";
|
import "./Nugget.css";
|
||||||
import "./PromptItem.css"
|
import "./PromptItem.css"
|
||||||
import { $sourceItem, cancelDrop, completeDrop, isPromptItemDropTarget, startDrag, startHoverOver } from '../store/prompt-dnd';
|
import { $sourceItem, cancelDrop, completeDrop, isPromptItemDropTarget, startDrag, startHoverOver } from '../store/prompt-dnd';
|
||||||
import { PromptItemProps } from './PromptItem';
|
import { PromptItemProps } from './PromptItem';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { VolumeMute, VolumeOff, VolumeUp } from '@mui/icons-material';
|
import { ArrowDownward, Delete, TextDecrease, VolumeMute, VolumeOff, VolumeUp } from '@mui/icons-material';
|
||||||
|
|
||||||
export interface NuggetProps extends PromptItemProps {
|
export interface NuggetProps extends PromptItemProps {
|
||||||
nugget: NuggetType,
|
nugget: NuggetType,
|
||||||
@ -19,8 +18,10 @@ export default function Nugget(props: NuggetProps) {
|
|||||||
|
|
||||||
const { nugget,
|
const { nugget,
|
||||||
onDragStart,
|
onDragStart,
|
||||||
|
onDragEnd,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
isTopLevel,
|
isTopLevel,
|
||||||
|
onDelete,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const scoreDisp = nugget.score > 0 ? "+" + nugget.score : nugget.score;
|
const scoreDisp = nugget.score > 0 ? "+" + nugget.score : nugget.score;
|
||||||
@ -37,7 +38,12 @@ export default function Nugget(props: NuggetProps) {
|
|||||||
|
|
||||||
console.log("nugget classname: %s", className);
|
console.log("nugget classname: %s", className);
|
||||||
|
|
||||||
const handleOnDragStart = () => {
|
const handleOnDragStart = (e: DragEvent) => {
|
||||||
|
if ("checkForDrag" in window) {
|
||||||
|
if (Math.abs((window.checkForDrag as number) - e.clientX) < 5) {
|
||||||
|
return e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
onDragStart ? onDragStart(nugget) : null;
|
onDragStart ? onDragStart(nugget) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +74,29 @@ export default function Nugget(props: NuggetProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleOnDragEnd = () => {
|
const handleOnDragEnd = () => {
|
||||||
completeDrop();
|
if (onDragEnd) onDragEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
onDelete(nugget);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mouseDownCoords = (e: MouseEvent) => {
|
||||||
|
(window as any).checkForDrag = e.clientX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleIncClick = () => {
|
||||||
|
console.log("decrease %s", nugget.id);
|
||||||
|
increaseNuggetScore(nugget.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDecClick = () => {
|
||||||
|
console.log("increase %s", nugget.id);
|
||||||
|
decreaseNuggetScore(nugget.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<li
|
||||||
className={className}
|
className={className}
|
||||||
id={thisId}
|
id={thisId}
|
||||||
draggable
|
draggable
|
||||||
@ -83,16 +107,22 @@ export default function Nugget(props: NuggetProps) {
|
|||||||
onMouseOut={handleOnMouseLeave}
|
onMouseOut={handleOnMouseLeave}
|
||||||
data-promptitem-id={nugget.id}
|
data-promptitem-id={nugget.id}
|
||||||
>
|
>
|
||||||
|
{isTopLevel && (<span className='delete'>
|
||||||
|
<Button onClick={handleDelete}>
|
||||||
|
<Delete />
|
||||||
|
</Button>
|
||||||
|
</span>)
|
||||||
|
}
|
||||||
<span className='text'>{nugget.item.name || nugget.item.prompt}</span>
|
<span className='text'>{nugget.item.name || nugget.item.prompt}</span>
|
||||||
<Divider orientation="vertical" variant="middle" flexItem />
|
<Divider orientation="vertical" variant="middle" flexItem />
|
||||||
<span className='score'>{scoreDisp}</span>
|
<span className='score'>{scoreDisp}</span>
|
||||||
<span className='buttons'>
|
<span className='buttons'>
|
||||||
<ButtonGroup size="small" orientation='vertical'>
|
<ButtonGroup size="small" orientation='vertical'>
|
||||||
<Button onClick={() => increaseNuggetScore(nugget.id)} className='incScore' aria-label="incScore">
|
<Button onClick={handleIncClick} className='incScore' aria-label="incScore">
|
||||||
<KeyboardArrowUpIcon />
|
<KeyboardArrowUp />
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => decreaseNuggetScore(nugget.id)} className='decScore' aria-label='decScore'>
|
<Button onClick={handleDecClick} className='decScore' aria-label='decScore'>
|
||||||
<KeyboardArrowDownIcon />
|
<KeyboardArrowDown />
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</span>
|
</span>
|
||||||
@ -103,6 +133,6 @@ export default function Nugget(props: NuggetProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,14 +1,20 @@
|
|||||||
.operation {
|
.operation {
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operation .delete {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
.operation .title {
|
.operation .title {
|
||||||
text-align: left;
|
position: absolute;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
padding: 2pt 5pt;
|
||||||
|
transform: translate(10px, -40px)
|
||||||
}
|
}
|
||||||
|
|
||||||
.operation .nuggets {
|
.operation .nuggets {
|
||||||
display: inline-flex;
|
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-radius: 10pt;
|
border-radius: 10pt;
|
||||||
}
|
}
|
||||||
@ -27,3 +33,7 @@
|
|||||||
background-color: #a1af86;
|
background-color: #a1af86;
|
||||||
border-color: #58663d;
|
border-color: #58663d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.op-icon {
|
||||||
|
padding-top: 20pt;
|
||||||
|
}
|
@ -3,19 +3,19 @@ import React, { Children, DragEvent, ReactNode, useEffect } from 'react';
|
|||||||
import "./Operation.css";
|
import "./Operation.css";
|
||||||
import { Op } from "../lib/operator";
|
import { Op } from "../lib/operator";
|
||||||
import { v4 as randomUUID } from "uuid";
|
import { v4 as randomUUID } from "uuid";
|
||||||
import { $composition, Operation as OperationType, changeOperationOp, togglePromptItemMute } from "../lib/prompt";
|
import { $composition, Category, Operation as OperationType, changeOperationOp, togglePromptItemMute, unlassooOperation } from "../lib/prompt";
|
||||||
import Nugget from "./Nugget";
|
import Nugget from "./Nugget";
|
||||||
import { PromptItemProps } from "./PromptItem";
|
import { PromptItemProps } from "./PromptItem";
|
||||||
import { useStore } from "@nanostores/react";
|
import { useStore } from "@nanostores/react";
|
||||||
import { $sourceItem, cancelDrop, completeDrop, startHoverOver } from "../store/prompt-dnd";
|
import { $sourceItem, cancelDrop, completeDrop, startHoverOver } from "../store/prompt-dnd";
|
||||||
import { VolumeUp, VolumeOff } from "@mui/icons-material";
|
import { VolumeUp, VolumeOff, Delete, Add, RotateLeftOutlined, Shuffle, Repeat, ArrowOutward } from "@mui/icons-material";
|
||||||
|
|
||||||
interface OperationProps extends PromptItemProps {
|
interface OperationProps extends PromptItemProps {
|
||||||
operation: OperationType
|
operation: OperationType
|
||||||
}
|
}
|
||||||
|
|
||||||
function Operation(props: OperationProps) {
|
function Operation(props: OperationProps) {
|
||||||
const { operation, onDragStart, onDragOver, onDragEnd, onDrop, onMouseEnter, onMouseLeave } = props;
|
const { operation, onDragStart, onDragOver, onDragEnd, onDrop, onMouseEnter, onDelete, onMouseLeave } = props;
|
||||||
|
|
||||||
const [contextMenu, setContextMenu] = React.useState<{
|
const [contextMenu, setContextMenu] = React.useState<{
|
||||||
mouseX: number;
|
mouseX: number;
|
||||||
@ -59,7 +59,7 @@ function Operation(props: OperationProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleOnDragEnd = () => {
|
const handleOnDragEnd = () => {
|
||||||
completeDrop();
|
if (onDragEnd) onDragEnd();
|
||||||
}
|
}
|
||||||
const handleContextMenu = (event: React.MouseEvent) => {
|
const handleContextMenu = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -80,6 +80,10 @@ function Operation(props: OperationProps) {
|
|||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
onDelete(operation);
|
||||||
|
}
|
||||||
|
|
||||||
const changeOperator = (opV: string) => {
|
const changeOperator = (opV: string) => {
|
||||||
changeOperationOp(operation.id, opV as Op);
|
changeOperationOp(operation.id, opV as Op);
|
||||||
handleClose();
|
handleClose();
|
||||||
@ -94,9 +98,23 @@ function Operation(props: OperationProps) {
|
|||||||
|
|
||||||
console.log("operation classname: %s", className);
|
console.log("operation classname: %s", className);
|
||||||
|
|
||||||
|
const handleUngroup = () => {
|
||||||
|
unlassooOperation(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCategoryIcon = () => {
|
||||||
|
return {
|
||||||
|
[Op.AND]: (<Add />),
|
||||||
|
[Op.JOINED]: (<Add />),
|
||||||
|
[Op.SWAP]: (<Shuffle />),
|
||||||
|
[Op.SWAPPED]: (<Shuffle />),
|
||||||
|
[Op.BLEND]: (<Repeat />),
|
||||||
|
[Op.BLENDED]: (<Repeat />),
|
||||||
|
}[operation.op];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<li
|
||||||
draggable
|
draggable
|
||||||
onDragStart={handleOnDragStart}
|
onDragStart={handleOnDragStart}
|
||||||
onDragEnd={handleOnDragEnd}
|
onDragEnd={handleOnDragEnd}
|
||||||
@ -107,11 +125,21 @@ function Operation(props: OperationProps) {
|
|||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
data-promptitem-id={operation.id}
|
data-promptitem-id={operation.id}
|
||||||
>
|
>
|
||||||
|
<span className='delete'>
|
||||||
|
<Button onClick={handleDelete}>
|
||||||
|
<Delete />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
<div className="title">{operation.op}</div>
|
<div className="title">{operation.op}</div>
|
||||||
<div className="nuggets">
|
<div className="nuggets">
|
||||||
{
|
{
|
||||||
operation.items.map(nugget => {
|
operation.items.map((nugget, i) => {
|
||||||
return <Nugget nugget={nugget} isTopLevel={false} />
|
return (
|
||||||
|
<>
|
||||||
|
<Nugget nugget={nugget} isTopLevel={false} onDelete={i => { }} />
|
||||||
|
{i < operation.items.length-1 && (<span className="op-icon">{getCategoryIcon()}</span>)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -135,8 +163,11 @@ function Operation(props: OperationProps) {
|
|||||||
<MenuItem onClick={() => changeOperator(v)}>{v}</MenuItem>
|
<MenuItem onClick={() => changeOperator(v)}>{v}</MenuItem>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
<MenuItem onClick={handleUngroup}>
|
||||||
|
Ungroup
|
||||||
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
.add-button {
|
.composer-main {
|
||||||
position: absolute;
|
padding-top: 30pt;
|
||||||
right: 10pt;
|
border-top: 1px solid black;
|
||||||
top: 10pt;
|
margin: 10pt;
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-item {
|
|
||||||
margin: 4pt;
|
|
||||||
padding: 2pt;
|
|
||||||
border-radius: 5pt;
|
|
||||||
}
|
}
|
@ -1,17 +1,18 @@
|
|||||||
import { Button } from '@material-ui/core';
|
import { Box, Button, ButtonGroup, Container, Paper, Snackbar, Typography } from '@material-ui/core';
|
||||||
import Masonry from '@mui/lab/Masonry';
|
import Masonry from '@mui/lab/Masonry';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import "./PromptComposer.css";
|
import "./PromptComposer.css";
|
||||||
import { PromptLibrary } from './PromptLibrary';
|
import { PromptLibrary } from './PromptLibrary';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { $composition, $library, $slottedComposition, LibraryItem, PromptItem, addToOperation, insertIntoComposition, itemIsNugget, itemIsOperation, lassoNuggets, Composition } from '../lib/prompt';
|
import { $composition, $library, $slottedComposition, LibraryItem, PromptItem, addToOperation, insertIntoComposition, itemIsNugget, itemIsOperation, lassoNuggets, Composition, _setComposition, removeItemFromLibrary, removeFromComposition, removeNuggetFromOperation } from '../lib/prompt';
|
||||||
import { Category } from '@mui/icons-material';
|
import { BackHand, Book, Category, DragHandle, LibraryBooks, MouseSharp, Score, Sort } from '@mui/icons-material';
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
import Nugget from './Nugget';
|
import Nugget from './Nugget';
|
||||||
import { Stack } from '@mui/material';
|
import { Stack, ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||||
import { Op, Operation } from './Operation';
|
import { Op, Operation } from './Operation';
|
||||||
import { PromptItemProps } from './PromptItem';
|
import { EditorMode, PromptItemProps } from './PromptItem';
|
||||||
import { $dragDropState, $dropCandidate, $isDragInProgress, $sourceItem, completeDrop, endHoverOver, startDrag, startHoverOver } from '../store/prompt-dnd';
|
import { ReactSortable } from "react-sortablejs";
|
||||||
|
import { $dragDropState, $dropCandidate, $isDragInProgress, $sourceItem, CategoryMismatchError, completeDrop, endHoverOver, startDrag, startHoverOver } from '../store/prompt-dnd';
|
||||||
|
|
||||||
export interface PromptComposerProps {
|
export interface PromptComposerProps {
|
||||||
|
|
||||||
@ -21,11 +22,11 @@ export default function PromptComposer(props: PromptComposerProps) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
if (!open) { setOpen(true); }
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
if (open) { setOpen(false); }
|
||||||
};
|
};
|
||||||
|
|
||||||
const composition = useStore($composition);
|
const composition = useStore($composition);
|
||||||
@ -38,6 +39,31 @@ export default function PromptComposer(props: PromptComposerProps) {
|
|||||||
console.log(composition)
|
console.log(composition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOnDeleteItem = (item: LibraryItem) => {
|
||||||
|
removeItemFromLibrary(item);
|
||||||
|
// also remove from the prompts
|
||||||
|
composition.filter(c => {
|
||||||
|
return (!("op" in c)) && c.item.id === item.id
|
||||||
|
}).forEach((c) => {
|
||||||
|
removeFromComposition(c);
|
||||||
|
});
|
||||||
|
// and from any operation
|
||||||
|
composition.filter(c => {
|
||||||
|
return "op" in c && c.items.find(o => o.item.id === item.id);
|
||||||
|
}).forEach((o) => {
|
||||||
|
if (!("op" in o)) return;
|
||||||
|
o.items.forEach((i) => {
|
||||||
|
if (i.item.id === item.id) removeNuggetFromOperation(o, i);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [doSort, setDoSort] = useState(false);
|
||||||
|
|
||||||
|
const [editMode, setEditMode] = useState("dnd" as EditorMode);
|
||||||
|
|
||||||
|
const [error, setError] = useState(null as Error | null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param promptItem The prompt item that we're rendering
|
* @param promptItem The prompt item that we're rendering
|
||||||
@ -51,12 +77,14 @@ export default function PromptComposer(props: PromptComposerProps) {
|
|||||||
// is either a Nugget or an Operation.
|
// is either a Nugget or an Operation.
|
||||||
const callbacks = {
|
const callbacks = {
|
||||||
onDragStart: (item: PromptItem) => {
|
onDragStart: (item: PromptItem) => {
|
||||||
|
if (editMode !== "dnd") return;
|
||||||
if (itemIsNugget(promptItem)) {
|
if (itemIsNugget(promptItem)) {
|
||||||
startDrag(item);
|
startDrag(item);
|
||||||
}
|
}
|
||||||
// TODO: operation
|
// TODO: operation
|
||||||
},
|
},
|
||||||
onDrop: (item: PromptItem) => {
|
onDrop: (item: PromptItem) => {
|
||||||
|
if (editMode !== "dnd") return;
|
||||||
const dnd = useStore($dragDropState);
|
const dnd = useStore($dragDropState);
|
||||||
const isDragInProgress = useStore($isDragInProgress);
|
const isDragInProgress = useStore($isDragInProgress);
|
||||||
const dropCandidate = useStore($dropCandidate);
|
const dropCandidate = useStore($dropCandidate);
|
||||||
@ -73,14 +101,27 @@ export default function PromptComposer(props: PromptComposerProps) {
|
|||||||
}
|
}
|
||||||
completeDrop();
|
completeDrop();
|
||||||
},
|
},
|
||||||
onDragEnd: (item: PromptItem) => {
|
onDragEnd: () => {
|
||||||
|
try {
|
||||||
|
completeDrop();
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof CategoryMismatchError) {
|
||||||
|
setError(err);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onMouseEnter: (item: PromptItem) => {
|
onMouseEnter: (item: PromptItem) => {
|
||||||
},
|
},
|
||||||
onMouseLeave: (item: PromptItem) => {
|
onMouseLeave: (item: PromptItem) => {
|
||||||
|
if (editMode !== "dnd") return;
|
||||||
endHoverOver();
|
endHoverOver();
|
||||||
},
|
},
|
||||||
|
onDelete: (item: PromptItem) => {
|
||||||
|
if (editMode !== "dnd") return;
|
||||||
|
removeFromComposition(item);
|
||||||
|
},
|
||||||
} as PromptItemProps;
|
} as PromptItemProps;
|
||||||
|
|
||||||
return ("op" in promptItem ?
|
return ("op" in promptItem ?
|
||||||
@ -88,23 +129,73 @@ export default function PromptComposer(props: PromptComposerProps) {
|
|||||||
: <Nugget nugget={promptItem} key={key} isTopLevel={true} {...callbacks} />)
|
: <Nugget nugget={promptItem} key={key} isTopLevel={true} {...callbacks} />)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setComposition(c: Composition) {
|
||||||
|
console.log("updated composition: %x", c)
|
||||||
|
_setComposition(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetEditMode = (
|
||||||
|
event: React.MouseEvent<HTMLElement>,
|
||||||
|
mode: EditorMode | null,
|
||||||
|
) => {
|
||||||
|
if (mode) setEditMode(mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleErrorSnackbarClose = () => {
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box>
|
||||||
<div>
|
<Container>
|
||||||
<Button className="add-button" onClick={handleClickOpen}>
|
<Stack direction="row" style={{ padding: "4pt" }} >
|
||||||
<AddIcon />
|
<ButtonGroup>
|
||||||
|
<Button onClick={handleClickOpen} >
|
||||||
|
<LibraryBooks />
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ToggleButtonGroup
|
||||||
|
value={editMode}
|
||||||
|
exclusive
|
||||||
|
onChange={handleSetEditMode}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<ToggleButton value="dnd" aria-label="drag-n-drop" size="large" style={{ padding: "4pt" }}>
|
||||||
|
<BackHand />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton value="sort" aria-label='score' style={{ padding: "4pt" }}>
|
||||||
|
<Sort />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
<Typography>
|
||||||
|
<strong>{editMode} Mode</strong> enabled
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
<Container className="composer-main">
|
||||||
|
{editMode == "sort" ? (
|
||||||
|
<ReactSortable list={composition} setList={setComposition}>
|
||||||
|
{composition.map(c => promptItemFactory(c, `item-${c.id}`))}
|
||||||
|
</ReactSortable>
|
||||||
|
) : composition.map(c => promptItemFactory(c, `item-${c.id}`))
|
||||||
|
}
|
||||||
|
</Container>
|
||||||
<PromptLibrary
|
<PromptLibrary
|
||||||
open={open}
|
open={open}
|
||||||
|
onDeleteItem={handleOnDeleteItem}
|
||||||
onInsertItem={handleOnInsertItem}
|
onInsertItem={handleOnInsertItem}
|
||||||
onClose={handleClose}
|
onClose={handleClose} />
|
||||||
></PromptLibrary>
|
<Snackbar
|
||||||
</Button>
|
message={error?.message}
|
||||||
</div>
|
open={error !== null}
|
||||||
<div>
|
autoHideDuration={6000}
|
||||||
{
|
onClose={handleErrorSnackbarClose}
|
||||||
composition.map(c => promptItemFactory(c, `item-${c.id}`))
|
anchorOrigin={{
|
||||||
}
|
vertical: "top",
|
||||||
</div>
|
horizontal: "center",
|
||||||
</div>
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -7,5 +7,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.prompt-item.muted, .operation.muted .nugget {
|
.prompt-item.muted, .operation.muted .nugget {
|
||||||
color: gray;
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-item {
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: 5pt;
|
||||||
|
padding: 2pt 0pt;
|
||||||
|
margin: 2pt 4pt;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-item.toplevel {
|
||||||
|
height: 55px;
|
||||||
}
|
}
|
@ -14,9 +14,12 @@ import {PromptItem as PIType} from "../lib/prompt"
|
|||||||
|
|
||||||
export interface PromptItemProps {
|
export interface PromptItemProps {
|
||||||
onDragStart?: (item : PIType) => void,
|
onDragStart?: (item : PIType) => void,
|
||||||
onDragEnd?: (item: PIType) => void,
|
onDragEnd?: () => void,
|
||||||
onDragOver?: (item: PIType) => void,
|
onDragOver?: (item: PIType) => void,
|
||||||
onDrop?: (item : PIType) => void,
|
onDrop?: (item : PIType) => void,
|
||||||
onMouseEnter?: (item : PIType) => void,
|
onMouseEnter?: (item : PIType) => void,
|
||||||
onMouseLeave?: (item : PIType) => void,
|
onMouseLeave?: (item : PIType) => void,
|
||||||
|
onDelete : (item : PIType) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EditorMode = "dnd" | "sort" | "score"
|
@ -25,6 +25,7 @@ const mockItem: LibItemType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mockOnClose = jest.fn();
|
const mockOnClose = jest.fn();
|
||||||
|
const mockOnDeleteItem = jest.fn();
|
||||||
|
|
||||||
const mockOpen: boolean = true;
|
const mockOpen: boolean = true;
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ const mockProps: SimpleDialogProps = {
|
|||||||
open: mockOpen,
|
open: mockOpen,
|
||||||
onInsertItem: mockOnAddItem,
|
onInsertItem: mockOnAddItem,
|
||||||
onClose: mockOnClose,
|
onClose: mockOnClose,
|
||||||
|
onDeleteItem: mockOnDeleteItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockLibrary: LibraryType = [
|
const mockLibrary: LibraryType = [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Dialog, DialogTitle } from "@material-ui/core";
|
import { Button, Dialog, DialogActions, DialogTitle } from "@material-ui/core";
|
||||||
import { LibraryItem as LibItemType, $library, Category, LibraryItem, insertIntoComposition } from "../lib/prompt";
|
import { LibraryItem as LibItemType, $library, Category, LibraryItem, insertIntoComposition } from "../lib/prompt";
|
||||||
import { MouseEvent, useMemo, useState } from "react";
|
import { MouseEvent, useMemo, useState } from "react";
|
||||||
import { useStore } from "@nanostores/react";
|
import { useStore } from "@nanostores/react";
|
||||||
@ -6,18 +6,19 @@ import { NewLibraryItem } from "./NewLibraryItem";
|
|||||||
import { DataGrid, GridApi, GridColDef, GridColTypeDef } from '@mui/x-data-grid';
|
import { DataGrid, GridApi, GridColDef, GridColTypeDef } from '@mui/x-data-grid';
|
||||||
import "./PromptLibrary.css"
|
import "./PromptLibrary.css"
|
||||||
import { title } from "../lib/util";
|
import { title } from "../lib/util";
|
||||||
import { Add } from "@mui/icons-material";
|
import { Add, Delete } from "@mui/icons-material";
|
||||||
|
|
||||||
export interface SimpleDialogProps {
|
export interface SimpleDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void,
|
onClose: () => void,
|
||||||
// onAddItem: (item: LibItemType) => void,
|
// onAddItem: (item: LibItemType) => void,
|
||||||
onInsertItem: (item: LibItemType) => void,
|
onInsertItem: (item: LibItemType) => void,
|
||||||
|
onDeleteItem: (item: LibItemType) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function PromptLibrary(props: SimpleDialogProps) {
|
export function PromptLibrary(props: SimpleDialogProps) {
|
||||||
const { open, onInsertItem, onClose } = props;
|
const { open, onInsertItem, onClose, onDeleteItem } = props;
|
||||||
|
|
||||||
const library = useStore($library);
|
const library = useStore($library);
|
||||||
|
|
||||||
@ -54,11 +55,23 @@ export function PromptLibrary(props: SimpleDialogProps) {
|
|||||||
<Add />
|
<Add />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}, headerName: "",
|
||||||
},
|
},
|
||||||
{ field: 'name', headerName: 'Name', width: 150 },
|
{ field: 'name', headerName: 'Name', width: 150 },
|
||||||
{ field: 'prompt', headerName: 'Prompt', width: 250 },
|
{ field: 'prompt', headerName: 'Prompt', width: 250 },
|
||||||
{ field: 'category', headerName: 'Category', width: 150 },
|
{ field: 'category', headerName: 'Category', width: 150 },
|
||||||
|
{ field: "delete", headerName: "headerName", width: 50, renderCell: (params) => {
|
||||||
|
const handleClick = ($e: MouseEvent<any>) => {
|
||||||
|
$e.stopPropagation();
|
||||||
|
const libItem = library.find(l => l.id === params.id) as LibItemType;
|
||||||
|
if (libItem) onDeleteItem(libItem);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button onClick={handleClick}>
|
||||||
|
<Delete />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
} },
|
||||||
];
|
];
|
||||||
|
|
||||||
const rows = filteredLibrary.map(item => ({
|
const rows = filteredLibrary.map(item => ({
|
||||||
@ -72,16 +85,22 @@ export function PromptLibrary(props: SimpleDialogProps) {
|
|||||||
<Dialog
|
<Dialog
|
||||||
hideBackdrop
|
hideBackdrop
|
||||||
disableEnforceFocus
|
disableEnforceFocus
|
||||||
style={{ position: "initial", top: "30%", left: "30%", height: "fit-content", width: "fit-content" }}
|
fullWidth={true}
|
||||||
|
maxWidth="lg"
|
||||||
className="prompt-library-dialog"
|
className="prompt-library-dialog"
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
open={open}
|
open={open}
|
||||||
>
|
>
|
||||||
<DialogTitle>Prompt Library</DialogTitle>
|
<DialogTitle>Prompt Library</DialogTitle>
|
||||||
<div>
|
<div>
|
||||||
<DataGrid rows={rows} columns={columns} />
|
<DataGrid rows={rows} columns={columns} style={{display: "block", width: "fit-contents"}} />
|
||||||
</div>
|
</div>
|
||||||
<NewLibraryItem onNewCreated={handleOnNewCreated} />
|
<NewLibraryItem onNewCreated={handleOnNewCreated} />
|
||||||
|
<DialogActions>
|
||||||
|
<Button autoFocus onClick={handleClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { v4 as randomUUID, v4 as uuidv4 } from "uuid";
|
import { v4 as randomUUID, v4 as uuidv4 } from "uuid";
|
||||||
import { Op } from "./operator";
|
import { Op } from "./operator";
|
||||||
import { Atom, ReadableAtom, Store, WritableAtom, atom, computed } from "nanostores";
|
import { atom, computed } from "nanostores";
|
||||||
|
|
||||||
type Id = string;
|
type Id = string;
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ export function removeItemFromLibrary(item: LibraryItem) {
|
|||||||
$library.set($library.get().filter(i => (i.id !== item.id)));
|
$library.set($library.get().filter(i => (i.id !== item.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const $composition = atom<Composition>([])
|
export const $composition = atom<Composition>([]);
|
||||||
|
|
||||||
export function insertIntoComposition(item: LibraryItem) {
|
export function insertIntoComposition(item: LibraryItem) {
|
||||||
$composition.set([
|
$composition.set([
|
||||||
@ -91,8 +91,19 @@ export function removeFromComposition(item: PromptItem) {
|
|||||||
function nuggetDelta(nuggetId: Id, delta: number) {
|
function nuggetDelta(nuggetId: Id, delta: number) {
|
||||||
$composition.set($composition.get().map(item => {
|
$composition.set($composition.get().map(item => {
|
||||||
if ((item.id === nuggetId) && ("score" in item)) {
|
if ((item.id === nuggetId) && ("score" in item)) {
|
||||||
const o = { ...item, score: item.score + delta };
|
return { ...item, score: item.score + delta };
|
||||||
return o;
|
}
|
||||||
|
if ("op" in item) {
|
||||||
|
return {
|
||||||
|
...item, items: item.items.map(
|
||||||
|
nug => {
|
||||||
|
return {
|
||||||
|
...nug,
|
||||||
|
score: nug.score + (nuggetId === nug.id ? delta : 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@ -228,3 +239,28 @@ export function togglePromptItemMute(id: Id) {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function _setComposition(newComp: Composition) {
|
||||||
|
$composition.set(newComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeNuggetFromOperation(operation: Operation, nugget: Nugget) {
|
||||||
|
$composition.set($composition.get().map(item => {
|
||||||
|
return "op" in item ? {
|
||||||
|
...item,
|
||||||
|
items: item.items.filter(i => i.id !== nugget.id),
|
||||||
|
} : item;
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unlassooOperation(operation: Operation) {
|
||||||
|
$composition.set(
|
||||||
|
$composition.get().flatMap(item => {
|
||||||
|
if ("op" in item && item.id === operation.id) {
|
||||||
|
return item.items;
|
||||||
|
} else {
|
||||||
|
return [item];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Atom, atom, computed } from "nanostores"
|
import { Atom, atom, computed } from "nanostores"
|
||||||
import { $composition, Nugget, Operation, PromptItem, addToOperation, itemIsNugget, itemIsOperation, lassoNuggets } from "../lib/prompt";
|
import { $composition, Category, Nugget, Operation, PromptItem, addToOperation, itemIsNugget, itemIsOperation, lassoNuggets } from "../lib/prompt";
|
||||||
import { Op } from "../lib/operator";
|
import { Op } from "../lib/operator";
|
||||||
|
|
||||||
export type DropCandidate = string | string []
|
export type DropCandidate = string | string []
|
||||||
@ -55,6 +55,12 @@ export function cancelDrop() {
|
|||||||
$dragDropState.set({});
|
$dragDropState.set({});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class CategoryMismatchError extends Error {
|
||||||
|
constructor(public c1 : Category, public c2 : Category) {
|
||||||
|
super(`Cannot merge '${c1}' into '${c2}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function completeDrop() {
|
export function completeDrop() {
|
||||||
const source = $sourceItem.get();
|
const source = $sourceItem.get();
|
||||||
const target = $dropCandidate.get();
|
const target = $dropCandidate.get();
|
||||||
@ -65,7 +71,7 @@ export function completeDrop() {
|
|||||||
const c1 = nSource.item.category;
|
const c1 = nSource.item.category;
|
||||||
const c2 = nTarget.items[0].item.category;
|
const c2 = nTarget.items[0].item.category;
|
||||||
if (c1 != c2) {
|
if (c1 != c2) {
|
||||||
console.error("Category mismatch: cannot drop a %s into %s", c1, c2);
|
throw new CategoryMismatchError(c1, c2);
|
||||||
} else {
|
} else {
|
||||||
addToOperation(source.id, target.id);
|
addToOperation(source.id, target.id);
|
||||||
}
|
}
|
||||||
@ -76,7 +82,7 @@ export function completeDrop() {
|
|||||||
const c1 = nSource.item.category;
|
const c1 = nSource.item.category;
|
||||||
const c2 = nTarget.item.category;
|
const c2 = nTarget.item.category;
|
||||||
if (c1 != c2) {
|
if (c1 != c2) {
|
||||||
console.error("Category mismatch: cannot drop a %s into %s", c1, c2);
|
throw new CategoryMismatchError(c1, c2);
|
||||||
} else {
|
} else {
|
||||||
lassoNuggets(source.id, target.id, Op.AND)
|
lassoNuggets(source.id, target.id, Op.AND)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user