@refinedev/devtools 1.1.37 → 1.2.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 (72) hide show
  1. package/dist/components/apply-styles.d.cts +6 -0
  2. package/dist/components/apply-styles.d.cts.map +1 -0
  3. package/dist/components/apply-styles.d.mts +6 -0
  4. package/dist/components/apply-styles.d.mts.map +1 -0
  5. package/dist/components/apply-styles.d.ts +6 -0
  6. package/dist/components/apply-styles.d.ts.map +1 -0
  7. package/dist/components/devtools-pin.d.cts +4 -2
  8. package/dist/components/devtools-pin.d.cts.map +1 -1
  9. package/dist/components/devtools-pin.d.mts +4 -2
  10. package/dist/components/devtools-pin.d.mts.map +1 -1
  11. package/dist/components/devtools-pin.d.ts +4 -2
  12. package/dist/components/devtools-pin.d.ts.map +1 -1
  13. package/dist/components/devtools-selector.d.cts +3 -2
  14. package/dist/components/devtools-selector.d.cts.map +1 -1
  15. package/dist/components/devtools-selector.d.mts +3 -2
  16. package/dist/components/devtools-selector.d.mts.map +1 -1
  17. package/dist/components/devtools-selector.d.ts +3 -2
  18. package/dist/components/devtools-selector.d.ts.map +1 -1
  19. package/dist/components/icons/selector-button.d.cts +1 -0
  20. package/dist/components/icons/selector-button.d.cts.map +1 -1
  21. package/dist/components/icons/selector-button.d.mts +1 -0
  22. package/dist/components/icons/selector-button.d.mts.map +1 -1
  23. package/dist/components/icons/selector-button.d.ts +1 -0
  24. package/dist/components/icons/selector-button.d.ts.map +1 -1
  25. package/dist/components/selectable-elements.d.cts +8 -0
  26. package/dist/components/selectable-elements.d.cts.map +1 -0
  27. package/dist/components/selectable-elements.d.mts +8 -0
  28. package/dist/components/selectable-elements.d.mts.map +1 -0
  29. package/dist/components/selectable-elements.d.ts +8 -0
  30. package/dist/components/selectable-elements.d.ts.map +1 -0
  31. package/dist/index.cjs +94 -2
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +2 -2
  34. package/dist/index.d.cts.map +1 -1
  35. package/dist/index.d.mts +2 -2
  36. package/dist/index.d.mts.map +1 -1
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.mjs +94 -2
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/panel.d.cts.map +1 -1
  42. package/dist/panel.d.mts.map +1 -1
  43. package/dist/panel.d.ts.map +1 -1
  44. package/dist/utilities/use-selector.d.cts +6 -7
  45. package/dist/utilities/use-selector.d.cts.map +1 -1
  46. package/dist/utilities/use-selector.d.mts +6 -7
  47. package/dist/utilities/use-selector.d.mts.map +1 -1
  48. package/dist/utilities/use-selector.d.ts +6 -7
  49. package/dist/utilities/use-selector.d.ts.map +1 -1
  50. package/package.json +5 -4
  51. package/src/components/apply-styles.tsx +19 -0
  52. package/src/components/devtools-pin.tsx +37 -26
  53. package/src/components/devtools-selector.tsx +34 -76
  54. package/src/components/icons/selector-button.tsx +18 -0
  55. package/src/components/selectable-elements.tsx +255 -0
  56. package/src/index.ts +2 -2
  57. package/src/panel.tsx +13 -2
  58. package/src/utilities/use-selector.tsx +74 -183
  59. package/dist/components/selector-box.d.cts +0 -8
  60. package/dist/components/selector-box.d.cts.map +0 -1
  61. package/dist/components/selector-box.d.mts +0 -8
  62. package/dist/components/selector-box.d.mts.map +0 -1
  63. package/dist/components/selector-box.d.ts +0 -8
  64. package/dist/components/selector-box.d.ts.map +0 -1
  65. package/dist/components/selector-hint.d.cts +0 -4
  66. package/dist/components/selector-hint.d.cts.map +0 -1
  67. package/dist/components/selector-hint.d.mts +0 -4
  68. package/dist/components/selector-hint.d.mts.map +0 -1
  69. package/dist/components/selector-hint.d.ts +0 -4
  70. package/dist/components/selector-hint.d.ts.map +0 -1
  71. package/src/components/selector-box.tsx +0 -107
  72. package/src/components/selector-hint.tsx +0 -57
