Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement β†’
Sign In

drill-widgets

Package Overview
Dependencies
Maintainers
2
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

drill-widgets

Easily embed powerful workflow building and running capabilities into your web applications with Lit-based web components. Includes HTML export for server-side PDF generation.

latest
Source
npmnpm
Version
2.7.36
Version published
Weekly downloads
292
-2.67%
Maintainers
2
Weekly downloads
Β 
Created
Source

✨ Drill Widgets ✨

Easily embed powerful workflow building and running capabilities into your web applications!

Drill Widgets provides Lit-based web components that let you:

  • Visually build workflow templates with custom forms and steps.
  • Run instances of those workflows, guiding users through steps.
  • Integrate seamlessly into your existing application with a simple JavaScript API.

Perfect for onboarding flows, request processes, checklists, approvals, and any task requiring structured, sequential steps.

Installation

npm install drill-widgets

5-Minute Integration (Fast Path)

If you're integrating Drill Widgets for the first time, follow this sequence:

  • Install package: npm install drill-widgets
  • Render Builder with createBuilder(...)
  • Save Builder output (Workflow) to your backend
  • Create an Instance from that Workflow
  • Render Runner with createRunner(...)
  • Implement onInstanceUpdated and return { success: boolean, error?: string }

1) Minimal Builder setup

import { createBuilder, type Workflow } from "drill-widgets";

const referenceWorkflow: Workflow | undefined = await api.getWorkflow("shared-block-library");

createBuilder("builder-root", {
  stepsWithPhasesSupport: true, // optional
  supportPriorStepAccess: true, // optional
  referenceWorkflow, // optional read-only source for Reuse block suggestions
  onWorkflowCreated: async (workflow) => {
    await api.saveWorkflow(workflow);
  },
});

2) Minimal Runner setup

import { createRunner, type Instance } from "drill-widgets";

const instance: Instance = await api.getInstance("inst-123");

createRunner("runner-root", {
  instance,
  mode: "default",
  currentUser: { targetId: "user-1", email: "user-1@example.com" },
  onInstanceUpdated: async (detail) => {
    const ok = await api.saveInstanceProgress(detail.instanceId!, detail);
    return ok ? { success: true } : { success: false, error: "Save failed" };
  },
});

Heads-up: step.description is now treated as authoring metadata. Runner and print HTML export display the step title, but do not render the step description text to end users.

3) Optional callbacks to add next

  • onSuggestedNotifications(records) for downstream notification orchestration
  • nextInstancesUponCompletion(detail) to offer direct links to the user's other pending workflow instances after completion
  • onSignatureCaptured(detail) for signature storage
  • onFileUploaded(detail) for file upload plumbing

⚠️ AI assignment status (important)

AI-assigned steps (assignment.category: "ai", assignment.type: "ai_model") are currently experimental.

  • Do not enable AI assignment in production workloads yet.
  • Behavior, schema details, and UX affordances may still evolve.
  • Keep AI assignment behind an internal feature flag until your team explicitly accepts this risk.

For production usage today, prefer human assignment categories (individual / role).

Advanced assignment filtering + multi-assignee scheme

StepAssignment supports richer launch-time targeting semantics for role/org-unit filtering and optional multi-assignee behavior.

What changed (integration-facing)

  • Split between template-time vs launch-time assignment choice

    • Template designer-chosen examples: individual/subject, individual/creator, individual/specific_user.
    • Launch-time chosen examples: instance_assignee_select, instance_role_assignee_select.
  • Role-based filtering can include multiple roles

    • Use eligibleRoleIDs as a set.
  • Org-unit filtering can include multiple units

    • Use eligibleOrgUnitIDs as a set.
    • This may be surfaced as "Programs" or another tenant-specific label in host apps.
  • Role/org-unit filters can be combined with AND/OR

    • Use filter.logicalCombination.operatorValue: "AND" | "OR".
  • Optional multi-assignee mode

    • permitMultipleAssignees: true allows many assignees for a step.
    • Completion model is first-to-finish (no advanced concurrency/consensus semantics).
  • Optional automatic assignment from filters

    • assignAnyMatchingUsersAutomatically: true is meaningful only when:
      • permitMultipleAssignees === true, and
      • at least one launch-time filter is selected.

Launch parameter advice (Builder host)

  • Provide role options via roles (or legacy alias groups).
  • Provide org-unit options via orgUnits (or legacy alias units).
  • If either list is empty, Builder hides that empty filter section in the launch-time assignment UI.
  • Avoid assuming both filter groups are always present in UI snapshots or automation tests.

Schema interpretation advice (backend)

  • Continue accepting and normalizing these fields:
    • permitMultipleAssignees?: boolean
    • assignAnyMatchingUsersAutomatically?: boolean
    • filter?: AssignmentFilter
    • eligibleRoleIDs?: string[]
    • eligibleOrgUnitIDs?: string[]
    • eligibleUnitIDs?: string[] (deprecated alias)
  • For resolved runtime assignment, support multi-user outputs:
    • targetIDs?: string[]
    • emails?: string[]

AssignmentFilter supports logical combinations of role + org-unit criteria:

  • top-level logicalCombination.operatorValue: "AND" | "OR"
  • each setCombination.operatorValue: "ANY"
  • per-set role criteria: eligibleRoleIDs
  • per-set org-unit criteria: eligibleOrgUnitIDs using typed selectors:
    • { type: "org_unit_id", value: "<unit-id>" }
    • { type: "dynamic_org_unit", value: "initiator_org_unit" | "subject_org_unit" }

