@waveinno/ui 0.6.0 → 0.8.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +27 -4
  3. package/README.md +6 -3
  4. package/dist/components/ui/bd-address.d.ts +30 -0
  5. package/dist/components/ui/bd-address.js +1 -0
  6. package/dist/components/ui/checkbox-card.d.ts +18 -0
  7. package/dist/components/ui/checkbox-card.js +1 -0
  8. package/dist/components/ui/context-menu.d.ts +32 -0
  9. package/dist/components/ui/context-menu.js +1 -0
  10. package/dist/components/ui/index.d.ts +9 -0
  11. package/dist/components/ui/index.js +1 -1
  12. package/dist/components/ui/input.d.ts +5 -0
  13. package/dist/components/ui/input.js +1 -1
  14. package/dist/components/ui/multi-select.d.ts +39 -0
  15. package/dist/components/ui/multi-select.js +1 -0
  16. package/dist/components/ui/nid-input.d.ts +23 -0
  17. package/dist/components/ui/nid-input.js +1 -0
  18. package/dist/components/ui/payment-method-picker.d.ts +34 -0
  19. package/dist/components/ui/payment-method-picker.js +1 -0
  20. package/dist/components/ui/phone-input.d.ts +26 -0
  21. package/dist/components/ui/phone-input.js +1 -0
  22. package/dist/components/ui/plate-input.d.ts +32 -0
  23. package/dist/components/ui/plate-input.js +1 -0
  24. package/dist/components/ui/select.js +1 -1
  25. package/dist/components/ui/sheet.d.ts +8 -3
  26. package/dist/components/ui/sheet.js +1 -1
  27. package/dist/components/ui/taka-input.d.ts +26 -0
  28. package/dist/components/ui/taka-input.js +1 -0
  29. package/dist/lib/bangla.d.ts +50 -0
  30. package/dist/lib/bangla.js +1 -0
  31. package/dist/lib/bd-geo.d.ts +39 -0
  32. package/dist/lib/bd-geo.js +1 -0
  33. package/dist/lib/index.d.ts +2 -0
  34. package/dist/lib/index.js +1 -1
  35. package/dist/styles.css +11 -0
  36. package/package.json +4 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,39 @@ prop, major = a renamed or removed export or prop.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.8.0 — 2026-10-03
10
+
11
+ - **Bangladesh kit** (new, free):
12
+ - **PhoneInput:** +880 mobile numbers typed any way, Bangla digits too; names the operator and returns
13
+ `+8801XXXXXXXXX`.
14
+ - **TakaInput:** lakh grouping as you type, the amount in words, holds a number.
15
+ - **BdAddressPicker:** 8 divisions and 64 districts in English or Bangla.
16
+ - **PaymentMethodPicker:** bKash, Nagad, Rocket, Upay, card, cash, bank, cash on delivery; cards, list or chips.
17
+ - **PlateInput:** with the plate drawn in Bangla. **NidInput:** 10, 13 or 17 digits.
18
+ - Helpers: `toBanglaDigits`, `fromBanglaDigits`, `toBongabdo` and `formatBanglaDate` (the revised Bangladesh
19
+ calendar), `lakhCrore`, `groupLakh`, `parseBdPhone`, `BD_DIVISIONS`, `BD_DISTRICTS`.
20
+ - **MultiSelect:** chips in the field, search, groups, select all, a `max` that says why it stops, full keyboard.
21
+ - **CheckboxCard:** RadioCard's twin for choosing several.
22
+ - **ContextMenu:** right-click or long-press, with the same items and looks as DropdownMenu. Adds the dependency
23
+ `@radix-ui/react-context-menu`.
24
+ - **Sheet `side="bottom"`:** a bottom sheet with a handle you can drag down to close; a centred card on wide screens.
25
+ - `fieldStateClass`: the input states for fields made of several parts.
26
+ - Docs site, **Templates**: four multi-page templates made of blocks (Launch, Control, Bazaar; Gate is free). Each has
27
+ a live preview of every page and downloads as a Next.js project that runs with `npm install && npm run dev`.
28
+
29
+ ## 0.7.0 — 2026-10-03
30
+
31
+ - **Licence: MIT.** `@waveinno/ui` is now MIT-licensed (© WAVEINNO): free to use in any project, commercial or not.
32
+ It was `UNLICENSED` before. The Pro blocks and templates are sold separately under the WAVEINNO UI Pro Licence.
33
+ - Input: a text adornment (`leading="+880"`, `trailing="kg"`) pads the field by its length, so the value never runs
34
+ under it. Icons keep the old padding.
35
+ - Select: a leading icon in `SelectTrigger` sits beside the value instead of pushing it to the middle.
36
+ - Docs site, **Blocks**: 91 blocks in 18 categories, at least 2 free and 3 Pro in each, from navbars and heroes to
37
+ dashboards, onboarding tours and order tracking. Category pages have a sidebar of every category, an All / Free / Pro
38
+ filter, numbered previews and the way to the next category. Free and Pro live in separate folders
39
+ (`playground/blocks/free`, `playground/blocks/pro`), and `npm run check` fails if a file and its tier disagree.
40
+ - Docs site: large headings, prices and stats no longer overlap the line below; text on dark cards is readable.
41
+
9
42
  ## 0.6.0 — 2026-10-02
10
43
 
