@modlin/ui 0.0.243 → 0.0.244
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/LICENSE +27 -0
- package/package.json +1 -1
- package/src/alert.tsx +2 -2
- package/src/avatar.tsx +17 -10
- package/src/button.tsx +63 -37
- package/src/card.tsx +12 -28
- package/src/checkbox.tsx +22 -12
- package/src/example.tsx +30 -0
- package/src/globals.ts +3 -0
- package/src/heading.tsx +6 -6
- package/src/input.tsx +79 -24
- package/src/select.native.tsx +45 -0
- package/src/global.ts +0 -38
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
The Modlin Distributable License (MDL)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Modlin
|
|
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
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
Any use, distribution, modification, integration, or implementation of the
|
|
16
|
+
Software in another project must include clear and visible credit to the
|
|
17
|
+
Software and to its original author. This credit must be placed in all public
|
|
18
|
+
distributions, documentation, and any “About” or “Credits” section of the
|
|
19
|
+
implementing project.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
27
|
+
THE SOFTWARE.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@modlin/ui",
|
|
4
4
|
"description": "A lightweight UI library built on the Suffix design system for consistent, fast, and elegant interfaces.",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.244",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"exports": {
|
package/src/alert.tsx
CHANGED
|
@@ -54,7 +54,7 @@ export function Alert(props: Readonly<AlertProps>) {
|
|
|
54
54
|
className={cn(
|
|
55
55
|
"grid grid-cols-[16px_1fr] auto-rows-auto gap-x-4 gap-y-1 items-center justify-center",
|
|
56
56
|
"p-4 rounded-2xl",
|
|
57
|
-
"inset-ring inset-ring-
|
|
57
|
+
"inset-ring inset-ring-border",
|
|
58
58
|
props.className,
|
|
59
59
|
)}
|
|
60
60
|
>
|
|
@@ -68,7 +68,7 @@ Alert.Destructive = (props: Readonly<AlertProps>) => {
|
|
|
68
68
|
className={cn(
|
|
69
69
|
"grid grid-cols-[16px_1fr] auto-rows-auto gap-x-4 gap-y-1 items-center justify-center",
|
|
70
70
|
"p-4 rounded-2xl",
|
|
71
|
-
"inset-ring inset-ring-
|
|
71
|
+
"inset-ring inset-ring-border",
|
|
72
72
|
"text-red-600 dark:text-red-400",
|
|
73
73
|
props.className,
|
|
74
74
|
)}
|
package/src/avatar.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { useState } from "react"
|
|
1
3
|
import { cn } from "./utils"
|
|
2
4
|
import Image, { type ImageProps } from "next/image"
|
|
3
5
|
|
|
@@ -5,22 +7,27 @@ export interface AvatarProps extends ImageProps {
|
|
|
5
7
|
fallback: string
|
|
6
8
|
}
|
|
7
9
|
export default function Avatar({ fallback, ...props }: Readonly<AvatarProps>) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
+
const [error, setError] = useState(false)
|
|
11
|
+
if (error) {
|
|
12
|
+
return (
|
|
13
|
+
<span
|
|
14
|
+
className={cn(
|
|
15
|
+
"relative flex shrink-0 size-8 overflow-hidden rounded-full bg-background inset-ring inset-ring-border text-sm font-medium leading-none",
|
|
16
|
+
props.className,
|
|
17
|
+
)}
|
|
18
|
+
>
|
|
19
|
+
<span className="flex items-center justify-center w-full font-mono text-sm">{fallback.slice(0, 3).toUpperCase()}</span>
|
|
20
|
+
</span>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
10
23
|
return (
|
|
11
24
|
<span
|
|
12
25
|
className={cn(
|
|
13
|
-
"relative flex shrink-0 size-8 overflow-hidden rounded-full
|
|
26
|
+
"relative flex shrink-0 size-8 overflow-hidden rounded-full bg-background border border-border text-sm font-medium leading-none",
|
|
14
27
|
props.className,
|
|
15
28
|
)}
|
|
16
29
|
>
|
|
17
|
-
{
|
|
18
|
-
<Image {...props} className="aspect-square size-full" />
|
|
19
|
-
) : (
|
|
20
|
-
<span className="flex items-center justify-center w-full text-sm font-medium leading-none">
|
|
21
|
-
{fallback.slice(0, 3).toUpperCase()}
|
|
22
|
-
</span>
|
|
23
|
-
)}
|
|
30
|
+
<Image width={32} height={32} onError={() => setError(true)} {...props} className={cn("aspect-square size-full", props.className)} />
|
|
24
31
|
</span>
|
|
25
32
|
)
|
|
26
33
|
}
|
package/src/button.tsx
CHANGED
|
@@ -2,22 +2,22 @@ import { cn } from "./utils"
|
|
|
2
2
|
import { IconLoader2 } from "@tabler/icons-react"
|
|
3
3
|
import type { MouseEvent, FocusEvent, ReactNode, ReactHTMLElement } from "react"
|
|
4
4
|
import React from "react"
|
|
5
|
-
import type { Variant,
|
|
5
|
+
import type { Variant, Size, Shape } from "./globals"
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
sm: cn("h-8 px-4 gap-x-1 text-sm
|
|
9
|
-
md: cn("h-9 px-4.5 gap-x-1 text-sm
|
|
10
|
-
lg: cn("h-11 px-5 gap-x-1 text-base
|
|
11
|
-
xl: cn("h-12 px-6 gap-x-1 text-base
|
|
12
|
-
sm_: "h-8 px-2.5 rounded-lg text-sm font-medium [&>svg]:size-4",
|
|
13
|
-
md_: cn("h-9 px-3.5 gap-x-1 text-sm rounded-xl font-medium", "[&>svg]:size-4"),
|
|
14
|
-
lg_: cn("h-11 px-4 gap-x-2 text-base rounded-2xl font-medium", "[&>svg]:size-5"),
|
|
15
|
-
xl_: cn("h-12 px-4 gap-x-2 text-base rounded-2xl font-medium", "[&>svg]:size-4 [&>svg]:scale-125"),
|
|
16
|
-
icon: cn("p-2
|
|
17
|
-
iconr: cn("p-2 rounded-2xl text-sm font-medium"),
|
|
7
|
+
const size: Record<Size, string> = {
|
|
8
|
+
sm: cn("h-8 px-4 gap-x-1 text-sm font-medium"),
|
|
9
|
+
md: cn("h-9 px-4.5 gap-x-1 text-sm font-medium"),
|
|
10
|
+
lg: cn("h-11 px-5 gap-x-1 text-base font-medium", "[&>svg]:size-4"),
|
|
11
|
+
xl: cn("h-12 px-6 gap-x-1 text-base font-semibold", "[&>svg]:size-4"),
|
|
12
|
+
// sm_: "h-8 px-2.5 rounded-lg text-sm font-medium [&>svg]:size-4",
|
|
13
|
+
// md_: cn("h-9 px-3.5 gap-x-1 text-sm rounded-xl font-medium", "[&>svg]:size-4"),
|
|
14
|
+
// lg_: cn("h-11 px-4 gap-x-2 text-base rounded-2xl font-medium", "[&>svg]:size-5"),
|
|
15
|
+
// xl_: cn("h-12 px-4 gap-x-2 text-base rounded-2xl font-medium", "[&>svg]:size-4 [&>svg]:scale-125"),
|
|
16
|
+
icon: cn("p-2 text-sm font-medium"),
|
|
17
|
+
// iconr: cn("p-2 rounded-2xl text-sm font-medium"),
|
|
18
18
|
none: cn("overflow-visible"),
|
|
19
19
|
}
|
|
20
|
-
const variant:
|
|
20
|
+
const variant: Record<Variant, string> = {
|
|
21
21
|
primary: "bg-primary disabled:bg-primary/60 hover:bg-primary/85 active:bg-primary/85 text-background",
|
|
22
22
|
secondary: "bg-secondary hover:bg-secondary/75",
|
|
23
23
|
destructive: "bg-red hover:bg-red/85 text-white",
|
|
@@ -32,43 +32,65 @@ const variant: Variants = {
|
|
|
32
32
|
// outline_red: "inset-ring inset-ring-(--red)/50 hover:bg-(--red)/5 text-(--red)",
|
|
33
33
|
// shadcn: "rounded-xl bg-(--primary) hover:bg-(--primary)/85 text-white dark:text-black",
|
|
34
34
|
} as const
|
|
35
|
-
const
|
|
35
|
+
const shape: Record<Shape, string> = {
|
|
36
36
|
square: "rounded-none",
|
|
37
|
-
rounded: "",
|
|
37
|
+
rounded: "rounded-2xl",
|
|
38
38
|
pill: "rounded-full",
|
|
39
39
|
}
|
|
40
40
|
export interface ButtonProps {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
disabled?: boolean
|
|
48
|
-
loading?: boolean
|
|
49
|
-
|
|
41
|
+
/** @android @ios @web */
|
|
42
|
+
disabled?: boolean // state
|
|
43
|
+
/** @android @ios @web */
|
|
44
|
+
loading?: boolean // state
|
|
45
|
+
/** @android @ios @web */
|
|
50
46
|
label?: string
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
/** @android @ios @web */
|
|
48
|
+
title?: string
|
|
49
|
+
/** @android @ios @web */
|
|
53
50
|
children?: ReactNode
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
/** @android @ios @web */
|
|
52
|
+
asChild?: boolean
|
|
53
|
+
/** @android @ios @web */
|
|
54
|
+
variant?: Variant
|
|
55
|
+
/** @android @ios @web */
|
|
56
|
+
size?: Size
|
|
57
|
+
/** @android @ios @web */
|
|
58
|
+
shape?: Shape
|
|
59
|
+
/** @android @ios */
|
|
60
|
+
haptics?: boolean
|
|
61
|
+
/** @android @ios */
|
|
62
|
+
full?: boolean // utility
|
|
63
|
+
/** @android @ios */
|
|
64
|
+
rounded?: number // utility
|
|
65
|
+
/** @web */
|
|
60
66
|
id?: string
|
|
67
|
+
/** @web */
|
|
68
|
+
type?: "button" | "reset" | "submit"
|
|
69
|
+
/** @web */
|
|
61
70
|
className?: string
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
/** @android @ios @web */
|
|
72
|
+
onPress?(event: MouseEvent): void | Promise<void>
|
|
73
|
+
/** @android @ios @web */
|
|
74
|
+
onPressIn?(event: MouseEvent): void | Promise<void>
|
|
75
|
+
/** @android @ios @web */
|
|
76
|
+
onPressOut?(event: MouseEvent): void | Promise<void>
|
|
77
|
+
/** @web */
|
|
78
|
+
onHover?(event: MouseEvent): void | Promise<void>
|
|
79
|
+
/** @web */
|
|
80
|
+
onFocus?(event: FocusEvent): void | Promise<void>
|
|
81
|
+
/** @web */
|
|
82
|
+
onBlur?(event: FocusEvent): void | Promise<void>
|
|
83
|
+
// tone?: "default" | "success" | "error" | "warning"
|
|
84
|
+
// elevation?: "none" | "xs" | "sm" | "md" | "lg" | "xl"
|
|
64
85
|
}
|
|
65
86
|
export default function Button(props: Readonly<ButtonProps>) {
|
|
66
87
|
const className = cn(
|
|
67
88
|
"line-clamp-1 flex items-center justify-center leading-none text-center",
|
|
68
89
|
"select-none hover:cursor-pointer disabled:hover:cursor-not-allowed",
|
|
69
|
-
"transition-[background-color]
|
|
90
|
+
"transition-[background-color] duration-250 ease",
|
|
91
|
+
size[props.size ?? "xl"],
|
|
70
92
|
variant[props.variant ?? "primary"],
|
|
71
|
-
|
|
93
|
+
shape[props.shape ?? "pill"],
|
|
72
94
|
props.className,
|
|
73
95
|
)
|
|
74
96
|
|
|
@@ -80,6 +102,8 @@ export default function Button(props: Readonly<ButtonProps>) {
|
|
|
80
102
|
disabled: props.disabled,
|
|
81
103
|
"aria-label": props.label,
|
|
82
104
|
onClick: props.onPress,
|
|
105
|
+
onMouseDown: props.onPressIn,
|
|
106
|
+
onMouseUp: props.onPressOut,
|
|
83
107
|
onMouseOver: props.onHover,
|
|
84
108
|
onFocus: props.onFocus,
|
|
85
109
|
id: props.id,
|
|
@@ -93,13 +117,15 @@ export default function Button(props: Readonly<ButtonProps>) {
|
|
|
93
117
|
disabled={props.loading ? true : props.disabled}
|
|
94
118
|
aria-label={props.label}
|
|
95
119
|
onClick={props.onPress}
|
|
120
|
+
onMouseDown={props.onPressIn}
|
|
121
|
+
onMouseUp={props.onPressOut}
|
|
96
122
|
onMouseOver={props.onHover}
|
|
97
123
|
onFocus={props.onFocus}
|
|
98
124
|
onBlur={props.onBlur}
|
|
99
125
|
id={props.id}
|
|
100
126
|
className={className}
|
|
101
127
|
>
|
|
102
|
-
{props.loading ? <IconLoader2 className="animate-spin" /> : props.children}
|
|
128
|
+
{props.loading ? <IconLoader2 className="animate-spin" /> : (props.title || props.children)}
|
|
103
129
|
</button>
|
|
104
130
|
)
|
|
105
131
|
}
|
package/src/card.tsx
CHANGED
|
@@ -2,23 +2,20 @@ import { cn } from "./utils"
|
|
|
2
2
|
import type * as React from "react"
|
|
3
3
|
|
|
4
4
|
export interface CardHeaderProps {
|
|
5
|
+
children?: React.ReactNode
|
|
5
6
|
className?: string
|
|
6
|
-
children: React.ReactNode
|
|
7
7
|
}
|
|
8
8
|
export const CardHeader: React.FC<CardHeaderProps> = props => {
|
|
9
9
|
return (
|
|
10
|
-
<header
|
|
11
|
-
data-slot="card-header"
|
|
12
|
-
className={cn("flex flex-col gap-4", props.className)}
|
|
13
|
-
>
|
|
10
|
+
<header data-slot="card-header" className={cn("flex flex-col gap-4", props.className)}>
|
|
14
11
|
{props.children}
|
|
15
12
|
</header>
|
|
16
13
|
)
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
export interface CardContentProps {
|
|
17
|
+
children?: React.ReactNode
|
|
20
18
|
className?: string
|
|
21
|
-
children: React.ReactNode
|
|
22
19
|
}
|
|
23
20
|
export const CardContent: React.FC<CardContentProps> = props => {
|
|
24
21
|
return (
|
|
@@ -29,22 +26,19 @@ export const CardContent: React.FC<CardContentProps> = props => {
|
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
export interface CardFooterProps {
|
|
29
|
+
children?: React.ReactNode
|
|
32
30
|
className?: string
|
|
33
|
-
children: React.ReactNode
|
|
34
31
|
}
|
|
35
32
|
export const CardFooter: React.FC<CardFooterProps> = props => {
|
|
36
33
|
return (
|
|
37
|
-
<footer
|
|
38
|
-
data-slot="card-footer"
|
|
39
|
-
className={cn("flex [.border-t]:pt-4", props.className)}
|
|
40
|
-
>
|
|
34
|
+
<footer data-slot="card-footer" className={cn("flex [.border-t]:pt-4", props.className)}>
|
|
41
35
|
{props.children}
|
|
42
36
|
</footer>
|
|
43
37
|
)
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
export interface CardAction {
|
|
47
|
-
children
|
|
41
|
+
children?: React.ReactNode
|
|
48
42
|
className?: string
|
|
49
43
|
}
|
|
50
44
|
export function CardAction(props: Readonly<CardAction>) {
|
|
@@ -56,29 +50,19 @@ export function CardAction(props: Readonly<CardAction>) {
|
|
|
56
50
|
}
|
|
57
51
|
|
|
58
52
|
const size = {
|
|
59
|
-
md: "p-4 gap-4
|
|
60
|
-
lg: "p-6 gap-6
|
|
61
|
-
xl: "p-8 gap-8
|
|
53
|
+
md: "p-4 gap-4 rounded-2xl",
|
|
54
|
+
lg: "p-6 gap-6 rounded-2xl",
|
|
55
|
+
xl: "p-8 gap-8 rounded-4xl",
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
export interface CardProps {
|
|
65
|
-
className?: string
|
|
66
59
|
size?: "lg" | "xl"
|
|
67
|
-
children
|
|
60
|
+
children?: React.ReactNode
|
|
61
|
+
className?: string
|
|
68
62
|
}
|
|
69
63
|
export const Card: React.FC<CardProps> = props => {
|
|
70
64
|
return (
|
|
71
|
-
<div
|
|
72
|
-
className={cn(
|
|
73
|
-
"flex flex-col",
|
|
74
|
-
size[props.size ?? "md"],
|
|
75
|
-
"bg-background",
|
|
76
|
-
"sm:inset-ring inset-ring-border",
|
|
77
|
-
props.className,
|
|
78
|
-
)}
|
|
79
|
-
>
|
|
80
|
-
{props.children}
|
|
81
|
-
</div>
|
|
65
|
+
<div className={cn("flex flex-col", size[props.size ?? "md"], "bg-background", "inset-ring inset-ring-border", props.className)}>{props.children}</div>
|
|
82
66
|
)
|
|
83
67
|
}
|
|
84
68
|
|
package/src/checkbox.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
|
-
import {
|
|
2
|
+
import { useRef, type ChangeEvent, type ReactElement, type ReactNode } from "react"
|
|
3
3
|
import { cn } from "./utils"
|
|
4
|
-
import { setConfig } from "next/config"
|
|
5
4
|
|
|
6
5
|
export interface ViewProps {
|
|
7
6
|
children?: ReactNode
|
|
@@ -9,18 +8,29 @@ export interface ViewProps {
|
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
export interface CheckboxProps {
|
|
11
|
+
/** @android @ios @web */
|
|
12
12
|
disabled?: boolean
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
/** @android @ios @web */
|
|
14
|
+
defaultChecked?: boolean
|
|
15
|
+
/** @android @ios @web */
|
|
16
|
+
checked?: boolean
|
|
17
|
+
/** @android @ios @web */
|
|
18
18
|
label?: string
|
|
19
|
-
|
|
20
|
-
required?: boolean
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/** @web */
|
|
20
|
+
required?: boolean
|
|
21
|
+
/** @web */
|
|
22
|
+
name?: string
|
|
23
|
+
/** @web */
|
|
24
|
+
id?: string
|
|
25
|
+
/** @web */
|
|
26
|
+
asChild?: boolean
|
|
27
|
+
/** @android @ios @web */
|
|
28
|
+
onChange?(checked: boolean): void
|
|
29
|
+
// note: dropped support for RHF to add native support
|
|
30
|
+
/** @deprecated @use checked */
|
|
31
|
+
value?: string
|
|
32
|
+
/** @deprecated @use onChange */
|
|
33
|
+
onValueChange?(event: ChangeEvent<HTMLInputElement>): void
|
|
24
34
|
}
|
|
25
35
|
export default function Checkbox(props: Readonly<CheckboxProps>): ReactElement<CheckboxProps> {
|
|
26
36
|
const input = useRef<HTMLInputElement>(null)
|
package/src/example.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Heading4 } from "./heading"
|
|
2
|
+
import Button from "./button"
|
|
3
|
+
import Input from "./input"
|
|
4
|
+
import Label from "./label"
|
|
5
|
+
import { Muted } from "./typography"
|
|
6
|
+
|
|
7
|
+
export function Example() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex flex-1 flex-col gap-8 p-8 bg-background">
|
|
10
|
+
<header>
|
|
11
|
+
<Heading4 className="h-8">Login to your account</Heading4>
|
|
12
|
+
<Muted>Enter your email address below to login to your account</Muted>
|
|
13
|
+
</header>
|
|
14
|
+
<div className="flex flex-col gap-4">
|
|
15
|
+
<div className="flex flex-col gap-2">
|
|
16
|
+
<Label htmlFor="email">Email address</Label>
|
|
17
|
+
<Input placeholder="m@example.com" id="email" type="email" inputMode="email" autoComplete="email" autoCapitalize="none" />
|
|
18
|
+
</div>
|
|
19
|
+
<div className="flex flex-col gap-2">
|
|
20
|
+
<Label htmlFor="password">Password</Label>
|
|
21
|
+
<Input id="password" type="password" autoComplete="password" autoCapitalize="none" />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="flex flex-col gap-4">
|
|
25
|
+
<Button title="Login" haptics />
|
|
26
|
+
<Button variant="outline" title="Login with Google" haptics />
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
package/src/globals.ts
ADDED
package/src/heading.tsx
CHANGED
|
@@ -17,7 +17,7 @@ export default function Heading(props: Readonly<HeadingProps>) {
|
|
|
17
17
|
)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
export function Heading1(props: Readonly<HeadingProps>) {
|
|
21
21
|
return (
|
|
22
22
|
<h1
|
|
23
23
|
className={cn(
|
|
@@ -29,7 +29,7 @@ Heading.H1 = (props: Readonly<HeadingProps>) => {
|
|
|
29
29
|
</h1>
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
export function Heading2(props: Readonly<HeadingProps>) {
|
|
33
33
|
return (
|
|
34
34
|
<h2
|
|
35
35
|
className={cn(
|
|
@@ -41,7 +41,7 @@ Heading.H2 = (props: Readonly<HeadingProps>) => {
|
|
|
41
41
|
</h2>
|
|
42
42
|
)
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
export function Heading3(props: Readonly<HeadingProps>) {
|
|
45
45
|
return (
|
|
46
46
|
<h3
|
|
47
47
|
className={cn(
|
|
@@ -53,7 +53,7 @@ Heading.H3 = (props: Readonly<HeadingProps>) => {
|
|
|
53
53
|
</h3>
|
|
54
54
|
)
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
export function Heading4(props: Readonly<HeadingProps>) {
|
|
57
57
|
return (
|
|
58
58
|
<h4
|
|
59
59
|
className={cn(
|
|
@@ -65,7 +65,7 @@ Heading.H4 = (props: Readonly<HeadingProps>) => {
|
|
|
65
65
|
</h4>
|
|
66
66
|
)
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
export function Heading5(props: Readonly<HeadingProps>) {
|
|
69
69
|
return (
|
|
70
70
|
<h5
|
|
71
71
|
className={cn(
|
|
@@ -77,7 +77,7 @@ Heading.H5 = (props: Readonly<HeadingProps>) => {
|
|
|
77
77
|
</h5>
|
|
78
78
|
)
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
export function Heading6(props: Readonly<HeadingProps>) {
|
|
81
81
|
return (
|
|
82
82
|
<h6
|
|
83
83
|
className={cn(
|
package/src/input.tsx
CHANGED
|
@@ -1,37 +1,89 @@
|
|
|
1
|
-
import { type ChangeEvent, type FocusEvent, type
|
|
2
|
-
import {
|
|
1
|
+
import { type ChangeEvent, type FocusEvent, type HTMLInputAutoCompleteAttribute, forwardRef } from "react"
|
|
2
|
+
import type { Variant } from "./globals"
|
|
3
3
|
import { cn } from "./utils"
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export const input_variant: Record<Variant, string> = {
|
|
6
|
+
primary: cn(
|
|
7
|
+
"bg-background/25 backdrop-blur-sm",
|
|
8
|
+
"placeholder:text-muted-foreground disabled:text-disabled",
|
|
9
|
+
"inset-ring inset-ring-border disabled:inset-ring-disabled focus:inset-ring-primary/75",
|
|
10
|
+
// "border border-border disabled:border-disabled focus:border-primary/75",
|
|
11
|
+
"focus:ring-4 focus:ring-primary/10 dark:focus:ring-primary/15",
|
|
12
|
+
"invalid:inset-ring-red",
|
|
13
|
+
// "data-[invalid=true]:border data-[invalid=true]:border-red/50 data-[invalid=true]:focus:border-red",
|
|
14
|
+
"data-[invalid=true]:inset-ring-red/50 data-[invalid=true]:focus:inset-ring-red/75",
|
|
15
|
+
"data-[invalid=true]:focus:ring-4 data-[invalid=true]:focus:ring-red/10",
|
|
16
|
+
),
|
|
17
|
+
secondary: cn(),
|
|
18
|
+
destructive: cn(
|
|
19
|
+
"bg-background",
|
|
20
|
+
"placeholder:text-muted-foreground disabled:text-disabled",
|
|
21
|
+
"inset-ring inset-ring-red/50 disabled:inset-ring-red/25 focus:inset-ring-red/75",
|
|
22
|
+
"focus:ring-4 focus:ring-red/10",
|
|
23
|
+
// "border border-red/50 disabled:border-red/25 focus:border-red",
|
|
24
|
+
"focus:ring-4 focus:ring-red/5",
|
|
25
|
+
),
|
|
26
|
+
outline: cn(),
|
|
27
|
+
ghost: cn(),
|
|
28
|
+
link: cn(),
|
|
29
|
+
none: cn("text-()"),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type InputType = "text" | "password" | "email" | "number" | "tel" | "url" | "file"
|
|
33
|
+
type InputMode = "search" | "text" | "email" | "tel" | "url" | "none" | "numeric" | "decimal"
|
|
34
|
+
type BlurEvent = FocusEvent<HTMLInputElement, Element>
|
|
7
35
|
|
|
8
|
-
|
|
9
|
-
|
|
36
|
+
export interface InputProps {
|
|
37
|
+
/** @android @ios @web */
|
|
10
38
|
placeholder?: string
|
|
39
|
+
/** @android @ios @web */
|
|
11
40
|
defaultValue?: string
|
|
12
|
-
|
|
41
|
+
/** @android @ios @web */
|
|
42
|
+
inputMode?: InputMode
|
|
43
|
+
/** @android @ios @web */
|
|
44
|
+
value?: string
|
|
45
|
+
/** @android @ios @web */
|
|
46
|
+
readOnly?: boolean
|
|
47
|
+
/** @android @ios @web */
|
|
48
|
+
maxLength?: number
|
|
49
|
+
/** @android @ios @web */
|
|
50
|
+
autoCapitalize?: "none" | "sentences" | "words" | "characters"
|
|
51
|
+
/** @android @ios @web */
|
|
52
|
+
autoComplete?: HTMLInputAutoCompleteAttribute
|
|
53
|
+
/** @android @ios @web */
|
|
54
|
+
autoCorrect?: boolean
|
|
55
|
+
/** @android @ios @web */
|
|
56
|
+
variant?: Variant
|
|
57
|
+
/** @web */
|
|
58
|
+
id?: string
|
|
59
|
+
/** @web */
|
|
60
|
+
type?: InputType
|
|
61
|
+
/** @web */
|
|
13
62
|
name?: string
|
|
63
|
+
/** @web */
|
|
64
|
+
required?: boolean // validation
|
|
65
|
+
/** @web */
|
|
66
|
+
disabled?: boolean
|
|
67
|
+
/** @web */
|
|
14
68
|
pattern?: string // validation
|
|
69
|
+
/** @web */
|
|
15
70
|
min?: number | string // validation
|
|
71
|
+
/** @web */
|
|
16
72
|
max?: number | string // validation
|
|
17
|
-
|
|
73
|
+
/** @web */
|
|
18
74
|
minLength?: number // validation
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
readOnly?: boolean
|
|
22
|
-
disabled?: boolean
|
|
23
|
-
|
|
24
|
-
value?: string
|
|
25
|
-
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
|
26
|
-
onBlur?: (event: FocusEvent<HTMLInputElement>) => void
|
|
27
|
-
onInput?: (event: InputEvent<HTMLInputElement>) => void
|
|
28
|
-
|
|
29
|
-
id?: string
|
|
30
|
-
autoComplete?: HTMLInputAutoCompleteAttribute
|
|
31
|
-
// onChange?: (value: string) => void
|
|
32
|
-
className?: string
|
|
75
|
+
/** @web */
|
|
33
76
|
invalid?: boolean
|
|
77
|
+
/** @web */
|
|
34
78
|
describedby?: string
|
|
79
|
+
/** @web */
|
|
80
|
+
className?: string
|
|
81
|
+
/** @android @ios @web */
|
|
82
|
+
onChange?(event: ChangeEvent): void
|
|
83
|
+
/** @android @ios @web */
|
|
84
|
+
onFocus?(event: FocusEvent): void
|
|
85
|
+
/** @android @ios @web */
|
|
86
|
+
onBlur?(event: BlurEvent): void
|
|
35
87
|
}
|
|
36
88
|
const Input = forwardRef<HTMLInputElement, Readonly<InputProps>>((props, ref) => {
|
|
37
89
|
const { onChange } = props
|
|
@@ -54,12 +106,15 @@ const Input = forwardRef<HTMLInputElement, Readonly<InputProps>>((props, ref) =>
|
|
|
54
106
|
readOnly={props.readOnly}
|
|
55
107
|
disabled={props.disabled}
|
|
56
108
|
onChange={onChange}
|
|
57
|
-
|
|
109
|
+
onFocus={props.onFocus}
|
|
110
|
+
onBlur={props.onBlur}
|
|
58
111
|
onInvalid={e => e.preventDefault()}
|
|
59
112
|
id={props.id}
|
|
113
|
+
autoCapitalize={props.autoCapitalize}
|
|
60
114
|
autoComplete={props.autoComplete}
|
|
61
|
-
|
|
115
|
+
autoCorrect={props.autoCorrect ? "on" : "off"}
|
|
62
116
|
aria-describedby={props.describedby}
|
|
117
|
+
data-invalid={props.invalid}
|
|
63
118
|
className={cn(
|
|
64
119
|
"flex items-center w-full h-12 px-4",
|
|
65
120
|
"transition transition-duration-150 transition-[box-shadow] ease-in",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ChangeEvent, ReactNode } from "react"
|
|
2
|
+
import { cn } from "./utils"
|
|
3
|
+
|
|
4
|
+
export interface SelectProps {
|
|
5
|
+
children?: ReactNode
|
|
6
|
+
name?: string
|
|
7
|
+
id?: string
|
|
8
|
+
onChange?: (event: ChangeEvent<HTMLSelectElement>) => void
|
|
9
|
+
}
|
|
10
|
+
export function Select(props: SelectProps) {
|
|
11
|
+
return (
|
|
12
|
+
<select
|
|
13
|
+
name={props.name}
|
|
14
|
+
id={props.id}
|
|
15
|
+
onChange={props.onChange}
|
|
16
|
+
className={cn(
|
|
17
|
+
"flex items-center h-12 px-4 rounded-2xl bg-background/25 backdrop-blur-sm inset-ring inset-ring-border",
|
|
18
|
+
"open:inset-ring-primary/75 open:ring-4 open:ring-primary/10 dark:open:ring-primary/15 focus:inset-ring-primary/75 focus:ring-4 focus:ring-primary/10 dark:focus:ring-primary/15",
|
|
19
|
+
"transition duration-150 ease-in",
|
|
20
|
+
)}
|
|
21
|
+
>
|
|
22
|
+
{props.children}
|
|
23
|
+
</select>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SelectItemProps {
|
|
28
|
+
value: string
|
|
29
|
+
children?: ReactNode
|
|
30
|
+
className?: string
|
|
31
|
+
}
|
|
32
|
+
export function SelectItem(props: SelectItemProps) {
|
|
33
|
+
return (
|
|
34
|
+
<option
|
|
35
|
+
className={cn(
|
|
36
|
+
"gap-4 h-9 min-h-9 px-3 rounded-lg text-foreground hover:bg-secondary focus:bg-secondary checked:bg-secondary",
|
|
37
|
+
"transition duration-150 ease-out",
|
|
38
|
+
props.className,
|
|
39
|
+
)}
|
|
40
|
+
value={props.value}
|
|
41
|
+
>
|
|
42
|
+
{props.children}
|
|
43
|
+
</option>
|
|
44
|
+
)
|
|
45
|
+
}
|
package/src/global.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { cn } from "./utils"
|
|
2
|
-
|
|
3
|
-
export type Variant =
|
|
4
|
-
| "primary"
|
|
5
|
-
| "secondary"
|
|
6
|
-
| "destructive"
|
|
7
|
-
| "outline"
|
|
8
|
-
| "ghost"
|
|
9
|
-
| "link"
|
|
10
|
-
| "none"
|
|
11
|
-
export type Variants = Record<Variant, string>
|
|
12
|
-
|
|
13
|
-
export const input_variant: Variants = {
|
|
14
|
-
primary: cn(
|
|
15
|
-
"bg-background",
|
|
16
|
-
"placeholder:text-description disabled:text-disabled",
|
|
17
|
-
"inset-ring inset-ring-border disabled:inset-ring-disabled focus:inset-ring-primary/75",
|
|
18
|
-
// "border border-border disabled:border-disabled focus:border-primary/75",
|
|
19
|
-
"focus:ring-4 focus:ring-primary/10",
|
|
20
|
-
"invalid:inset-ring-red",
|
|
21
|
-
// "data-[invalid=true]:border data-[invalid=true]:border-red/50 data-[invalid=true]:focus:border-red",
|
|
22
|
-
"data-[invalid=true]:inset-ring-red/50 data-[invalid=true]:focus:inset-ring-red/75",
|
|
23
|
-
"data-[invalid=true]:focus:ring-4 data-[invalid=true]:focus:ring-red/10",
|
|
24
|
-
),
|
|
25
|
-
secondary: cn(),
|
|
26
|
-
destructive: cn(
|
|
27
|
-
"bg-background",
|
|
28
|
-
"placeholder:text-description disabled:text-disabled",
|
|
29
|
-
"inset-ring inset-ring-red/50 disabled:inset-ring-red/25 focus:inset-ring-red/75",
|
|
30
|
-
"focus:ring-4 focus:ring-red/10",
|
|
31
|
-
// "border border-red/50 disabled:border-red/25 focus:border-red",
|
|
32
|
-
"focus:ring-4 focus:ring-red/5",
|
|
33
|
-
),
|
|
34
|
-
outline: cn(),
|
|
35
|
-
ghost: cn(),
|
|
36
|
-
link: cn(),
|
|
37
|
-
none: cn("text-()"),
|
|
38
|
-
}
|