@skalfa/skalfa-app 1.0.0 → 1.0.1

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 (87) hide show
  1. package/.env.example +43 -43
  2. package/.github/workflows/publish.yml +39 -0
  3. package/CONTRIBUTING.md +45 -0
  4. package/LICENSE +21 -0
  5. package/README.md +91 -28
  6. package/app/auth/edit/page.tsx +65 -65
  7. package/app/auth/login/page.tsx +63 -63
  8. package/app/auth/me/page.tsx +58 -58
  9. package/app/auth/register/page.tsx +69 -69
  10. package/app/auth/verify/page.tsx +53 -53
  11. package/app/dashboard/user/page.tsx +76 -76
  12. package/app/layout.tsx +37 -37
  13. package/app/manifest.ts +25 -0
  14. package/app/page.tsx +13 -13
  15. package/barrels.json +5 -5
  16. package/blueprints/starter.blueprint.json +102 -102
  17. package/bun.lock +916 -0
  18. package/components/base.components/chip/Chip.component.tsx +39 -39
  19. package/components/base.components/document/DocumentViewer.component.tsx +163 -163
  20. package/components/base.components/document/ExportExcel.component.tsx +340 -340
  21. package/components/base.components/document/ImportExcel.component.tsx +315 -315
  22. package/components/base.components/document/PrintTable.component.tsx +204 -204
  23. package/components/base.components/document/RenderPDF.component.tsx +415 -415
  24. package/components/base.components/input/Checkbox.component.tsx +109 -109
  25. package/components/base.components/input/Input.component.tsx +332 -332
  26. package/components/base.components/input/InputCheckbox.component.tsx +174 -174
  27. package/components/base.components/input/InputCurrency.component.tsx +163 -163
  28. package/components/base.components/input/InputDate.component.tsx +352 -352
  29. package/components/base.components/input/InputDatetime.component.tsx +260 -260
  30. package/components/base.components/input/InputDocument.component.tsx +351 -351
  31. package/components/base.components/input/InputImage.component.tsx +533 -533
  32. package/components/base.components/input/InputMap.component.tsx +317 -317
  33. package/components/base.components/input/InputNumber.component.tsx +192 -192
  34. package/components/base.components/input/InputOtp.component.tsx +169 -169
  35. package/components/base.components/input/InputPassword.component.tsx +236 -236
  36. package/components/base.components/input/InputRadio.component.tsx +175 -175
  37. package/components/base.components/input/InputTime.component.tsx +275 -275
  38. package/components/base.components/input/InputValues.component.tsx +68 -68
  39. package/components/base.components/input/Radio.component.tsx +102 -102
  40. package/components/base.components/input/Select.component.tsx +541 -541
  41. package/components/base.components/modal/BottomSheet.component.tsx +245 -245
  42. package/components/base.components/supervision/FormSupervision.component.tsx +433 -433
  43. package/components/base.components/supervision/TableSupervision.component.tsx +697 -697
  44. package/components/base.components/table/ControlBar.component.tsx +497 -497
  45. package/components/base.components/table/FilterComponent.tsx +518 -518
  46. package/components/base.components/table/Table.component.tsx +469 -469
  47. package/components/base.components/typography/TypographyArticle.component.tsx +26 -26
  48. package/components/base.components/typography/TypographyColumn.component.tsx +20 -20
  49. package/components/base.components/typography/TypographyContent.component.tsx +20 -20
  50. package/components/base.components/typography/TypographyTips.component.tsx +20 -20
  51. package/components/base.components/wrap/Draggable.component.tsx +303 -303
  52. package/components/base.components/wrap/IDBProvider.tsx +12 -12
  53. package/components/base.components/wrap/Image.component.tsx +9 -9
  54. package/components/base.components/wrap/ShortcutProvider.tsx +57 -57
  55. package/components/base.components/wrap/Swipe.component.tsx +93 -93
  56. package/components/index.ts +2 -2
  57. package/contexts/AppProvider.tsx +11 -11
  58. package/contexts/Auth.context.tsx +64 -64
  59. package/contexts/Toggle.context.tsx +44 -44
  60. package/next.config.ts +15 -1
  61. package/package.json +14 -13
  62. package/public/204.svg +19 -19
  63. package/public/500.svg +39 -39
  64. package/public/icon-192.png +0 -0
  65. package/public/icon-512.png +0 -0
  66. package/public/images/logo-fill.png +0 -0
  67. package/public/images/logo-full-fill.png +0 -0
  68. package/public/images/logo-full.png +0 -0
  69. package/public/images/logo.png +0 -0
  70. package/schema/idb/app.schema.ts +8 -8
  71. package/src-tauri/Cargo.toml +14 -0
  72. package/src-tauri/build.rs +3 -0
  73. package/src-tauri/capabilities/default.json +11 -0
  74. package/src-tauri/icons/128x128.png +0 -0
  75. package/src-tauri/icons/128x128@2x.png +0 -0
  76. package/src-tauri/icons/32x32.png +0 -0
  77. package/src-tauri/icons/icon.icns +0 -0
  78. package/src-tauri/icons/icon.ico +0 -0
  79. package/src-tauri/src/main.rs +7 -0
  80. package/src-tauri/tauri.conf.json +36 -0
  81. package/styles/globals.css +231 -231
  82. package/styles/tailwind.safelist +68 -68
  83. package/utils/commands/barrels.ts +27 -27
  84. package/utils/commands/light.ts +21 -21
  85. package/utils/commands/logger.ts +42 -42
  86. package/utils/commands/stubs/table-blueprint.stub +12 -12
  87. package/utils/commands/use-pdf.ts +29 -29
