Client: Replaced block-system with safe-zone

This commit is contained in:
Donkie
2023-08-10 21:40:30 +02:00
parent ec80973f96
commit 3f18c32c8a
4 changed files with 214 additions and 304 deletions

3
client/.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"printWidth": 120
}

View File

@@ -56,7 +56,7 @@
"printing": { "printing": {
"generic": { "generic": {
"title": "Printing", "title": "Printing",
"description": "Tune in the settings below to get the desired print layout. Keep in mind that printers and your OS may apply it's own margins and scaling, so you may need to adjust the settings accordingly. Test it on a piece of regular paper before printing on the actual labels.", "description": "Tune in the settings below to get the desired print layout. Keep in mind that printers and your OS may apply it's own margins and scaling, so you may need to perform some trial-and-error before it's all correct. Test it on a piece of regular paper before printing on the actual labels.\nThe \"Margin\" fields should be configured to match your label paper and printer, changing these will affect the size of the entire grid.\nThe \"Safe-Zone\" fields should be set to how close to the paper edge your printer can print, changing these will not affect the entire grid.",
"print": "Print", "print": "Print",
"columns": "Columns", "columns": "Columns",
"rows": "Rows", "rows": "Rows",
@@ -67,6 +67,10 @@
"marginRight": "Right Margin", "marginRight": "Right Margin",
"marginTop": "Top Margin", "marginTop": "Top Margin",
"marginBottom": "Bottom Margin", "marginBottom": "Bottom Margin",
"printerMarginLeft": "Safe-Zone Left",
"printerMarginRight": "Safe-Zone Right",
"printerMarginTop": "Safe-Zone Top",
"printerMarginBottom": "Safe-Zone Bottom",
"blockRowsTop": "Block Rows Top", "blockRowsTop": "Block Rows Top",
"blockRowsBottom": "Block Rows Bottom", "blockRowsBottom": "Block Rows Bottom",
"blockColumnsLeft": "Block Columns Left", "blockColumnsLeft": "Block Columns Left",

View File

@@ -1,17 +1,5 @@
import React, { useRef } from "react"; import React, { useRef } from "react";
import { import { Modal, Slider, Button, Select, Row, Col, Form, Divider, RadioChangeEvent, Radio, InputNumber } from "antd";
Modal,
Slider,
Button,
Select,
Row,
Col,
Form,
Divider,
RadioChangeEvent,
Radio,
InputNumber,
} from "antd";
import ReactToPrint from "react-to-print"; import ReactToPrint from "react-to-print";
import { useSavedState } from "../../utils/saveload"; import { useSavedState } from "../../utils/saveload";
import { useTranslate } from "@refinedev/core"; import { useTranslate } from "@refinedev/core";
@@ -57,120 +45,69 @@ const paperDimensions: { [key: string]: PaperDimensions } = {
}, },
}; };
const PrintingDialog: React.FC<PrintingDialogProps> = ({ const PrintingDialog: React.FC<PrintingDialogProps> = ({ items, style, extraSettings, visible, onCancel, title }) => {
items,
style,
extraSettings,
visible,
onCancel,
title,
}) => {
const t = useTranslate(); const t = useTranslate();
const [marginLeft, setMarginLeft] = useSavedState("print-marginLeft", 10); const [marginLeft, setMarginLeft] = useSavedState("print-marginLeft", 10);
const [marginTop, setMarginTop] = useSavedState("print-marginTop", 10); const [marginTop, setMarginTop] = useSavedState("print-marginTop", 10);
const [marginRight, setMarginRight] = useSavedState("print-marginRight", 10); const [marginRight, setMarginRight] = useSavedState("print-marginRight", 10);
const [marginBottom, setMarginBottom] = useSavedState( const [marginBottom, setMarginBottom] = useSavedState("print-marginBottom", 10);
"print-marginBottom", const [printerMarginLeft, setPrinterMarginLeft] = useSavedState("print-printerMarginLeft", 5);
10 const [printerMarginTop, setPrinterMarginTop] = useSavedState("print-printerMarginTop", 5);
); const [printerMarginRight, setPrinterMarginRight] = useSavedState("print-printerMarginRight", 5);
const [blockRowsTop, setBlockRowsTop] = useSavedState( const [printerMarginBottom, setPrinterMarginBottom] = useSavedState("print-printerMarginBottom", 5);
"print-blockRowsTop",
0
);
const [blockRowsBottom, setBlockRowsBottom] = useSavedState(
"print-blockRowsBottom",
0
);
const [blockColumnsLeft, setBlockColumnsLeft] = useSavedState(
"print-blockColumnsLeft",
0
);
const [blockColumnsRight, setBlockColumnsRight] = useSavedState(
"print-blockColumnsRight",
0
);
const [paperColumns, setPaperColumns] = useSavedState("print-itemsPerRow", 3); const [paperColumns, setPaperColumns] = useSavedState("print-itemsPerRow", 3);
const [paperRows, setPaperRows] = useSavedState("print-rowsPerPage", 8); const [paperRows, setPaperRows] = useSavedState("print-rowsPerPage", 8);
const [paperSize, setPaperSize] = useSavedState("print-paperSize", "A4"); const [paperSize, setPaperSize] = useSavedState("print-paperSize", "A4");
const [previewScale, setPreviewScale] = useSavedState( const [previewScale, setPreviewScale] = useSavedState("print-previewScale", 0.6);
"print-previewScale", const [borderShowMode, setBorderShowMode] = useSavedState<"none" | "border" | "grid">("print-borderShowMode", "grid");
0.6
);
const [borderShowMode, setBorderShowMode] = useSavedState<
"none" | "border" | "grid"
>("print-borderShowMode", "grid");
const paperWidth = paperDimensions[paperSize].width; const paperWidth = paperDimensions[paperSize].width;
const paperHeight = paperDimensions[paperSize].height; const paperHeight = paperDimensions[paperSize].height;
const printRef = useRef<HTMLDivElement>(null); const printRef = useRef<HTMLDivElement>(null);
const calculatedRowHeight = const calculatedRowHeight = (paperHeight - marginTop - marginBottom) / paperRows;
(paperHeight - marginTop - marginBottom) / paperRows;
const itemsPerRow = paperColumns - blockColumnsLeft - blockColumnsRight; const itemsPerRow = paperColumns;
const rowsPerPage = paperRows - blockRowsTop - blockRowsBottom; const rowsPerPage = paperRows;
const rowsOfItems = []; const rowsOfItems = [];
for (let row_idx = 0; row_idx <= items.length / itemsPerRow; row_idx += 1) { for (let row_idx = 0; row_idx <= items.length / itemsPerRow; row_idx += 1) {
rowsOfItems.push( rowsOfItems.push(items.slice(row_idx * itemsPerRow, (row_idx + 1) * itemsPerRow));
items.slice(row_idx * itemsPerRow, (row_idx + 1) * itemsPerRow)
);
} }
const pageBlocks = []; const pageBlocks = [];
for ( for (let page_idx = 0; page_idx <= rowsOfItems.length / rowsPerPage; page_idx += 1) {
let page_idx = 0; pageBlocks.push(rowsOfItems.slice(page_idx * rowsPerPage, (page_idx + 1) * rowsPerPage));
page_idx <= rowsOfItems.length / rowsPerPage;
page_idx += 1
) {
pageBlocks.push(
rowsOfItems.slice(page_idx * rowsPerPage, (page_idx + 1) * rowsPerPage)
);
} }
const pages = pageBlocks.map(function (rows, idx) { const pages = pageBlocks.map(function (rows, pageIdx) {
const itemRowsBlockTop = [...Array(blockRowsTop).keys()].map((row) => ( const itemRows = rows.map((row, rowIdx) => {
<tr key={row}> return (
<td> <tr key={rowIdx}>
<div style={{ height: `${calculatedRowHeight}mm` }}></div> {row.map(function (item, colIdx) {
</td>
</tr>
));
const itemRows = rows.map((row, idx) => {
const itemColumnsBlockLeft = [...Array(blockColumnsLeft).keys()].map(
(col) => (
<td key={col}>
<div
style={{
width: `${
(paperWidth - marginLeft - marginRight) / paperColumns
}mm`,
}}
></div>
</td>
)
);
return ( return (
<tr key={idx}>
{itemColumnsBlockLeft}
{row.map((item, index) => (
<td> <td>
<div <div
key={index} key={colIdx}
style={{ style={{
width: `${ width: `${(paperWidth - marginLeft - marginRight) / paperColumns}mm`,
(paperWidth - marginLeft - marginRight) / paperColumns
}mm`,
height: `${calculatedRowHeight}mm`, height: `${calculatedRowHeight}mm`,
border: borderShowMode === "grid" ? "1px solid #000" : "none", border: borderShowMode === "grid" ? "1px solid #000" : "none",
paddingLeft: colIdx === 0 ? `${Math.max(printerMarginLeft - marginLeft, 0)}mm` : 0,
paddingRight:
colIdx === paperColumns - 1 ? `${Math.max(printerMarginRight - marginRight, 0)}mm` : 0,
paddingTop: rowIdx === 0 ? `${Math.max(printerMarginTop - marginTop, 0)}mm` : 0,
paddingBottom:
rowIdx === paperRows - 1 ? `${Math.max(printerMarginBottom - marginBottom, 0)}mm` : 0,
}} }}
> >
{item} {item}
</div> </div>
</td> </td>
))} );
})}
</tr> </tr>
); );
}); });
@@ -178,7 +115,7 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
return ( return (
<div <div
className="print-page" className="print-page"
key={idx} key={pageIdx}
style={{ style={{
width: `${paperWidth}mm`, width: `${paperWidth}mm`,
height: `${paperHeight}mm`, height: `${paperHeight}mm`,
@@ -192,18 +129,10 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
border: borderShowMode !== "none" ? "1px solid #000" : "none", border: borderShowMode !== "none" ? "1px solid #000" : "none",
height: `${paperHeight - marginTop - marginBottom}mm`, height: `${paperHeight - marginTop - marginBottom}mm`,
width: `${paperWidth - marginLeft - marginRight}mm`, width: `${paperWidth - marginLeft - marginRight}mm`,
marginTop: `calc(${marginTop}mm - ${ marginTop: `calc(${marginTop}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
borderShowMode !== "none" ? "1px" : "0px" marginLeft: `calc(${marginLeft}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
})`, marginRight: `calc(${marginRight}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
marginLeft: `calc(${marginLeft}mm - ${ marginBottom: `calc(${marginBottom}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
borderShowMode !== "none" ? "1px" : "0px"
})`,
marginRight: `calc(${marginRight}mm - ${
borderShowMode !== "none" ? "1px" : "0px"
})`,
marginBottom: `calc(${marginBottom}mm - ${
borderShowMode !== "none" ? "1px" : "0px"
})`,
}} }}
> >
<table <table
@@ -211,7 +140,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
alignContent: "flex-start", alignContent: "flex-start",
}} }}
> >
{itemRowsBlockTop}
{itemRows} {itemRows}
</table> </table>
</div> </div>
@@ -227,16 +155,22 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
footer={[ footer={[
<ReactToPrint <ReactToPrint
key="print-button" key="print-button"
trigger={() => ( trigger={() => <Button type="primary">{t("printing.generic.print")}</Button>}
<Button type="primary">{t("printing.generic.print")}</Button>
)}
content={() => printRef.current} content={() => printRef.current}
/>, />,
]} ]}
width={1200} // Set the modal width to accommodate the preview width={1200} // Set the modal width to accommodate the preview
> >
<Row gutter={16}> <Row gutter={16}>
<Col span={24}>{t("printing.generic.description")}</Col> <Col
span={24}
style={{
whiteSpace: "pre-line",
marginBottom: "1em",
}}
>
{t("printing.generic.description")}
</Col>
<Col span={14}> <Col span={14}>
<div <div
style={{ style={{
@@ -286,13 +220,7 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
</div> </div>
</Col> </Col>
<Col span={10}> <Col span={10}>
<Form <Form labelAlign="left" colon={false} labelWrap={true} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
labelAlign="left"
colon={false}
labelWrap={true}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
>
<Form.Item label={t("printing.generic.marginLeft")}> <Form.Item label={t("printing.generic.marginLeft")}>
<Row> <Row>
<Col span={12}> <Col span={12}>
@@ -401,6 +329,114 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
</Col> </Col>
</Row> </Row>
</Form.Item> </Form.Item>
<Form.Item label={t("printing.generic.printerMarginLeft")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={50}
step={0.1}
tooltip={{ formatter: (value) => `${value} mm` }}
value={printerMarginLeft}
onChange={(value) => {
setPrinterMarginLeft(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
step={0.1}
style={{ margin: "0 16px" }}
value={printerMarginLeft}
addonAfter="mm"
onChange={(value) => {
setPrinterMarginLeft(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.printerMarginTop")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={50}
step={0.1}
tooltip={{ formatter: (value) => `${value} mm` }}
value={printerMarginTop}
onChange={(value) => {
setPrinterMarginTop(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
step={0.1}
style={{ margin: "0 16px" }}
value={printerMarginTop}
addonAfter="mm"
onChange={(value) => {
setPrinterMarginTop(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.printerMarginRight")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={50}
step={0.1}
tooltip={{ formatter: (value) => `${value} mm` }}
value={printerMarginRight}
onChange={(value) => {
setPrinterMarginRight(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
step={0.1}
style={{ margin: "0 16px" }}
value={printerMarginRight}
addonAfter="mm"
onChange={(value) => {
setPrinterMarginRight(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.printerMarginBottom")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={50}
step={0.1}
tooltip={{ formatter: (value) => `${value} mm` }}
value={printerMarginBottom}
onChange={(value) => {
setPrinterMarginBottom(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
step={0.1}
style={{ margin: "0 16px" }}
value={printerMarginBottom}
addonAfter="mm"
onChange={(value) => {
setPrinterMarginBottom(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.columns")}> <Form.Item label={t("printing.generic.columns")}>
<Row> <Row>
<Col span={12}> <Col span={12}>
@@ -409,8 +445,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
max={5} max={5}
value={paperColumns} value={paperColumns}
onChange={(value) => { onChange={(value) => {
if (value - blockColumnsLeft - blockColumnsRight < 1)
return;
setPaperColumns(value); setPaperColumns(value);
}} }}
/> />
@@ -421,11 +455,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
style={{ margin: "0 16px" }} style={{ margin: "0 16px" }}
value={paperColumns} value={paperColumns}
onChange={(value) => { onChange={(value) => {
if (
(value ?? 1) - blockColumnsLeft - blockColumnsRight <
1
)
return;
setPaperColumns(value ?? 1); setPaperColumns(value ?? 1);
}} }}
/> />
@@ -440,7 +469,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
max={15} max={15}
value={paperRows} value={paperRows}
onChange={(value) => { onChange={(value) => {
if (value - blockRowsTop - blockRowsBottom < 1) return;
setPaperRows(value); setPaperRows(value);
}} }}
/> />
@@ -451,8 +479,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
style={{ margin: "0 16px" }} style={{ margin: "0 16px" }}
value={paperRows} value={paperRows}
onChange={(value) => { onChange={(value) => {
if ((value ?? 1) - blockRowsTop - blockRowsBottom < 1)
return;
setPaperRows(value ?? 1); setPaperRows(value ?? 1);
}} }}
/> />
@@ -460,10 +486,7 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
</Row> </Row>
</Form.Item> </Form.Item>
<Form.Item label={t("printing.generic.paperSize")}> <Form.Item label={t("printing.generic.paperSize")}>
<Select <Select value={paperSize} onChange={(value) => setPaperSize(value)}>
value={paperSize}
onChange={(value) => setPaperSize(value)}
>
{Object.keys(paperDimensions).map((key) => ( {Object.keys(paperDimensions).map((key) => (
<Select.Option key={key} value={key}> <Select.Option key={key} value={key}>
{key} {key}
@@ -489,113 +512,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
buttonStyle="solid" buttonStyle="solid"
/> />
</Form.Item> </Form.Item>
<Form.Item label={t("printing.generic.blockRowsTop")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={3}
value={blockRowsTop}
onChange={(value) => {
if (paperRows - value - blockRowsBottom < 1) return;
setBlockRowsTop(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
min={0}
style={{ margin: "0 16px" }}
value={blockRowsTop}
onChange={(value) => {
if (paperRows - (value ?? 0) - blockRowsBottom < 1)
return;
setBlockRowsTop(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.blockRowsBottom")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={3}
value={blockRowsBottom}
onChange={(value) => {
if (paperRows - value - blockRowsTop < 1) return;
setBlockRowsBottom(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
min={0}
style={{ margin: "0 16px" }}
value={blockRowsBottom}
onChange={(value) => {
if (paperRows - (value ?? 0) - blockRowsTop < 1) return;
setBlockRowsBottom(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.blockColumnsLeft")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={3}
value={blockColumnsLeft}
onChange={(value) => {
if (paperColumns - value - blockColumnsRight < 1) return;
setBlockColumnsLeft(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
min={0}
style={{ margin: "0 16px" }}
value={blockColumnsLeft}
onChange={(value) => {
if (paperColumns - (value ?? 0) - blockColumnsRight < 1)
return;
setBlockColumnsLeft(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label={t("printing.generic.blockColumnsRight")}>
<Row>
<Col span={12}>
<Slider
min={0}
max={3}
value={blockColumnsRight}
onChange={(value) => {
if (paperColumns - value - blockColumnsLeft < 1) return;
setBlockColumnsRight(value);
}}
/>
</Col>
<Col span={12}>
<InputNumber
min={0}
style={{ margin: "0 16px" }}
value={blockColumnsRight}
onChange={(value) => {
if (paperColumns - (value ?? 0) - blockColumnsLeft < 1)
return;
setBlockColumnsRight(value ?? 0);
}}
/>
</Col>
</Row>
</Form.Item>
{extraSettings && <Divider />} {extraSettings && <Divider />}
{extraSettings} {extraSettings}
<Divider /> <Divider />

View File

@@ -15,34 +15,17 @@ interface QRCodePrintingDialogProps {
onCancel: () => void; onCancel: () => void;
} }
const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({ visible, items, onCancel }) => {
visible,
items,
onCancel,
}) => {
const t = useTranslate(); const t = useTranslate();
const [showContent, setShowContent] = useSavedState( const [showContent, setShowContent] = useSavedState("print-showContent", true);
"print-showContent",
true
);
const [textSize, setTextSize] = useSavedState("print-textSize", 3); const [textSize, setTextSize] = useSavedState("print-textSize", 3);
const [showSpoolmanIcon, setShowSpoolmanIcon] = useSavedState( const [showSpoolmanIcon, setShowSpoolmanIcon] = useSavedState("print-showSpoolmanIcon", true);
"print-showSpoolmanIcon",
true
);
const elements = items.map((item) => { const elements = items.map((item) => {
return ( return (
<div <div className="print-qrcode-item">
style={{ <div className="print-qrcode-container">
display: "flex",
justifyContent: "center",
width: "100%",
maxHeight: "100%",
flexDirection: "row",
}}
>
<QRCode <QRCode
className="print-qrcode" className="print-qrcode"
icon={showSpoolmanIcon ? "/favicon.ico" : undefined} icon={showSpoolmanIcon ? "/favicon.ico" : undefined}
@@ -51,14 +34,8 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
type="svg" type="svg"
color="#000" color="#000"
/> />
{showContent && (
<div
className="print-qrcode-title"
style={{ textAlign: "left", color: "#000", overflow: "hidden" }}
>
{item.label ?? item.value}
</div> </div>
)} {showContent && <div className="print-qrcode-title">{item.label ?? item.value}</div>}
</div> </div>
); );
}); });
@@ -71,10 +48,7 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
extraSettings={ extraSettings={
<> <>
<Form.Item label={t("printing.qrcode.showContent")}> <Form.Item label={t("printing.qrcode.showContent")}>
<Switch <Switch checked={showContent} onChange={(checked) => setShowContent(checked)} />
checked={showContent}
onChange={(checked) => setShowContent(checked)}
/>
</Form.Item> </Form.Item>
<Form.Item label={t("printing.qrcode.textSize")}> <Form.Item label={t("printing.qrcode.textSize")}>
<Row> <Row>
@@ -107,28 +81,41 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
</Row> </Row>
</Form.Item> </Form.Item>
<Form.Item label={t("printing.qrcode.showSpoolmanIcon")}> <Form.Item label={t("printing.qrcode.showSpoolmanIcon")}>
<Switch <Switch checked={showSpoolmanIcon} onChange={(checked) => setShowSpoolmanIcon(checked)} />
checked={showSpoolmanIcon}
onChange={(checked) => setShowSpoolmanIcon(checked)}
/>
</Form.Item> </Form.Item>
</> </>
} }
style={` style={`
.print-page .print-qrcode-item {
display: flex;
width: 100%;
max-height: 100%;
}
.print-page .print-qrcode-container {
max-width: 50%;
display: flex;
}
.print-page .print-qrcode { .print-page .print-qrcode {
height: 100% !important; width: auto !important;
width: 100% !important; height: auto !important;
} }
.print-page .print-qrcode-title { .print-page .print-qrcode-title {
width: 100%; flex: 1 1 auto;
font-size: ${textSize}mm; font-size: ${textSize}mm;
color: #000;
overflow: hidden;
} }
.print-page svg { .print-page canvas, .print-page svg {
display: block; /* display: block; */
height: 100%; object-fit: contain;
width: auto; height: 100% !important;
width: 100% !important;
max-height: 100%;
max-width: 100%;
} }
`} `}
onCancel={onCancel} onCancel={onCancel}