Tool Rail

PreviousNext

A compact rail of icon-only tools, welded into a single bordered strip.

<script setup lang="ts">
import { ref } from 'vue'
import {
  IconCursor,
  IconMove,
  IconSelectEdge,
  IconSelectFace,
  IconSelectObject,
} from '@/components/ui/icons'
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'

const tools = [
  { id: 'select', label: 'Select', icon: IconCursor },
  { id: 'move', label: 'Move', icon: IconMove },
  { id: 'object', label: 'Object mode', icon: IconSelectObject, arrow: true },
  { id: 'edge', label: 'Edge mode', icon: IconSelectEdge },
  { id: 'face', label: 'Face mode', icon: IconSelectFace },
]

const active = ref('object')

// Only the ends of the rail keep their rounding, so the buttons weld together.
function positionOf(index: number) {
  if (tools.length === 1)
    return 'default'
  if (index === 0)
    return 'top'
  if (index === tools.length - 1)
    return 'bottom'
  return 'middle'
}
</script>

<template>
  <ToolRail>
    <ToolRailButton
      v-for="(tool, index) in tools"
      :key="tool.id"
      :label="tool.label"
      :position="positionOf(index)"
      :active="active === tool.id"
      :show-arrow="tool.arrow"
      @click="active = tool.id"
    >
      <component :is="tool.icon" />
    </ToolRailButton>
  </ToolRail>
</template>

Installation

pnpm dlx hui-kit@latest add tool-rail

Usage

<script setup lang="ts">
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'
</script>

<template>
  <ToolRail>
    <ToolRailButton label="Select" position="top" active />
    <ToolRailButton label="Move" position="middle" />
    <ToolRailButton label="Rotate" position="bottom" show-arrow />
  </ToolRail>
</template>

position controls which corners stay round, so stacked buttons weld into one strip. Give the first button top, the last bottom, everything between middle — the same idea as position on Input, where it welds fields into a group.

Every button needs a label: it has no visible text, so without one the button is unnamed for a screen reader.

Examples

Horizontal

<script setup lang="ts">
import { ref } from 'vue'
import { IconAddObjectCube, IconAddObjectCylinder, IconAddObjectHex } from '@/components/ui/icons'
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'

const shapes = [
  { id: 'cube', label: 'Add cube', icon: IconAddObjectCube },
  { id: 'hex', label: 'Add hexagon', icon: IconAddObjectHex },
  { id: 'cylinder', label: 'Add cylinder', icon: IconAddObjectCylinder },
]

const active = ref('cube')
</script>

<template>
  <ToolRail orientation="horizontal">
    <ToolRailButton
      v-for="(shape, index) in shapes"
      :key="shape.id"
      :label="shape.label"
      :position="index === 0 ? 'top' : index === shapes.length - 1 ? 'bottom' : 'middle'"
      :active="active === shape.id"
      @click="active = shape.id"
    >
      <component :is="shape.icon" />
    </ToolRailButton>
  </ToolRail>
</template>