11
44
  - **OrgChart** is smooth: opening or closing a team never moves the person clicked (the chart is re-anchored
package/LICENSE CHANGED
@@ -1,5 +1,28 @@
1
- Copyright (c) 2026 WAVEINNO. All rights reserved.
1
+ MIT License
2
2
 
3
- This package is published for use in WAVEINNO products and by parties WAVEINNO
4
- has authorised. No licence to copy, modify or redistribute it is granted
5
- without written permission from WAVEINNO (info@waveinno.com).
3
+ Copyright (c) 2026 WAVEINNO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This licence covers the @waveinno/ui package: everything published to npm,
26
+ and the free blocks in playground/blocks/free. The Pro blocks and templates
27
+ (playground/blocks/pro) are not covered; they are licensed separately under
28
+ the WAVEINNO UI Pro Licence in playground/blocks/pro/LICENSE.md.
package/README.md CHANGED
@@ -63,7 +63,8 @@ through `labels` props, so each product passes its own language.
63
63
 
64
64
  | Group | Components |
65
65
  | --- | --- |
66
- | Primitives | Button (working + showcase variants), Badge (soft, solid, outline, dot, gradient, live), Input / Textarea (states, adornments), NumberInput, PasswordInput (strength, Caps Lock), PinInput (OTP), FileUpload, FormField, Select, Combobox (searchable), Checkbox, RadioGroup and RadioCard, Switch, Slider (range, marks), Segmented, Tabs (underline, pill, solid, enclosed), Calendar, DatePicker, DateTimePicker, DateRangePicker, TimePicker, Dialog (drawer that widens or goes full width), Sheet, ConfirmDialog, DropdownMenu (rich items, header, dark), Menu (soft, line, raised, gradient, dark, plus the shell's panel and rail; branches, sliding highlight), Popover (arrow, dark, brand), HoverCard, Tooltip, Alert (five tones, four looks), Toaster and toast, Spinner (ring, dots, wave), Kbd, Accordion, Pagination (seven looks), Steps (six looks), TreeView, ScrollArea, TagInput, Rating, Carousel, ColorPicker, Progress / ProgressRing, Avatar, Skeleton |
66
+ | Primitives | Button (working + showcase variants), Badge (soft, solid, outline, dot, gradient, live), Input / Textarea (states, adornments), NumberInput, PasswordInput (strength, Caps Lock), PinInput (OTP), FileUpload, FormField, Select, Combobox (searchable), MultiSelect, Checkbox and CheckboxCard, RadioGroup and RadioCard, Switch, Slider (range, marks), Segmented, Tabs (underline, pill, solid, enclosed), Calendar, DatePicker, DateTimePicker, DateRangePicker, TimePicker, Dialog (drawer that widens or goes full width), Sheet (right, left, bottom with drag to close), ConfirmDialog, DropdownMenu (rich items, header, dark), ContextMenu, Menu (soft, line, raised, gradient, dark, plus the shell's panel and rail; branches, sliding highlight), Popover (arrow, dark, brand), HoverCard, Tooltip, Alert (five tones, four looks), Toaster and toast, Spinner (ring, dots, wave), Kbd, Accordion, Pagination (seven looks), Steps (six looks), TreeView, ScrollArea, TagInput, Rating, Carousel, ColorPicker, Progress / ProgressRing, Avatar, Skeleton |
67
+ | Bangladesh | PhoneInput (+880, operator), TakaInput (lakh grouping, words), BdAddressPicker (divisions and districts, English or Bangla), PaymentMethodPicker (bKash, Nagad, Rocket, Upay, card, cash, bank, COD), PlateInput, NidInput; helpers for Bangla digits, the Bangla calendar and lakh/crore |
67
68
  | Patterns | AppLauncher (switch-app button: grid, list, spotlight, glass, dark), OrgChart (card, gradient, avatar, compact, list), DataTable (advanced filters, saved views, five looks, inline and bulk edit, bulk delete, sticky actions), UserCell, RowActions, PageHero, PageHeader, StatCard, KpiCard, Money, ActivityFeed, ActivityTimeline, ChangeHistory, ChangeNotes, Note, Kanban, empty / error / loading states |
68
69
  | Charts | TrendChart, BarChart, LineChart, DonutChart, GaugeChart, Heatmap, BarList, ChartCard, validated palette |
69
70
  | Brand | WaveinnoLogo, WaveinnoSymbol, WaveMark (app icon: seven tones, round, 3D, animated), PoweredBy, Watermark; `@waveinno/ui/brand/waveinno-logo.svg` |
@@ -73,11 +74,13 @@ through `labels` props, so each product passes its own language.
73
74
 
74
75
  ```bash
75
76
  npm install
76
- npm run dev # http://localhost:4500 — docs (/docs), theme builder (/themes), every component on one page (/playground), live from src/
77
+ npm run dev # http://localhost:4500 — docs (/docs), blocks (/blocks), templates (/templates), theme builder (/themes), every component on one page (/playground), live from src/
77
78
  ```
78
79
 
79
80
  Adding or changing a component: see [CONTRIBUTING.md](CONTRIBUTING.md).
80
81
 
81
82
  ## Licence
82
83
 
83
- Proprietary — © WAVEINNO. See [LICENSE](LICENSE).
84
+ MIT © WAVEINNO. See [LICENSE](LICENSE). The library and the free blocks are free to use in any project, commercial or not.
85
+
86
+ The Pro blocks and templates are sold separately under the [WAVEINNO UI Pro Licence](playground/blocks/pro/LICENSE.md).
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ /**
3
+ * BdAddressPicker — a Bangladeshi address: division, district (only those of
4
+ * the chosen division), then the area and postcode as text.
5
+ *
6
+ * Every name is in English and Bangla (`lang`); the value holds ids
7
+ * ("dhaka", "cox-s-bazar"), so a stored address does not change with the
8
+ * language. Changing the division clears a district that is not in it.
9
+ */
10
+ export interface BdAddress {
11
+ division?: string;
12
+ district?: string;
13
+ /** Thana, area, road and house: free text. */
14
+ area?: string;
15
+ /** Four digits. */
16
+ postcode?: string;
17
+ }
18
+ export interface BdAddressPickerProps {
19
+ value?: BdAddress;
20
+ defaultValue?: BdAddress;
21
+ onValueChange?: (address: BdAddress) => void;
22
+ lang?: "en" | "bn";
23
+ /** Which text fields follow the two lists. Default both. */
24
+ fields?: ("area" | "postcode")[];
25
+ disabled?: boolean;
26
+ required?: boolean;
27
+ labels?: Partial<Record<"division" | "district" | "area" | "postcode" | "chooseDivision" | "chooseDistrict" | "divisionFirst", string>>;
28
+ className?: string;
29
+ }
30
+ export declare function BdAddressPicker({ value, defaultValue, onValueChange, lang, fields, disabled, required, labels, className }: BdAddressPickerProps): React.JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as i,jsxs as p}from"react/jsx-runtime";import*as g from"react";import{BD_DIVISIONS as x,bdDivision as h}from"../../lib/bd-geo.js";import{cn as $}from"../../lib/utils.js";import{FormField as a}from"./form-field.js";import{Input as b}from"./input.js";import{Select as f,SelectContent as S,SelectItem as F,SelectTrigger as D,SelectValue as C}from"./select.js";const y={division:"Division",district:"District",area:"Area, road and house",postcode:"Postcode",chooseDivision:"Choose a division",chooseDistrict:"Choose a district",divisionFirst:"Choose the division first"},T={division:"\u09AC\u09BF\u09AD\u09BE\u0997",district:"\u099C\u09C7\u09B2\u09BE",area:"\u098F\u09B2\u09BE\u0995\u09BE, \u09B0\u09BE\u09B8\u09CD\u09A4\u09BE \u0993 \u09AC\u09BE\u09A1\u09BC\u09BF",postcode:"\u09AA\u09CB\u09B8\u09CD\u099F \u0995\u09CB\u09A1",chooseDivision:"\u09AC\u09BF\u09AD\u09BE\u0997 \u09AC\u09BE\u099B\u09C1\u09A8",chooseDistrict:"\u099C\u09C7\u09B2\u09BE \u09AC\u09BE\u099B\u09C1\u09A8",divisionFirst:"\u0986\u0997\u09C7 \u09AC\u09BF\u09AD\u09BE\u0997 \u09AC\u09BE\u099B\u09C1\u09A8"};function _({value:u,defaultValue:B={},onValueChange:A,lang:c="en",fields:l=["area","postcode"],disabled:d,required:m,labels:I,className:N}){const[P,V]=g.useState(B),s=u??P,o={...c==="bn"?T:y,...I},r=g.useId(),v=s.division?h(s.division):void 0,n=e=>{const t={...s,...e};e.division&&e.division!==s.division&&!h(e.division)?.districts.some(k=>k.id===s.district)&&(t.district=void 0),u===void 0&&V(t),A?.(t)};return p("div",{className:$("grid gap-4 sm:grid-cols-2",N),children:[i(a,{label:o.division,required:m,children:p(f,{value:s.division??"",onValueChange:e=>n({division:e}),disabled:d,children:[i(D,{"aria-label":o.division,children:i(C,{placeholder:o.chooseDivision})}),i(S,{children:x.map(e=>i(F,{value:e.id,children:c==="bn"?e.bn:e.en},e.id))})]})}),i(a,{label:o.district,required:m,children:p(f,{value:s.district??"",onValueChange:e=>n({district:e}),disabled:d||!v,children:[i(D,{"aria-label":o.district,children:i(C,{placeholder:v?o.chooseDistrict:o.divisionFirst})}),i(S,{children:v?.districts.map(e=>i(F,{value:e.id,children:c==="bn"?e.bn:e.en},e.id))})]})}),l.includes("area")?i(a,{label:o.area,htmlFor:`${r}-area`,className:l.includes("postcode")?void 0:"sm:col-span-2",children:i(b,{id:`${r}-area`,value:s.area??"",onChange:e=>n({area:e.target.value}),disabled:d,autoComplete:"street-address"})}):null,l.includes("postcode")?i(a,{label:o.postcode,htmlFor:`${r}-post`,children:i(b,{id:`${r}-post`,value:s.postcode??"",onChange:e=>n({postcode:e.target.value.replace(/[০-৯]/g,t=>String("\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF".indexOf(t))).replace(/\D/g,"").slice(0,4)}),inputMode:"numeric",autoComplete:"postal-code",placeholder:"1212",disabled:d,className:"num"})}):null]})}export{_ as BdAddressPicker};
@@ -0,0 +1,18 @@
1
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
2
+ import * as React from "react";
3
+ /**
4
+ * CheckboxCard — RadioCard's twin for choosing several: the whole card is the
5
+ * target, with an icon, a title, a line under it and something on the right (a
6
+ * price, a count). Add-ons, features to switch on, sites to include.
7
+ *
8
+ * It is a real checkbox (Space toggles it, screen readers say "checked"), so
9
+ * a grid of them needs no group wrapper; put them in any layout.
10
+ */
11
+ export interface CheckboxCardProps extends Omit<React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, "title"> {
12
+ title: React.ReactNode;
13
+ description?: React.ReactNode;
14
+ icon?: React.ReactNode;
15
+ /** On the right: a price, a time, a badge. */
16
+ aside?: React.ReactNode;
17
+ }
18
+ export declare const CheckboxCard: React.ForwardRefExoticComponent<CheckboxCardProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as i from"@radix-ui/react-checkbox";import{Check as l}from"lucide-react";import*as m from"react";import{cn as a}from"../../lib/utils.js";const b=m.forwardRef(({className:d,title:c,description:t,icon:o,aside:s,...n},p)=>r(i.Root,{ref:p,className:a("group relative flex w-full cursor-pointer items-start gap-3 rounded-[calc(12px*var(--radius-scale))] border border-border bg-raised p-3.5 pr-10 text-left outline-none","transition-[border-color,background-color,box-shadow] duration-200 hover:border-border2","focus-visible:ring-2 focus-visible:ring-primary/35","data-[state=checked]:border-primary data-[state=checked]:bg-[linear-gradient(180deg,var(--primary-subtle),transparent_120%)] data-[state=checked]:shadow-[0_0_0_3px_var(--primary-subtle),0_6px_16px_-8px_color-mix(in_srgb,var(--primary)_35%,transparent)]","disabled:cursor-not-allowed disabled:opacity-50",d),...n,children:[o?e("span",{className:a("grid size-9 shrink-0 place-items-center rounded-[calc(10px*var(--radius-scale))] border border-border bg-surface text-muted transition-colors duration-200 [&_svg]:size-[18px]","group-data-[state=checked]:border-primary/25 group-data-[state=checked]:bg-raised group-data-[state=checked]:text-primary"),children:o}):null,r("span",{className:"min-w-0 flex-1",children:[r("span",{className:"flex items-center justify-between gap-3",children:[e("span",{className:"truncate text-[13.5px] font-semibold text-text",children:c}),s?e("span",{className:"num shrink-0 text-[13px] font-semibold text-text",children:s}):null]}),t?e("span",{className:"mt-0.5 block text-[12px] leading-[17px] text-muted",children:t}):null]}),e("span",{"aria-hidden":!0,className:a("absolute right-3.5 top-3.5 grid size-[18px] place-items-center rounded-[5px] border border-border2 bg-raised text-white transition-[background-color,border-color] duration-200","group-data-[state=checked]:border-primary group-data-[state=checked]:bg-primary"),children:e(i.Indicator,{forceMount:!0,className:"grid place-items-center data-[state=unchecked]:hidden",children:e(l,{className:"size-3 animate-[wm-pop_180ms_var(--ease)]",strokeWidth:3})})})]}));b.displayName="CheckboxCard";export{b as CheckboxCard};
@@ -0,0 +1,32 @@
1
+ import * as Primitive from "@radix-ui/react-context-menu";
2
+ import * as React from "react";
3
+ import type { MenuVariant } from "./dropdown-menu";
4
+ /**
5
+ * ContextMenu — the menu a right-click (or a long press on a phone) opens over
6
+ * a row, a card, a file: the actions for that thing, where the pointer is.
7
+ *
8
+ * It looks and reads exactly like DropdownMenu, with the same item options
9
+ * (`icon`, `description`, `shortcut`, `danger`) and surfaces (`default`,
10
+ * `glass`, `dark`), so a table can offer the same actions both ways. Keep the
11
+ * actions available elsewhere too: a right-click is never the only way in.
12
+ */
13
+ export declare const ContextMenu: React.FC<Primitive.ContextMenuProps>;
14
+ export declare const ContextMenuTrigger: React.ForwardRefExoticComponent<Primitive.ContextMenuTriggerProps & React.RefAttributes<HTMLSpanElement>>;
15
+ export declare const ContextMenuGroup: React.ForwardRefExoticComponent<Primitive.ContextMenuGroupProps & React.RefAttributes<HTMLDivElement>>;
16
+ export declare const ContextMenuSub: React.FC<Primitive.ContextMenuSubProps>;
17
+ export declare const ContextMenuRadioGroup: React.ForwardRefExoticComponent<Primitive.ContextMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
18
+ export declare const ContextMenuContent: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
19
+ variant?: MenuVariant;
20
+ } & React.RefAttributes<HTMLDivElement>>;
21
+ export declare const ContextMenuItem: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
22
+ danger?: boolean;
23
+ icon?: React.ReactNode;
24
+ description?: React.ReactNode;
25
+ shortcut?: string;
26
+ } & React.RefAttributes<HTMLDivElement>>;
27
+ export declare const ContextMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
28
+ export declare const ContextMenuRadioItem: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
29
+ export declare const ContextMenuLabel: ({ className, ...props }: React.ComponentPropsWithoutRef<typeof Primitive.Label>) => React.JSX.Element;
30
+ export declare const ContextMenuSeparator: ({ className, ...props }: React.ComponentPropsWithoutRef<typeof Primitive.Separator>) => React.JSX.Element;
31
+ export declare const ContextMenuSubTrigger: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuSubTriggerProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
32
+ export declare const ContextMenuSubContent: React.ForwardRefExoticComponent<Omit<Primitive.ContextMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as o,jsxs as m}from"react/jsx-runtime";import*as e from"@radix-ui/react-context-menu";import{Check as c,ChevronRight as g}from"lucide-react";import*as s from"react";import{cn as i}from"../../lib/utils.js";const k=e.Root,S=e.Trigger,w=e.Group,W=e.Sub,T=e.RadioGroup,u="z-50 min-w-52 overflow-hidden rounded-dlg border border-border bg-raised p-1 shadow-elev3",l={default:"",glass:"border-white/40 bg-raised/70 backdrop-blur-xl backdrop-saturate-150 dark:border-white/10",dark:"dark border-white/10 bg-[var(--ink)] text-white [--hover:rgba(255,255,255,.08)] [--text:#f5f6fa] [--body:#d6d8e6] [--muted:#9aa1bd] [--border:rgba(255,255,255,.1)]"},x=s.createContext("default"),R=s.forwardRef(({className:r,variant:t="default",...n},a)=>o(x.Provider,{value:t,children:o(e.Portal,{children:o(e.Content,{ref:a,"data-pop":"",className:i(u,l[t],r),...n})})}));R.displayName="ContextMenuContent";const p="relative flex cursor-pointer select-none items-center gap-2.5 rounded-ctl px-2.5 py-2 text-[13px] text-body outline-none transition-colors data-[highlighted]:bg-hover data-[highlighted]:text-text data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted",h=s.forwardRef(({className:r,danger:t,icon:n,description:a,shortcut:d,children:f,...b},C)=>m(e.Item,{ref:C,className:i(p,a&&"items-start",t&&"text-danger data-[highlighted]:bg-danger-subtle data-[highlighted]:text-danger [&_svg]:text-danger",r),...b,children:[n?o("span",{className:i("grid shrink-0 place-items-center",a&&"mt-0.5"),children:n}):null,m("span",{className:"min-w-0 flex-1",children:[o("span",{className:"block truncate",children:f}),a?o("span",{className:"mt-0.5 block text-[11.5px] leading-4 text-muted",children:a}):null]}),d?o("kbd",{className:"ml-auto self-center rounded-[calc(5px*var(--radius-scale))] border border-border bg-surface px-1.5 py-px font-sans text-[10.5px] text-muted",children:d}):null]}));h.displayName="ContextMenuItem";const v=s.forwardRef(({className:r,children:t,...n},a)=>m(e.CheckboxItem,{ref:a,className:i(p,"pr-8",r),...n,children:[t,o(e.ItemIndicator,{className:"absolute right-2.5",children:o(c,{className:"size-3.5 !text-primary",strokeWidth:3})})]}));v.displayName="ContextMenuCheckboxItem";const P=s.forwardRef(({className:r,children:t,...n},a)=>m(e.RadioItem,{ref:a,className:i(p,"pr-8",r),...n,children:[t,o(e.ItemIndicator,{className:"absolute right-2.5",children:o(c,{className:"size-3.5 !text-primary",strokeWidth:3})})]}));P.displayName="ContextMenuRadioItem";const V=({className:r,...t})=>o(e.Label,{className:i("ov px-2.5 pb-1 pt-2",r),...t}),z=({className:r,...t})=>o(e.Separator,{className:i("-mx-1 my-1 h-px bg-border",r),...t}),y=s.forwardRef(({className:r,children:t,...n},a)=>m(e.SubTrigger,{ref:a,className:i(p,"data-[state=open]:bg-hover",r),...n,children:[t,o(g,{className:"ml-auto size-3.5"})]}));y.displayName="ContextMenuSubTrigger";const I=s.forwardRef(({className:r,...t},n)=>{const a=s.useContext(x);return o(e.Portal,{children:o(e.SubContent,{ref:n,"data-pop":"",className:i(u,l[a],r),...t})})});I.displayName="ContextMenuSubContent";export{k as ContextMenu,v as ContextMenuCheckboxItem,R as ContextMenuContent,w as ContextMenuGroup,h as ContextMenuItem,V as ContextMenuLabel,T as ContextMenuRadioGroup,P as ContextMenuRadioItem,z as ContextMenuSeparator,W as ContextMenuSub,I as ContextMenuSubContent,y as ContextMenuSubTrigger,S as ContextMenuTrigger};
@@ -45,3 +45,12 @@ export * from "./time-picker";
45
45
  export * from "./toast";
