react-robot-vacuum 1.0.2 → 1.0.4

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 CHANGED
@@ -1,20 +1,8 @@
1
1
  # 🤖 React Robot Vacuum
2
2
 
3
- An animated React component featuring an autonomous robot vacuum that cleans your page by collecting dirt particles. Built with TypeScript, React 19, and CSS Modules.
3
+ An animated React component featuring an autonomous robot vacuum that cleans your page by collecting dirt particles. Built with TypeScript, React and CSS
4
4
 
5
- ![Robot Vacuum Demo](https://via.placeholder.com/800x400?text=Robot+Vacuum+Demo)
6
-
7
- ## ✨ Features
8
-
9
- - 🎯 **Autonomous Navigation** - Robot intelligently navigates to dirt particles
10
- - 🔄 **Smooth Animations** - Realistic acceleration, rotation, and movement
11
- - ⏸️ **Window Focus Detection** - Automatically pauses when tab is inactive
12
- - 🎮 **Imperative Control** - Start, stop, and reset via ref
13
- - 📢 **Lifecycle Callbacks** - Track cleaning progress with events
14
- - ⚙️ **Fully Customizable** - Props for size, position, speed, and more
15
- - 💪 **TypeScript** - Full type safety with exported interfaces
16
- - 🎨 **CSS Modules** - Scoped styling, no conflicts
17
- - ⚡ **React 17/18/19** - Compatible with all modern React versions
5
+ ![Robot Vacuum Demo](https://codesandbox.io/p/sandbox/react-robot-vacuum-4xlm7j)
18
6
 
19
7
  ## 📦 Installation
20
8
 
@@ -87,86 +75,9 @@ function App() {
87
75
  - **`startCleaning()`** - Manually start the cleaning process
88
76
  - **`reset()`** - Reset robot to dock and generate new dirt
89
77
 
90
- ### Types
91
-
92
- ```tsx
93
- export interface RobotVacuumRef {
94
- startCleaning: () => void;
95
- reset: () => void;
96
- }
97
-
98
- export interface RobotVacuumProps {
99
- readonly numberOfDirtBits?: number;
100
- readonly minSpeed?: number;
101
- readonly speedFactor?: number;
102
- readonly rotationDuration?: number;
103
- readonly autoStart?: boolean;
104
- readonly onCleaningStart?: () => void;
105
- readonly onCleaningComplete?: () => void;
106
- readonly onDirtCollected?: (collected: number, total: number) => void;
107
- }
108
- ```
109
-
110
- ## 🎨 Examples
111
-
112
- ### Manual Control with Callbacks
113
-
114
- ```tsx
115
- import { useRef, useState } from "react";
116
- import { RobotVacuum, RobotVacuumRef } from "react-robot-vacuum";
117
-
118
- function App() {
119
- const robotRef = useRef<RobotVacuumRef>(null);
120
- const [progress, setProgress] = useState("0/0");
121
- const [status, setStatus] = useState("Ready");
122
-
123
- return (
124
- <div>
125
- <RobotVacuum
126
- ref={robotRef}
127
- autoStart={false}
128
- numberOfDirtBits={10}
129
- onCleaningStart={() => setStatus("Cleaning...")}
130
- onDirtCollected={(collected, total) => {
131
- setProgress(`${collected}/${total}`);
132
- }}
133
- onCleaningComplete={() => setStatus("Complete!")}
134
- />
135
-
136
- <div style={{ position: "fixed", top: 20, right: 20 }}>
137
- <p>Status: {status}</p>
138
- <p>Progress: {progress}</p>
139
- <button onClick={() => robotRef.current?.startCleaning()}>
140
- Start
141
- </button>
142
- <button onClick={() => robotRef.current?.reset()}>
143
- Reset
144
- </button>
145
- </div>
146
- </div>
147
- );
148
- }
149
- ```
150
-
151
- ### Slow and Methodical Cleaning
152
-
153
- ```tsx
154
- import { RobotVacuum } from "react-robot-vacuum";
155
-
156
- function App() {
157
- return (
158
- <RobotVacuum
159
- minSpeed={1.5}
160
- speedFactor={50}
161
- rotationDuration={1.2}
162
- />
163
- );
164
- }
165
- ```
166
-
167
78
  ## 🎮 Live Demo
168
79
 
169
- Try it on [CodeSandbox](https://codesandbox.io/s/react-robot-vacuum-demo)
80
+ Try it on [CodeSandbox](https://codesandbox.io/p/sandbox/react-robot-vacuum-4xlm7j)
170
81
 
171
82
  ## 🛠️ Development
172
83
 
@@ -186,7 +97,7 @@ npm run lint
186
97
 
187
98
  ## 📄 License
188
99
 
189
- MIT © [Your Name]
100
+ MIT © ZhanmuTW
190
101
 
191
102
  ## 🤝 Contributing
192
103
 
package/dist/index.js CHANGED
@@ -21,17 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.tsx
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
- RobotVacuum: () => RobotVacuum_default2
24
+ RobotVacuum: () => RobotVacuum_default
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
28
  // src/RobotVacuum/RobotVacuum.tsx
29
29
  var import_react = require("react");
30
-
31
- // src/RobotVacuum/RobotVacuum.module.css
32
- var RobotVacuum_default = {};
33
-
34
- // src/RobotVacuum/RobotVacuum.tsx
35
30
  var import_jsx_runtime = require("react/jsx-runtime");
36
31
  var Robotv2 = (0, import_react.forwardRef)(
37
32
  ({
@@ -248,50 +243,186 @@ var Robotv2 = (0, import_react.forwardRef)(
248
243
  cleanAndReturn();
249
244
  }
250
245
  }, [dirtPositions, robotState, isDocumentVisible, autoStart]);
251
- (0, import_react.useEffect)(() => {
252
- const robotElement = robotRef.current;
253
- if (!robotElement) return;
254
- robotElement.classList.remove(
255
- RobotVacuum_default.idle,
256
- RobotVacuum_default.cleaning,
257
- RobotVacuum_default.returning
258
- );
259
- robotElement.classList.add(RobotVacuum_default[robotState]);
260
- }, [robotState]);
261
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: backgroundRef, className: RobotVacuum_default.container, children: [
262
- dirtPositions.map(
263
- (dirt, index) => !dirt.collected ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
264
- "div",
265
- {
266
- className: RobotVacuum_default.dirt,
267
- style: { left: `${dirt.x}px`, top: `${dirt.y}px` },
268
- role: "presentation",
269
- "aria-label": "dirt particle"
270
- },
271
- `dirt-${index}`
272
- ) : null
273
- ),
274
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: RobotVacuum_default.dock, role: "presentation", "aria-label": "dock" }),
275
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
276
- "div",
277
- {
278
- ref: robotRef,
279
- className: RobotVacuum_default.robot,
280
- role: "presentation",
281
- "aria-label": "robot vacuum",
282
- children: [
283
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `${RobotVacuum_default.wheel} ${RobotVacuum_default.left}` }),
284
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `${RobotVacuum_default.wheel} ${RobotVacuum_default.right}` }),
285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `${RobotVacuum_default.sensor} ${RobotVacuum_default.left}` }),
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `${RobotVacuum_default.sensor} ${RobotVacuum_default.right}` })
287
- ]
288
- }
289
- )
290
- ] });
246
+ const getIndicatorStyle = () => {
247
+ const baseStyle = {
248
+ position: "absolute",
249
+ top: "50%",
250
+ left: "50%",
251
+ transform: "translate(-50%, -50%)",
252
+ width: "7px",
253
+ height: "7px",
254
+ borderRadius: "50%",
255
+ boxShadow: "inset 0 1px 2px rgba(0, 0, 0, 0.3)"
256
+ };
257
+ if (robotState === "idle") {
258
+ return {
259
+ ...baseStyle,
260
+ backgroundColor: "#c0c0c0",
261
+ boxShadow: "0 0 15px rgba(255, 255, 255, 0.9)"
262
+ };
263
+ } else if (robotState === "cleaning") {
264
+ return {
265
+ ...baseStyle,
266
+ backgroundColor: "#00ff00"
267
+ };
268
+ } else {
269
+ return {
270
+ ...baseStyle,
271
+ backgroundColor: "orange"
272
+ };
273
+ }
274
+ };
275
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
276
+ "div",
277
+ {
278
+ ref: backgroundRef,
279
+ style: {
280
+ position: "absolute",
281
+ width: "100%",
282
+ height: "100%",
283
+ overflow: "hidden",
284
+ top: 0,
285
+ left: 0
286
+ },
287
+ children: [
288
+ dirtPositions.map(
289
+ (dirt, index) => !dirt.collected ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
290
+ "div",
291
+ {
292
+ style: {
293
+ position: "absolute",
294
+ zIndex: 100,
295
+ width: "5px",
296
+ height: "5px",
297
+ backgroundColor: "#fff",
298
+ border: "1px solid black",
299
+ left: `${dirt.x}px`,
300
+ top: `${dirt.y}px`
301
+ },
302
+ role: "presentation",
303
+ "aria-label": "dirt particle"
304
+ },
305
+ `dirt-${index}`
306
+ ) : null
307
+ ),
308
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
309
+ "div",
310
+ {
311
+ style: {
312
+ position: "absolute",
313
+ top: "5px",
314
+ left: "25px",
315
+ transform: "translateX(-50%)",
316
+ width: "20px",
317
+ height: "20px",
318
+ backgroundColor: "#333",
319
+ borderRadius: "10px",
320
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)",
321
+ justifyContent: "center",
322
+ alignItems: "center"
323
+ },
324
+ role: "presentation",
325
+ "aria-label": "dock",
326
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
327
+ "div",
328
+ {
329
+ style: {
330
+ position: "absolute",
331
+ left: "-5px",
332
+ top: "-5px",
333
+ width: "30px",
334
+ height: "10px",
335
+ backgroundColor: "#333",
336
+ borderRadius: "5px 5px 0 0"
337
+ }
338
+ }
339
+ )
340
+ }
341
+ ),
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
343
+ "div",
344
+ {
345
+ ref: robotRef,
346
+ style: {
347
+ visibility: "hidden",
348
+ position: "absolute",
349
+ zIndex: 100,
350
+ width: "25px",
351
+ height: "25px",
352
+ backgroundColor: "#333",
353
+ borderRadius: "50%",
354
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)"
355
+ },
356
+ role: "presentation",
357
+ "aria-label": "robot vacuum",
358
+ children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: getIndicatorStyle() }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
361
+ "div",
362
+ {
363
+ style: {
364
+ position: "absolute",
365
+ width: "2px",
366
+ height: "7px",
367
+ backgroundColor: "#444",
368
+ borderRadius: "1px",
369
+ top: "calc(50% - 4px)",
370
+ left: "-1px"
371
+ }
372
+ }
373
+ ),
374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
375
+ "div",
376
+ {
377
+ style: {
378
+ position: "absolute",
379
+ width: "2px",
380
+ height: "7px",
381
+ backgroundColor: "#444",
382
+ borderRadius: "1px",
383
+ top: "calc(50% - 4px)",
384
+ right: "-1px"
385
+ }
386
+ }
387
+ ),
388
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
389
+ "div",
390
+ {
391
+ style: {
392
+ position: "absolute",
393
+ width: "4px",
394
+ height: "4px",
395
+ backgroundColor: "#444",
396
+ borderRadius: "50%",
397
+ top: "3px",
398
+ left: "3px"
399
+ }
400
+ }
401
+ ),
402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
403
+ "div",
404
+ {
405
+ style: {
406
+ position: "absolute",
407
+ width: "4px",
408
+ height: "4px",
409
+ backgroundColor: "#444",
410
+ borderRadius: "50%",
411
+ top: "3px",
412
+ right: "3px"
413
+ }
414
+ }
415
+ )
416
+ ]
417
+ }
418
+ )
419
+ ]
420
+ }
421
+ );
291
422
  }
