@frixaco/anitrack 0.0.6 → 0.0.8

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.
Files changed (2) hide show
  1. package/package.json +12 -8
  2. package/progress-bar.ts +100 -0
package/package.json CHANGED
@@ -1,25 +1,29 @@
1
1
  {
2
2
  "name": "@frixaco/anitrack",
3
- "version": "0.0.6",
4
- "module": "index.ts",
3
+ "version": "0.0.8",
5
4
  "type": "module",
6
5
  "bin": {
7
6
  "anitrack": "./index.ts"
8
7
  },
9
8
  "files": [
10
9
  "index.ts",
11
- "colors.ts"
10
+ "colors.ts",
11
+ "progress-bar.ts",
12
+ "README.md"
12
13
  ],
14
+ "engines": {
15
+ "bun": ">=1.0.0"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
13
20
  "scripts": {
14
21
  "dev": "bun run index.ts"
15
22
  },
16
23
  "dependencies": {
17
- "@frixaco/letui": "^0.0.10"
24
+ "@frixaco/letui": "^0.0.11"
18
25
  },
19
26
  "devDependencies": {
20
- "@types/bun": "1.3.5"
21
- },
22
- "peerDependencies": {
23
- "typescript": "^5"
27
+ "@types/bun": "1.3.9"
24
28
  }
25
29
  }
@@ -0,0 +1,100 @@
1
+ import type { Node } from "@frixaco/letui";
2
+ import { $, ff, Row, Text } from "@frixaco/letui";
3
+
4
+ export type LoadingBarProps = {
5
+ dotColor: number;
6
+ trackColor: number;
7
+ flexGrow?: number;
8
+ interval?: number; // ms between frames
9
+ };
10
+
11
+ export type LoadingBarController = {
12
+ node: Node;
13
+ start: () => void;
14
+ stop: () => void;
15
+ };
16
+
17
+ export function LoadingBar(props: LoadingBarProps): LoadingBarController {
18
+ const { dotColor, trackColor, flexGrow = 1, interval = 80 } = props;
19
+
20
+ const position = $(0);
21
+ const direction = $(1); // 1 = right, -1 = left
22
+ const active = $(false);
23
+ let timer: Timer | null = null;
24
+
25
+ const leftTrack = Text({
26
+ text: "",
27
+ background: trackColor,
28
+ foreground: trackColor,
29
+ });
30
+ const dot = Text({ text: "", background: dotColor, foreground: dotColor });
31
+ const rightTrack = Text({
32
+ text: "",
33
+ background: trackColor,
34
+ foreground: trackColor,
35
+ });
36
+
37
+ const node = Row({ flexGrow }, [leftTrack, dot, rightTrack]);
38
+
39
+ // React to position changes
40
+ ff(() => {
41
+ const isActive = active();
42
+ const pos = position();
43
+ const width = node.frameWidth();
44
+ if (width === 0 || !isActive) return;
45
+
46
+ const maxPos = width - 1;
47
+ const clampedPos = Math.max(0, Math.min(pos, maxPos));
48
+
49
+ leftTrack.setStyle?.({ text: " ".repeat(clampedPos) });
50
+ dot.setStyle?.({ text: " " });
51
+ rightTrack.setStyle?.({ text: " ".repeat(maxPos - clampedPos) });
52
+ });
53
+
54
+ function clearTimer() {
55
+ if (timer) {
56
+ clearInterval(timer);
57
+ timer = null;
58
+ }
59
+ }
60
+
61
+ function start() {
62
+ if (active()) return;
63
+ active(true);
64
+ position(0);
65
+ direction(1);
66
+
67
+ timer = setInterval(() => {
68
+ const width = node.frameWidth();
69
+ if (width === 0) return;
70
+
71
+ const maxPos = width - 1;
72
+ const pos = position();
73
+ const dir = direction();
74
+
75
+ const step = 12;
76
+ const nextPos = pos + dir * step;
77
+ if (nextPos >= maxPos) {
78
+ direction(-1);
79
+ position(maxPos);
80
+ } else if (nextPos <= 0) {
81
+ direction(1);
82
+ position(0);
83
+ } else {
84
+ position(nextPos);
85
+ }
86
+ }, interval);
87
+ }
88
+
89
+ function stop() {
90
+ clearTimer();
91
+ active(false);
92
+ position(0);
93
+ // Clear all three segments
94
+ leftTrack.setStyle?.({ text: "" });
95
+ dot.setStyle?.({ text: "" });
96
+ rightTrack.setStyle?.({ text: "" });
97
+ }
98
+
99
+ return { node, start, stop };
100
+ }