46
46
  export * from "./tooltip";
47
47
  export * from "./tree-view";
48
+ export * from "./bd-address";
49
+ export * from "./checkbox-card";
50
+ export * from "./context-menu";
51
+ export * from "./multi-select";
52
+ export * from "./nid-input";
53
+ export * from "./payment-method-picker";
54
+ export * from "./phone-input";
55
+ export * from "./plate-input";
56
+ export * from "./taka-input";
@@ -1 +1 @@
1
- export*from"./accordion.js";export*from"./alert.js";export*from"./avatar.js";export*from"./badge.js";export*from"./button.js";export*from"./calendar.js";export*from"./card.js";export*from"./carousel.js";export*from"./checkbox.js";export*from"./color-picker.js";export*from"./combobox.js";export*from"./confirm-dialog.js";export*from"./date-picker.js";export*from"./dialog.js";export*from"./dropdown-menu.js";export*from"./file-upload.js";export*from"./form-field.js";export*from"./hover-card.js";export*from"./input.js";export*from"./kbd.js";export*from"./label.js";export*from"./menu.js";export*from"./number-input.js";export*from"./pagination.js";export*from"./password-input.js";export*from"./pin-input.js";export*from"./popover.js";export*from"./progress.js";export*from"./radio-group.js";export*from"./rating.js";export*from"./scroll-area.js";export*from"./segmented.js";export*from"./select.js";export*from"./separator.js";export*from"./sheet.js";export*from"./skeleton.js";export*from"./slider.js";export*from"./spinner.js";export*from"./steps.js";export*from"./switch.js";export*from"./tabs.js";export*from"./tag-input.js";export*from"./time-picker.js";export*from"./toast.js";export*from"./tooltip.js";export*from"./tree-view.js";
1
+ export*from"./accordion.js";export*from"./alert.js";export*from"./avatar.js";export*from"./badge.js";export*from"./button.js";export*from"./calendar.js";export*from"./card.js";export*from"./carousel.js";export*from"./checkbox.js";export*from"./color-picker.js";export*from"./combobox.js";export*from"./confirm-dialog.js";export*from"./date-picker.js";export*from"./dialog.js";export*from"./dropdown-menu.js";export*from"./file-upload.js";export*from"./form-field.js";export*from"./hover-card.js";export*from"./input.js";export*from"./kbd.js";export*from"./label.js";export*from"./menu.js";export*from"./number-input.js";export*from"./pagination.js";export*from"./password-input.js";export*from"./pin-input.js";export*from"./popover.js";export*from"./progress.js";export*from"./radio-group.js";export*from"./rating.js";export*from"./scroll-area.js";export*from"./segmented.js";export*from"./select.js";export*from"./separator.js";export*from"./sheet.js";export*from"./skeleton.js";export*from"./slider.js";export*from"./spinner.js";export*from"./steps.js";export*from"./switch.js";export*from"./tabs.js";export*from"./tag-input.js";export*from"./time-picker.js";export*from"./toast.js";export*from"./tooltip.js";export*from"./tree-view.js";export*from"./bd-address.js";export*from"./checkbox-card.js";export*from"./context-menu.js";export*from"./multi-select.js";export*from"./nid-input.js";export*from"./payment-method-picker.js";export*from"./phone-input.js";export*from"./plate-input.js";export*from"./taka-input.js";
@@ -7,6 +7,11 @@ export declare const inputClass: string;
7
7
  * (FormField does that).
8
8
  */
9
9
  export type InputState = "default" | "error" | "warning" | "success";
