Skip to content

board.ui - UI Controls

Control component appearance, size, and user notifications.

Overview

board.ui provides methods to:

  • Update component preview (static mode)
  • Resize component
  • Show toast notifications
  • Request/release focus (Interaction Mode)
  • Access clipboard

Preview Updates

updatePreview() - Set Static Preview

Update the component's preview shown when not in Interaction Mode:

typescript
// From SVG string
const svg = '<svg>...</svg>';
await board.ui.updatePreview(svg);

// From canvas element
const canvas = document.querySelector('canvas');
await board.ui.updatePreview(canvas);

// From any DOM element (screenshot)
await board.ui.updatePreviewFromElement(document.body);

The SDK automatically generates previews on blur, but you can manually update for custom previews.

When to Use Manual Preview

typescript
board.on('beforeFreeze', async () => {
  // Generate custom preview before blur
  const specialView = renderSpecialView();
  await board.ui.updatePreview(specialView);
});

Resize

resize() - Change Component Size

typescript
// Set size
await board.ui.resize({ width: 400, height: 300 });

// Get current size
const size = board.ui.getSize();
console.log(size); // { width: 400, height: 300 }

Focus Management

requestFocus() - Enter Interaction Mode

typescript
// Ask platform to focus component
board.ui.requestFocus();

Use case: Component wants to grab attention (e.g., timer finished).

releaseFocus() - Exit Interaction Mode

typescript
// Exit Interaction Mode (like pressing ESC)
board.ui.releaseFocus();

Use case: Close modal, finish interaction.

Notifications

showToast() - Show Toast Message

typescript
// Success message
board.ui.showToast('Saved!', { type: 'success', duration: 3000 });

// Error message
board.ui.showToast('Failed to save', { type: 'error' });

// Info message (default)
board.ui.showToast('Processing...', { type: 'info', duration: 2000 });

// Warning message
board.ui.showToast('Low battery', { type: 'warning' });

Toast options:

typescript
interface ToastOptions {
  type?: 'success' | 'error' | 'info' | 'warning';
  duration?: number;  // milliseconds (default: 3000)
}

Clipboard

clipboard - Read/Write Clipboard

typescript
// Copy to clipboard
await board.ui.clipboard.write('Hello, World!');
board.ui.showToast('Copied to clipboard', { type: 'success' });

// Read from clipboard
const text = await board.ui.clipboard.read();
console.log('Pasted:', text);

Examples

Auto-Resize Based on Content

typescript
import { BoardAPI } from '@boardapi/sdk';

const board = new BoardAPI();

async function fitContent() {
  const content = document.querySelector('#content');
  const rect = content.getBoundingClientRect();

  await board.ui.resize({
    width: Math.ceil(rect.width) + 40,  // Add padding
    height: Math.ceil(rect.height) + 40
  });
}

board.on('ready', fitContent);

// Re-fit when content changes
board.storage.subscribe('items', fitContent);

Custom Preview for Chart

typescript
const board = new BoardAPI();
const chart = initChart();

board.on('beforeFreeze', async () => {
  // Generate high-quality chart preview
  const canvas = chart.toCanvas({ scale: 2 });
  await board.ui.updatePreview(canvas);
});

Copy/Paste Data

typescript
const board = new BoardAPI();

// Copy button
document.querySelector('#copy-btn').addEventListener('click', async () => {
  const data = board.storage.getAll();
  const text = JSON.stringify(data, null, 2);

  await board.ui.clipboard.write(text);
  board.ui.showToast('Copied to clipboard', { type: 'success' });
});

// Paste button
document.querySelector('#paste-btn').addEventListener('click', async () => {
  try {
    const text = await board.ui.clipboard.read();
    const data = JSON.parse(text);

    await board.storage.set('importedData', data);
    board.ui.showToast('Pasted successfully', { type: 'success' });
  } catch (err) {
    board.ui.showToast('Invalid data', { type: 'error' });
  }
});

Timer Notification

typescript
const board = new BoardAPI();

board.storage.subscribe('remaining', async (seconds) => {
  if (seconds === 0) {
    // Timer finished
    board.ui.showToast('Time is up!', { type: 'warning', duration: 5000 });

    // Request focus to grab attention
    board.ui.requestFocus();

    // Play sound
    const audio = new Audio('/alert.mp3');
    await audio.play();
  }
});

Form Validation

typescript
const board = new BoardAPI();

document.querySelector('#submit-btn').addEventListener('click', async () => {
  const name = document.querySelector<HTMLInputElement>('#name').value;
  const email = document.querySelector<HTMLInputElement>('#email').value;

  // Validate
  if (!name.trim()) {
    board.ui.showToast('Name is required', { type: 'error' });
    return;
  }

  if (!email.includes('@')) {
    board.ui.showToast('Invalid email', { type: 'error' });
    return;
  }

  // Save
  await board.storage.set('formData', { name, email });
  board.ui.showToast('Saved successfully', { type: 'success' });

  // Exit Interaction Mode
  board.ui.releaseFocus();
});

Export as Image

typescript
const board = new BoardAPI();

document.querySelector('#export-btn').addEventListener('click', async () => {
  board.ui.showToast('Generating image...', { type: 'info' });

  // Generate canvas from current view
  const canvas = await html2canvas(document.body);

  // Convert to blob
  canvas.toBlob(async (blob) => {
    // Copy image to clipboard
    await navigator.clipboard.write([
      new ClipboardItem({ 'image/png': blob })
    ]);

    board.ui.showToast('Image copied to clipboard', { type: 'success' });
  });
});

Responsive Sizing

typescript
const board = new BoardAPI();

function updateSize() {
  const items = board.storage.get('items', []);
  const itemsPerRow = 3;
  const itemWidth = 100;
  const itemHeight = 80;
  const padding = 20;

  const rows = Math.ceil(items.length / itemsPerRow);

  board.ui.resize({
    width: (itemsPerRow * itemWidth) + (padding * 2),
    height: (rows * itemHeight) + (padding * 2)
  });
}

board.on('ready', updateSize);
board.storage.subscribe('items', updateSize);

Progress Indicator

typescript
const board = new BoardAPI();

async function processItems(items: any[]) {
  const total = items.length;

  for (let i = 0; i < total; i++) {
    await processItem(items[i]);

    // Show progress
    const percent = Math.round(((i + 1) / total) * 100);
    board.ui.showToast(`Processing... ${percent}%`, {
      type: 'info',
      duration: 500
    });
  }

  board.ui.showToast('All done!', { type: 'success' });
}

Toast Best Practices

  1. Use appropriate types - Success for confirmations, error for failures
  2. Keep messages short - 2-5 words is ideal
  3. Don't spam - Debounce frequent operations
  4. Set duration wisely - Errors can stay longer (5s), success shorter (2s)
  5. Actionable messages - "Saved" is better than "Operation completed"

Preview Best Practices

  1. Let SDK auto-generate - Only customize when needed
  2. Use SVG when possible - Better quality, smaller size
  3. Optimize canvas - Scale down for preview
  4. Update on blur - Use beforeFreeze event
  5. Include key data - Preview should show component state

What's Next?