Template-side example:

assignment: {
  category: "role",
  type: "instance_role_assignee_select",
  permitMultipleAssignees: true,
  assignAnyMatchingUsersAutomatically: true,
  filter: {
    logicalCombination: {
      operatorValue: "OR",
      setCombinations: [
        {
          operatorValue: "ANY",
          eligibleRoleIDs: ["crew-lead", "supervisor"],
          eligibleOrgUnitIDs: [{ type: "org_unit_id", value: "north-yard" }],
        },
        {
          operatorValue: "ANY",
          eligibleRoleIDs: ["auditor"],
          eligibleOrgUnitIDs: [{ type: "dynamic_org_unit", value: "subject_org_unit" }],
        },
      ],
    },
  },
}

Resolved instance-side data may include multiple assignees:

assignment: {
  category: "role",
  type: "instance_role_assignee_select",
  targetIDs: ["user-1", "user-2"],
  emails: ["u1@example.com", "u2@example.com"],
}

Conditional rules: checkbox groups now support includes/not-includes semantics

For checkbox questions with multiple options (checkbox groups), condition authoring now uses option-membership operators instead of boolean equality.

  • Single checkbox (boolean): keep Equals / Does not equal.
  • Checkbox group (multi-select): use
    • Includes any of (includes_any_of)
    • Does not include any of (does_not_include_any_of)

In Builder, Value for checkbox-group conditions is a multi-select over checkbox option names.

Backend/schema guidance:

  • Expect checkbox-group predicate values for includes_any_of/does_not_include_any_of to be arrays of selected option labels.
  • Do not coerce those values to booleans for checkbox-group questions.
  • Existing eq/neq logic for single-checkbox questions remains valid.
  • Legacy in/not_in values are still accepted for backward compatibility.

Key Features & Benefits

  • πŸš€ Rapid Integration: Embed complex workflow UIs with just a few lines of JavaScript using createBuilder and createRunner.
  • 🧩 Focused Builder (builder-widget):
    • Clean, intuitive interface to create workflow templates with steps and form elements
    • Optional Steps with Phases support via stepsWithPhasesSupport toggle in createBuilder
    • Drag-and-drop step reordering for easy workflow organization
    • Block-level move up/down and duplicate actions for faster template authoring
    • Duplicate blocks and duplicated step titles automatically receive a (copy) suffix
    • Optional Prior Step Access authoring via supportPriorStepAccess
    • Optional referenceWorkflow support to enrich Reuse suggestions from a read-only source workflow
    • Step-by-step template creation with title, description, and elements
    • Modern UI design with proper form validation and user feedback
    • Support for multiple question types and content elements
    • Built-in Block Reuse picker for re-inserting existing question blocks
    • Built-in Conditional Steps authoring for step-level visibility rules
  • πŸƒβ€β™‚οΈ Smooth Runner (runner-widget):
    • Self-contained: Runs entirely from a single Instance object snapshot
    • Multiple execution modes: Default, Preview, Admin, View-Only, and Print modes for different use cases
    • Guides users step-by-step with clear progress indication
    • Includes comprehensive validation for required fields
    • Provides clear event hooks (onInstanceUpdated, onSignatureCaptured, onFileUploaded, onSuggestedNotifications) for saving progress and final results
    • Responsive design that adapts to different screen sizes
  • πŸ—οΈ Modern Web Components: Built with Lit for encapsulated, reusable, and framework-agnostic components.
  • πŸ”’ Consistent Runs: Instances are snapshots of the workflow at creation time, ensuring that changes to the main workflow template don't break in-progress tasks.
  • πŸ”Œ Event-Driven: Easily hook into the workflow lifecycle using callbacks to save data to your backend, trigger notifications, or update your UI.

Core Concepts

  • Workflow: The reusable blueprint or template for a process, containing steps and elements (form questions and content blocks).
  • Instance: A specific, runnable execution of a Workflow, captured as a snapshot. It includes the exact steps/elements from the Workflow at the time of creation, plus current progress and any instance-specific details.
  • Builder: The component (<builder-widget>) used to create Workflow templates with a focused, clean interface.
  • Runner: The component (<runner-widget>) used to execute an Instance, presenting the steps and elements to the user.

New Data/Callback Capabilities

1) Builder toggle: stepsWithPhasesSupport

When enabled in createBuilder, the Builder UI exposes Steps with Phases controls. The saved workflow JSON can include:

  • workflow.usePhases: boolean
  • step.waitForPrevious: boolean (per step, default true)
createBuilder("workflow-builder", {
  workflow,
  stepsWithPhasesSupport: true,
  onWorkflowCreated: (wf) => saveWorkflowTemplate(wf),
});

2) Builder option: referenceWorkflow (for Reuse block enrichment)

Builder always shows a Reuse block control in each step section footer.

  • Reuse suggestions are derived from question blocks (not content-only blocks)
  • Names are deduplicated and sorted alphabetically
  • Source list is the union of:
    • current workflow steps/elements
    • optional referenceWorkflow steps/elements (read-only)
  • If both sources contain the same block name, Builder prefers the block from the current workflow
createBuilder("workflow-builder", {
  workflow,
  referenceWorkflow: reusableBlockLibraryWorkflow, // optional
  onWorkflowCreated: (wf) => saveWorkflowTemplate(wf),
});

3) Built-in step-level conditional visibility

