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

Commit bcb0a6d

Browse files
feat: enhance download functionality with loading state and API integration
1 parent bcdb1b1 commit bcb0a6d

7 files changed

Lines changed: 83 additions & 19 deletions

File tree

.github/workflows/deploy-demo.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242
cache: "pnpm"
4343

4444
- name: Install and Build
45+
env:
46+
VITE_API_BASE_URL: https://esign-demo-proxy-server-191591660773.us-central1.run.app
4547
run: |
4648
pnpm install
4749
rm -rf dist

demo/src/App.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import CustomSignature from './CustomSignature';
1111
import 'superdoc/style.css';
1212
import './App.css';
1313

14+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '';
15+
1416
const documentSource =
1517
'https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement_updated.docx';
1618

@@ -91,7 +93,7 @@ export function App() {
9193
console.log('Submit data:', data);
9294

9395
try {
94-
const response = await fetch('/v1/sign', {
96+
const response = await fetch(`${API_BASE_URL}/v1/sign`, {
9597
method: 'POST',
9698
headers: { 'Content-Type': 'application/json' },
9799
body: JSON.stringify({
@@ -132,7 +134,7 @@ export function App() {
132134
return;
133135
}
134136

135-
const response = await fetch('/v1/download', {
137+
const response = await fetch(`${API_BASE_URL}/v1/download`, {
136138
method: 'POST',
137139
headers: { 'Content-Type': 'application/json' },
138140
body: JSON.stringify({

src/defaults/DownloadButton.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,39 @@ import React from 'react';
22
import type { DownloadButtonProps, DownloadConfig } from '../types';
33

44
export const createDownloadButton = (config?: DownloadConfig) => {
5-
const Component: React.FC<DownloadButtonProps> = ({ onClick, fileName, isDisabled }) => {
5+
const Component: React.FC<DownloadButtonProps> = ({
6+
onClick,
7+
fileName,
8+
isDisabled,
9+
isDownloading,
10+
}) => {
611
const label = config?.label || 'Download';
12+
const disabled = isDisabled || isDownloading;
713

814
return (
915
<button
1016
onClick={onClick}
11-
disabled={isDisabled}
12-
className={`superdoc-esign-btn superdoc-esign-btn--download`}
17+
disabled={disabled}
18+
className={`superdoc-esign-btn superdoc-esign-btn--download${isDownloading ? ' superdoc-esign-btn--loading' : ''}`}
1319
style={{
1420
padding: '8px 16px',
1521
borderRadius: '6px',
1622
border: '1px solid #d0d5dd',
1723
background: '#ffffff',
1824
color: '#333',
19-
cursor: isDisabled ? 'not-allowed' : 'pointer',
20-
opacity: isDisabled ? 0.5 : 1,
25+
cursor: disabled ? 'not-allowed' : 'pointer',
26+
opacity: disabled ? 0.7 : 1,
2127
fontSize: '16px',
2228
fontWeight: 'bold',
29+
display: 'inline-flex',
30+
alignItems: 'center',
31+
gap: '8px',
32+
transition: 'opacity 0.2s ease',
2333
}}
2434
>
25-
{label} {fileName && `(${fileName})`}
35+
{isDownloading && <span className="superdoc-esign-spinner" />}
36+
{isDownloading ? 'Downloading...' : label}
37+
{!isDownloading && fileName && ` (${fileName})`}
2638
</button>
2739
);
2840
};

src/defaults/SubmitButton.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,32 @@ export const createSubmitButton = (config?: SubmitConfig) => {
88
isDisabled,
99
isSubmitting,
1010
}) => {
11-
const getLabel = () => {
12-
return config?.label || 'Submit';
13-
};
11+
const label = config?.label || 'Submit';
12+
const disabled = !isValid || isDisabled || isSubmitting;
1413

1514
return (
1615
<button
1716
onClick={onClick}
18-
disabled={!isValid || isDisabled || isSubmitting}
19-
className={`superdoc-esign-btn superdoc-esign-btn--submit`}
17+
disabled={disabled}
18+
className={`superdoc-esign-btn superdoc-esign-btn--submit${isSubmitting ? ' superdoc-esign-btn--loading' : ''}`}
2019
style={{
2120
padding: '12px 24px',
2221
borderRadius: '6px',
2322
border: 'none',
2423
background: '#007bff',
2524
color: '#fff',
26-
cursor: !isValid || isDisabled ? 'not-allowed' : 'pointer',
27-
opacity: !isValid || isDisabled ? 0.5 : 1,
25+
cursor: disabled ? 'not-allowed' : 'pointer',
26+
opacity: disabled && !isSubmitting ? 0.5 : 1,
2827
fontSize: '16px',
2928
fontWeight: 'bold',
29+
display: 'inline-flex',
30+
alignItems: 'center',
31+
gap: '8px',
32+
transition: 'opacity 0.2s ease',
3033
}}
3134
>
32-
{getLabel()}
35+
{isSubmitting && <span className="superdoc-esign-spinner superdoc-esign-spinner--light" />}
36+
{isSubmitting ? 'Submitting...' : label}
3337
</button>
3438
);
3539
};

src/index.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const SuperDocESign = forwardRef<Types.SuperDocESignHandle, Types.SuperDocESignP
3636
const [fieldValues, setFieldValues] = useState<Map<string, Types.FieldValue>>(new Map());
3737
const [isValid, setIsValid] = useState(false);
3838
const [isSubmitting, setIsSubmitting] = useState(false);
39+
const [isDownloading, setIsDownloading] = useState(false);
3940
const [auditTrail, setAuditTrail] = useState<Types.AuditEvent[]>([]);
4041
const [isReady, setIsReady] = useState(false);
4142

@@ -298,7 +299,9 @@ const SuperDocESign = forwardRef<Types.SuperDocESignHandle, Types.SuperDocESignP
298299
}, [scrolled, fieldValues, isSubmitting, checkIsValid, onStateChange]);
299300

300301
const handleDownload = useCallback(async () => {
301-
if (isDisabled) return;
302+
if (isDisabled || isDownloading) return;
303+
304+
setIsDownloading(true);
302305

303306
const downloadData: Types.DownloadData = {
304307
eventId,
@@ -313,8 +316,21 @@ const SuperDocESign = forwardRef<Types.SuperDocESignHandle, Types.SuperDocESignP
313316
fileName: download?.fileName || 'document.pdf',
314317
};
315318

316-
await onDownload?.(downloadData);
317-
}, [isDisabled, eventId, document.source, fields, fieldValues, download, onDownload]);
319+
try {
320+
await onDownload?.(downloadData);
321+
} finally {
322+
setIsDownloading(false);
323+
}
324+
}, [
325+
isDisabled,
326+
isDownloading,
327+
eventId,
328+
document.source,
329+
fields,
330+
fieldValues,
331+
download,
332+
onDownload,
333+
]);
318334

319335
const handleSubmit = useCallback(async () => {
320336
if (!isValid || isDisabled || isSubmitting) return;
@@ -378,6 +394,7 @@ const SuperDocESign = forwardRef<Types.SuperDocESignHandle, Types.SuperDocESignP
378394
onClick={handleDownload}
379395
fileName={download?.fileName}
380396
isDisabled={isDisabled}
397+
isDownloading={isDownloading}
381398
/>
382399
);
383400
};

src/styles.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,29 @@
4949
display: flex;
5050
gap: 10px;
5151
}
52+
53+
/* Button loading state */
54+
.superdoc-esign-btn--loading {
55+
position: relative;
56+
}
57+
58+
/* Spinner */
59+
.superdoc-esign-spinner {
60+
width: 16px;
61+
height: 16px;
62+
border: 2px solid #d0d5dd;
63+
border-top-color: #333;
64+
border-radius: 50%;
65+
animation: superdoc-esign-spin 0.8s linear infinite;
66+
}
67+
68+
.superdoc-esign-spinner--light {
69+
border-color: rgba(255, 255, 255, 0.3);
70+
border-top-color: #fff;
71+
}
72+
73+
@keyframes superdoc-esign-spin {
74+
to {
75+
transform: rotate(360deg);
76+
}
77+
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface DownloadButtonProps {
3333
onClick: () => void;
3434
fileName?: string;
3535
isDisabled: boolean;
36+
isDownloading: boolean;
3637
}
3738

3839
export interface SubmitButtonProps {

0 commit comments

Comments
 (0)