Skip to content

Commit 4cfebdb

Browse files
authored
Merge pull request #1 from shakcho/fix/child-click-fix
fix: click issue from the child
2 parents b461af4 + a783ba1 commit 4cfebdb

9 files changed

Lines changed: 1883 additions & 42 deletions

File tree

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 20
21+
22+
- name: Install dependencies
23+
run: npm ci
24+
25+
- name: Run tests
26+
run: npm test
27+
28+
- name: Build
29+
run: npm run build

.github/workflows/release.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
inputs:
8+
release_type:
9+
description: 'Release type (patch, minor, major)'
10+
required: true
11+
type: choice
12+
options:
13+
- patch
14+
- minor
15+
- major
16+
17+
permissions:
18+
contents: write
19+
20+
jobs:
21+
release:
22+
runs-on: ubuntu-latest
23+
# Skip release commits to avoid infinite loops
24+
if: "!startsWith(github.event.head_commit.message, 'release:')"
25+
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
with:
30+
fetch-depth: 0
31+
32+
- name: Setup Node.js
33+
uses: actions/setup-node@v4
34+
with:
35+
node-version: 20
36+
registry-url: https://registry.npmjs.org
37+
38+
- name: Configure Git
39+
run: |
40+
git config user.name "github-actions[bot]"
41+
git config user.email "github-actions[bot]@users.noreply.github.com"
42+
43+
- name: Install dependencies
44+
run: npm ci
45+
46+
- name: Run tests
47+
run: npm test
48+
49+
- name: Build
50+
run: npm run build
51+
52+
- name: Determine version bump
53+
id: bump
54+
run: |
55+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
56+
echo "type=${{ inputs.release_type }}" >> "$GITHUB_OUTPUT"
57+
exit 0
58+
fi
59+
60+
# Get all commit messages since the last tag
61+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
62+
if [ -z "$LAST_TAG" ]; then
63+
COMMITS=$(git log --pretty=format:"%s" --no-merges)
64+
else
65+
COMMITS=$(git log "${LAST_TAG}..HEAD" --pretty=format:"%s" --no-merges)
66+
fi
67+
68+
echo "Commits since last release:"
69+
echo "$COMMITS"
70+
71+
# Determine bump type from commit messages
72+
# Conventional Commits: feat: = minor, fix: = patch, BREAKING CHANGE = major
73+
BUMP="none"
74+
75+
if echo "$COMMITS" | grep -qiE "BREAKING CHANGE|^[a-z]+(\(.+\))?!:"; then
76+
BUMP="major"
77+
elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
78+
BUMP="minor"
79+
elif echo "$COMMITS" | grep -qE "^fix(\(.+\))?:"; then
80+
BUMP="patch"
81+
fi
82+
83+
echo "type=$BUMP" >> "$GITHUB_OUTPUT"
84+
85+
- name: Bump version
86+
id: version
87+
if: steps.bump.outputs.type != 'none'
88+
run: |
89+
npm version ${{ steps.bump.outputs.type }} --no-git-tag-version
90+
VERSION=$(node -p "require('./package.json').version")
91+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
92+
93+
- name: Commit and tag
94+
if: steps.bump.outputs.type != 'none'
95+
run: |
96+
git add package.json package-lock.json
97+
git commit -m "release: v${{ steps.version.outputs.version }}"
98+
git tag "v${{ steps.version.outputs.version }}"
99+
git push origin main --tags
100+
101+
- name: Publish to npm
102+
if: steps.bump.outputs.type != 'none'
103+
run: npm publish
104+
env:
105+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
106+
107+
- name: Create GitHub Release
108+
if: steps.bump.outputs.type != 'none'
109+
uses: softprops/action-gh-release@v2
110+
with:
111+
tag_name: v${{ steps.version.outputs.version }}
112+
name: v${{ steps.version.outputs.version }}
113+
generate_release_notes: true
114+
115+
- name: Skip release
116+
if: steps.bump.outputs.type == 'none'
117+
run: echo "No release-triggering commits found (feat:/fix:/BREAKING CHANGE). Skipping release."

