Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ac46728a7 | |||
| b6b4698028 |
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# 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)
|
## 2026-05-05 - 1.6.1 - fix(workspace)
|
||||||
pass the active document into sign and audit views
|
pass the active document into sign and audit views
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@signature.digital/catalog",
|
"name": "@signature.digital/catalog",
|
||||||
"version": "1.6.1",
|
"version": "1.7.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A comprehensive catalog of customizable web components designed for building and managing e-signature applications.",
|
"description": "A comprehensive catalog of customizable web components designed for building and managing e-signature applications.",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@signature.digital/catalog',
|
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.'
|
description: 'A comprehensive catalog of customizable web components designed for building and managing e-signature applications.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DeesElement, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
|
import { DeesElement, property, 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 { 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 '../sdig-contextmenu/index.js';
|
||||||
import { type ISdigContextMenuAction, type ISdigContextMenuActionEventDetail } from '../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 step: number = 2;
|
||||||
@state() private accessor activeRecipient: number = 0;
|
@state() private accessor activeRecipient: number = 0;
|
||||||
@state() private accessor selectedFieldId: string | null = null;
|
@state() private accessor selectedFieldId: string | null = null;
|
||||||
@state() private accessor recipients: IRecipient[] = [...demoRecipients];
|
@property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0];
|
||||||
@state() private accessor fields: IFieldPlacement[] = [...demoFields];
|
@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 signingOrderDrag: TSigningOrderDrag | null = null;
|
||||||
@state() private accessor recipientContextMenu: TRecipientContextMenu | null = null;
|
@state() private accessor recipientContextMenu: TRecipientContextMenu | null = null;
|
||||||
private draggedFieldDefinition: TFieldDefinition | null = null;
|
private draggedFieldDefinition: TFieldDefinition | null = null;
|
||||||
@@ -169,8 +170,36 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
return Math.max(min, Math.min(max, value));
|
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<IFieldPlacement>) {
|
private updateField(fieldId: string, patch: Partial<IFieldPlacement>) {
|
||||||
this.fields = this.fields.map((field) => field.id === fieldId ? { ...field, ...patch } : field);
|
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<IFieldPlacement>) {
|
private updateSelectedField(patch: Partial<IFieldPlacement>) {
|
||||||
@@ -192,8 +221,15 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
|
|
||||||
private removeSelectedField() {
|
private removeSelectedField() {
|
||||||
if (!this.selectedFieldId) return;
|
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.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.selectedFieldId = null;
|
||||||
|
this.emitFieldsChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateRecipientRole(recipientId: number, role: TRecipientRole) {
|
private updateRecipientRole(recipientId: number, role: TRecipientRole) {
|
||||||
@@ -220,6 +256,7 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
targetMembers.splice(insertIndex, 0, { ...recipient, role: nextRole });
|
targetMembers.splice(insertIndex, 0, { ...recipient, role: nextRole });
|
||||||
nextByRole.set(nextRole, targetMembers);
|
nextByRole.set(nextRole, targetMembers);
|
||||||
this.recipients = recipientRoleDefinitions.flatMap((roleDefinition) => nextByRole.get(roleDefinition.role) || []).map((currentRecipient, index) => ({ ...currentRecipient, order: index + 1 }));
|
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 nextSigners = this.recipients.filter((currentRecipient) => currentRecipient.role === 'signer');
|
||||||
const fallbackSigner = nextSigners[0];
|
const fallbackSigner = nextSigners[0];
|
||||||
@@ -229,6 +266,7 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
if (this.activeRecipient === recipientId) {
|
if (this.activeRecipient === recipientId) {
|
||||||
this.activeRecipient = fallbackSigner.id;
|
this.activeRecipient = fallbackSigner.id;
|
||||||
}
|
}
|
||||||
|
this.emitFieldsChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,6 +492,12 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
this.selectedFieldId = nextField.id;
|
this.selectedFieldId = nextField.id;
|
||||||
this.draggedFieldDefinition = null;
|
this.draggedFieldDefinition = null;
|
||||||
this.draggedFieldGrabOffset = 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) {
|
private startFieldToolDrag(event: DragEvent, fieldType: TFieldDefinition) {
|
||||||
@@ -567,10 +611,11 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
|
const document = this.document || demoDocuments[0];
|
||||||
const selectedField = this.fields.find((field) => field.id === this.selectedFieldId);
|
const selectedField = this.fields.find((field) => field.id === this.selectedFieldId);
|
||||||
|
|
||||||
return html`
|
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.renderStepper()}
|
||||||
<div class="compose-workspace">
|
<div class="compose-workspace">
|
||||||
${this.renderRecipientContextMenu()}
|
${this.renderRecipientContextMenu()}
|
||||||
@@ -585,9 +630,9 @@ export class SdigWorkspaceCompose extends DeesElement {
|
|||||||
<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)}>
|
<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)}>
|
||||||
${fakeDocument()}
|
${fakeDocument()}
|
||||||
${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>`)}
|
${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>`)}
|
||||||
<div class="mono" style="position: absolute; bottom: 12px; right: 16px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of 14</div>
|
<div class="mono" style="position: absolute; bottom: 12px; right: 16px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of ${document.pages}</div>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
<div class="label-upper">Routing order · drag to reorder</div>
|
<div class="label-upper">Routing order · drag to reorder</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export class SdigWorkspaceSign extends DeesElement {
|
|||||||
public static demoGroups = ['Signature Digital Workspace'];
|
public static demoGroups = ['Signature Digital Workspace'];
|
||||||
|
|
||||||
@property({ attribute: false }) public accessor document: IDocumentRow = demoDocuments[0];
|
@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 activeFieldId: string = 'f1';
|
||||||
@state() private accessor signedFieldIds: string[] = [];
|
@state() private accessor signedFieldIds: string[] = [];
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ export class SdigWorkspaceSign extends DeesElement {
|
|||||||
`];
|
`];
|
||||||
|
|
||||||
private get signFields() {
|
private get signFields() {
|
||||||
return demoFields.slice(0, 3);
|
return this.fields.slice(0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fieldIcon(type: IFieldPlacement['type']): string {
|
private fieldIcon(type: IFieldPlacement['type']): string {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DeesElement, property, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
|
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-inbox.js';
|
||||||
import './sdig-workspace-compose.js';
|
import './sdig-workspace-compose.js';
|
||||||
import './sdig-workspace-sign.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({ type: String, reflect: true }) public accessor view: TWorkspaceView = 'inbox';
|
||||||
@property({ attribute: false }) public accessor documents: IDocumentRow[] = demoDocuments;
|
@property({ attribute: false }) public accessor documents: IDocumentRow[] = demoDocuments;
|
||||||
@property({ type: String }) public accessor activeDocumentId: string = '';
|
@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 () => {
|
public connectedCallback = async () => {
|
||||||
await super.connectedCallback();
|
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];
|
const activeDocument = this.documents.find((document) => document.id === this.activeDocumentId) || this.documents[0] || demoDocuments[0];
|
||||||
switch (this.view) {
|
switch (this.view) {
|
||||||
case 'inbox': return html`<sdig-workspace-inbox class="view-host" .density=${this.density} .documents=${this.documents}></sdig-workspace-inbox>`;
|
case 'inbox': return html`<sdig-workspace-inbox class="view-host" .density=${this.density} .documents=${this.documents}></sdig-workspace-inbox>`;
|
||||||
case 'compose': return html`<sdig-workspace-compose class="view-host"></sdig-workspace-compose>`;
|
case 'compose': return html`<sdig-workspace-compose class="view-host" .document=${activeDocument} .recipients=${this.recipients} .fields=${this.fields}></sdig-workspace-compose>`;
|
||||||
case 'sign': return html`<sdig-workspace-sign class="view-host" .document=${activeDocument}></sdig-workspace-sign>`;
|
case 'sign': return html`<sdig-workspace-sign class="view-host" .document=${activeDocument} .fields=${this.fields}></sdig-workspace-sign>`;
|
||||||
case 'audit': return html`<sdig-workspace-audit class="view-host" .document=${activeDocument}></sdig-workspace-audit>`;
|
case 'audit': return html`<sdig-workspace-audit class="view-host" .document=${activeDocument}></sdig-workspace-audit>`;
|
||||||
case 'developers': return html`<sdig-workspace-developers class="view-host"></sdig-workspace-developers>`;
|
case 'developers': return html`<sdig-workspace-developers class="view-host"></sdig-workspace-developers>`;
|
||||||
case 'templates': return html`<sdig-workspace-placeholder class="view-host" label="Templates" subtitle="Reusable agreement templates"></sdig-workspace-placeholder>`;
|
case 'templates': return html`<sdig-workspace-placeholder class="view-host" label="Templates" subtitle="Reusable agreement templates"></sdig-workspace-placeholder>`;
|
||||||
|
|||||||
Reference in New Issue
Block a user