@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.
Files changed (27) hide show
  1. package/.smartconfig.json +19 -6
  2. package/dist_bundle/bundle.js +338 -334
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/sdig-signbox/sdig-signbox.d.ts +1 -0
  5. package/dist_ts_web/elements/sdig-signbox/sdig-signbox.js +23 -9
  6. package/dist_ts_web/elements/sdig-signpad/sdig-signpad.d.ts +5 -2
  7. package/dist_ts_web/elements/sdig-signpad/sdig-signpad.js +12 -4
  8. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.d.ts +7 -2
  9. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.js +73 -11
  10. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-inbox.js +3 -2
  11. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.d.ts +5 -1
  12. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.js +64 -19
  13. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.d.ts +3 -1
  14. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.js +21 -5
  15. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.shared.js +11 -10
  16. package/dist_ts_web/plugins.d.ts +3 -3
  17. package/dist_ts_web/plugins.js +3 -4
  18. package/package.json +13 -12
  19. package/ts_web/00_commitinfo_data.ts +1 -1
  20. package/ts_web/elements/sdig-signbox/sdig-signbox.ts +21 -8
  21. package/ts_web/elements/sdig-signpad/sdig-signpad.ts +15 -5
  22. package/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts +65 -9
  23. package/ts_web/elements/sdig-workspace/sdig-workspace-inbox.ts +2 -1
  24. package/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts +54 -17
  25. package/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts +11 -9
  26. package/ts_web/elements/sdig-workspace/sdig-workspace.ts +5 -3
  27. 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
- @state() private accessor recipients: IRecipient[] = [...demoRecipients];
75
- @state() private accessor fields: IFieldPlacement[] = [...demoFields];
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 offset = this.draggedFieldGrabOffset || (transferredOffset ? JSON.parse(transferredOffset) as { x: number; y: number } : { x: definition.w / 2, y: definition.h / 2 });
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: 'Master Services Agreement', subtitle: pill('Draft · auto-saved'), actions: html`${actionButton('Save draft', 'ghost')}${actionButton('Preview', 'outline', 'eye')}${actionButton('Send for signature', 'primary', 'send')}` })}
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 14</div>
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 / 14</span>`}${actionButton('Next', 'outline')}</div>
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
- requestWorkspaceView(this, doc.status === 'signed' ? 'audit' : 'sign');
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 demoFields.slice(0, 3);
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
- if (!this.signedFieldIds.includes(fieldId)) {
42
- this.signedFieldIds = [...this.signedFieldIds, fieldId];
43
- }
44
- const next = this.signFields.find((field) => !this.signedFieldIds.includes(field.id) && field.id !== fieldId);
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-family: 'Plus Jakarta Sans', cursive; font-size: 22px; font-weight: 600; font-style: italic; color: hsl(220 50% 30%);">Sarah Chen</span>`;
50
- if (field.type === 'date') return html`<span class="mono" style="font-size: 12px; color: hsl(0 0% 18%);">2026-05-02</span>`;
51
- return html`<span style="font-size: 12px; color: hsl(0 0% 18%);">Sarah Chen</span>`;
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 progress = Math.round((completed / this.signFields.length) * 100);
58
- const activeField = this.signFields.find((field) => field.id === this.activeFieldId) || this.signFields[0];
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 success">${icon('shield', 12)} Verified sender · DKIM ✓</span>${actionButton('Decline', 'outline')}${actionButton('PDF', 'outline', 'download')}</div>
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 = this.activeFieldId === field.id && !filled;
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;">SC</span><div><div style="font-size: 13px; font-weight: 600;">Hi, Sarah</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">sarah@acme.com</div></div></div>
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;">/ ${this.signFields.length}</span></div><div style="font-size: 11px; color: var(--text-muted); margin-top: 2px;">${this.signFields.length - completed === 0 ? 'All fields complete' : `${this.signFields.length - completed} fields remaining`}</div><div class="progress-track" style="margin-top: 12px;"><div class="progress-fill" style="width: ${progress}%"></div></div></div>
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 = this.activeFieldId === field.id && !filled;
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.signField(activeField.id)}>${completed === this.signFields.length ? 'Finish & submit' : `Continue - ${activeField.label}`}</button>
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);">By signing, you agree to the ESIGN Act & eIDAS terms.<br /><span class="mono">IP 81.221.4.18 · Brussels, BE</span></div>
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}" @click=${onClick || (() => undefined)}>${iconName ? icon(iconName, 13) : ''}${label}</button>`;
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 signaturePadMod from 'signature_pad';
3
- type signaturePadType = (typeof import('signature_pad'))['default'];
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
  }