@trafica/editor 1.0.23 → 1.0.24

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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { forwardRef, useRef, useEffect, useCallback, useMemo, useImperativeHandle, useState, useLayoutEffect, useSyncExternalStore } from 'react';
2
- import { createPortal } from 'react-dom';
3
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { createPortal } from 'react-dom';
4
4
 
5
5
  // src/editor/core/DocumentModel.ts
6
6
  function isTextNode(node) {
@@ -3319,15 +3319,13 @@ var OPTIONS = [
3319
3319
  function AlignmentDropdown({ engine, activeAlignment }) {
3320
3320
  var _a;
3321
3321
  const [open, setOpen] = useState(false);
3322
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
3323
- const buttonRef = useRef(null);
3324
- const popupRef = useRef(null);
3322
+ const containerRef = useRef(null);
3325
3323
  useEffect(() => {
3326
3324
  if (!open) return;
3327
3325
  const handler = (e) => {
3328
- var _a2, _b;
3329
- const target = e.target;
3330
- if (!((_a2 = popupRef.current) == null ? void 0 : _a2.contains(target)) && !((_b = buttonRef.current) == null ? void 0 : _b.contains(target))) setOpen(false);
3326
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3327
+ setOpen(false);
3328
+ }
3331
3329
  };
3332
3330
  document.addEventListener("mousedown", handler);
3333
3331
  return () => document.removeEventListener("mousedown", handler);
@@ -3341,31 +3339,22 @@ function AlignmentDropdown({ engine, activeAlignment }) {
3341
3339
  return () => document.removeEventListener("keydown", handler);
3342
3340
  }, [open]);
3343
3341
  const activeOption = (_a = OPTIONS.find((o) => o.value === activeAlignment)) != null ? _a : OPTIONS[0];
3344
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3342
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
3345
3343
  /* @__PURE__ */ jsxs(
3346
3344
  "button",
3347
3345
  {
3348
- ref: buttonRef,
3349
3346
  type: "button",
3350
3347
  title: `Text alignment (${activeOption.label})`,
3351
3348
  "aria-haspopup": "listbox",
3352
3349
  "aria-expanded": open,
3353
3350
  onMouseDown: (e) => {
3354
3351
  e.preventDefault();
3355
- if (open) {
3356
- setOpen(false);
3357
- return;
3358
- }
3359
- if (buttonRef.current) {
3360
- const rect = buttonRef.current.getBoundingClientRect();
3361
- setPopupPos({ top: rect.bottom + 4, left: rect.left });
3362
- }
3363
- setOpen(true);
3352
+ setOpen((prev) => !prev);
3364
3353
  },
3365
3354
  className: [
3366
3355
  "flex items-center gap-0.5 px-1.5 h-8 rounded text-sm font-medium transition-colors",
3367
3356
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
3368
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3357
+ open ? "bg-gray-100 text-gray-900 " : "text-gray-700 hover:bg-gray-100 "
3369
3358
  ].join(" "),
3370
3359
  children: [
3371
3360
  activeOption.icon,
@@ -3373,45 +3362,40 @@ function AlignmentDropdown({ engine, activeAlignment }) {
3373
3362
  ]
3374
3363
  }
3375
3364
  ),
3376
- open && createPortal(
3377
- /* @__PURE__ */ jsx(
3378
- "div",
3379
- {
3380
- ref: popupRef,
3381
- role: "listbox",
3382
- "aria-label": "Text alignment",
3383
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, minWidth: 168 },
3384
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",
3385
- children: OPTIONS.map((option) => {
3386
- const isActive = activeAlignment === option.value;
3387
- return /* @__PURE__ */ jsxs(
3388
- "button",
3389
- {
3390
- type: "button",
3391
- role: "option",
3392
- "aria-selected": isActive,
3393
- title: `${option.label} (${option.shortcut})`,
3394
- onMouseDown: (e) => {
3395
- e.preventDefault();
3396
- setAlignment(option.value)(engine);
3397
- setOpen(false);
3398
- },
3399
- className: [
3400
- "w-full flex items-center gap-3 px-3 py-1.5 text-sm transition-colors",
3401
- isActive ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3402
- ].join(" "),
3403
- children: [
3404
- /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: option.icon }),
3405
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: option.label }),
3406
- isActive && /* @__PURE__ */ jsx(CheckIcon, {})
3407
- ]
3365
+ open && /* @__PURE__ */ jsx(
3366
+ "div",
3367
+ {
3368
+ role: "listbox",
3369
+ "aria-label": "Text alignment",
3370
+ className: "absolute top-full left-0 mt-1 z-50 bg-white border border-gray-200 rounded-lg shadow-lg py-1 min-w-[168px]",
3371
+ children: OPTIONS.map((option) => {
3372
+ const isActive = activeAlignment === option.value;
3373
+ return /* @__PURE__ */ jsxs(
3374
+ "button",
3375
+ {
3376
+ type: "button",
3377
+ role: "option",
3378
+ "aria-selected": isActive,
3379
+ title: `${option.label} (${option.shortcut})`,
3380
+ onMouseDown: (e) => {
3381
+ e.preventDefault();
3382
+ setAlignment(option.value)(engine);
3383
+ setOpen(false);
3408
3384
  },
3409
- option.value
3410
- );
3411
- })
3412
- }
3413
- ),
3414
- document.body
3385
+ className: [
3386
+ "w-full flex items-center gap-3 px-3 py-1.5 text-sm transition-colors",
3387
+ isActive ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
3388
+ ].join(" "),
3389
+ children: [
3390
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: option.icon }),
3391
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: option.label }),
3392
+ isActive && /* @__PURE__ */ jsx(CheckIcon, {})
3393
+ ]
3394
+ },
3395
+ option.value
3396
+ );
3397
+ })
3398
+ }
3415
3399
  )
3416
3400
  ] });
3417
3401
  }
