@lessly/ui 0.8.0 → 0.9.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/dist/index.d.ts +34 -1
- package/dist/index.js +111 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1057,4 +1057,37 @@ interface RequestRowProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, '
|
|
|
1057
1057
|
}
|
|
1058
1058
|
declare const RequestRow: React$1.ForwardRefExoticComponent<RequestRowProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1059
1059
|
|
|
1060
|
-
|
|
1060
|
+
interface FeedbackWidgetLabels {
|
|
1061
|
+
/** Floating trigger button + its aria-label. */
|
|
1062
|
+
trigger: string;
|
|
1063
|
+
/** Heading shown above the form. */
|
|
1064
|
+
title: string;
|
|
1065
|
+
/** Label for the Title input. */
|
|
1066
|
+
titleField: string;
|
|
1067
|
+
/** Label for the Details textarea. */
|
|
1068
|
+
detailsField: string;
|
|
1069
|
+
/** Submit button. */
|
|
1070
|
+
submit: string;
|
|
1071
|
+
/** Message shown after a successful submit. */
|
|
1072
|
+
success: string;
|
|
1073
|
+
/** Message shown after a failed submit. */
|
|
1074
|
+
error: string;
|
|
1075
|
+
}
|
|
1076
|
+
interface FeedbackWidgetProps {
|
|
1077
|
+
/**
|
|
1078
|
+
* Called with the entered feedback when the form is submitted. The widget is
|
|
1079
|
+
* presentational: it knows nothing about APIs, auth, or routing — the parent
|
|
1080
|
+
* decides what to do with the data. Rejecting shows the error state.
|
|
1081
|
+
*/
|
|
1082
|
+
onSubmit: (data: {
|
|
1083
|
+
title: string;
|
|
1084
|
+
details: string;
|
|
1085
|
+
}) => Promise<void>;
|
|
1086
|
+
/** Override any of the default copy. */
|
|
1087
|
+
labels?: Partial<FeedbackWidgetLabels>;
|
|
1088
|
+
/** Merged onto the fixed positioning wrapper (e.g. to reposition the button). */
|
|
1089
|
+
className?: string;
|
|
1090
|
+
}
|
|
1091
|
+
declare function FeedbackWidget({ onSubmit, labels, className }: FeedbackWidgetProps): React$1.JSX.Element;
|
|
1092
|
+
|
|
1093
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, type AppShellProps, AppSidebar, type AppSidebarProps, AspectRatio, AuthenticatedLayout, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, type CarouselContextProps, CarouselItem, CarouselNext, type CarouselOptions, type CarouselPlugin, CarouselPrevious, type CarouselProps, Checkbox, Col, type ColProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, type ConnectionAuth, ConnectionCard, type ConnectionCardProps, type ConnectionScope, type ConnectionStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, DatePicker, type DatePickerProps, type DeltaDirection, type DeltaTone, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DocsLink, type DocsLinkProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExtensionIcon, type ExtensionIconProps, ExtensionLink, type ExtensionLinkProps, type ExtensionLinkUiMode, FeedbackWidget, type FeedbackWidgetLabels, type FeedbackWidgetProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Grid, GridOverlay, type GridOverlayProps, type GridProps, HoverCard, HoverCardContent, HoverCardTrigger, InlineError, type InlineErrorProps, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, MenuDivider, MenuPopup, type MenuPopupProps, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, type NavItem, type NavLinkComponent, NavRow, type NavRowProps, type NavRowVariant, NavSectionLabel, type NavSectionLabelProps, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RequestRow, type RequestRowProps, type RequestState, type RequestSurface, type ResolvedTheme, type Responsive, ScrollArea, ScrollBar, SectionIntro, type SectionIntroProps, SegmentChip, type SegmentChipProps, Select, type SelectOption, type SelectProps, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarNav, SidebarNavLink, type SidebarNavLinkProps, type SidebarNavProps, SidebarProductHeader, type SidebarProductHeaderProps, Skeleton, Slider, StatCard, type StatCardProps, StatusDot, type StatusDotProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, type Theme, ThemeToggle, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseSidebarOptions, type UseSidebarResult, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuUser, alertVariants, badgeVariants, buttonVariants, cn, isKnownExtensionIcon, navigationMenuTriggerStyle, segmentChipVariants, statusDotVariants, toggleVariants, useCarousel, useFormField, useIsMobile, useSidebar, useTheme };
|
package/dist/index.js
CHANGED
|
@@ -3565,6 +3565,116 @@ var RequestRow = React57.forwardRef(
|
|
|
3565
3565
|
}
|
|
3566
3566
|
);
|
|
3567
3567
|
RequestRow.displayName = "RequestRow";
|
|
3568
|
+
|
|
3569
|
+
// src/components/feedback-widget.tsx
|
|
3570
|
+
import * as React58 from "react";
|
|
3571
|
+
import { MessageSquare, Loader2 as Loader22, CheckCircle2 } from "lucide-react";
|
|
3572
|
+
import { jsx as jsx68, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3573
|
+
var TITLE_MAX = 256;
|
|
3574
|
+
var DETAILS_MAX = 65536;
|
|
3575
|
+
var DEFAULT_LABELS = {
|
|
3576
|
+
trigger: "Feedback",
|
|
3577
|
+
title: "Send feedback",
|
|
3578
|
+
titleField: "Title",
|
|
3579
|
+
detailsField: "Details",
|
|
3580
|
+
submit: "Send feedback",
|
|
3581
|
+
success: "Thanks! Your feedback was sent.",
|
|
3582
|
+
error: "Couldn't send feedback. Please try again."
|
|
3583
|
+
};
|
|
3584
|
+
function FeedbackWidget({ onSubmit, labels, className }) {
|
|
3585
|
+
const l = { ...DEFAULT_LABELS, ...labels };
|
|
3586
|
+
const uid = React58.useId();
|
|
3587
|
+
const titleId = `${uid}-title`;
|
|
3588
|
+
const detailsId = `${uid}-details`;
|
|
3589
|
+
const titleErrId = `${uid}-title-error`;
|
|
3590
|
+
const detailsErrId = `${uid}-details-error`;
|
|
3591
|
+
const [open, setOpen] = React58.useState(false);
|
|
3592
|
+
const [status, setStatus] = React58.useState("idle");
|
|
3593
|
+
const [title, setTitle] = React58.useState("");
|
|
3594
|
+
const [details, setDetails] = React58.useState("");
|
|
3595
|
+
const titleTooLong = title.length > TITLE_MAX;
|
|
3596
|
+
const detailsTooLong = details.length > DETAILS_MAX;
|
|
3597
|
+
const titleEmpty = title.trim().length === 0;
|
|
3598
|
+
const detailsEmpty = details.trim().length === 0;
|
|
3599
|
+
const valid = !titleEmpty && !detailsEmpty && !titleTooLong && !detailsTooLong;
|
|
3600
|
+
const submitting = status === "submitting";
|
|
3601
|
+
const changeOpen = (next) => {
|
|
3602
|
+
if (submitting) return;
|
|
3603
|
+
setOpen(next);
|
|
3604
|
+
if (!next) setStatus("idle");
|
|
3605
|
+
};
|
|
3606
|
+
const handleSubmit = async (e) => {
|
|
3607
|
+
e.preventDefault();
|
|
3608
|
+
if (!valid || submitting) return;
|
|
3609
|
+
setStatus("submitting");
|
|
3610
|
+
try {
|
|
3611
|
+
await onSubmit({ title, details });
|
|
3612
|
+
setStatus("success");
|
|
3613
|
+
setTitle("");
|
|
3614
|
+
setDetails("");
|
|
3615
|
+
} catch {
|
|
3616
|
+
setStatus("error");
|
|
3617
|
+
}
|
|
3618
|
+
};
|
|
3619
|
+
return /* @__PURE__ */ jsxs35(Popover, { open, onOpenChange: changeOpen, children: [
|
|
3620
|
+
/* @__PURE__ */ jsx68("div", { className: cn("fixed bottom-4 right-4 z-50", className), children: /* @__PURE__ */ jsx68(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs35(Button, { "aria-label": l.trigger, children: [
|
|
3621
|
+
/* @__PURE__ */ jsx68(MessageSquare, { className: "size-4", "aria-hidden": "true" }),
|
|
3622
|
+
l.trigger
|
|
3623
|
+
] }) }) }),
|
|
3624
|
+
/* @__PURE__ */ jsx68(PopoverContent, { align: "end", side: "top", className: "w-80", children: status === "success" ? /* @__PURE__ */ jsxs35("div", { className: "flex flex-col items-center gap-3 py-2 text-center", children: [
|
|
3625
|
+
/* @__PURE__ */ jsx68(CheckCircle2, { className: "size-8 text-text-success", "aria-hidden": "true" }),
|
|
3626
|
+
/* @__PURE__ */ jsx68("p", { role: "status", className: "text-sm text-text-primary", children: l.success }),
|
|
3627
|
+
/* @__PURE__ */ jsx68(Button, { variant: "outline", size: "sm", onClick: () => changeOpen(false), children: "Close" })
|
|
3628
|
+
] }) : /* @__PURE__ */ jsxs35("form", { onSubmit: handleSubmit, className: "flex flex-col gap-3", children: [
|
|
3629
|
+
/* @__PURE__ */ jsx68("p", { className: "text-sm font-medium text-text-primary", children: l.title }),
|
|
3630
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1.5", children: [
|
|
3631
|
+
/* @__PURE__ */ jsx68(Label2, { htmlFor: titleId, children: l.titleField }),
|
|
3632
|
+
/* @__PURE__ */ jsx68(
|
|
3633
|
+
Input,
|
|
3634
|
+
{
|
|
3635
|
+
id: titleId,
|
|
3636
|
+
value: title,
|
|
3637
|
+
onChange: (e) => setTitle(e.target.value),
|
|
3638
|
+
disabled: submitting,
|
|
3639
|
+
"aria-invalid": titleTooLong || void 0,
|
|
3640
|
+
"aria-describedby": titleTooLong ? titleErrId : void 0
|
|
3641
|
+
}
|
|
3642
|
+
),
|
|
3643
|
+
titleTooLong && /* @__PURE__ */ jsxs35("p", { id: titleErrId, className: "text-xs text-text-danger", children: [
|
|
3644
|
+
l.titleField,
|
|
3645
|
+
" must be ",
|
|
3646
|
+
TITLE_MAX,
|
|
3647
|
+
" characters or fewer."
|
|
3648
|
+
] })
|
|
3649
|
+
] }),
|
|
3650
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1.5", children: [
|
|
3651
|
+
/* @__PURE__ */ jsx68(Label2, { htmlFor: detailsId, children: l.detailsField }),
|
|
3652
|
+
/* @__PURE__ */ jsx68(
|
|
3653
|
+
Textarea,
|
|
3654
|
+
{
|
|
3655
|
+
id: detailsId,
|
|
3656
|
+
value: details,
|
|
3657
|
+
onChange: (e) => setDetails(e.target.value),
|
|
3658
|
+
disabled: submitting,
|
|
3659
|
+
"aria-invalid": detailsTooLong || void 0,
|
|
3660
|
+
"aria-describedby": detailsTooLong ? detailsErrId : void 0
|
|
3661
|
+
}
|
|
3662
|
+
),
|
|
3663
|
+
detailsTooLong && /* @__PURE__ */ jsxs35("p", { id: detailsErrId, className: "text-xs text-text-danger", children: [
|
|
3664
|
+
l.detailsField,
|
|
3665
|
+
" must be ",
|
|
3666
|
+
DETAILS_MAX,
|
|
3667
|
+
" characters or fewer."
|
|
3668
|
+
] })
|
|
3669
|
+
] }),
|
|
3670
|
+
status === "error" && /* @__PURE__ */ jsx68(InlineError, { children: l.error }),
|
|
3671
|
+
/* @__PURE__ */ jsxs35(Button, { type: "submit", disabled: !valid || submitting, children: [
|
|
3672
|
+
submitting && /* @__PURE__ */ jsx68(Loader22, { className: "size-4 animate-spin", "aria-hidden": "true" }),
|
|
3673
|
+
l.submit
|
|
3674
|
+
] })
|
|
3675
|
+
] }) })
|
|
3676
|
+
] });
|
|
3677
|
+
}
|
|
3568
3678
|
export {
|
|
3569
3679
|
Accordion,
|
|
3570
3680
|
AccordionContent,
|
|
@@ -3684,6 +3794,7 @@ export {
|
|
|
3684
3794
|
EmptyState,
|
|
3685
3795
|
ExtensionIcon,
|
|
3686
3796
|
ExtensionLink,
|
|
3797
|
+
FeedbackWidget,
|
|
3687
3798
|
Form,
|
|
3688
3799
|
FormControl,
|
|
3689
3800
|
FormDescription,
|