@lessly/ui 0.7.2 → 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 +39 -2
- package/dist/index.js +119 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -87,6 +87,7 @@ declare const DropdownMenuSubContent: React$1.ForwardRefExoticComponent<Omit<Dro
|
|
|
87
87
|
declare const DropdownMenuContent: React$1.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
88
88
|
declare const DropdownMenuItem: React$1.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
89
89
|
inset?: boolean;
|
|
90
|
+
variant?: "default" | "destructive";
|
|
90
91
|
} & React$1.RefAttributes<HTMLDivElement>>;
|
|
91
92
|
declare const DropdownMenuCheckboxItem: React$1.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
92
93
|
declare const DropdownMenuRadioItem: React$1.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuRadioItemProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
|
|
@@ -122,9 +123,12 @@ interface SelectProps {
|
|
|
122
123
|
disabled?: boolean;
|
|
123
124
|
className?: string;
|
|
124
125
|
'data-testid'?: string;
|
|
126
|
+
id?: string;
|
|
127
|
+
'aria-label'?: string;
|
|
128
|
+
'aria-labelledby'?: string;
|
|
125
129
|
optionTestId?: (value: string) => string;
|
|
126
130
|
}
|
|
127
|
-
declare function Select({ value, onValueChange, options, placeholder, disabled, className, 'data-testid': testId, optionTestId, }: SelectProps): React$1.JSX.Element;
|
|
131
|
+
declare function Select({ value, onValueChange, options, placeholder, disabled, className, 'data-testid': testId, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, optionTestId, }: SelectProps): React$1.JSX.Element;
|
|
128
132
|
|
|
129
133
|
interface SwitchProps {
|
|
130
134
|
checked: boolean;
|
|
@@ -1053,4 +1057,37 @@ interface RequestRowProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, '
|
|
|
1053
1057
|
}
|
|
1054
1058
|
declare const RequestRow: React$1.ForwardRefExoticComponent<RequestRowProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1055
1059
|
|
|
1056
|
-
|
|
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
|
@@ -222,13 +222,14 @@ var DropdownMenuContent = React4.forwardRef(({ className, sideOffset = 4, ...pro
|
|
|
222
222
|
}
|
|
223
223
|
) }));
|
|
224
224
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
225
|
-
var DropdownMenuItem = React4.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
225
|
+
var DropdownMenuItem = React4.forwardRef(({ className, inset, variant = "default", ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
226
226
|
DropdownMenuPrimitive.Item,
|
|
227
227
|
{
|
|
228
228
|
ref,
|
|
229
229
|
className: cn(
|
|
230
230
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-bg-secondary focus:text-text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
231
231
|
inset && "pl-8",
|
|
232
|
+
variant === "destructive" && "text-text-danger focus:bg-bg-danger-subtle focus:text-text-danger",
|
|
232
233
|
className
|
|
233
234
|
),
|
|
234
235
|
...props
|
|
@@ -347,6 +348,9 @@ function Select({
|
|
|
347
348
|
disabled,
|
|
348
349
|
className,
|
|
349
350
|
"data-testid": testId,
|
|
351
|
+
id,
|
|
352
|
+
"aria-label": ariaLabel,
|
|
353
|
+
"aria-labelledby": ariaLabelledby,
|
|
350
354
|
optionTestId
|
|
351
355
|
}) {
|
|
352
356
|
const selected = options.find((o) => o.value === value);
|
|
@@ -356,6 +360,9 @@ function Select({
|
|
|
356
360
|
{
|
|
357
361
|
type: "button",
|
|
358
362
|
"data-testid": testId,
|
|
363
|
+
id,
|
|
364
|
+
"aria-label": ariaLabel,
|
|
365
|
+
"aria-labelledby": ariaLabelledby,
|
|
359
366
|
disabled,
|
|
360
367
|
className: cn(
|
|
361
368
|
"flex h-10 w-full items-center justify-between rounded-md border border-border-default bg-bg-primary px-3 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -3558,6 +3565,116 @@ var RequestRow = React57.forwardRef(
|
|
|
3558
3565
|
}
|
|
3559
3566
|
);
|
|
3560
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
|
+
}
|
|
3561
3678
|
export {
|
|
3562
3679
|
Accordion,
|
|
3563
3680
|
AccordionContent,
|
|
@@ -3677,6 +3794,7 @@ export {
|
|
|
3677
3794
|
EmptyState,
|
|
3678
3795
|
ExtensionIcon,
|
|
3679
3796
|
ExtensionLink,
|
|
3797
|
+
FeedbackWidget,
|
|
3680
3798
|
Form,
|
|
3681
3799
|
FormControl,
|
|
3682
3800
|
FormDescription,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessly/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Lessly design system — shared UI primitives, tokens, and theme",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.30.3",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"tw-animate-css": "^1.4.0",
|
|
122
122
|
"typescript": "^5.0.0",
|
|
123
123
|
"typescript-eslint": "^8.63.0",
|
|
124
|
-
"vite": "^
|
|
124
|
+
"vite": "^6.4.3",
|
|
125
125
|
"vitest": "^3.0.5"
|
|
126
126
|
}
|
|
127
127
|
}
|