@stroke-stabilizer/react 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # @stroke-stabilizer/react
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@stroke-stabilizer/react.svg)](https://www.npmjs.com/package/@stroke-stabilizer/react)
4
+
5
+ [日本語](./docs/README.ja.md)
6
+
7
+ > This is part of the [stroke-stabilizer](https://github.com/usapopopooon/stroke-stabilizer) monorepo
8
+
9
+ React hooks for stroke stabilization in digital drawing applications.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @stroke-stabilizer/react @stroke-stabilizer/core
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ### useStabilizedPointer
20
+
21
+ ```tsx
22
+ import { useStabilizedPointer } from '@stroke-stabilizer/react'
23
+ import { oneEuroFilter } from '@stroke-stabilizer/core'
24
+
25
+ function DrawingCanvas() {
26
+ const { process, reset, pointer } = useStabilizedPointer({
27
+ level: 50,
28
+ onPoint: (point) => {
29
+ draw(point.x, point.y)
30
+ },
31
+ })
32
+
33
+ const handlePointerMove = (e: React.PointerEvent) => {
34
+ process({
35
+ x: e.clientX,
36
+ y: e.clientY,
37
+ pressure: e.pressure,
38
+ timestamp: e.timeStamp,
39
+ })
40
+ }
41
+
42
+ const handlePointerUp = () => {
43
+ reset()
44
+ }
45
+
46
+ return (
47
+ <canvas onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} />
48
+ )
49
+ }
50
+ ```
51
+
52
+ ### With rAF Batch Processing
53
+
54
+ For high-frequency input devices, use the underlying `StabilizedPointer`'s batch processing:
55
+
56
+ ```tsx
57
+ import { useStabilizedPointer } from '@stroke-stabilizer/react'
58
+ import { useEffect } from 'react'
59
+
60
+ function DrawingCanvas() {
61
+ const { pointer } = useStabilizedPointer({ level: 50 })
62
+
63
+ useEffect(() => {
64
+ pointer.enableBatching({
65
+ onBatch: (points) => drawPoints(points),
66
+ })
67
+ return () => pointer.disableBatching()
68
+ }, [pointer])
69
+
70
+ const handlePointerMove = (e: React.PointerEvent) => {
71
+ pointer.queue({
72
+ x: e.clientX,
73
+ y: e.clientY,
74
+ pressure: e.pressure,
75
+ timestamp: e.timeStamp,
76
+ })
77
+ }
78
+
79
+ return <canvas onPointerMove={handlePointerMove} />
80
+ }
81
+ ```
82
+
83
+ ### useStabilizationLevel
84
+
85
+ A hook for managing stabilization level state.
86
+
87
+ ```tsx
88
+ import { useStabilizationLevel } from '@stroke-stabilizer/react'
89
+
90
+ function StabilizationSlider() {
91
+ const { level, setLevel, isEnabled } = useStabilizationLevel({
92
+ initialLevel: 50,
93
+ })
94
+
95
+ return (
96
+ <div>
97
+ <input
98
+ type="range"
99
+ min={0}
100
+ max={100}
101
+ value={level}
102
+ onChange={(e) => setLevel(Number(e.target.value))}
103
+ />
104
+ <span>{level}%</span>
105
+ {isEnabled && <span>Stabilization enabled</span>}
106
+ </div>
107
+ )
108
+ }
109
+ ```
110
+
111
+ ## API
112
+
113
+ ### useStabilizedPointer(options?)
114
+
115
+ Creates a stabilized pointer instance.
116
+
117
+ **Options:**
118
+
119
+ - `level` - Stabilization level (0-100). Uses preset when specified
120
+ - `filters` - Custom filter array. Used when level is not specified
121
+ - `onPoint` - Callback when a point is processed
122
+
123
+ **Returns:**
124
+
125
+ - `process(point)` - Process a single point
126
+ - `processAll(points)` - Process multiple points
127
+ - `flushBuffer()` - Flush internal buffer
128
+ - `reset()` - Reset the pointer state
129
+ - `addFilter(filter)` - Add a filter dynamically
130
+ - `removeFilter(type)` - Remove a filter by type
131
+ - `updateFilter(type, params)` - Update filter parameters
132
+ - `pointer` - Reference to the StabilizedPointer instance
133
+
134
+ ### useStabilizationLevel(options?)
135
+
136
+ Manages stabilization level state.
137
+
138
+ **Options:**
139
+
140
+ - `initialLevel` - Initial level (default: 0)
141
+ - `min` - Minimum level (default: 0)
142
+ - `max` - Maximum level (default: 100)
143
+ - `onChange` - Callback when level changes
144
+
145
+ **Returns:**
146
+
147
+ - `level` - Current level
148
+ - `setLevel(value)` - Set the level
149
+ - `increase(amount?)` - Increase level by amount (default: 10)
150
+ - `decrease(amount?)` - Decrease level by amount (default: 10)
151
+ - `isEnabled` - Whether stabilization is active (level > 0)
152
+
153
+ ## License
154
+
155
+ [MIT](../../LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const core = require("@stroke-stabilizer/core");
5
+ function useStabilizedPointer(options = {}) {
6
+ const { level, filters, onPoint } = options;
7
+ const pointerRef = react.useRef(null);
8
+ const getPointer = react.useCallback(() => {
9
+ if (!pointerRef.current) {
10
+ if (level !== void 0) {
11
+ pointerRef.current = core.createStabilizedPointer(level);
12
+ } else {
13
+ pointerRef.current = new core.StabilizedPointer();
14
+ if (filters) {
15
+ for (const filter of filters) {
16
+ pointerRef.current.addFilter(filter);
17
+ }
18
+ }
19
+ }
20
+ }
21
+ return pointerRef.current;
22
+ }, [level, filters]);
23
+ const process = react.useCallback(
24
+ (point) => {
25
+ const result = getPointer().process(point);
26
+ if (result && onPoint) {
27
+ onPoint(result);
28
+ }
29
+ return result;
30
+ },
31
+ [getPointer, onPoint]
32
+ );
33
+ const processAll = react.useCallback(
34
+ (points) => {
35
+ const results = getPointer().processAll(points);
36
+ if (onPoint) {
37
+ for (const point of results) {
38
+ onPoint(point);
39
+ }
40
+ }
41
+ return results;
42
+ },
43
+ [getPointer, onPoint]
44
+ );
45
+ const flushBuffer = react.useCallback(() => {
46
+ return getPointer().flushBuffer();
47
+ }, [getPointer]);
48
+ const reset = react.useCallback(() => {
49
+ getPointer().reset();
50
+ }, [getPointer]);
51
+ const addFilter = react.useCallback(
52
+ (filter) => {
53
+ getPointer().addFilter(filter);
54
+ },
55
+ [getPointer]
56
+ );
57
+ const removeFilter = react.useCallback(
58
+ (type) => {
59
+ return getPointer().removeFilter(type);
60
+ },
61
+ [getPointer]
62
+ );
63
+ const updateFilter = react.useCallback(
64
+ (type, params) => {
65
+ return getPointer().updateFilter(type, params);
66
+ },
67
+ [getPointer]
68
+ );
69
+ return react.useMemo(
70
+ () => ({
71
+ process,
72
+ processAll,
73
+ flushBuffer,
74
+ reset,
75
+ addFilter,
76
+ removeFilter,
77
+ updateFilter,
78
+ pointer: getPointer()
79
+ }),
80
+ [
81
+ process,
82
+ processAll,
83
+ flushBuffer,
84
+ reset,
85
+ addFilter,
86
+ removeFilter,
87
+ updateFilter,
88
+ getPointer
89
+ ]
90
+ );
91
+ }
92
+ function useStabilizationLevel(options = {}) {
93
+ const { initialLevel = 0, min = 0, max = 100, onChange } = options;
94
+ const [level, setLevelState] = react.useState(
95
+ () => Math.max(min, Math.min(max, initialLevel))
96
+ );
97
+ const setLevel = react.useCallback(
98
+ (newLevel) => {
99
+ const clamped = Math.max(min, Math.min(max, newLevel));
100
+ setLevelState(clamped);
101
+ onChange == null ? void 0 : onChange(clamped);
102
+ },
103
+ [min, max, onChange]
104
+ );
105
+ const increase = react.useCallback(
106
+ (amount = 10) => {
107
+ setLevel(level + amount);
108
+ },
109
+ [level, setLevel]
110
+ );
111
+ const decrease = react.useCallback(
112
+ (amount = 10) => {
113
+ setLevel(level - amount);
114
+ },
115
+ [level, setLevel]
116
+ );
117
+ return {
118
+ level,
119
+ setLevel,
120
+ increase,
121
+ decrease,
122
+ isEnabled: level > 0
123
+ };
124
+ }
125
+ exports.useStabilizationLevel = useStabilizationLevel;
126
+ exports.useStabilizedPointer = useStabilizedPointer;
127
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/useStabilizedPointer.ts","../src/useStabilizationLevel.ts"],"sourcesContent":["import { useCallback, useRef, useMemo } from 'react'\nimport {\n StabilizedPointer,\n createStabilizedPointer,\n type PointerPoint,\n type Filter,\n} from '@stroke-stabilizer/core'\n\nexport interface UseStabilizedPointerOptions {\n /** Stabilization level (0-100). Uses preset when specified */\n level?: number\n /** Custom filters. Used when level is not specified */\n filters?: Filter[]\n /** Callback when a point is processed */\n onPoint?: (point: PointerPoint) => void\n}\n\nexport interface UseStabilizedPointerReturn {\n /** Process a point */\n process: (point: PointerPoint) => PointerPoint | null\n /** Process multiple points at once */\n processAll: (points: PointerPoint[]) => PointerPoint[]\n /** Flush buffer */\n flushBuffer: () => PointerPoint[]\n /** Reset */\n reset: () => void\n /** Add a filter */\n addFilter: (filter: Filter) => void\n /** Remove a filter */\n removeFilter: (type: string) => boolean\n /** Update filter parameters */\n updateFilter: <T>(type: string, params: Partial<T>) => boolean\n /** Reference to StabilizedPointer instance */\n pointer: StabilizedPointer\n}\n\n/**\n * React Hook for stroke stabilization\n *\n * @example\n * ```tsx\n * function DrawingCanvas() {\n * const { process, reset } = useStabilizedPointer({\n * level: 50,\n * onPoint: (point) => {\n * // Draw with stabilized point\n * drawPoint(point)\n * }\n * })\n *\n * const handlePointerMove = (e: React.PointerEvent) => {\n * process({\n * x: e.clientX,\n * y: e.clientY,\n * pressure: e.pressure,\n * timestamp: e.timeStamp\n * })\n * }\n *\n * const handlePointerUp = () => {\n * reset()\n * }\n *\n * return <canvas onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} />\n * }\n * ```\n */\nexport function useStabilizedPointer(\n options: UseStabilizedPointerOptions = {}\n): UseStabilizedPointerReturn {\n const { level, filters, onPoint } = options\n\n const pointerRef = useRef<StabilizedPointer | null>(null)\n\n // Lazy initialization\n const getPointer = useCallback(() => {\n if (!pointerRef.current) {\n if (level !== undefined) {\n pointerRef.current = createStabilizedPointer(level)\n } else {\n pointerRef.current = new StabilizedPointer()\n if (filters) {\n for (const filter of filters) {\n pointerRef.current.addFilter(filter)\n }\n }\n }\n }\n return pointerRef.current\n }, [level, filters])\n\n const process = useCallback(\n (point: PointerPoint): PointerPoint | null => {\n const result = getPointer().process(point)\n if (result && onPoint) {\n onPoint(result)\n }\n return result\n },\n [getPointer, onPoint]\n )\n\n const processAll = useCallback(\n (points: PointerPoint[]): PointerPoint[] => {\n const results = getPointer().processAll(points)\n if (onPoint) {\n for (const point of results) {\n onPoint(point)\n }\n }\n return results\n },\n [getPointer, onPoint]\n )\n\n const flushBuffer = useCallback(() => {\n return getPointer().flushBuffer()\n }, [getPointer])\n\n const reset = useCallback(() => {\n getPointer().reset()\n }, [getPointer])\n\n const addFilter = useCallback(\n (filter: Filter) => {\n getPointer().addFilter(filter)\n },\n [getPointer]\n )\n\n const removeFilter = useCallback(\n (type: string) => {\n return getPointer().removeFilter(type)\n },\n [getPointer]\n )\n\n const updateFilter = useCallback(\n <T>(type: string, params: Partial<T>) => {\n return getPointer().updateFilter(type, params)\n },\n [getPointer]\n )\n\n return useMemo(\n () => ({\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n pointer: getPointer(),\n }),\n [\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n getPointer,\n ]\n )\n}\n","import { useState, useCallback } from 'react'\n\nexport interface UseStabilizationLevelOptions {\n /** Initial value (default: 0) */\n initialLevel?: number\n /** Minimum value (default: 0) */\n min?: number\n /** Maximum value (default: 100) */\n max?: number\n /** Callback when level changes */\n onChange?: (level: number) => void\n}\n\nexport interface UseStabilizationLevelReturn {\n /** Current stabilization level */\n level: number\n /** Set level */\n setLevel: (level: number) => void\n /** Increase level */\n increase: (amount?: number) => void\n /** Decrease level */\n decrease: (amount?: number) => void\n /** Whether stabilization is enabled */\n isEnabled: boolean\n}\n\n/**\n * React Hook for managing stabilization level\n *\n * @example\n * ```tsx\n * function StabilizationControl() {\n * const { level, setLevel, isEnabled } = useStabilizationLevel({\n * initialLevel: 50,\n * onChange: (newLevel) => console.log('Level changed:', newLevel)\n * })\n *\n * return (\n * <div>\n * <input\n * type=\"range\"\n * min={0}\n * max={100}\n * value={level}\n * onChange={(e) => setLevel(Number(e.target.value))}\n * />\n * <span>{level}%</span>\n * {isEnabled && <span>Stabilization enabled</span>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useStabilizationLevel(\n options: UseStabilizationLevelOptions = {}\n): UseStabilizationLevelReturn {\n const { initialLevel = 0, min = 0, max = 100, onChange } = options\n\n const [level, setLevelState] = useState(() =>\n Math.max(min, Math.min(max, initialLevel))\n )\n\n const setLevel = useCallback(\n (newLevel: number) => {\n const clamped = Math.max(min, Math.min(max, newLevel))\n setLevelState(clamped)\n onChange?.(clamped)\n },\n [min, max, onChange]\n )\n\n const increase = useCallback(\n (amount = 10) => {\n setLevel(level + amount)\n },\n [level, setLevel]\n )\n\n const decrease = useCallback(\n (amount = 10) => {\n setLevel(level - amount)\n },\n [level, setLevel]\n )\n\n return {\n level,\n setLevel,\n increase,\n decrease,\n isEnabled: level > 0,\n }\n}\n"],"names":["useRef","useCallback","createStabilizedPointer","StabilizedPointer","useMemo","useState"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { useStabilizedPointer } from './useStabilizedPointer';
2
+ export type { UseStabilizedPointerOptions, UseStabilizedPointerReturn, } from './useStabilizedPointer';
3
+ export { useStabilizationLevel } from './useStabilizationLevel';
4
+ export type { UseStabilizationLevelOptions, UseStabilizationLevelReturn, } from './useStabilizationLevel';
5
+ export type { Point, PointerPoint, Filter } from '@stroke-stabilizer/core';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,yBAAyB,CAAA;AAGhC,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ import { useRef, useCallback, useMemo, useState } from "react";
2
+ import { createStabilizedPointer, StabilizedPointer } from "@stroke-stabilizer/core";
3
+ function useStabilizedPointer(options = {}) {
4
+ const { level, filters, onPoint } = options;
5
+ const pointerRef = useRef(null);
6
+ const getPointer = useCallback(() => {
7
+ if (!pointerRef.current) {
8
+ if (level !== void 0) {
9
+ pointerRef.current = createStabilizedPointer(level);
10
+ } else {
11
+ pointerRef.current = new StabilizedPointer();
12
+ if (filters) {
13
+ for (const filter of filters) {
14
+ pointerRef.current.addFilter(filter);
15
+ }
16
+ }
17
+ }
18
+ }
19
+ return pointerRef.current;
20
+ }, [level, filters]);
21
+ const process = useCallback(
22
+ (point) => {
23
+ const result = getPointer().process(point);
24
+ if (result && onPoint) {
25
+ onPoint(result);
26
+ }
27
+ return result;
28
+ },
29
+ [getPointer, onPoint]
30
+ );
31
+ const processAll = useCallback(
32
+ (points) => {
33
+ const results = getPointer().processAll(points);
34
+ if (onPoint) {
35
+ for (const point of results) {
36
+ onPoint(point);
37
+ }
38
+ }
39
+ return results;
40
+ },
41
+ [getPointer, onPoint]
42
+ );
43
+ const flushBuffer = useCallback(() => {
44
+ return getPointer().flushBuffer();
45
+ }, [getPointer]);
46
+ const reset = useCallback(() => {
47
+ getPointer().reset();
48
+ }, [getPointer]);
49
+ const addFilter = useCallback(
50
+ (filter) => {
51
+ getPointer().addFilter(filter);
52
+ },
53
+ [getPointer]
54
+ );
55
+ const removeFilter = useCallback(
56
+ (type) => {
57
+ return getPointer().removeFilter(type);
58
+ },
59
+ [getPointer]
60
+ );
61
+ const updateFilter = useCallback(
62
+ (type, params) => {
63
+ return getPointer().updateFilter(type, params);
64
+ },
65
+ [getPointer]
66
+ );
67
+ return useMemo(
68
+ () => ({
69
+ process,
70
+ processAll,
71
+ flushBuffer,
72
+ reset,
73
+ addFilter,
74
+ removeFilter,
75
+ updateFilter,
76
+ pointer: getPointer()
77
+ }),
78
+ [
79
+ process,
80
+ processAll,
81
+ flushBuffer,
82
+ reset,
83
+ addFilter,
84
+ removeFilter,
85
+ updateFilter,
86
+ getPointer
87
+ ]
88
+ );
89
+ }
90
+ function useStabilizationLevel(options = {}) {
91
+ const { initialLevel = 0, min = 0, max = 100, onChange } = options;
92
+ const [level, setLevelState] = useState(
93
+ () => Math.max(min, Math.min(max, initialLevel))
94
+ );
95
+ const setLevel = useCallback(
96
+ (newLevel) => {
97
+ const clamped = Math.max(min, Math.min(max, newLevel));
98
+ setLevelState(clamped);
99
+ onChange == null ? void 0 : onChange(clamped);
100
+ },
101
+ [min, max, onChange]
102
+ );
103
+ const increase = useCallback(
104
+ (amount = 10) => {
105
+ setLevel(level + amount);
106
+ },
107
+ [level, setLevel]
108
+ );
109
+ const decrease = useCallback(
110
+ (amount = 10) => {
111
+ setLevel(level - amount);
112
+ },
113
+ [level, setLevel]
114
+ );
115
+ return {
116
+ level,
117
+ setLevel,
118
+ increase,
119
+ decrease,
120
+ isEnabled: level > 0
121
+ };
122
+ }
123
+ export {
124
+ useStabilizationLevel,
125
+ useStabilizedPointer
126
+ };
127
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/useStabilizedPointer.ts","../src/useStabilizationLevel.ts"],"sourcesContent":["import { useCallback, useRef, useMemo } from 'react'\nimport {\n StabilizedPointer,\n createStabilizedPointer,\n type PointerPoint,\n type Filter,\n} from '@stroke-stabilizer/core'\n\nexport interface UseStabilizedPointerOptions {\n /** Stabilization level (0-100). Uses preset when specified */\n level?: number\n /** Custom filters. Used when level is not specified */\n filters?: Filter[]\n /** Callback when a point is processed */\n onPoint?: (point: PointerPoint) => void\n}\n\nexport interface UseStabilizedPointerReturn {\n /** Process a point */\n process: (point: PointerPoint) => PointerPoint | null\n /** Process multiple points at once */\n processAll: (points: PointerPoint[]) => PointerPoint[]\n /** Flush buffer */\n flushBuffer: () => PointerPoint[]\n /** Reset */\n reset: () => void\n /** Add a filter */\n addFilter: (filter: Filter) => void\n /** Remove a filter */\n removeFilter: (type: string) => boolean\n /** Update filter parameters */\n updateFilter: <T>(type: string, params: Partial<T>) => boolean\n /** Reference to StabilizedPointer instance */\n pointer: StabilizedPointer\n}\n\n/**\n * React Hook for stroke stabilization\n *\n * @example\n * ```tsx\n * function DrawingCanvas() {\n * const { process, reset } = useStabilizedPointer({\n * level: 50,\n * onPoint: (point) => {\n * // Draw with stabilized point\n * drawPoint(point)\n * }\n * })\n *\n * const handlePointerMove = (e: React.PointerEvent) => {\n * process({\n * x: e.clientX,\n * y: e.clientY,\n * pressure: e.pressure,\n * timestamp: e.timeStamp\n * })\n * }\n *\n * const handlePointerUp = () => {\n * reset()\n * }\n *\n * return <canvas onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} />\n * }\n * ```\n */\nexport function useStabilizedPointer(\n options: UseStabilizedPointerOptions = {}\n): UseStabilizedPointerReturn {\n const { level, filters, onPoint } = options\n\n const pointerRef = useRef<StabilizedPointer | null>(null)\n\n // Lazy initialization\n const getPointer = useCallback(() => {\n if (!pointerRef.current) {\n if (level !== undefined) {\n pointerRef.current = createStabilizedPointer(level)\n } else {\n pointerRef.current = new StabilizedPointer()\n if (filters) {\n for (const filter of filters) {\n pointerRef.current.addFilter(filter)\n }\n }\n }\n }\n return pointerRef.current\n }, [level, filters])\n\n const process = useCallback(\n (point: PointerPoint): PointerPoint | null => {\n const result = getPointer().process(point)\n if (result && onPoint) {\n onPoint(result)\n }\n return result\n },\n [getPointer, onPoint]\n )\n\n const processAll = useCallback(\n (points: PointerPoint[]): PointerPoint[] => {\n const results = getPointer().processAll(points)\n if (onPoint) {\n for (const point of results) {\n onPoint(point)\n }\n }\n return results\n },\n [getPointer, onPoint]\n )\n\n const flushBuffer = useCallback(() => {\n return getPointer().flushBuffer()\n }, [getPointer])\n\n const reset = useCallback(() => {\n getPointer().reset()\n }, [getPointer])\n\n const addFilter = useCallback(\n (filter: Filter) => {\n getPointer().addFilter(filter)\n },\n [getPointer]\n )\n\n const removeFilter = useCallback(\n (type: string) => {\n return getPointer().removeFilter(type)\n },\n [getPointer]\n )\n\n const updateFilter = useCallback(\n <T>(type: string, params: Partial<T>) => {\n return getPointer().updateFilter(type, params)\n },\n [getPointer]\n )\n\n return useMemo(\n () => ({\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n pointer: getPointer(),\n }),\n [\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n getPointer,\n ]\n )\n}\n","import { useState, useCallback } from 'react'\n\nexport interface UseStabilizationLevelOptions {\n /** Initial value (default: 0) */\n initialLevel?: number\n /** Minimum value (default: 0) */\n min?: number\n /** Maximum value (default: 100) */\n max?: number\n /** Callback when level changes */\n onChange?: (level: number) => void\n}\n\nexport interface UseStabilizationLevelReturn {\n /** Current stabilization level */\n level: number\n /** Set level */\n setLevel: (level: number) => void\n /** Increase level */\n increase: (amount?: number) => void\n /** Decrease level */\n decrease: (amount?: number) => void\n /** Whether stabilization is enabled */\n isEnabled: boolean\n}\n\n/**\n * React Hook for managing stabilization level\n *\n * @example\n * ```tsx\n * function StabilizationControl() {\n * const { level, setLevel, isEnabled } = useStabilizationLevel({\n * initialLevel: 50,\n * onChange: (newLevel) => console.log('Level changed:', newLevel)\n * })\n *\n * return (\n * <div>\n * <input\n * type=\"range\"\n * min={0}\n * max={100}\n * value={level}\n * onChange={(e) => setLevel(Number(e.target.value))}\n * />\n * <span>{level}%</span>\n * {isEnabled && <span>Stabilization enabled</span>}\n * </div>\n * )\n * }\n * ```\n */\nexport function useStabilizationLevel(\n options: UseStabilizationLevelOptions = {}\n): UseStabilizationLevelReturn {\n const { initialLevel = 0, min = 0, max = 100, onChange } = options\n\n const [level, setLevelState] = useState(() =>\n Math.max(min, Math.min(max, initialLevel))\n )\n\n const setLevel = useCallback(\n (newLevel: number) => {\n const clamped = Math.max(min, Math.min(max, newLevel))\n setLevelState(clamped)\n onChange?.(clamped)\n },\n [min, max, onChange]\n )\n\n const increase = useCallback(\n (amount = 10) => {\n setLevel(level + amount)\n },\n [level, setLevel]\n )\n\n const decrease = useCallback(\n (amount = 10) => {\n setLevel(level - amount)\n },\n [level, setLevel]\n )\n\n return {\n level,\n setLevel,\n increase,\n decrease,\n isEnabled: level > 0,\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ export interface UseStabilizationLevelOptions {
2
+ /** Initial value (default: 0) */
3
+ initialLevel?: number;
4
+ /** Minimum value (default: 0) */
5
+ min?: number;
6
+ /** Maximum value (default: 100) */
7
+ max?: number;
8
+ /** Callback when level changes */
9
+ onChange?: (level: number) => void;
10
+ }
11
+ export interface UseStabilizationLevelReturn {
12
+ /** Current stabilization level */
13
+ level: number;
14
+ /** Set level */
15
+ setLevel: (level: number) => void;
16
+ /** Increase level */
17
+ increase: (amount?: number) => void;
18
+ /** Decrease level */
19
+ decrease: (amount?: number) => void;
20
+ /** Whether stabilization is enabled */
21
+ isEnabled: boolean;
22
+ }
23
+ /**
24
+ * React Hook for managing stabilization level
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * function StabilizationControl() {
29
+ * const { level, setLevel, isEnabled } = useStabilizationLevel({
30
+ * initialLevel: 50,
31
+ * onChange: (newLevel) => console.log('Level changed:', newLevel)
32
+ * })
33
+ *
34
+ * return (
35
+ * <div>
36
+ * <input
37
+ * type="range"
38
+ * min={0}
39
+ * max={100}
40
+ * value={level}
41
+ * onChange={(e) => setLevel(Number(e.target.value))}
42
+ * />
43
+ * <span>{level}%</span>
44
+ * {isEnabled && <span>Stabilization enabled</span>}
45
+ * </div>
46
+ * )
47
+ * }
48
+ * ```
49
+ */
50
+ export declare function useStabilizationLevel(options?: UseStabilizationLevelOptions): UseStabilizationLevelReturn;
51
+ //# sourceMappingURL=useStabilizationLevel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStabilizationLevel.d.ts","sourceRoot":"","sources":["../src/useStabilizationLevel.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,4BAA4B;IAC3C,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED,MAAM,WAAW,2BAA2B;IAC1C,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,gBAAgB;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,qBAAqB;IACrB,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,qBAAqB;IACrB,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,2BAA2B,CAqC7B"}
@@ -0,0 +1,60 @@
1
+ import { StabilizedPointer, type PointerPoint, type Filter } from '@stroke-stabilizer/core';
2
+ export interface UseStabilizedPointerOptions {
3
+ /** Stabilization level (0-100). Uses preset when specified */
4
+ level?: number;
5
+ /** Custom filters. Used when level is not specified */
6
+ filters?: Filter[];
7
+ /** Callback when a point is processed */
8
+ onPoint?: (point: PointerPoint) => void;
9
+ }
10
+ export interface UseStabilizedPointerReturn {
11
+ /** Process a point */
12
+ process: (point: PointerPoint) => PointerPoint | null;
13
+ /** Process multiple points at once */
14
+ processAll: (points: PointerPoint[]) => PointerPoint[];
15
+ /** Flush buffer */
16
+ flushBuffer: () => PointerPoint[];
17
+ /** Reset */
18
+ reset: () => void;
19
+ /** Add a filter */
20
+ addFilter: (filter: Filter) => void;
21
+ /** Remove a filter */
22
+ removeFilter: (type: string) => boolean;
23
+ /** Update filter parameters */
24
+ updateFilter: <T>(type: string, params: Partial<T>) => boolean;
25
+ /** Reference to StabilizedPointer instance */
26
+ pointer: StabilizedPointer;
27
+ }
28
+ /**
29
+ * React Hook for stroke stabilization
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * function DrawingCanvas() {
34
+ * const { process, reset } = useStabilizedPointer({
35
+ * level: 50,
36
+ * onPoint: (point) => {
37
+ * // Draw with stabilized point
38
+ * drawPoint(point)
39
+ * }
40
+ * })
41
+ *
42
+ * const handlePointerMove = (e: React.PointerEvent) => {
43
+ * process({
44
+ * x: e.clientX,
45
+ * y: e.clientY,
46
+ * pressure: e.pressure,
47
+ * timestamp: e.timeStamp
48
+ * })
49
+ * }
50
+ *
51
+ * const handlePointerUp = () => {
52
+ * reset()
53
+ * }
54
+ *
55
+ * return <canvas onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} />
56
+ * }
57
+ * ```
58
+ */
59
+ export declare function useStabilizedPointer(options?: UseStabilizedPointerOptions): UseStabilizedPointerReturn;
60
+ //# sourceMappingURL=useStabilizedPointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStabilizedPointer.d.ts","sourceRoot":"","sources":["../src/useStabilizedPointer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EAEjB,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,0BAA0B;IACzC,sBAAsB;IACtB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,GAAG,IAAI,CAAA;IACrD,sCAAsC;IACtC,UAAU,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,CAAA;IACtD,mBAAmB;IACnB,WAAW,EAAE,MAAM,YAAY,EAAE,CAAA;IACjC,YAAY;IACZ,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,mBAAmB;IACnB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,sBAAsB;IACtB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACvC,+BAA+B;IAC/B,YAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;IAC9D,8CAA8C;IAC9C,OAAO,EAAE,iBAAiB,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,0BAA0B,CAiG5B"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@stroke-stabilizer/react",
3
+ "version": "0.1.3",
4
+ "description": "React hooks for stroke stabilization",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "dev": "vite build --watch",
21
+ "build": "vite build && tsc --emitDeclarationOnly",
22
+ "typecheck": "tsc --noEmit"
23
+ },
24
+ "dependencies": {
25
+ "@stroke-stabilizer/core": "workspace:*"
26
+ },
27
+ "peerDependencies": {
28
+ "react": ">=17.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/react": "^18.2.0",
32
+ "react": "^18.2.0",
33
+ "vite": "^5.4.0",
34
+ "vite-plugin-dts": "^4.0.0"
35
+ },
36
+ "keywords": [
37
+ "react",
38
+ "hook",
39
+ "stroke",
40
+ "stabilization",
41
+ "drawing",
42
+ "canvas"
43
+ ],
44
+ "author": "usapopopooon <usapopopooon@gmail.com>",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/usapopopooon/stroke-stabilizer"
49
+ },
50
+ "homepage": "https://github.com/usapopopooon/stroke-stabilizer",
51
+ "sideEffects": false
52
+ }