feat(split): add snap points support to Split and Split.Resizer#37
feat(split): add snap points support to Split and Split.Resizer#37gfazioli merged 4 commits intogfazioli:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds snap point support to @gfazioli/mantine-split-pane so resizers can “snap” to common pane sizes during mouse/touch drag and keyboard resizing. This extends the existing Split → Resizer context inheritance model, allowing split-level defaults with per-resizer overrides.
Changes:
- Introduces
snapPointsandsnapToleranceprops onSplit/Split.Resizer, and applies snapping in the resizer resize logic. - Adds internal snapping utilities (
normalizeSnapPoints,snapToNearestPoint,calculateSnappedPaneSizes) with dedicated unit tests. - Updates README + docs + demos, and expands component tests to cover drag/keyboard snapping and autoResizers inheritance.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents snap points usage and adds an example snippet. |
| package/src/Split.tsx | Wires snapPoints/snapTolerance through Split context so resizers can inherit defaults. |
| package/src/Split.test.tsx | Adds integration-style tests for drag/keyboard snapping and inheritance/overrides. |
| package/src/Split.story.tsx | Exposes snap props as Storybook controls for Split. |
| package/src/Resizer/SplitResizer.tsx | Applies snapping during resize (mouse/touch/keyboard) via shared helper. |
| package/src/Resizer/SplitResizer.story.tsx | Exposes snap props as Storybook controls for Resizer. |
| package/src/Resizer/snap.ts | Adds snap point normalization and snapped-size calculation helpers. |
| package/src/Resizer/snap.test.ts | Adds unit tests for snap helper behavior and constraint handling. |
| docs/docs.mdx | Adds docs section describing snap points behavior and inheritance rules. |
| docs/demos/Split.demo.snap.tsx | Adds a new documentation demo showcasing inherited snap points and per-resizer overrides. |
| docs/demos/index.ts | Exports the new snap demo for the docs site. |
Review NotesExcellent contribution! The implementation is clean, well-tested, and follows the project's patterns perfectly. A few non-blocking observations: Bug fix needed (minor)
// snap.ts:39
const points = Array.isArray(snapPoints) ? snapPoints : [];Storybook UXThe current stories only add Future enhancements (non-blocking)These are not blockers for this PR, but worth tracking for the future:
Overall: great work 👍 |
Really appreciate the thoughtful review and kind feedback, thank you! 🙏 |
- Add Array.isArray guard in normalizeSnapPoints to prevent crash when Storybook object control passes non-array values - Add unit test for non-array snapPoints input - Add dedicated SnapPoints story with pre-configured values (snapPoints=[200,400,600], snapTolerance=20) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fd021b2 to
b25212a
Compare
…llback Extends the snap points feature introduced in #37 with: - Percentage snap points (e.g. `'50%'`), resolved against the combined size of the two adjacent panes. - Responsive snap sets via Mantine breakpoint maps (e.g. `{ base: [200], md: [300, 500] }`). Closes #39. - `snapFrom?: 'before' | 'after'` prop on `Split` and `Split.Resizer` to interpret each snap point as the target size of the pane after the resizer instead of the pane before. Closes #40. - `onSnap?: (point: number | null) => void` callback on `Split.Resizer`, fired once on snap enter (with the resolved pixel value in the `snapFrom` reference) and once on exit (with `null`). Not fired on every pointer tick — state-based by design. Closes #41. - `data-snapping` attribute on the resizer root while inside a snap zone, for CSS-driven visual feedback. - Stricter `normalizeSnapPoints`: rejects negative values and malformed percentage strings; trims whitespace from valid percentage strings. Also extends `useResponsiveValue` to treat arrays as plain values so that `ResponsiveValue<T[]>` works correctly for array props.
…llback Extends the snap points feature introduced in #37 with: - Percentage snap points (e.g. `'50%'`), resolved against the combined size of the two adjacent panes. - Responsive snap sets via Mantine breakpoint maps (e.g. `{ base: [200], md: [300, 500] }`). Closes #39. - `snapFrom?: 'before' | 'after'` prop on `Split` and `Split.Resizer` to interpret each snap point as the target size of the pane after the resizer instead of the pane before. Closes #40. - `onSnap?: (point: number | null) => void` callback on `Split.Resizer`, fired once on snap enter (with the resolved pixel value in the `snapFrom` reference) and once on exit (with `null`). Not fired on every pointer tick — state-based by design. Closes #41. - `data-snapping` attribute on the resizer root while inside a snap zone, for CSS-driven visual feedback. - Stricter `normalizeSnapPoints`: rejects negative values and malformed percentage strings; trims whitespace from valid percentage strings. Also extends `useResponsiveValue` to treat arrays as plain values so that `ResponsiveValue<T[]>` works correctly for array props.
Summary
Adds optional snap point support to
mantine-split-pane.New props:
snapPoints?: number[]snapTolerance?: numberThese props are supported on both
SplitandSplit.Resizer.Splitprovides inherited defaults through the existing resizer context, whileSplit.Resizercan override behavior per divider.Details
autoResizerssnapTolerancedefaults to10snapToleranceis treated as0Docs and tests
Verification
Ran:
yarn buildyarn docgenyarn format:testyarn typecheckyarn eslint package/src docs/demosyarn jest package/src/Resizer/snap.test.ts package/src/Split.test.tsx --runInBand