react-linear-feedback 0.3.0 → 0.4.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/README.md +4 -3
- package/dist/embed/index.js +134 -87
- package/dist/embed/linear-feedback.js +11 -8
- package/dist/react/index.cjs +134 -87
- package/dist/react/index.js +134 -87
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -195,6 +195,8 @@ var CSS2 = `
|
|
|
195
195
|
|
|
196
196
|
.lfb-rect { position: absolute; border: 2px solid var(--lfb-rect); background: rgba(255,0,85,0.12); border-radius: 3px; pointer-events: none; }
|
|
197
197
|
|
|
198
|
+
.lfb-shield { position: fixed; inset: 0; pointer-events: auto; cursor: default; }
|
|
199
|
+
|
|
198
200
|
.lfb-anchor { position: absolute; pointer-events: auto; }
|
|
199
201
|
|
|
200
202
|
.lfb-card {
|
|
@@ -209,6 +211,7 @@ var CSS2 = `
|
|
|
209
211
|
line-height: 1.4;
|
|
210
212
|
}
|
|
211
213
|
.lfb-composer { position: absolute; top: 100%; left: 0; margin-top: 8px; width: 320px; max-width: calc(100vw - 32px); }
|
|
214
|
+
.lfb-composer--above { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 8px; }
|
|
212
215
|
|
|
213
216
|
.lfb-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
214
217
|
.lfb-eyebrow { font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--lfb-fg-tertiary); }
|
|
@@ -292,6 +295,8 @@ function ensureStyles() {
|
|
|
292
295
|
}
|
|
293
296
|
var MIN_DRAG = 12;
|
|
294
297
|
var DEFAULT_BOX = { width: 220, height: 130 };
|
|
298
|
+
var COMPOSER_WIDTH = 320;
|
|
299
|
+
var COMPOSER_EST_HEIGHT = 320;
|
|
295
300
|
var DEFAULT_TYPES = [
|
|
296
301
|
{ id: "bug", label: "Bug", color: "#ef4444", icon: "bug" },
|
|
297
302
|
{ id: "improvement", label: "Improvement", color: "#22c55e", icon: "improvement" }
|
|
@@ -320,6 +325,11 @@ function FeedbackWidget({
|
|
|
320
325
|
const [drag, setDrag] = react.useState(null);
|
|
321
326
|
const textareaRef = react.useRef(null);
|
|
322
327
|
const nameInputRef = react.useRef(null);
|
|
328
|
+
const nameEditRef = react.useRef(null);
|
|
329
|
+
const submittingRef = react.useRef(false);
|
|
330
|
+
submittingRef.current = submitting;
|
|
331
|
+
const editingNameRef = react.useRef(false);
|
|
332
|
+
editingNameRef.current = editingName;
|
|
323
333
|
react.useEffect(() => {
|
|
324
334
|
ensureStyles();
|
|
325
335
|
try {
|
|
@@ -329,12 +339,19 @@ function FeedbackWidget({
|
|
|
329
339
|
}
|
|
330
340
|
}, [nameStorageKey]);
|
|
331
341
|
react.useEffect(() => {
|
|
332
|
-
if (mode.kind
|
|
342
|
+
if (mode.kind === "idle") return;
|
|
333
343
|
const onKey = (e) => {
|
|
334
|
-
if (e.key
|
|
335
|
-
|
|
336
|
-
|
|
344
|
+
if (e.key !== "Escape" || submittingRef.current) return;
|
|
345
|
+
if (editingNameRef.current) {
|
|
346
|
+
setEditingName(false);
|
|
347
|
+
return;
|
|
337
348
|
}
|
|
349
|
+
setMode({ kind: "idle" });
|
|
350
|
+
setDrag(null);
|
|
351
|
+
setText("");
|
|
352
|
+
setError(null);
|
|
353
|
+
setSubmitting(false);
|
|
354
|
+
setEditingName(false);
|
|
338
355
|
};
|
|
339
356
|
document.addEventListener("keydown", onKey);
|
|
340
357
|
return () => document.removeEventListener("keydown", onKey);
|
|
@@ -349,6 +366,22 @@ function FeedbackWidget({
|
|
|
349
366
|
return () => window.clearTimeout(t);
|
|
350
367
|
}
|
|
351
368
|
}, [mode.kind]);
|
|
369
|
+
react.useEffect(() => {
|
|
370
|
+
if (editingName) nameEditRef.current?.focus();
|
|
371
|
+
}, [editingName]);
|
|
372
|
+
react.useEffect(() => {
|
|
373
|
+
if (mode.kind !== "drawing" && mode.kind !== "composing") return;
|
|
374
|
+
const el = document.documentElement;
|
|
375
|
+
const prevOverflow = el.style.overflow;
|
|
376
|
+
const prevPaddingRight = el.style.paddingRight;
|
|
377
|
+
const scrollbar = window.innerWidth - el.clientWidth;
|
|
378
|
+
el.style.overflow = "hidden";
|
|
379
|
+
if (scrollbar > 0) el.style.paddingRight = `${scrollbar}px`;
|
|
380
|
+
return () => {
|
|
381
|
+
el.style.overflow = prevOverflow;
|
|
382
|
+
el.style.paddingRight = prevPaddingRight;
|
|
383
|
+
};
|
|
384
|
+
}, [mode.kind]);
|
|
352
385
|
const persistName = (value) => {
|
|
353
386
|
try {
|
|
354
387
|
window.localStorage.setItem(nameStorageKey, value);
|
|
@@ -402,12 +435,17 @@ function FeedbackWidget({
|
|
|
402
435
|
vy = end.y - vh / 2;
|
|
403
436
|
}
|
|
404
437
|
const rect = { x: Math.max(0, vx + window.scrollX), y: Math.max(0, vy + window.scrollY), width: vw, height: vh };
|
|
438
|
+
const fitsBelow = vy + vh + COMPOSER_EST_HEIGHT <= window.innerHeight;
|
|
439
|
+
const fitsAbove = vy >= COMPOSER_EST_HEIGHT;
|
|
440
|
+
const placement = !fitsBelow && fitsAbove ? "above" : "below";
|
|
441
|
+
const maxAnchorX = window.scrollX + Math.max(16, window.innerWidth - COMPOSER_WIDTH - 16);
|
|
442
|
+
const anchorX = Math.min(rect.x, maxAnchorX);
|
|
405
443
|
setDrag(null);
|
|
406
444
|
setText("");
|
|
407
445
|
setIssueType(types[0]?.id ?? "bug");
|
|
408
446
|
setError(null);
|
|
409
447
|
setEditingName(false);
|
|
410
|
-
setMode({ kind: "composing", rect });
|
|
448
|
+
setMode({ kind: "composing", rect, placement, anchorX });
|
|
411
449
|
};
|
|
412
450
|
const cancelComposer = () => {
|
|
413
451
|
setMode({ kind: "idle" });
|
|
@@ -454,93 +492,102 @@ function FeedbackWidget({
|
|
|
454
492
|
} : null;
|
|
455
493
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-root", style: rootStyle, children: [
|
|
456
494
|
/* @__PURE__ */ jsxRuntime.jsx("div", { ...overlayProps, className: "lfb-doc-layer", children: mode.kind === "composing" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lfb-shield", "aria-hidden": "true" }),
|
|
457
496
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lfb-rect", style: { left: mode.rect.x, top: mode.rect.y, width: mode.rect.width, height: mode.rect.height } }),
|
|
458
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
497
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
498
|
+
"div",
|
|
499
|
+
{
|
|
500
|
+
className: "lfb-anchor",
|
|
501
|
+
style: { left: mode.anchorX, top: mode.placement === "below" ? mode.rect.y + mode.rect.height : mode.rect.y },
|
|
502
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("form", { className: `lfb-card lfb-composer${mode.placement === "above" ? " lfb-composer--above" : ""}`, onSubmit: handleSubmit, children: [
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-row", children: [
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lfb-eyebrow", children: "New feedback" }),
|
|
505
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lfb-iconbtn", "aria-label": "Cancel", onClick: cancelComposer, children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {}) })
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 12 }, children: [
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lfb-field-label", children: "Issue type" }),
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lfb-types", children: types.map((t) => {
|
|
510
|
+
const Icon = TYPE_ICONS[t.icon ?? "dot"];
|
|
511
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
512
|
+
"button",
|
|
513
|
+
{
|
|
514
|
+
type: "button",
|
|
515
|
+
className: "lfb-type",
|
|
516
|
+
"aria-pressed": issueType === t.id,
|
|
517
|
+
onClick: () => setIssueType(t.id),
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lfb-swatch", style: { background: t.color }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 14 }) }),
|
|
520
|
+
t.label
|
|
521
|
+
]
|
|
522
|
+
},
|
|
523
|
+
t.id
|
|
524
|
+
);
|
|
525
|
+
}) })
|
|
526
|
+
] }),
|
|
527
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
+
"textarea",
|
|
469
529
|
{
|
|
470
|
-
|
|
471
|
-
className: "lfb-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
{
|
|
486
|
-
ref: textareaRef,
|
|
487
|
-
className: "lfb-textarea",
|
|
488
|
-
placeholder: "What's on your mind?",
|
|
489
|
-
value: text,
|
|
490
|
-
onChange: (e) => setText(e.target.value),
|
|
491
|
-
maxLength: 5e3,
|
|
492
|
-
required: true,
|
|
493
|
-
disabled: submitting,
|
|
494
|
-
rows: 3,
|
|
495
|
-
onKeyDown: (e) => {
|
|
496
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
497
|
-
e.preventDefault();
|
|
498
|
-
handleSubmit(e);
|
|
530
|
+
ref: textareaRef,
|
|
531
|
+
className: "lfb-textarea",
|
|
532
|
+
placeholder: "What's on your mind?",
|
|
533
|
+
value: text,
|
|
534
|
+
onChange: (e) => setText(e.target.value),
|
|
535
|
+
maxLength: 5e3,
|
|
536
|
+
required: true,
|
|
537
|
+
disabled: submitting,
|
|
538
|
+
rows: 3,
|
|
539
|
+
onKeyDown: (e) => {
|
|
540
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
541
|
+
e.preventDefault();
|
|
542
|
+
handleSubmit(e);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
499
545
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
546
|
+
),
|
|
547
|
+
editingName ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
548
|
+
"input",
|
|
549
|
+
{
|
|
550
|
+
ref: nameEditRef,
|
|
551
|
+
className: "lfb-input",
|
|
552
|
+
type: "text",
|
|
553
|
+
value: name,
|
|
554
|
+
maxLength: 80,
|
|
555
|
+
onChange: (e) => setName(e.target.value),
|
|
556
|
+
onBlur: () => {
|
|
557
|
+
const t = name.trim();
|
|
558
|
+
if (t) persistName(t);
|
|
559
|
+
setEditingName(false);
|
|
560
|
+
},
|
|
561
|
+
onKeyDown: (e) => {
|
|
562
|
+
if (e.key === "Enter") {
|
|
563
|
+
e.preventDefault();
|
|
564
|
+
e.target.blur();
|
|
565
|
+
}
|
|
566
|
+
if (e.key === "Escape") {
|
|
567
|
+
e.preventDefault();
|
|
568
|
+
e.stopPropagation();
|
|
569
|
+
setEditingName(false);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
525
572
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
573
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-namerow", children: [
|
|
574
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
575
|
+
"Posting as ",
|
|
576
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lfb-name", children: name || "anonymous" })
|
|
577
|
+
] }),
|
|
578
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "lfb-link", onClick: () => setEditingName(true), children: [
|
|
579
|
+
/* @__PURE__ */ jsxRuntime.jsx(EditIcon, { size: 12 }),
|
|
580
|
+
"change"
|
|
581
|
+
] })
|
|
582
|
+
] }),
|
|
583
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lfb-error", children: error }),
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-actions", children: [
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lfb-btn lfb-btn-ghost", onClick: cancelComposer, disabled: submitting, children: "Cancel" }),
|
|
586
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "lfb-btn lfb-btn-primary", disabled: submitting || !text.trim(), children: submitting ? "Sending\u2026" : "Send to Linear" })
|
|
587
|
+
] })
|
|
536
588
|
] })
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-actions", children: [
|
|
540
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lfb-btn lfb-btn-ghost", onClick: cancelComposer, disabled: submitting, children: "Cancel" }),
|
|
541
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "lfb-btn lfb-btn-primary", disabled: submitting || !text.trim(), children: submitting ? "Sending\u2026" : "Send to Linear" })
|
|
542
|
-
] })
|
|
543
|
-
] }) })
|
|
589
|
+
}
|
|
590
|
+
)
|
|
544
591
|
] }) }),
|
|
545
592
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { ...overlayProps, className: "lfb-fixed-layer", children: [
|
|
546
593
|
mode.kind === "drawing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lfb-draw", role: "presentation", onMouseDown: onDrawMouseDown, onMouseMove: onDrawMouseMove, onMouseUp: onDrawMouseUp, children: [
|
package/dist/react/index.js
CHANGED
|
@@ -193,6 +193,8 @@ var CSS2 = `
|
|
|
193
193
|
|
|
194
194
|
.lfb-rect { position: absolute; border: 2px solid var(--lfb-rect); background: rgba(255,0,85,0.12); border-radius: 3px; pointer-events: none; }
|
|
195
195
|
|
|
196
|
+
.lfb-shield { position: fixed; inset: 0; pointer-events: auto; cursor: default; }
|
|
197
|
+
|
|
196
198
|
.lfb-anchor { position: absolute; pointer-events: auto; }
|
|
197
199
|
|
|
198
200
|
.lfb-card {
|
|
@@ -207,6 +209,7 @@ var CSS2 = `
|
|
|
207
209
|
line-height: 1.4;
|
|
208
210
|
}
|
|
209
211
|
.lfb-composer { position: absolute; top: 100%; left: 0; margin-top: 8px; width: 320px; max-width: calc(100vw - 32px); }
|
|
212
|
+
.lfb-composer--above { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 8px; }
|
|
210
213
|
|
|
211
214
|
.lfb-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
212
215
|
.lfb-eyebrow { font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--lfb-fg-tertiary); }
|
|
@@ -290,6 +293,8 @@ function ensureStyles() {
|
|
|
290
293
|
}
|
|
291
294
|
var MIN_DRAG = 12;
|
|
292
295
|
var DEFAULT_BOX = { width: 220, height: 130 };
|
|
296
|
+
var COMPOSER_WIDTH = 320;
|
|
297
|
+
var COMPOSER_EST_HEIGHT = 320;
|
|
293
298
|
var DEFAULT_TYPES = [
|
|
294
299
|
{ id: "bug", label: "Bug", color: "#ef4444", icon: "bug" },
|
|
295
300
|
{ id: "improvement", label: "Improvement", color: "#22c55e", icon: "improvement" }
|
|
@@ -318,6 +323,11 @@ function FeedbackWidget({
|
|
|
318
323
|
const [drag, setDrag] = useState(null);
|
|
319
324
|
const textareaRef = useRef(null);
|
|
320
325
|
const nameInputRef = useRef(null);
|
|
326
|
+
const nameEditRef = useRef(null);
|
|
327
|
+
const submittingRef = useRef(false);
|
|
328
|
+
submittingRef.current = submitting;
|
|
329
|
+
const editingNameRef = useRef(false);
|
|
330
|
+
editingNameRef.current = editingName;
|
|
321
331
|
useEffect(() => {
|
|
322
332
|
ensureStyles();
|
|
323
333
|
try {
|
|
@@ -327,12 +337,19 @@ function FeedbackWidget({
|
|
|
327
337
|
}
|
|
328
338
|
}, [nameStorageKey]);
|
|
329
339
|
useEffect(() => {
|
|
330
|
-
if (mode.kind
|
|
340
|
+
if (mode.kind === "idle") return;
|
|
331
341
|
const onKey = (e) => {
|
|
332
|
-
if (e.key
|
|
333
|
-
|
|
334
|
-
|
|
342
|
+
if (e.key !== "Escape" || submittingRef.current) return;
|
|
343
|
+
if (editingNameRef.current) {
|
|
344
|
+
setEditingName(false);
|
|
345
|
+
return;
|
|
335
346
|
}
|
|
347
|
+
setMode({ kind: "idle" });
|
|
348
|
+
setDrag(null);
|
|
349
|
+
setText("");
|
|
350
|
+
setError(null);
|
|
351
|
+
setSubmitting(false);
|
|
352
|
+
setEditingName(false);
|
|
336
353
|
};
|
|
337
354
|
document.addEventListener("keydown", onKey);
|
|
338
355
|
return () => document.removeEventListener("keydown", onKey);
|
|
@@ -347,6 +364,22 @@ function FeedbackWidget({
|
|
|
347
364
|
return () => window.clearTimeout(t);
|
|
348
365
|
}
|
|
349
366
|
}, [mode.kind]);
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (editingName) nameEditRef.current?.focus();
|
|
369
|
+
}, [editingName]);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
if (mode.kind !== "drawing" && mode.kind !== "composing") return;
|
|
372
|
+
const el = document.documentElement;
|
|
373
|
+
const prevOverflow = el.style.overflow;
|
|
374
|
+
const prevPaddingRight = el.style.paddingRight;
|
|
375
|
+
const scrollbar = window.innerWidth - el.clientWidth;
|
|
376
|
+
el.style.overflow = "hidden";
|
|
377
|
+
if (scrollbar > 0) el.style.paddingRight = `${scrollbar}px`;
|
|
378
|
+
return () => {
|
|
379
|
+
el.style.overflow = prevOverflow;
|
|
380
|
+
el.style.paddingRight = prevPaddingRight;
|
|
381
|
+
};
|
|
382
|
+
}, [mode.kind]);
|
|
350
383
|
const persistName = (value) => {
|
|
351
384
|
try {
|
|
352
385
|
window.localStorage.setItem(nameStorageKey, value);
|
|
@@ -400,12 +433,17 @@ function FeedbackWidget({
|
|
|
400
433
|
vy = end.y - vh / 2;
|
|
401
434
|
}
|
|
402
435
|
const rect = { x: Math.max(0, vx + window.scrollX), y: Math.max(0, vy + window.scrollY), width: vw, height: vh };
|
|
436
|
+
const fitsBelow = vy + vh + COMPOSER_EST_HEIGHT <= window.innerHeight;
|
|
437
|
+
const fitsAbove = vy >= COMPOSER_EST_HEIGHT;
|
|
438
|
+
const placement = !fitsBelow && fitsAbove ? "above" : "below";
|
|
439
|
+
const maxAnchorX = window.scrollX + Math.max(16, window.innerWidth - COMPOSER_WIDTH - 16);
|
|
440
|
+
const anchorX = Math.min(rect.x, maxAnchorX);
|
|
403
441
|
setDrag(null);
|
|
404
442
|
setText("");
|
|
405
443
|
setIssueType(types[0]?.id ?? "bug");
|
|
406
444
|
setError(null);
|
|
407
445
|
setEditingName(false);
|
|
408
|
-
setMode({ kind: "composing", rect });
|
|
446
|
+
setMode({ kind: "composing", rect, placement, anchorX });
|
|
409
447
|
};
|
|
410
448
|
const cancelComposer = () => {
|
|
411
449
|
setMode({ kind: "idle" });
|
|
@@ -452,93 +490,102 @@ function FeedbackWidget({
|
|
|
452
490
|
} : null;
|
|
453
491
|
return /* @__PURE__ */ jsxs("div", { className: "lfb-root", style: rootStyle, children: [
|
|
454
492
|
/* @__PURE__ */ jsx("div", { ...overlayProps, className: "lfb-doc-layer", children: mode.kind === "composing" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
493
|
+
/* @__PURE__ */ jsx("div", { className: "lfb-shield", "aria-hidden": "true" }),
|
|
455
494
|
/* @__PURE__ */ jsx("div", { className: "lfb-rect", style: { left: mode.rect.x, top: mode.rect.y, width: mode.rect.width, height: mode.rect.height } }),
|
|
456
|
-
/* @__PURE__ */ jsx(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
495
|
+
/* @__PURE__ */ jsx(
|
|
496
|
+
"div",
|
|
497
|
+
{
|
|
498
|
+
className: "lfb-anchor",
|
|
499
|
+
style: { left: mode.anchorX, top: mode.placement === "below" ? mode.rect.y + mode.rect.height : mode.rect.y },
|
|
500
|
+
children: /* @__PURE__ */ jsxs("form", { className: `lfb-card lfb-composer${mode.placement === "above" ? " lfb-composer--above" : ""}`, onSubmit: handleSubmit, children: [
|
|
501
|
+
/* @__PURE__ */ jsxs("div", { className: "lfb-row", children: [
|
|
502
|
+
/* @__PURE__ */ jsx("span", { className: "lfb-eyebrow", children: "New feedback" }),
|
|
503
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lfb-iconbtn", "aria-label": "Cancel", onClick: cancelComposer, children: /* @__PURE__ */ jsx(XIcon, {}) })
|
|
504
|
+
] }),
|
|
505
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
|
|
506
|
+
/* @__PURE__ */ jsx("span", { className: "lfb-field-label", children: "Issue type" }),
|
|
507
|
+
/* @__PURE__ */ jsx("div", { className: "lfb-types", children: types.map((t) => {
|
|
508
|
+
const Icon = TYPE_ICONS[t.icon ?? "dot"];
|
|
509
|
+
return /* @__PURE__ */ jsxs(
|
|
510
|
+
"button",
|
|
511
|
+
{
|
|
512
|
+
type: "button",
|
|
513
|
+
className: "lfb-type",
|
|
514
|
+
"aria-pressed": issueType === t.id,
|
|
515
|
+
onClick: () => setIssueType(t.id),
|
|
516
|
+
children: [
|
|
517
|
+
/* @__PURE__ */ jsx("span", { className: "lfb-swatch", style: { background: t.color }, children: /* @__PURE__ */ jsx(Icon, { size: 14 }) }),
|
|
518
|
+
t.label
|
|
519
|
+
]
|
|
520
|
+
},
|
|
521
|
+
t.id
|
|
522
|
+
);
|
|
523
|
+
}) })
|
|
524
|
+
] }),
|
|
525
|
+
/* @__PURE__ */ jsx(
|
|
526
|
+
"textarea",
|
|
467
527
|
{
|
|
468
|
-
|
|
469
|
-
className: "lfb-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
{
|
|
484
|
-
ref: textareaRef,
|
|
485
|
-
className: "lfb-textarea",
|
|
486
|
-
placeholder: "What's on your mind?",
|
|
487
|
-
value: text,
|
|
488
|
-
onChange: (e) => setText(e.target.value),
|
|
489
|
-
maxLength: 5e3,
|
|
490
|
-
required: true,
|
|
491
|
-
disabled: submitting,
|
|
492
|
-
rows: 3,
|
|
493
|
-
onKeyDown: (e) => {
|
|
494
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
495
|
-
e.preventDefault();
|
|
496
|
-
handleSubmit(e);
|
|
528
|
+
ref: textareaRef,
|
|
529
|
+
className: "lfb-textarea",
|
|
530
|
+
placeholder: "What's on your mind?",
|
|
531
|
+
value: text,
|
|
532
|
+
onChange: (e) => setText(e.target.value),
|
|
533
|
+
maxLength: 5e3,
|
|
534
|
+
required: true,
|
|
535
|
+
disabled: submitting,
|
|
536
|
+
rows: 3,
|
|
537
|
+
onKeyDown: (e) => {
|
|
538
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
539
|
+
e.preventDefault();
|
|
540
|
+
handleSubmit(e);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
497
543
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
544
|
+
),
|
|
545
|
+
editingName ? /* @__PURE__ */ jsx(
|
|
546
|
+
"input",
|
|
547
|
+
{
|
|
548
|
+
ref: nameEditRef,
|
|
549
|
+
className: "lfb-input",
|
|
550
|
+
type: "text",
|
|
551
|
+
value: name,
|
|
552
|
+
maxLength: 80,
|
|
553
|
+
onChange: (e) => setName(e.target.value),
|
|
554
|
+
onBlur: () => {
|
|
555
|
+
const t = name.trim();
|
|
556
|
+
if (t) persistName(t);
|
|
557
|
+
setEditingName(false);
|
|
558
|
+
},
|
|
559
|
+
onKeyDown: (e) => {
|
|
560
|
+
if (e.key === "Enter") {
|
|
561
|
+
e.preventDefault();
|
|
562
|
+
e.target.blur();
|
|
563
|
+
}
|
|
564
|
+
if (e.key === "Escape") {
|
|
565
|
+
e.preventDefault();
|
|
566
|
+
e.stopPropagation();
|
|
567
|
+
setEditingName(false);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
523
570
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
571
|
+
) : /* @__PURE__ */ jsxs("div", { className: "lfb-namerow", children: [
|
|
572
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
573
|
+
"Posting as ",
|
|
574
|
+
/* @__PURE__ */ jsx("span", { className: "lfb-name", children: name || "anonymous" })
|
|
575
|
+
] }),
|
|
576
|
+
/* @__PURE__ */ jsxs("button", { type: "button", className: "lfb-link", onClick: () => setEditingName(true), children: [
|
|
577
|
+
/* @__PURE__ */ jsx(EditIcon, { size: 12 }),
|
|
578
|
+
"change"
|
|
579
|
+
] })
|
|
580
|
+
] }),
|
|
581
|
+
error && /* @__PURE__ */ jsx("p", { className: "lfb-error", children: error }),
|
|
582
|
+
/* @__PURE__ */ jsxs("div", { className: "lfb-actions", children: [
|
|
583
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "lfb-btn lfb-btn-ghost", onClick: cancelComposer, disabled: submitting, children: "Cancel" }),
|
|
584
|
+
/* @__PURE__ */ jsx("button", { type: "submit", className: "lfb-btn lfb-btn-primary", disabled: submitting || !text.trim(), children: submitting ? "Sending\u2026" : "Send to Linear" })
|
|
585
|
+
] })
|
|
534
586
|
] })
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
/* @__PURE__ */ jsxs("div", { className: "lfb-actions", children: [
|
|
538
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "lfb-btn lfb-btn-ghost", onClick: cancelComposer, disabled: submitting, children: "Cancel" }),
|
|
539
|
-
/* @__PURE__ */ jsx("button", { type: "submit", className: "lfb-btn lfb-btn-primary", disabled: submitting || !text.trim(), children: submitting ? "Sending\u2026" : "Send to Linear" })
|
|
540
|
-
] })
|
|
541
|
-
] }) })
|
|
587
|
+
}
|
|
588
|
+
)
|
|
542
589
|
] }) }),
|
|
543
590
|
/* @__PURE__ */ jsxs("div", { ...overlayProps, className: "lfb-fixed-layer", children: [
|
|
544
591
|
mode.kind === "drawing" && /* @__PURE__ */ jsxs("div", { className: "lfb-draw", role: "presentation", onMouseDown: onDrawMouseDown, onMouseMove: onDrawMouseMove, onMouseUp: onDrawMouseUp, children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-linear-feedback",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Drop-in feedback widget for React apps or any site via a script tag: draw a box, write a note, and it captures a screenshot and opens a Linear issue. Self-contained styles, zero design-system dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Oliver Odgaard",
|