@@ -3456,15 +3440,11 @@ function CheckIcon() {
3456
3440
  var SIZES = ["8", "10", "12", "14", "16", "18", "24", "32", "48"];
3457
3441
  function FontSizeDropdown({ engine, activeFontSize }) {
3458
3442
  const [open, setOpen] = useState(false);
3459
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
3460
- const buttonRef = useRef(null);
3461
- const popupRef = useRef(null);
3443
+ const containerRef = useRef(null);
3462
3444
  useEffect(() => {
3463
3445
  if (!open) return;
3464
3446
  const handler = (e) => {
3465
- var _a, _b;
3466
- const target = e.target;
3467
- if (!((_a = popupRef.current) == null ? void 0 : _a.contains(target)) && !((_b = buttonRef.current) == null ? void 0 : _b.contains(target))) {
3447
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3468
3448
  setOpen(false);
3469
3449
  }
3470
3450
  };
@@ -3479,36 +3459,24 @@ function FontSizeDropdown({ engine, activeFontSize }) {
3479
3459
  document.addEventListener("keydown", handler);
3480
3460
  return () => document.removeEventListener("keydown", handler);
3481
3461
  }, [open]);
3482
- function openDropdown() {
3483
- if (buttonRef.current) {
3484
- const rect = buttonRef.current.getBoundingClientRect();
3485
- setPopupPos({ top: rect.bottom + 4, left: rect.left });
3486
- }
3487
- setOpen(true);
3488
- }
3489
3462
  const activeNum = activeFontSize ? activeFontSize.replace("px", "") : null;
3490
3463
  const label = activeNum != null ? activeNum : "Size";
3491
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3464
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
3492
3465
  /* @__PURE__ */ jsxs(
3493
3466
  "button",
3494
3467
  {
3495
- ref: buttonRef,
3496
3468
  type: "button",
3497
3469
  title: "Font size",
3498
3470
  "aria-haspopup": "listbox",
3499
3471
  "aria-expanded": open,
3500
3472
  onMouseDown: (e) => {
3501
3473
  e.preventDefault();
3502
- if (open) {
3503
- setOpen(false);
3504
- } else {
3505
- openDropdown();
3506
- }
3474
+ setOpen((prev) => !prev);
3507
3475
  },
3508
3476
  className: [
3509
3477
  "flex items-center gap-0.5 px-1.5 h-8 rounded text-sm font-medium transition-colors min-w-[52px]",
3510
3478
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
3511
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3479
+ open ? "bg-gray-100 text-gray-900 " : "text-gray-700 hover:bg-gray-100 "
3512
3480
  ].join(" "),
3513
3481
  children: [
3514
3482
  /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: label }),
@@ -3516,66 +3484,61 @@ function FontSizeDropdown({ engine, activeFontSize }) {
3516
3484
  ]
3517
3485
  }
3518
3486
  ),
3519
- open && createPortal(
3520
- /* @__PURE__ */ jsxs(
3521
- "div",
3522
- {
3523
- ref: popupRef,
3524
- role: "listbox",
3525
- "aria-label": "Font size",
3526
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, minWidth: 88, maxHeight: 256, overflowY: "auto" },
3527
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",
3528
- children: [
3529
- /* @__PURE__ */ jsxs(
3487
+ open && /* @__PURE__ */ jsxs(
3488
+ "div",
3489
+ {
3490
+ role: "listbox",
3491
+ "aria-label": "Font size",
3492
+ className: "absolute top-full left-0 mt-1 z-50 bg-white border border-gray-200 rounded-lg shadow-lg py-1 min-w-[88px] max-h-64 overflow-y-auto",
3493
+ children: [
3494
+ /* @__PURE__ */ jsxs(
3495
+ "button",
3496
+ {
3497
+ type: "button",
3498
+ role: "option",
3499
+ "aria-selected": activeFontSize === null,
3500
+ onMouseDown: (e) => {
3501
+ e.preventDefault();
3502
+ setFontSize(null)(engine);
3503
+ setOpen(false);
3504
+ },
3505
+ className: [
3506
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm transition-colors",
3507
+ activeFontSize === null ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
3508
+ ].join(" "),
3509
+ children: [
3510
+ /* @__PURE__ */ jsx("span", { children: "Default" }),
3511
+ activeFontSize === null && /* @__PURE__ */ jsx(CheckIcon2, {})
3512
+ ]
3513
+ }
3514
+ ),
3515
+ SIZES.map((size) => {
3516
+ const isActive = activeNum === size;
3517
+ return /* @__PURE__ */ jsxs(
3530
3518
  "button",
3531
3519
  {
3532
3520
  type: "button",
3533
3521
  role: "option",
3534
- "aria-selected": activeFontSize === null,
3522
+ "aria-selected": isActive,
3535
3523
  onMouseDown: (e) => {
3536
3524
  e.preventDefault();
3537
- setFontSize(null)(engine);
3525
+ setFontSize(`${size}px`)(engine);
3538
3526
  setOpen(false);
3539
3527
  },
3540
3528
  className: [
3541
- "w-full flex items-center justify-between px-3 py-1.5 text-sm transition-colors",
3542
- activeFontSize === null ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3529
+ "w-full flex items-center justify-between px-3 py-1.5 transition-colors",
3530
+ isActive ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
3543
3531
  ].join(" "),
3544
3532
  children: [
3545
- /* @__PURE__ */ jsx("span", { children: "Default" }),
3546
- activeFontSize === null && /* @__PURE__ */ jsx(CheckIcon2, {})
3533
+ /* @__PURE__ */ jsx("span", { style: { fontSize: `${size}px`, lineHeight: 1.3 }, children: size }),
3534
+ isActive && /* @__PURE__ */ jsx(CheckIcon2, {})
3547
3535
  ]
3548
- }
3549
- ),
3550
- SIZES.map((size) => {
3551
- const isActive = activeNum === size;
3552
- return /* @__PURE__ */ jsxs(
3553
- "button",
3554
- {
3555
- type: "button",
3556
- role: "option",
3557
- "aria-selected": isActive,
3558
- onMouseDown: (e) => {
3559
- e.preventDefault();
3560
- setFontSize(`${size}px`)(engine);
3561
- setOpen(false);
3562
- },
3563
- className: [
3564
- "w-full flex items-center justify-between px-3 py-1.5 transition-colors",
3565
- isActive ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3566
- ].join(" "),
3567
- children: [
3568
- /* @__PURE__ */ jsx("span", { style: { fontSize: `${size}px`, lineHeight: 1.3 }, children: size }),
3569
- isActive && /* @__PURE__ */ jsx(CheckIcon2, {})
3570
- ]
3571
- },
3572
- size
3573
- );
3574
- })
3575
- ]
3576
- }
3577
- ),
3578
- document.body
3536
+ },
3537
+ size
3538
+ );
3539
+ })
3540
+ ]
3541
+ }
3579
3542
  )
3580
3543
  ] });
3581
3544
  }
@@ -3601,16 +3564,12 @@ function FontFamilyDropdown({ engine, activeFontFamily }) {
3601
3564
  const [open, setOpen] = useState(false);
3602
3565
  const [showCustomInput, setShowCustomInput] = useState(false);
3603
3566
  const [customValue, setCustomValue] = useState("");
3604
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
3605
- const buttonRef = useRef(null);
3606
- const popupRef = useRef(null);
3567
+ const containerRef = useRef(null);
3607
3568
  const customInputRef = useRef(null);
3608
3569
  useEffect(() => {
3609
3570
  if (!open) return;
3610
3571
  const handler = (e) => {
3611
- var _a2, _b;
3612
- const target = e.target;
3613
- if (!((_a2 = popupRef.current) == null ? void 0 : _a2.contains(target)) && !((_b = buttonRef.current) == null ? void 0 : _b.contains(target))) {
3572
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3614
3573
  closeDropdown();
3615
3574
  }
3616
3575
  };
@@ -3629,17 +3588,6 @@ function FontFamilyDropdown({ engine, activeFontFamily }) {
3629
3588
  var _a2;
3630
3589
  if (showCustomInput) (_a2 = customInputRef.current) == null ? void 0 : _a2.focus();
3631
3590
  }, [showCustomInput]);
3632
- function openDropdown() {
3633
- if (buttonRef.current) {
3634
- const rect = buttonRef.current.getBoundingClientRect();
3635
- const popupWidth = 200;
3636
- const left = Math.min(rect.left, window.innerWidth - popupWidth - 8);
3637
- setPopupPos({ top: rect.bottom + 4, left: Math.max(8, left) });
3638
- }
3639
- setShowCustomInput(false);
3640
- setCustomValue("");
3641
- setOpen(true);
3642
- }
3643
3591
  function closeDropdown() {
3644
3592
  setOpen(false);
3645
3593
  setShowCustomInput(false);
@@ -3651,11 +3599,10 @@ function FontFamilyDropdown({ engine, activeFontFamily }) {
3651
3599
  }
3652
3600
  const activeOption = activeFontFamily ? (_a = FONTS.find((f) => f.value === activeFontFamily)) != null ? _a : null : null;
3653
3601
  const triggerLabel = activeOption ? activeOption.label : activeFontFamily ? activeFontFamily.split(",")[0].replace(/['"]/g, "").trim().slice(0, 10) : "Font";
3654
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3602
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
3655
3603
  /* @__PURE__ */ jsxs(
3656
3604
  "button",
3657
3605
  {
3658
- ref: buttonRef,
3659
3606
  type: "button",
3660
3607
  title: "Font family",
3661
3608
  "aria-haspopup": "listbox",
@@ -3664,14 +3611,16 @@ function FontFamilyDropdown({ engine, activeFontFamily }) {
3664
3611
  e.preventDefault();
3665
3612
  if (open) {
3666
3613
  closeDropdown();
3667
- } else {
3668
- openDropdown();
3614
+ return;
3669
3615
  }
3616
+ setShowCustomInput(false);
3617
+ setCustomValue("");
3618
+ setOpen(true);
3670
3619
  },
3671
3620
  className: [
3672
3621
  "flex items-center gap-0.5 px-1.5 h-8 rounded text-sm font-medium transition-colors min-w-[60px] max-w-[120px]",
3673
3622
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
3674
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3623
+ open ? "bg-gray-100 text-gray-900 " : "text-gray-700 hover:bg-gray-100 "
3675
3624
  ].join(" "),
3676
3625
  children: [
3677
3626
  /* @__PURE__ */ jsx(FontIcon, {}),
@@ -3680,65 +3629,60 @@ function FontFamilyDropdown({ engine, activeFontFamily }) {
3680
3629
  ]
3681
3630
  }
3682
3631
  ),
3683
- open && createPortal(
3684
- /* @__PURE__ */ jsxs(
3685
- "div",
3686
- {
3687
- ref: popupRef,
3688
- role: "listbox",
3689
- "aria-label": "Font family",
3690
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, minWidth: 200 },
3691
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",
3692
- children: [
3693
- /* @__PURE__ */ jsxs(
3632
+ open && /* @__PURE__ */ jsxs(
3633
+ "div",
3634
+ {
3635
+ role: "listbox",
3636
+ "aria-label": "Font family",
3637
+ className: "absolute top-full left-0 mt-1 z-50 bg-white border border-gray-200 rounded-lg shadow-lg py-1 min-w-[200px]",
3638
+ children: [
3639
+ /* @__PURE__ */ jsxs(
3640
+ "button",
3641
+ {
3642
+ type: "button",
3643
+ role: "option",
3644
+ "aria-selected": activeFontFamily === null,
3645
+ onMouseDown: (e) => {
3646
+ e.preventDefault();
3647
+ applyFont(null);
3648
+ },
3649
+ className: [
3650
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm transition-colors",
3651
+ activeFontFamily === null ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
3652
+ ].join(" "),
3653
+ children: [
3654
+ /* @__PURE__ */ jsx("span", { children: "Default" }),
3655
+ activeFontFamily === null && /* @__PURE__ */ jsx(CheckIcon3, {})
3656
+ ]
3657
+ }
3658
+ ),
3659
+ /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-gray-100 " }),
3660
+ FONTS.map((font) => {
3661
+ const isActive = activeFontFamily === font.value;
3662
+ return /* @__PURE__ */ jsxs(
3694
3663
  "button",
3695
3664
  {
3696
3665
  type: "button",
3697
3666
  role: "option",
3698
- "aria-selected": activeFontFamily === null,
3667
+ "aria-selected": isActive,
3699
3668
  onMouseDown: (e) => {
3700
3669
  e.preventDefault();
3701
- applyFont(null);
3670
+ applyFont(font.value);
3702
3671
  },
3703
3672
  className: [
3704
3673
  "w-full flex items-center justify-between px-3 py-1.5 text-sm transition-colors",
3705
- activeFontFamily === null ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3674
+ isActive ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
3706
3675
  ].join(" "),
3707
3676
  children: [
3708
- /* @__PURE__ */ jsx("span", { children: "Default" }),
3709
- activeFontFamily === null && /* @__PURE__ */ jsx(CheckIcon3, {})
3677
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: font.value }, children: font.label }),
3678
+ isActive && /* @__PURE__ */ jsx(CheckIcon3, {})
3710
3679
  ]
3711
- }
3712
- ),
3713
- /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-gray-100 dark:border-gray-700" }),
3714
- FONTS.map((font) => {
3715
- const isActive = activeFontFamily === font.value;
3716
- return /* @__PURE__ */ jsxs(
3717
- "button",
3718
- {
3719
- type: "button",
3720
- role: "option",
3721
- "aria-selected": isActive,
3722
- onMouseDown: (e) => {
3723
- e.preventDefault();
3724
- applyFont(font.value);
3725
- },
3726
- className: [
3727
- "w-full flex items-center justify-between px-3 py-1.5 text-sm transition-colors",
3728
- isActive ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
3729
- ].join(" "),
3730
- children: [
3731
- /* @__PURE__ */ jsx("span", { style: { fontFamily: font.value }, children: font.label }),
3732
- isActive && /* @__PURE__ */ jsx(CheckIcon3, {})
3733
- ]
3734
- },
3735
- font.value
3736
- );
3737
- })
3738
- ]
3739
- }
3740
- ),
3741
- document.body
3680
+ },
3681
+ font.value
3682
+ );
3683
+ })
3684
+ ]
3685
+ }
3742
3686
  )
3743
3687
  ] });
