azamat-ui-kit-cli 0.2.2 → 0.3.3
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 +11 -0
- package/dist/index.cjs +452 -0
- package/package.json +2 -2
- package/vendor/src/components/actions/action-menu.tsx +21 -18
- package/vendor/src/components/calendar/calendar.tsx +153 -102
- package/vendor/src/components/calendar/date-picker.tsx +24 -14
- package/vendor/src/components/calendar/date-range-picker.tsx +137 -58
- package/vendor/src/components/charts/charts.tsx +32 -21
- package/vendor/src/components/command/command-palette.tsx +68 -57
- package/vendor/src/components/data-table/data-table-bulk-actions.tsx +23 -20
- package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +21 -10
- package/vendor/src/components/data-table/data-table-pagination.tsx +6 -6
- package/vendor/src/components/data-table/data-table-toolbar.tsx +72 -44
- package/vendor/src/components/data-table/data-table.tsx +15 -11
- package/vendor/src/components/data-table/table-export-menu.tsx +1 -1
- package/vendor/src/components/data-table/table-import-button.tsx +1 -1
- package/vendor/src/components/display/data-state.tsx +20 -8
- package/vendor/src/components/display/index.ts +19 -15
- package/vendor/src/components/display/metric-card.tsx +35 -0
- package/vendor/src/components/display/progress-circle.tsx +24 -0
- package/vendor/src/components/display/smart-card.tsx +49 -27
- package/vendor/src/components/display/status-dot.tsx +45 -0
- package/vendor/src/components/display/user-card.tsx +30 -0
- package/vendor/src/components/feedback/alert.tsx +21 -11
- package/vendor/src/components/feedback/empty-state.tsx +2 -2
- package/vendor/src/components/feedback/loading-state.tsx +2 -2
- package/vendor/src/components/feedback/page-state.tsx +19 -15
- package/vendor/src/components/feedback/status-badge.tsx +43 -43
- package/vendor/src/components/form/form-app-input.tsx +147 -0
- package/vendor/src/components/form/form-date-input.tsx +16 -19
- package/vendor/src/components/form/form-field-shell.tsx +11 -8
- package/vendor/src/components/form/form-field-utils.ts +76 -0
- package/vendor/src/components/form/form-input.tsx +423 -44
- package/vendor/src/components/form/form-number-input.tsx +16 -15
- package/vendor/src/components/form/form-phone-input.tsx +15 -9
- package/vendor/src/components/form/form-search-input.tsx +16 -19
- package/vendor/src/components/form/form-select.tsx +4 -3
- package/vendor/src/components/form/public.ts +16 -14
- package/vendor/src/components/form/smart-form-shell.tsx +13 -12
- package/vendor/src/components/inputs/app-input.tsx +27 -0
- package/vendor/src/components/inputs/async-select.tsx +113 -84
- package/vendor/src/components/inputs/clearable-input.tsx +81 -61
- package/vendor/src/components/inputs/date-input.tsx +21 -17
- package/vendor/src/components/inputs/date-range-input.tsx +10 -10
- package/vendor/src/components/inputs/index.ts +1 -0
- package/vendor/src/components/inputs/input-decorator.tsx +101 -57
- package/vendor/src/components/inputs/masked-input.tsx +20 -20
- package/vendor/src/components/inputs/money-input.tsx +2 -2
- package/vendor/src/components/inputs/number-input.tsx +29 -19
- package/vendor/src/components/inputs/password-input.tsx +82 -45
- package/vendor/src/components/inputs/phone-input.tsx +24 -2
- package/vendor/src/components/inputs/quantity-input.tsx +2 -2
- package/vendor/src/components/inputs/search-input.tsx +54 -3
- package/vendor/src/components/inputs/simple-select.tsx +110 -22
- package/vendor/src/components/layout/app-shell.tsx +2 -2
- package/vendor/src/components/layout/index.ts +5 -4
- package/vendor/src/components/layout/page-header.tsx +79 -35
- package/vendor/src/components/layout/public.ts +12 -10
- package/vendor/src/components/layout/section-header.tsx +56 -0
- package/vendor/src/components/layout/stack.tsx +106 -0
- package/vendor/src/components/layout/stat-card.tsx +66 -29
- package/vendor/src/components/navigation/index.ts +1 -0
- package/vendor/src/components/navigation/nav-tabs.tsx +60 -0
- package/vendor/src/components/navigation/page-tabs.tsx +41 -26
- package/vendor/src/components/navigation/pagination.tsx +14 -10
- package/vendor/src/components/overlay/alert-dialog.tsx +65 -0
- package/vendor/src/components/overlay/drawer.tsx +71 -0
- package/vendor/src/components/overlay/index.ts +4 -2
- package/vendor/src/components/patterns/data-view.tsx +13 -8
- package/vendor/src/components/ui/badge.tsx +96 -52
- package/vendor/src/components/ui/button.tsx +99 -61
- package/vendor/src/components/ui/card.tsx +84 -25
- package/vendor/src/components/ui/checkbox.tsx +68 -68
- package/vendor/src/components/ui/command.tsx +32 -32
- package/vendor/src/components/ui/dialog.tsx +135 -138
- package/vendor/src/components/ui/dropdown-menu.tsx +21 -21
- package/vendor/src/components/ui/hover-card.tsx +49 -0
- package/vendor/src/components/ui/input-primitive.tsx +24 -0
- package/vendor/src/components/ui/input.tsx +191 -20
- package/vendor/src/components/ui/kbd.tsx +33 -0
- package/vendor/src/components/ui/popover.tsx +11 -11
- package/vendor/src/components/ui/radio-group.tsx +102 -0
- package/vendor/src/components/ui/right-click-menu.tsx +60 -0
- package/vendor/src/components/ui/scroll-box.tsx +27 -0
- package/vendor/src/components/ui/segmented-control.tsx +21 -17
- package/vendor/src/components/ui/select.tsx +187 -189
- package/vendor/src/components/ui/skeleton.tsx +2 -2
- package/vendor/src/components/ui/switch.tsx +60 -60
- package/vendor/src/components/ui/table.tsx +114 -114
- package/vendor/src/components/ui/tabs.tsx +2 -2
- package/vendor/src/components/ui/textarea.tsx +1 -1
- package/vendor/src/components/upload/file-dropzone.tsx +38 -0
- package/vendor/src/components/upload/file-upload.tsx +4 -4
- package/vendor/src/components/upload/image-upload.tsx +22 -19
- package/vendor/src/components/upload/index.ts +2 -0
- package/vendor/src/families/catalog.ts +1 -0
- package/vendor/src/families/docs-groups.ts +10 -1
- package/vendor/src/families/member-metadata.ts +24 -0
- package/vendor/src/families/member-snippets.ts +41 -2
- package/vendor/src/families/migration-map.ts +3 -0
- package/vendor/src/index.ts +23 -18
- package/vendor/templates/styles/globals.css +253 -0
- package/dist/index.js +0 -432
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { CalendarDaysIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { InputDecorator } from "@/components/inputs/input-decorator"
|
|
5
|
+
import { createInputChangeHandler, getInputValue } from "@/components/inputs/input-value"
|
|
5
6
|
|
|
6
7
|
export type DateInputProps = Omit<
|
|
7
|
-
React.ComponentProps<typeof
|
|
8
|
+
React.ComponentProps<typeof InputDecorator>,
|
|
8
9
|
"type" | "value" | "onChange"
|
|
9
10
|
> & {
|
|
10
11
|
value?: string | null
|
|
11
12
|
onChange?: React.ChangeEventHandler<HTMLInputElement>
|
|
12
13
|
onValueChange?: (value: string) => void
|
|
14
|
+
showIcon?: boolean
|
|
15
|
+
icon?: React.ReactNode
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
const DateInput = React.forwardRef<HTMLInputElement, DateInputProps>(
|
|
16
|
-
({ value, onChange, onValueChange, ...props }, ref) => {
|
|
17
|
-
const handleChange = createInputChangeHandler({ onChange, onValueChange })
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<
|
|
21
|
-
ref={ref}
|
|
22
|
-
type="date"
|
|
23
|
-
value={getInputValue(value)}
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
18
|
+
const DateInput = React.forwardRef<HTMLInputElement, DateInputProps>(
|
|
19
|
+
({ value, onChange, onValueChange, showIcon = true, icon, leading, ...props }, ref) => {
|
|
20
|
+
const handleChange = createInputChangeHandler({ onChange, onValueChange })
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<InputDecorator
|
|
24
|
+
ref={ref}
|
|
25
|
+
type="date"
|
|
26
|
+
value={getInputValue(value)}
|
|
27
|
+
leading={showIcon ? icon ?? leading ?? <CalendarDaysIcon /> : leading}
|
|
28
|
+
onChange={handleChange}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
27
31
|
)
|
|
28
32
|
}
|
|
29
33
|
)
|
|
@@ -46,20 +46,20 @@ function DateRangeInput({
|
|
|
46
46
|
{...props}
|
|
47
47
|
>
|
|
48
48
|
<DateInput
|
|
49
|
-
value={value?.from ?? ""}
|
|
50
|
-
placeholder={fromPlaceholder}
|
|
51
|
-
className={inputClassName}
|
|
52
|
-
onValueChange={(from) => nextValue({ from })}
|
|
49
|
+
value={value?.from ?? ""}
|
|
50
|
+
placeholder={fromPlaceholder}
|
|
51
|
+
className={inputClassName}
|
|
52
|
+
onValueChange={(from: string) => nextValue({ from })}
|
|
53
53
|
{...fromInputProps}
|
|
54
54
|
/>
|
|
55
55
|
<span className="hidden text-sm text-muted-foreground sm:inline-flex">{separator}</span>
|
|
56
56
|
<DateInput
|
|
57
|
-
value={value?.to ?? ""}
|
|
58
|
-
placeholder={toPlaceholder}
|
|
59
|
-
className={inputClassName}
|
|
60
|
-
onValueChange={(to) => nextValue({ to })}
|
|
61
|
-
{...toInputProps}
|
|
62
|
-
/>
|
|
57
|
+
value={value?.to ?? ""}
|
|
58
|
+
placeholder={toPlaceholder}
|
|
59
|
+
className={inputClassName}
|
|
60
|
+
onValueChange={(to: string) => nextValue({ to })}
|
|
61
|
+
{...toInputProps}
|
|
62
|
+
/>
|
|
63
63
|
</div>
|
|
64
64
|
)
|
|
65
65
|
}
|
|
@@ -1,64 +1,108 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { InputPrimitive } from "@/components/ui/input-primitive"
|
|
4
5
|
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
6
|
+
|
|
7
|
+
const inputDecoratorVariants = cva("relative flex w-full items-center", {
|
|
8
|
+
variants: {
|
|
9
|
+
density: {
|
|
10
|
+
compact: "text-xs",
|
|
11
|
+
default: "text-sm",
|
|
12
|
+
comfortable: "text-base",
|
|
13
|
+
},
|
|
14
|
+
tone: {
|
|
15
|
+
neutral: "",
|
|
16
|
+
info: "[&_[data-slot=input]]:border-blue-500/30 [&_[data-slot=input]]:focus-visible:ring-blue-500/20",
|
|
17
|
+
success: "[&_[data-slot=input]]:border-emerald-500/30 [&_[data-slot=input]]:focus-visible:ring-emerald-500/20",
|
|
18
|
+
warning: "[&_[data-slot=input]]:border-amber-500/35 [&_[data-slot=input]]:focus-visible:ring-amber-500/20",
|
|
19
|
+
danger: "[&_[data-slot=input]]:border-destructive/40 [&_[data-slot=input]]:focus-visible:ring-destructive/20",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
density: "default",
|
|
24
|
+
tone: "neutral",
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export type InputDecoratorProps = Omit<React.ComponentProps<typeof InputPrimitive>, "value"> &
|
|
29
|
+
VariantProps<typeof inputDecoratorVariants> & {
|
|
30
|
+
value?: string | number | null
|
|
31
|
+
leading?: React.ReactNode
|
|
32
|
+
trailing?: React.ReactNode
|
|
33
|
+
leadingPointerEvents?: boolean
|
|
34
|
+
trailingPointerEvents?: boolean
|
|
35
|
+
wrapperClassName?: string
|
|
36
|
+
inputClassName?: string
|
|
37
|
+
leadingClassName?: string
|
|
38
|
+
trailingClassName?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const InputDecorator = React.forwardRef<HTMLInputElement, InputDecoratorProps>(
|
|
42
|
+
(
|
|
43
|
+
{
|
|
44
|
+
className,
|
|
45
|
+
value,
|
|
46
|
+
leading,
|
|
47
|
+
trailing,
|
|
48
|
+
leadingPointerEvents = false,
|
|
49
|
+
trailingPointerEvents = true,
|
|
50
|
+
wrapperClassName,
|
|
51
|
+
inputClassName,
|
|
52
|
+
leadingClassName,
|
|
53
|
+
trailingClassName,
|
|
54
|
+
density,
|
|
55
|
+
tone,
|
|
56
|
+
...props
|
|
57
|
+
},
|
|
58
|
+
ref
|
|
59
|
+
) => {
|
|
60
|
+
const hasLeading = Boolean(leading)
|
|
61
|
+
const hasTrailing = Boolean(trailing)
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
data-slot="input-decorator"
|
|
66
|
+
data-has-leading={hasLeading || undefined}
|
|
67
|
+
data-has-trailing={hasTrailing || undefined}
|
|
68
|
+
className={cn(inputDecoratorVariants({ density, tone }), wrapperClassName)}
|
|
69
|
+
>
|
|
70
|
+
{hasLeading && (
|
|
71
|
+
<span
|
|
72
|
+
data-slot="input-leading"
|
|
73
|
+
className={cn(
|
|
74
|
+
"absolute left-2.5 z-10 flex items-center text-muted-foreground [&_svg]:size-4",
|
|
75
|
+
!leadingPointerEvents && "pointer-events-none",
|
|
76
|
+
leadingClassName
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{leading}
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
<InputPrimitive
|
|
42
84
|
ref={ref}
|
|
43
85
|
value={value == null ? "" : String(value)}
|
|
44
|
-
className={cn(
|
|
45
|
-
hasLeading && "pl-8",
|
|
46
|
-
hasTrailing && "pr-9",
|
|
47
|
-
inputClassName,
|
|
48
|
-
className
|
|
49
|
-
)}
|
|
86
|
+
className={cn(hasLeading && "pl-8", hasTrailing && "pr-9", inputClassName, className)}
|
|
50
87
|
{...props}
|
|
51
88
|
/>
|
|
52
|
-
|
|
53
|
-
{hasTrailing && (
|
|
54
|
-
<span
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
|
|
90
|
+
{hasTrailing && (
|
|
91
|
+
<span
|
|
92
|
+
data-slot="input-trailing"
|
|
93
|
+
className={cn(
|
|
94
|
+
"absolute right-2 z-10 flex items-center gap-1 text-muted-foreground",
|
|
95
|
+
!trailingPointerEvents && "pointer-events-none",
|
|
96
|
+
trailingClassName
|
|
97
|
+
)}
|
|
98
|
+
>
|
|
99
|
+
{trailing}
|
|
100
|
+
</span>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
InputDecorator.displayName = "InputDecorator"
|
|
107
|
+
|
|
108
|
+
export { InputDecorator, inputDecoratorVariants }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { InputDecorator } from "@/components/inputs/input-decorator"
|
|
4
|
+
import { getInputValue, setInputElementValue } from "@/components/inputs/input-value"
|
|
5
5
|
|
|
6
6
|
export type MaskedInputProps = Omit<
|
|
7
|
-
React.ComponentProps<typeof
|
|
7
|
+
React.ComponentProps<typeof InputDecorator>,
|
|
8
8
|
"value" | "onChange"
|
|
9
9
|
> & {
|
|
10
10
|
value?: string
|
|
@@ -31,21 +31,21 @@ const MaskedInput = React.forwardRef<HTMLInputElement, MaskedInputProps>(
|
|
|
31
31
|
mask = defaultMask,
|
|
32
32
|
unmask = defaultUnmask,
|
|
33
33
|
...props
|
|
34
|
-
},
|
|
35
|
-
ref
|
|
36
|
-
) => {
|
|
37
|
-
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
38
|
-
const rawValue = unmask(event.target.value)
|
|
39
|
-
const maskedValue = mask(rawValue)
|
|
40
|
-
|
|
41
|
-
setInputElementValue(event, maskedValue)
|
|
42
|
-
onChange?.(event)
|
|
43
|
-
onValueChange?.(maskedValue, rawValue)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return <
|
|
47
|
-
}
|
|
48
|
-
)
|
|
34
|
+
},
|
|
35
|
+
ref
|
|
36
|
+
) => {
|
|
37
|
+
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
38
|
+
const rawValue = unmask(event.target.value)
|
|
39
|
+
const maskedValue = mask(rawValue)
|
|
40
|
+
|
|
41
|
+
setInputElementValue(event, maskedValue)
|
|
42
|
+
onChange?.(event)
|
|
43
|
+
onValueChange?.(maskedValue, rawValue)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <InputDecorator ref={ref} value={getInputValue(value)} onChange={handleChange} {...props} />
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
49
|
MaskedInput.displayName = "MaskedInput"
|
|
50
50
|
|
|
51
51
|
export { MaskedInput }
|
|
@@ -3,7 +3,7 @@ import * as React from "react"
|
|
|
3
3
|
import { InputChrome } from "@/components/inputs/input-chrome"
|
|
4
4
|
import { getInputValue } from "@/components/inputs/input-value"
|
|
5
5
|
import { parseMoneyLikeInput } from "@/components/inputs/numeric-value"
|
|
6
|
-
import {
|
|
6
|
+
import { InputPrimitive } from "@/components/ui/input-primitive"
|
|
7
7
|
import { cn } from "@/lib/utils"
|
|
8
8
|
|
|
9
9
|
export type MoneyInputProps = Omit<
|
|
@@ -55,7 +55,7 @@ function MoneyInput({
|
|
|
55
55
|
) : null
|
|
56
56
|
}
|
|
57
57
|
>
|
|
58
|
-
<
|
|
58
|
+
<InputPrimitive
|
|
59
59
|
value={getInputValue(value)}
|
|
60
60
|
onChange={handleChange}
|
|
61
61
|
inputMode={inputMode}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { InputDecorator } from "@/components/inputs/input-decorator"
|
|
4
|
+
import { getInputValue } from "@/components/inputs/input-value"
|
|
5
|
+
import { clampNumericValue, parseDecimalInput } from "@/components/inputs/numeric-value"
|
|
6
6
|
|
|
7
7
|
export type NumberInputProps = Omit<
|
|
8
|
-
React.ComponentProps<typeof
|
|
8
|
+
React.ComponentProps<typeof InputDecorator>,
|
|
9
9
|
"type" | "value" | "onChange" | "min" | "max" | "step"
|
|
10
10
|
> & {
|
|
11
11
|
value?: number | string | null
|
|
@@ -16,15 +16,17 @@ export type NumberInputProps = Omit<
|
|
|
16
16
|
max?: number
|
|
17
17
|
step?: number
|
|
18
18
|
allowEmpty?: boolean
|
|
19
|
+
prefix?: React.ReactNode
|
|
20
|
+
suffix?: React.ReactNode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseNumberInput(value: string) {
|
|
24
|
+
return parseDecimalInput(value)
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
function
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function clampNumber(value: number, min?: number, max?: number) {
|
|
26
|
-
return clampNumericValue(value, min, max)
|
|
27
|
-
}
|
|
27
|
+
function clampNumber(value: number, min?: number, max?: number) {
|
|
28
|
+
return clampNumericValue(value, min, max)
|
|
29
|
+
}
|
|
28
30
|
|
|
29
31
|
const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
30
32
|
(
|
|
@@ -37,13 +39,17 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
37
39
|
max,
|
|
38
40
|
step,
|
|
39
41
|
allowEmpty = true,
|
|
42
|
+
prefix,
|
|
43
|
+
suffix,
|
|
44
|
+
leading,
|
|
45
|
+
trailing,
|
|
40
46
|
inputMode = "decimal",
|
|
41
47
|
onBlur,
|
|
42
48
|
...props
|
|
43
|
-
},
|
|
44
|
-
ref
|
|
45
|
-
) => {
|
|
46
|
-
const stringValue = getInputValue(value)
|
|
49
|
+
},
|
|
50
|
+
ref
|
|
51
|
+
) => {
|
|
52
|
+
const stringValue = getInputValue(value)
|
|
47
53
|
|
|
48
54
|
const emitValue = (rawValue: string) => {
|
|
49
55
|
const parsed = parseNumberInput(rawValue)
|
|
@@ -60,14 +66,16 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
60
66
|
|
|
61
67
|
const handleBlur: React.FocusEventHandler<HTMLInputElement> = (event) => {
|
|
62
68
|
if (!allowEmpty && !event.target.value.trim()) {
|
|
63
|
-
|
|
69
|
+
const fallbackValue = String(min ?? 0)
|
|
70
|
+
event.currentTarget.value = fallbackValue
|
|
71
|
+
emitValue(fallbackValue)
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
onBlur?.(event)
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
return (
|
|
70
|
-
<
|
|
78
|
+
<InputDecorator
|
|
71
79
|
ref={ref}
|
|
72
80
|
type="text"
|
|
73
81
|
value={stringValue}
|
|
@@ -75,6 +83,8 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
75
83
|
min={min}
|
|
76
84
|
max={max}
|
|
77
85
|
step={step}
|
|
86
|
+
leading={prefix ?? leading}
|
|
87
|
+
trailing={suffix ?? trailing}
|
|
78
88
|
onChange={handleChange}
|
|
79
89
|
onBlur={handleBlur}
|
|
80
90
|
{...props}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { EyeIcon, EyeOffIcon } from "lucide-react"
|
|
3
|
-
|
|
4
|
-
import { InputDecorator } from "@/components/inputs/input-decorator"
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { EyeIcon, EyeOffIcon } from "lucide-react"
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
value
|
|
4
|
+
import { InputDecorator } from "@/components/inputs/input-decorator"
|
|
5
|
+
|
|
6
|
+
export type PasswordInputProps = Omit<
|
|
7
|
+
React.ComponentProps<typeof InputDecorator>,
|
|
8
|
+
"type" | "value" | "onChange"
|
|
9
|
+
> & {
|
|
10
|
+
value?: string | null
|
|
11
11
|
onChange?: React.ChangeEventHandler<HTMLInputElement>
|
|
12
12
|
onValueChange?: (value: string) => void
|
|
13
13
|
visible?: boolean
|
|
14
14
|
defaultVisible?: boolean
|
|
15
15
|
onVisibleChange?: (visible: boolean) => void
|
|
16
16
|
showToggle?: boolean
|
|
17
|
+
showCapsLockWarning?: boolean
|
|
18
|
+
capsLockLabel?: string
|
|
17
19
|
wrapperClassName?: string
|
|
18
20
|
inputClassName?: string
|
|
19
21
|
showLabel?: string
|
|
@@ -22,23 +24,30 @@ export type PasswordInputProps = Omit<
|
|
|
22
24
|
|
|
23
25
|
const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
24
26
|
(
|
|
25
|
-
{
|
|
26
|
-
value,
|
|
27
|
-
onChange,
|
|
28
|
-
onValueChange,
|
|
27
|
+
{
|
|
28
|
+
value,
|
|
29
|
+
onChange,
|
|
30
|
+
onValueChange,
|
|
29
31
|
visible,
|
|
30
32
|
defaultVisible = false,
|
|
31
|
-
onVisibleChange,
|
|
32
|
-
showToggle = true,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
onVisibleChange,
|
|
34
|
+
showToggle = true,
|
|
35
|
+
showCapsLockWarning = true,
|
|
36
|
+
capsLockLabel = "Caps Lock is on",
|
|
37
|
+
showLabel = "Show password",
|
|
38
|
+
hideLabel = "Hide password",
|
|
39
|
+
disabled,
|
|
40
|
+
autoComplete = "current-password",
|
|
41
|
+
trailing,
|
|
42
|
+
onKeyDown,
|
|
43
|
+
onKeyUp,
|
|
36
44
|
...props
|
|
37
45
|
},
|
|
38
46
|
ref
|
|
39
47
|
) => {
|
|
40
48
|
const isControlled = visible !== undefined
|
|
41
49
|
const [internalVisible, setInternalVisible] = React.useState(defaultVisible)
|
|
50
|
+
const [capsLockOn, setCapsLockOn] = React.useState(false)
|
|
42
51
|
const currentVisible = isControlled ? visible : internalVisible
|
|
43
52
|
|
|
44
53
|
const setVisibleState = (nextVisible: boolean) => {
|
|
@@ -53,33 +62,61 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
|
53
62
|
onChange?.(event)
|
|
54
63
|
onValueChange?.(event.target.value)
|
|
55
64
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
|
|
66
|
+
const updateCapsLock = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
67
|
+
if (!showCapsLockWarning) return
|
|
68
|
+
setCapsLockOn(event.getModifierState("CapsLock"))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
72
|
+
updateCapsLock(event)
|
|
73
|
+
onKeyDown?.(event)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleKeyUp: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
77
|
+
updateCapsLock(event)
|
|
78
|
+
onKeyUp?.(event)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const trailingContent = (
|
|
82
|
+
<>
|
|
83
|
+
{trailing}
|
|
84
|
+
{showCapsLockWarning && capsLockOn ? (
|
|
85
|
+
<span data-slot="password-caps-lock" className="hidden text-xs text-amber-600 sm:inline" aria-live="polite">
|
|
86
|
+
{capsLockLabel}
|
|
87
|
+
</span>
|
|
88
|
+
) : null}
|
|
89
|
+
{showToggle ? (
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
disabled={disabled}
|
|
93
|
+
aria-label={currentVisible ? hideLabel : showLabel}
|
|
94
|
+
className="rounded-sm p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
|
95
|
+
onClick={() => setVisibleState(!currentVisible)}
|
|
96
|
+
>
|
|
97
|
+
{currentVisible ? <EyeOffIcon className="size-4" /> : <EyeIcon className="size-4" />}
|
|
98
|
+
</button>
|
|
99
|
+
) : null}
|
|
100
|
+
</>
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<InputDecorator
|
|
105
|
+
data-slot="password-input"
|
|
106
|
+
ref={ref}
|
|
107
|
+
type={currentVisible ? "text" : "password"}
|
|
108
|
+
value={value ?? ""}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
autoComplete={autoComplete}
|
|
111
|
+
onChange={handleChange}
|
|
112
|
+
onKeyDown={handleKeyDown}
|
|
113
|
+
onKeyUp={handleKeyUp}
|
|
114
|
+
trailing={trailingContent}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
)
|
|
83
120
|
PasswordInput.displayName = "PasswordInput"
|
|
84
121
|
|
|
85
122
|
export { PasswordInput }
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
+
import { PhoneIcon } from "lucide-react"
|
|
2
3
|
|
|
3
4
|
import { MaskedInput, type MaskedInputProps } from "@/components/inputs/masked-input"
|
|
4
5
|
|
|
5
6
|
export type PhoneInputProps = Omit<MaskedInputProps, "mask" | "unmask" | "inputMode"> & {
|
|
6
7
|
countryCode?: string
|
|
7
8
|
maxDigits?: number
|
|
9
|
+
showCountryCode?: boolean
|
|
10
|
+
showIcon?: boolean
|
|
11
|
+
icon?: React.ReactNode
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
function onlyDigits(value: string) {
|
|
@@ -28,14 +32,32 @@ function formatPhoneDigits(digits: string, countryCode = "+998", maxDigits = 12)
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
|
|
31
|
-
(
|
|
35
|
+
(
|
|
36
|
+
{
|
|
37
|
+
countryCode = "+998",
|
|
38
|
+
maxDigits = 12,
|
|
39
|
+
showCountryCode = false,
|
|
40
|
+
showIcon = false,
|
|
41
|
+
icon,
|
|
42
|
+
leading,
|
|
43
|
+
trailing,
|
|
44
|
+
onValueChange,
|
|
45
|
+
...props
|
|
46
|
+
},
|
|
47
|
+
ref
|
|
48
|
+
) => {
|
|
49
|
+
const leadingContent = showIcon ? icon ?? leading ?? <PhoneIcon /> : leading
|
|
50
|
+
const trailingContent = showCountryCode ? <span className="font-mono text-xs">{countryCode}</span> : trailing
|
|
51
|
+
|
|
32
52
|
return (
|
|
33
53
|
<MaskedInput
|
|
34
54
|
ref={ref}
|
|
35
55
|
inputMode="tel"
|
|
56
|
+
leading={leadingContent}
|
|
57
|
+
trailing={trailingContent}
|
|
36
58
|
mask={(rawValue) => formatPhoneDigits(rawValue, countryCode, maxDigits)}
|
|
37
59
|
unmask={onlyDigits}
|
|
38
|
-
onValueChange={(maskedValue, rawValue) => onValueChange?.(maskedValue, rawValue)}
|
|
60
|
+
onValueChange={(maskedValue: string, rawValue: string) => onValueChange?.(maskedValue, rawValue)}
|
|
39
61
|
{...props}
|
|
40
62
|
/>
|
|
41
63
|
)
|
|
@@ -5,7 +5,7 @@ import { InputChrome } from "@/components/inputs/input-chrome"
|
|
|
5
5
|
import { getInputValue } from "@/components/inputs/input-value"
|
|
6
6
|
import { clampNumericValue, parseDecimalInput } from "@/components/inputs/numeric-value"
|
|
7
7
|
import { Button } from "@/components/ui/button"
|
|
8
|
-
import {
|
|
8
|
+
import { InputPrimitive } from "@/components/ui/input-primitive"
|
|
9
9
|
import { cn } from "@/lib/utils"
|
|
10
10
|
|
|
11
11
|
export type QuantityInputProps = Omit<
|
|
@@ -98,7 +98,7 @@ function QuantityInput({
|
|
|
98
98
|
) : null
|
|
99
99
|
}
|
|
100
100
|
>
|
|
101
|
-
<
|
|
101
|
+
<InputPrimitive
|
|
102
102
|
value={getInputValue(value)}
|
|
103
103
|
onChange={handleInputChange}
|
|
104
104
|
inputMode={inputMode}
|