10
+ /**
11
+ * The same states for a field made of several parts (a prefix, an input, a
12
+ * badge) where the focus ring belongs on the wrapper: PhoneInput, TakaInput….
13
+ */
14
+ export declare const fieldStateClass: Record<InputState, string>;
10
15
  interface Extra {
11
16
  state?: InputState;
12
17
  /** Icon or text inside the box, before the value (a search icon, "৳", "+880"). */
@@ -1 +1 @@
1
- "use client";import{jsx as a,jsxs as g}from"react/jsx-runtime";import*as i from"react";import{cn as d}from"../../lib/utils.js";const l="h-ctl w-full rounded-ctl border border-border2 bg-raised px-3 text-[13px] text-text placeholder:text-placeholder outline-none transition-[border-color,box-shadow] duration-150 focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:cursor-not-allowed disabled:bg-hover disabled:text-muted aria-[invalid=true]:border-danger aria-[invalid=true]:ring-danger/20",u={default:"",error:"border-danger bg-danger-subtle/30 focus:border-danger focus:ring-danger/20 wm-shake",warning:"border-warning focus:border-warning focus:ring-warning/20",success:"border-success focus:border-success focus:ring-success/20"};function p({leading:r,trailing:e,children:t}){return!r&&!e?t:g("div",{className:"relative w-full",children:[r?a("span",{className:"pointer-events-none absolute left-3 top-1/2 flex -translate-y-1/2 items-center text-[13px] text-muted [&_svg]:size-4",children:r}):null,t,e?a("span",{className:"absolute right-2.5 top-1/2 flex -translate-y-1/2 items-center text-[13px] text-muted [&_svg]:size-4",children:e}):null]})}const f=i.forwardRef(({className:r,type:e="text",state:t="default",leading:n,trailing:s,...o},c)=>a(p,{leading:n,trailing:s,children:a("input",{ref:c,type:e,"aria-invalid":t==="error"?!0:o["aria-invalid"],className:d(l,u[t],n?"pl-9":null,s?"pr-10":null,r),...o})}));f.displayName="Input";const x=i.forwardRef(({className:r,state:e="default",...t},n)=>a("textarea",{ref:n,"aria-invalid":e==="error"?!0:t["aria-invalid"],className:d(l,"h-auto min-h-20 resize-y py-2 leading-5",u[e],r),...t}));x.displayName="Textarea";export{f as Input,x as Textarea,l as inputClass};
1
+ "use client";import{jsx as n,jsxs as m}from"react/jsx-runtime";import*as o from"react";import{cn as d}from"../../lib/utils.js";const c="h-ctl w-full rounded-ctl border border-border2 bg-raised px-3 text-[13px] text-text placeholder:text-placeholder outline-none transition-[border-color,box-shadow] duration-150 focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:cursor-not-allowed disabled:bg-hover disabled:text-muted aria-[invalid=true]:border-danger aria-[invalid=true]:ring-danger/20",h={default:"border-border2 focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20",error:"border-danger bg-danger-subtle/30 focus-within:ring-2 focus-within:ring-danger/20 wm-shake",warning:"border-warning focus-within:ring-2 focus-within:ring-warning/20",success:"border-success focus-within:ring-2 focus-within:ring-success/20"},u={default:"",error:"border-danger bg-danger-subtle/30 focus:border-danger focus:ring-danger/20 wm-shake",warning:"border-warning focus:border-warning focus:ring-warning/20",success:"border-success focus:border-success focus:ring-success/20"};function p({leading:r,trailing:e,children:t}){return!r&&!e?t:m("div",{className:"relative w-full",children:[r?n("span",{className:"pointer-events-none absolute left-3 top-1/2 flex -translate-y-1/2 items-center text-[13px] text-muted [&_svg]:size-4",children:r}):null,t,e?n("span",{className:"absolute right-2.5 top-1/2 flex -translate-y-1/2 items-center text-[13px] text-muted [&_svg]:size-4",children:e}):null]})}const b=o.forwardRef(({className:r,type:e="text",state:t="default",leading:a,trailing:i,style:f,...s},g)=>n(p,{leading:a,trailing:i,children:n("input",{ref:g,type:e,"aria-invalid":t==="error"?!0:s["aria-invalid"],className:d(c,u[t],a?"pl-9":null,i?"pr-10":null,r),style:{...l("paddingLeft",a),...l("paddingRight",i),...f},...s})}));function l(r,e){if(typeof e!="string"&&typeof e!="number")return;const t=String(e).length;return t>1?{[r]:`calc(${t}ch + 20px)`}:void 0}b.displayName="Input";const x=o.forwardRef(({className:r,state:e="default",...t},a)=>n("textarea",{ref:a,"aria-invalid":e==="error"?!0:t["aria-invalid"],className:d(c,"h-auto min-h-20 resize-y py-2 leading-5",u[e],r),...t}));x.displayName="Textarea";export{b as Input,x as Textarea,h as fieldStateClass,c as inputClass};
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+ import { type InputState } from "./input";
3
+ /**
4
+ * MultiSelect — choose several from a list too long for checkboxes: sites a
5
+ * manager can see, payment methods a gate accepts.
6
+ *
7
+ * The field shows the choices as chips (removable in place) and "+3" past
8
+ * `maxShown`. The list searches as you type, keeps `group`s under headings,
9
+ * and is driven from the keyboard: ↑ ↓ to move, Enter or Space to toggle, Esc
10
+ * to close; Backspace in an empty search removes the last choice. `max` stops
11
+ * further choices with a note rather than silently ignoring the click.
12
+ */
13
+ export interface MultiSelectOption {
14
+ value: string;
15
+ label: string;
16
+ description?: string;
17
+ icon?: React.ReactNode;
18
+ /** A heading the option is listed under. */
19
+ group?: string;
20
+ disabled?: boolean;
21
+ }
22
+ export interface MultiSelectProps {
23
+ options: MultiSelectOption[];
24
+ value?: string[];
25
+ defaultValue?: string[];
26
+ onValueChange?: (value: string[]) => void;
27
+ placeholder?: string;
28
+ /** Chips shown in the field before "+N". Default 3. */
29
+ maxShown?: number;
30
+ max?: number;
31
+ /** Offer "Select all" and "Clear" under the list. Default true. */
32
+ bulk?: boolean;
33
+ disabled?: boolean;
34
+ state?: InputState;
35
+ labels?: Partial<Record<"search" | "empty" | "selectAll" | "clear" | "selected" | "limit" | "remove", string>>;
36
+ "aria-label"?: string;
37
+ className?: string;
38
+ }
39
+ export declare function MultiSelect({ options, value, defaultValue, onValueChange, placeholder, maxShown, max, bulk, disabled, state, labels, "aria-label": ariaLabel, className, }: MultiSelectProps): React.JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{Fragment as Y,jsx as t,jsxs as r}from"react/jsx-runtime";import{Check as _,ChevronDown as U,Search as B,X as G}from"lucide-react";import*as l from"react";import{cn as g}from"../../lib/utils.js";import{fieldStateClass as Q}from"./input.js";import{Popover as W,PopoverAnchor as X,PopoverContent as j}from"./popover.js";const J={search:"Search\u2026",empty:"Nothing matches",selectAll:"Select all",clear:"Clear",selected:"selected",limit:"Up to {max}",remove:"Remove"};function ne({options:w,value:k,defaultValue:R=[],onValueChange:z,placeholder:I="Choose\u2026",maxShown:h=3,max:i,bulk:C=!0,disabled:p,state:L="default",labels:O,"aria-label":q,className:K}){const s={...J,...O},[T,V]=l.useState(R),a=k??T,[m,v]=l.useState(!1),[f,S]=l.useState(""),[c,b]=l.useState(0),x=l.useId(),D=l.useRef(null),u=e=>{k===void 0&&V(e),z?.(e)},M=i!==void 0&&a.length>=i,A=e=>{a.includes(e)?u(a.filter(n=>n!==e)):M||u([...a,e])},y=f.trim().toLowerCase(),o=w.filter(e=>!y||`${e.label} ${e.description??""}`.toLowerCase().includes(y)),E=new Map(w.map(e=>[e.value,e]));l.useEffect(()=>b(0),[y,m]),l.useEffect(()=>{D.current?.querySelector(`[data-index="${c}"]`)?.scrollIntoView({block:"nearest"})},[c]);const F=e=>{if(e.key==="ArrowDown")e.preventDefault(),b(n=>Math.min(o.length-1,n+1));else if(e.key==="ArrowUp")e.preventDefault(),b(n=>Math.max(0,n-1));else if(e.key==="Enter"||e.key===" "&&!f){e.preventDefault();const n=o[c];n&&!n.disabled&&A(n.value)}else e.key==="Backspace"&&!f&&a.length&&u(a.slice(0,-1))};let P;return r(W,{open:m,onOpenChange:e=>(v(e),e||S("")),children:[t(X,{asChild:!0,children:r("div",{role:"combobox",tabIndex:p?-1:0,"aria-expanded":m,"aria-controls":x,"aria-haspopup":"listbox","aria-label":q,"aria-disabled":p||void 0,"data-open":m,onClick:()=>!p&&v(e=>!e),onKeyDown:e=>{!p&&(e.key==="Enter"||e.key===" "||e.key==="ArrowDown")&&(e.preventDefault(),v(!0))},className:g("flex min-h-ctl w-full cursor-pointer items-center gap-1.5 rounded-ctl border bg-raised py-1 pl-1.5 pr-2.5 text-[13px] outline-none transition-[border-color,box-shadow]",Q[L],"data-[open=true]:border-primary data-[open=true]:ring-2 data-[open=true]:ring-primary/20",p&&"cursor-not-allowed bg-hover opacity-70",K),children:[t("span",{className:"flex min-w-0 flex-1 flex-wrap items-center gap-1",children:a.length?r(Y,{children:[a.slice(0,h).map(e=>r("span",{className:"inline-flex h-6 max-w-[160px] items-center gap-1 rounded-[7px] bg-primary-subtle pl-2 pr-0.5 text-[12px] font-medium text-primary animate-[wm-pop_180ms_var(--ease)]",children:[t("span",{className:"truncate",children:E.get(e)?.label??e}),t("button",{type:"button","aria-label":`${s.remove} ${E.get(e)?.label??e}`,disabled:p,onClick:n=>{n.stopPropagation(),u(a.filter(d=>d!==e))},className:"grid size-5 cursor-pointer place-items-center rounded-[5px] hover:bg-primary/15",children:t(G,{className:"size-3"})})]},e)),a.length>h?r("span",{className:"rounded-[7px] bg-hover px-2 py-0.5 text-[12px] font-medium text-muted",children:["+",a.length-h]}):null]}):t("span",{className:"px-1.5 text-placeholder",children:I})}),t(U,{className:g("size-4 shrink-0 text-muted transition-transform duration-200",m&&"rotate-180")})]})}),r(j,{className:"w-[var(--radix-popover-trigger-width)] min-w-56 p-0",onOpenAutoFocus:e=>(e.preventDefault(),e.currentTarget.querySelector("input")?.focus()),children:[r("div",{className:"flex items-center gap-2 border-b border-border px-3",children:[t(B,{className:"size-4 shrink-0 text-muted"}),t("input",{value:f,onChange:e=>S(e.target.value),onKeyDown:F,placeholder:s.search,"aria-label":s.search,"aria-controls":x,"aria-activedescendant":o[c]?`${x}-${o[c].value}`:void 0,className:"h-10 min-w-0 flex-1 bg-transparent text-[13px] text-text outline-none placeholder:text-placeholder"})]}),t("div",{ref:D,id:x,role:"listbox","aria-multiselectable":!0,className:"max-h-64 overflow-y-auto p-1",children:o.length?o.map((e,n)=>{const d=a.includes(e.value),N=e.disabled||M&&!d,$=e.group&&e.group!==P?e.group:null;return P=e.group,r(l.Fragment,{children:[$?t("p",{className:"px-2.5 pb-1 pt-2.5 text-[11px] font-semibold uppercase tracking-[0.1em] text-muted",children:$}):null,r("div",{id:`${x}-${e.value}`,role:"option","aria-selected":d,"aria-disabled":N||void 0,"data-index":n,onMouseEnter:()=>b(n),onMouseDown:H=>H.preventDefault(),onClick:()=>!N&&A(e.value),className:g("flex cursor-pointer items-center gap-2.5 rounded-[8px] px-2.5 py-2 text-[13px] text-text",n===c&&"bg-hover",N&&"cursor-not-allowed opacity-45"),children:[t("span",{"aria-hidden":!0,className:g("grid size-4 shrink-0 place-items-center rounded-[5px] border transition-colors",d?"border-primary bg-primary text-white":"border-border2 bg-raised"),children:d?t(_,{className:"size-3",strokeWidth:3}):null}),e.icon?t("span",{className:"grid shrink-0 place-items-center text-muted [&_svg]:size-4",children:e.icon}):null,r("span",{className:"min-w-0 flex-1",children:[t("span",{className:"block truncate",children:e.label}),e.description?t("span",{className:"block truncate text-[11.5px] text-muted",children:e.description}):null]})]})]},e.value)}):t("p",{className:"px-3 py-6 text-center text-[13px] text-muted",children:s.empty})}),C||i!==void 0?r("div",{className:"flex items-center justify-between border-t border-border px-3 py-2 text-[12px]",children:[r("span",{className:"text-muted",children:[a.length," ",s.selected,i!==void 0?` \xB7 ${s.limit.replace("{max}",String(i))}`:""]}),C?r("span",{className:"flex gap-3 font-medium",children:[i===void 0?t("button",{type:"button",onClick:()=>u([...new Set([...a,...o.filter(e=>!e.disabled).map(e=>e.value)])]),className:"cursor-pointer text-primary hover:underline",children:s.selectAll}):null,t("button",{type:"button",disabled:!a.length,onClick:()=>u([]),className:"cursor-pointer text-muted hover:text-text disabled:opacity-40",children:s.clear})]}):null]}):null]})]})}export{ne as MultiSelect};
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import { type InputState } from "./input";
3
+ /**
4
+ * NidInput — a Bangladeshi national ID number.
5
+ *
6
+ * Takes digits only (Bangla digits too) and groups them as they are printed:
7
+ * the 10-digit smart card as 123 456 7890, the older 13- and 17-digit numbers
8
+ * in fours. It names the kind once the length matches one and ticks; any other
9
+ * length is incomplete, never silently accepted. `onValueChange` gets the bare
10
+ * digits and whether they form a valid length.
11
+ */
12
+ export type NidKind = "smart" | "old13" | "old17";
13
+ export interface NidInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type"> {
14
+ value?: string;
15
+ defaultValue?: string;
16
+ onValueChange?: (digits: string, info: {
17
+ valid: boolean;
18
+ kind: NidKind | null;
19
+ }) => void;
20
+ state?: InputState;
21
+ labels?: Partial<Record<NidKind, string>>;
22
+ }
23
+ export declare const NidInput: React.ForwardRefExoticComponent<NidInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as u,jsxs as m}from"react/jsx-runtime";import{Check as v,IdCard as y}from"lucide-react";import*as p from"react";import{cn as g}from"../../lib/utils.js";import{fieldStateClass as w}from"./input.js";const a={10:"smart",13:"old13",17:"old17"},C={smart:"Smart card",old13:"Old NID",old17:"Old NID, with birth year"},d=e=>e.replace(/[০-৯]/g,i=>String("\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF".indexOf(i))).replace(/\D/g,"").slice(0,17);function S(e){const i=e.length<=10?[3,3,4]:e.length<=13?[4,4,5]:[4,4,4,5],r=[];let t=0;for(const o of i){if(t>=e.length)break;r.push(e.slice(t,t+o)),t+=o}return r.join(" ")}const K=p.forwardRef(({value:e,defaultValue:i="",onValueChange:r,state:t="default",labels:o,className:f,disabled:c,placeholder:h="123 456 7890",...x},N)=>{const[b,I]=p.useState(()=>d(i)),n=e!==void 0?d(e):b,l=a[n.length]??null;return m("div",{className:g("flex h-ctl w-full items-center gap-2 rounded-ctl border bg-raised px-3 shadow-[inset_0_1px_1px_rgba(5,8,22,.04)] transition-[border-color,box-shadow]",w[t],c&&"cursor-not-allowed bg-hover opacity-70",f),children:[u(y,{className:"size-4 shrink-0 text-muted"}),u("input",{ref:N,type:"text",inputMode:"numeric",autoComplete:"off",disabled:c,placeholder:h,value:S(n),onChange:k=>{const s=d(k.target.value);e===void 0&&I(s),r?.(s,{valid:!!a[s.length],kind:a[s.length]??null})},className:"num h-full min-w-0 flex-1 bg-transparent text-[13.5px] tracking-[0.04em] text-text outline-none placeholder:tracking-normal placeholder:text-placeholder",...x}),n?m("span",{className:g("inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-[11.5px] font-medium",l?"bg-success-subtle text-success":"bg-hover text-muted"),children:[l?u(v,{className:"size-3",strokeWidth:3}):null,l?o?.[l]??C[l]:`${n.length} / ${n.length<10?10:n.length<13?13:17}`]}):null]})});K.displayName="NidInput";export{K as NidInput};
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ /**
3
+ * PaymentMethodPicker — how a customer in Bangladesh pays: bKash, Nagad,
4
+ * Rocket, Upay, a card, cash, a bank transfer, cash on delivery.
5
+ *
6
+ * Wallets get a lettered tile in their own colour (no logos, so nothing to
7
+ * license); the rest get an icon. Pass `methods` to choose and order them, as
8
+ * ids or as your own `{ id, name, description, mark }`, and `fees` to show a
9
+ * charge or a discount beside each one. Looks: `cards` (default), `list` (one
10
+ * column) or `chips` (one row, for a summary sidebar). Arrow keys move the
11
+ * choice, as with any radio group.
12
+ */
13
+ export type PaymentMethodId = "bkash" | "nagad" | "rocket" | "upay" | "card" | "cash" | "bank" | "cod";
14
+ export interface PaymentMethod {
15
+ id: string;
16
+ name: string;
17
+ description?: string;
18
+ /** A tile: an icon, or the colour and letter of a wallet. */
19
+ mark: React.ReactNode;
20
+ }
21
+ export declare const PAYMENT_METHODS: Record<PaymentMethodId, PaymentMethod>;
22
+ export interface PaymentMethodPickerProps {
23
+ value?: string;
24
+ defaultValue?: string;
25
+ onValueChange?: (id: string) => void;
26
+ methods?: (PaymentMethodId | PaymentMethod)[];
27
+ /** Beside each method: "No fee", "1.85%", "−৳50". */
28
+ fees?: Partial<Record<string, React.ReactNode>>;
29
+ variant?: "cards" | "list" | "chips";
30
+ disabled?: boolean;
31
+ "aria-label"?: string;
32
+ className?: string;
33
+ }
34
+ export declare function PaymentMethodPicker({ value, defaultValue, onValueChange, methods, fees, variant, disabled, "aria-label": ariaLabel, className, }: PaymentMethodPickerProps): React.JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as a,jsxs as M}from"react/jsx-runtime";import{Banknote as b,Check as k,CreditCard as f,Landmark as y,Truck as P}from"lucide-react";import*as h from"@radix-ui/react-radio-group";import{cn as o}from"../../lib/utils.js";import{RadioCard as x,RadioGroup as v}from"./radio-group.js";const i=(r,t)=>a("span",{"aria-hidden":!0,className:"grid size-full place-items-center rounded-[inherit] text-[13px] font-bold text-white",style:{background:t},children:r}),R={bkash:{id:"bkash",name:"bKash",description:"Mobile wallet",mark:i("b","#e2136e")},nagad:{id:"nagad",name:"Nagad",description:"Mobile wallet",mark:i("N","#ec1c24")},rocket:{id:"rocket",name:"Rocket",description:"Dutch-Bangla mobile banking",mark:i("R","#8c3494")},upay:{id:"upay",name:"Upay",description:"Mobile wallet",mark:i("U","#00579f")},card:{id:"card",name:"Card",description:"Visa, Mastercard, Amex",mark:a(f,{})},cash:{id:"cash",name:"Cash",description:"Pay at the counter",mark:a(b,{})},bank:{id:"bank",name:"Bank transfer",description:"BEFTN or NPSB",mark:a(y,{})},cod:{id:"cod",name:"Cash on delivery",description:"Pay when it arrives",mark:a(P,{})}};function _({value:r,defaultValue:t,onValueChange:n,methods:u=["bkash","nagad","card","cash"],fees:g,variant:s="cards",disabled:c,"aria-label":l="Payment method",className:p}){const d=u.map(e=>typeof e=="string"?R[e]:e),m=t??(r===void 0?d[0]?.id:void 0);return s==="chips"?a(h.Root,{value:r,defaultValue:m,onValueChange:n,disabled:c,"aria-label":l,orientation:"horizontal",className:o("flex flex-wrap gap-2",p),children:d.map(e=>M(h.Item,{value:e.id,className:o("group inline-flex h-10 cursor-pointer items-center gap-2 rounded-full border border-border bg-raised py-1 pl-1 pr-3.5 text-[13px] font-medium text-text outline-none transition-[border-color,box-shadow,background-color]","hover:border-border2 focus-visible:ring-2 focus-visible:ring-primary/35 disabled:cursor-not-allowed disabled:opacity-50","data-[state=checked]:border-primary data-[state=checked]:bg-primary-subtle data-[state=checked]:shadow-[0_0_0_3px_var(--primary-subtle)]"),children:[a("span",{className:"grid size-8 place-items-center overflow-hidden rounded-full bg-surface text-muted [&_svg]:size-4",children:e.mark}),e.name,a(k,{className:"size-3.5 text-primary opacity-0 transition-opacity group-data-[state=checked]:opacity-100",strokeWidth:3})]},e.id))}):a(v,{value:r,defaultValue:m,onValueChange:n,disabled:c,"aria-label":l,className:o(s==="cards"?"grid gap-2.5 sm:grid-cols-2":"grid gap-2",p),children:d.map(e=>a(x,{value:e.id,title:e.name,description:e.description,aside:g?.[e.id],icon:a("span",{className:"grid size-full place-items-center overflow-hidden rounded-[inherit] [&_svg]:size-[18px]",children:e.mark}),className:"[&>span:first-child]:overflow-hidden [&>span:first-child]:p-0"},e.id))})}export{R as PAYMENT_METHODS,_ as PaymentMethodPicker};
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { type BdPhone } from "../../lib/bd-geo";
3
+ import { type InputState } from "./input";
4
+ /**
5
+ * PhoneInput — a Bangladeshi mobile number, typed any way people type it.
6
+ *
7
+ * "01712345678", "+880 1712-345678", "8801712345678" and Bangla digits all
8
+ * read the same; the field shows +880 and groups the rest as people write it.
9
+ * It names the operator from the prefix as you type (013/017 Grameenphone,
10
+ * 014/019 Banglalink, 016/018 Robi, 015 Teletalk) and ticks when the number is
11
+ * complete. `onValueChange` gets "+8801712345678" (or "" until it is whole) and
12
+ * the parsed details, so a form never stores the formatting.
13
+ */
14
+ export interface PhoneInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type"> {
15
+ /** Any form of the number; "+8801712345678" is what you get back. */
16
+ value?: string;
17
+ defaultValue?: string;
18
+ onValueChange?: (e164: string, phone: BdPhone) => void;
19
+ /** Show the operator on the right as you type. Default true. */
20
+ showOperator?: boolean;
21
+ state?: InputState;
22
+ labels?: {
23
+ invalidPrefix?: string;
24
+ };
25
+ }
26
+ export declare const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as r,jsxs as d}from"react/jsx-runtime";import{Check as _}from"lucide-react";import*as m from"react";import{BD_OPERATOR_NAMES as C,parseBdPhone as o}from"../../lib/bd-geo.js";import{cn as h}from"../../lib/utils.js";import{fieldStateClass as M}from"./input.js";const R=e=>e.length>4?`${e.slice(0,4)}-${e.slice(4)}`:e,S=m.forwardRef(({value:e,defaultValue:b="",onValueChange:u,showOperator:g=!0,state:c="default",labels:v,className:P,disabled:f,placeholder:w="1712-345678",...N},I)=>{const s=e!==void 0,[y,x]=m.useState(()=>o(b).national),i=s?o(e).national:y,n=o(i),a=i.length>=2&&!n.operator;return d("div",{className:h("flex h-ctl w-full items-center overflow-hidden rounded-ctl border bg-raised text-[13px] shadow-[inset_0_1px_1px_rgba(5,8,22,.04)] transition-[border-color,box-shadow]",M[a?"error":c],f&&"cursor-not-allowed bg-hover opacity-70",P),children:[d("span",{className:"flex h-full shrink-0 items-center gap-1.5 border-r border-border bg-surface px-2.5 text-muted",children:[r("span",{"aria-hidden":!0,className:"relative h-3 w-[18px] overflow-hidden rounded-[2px] bg-[#006a4e]",children:r("span",{className:"absolute left-[38%] top-1/2 size-[7px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[#f42a41]"})}),r("span",{className:"num font-medium text-text",children:"+880"})]}),r("input",{ref:I,type:"tel",inputMode:"tel",autoComplete:"tel-national",disabled:f,placeholder:w,"aria-invalid":a||c==="error"||void 0,value:R(i),onChange:l=>{const t=o(l.target.value);s||x(t.national),u?.(t.e164,t)},onPaste:l=>{const t=l.clipboardData.getData("text");if(!t)return;l.preventDefault();const p=o(t);s||x(p.national),u?.(p.e164,p)},className:"num h-full min-w-0 flex-1 bg-transparent px-3 text-text outline-none placeholder:text-placeholder disabled:cursor-not-allowed",...N}),g&&(n.operator||a)?d("span",{className:h("mr-2 inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-[11.5px] font-medium",a?"bg-danger-subtle text-danger":n.valid?"bg-success-subtle text-success":"bg-hover text-muted"),children:[n.valid?r(_,{className:"size-3",strokeWidth:3}):null,a?v?.invalidPrefix??"Not a mobile prefix":C[n.operator]]}):null]})});S.displayName="PhoneInput";export{S as PhoneInput};
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ import { type InputState } from "./input";
3
+ /**
4
+ * PlateInput — a Bangladeshi number plate: where it is registered, its class
5
+ * letter and the six digits, with the plate itself drawn beside the fields.
6
+ *
7
+ * The value is the plate as it is written in English, "DHAKA METRO-GA
8
+ * 12-3456"; the drawing shows it in Bangla script (ঢাকা মেট্রো-গ ১২-৩৪৫৬), the
9
+ * way it is on the car, or in English with `plateScript="latin"`. The number
10
+ * field takes digits only and adds the dash. `onValueChange` gets the plate
11
+ * (or "" until it is complete) and its parts.
12
+ */
13
+ export interface PlateParts {
14
+ area: string;
15
+ cls: string;
16
+ number: string;
17
+ }
18
+ export interface PlateInputProps {
19
+ value?: string;
20
+ defaultValue?: string;
21
+ onValueChange?: (plate: string, parts: PlateParts & {
22
+ valid: boolean;
23
+ }) => void;
24
+ /** Draw the plate next to the fields. Default true. */
25
+ preview?: boolean;
26
+ plateScript?: "bn" | "latin";
27
+ state?: InputState;
28
+ disabled?: boolean;
29
+ className?: string;
30
+ }
31
+ export declare function parsePlate(plate: string): PlateParts;
32
+ export declare function PlateInput({ value, defaultValue, onValueChange, preview, plateScript, state, disabled, className }: PlateInputProps): React.JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as a,jsxs as r}from"react/jsx-runtime";import*as $ from"react";import{toBanglaDigits as E}from"../../lib/bangla.js";import{BD_DISTRICTS as D}from"../../lib/bd-geo.js";import{cn as m}from"../../lib/utils.js";import{fieldStateClass as L}from"./input.js";import{Select as b,SelectContent as x,SelectGroup as A,SelectItem as u,SelectLabel as S,SelectTrigger as f,SelectValue as h}from"./select.js";const g=[["DHAKA METRO","\u09A2\u09BE\u0995\u09BE \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"],["CHATTA METRO","\u099A\u099F\u09CD\u099F \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"],["KHULNA METRO","\u0996\u09C1\u09B2\u09A8\u09BE \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"],["RAJSHAHI METRO","\u09B0\u09BE\u099C\u09B6\u09BE\u09B9\u09C0 \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"],["SYLHET METRO","\u09B8\u09BF\u09B2\u09C7\u099F \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"],["BARISHAL METRO","\u09AC\u09B0\u09BF\u09B6\u09BE\u09B2 \u09AE\u09C7\u099F\u09CD\u09B0\u09CB"]],H=[...g,...D.map(l=>[l.en.toUpperCase(),l.bn])],O=new Map(H),_=[["KA","\u0995"],["KHA","\u0996"],["GA","\u0997"],["GHA","\u0998"],["CHA","\u099A"],["CHHA","\u099B"],["JA","\u099C"],["JHA","\u099D"],["TA","\u09A4"],["THA","\u09A5"],["DA","\u09A6"],["DHA","\u09A7"],["NA","\u09A8"],["PA","\u09AA"],["BHA","\u09AD"],["MA","\u09AE"],["LA","\u09B2"],["SHA","\u09B6"],["SA","\u09B8"],["HA","\u09B9"]],k=new Map(_);function v(l){const s=/^(.+)-([A-Z]+) (\d{2})-?(\d{0,4})$/.exec(l.trim().toUpperCase());return s?{area:s[1],cls:s[2],number:`${s[3]}${s[4]}`}:{area:"DHAKA METRO",cls:"GA",number:""}}function J({value:l,defaultValue:s="",onValueChange:P,preview:T=!0,plateScript:c="bn",state:C="default",disabled:i,className:N}){const[R,I]=$.useState(()=>v(s)),t=l!==void 0?v(l):R,o=t.number.length>2?`${t.number.slice(0,2)}-${t.number.slice(2)}`:t.number,p=e=>{const n={...t,...e};l===void 0&&I(n);const d=n.number.length===6;P?.(d?`${n.area}-${n.cls} ${n.number.slice(0,2)}-${n.number.slice(2)}`:"",{...n,valid:d})},w=c==="bn"?`${O.get(t.area)??t.area}-${k.get(t.cls)??t.cls}`:`${t.area}-${t.cls}`,M=t.number?c==="bn"?E(o):o:c==="bn"?"\u09E7\u09E8-\u09E9\u09EA\u09EB\u09EC":"12-3456";return r("div",{className:m("flex flex-wrap items-center gap-3",N),children:[r("div",{className:"grid min-w-0 flex-1 grid-cols-[minmax(0,1.6fr)_minmax(0,0.9fr)_minmax(0,1fr)] gap-2",children:[r(b,{value:t.area,onValueChange:e=>p({area:e}),disabled:i,children:[a(f,{"aria-label":"Registered in",children:a(h,{})}),r(x,{className:"max-h-72",children:[r(A,{children:[a(S,{children:"Metro"}),g.map(([e])=>a(u,{value:e,children:e},e))]}),r(A,{children:[a(S,{children:"District"}),H.slice(g.length).map(([e])=>a(u,{value:e,children:e},e))]})]})]}),r(b,{value:t.cls,onValueChange:e=>p({cls:e}),disabled:i,children:[a(f,{"aria-label":"Class",children:a(h,{})}),a(x,{className:"max-h-72",children:_.map(([e,n])=>r(u,{value:e,children:[e," \xB7 ",n]},e))})]}),a("div",{className:m("flex h-ctl items-center rounded-ctl border bg-raised px-3 transition-[border-color,box-shadow]",L[C],i&&"opacity-70"),children:a("input",{"aria-label":"Number",inputMode:"numeric",disabled:i,placeholder:"12-3456",value:o,onChange:e=>p({number:e.target.value.replace(/[০-৯]/g,n=>String("\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF".indexOf(n))).replace(/\D/g,"").slice(0,6)}),className:"num h-full w-full min-w-0 bg-transparent text-[13.5px] tracking-[0.06em] text-text outline-none placeholder:text-placeholder"})})]}),T?r("div",{"aria-hidden":!0,className:"grid h-[60px] w-[150px] shrink-0 place-items-center rounded-[6px] border-[2.5px] border-[#111] bg-white px-2 text-center leading-none text-[#111] shadow-[inset_0_0_0_2px_#fff,inset_0_0_0_3px_#111,0_4px_12px_-4px_rgba(5,8,22,.35)]",children:[a("span",{className:"w-full truncate text-[12.5px] font-bold",children:w}),a("span",{className:m("num text-[21px] font-bold tracking-[0.04em]",!t.number&&"text-[#111]/30"),children:M})]}):null]})}export{J as PlateInput,v as parsePlate};
@@ -1 +1 @@
1
- "use client";import{jsx as t,jsxs as n}from"react/jsx-runtime";import*as e from"@radix-ui/react-select";import{Check as s,ChevronDown as m}from"lucide-react";import*as l from"react";import{cn as c}from"../../lib/utils.js";const g=e.Root,b=e.Group,u=e.Value,d=l.forwardRef(({className:o,children:r,...i},a)=>n(e.Trigger,{ref:a,className:c("flex h-ctl w-full cursor-pointer items-center justify-between gap-2 rounded-ctl border border-border2 bg-raised px-3 text-[13px] text-text outline-none transition-[border-color,box-shadow] data-[placeholder]:text-placeholder focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:cursor-not-allowed disabled:bg-hover disabled:text-muted",o),...i,children:[r,t(e.Icon,{asChild:!0,children:t(m,{className:"size-4 shrink-0 text-muted"})})]}));d.displayName="SelectTrigger";const f=l.forwardRef(({className:o,children:r,position:i="popper",...a},p)=>t(e.Portal,{children:t(e.Content,{ref:p,"data-pop":"",position:i,sideOffset:6,className:c("z-50 max-h-72 min-w-[var(--radix-select-trigger-width)] overflow-hidden rounded-dlg border border-border bg-raised p-1 shadow-elev3",o),...a,children:t(e.Viewport,{children:r})})}));f.displayName="SelectContent";const x=l.forwardRef(({className:o,children:r,...i},a)=>n(e.Item,{ref:a,className:c("relative flex cursor-pointer select-none items-center rounded-ctl py-2 pl-2.5 pr-8 text-[13px] text-body outline-none data-[highlighted]:bg-hover data-[highlighted]:text-text data-[state=checked]:text-primary data-[state=checked]:font-medium data-[disabled]:pointer-events-none data-[disabled]:opacity-50",o),...i,children:[t(e.ItemText,{children:r}),t(e.ItemIndicator,{className:"absolute right-2.5",children:t(s,{className:"size-3.5",strokeWidth:3})})]}));x.displayName="SelectItem";const v=({className:o,...r})=>t(e.Label,{className:c("ov px-2.5 pb-1 pt-2",o),...r}),P=({className:o,...r})=>t(e.Separator,{className:c("-mx-1 my-1 h-px bg-border",o),...r});export{g as Select,f as SelectContent,b as SelectGroup,x as SelectItem,v as SelectLabel,P as SelectSeparator,d as SelectTrigger,u as SelectValue};
1
+ "use client";import{jsx as t,jsxs as n}from"react/jsx-runtime";import*as e from"@radix-ui/react-select";import{Check as p,ChevronDown as m}from"lucide-react";import*as l from"react";import{cn as c}from"../../lib/utils.js";const S=e.Root,v=e.Group,u=e.Value,d=l.forwardRef(({className:o,children:r,...i},a)=>n(e.Trigger,{ref:a,className:c("flex h-ctl w-full cursor-pointer items-center justify-between gap-2 rounded-ctl border border-border2 bg-raised px-3 text-[13px] text-text outline-none transition-[border-color,box-shadow] data-[placeholder]:text-placeholder focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:cursor-not-allowed disabled:bg-hover disabled:text-muted",o),...i,children:[t("span",{className:"flex min-w-0 flex-1 items-center gap-2 text-left [&>span]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-muted",children:r}),t(e.Icon,{asChild:!0,children:t(m,{className:"size-4 shrink-0 text-muted"})})]}));d.displayName="SelectTrigger";const f=l.forwardRef(({className:o,children:r,position:i="popper",...a},s)=>t(e.Portal,{children:t(e.Content,{ref:s,"data-pop":"",position:i,sideOffset:6,className:c("z-50 max-h-72 min-w-[var(--radix-select-trigger-width)] overflow-hidden rounded-dlg border border-border bg-raised p-1 shadow-elev3",o),...a,children:t(e.Viewport,{children:r})})}));f.displayName="SelectContent";const x=l.forwardRef(({className:o,children:r,...i},a)=>n(e.Item,{ref:a,className:c("relative flex cursor-pointer select-none items-center rounded-ctl py-2 pl-2.5 pr-8 text-[13px] text-body outline-none data-[highlighted]:bg-hover data-[highlighted]:text-text data-[state=checked]:text-primary data-[state=checked]:font-medium data-[disabled]:pointer-events-none data-[disabled]:opacity-50",o),...i,children:[t(e.ItemText,{children:r}),t(e.ItemIndicator,{className:"absolute right-2.5",children:t(p,{className:"size-3.5",strokeWidth:3})})]}));x.displayName="SelectItem";const b=({className:o,...r})=>t(e.Label,{className:c("ov px-2.5 pb-1 pt-2",o),...r}),P=({className:o,...r})=>t(e.Separator,{className:c("-mx-1 my-1 h-px bg-border",o),...r});export{S as Select,f as SelectContent,v as SelectGroup,x as SelectItem,b as SelectLabel,P as SelectSeparator,d as SelectTrigger,u as SelectValue};
@@ -1,16 +1,21 @@
1
1
  import * as DialogPrimitive from "@radix-ui/react-dialog";
