diff --git a/changelog.md b/changelog.md index f78bdf9..45719de 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-05-05 - 1.7.0 - feat(workspace) +make compose and sign views accept document, recipient, and field state via properties and emit field and routing change events + +- Pass active document, recipients, and fields into workspace compose and sign views instead of relying on demo-only state. +- Emit field-create, field-update, field-delete, fields-change, recipients-change, and routing-change events from the compose workspace. +- Use document metadata for compose titles and page counts so the UI reflects the selected document. + ## 2026-05-05 - 1.6.1 - fix(workspace) pass the active document into sign and audit views diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 1e84d7a..447c7e4 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@signature.digital/catalog', - version: '1.6.1', + version: '1.7.0', description: 'A comprehensive catalog of customizable web components designed for building and managing e-signature applications.' } diff --git a/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts b/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts index e7e9eec..f1e8dc4 100644 --- a/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts +++ b/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts @@ -1,5 +1,5 @@ -import { DeesElement, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element'; -import { actionButton, demoFields, demoRecipients, fakeDocument, icon, pill, topBar, workspaceBaseStyles, workspaceDemoFrame, type IFieldPlacement, type IRecipient, type TRecipientRole } from './sdig-workspace.shared.js'; +import { DeesElement, property, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element'; +import { actionButton, demoDocuments, demoFields, demoRecipients, fakeDocument, icon, pill, topBar, workspaceBaseStyles, workspaceDemoFrame, type IDocumentRow, type IFieldPlacement, type IRecipient, type TRecipientRole } from './sdig-workspace.shared.js'; import '../sdig-contextmenu/index.js'; import { type ISdigContextMenuAction, type ISdigContextMenuActionEventDetail } from '../sdig-contextmenu/index.js'; @@ -71,8 +71,9 @@ export class SdigWorkspaceCompose extends DeesElement { @state() private accessor step: number = 2; @state() private accessor activeRecipient: number = 0; @state() private accessor selectedFieldId: string | null = null; - @state() private accessor recipients: IRecipient[] = [...demoRecipients]; - @state() private accessor fields: IFieldPlacement[] = [...demoFields]; + @property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0]; + @property({ attribute: false }) public accessor recipients: IRecipient[] = [...demoRecipients]; + @property({ attribute: false }) public accessor fields: IFieldPlacement[] = [...demoFields]; @state() private accessor signingOrderDrag: TSigningOrderDrag | null = null; @state() private accessor recipientContextMenu: TRecipientContextMenu | null = null; private draggedFieldDefinition: TFieldDefinition | null = null; @@ -169,8 +170,36 @@ export class SdigWorkspaceCompose extends DeesElement { return Math.max(min, Math.min(max, value)); } + private emitFieldsChange() { + this.dispatchEvent(new CustomEvent('fields-change', { + detail: { fields: this.fields }, + bubbles: true, + composed: true, + })); + } + + private emitRecipientsChange() { + this.dispatchEvent(new CustomEvent('recipients-change', { + detail: { recipients: this.recipients }, + bubbles: true, + composed: true, + })); + this.dispatchEvent(new CustomEvent('routing-change', { + detail: { recipients: this.recipients }, + bubbles: true, + composed: true, + })); + } + private updateField(fieldId: string, patch: Partial) { this.fields = this.fields.map((field) => field.id === fieldId ? { ...field, ...patch } : field); + const field = this.fields.find((currentField) => currentField.id === fieldId); + this.dispatchEvent(new CustomEvent('field-update', { + detail: { fieldId, field, patch, fields: this.fields }, + bubbles: true, + composed: true, + })); + this.emitFieldsChange(); } private updateSelectedField(patch: Partial) { @@ -192,8 +221,15 @@ export class SdigWorkspaceCompose extends DeesElement { private removeSelectedField() { if (!this.selectedFieldId) return; + const field = this.fields.find((currentField) => currentField.id === this.selectedFieldId); this.fields = this.fields.filter((field) => field.id !== this.selectedFieldId); + this.dispatchEvent(new CustomEvent('field-delete', { + detail: { fieldId: this.selectedFieldId, field, fields: this.fields }, + bubbles: true, + composed: true, + })); this.selectedFieldId = null; + this.emitFieldsChange(); } private updateRecipientRole(recipientId: number, role: TRecipientRole) { @@ -220,6 +256,7 @@ export class SdigWorkspaceCompose extends DeesElement { targetMembers.splice(insertIndex, 0, { ...recipient, role: nextRole }); nextByRole.set(nextRole, targetMembers); this.recipients = recipientRoleDefinitions.flatMap((roleDefinition) => nextByRole.get(roleDefinition.role) || []).map((currentRecipient, index) => ({ ...currentRecipient, order: index + 1 })); + this.emitRecipientsChange(); const nextSigners = this.recipients.filter((currentRecipient) => currentRecipient.role === 'signer'); const fallbackSigner = nextSigners[0]; @@ -229,6 +266,7 @@ export class SdigWorkspaceCompose extends DeesElement { if (this.activeRecipient === recipientId) { this.activeRecipient = fallbackSigner.id; } + this.emitFieldsChange(); } } @@ -454,6 +492,12 @@ export class SdigWorkspaceCompose extends DeesElement { this.selectedFieldId = nextField.id; this.draggedFieldDefinition = null; this.draggedFieldGrabOffset = null; + this.dispatchEvent(new CustomEvent('field-create', { + detail: { field: nextField, fields: this.fields }, + bubbles: true, + composed: true, + })); + this.emitFieldsChange(); } private startFieldToolDrag(event: DragEvent, fieldType: TFieldDefinition) { @@ -567,10 +611,11 @@ export class SdigWorkspaceCompose extends DeesElement { } public render(): TemplateResult { + const document = this.document || demoDocuments[0]; const selectedField = this.fields.find((field) => field.id === this.selectedFieldId); return html` - ${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')}` })} + ${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')}` })} ${this.renderStepper()}
${this.renderRecipientContextMenu()} @@ -585,9 +630,9 @@ export class SdigWorkspaceCompose extends DeesElement {
{ 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)}> ${fakeDocument()} ${this.fields.map((field) => html`
this.selectedFieldId = field.id} @pointerdown=${(event: PointerEvent) => this.startFieldMove(event, field)}>
${icon(this.fieldIcon(field.type), 12)}${field.label}
${this.selectedFieldId === field.id ? this.renderResizeHandles(field) : ''}
`)} -
Page 1 of 14
+
Page 1 of ${document.pages}
-
${actionButton('Prev', 'outline')}${html`1 / 14`}${actionButton('Next', 'outline')}
+
${actionButton('Prev', 'outline')}${html`1 / ${document.pages}`}${actionButton('Next', 'outline')}
Routing order · drag to reorder
diff --git a/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts b/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts index 7be744f..821b61d 100644 --- a/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts +++ b/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts @@ -13,6 +13,7 @@ export class SdigWorkspaceSign extends DeesElement { public static demoGroups = ['Signature Digital Workspace']; @property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0]; + @property({ attribute: false }) public accessor fields: IFieldPlacement[] = demoFields; @state() private accessor activeFieldId: string = 'f1'; @state() private accessor signedFieldIds: string[] = []; @@ -28,7 +29,7 @@ export class SdigWorkspaceSign extends DeesElement { `]; private get signFields() { - return demoFields.slice(0, 3); + return this.fields.slice(0, 3); } private fieldIcon(type: IFieldPlacement['type']): string { diff --git a/ts_web/elements/sdig-workspace/sdig-workspace.ts b/ts_web/elements/sdig-workspace/sdig-workspace.ts index 523a0ce..af7b66a 100644 --- a/ts_web/elements/sdig-workspace/sdig-workspace.ts +++ b/ts_web/elements/sdig-workspace/sdig-workspace.ts @@ -1,5 +1,5 @@ import { DeesElement, property, html, customElement, type TemplateResult, css } from '@design.estate/dees-element'; -import { demoDocuments, icon, type IDocumentRow, type TDensity, type TWorkspaceTheme, type TWorkspaceView } from './sdig-workspace.shared.js'; +import { demoDocuments, demoFields, demoRecipients, icon, type IDocumentRow, type IFieldPlacement, type IRecipient, type TDensity, type TWorkspaceTheme, type TWorkspaceView } from './sdig-workspace.shared.js'; import './sdig-workspace-inbox.js'; import './sdig-workspace-compose.js'; import './sdig-workspace-sign.js'; @@ -25,6 +25,8 @@ export class SdigWorkspace extends DeesElement { @property({ type: String, reflect: true }) public accessor view: TWorkspaceView = 'inbox'; @property({ attribute: false }) public accessor documents: IDocumentRow[] = demoDocuments; @property({ type: String }) public accessor activeDocumentId: string = ''; + @property({ attribute: false }) public accessor recipients: IRecipient[] = demoRecipients; + @property({ attribute: false }) public accessor fields: IFieldPlacement[] = demoFields; public connectedCallback = async () => { await super.connectedCallback(); @@ -163,8 +165,8 @@ export class SdigWorkspace extends DeesElement { const activeDocument = this.documents.find((document) => document.id === this.activeDocumentId) || this.documents[0] || demoDocuments[0]; switch (this.view) { case 'inbox': return html``; - case 'compose': return html``; - case 'sign': return html``; + case 'compose': return html``; + case 'sign': return html``; case 'audit': return html``; case 'developers': return html``; case 'templates': return html``;