Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Commit 5e4de00

Browse files
committed
refactor: update document source and streamline field handling in SuperDocESign component
1 parent 1779d28 commit 5e4de00

4 files changed

Lines changed: 39 additions & 71 deletions

File tree

demo/src/App.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,24 @@ export function App() {
9797
<SuperDocESign
9898
eventId={`demo-${Date.now()}`}
9999
document={{
100-
source: "https://storage.googleapis.com/public_statichosting/word_documents/agreement_template.docx",
100+
source: "https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement.docx",
101101
mode: 'full',
102102
displayOptions: {
103103
scrollRequired: true
104104
}
105105
}}
106106
fields={{
107107
document: [
108-
{ alias: 'userName', value: 'John Doe' },
109-
{ alias: 'date', value: new Date().toLocaleDateString() },
110-
{ alias: 'company', value: 'SuperDoc' },
111-
{ alias: 'serviceType', value: 'Premium' },
112-
{ alias: 'jurisdiction', value: 'CA' },
113-
{ alias: 'companyAddress', value: '123 Main St, Anytown, USA' }
108+
{ id: 'user_name', value: 'John Doe' },
109+
{ id: 'agreement_date', value: new Date().toLocaleDateString() },
110+
{ id: 'company_name', value: 'SuperDoc' },
111+
{ id: 'service_type', value: 'Premium' },
112+
{ id: 'agreement_jurisdiction', value: 'CA' },
113+
{ id: 'company_address', value: '123 Main St, Anytown, USA' }
114114
],
115115
signer: [
116116
{
117117
id: 'signature',
118-
alias: 'signature',
119118
type: 'signature',
120119
label: 'Your Signature',
121120
validation: { required: true }

src/defaults/SignatureInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const SignatureInput: React.FC<FieldComponentProps> = ({
88
label,
99
}) => {
1010
return (
11-
<div>
11+
<div className={`superdoc-esign-signature-input`} style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
1212
{label && <label>{label}</label>}
1313
<input
1414
type="text"

src/index.tsx

Lines changed: 30 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {
1+
import {
22
useRef,
33
useState,
44
useEffect,
@@ -39,7 +39,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
3939
documentHeight = "600px",
4040
} = props;
4141

42-
// State
4342
const [scrolled, setScrolled] = useState(
4443
!document.displayOptions?.scrollRequired,
4544
);
@@ -51,7 +50,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
5150
const [auditTrail, setAuditTrail] = useState<Types.AuditEvent[]>([]);
5251
const [isReady, setIsReady] = useState(false);
5352

54-
// Refs
5553
const containerRef = useRef<HTMLDivElement>(null);
5654
const superdocRef = useRef<SuperDoc | null>(null);
5755
const startTimeRef = useRef(Date.now());
@@ -62,17 +60,15 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
6260
if (!superdocRef.current?.activeEditor) return;
6361
const editor = superdocRef.current.activeEditor;
6462

65-
const signerField = fieldsRef.current.signer?.find(
66-
(f) => f.id === field.id || f.alias === field.alias,
67-
);
63+
const signerField = fieldsRef.current.signer?.find((f) => f.id === field.id);
6864

6965
let updatePayload;
7066

7167
// For signature fields, always convert to image regardless of input
7268
if (signerField?.type === "signature" && field.value) {
7369
const imageUrl =
7470
typeof field.value === "string" &&
75-
field.value.startsWith("data:image/")
71+
field.value.startsWith("data:image/")
7672
? field.value // Already an image
7773
: textToImageDataUrl(String(field.value)); // Convert text to image
7874

@@ -86,33 +82,45 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
8682
updatePayload = { text: String(field.value ?? "") };
8783
}
8884

89-
if (field.alias) {
90-
editor.commands.updateStructuredContentByAlias(
91-
field.alias,
85+
if (field.id) {
86+
editor.commands.updateStructuredContentById(
87+
field.id,
9288
updatePayload,
9389
);
9490
}
9591
}, []);
9692

97-
// Synchronous helper function
9893
function textToImageDataUrl(text: string): string {
9994
const canvas = globalThis.document.createElement("canvas");
100-
canvas.width = 300;
101-
canvas.height = 80;
10295
const ctx = canvas.getContext("2d")!;
10396

104-
ctx.fillStyle = "white";
105-
ctx.fillRect(0, 0, canvas.width, canvas.height);
106-
ctx.font = "italic 30px cursive";
97+
const fontSize = 30;
98+
ctx.font = `italic ${fontSize}px cursive`;
99+
100+
const metrics = ctx.measureText(text);
101+
const textWidth = metrics.width;
102+
103+
const estimatedHeight = fontSize * 1.3; // Cursive fonts typically need ~1.3x font size
104+
const paddingX = 4;
105+
const paddingY = 6; // Extra vertical padding for cursive descenders
106+
107+
canvas.width = Math.ceil(textWidth + paddingX * 2);
108+
canvas.height = Math.ceil(estimatedHeight + paddingY * 2);
109+
110+
ctx.font = `italic ${fontSize}px cursive`;
107111
ctx.fillStyle = "black";
108112
ctx.textAlign = "center";
109113
ctx.textBaseline = "middle";
110-
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
111114

112-
return canvas.toDataURL();
115+
ctx.fillText(
116+
text,
117+
canvas.width / 2,
118+
canvas.height / 2
119+
);
120+
121+
return canvas.toDataURL("image/png");
113122
}
114123

115-
// Discover fields in document and apply initial values
116124
const discoverAndApplyFields = useCallback(
117125
(editor: Editor) => {
118126
if (!editor) return;
@@ -122,39 +130,32 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
122130
editor.state,
123131
);
124132

125-
// Build initial values map
126133
const configValues = new Map<string, Types.FieldValue>();
127134

128135
fieldsRef.current.document?.forEach((f) => {
129136
if (f.id) configValues.set(f.id, f.value);
130-
if (f.alias) configValues.set(f.alias, f.value);
131137
});
132138

133139
fieldsRef.current.signer?.forEach((f) => {
134140
if (f.value !== undefined) {
135141
configValues.set(f.id, f.value);
136-
if (f.alias) configValues.set(f.alias, f.value);
137142
}
138143
});
139144

140-
// Discover fields
141145
const discovered: Types.FieldInfo[] = tags
142146
.map(({ node }: any) => ({
143147
id: node.attrs.id,
144-
alias: node.attrs.alias,
145-
label: node.attrs.alias,
148+
label: node.attrs.label,
146149
value:
147150
configValues.get(node.attrs.id) ??
148-
configValues.get(node.attrs.alias) ??
149151
node.textContent ??
150152
"",
151153
}))
152-
.filter((f: Types.FieldInfo) => f.id || f.alias);
154+
.filter((f: Types.FieldInfo) => f.id);
153155

154156
if (discovered.length > 0) {
155157
onFieldsDiscovered?.(discovered);
156158

157-
// Apply initial values from both document and signer fields
158159
const allFields = [
159160
...(fieldsRef.current.document || []),
160161
...(fieldsRef.current.signer || []),
@@ -165,7 +166,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
165166
.forEach((field) =>
166167
updateFieldInDocument({
167168
id: field.id,
168-
alias: field.alias,
169169
value: field.value!,
170170
}),
171171
);
@@ -174,7 +174,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
174174
[onFieldsDiscovered, updateFieldInDocument],
175175
);
176176

177-
// Add audit event
178177
const addAuditEvent = (event: Omit<Types.AuditEvent, "timestamp">) => {
179178
const auditEvent: Types.AuditEvent = {
180179
...event,
@@ -216,7 +215,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
216215
};
217216
}, [document.source, document.mode, discoverAndApplyFields]);
218217

219-
// Track scroll manually
220218
useEffect(() => {
221219
if (!document.displayOptions?.scrollRequired || !isReady) return;
222220

@@ -227,7 +225,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
227225
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
228226
const scrollPercentage = scrollTop / (scrollHeight - clientHeight);
229227

230-
// Trigger at 95% or if content fits in viewport
231228
if (scrollPercentage >= 0.95 || scrollHeight <= clientHeight) {
232229
setScrolled(true);
233230
addAuditEvent({
@@ -238,38 +235,28 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
238235
};
239236

240237
scrollContainer.addEventListener("scroll", handleScroll);
241-
handleScroll(); // Check initial state
238+
handleScroll();
242239

243240
return () => scrollContainer.removeEventListener("scroll", handleScroll);
244241
}, [document.displayOptions?.scrollRequired, isReady]);
245242

246-
// Handle field change from signer inputs
247243
const handleFieldChange = useCallback(
248244
(fieldId: string, value: Types.FieldValue) => {
249245
setFieldValues((prev) => {
250246
const previousValue = prev.get(fieldId);
251247
const newMap = new Map(prev);
252248
newMap.set(fieldId, value);
253249

254-
// Find the field config to get alias
255-
const fieldConfig = fieldsRef.current.signer?.find(
256-
(f) => f.id === fieldId,
257-
);
258-
259-
// Update in document
260250
updateFieldInDocument({
261251
id: fieldId,
262-
alias: fieldConfig?.alias,
263252
value: value,
264253
});
265254

266-
// Add to audit trail
267255
addAuditEvent({
268256
type: "field_change",
269257
data: { fieldId, value, previousValue },
270258
});
271259

272-
// Notify parent
273260
onFieldChange?.({
274261
id: fieldId,
275262
value,
@@ -281,23 +268,17 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
281268
},
282269
[onFieldChange, updateFieldInDocument],
283270
);
284-
285-
// Check if form is valid
286271
const checkIsValid = useCallback((): boolean => {
287-
// Check scroll requirement
288272
if (document.displayOptions?.scrollRequired && !scrolled) {
289273
return false;
290274
}
291275

292-
// Check required fields
293276
return (fields.signer || []).every((field) => {
294277
if (!field.validation?.required) return true;
295278
const value = fieldValues.get(field.id);
296279
return value && (typeof value !== "string" || value.trim());
297280
});
298281
}, [scrolled, fields.signer, fieldValues, document.displayOptions]);
299-
300-
// Validate form and notify state changes
301282
useEffect(() => {
302283
const valid = checkIsValid();
303284
setIsValid(valid);
@@ -311,7 +292,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
311292
onStateChange?.(state);
312293
}, [scrolled, fieldValues, isSubmitting, checkIsValid, onStateChange]);
313294

314-
// Handle download
315295
const handleDownload = useCallback(async () => {
316296
if (isDisabled) return;
317297

@@ -323,7 +303,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
323303
if (blob && onDownload) {
324304
onDownload(blob, download?.fileName || "document.pdf");
325305
} else if (blob) {
326-
// Default download behavior
327306
const url = URL.createObjectURL(blob);
328307
const a = globalThis.document.createElement("a");
329308
a.href = url;
@@ -332,8 +311,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
332311
URL.revokeObjectURL(url);
333312
}
334313
}, [isDisabled, download, onDownload]);
335-
336-
// Handle submit
337314
const handleSubmit = useCallback(async () => {
338315
if (!isValid || isDisabled || isSubmitting) return;
339316

@@ -348,7 +325,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
348325
documentFields: fields.document || [],
349326
signerFields: (fields.signer || []).map((field) => ({
350327
id: field.id,
351-
alias: field.alias,
352328
value: fieldValues.get(field.id) ?? null,
353329
})),
354330
isFullyCompleted: isValid,
@@ -370,7 +346,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
370346
onSubmit,
371347
]);
372348

373-
// Render field
374349
const renderField = (field: Types.SignerField) => {
375350
const Component = field.component || getDefaultComponent(field.type);
376351

@@ -385,7 +360,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
385360
);
386361
};
387362

388-
// Get default component
389363
const getDefaultComponent = (
390364
type: "signature" | "consent" | "checkbox" | "text",
391365
) => {
@@ -399,7 +373,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
399373
}
400374
};
401375

402-
// Render action buttons
403376
const renderActionButtons = () => {
404377
const DownloadButton =
405378
download?.component || createDownloadButton(download);
@@ -427,7 +400,6 @@ const SuperDocESign = forwardRef<any, Types.SuperDocESignProps>(
427400
);
428401
};
429402

430-
// Expose methods via ref
431403
useImperativeHandle(ref, () => ({
432404
getState: () => ({
433405
scrolled,

src/types.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ import type { SuperDoc } from "superdoc"; // eslint-disable-line
33
export type FieldValue = string | boolean | number | null | undefined;
44

55
export interface FieldReference {
6-
id?: string;
7-
alias?: string;
6+
id: string;
87
}
98

109
export interface DocumentField extends FieldReference {
1110
value: FieldValue;
1211
}
1312

1413
export interface SignerField extends FieldReference {
15-
id: string;
1614
type: "signature" | "consent" | "checkbox" | "text";
1715
label?: string;
1816
value?: FieldValue;
@@ -119,6 +117,5 @@ export type FieldChange = DocumentField & { previousValue?: FieldValue };
119117

120118
export interface SignerFieldValue {
121119
id: string;
122-
alias?: string;
123120
value: FieldValue;
124121
}

0 commit comments

Comments
 (0)