@@ -1,303 +1,303 @@
1
- "use client"
2
-
3
- import { useRef, useEffect, useState, ReactNode, forwardRef, useImperativeHandle, CSSProperties } from 'react';
4
-
5
-
6
-
7
- type SpringConfig = {
8
- stiffness ?: number;
9
- damping ?: number;
10
- mass ?: number;
11
- stopThreshold ?: number;
12
- };
13
-
14
- type Bounds = {
15
- left ?: number;
16
- top ?: number;
17
- right ?: number;
18
- bottom ?: number;
19
- };
20
-
21
- type DragState = {
22
- x : number;
23
- y : number;
24
- vx : number;
25
- vy : number;
26
- };
27
-
28
- type DraggableProps = {
29
- children ?: ReactNode;
30
- className ?: string;
31
- style ?: CSSProperties;
32
- initial ?: { x?: number; y?: number };
33
- bounds ?: Bounds;
34
- spring ?: SpringConfig;
35
- onDrag ?: (state: { x: number; y: number; vx: number; vy: number; dragging: boolean }) => void;
36
- useTranslate3d ?: boolean;
37
- };
38
-
39
- // -----------------------------------------------------
40
- // DraggableComponent
41
- // -----------------------------------------------------
42
- export const DraggableComponent = forwardRef(function DraggableComponent(
43
- {
44
- children,
45
- className,
46
- style,
47
- initial = { x: 0, y: 0 },
48
- bounds,
49
- spring,
50
- onDrag,
51
- useTranslate3d = true,
52
- }: DraggableProps,
53
- ref
54
- ) {
55
- const elRef = useRef<HTMLDivElement | null>(null);
56
- const frameRef = useRef<number | null>(null);
57
- const draggingRef = useRef(false);
58
- const pointerIdRef = useRef<number | null>(null);
59
- const stateRef = useRef<DragState>({ x: initial.x || 0, y: initial.y || 0, vx: 0, vy: 0 });
60
- const targetRef = useRef<{ x: number; y: number }>({ x: initial.x || 0, y: initial.y || 0 });
61
-
62
- const historyRef = useRef<Array<{ t: number; x: number; y: number }>>([]);
63
-
64
- const cfg: Required<SpringConfig> = {
65
- stiffness : spring?.stiffness ?? 180,
66
- damping : spring?.damping ?? 24,
67
- mass : spring?.mass ?? 1,
68
- stopThreshold : spring?.stopThreshold ?? 0.02,
69
- };
70
-
71
- const [, setTick] = useState(0);
72
-
73
- const now = () => performance.now();
74
-
75
- function applyStyle(x: number, y: number) {
76
- const el = elRef.current;
77
- if (!el) return;
78
- const transform = useTranslate3d ? `translate3d(${x}px, ${y}px, 0)` : `translate(${x}px, ${y}px)`;
79
- el.style.transform = transform;
80
- }
81
-
82
- function clampToBounds(x: number, y: number) {
83
- if (!bounds) return { x, y };
84
- return {
85
- x: Math.max(bounds.left ?? -Infinity, Math.min(x, bounds.right ?? Infinity)),
86
- y: Math.max(bounds.top ?? -Infinity, Math.min(y, bounds.bottom ?? Infinity)),
87
- };
88
- }
89
-
90
- function estimateVelocity() {
91
- const h = historyRef.current;
92
- if (h.length < 2) return { vx: 0, vy: 0 };
93
- const last = h[h.length - 1];
94
- const threshold = 50;
95
- let idx = h.length - 2;
96
- while (idx > 0 && last.t - h[idx].t < threshold) idx--;
97
- const prev = h[idx];
98
- const dt = Math.max(1, last.t - prev.t);
99
- return {
100
- vx: (last.x - prev.x) / (dt / 1000),
101
- vy: (last.y - prev.y) / (dt / 1000),
102
- };
103
- }
104
-
105
- function cancelFrame() {
106
- if (frameRef.current != null) {
107
- cancelAnimationFrame(frameRef.current);
108
- frameRef.current = null;
109
- }
110
- }
111
-
112
- function startSpringAnimation(initVX = 0, initVY = 0) {
113
- cancelFrame();
114
-
115
- const mass = cfg.mass;
116
- const k = cfg.stiffness;
117
- const c = cfg.damping;
118
-
119
- stateRef.current.vx = initVX;
120
- stateRef.current.vy = initVY;
121
-
122
- let lastTime = now();
123
-
124
- function step() {
125
- const t = now();
126
- const dt = Math.min(32, t - lastTime) / 1000;
127
- lastTime = t;
128
-
129
- const sx = stateRef.current.x;
130
- const sy = stateRef.current.y;
131
- const vx = stateRef.current.vx;
132
- const vy = stateRef.current.vy;
133
- const tx = targetRef.current.x;
134
- const ty = targetRef.current.y;
135
-
136
- const ax = (-c * vx - k * (sx - tx)) / mass;
137
- const ay = (-c * vy - k * (sy - ty)) / mass;
138
-
139
- const nvx = vx + ax * dt;
140
- const nvy = vy + ay * dt;
141
- const nx = sx + nvx * dt;
142
- const ny = sy + nvy * dt;
143
-
144
- stateRef.current.x = nx;
145
- stateRef.current.y = ny;
146
- stateRef.current.vx = nvx;
147
- stateRef.current.vy = nvy;
148
-
149
- applyStyle(nx, ny);
150
- onDrag?.({ x: nx, y: ny, vx: nvx, vy: nvy, dragging: false });
151
-
152
- const disp = Math.hypot(nx - tx, ny - ty);
153
- const vel = Math.hypot(nvx, nvy);
154
-
155
- if (disp < cfg.stopThreshold && vel < cfg.stopThreshold) {
156
- stateRef.current.x = tx;
157
- stateRef.current.y = ty;
158
- stateRef.current.vx = 0;
159
- stateRef.current.vy = 0;
160
- applyStyle(tx, ty);
161
- onDrag?.({ x: tx, y: ty, vx: 0, vy: 0, dragging: false });
162
- frameRef.current = null;
163
- return;
164
- }
165
-
166
- frameRef.current = requestAnimationFrame(step);
167
- }
168
-
169
- frameRef.current = requestAnimationFrame(step);
170
- }
171
-
172
- // -------------------------------------------------------------------
173
- // Expose API ke parent: setTarget & setPositionImmediate
174
- // -------------------------------------------------------------------
175
- useImperativeHandle(ref, () => ({
176
- setTarget(pos: { x: number; y: number }) {
177
- targetRef.current.x = pos.x;
178
- targetRef.current.y = pos.y;
179
- startSpringAnimation(0, 0);
180
- },
181
-
182
- setPosition(pos: { x: number; y: number }) {
183
- stateRef.current.x = pos.x;
184
- stateRef.current.y = pos.y;
185
- targetRef.current.x = pos.x;
186
- targetRef.current.y = pos.y;
187
- applyStyle(pos.x, pos.y);
188
- },
189
- }));
190
-
191
- useEffect(() => {
192
- const el = elRef.current;
193
- if (!el) return;
194
-
195
- function onDown(e: PointerEvent) {
196
- if (e.pointerType === 'mouse' && e.button !== 0) return;
197
-
198
- (e.target as Element).setPointerCapture?.(e.pointerId);
199
- pointerIdRef.current = e.pointerId;
200
- draggingRef.current = true;
201
- cancelFrame();
202
-
203
- historyRef.current = [{ t: now(), x: stateRef.current.x, y: stateRef.current.y }];
204
-
205
- (el as any).__dragBase = {
206
- px: e.clientX,
207
- py: e.clientY,
208
- startX: stateRef.current.x,
209
- startY: stateRef.current.y,
210
- };
211
-
212
- onDrag?.({
213
- x: stateRef.current.x,
214
- y: stateRef.current.y,
215
- vx: 0,
216
- vy: 0,
217
- dragging: true,
218
- });
219
- }
220
-
221
- function onMove(e: PointerEvent) {
222
- if (!draggingRef.current) return;
223
- if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
224
-
225
- const base = (el as any).__dragBase;
226
- if (!base) return;
227
-
228
- let nx = base.startX + (e.clientX - base.px);
229
- let ny = base.startY + (e.clientY - base.py);
230
-
231
- ({ x: nx, y: ny } = clampToBounds(nx, ny));
232
-
233
- historyRef.current.push({ t: now(), x: nx, y: ny });
234
- if (historyRef.current.length > 8) historyRef.current.shift();
235
-
236
- stateRef.current.x = nx;
237
- stateRef.current.y = ny;
238
- stateRef.current.vx = 0;
239
- stateRef.current.vy = 0;
240
-
241
- applyStyle(nx, ny);
242
- onDrag?.({ x: nx, y: ny, vx: 0, vy: 0, dragging: true });
243
-
244
- setTick(t => t + 1);
245
- }
246
-
247
- function onUp(e: PointerEvent) {
248
- if (!draggingRef.current) return;
249
- if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
250
-
251
- (e.target as Element).releasePointerCapture?.(e.pointerId);
252
- draggingRef.current = false;
253
- pointerIdRef.current = null;
254
-
255
- const { vx, vy } = estimateVelocity();
256
-
257
- let tx = stateRef.current.x;
258
- let ty = stateRef.current.y;
259
- ({ x: tx, y: ty } = clampToBounds(tx, ty));
260
-
261
- targetRef.current.x = tx;
262
- targetRef.current.y = ty;
263
-
264
- startSpringAnimation(vx, vy);
265
- }
266
-
267
- el.addEventListener('pointerdown', onDown as EventListener);
268
- window.addEventListener('pointermove', onMove as EventListener);
269
- window.addEventListener('pointerup', onUp as EventListener);
270
- window.addEventListener('pointercancel', onUp as EventListener);
271
-
272
- return () => {
273
- el.removeEventListener('pointerdown', onDown as EventListener);
274
- window.removeEventListener('pointermove', onMove as EventListener);
275
- window.removeEventListener('pointerup', onUp as EventListener);
276
- window.removeEventListener('pointercancel', onUp as EventListener);
277
- cancelFrame();
278
- };
279
- }, [bounds]);
280
-
281
- // initial pos
282
- useEffect(() => {
283
- stateRef.current.x = initial.x || 0;
284
- stateRef.current.y = initial.y || 0;
285
- applyStyle(initial.x || 0, initial.y || 0);
286
- }, []);
287
-
288
- return (
289
- <div
290
- ref={elRef}
291
- className={className}
292
- style={{
293
- touchAction: 'none',
294
- cursor: 'grab',
295
- display: 'inline-block',
296
- ...style,
297
- }}
298
- onMouseDown={(e) => e.preventDefault()}
299
- >
300
- {children}
301
- </div>
302
- );
303
- });
1
+ "use client"
2
+
3
+ import { useRef, useEffect, useState, ReactNode, forwardRef, useImperativeHandle, CSSProperties } from 'react';
4
+
5
+
6
+
7
+ type SpringConfig = {
8
+ stiffness ?: number;
9
+ damping ?: number;
10
+ mass ?: number;
11
+ stopThreshold ?: number;
12
+ };
13
+
14
+ type Bounds = {
15
+ left ?: number;
16
+ top ?: number;
17
+ right ?: number;
18
+ bottom ?: number;
19
+ };
20
+
21
+ type DragState = {
22
+ x : number;
23
+ y : number;
24
+ vx : number;
25
+ vy : number;
26
+ };
27
+
28
+ type DraggableProps = {
29
+ children ?: ReactNode;
30
+ className ?: string;
31
+ style ?: CSSProperties;
32
+ initial ?: { x?: number; y?: number };
33
+ bounds ?: Bounds;
34
+ spring ?: SpringConfig;
35
+ onDrag ?: (state: { x: number; y: number; vx: number; vy: number; dragging: boolean }) => void;
36
+ useTranslate3d ?: boolean;
37
+ };
38
+
39
+ // -----------------------------------------------------
40
+ // DraggableComponent
41
+ // -----------------------------------------------------
42
+ export const DraggableComponent = forwardRef(function DraggableComponent(
43
+ {
44
+ children,
45
+ className,
46
+ style,
47
+ initial = { x: 0, y: 0 },
48
+ bounds,
49
+ spring,
50
+ onDrag,
51
+ useTranslate3d = true,
52
+ }: DraggableProps,
53
+ ref
54
+ ) {
55
+ const elRef = useRef<HTMLDivElement | null>(null);
56
+ const frameRef = useRef<number | null>(null);
57
+ const draggingRef = useRef(false);
58
+ const pointerIdRef = useRef<number | null>(null);
59
+ const stateRef = useRef<DragState>({ x: initial.x || 0, y: initial.y || 0, vx: 0, vy: 0 });
60
+ const targetRef = useRef<{ x: number; y: number }>({ x: initial.x || 0, y: initial.y || 0 });
61
+
62
+ const historyRef = useRef<Array<{ t: number; x: number; y: number }>>([]);
63
+
64
+ const cfg: Required<SpringConfig> = {
65
+ stiffness : spring?.stiffness ?? 180,
66
+ damping : spring?.damping ?? 24,
67
+ mass : spring?.mass ?? 1,
68
+ stopThreshold : spring?.stopThreshold ?? 0.02,
69
+ };
70
+
71
+ const [, setTick] = useState(0);
72
+
73
+ const now = () => performance.now();
74
+
75
+ function applyStyle(x: number, y: number) {
76
+ const el = elRef.current;
77
+ if (!el) return;
78
+ const transform = useTranslate3d ? `translate3d(${x}px, ${y}px, 0)` : `translate(${x}px, ${y}px)`;
79
+ el.style.transform = transform;
80
+ }
81
+
82
+ function clampToBounds(x: number, y: number) {
83
+ if (!bounds) return { x, y };
84
+ return {
85
+ x: Math.max(bounds.left ?? -Infinity, Math.min(x, bounds.right ?? Infinity)),
86
+ y: Math.max(bounds.top ?? -Infinity, Math.min(y, bounds.bottom ?? Infinity)),
87
+ };
88
+ }
89
+
90
+ function estimateVelocity() {
91
+ const h = historyRef.current;
92
+ if (h.length < 2) return { vx: 0, vy: 0 };
93
+ const last = h[h.length - 1];
94
+ const threshold = 50;
95
+ let idx = h.length - 2;
96
+ while (idx > 0 && last.t - h[idx].t < threshold) idx--;
97
+ const prev = h[idx];
98
+ const dt = Math.max(1, last.t - prev.t);
99
+ return {
100
+ vx: (last.x - prev.x) / (dt / 1000),
101
+ vy: (last.y - prev.y) / (dt / 1000),
102
+ };
103
+ }
104
+
105
+ function cancelFrame() {
106
+ if (frameRef.current != null) {
107
+ cancelAnimationFrame(frameRef.current);
108
+ frameRef.current = null;
109
+ }
110
+ }
111
+
112
+ function startSpringAnimation(initVX = 0, initVY = 0) {
113
+ cancelFrame();
114
+
115
+ const mass = cfg.mass;
116
+ const k = cfg.stiffness;
117
+ const c = cfg.damping;
118
+
119
+ stateRef.current.vx = initVX;
120
+ stateRef.current.vy = initVY;
121
+
122
+ let lastTime = now();
123
+
124
+ function step() {
125
+ const t = now();
126
+ const dt = Math.min(32, t - lastTime) / 1000;
127
+ lastTime = t;
128
+
129
+ const sx = stateRef.current.x;
130
+ const sy = stateRef.current.y;
131
+ const vx = stateRef.current.vx;
132
+ const vy = stateRef.current.vy;
133
+ const tx = targetRef.current.x;
134
+ const ty = targetRef.current.y;
135
+
136
+ const ax = (-c * vx - k * (sx - tx)) / mass;
137
+ const ay = (-c * vy - k * (sy - ty)) / mass;
138
+
139
+ const nvx = vx + ax * dt;
140
+ const nvy = vy + ay * dt;
141
+ const nx = sx + nvx * dt;
142
+ const ny = sy + nvy * dt;
143
+
144
+ stateRef.current.x = nx;
145
+ stateRef.current.y = ny;
146
+ stateRef.current.vx = nvx;
147
+ stateRef.current.vy = nvy;
148
+
149
+ applyStyle(nx, ny);
150
+ onDrag?.({ x: nx, y: ny, vx: nvx, vy: nvy, dragging: false });
151
+
152
+ const disp = Math.hypot(nx - tx, ny - ty);
153
+ const vel = Math.hypot(nvx, nvy);
154
+
155
+ if (disp < cfg.stopThreshold && vel < cfg.stopThreshold) {
156
+ stateRef.current.x = tx;
157
+ stateRef.current.y = ty;
158
+ stateRef.current.vx = 0;
159
+ stateRef.current.vy = 0;
160
+ applyStyle(tx, ty);
161
+ onDrag?.({ x: tx, y: ty, vx: 0, vy: 0, dragging: false });
162
+ frameRef.current = null;
163
+ return;
164
+ }
165
+
166
+ frameRef.current = requestAnimationFrame(step);
167
+ }
168
+
169
+ frameRef.current = requestAnimationFrame(step);
170
+ }
171
+
172
+ // -------------------------------------------------------------------
173
+ // Expose API ke parent: setTarget & setPositionImmediate
174
+ // -------------------------------------------------------------------
175
+ useImperativeHandle(ref, () => ({
176
+ setTarget(pos: { x: number; y: number }) {
177
+ targetRef.current.x = pos.x;
178
+ targetRef.current.y = pos.y;
179
+ startSpringAnimation(0, 0);
180
+ },
181
+
182
+ setPosition(pos: { x: number; y: number }) {
183
+ stateRef.current.x = pos.x;
184
+ stateRef.current.y = pos.y;
185
+ targetRef.current.x = pos.x;
186
+ targetRef.current.y = pos.y;
187
+ applyStyle(pos.x, pos.y);
188
+ },
189
+ }));
190
+
191
+ useEffect(() => {
192
+ const el = elRef.current;
193
+ if (!el) return;
194
+
195
+ function onDown(e: PointerEvent) {
196
+ if (e.pointerType === 'mouse' && e.button !== 0) return;
197
+
198
+ (e.target as Element).setPointerCapture?.(e.pointerId);
199
+ pointerIdRef.current = e.pointerId;
200
+ draggingRef.current = true;
201
+ cancelFrame();
202
+
203
+ historyRef.current = [{ t: now(), x: stateRef.current.x, y: stateRef.current.y }];
204
+
205
+ (el as any).__dragBase = {
206
+ px: e.clientX,
207
+ py: e.clientY,
208
+ startX: stateRef.current.x,
209
+ startY: stateRef.current.y,
210
+ };
211
+
212
+ onDrag?.({
213
+ x: stateRef.current.x,
214
+ y: stateRef.current.y,
215
+ vx: 0,
216
+ vy: 0,
217
+ dragging: true,
218
+ });
219
+ }
220
+
221
+ function onMove(e: PointerEvent) {
222
+ if (!draggingRef.current) return;
223
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
224
+
225
+ const base = (el as any).__dragBase;
226
+ if (!base) return;
227
+
228
+ let nx = base.startX + (e.clientX - base.px);
229
+ let ny = base.startY + (e.clientY - base.py);
230
+
231
+ ({ x: nx, y: ny } = clampToBounds(nx, ny));
232
+
233
+ historyRef.current.push({ t: now(), x: nx, y: ny });
234
+ if (historyRef.current.length > 8) historyRef.current.shift();
235
+
236
+ stateRef.current.x = nx;
237
+ stateRef.current.y = ny;
238
+ stateRef.current.vx = 0;
239
+ stateRef.current.vy = 0;
240
+
241
+ applyStyle(nx, ny);
242
+ onDrag?.({ x: nx, y: ny, vx: 0, vy: 0, dragging: true });
243
+
244
+ setTick(t => t + 1);
245
+ }
246
+
247
+ function onUp(e: PointerEvent) {
248
+ if (!draggingRef.current) return;
249
+ if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current) return;
250
+
251
+ (e.target as Element).releasePointerCapture?.(e.pointerId);
252
+ draggingRef.current = false;
253
+ pointerIdRef.current = null;
254
+
255
+ const { vx, vy } = estimateVelocity();
256
+
257
+ let tx = stateRef.current.x;
258
+ let ty = stateRef.current.y;
259
+ ({ x: tx, y: ty } = clampToBounds(tx, ty));
260
+
261
+ targetRef.current.x = tx;
262
+ targetRef.current.y = ty;
263
+
264
+ startSpringAnimation(vx, vy);
265
+ }
266
+
267
+ el.addEventListener('pointerdown', onDown as EventListener);
268
+ window.addEventListener('pointermove', onMove as EventListener);
269
+ window.addEventListener('pointerup', onUp as EventListener);
270
+ window.addEventListener('pointercancel', onUp as EventListener);
271
+
272
+ return () => {
273
+ el.removeEventListener('pointerdown', onDown as EventListener);
274
+ window.removeEventListener('pointermove', onMove as EventListener);
275
+ window.removeEventListener('pointerup', onUp as EventListener);
276
+ window.removeEventListener('pointercancel', onUp as EventListener);
277
+ cancelFrame();
278
+ };
279
+ }, [bounds]);
280
+
281
+ // initial pos
282
+ useEffect(() => {
283
+ stateRef.current.x = initial.x || 0;
284
+ stateRef.current.y = initial.y || 0;
285
+ applyStyle(initial.x || 0, initial.y || 0);
286
+ }, []);
287
+
288
+ return (
289
+ <div
290
+ ref={elRef}
291
+ className={className}
292
+ style={{
293
+ touchAction: 'none',
294
+ cursor: 'grab',
295
+ display: 'inline-block',
296
+ ...style,
297
+ }}
298
+ onMouseDown={(e) => e.preventDefault()}
299
+ >
300
+ {children}
301
+ </div>
302
+ );
303
+ });
@@ -1,12 +1,12 @@
1
- "use client"
2
-
3
- import { useEffect } from "react"
4
- import { idb } from "@skalfa/skalfa-idb"
5
- import { AppSchema } from "@schema"
6
-
7
-
8
- export function IDBProvider({ children }: { children: React.ReactNode }) {
9
- useEffect(() => idb.setDefaultSchema(AppSchema), [])
10
-
11
- return <>{children}</>
12
- }
1
+ "use client"
2
+
3
+ import { useEffect } from "react"
4
+ import { idb } from "@skalfa/skalfa-idb"
5
+ import { AppSchema } from "@schema"
6
+
7
+
8
+ export function IDBProvider({ children }: { children: React.ReactNode }) {
9
+ useEffect(() => idb.setDefaultSchema(AppSchema), [])
10
+
11
+ return <>{children}</>
12
+ }
@@ -1,10 +1,10 @@
1
- /* eslint-disable jsx-a11y/alt-text */
2
-
3
-
4
- import Image, { ImageProps } from "next/image";
5
-
6
- export function ImageComponent(props: ImageProps) {
7
- const isProd = process.env.NODE_ENV === "production";
8
-
9
- return <Image {...props} unoptimized={!isProd} />;
1
+ /* eslint-disable jsx-a11y/alt-text */
2
+
3
+
4
+ import Image, { ImageProps } from "next/image";
5
+
6
+ export function ImageComponent(props: ImageProps) {
7
+ const isProd = process.env.NODE_ENV === "production";
8
+
9
+ return <Image {...props} unoptimized={!isProd} />;
10
10
  }