2
2
  import * as React from "react";
3
3
  /**
4
- * Right-side sheet — the physical basis of the record drawer (§2.2).
4
+ * Sheet — a panel that slides in from an edge: the record drawer (§2.2) on
5
+ * the right, navigation on the left, and on a phone a bottom sheet.
5
6
  *
6
7
  * Modal by default: the page behind is blurred and inert until the sheet is
7
- * closed (Escape, the close button or a click on the blurred page).
8
+ * closed (Escape, the close button or a click on the blurred page). A bottom
9
+ * sheet has a handle and can also be dragged down to close; on a wide screen
10
+ * it becomes a card centred above the bottom edge.
8
11
  */
9
12
  export declare const Sheet: React.FC<DialogPrimitive.DialogProps>;
10
13
  export declare const SheetTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
11
14
  export declare const SheetClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
15
+ export type SheetSide = "right" | "left" | "bottom";
12
16
  export declare const SheetContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
13
- side?: "right" | "left";
17
+ side?: SheetSide;
18
+ /** Width classes for a side sheet. Default "w-full sm:w-drawer". */
14
19
  width?: string;
15
20
  scrim?: boolean;
16
21
  hideClose?: boolean;
@@ -1 +1 @@
1
- "use client";import{jsx as t,jsxs as r}from"react/jsx-runtime";import*as e from"@radix-ui/react-dialog";import{X as d}from"lucide-react";import*as p from"react";import{cn as g}from"../../lib/utils.js";const u=e.Root,x=e.Trigger,v=e.Close,b=p.forwardRef(({className:i,children:s,side:o="right",width:a="w-full sm:w-drawer",scrim:l=!0,hideClose:n,...m},c)=>r(e.Portal,{children:[l?t(e.Overlay,{"data-scrim":"",className:"fixed inset-0 z-50 bg-[rgba(5,8,22,.42)] backdrop-blur-[3px]"}):null,r(e.Content,{ref:c,"data-sheet":o,className:g("fixed inset-y-0 z-50 flex max-w-full flex-col border-border bg-raised shadow-elev3 outline-none",o==="right"?"right-0 max-sm:border-l sm:inset-y-2 sm:right-2 sm:rounded-[calc(16px*var(--radius-scale))] sm:border":"left-0 max-sm:border-r sm:inset-y-2 sm:left-2 sm:rounded-[calc(16px*var(--radius-scale))] sm:border",a,i),...m,children:[s,n?null:r(e.Close,{className:"absolute right-4 top-4 z-10 grid size-8 cursor-pointer place-items-center rounded-full border border-border bg-raised/80 text-muted backdrop-blur transition-[background-color,color,transform] hover:bg-hover hover:text-text active:scale-95",children:[t(d,{className:"size-4"}),t("span",{className:"sr-only",children:"Close"})]})]})]}));b.displayName="SheetContent";const C=e.Title,P=e.Description;export{u as Sheet,v as SheetClose,b as SheetContent,P as SheetDescription,C as SheetTitle,x as SheetTrigger};
1
+ "use client";import{jsx as s,jsxs as m}from"react/jsx-runtime";import*as t from"@radix-ui/react-dialog";import{X as v}from"lucide-react";import*as l from"react";import{cn as f}from"../../lib/utils.js";const w=t.Root,T=t.Trigger,N=t.Close,S={right:"inset-y-0 right-0 max-sm:border-l sm:inset-y-2 sm:right-2 sm:rounded-[calc(16px*var(--radius-scale))] sm:border",left:"inset-y-0 left-0 max-sm:border-r sm:inset-y-2 sm:left-2 sm:rounded-[calc(16px*var(--radius-scale))] sm:border",bottom:"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[calc(22px*var(--radius-scale))] border-t pb-[env(safe-area-inset-bottom)] sm:inset-x-auto sm:bottom-3 sm:left-1/2 sm:w-[min(640px,calc(100%-24px))] sm:-translate-x-1/2 sm:rounded-[calc(20px*var(--radius-scale))] sm:border"},P=110,R=l.forwardRef(({className:p,children:b,side:i="right",width:g,scrim:h=!0,hideClose:c,...x},a)=>{const o=l.useRef(null),u=l.useRef(null),n=l.useRef(null),y=e=>{o.current=e,typeof a=="function"?a(e):a&&(a.current=e)},d=i==="bottom"?{onPointerDown:e=>{n.current={y:e.clientY,dy:0},e.currentTarget.setPointerCapture(e.pointerId),o.current&&(o.current.style.transition="none")},onPointerMove:e=>{if(!n.current||!o.current)return;const r=Math.max(0,e.clientY-n.current.y);n.current.dy=r,o.current.style.transform=`translateY(${r}px)`},onPointerUp:()=>{const e=n.current?.dy??0;n.current=null;const r=o.current;r&&(r.style.transition="transform 260ms cubic-bezier(.22,1,.36,1)",e>P?(r.style.transform="translateY(110%)",window.setTimeout(()=>u.current?.click(),180)):r.style.transform="")}}:null;return m(t.Portal,{children:[h?s(t.Overlay,{"data-scrim":"",className:"fixed inset-0 z-50 bg-[rgba(5,8,22,.42)] backdrop-blur-[3px]"}):null,m(t.Content,{ref:y,"data-sheet":i,className:f("fixed z-50 flex max-w-full flex-col border-border bg-raised shadow-elev3 outline-none",S[i],g??(i==="bottom"?null:"w-full sm:w-drawer"),p),...x,children:[d?s("div",{...d,className:"flex shrink-0 cursor-grab touch-none justify-center pb-1 pt-2.5 active:cursor-grabbing",children:s("span",{"aria-hidden":!0,className:"h-1.5 w-11 rounded-full bg-border2"})}):null,b,m(t.Close,{ref:u,className:f("absolute right-4 top-4 z-10 grid size-8 cursor-pointer place-items-center rounded-full border border-border bg-raised/80 text-muted backdrop-blur transition-[background-color,color,transform] hover:bg-hover hover:text-text active:scale-95",c&&"sr-only"),tabIndex:c?-1:void 0,"aria-hidden":c||void 0,children:[s(v,{className:"size-4"}),s("span",{className:"sr-only",children:"Close"})]})]})]})});R.displayName="SheetContent";const E=t.Title,M=t.Description;export{w as Sheet,N as SheetClose,R as SheetContent,M as SheetDescription,E as SheetTitle,T as SheetTrigger};
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { type InputState } from "./input";
3
+ /**
4
+ * TakaInput — an amount in taka, grouped the Bangladeshi way as you type
5
+ * (12,34,567, not 1,234,567), with the amount in words under it: "12.35 lakh".
6
+ *
7
+ * It holds a number, never a string: `onValueChange` gets 1234567 (or null when
8
+ * empty). Typing keeps the cursor where it was while commas come and go, Bangla
9
+ * digits are read, and `min`/`max` clamp on blur, not while typing, so nobody
10
+ * is fought mid-number. `decimals` allows paisa.
11
+ */
12
+ export interface TakaInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "min" | "max"> {
13
+ value?: number | null;
14
+ defaultValue?: number | null;
15
+ onValueChange?: (value: number | null) => void;
16
+ min?: number;
17
+ max?: number;
18
+ /** Digits after the point. 0 (default) for whole taka, 2 for paisa. */
19
+ decimals?: number;
20
+ /** The amount in words under the field: "latin" (12.35 lakh), "bn" (১২.৩৫ লাখ) or false. */
21
+ words?: "latin" | "bn" | false;
22
+ /** The sign before the number. Default "৳". */
23
+ symbol?: React.ReactNode;
24
+ state?: InputState;
25
+ }
26
+ export declare const TakaInput: React.ForwardRefExoticComponent<TakaInputProps & React.RefAttributes<HTMLInputElement>>;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as k,jsxs as v}from"react/jsx-runtime";import*as s from"react";import{fromBanglaDigits as $,groupLakh as c,lakhCrore as A}from"../../lib/bangla.js";import{cn as R}from"../../lib/utils.js";import{fieldStateClass as P}from"./input.js";function I(t,a){const l=$(t).replace(/[^\d.]/g,""),[f="",...m]=l.split("."),e=m.join("").slice(0,a);return{int:f.replace(/^0+(?=\d)/,""),frac:e,point:a>0&&l.includes(".")}}function q({int:t,frac:a,point:l}){return!t&&!l?"":`${t?c(Number(t)):"0"}${l?`.${a}`:""}`}const D=s.forwardRef(({value:t,defaultValue:a=null,onValueChange:l,min:f,max:m,decimals:e=0,words:d="latin",symbol:M="\u09F3",state:S="default",className:H,disabled:N,onBlur:L,...C},E)=>{const w=t!==void 0,[x,b]=s.useState(()=>a==null?"":c(a,e)),y=s.useRef(null);s.useImperativeHandle(E,()=>y.current),s.useEffect(()=>{if(!w)return;const r=I(x,e);(r.int||r.frac?+`${r.int||0}.${r.frac||0}`:null)!==t&&b(t==null?"":c(t,e))},[t,w,e]);const p=I(x,e),o=p.int||p.frac?+`${p.int||0}.${p.frac||0}`:null;return v("div",{className:R("w-full",H),children:[v("div",{className:R("flex h-ctl w-full items-center rounded-ctl border bg-raised shadow-[inset_0_1px_1px_rgba(5,8,22,.04)] transition-[border-color,box-shadow]",P[S],N&&"cursor-not-allowed bg-hover opacity-70"),children:[k("span",{className:"pl-3 text-[14px] font-medium text-muted",children:M}),k("input",{ref:y,type:"text",inputMode:e?"decimal":"numeric",disabled:N,value:x,onChange:r=>{const n=r.target,h=n.selectionStart??n.value.length,_=$(n.value.slice(0,h)).replace(/[^\d.]/g,"").length,u=I(n.value,e),g=q(u);b(g);const B=u.int||u.frac?+`${u.int||0}.${u.frac||0}`:null;l?.(B),requestAnimationFrame(()=>{let i=0,T=0;for(;i<g.length&&T<_;)/[\d.]/.test(g[i])&&T++,i++;n.setSelectionRange(i,i)})},onBlur:r=>{if(o!=null){const n=Math.min(m??1/0,Math.max(f??-1/0,o)),h=e?c(n,e):c(n);b(h),n!==o&&l?.(n)}L?.(r)},className:"num h-full min-w-0 flex-1 bg-transparent px-2 text-[14px] text-text outline-none placeholder:text-placeholder disabled:cursor-not-allowed",...C})]}),d&&o!=null&&o>=1e3?v("p",{"aria-live":"polite",className:"mt-1 text-[12px] text-muted",children:[A(o,{script:d}),d==="latin"?" taka":" \u099F\u09BE\u0995\u09BE"]}):null]})});D.displayName="TakaInput";export{D as TakaInput};
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Bangla helpers: digits, the Bangla calendar (Bongabdo) and lakh/crore.
3
+ *
4
+ * The calendar is the one Bangladesh uses since the 2019 revision: the year
5
+ * starts on 14 April (Pohela Boishakh); Boishakh to Ashwin have 31 days,
6
+ * Falgun has 29 (30 when February has 29), every other month 30. So 16
7
+ * December is always 1 Poush and 21 February always 8 Falgun.
8
+ */
9
+ /** "2026" → "২০২৬". Leaves everything that is not a digit alone. */
10
+ export declare function toBanglaDigits(input: string | number): string;
11
+ /** "২০২৬" → "2026", for reading what someone typed on a Bangla keyboard. */
12
+ export declare function fromBanglaDigits(input: string): string;
13
+ export declare const BANGLA_MONTHS: readonly ["বৈশাখ", "জ্যৈষ্ঠ", "আষাঢ়", "শ্রাবণ", "ভাদ্র", "আশ্বিন", "কার্তিক", "অগ্রহায়ণ", "পৌষ", "মাঘ", "ফাল্গুন", "চৈত্র"];
14
+ export declare const BANGLA_MONTHS_LATIN: readonly ["Boishakh", "Joishtho", "Asharh", "Shrabon", "Bhadro", "Ashwin", "Kartik", "Ogrohayon", "Poush", "Magh", "Falgun", "Choitro"];
15
+ export declare const BANGLA_WEEKDAYS: readonly ["রবিবার", "সোমবার", "মঙ্গলবার", "বুধবার", "বৃহস্পতিবার", "শুক্রবার", "শনিবার"];
16
+ /** The six seasons, two months each, from Boishakh. */
17
+ export declare const BANGLA_SEASONS: readonly ["গ্রীষ্ম", "বর্ষা", "শরৎ", "হেমন্ত", "শীত", "বসন্ত"];
18
+ export interface BongabdoDate {
19
+ /** Bangla year, e.g. 1433. */
20
+ year: number;
21
+ /** 0 = Boishakh … 11 = Choitro. */
22
+ month: number;
23
+ /** 1-based day of the month. */
24
+ day: number;
25
+ monthName: string;
26
+ monthNameLatin: string;
27
+ season: string;
28
+ }
29
+ /** The Bangla date of a day (its local calendar date; the time is ignored). */
30
+ export declare function toBongabdo(date: Date): BongabdoDate;
31
+ export interface FormatBanglaOptions {
32
+ /** Add the weekday: "শনিবার, ১৮ আশ্বিন ১৪৩৩". */
33
+ weekday?: boolean;
34
+ /** "bn" (default) for Bangla script and digits, "latin" for "18 Ashwin 1433". */
35
+ script?: "bn" | "latin";
36
+ /** Add the year. Default true. */
37
+ year?: boolean;
38
+ }
39
+ /** "১৮ আশ্বিন ১৪৩৩", or "18 Ashwin 1433" with `script: "latin"`. */
40
+ export declare function formatBanglaDate(date: Date, { weekday, script, year }?: FormatBanglaOptions): string;
41
+ /**
42
+ * A large amount in words the way Bangladesh says it: 3,20,000 → "3.2 lakh",
43
+ * 1,25,00,000 → "1.25 crore". Under a lakh it returns the grouped number.
44
+ */
45
+ export declare function lakhCrore(n: number, { script, digits }?: {
46
+ script?: "latin" | "bn";
47
+ digits?: number;
48
+ }): string;
49
+ /** 1234567 → "12,34,567": groups of two after the first three, as on every BD receipt. */
50
+ export declare function groupLakh(n: number, decimals?: number): string;
@@ -0,0 +1 @@
1
+ const c="\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF";function i(t){return String(t).replace(/[0-9]/g,n=>c[Number(n)])}function p(t){return t.replace(/[০-৯]/g,n=>String(c.indexOf(n)))}const u=["\u09AC\u09C8\u09B6\u09BE\u0996","\u099C\u09CD\u09AF\u09C8\u09B7\u09CD\u09A0","\u0986\u09B7\u09BE\u09A2\u09BC","\u09B6\u09CD\u09B0\u09BE\u09AC\u09A3","\u09AD\u09BE\u09A6\u09CD\u09B0","\u0986\u09B6\u09CD\u09AC\u09BF\u09A8","\u0995\u09BE\u09B0\u09CD\u09A4\u09BF\u0995","\u0985\u0997\u09CD\u09B0\u09B9\u09BE\u09AF\u09BC\u09A3","\u09AA\u09CC\u09B7","\u09AE\u09BE\u0998","\u09AB\u09BE\u09B2\u09CD\u0997\u09C1\u09A8","\u099A\u09C8\u09A4\u09CD\u09B0"],l=["Boishakh","Joishtho","Asharh","Shrabon","Bhadro","Ashwin","Kartik","Ogrohayon","Poush","Magh","Falgun","Choitro"],m=["\u09B0\u09AC\u09BF\u09AC\u09BE\u09B0","\u09B8\u09CB\u09AE\u09AC\u09BE\u09B0","\u09AE\u0999\u09CD\u0997\u09B2\u09AC\u09BE\u09B0","\u09AC\u09C1\u09A7\u09AC\u09BE\u09B0","\u09AC\u09C3\u09B9\u09B8\u09CD\u09AA\u09A4\u09BF\u09AC\u09BE\u09B0","\u09B6\u09C1\u0995\u09CD\u09B0\u09AC\u09BE\u09B0","\u09B6\u09A8\u09BF\u09AC\u09BE\u09B0"],b=["\u0997\u09CD\u09B0\u09C0\u09B7\u09CD\u09AE","\u09AC\u09B0\u09CD\u09B7\u09BE","\u09B6\u09B0\u09CE","\u09B9\u09C7\u09AE\u09A8\u09CD\u09A4","\u09B6\u09C0\u09A4","\u09AC\u09B8\u09A8\u09CD\u09A4"],h=t=>t%4===0&&t%100!==0||t%400===0,g=(t,n,s)=>Date.UTC(t,n,s)/864e5;function $(t){const n=t.getFullYear(),s=g(n,t.getMonth(),t.getDate()),r=s>=g(n,3,14)?n:n-1;let e=s-g(r,3,14);const o=[31,31,31,31,31,31,30,30,30,30,h(r+1)?30:29,30];let a=0;for(;e>=o[a];)e-=o[a],a++;return{year:r-593,month:a,day:e+1,monthName:u[a],monthNameLatin:l[a],season:b[Math.floor(a/2)]}}function f(t,{weekday:n=!1,script:s="bn",year:r=!0}={}){const e=$(t);if(s==="latin"){const a=`${e.day} ${e.monthNameLatin}${r?` ${e.year}`:""}`;return n?`${t.toLocaleDateString("en-GB",{weekday:"long"})}, ${a}`:a}const o=`${i(e.day)} ${e.monthName}${r?` ${i(e.year)}`:""}`;return n?`${m[t.getDay()]}, ${o}`:o}function B(t,{script:n="latin",digits:s=2}={}){const r=Math.abs(t),e=a=>String(Number(a.toFixed(s)));let o;return r>=1e7?o=`${e(t/1e7)} ${n==="bn"?"\u0995\u09CB\u099F\u09BF":"crore"}`:r>=1e5?o=`${e(t/1e5)} ${n==="bn"?"\u09B2\u09BE\u0996":"lakh"}`:r>=1e3?o=`${e(t/1e3)} ${n==="bn"?"\u09B9\u09BE\u099C\u09BE\u09B0":"thousand"}`:o=String(t),n==="bn"?i(o):o}function N(t,n=0){const[s,r]=Math.abs(t).toFixed(n).split("."),e=s.slice(-3),o=s.slice(0,-3).replace(/\B(?=(\d{2})+(?!\d))/g,",");return`${t<0?"-":""}${o?`${o},`:""}${e}${r?`.${r}`:""}`}export{u as BANGLA_MONTHS,l as BANGLA_MONTHS_LATIN,b as BANGLA_SEASONS,m as BANGLA_WEEKDAYS,f as formatBanglaDate,p as fromBanglaDigits,N as groupLakh,B as lakhCrore,i as toBanglaDigits,$ as toBongabdo};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Bangladesh's 8 divisions and 64 districts, in English and Bangla, for
3
+ * address forms. Names follow the current official spellings (Chattogram,
4
+ * Cumilla, Barishal, Jashore, Bogura).
5
+ */
6
+ export interface BdPlace {
7
+ id: string;
8
+ en: string;
9
+ bn: string;
10
+ }
11
+ export interface BdDivision extends BdPlace {
12
+ districts: BdPlace[];
13
+ }
14
+ export declare const BD_DIVISIONS: BdDivision[];
15
+ /** Every district, with the id of its division. */
16
+ export declare const BD_DISTRICTS: (BdPlace & {
17
+ division: string;
18
+ })[];
19
+ export declare const bdDivision: (id: string) => BdDivision | undefined;
20
+ export declare const bdDistrict: (id: string) => (BdPlace & {
21
+ division: string;
22
+ }) | undefined;
23
+ export type BdOperator = "grameenphone" | "banglalink" | "robi" | "teletalk";
24
+ export declare const BD_OPERATOR_NAMES: Record<BdOperator, string>;
25
+ export interface BdPhone {
26
+ /** The ten digits after +880, e.g. "1712345678". */
27
+ national: string;
28
+ /** "+8801712345678" when complete, else "". */
29
+ e164: string;
30
+ /** "01712-345678" as people write it. */
31
+ display: string;
32
+ operator: BdOperator | null;
33
+ valid: boolean;
34
+ }
35
+ /**
36
+ * Reads a Bangladeshi mobile number however it was typed: "01712345678",
37
+ * "+880 1712-345678", "8801712345678", in Bangla digits too.
38
+ */
39
+ export declare function parseBdPhone(input: string): BdPhone;
@@ -0,0 +1 @@
1
+ const a=(r,i)=>({id:r.toLowerCase().replace(/[^a-z]+/g,"-").replace(/-$/,""),en:r,bn:i}),e=[{...a("Barishal","\u09AC\u09B0\u09BF\u09B6\u09BE\u09B2"),districts:[a("Barguna","\u09AC\u09B0\u0997\u09C1\u09A8\u09BE"),a("Barishal","\u09AC\u09B0\u09BF\u09B6\u09BE\u09B2"),a("Bhola","\u09AD\u09CB\u09B2\u09BE"),a("Jhalokati","\u099D\u09BE\u09B2\u0995\u09BE\u09A0\u09BF"),a("Patuakhali","\u09AA\u099F\u09C1\u09AF\u09BC\u09BE\u0996\u09BE\u09B2\u09C0"),a("Pirojpur","\u09AA\u09BF\u09B0\u09CB\u099C\u09AA\u09C1\u09B0")]},{...a("Chattogram","\u099A\u099F\u09CD\u099F\u0997\u09CD\u09B0\u09BE\u09AE"),districts:[a("Bandarban","\u09AC\u09BE\u09A8\u09CD\u09A6\u09B0\u09AC\u09BE\u09A8"),a("Brahmanbaria","\u09AC\u09CD\u09B0\u09BE\u09B9\u09CD\u09AE\u09A3\u09AC\u09BE\u09A1\u09BC\u09BF\u09AF\u09BC\u09BE"),a("Chandpur","\u099A\u09BE\u0981\u09A6\u09AA\u09C1\u09B0"),a("Chattogram","\u099A\u099F\u09CD\u099F\u0997\u09CD\u09B0\u09BE\u09AE"),a("Cox's Bazar","\u0995\u0995\u09CD\u09B8\u09AC\u09BE\u099C\u09BE\u09B0"),a("Cumilla","\u0995\u09C1\u09AE\u09BF\u09B2\u09CD\u09B2\u09BE"),a("Feni","\u09AB\u09C7\u09A8\u09C0"),a("Khagrachhari","\u0996\u09BE\u0997\u09A1\u09BC\u09BE\u099B\u09A1\u09BC\u09BF"),a("Lakshmipur","\u09B2\u0995\u09CD\u09B7\u09CD\u09AE\u09C0\u09AA\u09C1\u09B0"),a("Noakhali","\u09A8\u09CB\u09AF\u09BC\u09BE\u0996\u09BE\u09B2\u09C0"),a("Rangamati","\u09B0\u09BE\u0999\u09CD\u0997\u09BE\u09AE\u09BE\u099F\u09BF")]},{...a("Dhaka","\u09A2\u09BE\u0995\u09BE"),districts:[a("Dhaka","\u09A2\u09BE\u0995\u09BE"),a("Faridpur","\u09AB\u09B0\u09BF\u09A6\u09AA\u09C1\u09B0"),a("Gazipur","\u0997\u09BE\u099C\u09C0\u09AA\u09C1\u09B0"),a("Gopalganj","\u0997\u09CB\u09AA\u09BE\u09B2\u0997\u099E\u09CD\u099C"),a("Kishoreganj","\u0995\u09BF\u09B6\u09CB\u09B0\u0997\u099E\u09CD\u099C"),a("Madaripur","\u09AE\u09BE\u09A6\u09BE\u09B0\u09C0\u09AA\u09C1\u09B0"),a("Manikganj","\u09AE\u09BE\u09A8\u09BF\u0995\u0997\u099E\u09CD\u099C"),a("Munshiganj","\u09AE\u09C1\u09A8\u09CD\u09B8\u09C0\u0997\u099E\u09CD\u099C"),a("Narayanganj","\u09A8\u09BE\u09B0\u09BE\u09AF\u09BC\u09A3\u0997\u099E\u09CD\u099C"),a("Narsingdi","\u09A8\u09B0\u09B8\u09BF\u0982\u09A6\u09C0"),a("Rajbari","\u09B0\u09BE\u099C\u09AC\u09BE\u09A1\u09BC\u09C0"),a("Shariatpur","\u09B6\u09B0\u09C0\u09AF\u09BC\u09A4\u09AA\u09C1\u09B0"),a("Tangail","\u099F\u09BE\u0999\u09CD\u0997\u09BE\u0987\u09B2")]},{...a("Khulna","\u0996\u09C1\u09B2\u09A8\u09BE"),districts:[a("Bagerhat","\u09AC\u09BE\u0997\u09C7\u09B0\u09B9\u09BE\u099F"),a("Chuadanga","\u099A\u09C1\u09AF\u09BC\u09BE\u09A1\u09BE\u0999\u09CD\u0997\u09BE"),a("Jashore","\u09AF\u09B6\u09CB\u09B0"),a("Jhenaidah","\u099D\u09BF\u09A8\u09BE\u0987\u09A6\u09B9"),a("Khulna","\u0996\u09C1\u09B2\u09A8\u09BE"),a("Kushtia","\u0995\u09C1\u09B7\u09CD\u099F\u09BF\u09AF\u09BC\u09BE"),a("Magura","\u09AE\u09BE\u0997\u09C1\u09B0\u09BE"),a("Meherpur","\u09AE\u09C7\u09B9\u09C7\u09B0\u09AA\u09C1\u09B0"),a("Narail","\u09A8\u09A1\u09BC\u09BE\u0987\u09B2"),a("Satkhira","\u09B8\u09BE\u09A4\u0995\u09CD\u09B7\u09C0\u09B0\u09BE")]},{...a("Mymensingh","\u09AE\u09AF\u09BC\u09AE\u09A8\u09B8\u09BF\u0982\u09B9"),districts:[a("Jamalpur","\u099C\u09BE\u09AE\u09BE\u09B2\u09AA\u09C1\u09B0"),a("Mymensingh","\u09AE\u09AF\u09BC\u09AE\u09A8\u09B8\u09BF\u0982\u09B9"),a("Netrokona","\u09A8\u09C7\u09A4\u09CD\u09B0\u0995\u09CB\u09A3\u09BE"),a("Sherpur","\u09B6\u09C7\u09B0\u09AA\u09C1\u09B0")]},{...a("Rajshahi","\u09B0\u09BE\u099C\u09B6\u09BE\u09B9\u09C0"),districts:[a("Bogura","\u09AC\u0997\u09C1\u09A1\u09BC\u09BE"),a("Chapainawabganj","\u099A\u09BE\u0981\u09AA\u09BE\u0987\u09A8\u09AC\u09BE\u09AC\u0997\u099E\u09CD\u099C"),a("Joypurhat","\u099C\u09AF\u09BC\u09AA\u09C1\u09B0\u09B9\u09BE\u099F"),a("Naogaon","\u09A8\u0993\u0997\u09BE\u0981"),a("Natore","\u09A8\u09BE\u099F\u09CB\u09B0"),a("Pabna","\u09AA\u09BE\u09AC\u09A8\u09BE"),a("Rajshahi","\u09B0\u09BE\u099C\u09B6\u09BE\u09B9\u09C0"),a("Sirajganj","\u09B8\u09BF\u09B0\u09BE\u099C\u0997\u099E\u09CD\u099C")]},{...a("Rangpur","\u09B0\u0982\u09AA\u09C1\u09B0"),districts:[a("Dinajpur","\u09A6\u09BF\u09A8\u09BE\u099C\u09AA\u09C1\u09B0"),a("Gaibandha","\u0997\u09BE\u0987\u09AC\u09BE\u09A8\u09CD\u09A7\u09BE"),a("Kurigram","\u0995\u09C1\u09A1\u09BC\u09BF\u0997\u09CD\u09B0\u09BE\u09AE"),a("Lalmonirhat","\u09B2\u09BE\u09B2\u09AE\u09A8\u09BF\u09B0\u09B9\u09BE\u099F"),a("Nilphamari","\u09A8\u09C0\u09B2\u09AB\u09BE\u09AE\u09BE\u09B0\u09C0"),a("Panchagarh","\u09AA\u099E\u09CD\u099A\u0997\u09A1\u09BC"),a("Rangpur","\u09B0\u0982\u09AA\u09C1\u09B0"),a("Thakurgaon","\u09A0\u09BE\u0995\u09C1\u09B0\u0997\u09BE\u0981\u0993")]},{...a("Sylhet","\u09B8\u09BF\u09B2\u09C7\u099F"),districts:[a("Habiganj","\u09B9\u09AC\u09BF\u0997\u099E\u09CD\u099C"),a("Moulvibazar","\u09AE\u09CC\u09B2\u09AD\u09C0\u09AC\u09BE\u099C\u09BE\u09B0"),a("Sunamganj","\u09B8\u09C1\u09A8\u09BE\u09AE\u0997\u099E\u09CD\u099C"),a("Sylhet","\u09B8\u09BF\u09B2\u09C7\u099F")]}],o=e.flatMap(r=>r.districts.map(i=>({...i,division:r.id}))),h=r=>e.find(i=>i.id===r),g=r=>o.find(i=>i.id===r),l={13:"grameenphone",17:"grameenphone",14:"banglalink",19:"banglalink",16:"robi",18:"robi",15:"teletalk"},d={grameenphone:"Grameenphone",banglalink:"Banglalink",robi:"Robi",teletalk:"Teletalk"};function p(r){let i=r.replace(/[০-৯]/g,s=>String("\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF".indexOf(s))).replace(/\D/g,"");i.startsWith("880")&&(i=i.slice(3)),i.startsWith("0")&&(i=i.slice(1)),i=i.slice(0,10);const n=l[i.slice(0,2)]??null,t=i.length===10&&n!==null;return{national:i,e164:t?`+880${i}`:"",display:i?`0${i.slice(0,4)}${i.length>4?`-${i.slice(4)}`:""}`:"",operator:n,valid:t}}export{o as BD_DISTRICTS,e as BD_DIVISIONS,d as BD_OPERATOR_NAMES,g as bdDistrict,h as bdDivision,p as parseBdPhone};
@@ -4,3 +4,5 @@ export * from "./format";
4
4
  export * from "./utils";
