react-boxmodel-inspector 1.0.0

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.
@@ -0,0 +1,737 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ const C = {
5
+ margin: { fill: "rgba(232,168,56,0.09)", stroke: "rgba(232,168,56,0.75)", text: "#e8a838" },
6
+ padding: { fill: "rgba(93,168,93,0.09)", stroke: "rgba(93,168,93,0.75)", text: "#5da85d" },
7
+ content: { fill: "rgba(167,139,250,0.12)", stroke: "rgba(167,139,250,0.75)", text: "#a78bfa" }
8
+ };
9
+ function EL({ v, style, color }) {
10
+ if (!v) return null;
11
+ return /* @__PURE__ */ jsxs("div", { style: {
12
+ position: "absolute",
13
+ fontSize: 9,
14
+ fontFamily: "monospace",
15
+ fontWeight: 700,
16
+ color,
17
+ background: "rgba(0,0,0,0.65)",
18
+ border: `1px solid ${color}44`,
19
+ borderRadius: 3,
20
+ padding: "0 3px",
21
+ lineHeight: "15px",
22
+ pointerEvents: "none",
23
+ transform: "translate(-50%,-50%)",
24
+ whiteSpace: "nowrap",
25
+ zIndex: 100001,
26
+ ...style
27
+ }, children: [
28
+ v,
29
+ "px"
30
+ ] });
31
+ }
32
+ function BoxOverlay({ box, rect, tagName }) {
33
+ const { width: w, height: h, margin: m, padding: p, border: b } = box;
34
+ const f = (n) => n % 1 === 0 ? String(n) : n.toFixed(1);
35
+ const cW = Math.max(0, w - p.left - p.right - b.left - b.right);
36
+ const cH = Math.max(0, h - p.top - p.bottom - b.top - b.bottom);
37
+ const containerStyle = {
38
+ position: "fixed",
39
+ left: rect.left - m.left,
40
+ top: rect.top - m.top,
41
+ width: w + m.left + m.right,
42
+ height: h + m.top + m.bottom,
43
+ pointerEvents: "none",
44
+ zIndex: 1e5
45
+ };
46
+ return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
47
+ /* @__PURE__ */ jsx("div", { style: {
48
+ position: "absolute",
49
+ top: -18,
50
+ left: 0,
51
+ fontSize: 10,
52
+ fontWeight: 700,
53
+ fontFamily: "monospace",
54
+ letterSpacing: "0.06em",
55
+ color: "#fff",
56
+ background: "#4a90d9",
57
+ borderRadius: "4px 4px 4px 0",
58
+ padding: "1px 7px",
59
+ lineHeight: "17px",
60
+ zIndex: 100003,
61
+ pointerEvents: "none"
62
+ }, children: tagName }),
63
+ /* @__PURE__ */ jsxs("div", { style: {
64
+ position: "absolute",
65
+ top: 0,
66
+ left: 0,
67
+ width: "100%",
68
+ height: "100%",
69
+ background: C.margin.fill,
70
+ border: `1.5px dashed ${C.margin.stroke}`,
71
+ boxSizing: "border-box"
72
+ }, children: [
73
+ /* @__PURE__ */ jsx(EL, { v: f(m.top), color: C.margin.text, style: { top: m.top / 2, left: "50%" } }),
74
+ /* @__PURE__ */ jsx(EL, { v: f(m.bottom), color: C.margin.text, style: { top: m.top + h + m.bottom / 2, left: "50%" } }),
75
+ /* @__PURE__ */ jsx(EL, { v: f(m.left), color: C.margin.text, style: { top: "50%", left: m.left / 2 } }),
76
+ /* @__PURE__ */ jsx(EL, { v: f(m.right), color: C.margin.text, style: { top: "50%", left: m.left + w + m.right / 2 } })
77
+ ] }),
78
+ /* @__PURE__ */ jsxs("div", { style: {
79
+ position: "absolute",
80
+ top: m.top,
81
+ left: m.left,
82
+ width: w,
83
+ height: h,
84
+ background: C.padding.fill,
85
+ border: `1.5px dashed ${C.padding.stroke}`,
86
+ boxSizing: "border-box"
87
+ }, children: [
88
+ /* @__PURE__ */ jsx(EL, { v: f(p.top), color: C.padding.text, style: { top: p.top / 2, left: "50%" } }),
89
+ /* @__PURE__ */ jsx(EL, { v: f(p.bottom), color: C.padding.text, style: { top: h - p.bottom / 2, left: "50%" } }),
90
+ /* @__PURE__ */ jsx(EL, { v: f(p.left), color: C.padding.text, style: { top: "50%", left: p.left / 2 } }),
91
+ /* @__PURE__ */ jsx(EL, { v: f(p.right), color: C.padding.text, style: { top: "50%", left: w - p.right / 2 } })
92
+ ] }),
93
+ /* @__PURE__ */ jsx("div", { style: {
94
+ position: "absolute",
95
+ top: m.top + p.top + b.top,
96
+ left: m.left + p.left + b.left,
97
+ width: cW,
98
+ height: cH,
99
+ background: C.content.fill,
100
+ border: `1.5px solid ${C.content.stroke}`,
101
+ boxSizing: "border-box"
102
+ }, children: /* @__PURE__ */ jsxs("div", { style: {
103
+ position: "absolute",
104
+ bottom: 2,
105
+ right: 3,
106
+ fontSize: 9,
107
+ fontFamily: "monospace",
108
+ fontWeight: 700,
109
+ color: C.content.text
110
+ }, children: [
111
+ Math.round(cW),
112
+ "×",
113
+ Math.round(cH)
114
+ ] }) })
115
+ ] });
116
+ }
117
+ const EXTRA_GROUPS = [
118
+ {
119
+ key: "typography",
120
+ label: "Typography",
121
+ color: "#f472b6",
122
+ props: [
123
+ "fontFamily",
124
+ "fontSize",
125
+ "fontWeight",
126
+ "fontStyle",
127
+ "lineHeight",
128
+ "letterSpacing",
129
+ "textAlign",
130
+ "textDecoration",
131
+ "textTransform",
132
+ "whiteSpace",
133
+ "wordBreak"
134
+ ]
135
+ },
136
+ {
137
+ key: "color",
138
+ label: "Color & Background",
139
+ color: "#fb923c",
140
+ props: [
141
+ "color",
142
+ "backgroundColor",
143
+ "backgroundImage",
144
+ "opacity",
145
+ "visibility"
146
+ ]
147
+ },
148
+ {
149
+ key: "layout",
150
+ label: "Layout",
151
+ color: "#34d399",
152
+ props: [
153
+ "display",
154
+ "position",
155
+ "top",
156
+ "right",
157
+ "bottom",
158
+ "left",
159
+ "float",
160
+ "clear",
161
+ "zIndex",
162
+ "overflow",
163
+ "overflowX",
164
+ "overflowY"
165
+ ]
166
+ },
167
+ {
168
+ key: "flex",
169
+ label: "Flex / Grid",
170
+ color: "#60a5fa",
171
+ props: [
172
+ "flexDirection",
173
+ "flexWrap",
174
+ "justifyContent",
175
+ "alignItems",
176
+ "alignContent",
177
+ "flex",
178
+ "flexGrow",
179
+ "flexShrink",
180
+ "flexBasis",
181
+ "gap",
182
+ "gridTemplateColumns",
183
+ "gridTemplateRows",
184
+ "gridArea"
185
+ ]
186
+ },
187
+ {
188
+ key: "box",
189
+ label: "Box",
190
+ color: "#a78bfa",
191
+ props: [
192
+ "width",
193
+ "height",
194
+ "minWidth",
195
+ "maxWidth",
196
+ "minHeight",
197
+ "maxHeight",
198
+ "boxSizing",
199
+ "boxShadow",
200
+ "borderRadius",
201
+ "outline",
202
+ "cursor"
203
+ ]
204
+ },
205
+ {
206
+ key: "transform",
207
+ label: "Transform & Animation",
208
+ color: "#e879f9",
209
+ props: [
210
+ "transform",
211
+ "transition",
212
+ "animation",
213
+ "willChange",
214
+ "pointerEvents"
215
+ ]
216
+ }
217
+ ];
218
+ function camelToKebab(str) {
219
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
220
+ }
221
+ function PropRow({ name, value }) {
222
+ if (!value || value === "none" || value === "normal" || value === "auto" || value === "0px") return null;
223
+ return /* @__PURE__ */ jsxs("div", { style: {
224
+ display: "flex",
225
+ gap: 6,
226
+ alignItems: "baseline",
227
+ padding: "2px 0",
228
+ borderBottom: "1px solid rgba(255,255,255,0.04)"
229
+ }, children: [
230
+ /* @__PURE__ */ jsx("span", { style: {
231
+ fontSize: 10,
232
+ color: "rgba(255,255,255,0.35)",
233
+ fontFamily: "monospace",
234
+ flexShrink: 0,
235
+ minWidth: 140
236
+ }, children: camelToKebab(name) }),
237
+ /* @__PURE__ */ jsx(
238
+ "span",
239
+ {
240
+ style: {
241
+ fontSize: 10,
242
+ color: "#e2e8f0",
243
+ fontFamily: "monospace",
244
+ wordBreak: "break-all",
245
+ maxWidth: 180,
246
+ overflow: "hidden",
247
+ textOverflow: "ellipsis",
248
+ whiteSpace: "nowrap"
249
+ },
250
+ title: value,
251
+ children: value
252
+ }
253
+ )
254
+ ] });
255
+ }
256
+ function ExtraSection({ computed }) {
257
+ const [openGroups, setOpenGroups] = useState({});
258
+ const toggle = (key) => setOpenGroups((prev) => ({ ...prev, [key]: !prev[key] }));
259
+ return /* @__PURE__ */ jsx("div", { style: { marginTop: 8, borderTop: "1px solid rgba(255,255,255,0.07)", paddingTop: 8 }, children: EXTRA_GROUPS.map(({ key, label, color, props }) => {
260
+ const entries = props.map((p) => ({ name: p, value: computed[p] })).filter(({ value }) => value && value !== "none" && value !== "normal" && value !== "auto" && value !== "0px");
261
+ if (entries.length === 0) return null;
262
+ const open = openGroups[key];
263
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 4 }, children: [
264
+ /* @__PURE__ */ jsxs(
265
+ "div",
266
+ {
267
+ onClick: () => toggle(key),
268
+ style: {
269
+ display: "flex",
270
+ alignItems: "center",
271
+ gap: 6,
272
+ cursor: "pointer",
273
+ padding: "3px 0"
274
+ },
275
+ children: [
276
+ /* @__PURE__ */ jsx("span", { style: {
277
+ width: 7,
278
+ height: 7,
279
+ borderRadius: "50%",
280
+ background: color,
281
+ flexShrink: 0,
282
+ boxShadow: `0 0 4px ${color}88`
283
+ } }),
284
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.55)", flex: 1 }, children: label }),
285
+ /* @__PURE__ */ jsx("span", { style: {
286
+ fontSize: 10,
287
+ color: "rgba(255,255,255,0.25)",
288
+ marginRight: 2,
289
+ transition: "transform 0.15s",
290
+ display: "inline-block",
291
+ transform: open ? "rotate(180deg)" : "rotate(0deg)"
292
+ }, children: "▾" })
293
+ ]
294
+ }
295
+ ),
296
+ open && /* @__PURE__ */ jsx("div", { style: { paddingLeft: 13, paddingTop: 3 }, children: entries.map(({ name, value }) => /* @__PURE__ */ jsx(PropRow, { name, value }, name)) })
297
+ ] }, key);
298
+ }) });
299
+ }
300
+ function Panel({ box, tagName, initialPos, onClose }) {
301
+ const [pos, setPos] = useState(initialPos);
302
+ const [expanded, setExp] = useState(false);
303
+ const dragging = useRef(false);
304
+ const offset = useRef({ x: 0, y: 0 });
305
+ const onMouseDown = (e) => {
306
+ if (e.target.closest("[data-nodrag]")) return;
307
+ dragging.current = true;
308
+ offset.current = { x: e.clientX - pos.x, y: e.clientY - pos.y };
309
+ e.preventDefault();
310
+ };
311
+ useEffect(() => {
312
+ const move = (e) => {
313
+ if (!dragging.current) return;
314
+ setPos({ x: e.clientX - offset.current.x, y: e.clientY - offset.current.y });
315
+ };
316
+ const up = () => {
317
+ dragging.current = false;
318
+ };
319
+ window.addEventListener("mousemove", move);
320
+ window.addEventListener("mouseup", up);
321
+ return () => {
322
+ window.removeEventListener("mousemove", move);
323
+ window.removeEventListener("mouseup", up);
324
+ };
325
+ }, []);
326
+ if (!box) return null;
327
+ const { margin: m, padding: p, border: b, width, height, computed } = box;
328
+ const f = (n) => n % 1 === 0 ? String(n) : n.toFixed(1);
329
+ const cW = Math.round(Math.max(0, width - p.left - p.right - b.left - b.right));
330
+ const cH = Math.round(Math.max(0, height - p.top - p.bottom - b.top - b.bottom));
331
+ const rows = [
332
+ { key: "margin", color: C.margin.text, label: "margin", vals: [m.top, m.right, m.bottom, m.left] },
333
+ { key: "border", color: "#4a90d9", label: "border", vals: [b.top, b.right, b.bottom, b.left] },
334
+ { key: "padding", color: C.padding.text, label: "padding", vals: [p.top, p.right, p.bottom, p.left] }
335
+ ];
336
+ return /* @__PURE__ */ jsxs(
337
+ "div",
338
+ {
339
+ onMouseDown,
340
+ style: {
341
+ position: "fixed",
342
+ left: pos.x,
343
+ top: pos.y,
344
+ zIndex: 999999,
345
+ cursor: "grab",
346
+ userSelect: "none",
347
+ background: "rgba(10,11,16,0.97)",
348
+ border: "1px solid rgba(255,255,255,0.12)",
349
+ borderRadius: 8,
350
+ padding: "10px 14px 10px",
351
+ minWidth: 260,
352
+ maxWidth: 340,
353
+ fontFamily: "'SF Mono','Fira Code','Cascadia Code',monospace",
354
+ boxShadow: "0 8px 32px rgba(0,0,0,0.5), 0 2px 8px rgba(0,0,0,0.3)",
355
+ whiteSpace: "nowrap",
356
+ maxHeight: "80vh",
357
+ overflowY: "auto",
358
+ scrollbarWidth: "thin",
359
+ scrollbarColor: "#333 transparent"
360
+ },
361
+ children: [
362
+ /* @__PURE__ */ jsxs("div", { style: {
363
+ display: "flex",
364
+ justifyContent: "space-between",
365
+ alignItems: "center",
366
+ marginBottom: 8,
367
+ paddingBottom: 7,
368
+ borderBottom: "1px solid rgba(255,255,255,0.08)"
369
+ }, children: [
370
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
371
+ /* @__PURE__ */ jsx("span", { style: {
372
+ fontSize: 10,
373
+ fontWeight: 700,
374
+ color: "#4a90d9",
375
+ background: "rgba(74,144,217,0.15)",
376
+ border: "1px solid rgba(74,144,217,0.35)",
377
+ borderRadius: 4,
378
+ padding: "0 6px",
379
+ lineHeight: "18px"
380
+ }, children: tagName }),
381
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", color: "rgba(255,255,255,0.28)" }, children: "box model" })
382
+ ] }),
383
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
384
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 9, color: "rgba(255,255,255,0.18)" }, children: "T · R · B · L" }),
385
+ /* @__PURE__ */ jsx(
386
+ "span",
387
+ {
388
+ "data-nodrag": "true",
389
+ onClick: onClose,
390
+ title: "Close (ESC)",
391
+ style: {
392
+ cursor: "pointer",
393
+ color: "rgba(255,255,255,0.35)",
394
+ fontSize: 14,
395
+ lineHeight: 1,
396
+ padding: "0 2px",
397
+ borderRadius: 3,
398
+ userSelect: "none"
399
+ },
400
+ onMouseEnter: (e) => e.currentTarget.style.color = "#ff6b6b",
401
+ onMouseLeave: (e) => e.currentTarget.style.color = "rgba(255,255,255,0.35)",
402
+ children: "✕"
403
+ }
404
+ )
405
+ ] })
406
+ ] }),
407
+ rows.map(({ key, color, label: rowLabel, vals }) => {
408
+ const allZero = vals.every((v) => v === 0);
409
+ return /* @__PURE__ */ jsxs("div", { style: {
410
+ display: "flex",
411
+ alignItems: "center",
412
+ gap: 8,
413
+ marginBottom: 5,
414
+ opacity: allZero ? 0.25 : 1
415
+ }, children: [
416
+ /* @__PURE__ */ jsx("span", { style: {
417
+ width: 7,
418
+ height: 7,
419
+ borderRadius: "50%",
420
+ background: color,
421
+ flexShrink: 0,
422
+ boxShadow: `0 0 5px ${color}88`
423
+ } }),
424
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.35)", width: 46, flexShrink: 0 }, children: rowLabel }),
425
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 3 }, children: vals.map((v, i) => /* @__PURE__ */ jsx("span", { style: {
426
+ fontSize: 11,
427
+ fontWeight: 600,
428
+ color: v === 0 ? "rgba(255,255,255,0.18)" : color,
429
+ background: v === 0 ? "transparent" : `${color}18`,
430
+ border: `1px solid ${v === 0 ? "rgba(255,255,255,0.05)" : `${color}44`}`,
431
+ borderRadius: 4,
432
+ padding: "1px 5px",
433
+ minWidth: 32,
434
+ textAlign: "center"
435
+ }, children: f(v) }, i)) })
436
+ ] }, key);
437
+ }),
438
+ /* @__PURE__ */ jsxs("div", { style: {
439
+ display: "flex",
440
+ alignItems: "center",
441
+ gap: 8,
442
+ marginTop: 8,
443
+ paddingTop: 7,
444
+ borderTop: "1px solid rgba(255,255,255,0.07)"
445
+ }, children: [
446
+ /* @__PURE__ */ jsx("span", { style: {
447
+ width: 7,
448
+ height: 7,
449
+ borderRadius: 2,
450
+ background: C.content.text,
451
+ flexShrink: 0,
452
+ boxShadow: `0 0 5px ${C.content.text}88`
453
+ } }),
454
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.35)", width: 46 }, children: "content" }),
455
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, fontWeight: 600, color: C.content.text }, children: [
456
+ cW,
457
+ " × ",
458
+ cH,
459
+ " px"
460
+ ] })
461
+ ] }),
462
+ /* @__PURE__ */ jsxs(
463
+ "div",
464
+ {
465
+ "data-nodrag": "true",
466
+ onClick: () => setExp((v) => !v),
467
+ style: {
468
+ display: "flex",
469
+ alignItems: "center",
470
+ justifyContent: "center",
471
+ gap: 5,
472
+ marginTop: 10,
473
+ paddingTop: 8,
474
+ borderTop: "1px solid rgba(255,255,255,0.07)",
475
+ cursor: "pointer",
476
+ color: "rgba(255,255,255,0.35)",
477
+ fontSize: 10,
478
+ letterSpacing: "0.05em"
479
+ },
480
+ children: [
481
+ /* @__PURE__ */ jsx("span", { children: "All properties" }),
482
+ /* @__PURE__ */ jsx("span", { style: {
483
+ display: "inline-block",
484
+ transition: "transform 0.2s",
485
+ transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
486
+ fontSize: 11
487
+ }, children: "▾" })
488
+ ]
489
+ }
490
+ ),
491
+ expanded && computed && /* @__PURE__ */ jsx(ExtraSection, { computed }),
492
+ /* @__PURE__ */ jsx("div", { style: {
493
+ marginTop: 8,
494
+ paddingTop: 6,
495
+ borderTop: "1px solid rgba(255,255,255,0.05)",
496
+ fontSize: 9,
497
+ color: "rgba(255,255,255,0.12)",
498
+ textAlign: "center",
499
+ letterSpacing: "0.05em"
500
+ }, children: "⠿ drag to move · ESC to close" })
501
+ ]
502
+ }
503
+ );
504
+ }
505
+ function LongPressIndicator({ x, y, progress }) {
506
+ if (progress === 0) return null;
507
+ return createPortal(
508
+ /* @__PURE__ */ jsx("div", { style: {
509
+ position: "fixed",
510
+ left: x,
511
+ top: y,
512
+ width: 0,
513
+ height: 0,
514
+ pointerEvents: "none",
515
+ zIndex: 99999
516
+ }, children: /* @__PURE__ */ jsx("div", { style: {
517
+ position: "absolute",
518
+ top: "50%",
519
+ left: "50%",
520
+ width: 60,
521
+ height: 60,
522
+ borderRadius: "50%",
523
+ background: `rgba(74,144,217,${0.1 + progress * 0.2})`,
524
+ border: `2px solid rgba(74,144,217,${0.3 + progress * 0.5})`,
525
+ transform: `translate(-50%, -50%) scale(${progress})`,
526
+ transition: "transform 0.05s linear"
527
+ } }) }),
528
+ document.body
529
+ );
530
+ }
531
+ function BoxModelInspector({
532
+ children,
533
+ longPressDuration = 500,
534
+ toggleKey = "`"
535
+ }) {
536
+ const [active, setActive] = useState(false);
537
+ const [target, setTarget] = useState(null);
538
+ const [box, setBox] = useState(null);
539
+ const [rect, setRect] = useState(null);
540
+ const [panelPos, setPanelPos] = useState(null);
541
+ const [longPressState, setLongPressState] = useState({ active: false, x: 0, y: 0, progress: 0 });
542
+ const timerRef = useRef(null);
543
+ const progressIntervalRef = useRef(null);
544
+ const readBox = (el) => {
545
+ if (!el) return;
546
+ const s = getComputedStyle(el);
547
+ const r = el.getBoundingClientRect();
548
+ const computed = {};
549
+ EXTRA_GROUPS.forEach(({ props }) => {
550
+ props.forEach((prop) => {
551
+ computed[prop] = s[prop];
552
+ });
553
+ });
554
+ setBox({
555
+ width: r.width,
556
+ height: r.height,
557
+ margin: {
558
+ top: parseFloat(s.marginTop),
559
+ right: parseFloat(s.marginRight),
560
+ bottom: parseFloat(s.marginBottom),
561
+ left: parseFloat(s.marginLeft)
562
+ },
563
+ padding: {
564
+ top: parseFloat(s.paddingTop),
565
+ right: parseFloat(s.paddingRight),
566
+ bottom: parseFloat(s.paddingBottom),
567
+ left: parseFloat(s.paddingLeft)
568
+ },
569
+ border: {
570
+ top: parseFloat(s.borderTopWidth),
571
+ right: parseFloat(s.borderRightWidth),
572
+ bottom: parseFloat(s.borderBottomWidth),
573
+ left: parseFloat(s.borderLeftWidth)
574
+ },
575
+ computed
576
+ });
577
+ setRect(r);
578
+ setPanelPos({
579
+ x: Math.min(r.right + 24, window.innerWidth - 320),
580
+ y: Math.max(20, r.top)
581
+ });
582
+ };
583
+ const handlePointerDown = (e) => {
584
+ if (!active) return;
585
+ if (e.target.closest("[data-boxmodel-ui]")) return;
586
+ const el = e.target;
587
+ setLongPressState({
588
+ active: true,
589
+ x: e.clientX,
590
+ y: e.clientY,
591
+ progress: 0
592
+ });
593
+ const startTime = Date.now();
594
+ progressIntervalRef.current = setInterval(() => {
595
+ const elapsed = Date.now() - startTime;
596
+ const progress = Math.min(elapsed / longPressDuration, 1);
597
+ setLongPressState((prev) => ({ ...prev, progress }));
598
+ }, 16);
599
+ timerRef.current = setTimeout(() => {
600
+ setLongPressState({ active: false, x: 0, y: 0, progress: 0 });
601
+ clearInterval(progressIntervalRef.current);
602
+ setTarget(el);
603
+ readBox(el);
604
+ }, longPressDuration);
605
+ };
606
+ const handlePointerUp = () => {
607
+ if (timerRef.current) {
608
+ clearTimeout(timerRef.current);
609
+ timerRef.current = null;
610
+ }
611
+ if (progressIntervalRef.current) {
612
+ clearInterval(progressIntervalRef.current);
613
+ progressIntervalRef.current = null;
614
+ }
615
+ setLongPressState({ active: false, x: 0, y: 0, progress: 0 });
616
+ };
617
+ const handlePointerMove = (e) => {
618
+ if (!longPressState.active) return;
619
+ const dx = e.clientX - longPressState.x;
620
+ const dy = e.clientY - longPressState.y;
621
+ if (Math.sqrt(dx * dx + dy * dy) > 10) {
622
+ handlePointerUp();
623
+ }
624
+ };
625
+ const close = () => {
626
+ setTarget(null);
627
+ setBox(null);
628
+ setRect(null);
629
+ setPanelPos(null);
630
+ };
631
+ useEffect(() => {
632
+ const handleKey = (e) => {
633
+ if (e.key === "Escape" && target) {
634
+ close();
635
+ } else if (e.key === toggleKey && !e.repeat) {
636
+ setActive((v) => {
637
+ const newVal = !v;
638
+ if (!newVal && target) close();
639
+ return newVal;
640
+ });
641
+ }
642
+ };
643
+ window.addEventListener("keydown", handleKey);
644
+ return () => window.removeEventListener("keydown", handleKey);
645
+ }, [target, toggleKey]);
646
+ useEffect(() => {
647
+ if (!target) return;
648
+ const update = () => readBox(target);
649
+ const ro = new ResizeObserver(update);
650
+ ro.observe(target);
651
+ const mo = new MutationObserver(update);
652
+ mo.observe(target, { attributes: true, attributeFilter: ["style", "class"] });
653
+ const handleScroll = () => update();
654
+ const handleResize = () => update();
655
+ window.addEventListener("scroll", handleScroll, { passive: true });
656
+ window.addEventListener("resize", handleResize);
657
+ return () => {
658
+ ro.disconnect();
659
+ mo.disconnect();
660
+ window.removeEventListener("scroll", handleScroll);
661
+ window.removeEventListener("resize", handleResize);
662
+ };
663
+ }, [target]);
664
+ useEffect(() => {
665
+ if (!active) {
666
+ handlePointerUp();
667
+ return;
668
+ }
669
+ window.addEventListener("pointerdown", handlePointerDown, true);
670
+ window.addEventListener("pointerup", handlePointerUp, true);
671
+ window.addEventListener("pointermove", handlePointerMove, true);
672
+ return () => {
673
+ window.removeEventListener("pointerdown", handlePointerDown, true);
674
+ window.removeEventListener("pointerup", handlePointerUp, true);
675
+ window.removeEventListener("pointermove", handlePointerMove, true);
676
+ };
677
+ });
678
+ const tagName = target ? `${target.tagName.toLowerCase()}${target.id ? `#${target.id}` : ""}${target.className ? `.${target.className.split(" ")[0]}` : ""}` : "";
679
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
680
+ children,
681
+ active && createPortal(
682
+ /* @__PURE__ */ jsxs(
683
+ "div",
684
+ {
685
+ "data-boxmodel-ui": "true",
686
+ style: {
687
+ position: "fixed",
688
+ top: 12,
689
+ left: "50%",
690
+ transform: "translateX(-50%)",
691
+ zIndex: 999998,
692
+ background: "rgba(74,144,217,0.95)",
693
+ color: "#fff",
694
+ fontSize: 11,
695
+ fontFamily: "monospace",
696
+ padding: "6px 14px",
697
+ borderRadius: 6,
698
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
699
+ pointerEvents: "none",
700
+ letterSpacing: "0.03em"
701
+ },
702
+ children: [
703
+ "🔍 Inspector active · Long-press any element · Press ",
704
+ /* @__PURE__ */ jsx("kbd", { style: {
705
+ background: "rgba(255,255,255,0.2)",
706
+ padding: "1px 5px",
707
+ borderRadius: 3,
708
+ fontWeight: 700
709
+ }, children: toggleKey }),
710
+ " to toggle"
711
+ ]
712
+ }
713
+ ),
714
+ document.body
715
+ ),
716
+ longPressState.progress > 0 && /* @__PURE__ */ jsx(
717
+ LongPressIndicator,
718
+ {
719
+ x: longPressState.x,
720
+ y: longPressState.y,
721
+ progress: longPressState.progress
722
+ }
723
+ ),
724
+ target && box && rect && createPortal(
725
+ /* @__PURE__ */ jsx("div", { "data-boxmodel-ui": "true", children: /* @__PURE__ */ jsx(BoxOverlay, { box, rect, tagName }) }),
726
+ document.body
727
+ ),
728
+ target && box && panelPos && createPortal(
729
+ /* @__PURE__ */ jsx("div", { "data-boxmodel-ui": "true", children: /* @__PURE__ */ jsx(Panel, { box, tagName, initialPos: panelPos, onClose: close }) }),
730
+ document.body
731
+ )
732
+ ] });
733
+ }
734
+ export {
735
+ BoxModelInspector
736
+ };
737
+ //# sourceMappingURL=react-boxmodel-inspector.es.js.map