3744
3688
  }
@@ -3784,15 +3728,11 @@ function BackgroundColorDropdown({
3784
3728
  const [lastColor, setLastColor] = useState(PRESET_COLORS[6].value);
3785
3729
  const [hexInput, setHexInput] = useState("");
3786
3730
  const [hexError, setHexError] = useState(false);
3787
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
3788
- const chevronRef = useRef(null);
3789
- const popupRef = useRef(null);
3731
+ const containerRef = useRef(null);
3790
3732
  useEffect(() => {
3791
3733
  if (!open) return;
3792
3734
  const handler = (e) => {
3793
- var _a, _b;
3794
- const target = e.target;
3795
- if (!((_a = popupRef.current) == null ? void 0 : _a.contains(target)) && !((_b = chevronRef.current) == null ? void 0 : _b.contains(target)))
3735
+ if (containerRef.current && !containerRef.current.contains(e.target))
3796
3736
  setOpen(false);
3797
3737
  };
3798
3738
  document.addEventListener("mousedown", handler);
@@ -3835,7 +3775,7 @@ function BackgroundColorDropdown({
3835
3775
  (c) => !PRESET_COLORS.some((p) => normalizeHex(p.value) === normalizeHex(c))
3836
3776
  );
3837
3777
  const normActive = activeColor ? normalizeHex(activeColor) : null;
3838
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
3778
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative flex items-center", children: [
3839
3779
  /* @__PURE__ */ jsxs(
3840
3780
  "button",
3841
3781
  {
@@ -3845,7 +3785,7 @@ function BackgroundColorDropdown({
3845
3785
  e.preventDefault();
3846
3786
  handleQuickApply();
3847
3787
  },
3848
- className: "flex flex-col items-center justify-center gap-0.5 w-8 h-8 rounded-l\n text-gray-700 dark:text-gray-300\n hover:bg-gray-100 dark:hover:bg-gray-700\n focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500\n transition-colors",
3788
+ className: "flex flex-col items-center justify-center gap-0.5 w-8 h-8 rounded-l\r\n text-gray-700 \r\n hover:bg-gray-100 \r\n focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500\r\n transition-colors",
3849
3789
  children: [
3850
3790
  /* @__PURE__ */ jsx(BgColorIcon, {}),
3851
3791
  /* @__PURE__ */ jsx(ColorBar, { color: normActive != null ? normActive : lastColor })
@@ -3855,145 +3795,135 @@ function BackgroundColorDropdown({
3855
3795
  /* @__PURE__ */ jsx(
3856
3796
  "button",
3857
3797
  {
3858
- ref: chevronRef,
3859
3798
  type: "button",
3860
3799
  title: "More background colors",
3861
3800
  "aria-haspopup": "listbox",
3862
3801
  "aria-expanded": open,
3863
3802
  onMouseDown: (e) => {
3864
3803
  e.preventDefault();
3865
- if (open) {
3866
- setOpen(false);
3867
- return;
3868
- }
3869
- if (chevronRef.current) {
3870
- const rect = chevronRef.current.getBoundingClientRect();
3871
- setPopupPos({ top: rect.bottom + 4, left: rect.left - 190 });
3872
- }
3873
- setHexInput("");
3874
- setHexError(false);
3875
- setOpen(true);
3804
+ setOpen((p) => {
3805
+ if (!p) {
3806
+ setHexInput("");
3807
+ setHexError(false);
3808
+ }
3809
+ return !p;
3810
+ });
3876
3811
  },
3877
3812
  className: [
3878
- "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 dark:border-gray-600",
3813
+ "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 ",
3879
3814
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500",
3880
3815
  "transition-colors",
3881
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200" : "text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
3816
+ open ? "bg-gray-100 text-gray-700 " : "text-gray-400 hover:bg-gray-100 "
3882
3817
  ].join(" "),
3883
3818
  children: /* @__PURE__ */ jsx(ChevronDownIcon4, {})
3884
3819
  }
3885
3820
  ),
3886
- open && createPortal(
3887
- /* @__PURE__ */ jsxs(
3888
- "div",
3889
- {
3890
- ref: popupRef,
3891
- role: "dialog",
3892
- "aria-label": "Background color",
3893
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, width: 208 },
3894
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-xl p-3",
3895
- children: [
3821
+ open && /* @__PURE__ */ jsxs(
3822
+ "div",
3823
+ {
3824
+ role: "dialog",
3825
+ "aria-label": "Background color",
3826
+ className: "absolute top-full left-0 mt-1 z-50\r\n bg-white \r\n border border-gray-200 \r\n rounded-lg shadow-xl\r\n p-3 w-[208px]",
3827
+ children: [
3828
+ /* @__PURE__ */ jsxs(
3829
+ "button",
3830
+ {
3831
+ type: "button",
3832
+ onMouseDown: (e) => {
3833
+ e.preventDefault();
3834
+ applyColor(null);
3835
+ },
3836
+ className: "flex items-center gap-2 w-full px-2 py-1.5 mb-2.5 text-xs font-medium\r\n text-gray-600 \r\n rounded hover:bg-gray-100 \r\n transition-colors",
3837
+ children: [
3838
+ /* @__PURE__ */ jsx(RemoveColorIcon, {}),
3839
+ "Remove background"
3840
+ ]
3841
+ }
3842
+ ),
3843
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" }),
3844
+ extraDocColors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3845
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 mb-1.5 px-0.5", children: "Document colors" }),
3846
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-2.5", children: extraDocColors.slice(0, 10).map((c) => /* @__PURE__ */ jsx(
3847
+ Swatch,
3848
+ {
3849
+ color: { label: c, value: c },
3850
+ isActive: normActive === normalizeHex(c),
3851
+ onSelect: applyColor
3852
+ },
3853
+ c
3854
+ )) }),
3855
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" })
3856
+ ] }),
3857
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-3", children: PRESET_COLORS.map((c) => /* @__PURE__ */ jsx(
3858
+ Swatch,
3859
+ {
3860
+ color: c,
3861
+ isActive: normActive === normalizeHex(c.value),
3862
+ onSelect: applyColor
3863
+ },
3864
+ c.value
3865
+ )) }),
3866
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" }),
3867
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 mb-1.5 px-0.5", children: "Custom color" }),
3868
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3869
+ /* @__PURE__ */ jsx(
3870
+ "div",
3871
+ {
3872
+ className: "shrink-0 w-7 h-7 rounded border border-gray-300 ",
3873
+ style: { backgroundColor: hexInputToPreview(hexInput) }
3874
+ }
3875
+ ),
3896
3876
  /* @__PURE__ */ jsxs(
3877
+ "div",
3878
+ {
3879
+ className: "flex flex-1 items-center border rounded overflow-hidden\r\n border-gray-300 \r\n focus-within:ring-2 focus-within:ring-blue-500",
3880
+ children: [
3881
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-gray-400 select-none", children: "#" }),
3882
+ /* @__PURE__ */ jsx(
3883
+ "input",
3884
+ {
3885
+ type: "text",
3886
+ maxLength: 6,
3887
+ value: hexInput,
3888
+ onChange: (e) => {
3889
+ setHexInput(e.target.value.replace(/[^0-9a-fA-F]/g, ""));
3890
+ setHexError(false);
3891
+ },
3892
+ onKeyDown: (e) => {
3893
+ if (e.key === "Enter") {
3894
+ e.preventDefault();
3895
+ commitHexInput();
3896
+ }
3897
+ if (e.key === "Escape") setOpen(false);
3898
+ },
3899
+ placeholder: "e.g. ffff00",
3900
+ className: [
3901
+ "flex-1 px-1 py-1.5 text-xs bg-transparent outline-none font-mono",
3902
+ "text-gray-900 ",
3903
+ hexError ? "text-red-500" : ""
3904
+ ].join(" "),
3905
+ "aria-label": "Hex colour value"
3906
+ }
3907
+ )
3908
+ ]
3909
+ }
3910
+ ),
3911
+ /* @__PURE__ */ jsx(
3897
3912
  "button",
3898
3913
  {
3899
3914
  type: "button",
3900
3915
  onMouseDown: (e) => {
3901
3916
  e.preventDefault();
3902
- applyColor(null);
3917
+ commitHexInput();
3903
3918
  },
3904
- className: "flex items-center gap-2 w-full px-2 py-1.5 mb-2.5 text-xs font-medium\n text-gray-600 dark:text-gray-300\n rounded hover:bg-gray-100 dark:hover:bg-gray-700\n transition-colors",
3905
- children: [
3906
- /* @__PURE__ */ jsx(RemoveColorIcon, {}),
3907
- "Remove background"
3908
- ]
3919
+ className: "shrink-0 px-2 py-1.5 text-xs bg-blue-600 text-white rounded\r\n hover:bg-blue-700 transition-colors",
3920
+ children: "\u2713"
3909
3921
  }
3910
- ),
3911
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" }),
3912
- extraDocColors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3913
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 dark:text-gray-500 mb-1.5 px-0.5", children: "Document colors" }),
3914
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-2.5", children: extraDocColors.slice(0, 10).map((c) => /* @__PURE__ */ jsx(
3915
- Swatch,
3916
- {
3917
- color: { label: c, value: c },
3918
- isActive: normActive === normalizeHex(c),
3919
- onSelect: applyColor
3920
- },
3921
- c
3922
- )) }),
3923
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" })
3924
- ] }),
3925
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-3", children: PRESET_COLORS.map((c) => /* @__PURE__ */ jsx(
3926
- Swatch,
3927
- {
3928
- color: c,
3929
- isActive: normActive === normalizeHex(c.value),
3930
- onSelect: applyColor
3931
- },
3932
- c.value
3933
- )) }),
3934
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" }),
3935
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 dark:text-gray-500 mb-1.5 px-0.5", children: "Custom color" }),
3936
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3937
- /* @__PURE__ */ jsx(
3938
- "div",
3939
- {
3940
- className: "shrink-0 w-7 h-7 rounded border border-gray-300 dark:border-gray-600",
3941
- style: { backgroundColor: hexInputToPreview(hexInput) }
3942
- }
3943
- ),
3944
- /* @__PURE__ */ jsxs(
3945
- "div",
3946
- {
3947
- className: "flex flex-1 items-center border rounded overflow-hidden\n border-gray-300 dark:border-gray-600\n focus-within:ring-2 focus-within:ring-blue-500",
3948
- children: [
3949
- /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-gray-400 select-none", children: "#" }),
3950
- /* @__PURE__ */ jsx(
3951
- "input",
3952
- {
3953
- type: "text",
3954
- maxLength: 6,
3955
- value: hexInput,
3956
- onChange: (e) => {
3957
- setHexInput(e.target.value.replace(/[^0-9a-fA-F]/g, ""));
3958
- setHexError(false);
3959
- },
3960
- onKeyDown: (e) => {
3961
- if (e.key === "Enter") {
3962
- e.preventDefault();
3963
- commitHexInput();
3964
- }
3965
- if (e.key === "Escape") setOpen(false);
3966
- },
3967
- placeholder: "e.g. ffff00",
3968
- className: [
3969
- "flex-1 px-1 py-1.5 text-xs bg-transparent outline-none font-mono",
3970
- "text-gray-900 dark:text-gray-100",
3971
- hexError ? "text-red-500" : ""
3972
- ].join(" "),
3973
- "aria-label": "Hex colour value"
3974
- }
3975
- )
3976
- ]
3977
- }
3978
- ),
3979
- /* @__PURE__ */ jsx(
3980
- "button",
3981
- {
3982
- type: "button",
3983
- onMouseDown: (e) => {
3984
- e.preventDefault();
3985
- commitHexInput();
3986
- },
3987
- className: "shrink-0 px-2 py-1.5 text-xs bg-blue-600 text-white rounded\n hover:bg-blue-700 transition-colors",
3988
- children: "\u2713"
3989
- }
3990
- )
3991
- ] }),
3992
- hexError && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-red-500 mt-1 px-0.5", children: "Invalid hex colour" })
3993
- ]
3994
- }
3995
- ),
3996
- document.body
3922
+ )
3923
+ ] }),
3924
+ hexError && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-red-500 mt-1 px-0.5", children: "Invalid hex colour" })
3925
+ ]
3926
+ }
3997
3927
  )
