Vela UI Docsvenizia.ai

Doc List

One bulleted, numbered or to-do list item built on the doc text line.

npx shadcn@latest add @velaui/doc-list
npx shadcn@latest add @velaui/doc-list

Examples

  • Default
  • Bulleted
  • Todo

View code

Doc List · Default

Installation

pnpm dlx velaui add @velaui/doc-list
pnpm dlx velaui add @velaui/doc-list

Code

doc-list-default.tsx
import * as React from "react";

import { DocListItem } from "@/components/ui/doc-list";

export default function Example() {
  const [items, setItems] = React.useState(["Freeze the schema", "Run the dry migration", "Migrate"]);
  return (
    <div className="w-full max-w-xl p-6">
      {items.map((v, i) => (
        <DocListItem
          key={i}
          type="numbered"
          number={i + 1}
          value={v}
          onChange={(t) => setItems(items.map((x, j) => (j === i ? t : x)))}
        />
      ))}
    </div>
  );
}

View code

Doc List · Bulleted

Installation

pnpm dlx velaui add @velaui/doc-list
pnpm dlx velaui add @velaui/doc-list

Code

doc-list-bulleted.tsx
import { DocListItem } from "@/components/ui/doc-list";

export default function Example() {
  return (
    <div className="w-full max-w-xl p-6">
      <DocListItem type="bulleted" value="Onboarding" readOnly />
      <DocListItem type="bulleted" value="Billing" readOnly />
    </div>
  );
}

View code

Doc List · Todo

Installation

pnpm dlx velaui add @velaui/doc-list
pnpm dlx velaui add @velaui/doc-list

Code

doc-list-todo.tsx
import * as React from "react";

import { DocListItem } from "@/components/ui/doc-list";

export default function Example() {
  const [done, setDone] = React.useState([true, false]);
  const tasks = ["Ship the new onboarding", "Move billing to the new provider"];
  return (
    <div className="w-full max-w-xl p-6">
      {tasks.map((t, i) => (
        <DocListItem
          key={t}
          type="todo"
          value={t}
          checked={done[i]}
          onCheckedChange={(c) => setDone(done.map((x, j) => (j === i ? c : x)))}
          readOnly={false}
        />
      ))}
    </div>
  );
}

Usage

import * as React from "react";

import { DocListItem } from "@/components/ui/doc-list";

export default function Example() {
  const [items, setItems] = React.useState(["Freeze the schema", "Run the dry migration", "Migrate"]);
  return (
    <div className="w-full max-w-xl p-6">
      {items.map((v, i) => (
        <DocListItem
          key={i}
          type="numbered"
          number={i + 1}
          value={v}
          onChange={(t) => setItems(items.map((x, j) => (j === i ? t : x)))}
        />
      ))}
    </div>
  );
}

Props

PropTypeDefault
checkedbooleanfalse
highlightsstring[]—
numbernumber1
onChange(value: string, caret: number) => void—
onCheckedChange(checked: boolean) => void—
typeDocListType"bulleted"
valuerequiredstring—

Takes these and every prop of DocListItemProps.

Dependencies

npm
none besides React
Installs with it
Doc Typography

Source

components/ui/doc-list.tsx

import { DocTypography, type DocTypographyProps } from "@/components/ui/doc-typography";
import { cn } from "@/lib/utils";

export type DocListType = "bulleted" | "numbered" | "todo";

export interface DocListItemProps extends Omit<DocTypographyProps, "type"> {
  type?: DocListType;
  /** The 1-based position of a numbered item in its run. */
  number?: number;
  checked?: boolean;
  onCheckedChange?: (checked: boolean) => void;
}

/** One item of a bulleted, numbered or to-do list. */
function DocListItem({
  type = "bulleted",
  number = 1,
  checked = false,
  onCheckedChange,
  readOnly,
  className,
  ...props
}: DocListItemProps) {
  return (
    <div className="flex items-start gap-2">
      {type === "todo" ? (
        <label className="mt-1 flex shrink-0 pointer-coarse:-m-2.5 pointer-coarse:mt-[-0.375rem] pointer-coarse:p-2.5">
          <input
            type="checkbox"
            aria-label="Done"
            className="size-4 cursor-pointer accent-(--primary-default)"
            checked={checked}
            disabled={readOnly}
            onChange={(e) => onCheckedChange?.(e.target.checked)}
          />
        </label>
      ) : (
        <span aria-hidden={type === "bulleted"} className="w-4 shrink-0 pt-0.5 text-sm text-text-muted">
          {type === "numbered" ? `${number}.` : "•"}
        </span>
      )}
      <div className="min-w-0 flex-1">
        <DocTypography
          aria-label={type === "todo" ? "To-do" : "List item"}
          readOnly={readOnly}
          className={cn(type === "todo" && checked && "text-text-muted line-through", className)}
          {...props}
        />
      </div>
    </div>
  );
}

export { DocListItem };