Builder always supports authoring step-level conditional visibility using rules.

  • Step visibility mode can be set to always, never, or conditional
  • Conditional step visibility rules are stored in step.metadata.rules
  • Rule predicates use when (all / any) with operators such as eq, neq, includes_any_of, does_not_include_any_of, gt, gte, lt, lte (legacy in/not_in is still supported)
createBuilder("workflow-builder", {
  workflow,
  onWorkflowCreated: (wf) => saveWorkflowTemplate(wf),
});

4) Builder option: supportPriorStepAccess

When supportPriorStepAccess is enabled, Builder exposes Prior Step Access controls on non-first steps.

  • all: true means users can reference any eligible prior/concurrent step in-context.
  • all: false + stepIdsToRead means references are limited to the selected step IDs.
  • Runner additionally permits access to prior steps previously filled out by the same user, even when not listed in stepIdsToRead.
  • No extra JSON field is required for this same-user exception.

Example:

createBuilder("workflow-builder", {
  workflow,
  supportPriorStepAccess: true,
  onWorkflowCreated: (wf) => saveWorkflowTemplate(wf),
});

Step JSON shape:

step: {
  id: "step-3",
  priorStepAccess: {
    all: false,
    stepIdsToRead: ["step-1", "step-2"],
  },
}

5) Critical schema transmission: Workflow β†’ Instance

For Runner behavior to remain correct, the instance snapshot must preserve these fields from the workflow:

  • Workflow-level
    • usePhases
  • Step-level
    • id, order, title, assignment, waitForPrevious
    • priorStepAccess (all, stepIdsToRead) when Prior Step Access is enabled
    • visibility (especially when: "conditional")
    • metadata.rules (step-target visibility.set, required.set, etc.)
  • Element-level
    • id, order, type, base config (label, required, options, etc.)
    • visibility
    • metadata.rules

If step.visibility or step.metadata.rules are dropped during transformation, conditional-step logic in Runner can regress (for example, missed skip/unneeded behavior).

6) Phase-aware workflow JSON

When a workflow is saved from Builder with phase support enabled:

  • usePhases: true means the runner uses phase-based progression.
  • A step with waitForPrevious: false is parallel within its phase.
  • A step with waitForPrevious: true starts a new phase boundary.

Note that with waitForPrevious being optional and true by default, step progression is entirely serial by default, for backward compatibility.

7) Runner callback: onSuggestedNotifications

Runner can emit suggested notification records after step completion:

createRunner("workflow-runner", {
  instance,
  mode: "default",
  onSuggestedNotifications: async (records) => {
    // send suggestions to your API or queue
    await saveSuggestedNotifications(records);
  },
});

Each record includes:

  • instanceId
  • userEmail
  • userId
  • stepId
  • mayBecomeMootIfUserCompletesOnline
  • hasBecomeMoot

Note that if there is more than one step in a phase, supporting parallel execution of those steps, the list of suggested notifications will include one record per step with someone to notify. It's possible that in a 4-step phase, 2 of the steps can have the same user assigned, but the Runner does not attempt to consolidate those notifications.

Also note that the flags around mootness are intended to help avoid extraneous notifications that might otherwise be sent to users who have progressed from one step to another in the UI. A possibly moot notification is one that will ideally be sent only if the user doesn't complete the step during the session in which that step was first encountered in the UI. If a possibly moot suggestion is followed by a has-become-moot suggestion, then the user has completed that step and doesn't need the notification. You may consider possibly moot notifications as a recommendation to send sometime soon but not immediately, proceeding with it if there are no further instructions to the contrary.

8) Instance revisions status

Instance.status now supports "revisions_needed" in addition to "draft", "active", "completed", and "canceled".

For app UX, treat "revisions_needed" as active work still in progress (not completed).

Use this to reflect workflows that require assignee follow-up revisions.

9) Block-level revision metadata

Step response records support revision-specific fields:

  • needsRevision?: boolean
  • revisionNotes?: string

These live on InstanceStepElement / QuestionResponse entries and allow per-block revision guidance.

Integration FAQ (Important)

What is the minimum valid Workflow payload?

At minimum:

const workflow = {
  id: "wf-1",
  name: "Example Workflow",
  steps: [
    {
      id: "step-1",
      order: 1,
      title: "Step 1",
      elements: [],
      assignment: { category: "individual", type: "specific_user" },
      status: "pending",
    },
  ],
};

What is the minimum valid Instance payload?

At minimum:

const instance = {
  id: "inst-1",
  workflowId: "wf-1",
  name: "Example Instance",
  status: "active",
  steps: workflow.steps,
  completedSteps: [],
};

Exactly when does onInstanceUpdated run, and what must it return?

onInstanceUpdated is used when Runner persists step progress/submission. It must resolve to:

{ success: boolean; error?: string }

If your callback does not return this shape, Runner treats it as an error.

How should we handle dates crossing JSON boundaries?

Type definitions use Date for fields such as dueDate and answeredAt. If your backend sends ISO strings, convert them to Date objects before passing data into widgets.

How should we process onSuggestedNotifications records?

Recommended approach:

  • Treat each record as a per-step suggestion (do not assume deduped by user).
  • Use idempotent persistence/delivery on your backend.
  • Delay sending records with mayBecomeMootIfUserCompletesOnline: true briefly.
  • If a later record arrives with hasBecomeMoot: true for the same user/step context, suppress/cancel that notification.

How can we offer users a direct path to their other pending workflow instances after completion?

Provide nextInstancesUponCompletion in createRunner(...).

