npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sketchmonk/value-mapper-vue

v0.1.0-alpha.4

Published

A headless component for mapping values between different states

Readme

Value Mapper Vue

A headless Vue 3 component library for creating interactive value mapping interfaces. Draw connections between stationary source and target nodes with customizable styling and behavior.

Features

  • 🎯 Headless Components - Complete control over styling and layout
  • 🖱️ Connection Drawing - Intuitive connection creation with mouse and touch support
  • 📱 Mobile Friendly - Touch events support for mobile devices
  • 🎨 Customizable Paths - Bezier curves and smooth step connections
  • Vue 3 Composition API - Built with modern Vue patterns
  • 🔗 Reactive Connections - Real-time updates with v-model binding
  • 🪝 Composable Utilities - Reusable logic for custom implementations

Installation

npm install @sketchmonk/value-mapper-vue

Quick Start

<script setup lang="ts">
import {
  ValueMapperRoot,
  ValueMapperNode,
  ValueMapperConnection,
  ValueMapperConnectionPreview
} from '@sketchmonk/value-mapper-vue';

const mapping = ref<Record<string, string>>({});

const sourceItems = [
  { id: 'item1', label: 'Source 1' },
  { id: 'item2', label: 'Source 2' }
];

const targetItems = [
  { id: 'target1', label: 'Target 1' },
  { id: 'target2', label: 'Target 2' }
];
</script>

<template>
  <ValueMapperRoot v-model="mapping" class="w-full h-96">
    <template #connections="{ connections }">
      <ValueMapperConnection
        v-for="connection in connections"
        :key="connection.id"
        :connection="connection"
        v-slot="{ getBezierPath }"
      >
        <path
          :d="getBezierPath()"
          fill="none"
          stroke="#10b981"
          stroke-width="2"
        />
      </ValueMapperConnection>
      
      <ValueMapperConnectionPreview v-slot="{ getBezierPath }">
        <path
          :d="getBezierPath()"
          fill="none"
          stroke="#6b7280"
          stroke-dasharray="5,5"
          stroke-width="1"
        />
      </ValueMapperConnectionPreview>
    </template>

    <div class="flex justify-between items-center h-full">
      <!-- Source nodes -->
      <div class="flex flex-col gap-4">
        <ValueMapperNode
          v-for="item in sourceItems"
          :key="item.id"
          :identifier="item.id"
          type="source"
        >
          <div class="px-4 py-2 bg-blue-100 rounded border">
            {{ item.label }}
          </div>
          <template #handle>
            <div class="w-3 h-3 bg-blue-500 rounded-full"></div>
          </template>
        </ValueMapperNode>
      </div>

      <!-- Target nodes -->
      <div class="flex flex-col gap-4">
        <ValueMapperNode
          v-for="item in targetItems"
          :key="item.id"
          :identifier="item.id"
          type="target"
        >
          <div class="px-4 py-2 bg-green-100 rounded border">
            {{ item.label }}
          </div>
          <template #handle>
            <div class="w-3 h-3 bg-green-500 rounded-full"></div>
          </template>
        </ValueMapperNode>
      </div>
    </div>
  </ValueMapperRoot>
</template>

Components

ValueMapperRoot

The root container component that manages the overall state and provides context to child components.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | v-model | Record<string, string> | {} | The mapping object where keys are source IDs and values are target IDs | | class | string | undefined | CSS class for the root element | | style | object | undefined | Inline styles for the root element |

Slots

| Slot | Props | Description | |------|-------|-------------| | default | { isDragging: boolean } | Main content area for nodes | | connections | { connections: Connection[] } | SVG overlay for rendering connections |

Example

<ValueMapperRoot v-model="mapping" class="relative w-full h-96">
  <template #connections="{ connections }">
    <!-- Connection rendering -->
  </template>
  
  <template #default="{ isDragging }">
    <!-- Your nodes here -->
    <div :class="{ 'pointer-events-none': isDragging }">
      <!-- Node content -->
    </div>
  </template>
</ValueMapperRoot>

ValueMapperNode

Represents a stationary node in the mapping interface that can be connected to other nodes.

Props

| Prop | Type | Required | Description | |------|------|----------|-------------| | identifier | string | ✅ | Unique identifier for the node | | type | "source" \| "target" | ✅ | Whether this is a source or target node | | class | string | ❌ | CSS class for the node element | | style | object | ❌ | Inline styles for the node element |

Slots

| Slot | Props | Description | |------|-------|-------------| | default | { isConnected: boolean, isConnecting: boolean } | Main node content | | handle | { isConnected: boolean, isConnecting: boolean } | Connection handle (positioned automatically) |

Example