3998
3928
  ] });
3999
3929
  }
@@ -4018,7 +3948,7 @@ function Swatch({
4018
3948
  "hover:scale-110 focus:outline-none",
4019
3949
  "focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-blue-500",
4020
3950
  isActive ? "ring-2 ring-offset-1 ring-blue-500 scale-105" : "",
4021
- color.hasBorder ? "border border-gray-300 dark:border-gray-500" : ""
3951
+ color.hasBorder ? "border border-gray-300 " : ""
4022
3952
  ].join(" "),
4023
3953
  style: { backgroundColor: color.value },
4024
3954
  children: isActive && /* @__PURE__ */ jsx(CheckIcon4, { light: isLight(color.value) })
@@ -4141,15 +4071,11 @@ function TextColorDropdown({
4141
4071
  const [lastColor, setLastColor] = useState(PRESET_COLORS2[0].value);
4142
4072
  const [hexInput, setHexInput] = useState("");
4143
4073
  const [hexError, setHexError] = useState(false);
4144
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
4145
- const chevronRef = useRef(null);
4146
- const popupRef = useRef(null);
4074
+ const containerRef = useRef(null);
4147
4075
  useEffect(() => {
4148
4076
  if (!open) return;
4149
4077
  const handler = (e) => {
4150
- var _a, _b;
4151
- const target = e.target;
4152
- if (!((_a = popupRef.current) == null ? void 0 : _a.contains(target)) && !((_b = chevronRef.current) == null ? void 0 : _b.contains(target)))
4078
+ if (containerRef.current && !containerRef.current.contains(e.target))
4153
4079
  setOpen(false);
4154
4080
  };
4155
4081
  document.addEventListener("mousedown", handler);
@@ -4189,7 +4115,7 @@ function TextColorDropdown({
4189
4115
  (c) => !PRESET_COLORS2.some((p) => normalizeHex2(p.value) === normalizeHex2(c))
4190
4116
  );
4191
4117
  const normActive = activeColor ? normalizeHex2(activeColor) : null;
4192
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
4118
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative flex items-center", children: [
4193
4119
  /* @__PURE__ */ jsxs(
4194
4120
  "button",
4195
4121
  {
@@ -4199,7 +4125,7 @@ function TextColorDropdown({
4199
4125
  e.preventDefault();
4200
4126
  handleQuickApply();
4201
4127
  },
4202
- className: "flex flex-col items-center justify-center gap-0.5 w-8 h-8 rounded-l\n text-gray-700 dark:text-gray-300\n hover:bg-gray-100 dark:hover:bg-gray-700\n focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500\n transition-colors",
4128
+ className: "flex flex-col items-center justify-center gap-0.5 w-8 h-8 rounded-l\r\n text-gray-700 \r\n hover:bg-gray-100 \r\n focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500\r\n transition-colors",
4203
4129
  children: [
4204
4130
  /* @__PURE__ */ jsx(TextColorIcon, {}),
4205
4131
  /* @__PURE__ */ jsx(
@@ -4215,139 +4141,129 @@ function TextColorDropdown({
4215
4141
  /* @__PURE__ */ jsx(
4216
4142
  "button",
4217
4143
  {
4218
- ref: chevronRef,
4219
4144
  type: "button",
4220
4145
  title: "More text colors",
4221
4146
  "aria-haspopup": "listbox",
4222
4147
  "aria-expanded": open,
4223
4148
  onMouseDown: (e) => {
4224
4149
  e.preventDefault();
4225
- if (open) {
4226
- setOpen(false);
4227
- return;
4228
- }
4229
- if (chevronRef.current) {
4230
- const rect = chevronRef.current.getBoundingClientRect();
4231
- setPopupPos({ top: rect.bottom + 4, left: rect.left - 190 });
4232
- }
4233
- setHexInput("");
4234
- setHexError(false);
4235
- setOpen(true);
4150
+ setOpen((p) => {
4151
+ if (!p) {
4152
+ setHexInput("");
4153
+ setHexError(false);
4154
+ }
4155
+ return !p;
4156
+ });
4236
4157
  },
4237
4158
  className: [
4238
- "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 dark:border-gray-600",
4159
+ "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 ",
4239
4160
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500",
4240
4161
  "transition-colors",
4241
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200" : "text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
4162
+ open ? "bg-gray-100 text-gray-700 " : "text-gray-400 hover:bg-gray-100 "
4242
4163
  ].join(" "),
4243
4164
  children: /* @__PURE__ */ jsx(ChevronDownIcon5, {})
4244
4165
  }
4245
4166
  ),
4246
- open && createPortal(
4247
- /* @__PURE__ */ jsxs(
4248
- "div",
4249
- {
4250
- ref: popupRef,
4251
- role: "dialog",
4252
- "aria-label": "Text color",
4253
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, width: 208 },
4254
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-xl p-3",
4255
- children: [
4256
- /* @__PURE__ */ jsxs(
4257
- "button",
4258
- {
4259
- type: "button",
4260
- onMouseDown: (e) => {
4261
- e.preventDefault();
4262
- applyColor(null);
4263
- },
4264
- className: "flex items-center gap-2 w-full px-2 py-1.5 mb-2.5 text-xs font-medium\n text-gray-600 dark:text-gray-300\n rounded hover:bg-gray-100 dark:hover:bg-gray-700\n transition-colors",
4265
- children: [
4266
- /* @__PURE__ */ jsx(RemoveColorIcon2, {}),
4267
- "Remove color"
4268
- ]
4269
- }
4270
- ),
4271
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" }),
4272
- extraDocColors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4273
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 dark:text-gray-500 mb-1.5 px-0.5", children: "Document colors" }),
4274
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-2.5", children: extraDocColors.slice(0, 10).map((c) => /* @__PURE__ */ jsx(
4275
- Swatch2,
4276
- {
4277
- color: { label: c, value: c },
4278
- isActive: normActive === normalizeHex2(c),
4279
- onSelect: applyColor
4280
- },
4281
- c
4282
- )) }),
4283
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" })
4284
- ] }),
4285
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-3", children: PRESET_COLORS2.map((c) => /* @__PURE__ */ jsx(
4167
+ open && /* @__PURE__ */ jsxs(
4168
+ "div",
4169
+ {
4170
+ role: "dialog",
4171
+ "aria-label": "Text color",
4172
+ className: "absolute top-full left-0 mt-1 z-50\r\n bg-white \r\n border border-gray-200 \r\n rounded-lg shadow-xl\r\n p-3 w-[208px]",
4173
+ children: [
4174
+ /* @__PURE__ */ jsxs(
4175
+ "button",
4176
+ {
4177
+ type: "button",
4178
+ onMouseDown: (e) => {
4179
+ e.preventDefault();
4180
+ applyColor(null);
4181
+ },
4182
+ className: "flex items-center gap-2 w-full px-2 py-1.5 mb-2.5 text-xs font-medium\r\n text-gray-600 \r\n rounded hover:bg-gray-100 \r\n transition-colors",
4183
+ children: [
4184
+ /* @__PURE__ */ jsx(RemoveColorIcon2, {}),
4185
+ "Remove color"
4186
+ ]
4187
+ }
4188
+ ),
4189
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" }),
4190
+ extraDocColors.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4191
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 mb-1.5 px-0.5", children: "Document colors" }),
4192
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-2.5", children: extraDocColors.slice(0, 10).map((c) => /* @__PURE__ */ jsx(
4286
4193
  Swatch2,
4287
4194
  {
4288
- color: c,
4289
- isActive: normActive === normalizeHex2(c.value),
4195
+ color: { label: c, value: c },
4196
+ isActive: normActive === normalizeHex2(c),
4290
4197
  onSelect: applyColor
4291
4198
  },
4292
- c.value
4199
+ c
4293
4200
  )) }),
4294
- /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 dark:bg-gray-700 mb-2.5" }),
4295
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 dark:text-gray-500 mb-1.5 px-0.5", children: "Custom color" }),
4296
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
4297
- /* @__PURE__ */ jsx(
4298
- "div",
4299
- {
4300
- className: "shrink-0 w-7 h-7 rounded border border-gray-300 dark:border-gray-600",
4301
- style: { backgroundColor: hexInputToPreview2(hexInput) }
4302
- }
4303
- ),
4304
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center border rounded overflow-hidden\n border-gray-300 dark:border-gray-600\n focus-within:ring-2 focus-within:ring-blue-500", children: [
4305
- /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-gray-400 select-none", children: "#" }),
4306
- /* @__PURE__ */ jsx(
4307
- "input",
4308
- {
4309
- type: "text",
4310
- maxLength: 6,
4311
- value: hexInput,
4312
- onChange: (e) => {
4313
- setHexInput(e.target.value.replace(/[^0-9a-fA-F]/g, ""));
4314
- setHexError(false);
4315
- },
4316
- onKeyDown: (e) => {
4317
- if (e.key === "Enter") {
4318
- e.preventDefault();
4319
- commitHexInput();
4320
- }
4321
- if (e.key === "Escape") setOpen(false);
4322
- },
4323
- placeholder: "e.g. 000000",
4324
- className: [
4325
- "flex-1 px-1 py-1.5 text-xs bg-transparent outline-none font-mono",
4326
- "text-gray-900 dark:text-gray-100",
4327
- hexError ? "text-red-500" : ""
4328
- ].join(" "),
4329
- "aria-label": "Hex colour value"
4330
- }
4331
- )
4332
- ] }),
4201
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" })
4202
+ ] }),
4203
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-1 mb-3", children: PRESET_COLORS2.map((c) => /* @__PURE__ */ jsx(
4204
+ Swatch2,
4205
+ {
4206
+ color: c,
4207
+ isActive: normActive === normalizeHex2(c.value),
4208
+ onSelect: applyColor
4209
+ },
4210
+ c.value
4211
+ )) }),
4212
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-gray-100 mb-2.5" }),
4213
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-widest text-gray-400 mb-1.5 px-0.5", children: "Custom color" }),
4214
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
4215
+ /* @__PURE__ */ jsx(
4216
+ "div",
4217
+ {
4218
+ className: "shrink-0 w-7 h-7 rounded border border-gray-300 ",
4219
+ style: { backgroundColor: hexInputToPreview2(hexInput) }
4220
+ }
4221
+ ),
4222
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center border rounded overflow-hidden\r\n border-gray-300 \r\n focus-within:ring-2 focus-within:ring-blue-500", children: [
4223
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-gray-400 select-none", children: "#" }),
4333
4224
  /* @__PURE__ */ jsx(
4334
- "button",
4225
+ "input",
4335
4226
  {
4336
- type: "button",
4337
- onMouseDown: (e) => {
4338
- e.preventDefault();
4339
- commitHexInput();
4227
+ type: "text",
4228
+ maxLength: 6,
4229
+ value: hexInput,
4230
+ onChange: (e) => {
4231
+ setHexInput(e.target.value.replace(/[^0-9a-fA-F]/g, ""));
4232
+ setHexError(false);
4340
4233
  },
4341
- className: "shrink-0 px-2 py-1.5 text-xs bg-blue-600 text-white rounded\n hover:bg-blue-700 transition-colors",
4342
- children: "\u2713"
4234
+ onKeyDown: (e) => {
4235
+ if (e.key === "Enter") {
4236
+ e.preventDefault();
4237
+ commitHexInput();
4238
+ }
4239
+ if (e.key === "Escape") setOpen(false);
4240
+ },
4241
+ placeholder: "e.g. 000000",
4242
+ className: [
4243
+ "flex-1 px-1 py-1.5 text-xs bg-transparent outline-none font-mono",
4244
+ "text-gray-900 ",
4245
+ hexError ? "text-red-500" : ""
4246
+ ].join(" "),
4247
+ "aria-label": "Hex colour value"
4343
4248
  }
4344
4249
  )
4345
4250
  ] }),