When the current assigned-step submission or full workflow submission completes, Runner can show a post-submission panel with direct links to other pending workflow instances assigned to that same user.

Typical usage:

createRunner("workflow-runner", {
  instance,
  mode: "default",
  nextInstancesUponCompletion: async (detail) => {
    return await api.getNextPendingInstancesForUser({
      instanceId: detail.instanceId,
      workflowId: detail.workflowId,
      completionType: detail.completionType,
    });
  },
});

Runner expects an array of objects shaped like:

[
  {
    stepName: "Review and Sign",
    instanceName: "Vendor Packet",
    createdDate: "2026-06-01T12:00:00.000Z",
    dueDate: "2026-06-03T12:00:00.000Z",
    url: "https://app.example.com/drills/run/inst-456",
  },
];

Notes:

  • url is optional. If omitted, Runner shows the destination step name as text without a link.
  • This is intended for host apps that want users to proceed directly to one of their other pending workflow instances instead of stopping at a generic completion screen.
  • The callback is only consulted after successful completion flows, not during ordinary step-to-step navigation.

How should apps display revisions_needed?

Recommended UX:

  • Include it in β€œopen”/β€œactive” work queues.
  • Distinguish it visually from generic active work (badge/tag).
  • Do not treat it as terminal/completed.

How does Builder decide what appears in β€œReuse block”?

Builder only includes reusable question blocks from:

  • the active workflow currently being edited
  • optional referenceWorkflow (read-only)

Names are shown once (distinct, A→Z). If there is a name collision between active and reference workflows, the active workflow version is used when inserting. If there's a name collision within a workflow, the earlier one is used.

Runner Modes

The Runner widget supports distinct modes for different use cases:

Default Mode

Purpose: Standard workflow execution with full functionality.

Configuration:

createRunner("runner-container", {
  instance: instanceData, // Required: Instance object
  mode: "default", // Optional: Default if not specified
  // ... other options
});

Features:

  • βœ… Data Persistence: Form data is automatically saved to localStorage
  • βœ… Validation: Required field validation runs on each step
  • βœ… Step Completion Tracking: Progress is tracked and steps are marked as completed
  • βœ… Callbacks: All callbacks fire (signature capture, file upload, etc.)
  • βœ… Smart Navigation: Next button progresses through assigned steps only
  • βœ… Transparent Unassigned Steps: Clickable sidebar access to view unassigned steps in read-only mode
  • βœ… Assignment-Based Interaction: Full editing for assigned steps, read-only viewing for unassigned steps
  • βœ… Next/Submit Buttons: Visible only when viewing assigned steps

Use Cases:

  • Production workflow execution
  • User onboarding processes
  • Approval workflows
  • Collaborative workflows where users need transparency into other steps
  • Any scenario requiring data persistence, validation, and step-based assignments

Default Mode: Transparent Unassigned Step Viewing

In default mode, users can now click on any step in the sidebar for full workflow transparency:

Assigned Steps (Full Mode):

  • βœ… Full interaction: Edit, validate, save, and progress through workflow
  • βœ… Navigation buttons: Next/Save/Submit buttons are visible
  • βœ… Data persistence: Changes are saved and callbacks fire
  • 🎯 Badge shows: "Assigned"

Unassigned Steps (Read-Only Mode):

  • πŸ‘οΈ View-only access: Can see existing responses and form structure
  • ❌ No interaction: All inputs are disabled and read-only
  • ❌ No buttons: Next/Save/Submit buttons are hidden
  • ❌ No data changes: Cannot modify or save any data
  • 🏷️ Badge shows: "Not Assigned"

Next Button Behavior:

  • The Next button still progresses through assigned steps only
  • Clicking unassigned steps is for viewing/transparency only
  • Users can freely navigate back to their assigned workflow

This provides complete workflow transparency while maintaining assignment boundaries and data integrity.

Preview Mode

Purpose: Interactive preview of workflow templates without data persistence.

Configuration:

createRunner("runner-container", {
  workflow: workflowData, // Required: Workflow object (not Instance)
  mode: "preview", // Required: Must be explicitly set
  // ... other options
});

Features:

  • βœ… Interactive Inputs: Users can interact with all form elements
  • βœ… No Data Saving: Form data is not persisted (no localStorage usage)
  • βœ… No Validation: Required field validation is disabled
  • βœ… Free Navigation: Users can click any step in the sidebar to navigate freely
  • βœ… No Callbacks: Signature, file upload, suggested notifications, and instance update callbacks are disabled
  • βœ… No Navigation Buttons: Next/Submit buttons are hidden
  • βœ… Same Visual Layout: Identical appearance to default mode

Use Cases:

  • Workflow template preview
  • User experience testing
  • Stakeholder demonstrations
  • Template review and approval processes

Admin Mode

Purpose: Administrative access for workflow management with unrestricted navigation and step-by-step saving.

Configuration:

createRunner("runner-container", {
  instance: instanceData, // Required: Instance object
  mode: "admin", // Required: Must be explicitly set
  currentUser: userData, // Required: User with admin access
  // ... other options
});

Features:

  • βœ… Unrestricted Navigation: Access any step regardless of assignment or completion status
  • βœ… Step-by-Step Saving: "Save" button (instead of "Next") allows saving progress without advancing
  • βœ… All Callbacks Active: Signature capture, file upload, and instance update callbacks fire normally
  • βœ… Assignment Bypass: Can view and edit steps not assigned to current user
  • βœ… Data Persistence: Form data is saved to localStorage
  • βœ… Post-Submission Access: Can still navigate and edit after workflow submission
  • βœ… Validation Active: Required field validation still applies