5
5
  export * from "./boot-script";
6
6
  export * from "./theme";
7
+ export * from "./bangla";
8
+ export * from "./bd-geo";
package/dist/lib/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./currency.js";export*from"./format.js";export*from"./utils.js";export*from"./boot-script.js";export*from"./theme.js";
1
+ export*from"./currency.js";export*from"./format.js";export*from"./utils.js";export*from"./boot-script.js";export*from"./theme.js";export*from"./bangla.js";export*from"./bd-geo.js";
package/dist/styles.css CHANGED
@@ -484,6 +484,17 @@
484
484
  [data-sheet="right"][data-state="closed"] { animation: sheet-out-right 220ms cubic-bezier(0.4, 0, 1, 1) both }
485
485
  [data-sheet="left"][data-state="open"] { animation: sheet-in-left var(--dur) var(--ease) }
486
486
  [data-sheet="left"][data-state="closed"] { animation: sheet-out-left 190ms var(--ease) }
487
+ [data-sheet="bottom"][data-state="open"] { animation: sheet-in-bottom 360ms var(--ease) }
488
+ [data-sheet="bottom"][data-state="closed"] { animation: sheet-out-bottom 220ms cubic-bezier(0.4, 0, 1, 1) both }
489
+
490
+ @keyframes sheet-in-bottom {
491
+ from { transform: translateY(100%) }
492
+ to { transform: none }
493
+ }
494
+ @keyframes sheet-out-bottom {
495
+ from { transform: none }
496
+ to { transform: translateY(100%) }
497
+ }
487
498
 
