Skip to content

Commit 5c70f7e

Browse files
committed
fix: convert size container into jsx and add filename property
1 parent 084ca3a commit 5c70f7e

6 files changed

Lines changed: 57 additions & 31 deletions

File tree

packages/pluggableWidgets/signature-web/src/Signature.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<caption>Image value</caption>
1111
<description />
1212
</property>
13+
<property key="fileName" type="textTemplate" required="false">
14+
<caption>File name</caption>
15+
<description>Custom filename for the file (without extension). If empty, generates automatically based on format and value.</description>
16+
</property>
1317
<property key="hasSignatureAttribute" type="attribute" required="false">
1418
<caption>Has signature</caption>
1519
<description>Optional boolean attribute, which will be set to true after the canvas is signed. When the attribute is set to false, the signature is cleared, but will not clear the stored image</description>

packages/pluggableWidgets/signature-web/src/components/Signature.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { SignatureProps } from "../utils/customTypes";
1010
import Utils from "../utils/Utils";
1111

1212
export function SignatureComponent(props: SignatureProps): ReactElement {
13-
const { className, alertMessage, wrapperStyle, imageSource, onSignEndAction } = props;
13+
const { className, alertMessage, wrapperStyle, imageSource, fileName, onSignEndAction } = props;
1414
const readOnly = imageSource.readOnly;
1515
const showGrid = props.showGrid && !readOnly;
1616

1717
const handleSignEnd = (imageDataUrl?: string): void => {
1818
if (imageDataUrl) {
19-
imageSource.setValue(Utils.convertUrlToBlob(imageDataUrl));
19+
const customFileName = fileName?.value || Utils.generateFileName("signature");
20+
imageSource.setValue(Utils.convertUrlToBlob(imageDataUrl, customFileName));
2021
}
2122

2223
// Trigger microflow to update signature attribute

packages/pluggableWidgets/signature-web/src/components/SizeContainer.ts renamed to packages/pluggableWidgets/signature-web/src/components/SizeContainer.tsx

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { createElement, CSSProperties, FC, PropsWithChildren } from "react";
21
import classNames from "classnames";
2+
import { CSSProperties, FC, PropsWithChildren } from "react";
33
import { useResizeObserver } from "@mendix/widget-plugin-hooks/useResizeObserver";
44
import { constructWrapperStyle, DimensionsProps } from "../utils/dimensions";
55

@@ -13,35 +13,23 @@ export interface SizeProps extends DimensionsProps, PropsWithChildren {
1313

1414
export const SizeContainer: FC<SizeProps> = (props: SizeProps) => {
1515
const { className, children, classNameInner, readOnly = false, style, onResize } = props;
16-
const ref = useResizeObserver(() => onResize?.());
16+
const ref = useResizeObserver(() => onResize?.()) as React.RefObject<HTMLDivElement>;
1717
const wrapperStyle = constructWrapperStyle(props);
18-
return createElement(
19-
"div",
20-
{
21-
ref,
22-
className: classNames(className, "size-box"),
23-
style: {
18+
19+
return (
20+
<div
21+
ref={ref}
22+
className={classNames(className, "size-box")}
23+
style={{
2424
position: "relative",
2525
...wrapperStyle,
2626
...style
27-
}
28-
},
29-
createElement(
30-
"div",
31-
{
32-
className: classNames("size-box-inner", classNameInner),
33-
readOnly,
34-
disabled: readOnly,
35-
style: {
36-
position: "absolute",
37-
top: "0",
38-
right: "0",
39-
bottom: "0",
40-
left: "0"
41-
}
42-
},
43-
children
44-
)
27+
}}
28+
>
29+
<div className={classNames("size-box-inner", classNameInner)} aria-disabled={readOnly}>
30+
{children}
31+
</div>
32+
</div>
4533
);
4634
};
4735

packages/pluggableWidgets/signature-web/src/ui/Signature.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,12 @@
1515
.alert {
1616
position: absolute;
1717
}
18+
19+
.size-box-inner {
20+
position: absolute;
21+
top: 0;
22+
left: 0;
23+
bottom: 0;
24+
right: 0;
25+
}
1826
}

packages/pluggableWidgets/signature-web/src/utils/Utils.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
22
export default class Utils {
3-
static convertUrlToBlob(base64Uri: string): Blob {
3+
static convertUrlToBlob(base64Uri: string, fileName: string): File {
44
const contentType = "image/png";
55
const sliceSize = 512;
66
const byteCharacters = atob(base64Uri.split(";base64,")[1]);
@@ -16,6 +16,29 @@ export default class Utils {
1616
byteArrays.push(byteArray);
1717
}
1818

19-
return new Blob(byteArrays, { type: contentType });
19+
const blob = new Blob(byteArrays, { type: contentType });
20+
const fullFileName = fileName.endsWith(".png") ? fileName : `${fileName}.png`;
21+
return new File([blob], fullFileName, { type: contentType });
22+
}
23+
24+
static generateFileName(prefix: string): string {
25+
// Auto-generate filename with format and hash
26+
const timestamp = Utils.generateTimestamp();
27+
return `${prefix}_${timestamp}.png`;
28+
}
29+
30+
static generateTimestamp(): string {
31+
// Get current date and time
32+
const now = new Date();
33+
34+
// Format: YYYYMMDD_HHMMSS
35+
const year = now.getFullYear();
36+
const month = String(now.getMonth() + 1).padStart(2, "0");
37+
const day = String(now.getDate()).padStart(2, "0");
38+
const hours = String(now.getHours()).padStart(2, "0");
39+
const minutes = String(now.getMinutes()).padStart(2, "0");
40+
const seconds = String(now.getSeconds()).padStart(2, "0");
41+
42+
return `${year}${month}${day}_${hours}${minutes}${seconds}`;
2043
}
2144
}

packages/pluggableWidgets/signature-web/typings/SignatureProps.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* @author Mendix Widgets Framework Team
55
*/
66
import { CSSProperties } from "react";
7-
import { ActionValue, EditableValue, EditableImageValue, Option, WebImage } from "mendix";
7+
import { ActionValue, DynamicValue, EditableValue, EditableImageValue, Option, WebImage } from "mendix";
88

99
export type PenTypeEnum = "fountain" | "ballpoint" | "marker";
1010

@@ -24,6 +24,7 @@ export interface SignatureContainerProps {
2424
style?: CSSProperties;
2525
tabIndex?: number;
2626
imageSource: EditableImageValue<WebImage>;
27+
fileName?: DynamicValue<string>;
2728
hasSignatureAttribute?: EditableValue<boolean>;
2829
penType: PenTypeEnum;
2930
penColor: string;
@@ -56,6 +57,7 @@ export interface SignaturePreviewProps {
5657
renderMode: "design" | "xray" | "structure";
5758
translate: (text: string) => string;
5859
imageSource: { type: "static"; imageUrl: string; } | { type: "dynamic"; entity: string; } | null;
60+
fileName: string;
5961
hasSignatureAttribute: string;
6062
penType: PenTypeEnum;
6163
penColor: string;

0 commit comments

Comments
 (0)