<ValueMapperNode identifier="node1" type="source" class="group">
  <template #default="{ isConnected, isConnecting }">
    <div :class="[
      'px-4 py-2 rounded border',
      { 'border-blue-500': isConnecting, 'border-green-500': isConnected }
    ]">
      Node Content
    </div>
  </template>
  
  <template #handle="{ isConnected }">
    <div :class="[
      'w-3 h-3 rounded-full',
      isConnected ? 'bg-green-500' : 'bg-gray-300'
    ]"></div>
  </template>
</ValueMapperNode>

ValueMapperConnection

Renders individual connections between nodes.

Props

| Prop | Type | Required | Description | |------|------|----------|-------------| | connection | Connection | ✅ | Connection object with source and target IDs |

Slots

| Slot | Props | Description | |------|-------|-------------| | default | See below | Slot for rendering the connection path |

Slot Props

{
  source: string;
  target: string;
  coords: { source: { x: number, y: number }, target: { x: number, y: number } } | null;
  removeConnection: () => void;
  getBezierPath: () => string;
  getSmoothStepPath: (radius?: number) => string;
  getArrowPath: (size?: number, offset?: number) => string;
}

Example

<ValueMapperConnection :connection="connection" v-slot="{ getBezierPath, getArrowPath, removeConnection }">
  <g @click="removeConnection">
    <path
      :d="getBezierPath()"
      fill="none"
      stroke="#10b981"
      stroke-width="2"
      class="cursor-pointer hover:stroke-red-500"
    />
    <path
      :d="getArrowPath(8, 4)"
      fill="#10b981"
      class="cursor-pointer hover:fill-red-500"
    />
  </g>
</ValueMapperConnection>

ValueMapperConnectionPreview

Shows a preview of the connection being drawn.

Slots

| Slot | Props | Description | |------|-------|-------------| | default | See below | Slot for rendering the preview path |

Slot Props

{
  coords: { source: { x: number, y: number }, target: { x: number, y: number } } | null;
  getBezierPath: () => string;
  getSmoothStepPath: (radius?: number) => string;
  getArrowPath: (size?: number, offset?: number) => string;
}

Example

<ValueMapperConnectionPreview v-slot="{ getBezierPath, getArrowPath }">
  <path
    :d="getBezierPath()"
    fill="none"
    stroke="#6b7280"
    stroke-dasharray="5,5"
    stroke-width="1"
  />
  <path
    :d="getArrowPath(8)"
    fill="#6b7280"
    opacity="0.7"
  />
</ValueMapperConnectionPreview>

Composables

useValueMapperStore()

Access the value mapper store from within child components.

const {
  mapping,
  connections,
  isDragging,
  dragSource,
  addConnection,
  removeConnection,
  startConnection,
  endConnection,
  cancelConnection,
  hasConnection
} = useValueMapperStore();

useNode(id, type)

Manage individual node state and interactions.

const {
  el,
  rect,
  isConnected,
  isDragSource,
  onMouseDown,
  onMouseUp,
  onTouchEnd,
  onClick
} = useNode('node-id', 'source');

useConnectionUtils(source, target)

Utilities for managing connections between specific nodes.

const {
  removeConnection,
  getBezierPath,
  getSmoothStepPath,
  getArrowPath,
  coords
} = useConnectionUtils('source-id', 'target-id');

useConnectionPreview()

Manage connection preview during drawing operations.

const {
  getBezierPath,
  getSmoothStepPath,
  getArrowPath,
  coords
} = useConnectionPreview();

Types

export type NodeType = "source" | "target";

export interface Connection {
  id: string;
  source: string;
  target: string;
}

Advanced Usage

Custom Path Styles

You can create different connection styles using the provided path generators:

<!-- Bezier curves -->
<path :d="getBezierPath()" />

<!-- Smooth step paths -->
<path :d="getSmoothStepPath(5)" />

<!-- Custom arrows -->
<path :d="getArrowPath(12, 6)" />

Dynamic Styling

Use the slot props to create dynamic styles based on connection state:

<ValueMapperNode identifier="node1" type="source">
  <template #default="{ isConnected, isConnecting }">
    <div :class="{
      'border-blue-500': isConnecting,
      'border-green-500': isConnected,
      'border-gray-300': !isConnected && !isConnecting
    }">
      <!-- Node content -->
    </div>
  </template>
</ValueMapperNode>

Touch Support

The components automatically handle touch events for mobile devices. The connection drawing behavior works seamlessly across desktop and mobile platforms.

Browser Support

  • Modern browsers with ES2015+ support
  • Vue 3.x
  • Touch-enabled devices

License

MIT License

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to the repository.