Merge pull request #397 from johndoe6345789/copilot/sub-pr-246

Extract BlockSection and BlockFields from BlockItem component
This commit is contained in:
2025-12-29 18:26:16 +00:00
committed by GitHub
3 changed files with 106 additions and 112 deletions

View File

@@ -0,0 +1,55 @@
import { Box, MenuItem, TextField, Typography } from '@mui/material'
import type { BlockDefinition, LuaBlock } from '../types'
import styles from '../LuaBlocksEditor.module.scss'
interface BlockFieldsProps {
block: LuaBlock
definition: BlockDefinition
onUpdateField: (blockId: string, fieldName: string, value: string) => void
}
export const BlockFields = ({ block, definition, onUpdateField }: BlockFieldsProps) => {
if (definition.fields.length === 0) return null
return (
<Box className={styles.blockFields}>
{definition.fields.map((field) => (
<Box key={field.name}>
<Typography className={styles.blockFieldLabel}>{field.label}</Typography>
{field.type === 'select' ? (
<TextField
select
size="small"
value={block.fields[field.name]}
onChange={(event) => onUpdateField(block.id, field.name, event.target.value)}
fullWidth
variant="outlined"
InputProps={{
sx: { backgroundColor: 'rgba(255,255,255,0.95)' },
}}
>
{field.options?.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
) : (
<TextField
size="small"
value={block.fields[field.name]}
onChange={(event) => 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)' },
}}
/>
)}
</Box>
))}
</Box>
)
}

View File

@@ -1,21 +1,9 @@
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, IconButton, Tooltip, Typography } from '@mui/material'
import { ArrowDownward, ArrowUpward, ContentCopy, Delete as DeleteIcon } from '@mui/icons-material'
import type { BlockDefinition, BlockSlot, LuaBlock } from '../types'
import { BlockSection } from './BlockSection'
import { BlockFields } from './BlockFields'
import styles from '../LuaBlocksEditor.module.scss'
interface BlockItemProps {
@@ -34,102 +22,6 @@ interface BlockItemProps {
renderNestedList: (blocks?: LuaBlock[]) => JSX.Element
}
interface BlockSectionProps {
title: string
blocks: LuaBlock[] | undefined
parentId: string
slot: BlockSlot
onRequestAddBlock: (
event: MouseEvent<HTMLElement>,
target: { parentId: string | null; slot: BlockSlot }
) => void
renderNestedList: (blocks?: LuaBlock[]) => JSX.Element
}
const BlockSection = ({
title,
blocks,
parentId,
slot,
onRequestAddBlock,
renderNestedList,
}: BlockSectionProps) => (
<Box className={styles.blockSection}>
<Box className={styles.blockSectionHeader}>
<Typography className={styles.blockSectionTitle}>{title}</Typography>
<Button
size="small"
variant="contained"
onClick={(event) => onRequestAddBlock(event, { parentId, slot })}
startIcon={<AddIcon fontSize="small" />}
>
Add block
</Button>
</Box>
<Box className={styles.blockSectionBody}>
{blocks && blocks.length > 0 ? (
renderNestedList(blocks)
) : (
<Box className={styles.blockEmpty}>Drop blocks here to build this section.</Box>
)}
</Box>
</Box>
)
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 (
<Box className={styles.blockFields}>
{definition.fields.map((field) => (
<Box key={field.name}>
<Typography className={styles.blockFieldLabel}>{field.label}</Typography>
{field.type === 'select' ? (
<TextField
select
size="small"
value={block.fields[field.name]}
onChange={(event) => onUpdateField(block.id, field.name, event.target.value)}
fullWidth
variant="outlined"
InputProps={{
sx: { backgroundColor: 'rgba(255,255,255,0.95)' },
}}
>
{field.options?.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
) : (
<TextField
size="small"
value={block.fields[field.name]}
onChange={(event) => 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)' },
}}
/>
)}
</Box>
))}
</Box>
)
}
export const BlockItem = ({
block,
definition,

View File

@@ -0,0 +1,47 @@
import type { MouseEvent } from 'react'
import { Box, Button, Typography } from '@mui/material'
import { Add as AddIcon } from '@mui/icons-material'
import type { BlockSlot, LuaBlock } from '../types'
import styles from '../LuaBlocksEditor.module.scss'
interface BlockSectionProps {
title: string
blocks: LuaBlock[] | undefined
parentId: string
slot: BlockSlot
onRequestAddBlock: (
event: MouseEvent<HTMLElement>,
target: { parentId: string | null; slot: BlockSlot }
) => void
renderNestedList: (blocks?: LuaBlock[]) => JSX.Element
}
export const BlockSection = ({
title,
blocks,
parentId,
slot,
onRequestAddBlock,
renderNestedList,
}: BlockSectionProps) => (
<Box className={styles.blockSection}>
<Box className={styles.blockSectionHeader}>
<Typography className={styles.blockSectionTitle}>{title}</Typography>
<Button
size="small"
variant="contained"
onClick={(event) => onRequestAddBlock(event, { parentId, slot })}
startIcon={<AddIcon fontSize="small" />}
>
Add block
</Button>
</Box>
<Box className={styles.blockSectionBody}>
{blocks && blocks.length > 0 ? (
renderNestedList(blocks)
) : (
<Box className={styles.blockEmpty}>Drop blocks here to build this section.</Box>
)}
</Box>
</Box>
)