Partition Explorer
A step-by-step visualisation of the partition operation at the heart of quicksort. Given an initial array and a pivot strategy, every comparison and swap is precomputed; cells glide across the partition boundary via shared layoutId so the eye tracks each value. Pick lomuto for the single-pointer canonical walk or hoare for the original two-pointer version.
3
6
1
8
2
7
4
5
Pivot: 5 (index 7). Move it to the end as a starting position.
Customize
Algorithm
last
Playback
600ms
Installation
npx shadcn@latest add https://craftbits.dev/r/partition-explorer.jsonUsage
import { PartitionExplorer } from "@craft-bits/core";
<PartitionExplorer
values={[3, 6, 1, 8, 2, 7, 4, 5]}
pivot="last"
scheme="lomuto"
/>Drive playback from outside to sync with narration or a scrubber:
const [step, setStep] = useState(0);
<PartitionExplorer
values={values}
currentStep={step}
onCurrentStepChange={setStep}
playing
playSpeed={400}
/>Understanding the component
- Precomputed snapshots. When
values,pivot, orschemechange, the walk is recomputed end-to-end insideuseMemo. Each snapshot carries the array, the pivot identity, the currenti/jpointer positions, and a human-readable caption. - Stable cell identities. Every value is tagged with an
idon mount and the same id follows it through every swap. Cells render with a sharedlayoutId, so a value that crosses the partition boundary glides to its new index instead of cross-fading. - Two schemes.
lomutois the canonical single-pointer walk —itracks the partition boundary,jscans every element.hoareis the two-pointer version —iandjmarch inward and swap any pair on the wrong side of the pivot. - Pivot semantics.
"first" | "middle" | "last"selects a position; a numericpivotselects an explicit index (clamped). The pivot cell wears the accent fill across the entire walk. - Reduced motion. The spring collapses to
duration: 0, autoplay is suppressed, and the component snaps to the final snapshot — so the result is reachable without animation.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
values | readonly number[] | required | Initial array to partition. |
pivot | "first" | "middle" | "last" | number | "last" | Pivot index strategy. |
scheme | "lomuto" | "hoare" | "lomuto" | Partition algorithm to visualise. |
currentStep | number | — | Controlled active step. Pair with onCurrentStepChange. |
defaultCurrentStep | number | 0 | Uncontrolled initial step. |
onCurrentStepChange | (step: number) => void | — | Fires whenever the active step changes. |
playing | boolean | — | Controlled play state. Pair with onPlayingChange. |
defaultPlaying | boolean | false | Uncontrolled initial play state. |
onPlayingChange | (playing: boolean) => void | — | Fires when play/pause flips. |
playSpeed | number | 600 | Milliseconds between auto-played steps. |
showCaption | boolean | true | Render the per-step description beneath the array. |
className | string | — | Merged onto the outer <div> via cn(). |
Accessibility
- The root is
role="group"with anaria-labelnaming the scheme and total step count. - Each cell carries
data-cell-state(pivot/less/geq/default) and anaria-labeldescribing its index, value, and partition side — three orthogonal signals (label, ring, fill) for colorblind users. - The caption sits in an
aria-live="polite"region so screen-reader users hear the new step on every change. - Motion respects
prefers-reduced-motion: transitions collapse toduration: 0, autoplay is suppressed, and the component jumps to the final snapshot on mount.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/PartitionExplorer.tsx). The original is a dual-array median-of-two-sorted-arrays primitive tied to AF's track-colour system; the library extract is a focused quicksort partition walker. Identity-preserving swaps, Lomuto vs Hoare, and the reduced-motion snap-to-final behaviour are new for craft-bits.