Use Cases:

  • Workflow administration and troubleshooting
  • Helping users complete stuck workflows
  • Data correction and updates
  • Workflow testing and QA
  • Support team assistance

View-Only Mode

Purpose: Read-only viewing of completed workflow data with pre-filled responses displayed as non-editable content.

Configuration:

createRunner("runner-container", {
  instance: completedInstanceData, // Required: Instance with responses
  mode: "view-only", // Required: Must be explicitly set
  currentUser: userData, // Optional: For display purposes
  // ... other options
});

Features:

  • βœ… Free Navigation: Navigate to any step freely (like admin mode)
  • βœ… Pre-filled Display: All form inputs show existing response data
  • βœ… All Inputs Disabled: No editing possible - purely read-only
  • βœ… File/Signature Links: File uploads and signatures display as clickable links
  • βœ… No Action Buttons: No Next/Save/Submit buttons visible
  • βœ… No Data Persistence: No localStorage usage or data saving
  • βœ… No Callbacks: Signature, file upload, suggested notifications, and instance update callbacks are disabled
  • βœ… Post-Submission View: Perfect for viewing submitted/completed workflows

Use Cases:

  • Viewing completed workflow submissions
  • Audit trails and compliance documentation
  • Historical workflow data review
  • Sharing completed forms with stakeholders
  • Read-only workflow previews for approval

Print Mode

Purpose: Print-optimized display of completed workflow data with all steps shown on one continuous page.

Configuration:

createRunner("runner-container", {
  instance: completedInstanceData, // Required: Instance with responses
  mode: "print", // Required: Must be explicitly set
  currentUser: userData, // Optional: For display purposes
  // ... other options
});

Features:

  • βœ… Single Page Layout: All workflow steps displayed continuously on one page
  • βœ… No Sidebar: Clean layout without step navigation for optimal printing
  • βœ… Pre-filled Display: All form inputs show existing response data
  • βœ… All Inputs Disabled: No editing possible - purely read-only
  • βœ… Detailed File Information: File uploads show filename, size, type, and modification date
  • βœ… Print-Optimized Styling: Special CSS for clean printing with proper page breaks
  • βœ… No Action Buttons: No interactive elements that don't work in print
  • βœ… No Data Persistence: No localStorage usage or data saving
  • βœ… No Callbacks: Signature, file upload, suggested notifications, and instance update callbacks are disabled

File Display Enhancement:

  • File Info Objects: Shows detailed metadata (filename, size, type, date)
  • URL References: Extracts filename and type from URLs automatically
  • Print-Friendly: No non-functional buttons or links

Use Cases:

  • Generating PDF documents of completed workflows
  • Physical document printing for records
  • Compliance documentation requiring paper copies
  • Executive summaries and reports
  • Archival purposes

Mode Comparison

FeatureDefault ModePreview ModeAdmin ModeView-Only ModePrint Mode
Data SourceInstance objectWorkflow objectInstance objectInstance object (with responses)Instance object (with responses)
Data Persistenceβœ… localStorage❌ Noneβœ… localStorage❌ None❌ None
Validationβœ… Required fields❌ Disabledβœ… Required fields❌ N/A (read-only)❌ N/A (read-only)
Step NavigationπŸ”„ Smart (assigned: restricted, unassigned: view-only)βœ… Free (any step)βœ… Free (any step)βœ… Free (any step)❌ Single page view
Input EditingπŸ”„ Conditional (assigned: enabled, unassigned: disabled)βœ… Enabledβœ… Enabled❌ Disabled (read-only)❌ Disabled (read-only)
Callbacksβœ… All callbacks fire (assigned steps only)❌ Disabledβœ… All callbacks fire❌ Disabled❌ Disabled
Navigation ButtonsπŸ”„ Conditional (visible for assigned steps only)❌ Hiddenβœ… Save button visible❌ Hidden❌ Hidden
Assignment Checksβœ… Enforced❌ Bypassed❌ Bypassed❌ Bypassed❌ Bypassed
Post-Submission Access❌ Blockedβœ… Always availableβœ… Always availableβœ… Always availableβœ… Always available
File/Signature DisplayForm inputsForm inputsForm inputsπŸ”— Clickable linksπŸ“„ Detailed info display
LayoutSidebar + single stepSidebar + single stepSidebar + single stepSidebar + single stepπŸ“„ All steps on one page
Primary Use CaseProduction executionTemplate previewAdmin managementCompleted data viewingDocument printing

Supported Elements

Question Elements (Form Inputs)

TypeDescriptionFeatures
text_inputSingle-line text inputPlaceholder text, validation
textareaMulti-line text areaResizable, placeholder text
selectDropdown selectionMultiple options, placeholder
numberNumeric inputNumber validation
radioRadio button groupMultiple options, single selection
checkboxCheckbox inputSingle checkbox or checkbox group
dateDate pickerDate validation
file_uploadFile uploadFile selection, 5MB limit, client callback
signatureDigital signature padSVG output, responsive canvas, callback on next/submit

Content Elements (Display Only)

TypeDescriptionFeatures
textPlain text contentSimple text display
markdownMarkdown contentHeaders, lists, formatting
htmlHTML contentCustom HTML rendering
dividerVisual separatorOptional caption
imageImage displayURL, alt text, caption
videoVideo displayURL, caption
fileFile displayURL, caption

