@signature.digital/catalog 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.smartconfig.json +19 -6
- package/dist_bundle/bundle.js +338 -334
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/sdig-signbox/sdig-signbox.d.ts +1 -0
- package/dist_ts_web/elements/sdig-signbox/sdig-signbox.js +23 -9
- package/dist_ts_web/elements/sdig-signpad/sdig-signpad.d.ts +5 -2
- package/dist_ts_web/elements/sdig-signpad/sdig-signpad.js +12 -4
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.d.ts +7 -2
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.js +73 -11
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace-inbox.js +3 -2
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.d.ts +5 -1
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.js +64 -19
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace.d.ts +3 -1
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace.js +21 -5
- package/dist_ts_web/elements/sdig-workspace/sdig-workspace.shared.js +11 -10
- package/dist_ts_web/plugins.d.ts +3 -3
- package/dist_ts_web/plugins.js +3 -4
- package/package.json +13 -12
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/sdig-signbox/sdig-signbox.ts +21 -8
- package/ts_web/elements/sdig-signpad/sdig-signpad.ts +15 -5
- package/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts +65 -9
- package/ts_web/elements/sdig-workspace/sdig-workspace-inbox.ts +2 -1
- package/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts +54 -17
- package/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts +11 -9
- package/ts_web/elements/sdig-workspace/sdig-workspace.ts +5 -3
- package/ts_web/plugins.ts +3 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DeesElement, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
|
|
2
|
-
import { actionButton, demoFields, demoRecipients, fakeDocument, icon, pill, topBar, workspaceBaseStyles, workspaceDemoFrame, type IFieldPlacement, type IRecipient, type TRecipientRole } from './sdig-workspace.shared.js';
|
|
1
|
+
import { DeesElement, property, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
|
|
2
|
+
import { actionButton, demoDocuments, demoFields, demoRecipients, fakeDocument, icon, pill, topBar, workspaceBaseStyles, workspaceDemoFrame, type IDocumentRow, type IFieldPlacement, type IRecipient, type TRecipientRole } from './sdig-workspace.shared.js';
|
|
3
3
|
import '../sdig-contextmenu/index.js';
|
|
4
4
|
import { type ISdigContextMenuAction, type ISdigContextMenuActionEventDetail } from '../sdig-contextmenu/index.js';
|
|
5
5
|
|
|
@@ -71,8 +71,9 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
71
71
|
@state() private accessor step: number = 2;
|
|
72
72
|
@state() private accessor activeRecipient: number = 0;
|
|
73
73
|
@state() private accessor selectedFieldId: string | null = null;
|
|
74
|
-
@
|
|
75
|
-
@
|
|
74
|
+
@property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0];
|
|
75
|
+
@property({ attribute: false }) public accessor recipients: IRecipient[] = [...demoRecipients];
|
|
76
|
+
@property({ attribute: false }) public accessor fields: IFieldPlacement[] = [...demoFields];
|
|
76
77
|
@state() private accessor signingOrderDrag: TSigningOrderDrag | null = null;
|
|
77
78
|
@state() private accessor recipientContextMenu: TRecipientContextMenu | null = null;
|
|
78
79
|
private draggedFieldDefinition: TFieldDefinition | null = null;
|
|
@@ -169,8 +170,46 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
169
170
|
return Math.max(min, Math.min(max, value));
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
private parseDragOffset(rawOffset: string | undefined, fallback: { x: number; y: number }): { x: number; y: number } {
|
|
174
|
+
if (!rawOffset) return fallback;
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(rawOffset) as Partial<{ x: number; y: number }>;
|
|
177
|
+
return Number.isFinite(parsed.x) && Number.isFinite(parsed.y) ? { x: parsed.x!, y: parsed.y! } : fallback;
|
|
178
|
+
} catch {
|
|
179
|
+
return fallback;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private emitFieldsChange() {
|
|
184
|
+
this.dispatchEvent(new CustomEvent('fields-change', {
|
|
185
|
+
detail: { fields: this.fields },
|
|
186
|
+
bubbles: true,
|
|
187
|
+
composed: true,
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private emitRecipientsChange() {
|
|
192
|
+
this.dispatchEvent(new CustomEvent('recipients-change', {
|
|
193
|
+
detail: { recipients: this.recipients },
|
|
194
|
+
bubbles: true,
|
|
195
|
+
composed: true,
|
|
196
|
+
}));
|
|
197
|
+
this.dispatchEvent(new CustomEvent('routing-change', {
|
|
198
|
+
detail: { recipients: this.recipients },
|
|
199
|
+
bubbles: true,
|
|
200
|
+
composed: true,
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
|
|
172
204
|
private updateField(fieldId: string, patch: Partial<IFieldPlacement>) {
|
|
173
205
|
this.fields = this.fields.map((field) => field.id === fieldId ? { ...field, ...patch } : field);
|
|
206
|
+
const field = this.fields.find((currentField) => currentField.id === fieldId);
|
|
207
|
+
this.dispatchEvent(new CustomEvent('field-update', {
|
|
208
|
+
detail: { fieldId, field, patch, fields: this.fields },
|
|
209
|
+
bubbles: true,
|
|
210
|
+
composed: true,
|
|
211
|
+
}));
|
|
212
|
+
this.emitFieldsChange();
|
|
174
213
|
}
|
|
175
214
|
|
|
176
215
|
private updateSelectedField(patch: Partial<IFieldPlacement>) {
|
|
@@ -192,8 +231,15 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
192
231
|
|
|
193
232
|
private removeSelectedField() {
|
|
194
233
|
if (!this.selectedFieldId) return;
|
|
234
|
+
const field = this.fields.find((currentField) => currentField.id === this.selectedFieldId);
|
|
195
235
|
this.fields = this.fields.filter((field) => field.id !== this.selectedFieldId);
|
|
236
|
+
this.dispatchEvent(new CustomEvent('field-delete', {
|
|
237
|
+
detail: { fieldId: this.selectedFieldId, field, fields: this.fields },
|
|
238
|
+
bubbles: true,
|
|
239
|
+
composed: true,
|
|
240
|
+
}));
|
|
196
241
|
this.selectedFieldId = null;
|
|
242
|
+
this.emitFieldsChange();
|
|
197
243
|
}
|
|
198
244
|
|
|
199
245
|
private updateRecipientRole(recipientId: number, role: TRecipientRole) {
|
|
@@ -220,6 +266,7 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
220
266
|
targetMembers.splice(insertIndex, 0, { ...recipient, role: nextRole });
|
|
221
267
|
nextByRole.set(nextRole, targetMembers);
|
|
222
268
|
this.recipients = recipientRoleDefinitions.flatMap((roleDefinition) => nextByRole.get(roleDefinition.role) || []).map((currentRecipient, index) => ({ ...currentRecipient, order: index + 1 }));
|
|
269
|
+
this.emitRecipientsChange();
|
|
223
270
|
|
|
224
271
|
const nextSigners = this.recipients.filter((currentRecipient) => currentRecipient.role === 'signer');
|
|
225
272
|
const fallbackSigner = nextSigners[0];
|
|
@@ -229,6 +276,7 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
229
276
|
if (this.activeRecipient === recipientId) {
|
|
230
277
|
this.activeRecipient = fallbackSigner.id;
|
|
231
278
|
}
|
|
279
|
+
this.emitFieldsChange();
|
|
232
280
|
}
|
|
233
281
|
}
|
|
234
282
|
|
|
@@ -435,12 +483,13 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
435
483
|
if (!this.draggedFieldDefinition && !transferredType) return;
|
|
436
484
|
const definition = this.draggedFieldDefinition || this.fieldDefinition(transferredType);
|
|
437
485
|
const transferredOffset = event.dataTransfer?.getData('application/x-signature-field-offset');
|
|
438
|
-
const
|
|
486
|
+
const fallbackOffset = { x: definition.w / 2, y: definition.h / 2 };
|
|
487
|
+
const offset = this.draggedFieldGrabOffset || this.parseDragOffset(transferredOffset, fallbackOffset);
|
|
439
488
|
const rect = page.getBoundingClientRect();
|
|
440
489
|
const x = Math.round(event.clientX - rect.left - offset.x);
|
|
441
490
|
const y = Math.round(event.clientY - rect.top - offset.y);
|
|
442
491
|
const nextField: IFieldPlacement = {
|
|
443
|
-
id: `field_${Date.now()}`,
|
|
492
|
+
id: globalThis.crypto?.randomUUID?.() || `field_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
444
493
|
type: definition.type,
|
|
445
494
|
x: Math.max(0, Math.min(Math.max(0, rect.width - definition.w), x)),
|
|
446
495
|
y: Math.max(0, Math.min(Math.max(0, rect.height - definition.h), y)),
|
|
@@ -454,6 +503,12 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
454
503
|
this.selectedFieldId = nextField.id;
|
|
455
504
|
this.draggedFieldDefinition = null;
|
|
456
505
|
this.draggedFieldGrabOffset = null;
|
|
506
|
+
this.dispatchEvent(new CustomEvent('field-create', {
|
|
507
|
+
detail: { field: nextField, fields: this.fields },
|
|
508
|
+
bubbles: true,
|
|
509
|
+
composed: true,
|
|
510
|
+
}));
|
|
511
|
+
this.emitFieldsChange();
|
|
457
512
|
}
|
|
458
513
|
|
|
459
514
|
private startFieldToolDrag(event: DragEvent, fieldType: TFieldDefinition) {
|
|
@@ -567,10 +622,11 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
567
622
|
}
|
|
568
623
|
|
|
569
624
|
public render(): TemplateResult {
|
|
625
|
+
const document = this.document || demoDocuments[0];
|
|
570
626
|
const selectedField = this.fields.find((field) => field.id === this.selectedFieldId);
|
|
571
627
|
|
|
572
628
|
return html`
|
|
573
|
-
${topBar({ breadcrumb: ['signature.digital', 'Inbox', 'Compose'], title:
|
|
629
|
+
${topBar({ breadcrumb: ['signature.digital', 'Inbox', 'Compose'], title: document.title, subtitle: pill('Draft · auto-saved'), actions: html`${actionButton('Save draft', 'ghost')}${actionButton('Preview', 'outline', 'eye')}${actionButton('Send for signature', 'primary', 'send')}` })}
|
|
574
630
|
${this.renderStepper()}
|
|
575
631
|
<div class="compose-workspace">
|
|
576
632
|
${this.renderRecipientContextMenu()}
|
|
@@ -585,9 +641,9 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|
|
585
641
|
<div class="document-page page-drop-target" @click=${this.handleDocumentClick} @dragover=${(event: DragEvent) => { event.preventDefault(); if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'; (event.currentTarget as HTMLElement).classList.add('drag-over'); }} @dragleave=${(event: DragEvent) => (event.currentTarget as HTMLElement).classList.remove('drag-over')} @drop=${(event: DragEvent) => this.addFieldFromDrop(event)}>
|
|
586
642
|
${fakeDocument()}
|
|
587
643
|
${this.fields.map((field) => html`<div class="field-box ${this.selectedFieldId === field.id ? 'selected' : ''}" style="--x: ${field.x}px; --y: ${field.y}px; --w: ${field.w}px; --h: ${field.h}px; --field-color: ${this.recipientColor(field.recipient)};" @click=${() => this.selectedFieldId = field.id} @pointerdown=${(event: PointerEvent) => this.startFieldMove(event, field)}><div class="field-content">${icon(this.fieldIcon(field.type), 12)}<span>${field.label}</span></div>${this.selectedFieldId === field.id ? this.renderResizeHandles(field) : ''}</div>`)}
|
|
588
|
-
<div class="mono" style="position: absolute; bottom: 12px; right: 16px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of
|
|
644
|
+
<div class="mono" style="position: absolute; bottom: 12px; right: 16px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of ${document.pages}</div>
|
|
589
645
|
</div>
|
|
590
|
-
<div style="display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--text-muted);">${actionButton('Prev', 'outline')}${html`<span class="mono">1 /
|
|
646
|
+
<div style="display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--text-muted);">${actionButton('Prev', 'outline')}${html`<span class="mono">1 / ${document.pages}</span>`}${actionButton('Next', 'outline')}</div>
|
|
591
647
|
</div>
|
|
592
648
|
<div class="right-panel">
|
|
593
649
|
<div class="label-upper">Routing order · drag to reorder</div>
|
|
@@ -68,7 +68,8 @@ export class SdigWorkspaceInbox extends DeesElement {
|
|
|
68
68
|
bubbles: true,
|
|
69
69
|
composed: true,
|
|
70
70
|
}));
|
|
71
|
-
|
|
71
|
+
const view = doc.status === 'draft' ? 'compose' : doc.status === 'signed' || doc.status === 'declined' ? 'audit' : 'sign';
|
|
72
|
+
requestWorkspaceView(this, view);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
public render(): TemplateResult {
|
|
@@ -13,6 +13,7 @@ export class SdigWorkspaceSign extends DeesElement {
|
|
|
13
13
|
public static demoGroups = ['Signature Digital Workspace'];
|
|
14
14
|
|
|
15
15
|
@property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0];
|
|
16
|
+
@property({ attribute: false }) public accessor fields: IFieldPlacement[] = demoFields;
|
|
16
17
|
@state() private accessor activeFieldId: string = 'f1';
|
|
17
18
|
@state() private accessor signedFieldIds: string[] = [];
|
|
18
19
|
|
|
@@ -28,7 +29,13 @@ export class SdigWorkspaceSign extends DeesElement {
|
|
|
28
29
|
`];
|
|
29
30
|
|
|
30
31
|
private get signFields() {
|
|
31
|
-
return
|
|
32
|
+
return this.fields;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private get activeField(): IFieldPlacement | undefined {
|
|
36
|
+
return this.signFields.find((field) => field.id === this.activeFieldId && !this.signedFieldIds.includes(field.id))
|
|
37
|
+
|| this.signFields.find((field) => !this.signedFieldIds.includes(field.id))
|
|
38
|
+
|| this.signFields[0];
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
private fieldIcon(type: IFieldPlacement['type']): string {
|
|
@@ -38,29 +45,59 @@ export class SdigWorkspaceSign extends DeesElement {
|
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
private signField(fieldId: string) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
const field = this.signFields.find((currentField) => currentField.id === fieldId);
|
|
49
|
+
if (!field || this.signedFieldIds.includes(fieldId)) return;
|
|
50
|
+
this.signedFieldIds = [...this.signedFieldIds, fieldId];
|
|
51
|
+
this.dispatchEvent(new CustomEvent('field-signed', {
|
|
52
|
+
detail: { document: this.document, field, signedFieldIds: this.signedFieldIds },
|
|
53
|
+
bubbles: true,
|
|
54
|
+
composed: true,
|
|
55
|
+
}));
|
|
56
|
+
const next = this.signFields.find((currentField) => !this.signedFieldIds.includes(currentField.id));
|
|
45
57
|
if (next) this.activeFieldId = next.id;
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
private emitSigningComplete() {
|
|
61
|
+
if (this.signedFieldIds.length !== this.signFields.length) return;
|
|
62
|
+
this.dispatchEvent(new CustomEvent('signing-complete', {
|
|
63
|
+
detail: {
|
|
64
|
+
document: this.document,
|
|
65
|
+
fields: this.signFields,
|
|
66
|
+
signedFieldIds: this.signedFieldIds,
|
|
67
|
+
completedAt: Date.now(),
|
|
68
|
+
},
|
|
69
|
+
bubbles: true,
|
|
70
|
+
composed: true,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private handlePrimaryAction() {
|
|
75
|
+
if (!this.signFields.length) return;
|
|
76
|
+
if (this.signedFieldIds.length === this.signFields.length) {
|
|
77
|
+
this.emitSigningComplete();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const activeField = this.activeField;
|
|
81
|
+
if (activeField) this.signField(activeField.id);
|
|
82
|
+
}
|
|
83
|
+
|
|
48
84
|
private renderSignedValue(field: IFieldPlacement): TemplateResult {
|
|
49
|
-
if (field.type === 'signature') return html`<span style="font-
|
|
50
|
-
if (field.type === 'date') return html`<span class="mono" style="font-size: 12px; color: hsl(0 0% 18%);">
|
|
51
|
-
return html`<span style="font-size: 12px; color: hsl(0 0% 18%);">
|
|
85
|
+
if (field.type === 'signature') return html`<span style="font-size: 12px; font-weight: 600; color: hsl(220 50% 30%);">Signature captured</span>`;
|
|
86
|
+
if (field.type === 'date') return html`<span class="mono" style="font-size: 12px; color: hsl(0 0% 18%);">Date captured</span>`;
|
|
87
|
+
return html`<span style="font-size: 12px; color: hsl(0 0% 18%);">Completed</span>`;
|
|
52
88
|
}
|
|
53
89
|
|
|
54
90
|
public render(): TemplateResult {
|
|
55
91
|
const document = this.document || demoDocuments[0];
|
|
56
92
|
const completed = this.signedFieldIds.length;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
93
|
+
const total = this.signFields.length;
|
|
94
|
+
const progress = total ? Math.round((completed / total) * 100) : 0;
|
|
95
|
+
const activeField = this.activeField;
|
|
59
96
|
|
|
60
97
|
return html`
|
|
61
98
|
<div class="recipient-header">
|
|
62
99
|
<div style="display: flex; align-items: center; gap: 12px;"><span class="logomark">s</span><div><div style="font-size: 12px; font-weight: 600;">${document.title}</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">From ${document.sender} · ${document.id} · ${document.pages} pages</div></div></div>
|
|
63
|
-
<div class="actions"><span class="pill
|
|
100
|
+
<div class="actions"><span class="pill info">${icon('shield', 12)} Sender details supplied by host</span>${actionButton('Decline', 'outline')}${actionButton('PDF', 'outline', 'download')}</div>
|
|
64
101
|
</div>
|
|
65
102
|
<div class="progress-track"><div class="progress-fill" style="width: ${progress}%"></div></div>
|
|
66
103
|
<div class="sign-layout">
|
|
@@ -69,23 +106,23 @@ export class SdigWorkspaceSign extends DeesElement {
|
|
|
69
106
|
${fakeDocument()}
|
|
70
107
|
${this.signFields.map((field) => {
|
|
71
108
|
const filled = this.signedFieldIds.includes(field.id);
|
|
72
|
-
const active =
|
|
109
|
+
const active = activeField?.id === field.id && !filled;
|
|
73
110
|
return html`<div class="field-box ${active ? 'selected' : ''}" style="--x: ${field.x}px; --y: ${field.y}px; --w: ${field.w}px; --h: ${field.h}px; --field-color: ${filled ? 'transparent' : 'var(--accent)'}; color: ${filled ? 'hsl(220 50% 30%)' : 'var(--accent)'}; background: ${filled ? 'transparent' : 'color-mix(in srgb, var(--accent) 12%, transparent)'};" @click=${() => !filled ? this.signField(field.id) : undefined}>${filled ? this.renderSignedValue(field) : html`${icon(this.fieldIcon(field.type), 12)}<span>${active ? html`<span style="display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); animation: pulse 1.4s infinite;"></span>` : ''}${field.label}</span>`}</div>`;
|
|
74
111
|
})}
|
|
75
112
|
<div class="mono" style="position: absolute; bottom: 14px; right: 18px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of ${document.pages}</div>
|
|
76
113
|
</div>
|
|
77
114
|
</div>
|
|
78
115
|
<div class="sign-panel">
|
|
79
|
-
<div style="display: flex; align-items: center; gap: 8px;"><span class="avatar" style="background: #60a5fa;">
|
|
80
|
-
<div class="card" style="padding: 14px; margin-top: 16px;"><div class="label-upper">Your progress</div><div style="font-size: 24px; font-weight: 700;">${completed} <span style="color: var(--text-muted); font-weight: 400;">/ ${
|
|
116
|
+
<div style="display: flex; align-items: center; gap: 8px;"><span class="avatar" style="background: #60a5fa;">SG</span><div><div style="font-size: 13px; font-weight: 600;">Signing session</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">${document.id}</div></div></div>
|
|
117
|
+
<div class="card" style="padding: 14px; margin-top: 16px;"><div class="label-upper">Your progress</div><div style="font-size: 24px; font-weight: 700;">${completed} <span style="color: var(--text-muted); font-weight: 400;">/ ${total}</span></div><div style="font-size: 11px; color: var(--text-muted); margin-top: 2px;">${total === 0 ? 'No fields assigned' : total - completed === 0 ? 'All fields complete' : `${total - completed} fields remaining`}</div><div class="progress-track" style="margin-top: 12px;"><div class="progress-fill" style="width: ${progress}%"></div></div></div>
|
|
81
118
|
<div style="margin-top: 20px;" class="label-upper">Step by step</div>
|
|
82
119
|
${this.signFields.map((field, index) => {
|
|
83
120
|
const filled = this.signedFieldIds.includes(field.id);
|
|
84
|
-
const active =
|
|
121
|
+
const active = activeField?.id === field.id && !filled;
|
|
85
122
|
return html`<div class="recipient-line ${active ? 'active' : ''}" @click=${() => !filled ? this.activeFieldId = field.id : undefined}><span class="avatar" style="width: 22px; height: 22px; background: ${filled ? 'var(--success)' : active ? 'var(--accent)' : 'var(--bg-input)'}; color: ${filled || active ? 'white' : 'var(--text-muted)'};">${filled ? '✓' : index + 1}</span><div style="flex: 1;"><div style="font-size: 12px; font-weight: 500; text-decoration: ${filled ? 'line-through' : 'none'};">${field.label}</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">${field.type} · page ${field.page}</div></div>${active ? icon('chevronRight', 12) : ''}</div>`;
|
|
86
123
|
})}
|
|
87
|
-
<button class="btn primary" style="width: 100%; height: 44px; margin-top: 20px;" @click=${() => this.
|
|
88
|
-
<div style="margin-top: 12px; padding: 10px; font-size: 10px; color: var(--text-muted); line-height: 1.5; text-align: center; border-radius: 6px; background: var(--bg-el);">
|
|
124
|
+
<button class="btn primary" style="width: 100%; height: 44px; margin-top: 20px;" ?disabled=${total === 0} @click=${() => this.handlePrimaryAction()}>${total === 0 ? 'No fields to sign' : completed === total ? 'Finish & submit' : `Continue - ${activeField?.label || 'next field'}`}</button>
|
|
125
|
+
<div style="margin-top: 12px; padding: 10px; font-size: 10px; color: var(--text-muted); line-height: 1.5; text-align: center; border-radius: 6px; background: var(--bg-el);">Field completion is captured locally until the host application submits it through a signing API.</div>
|
|
89
126
|
</div>
|
|
90
127
|
</div>
|
|
91
128
|
`;
|
|
@@ -84,6 +84,7 @@ export const workspaceBaseStyles = css`
|
|
|
84
84
|
* { box-sizing: border-box; }
|
|
85
85
|
button, input, textarea { font: inherit; }
|
|
86
86
|
button { border: 0; cursor: pointer; }
|
|
87
|
+
button:disabled { cursor: not-allowed; opacity: 0.52; }
|
|
87
88
|
dees-icon { flex-shrink: 0; }
|
|
88
89
|
|
|
89
90
|
.mono {
|
|
@@ -341,23 +342,24 @@ export const workspaceBaseStyles = css`
|
|
|
341
342
|
`;
|
|
342
343
|
|
|
343
344
|
export function icon(name: string, size = 14): TemplateResult {
|
|
344
|
-
const iconMap: Record<string, string> = {
|
|
345
|
-
inbox: 'lucide:Inbox', plus: 'lucide:Plus', folder: 'lucide:Folder', shield: 'lucide:Shield', code: 'lucide:Code2',
|
|
346
|
-
user: 'lucide:User', settings: 'lucide:Settings', upload: 'lucide:Upload', file: 'lucide:FileText', sign: 'lucide:PenTool',
|
|
347
|
-
clock: 'lucide:Clock', search: 'lucide:Search', more: 'lucide:MoreHorizontal', send: 'lucide:Send', check: 'lucide:Check',
|
|
348
|
-
eye: 'lucide:Eye', calendar: 'lucide:Calendar', type: 'lucide:Type', download: 'lucide:Download', hash: 'lucide:Hash',
|
|
349
|
-
github: 'lucide:GitBranch', git: 'lucide:GitBranch', server: 'lucide:Server', star: 'lucide:Star', sparkle: 'lucide:Sparkles',
|
|
350
|
-
chevronRight: 'lucide:ChevronRight', chevronDown: 'lucide:ChevronDown', x: 'lucide:X', activity: 'lucide:Activity',
|
|
351
|
-
};
|
|
352
345
|
return html`<dees-icon .icon=${iconMap[name] || iconMap.file} style="font-size: ${size}px;"></dees-icon>`;
|
|
353
346
|
}
|
|
354
347
|
|
|
348
|
+
const iconMap: Record<string, string> = {
|
|
349
|
+
inbox: 'lucide:Inbox', plus: 'lucide:Plus', folder: 'lucide:Folder', shield: 'lucide:Shield', code: 'lucide:Code2',
|
|
350
|
+
user: 'lucide:User', settings: 'lucide:Settings', upload: 'lucide:Upload', file: 'lucide:FileText', sign: 'lucide:PenTool',
|
|
351
|
+
clock: 'lucide:Clock', search: 'lucide:Search', more: 'lucide:MoreHorizontal', send: 'lucide:Send', check: 'lucide:Check',
|
|
352
|
+
eye: 'lucide:Eye', calendar: 'lucide:Calendar', type: 'lucide:Type', download: 'lucide:Download', hash: 'lucide:Hash',
|
|
353
|
+
github: 'lucide:GitBranch', git: 'lucide:GitBranch', server: 'lucide:Server', star: 'lucide:Star', sparkle: 'lucide:Sparkles',
|
|
354
|
+
chevronRight: 'lucide:ChevronRight', chevronDown: 'lucide:ChevronDown', x: 'lucide:X', activity: 'lucide:Activity',
|
|
355
|
+
};
|
|
356
|
+
|
|
355
357
|
export function pill(label: string, tone: 'default' | 'success' | 'warning' | 'error' | 'info' = 'default', dot = false): TemplateResult {
|
|
356
358
|
return html`<span class="pill ${tone} ${dot ? 'dot' : ''}">${label}</span>`;
|
|
357
359
|
}
|
|
358
360
|
|
|
359
361
|
export function actionButton(label: string, variant: 'primary' | 'outline' | 'ghost' = 'outline', iconName?: string, onClick?: () => void): TemplateResult {
|
|
360
|
-
return html`<button class="btn ${variant}"
|
|
362
|
+
return html`<button class="btn ${variant}" ?disabled=${!onClick} title=${onClick ? '' : 'This action must be wired by the host application.'} @click=${onClick}>${iconName ? icon(iconName, 13) : ''}${label}</button>`;
|
|
361
363
|
}
|
|
362
364
|
|
|
363
365
|
export function topBar(config: { breadcrumb: string[]; title: string; subtitle?: TemplateResult; actions?: TemplateResult }): TemplateResult {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DeesElement, property, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
|
|
2
|
-
import { demoDocuments, icon, type IDocumentRow, type TDensity, type TWorkspaceTheme, type TWorkspaceView } from './sdig-workspace.shared.js';
|
|
2
|
+
import { demoDocuments, demoFields, demoRecipients, icon, type IDocumentRow, type IFieldPlacement, type IRecipient, type TDensity, type TWorkspaceTheme, type TWorkspaceView } from './sdig-workspace.shared.js';
|
|
3
3
|
import './sdig-workspace-inbox.js';
|
|
4
4
|
import './sdig-workspace-compose.js';
|
|
5
5
|
import './sdig-workspace-sign.js';
|
|
@@ -25,6 +25,8 @@ export class SdigWorkspace extends DeesElement {
|
|
|
25
25
|
@property({ type: String, reflect: true }) public accessor view: TWorkspaceView = 'inbox';
|
|
26
26
|
@property({ attribute: false }) public accessor documents: IDocumentRow[] = demoDocuments;
|
|
27
27
|
@property({ type: String }) public accessor activeDocumentId: string = '';
|
|
28
|
+
@property({ attribute: false }) public accessor recipients: IRecipient[] = demoRecipients;
|
|
29
|
+
@property({ attribute: false }) public accessor fields: IFieldPlacement[] = demoFields;
|
|
28
30
|
|
|
29
31
|
public connectedCallback = async () => {
|
|
30
32
|
await super.connectedCallback();
|
|
@@ -163,8 +165,8 @@ export class SdigWorkspace extends DeesElement {
|
|
|
163
165
|
const activeDocument = this.documents.find((document) => document.id === this.activeDocumentId) || this.documents[0] || demoDocuments[0];
|
|
164
166
|
switch (this.view) {
|
|
165
167
|
case 'inbox': return html`<sdig-workspace-inbox class="view-host" .density=${this.density} .documents=${this.documents}></sdig-workspace-inbox>`;
|
|
166
|
-
case 'compose': return html`<sdig-workspace-compose class="view-host"></sdig-workspace-compose>`;
|
|
167
|
-
case 'sign': return html`<sdig-workspace-sign class="view-host" .document=${activeDocument}></sdig-workspace-sign>`;
|
|
168
|
+
case 'compose': return html`<sdig-workspace-compose class="view-host" .document=${activeDocument} .recipients=${this.recipients} .fields=${this.fields}></sdig-workspace-compose>`;
|
|
169
|
+
case 'sign': return html`<sdig-workspace-sign class="view-host" .document=${activeDocument} .fields=${this.fields}></sdig-workspace-sign>`;
|
|
168
170
|
case 'audit': return html`<sdig-workspace-audit class="view-host" .document=${activeDocument}></sdig-workspace-audit>`;
|
|
169
171
|
case 'developers': return html`<sdig-workspace-developers class="view-host"></sdig-workspace-developers>`;
|
|
170
172
|
case 'templates': return html`<sdig-workspace-placeholder class="view-host" label="Templates" subtitle="Reusable agreement templates"></sdig-workspace-placeholder>`;
|
package/ts_web/plugins.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// third party
|
|
2
|
-
import
|
|
3
|
-
type
|
|
4
|
-
const signaturePad = signaturePadMod as any as signaturePadType;
|
|
2
|
+
import SignaturePad from 'signature_pad';
|
|
3
|
+
export type { PointGroup as TSignaturePadPointGroup } from 'signature_pad';
|
|
5
4
|
|
|
6
5
|
export {
|
|
7
|
-
signaturePad,
|
|
6
|
+
SignaturePad as signaturePad,
|
|
8
7
|
}
|