README.md

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,48 @@
1+
<div align="center">
2+
13
# react-driftkit
24

3-
A lightweight, draggable floating widget wrapper for React — snap to corners or drag anywhere.
5+
**A lightweight, draggable floating widget wrapper for React.**
6+
Snap to corners, drag anywhere, stay in bounds — all under 3KB.
47

58
[![npm version](https://img.shields.io/npm/v/react-driftkit)](https://www.npmjs.com/package/react-driftkit)
9+
[![npm downloads](https://img.shields.io/npm/dm/react-driftkit)](https://www.npmjs.com/package/react-driftkit)
10+
[![bundle size](https://img.shields.io/bundlephobia/minzip/react-driftkit)](https://bundlephobia.com/package/react-driftkit)
611
[![license](https://img.shields.io/npm/l/react-driftkit)](./LICENSE)
712

13+
[Live Demo](https://react-driftkit.saktichourasia.dev/) · [NPM](https://www.npmjs.com/package/react-driftkit) · [GitHub](https://github.com/shakcho/react-drift)
14+
15+
</div>
16+
17+
---
18+
19+
## Why react-driftkit?
20+
21+
Building a chat widget, floating toolbar, or debug panel? You need it to be draggable, stay on screen, and not fight your existing styles. Most draggable libraries are either too heavy, too opinionated, or don't handle edge cases like viewport resizing and touch input.
22+
23+
**react-driftkit** solves exactly this — one component, zero config, works everywhere.
24+
825
## Features
926

10-
- **Drag anywhere** — smooth pointer-based dragging (mouse, touch, pen)
11-
- **Snap to corners** — optional snapping to the nearest viewport corner on release
12-
- **Flexible positioning** — named corners or custom `{ x, y }` coordinates
13-
- **Viewport-aware** — auto-repositions on window resize, stays within bounds
14-
- **Zero styling opinions** — fully customizable via `className` and `style` props
15-
- **Tiny footprint** — under 3KB gzipped, zero dependencies beyond React
27+
- **Drag anywhere** — smooth pointer-based dragging with mouse, touch, and pen support
28+
- **Snap to corners** — optional bounce-animated snapping to the nearest viewport corner
29+
- **Smart positioning** — named corners (`top-left`, `bottom-right`, ...) or custom `{ x, y }` coordinates
30+
- **Viewport-aware** — auto-repositions on window resize and content size changes
31+
- **5px drag threshold** — distinguishes clicks from drags, so child buttons still work
32+
- **Zero dependencies** — only React as a peer dependency
33+
- **Tiny bundle** — under 3KB gzipped
34+
- **TypeScript-first** — fully typed props and exports
35+
- **Works with React 18 and 19**
1636

1737
## Installation
1838

1939
```bash
2040
npm install react-driftkit
2141
```
2242

43+
<details>
44+
<summary>yarn / pnpm / bun</summary>
45+
2346
```bash
2447
yarn add react-driftkit
2548
```
@@ -28,6 +51,12 @@ yarn add react-driftkit
2851
pnpm add react-driftkit
2952
```
3053

54+
```bash
55+
bun add react-driftkit
56+
```
57+
58+
</details>
59+
3160
## Quick Start
3261

3362
```tsx
@@ -36,37 +65,66 @@ import { MovableLauncher } from 'react-driftkit';
3665
function App() {
3766
return (
3867
<MovableLauncher defaultPosition="bottom-right">
39-
<button>💬 Chat</button>
68+
<button>Chat with us</button>
4069
</MovableLauncher>
4170
);
4271
}
4372
```
4473

74+
That's it. Your button is now a draggable floating widget pinned to the bottom-right corner.
75+
76+
## Examples
77+
4578
### Snap to Corners
4679

80+
Release the widget and it bounces to the nearest corner:
81+
4782
```tsx
4883
<MovableLauncher defaultPosition="bottom-right" snapToCorners>
4984
<div className="my-widget">Drag me!</div>
5085
</MovableLauncher>
5186
```
5287

53-
### Custom Position
88+
### Free Positioning
89+
90+
Place the widget at exact coordinates:
5491

5592
```tsx
5693
<MovableLauncher defaultPosition={{ x: 100, y: 200 }}>
5794
<div className="toolbar">Toolbar</div>
5895
</MovableLauncher>
5996
```
6097

61-
## API
98+
### Styled Widget
99+
100+
Combine with your own styles and classes:
101+
102+
```tsx
103+
<MovableLauncher
104+
defaultPosition="top-right"
105+
snapToCorners
106+
className="my-launcher"
107+
style={{ borderRadius: 12, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
108+
>
109+
<div className="floating-panel">
110+
<h3>Quick Actions</h3>
111+
<button>New Task</button>
112+
<button>Settings</button>
113+
</div>
114+
</MovableLauncher>
115+
```
116+
117+
## API Reference
118+
119+
### `<MovableLauncher>`
62120

63121
| Prop | Type | Default | Description |
64122
|------|------|---------|-------------|
65-
| `children` | `ReactNode` | | Content to render inside the draggable wrapper |
66-
| `defaultPosition` | `Corner \| { x: number, y: number }` | `'bottom-right'` | Initial position of the widget |
67-
| `snapToCorners` | `boolean` | `false` | Snap to the nearest corner when released |
68-
| `style` | `CSSProperties` | `{}` | Inline styles for the wrapper |
69-
| `className` | `string` | `''` | CSS class for the wrapper |
123+
| `children` | `ReactNode` | *required* | Content to render inside the draggable wrapper |
124+
| `defaultPosition` | `Corner \| { x, y }` | `'bottom-right'` | Initial position — a named corner or pixel coordinates |
125+
| `snapToCorners` | `boolean` | `false` | Snap to the nearest viewport corner on release |
126+
| `style` | `CSSProperties` | `{}` | Inline styles merged with the wrapper |
127+
| `className` | `string` | `''` | CSS class added to the wrapper |
70128

71129
### Types
72130

@@ -79,10 +137,40 @@ interface Position {
79137
}
80138
```
81139

82-
## Requirements
140+
### CSS Classes
141+
142+
The wrapper element exposes these classes for styling:
143+
144+
| Class | When |
145+
|-------|------|
146+
| `movable-launcher` | Always present |
147+
| `movable-launcher--dragging` | While the user is actively dragging |
83148

84-
- React 18+ or 19+
149+
## Use Cases
150+
151+
- **Chat widgets** — floating support/chat buttons that stay accessible
152+
- **Floating toolbars** — draggable formatting bars or quick-action panels
153+
- **Debug panels** — dev tools overlays that can be moved out of the way
154+
- **Media controls** — picture-in-picture style video or audio controls
155+
- **Notification centers** — persistent notification panels users can reposition
156+
- **Accessibility helpers** — movable assistive overlays
157+
158+
## How It Works
159+
160+
Under the hood, react-driftkit uses the [Pointer Events API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) for universal input handling and a `ResizeObserver` to keep the widget positioned correctly when its content changes size. The widget renders as a `position: fixed` div at the highest possible z-index (`2147483647`), so it floats above everything without interfering with your layout.
161+
162+
## Contributing
163+
164+
Contributions are welcome! Feel free to open an issue or submit a pull request.
165+
166+
```bash
167+
git clone https://github.com/shakcho/react-drift.git
168+
cd react-drift
169+
npm install
170+
npm run dev # Start the demo app
171+
npm test # Run the test suite
172+
```
85173

86174
## License
87175

88-
MIT © Sakti Kumar Chourasia
176+
MIT © [Sakti Kumar Chourasia](https://github.com/shakcho)

0 commit comments

Comments
 (0)