Features

  • Validation: Required field validation for all question types
  • Responsive Design: All elements adapt to different screen sizes
  • Accessibility: Proper ARIA labels and keyboard navigation
  • Type Safety: Full TypeScript support for all element types
  • Digital Signatures: Responsive signature pad with SVG output and data URL generation. Signature callbacks are triggered when users click "Next" or "Submit" to capture the final signature data.
  • Multiple Signatures: Support for multiple signature elements on the same form
  • File Uploads: File selection with 5MB limit. File upload callbacks are triggered immediately when a file is selected.
  • Progress Tracking: Real-time progress updates and completion tracking

Getting Started

Prerequisites

  • Node.js (v18+ recommended)
  • npm

Installation & Setup

  • Clone: git clone <your-repo-url> && cd drill-widgets
  • Install: npm install

Development Workflow

This project supports two distinct modes for different use cases:

πŸ”§ Development Mode

For active development with hot reload and fast iteration:

npm run dev
  • URL: http://localhost:5173/
  • Features: Hot module replacement, TypeScript compilation, ES modules
  • Use case: Active development, debugging, adding features
  • How it works: Vite serves source files directly using ES modules

πŸš€ Production Preview Mode

For testing the built library as it would be distributed:

npm run build
npm run preview
  • URL: http://localhost:4173/
  • Features: Optimized UMD bundle, production-ready assets
  • Use case: Testing final builds, integration testing, demo purposes
  • How it works: Creates a UMD bundle and serves the optimized HTML

Key Differences

AspectDevelopment (npm run dev)Production (npm run preview)
LoadingES modules from /src/UMD bundle from /dist/
SpeedFast startup, HMRSlower startup, no HMR
FilesSource TypeScript filesCompiled JavaScript bundle
DebuggingSource maps, readable codeMinified bundle
Use CasesDevelopment, debuggingFinal testing, demos

Demo Features

Both modes include a demo with:

  • Builder Tab: Create workflow templates with steps and form elements (questions, content, etc.) using an intuitive interface, including block move/duplicate and built-in Reuse block with reference workflow source
  • Runner Tab: Execute workflow instances with step-by-step progression
  • Sample Data: Pre-loaded workflows and instances for testing

Build Process Details

The build process includes a custom script that:

  • TypeScript Compilation: tsc compiles source files
  • Bundle Creation: vite build creates the UMD bundle
  • HTML Processing: scripts/build-html.js converts the HTML to use the UMD bundle

Quick Usage Example

npm install drill-widgets
import { createBuilder, createRunner, type Workflow } from "drill-widgets";

const reusableBlockLibraryWorkflow: Workflow | undefined =
  await fetchWorkflowFromBackend("workflow-shared-library");

// Initialize Builder
createBuilder("workflow-builder", {
  workflow: existingWorkflow, // optional, for edit mode
  stepsWithPhasesSupport: true, // enable Steps with Phases UI
  supportPriorStepAccess: true, // enable Prior Step Access controls
  referenceWorkflow: reusableBlockLibraryWorkflow, // optional read-only source
  onWorkflowCreated: (workflowData) => {
    console.log("Workflow template created:", workflowData);
    saveWorkflowTemplate(workflowData);
  },
  onWorkflowUpdated: (workflowData) => {
    console.log("Workflow template updated:", workflowData);
    updateWorkflowTemplate(workflowData);
  },
  onWorkflowDeleted: (workflowId) => {
    console.log("Workflow template deleted:", workflowId);
    deleteWorkflowTemplate(workflowId);
  },
});

// Initialize Runner (Default Mode)
const instanceData = await fetchInstanceFromBackend("instance-id-123");

if (instanceData && instanceData.steps) {
  createRunner("workflow-runner", {
    instance: instanceData,
    mode: "default", // Optional: Default if not specified
    onInstanceUpdated: (detail) => {
      console.log("Progress Update:", detail);
      // Save progress data (for both step updates and final submission)
      saveInstanceProgress(detail.instanceId, detail);

      // Check if this is the final submission (no current step)
      if (!detail.currentStep) {
        console.log("Workflow Complete!");
      }
    },
    onSignatureCaptured: (detail) => {
      console.log("Signature captured:", detail.questionId);
      console.log("Instance ID:", detail.instanceId);
      console.log("Step ID:", detail.stepId);
      console.log("SVG Data:", detail.svgData);
      console.log("Data URL:", detail.dataURL);
      // Save signature data to your backend
      // Note: This callback is triggered when user clicks "Next" or "Submit"
      saveSignatureData(detail.instanceId, detail.questionId, detail.svgData, detail.dataURL);
    },
    onFileUploaded: (detail) => {
      console.log("File uploaded:", detail.questionId);
      console.log("Instance ID:", detail.instanceId);
      console.log("Step ID:", detail.stepId);
      console.log("File name:", detail.fileName);
      console.log("File size:", detail.fileSize);
      console.log("File type:", detail.fileType);
      console.log("File object:", detail.file);
      // Handle file upload to your backend/storage
      // Note: This callback is triggered immediately when a file is selected
      handleFileUpload(detail.instanceId, detail.questionId, detail.file);
    },
    onSuggestedNotifications: async (records) => {
      console.log("Suggested notifications:", records);
      await saveSuggestedNotifications(records);
    },
  });
}

// Initialize Runner (Preview Mode)
const workflowData = await fetchWorkflowFromBackend("workflow-id-456");

if (workflowData && workflowData.steps) {
  createRunner("workflow-preview", {
    workflow: workflowData, // Note: workflow, not instance
    mode: "preview", // Required for preview mode
  });
}