488
499
  @keyframes sheet-content-in {
489
500
  from { opacity: 0 }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waveinno/ui",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "WAVEINNO design system — tokens, shell, primitives and patterns shared by every WAVEINNO product",
5
5
  "keywords": [
6
6
  "waveinno",
@@ -10,7 +10,7 @@
10
10
  "tailwindcss",
11
11
  "radix-ui"
12
12
  ],
13
- "license": "UNLICENSED",
13
+ "license": "MIT",
14
14
  "author": "WAVEINNO <info@waveinno.com>",
15
15
  "homepage": "https://github.com/waveinno/waveinno-ui#readme",
16
16
  "repository": {
@@ -41,6 +41,7 @@
41
41
  "files": [
42
42
  "dist",
43
43
  "README.md",
44
+ "LICENSE",
44
45
  "CHANGELOG.md"
45
46
  ],
46
47
  "workspaces": [
@@ -65,6 +66,7 @@
65
66
  "@radix-ui/react-accordion": "^1.2.20",
66
67
  "@radix-ui/react-avatar": "^1.2.6",
67
68
  "@radix-ui/react-checkbox": "^1.3.11",
69
+ "@radix-ui/react-context-menu": "^2.3.7",
68
70
  "@radix-ui/react-dialog": "^1.1.23",
69
71
  "@radix-ui/react-dropdown-menu": "^2.1.24",
70
72
  "@radix-ui/react-hover-card": "^1.1.23",