4346
- hexError && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-red-500 mt-1 px-0.5", children: "Invalid hex colour" })
4347
- ]
4348
- }
4349
- ),
4350
- document.body
4251
+ /* @__PURE__ */ jsx(
4252
+ "button",
4253
+ {
4254
+ type: "button",
4255
+ onMouseDown: (e) => {
4256
+ e.preventDefault();
4257
+ commitHexInput();
4258
+ },
4259
+ className: "shrink-0 px-2 py-1.5 text-xs bg-blue-600 text-white rounded\r\n hover:bg-blue-700 transition-colors",
4260
+ children: "\u2713"
4261
+ }
4262
+ )
4263
+ ] }),
4264
+ hexError && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-red-500 mt-1 px-0.5", children: "Invalid hex colour" })
4265
+ ]
4266
+ }
4351
4267
  )
4352
4268
  ] });
4353
4269
  }
@@ -4372,7 +4288,7 @@ function Swatch2({
4372
4288
  "hover:scale-110 focus:outline-none",
4373
4289
  "focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-blue-500",
4374
4290
  isActive ? "ring-2 ring-offset-1 ring-blue-500 scale-105" : "",
4375
- color.hasBorder ? "border border-gray-300 dark:border-gray-500" : ""
4291
+ color.hasBorder ? "border border-gray-300 " : ""
4376
4292
  ].join(" "),
4377
4293
  style: { backgroundColor: color.value },
4378
4294
  children: isActive && /* @__PURE__ */ jsx(CheckIcon5, { light: isLight2(color.value) })
@@ -5904,9 +5820,7 @@ function CodeBlockButton({ engine }) {
5904
5820
  const activeBlock = getActiveBlockType(state.doc, state.selection);
5905
5821
  const isActive = activeBlock === "code_block";
5906
5822
  const [open, setOpen] = useState(false);
5907
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
5908
- const chevronRef = useRef(null);
5909
- const popupRef = useRef(null);
5823
+ const containerRef = useRef(null);
5910
5824
  const currentLang = (() => {
5911
5825
  var _a2;
5912
5826
  if (!isActive || !state.selection) return "plaintext";
@@ -5919,9 +5833,9 @@ function CodeBlockButton({ engine }) {
5919
5833
  useEffect(() => {
5920
5834
  if (!open) return;
5921
5835
  const handler = (e) => {
5922
- var _a2, _b;
5923
- const target = e.target;
5924
- if (!((_a2 = popupRef.current) == null ? void 0 : _a2.contains(target)) && !((_b = chevronRef.current) == null ? void 0 : _b.contains(target))) closeDropdown();
5836
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
5837
+ closeDropdown();
5838
+ }
5925
5839
  };
5926
5840
  document.addEventListener("mousedown", handler);
5927
5841
  return () => document.removeEventListener("mousedown", handler);
@@ -5948,7 +5862,7 @@ function CodeBlockButton({ engine }) {
5948
5862
  setOpen(false);
5949
5863
  };
5950
5864
  const activeLang = LANGUAGES.find((l) => l.value === currentLang);
5951
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
5865
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative flex items-center", children: [
5952
5866
  /* @__PURE__ */ jsx(
5953
5867
  "button",
5954
5868
  {
@@ -5960,7 +5874,7 @@ function CodeBlockButton({ engine }) {
5960
5874
  },
5961
5875
  className: [
5962
5876
  "flex items-center justify-center w-8 h-8 rounded-l text-sm transition-colors",
5963
- isActive ? "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
5877
+ isActive ? "bg-blue-100 text-blue-700 " : "text-gray-700 hover:bg-gray-100 "
5964
5878
  ].join(" "),
5965
5879
  children: /* @__PURE__ */ jsx(CodeBlockIcon, {})
5966
5880
  }
@@ -5968,69 +5882,55 @@ function CodeBlockButton({ engine }) {
5968
5882
  /* @__PURE__ */ jsx(
5969
5883
  "button",
5970
5884
  {
5971
- ref: chevronRef,
5972
5885
  type: "button",
5973
5886
  title: isActive ? `Language: ${(_a = activeLang == null ? void 0 : activeLang.label) != null ? _a : "Plain text"}` : "Select language",
5974
5887
  "aria-haspopup": "listbox",
5975
5888
  "aria-expanded": open,
5976
5889
  onMouseDown: (e) => {
5977
5890
  e.preventDefault();
5978
- if (open) {
5979
- closeDropdown();
5980
- return;
5981
- }
5982
- if (chevronRef.current) {
5983
- const rect = chevronRef.current.getBoundingClientRect();
5984
- setPopupPos({ top: rect.bottom + 4, left: rect.left - 160 });
5985
- }
5986
- setOpen(true);
5891
+ setOpen((p) => !p);
5987
5892
  },
5988
5893
  className: [
5989
- "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 dark:border-gray-600 transition-colors",
5990
- open ? "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200" : "text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
5894
+ "flex items-center justify-center w-4 h-8 rounded-r border-l border-gray-200 transition-colors",
5895
+ open ? "bg-gray-100 text-gray-700 " : "text-gray-400 hover:bg-gray-100 "
5991
5896
  ].join(" "),
5992
5897
  children: /* @__PURE__ */ jsx(ChevronIcon, {})
5993
5898
  }
5994
5899
  ),
5995
- open && createPortal(
5996
- /* @__PURE__ */ jsxs(
5997
- "div",
5998
- {
5999
- ref: popupRef,
6000
- role: "listbox",
6001
- "aria-label": "Code block language",
6002
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, width: 176, maxHeight: 288, overflowY: "auto" },
6003
- className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-xl py-1",
6004
- children: [
6005
- /* @__PURE__ */ jsx("p", { className: "px-3 py-1.5 text-[10px] font-semibold uppercase tracking-widest text-gray-400 dark:text-gray-500", children: "Language" }),
6006
- LANGUAGES.map((lang) => {
6007
- const isSelected = isActive && currentLang === lang.value;
6008
- return /* @__PURE__ */ jsxs(
6009
- "button",
6010
- {
6011
- type: "button",
6012
- role: "option",
6013
- "aria-selected": isSelected,
6014
- onMouseDown: (e) => {
6015
- e.preventDefault();
6016
- handleSelectLanguage(lang.value);
6017
- },
6018
- className: [
6019
- "w-full text-left px-3 py-1.5 text-sm flex items-center gap-2 transition-colors",
6020
- isSelected ? "bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-medium" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
6021
- ].join(" "),
6022
- children: [
6023
- isSelected && /* @__PURE__ */ jsx("svg", { className: "w-3 h-3 shrink-0", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ jsx("polyline", { points: "1 6 4.5 9.5 11 2.5", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }),
6024
- /* @__PURE__ */ jsx("span", { className: isSelected ? "" : "ml-5", children: lang.label })
6025
- ]
5900
+ open && /* @__PURE__ */ jsxs(
5901
+ "div",
5902
+ {
5903
+ role: "listbox",
5904
+ "aria-label": "Code block language",
5905
+ className: "absolute top-full left-0 mt-1 z-50 bg-white border border-gray-200 rounded-lg shadow-xl py-1 w-44 max-h-72 overflow-y-auto",
5906
+ children: [
5907
+ /* @__PURE__ */ jsx("p", { className: "px-3 py-1.5 text-[10px] font-semibold uppercase tracking-widest text-gray-400 ", children: "Language" }),
5908
+ LANGUAGES.map((lang) => {
5909
+ const isSelected = isActive && currentLang === lang.value;
5910
+ return /* @__PURE__ */ jsxs(
5911
+ "button",
5912
+ {
5913
+ type: "button",
5914
+ role: "option",
5915
+ "aria-selected": isSelected,
5916
+ onMouseDown: (e) => {
5917
+ e.preventDefault();
5918
+ handleSelectLanguage(lang.value);
6026
5919
  },
6027
- lang.value
6028
- );
6029
- })
6030
- ]
6031
- }
6032
- ),
6033
- document.body
5920
+ className: [
5921
+ "w-full text-left px-3 py-1.5 text-sm flex items-center gap-2 transition-colors",
5922
+ isSelected ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-700 hover:bg-gray-100 "
5923
+ ].join(" "),
5924
+ children: [
5925
+ isSelected && /* @__PURE__ */ jsx("svg", { className: "w-3 h-3 shrink-0", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ jsx("polyline", { points: "1 6 4.5 9.5 11 2.5", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }),
5926
+ /* @__PURE__ */ jsx("span", { className: isSelected ? "" : "ml-5", children: lang.label })
5927
+ ]
5928
+ },
5929
+ lang.value
5930
+ );
5931
+ })
5932
+ ]
5933
+ }
6034
5934
  )
6035
5935
  ] });
6036
5936
  }
@@ -6360,16 +6260,12 @@ function HeadingDropdown({
6360
6260
  }) {
6361
6261
  var _a;
6362
6262
  const [open, setOpen] = useState(false);
6363
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
6364
- const buttonRef = useRef(null);
6365
- const popupRef = useRef(null);
6263
+ const containerRef = useRef(null);
6366
6264
  const run = (command) => command(engine);
6367
6265
  useEffect(() => {
6368
6266
  if (!open) return;
6369
6267
  const handler = (e) => {
6370
- var _a2, _b;
6371
- const target = e.target;
6372
- if (!((_a2 = popupRef.current) == null ? void 0 : _a2.contains(target)) && !((_b = buttonRef.current) == null ? void 0 : _b.contains(target)))
6268
+ if (containerRef.current && !containerRef.current.contains(e.target))
6373
6269
  setOpen(false);
6374
6270
  };
6375
6271
  document.addEventListener("mousedown", handler);
@@ -6387,23 +6283,14 @@ function HeadingDropdown({
6387
6283
  code_block: "Code"
6388
6284
  };
6389
6285
  const label = (_a = BLOCK_LABELS[activeBlock]) != null ? _a : "Paragraph";
6390
- return /* @__PURE__ */ jsxs(Fragment, { children: [
6286
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
6391
6287
  /* @__PURE__ */ jsxs(
6392
6288
  "button",
6393
6289
  {
6394
- ref: buttonRef,
6395
6290
  type: "button",
6396
6291
  onMouseDown: (e) => {
6397
6292
  e.preventDefault();
6398
- if (open) {
6399
- setOpen(false);
6400
- return;
6401
- }
6402
- if (buttonRef.current) {
6403
- const rect = buttonRef.current.getBoundingClientRect();
6404
- setPopupPos({ top: rect.bottom + 2, left: rect.left });
6405
- }
6406
- setOpen(true);
6293
+ setOpen((v) => !v);
6407
6294
  },
6408
6295
  className: "flex items-center gap-1 px-2 h-8 rounded text-sm font-medium text-gray-700 hover:bg-gray-100 cursor-pointer",
6409
6296
  children: [
@@ -6412,7 +6299,7 @@ function HeadingDropdown({
6412
6299
  ]
6413
6300
  }
6414
6301
  ),
6415
- open && createPortal(/* @__PURE__ */ jsx("div", { ref: popupRef, style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, width: 160 }, className: "bg-white border border-gray-200 rounded shadow-lg", children: [
6302
+ open && /* @__PURE__ */ jsx("div", { className: "absolute top-full left-0 mt-0.5 w-40 bg-white border border-gray-200 rounded shadow-lg z-50", children: [
6416
6303
  {
6417
6304
  label: "Paragraph",
6418
6305
  cmd: setParagraph,
@@ -6464,7 +6351,7 @@ function HeadingDropdown({
6464
6351
  children: label2
6465
6352
  },
6466
6353
  label2
6467
- )) }), document.body)
6354
+ )) })
6468
6355
  ] });
6469
6356
  }
6470
6357
  function LinkButton({
@@ -6479,16 +6366,10 @@ function LinkButton({
6479
6366
  const [editMode, setEditMode] = useState(false);
6480
6367
  const [url, setUrl] = useState("");
6481
6368
  const [displayText, setDisplayText] = useState("");
6482
- const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
6483
6369
  const containerRef = useRef(null);
6484
- const popupRef = useRef(null);
6485
6370
  const urlInputRef = useRef(null);
6486
6371
  const textInputRef = useRef(null);
6487
6372
  const openPopup = () => {
6488
- if (containerRef.current) {
6489
- const rect = containerRef.current.getBoundingClientRect();
6490
- setPopupPos({ top: rect.bottom + 4, left: rect.left });
6491
- }
6492
6373
  if (isActive) {
6493
6374
  setUrl(activeHref != null ? activeHref : "");
6494
6375
  setDisplayText("");
@@ -6520,9 +6401,7 @@ function LinkButton({
6520
6401
  useEffect(() => {
6521
6402
  if (!open) return;
6522
6403
  const handler = (e) => {
6523
- var _a, _b;
6524
- const target = e.target;
6525
- if (!((_a = popupRef.current) == null ? void 0 : _a.contains(target)) && !((_b = containerRef.current) == null ? void 0 : _b.contains(target))) {
6404
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
6526
6405
  closePopup();
6527
6406
  }
6528
6407
  };
@@ -6562,7 +6441,7 @@ function LinkButton({
6562
6441
  setEditMode(true);
6563
6442
  };
6564
6443
  const showTextInput = !hasTextSelected && !isActive;
6565
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, children: [
6444
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
6566
6445
  /* @__PURE__ */ jsx(
6567
6446
  ToolbarButton,
6568
6447
  {
@@ -6573,43 +6452,115 @@ function LinkButton({
6573
6452
  icon: /* @__PURE__ */ jsx(LinkIcon, {})
6574
6453
  }
6575
6454
  ),
6576
- open && createPortal(
6577
- /* @__PURE__ */ jsx(
6578
- "div",
6579
- {
6580
- ref: popupRef,
6581
- role: "dialog",
6582
- "aria-label": "Link editor",
6583
- style: { position: "fixed", top: popupPos.top, left: popupPos.left, zIndex: 9999, width: 320 },
6584
- className: "bg-white border border-gray-200 rounded-lg shadow-xl p-3",
6585
- children: isActive && !editMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
6586
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-400 mb-2 uppercase tracking-wide", children: "Link" }),
6587
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
6455
+ open && /* @__PURE__ */ jsx(
6456
+ "div",
6457
+ {
6458
+ role: "dialog",
6459
+ "aria-label": "Link editor",
6460
+ className: "absolute top-full left-0 mt-1 z-50 bg-white border border-gray-200 rounded-lg shadow-xl p-3 w-80",
6461
+ children: isActive && !editMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
6462
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-400 mb-2 uppercase tracking-wide", children: "Link" }),
6463
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
6464
+ /* @__PURE__ */ jsx(
6465
+ "a",
6466
+ {
6467
+ href: activeHref != null ? activeHref : "#",
6468
+ target: "_blank",
6469
+ rel: "noopener noreferrer",
6470
+ className: "flex-1 text-sm text-blue-600 hover:underline truncate",
6471
+ title: activeHref != null ? activeHref : "",
6472
+ children: activeHref
6473
+ }
6474
+ ),
6475
+ /* @__PURE__ */ jsx(
6476
+ "button",
6477
+ {
6478
+ type: "button",
6479
+ onMouseDown: (e) => {
6480
+ e.preventDefault();
6481
+ handleStartEdit();
6482
+ },
6483
+ className: "shrink-0 text-xs text-gray-500 hover:text-gray-700 px-2 py-1 rounded hover:bg-gray-100 ",
6484
+ "aria-label": "Edit link",
6485
+ children: "Edit"
6486
+ }
6487
+ ),
6488
+ /* @__PURE__ */ jsx(
6489
+ "button",
6490
+ {
6491
+ type: "button",
6492
+ onMouseDown: (e) => {
6493
+ e.preventDefault();
6494
+ handleRemove();
6495
+ },
6496
+ className: "shrink-0 text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded hover:bg-red-50 ",
6497
+ "aria-label": "Remove link",
6498
+ children: "Remove"
6499
+ }
6500
+ )
6501
+ ] })
6502
+ ] }) : (
6503
+ /* ── Edit / Insert mode ── */
6504
+ /* @__PURE__ */ jsxs(Fragment, { children: [
6505
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-400 mb-3 uppercase tracking-wide", children: isActive ? "Edit link" : "Insert link" }),
6506
+ showTextInput && /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
6507
+ /* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-600 mb-1", children: "Display text" }),
6588
6508
  /* @__PURE__ */ jsx(
6589
- "a",
6509
+ "input",
6590
6510
  {
6591
- href: activeHref != null ? activeHref : "#",
6592
- target: "_blank",
6593
- rel: "noopener noreferrer",
6594
- className: "flex-1 text-sm text-blue-600 hover:underline truncate",
6595
- title: activeHref != null ? activeHref : "",
6596
- children: activeHref
6511
+ ref: textInputRef,
6512
+ type: "text",
6513
+ value: displayText,
6514
+ onChange: (e) => setDisplayText(e.target.value),
6515
+ onKeyDown: (e) => {
6516
+ var _a;
6517
+ if (e.key === "Enter") {
6518
+ e.preventDefault();
6519
+ (_a = urlInputRef.current) == null ? void 0 : _a.focus();
6520
+ }
6521
+ if (e.key === "Escape") closePopup();
6522
+ },
6523
+ placeholder: "Link text",
6524
+ className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
6597
6525
  }
6598
- ),
6526
+ )
6527
+ ] }),
6528
+ /* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
6529
+ /* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-600 mb-1", children: "URL" }),
6530
+ /* @__PURE__ */ jsx(
6531
+ "input",
6532
+ {
6533
+ ref: urlInputRef,
6534
+ type: "url",
6535
+ value: url,
6536
+ onChange: (e) => setUrl(e.target.value),
6537
+ onKeyDown: (e) => {
6538
+ if (e.key === "Enter") {
6539
+ e.preventDefault();
6540
+ handleApply();
6541
+ }
6542
+ if (e.key === "Escape") closePopup();
6543
+ },
6544
+ placeholder: "https://example.com",
6545
+ className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
6546
+ }
6547
+ )
6548
+ ] }),
6549
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
6599
6550
  /* @__PURE__ */ jsx(
6600
6551
  "button",
6601
6552
  {
6602
6553
  type: "button",
6603
6554
  onMouseDown: (e) => {
6604
6555
  e.preventDefault();
6605
- handleStartEdit();
6556
+ handleApply();
6606
6557
  },
6607
- className: "shrink-0 text-xs text-gray-500 hover:text-gray-700 px-2 py-1 rounded hover:bg-gray-100 ",
6608
- "aria-label": "Edit link",
6609
- children: "Edit"
6558
+ disabled: !url.trim() || showTextInput && !displayText.trim() && !hasTextSelected && !isActive,
6559
+ className: "flex-1 px-3 py-1.5 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 disabled:opacity-40 disabled:cursor-not-allowed",
6560
+ children: isActive ? "Update" : "Insert"
6610
6561
  }
6611
6562
  ),
6612
- /* @__PURE__ */ jsx(
6563
+ isActive && /* @__PURE__ */ jsx(
6613
6564
  "button",
6614
6565
  {
6615
6566
  type: "button",
@@ -6617,104 +6568,27 @@ function LinkButton({
6617
6568
  e.preventDefault();
6618
6569
  handleRemove();
6619
6570
  },
6620
- className: "shrink-0 text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded hover:bg-red-50 ",
6621
- "aria-label": "Remove link",
6571
+ className: "px-3 py-1.5 text-sm text-red-600 border border-red-200 rounded hover:bg-red-50 ",
6622
6572
  children: "Remove"
6623
6573
  }
6574
+ ),
6575
+ /* @__PURE__ */ jsx(
6576
+ "button",
6577
+ {
6578
+ type: "button",
6579
+ onMouseDown: (e) => {
6580
+ e.preventDefault();
6581
+ closePopup();
6582
+ },
6583
+ className: "px-3 py-1.5 text-sm text-gray-500 hover:text-gray-700 ",
6584
+ "aria-label": "Cancel",
6585
+ children: "\u2715"
6586
+ }
6624
6587
  )
6625
6588
  ] })
6626
- ] }) : (
6627
- /* ── Edit / Insert mode ── */
6628
- /* @__PURE__ */ jsxs(Fragment, { children: [
6629
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-400 mb-3 uppercase tracking-wide", children: isActive ? "Edit link" : "Insert link" }),
6630
- showTextInput && /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
6631
- /* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-600 mb-1", children: "Display text" }),
6632
- /* @__PURE__ */ jsx(
6633
- "input",
6634
- {
6635
- ref: textInputRef,
6636
- type: "text",
6637
- value: displayText,
6638
- onChange: (e) => setDisplayText(e.target.value),
6639
- onKeyDown: (e) => {
6640
- var _a;
6641
- if (e.key === "Enter") {
6642
- e.preventDefault();
6643
- (_a = urlInputRef.current) == null ? void 0 : _a.focus();
6644
- }
6645
- if (e.key === "Escape") closePopup();
6646
- },
6647
- placeholder: "Link text",
6648
- className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
6649
- }
6650
- )
6651
- ] }),
6652
- /* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
6653
- /* @__PURE__ */ jsx("label", { className: "block text-xs text-gray-600 mb-1", children: "URL" }),
6654
- /* @__PURE__ */ jsx(
6655
- "input",
6656
- {
6657
- ref: urlInputRef,
6658
- type: "url",
6659
- value: url,
6660
- onChange: (e) => setUrl(e.target.value),
6661
- onKeyDown: (e) => {
6662
- if (e.key === "Enter") {
6663
- e.preventDefault();
6664
- handleApply();
6665
- }
6666
- if (e.key === "Escape") closePopup();
6667
- },
6668
- placeholder: "https://example.com",
6669
- className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
6670
- }
6671
- )
6672
- ] }),
6673
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
6674
- /* @__PURE__ */ jsx(
6675
- "button",
6676
- {
6677
- type: "button",
6678
- onMouseDown: (e) => {
6679
- e.preventDefault();
6680
- handleApply();
6681
- },
6682
- disabled: !url.trim() || showTextInput && !displayText.trim() && !hasTextSelected && !isActive,
6683
- className: "flex-1 px-3 py-1.5 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 disabled:opacity-40 disabled:cursor-not-allowed",
6684
- children: isActive ? "Update" : "Insert"
6685
- }
6686
- ),
6687
- isActive && /* @__PURE__ */ jsx(
6688
- "button",
6689
- {
6690
- type: "button",
6691
- onMouseDown: (e) => {
6692
- e.preventDefault();
6693
- handleRemove();
6694
- },
6695
- className: "px-3 py-1.5 text-sm text-red-600 border border-red-200 rounded hover:bg-red-50 ",
6696
- children: "Remove"
6697
- }
6698
- ),
6699
- /* @__PURE__ */ jsx(
6700
- "button",
6701
- {
6702
- type: "button",
6703
- onMouseDown: (e) => {
6704
- e.preventDefault();
6705
- closePopup();
6706
- },
6707
- className: "px-3 py-1.5 text-sm text-gray-500 hover:text-gray-700 ",
6708
- "aria-label": "Cancel",
6709
- children: "\u2715"
6710
- }
6711
- )
6712
- ] })
6713
- ] })
6714
- )
6715
- }
6716
- ),
6717
- document.body
6589
+ ] })
6590
+ )
6591
+ }
6718
6592
  )
6719
6593
  ] });
6720
6594
  }