diff --git a/frontends/nextjs/src/components/editors/lua/blocks/BlockItem.tsx b/frontends/nextjs/src/components/editors/lua/blocks/BlockItem.tsx new file mode 100644 index 000000000..938a26451 --- /dev/null +++ b/frontends/nextjs/src/components/editors/lua/blocks/BlockItem.tsx @@ -0,0 +1,218 @@ +import type { MouseEvent } from 'react' +import { + Box, + Button, + IconButton, + MenuItem, + TextField, + Tooltip, + Typography, +} from '@mui/material' +import { + Add as AddIcon, + ArrowDownward, + ArrowUpward, + ContentCopy, + Delete as DeleteIcon, +} from '@mui/icons-material' +import type { BlockDefinition, BlockSlot, LuaBlock } from '../types' +import styles from '../LuaBlocksEditor.module.scss' + +interface BlockItemProps { + block: LuaBlock + definition: BlockDefinition + index: number + total: number + onRequestAddBlock: ( + event: MouseEvent, + target: { parentId: string | null; slot: BlockSlot } + ) => void + onMoveBlock: (blockId: string, direction: 'up' | 'down') => void + onDuplicateBlock: (blockId: string) => void + onRemoveBlock: (blockId: string) => void + onUpdateField: (blockId: string, fieldName: string, value: string) => void + renderNestedList: (blocks?: LuaBlock[]) => JSX.Element +} + +interface BlockSectionProps { + title: string + blocks: LuaBlock[] | undefined + parentId: string + slot: BlockSlot + onRequestAddBlock: ( + event: MouseEvent, + target: { parentId: string | null; slot: BlockSlot } + ) => void + renderNestedList: (blocks?: LuaBlock[]) => JSX.Element +} + +const BlockSection = ({ + title, + blocks, + parentId, + slot, + onRequestAddBlock, + renderNestedList, +}: BlockSectionProps) => ( + + + {title} + + + + {blocks && blocks.length > 0 ? ( + renderNestedList(blocks) + ) : ( + Drop blocks here to build this section. + )} + + +) + +const BlockFields = ({ + block, + definition, + onUpdateField, +}: { + block: LuaBlock + definition: BlockDefinition + onUpdateField: (blockId: string, fieldName: string, value: string) => void +}) => { + if (definition.fields.length === 0) return null + + return ( + + {definition.fields.map((field) => ( + + {field.label} + {field.type === 'select' ? ( + onUpdateField(block.id, field.name, event.target.value)} + fullWidth + variant="outlined" + InputProps={{ + sx: { backgroundColor: 'rgba(255,255,255,0.95)' }, + }} + > + {field.options?.map((option) => ( + + {option.label} + + ))} + + ) : ( + onUpdateField(block.id, field.name, event.target.value)} + placeholder={field.placeholder} + fullWidth + variant="outlined" + type={field.type === 'number' ? 'number' : 'text'} + InputProps={{ + sx: { backgroundColor: 'rgba(255,255,255,0.95)' }, + }} + /> + )} + + ))} + + ) +} + +export const BlockItem = ({ + block, + definition, + index, + total, + onRequestAddBlock, + onMoveBlock, + onDuplicateBlock, + onRemoveBlock, + onUpdateField, + renderNestedList, +}: BlockItemProps) => ( + + + {definition.label} + + + + onMoveBlock(block.id, 'up')} + disabled={index === 0} + sx={{ color: 'rgba(255,255,255,0.85)' }} + > + + + + + + + onMoveBlock(block.id, 'down')} + disabled={index === total - 1} + sx={{ color: 'rgba(255,255,255,0.85)' }} + > + + + + + + onDuplicateBlock(block.id)} + sx={{ color: 'rgba(255,255,255,0.85)' }} + > + + + + + onRemoveBlock(block.id)} + sx={{ color: 'rgba(255,255,255,0.85)' }} + > + + + + + + + + + {definition.hasChildren && ( + + )} + + {definition.hasElseChildren && ( + + )} + +) diff --git a/frontends/nextjs/src/components/editors/lua/blocks/BlockList.tsx b/frontends/nextjs/src/components/editors/lua/blocks/BlockList.tsx index afe08e023..1c4c3052b 100644 --- a/frontends/nextjs/src/components/editors/lua/blocks/BlockList.tsx +++ b/frontends/nextjs/src/components/editors/lua/blocks/BlockList.tsx @@ -1,22 +1,8 @@ import type { MouseEvent } from 'react' -import { - Box, - Button, - IconButton, - MenuItem, - TextField, - Tooltip, - Typography, -} from '@mui/material' -import { - Add as AddIcon, - ArrowDownward, - ArrowUpward, - ContentCopy, - Delete as DeleteIcon, -} from '@mui/icons-material' +import { Box } from '@mui/material' import type { BlockDefinition, BlockSlot, LuaBlock, LuaBlockType } from '../types' import styles from '../LuaBlocksEditor.module.scss' +import { BlockItem } from './BlockItem' interface BlockListProps { blocks: LuaBlock[] @@ -31,89 +17,6 @@ interface BlockListProps { onUpdateField: (blockId: string, fieldName: string, value: string) => void } -const renderBlockFields = ( - block: LuaBlock, - definition: BlockDefinition, - onUpdateField: (blockId: string, fieldName: string, value: string) => void -) => { - if (definition.fields.length === 0) return null - - return ( - - {definition.fields.map((field) => ( - - {field.label} - {field.type === 'select' ? ( - onUpdateField(block.id, field.name, event.target.value)} - fullWidth - variant="outlined" - InputProps={{ - sx: { backgroundColor: 'rgba(255,255,255,0.95)' }, - }} - > - {field.options?.map((option) => ( - - {option.label} - - ))} - - ) : ( - onUpdateField(block.id, field.name, event.target.value)} - placeholder={field.placeholder} - fullWidth - variant="outlined" - type={field.type === 'number' ? 'number' : 'text'} - InputProps={{ - sx: { backgroundColor: 'rgba(255,255,255,0.95)' }, - }} - /> - )} - - ))} - - ) -} - -const renderBlockSection = ( - title: string, - blocks: LuaBlock[] | undefined, - parentId: string | null, - slot: BlockSlot, - onRequestAddBlock: ( - event: MouseEvent, - target: { parentId: string | null; slot: BlockSlot } - ) => void, - renderBlockCard: (block: LuaBlock, index: number, total: number) => JSX.Element | null -) => ( - - - {title} - - - - {blocks && blocks.length > 0 ? ( - blocks.map((child, index) => renderBlockCard(child, index, blocks.length)) - ) : ( - Drop blocks here to build this section. - )} - - -) - export const BlockList = ({ blocks, blockDefinitionMap, @@ -123,78 +26,40 @@ export const BlockList = ({ onRemoveBlock, onUpdateField, }: BlockListProps) => { - const renderBlockCard = (block: LuaBlock, index: number, total: number) => { - const definition = blockDefinitionMap.get(block.type) - if (!definition) return null - - return ( - - - {definition.label} - - - - onMoveBlock(block.id, 'up')} - disabled={index === 0} - sx={{ color: 'rgba(255,255,255,0.85)' }} - > - - - - - - - onMoveBlock(block.id, 'down')} - disabled={index === total - 1} - sx={{ color: 'rgba(255,255,255,0.85)' }} - > - - - - - - onDuplicateBlock(block.id)} - sx={{ color: 'rgba(255,255,255,0.85)' }} - > - - - - - onRemoveBlock(block.id)} - sx={{ color: 'rgba(255,255,255,0.85)' }} - > - - - - - - {renderBlockFields(block, definition, onUpdateField)} - {definition.hasChildren && - renderBlockSection('Then', block.children, block.id, 'children', onRequestAddBlock, renderBlockCard)} - {definition.hasElseChildren && - renderBlockSection( - 'Else', - block.elseChildren, - block.id, - 'elseChildren', - onRequestAddBlock, - renderBlockCard - )} - - ) - } + const renderNestedList = (childBlocks?: LuaBlock[]) => ( + + ) return ( - {blocks.map((block, index) => renderBlockCard(block, index, blocks.length))} + {blocks.map((block, index) => { + const definition = blockDefinitionMap.get(block.type) + if (!definition) return null + + return ( + + ) + })} ) } diff --git a/frontends/nextjs/src/components/editors/lua/blocks/grouping.ts b/frontends/nextjs/src/components/editors/lua/blocks/grouping.ts new file mode 100644 index 000000000..786b6f586 --- /dev/null +++ b/frontends/nextjs/src/components/editors/lua/blocks/grouping.ts @@ -0,0 +1,20 @@ +import type { BlockCategory, BlockDefinition } from '../types' + +const createCategoryIndex = (): Record => ({ + Basics: [], + Logic: [], + Loops: [], + Data: [], + Functions: [], +}) + +export const groupBlockDefinitionsByCategory = (definitions: BlockDefinition[]) => { + const categories = createCategoryIndex() + definitions.forEach((definition) => { + categories[definition.category].push(definition) + }) + return categories +} + +export const buildBlockDefinitionMap = (definitions: BlockDefinition[]) => + new Map(definitions.map((definition) => [definition.type, definition])) diff --git a/frontends/nextjs/src/components/editors/lua/blocks/index.ts b/frontends/nextjs/src/components/editors/lua/blocks/index.ts index 33cf9167d..b9706fcc0 100644 --- a/frontends/nextjs/src/components/editors/lua/blocks/index.ts +++ b/frontends/nextjs/src/components/editors/lua/blocks/index.ts @@ -1,4 +1,4 @@ -import type { BlockCategory, BlockDefinition } from '../types' +import type { BlockDefinition } from '../types' import { basicBlocks } from './basics' import { dataBlocks } from './data' import { functionBlocks } from './functions' @@ -13,21 +13,4 @@ export const BLOCK_DEFINITIONS: BlockDefinition[] = [ ...functionBlocks, ] -const createCategoryIndex = (): Record => ({ - Basics: [], - Logic: [], - Loops: [], - Data: [], - Functions: [], -}) - -export const groupBlockDefinitionsByCategory = (definitions: BlockDefinition[]) => { - const categories = createCategoryIndex() - definitions.forEach((definition) => { - categories[definition.category].push(definition) - }) - return categories -} - -export const buildBlockDefinitionMap = (definitions: BlockDefinition[]) => - new Map(definitions.map((definition) => [definition.type, definition])) +export { buildBlockDefinitionMap, groupBlockDefinitionsByCategory } from './grouping'