This commit is contained in:
seb
2026-08-05 17:01:42 +02:00
parent 7cecb3dd1f
commit 198e800fd3
14 changed files with 1250 additions and 929 deletions

View File

@@ -1,4 +1,4 @@
import { Component, createRef } from 'react';
import { Component, cloneElement, createRef, isValidElement } from 'react';
import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
@@ -9,7 +9,6 @@ import IconButton from '@mui/material/IconButton';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import { injectMuiTheme } from './injectMuiTheme';
import { enqueueSnackbar } from 'notistack';
import { notifyError } from '../../utils/notify';
import { panelTilePaperSx, TILE_ACTION_PADDING, tileActionIconSx, tileActionSx } from './panelTileLayout';
@@ -20,6 +19,11 @@ function valuesEqual(a, b) {
/**
* Panel tile with centralized inline-edit chrome.
* Pass `onSave` to enable edit UI; omit it for read-only tiles (no edit icon).
*
* Content is element props (not render callbacks):
* display — React node shown when not editing
* edit — element cloned with { draft, onChange, inputRef } while editing
* overlay — optional element cloned with { editing, draft, onChange }
*/
class EditablePanelTileBase extends Component {
constructor(props) {
@@ -191,6 +195,31 @@ class EditablePanelTileBase extends Component {
);
}
renderBody() {
const { display, edit } = this.props;
const { editing, draft } = this.state;
if (editing && draft != null && isValidElement(edit)) {
return cloneElement(edit, {
draft,
onChange: this.handleDraftChange,
inputRef: this.inputRef,
});
}
return display;
}
renderOverlayNode() {
const { overlay } = this.props;
const { editing, draft } = this.state;
if (!isValidElement(overlay)) return null;
return cloneElement(overlay, {
editing,
draft,
onChange: this.handleDraftChange,
});
}
render() {
const {
onSave,
@@ -199,13 +228,9 @@ class EditablePanelTileBase extends Component {
sx,
minHeight = 120,
tileKey,
renderDisplay,
renderEdit,
renderOverlay,
contentAlignItems = 'center',
} = this.props;
const { editing, draft, error } = this.state;
const savedValue = this.getSavedValue();
const { editing, error } = this.state;
const pending = this.isDirty();
const actionPadding = onSave ? TILE_ACTION_PADDING : 0;
@@ -223,13 +248,7 @@ class EditablePanelTileBase extends Component {
})}
>
{this.renderActions()}
{typeof renderOverlay === 'function'
? renderOverlay({
editing,
draft,
onChange: this.handleDraftChange,
})
: null}
{this.renderOverlayNode()}
{this.renderTitle()}
<Box
sx={{
@@ -241,9 +260,7 @@ class EditablePanelTileBase extends Component {
}}
onKeyDown={editing ? this.handleEditKeyDown : undefined}
>
{editing && draft != null
? renderEdit(draft, this.handleDraftChange, this.inputRef)
: renderDisplay(savedValue)}
{this.renderBody()}
</Box>
{error ? (
<Alert severity="error" sx={{ mt: 1, py: 0 }}>