issue-sticker 0.1.1 → 0.2.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.
- package/CHANGELOG.md +44 -0
- package/README.md +82 -415
- package/dist/NetworkCollector-BlY2qUKF.js +12278 -0
- package/dist/createLucideIcon-B457EluY.js +27415 -0
- package/dist/{index-DN49yZjv.js → index-D6SaRFm4.js} +98 -1
- package/dist/issue-sticker-headless-react.js +764 -0
- package/dist/issue-sticker-headless.js +1914 -0
- package/dist/issue-sticker-react.js +17399 -46756
- package/dist/tooltip-BkQcdrki.js +512 -0
- package/headless-react.d.ts +12 -0
- package/headless.d.ts +172 -0
- package/package.json +26 -26
- package/react.d.ts +29 -2
- package/dist/index.d.ts +0 -37
- package/dist/issue-sticker-runtime.js +0 -37
- package/dist/issue-sticker.js +0 -2105
- package/dist/issue-sticker.umd.js +0 -1
- package/dist/sdkEventListeners-B7idjnFi.js +0 -498
- package/runtime.d.ts +0 -11
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { h as Presence, n as Portal$1, w as Arrow, D as DismissableLayer, C as Content, y as Root, j as useComposedRefs, t as createPopperScope, i as createContextScope, z as createSlottable, f as useId, e as useControllableState, v as Root2, A as Anchor, P as Primitive, g as composeEventHandlers, b as usePortalContainer, a as cn, O as OVERLAY_LAYER, s as stopEscapePropagation } from "./createLucideIcon-B457EluY.js";
|
|
4
|
+
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
5
|
+
createPopperScope
|
|
6
|
+
]);
|
|
7
|
+
var usePopperScope = createPopperScope();
|
|
8
|
+
var PROVIDER_NAME = "TooltipProvider";
|
|
9
|
+
var DEFAULT_DELAY_DURATION = 700;
|
|
10
|
+
var TOOLTIP_OPEN = "tooltip.open";
|
|
11
|
+
var [TooltipProviderContextProvider, useTooltipProviderContext] = createTooltipContext(PROVIDER_NAME);
|
|
12
|
+
var TooltipProvider$1 = (props) => {
|
|
13
|
+
const {
|
|
14
|
+
__scopeTooltip,
|
|
15
|
+
delayDuration = DEFAULT_DELAY_DURATION,
|
|
16
|
+
skipDelayDuration = 300,
|
|
17
|
+
disableHoverableContent = false,
|
|
18
|
+
children
|
|
19
|
+
} = props;
|
|
20
|
+
const isOpenDelayedRef = React.useRef(true);
|
|
21
|
+
const isPointerInTransitRef = React.useRef(false);
|
|
22
|
+
const skipDelayTimerRef = React.useRef(0);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
const skipDelayTimer = skipDelayTimerRef.current;
|
|
25
|
+
return () => window.clearTimeout(skipDelayTimer);
|
|
26
|
+
}, []);
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
TooltipProviderContextProvider,
|
|
29
|
+
{
|
|
30
|
+
scope: __scopeTooltip,
|
|
31
|
+
isOpenDelayedRef,
|
|
32
|
+
delayDuration,
|
|
33
|
+
onOpen: React.useCallback(() => {
|
|
34
|
+
if (skipDelayDuration <= 0) return;
|
|
35
|
+
window.clearTimeout(skipDelayTimerRef.current);
|
|
36
|
+
isOpenDelayedRef.current = false;
|
|
37
|
+
}, [skipDelayDuration]),
|
|
38
|
+
onClose: React.useCallback(() => {
|
|
39
|
+
if (skipDelayDuration <= 0) return;
|
|
40
|
+
window.clearTimeout(skipDelayTimerRef.current);
|
|
41
|
+
skipDelayTimerRef.current = window.setTimeout(
|
|
42
|
+
() => isOpenDelayedRef.current = true,
|
|
43
|
+
skipDelayDuration
|
|
44
|
+
);
|
|
45
|
+
}, [skipDelayDuration]),
|
|
46
|
+
isPointerInTransitRef,
|
|
47
|
+
onPointerInTransitChange: React.useCallback((inTransit) => {
|
|
48
|
+
isPointerInTransitRef.current = inTransit;
|
|
49
|
+
}, []),
|
|
50
|
+
disableHoverableContent,
|
|
51
|
+
children
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
TooltipProvider$1.displayName = PROVIDER_NAME;
|
|
56
|
+
var TOOLTIP_NAME = "Tooltip";
|
|
57
|
+
var [TooltipContextProvider, useTooltipContext] = createTooltipContext(TOOLTIP_NAME);
|
|
58
|
+
var Tooltip$1 = (props) => {
|
|
59
|
+
const {
|
|
60
|
+
__scopeTooltip,
|
|
61
|
+
children,
|
|
62
|
+
open: openProp,
|
|
63
|
+
defaultOpen,
|
|
64
|
+
onOpenChange,
|
|
65
|
+
disableHoverableContent: disableHoverableContentProp,
|
|
66
|
+
delayDuration: delayDurationProp
|
|
67
|
+
} = props;
|
|
68
|
+
const providerContext = useTooltipProviderContext(TOOLTIP_NAME, props.__scopeTooltip);
|
|
69
|
+
const popperScope = usePopperScope(__scopeTooltip);
|
|
70
|
+
const [trigger, setTrigger] = React.useState(null);
|
|
71
|
+
const contentId = useId();
|
|
72
|
+
const openTimerRef = React.useRef(0);
|
|
73
|
+
const disableHoverableContent = disableHoverableContentProp ?? providerContext.disableHoverableContent;
|
|
74
|
+
const delayDuration = delayDurationProp ?? providerContext.delayDuration;
|
|
75
|
+
const wasOpenDelayedRef = React.useRef(false);
|
|
76
|
+
const [open, setOpen] = useControllableState({
|
|
77
|
+
prop: openProp,
|
|
78
|
+
defaultProp: defaultOpen ?? false,
|
|
79
|
+
onChange: (open2) => {
|
|
80
|
+
if (open2) {
|
|
81
|
+
providerContext.onOpen();
|
|
82
|
+
document.dispatchEvent(new CustomEvent(TOOLTIP_OPEN));
|
|
83
|
+
} else {
|
|
84
|
+
providerContext.onClose();
|
|
85
|
+
}
|
|
86
|
+
onOpenChange == null ? void 0 : onOpenChange(open2);
|
|
87
|
+
},
|
|
88
|
+
caller: TOOLTIP_NAME
|
|
89
|
+
});
|
|
90
|
+
const stateAttribute = React.useMemo(() => {
|
|
91
|
+
return open ? wasOpenDelayedRef.current ? "delayed-open" : "instant-open" : "closed";
|
|
92
|
+
}, [open]);
|
|
93
|
+
const handleOpen = React.useCallback(() => {
|
|
94
|
+
window.clearTimeout(openTimerRef.current);
|
|
95
|
+
openTimerRef.current = 0;
|
|
96
|
+
wasOpenDelayedRef.current = false;
|
|
97
|
+
setOpen(true);
|
|
98
|
+
}, [setOpen]);
|
|
99
|
+
const handleClose = React.useCallback(() => {
|
|
100
|
+
window.clearTimeout(openTimerRef.current);
|
|
101
|
+
openTimerRef.current = 0;
|
|
102
|
+
setOpen(false);
|
|
103
|
+
}, [setOpen]);
|
|
104
|
+
const handleDelayedOpen = React.useCallback(() => {
|
|
105
|
+
window.clearTimeout(openTimerRef.current);
|
|
106
|
+
openTimerRef.current = window.setTimeout(() => {
|
|
107
|
+
wasOpenDelayedRef.current = true;
|
|
108
|
+
setOpen(true);
|
|
109
|
+
openTimerRef.current = 0;
|
|
110
|
+
}, delayDuration);
|
|
111
|
+
}, [delayDuration, setOpen]);
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
return () => {
|
|
114
|
+
if (openTimerRef.current) {
|
|
115
|
+
window.clearTimeout(openTimerRef.current);
|
|
116
|
+
openTimerRef.current = 0;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
120
|
+
return /* @__PURE__ */ jsx(Root2, { ...popperScope, children: /* @__PURE__ */ jsx(
|
|
121
|
+
TooltipContextProvider,
|
|
122
|
+
{
|
|
123
|
+
scope: __scopeTooltip,
|
|
124
|
+
contentId,
|
|
125
|
+
open,
|
|
126
|
+
stateAttribute,
|
|
127
|
+
trigger,
|
|
128
|
+
onTriggerChange: setTrigger,
|
|
129
|
+
onTriggerEnter: React.useCallback(() => {
|
|
130
|
+
if (providerContext.isOpenDelayedRef.current) handleDelayedOpen();
|
|
131
|
+
else handleOpen();
|
|
132
|
+
}, [providerContext.isOpenDelayedRef, handleDelayedOpen, handleOpen]),
|
|
133
|
+
onTriggerLeave: React.useCallback(() => {
|
|
134
|
+
if (disableHoverableContent) {
|
|
135
|
+
handleClose();
|
|
136
|
+
} else {
|
|
137
|
+
window.clearTimeout(openTimerRef.current);
|
|
138
|
+
openTimerRef.current = 0;
|
|
139
|
+
}
|
|
140
|
+
}, [handleClose, disableHoverableContent]),
|
|
141
|
+
onOpen: handleOpen,
|
|
142
|
+
onClose: handleClose,
|
|
143
|
+
disableHoverableContent,
|
|
144
|
+
children
|
|
145
|
+
}
|
|
146
|
+
) });
|
|
147
|
+
};
|
|
148
|
+
Tooltip$1.displayName = TOOLTIP_NAME;
|
|
149
|
+
var TRIGGER_NAME = "TooltipTrigger";
|
|
150
|
+
var TooltipTrigger$1 = React.forwardRef(
|
|
151
|
+
(props, forwardedRef) => {
|
|
152
|
+
const { __scopeTooltip, ...triggerProps } = props;
|
|
153
|
+
const context = useTooltipContext(TRIGGER_NAME, __scopeTooltip);
|
|
154
|
+
const providerContext = useTooltipProviderContext(TRIGGER_NAME, __scopeTooltip);
|
|
155
|
+
const popperScope = usePopperScope(__scopeTooltip);
|
|
156
|
+
const ref = React.useRef(null);
|
|
157
|
+
const composedRefs = useComposedRefs(forwardedRef, ref, context.onTriggerChange);
|
|
158
|
+
const isPointerDownRef = React.useRef(false);
|
|
159
|
+
const hasPointerMoveOpenedRef = React.useRef(false);
|
|
160
|
+
const handlePointerUp = React.useCallback(() => isPointerDownRef.current = false, []);
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
return () => document.removeEventListener("pointerup", handlePointerUp);
|
|
163
|
+
}, [handlePointerUp]);
|
|
164
|
+
return /* @__PURE__ */ jsx(Anchor, { asChild: true, ...popperScope, children: /* @__PURE__ */ jsx(
|
|
165
|
+
Primitive.button,
|
|
166
|
+
{
|
|
167
|
+
"aria-describedby": context.open ? context.contentId : void 0,
|
|
168
|
+
"data-state": context.stateAttribute,
|
|
169
|
+
...triggerProps,
|
|
170
|
+
ref: composedRefs,
|
|
171
|
+
onPointerMove: composeEventHandlers(props.onPointerMove, (event) => {
|
|
172
|
+
if (event.pointerType === "touch") return;
|
|
173
|
+
if (!hasPointerMoveOpenedRef.current && !providerContext.isPointerInTransitRef.current) {
|
|
174
|
+
context.onTriggerEnter();
|
|
175
|
+
hasPointerMoveOpenedRef.current = true;
|
|
176
|
+
}
|
|
177
|
+
}),
|
|
178
|
+
onPointerLeave: composeEventHandlers(props.onPointerLeave, () => {
|
|
179
|
+
context.onTriggerLeave();
|
|
180
|
+
hasPointerMoveOpenedRef.current = false;
|
|
181
|
+
}),
|
|
182
|
+
onPointerDown: composeEventHandlers(props.onPointerDown, () => {
|
|
183
|
+
if (context.open) {
|
|
184
|
+
context.onClose();
|
|
185
|
+
}
|
|
186
|
+
isPointerDownRef.current = true;
|
|
187
|
+
document.addEventListener("pointerup", handlePointerUp, { once: true });
|
|
188
|
+
}),
|
|
189
|
+
onFocus: composeEventHandlers(props.onFocus, () => {
|
|
190
|
+
if (!isPointerDownRef.current) context.onOpen();
|
|
191
|
+
}),
|
|
192
|
+
onBlur: composeEventHandlers(props.onBlur, context.onClose),
|
|
193
|
+
onClick: composeEventHandlers(props.onClick, context.onClose)
|
|
194
|
+
}
|
|
195
|
+
) });
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
TooltipTrigger$1.displayName = TRIGGER_NAME;
|
|
199
|
+
var PORTAL_NAME = "TooltipPortal";
|
|
200
|
+
var [PortalProvider, usePortalContext] = createTooltipContext(PORTAL_NAME, {
|
|
201
|
+
forceMount: void 0
|
|
202
|
+
});
|
|
203
|
+
var TooltipPortal = (props) => {
|
|
204
|
+
const { __scopeTooltip, forceMount, children, container } = props;
|
|
205
|
+
const context = useTooltipContext(PORTAL_NAME, __scopeTooltip);
|
|
206
|
+
return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopeTooltip, forceMount, children: /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Portal$1, { asChild: true, container, children }) }) });
|
|
207
|
+
};
|
|
208
|
+
TooltipPortal.displayName = PORTAL_NAME;
|
|
209
|
+
var CONTENT_NAME = "TooltipContent";
|
|
210
|
+
var TooltipContent$1 = React.forwardRef(
|
|
211
|
+
(props, forwardedRef) => {
|
|
212
|
+
const portalContext = usePortalContext(CONTENT_NAME, props.__scopeTooltip);
|
|
213
|
+
const { forceMount = portalContext.forceMount, side = "top", ...contentProps } = props;
|
|
214
|
+
const context = useTooltipContext(CONTENT_NAME, props.__scopeTooltip);
|
|
215
|
+
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: context.disableHoverableContent ? /* @__PURE__ */ jsx(TooltipContentImpl, { side, ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(TooltipContentHoverable, { side, ...contentProps, ref: forwardedRef }) });
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
var TooltipContentHoverable = React.forwardRef((props, forwardedRef) => {
|
|
219
|
+
const context = useTooltipContext(CONTENT_NAME, props.__scopeTooltip);
|
|
220
|
+
const providerContext = useTooltipProviderContext(CONTENT_NAME, props.__scopeTooltip);
|
|
221
|
+
const ref = React.useRef(null);
|
|
222
|
+
const composedRefs = useComposedRefs(forwardedRef, ref);
|
|
223
|
+
const [pointerGraceArea, setPointerGraceArea] = React.useState(null);
|
|
224
|
+
const { trigger, onClose } = context;
|
|
225
|
+
const content = ref.current;
|
|
226
|
+
const { onPointerInTransitChange } = providerContext;
|
|
227
|
+
const handleRemoveGraceArea = React.useCallback(() => {
|
|
228
|
+
setPointerGraceArea(null);
|
|
229
|
+
onPointerInTransitChange(false);
|
|
230
|
+
}, [onPointerInTransitChange]);
|
|
231
|
+
const handleCreateGraceArea = React.useCallback(
|
|
232
|
+
(event, hoverTarget) => {
|
|
233
|
+
const currentTarget = event.currentTarget;
|
|
234
|
+
const exitPoint = { x: event.clientX, y: event.clientY };
|
|
235
|
+
const exitSide = getExitSideFromRect(exitPoint, currentTarget.getBoundingClientRect());
|
|
236
|
+
const paddedExitPoints = getPaddedExitPoints(exitPoint, exitSide);
|
|
237
|
+
const hoverTargetPoints = getPointsFromRect(hoverTarget.getBoundingClientRect());
|
|
238
|
+
const graceArea = getHull([...paddedExitPoints, ...hoverTargetPoints]);
|
|
239
|
+
setPointerGraceArea(graceArea);
|
|
240
|
+
onPointerInTransitChange(true);
|
|
241
|
+
},
|
|
242
|
+
[onPointerInTransitChange]
|
|
243
|
+
);
|
|
244
|
+
React.useEffect(() => {
|
|
245
|
+
return () => handleRemoveGraceArea();
|
|
246
|
+
}, [handleRemoveGraceArea]);
|
|
247
|
+
React.useEffect(() => {
|
|
248
|
+
if (trigger && content) {
|
|
249
|
+
const handleTriggerLeave = (event) => handleCreateGraceArea(event, content);
|
|
250
|
+
const handleContentLeave = (event) => handleCreateGraceArea(event, trigger);
|
|
251
|
+
trigger.addEventListener("pointerleave", handleTriggerLeave);
|
|
252
|
+
content.addEventListener("pointerleave", handleContentLeave);
|
|
253
|
+
return () => {
|
|
254
|
+
trigger.removeEventListener("pointerleave", handleTriggerLeave);
|
|
255
|
+
content.removeEventListener("pointerleave", handleContentLeave);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}, [trigger, content, handleCreateGraceArea, handleRemoveGraceArea]);
|
|
259
|
+
React.useEffect(() => {
|
|
260
|
+
if (pointerGraceArea) {
|
|
261
|
+
const handleTrackPointerGrace = (event) => {
|
|
262
|
+
const target = event.target;
|
|
263
|
+
const pointerPosition = { x: event.clientX, y: event.clientY };
|
|
264
|
+
const hasEnteredTarget = (trigger == null ? void 0 : trigger.contains(target)) || (content == null ? void 0 : content.contains(target));
|
|
265
|
+
const isPointerOutsideGraceArea = !isPointInPolygon(pointerPosition, pointerGraceArea);
|
|
266
|
+
if (hasEnteredTarget) {
|
|
267
|
+
handleRemoveGraceArea();
|
|
268
|
+
} else if (isPointerOutsideGraceArea) {
|
|
269
|
+
handleRemoveGraceArea();
|
|
270
|
+
onClose();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
document.addEventListener("pointermove", handleTrackPointerGrace);
|
|
274
|
+
return () => document.removeEventListener("pointermove", handleTrackPointerGrace);
|
|
275
|
+
}
|
|
276
|
+
}, [trigger, content, pointerGraceArea, onClose, handleRemoveGraceArea]);
|
|
277
|
+
return /* @__PURE__ */ jsx(TooltipContentImpl, { ...props, ref: composedRefs });
|
|
278
|
+
});
|
|
279
|
+
var [VisuallyHiddenContentContextProvider, useVisuallyHiddenContentContext] = createTooltipContext(TOOLTIP_NAME, { isInside: false });
|
|
280
|
+
var Slottable = createSlottable("TooltipContent");
|
|
281
|
+
var TooltipContentImpl = React.forwardRef(
|
|
282
|
+
(props, forwardedRef) => {
|
|
283
|
+
const {
|
|
284
|
+
__scopeTooltip,
|
|
285
|
+
children,
|
|
286
|
+
"aria-label": ariaLabel,
|
|
287
|
+
onEscapeKeyDown,
|
|
288
|
+
onPointerDownOutside,
|
|
289
|
+
...contentProps
|
|
290
|
+
} = props;
|
|
291
|
+
const context = useTooltipContext(CONTENT_NAME, __scopeTooltip);
|
|
292
|
+
const popperScope = usePopperScope(__scopeTooltip);
|
|
293
|
+
const { onClose } = context;
|
|
294
|
+
React.useEffect(() => {
|
|
295
|
+
document.addEventListener(TOOLTIP_OPEN, onClose);
|
|
296
|
+
return () => document.removeEventListener(TOOLTIP_OPEN, onClose);
|
|
297
|
+
}, [onClose]);
|
|
298
|
+
React.useEffect(() => {
|
|
299
|
+
if (context.trigger) {
|
|
300
|
+
const handleScroll = (event) => {
|
|
301
|
+
if (event.target instanceof Node && event.target.contains(context.trigger)) {
|
|
302
|
+
onClose();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
window.addEventListener("scroll", handleScroll, { capture: true });
|
|
306
|
+
return () => window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
307
|
+
}
|
|
308
|
+
}, [context.trigger, onClose]);
|
|
309
|
+
return /* @__PURE__ */ jsx(
|
|
310
|
+
DismissableLayer,
|
|
311
|
+
{
|
|
312
|
+
asChild: true,
|
|
313
|
+
disableOutsidePointerEvents: false,
|
|
314
|
+
onEscapeKeyDown,
|
|
315
|
+
onPointerDownOutside,
|
|
316
|
+
onFocusOutside: (event) => event.preventDefault(),
|
|
317
|
+
onDismiss: onClose,
|
|
318
|
+
children: /* @__PURE__ */ jsxs(
|
|
319
|
+
Content,
|
|
320
|
+
{
|
|
321
|
+
"data-state": context.stateAttribute,
|
|
322
|
+
...popperScope,
|
|
323
|
+
...contentProps,
|
|
324
|
+
ref: forwardedRef,
|
|
325
|
+
style: {
|
|
326
|
+
...contentProps.style,
|
|
327
|
+
// re-namespace exposed content custom properties
|
|
328
|
+
...{
|
|
329
|
+
"--radix-tooltip-content-transform-origin": "var(--radix-popper-transform-origin)",
|
|
330
|
+
"--radix-tooltip-content-available-width": "var(--radix-popper-available-width)",
|
|
331
|
+
"--radix-tooltip-content-available-height": "var(--radix-popper-available-height)",
|
|
332
|
+
"--radix-tooltip-trigger-width": "var(--radix-popper-anchor-width)",
|
|
333
|
+
"--radix-tooltip-trigger-height": "var(--radix-popper-anchor-height)"
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
children: [
|
|
337
|
+
/* @__PURE__ */ jsx(Slottable, { children }),
|
|
338
|
+
/* @__PURE__ */ jsx(VisuallyHiddenContentContextProvider, { scope: __scopeTooltip, isInside: true, children: /* @__PURE__ */ jsx(Root, { id: context.contentId, role: "tooltip", children: ariaLabel || children }) })
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
TooltipContent$1.displayName = CONTENT_NAME;
|
|
347
|
+
var ARROW_NAME = "TooltipArrow";
|
|
348
|
+
var TooltipArrow = React.forwardRef(
|
|
349
|
+
(props, forwardedRef) => {
|
|
350
|
+
const { __scopeTooltip, ...arrowProps } = props;
|
|
351
|
+
const popperScope = usePopperScope(__scopeTooltip);
|
|
352
|
+
const visuallyHiddenContentContext = useVisuallyHiddenContentContext(
|
|
353
|
+
ARROW_NAME,
|
|
354
|
+
__scopeTooltip
|
|
355
|
+
);
|
|
356
|
+
return visuallyHiddenContentContext.isInside ? null : /* @__PURE__ */ jsx(Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef });
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
TooltipArrow.displayName = ARROW_NAME;
|
|
360
|
+
function getExitSideFromRect(point, rect) {
|
|
361
|
+
const top = Math.abs(rect.top - point.y);
|
|
362
|
+
const bottom = Math.abs(rect.bottom - point.y);
|
|
363
|
+
const right = Math.abs(rect.right - point.x);
|
|
364
|
+
const left = Math.abs(rect.left - point.x);
|
|
365
|
+
switch (Math.min(top, bottom, right, left)) {
|
|
366
|
+
case left:
|
|
367
|
+
return "left";
|
|
368
|
+
case right:
|
|
369
|
+
return "right";
|
|
370
|
+
case top:
|
|
371
|
+
return "top";
|
|
372
|
+
case bottom:
|
|
373
|
+
return "bottom";
|
|
374
|
+
default:
|
|
375
|
+
throw new Error("unreachable");
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function getPaddedExitPoints(exitPoint, exitSide, padding = 5) {
|
|
379
|
+
const paddedExitPoints = [];
|
|
380
|
+
switch (exitSide) {
|
|
381
|
+
case "top":
|
|
382
|
+
paddedExitPoints.push(
|
|
383
|
+
{ x: exitPoint.x - padding, y: exitPoint.y + padding },
|
|
384
|
+
{ x: exitPoint.x + padding, y: exitPoint.y + padding }
|
|
385
|
+
);
|
|
386
|
+
break;
|
|
387
|
+
case "bottom":
|
|
388
|
+
paddedExitPoints.push(
|
|
389
|
+
{ x: exitPoint.x - padding, y: exitPoint.y - padding },
|
|
390
|
+
{ x: exitPoint.x + padding, y: exitPoint.y - padding }
|
|
391
|
+
);
|
|
392
|
+
break;
|
|
393
|
+
case "left":
|
|
394
|
+
paddedExitPoints.push(
|
|
395
|
+
{ x: exitPoint.x + padding, y: exitPoint.y - padding },
|
|
396
|
+
{ x: exitPoint.x + padding, y: exitPoint.y + padding }
|
|
397
|
+
);
|
|
398
|
+
break;
|
|
399
|
+
case "right":
|
|
400
|
+
paddedExitPoints.push(
|
|
401
|
+
{ x: exitPoint.x - padding, y: exitPoint.y - padding },
|
|
402
|
+
{ x: exitPoint.x - padding, y: exitPoint.y + padding }
|
|
403
|
+
);
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
return paddedExitPoints;
|
|
407
|
+
}
|
|
408
|
+
function getPointsFromRect(rect) {
|
|
409
|
+
const { top, right, bottom, left } = rect;
|
|
410
|
+
return [
|
|
411
|
+
{ x: left, y: top },
|
|
412
|
+
{ x: right, y: top },
|
|
413
|
+
{ x: right, y: bottom },
|
|
414
|
+
{ x: left, y: bottom }
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
function isPointInPolygon(point, polygon) {
|
|
418
|
+
const { x, y } = point;
|
|
419
|
+
let inside = false;
|
|
420
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
421
|
+
const ii = polygon[i];
|
|
422
|
+
const jj = polygon[j];
|
|
423
|
+
const xi = ii.x;
|
|
424
|
+
const yi = ii.y;
|
|
425
|
+
const xj = jj.x;
|
|
426
|
+
const yj = jj.y;
|
|
427
|
+
const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
|
|
428
|
+
if (intersect) inside = !inside;
|
|
429
|
+
}
|
|
430
|
+
return inside;
|
|
431
|
+
}
|
|
432
|
+
function getHull(points) {
|
|
433
|
+
const newPoints = points.slice();
|
|
434
|
+
newPoints.sort((a, b) => {
|
|
435
|
+
if (a.x < b.x) return -1;
|
|
436
|
+
else if (a.x > b.x) return 1;
|
|
437
|
+
else if (a.y < b.y) return -1;
|
|
438
|
+
else if (a.y > b.y) return 1;
|
|
439
|
+
else return 0;
|
|
440
|
+
});
|
|
441
|
+
return getHullPresorted(newPoints);
|
|
442
|
+
}
|
|
443
|
+
function getHullPresorted(points) {
|
|
444
|
+
if (points.length <= 1) return points.slice();
|
|
445
|
+
const upperHull = [];
|
|
446
|
+
for (let i = 0; i < points.length; i++) {
|
|
447
|
+
const p = points[i];
|
|
448
|
+
while (upperHull.length >= 2) {
|
|
449
|
+
const q = upperHull[upperHull.length - 1];
|
|
450
|
+
const r = upperHull[upperHull.length - 2];
|
|
451
|
+
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) upperHull.pop();
|
|
452
|
+
else break;
|
|
453
|
+
}
|
|
454
|
+
upperHull.push(p);
|
|
455
|
+
}
|
|
456
|
+
upperHull.pop();
|
|
457
|
+
const lowerHull = [];
|
|
458
|
+
for (let i = points.length - 1; i >= 0; i--) {
|
|
459
|
+
const p = points[i];
|
|
460
|
+
while (lowerHull.length >= 2) {
|
|
461
|
+
const q = lowerHull[lowerHull.length - 1];
|
|
462
|
+
const r = lowerHull[lowerHull.length - 2];
|
|
463
|
+
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) lowerHull.pop();
|
|
464
|
+
else break;
|
|
465
|
+
}
|
|
466
|
+
lowerHull.push(p);
|
|
467
|
+
}
|
|
468
|
+
lowerHull.pop();
|
|
469
|
+
if (upperHull.length === 1 && lowerHull.length === 1 && upperHull[0].x === lowerHull[0].x && upperHull[0].y === lowerHull[0].y) {
|
|
470
|
+
return upperHull;
|
|
471
|
+
} else {
|
|
472
|
+
return upperHull.concat(lowerHull);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
var Provider = TooltipProvider$1;
|
|
476
|
+
var Root3 = Tooltip$1;
|
|
477
|
+
var Trigger = TooltipTrigger$1;
|
|
478
|
+
var Portal = TooltipPortal;
|
|
479
|
+
var Content2 = TooltipContent$1;
|
|
480
|
+
var Arrow2 = TooltipArrow;
|
|
481
|
+
const TooltipProvider = Provider;
|
|
482
|
+
const Tooltip = Root3;
|
|
483
|
+
const TooltipTrigger = Trigger;
|
|
484
|
+
const TooltipContent = React.forwardRef(({ className, sideOffset = 8, onEscapeKeyDown, children, ...props }, ref) => {
|
|
485
|
+
const portalContainer = usePortalContainer();
|
|
486
|
+
return /* @__PURE__ */ jsx(Portal, { container: portalContainer, children: /* @__PURE__ */ jsxs(
|
|
487
|
+
Content2,
|
|
488
|
+
{
|
|
489
|
+
ref,
|
|
490
|
+
sideOffset,
|
|
491
|
+
onEscapeKeyDown: stopEscapePropagation(onEscapeKeyDown),
|
|
492
|
+
className: cn(
|
|
493
|
+
`pointer-events-none ${OVERLAY_LAYER.floating} max-w-[280px] select-none whitespace-nowrap rounded-md border border-line-subtle bg-text-primary px-2.5 py-1.5 font-sans text-xs font-medium leading-4 text-text-inverse shadow-md outline-none`,
|
|
494
|
+
"data-[state=delayed-open]:animate-scale-in data-[state=closed]:animate-scale-out",
|
|
495
|
+
"motion-reduce:animate-none",
|
|
496
|
+
className
|
|
497
|
+
),
|
|
498
|
+
...props,
|
|
499
|
+
children: [
|
|
500
|
+
children,
|
|
501
|
+
/* @__PURE__ */ jsx(Arrow2, { className: "fill-text-primary", width: 8, height: 4 })
|
|
502
|
+
]
|
|
503
|
+
}
|
|
504
|
+
) });
|
|
505
|
+
});
|
|
506
|
+
TooltipContent.displayName = Content2.displayName;
|
|
507
|
+
export {
|
|
508
|
+
TooltipProvider as T,
|
|
509
|
+
Tooltip as a,
|
|
510
|
+
TooltipTrigger as b,
|
|
511
|
+
TooltipContent as c
|
|
512
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface HeadlessSessionReplayIssue {
|
|
2
|
+
session_replay_url?: string | null;
|
|
3
|
+
session_replay?: { url?: string | null } | null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SessionReplayViewerProps {
|
|
7
|
+
issue: HeadlessSessionReplayIssue | null | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function SessionReplayViewer(
|
|
11
|
+
props: SessionReplayViewerProps
|
|
12
|
+
): import('react').ReactElement;
|
package/headless.d.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Issue,
|
|
3
|
+
IssueFilterGroup,
|
|
4
|
+
IssueFilters,
|
|
5
|
+
IssuePosition,
|
|
6
|
+
IssueStickerSummary,
|
|
7
|
+
SessionReplayMeta,
|
|
8
|
+
} from '@issue-sticker/shared';
|
|
9
|
+
|
|
10
|
+
export type HeadlessPhase =
|
|
11
|
+
| 'initializing'
|
|
12
|
+
| 'idle'
|
|
13
|
+
| 'loadingIssues'
|
|
14
|
+
| 'viewingIssue'
|
|
15
|
+
| 'recordingVideo'
|
|
16
|
+
| 'selectingTarget'
|
|
17
|
+
| 'draftingIssue'
|
|
18
|
+
| 'submittingIssue';
|
|
19
|
+
|
|
20
|
+
export type HeadlessErrorCode =
|
|
21
|
+
| 'INVALID_PHASE'
|
|
22
|
+
| 'NOT_INITIALIZED'
|
|
23
|
+
| 'AUTH_FAILED'
|
|
24
|
+
| 'MEMBER_AUTH_REQUIRED'
|
|
25
|
+
| 'NETWORK_ERROR'
|
|
26
|
+
| 'CAPTURE_FAILED'
|
|
27
|
+
| 'VALIDATION_ERROR';
|
|
28
|
+
|
|
29
|
+
export type HeadlessAuthMode = 'public' | 'member';
|
|
30
|
+
export type HeadlessCaptureMediaMode = 'screenshot' | 'video';
|
|
31
|
+
|
|
32
|
+
export interface HeadlessIssueCaptureOptions {
|
|
33
|
+
readonly mediaMode?: 'screenshot';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface HeadlessUser {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HeadlessConfig {
|
|
42
|
+
apiKey: string;
|
|
43
|
+
user?: HeadlessUser;
|
|
44
|
+
capture?: {
|
|
45
|
+
networkLogs?: boolean;
|
|
46
|
+
consoleLogs?: boolean;
|
|
47
|
+
excludeSelectors?: string[];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface HeadlessAuthState {
|
|
52
|
+
readonly mode: HeadlessAuthMode;
|
|
53
|
+
readonly isReady: boolean;
|
|
54
|
+
readonly canUseIntegrations: boolean;
|
|
55
|
+
readonly tenantId: string | null;
|
|
56
|
+
readonly projectId: string | null;
|
|
57
|
+
readonly accountId: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type DynamicFieldSource = 'jira' | 'notion' | 'custom';
|
|
61
|
+
|
|
62
|
+
export interface DynamicFieldOption {
|
|
63
|
+
readonly label: string;
|
|
64
|
+
readonly value: string;
|
|
65
|
+
readonly description?: string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DynamicFieldDescriptor {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
readonly source: DynamicFieldSource;
|
|
71
|
+
readonly label: string;
|
|
72
|
+
readonly inputType:
|
|
73
|
+
| 'text'
|
|
74
|
+
| 'number'
|
|
75
|
+
| 'select'
|
|
76
|
+
| 'multi_select'
|
|
77
|
+
| 'date'
|
|
78
|
+
| 'checkbox'
|
|
79
|
+
| 'user'
|
|
80
|
+
| 'issue'
|
|
81
|
+
| 'people'
|
|
82
|
+
| 'readonly';
|
|
83
|
+
readonly options: readonly DynamicFieldOption[];
|
|
84
|
+
readonly search?: (query: string) => Promise<readonly DynamicFieldOption[]>;
|
|
85
|
+
readonly resolveValueLabel?: (value: string) => Promise<string | null>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface IssueFormSchema {
|
|
89
|
+
readonly fields: readonly DynamicFieldDescriptor[];
|
|
90
|
+
readonly unavailableReason?: 'public-mode' | 'not-configured' | 'loading-failed';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface IssueFilterSchema {
|
|
94
|
+
readonly groups: readonly IssueFilterGroup[];
|
|
95
|
+
readonly unavailableReason?: 'public-mode' | 'not-configured' | 'loading-failed';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface HeadlessIssueDraft {
|
|
99
|
+
readonly mediaMode: HeadlessCaptureMediaMode;
|
|
100
|
+
readonly position: IssuePosition;
|
|
101
|
+
readonly screenshotPreviewUrls: readonly string[];
|
|
102
|
+
readonly videoPreviewUrl: string | null;
|
|
103
|
+
readonly targetSelector: string;
|
|
104
|
+
readonly sessionReplayEnabled: boolean;
|
|
105
|
+
readonly sessionReplayPreview: SessionReplayMeta | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface HeadlessVideoCaptureState {
|
|
109
|
+
readonly isRecording: boolean;
|
|
110
|
+
readonly hasRecordedVideo: boolean;
|
|
111
|
+
readonly error: string | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface HeadlessSessionReplayState {
|
|
115
|
+
readonly available: boolean;
|
|
116
|
+
readonly canAttach: boolean;
|
|
117
|
+
readonly enabled: boolean;
|
|
118
|
+
readonly error: string | null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface HeadlessSnapshot {
|
|
122
|
+
readonly phase: HeadlessPhase;
|
|
123
|
+
readonly auth: HeadlessAuthState;
|
|
124
|
+
readonly issues: readonly IssueStickerSummary[];
|
|
125
|
+
readonly activeIssue: (Issue & { comments?: unknown[] }) | null;
|
|
126
|
+
readonly draft: HeadlessIssueDraft | null;
|
|
127
|
+
readonly videoCapture: HeadlessVideoCaptureState;
|
|
128
|
+
readonly sessionReplay: HeadlessSessionReplayState;
|
|
129
|
+
readonly formSchema: IssueFormSchema | null;
|
|
130
|
+
readonly filterSchema: IssueFilterSchema | null;
|
|
131
|
+
readonly error: IssueStickerHeadlessError | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface HeadlessIssueFilters extends Omit<IssueFilters, 'projectId' | 'externalUserId'> {
|
|
135
|
+
externalUserId?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface SubmitDraftInput {
|
|
139
|
+
title: string;
|
|
140
|
+
description: string;
|
|
141
|
+
dynamicFields?: Record<string, unknown>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface HeadlessClient {
|
|
145
|
+
readonly phase: HeadlessPhase;
|
|
146
|
+
getSnapshot(): HeadlessSnapshot;
|
|
147
|
+
subscribe(listener: (snapshot: HeadlessSnapshot) => void): () => void;
|
|
148
|
+
initialize(): Promise<void>;
|
|
149
|
+
refreshIssues(filters?: HeadlessIssueFilters): Promise<void>;
|
|
150
|
+
refreshFilterSchema(): Promise<void>;
|
|
151
|
+
openIssue(issueId: string): Promise<void>;
|
|
152
|
+
closeIssue(): void;
|
|
153
|
+
startIssueCapture(options?: HeadlessIssueCaptureOptions): Promise<void>;
|
|
154
|
+
startVideoCapture(): Promise<void>;
|
|
155
|
+
stopVideoCapture(): Promise<void>;
|
|
156
|
+
setSessionReplayEnabled(enabled: boolean): void;
|
|
157
|
+
cancelIssueCapture(): void;
|
|
158
|
+
submitDraft(input: SubmitDraftInput): Promise<Issue>;
|
|
159
|
+
loginAsMember(): Promise<void>;
|
|
160
|
+
logoutMember(): Promise<void>;
|
|
161
|
+
destroy(): void;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export declare class IssueStickerHeadlessError extends Error {
|
|
165
|
+
readonly code: HeadlessErrorCode;
|
|
166
|
+
readonly cause?: unknown;
|
|
167
|
+
constructor(code: HeadlessErrorCode, message: string, cause?: unknown);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export declare function createIssueStickerHeadless(config: HeadlessConfig): HeadlessClient;
|
|
171
|
+
|
|
172
|
+
export default createIssueStickerHeadless;
|