Loading State

The Runner widget supports a loading state that can be used with any mode to show a loading indicator and disable user interaction while data is being processed or fetched.

Configuration:

const runnerWidget = createRunner("runner-container", {
  instance: instanceData,
  mode: "default",
  isLoading: true, // Shows loading spinner and disables step navigation
  currentUser: userData,
  onInstanceUpdated: (detail) => {
    console.log("Progress:", detail);
  },
});

// Update loading state dynamically
runnerWidget.isLoading = false; // Hide loading spinner and re-enable navigation

Features:

  • βœ… Loading Spinner: Displays an animated spinner with "Loading..." text
  • βœ… Disabled Navigation: All step navigation (sidebar and mobile) is disabled
  • βœ… Visual Overlay: Semi-transparent overlay covers the entire widget
  • βœ… Dynamic Control: Can be toggled on/off programmatically at any time
  • βœ… Mode Compatible: Works with all runner modes (default, preview, admin, view-only, print)

Use Cases:

  • Loading workflow data from backend
  • Processing form submissions
  • Saving progress to external systems
  • Waiting for validation responses
  • Any asynchronous operation that requires user to wait

In view-only mode, file uploads and signatures are displayed as clickable links instead of form inputs. This allows users to view and download the actual files and signatures that were submitted:

Print Mode: Enhanced File and Signature Display

In print mode, file uploads and signatures are displayed with detailed information instead of interactive elements, making them perfect for printed documents:

File Upload Display:

  • File Info Objects: Shows complete metadata including filename, size (human-readable), file type, and modification date
  • URL References: Automatically extracts filename and file type from URLs when only URL is available
  • Print-Friendly Format: Clean, structured display without any interactive elements

Signature Display:

  • Embedded Images: Signatures are displayed as images directly in the document
  • Fallback Handling: Graceful handling of missing or corrupted signature data
  • No Interactive Elements: Pure display mode suitable for printing

Example Print Mode File Display:

πŸ“Ž File: contract_agreement.pdf
πŸ“ Size: 2.3 MB
πŸ“„ Type: PDF Document
πŸ“… Modified: 12/15/2024, 2:30:15 PM
// Example: Instance with file upload and signature responses
const completedInstanceData = {
  id: "inst-123",
  workflowId: "wf-onboarding",
  name: "John Doe Onboarding",
  status: "completed",
  steps: [
    {
      id: "step-1",
      title: "Document Upload",
      responses: [
        {
          elementId: "resume-upload",
          value: "https://storage.example.com/files/john-doe-resume.pdf", // File URL
          answeredAt: new Date("2024-01-15T10:30:00Z"),
        },
        {
          elementId: "signature-pad",
          value: "https://storage.example.com/signatures/john-doe-sig.svg", // Signature URL directly in value
          answeredAt: new Date("2024-01-15T10:35:00Z"),
        },
      ],
      elements: [
        {
          id: "resume-upload",
          type: "file_upload",
          label: "Upload Resume",
          required: true,
        },
        {
          id: "signature-pad",
          type: "signature",
          label: "Digital Signature",
          required: true,
        },
      ],
    },
  ],
  completedSteps: ["step-1"],
};

// When rendered in view-only mode:
// - File upload shows: "πŸ“Ž filename.pdf" (clickable link that opens file in new tab)
// - Signature shows: embedded signature image (same as print mode)

HTML Export Utility

Drill Widgets includes a powerful HTML export utility that generates print-ready HTML from workflow instances. This is perfect for server-side PDF generation, email reports, and documentation.

Overview

The generateInstanceHTML() function takes a workflow Instance object and produces complete HTML that matches the appearance of the runner widget's print mode exactly. By default, it renders only the workflow steps and elements (just like print mode). Optionally, you can add metadata headers with instance information.

Basic Usage

import { generateInstanceHTML } from "drill-widgets";

const result = generateInstanceHTML(myInstance);
console.log(result.html); // Complete HTML string ready for PDF generation

With Options

const result = generateInstanceHTML(myInstance, {
  title: "Employee Onboarding Report",
  showTimestamps: true, // Add metadata header (not in print mode)
  customCSS: `
    .print-layout { 
      font-family: 'Times New Roman', serif; 
    }
  `,
});

PDF Generation Example

const puppeteer = require("puppeteer");

async function generatePDF(instance) {
  const result = generateInstanceHTML(instance, {
    title: "Workflow Report",
    showTimestamps: true,
  });

  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setContent(result.html, { waitUntil: "networkidle0" });
  await page.pdf({
    path: "./report.pdf",
    format: "A4",
    printBackground: true,
    margin: { top: "20mm", right: "20mm", bottom: "20mm", left: "20mm" },
  });

  await browser.close();
}

Server-Side Usage

const express = require("express");
const { generateInstanceHTML } = require("drill-widgets");

app.get("/report/:instanceId", async (req, res) => {
  const instance = await getInstanceFromDatabase(req.params.instanceId);

  const result = generateInstanceHTML(instance, {
    title: `Report: ${instance.name}`,
    showTimestamps: true,
  });

  res.setHeader("Content-Type", "text/html");
  res.send(result.html);
});

Features

  • βœ… Exact Print Mode Replica - Matches runner widget print mode exactly
  • βœ… All Question Types - Text, select, radio, checkbox, file uploads, signatures
  • βœ… File Metadata - Shows filename, size, type, modification dates
  • βœ… Signature Images - Embedded signatures from provided URLs
  • βœ… PDF-Ready - Print media queries, page breaks, embedded CSS
  • βœ… Server-Side Compatible - No browser dependencies, works in Node.js
  • βœ… Zero Dependencies - Pure HTML/CSS output