292
423
  );
293
424
  Robotv2.displayName = "Robotv2";
294
- var RobotVacuum_default2 = Robotv2;
425
+ var RobotVacuum_default = Robotv2;
295
426
  // Annotate the CommonJS export names for ESM import in node:
296
427
  0 && (module.exports = {
297
428
  RobotVacuum
package/dist/index.mjs CHANGED
@@ -8,11 +8,6 @@ import {
8
8
  useRef,
9
9
  useState
10
10
  } from "react";
11
-
12
- // src/RobotVacuum/RobotVacuum.module.css
13
- var RobotVacuum_default = {};
14
-
15
- // src/RobotVacuum/RobotVacuum.tsx
16
11
  import { jsx, jsxs } from "react/jsx-runtime";
17
12
  var Robotv2 = forwardRef(
18
13
  ({
@@ -229,50 +224,186 @@ var Robotv2 = forwardRef(
229
224
  cleanAndReturn();
230
225
  }
231
226
  }, [dirtPositions, robotState, isDocumentVisible, autoStart]);
232
- useEffect(() => {
233
- const robotElement = robotRef.current;
234
- if (!robotElement) return;
235
- robotElement.classList.remove(
236
- RobotVacuum_default.idle,
237
- RobotVacuum_default.cleaning,
238
- RobotVacuum_default.returning
239
- );
240
- robotElement.classList.add(RobotVacuum_default[robotState]);
241
- }, [robotState]);
242
- return /* @__PURE__ */ jsxs("div", { ref: backgroundRef, className: RobotVacuum_default.container, children: [
243
- dirtPositions.map(
244
- (dirt, index) => !dirt.collected ? /* @__PURE__ */ jsx(
245
- "div",
246
- {
247
- className: RobotVacuum_default.dirt,
248
- style: { left: `${dirt.x}px`, top: `${dirt.y}px` },
249
- role: "presentation",
250
- "aria-label": "dirt particle"
251
- },
252
- `dirt-${index}`
253
- ) : null
254
- ),
255
- /* @__PURE__ */ jsx("div", { className: RobotVacuum_default.dock, role: "presentation", "aria-label": "dock" }),
256
- /* @__PURE__ */ jsxs(
257
- "div",
258
- {
259
- ref: robotRef,
260
- className: RobotVacuum_default.robot,
261
- role: "presentation",
262
- "aria-label": "robot vacuum",
263
- children: [
264
- /* @__PURE__ */ jsx("div", { className: `${RobotVacuum_default.wheel} ${RobotVacuum_default.left}` }),
265
- /* @__PURE__ */ jsx("div", { className: `${RobotVacuum_default.wheel} ${RobotVacuum_default.right}` }),
266
- /* @__PURE__ */ jsx("div", { className: `${RobotVacuum_default.sensor} ${RobotVacuum_default.left}` }),
267
- /* @__PURE__ */ jsx("div", { className: `${RobotVacuum_default.sensor} ${RobotVacuum_default.right}` })
268
- ]
269
- }
270
- )
271
- ] });
227
+ const getIndicatorStyle = () => {
228
+ const baseStyle = {
229
+ position: "absolute",
230
+ top: "50%",
231
+ left: "50%",
232
+ transform: "translate(-50%, -50%)",
233
+ width: "7px",
234
+ height: "7px",
235
+ borderRadius: "50%",
236
+ boxShadow: "inset 0 1px 2px rgba(0, 0, 0, 0.3)"
237
+ };
238
+ if (robotState === "idle") {
239
+ return {
240
+ ...baseStyle,
241
+ backgroundColor: "#c0c0c0",
242
+ boxShadow: "0 0 15px rgba(255, 255, 255, 0.9)"
243
+ };
244
+ } else if (robotState === "cleaning") {
245
+ return {
246
+ ...baseStyle,
247
+ backgroundColor: "#00ff00"
248
+ };
249
+ } else {
250
+ return {
251
+ ...baseStyle,
252
+ backgroundColor: "orange"
253
+ };
254
+ }
255
+ };
256
+ return /* @__PURE__ */ jsxs(
257
+ "div",
258
+ {
259
+ ref: backgroundRef,
260
+ style: {
261
+ position: "absolute",
262
+ width: "100%",
263
+ height: "100%",
264
+ overflow: "hidden",
265
+ top: 0,
266
+ left: 0
267
+ },
268
+ children: [
269
+ dirtPositions.map(
270
+ (dirt, index) => !dirt.collected ? /* @__PURE__ */ jsx(
271
+ "div",
272
+ {
273
+ style: {
274
+ position: "absolute",
275
+ zIndex: 100,
276
+ width: "5px",
277
+ height: "5px",
278
+ backgroundColor: "#fff",
279
+ border: "1px solid black",
280
+ left: `${dirt.x}px`,
281
+ top: `${dirt.y}px`
282
+ },
283
+ role: "presentation",
284
+ "aria-label": "dirt particle"
285
+ },
286
+ `dirt-${index}`
287
+ ) : null
288
+ ),
289
+ /* @__PURE__ */ jsx(
290
+ "div",
291
+ {
292
+ style: {
293
+ position: "absolute",
294
+ top: "5px",
295
+ left: "25px",
296
+ transform: "translateX(-50%)",
297
+ width: "20px",
298
+ height: "20px",
299
+ backgroundColor: "#333",
300
+ borderRadius: "10px",
301
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)",
302
+ justifyContent: "center",
303
+ alignItems: "center"
304
+ },
305
+ role: "presentation",
306
+ "aria-label": "dock",
307
+ children: /* @__PURE__ */ jsx(
308
+ "div",
309
+ {
310
+ style: {
311
+ position: "absolute",
312
+ left: "-5px",
313
+ top: "-5px",
314
+ width: "30px",
315
+ height: "10px",
316
+ backgroundColor: "#333",
317
+ borderRadius: "5px 5px 0 0"
318
+ }
319
+ }
320
+ )
321
+ }
322
+ ),
323
+ /* @__PURE__ */ jsxs(
324
+ "div",
325
+ {
326
+ ref: robotRef,
327
+ style: {
328
+ visibility: "hidden",
329
+ position: "absolute",
330
+ zIndex: 100,
331
+ width: "25px",
332
+ height: "25px",
333
+ backgroundColor: "#333",
334
+ borderRadius: "50%",
335
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)"
336
+ },
337
+ role: "presentation",
338
+ "aria-label": "robot vacuum",
339
+ children: [
340
+ /* @__PURE__ */ jsx("div", { style: getIndicatorStyle() }),
341
+ /* @__PURE__ */ jsx(
342
+ "div",
343
+ {
344
+ style: {
345
+ position: "absolute",
346
+ width: "2px",
347
+ height: "7px",
348
+ backgroundColor: "#444",
349
+ borderRadius: "1px",
350
+ top: "calc(50% - 4px)",
351
+ left: "-1px"
352
+ }
353
+ }
354
+ ),
355
+ /* @__PURE__ */ jsx(
356
+ "div",
357
+ {
358
+ style: {
359
+ position: "absolute",
360
+ width: "2px",
361
+ height: "7px",
362
+ backgroundColor: "#444",
363
+ borderRadius: "1px",
364
+ top: "calc(50% - 4px)",
365
+ right: "-1px"
366
+ }
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsx(
370
+ "div",
371
+ {
372
+ style: {
373
+ position: "absolute",
374
+ width: "4px",
375
+ height: "4px",
376
+ backgroundColor: "#444",
377
+ borderRadius: "50%",
378
+ top: "3px",
379
+ left: "3px"
380
+ }
381
+ }
382
+ ),
383
+ /* @__PURE__ */ jsx(
384
+ "div",
385
+ {
386
+ style: {
387
+ position: "absolute",
388
+ width: "4px",
389
+ height: "4px",
390
+ backgroundColor: "#444",
391
+ borderRadius: "50%",
392
+ top: "3px",
393
+ right: "3px"
394
+ }
395
+ }
396
+ )
397
+ ]
398
+ }
399
+ )
400
+ ]
401
+ }
402
+ );
272
403
  }
273
404
  );
274
405
  Robotv2.displayName = "Robotv2";
275
- var RobotVacuum_default2 = Robotv2;
406
+ var RobotVacuum_default = Robotv2;
276
407
  export {
277
- RobotVacuum_default2 as RobotVacuum
408
+ RobotVacuum_default as RobotVacuum
278
409
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-robot-vacuum",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "An animated React component featuring a robot vacuum that autonomously cleans your page by collecting dirt particles",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -10,16 +10,14 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.mjs",
12
12
  "require": "./dist/index.js"
13
- },
14
- "./styles.css": "./dist/RobotVacuum.module.css",
15
- "./dist/RobotVacuum.module.css": "./dist/RobotVacuum.module.css"
13
+ }
16
14
  },
17
15
  "files": [
18
16
  "dist",
19
17
  "README.md"
20
18
  ],
21
19
  "scripts": {
22
- "build": "tsup && cp src/RobotVacuum/RobotVacuum.module.css dist/",
20
+ "build": "tsup",
23
21
  "dev": "tsup src/index.tsx --format cjs,esm --dts --watch",
24
22
  "vite:dev": "vite",
25
23
  "vite:build": "vite build",
@@ -69,6 +67,6 @@
69
67
  "vite": "^7.3.0"
70
68
  },
71
69
  "dependencies": {
72
- "react-robot-vacuum": "^1.0.0"
70
+ "react-robot-vacuum": "^1.0.2"
73
71
  }
74
72
  }