Skip to content

Commit ec11db5

Browse files
authored
Merge pull request #13 from shakcho/fix/import-update
fix: updated import method and updated demo website to reflect that
2 parents 05861b2 + ce924d4 commit ec11db5

11 files changed

Lines changed: 188 additions & 23 deletions

File tree

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Building a chat widget, floating toolbar, debug panel, or side dock? You want th
3232
| [`<ResizableSplitPane>`](#resizablesplitter) | An N-pane resizable split layout with draggable handles, min/max constraints, and localStorage-persisted ratios. |
3333
| [`<InspectorBubble>`](#inspectorbubble) | A Chrome-DevTools-style element picker overlay for design QA — hover to see tag, selector, dimensions, font, colors + WCAG contrast, box model, ARIA role, and accessible name. |
3434
| [`<ZoomLens>`](#zoomlens) | A draggable magnifier circle that zooms into whatever it hovers — free-drag over the whole page or scope it to one element (product-image-zoom style). Wheel to zoom, hotkey or Escape to dismiss. |
35+
| [`<FlickDeck>`](#flickdeck) | A stack of cards where each back card peeks from one edge. Click the peek to flick that card to the front, or optionally swipe the front card off to dismiss it. |
3536

3637
## Installation
3738

@@ -56,6 +57,21 @@ bun add react-driftkit
5657

5758
</details>
5859

60+
## Importing
61+
62+
Every component is available two ways — both tree-shake to the same code.
63+
64+
```tsx
65+
// 1. Barrel import — grab multiple components at once
66+
import { MovableLauncher, SnapDock, InspectorBubble } from 'react-driftkit';
67+
68+
// 2. Per-component subpath — named or default, pick the style you prefer
69+
import { SnapDock } from 'react-driftkit/SnapDock';
70+
import SnapDock from 'react-driftkit/SnapDock';
71+
```
72+
73+
Subpath imports are handy when you only need one component and want the import line to be explicit about bundle cost (your bundler will tree-shake the barrel import down to the same output either way, as long as `sideEffects` is respected — which it is here).
74+
5975
## Quick Start
6076

6177
```tsx
@@ -67,6 +83,7 @@ import {
6783
ResizableSplitPane,
6884
InspectorBubble,
6985
ZoomLens,
86+
FlickDeck,
7087
} from 'react-driftkit';
7188

7289
function App() {
@@ -104,6 +121,13 @@ function App() {
104121
{/* Magnifier scoped to one element — hover to zoom. */}
105122
<img ref={productRef} src="/product.jpg" alt="Product" />
106123
<ZoomLens defaultActive target={productRef} defaultZoom={3} />
124+
125+
{/* Stack of cards — click a peeking card to flick it to the front. */}
126+
<FlickDeck defaultFrontId="overview" peek="bottom" peekSize={28}>
127+
<div key="overview">Overview</div>
128+
<div key="details">Details</div>
129+
<div key="stats">Stats</div>
130+
</FlickDeck>
107131
</>
108132
);
109133
}
@@ -125,6 +149,8 @@ A draggable floating wrapper that lets users pick up any widget and drop it anyw
125149
- **Click vs. drag threshold** (5 px) so nested buttons still fire
126150

127151
```tsx
152+
import MovableLauncher from 'react-driftkit/MovableLauncher';
153+
128154
<MovableLauncher defaultPosition="bottom-right" snapToCorners>
129155
<button>Chat with us</button>
130156
</MovableLauncher>
@@ -143,6 +169,8 @@ An edge-pinned dock that slides along any side of the viewport. Drop it anywhere
143169
- **Unstyled**`data-edge` and `data-orientation` attributes let you drive CSS without re-rendering
144170

145171
```tsx
172+
import SnapDock from 'react-driftkit/SnapDock';
173+
146174
<SnapDock defaultEdge="bottom" shadow>
147175
<button>Home</button>
148176
<button>Search</button>
@@ -164,6 +192,8 @@ A pull-up / pull-down sheet pinned to an edge, with snap points like `peek`, `ha
164192
- **Drag handle selector** — confine drag to a nested handle so inner content stays scrollable
165193

166194
```tsx
195+
import DraggableSheet from 'react-driftkit/DraggableSheet';
196+
167197
<DraggableSheet snapPoints={['peek', 'half', 'full']} defaultSnap="half">
168198
<div data-handle className="sheet-handle" />
169199
<div className="sheet-body">Details, filters, cart...</div>
@@ -184,6 +214,8 @@ An N-pane resizable split layout. Drag the handles between panes to redistribute
184214
- **Double-click reset** to `defaultSizes`
185215

186216
```tsx
217+
import ResizableSplitPane from 'react-driftkit/ResizableSplitPane';
218+
187219
<ResizableSplitPane defaultSizes={[0.3, 0.7]} persistKey="app-split">
188220
<Sidebar />
189221
<MainContent />
@@ -204,6 +236,8 @@ A Chrome-DevTools-style element picker overlay for design QA. Turn it on, hover
204236
- **Hotkey toggle**, ignore rules, and self-skipping overlay chrome
205237

206238
```tsx
239+
import InspectorBubble from 'react-driftkit/InspectorBubble';
240+
207241
<InspectorBubble
208242
defaultActive
209243
behavior={{ hotkey: 'cmd+shift+c' }}
@@ -225,6 +259,8 @@ A draggable magnifier circle that zooms into whatever it hovers. Great for desig
225259
- **Ignore rules** — strip elements from the clone via `behavior.ignoreSelector` or `[data-zoom-lens-ignore]`
226260

227261
```tsx
262+
import ZoomLens from 'react-driftkit/ZoomLens';
263+
228264
<ZoomLens
229265
defaultActive
230266
target={imageRef} // omit for a free-drag whole-page lens
@@ -237,6 +273,30 @@ A draggable magnifier circle that zooms into whatever it hovers. Great for desig
237273

238274
---
239275

276+
## FlickDeck
277+
278+
A stack of cards where each back card peeks from one edge — receding into depth for `top`/`bottom` peek, fanning out at an angle for `left`/`right`. Click the peek to flick that card to the front, or optionally swipe the front card off to dismiss it. Great for toggles between views, tip stacks, onboarding cards, and side-by-side comparisons.
279+
280+
- **Four peek edges**`top`, `bottom`, `left`, `right`; each child's React `key` is its card id
281+
- **Depth cues you can tune**`depthScale` shrinks back cards, `fanAngle` rotates them, `depthFade` dims them, or set any to `0` for a flat stack
282+
- **Controlled or uncontrolled** — drive the front card via `frontId` + `on.frontChange`, or just pass `defaultFrontId`
283+
- **Optional swipe-to-dismiss** — drag the front card off in the opposite direction of the peek to fire `on.dismiss(id)`
284+
- **Unstyled**`data-flick-deck-front`, `data-flick-deck-depth`, and `data-flick-deck-active` attributes let you drive CSS without re-rendering
285+
286+
```tsx
287+
import FlickDeck from 'react-driftkit/FlickDeck';
288+
289+
<FlickDeck defaultFrontId="overview" peek="bottom" peekSize={28}>
290+
<Card key="overview">Overview</Card>
291+
<Card key="details">Details</Card>
292+
<Card key="stats">Stats</Card>
293+
</FlickDeck>
294+
```
295+
296+
**Full API, more examples, and live demo →** <https://react-driftkit.saktichourasia.dev/flick-deck>
297+
298+
---
299+
240300
## Use Cases
241301

242302
- **Chat widgets** — floating support buttons that stay accessible
@@ -254,6 +314,8 @@ A draggable magnifier circle that zooms into whatever it hovers. Great for desig
254314
- **In-house devtools** — a built-in element picker for style audits, a11y audits, or click-to-log workflows
255315
- **Product image zoom** — magnifier scoped to a single image, follows the cursor, hides on leave
256316
- **Data table inspection** — drag a magnifier over dense tables, charts, or heatmaps to read small values without re-flowing the page
317+
- **Tip / onboarding stacks** — a deck of coachmark cards the user can flick through and swipe off one by one
318+
- **Comparison decks** — toggle between product plans, chart variants, or before/after states with a single click on the peek
257319

258320
## How it works
259321

@@ -267,6 +329,8 @@ Under the hood all components use the [Pointer Events API](https://developer.moz
267329

268330
`ZoomLens` live-clones either `document.body` (free mode) or a target element (target mode) into a portalled host, then applies a `translate()` + `scale()` transform so the point under the lens center maps to target-local — or document — coords. A `MutationObserver` rebuilds the clone when the real DOM changes, debounced to 150 ms and skipped during drag. In target mode, pointer tracking attaches directly to the target, and the lens overlay is `pointer-events: none` so hover state keeps passing through to the real element underneath.
269331

332+
`FlickDeck` lays every card in a single CSS grid cell so the container auto-sizes to the largest card, then offsets back cards with pure `transform``translate` along the peek axis, plus `scale` (top/bottom peek) or `rotate` around the attached edge (left/right peek). Depth fade uses `opacity`. The front card's `key` is its id; a click or keyboard activation on a peek swaps ids with a CSS transition. Swipe-to-dismiss tracks pointer movement on the front card and fires `on.dismiss(id)` once the drag crosses `dismissThreshold` in the direction opposite the peek, leaving the consumer to remove that child.
333+
270334
## Contributing
271335

272336
Contributions are welcome. Open an issue or send a pull request.

demo/src/components/islands/InstallTabs.tsx

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,68 @@ const installCommands = [
77
{ pm: 'pnpm', cmd: 'pnpm add react-driftkit' },
88
];
99

10+
const importStyles = [
11+
{
12+
key: 'barrel',
13+
label: 'Barrel',
14+
code: `import { SnapDock, InspectorBubble } from 'react-driftkit';`,
15+
},
16+
{
17+
key: 'subpath-named',
18+
label: 'Per-component (named)',
19+
code: `import { SnapDock } from 'react-driftkit/SnapDock';`,
20+
},
21+
{
22+
key: 'subpath-default',
23+
label: 'Per-component (default)',
24+
code: `import SnapDock from 'react-driftkit/SnapDock';`,
25+
},
26+
];
27+
1028
export default function InstallTabs() {
1129
const [activePm, setActivePm] = useState('npm');
30+
const [activeImport, setActiveImport] = useState('barrel');
1231
const active = installCommands.find((c) => c.pm === activePm) ?? installCommands[0];
32+
const activeImp = importStyles.find((i) => i.key === activeImport) ?? importStyles[0];
1333
return (
14-
<div className="install-tabs">
15-
<div className="install-tabs-header">
16-
{installCommands.map(({ pm }) => (
17-
<button
18-
key={pm}
19-
type="button"
20-
className={`install-tab ${activePm === pm ? 'install-tab--active' : ''}`}
21-
onClick={() => setActivePm(pm)}
22-
>
23-
{pm}
24-
</button>
25-
))}
34+
<>
35+
<div className="install-tabs">
36+
<div className="install-tabs-header">
37+
{installCommands.map(({ pm }) => (
38+
<button
39+
key={pm}
40+
type="button"
41+
className={`install-tab ${activePm === pm ? 'install-tab--active' : ''}`}
42+
onClick={() => setActivePm(pm)}
43+
>
44+
{pm}
45+
</button>
46+
))}
47+
</div>
48+
<div className="install-tabs-body">
49+
<code>{active.cmd}</code>
50+
<CopyButton text={active.cmd} />
51+
</div>
2652
</div>
27-
<div className="install-tabs-body">
28-
<code>{active.cmd}</code>
29-
<CopyButton text={active.cmd} />
53+
54+
<div className="install-tabs" style={{ marginTop: 16 }}>
55+
<div className="install-tabs-header">
56+
{importStyles.map(({ key, label }) => (
57+
<button
58+
key={key}
59+
type="button"
60+
className={`install-tab ${activeImport === key ? 'install-tab--active' : ''}`}
61+
onClick={() => setActiveImport(key)}
62+
>
63+
{label}
64+
</button>
65+
))}
66+
</div>
67+
<div className="install-tabs-body">
68+
<code>{activeImp.code}</code>
69+
<CopyButton text={activeImp.code} />
70+
</div>
3071
</div>
31-
</div>
72+
</>
3273
);
3374
}

package.json

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,50 @@
33
"version": "0.7.0",
44
"description": "Small React primitives for floating UI: draggable launchers and edge-pinned docks",
55
"type": "module",
6-
"main": "dist/react-driftkit.cjs",
7-
"module": "dist/react-driftkit.js",
6+
"sideEffects": false,
7+
"main": "dist/index.cjs",
8+
"module": "dist/index.js",
89
"types": "dist/index.d.ts",
910
"exports": {
1011
".": {
1112
"types": "./dist/index.d.ts",
12-
"import": "./dist/react-driftkit.js",
13-
"require": "./dist/react-driftkit.cjs"
13+
"import": "./dist/index.js",
14+
"require": "./dist/index.cjs"
15+
},
16+
"./MovableLauncher": {
17+
"types": "./dist/MovableLauncher.d.ts",
18+
"import": "./dist/MovableLauncher.js",
19+
"require": "./dist/MovableLauncher.cjs"
20+
},
21+
"./SnapDock": {
22+
"types": "./dist/SnapDock.d.ts",
23+
"import": "./dist/SnapDock.js",
24+
"require": "./dist/SnapDock.cjs"
25+
},
26+
"./DraggableSheet": {
27+
"types": "./dist/DraggableSheet.d.ts",
28+
"import": "./dist/DraggableSheet.js",
29+
"require": "./dist/DraggableSheet.cjs"
30+
},
31+
"./ResizableSplitPane": {
32+
"types": "./dist/ResizableSplitPane.d.ts",
33+
"import": "./dist/ResizableSplitPane.js",
34+
"require": "./dist/ResizableSplitPane.cjs"
35+
},
36+
"./InspectorBubble": {
37+
"types": "./dist/InspectorBubble.d.ts",
38+
"import": "./dist/InspectorBubble.js",
39+
"require": "./dist/InspectorBubble.cjs"
40+
},
41+
"./ZoomLens": {
42+
"types": "./dist/ZoomLens.d.ts",
43+
"import": "./dist/ZoomLens.js",
44+
"require": "./dist/ZoomLens.cjs"
45+
},
46+
"./FlickDeck": {
47+
"types": "./dist/FlickDeck.d.ts",
48+
"import": "./dist/FlickDeck.js",
49+
"require": "./dist/FlickDeck.cjs"
1450
}
1551
},
1652
"files": [

src/DraggableSheet.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,5 @@ export function DraggableSheet({
380380
</div>
381381
);
382382
}
383+
384+
export default DraggableSheet;

src/FlickDeck.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,5 @@ export function FlickDeck({
378378
</div>
379379
);
380380
}
381+
382+
export default FlickDeck;

src/InspectorBubble.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,3 +930,5 @@ function BubbleAnchor({
930930
</div>
931931
);
932932
}
933+
934+
export default InspectorBubble;

src/MovableLauncher.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,5 @@ export function MovableLauncher({
266266
</div>
267267
);
268268
}
269+
270+
export default MovableLauncher;

src/ResizableSplitPane.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,5 @@ export function ResizableSplitPane({
392392
</div>
393393
);
394394
}
395+
396+
export default ResizableSplitPane;

src/SnapDock.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,5 @@ export function SnapDock({
408408
</div>
409409
);
410410
}
411+
412+
export default SnapDock;

src/ZoomLens.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,3 +680,5 @@ export function ZoomLens({
680680

681681
return createPortal(overlay, document.body);
682682
}
683+
684+
export default ZoomLens;

0 commit comments

Comments
 (0)