AI
Kanban logov1.0.0

Framework-Agnostic Core (createKanbanBoard)

Under the hood, Nofinite Kanban is built upon a framework-agnostic, zero-React core: createKanbanBoard.

The vanilla core mounts directly into any standard DOM container in Vanilla TypeScript, Vue 3, Svelte 5, SolidJS, or Angular, while consuming identical NUICSS superclasses.

typescript
import { 
  createKanbanBoard, 
  type KanbanColumn, 
  type KanbanItem, 
  type KanbanBoardInstance 
} from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';

1. Quickstart (Vanilla TypeScript / DOM)

typescript
import { createKanbanBoard, type KanbanColumn } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';

const container = document.getElementById('kanban-root')!;

const initialColumns: KanbanColumn[] = [
  {
    id: 'todo',
    title: 'To Do',
    wipLimit: 5,
    items: [
      { id: 'task-1', title: 'Implement Auth Tokens', badge: { text: 'Security', variant: 'danger' } },
      { id: 'task-2', title: 'Write Playwright Tests', badge: { text: 'QA', variant: 'success' } },
    ],
  },
  {
    id: 'in-progress',
    title: 'In Progress',
    wipLimit: 2,
    items: [{ id: 'task-3', title: 'Optimize Bundle Sizing' }],
  },
  {
    id: 'done',
    title: 'Done',
    items: [],
  },
];

const board: KanbanBoardInstance = createKanbanBoard(container, {
  columns: initialColumns,
  enforceWipLimit: true,
  onCardMove: (event) => console.log('Card moved:', event),
  onColumnMove: (event) => console.log('Column moved:', event)
});

// Imperative controls:
// board.addCard('todo', { id: 'task-4', title: 'Deploy to Cloudflare' });
// board.setFilter({ query: 'Tokens' });
// board.destroy();

2. Vue 3 Composition API Integration

Integrate effortlessly into Vue 3 templates using a template ref:

vue
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { createKanbanBoard, type KanbanBoardInstance, type KanbanColumn } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';

const containerRef = ref<HTMLElement | null>(null);
let board: KanbanBoardInstance | null = null;

const props = defineProps<{ columns: KanbanColumn[] }>();
const emit = defineEmits(['change']);

onMounted(() => {
  if (containerRef.value) {
    board = createKanbanBoard(containerRef.value, {
      columns: props.columns,
      onCardMove: () => emit('change', board?.getColumns()),
      onColumnMove: () => emit('change', board?.getColumns())
    });
  }
});

watch(() => props.columns, (newCols) => {
  board?.setColumns(newCols);
}, { deep: true });

onBeforeUnmount(() => {
  board?.destroy();
});
</script>

<template>
  <div ref="containerRef" class="w-full h-full min-h-[500px]" />
</template>

3. Svelte 5 Integration

Use standard Svelte $effect and action mounting:

svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import { createKanbanBoard, type KanbanBoardInstance, type KanbanColumn } from '@nofinite/kanban';
  import '@nofinite/nuicss/styles.css';

  let { columns = $bindable() }: { columns: KanbanColumn[] } = $props();
  let container: HTMLElement;
  let board: KanbanBoardInstance;

  onMount(() => {
    board = createKanbanBoard(container, {
      columns,
      onCardMove: () => {
        columns = board.getColumns();
      }
    });

    return () => board.destroy();
  });
</script>

<div bind:this={container} class="w-full h-full min-h-[550px]" />