@cl-light-ds/ui 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -45
- package/package.json +2 -2
- package/src/components/accordion/Accordion.astro +248 -248
- package/src/components/accordion/AccordionContent.astro +26 -26
- package/src/components/accordion/AccordionItem.astro +36 -36
- package/src/components/accordion/AccordionTrigger.astro +118 -118
- package/src/components/accordion/CHANGELOG.md +41 -41
- package/src/components/accordion/index.ts +4 -4
- package/src/components/alert/Alert.astro +167 -167
- package/src/components/alert/AlertDescription.astro +24 -24
- package/src/components/alert/AlertTitle.astro +24 -24
- package/src/components/alert/CHANGELOG.md +5 -5
- package/src/components/alert/index.ts +4 -4
- package/src/components/avatar/Avatar.astro +197 -197
- package/src/components/avatar/AvatarBadge.astro +44 -44
- package/src/components/avatar/AvatarFallback.astro +26 -26
- package/src/components/avatar/AvatarGroup.astro +26 -26
- package/src/components/avatar/AvatarGroupCount.astro +30 -30
- package/src/components/avatar/AvatarImage.astro +46 -46
- package/src/components/avatar/CHANGELOG.md +44 -44
- package/src/components/avatar/index.ts +6 -6
- package/src/components/badge/Badge.astro +167 -167
- package/src/components/badge/CHANGELOG.md +49 -49
- package/src/components/badge/index.ts +2 -2
- package/src/components/breadcrumb/Breadcrumb.astro +76 -76
- package/src/components/breadcrumb/BreadcrumbItem.astro +32 -32
- package/src/components/breadcrumb/BreadcrumbList.astro +26 -26
- package/src/components/breadcrumb/BreadcrumbSeparator.astro +26 -26
- package/src/components/breadcrumb/CHANGELOG.md +24 -24
- package/src/components/breadcrumb/index.ts +5 -5
- package/src/components/button/Button.astro +233 -233
- package/src/components/button/CHANGELOG.md +41 -41
- package/src/components/button/index.ts +2 -2
- package/src/components/button-group/ButtonGroup.astro +130 -130
- package/src/components/button-group/ButtonGroupSeparator.astro +30 -30
- package/src/components/button-group/ButtonGroupText.astro +21 -21
- package/src/components/button-group/CHANGELOG.md +34 -34
- package/src/components/button-group/index.ts +3 -3
- package/src/components/callout/CHANGELOG.md +32 -0
- package/src/components/callout/Callout.astro +147 -0
- package/src/components/callout/CalloutDescription.astro +16 -0
- package/src/components/callout/CalloutTitle.astro +16 -0
- package/src/components/callout/index.ts +3 -0
- package/src/components/card/CHANGELOG.md +162 -119
- package/src/components/card/Card.astro +197 -182
- package/src/components/card/CardAction.astro +26 -26
- package/src/components/card/CardContent.astro +28 -28
- package/src/components/card/CardDescription.astro +28 -28
- package/src/components/card/CardFooter.astro +24 -24
- package/src/components/card/CardHeader.astro +30 -30
- package/src/components/card/CardMedia.astro +27 -27
- package/src/components/card/CardMediaActions.astro +21 -0
- package/src/components/card/CardMediaSelect.astro +21 -0
- package/src/components/card/CardOverlay.astro +32 -32
- package/src/components/card/CardTitle.astro +21 -21
- package/src/components/card/index.ts +12 -10
- package/src/components/carousel/CHANGELOG.md +5 -5
- package/src/components/carousel/Carousel.astro +106 -106
- package/src/components/carousel/CarouselSlide.astro +22 -22
- package/src/components/carousel/index.ts +3 -3
- package/src/components/checkbox/CHANGELOG.md +58 -58
- package/src/components/checkbox/Checkbox.astro +159 -159
- package/src/components/checkbox/index.ts +1 -1
- package/src/components/collapsible/CHANGELOG.md +5 -5
- package/src/components/collapsible/Collapsible.astro +202 -202
- package/src/components/collapsible/CollapsibleContent.astro +36 -36
- package/src/components/collapsible/CollapsibleTrigger.astro +34 -34
- package/src/components/collapsible/index.ts +4 -4
- package/src/components/combobox/CHANGELOG.md +27 -27
- package/src/components/combobox/Combobox.astro +365 -365
- package/src/components/combobox/ComboboxContent.astro +37 -37
- package/src/components/combobox/ComboboxEmpty.astro +18 -18
- package/src/components/combobox/ComboboxGroup.astro +19 -19
- package/src/components/combobox/ComboboxInput.astro +74 -74
- package/src/components/combobox/ComboboxItem.astro +39 -39
- package/src/components/combobox/ComboboxLabel.astro +18 -18
- package/src/components/combobox/ComboboxList.astro +21 -21
- package/src/components/combobox/ComboboxSeparator.astro +17 -17
- package/src/components/combobox/ComboboxTrigger.astro +30 -30
- package/src/components/combobox/ComboboxValue.astro +22 -22
- package/src/components/combobox/index.ts +11 -11
- package/src/components/date/CHANGELOG.md +18 -18
- package/src/components/date/Date.astro +137 -137
- package/src/components/date/index.ts +2 -2
- package/src/components/dialog/CHANGELOG.md +39 -39
- package/src/components/dialog/Dialog.astro +196 -196
- package/src/components/dialog/DialogClose.astro +23 -23
- package/src/components/dialog/DialogContent.astro +42 -42
- package/src/components/dialog/DialogDescription.astro +18 -18
- package/src/components/dialog/DialogFooter.astro +18 -18
- package/src/components/dialog/DialogHeader.astro +18 -18
- package/src/components/dialog/DialogOverlay.astro +19 -19
- package/src/components/dialog/DialogTitle.astro +18 -18
- package/src/components/dialog/DialogTrigger.astro +37 -37
- package/src/components/dialog/index.ts +9 -9
- package/src/components/dropdown-menu/CHANGELOG.md +194 -194
- package/src/components/dropdown-menu/DropdownMenu.astro +226 -226
- package/src/components/dropdown-menu/DropdownMenuCheckboxItem.astro +114 -114
- package/src/components/dropdown-menu/DropdownMenuContent.astro +42 -42
- package/src/components/dropdown-menu/DropdownMenuGroup.astro +19 -19
- package/src/components/dropdown-menu/DropdownMenuItem.astro +220 -220
- package/src/components/dropdown-menu/DropdownMenuItemContent.astro +79 -79
- package/src/components/dropdown-menu/DropdownMenuLabel.astro +20 -20
- package/src/components/dropdown-menu/DropdownMenuPortal.astro +13 -13
- package/src/components/dropdown-menu/DropdownMenuRadioGroup.astro +13 -13
- package/src/components/dropdown-menu/DropdownMenuRadioItem.astro +101 -101
- package/src/components/dropdown-menu/DropdownMenuSeparator.astro +18 -18
- package/src/components/dropdown-menu/DropdownMenuShortcut.astro +18 -18
- package/src/components/dropdown-menu/DropdownMenuTrigger.astro +49 -49
- package/src/components/dropdown-menu/dropdown-menu-item-variants.ts +24 -24
- package/src/components/dropdown-menu/index.ts +12 -12
- package/src/components/field/CHANGELOG.md +33 -33
- package/src/components/field/Field.astro +664 -664
- package/src/components/field/FieldContent.astro +20 -20
- package/src/components/field/FieldDescription.astro +22 -22
- package/src/components/field/FieldError.astro +51 -51
- package/src/components/field/FieldGroup.astro +20 -20
- package/src/components/field/FieldLabel.astro +22 -22
- package/src/components/field/FieldLegend.astro +23 -23
- package/src/components/field/FieldSeparator.astro +30 -30
- package/src/components/field/FieldSet.astro +20 -20
- package/src/components/field/FieldTitle.astro +20 -20
- package/src/components/field/index.ts +10 -10
- package/src/components/hover-card/CHANGELOG.md +20 -20
- package/src/components/hover-card/HoverCard.astro +235 -235
- package/src/components/hover-card/HoverCardContent.astro +54 -54
- package/src/components/hover-card/HoverCardTrigger.astro +38 -38
- package/src/components/hover-card/index.ts +3 -3
- package/src/components/input/CHANGELOG.md +44 -29
- package/src/components/input/Input.astro +130 -130
- package/src/components/input/InputOTP.astro +211 -0
- package/src/components/input/index.ts +2 -2
- package/src/components/input-group/CHANGELOG.md +49 -49
- package/src/components/input-group/InputGroup.astro +135 -135
- package/src/components/input-group/InputGroupAddon.astro +49 -49
- package/src/components/input-group/InputGroupButton.astro +58 -58
- package/src/components/input-group/InputGroupInput.astro +18 -18
- package/src/components/input-group/InputGroupText.astro +21 -21
- package/src/components/input-group/InputGroupTextarea.astro +17 -17
- package/src/components/input-group/controlClasses.ts +2 -2
- package/src/components/input-group/index.ts +6 -6
- package/src/components/item/CHANGELOG.md +27 -27
- package/src/components/item/Item.astro +122 -122
- package/src/components/item/ItemActions.astro +21 -21
- package/src/components/item/ItemContent.astro +25 -25
- package/src/components/item/ItemDescription.astro +25 -25
- package/src/components/item/ItemFooter.astro +24 -24
- package/src/components/item/ItemGroup.astro +24 -24
- package/src/components/item/ItemHeader.astro +24 -24
- package/src/components/item/ItemMedia.astro +47 -47
- package/src/components/item/ItemSeparator.astro +21 -21
- package/src/components/item/ItemTitle.astro +25 -25
- package/src/components/item/index.ts +10 -10
- package/src/components/kbd/CHANGELOG.md +5 -5
- package/src/components/kbd/Kbd.astro +145 -145
- package/src/components/kbd/KbdGroup.astro +55 -55
- package/src/components/kbd/index.ts +2 -2
- package/src/components/label/CHANGELOG.md +28 -28
- package/src/components/label/Label.astro +97 -97
- package/src/components/label/index.ts +2 -2
- package/src/components/link-group/CHANGELOG.md +75 -75
- package/src/components/link-group/LinkGroup.astro +67 -67
- package/src/components/link-group/LinkGroupItem.astro +32 -32
- package/src/components/link-group/LinkGroupList.astro +25 -25
- package/src/components/link-group/LinkGroupTitle.astro +22 -22
- package/src/components/link-group/index.ts +5 -5
- package/src/components/link-group/link-group-list-variants.ts +15 -15
- package/src/components/logo/CHANGELOG.md +17 -0
- package/src/components/logo/Logo.astro +90 -0
- package/src/components/logo/index.ts +3 -0
- package/src/components/logo/logo-paths.ts +57 -0
- package/src/components/marquee/CHANGELOG.md +5 -5
- package/src/components/marquee/Marquee.astro +119 -119
- package/src/components/marquee/index.ts +2 -2
- package/src/components/native-select/CHANGELOG.md +5 -5
- package/src/components/native-select/NativeSelect.astro +125 -125
- package/src/components/native-select/NativeSelectOptGroup.astro +14 -14
- package/src/components/native-select/NativeSelectOption.astro +9 -9
- package/src/components/native-select/index.ts +3 -3
- package/src/components/navigation-menu/CHANGELOG.md +63 -63
- package/src/components/navigation-menu/NavigationMenu.astro +414 -414
- package/src/components/navigation-menu/NavigationMenuContent.astro +37 -37
- package/src/components/navigation-menu/NavigationMenuIndicator.astro +24 -24
- package/src/components/navigation-menu/NavigationMenuItem.astro +22 -22
- package/src/components/navigation-menu/NavigationMenuLink.astro +18 -18
- package/src/components/navigation-menu/NavigationMenuList.astro +21 -21
- package/src/components/navigation-menu/NavigationMenuPositioner.astro +44 -44
- package/src/components/navigation-menu/NavigationMenuTrigger.astro +30 -30
- package/src/components/navigation-menu/NavigationMenuViewport.astro +19 -19
- package/src/components/navigation-menu/index.ts +10 -10
- package/src/components/organisms/lightning-sidebar-nav/CHANGELOG.md +50 -0
- package/src/components/organisms/lightning-sidebar-nav/LightningSidebarNav.astro +176 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNav.astro +50 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavGroup.astro +74 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavItem.astro +94 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavProBadge.astro +26 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSection.astro +71 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSkeleton.astro +67 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSubItem.astro +69 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavUsage.astro +78 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceAvatar.astro +46 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceSwitcher.astro +130 -0
- package/src/components/organisms/lightning-sidebar-nav/icons.ts +46 -0
- package/src/components/organisms/lightning-sidebar-nav/index.ts +18 -0
- package/src/components/organisms/lightning-sidebar-nav/types.ts +77 -0
- package/src/components/popover/CHANGELOG.md +20 -20
- package/src/components/popover/Popover.astro +211 -211
- package/src/components/popover/PopoverClose.astro +18 -18
- package/src/components/popover/PopoverContent.astro +50 -50
- package/src/components/popover/PopoverDescription.astro +18 -18
- package/src/components/popover/PopoverHeader.astro +18 -18
- package/src/components/popover/PopoverTitle.astro +18 -18
- package/src/components/popover/PopoverTrigger.astro +35 -35
- package/src/components/popover/index.ts +7 -7
- package/src/components/radio-group/CHANGELOG.md +31 -31
- package/src/components/radio-group/RadioGroup.astro +81 -81
- package/src/components/radio-group/RadioGroupItem.astro +51 -51
- package/src/components/radio-group/index.ts +3 -3
- package/src/components/select/CHANGELOG.md +39 -39
- package/src/components/select/Select.astro +373 -373
- package/src/components/select/SelectContent.astro +91 -91
- package/src/components/select/SelectGroup.astro +19 -19
- package/src/components/select/SelectItem.astro +48 -48
- package/src/components/select/SelectLabel.astro +18 -18
- package/src/components/select/SelectSeparator.astro +17 -17
- package/src/components/select/SelectTrigger.astro +34 -34
- package/src/components/select/SelectValue.astro +22 -22
- package/src/components/select/index.ts +8 -8
- package/src/components/separator/CHANGELOG.md +5 -5
- package/src/components/separator/Separator.astro +60 -60
- package/src/components/separator/index.ts +2 -2
- package/src/components/skeleton/CHANGELOG.md +5 -5
- package/src/components/skeleton/Skeleton.astro +121 -121
- package/src/components/skeleton/index.ts +1 -1
- package/src/components/slider/CHANGELOG.md +5 -5
- package/src/components/slider/Slider.astro +198 -198
- package/src/components/slider/index.ts +1 -1
- package/src/components/spinner/CHANGELOG.md +18 -18
- package/src/components/spinner/Spinner.astro +135 -135
- package/src/components/spinner/index.ts +1 -1
- package/src/components/sticky-surface/CHANGELOG.md +5 -5
- package/src/components/sticky-surface/StickySurface.astro +152 -152
- package/src/components/sticky-surface/index.ts +2 -2
- package/src/components/switch/CHANGELOG.md +29 -29
- package/src/components/switch/Switch.astro +106 -106
- package/src/components/switch/index.ts +1 -1
- package/src/components/table/CHANGELOG.md +24 -24
- package/src/components/table/Table.astro +358 -251
- package/src/components/table/TableBody.astro +17 -18
- package/src/components/table/TableBulkToolbar.astro +37 -0
- package/src/components/table/TableCaption.astro +17 -17
- package/src/components/table/TableCell.astro +45 -21
- package/src/components/table/TableCellText.astro +17 -0
- package/src/components/table/TableColumnMenu.astro +50 -0
- package/src/components/table/TableEditableCell.astro +287 -0
- package/src/components/table/TableEmpty.astro +17 -0
- package/src/components/table/TableFolderCell.astro +38 -0
- package/src/components/table/TableFooter.astro +21 -21
- package/src/components/table/TableHead.astro +90 -21
- package/src/components/table/TableHeader.astro +17 -17
- package/src/components/table/TablePagination.astro +57 -0
- package/src/components/table/TableRow.astro +52 -21
- package/src/components/table/TableSelectAllBanner.astro +34 -0
- package/src/components/table/TableSelectCell.astro +50 -0
- package/src/components/table/TableSkeletonRow.astro +21 -0
- package/src/components/table/index.ts +19 -9
- package/src/components/table/table-cell-variants.ts +29 -0
- package/src/components/tabs/CHANGELOG.md +5 -5
- package/src/components/tabs/Tabs.astro +306 -306
- package/src/components/tabs/TabsContent.astro +47 -47
- package/src/components/tabs/TabsList.astro +69 -69
- package/src/components/tabs/TabsTrigger.astro +40 -40
- package/src/components/tabs/index.ts +6 -6
- package/src/components/textarea/CHANGELOG.md +22 -22
- package/src/components/textarea/Textarea.astro +58 -58
- package/src/components/textarea/index.ts +2 -2
- package/src/components/toggle/CHANGELOG.md +23 -23
- package/src/components/toggle/Toggle.astro +150 -150
- package/src/components/toggle/index.ts +1 -1
- package/src/components/toggle-group/CHANGELOG.md +18 -18
- package/src/components/toggle-group/ToggleGroup.astro +236 -236
- package/src/components/toggle-group/ToggleGroupItem.astro +48 -48
- package/src/components/toggle-group/index.ts +2 -2
- package/src/components/tooltip/CHANGELOG.md +22 -22
- package/src/components/tooltip/Tooltip.astro +357 -357
- package/src/components/tooltip/TooltipContent.astro +85 -85
- package/src/components/tooltip/TooltipTrigger.astro +34 -34
- package/src/components/tooltip/index.ts +3 -3
- package/src/lib/table-controller.ts +1090 -0
- package/src/lib/toggle-group-controller.ts +113 -113
- package/src/lib/toggle-shared.ts +27 -27
- package/src/lib/utils.ts +6 -6
- package/src/styles/globals.css +209 -177
|
@@ -1,29 +1,44 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to the `Input` component will be documented in this file.
|
|
4
|
-
|
|
5
|
-
## 2026-
|
|
6
|
-
|
|
7
|
-
Figma
|
|
8
|
-
|
|
9
|
-
###
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `Input` component will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 2026-10-06 — OTP Input (ClickLearn Lightning Design Library, node `7650-4177`)
|
|
6
|
+
|
|
7
|
+
Figma adds an "OTP Input" component set to the Input page with states Default, Focused, Filled, Disabled and Error: six joined 56×40 slots (outer corners `rounded-lg`, shared inner borders, 14px medium centered text), a label above and hint text below.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `InputOTP` (exported from `components/input`). Props: `length` (default 6), `name`, `value`, `size` (`sm` | `default` | `lg`; `default` is Figma's 56×40), `mode` (`numeric` | `alphanumeric`), `id` (applied to the first slot so `FieldLabel for` works), `disabled`, `required`, `autofocus`, `aria-invalid`.
|
|
12
|
+
- Joined value is submitted through a hidden input named by `name`.
|
|
13
|
+
- Inline script: auto-advance on type, Backspace to previous slot, arrow-key navigation, paste/OS autofill distributes characters across slots. Emits `input-otp:change` and `input-otp:complete` (`detail.value`).
|
|
14
|
+
|
|
15
|
+
### Notes
|
|
16
|
+
|
|
17
|
+
- States reuse Input's tokens exactly: `border-input`, `bg-card`, `shadow-xs`; Focus `border-ring ring-3 ring-ring/50`; Error `aria-invalid:border-destructive` (no outer ring, matching Figma); Disabled `bg-muted text-muted-foreground`. Hover not implemented (standing direction).
|
|
18
|
+
- Label and hint are composed with `Field` / `FieldLabel` / `FieldDescription` / `FieldError`, same as Input, rather than new props.
|
|
19
|
+
|
|
20
|
+
## 2026-09-09 — Figma alignment (ClickLearn Lightning Design Library, node `145-3`)
|
|
21
|
+
|
|
22
|
+
Figma documents Input across four pages: Variants (Default, Lead, Tail, Complex, Wide), Sizes (Default 32px, Large 40px), States (Default, Hover, Pressed, Focus, Disabled, Error), and Contents (Default, Lead Icon, Tail Icon, Label, Counter, Helper, Error text, All Details).
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Updated `@figmaUrl` from the old Bejamas file to the ClickLearn Lightning Design Library link.
|
|
27
|
+
- Background: `bg-transparent dark:bg-input/30` → `bg-card` — Figma's surface token is `card` (`#ffffff`), which genuinely differs from `--background`/transparent in dark mode, same category of fix already made to `DialogContent`/`HoverCardContent`.
|
|
28
|
+
- Disabled: replaced the blanket `disabled:opacity-50` fade with an explicit `disabled:bg-muted disabled:text-muted-foreground`, matching Figma's actual flat muted-gray disabled treatment (same category of fix as Checkbox's disabled state).
|
|
29
|
+
- Large size: `data-[size=lg]:md:text-base` (16px) → `data-[size=lg]:md:text-sm` (14px) — Figma's Large instance binds the identical 14px font token as Default; only padding/height grow, not font size. **Reverted in the follow-up below** — the three sizes read as visually indistinct with matching font sizes, so the user asked to keep them differentiated.
|
|
30
|
+
- Added a Pressed (`:active`) state: `active:border-ring`, matching Figma's Pressed state (same border-color token as Focus, without Focus's outer glow).
|
|
31
|
+
- Removed dead, redundant unconditional legacy classes (`h-9`, `rounded-md`, `px-2.5 py-1`, `text-base`, `md:text-sm`) that were fully superseded by the newer `data-[size=*]:...` gated system already present in the same class string — same class of cleanup already done on Dialog/HoverCard/TooltipContent.
|
|
32
|
+
|
|
33
|
+
### Notes (not changed)
|
|
34
|
+
|
|
35
|
+
- Border color (`border-input`), radius (`rounded-lg`), Focus (`focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50`), and Error/`aria-invalid` styling already matched Figma's tokens exactly — no changes needed.
|
|
36
|
+
- Figma's Hover state isn't implemented, per the standing project direction to skip hover states ("ignore hover states, they are not necessary... I will delete from figma").
|
|
37
|
+
- Figma's two documented sizes — Default (32px) and Large (40px) — correspond exactly to the existing `sm` (`h-8`) and `lg` (`h-10`) tiers. The code's own literal `"default"` tier (`h-9`, 36px) isn't represented in Figma at all — flagging in case you want to rename in Figma, same as was done for Accordion/Checkbox.
|
|
38
|
+
- Lead/Tail/Complex/Wide (icon and toolbar compositions) are already fully covered by the existing `InputGroup` family — no new props added to `Input`. Added storybook examples for all four so they're visibly demonstrated on the Input docs page.
|
|
39
|
+
- Label/Helper/Error-text content variants are already covered by the existing `Field`/`FieldLabel`/`FieldDescription`/`FieldError` primitives (shared across the whole design system, not Input-specific) — no new props added. Added a storybook example combining `Field` + `Input` to demonstrate Helper text, a character counter, and an Error state, since none of these Figma-documented compositions had a visible Input demo before. The small icon Figma shows before Helper/Error text is left as consumer-supplied slot content rather than hardcoded, since `Field` is used well beyond Input.
|
|
40
|
+
- Counter (e.g. "10/100") has no dedicated primitive — it's a plain flex row + a counter `<span>` next to `FieldLabel`, demonstrated in the new storybook example above.
|
|
41
|
+
|
|
42
|
+
### Fixed (follow-up)
|
|
43
|
+
|
|
44
|
+
- User correction: the three sizes weren't visually distinct enough with matching font sizes (per Figma's literal token match above). Restored a differentiated type scale across the existing Tailwind tokens: `sm` → `text-xs` (12px, unchanged), `default` → `text-sm` (14px, unchanged), `lg` → `text-base` (16px, reverted from the Figma-matched 14px).
|
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* @component Input
|
|
4
|
-
* @title Input
|
|
5
|
-
* @description Base input field used in forms.
|
|
6
|
-
* @description A field for collecting user input.
|
|
7
|
-
* @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=145-3
|
|
8
|
-
* @status stable
|
|
9
|
-
* @since 0.1.0
|
|
10
|
-
*
|
|
11
|
-
* @preview
|
|
12
|
-
*
|
|
13
|
-
* <Input type="text" placeholder="Enter your name" />
|
|
14
|
-
*
|
|
15
|
-
* @usage
|
|
16
|
-
*
|
|
17
|
-
* ```astro nocollapse
|
|
18
|
-
* ---
|
|
19
|
-
* import { Input } from '@bejamas/ui/components/input';
|
|
20
|
-
* ---
|
|
21
|
-
* <Input type="email" placeholder="Enter your email" />
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* @examples
|
|
25
|
-
*
|
|
26
|
-
* ### Default
|
|
27
|
-
*
|
|
28
|
-
* <Input type="email" placeholder="Enter your email" />
|
|
29
|
-
*
|
|
30
|
-
* ### Disabled
|
|
31
|
-
*
|
|
32
|
-
* <Input type="email" placeholder="Enter your email" disabled />
|
|
33
|
-
*
|
|
34
|
-
* ### Sizes
|
|
35
|
-
*
|
|
36
|
-
* <div class="grid w-full max-w-sm gap-3">
|
|
37
|
-
* <div class="flex items-center gap-2">
|
|
38
|
-
* <Input size="sm" placeholder="Small input (h-8)" />
|
|
39
|
-
* <Button size="sm" variant="outline">
|
|
40
|
-
* Small
|
|
41
|
-
* </Button>
|
|
42
|
-
* </div>
|
|
43
|
-
* <div class="flex items-center gap-2">
|
|
44
|
-
* <Input placeholder="Default input (h-9)" />
|
|
45
|
-
* <Button variant="outline">Default</Button>
|
|
46
|
-
* </div>
|
|
47
|
-
* <div class="flex items-center gap-2">
|
|
48
|
-
* <Input size="lg" placeholder="Large input (h-10)" />
|
|
49
|
-
* <Button size="lg" variant="outline">
|
|
50
|
-
* Large
|
|
51
|
-
* </Button>
|
|
52
|
-
* </div>
|
|
53
|
-
* </div>
|
|
54
|
-
*
|
|
55
|
-
* ### File
|
|
56
|
-
*
|
|
57
|
-
* <div class="grid w-full max-w-sm items-center gap-3">
|
|
58
|
-
* <Label htmlFor="picture">Picture</Label>
|
|
59
|
-
* <Input id="picture" type="file" />
|
|
60
|
-
* </div>
|
|
61
|
-
*
|
|
62
|
-
* ### With Label
|
|
63
|
-
*
|
|
64
|
-
* <div class="grid w-full max-w-sm items-center gap-3">
|
|
65
|
-
* <Label htmlFor="email">Email</Label>
|
|
66
|
-
* <Input id="email" type="email" placeholder="Enter your email" />
|
|
67
|
-
* </div>
|
|
68
|
-
*
|
|
69
|
-
* ### With Button
|
|
70
|
-
*
|
|
71
|
-
* <div class="flex w-full max-w-sm items-center gap-2">
|
|
72
|
-
* <Input type="email" placeholder="Email" />
|
|
73
|
-
* <Button type="submit" variant="outline">
|
|
74
|
-
* Subscribe
|
|
75
|
-
* </Button>
|
|
76
|
-
* </div>
|
|
77
|
-
*/
|
|
78
|
-
|
|
79
|
-
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
80
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
81
|
-
import type { HTMLAttributes } from "astro/types";
|
|
82
|
-
|
|
83
|
-
const inputVariants = cva(
|
|
84
|
-
"w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed",
|
|
85
|
-
{
|
|
86
|
-
variants: {
|
|
87
|
-
size: {
|
|
88
|
-
sm: "data-[size=sm]",
|
|
89
|
-
default: "data-[size=default]",
|
|
90
|
-
lg: "data-[size=lg]",
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
defaultVariants: {
|
|
94
|
-
size: "default",
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
type InputUiSize = NonNullable<VariantProps<typeof inputVariants>["size"]>;
|
|
100
|
-
type NativeInputSize = HTMLAttributes<"input">["size"];
|
|
101
|
-
|
|
102
|
-
interface Props extends Omit<HTMLAttributes<"input">, "size"> {
|
|
103
|
-
size?: InputUiSize | NativeInputSize;
|
|
104
|
-
nativeSize?: NativeInputSize;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const {
|
|
108
|
-
class: className = "",
|
|
109
|
-
type = "text",
|
|
110
|
-
size: sizeProp,
|
|
111
|
-
nativeSize,
|
|
112
|
-
...props
|
|
113
|
-
} = Astro.props as Props;
|
|
114
|
-
|
|
115
|
-
const isUiSize = (value: unknown): value is InputUiSize =>
|
|
116
|
-
value === "sm" || value === "default" || value === "lg";
|
|
117
|
-
|
|
118
|
-
const resolvedUiSize = isUiSize(sizeProp) ? sizeProp : "default";
|
|
119
|
-
const resolvedNativeSize =
|
|
120
|
-
nativeSize ?? (!isUiSize(sizeProp) ? sizeProp : undefined);
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
<input
|
|
124
|
-
type={type}
|
|
125
|
-
size={resolvedNativeSize}
|
|
126
|
-
data-slot={props["data-slot"] ?? "input"}
|
|
127
|
-
data-size={resolvedUiSize}
|
|
128
|
-
class={cn("border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 active:border-ring rounded-lg border bg-card shadow-xs transition-[color,box-shadow] file:h-7 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 disabled:bg-muted disabled:text-muted-foreground data-[size=sm]:h-8 data-[size=sm]:px-2.5 data-[size=sm]:py-1 data-[size=sm]:md:text-xs data-[size=default]:h-9 data-[size=default]:px-2.5 data-[size=default]:py-1 data-[size=default]:md:text-sm data-[size=lg]:h-10 data-[size=lg]:px-3.5 data-[size=lg]:py-1.5 data-[size=lg]:md:text-base", inputVariants({ size: resolvedUiSize }), className)}
|
|
129
|
-
{...props}
|
|
130
|
-
/>
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* @component Input
|
|
4
|
+
* @title Input
|
|
5
|
+
* @description Base input field used in forms.
|
|
6
|
+
* @description A field for collecting user input.
|
|
7
|
+
* @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=145-3
|
|
8
|
+
* @status stable
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*
|
|
11
|
+
* @preview
|
|
12
|
+
*
|
|
13
|
+
* <Input type="text" placeholder="Enter your name" />
|
|
14
|
+
*
|
|
15
|
+
* @usage
|
|
16
|
+
*
|
|
17
|
+
* ```astro nocollapse
|
|
18
|
+
* ---
|
|
19
|
+
* import { Input } from '@bejamas/ui/components/input';
|
|
20
|
+
* ---
|
|
21
|
+
* <Input type="email" placeholder="Enter your email" />
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @examples
|
|
25
|
+
*
|
|
26
|
+
* ### Default
|
|
27
|
+
*
|
|
28
|
+
* <Input type="email" placeholder="Enter your email" />
|
|
29
|
+
*
|
|
30
|
+
* ### Disabled
|
|
31
|
+
*
|
|
32
|
+
* <Input type="email" placeholder="Enter your email" disabled />
|
|
33
|
+
*
|
|
34
|
+
* ### Sizes
|
|
35
|
+
*
|
|
36
|
+
* <div class="grid w-full max-w-sm gap-3">
|
|
37
|
+
* <div class="flex items-center gap-2">
|
|
38
|
+
* <Input size="sm" placeholder="Small input (h-8)" />
|
|
39
|
+
* <Button size="sm" variant="outline">
|
|
40
|
+
* Small
|
|
41
|
+
* </Button>
|
|
42
|
+
* </div>
|
|
43
|
+
* <div class="flex items-center gap-2">
|
|
44
|
+
* <Input placeholder="Default input (h-9)" />
|
|
45
|
+
* <Button variant="outline">Default</Button>
|
|
46
|
+
* </div>
|
|
47
|
+
* <div class="flex items-center gap-2">
|
|
48
|
+
* <Input size="lg" placeholder="Large input (h-10)" />
|
|
49
|
+
* <Button size="lg" variant="outline">
|
|
50
|
+
* Large
|
|
51
|
+
* </Button>
|
|
52
|
+
* </div>
|
|
53
|
+
* </div>
|
|
54
|
+
*
|
|
55
|
+
* ### File
|
|
56
|
+
*
|
|
57
|
+
* <div class="grid w-full max-w-sm items-center gap-3">
|
|
58
|
+
* <Label htmlFor="picture">Picture</Label>
|
|
59
|
+
* <Input id="picture" type="file" />
|
|
60
|
+
* </div>
|
|
61
|
+
*
|
|
62
|
+
* ### With Label
|
|
63
|
+
*
|
|
64
|
+
* <div class="grid w-full max-w-sm items-center gap-3">
|
|
65
|
+
* <Label htmlFor="email">Email</Label>
|
|
66
|
+
* <Input id="email" type="email" placeholder="Enter your email" />
|
|
67
|
+
* </div>
|
|
68
|
+
*
|
|
69
|
+
* ### With Button
|
|
70
|
+
*
|
|
71
|
+
* <div class="flex w-full max-w-sm items-center gap-2">
|
|
72
|
+
* <Input type="email" placeholder="Email" />
|
|
73
|
+
* <Button type="submit" variant="outline">
|
|
74
|
+
* Subscribe
|
|
75
|
+
* </Button>
|
|
76
|
+
* </div>
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
80
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
81
|
+
import type { HTMLAttributes } from "astro/types";
|
|
82
|
+
|
|
83
|
+
const inputVariants = cva(
|
|
84
|
+
"w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed",
|
|
85
|
+
{
|
|
86
|
+
variants: {
|
|
87
|
+
size: {
|
|
88
|
+
sm: "data-[size=sm]",
|
|
89
|
+
default: "data-[size=default]",
|
|
90
|
+
lg: "data-[size=lg]",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
defaultVariants: {
|
|
94
|
+
size: "default",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
type InputUiSize = NonNullable<VariantProps<typeof inputVariants>["size"]>;
|
|
100
|
+
type NativeInputSize = HTMLAttributes<"input">["size"];
|
|
101
|
+
|
|
102
|
+
interface Props extends Omit<HTMLAttributes<"input">, "size"> {
|
|
103
|
+
size?: InputUiSize | NativeInputSize;
|
|
104
|
+
nativeSize?: NativeInputSize;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const {
|
|
108
|
+
class: className = "",
|
|
109
|
+
type = "text",
|
|
110
|
+
size: sizeProp,
|
|
111
|
+
nativeSize,
|
|
112
|
+
...props
|
|
113
|
+
} = Astro.props as Props;
|
|
114
|
+
|
|
115
|
+
const isUiSize = (value: unknown): value is InputUiSize =>
|
|
116
|
+
value === "sm" || value === "default" || value === "lg";
|
|
117
|
+
|
|
118
|
+
const resolvedUiSize = isUiSize(sizeProp) ? sizeProp : "default";
|
|
119
|
+
const resolvedNativeSize =
|
|
120
|
+
nativeSize ?? (!isUiSize(sizeProp) ? sizeProp : undefined);
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
<input
|
|
124
|
+
type={type}
|
|
125
|
+
size={resolvedNativeSize}
|
|
126
|
+
data-slot={props["data-slot"] ?? "input"}
|
|
127
|
+
data-size={resolvedUiSize}
|
|
128
|
+
class={cn("border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 active:border-ring rounded-lg border bg-card shadow-xs transition-[color,box-shadow] file:h-7 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 disabled:bg-muted disabled:text-muted-foreground data-[size=sm]:h-8 data-[size=sm]:px-2.5 data-[size=sm]:py-1 data-[size=sm]:md:text-xs data-[size=default]:h-9 data-[size=default]:px-2.5 data-[size=default]:py-1 data-[size=default]:md:text-sm data-[size=lg]:h-10 data-[size=lg]:px-3.5 data-[size=lg]:py-1.5 data-[size=lg]:md:text-base", inputVariants({ size: resolvedUiSize }), className)}
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* @component InputOTP
|
|
4
|
+
* @title OTP Input
|
|
5
|
+
* @description A row of single-character slots for entering one-time codes, with auto-advance, backspace and paste support.
|
|
6
|
+
* @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7650-4177
|
|
7
|
+
* @status stable
|
|
8
|
+
* @since 0.4.0
|
|
9
|
+
*
|
|
10
|
+
* @preview
|
|
11
|
+
*
|
|
12
|
+
* <Field class="w-fit">
|
|
13
|
+
* <FieldLabel for="otp">Verification code</FieldLabel>
|
|
14
|
+
* <InputOTP id="otp" name="code" />
|
|
15
|
+
* <FieldDescription>Enter the 6-digit code we sent to your email.</FieldDescription>
|
|
16
|
+
* </Field>
|
|
17
|
+
*
|
|
18
|
+
* @usage
|
|
19
|
+
*
|
|
20
|
+
* ```astro nocollapse
|
|
21
|
+
* ---
|
|
22
|
+
* import { InputOTP } from '@cl-light-ds/ui/components/input';
|
|
23
|
+
* ---
|
|
24
|
+
* <InputOTP id="otp" name="code" length={6} />
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @api
|
|
28
|
+
*
|
|
29
|
+
* ### Events
|
|
30
|
+
*
|
|
31
|
+
* | Event | Detail | Description |
|
|
32
|
+
* |-------|--------|-------------|
|
|
33
|
+
* | `input-otp:change` | `{ value: string }` | Fired whenever the code changes |
|
|
34
|
+
* | `input-otp:complete` | `{ value: string }` | Fired when every slot is filled |
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
38
|
+
import type { HTMLAttributes } from "astro/types";
|
|
39
|
+
|
|
40
|
+
type InputOTPSize = "sm" | "default" | "lg";
|
|
41
|
+
type InputOTPMode = "numeric" | "alphanumeric";
|
|
42
|
+
|
|
43
|
+
interface Props extends Omit<HTMLAttributes<"div">, "id"> {
|
|
44
|
+
/** Number of slots. */
|
|
45
|
+
length?: number;
|
|
46
|
+
/** Name of the hidden input that carries the joined code on form submit. */
|
|
47
|
+
name?: string;
|
|
48
|
+
/** Initial value; characters fill slots from the left. */
|
|
49
|
+
value?: string;
|
|
50
|
+
size?: InputOTPSize;
|
|
51
|
+
/** `numeric` accepts digits only; `alphanumeric` accepts letters and digits. */
|
|
52
|
+
mode?: InputOTPMode;
|
|
53
|
+
/** Applied to the first slot so a `<label for>` / `FieldLabel` focuses it. */
|
|
54
|
+
id?: string;
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
required?: boolean;
|
|
57
|
+
autofocus?: boolean;
|
|
58
|
+
"aria-invalid"?: boolean | "true" | "false";
|
|
59
|
+
class?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const {
|
|
63
|
+
class: className = "",
|
|
64
|
+
length = 6,
|
|
65
|
+
name,
|
|
66
|
+
value = "",
|
|
67
|
+
size = "default",
|
|
68
|
+
mode = "numeric",
|
|
69
|
+
id,
|
|
70
|
+
disabled = false,
|
|
71
|
+
required = false,
|
|
72
|
+
autofocus = false,
|
|
73
|
+
"aria-invalid": ariaInvalid,
|
|
74
|
+
...rest
|
|
75
|
+
} = Astro.props as Props;
|
|
76
|
+
|
|
77
|
+
const allowed = mode === "alphanumeric" ? /[a-z0-9]/i : /\d/;
|
|
78
|
+
const chars = [...value].filter((char) => allowed.test(char)).slice(0, length);
|
|
79
|
+
const slots = Array.from({ length }, (_, index) => chars[index] ?? "");
|
|
80
|
+
const invalid = ariaInvalid === true || ariaInvalid === "true";
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
<div
|
|
84
|
+
{...rest}
|
|
85
|
+
role="group"
|
|
86
|
+
data-slot="input-otp"
|
|
87
|
+
data-size={size}
|
|
88
|
+
data-mode={mode}
|
|
89
|
+
data-length={length}
|
|
90
|
+
data-disabled={disabled ? "" : undefined}
|
|
91
|
+
data-invalid={invalid ? "" : undefined}
|
|
92
|
+
class={cn("flex w-fit items-center", className)}
|
|
93
|
+
>
|
|
94
|
+
{
|
|
95
|
+
slots.map((char, index) => (
|
|
96
|
+
<input
|
|
97
|
+
type="text"
|
|
98
|
+
id={index === 0 ? id : undefined}
|
|
99
|
+
data-slot="input-otp-slot"
|
|
100
|
+
data-size={size}
|
|
101
|
+
value={char}
|
|
102
|
+
inputmode={mode === "numeric" ? "numeric" : "text"}
|
|
103
|
+
autocomplete={index === 0 ? "one-time-code" : "off"}
|
|
104
|
+
autocapitalize="off"
|
|
105
|
+
spellcheck="false"
|
|
106
|
+
aria-label={`Character ${index + 1} of ${length}`}
|
|
107
|
+
aria-invalid={invalid ? "true" : undefined}
|
|
108
|
+
disabled={disabled}
|
|
109
|
+
autofocus={index === 0 && autofocus}
|
|
110
|
+
class={cn(
|
|
111
|
+
"relative -ml-px min-w-0 border border-input bg-card text-center font-medium text-foreground shadow-xs outline-none transition-[color,box-shadow] first:ml-0 first:rounded-l-lg last:rounded-r-lg",
|
|
112
|
+
"focus-visible:z-10 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:border-ring",
|
|
113
|
+
"aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 aria-invalid:focus-visible:ring-destructive/20",
|
|
114
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground",
|
|
115
|
+
"data-[size=sm]:h-8 data-[size=sm]:w-10 data-[size=sm]:text-xs",
|
|
116
|
+
"data-[size=default]:h-10 data-[size=default]:w-14 data-[size=default]:text-sm",
|
|
117
|
+
"data-[size=lg]:h-12 data-[size=lg]:w-16 data-[size=lg]:text-base",
|
|
118
|
+
)}
|
|
119
|
+
/>
|
|
120
|
+
))
|
|
121
|
+
}
|
|
122
|
+
<input
|
|
123
|
+
type="hidden"
|
|
124
|
+
data-slot="input-otp-value"
|
|
125
|
+
name={name}
|
|
126
|
+
value={chars.join("")}
|
|
127
|
+
required={required}
|
|
128
|
+
disabled={disabled}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<script>
|
|
133
|
+
function initInputOTP(root: HTMLElement) {
|
|
134
|
+
if (root.dataset.initialized !== undefined) return;
|
|
135
|
+
root.dataset.initialized = "";
|
|
136
|
+
|
|
137
|
+
const slots = Array.from(root.querySelectorAll<HTMLInputElement>('[data-slot="input-otp-slot"]'));
|
|
138
|
+
const hidden = root.querySelector<HTMLInputElement>('[data-slot="input-otp-value"]');
|
|
139
|
+
const allowed = root.dataset.mode === "alphanumeric" ? /[a-z0-9]/i : /\d/;
|
|
140
|
+
const accept = (text: string) => [...text].filter((char) => allowed.test(char));
|
|
141
|
+
|
|
142
|
+
function sync() {
|
|
143
|
+
const value = slots.map((slot) => slot.value).join("");
|
|
144
|
+
if (hidden) hidden.value = value;
|
|
145
|
+
root.dispatchEvent(new CustomEvent("input-otp:change", { bubbles: true, detail: { value } }));
|
|
146
|
+
if (slots.every((slot) => slot.value)) {
|
|
147
|
+
root.dispatchEvent(new CustomEvent("input-otp:complete", { bubbles: true, detail: { value } }));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Writes characters into consecutive slots starting at `start`, then focuses the next empty one.
|
|
152
|
+
function fill(start: number, chars: string[]) {
|
|
153
|
+
let index = start;
|
|
154
|
+
for (const char of chars) {
|
|
155
|
+
const slot = slots[index];
|
|
156
|
+
if (!slot) break;
|
|
157
|
+
slot.value = char;
|
|
158
|
+
index++;
|
|
159
|
+
}
|
|
160
|
+
slots[Math.min(index, slots.length - 1)]?.focus();
|
|
161
|
+
sync();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
slots.forEach((slot, index) => {
|
|
165
|
+
slot.addEventListener("focus", () => slot.select());
|
|
166
|
+
|
|
167
|
+
let previousValue = slot.value;
|
|
168
|
+
slot.addEventListener("beforeinput", () => (previousValue = slot.value));
|
|
169
|
+
|
|
170
|
+
slot.addEventListener("input", (event) => {
|
|
171
|
+
const { data: typed, inputType } = event as InputEvent;
|
|
172
|
+
// Single keystroke: use only the typed character, so typing into a filled slot replaces it.
|
|
173
|
+
// Anything else (OS autofill, IME) carries the whole string in the value.
|
|
174
|
+
const chars = accept(typed && typed.length === 1 ? typed : slot.value);
|
|
175
|
+
if (chars.length) {
|
|
176
|
+
slot.value = "";
|
|
177
|
+
fill(index, chars);
|
|
178
|
+
} else {
|
|
179
|
+
// Rejected characters leave the slot as it was; deletions clear it.
|
|
180
|
+
slot.value = inputType.startsWith("insert") ? previousValue : "";
|
|
181
|
+
sync();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
slot.addEventListener("paste", (event) => {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
const chars = accept(event.clipboardData?.getData("text") ?? "");
|
|
188
|
+
if (chars.length) fill(index, chars);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
slot.addEventListener("keydown", (event) => {
|
|
192
|
+
const previous = slots[index - 1];
|
|
193
|
+
const next = slots[index + 1];
|
|
194
|
+
if (event.key === "Backspace" && !slot.value && previous) {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
previous.value = "";
|
|
197
|
+
previous.focus();
|
|
198
|
+
sync();
|
|
199
|
+
} else if (event.key === "ArrowLeft" && previous) {
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
previous.focus();
|
|
202
|
+
} else if (event.key === "ArrowRight" && next) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
next.focus();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
document.querySelectorAll<HTMLElement>('[data-slot="input-otp"]').forEach(initInputOTP);
|
|
211
|
+
</script>
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as Input } from "./Input.astro";
|
|
2
|
-
|
|
1
|
+
export { default as Input } from "./Input.astro";
|
|
2
|
+
export { default as InputOTP } from "./InputOTP.astro";
|