@@ -1,109 +1,67 @@
1
1
  import React from "react";
2
- import { createPortal } from "react-dom";
3
2
  import { useSelector } from "src/utilities/use-selector";
4
- import { SelectorBox } from "./selector-box";
5
- import { SelectorHint } from "./selector-hint";
3
+ import { ApplyStyles } from "./apply-styles";
4
+ import { SelectableElements } from "./selectable-elements";
6
5
 
7
6
  type Props = {
8
- onSelectorOpen: () => void;
7
+ active: boolean;
8
+ setActive: React.Dispatch<React.SetStateAction<boolean>>;
9
9
  onHighlight: (name: string) => void;
10
10
  icon?: React.ReactNode;
11
11
  style?: React.CSSProperties;
12
12
  };
13
13
 
14
14
  export const DevtoolsSelector = ({
15
- onSelectorOpen,
15
+ active,
16
+ setActive,
16
17
  onHighlight,
17
18
  icon,
18
19
  style,
19
20
  }: Props) => {
20
- const [hover, setHover] = React.useState(false);
21
- const [active, setActive] = React.useState(false);
22
- const { rect, name } = useSelector(active);
21
+ const { selectableElements } = useSelector(active);
23
22
 
24
- const [selectorBoxRoot, setSelectorBoxRoot] =
25
- React.useState<HTMLElement | null>(null);
26
-
27
- React.useEffect(() => {
28
- if (!selectorBoxRoot) {
29
- const element = document.createElement("div");
30
- element.id = "selector-box-root";
31
-
32
- document.body.appendChild(element);
33
-
34
- setSelectorBoxRoot(element);
35
- }
36
- }, []);
37
-
38
- React.useEffect(() => {
39
- if (active) {
40
- document.body.style.cursor = "crosshair";
41
- } else {
42
- document.body.style.cursor = "default";
43
- }
44
- }, [active]);
45
-
46
- React.useEffect(() => {
47
- const onMouseClick = (e: MouseEvent) => {
48
- if (!active) return;
49
- if (!name) return;
50
-
51
- e?.preventDefault();
52
- e?.stopPropagation();
53
- e.stopImmediatePropagation();
54
- onHighlight(name);
55
- setActive(false);
56
- };
57
-
58
- if (active) {
59
- document.addEventListener("click", onMouseClick, {
60
- capture: true,
61
- });
62
-
63
- return () => {
64
- document.removeEventListener("click", onMouseClick, {
65
- capture: true,
66
- });
67
- };
68
- }
69
-
70
- return () => 0;
71
- }, [name, onHighlight, active]);
72
-
73
- React.useEffect(() => {
74
- if (active) {
75
- onSelectorOpen();
76
- }
77
- }, [active, onSelectorOpen]);
23
+ const onSelect = (name: string) => {
24
+ onHighlight(name);
25
+ setActive(false);
26
+ };
78
27
 
79
28
  return (
80
29
  <div style={style}>
30
+ {/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
81
31
  <div
82
32
  role="button"
83
33
  title="Element Selector"
84
- onMouseOver={() => setHover(true)}
85
- onMouseOut={() => setHover(false)}
34
+ className="refine-devtools-selector-button"
86
35
  onClick={(event) => {
87
36
  event.preventDefault();
88
37
  event.stopPropagation();
89
38
  (document?.activeElement as HTMLElement)?.blur();
90
39
  setActive((active) => !active);
91
40
  }}
92
- style={{
93
- padding: 0,
94
- margin: 0,
95
- height: "100%",
96
- width: "100%",
97
- transform: hover ? "rotate(180deg)" : "rotate(0deg)",
98
- transition: "transform 0.2s ease-in-out",
99
- }}
100
41
  >
101
42
  {icon}
102
43
  </div>
103
- <SelectorHint active={active} />
104
- {active &&
105
- selectorBoxRoot &&
106
- createPortal(<SelectorBox {...rect} name={name} />, selectorBoxRoot)}
44
+ {active && (
45
+ <SelectableElements elements={selectableElements} onSelect={onSelect} />
46
+ )}
47
+ <ApplyStyles>
48
+ {
49
+ /* css */ `
50
+ .refine-devtools-selector-button {
51
+ padding: 0;
52
+ margin: 0;
53
+ height: 100%;
54
+ width: 100%;
55
+ transform: rotate(0deg);
56
+ transition: transform 0.2s ease-in-out;
57
+ }
58
+
59
+ .refine-devtools-selector-button:hover {
60
+ transform: rotate(180deg);
61
+ }
62
+ `
63
+ }
64
+ </ApplyStyles>
107
65
  </div>
108
66
  );
109
67
  };
@@ -17,3 +17,21 @@ export const SelectorButtonIcon = (props: React.SVGProps<SVGSVGElement>) => (
17
17
  />
18
18
  </svg>
19
19
  );
20
+
21
+ export const SelectorIcon = (props: React.SVGProps<SVGSVGElement>) => (
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ width={16}
25
+ height={16}
26
+ viewBox="0 0 16 16"
27
+ fill="none"
28
+ {...props}
29
+ >
30
+ <path
31
+ fill="#14141F"
32
+ fillRule="evenodd"
33
+ d="M9 1a1 1 0 0 0-2 0v2.1A5.006 5.006 0 0 0 3.1 7H1a1 1 0 0 0 0 2h2.1A5.006 5.006 0 0 0 7 12.9V15a1 1 0 1 0 2 0v-2.1A5.006 5.006 0 0 0 12.9 9H15a1 1 0 1 0 0-2h-2.1A5.006 5.006 0 0 0 9 3.1V1Zm2 7a3 3 0 1 0-6 0 3 3 0 0 0 6 0Z"
34
+ clipRule="evenodd"
35
+ />
36
+ </svg>
37
+ );
@@ -0,0 +1,255 @@
1
+ import React from "react";
2
+ import debounce from "lodash/debounce";
3
+ import { createPortal } from "react-dom";
4
+ import { ApplyStyles } from "./apply-styles";
5
+ import { SelectorIcon } from "./icons/selector-button";
6
+
7
+ const SelectableElement = ({
8
+ element,
9
+ name,
10
+ onSelect,
11
+ }: {
12
+ element: HTMLElement;
13
+ name: string;
14
+ onSelect: (name: string) => void;
15
+ }) => {
16
+ const [position] = React.useState(() => {
17
+ const { top, left, width, height } = element.getBoundingClientRect();
18
+ const { scrollLeft, scrollTop } = document.documentElement;
19
+ const positionLeft = left + scrollLeft;
20
+ const positionTop = top + scrollTop;
21
+
22
+ return { left: positionLeft, top: positionTop, width, height };
23
+ });
24
+
25
+ const elementRef = React.useRef<HTMLButtonElement | null>(null);
26
+
27
+ React.useEffect(() => {
28
+ // use scroll event listener
29
+ const onScroll = debounce(
30
+ () => {
31
+ const { top, left, width, height } = element.getBoundingClientRect();
32
+ const { scrollLeft, scrollTop } = document.documentElement;
33
+ const positionLeft = left + scrollLeft;
34
+ const positionTop = top + scrollTop;
35
+
36
+ elementRef.current?.style.setProperty("left", `${positionLeft}px`);
37
+ elementRef.current?.style.setProperty("top", `${positionTop}px`);
38
+ elementRef.current?.style.setProperty("width", `${width}px`);
39
+ elementRef.current?.style.setProperty("height", `${height}px`);
40
+ elementRef.current?.style.setProperty("opacity", "1");
41
+ },
42
+ 200,
43
+ {
44
+ leading: false,
45
+ trailing: true,
46
+ },
47
+ );
48
+
49
+ const opacityOnScroll = debounce(
50
+ () => {
51
+ elementRef.current?.style.setProperty("opacity", "0");
52
+ },
53
+ 200,
54
+ {
55
+ leading: true,
56
+ trailing: false,
57
+ },
58
+ );
59
+
60
+ document.addEventListener("scroll", onScroll);
61
+ document.addEventListener("scroll", opacityOnScroll);
62
+
63
+ return () => {
64
+ document.removeEventListener("scroll", onScroll);
65
+ document.removeEventListener("scroll", opacityOnScroll);
66
+ };
67
+ }, [element]);
68
+
69
+ const placement = React.useMemo(() => {
70
+ const tooltipBaseSize = { width: 22, height: 22 };
71
+ const nameWidth = name.length * 7.5;
72
+ const tooltipSize = {
73
+ width: tooltipBaseSize.width + nameWidth,
74
+ height: tooltipBaseSize.height,
75
+ };
76
+ const gap = 4;
77
+
78
+ // outside top start
79
+ if (
80
+ position.top - tooltipSize.height > 0 &&
81
+ position.left + tooltipSize.width < window.innerWidth &&
82
+ position.width > tooltipSize.width
83
+ ) {
84
+ return { left: gap / 2, top: tooltipSize.height * -1 - gap };
85
+ }
86
+ // inside top start
87
+ if (
88
+ position.height >= tooltipSize.height * 1.5 &&
89
+ position.width >= tooltipSize.width * 1.5
90
+ ) {
91
+ return { left: 0 + gap, top: 0 + gap };
92
+ }
93
+ // outside left start
94
+ if (position.left - tooltipSize.width > 0) {
95
+ return { right: position.width + gap, top: 0 - 1 };
96
+ }
97
+ // outside right start
98
+ if (
99
+ position.left + position.width + tooltipSize.width <
100
+ window.innerWidth
101
+ ) {
102
+ return { left: position.width + gap, top: 0 - 1 };
103
+ }
104
+ // outside bottom start
105
+ if (
106
+ position.top + position.height + tooltipSize.height <
107
+ document.documentElement.scrollHeight
108
+ ) {
109
+ return { left: 0 - 1, top: position.height + gap };
110
+ }
111
+
112
+ return { left: 0, top: 0 };
113
+ }, [position, name.length]);
114
+
115
+ return (
116
+ <button
117
+ type="button"
118
+ className="selector-xray-box"
119
+ onClick={(event) => {
120
+ event?.preventDefault();
121
+ event?.stopPropagation();
122
+ onSelect(name);
123
+ }}
124
+ ref={elementRef}
125
+ style={{
126
+ position: "absolute",
127
+ ...position,
128
+ }}
129
+ >
130
+ <div
131
+ style={{
132
+ position: "absolute",
133
+ ...placement,
134
+ }}
135
+ className="selector-xray-info"
136
+ >
137
+ <span className="selector-xray-info-icon">
138
+ <SelectorIcon
139
+ width={12}
140
+ height={12}
141
+ style={{ pointerEvents: "none" }}
142
+ />
143
+ </span>
144
+ <span className="selector-xray-info-title">{` ${name}`}</span>
145
+ </div>
146
+ </button>
147
+ );
148
+ };
149
+
150
+ export const SelectableElements = ({
151
+ elements,
152
+ onSelect,
153
+ }: {
154
+ elements: Array<{ element: HTMLElement; name: string }>;
155
+ onSelect: (name: string) => void;
156
+ }) => {
157
+ const [selectorBoxRoot, setSelectorBoxRoot] =
158
+ React.useState<HTMLElement | null>(null);
159
+
160
+ React.useEffect(() => {
161
+ if (!selectorBoxRoot) {
162
+ const element = document.createElement("div");
163
+ element.id = "selector-box-root";
164
+
165
+ document.body.appendChild(element);
166
+
167
+ setSelectorBoxRoot(element);
168
+
169
+ return () => {
170
+ document.body.removeChild(element);
171
+ setSelectorBoxRoot(null);
172
+ };
173
+ }
174
+
175
+ return () => 0;
176
+ }, []);
177
+
178
+ if (!selectorBoxRoot) return null;
179
+
180
+ return (
181
+ <>
182
+ {createPortal(
183
+ elements.map((element, idx) => (
184
+ <SelectableElement
185
+ key={`selector-element-${idx}-${element.name}`}
186
+ {...element}
187
+ onSelect={onSelect}
188
+ />
189
+ )),
190
+ selectorBoxRoot,
191
+ )}
192
+ <ApplyStyles>
193
+ {
194
+ /* css */ `
195
+ .selector-xray-box {
196
+ display: flex;
197
+ margin: 0;
198
+ padding: 0;
199
+ appearance: none;
200
+ z-index: 9999;
201
+ border: 2px dashed #47EBEB;
202
+ border-radius: 6px;
203
+ background: rgba(71, 235, 235, 0.01);
204
+ transition: opacity 0.2s ease-in-out;
205
+ cursor: crosshair;
206
+ }
207
+
208
+ .selector-xray-info {
209
+ display: flex;
210
+ justify-content: center;
211
+ align-items: center;
212
+
213
+ z-index: 10;
214
+
215
+ padding: 3px 0;
216
+ min-width: 22px;
217
+ height: 22px;
218
+
219
+ color: #14141F;
220
+ background: #47EBEB;
221
+
222
+ font-size: 12px;
223
+ line-height: 16px;
224
+ font-family: monospace;
225
+ border-radius: 11px;
226
+ }
227
+
228
+ .selector-xray-info-icon {
229
+ display: flex;
230
+ min-width: 22px;
231
+ justify-content: center;
232
+ align-items: center;
233
+ flex-shrink: 0;
234
+ }
235
+
236
+ .selector-xray-info-title {
237
+ display: block;
238
+ max-width: 0;
239
+ overflow: hidden;
240
+ transition-property: max-width, padding-right;
241
+ transition-duration: 0.2s;
242
+ transition-timing-function: ease-in-out;
243
+ transition-delay: 0.1s;
244
+ }
245
+
246
+ .selector-xray-box:hover .selector-xray-info-title {
247
+ max-width: 200px;
248
+ padding-right: 8px;
249
+ }
250
+ `
251
+ }
252
+ </ApplyStyles>
253
+ </>
254
+ );
255
+ };
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { DevtoolsPanel } from "./panel";
2
- export { DevtoolsProvider } from "./provider";
1
+ export { DevtoolsPanel } from "./panel.js";
2
+ export { DevtoolsProvider } from "./provider.js";
package/src/panel.tsx CHANGED
@@ -19,6 +19,7 @@ export const DevtoolsPanel =
19
19
  const [placement] = React.useState<Placement>("bottom");
20
20
  const { devtoolsUrl, ws } = React.useContext(DevToolsContext);
21
21
  const [width, setWidth] = React.useState<number>(0);
22
+ const [selectorActive, setSelectorActive] = React.useState(false);
22
23
 
23
24
  const onSelectorHighlight = React.useCallback(
24
25
  (name: string) => {
@@ -36,6 +37,12 @@ export const DevtoolsPanel =
36
37
  setVisible(false);
37
38
  }, []);
38
39
 
40
+ React.useEffect(() => {
41
+ if (selectorActive) {
42
+ setVisible(false);
43
+ }
44
+ }, [selectorActive]);
45
+
39
46
  React.useEffect(() => {
40
47
  if (typeof window !== "undefined") {
41
48
  setBrowser(true);
@@ -76,9 +83,13 @@ export const DevtoolsPanel =
76
83
  }}
77
84
  >
78
85
  <DevtoolsPin
79
- onClick={() => setVisible((v) => !v)}
86
+ onClick={() => {
87
+ setVisible((v) => !v);
88
+ setSelectorActive(false);
89
+ }}
80
90
  onSelectorHighlight={onSelectorHighlight}
81
- onSelectorOpen={onSelectorOpen}
91
+ selectorActive={selectorActive}
92
+ setSelectorActive={setSelectorActive}
82
93
  />
83
94
  <ResizablePane visible={visible} placement={placement}>
84
95
  {({ resizing }) => (