API Reference

interface HTMLExportOptions {
  title?: string; // Custom document title (default: instance.name)
  includeStyles?: boolean; // Include embedded CSS (default: true)
  customCSS?: string; // Additional CSS to inject
  showTimestamps?: boolean; // Show instance metadata header (default: false)
}

interface HTMLExportResult {
  html: string; // Complete HTML document
  title: string; // Document title used
  generatedAt: Date; // Generation timestamp
}

TypeScript Support

Drill Widgets includes comprehensive TypeScript definitions for full type safety and developer experience.

Installing Types

When you install drill-widgets from npm, the TypeScript declaration files are automatically included:

npm install drill-widgets

Importing Types

import {
  createBuilder,
  createRunner,
  type Workflow,
  type Instance,
  type Step,
  type StepElement,
  type QuestionElement,
  type ContentElement,
} from "drill-widgets";

// Use the types for type safety
const myWorkflow: Workflow = {
  id: "my-workflow",
  name: "My Workflow",
  steps: [],
};

const myInstance: Instance = {
  id: "my-instance",
  workflowId: "my-workflow",
  name: "My Instance",
  status: "active",
  steps: [],
  completedSteps: [],
};

Option 2: Import Types Only

import type {
  Workflow,
  Instance,
  Step,
  StepElement,
  QuestionElement,
  ContentElement,
  QuestionResponse,
  StepAssignment,
  BuilderConfig,
  RunnerConfig,
} from "drill-widgets";

Option 3: Dedicated Types Entry Point

import type {
  Workflow,
  Instance,
  StepElement,
  QuestionElement,
  ContentElement,
  BuilderConfig,
  RunnerConfig,
} from "drill-widgets/types";

Available Types

TypeDescription
WorkflowComplete workflow template definition
InstanceWorkflow instance with data and progress
StepIndividual workflow step
StepElementUnion of all possible step elements
QuestionElementForm question element
ContentElementContent/display element
QuestionResponseUser's answer to a question
InstanceStepElementStep response entry with revision metadata (needsRevision, revisionNotes)
StepAssignmentAssignment target + optional role/unit filters + optional multi-assignee data
StepStatusCurrent status of a step
WorkflowInstanceStatusOverall instance status, including revisions_needed
BuilderConfigConfiguration for the builder widget
RunnerConfigConfiguration for the runner widget
VisibilityElement visibility rules
VisibilityRuleIndividual visibility condition

Note: The Question type is deprecated and only included for legacy support. Use QuestionElement for all new development.

JavaScript Projects with JSDoc

For JavaScript projects, you can still get type hints using JSDoc:

/**
 * @typedef {import('drill-widgets').Workflow} Workflow
 * @typedef {import('drill-widgets').Instance} Instance
 */

/**
 * @param {Workflow} workflow
 * @param {Instance} instance
 */
function processWorkflow(workflow, instance) {
  // Full intellisense and type checking here
  console.log(workflow.name);
  console.log(instance.status);
}

Type Safety Benefits

  • Intellisense: Full autocomplete in VS Code, WebStorm, and other IDEs
  • Compile-time Checking: Catch errors before runtime
  • Documentation: Types serve as inline documentation
  • Refactoring: Safe refactoring with type checking
  • Zero Runtime Cost: Types are compile-time only, no bundle size impact

Project Structure

drill-widgets/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ models.ts          # TypeScript interfaces
β”‚   β”œβ”€β”€ lib.ts             # Main library API
β”‚   β”œβ”€β”€ builder-widget.ts  # Workflow template builder component
β”‚   β”œβ”€β”€ runner-widget.ts   # Workflow runner component
β”‚   β”œβ”€β”€ styles/            # Organized CSS styles for components
β”‚   β”‚   β”œβ”€β”€ base-styles.ts
β”‚   β”‚   β”œβ”€β”€ form-styles.ts
β”‚   β”‚   β”œβ”€β”€ section-styles.ts
β”‚   β”‚   β”œβ”€β”€ step-styles.ts
β”‚   β”‚   β”œβ”€β”€ modal-styles.ts
β”‚   β”‚   └── index.ts
β”‚   └── main.ts            # Entry point for development
β”œβ”€β”€ scripts/
β”‚   └── build-html.js      # Build script for production HTML
β”œβ”€β”€ dist/                  # Generated files (after build)
β”‚   β”œβ”€β”€ drill-widgets.umd.js
β”‚   └── index.html
β”œβ”€β”€ index.html             # Development HTML (ES modules)
β”œβ”€β”€ vite.config.js         # Vite configuration
└── package.json

Development Commands

# Start development server with hot reload
npm run dev

# Build the production bundle
npm run build

# Preview the production build
npm run preview

# Run tests (placeholder)
npm test

Troubleshooting

"Drill library not found" Error

  • In Development: Make sure you're using npm run dev, not npm run preview
  • In Production: Make sure you ran npm run build before npm run preview

Module Loading Issues

  • Development mode uses ES modules and requires a modern browser
  • Production mode uses UMD bundle and works in older browsers

Build Errors

  • Ensure TypeScript compiles without errors: npx tsc --noEmit
  • Check that all imports are correctly typed and available

Let Drill Widgets streamline your application's workflows!

Keywords

workflow

FAQs

Package last updated on 10 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts