@refraction-ui/react 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +300 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +300 -166
- package/dist/index.js.map +1 -1
- package/dist/internal/react-conversation/index.d.cts +4 -1
- package/dist/internal/react-conversation/index.d.ts +4 -1
- package/dist/internal/react-cookie-consent/index.d.cts +1 -2
- package/dist/internal/react-cookie-consent/index.d.ts +1 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3368,6 +3368,65 @@ function useConversation(config) {
|
|
|
3368
3368
|
};
|
|
3369
3369
|
}
|
|
3370
3370
|
var h = React11.createElement;
|
|
3371
|
+
var svg = (children, size = 16) => h(
|
|
3372
|
+
"svg",
|
|
3373
|
+
{
|
|
3374
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3375
|
+
viewBox: "0 0 24 24",
|
|
3376
|
+
width: size,
|
|
3377
|
+
height: size,
|
|
3378
|
+
fill: "none",
|
|
3379
|
+
stroke: "currentColor",
|
|
3380
|
+
strokeWidth: 2,
|
|
3381
|
+
strokeLinecap: "round",
|
|
3382
|
+
strokeLinejoin: "round",
|
|
3383
|
+
"aria-hidden": true
|
|
3384
|
+
},
|
|
3385
|
+
children
|
|
3386
|
+
);
|
|
3387
|
+
var IconAttach = () => svg(h("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 17.93 8.81l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }));
|
|
3388
|
+
var IconBold = () => svg([
|
|
3389
|
+
h("path", { key: "a", d: "M6 4h8a4 4 0 0 1 0 8H6z" }),
|
|
3390
|
+
h("path", { key: "b", d: "M6 12h9a4 4 0 0 1 0 8H6z" })
|
|
3391
|
+
]);
|
|
3392
|
+
var IconItalic = () => svg([
|
|
3393
|
+
h("line", { key: "a", x1: 19, y1: 4, x2: 10, y2: 4 }),
|
|
3394
|
+
h("line", { key: "b", x1: 14, y1: 20, x2: 5, y2: 20 }),
|
|
3395
|
+
h("line", { key: "c", x1: 15, y1: 4, x2: 9, y2: 20 })
|
|
3396
|
+
]);
|
|
3397
|
+
var IconCode = () => svg([
|
|
3398
|
+
h("polyline", { key: "a", points: "16 18 22 12 16 6" }),
|
|
3399
|
+
h("polyline", { key: "b", points: "8 6 2 12 8 18" })
|
|
3400
|
+
]);
|
|
3401
|
+
var IconLink = () => svg([
|
|
3402
|
+
h("path", { key: "a", d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
3403
|
+
h("path", { key: "b", d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
3404
|
+
]);
|
|
3405
|
+
var IconQuote = () => svg([
|
|
3406
|
+
h("path", { key: "a", d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" }),
|
|
3407
|
+
h("path", { key: "b", d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" })
|
|
3408
|
+
]);
|
|
3409
|
+
var IconListUl = () => svg([
|
|
3410
|
+
h("line", { key: "a", x1: 8, y1: 6, x2: 21, y2: 6 }),
|
|
3411
|
+
h("line", { key: "b", x1: 8, y1: 12, x2: 21, y2: 12 }),
|
|
3412
|
+
h("line", { key: "c", x1: 8, y1: 18, x2: 21, y2: 18 }),
|
|
3413
|
+
h("line", { key: "d", x1: 3, y1: 6, x2: 3.01, y2: 6 }),
|
|
3414
|
+
h("line", { key: "e", x1: 3, y1: 12, x2: 3.01, y2: 12 }),
|
|
3415
|
+
h("line", { key: "f", x1: 3, y1: 18, x2: 3.01, y2: 18 })
|
|
3416
|
+
]);
|
|
3417
|
+
var IconListOl = () => svg([
|
|
3418
|
+
h("line", { key: "a", x1: 10, y1: 6, x2: 21, y2: 6 }),
|
|
3419
|
+
h("line", { key: "b", x1: 10, y1: 12, x2: 21, y2: 12 }),
|
|
3420
|
+
h("line", { key: "c", x1: 10, y1: 18, x2: 21, y2: 18 }),
|
|
3421
|
+
h("path", { key: "d", d: "M4 6h1v4" }),
|
|
3422
|
+
h("path", { key: "e", d: "M4 10h2" }),
|
|
3423
|
+
h("path", { key: "f", d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
|
|
3424
|
+
]);
|
|
3425
|
+
var IconArrowUp = () => svg([
|
|
3426
|
+
h("path", { key: "a", d: "M12 19V5" }),
|
|
3427
|
+
h("path", { key: "b", d: "m5 12 7-7 7 7" })
|
|
3428
|
+
]);
|
|
3429
|
+
var IconStop = () => svg(h("rect", { x: 7, y: 7, width: 10, height: 10, rx: 1.5 }));
|
|
3371
3430
|
var EMOJI = {
|
|
3372
3431
|
smile: "\u{1F604}",
|
|
3373
3432
|
grin: "\u{1F601}",
|
|
@@ -3420,13 +3479,15 @@ function detectTrigger(text, caret) {
|
|
|
3420
3479
|
return { type, query, start: caret - query.length - 1, end: caret };
|
|
3421
3480
|
}
|
|
3422
3481
|
function Composer({
|
|
3423
|
-
placeholder = "
|
|
3482
|
+
placeholder = "Send a message\u2026",
|
|
3424
3483
|
busy = false,
|
|
3425
3484
|
slashCommands = [],
|
|
3426
3485
|
mentions,
|
|
3427
3486
|
toolbar = true,
|
|
3428
3487
|
emoji = true,
|
|
3429
3488
|
attachments = true,
|
|
3489
|
+
error,
|
|
3490
|
+
onRetry,
|
|
3430
3491
|
onSubmit,
|
|
3431
3492
|
onStop,
|
|
3432
3493
|
onSlashCommand,
|
|
@@ -3567,148 +3628,157 @@ ${sel}
|
|
|
3567
3628
|
submit();
|
|
3568
3629
|
}
|
|
3569
3630
|
}
|
|
3570
|
-
const
|
|
3631
|
+
const iconBtn = "flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground";
|
|
3632
|
+
const toolbarBtn = (icon, title, kind) => h(
|
|
3571
3633
|
"button",
|
|
3572
3634
|
{
|
|
3573
3635
|
key: kind,
|
|
3574
3636
|
type: "button",
|
|
3575
3637
|
title,
|
|
3576
|
-
|
|
3638
|
+
"aria-label": title,
|
|
3639
|
+
className: iconBtn,
|
|
3577
3640
|
onMouseDown: (e) => e.preventDefault(),
|
|
3578
3641
|
// keep textarea selection
|
|
3579
3642
|
onClick: () => format(kind)
|
|
3580
3643
|
},
|
|
3581
|
-
|
|
3644
|
+
icon
|
|
3582
3645
|
);
|
|
3583
|
-
|
|
3646
|
+
const divider = () => h("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": true });
|
|
3647
|
+
const menu = menuOpen ? h(
|
|
3584
3648
|
"div",
|
|
3585
|
-
{
|
|
3586
|
-
|
|
3587
|
-
|
|
3649
|
+
{
|
|
3650
|
+
className: "absolute bottom-full left-0 z-20 mb-2 w-72 overflow-hidden rounded-xl border border-border bg-popover shadow-lg",
|
|
3651
|
+
role: "listbox"
|
|
3652
|
+
},
|
|
3653
|
+
h(
|
|
3588
3654
|
"div",
|
|
3589
|
-
{ className: "
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
"
|
|
3603
|
-
)
|
|
3604
|
-
|
|
3655
|
+
{ className: "border-b border-border px-3 py-1.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground" },
|
|
3656
|
+
trigger?.type === "/" ? "Commands" : trigger?.type === "@" ? "Mentions" : "Emoji"
|
|
3657
|
+
),
|
|
3658
|
+
...items.map(
|
|
3659
|
+
(it, i) => h(
|
|
3660
|
+
"button",
|
|
3661
|
+
{
|
|
3662
|
+
key: it.key,
|
|
3663
|
+
type: "button",
|
|
3664
|
+
role: "option",
|
|
3665
|
+
"aria-selected": i === active,
|
|
3666
|
+
className: cn(
|
|
3667
|
+
"flex w-full items-center gap-2 px-3 py-2 text-left text-sm",
|
|
3668
|
+
i === active ? "bg-accent" : "hover:bg-accent/50"
|
|
3669
|
+
),
|
|
3670
|
+
onMouseEnter: () => setActive(i),
|
|
3671
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3672
|
+
onClick: () => selectItem(i)
|
|
3673
|
+
},
|
|
3674
|
+
it.icon ? h("span", { className: "w-4 text-center text-muted-foreground" }, it.icon) : null,
|
|
3675
|
+
h("span", { className: "flex-1 truncate" }, it.primary),
|
|
3676
|
+
it.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it.secondary) : null
|
|
3605
3677
|
)
|
|
3606
|
-
)
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
toolbarBtn("B", "Bold (\u2318B)", "bold"),
|
|
3612
|
-
toolbarBtn("\u{1D456}", "Italic (\u2318I)", "italic"),
|
|
3613
|
-
toolbarBtn("</>", "Code (\u2318E)", "code"),
|
|
3614
|
-
toolbarBtn("\u{1F517}", "Link (\u2318K)", "link"),
|
|
3615
|
-
toolbarBtn("\u275D", "Quote", "quote"),
|
|
3616
|
-
toolbarBtn("\u2022", "Bulleted list", "ul"),
|
|
3617
|
-
toolbarBtn("1.", "Numbered list", "ol")
|
|
3618
|
-
) : null,
|
|
3619
|
-
// input row (relative for the popup menu)
|
|
3678
|
+
)
|
|
3679
|
+
) : null;
|
|
3680
|
+
return h(
|
|
3681
|
+
"div",
|
|
3682
|
+
{ className: "p-3" },
|
|
3620
3683
|
h(
|
|
3621
3684
|
"div",
|
|
3622
|
-
{ className: "relative
|
|
3623
|
-
|
|
3685
|
+
{ className: "relative" },
|
|
3686
|
+
menu,
|
|
3687
|
+
// unified input card
|
|
3688
|
+
h(
|
|
3624
3689
|
"div",
|
|
3625
3690
|
{
|
|
3626
|
-
className: "
|
|
3627
|
-
role: "listbox"
|
|
3691
|
+
className: "overflow-hidden rounded-2xl border border-border bg-background transition focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/40"
|
|
3628
3692
|
},
|
|
3693
|
+
// error banner
|
|
3694
|
+
error ? h(
|
|
3695
|
+
"div",
|
|
3696
|
+
{ className: "flex items-center gap-2 border-b border-border bg-destructive/5 px-3 py-2 text-xs text-destructive", role: "alert" },
|
|
3697
|
+
h("span", { className: "flex-1 truncate" }, error),
|
|
3698
|
+
onRetry ? h("button", { type: "button", className: "font-medium underline", onClick: () => onRetry() }, "Retry") : null
|
|
3699
|
+
) : null,
|
|
3700
|
+
// attachment chips
|
|
3701
|
+
pending.length > 0 ? h(
|
|
3702
|
+
"div",
|
|
3703
|
+
{ className: "flex flex-wrap gap-2 px-3 pt-3" },
|
|
3704
|
+
...pending.map(
|
|
3705
|
+
(a) => h(
|
|
3706
|
+
"span",
|
|
3707
|
+
{ key: a.id, className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-0.5 text-xs" },
|
|
3708
|
+
a.name,
|
|
3709
|
+
h(
|
|
3710
|
+
"button",
|
|
3711
|
+
{ type: "button", className: "text-muted-foreground hover:text-destructive", onClick: () => setPending((p) => p.filter((x) => x.id !== a.id)) },
|
|
3712
|
+
"\u2715"
|
|
3713
|
+
)
|
|
3714
|
+
)
|
|
3715
|
+
)
|
|
3716
|
+
) : null,
|
|
3717
|
+
// textarea (borderless)
|
|
3718
|
+
h("textarea", {
|
|
3719
|
+
ref,
|
|
3720
|
+
className: "block max-h-40 w-full resize-none bg-transparent px-3.5 py-3 text-sm placeholder:text-muted-foreground focus:outline-none",
|
|
3721
|
+
rows: 1,
|
|
3722
|
+
value,
|
|
3723
|
+
placeholder,
|
|
3724
|
+
autoFocus,
|
|
3725
|
+
"aria-label": "Message",
|
|
3726
|
+
onChange: (e) => syncFromTextarea(e.target),
|
|
3727
|
+
onClick: (e) => syncFromTextarea(e.currentTarget),
|
|
3728
|
+
onKeyUp: (e) => syncFromTextarea(e.currentTarget),
|
|
3729
|
+
onKeyDown,
|
|
3730
|
+
onBlur: () => setTimeout(() => setTrigger(null), 120)
|
|
3731
|
+
}),
|
|
3732
|
+
// bottom action bar
|
|
3629
3733
|
h(
|
|
3630
3734
|
"div",
|
|
3631
|
-
{ className: "
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3735
|
+
{ className: "flex items-center gap-0.5 px-2 pb-2" },
|
|
3736
|
+
attachments ? h(
|
|
3737
|
+
React11.Fragment,
|
|
3738
|
+
null,
|
|
3739
|
+
h("input", {
|
|
3740
|
+
ref: fileRef,
|
|
3741
|
+
type: "file",
|
|
3742
|
+
accept: "image/*",
|
|
3743
|
+
multiple: true,
|
|
3744
|
+
className: "hidden",
|
|
3745
|
+
onChange: (e) => {
|
|
3746
|
+
onFiles(e.target.files);
|
|
3747
|
+
e.target.value = "";
|
|
3748
|
+
}
|
|
3749
|
+
}),
|
|
3750
|
+
h("button", { type: "button", className: iconBtn, "aria-label": "Attach image or GIF", onClick: () => fileRef.current?.click() }, h(IconAttach))
|
|
3751
|
+
) : null,
|
|
3752
|
+
attachments && toolbar ? divider() : null,
|
|
3753
|
+
toolbar ? h(
|
|
3754
|
+
React11.Fragment,
|
|
3755
|
+
null,
|
|
3756
|
+
toolbarBtn(h(IconBold), "Bold (\u2318B)", "bold"),
|
|
3757
|
+
toolbarBtn(h(IconItalic), "Italic (\u2318I)", "italic"),
|
|
3758
|
+
toolbarBtn(h(IconCode), "Code (\u2318E)", "code"),
|
|
3759
|
+
toolbarBtn(h(IconLink), "Link (\u2318K)", "link"),
|
|
3760
|
+
divider(),
|
|
3761
|
+
toolbarBtn(h(IconQuote), "Quote", "quote"),
|
|
3762
|
+
toolbarBtn(h(IconListUl), "Bulleted list", "ul"),
|
|
3763
|
+
toolbarBtn(h(IconListOl), "Numbered list", "ol")
|
|
3764
|
+
) : null,
|
|
3765
|
+
h("div", { className: "flex-1" }),
|
|
3766
|
+
busy ? h(
|
|
3767
|
+
"button",
|
|
3768
|
+
{ type: "button", "aria-label": "Stop", className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition hover:opacity-90", onClick: () => onStop?.() },
|
|
3769
|
+
h(IconStop)
|
|
3770
|
+
) : h(
|
|
3636
3771
|
"button",
|
|
3637
3772
|
{
|
|
3638
|
-
key: it.key,
|
|
3639
3773
|
type: "button",
|
|
3640
|
-
|
|
3641
|
-
"
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
i === active ? "bg-accent" : "hover:bg-accent/50"
|
|
3645
|
-
),
|
|
3646
|
-
onMouseEnter: () => setActive(i),
|
|
3647
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
3648
|
-
onClick: () => selectItem(i)
|
|
3774
|
+
"aria-label": "Send",
|
|
3775
|
+
className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition hover:opacity-90 disabled:bg-muted disabled:text-muted-foreground disabled:opacity-100",
|
|
3776
|
+
disabled: !value.trim() && pending.length === 0,
|
|
3777
|
+
onClick: submit
|
|
3649
3778
|
},
|
|
3650
|
-
|
|
3651
|
-
h("span", { className: "flex-1 truncate" }, it.primary),
|
|
3652
|
-
it.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it.secondary) : null
|
|
3779
|
+
h(IconArrowUp)
|
|
3653
3780
|
)
|
|
3654
3781
|
)
|
|
3655
|
-
) : null,
|
|
3656
|
-
attachments ? h(
|
|
3657
|
-
React11.Fragment,
|
|
3658
|
-
null,
|
|
3659
|
-
h("input", {
|
|
3660
|
-
ref: fileRef,
|
|
3661
|
-
type: "file",
|
|
3662
|
-
accept: "image/*",
|
|
3663
|
-
multiple: true,
|
|
3664
|
-
className: "hidden",
|
|
3665
|
-
onChange: (e) => {
|
|
3666
|
-
onFiles(e.target.files);
|
|
3667
|
-
e.target.value = "";
|
|
3668
|
-
}
|
|
3669
|
-
}),
|
|
3670
|
-
h(
|
|
3671
|
-
"button",
|
|
3672
|
-
{
|
|
3673
|
-
type: "button",
|
|
3674
|
-
className: "rounded-md border border-border px-2 py-2 text-sm hover:bg-accent",
|
|
3675
|
-
"aria-label": "Attach image or GIF",
|
|
3676
|
-
onClick: () => fileRef.current?.click()
|
|
3677
|
-
},
|
|
3678
|
-
"\u{1F4CE}"
|
|
3679
|
-
)
|
|
3680
|
-
) : null,
|
|
3681
|
-
h("textarea", {
|
|
3682
|
-
ref,
|
|
3683
|
-
className: "max-h-40 flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary",
|
|
3684
|
-
rows: 1,
|
|
3685
|
-
value,
|
|
3686
|
-
placeholder,
|
|
3687
|
-
autoFocus,
|
|
3688
|
-
"aria-label": "Message",
|
|
3689
|
-
onChange: (e) => syncFromTextarea(e.target),
|
|
3690
|
-
onClick: (e) => syncFromTextarea(e.currentTarget),
|
|
3691
|
-
onKeyUp: (e) => syncFromTextarea(e.currentTarget),
|
|
3692
|
-
onKeyDown,
|
|
3693
|
-
onBlur: () => setTimeout(() => setTrigger(null), 120)
|
|
3694
|
-
}),
|
|
3695
|
-
busy ? h(
|
|
3696
|
-
"button",
|
|
3697
|
-
{
|
|
3698
|
-
type: "button",
|
|
3699
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground",
|
|
3700
|
-
onClick: () => onStop?.()
|
|
3701
|
-
},
|
|
3702
|
-
"Stop"
|
|
3703
|
-
) : h(
|
|
3704
|
-
"button",
|
|
3705
|
-
{
|
|
3706
|
-
type: "button",
|
|
3707
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50",
|
|
3708
|
-
disabled: !value.trim() && pending.length === 0,
|
|
3709
|
-
onClick: submit
|
|
3710
|
-
},
|
|
3711
|
-
"Send"
|
|
3712
3782
|
)
|
|
3713
3783
|
)
|
|
3714
3784
|
);
|
|
@@ -4056,9 +4126,13 @@ function Chat({
|
|
|
4056
4126
|
const timeline = selectMainTimeline(state.messages, state.threadingMode);
|
|
4057
4127
|
const activeConv = state.conversations.find((c) => c.id === state.activeConversationId);
|
|
4058
4128
|
const busy = state.status === "sending" || state.status === "streaming";
|
|
4129
|
+
const error = state.status === "error" ? state.error : null;
|
|
4130
|
+
const onRetry = () => void conversation.retryLast();
|
|
4059
4131
|
const mainComposer = h2(Composer, {
|
|
4060
4132
|
placeholder,
|
|
4061
4133
|
busy,
|
|
4134
|
+
error,
|
|
4135
|
+
onRetry,
|
|
4062
4136
|
slashCommands,
|
|
4063
4137
|
mentions,
|
|
4064
4138
|
onSlashCommand,
|
|
@@ -4069,6 +4143,8 @@ function Chat({
|
|
|
4069
4143
|
const threadComposer = state.openThreadRootId ? h2(Composer, {
|
|
4070
4144
|
placeholder: "Reply\u2026",
|
|
4071
4145
|
busy,
|
|
4146
|
+
error,
|
|
4147
|
+
onRetry,
|
|
4072
4148
|
slashCommands,
|
|
4073
4149
|
mentions,
|
|
4074
4150
|
onSlashCommand,
|
|
@@ -4372,14 +4448,56 @@ function useCookieConsent(config) {
|
|
|
4372
4448
|
};
|
|
4373
4449
|
}
|
|
4374
4450
|
var h3 = React11.createElement;
|
|
4375
|
-
var
|
|
4376
|
-
var
|
|
4377
|
-
var
|
|
4451
|
+
var btnBase = "inline-flex items-center justify-center rounded-lg px-3.5 py-2 text-sm font-medium transition-colors";
|
|
4452
|
+
var btnPrimary = cn(btnBase, "bg-primary text-primary-foreground hover:opacity-90");
|
|
4453
|
+
var btnGhost = cn(btnBase, "border border-border hover:bg-accent");
|
|
4454
|
+
var btnLink = "text-sm font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline";
|
|
4455
|
+
function Toggle({
|
|
4456
|
+
checked,
|
|
4457
|
+
disabled,
|
|
4458
|
+
onChange,
|
|
4459
|
+
label
|
|
4460
|
+
}) {
|
|
4461
|
+
if (disabled) {
|
|
4462
|
+
return h3(
|
|
4463
|
+
"span",
|
|
4464
|
+
{ className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground" },
|
|
4465
|
+
"Always on"
|
|
4466
|
+
);
|
|
4467
|
+
}
|
|
4468
|
+
return h3(
|
|
4469
|
+
"button",
|
|
4470
|
+
{
|
|
4471
|
+
type: "button",
|
|
4472
|
+
role: "switch",
|
|
4473
|
+
"aria-checked": checked,
|
|
4474
|
+
"aria-label": label,
|
|
4475
|
+
onClick: () => onChange(!checked),
|
|
4476
|
+
className: cn(
|
|
4477
|
+
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors",
|
|
4478
|
+
checked ? "bg-primary" : "bg-muted"
|
|
4479
|
+
)
|
|
4480
|
+
},
|
|
4481
|
+
h3("span", {
|
|
4482
|
+
className: cn(
|
|
4483
|
+
"inline-block h-4 w-4 transform rounded-full bg-background shadow transition-transform",
|
|
4484
|
+
checked ? "translate-x-[1.125rem]" : "translate-x-0.5"
|
|
4485
|
+
)
|
|
4486
|
+
})
|
|
4487
|
+
);
|
|
4488
|
+
}
|
|
4489
|
+
function CookieIcon() {
|
|
4490
|
+
return h3(
|
|
4491
|
+
"div",
|
|
4492
|
+
{ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-xl", "aria-hidden": true },
|
|
4493
|
+
"\u{1F36A}"
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4378
4496
|
function CookieConsent({
|
|
4379
4497
|
consent,
|
|
4380
4498
|
position = "bottom",
|
|
4381
4499
|
title = "We use cookies",
|
|
4382
|
-
description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which
|
|
4500
|
+
description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which to allow.",
|
|
4383
4501
|
policyUrl,
|
|
4384
4502
|
policyLabel = "Cookie policy",
|
|
4385
4503
|
className
|
|
@@ -4387,64 +4505,80 @@ function CookieConsent({
|
|
|
4387
4505
|
const { state, acceptAll, rejectAll, savePreferences, setPreference } = consent;
|
|
4388
4506
|
const [settings, setSettings] = React11.useState(false);
|
|
4389
4507
|
if (!state.open) return null;
|
|
4390
|
-
const wrapper = cn(
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
);
|
|
4395
|
-
const panel = "mx-auto max-w-3xl rounded-xl border border-border bg-background p-4 shadow-lg";
|
|
4396
|
-
const header = h3(
|
|
4397
|
-
"div",
|
|
4398
|
-
null,
|
|
4399
|
-
h3("h2", { className: "text-base font-semibold" }, title),
|
|
4400
|
-
h3("p", { className: "mt-1 text-sm text-muted-foreground" }, description),
|
|
4401
|
-
policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "mt-1 inline-block") }, policyLabel) : null
|
|
4402
|
-
);
|
|
4403
|
-
const promptActions = h3(
|
|
4508
|
+
const wrapper = cn("fixed inset-x-0 z-50 p-4", position === "bottom" ? "bottom-0" : "top-0", className);
|
|
4509
|
+
const panel = "mx-auto max-w-2xl overflow-hidden rounded-2xl border border-border bg-background shadow-lg";
|
|
4510
|
+
const policy = policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "whitespace-nowrap") }, policyLabel) : null;
|
|
4511
|
+
const promptView = h3(
|
|
4404
4512
|
"div",
|
|
4405
|
-
{ className: "
|
|
4406
|
-
h3(
|
|
4407
|
-
h3(
|
|
4408
|
-
|
|
4513
|
+
{ className: "flex flex-col gap-4 p-5 sm:flex-row sm:items-center" },
|
|
4514
|
+
h3(CookieIcon),
|
|
4515
|
+
h3(
|
|
4516
|
+
"div",
|
|
4517
|
+
{ className: "min-w-0 flex-1" },
|
|
4518
|
+
h3("p", { className: "text-sm font-semibold" }, title),
|
|
4519
|
+
h3(
|
|
4520
|
+
"p",
|
|
4521
|
+
{ className: "mt-0.5 text-sm leading-relaxed text-muted-foreground" },
|
|
4522
|
+
description,
|
|
4523
|
+
policy ? h3(React11.Fragment, null, " ", policy) : null
|
|
4524
|
+
)
|
|
4525
|
+
),
|
|
4526
|
+
h3(
|
|
4527
|
+
"div",
|
|
4528
|
+
{ className: "flex flex-wrap items-center gap-2 sm:shrink-0" },
|
|
4529
|
+
h3("button", { type: "button", className: btnLink, onClick: () => setSettings(true) }, "Customize"),
|
|
4530
|
+
h3("button", { type: "button", className: btnGhost, onClick: () => rejectAll() }, "Reject all"),
|
|
4531
|
+
h3("button", { type: "button", className: btnPrimary, onClick: () => acceptAll() }, "Accept all")
|
|
4532
|
+
)
|
|
4409
4533
|
);
|
|
4410
4534
|
const settingsView = h3(
|
|
4411
4535
|
"div",
|
|
4412
|
-
{ className: "
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
h3(
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4536
|
+
{ className: "p-5" },
|
|
4537
|
+
h3(
|
|
4538
|
+
"div",
|
|
4539
|
+
{ className: "flex items-center gap-3" },
|
|
4540
|
+
h3(CookieIcon),
|
|
4541
|
+
h3(
|
|
4542
|
+
"div",
|
|
4543
|
+
null,
|
|
4544
|
+
h3("p", { className: "text-sm font-semibold" }, "Cookie preferences"),
|
|
4545
|
+
h3("p", { className: "text-xs text-muted-foreground" }, "Toggle the categories you want to allow.")
|
|
4546
|
+
)
|
|
4547
|
+
),
|
|
4548
|
+
h3(
|
|
4549
|
+
"div",
|
|
4550
|
+
{ className: "mt-4 space-y-2" },
|
|
4551
|
+
...state.categories.map(
|
|
4552
|
+
(cat) => h3(
|
|
4553
|
+
"div",
|
|
4554
|
+
{ key: cat.id, className: "flex items-center justify-between gap-4 rounded-xl border border-border p-3" },
|
|
4555
|
+
h3(
|
|
4556
|
+
"div",
|
|
4557
|
+
{ className: "min-w-0" },
|
|
4558
|
+
h3("p", { className: "text-sm font-medium" }, cat.label),
|
|
4559
|
+
cat.description ? h3("p", { className: "mt-0.5 text-xs text-muted-foreground" }, cat.description) : null
|
|
4560
|
+
),
|
|
4561
|
+
h3(Toggle, {
|
|
4562
|
+
checked: !!state.preferences[cat.id],
|
|
4563
|
+
disabled: cat.required,
|
|
4564
|
+
label: cat.label,
|
|
4565
|
+
onChange: (v) => setPreference(cat.id, v)
|
|
4566
|
+
})
|
|
4567
|
+
)
|
|
4434
4568
|
)
|
|
4435
4569
|
),
|
|
4436
4570
|
h3(
|
|
4437
4571
|
"div",
|
|
4438
|
-
{ className: "flex flex-wrap items-center gap-2
|
|
4439
|
-
h3("button", { type: "button", className:
|
|
4440
|
-
h3("button", { type: "button", className: btnGhost, onClick: () => acceptAll() }, "Accept all"),
|
|
4441
|
-
h3("button", { type: "button", className:
|
|
4572
|
+
{ className: "mt-4 flex flex-wrap items-center gap-2" },
|
|
4573
|
+
h3("button", { type: "button", className: btnLink, onClick: () => setSettings(false) }, "\u2190 Back"),
|
|
4574
|
+
h3("button", { type: "button", className: cn(btnGhost, "sm:ml-auto"), onClick: () => acceptAll() }, "Accept all"),
|
|
4575
|
+
h3("button", { type: "button", className: btnPrimary, onClick: () => savePreferences(state.preferences) }, "Save preferences")
|
|
4442
4576
|
)
|
|
4443
4577
|
);
|
|
4444
4578
|
return h3(
|
|
4445
4579
|
"div",
|
|
4446
4580
|
{ className: wrapper, role: "dialog", "aria-label": "Cookie consent", "aria-modal": false },
|
|
4447
|
-
h3("div", { className: panel },
|
|
4581
|
+
h3("div", { className: panel }, settings ? settingsView : promptView)
|
|
4448
4582
|
);
|
|
4449
4583
|
}
|
|
4450
4584
|
|