b44ui 0.0.10 → 0.0.12
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/index.tsx +141 -12
- package/package.json +1 -1
- package/readme.md +10 -4
package/index.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReactNode, Children, useMemo, useState } from "react"
|
|
1
|
+
import { type ReactNode, Children, useMemo, useRef, useState } from "react"
|
|
2
2
|
import { Marked } from "marked"
|
|
3
3
|
import { markedHighlight } from "marked-highlight"
|
|
4
4
|
import hljs from "highlight.js"
|
|
@@ -6,13 +6,14 @@ import type { ClassValue } from "clsx"
|
|
|
6
6
|
import clsx from "clsx"
|
|
7
7
|
import { twMerge } from "tailwind-merge"
|
|
8
8
|
|
|
9
|
-
export type Color = 'red' | 'blue' | 'orange' | 'purple' | 'yellow'
|
|
9
|
+
export type Color = 'red' | 'blue' | 'orange' | 'purple' | 'yellow' | 'green'
|
|
10
10
|
const tintMap: Record<Color, string> = {
|
|
11
11
|
red: 'bg-red-500/20 border-red-500',
|
|
12
12
|
blue: 'bg-blue-500/20 border-blue-500',
|
|
13
13
|
orange: 'bg-orange-500/20 border-orange-500',
|
|
14
14
|
purple: 'bg-purple-500/20 border-purple-500',
|
|
15
15
|
yellow: 'bg-yellow-500/20 border-yellow-500',
|
|
16
|
+
green: 'bg-green-500/20 border-green-500',
|
|
16
17
|
}
|
|
17
18
|
const colorMap: Record<Color, string> = {
|
|
18
19
|
red: 'bg-red-600 hover:bg-red-500',
|
|
@@ -20,6 +21,7 @@ const colorMap: Record<Color, string> = {
|
|
|
20
21
|
orange: 'bg-orange-600 hover:bg-orange-500',
|
|
21
22
|
purple: 'bg-purple-600 hover:bg-purple-500',
|
|
22
23
|
yellow: 'bg-yellow-600 hover:bg-yellow-500',
|
|
24
|
+
green: 'bg-green-600 hover:bg-green-500',
|
|
23
25
|
}
|
|
24
26
|
const tintCn = (c: Color) => tintMap[c]
|
|
25
27
|
const colorCn = (c: Color) => colorMap[c]
|
|
@@ -39,12 +41,13 @@ export type DProps = {
|
|
|
39
41
|
grow?: boolean
|
|
40
42
|
gap?: number
|
|
41
43
|
p?: number
|
|
44
|
+
wd?: number
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
const dStyle = ({ gap, p, style }: DProps): React.CSSProperties | undefined => {
|
|
47
|
+
const dStyle = ({ gap, p, wd, style }: DProps): React.CSSProperties | undefined => {
|
|
45
48
|
const g = sc(gap), pd = sc(p)
|
|
46
|
-
if (!g && !pd && !style) return undefined
|
|
47
|
-
return { ...(g !== undefined && { gap: g }), ...(pd !== undefined && { padding: pd }), ...style }
|
|
49
|
+
if (!g && !pd && wd === undefined && !style) return undefined
|
|
50
|
+
return { ...(g !== undefined && { gap: g }), ...(pd !== undefined && { padding: pd }), ...(wd !== undefined && { width: `${wd * 100}%` }), ...style }
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export const D = (props: DProps) =>
|
|
@@ -64,6 +67,23 @@ export const Centered = (props: DProps & { width?: number }) => {
|
|
|
64
67
|
return <D cn={CN("mx-auto max-w-full px-6", rcn(props))} grow={grow} gap={gap} p={p} style={{ width }}>{children}</D>
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
export const TabList = (props: DProps) => {
|
|
71
|
+
const { children, grow, gap, p } = props
|
|
72
|
+
return <D cn={CN("flex items-end gap-4", rcn(props))} grow={grow} gap={gap} p={p}>{children}</D>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const Tab = ({ title, active, click, grow, gap, p, ...rest }: DProps & { title: ReactNode, active?: boolean, click?: () => void } & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
|
|
76
|
+
const c = rcn(rest as DProps)
|
|
77
|
+
return <button
|
|
78
|
+
className={CN("cursor-pointer border-b-2 pb-1.5 text-sm font-medium", active ? "border-zinc-100 text-zinc-100" : "border-transparent text-zinc-400 hover:text-zinc-200", grow && "flex-1", c)}
|
|
79
|
+
style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
|
|
80
|
+
onClick={click}
|
|
81
|
+
role="tab"
|
|
82
|
+
aria-selected={active}
|
|
83
|
+
{...rest}
|
|
84
|
+
>{title}</button>
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
export const Block = (props: DProps & { label?: ReactNode, row?: boolean, dashed?: boolean }) => {
|
|
68
88
|
const { label, children, row, dashed, grow, gap, p } = props
|
|
69
89
|
return <D cn={CN("rounded flex flex-col items-center", dashed && "border border-dashed border-zinc-700", rcn(props))} grow={grow} gap={gap ?? 4} p={p ?? 4}>
|
|
@@ -79,8 +99,9 @@ export const BlockSm = (props: DProps & { dashed?: boolean }) => {
|
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
export const Chip = (props: DProps & { click?: () => void }) => {
|
|
82
|
-
const { children, click } = props
|
|
102
|
+
const { children, click, grow, gap, p, wd, style } = props
|
|
83
103
|
return <span className={CN("bg-zinc-800 rounded px-2 py-0.5 text-xs", click && "cursor-pointer hover:bg-zinc-700", rcn(props))}
|
|
104
|
+
style={dStyle({ gap, p, wd, style })}
|
|
84
105
|
onClick={click}>{children}</span>
|
|
85
106
|
}
|
|
86
107
|
|
|
@@ -112,11 +133,39 @@ export const Muted = (props: DProps) => {
|
|
|
112
133
|
return <span className={CN("text-sm text-zinc-400", grow && "flex-1", rcn(props))}>{children}</span>
|
|
113
134
|
}
|
|
114
135
|
|
|
136
|
+
export const A = ({
|
|
137
|
+
children, cn, cnIgnoreWrongUsage, grow, gap, p, style, click, href, onClick, onKeyDown, role, tabIndex, ...rest
|
|
138
|
+
}: DProps & { click?: () => void } & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
|
139
|
+
const c = rcn({ cn, cnIgnoreWrongUsage })
|
|
140
|
+
const fire = (e: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>) => {
|
|
141
|
+
click?.()
|
|
142
|
+
onClick?.(e as React.MouseEvent<HTMLAnchorElement>)
|
|
143
|
+
}
|
|
144
|
+
const buttonLike = !href
|
|
145
|
+
|
|
146
|
+
return <a
|
|
147
|
+
{...rest}
|
|
148
|
+
href={href}
|
|
149
|
+
role={buttonLike ? role ?? 'button' : role}
|
|
150
|
+
tabIndex={buttonLike ? tabIndex ?? 0 : tabIndex}
|
|
151
|
+
className={CN("inline-flex items-center gap-1 text-zinc-300 underline underline-offset-4 cursor-pointer hover:text-zinc-100", grow && "flex-1", c)}
|
|
152
|
+
style={dStyle({ gap, p, style })}
|
|
153
|
+
onClick={fire}
|
|
154
|
+
onKeyDown={e => {
|
|
155
|
+
if (buttonLike && (e.key === 'Enter' || e.key === ' ')) {
|
|
156
|
+
e.preventDefault()
|
|
157
|
+
fire(e)
|
|
158
|
+
}
|
|
159
|
+
onKeyDown?.(e)
|
|
160
|
+
}}
|
|
161
|
+
>{children}</a>
|
|
162
|
+
}
|
|
163
|
+
|
|
115
164
|
export const Btn = ({ children, grow, gap, p, click, color, ghost, sm, ...rest }: DProps & { click?: () => void, color?: Color, ghost?: boolean, sm?: boolean } & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
|
|
116
165
|
const c = rcn(rest as DProps)
|
|
117
166
|
return <button className={CN("rounded cursor-pointer", sm ? "px-3 py-1 text-xs" : "px-4 py-2 text-sm",
|
|
118
167
|
ghost ? "bg-transparent text-zinc-400 hover:bg-zinc-800" : color ? colorCn(color) : "bg-zinc-800 hover:bg-zinc-700", grow && "flex-1", c)}
|
|
119
|
-
style={dStyle({ gap, p })} onClick={click} {...rest}>{children}</button>
|
|
168
|
+
style={dStyle({ gap, p, wd: rest.wd, style: rest.style })} onClick={click} {...rest}>{children}</button>
|
|
120
169
|
}
|
|
121
170
|
|
|
122
171
|
export const Tint = (props: DProps & { color: Color }) => {
|
|
@@ -125,9 +174,9 @@ export const Tint = (props: DProps & { color: Color }) => {
|
|
|
125
174
|
}
|
|
126
175
|
|
|
127
176
|
export const Row = (props: DProps & { ratio?: number, align?: 'mid' | 'start' | 'end' }) => {
|
|
128
|
-
const { children, ratio, align, grow, gap, p } = props
|
|
177
|
+
const { children, ratio, align, grow, gap, p, wd, style } = props
|
|
129
178
|
return <div className={CN("flex items-center justify-center", align === 'mid' && "justify-between", align == 'end' && "justify-end", align == 'start' && "justify-start", grow && "flex-1", rcn(props))}
|
|
130
|
-
style={{ gap:
|
|
179
|
+
style={{ ...dStyle({ gap: gap ?? 4, p, wd, style }), ...(ratio ? { width: `${ratio * 100}%` } : {}) }}>{children}</div>
|
|
131
180
|
}
|
|
132
181
|
|
|
133
182
|
export const Toolbar = (props: DProps) => {
|
|
@@ -144,17 +193,17 @@ export const Modal = (props: DProps & { open: boolean }) => {
|
|
|
144
193
|
|
|
145
194
|
export const Input = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.InputHTMLAttributes<HTMLInputElement>) => {
|
|
146
195
|
const c = rcn({ cn, cnIgnoreWrongUsage })
|
|
147
|
-
return <input className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none", grow && "flex-1", c)} {...rest} />
|
|
196
|
+
return <input className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
|
|
148
197
|
}
|
|
149
198
|
|
|
150
199
|
export const Textarea = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.TextareaHTMLAttributes<HTMLTextAreaElement>) => {
|
|
151
200
|
const c = rcn({ cn, cnIgnoreWrongUsage })
|
|
152
|
-
return <textarea className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none w-full", grow && "flex-1", c)} {...rest} />
|
|
201
|
+
return <textarea className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none w-full", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
|
|
153
202
|
}
|
|
154
203
|
|
|
155
204
|
export const Select = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.SelectHTMLAttributes<HTMLSelectElement>) => {
|
|
156
205
|
const c = rcn({ cn, cnIgnoreWrongUsage })
|
|
157
|
-
return <select className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none cursor-pointer", grow && "flex-1", c)} {...rest} />
|
|
206
|
+
return <select className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none cursor-pointer", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
|
|
158
207
|
}
|
|
159
208
|
|
|
160
209
|
export const Grid = (props: DProps & { cols?: number }) => {
|
|
@@ -162,6 +211,86 @@ export const Grid = (props: DProps & { cols?: number }) => {
|
|
|
162
211
|
return <D cn={CN("grid ui-grid", rcn(props))} grow={grow} p={p} style={{ gap: sc(gap ?? 4), '--cols': cols ?? Children.count(children) } as React.CSSProperties}>{children}</D>
|
|
163
212
|
}
|
|
164
213
|
|
|
214
|
+
export const Hr = ({ vertical, color = 'gray', grow, gap, p, ...rest }: DProps & { vertical?: boolean, color?: Color | 'gray' }) => {
|
|
215
|
+
const c = rcn(rest as DProps)
|
|
216
|
+
const border = color === 'gray' ? "border-zinc-700" : ({
|
|
217
|
+
red: "border-red-500",
|
|
218
|
+
blue: "border-blue-500",
|
|
219
|
+
orange: "border-orange-500",
|
|
220
|
+
purple: "border-purple-500",
|
|
221
|
+
yellow: "border-yellow-500",
|
|
222
|
+
green: "border-green-500",
|
|
223
|
+
} satisfies Record<Color, string>)[color]
|
|
224
|
+
|
|
225
|
+
return <div
|
|
226
|
+
className={CN(vertical ? "self-stretch border-l" : "w-full border-t", border, grow && "flex-1", c)}
|
|
227
|
+
style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
|
|
228
|
+
aria-hidden="true"
|
|
229
|
+
/>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const Progress = ({ value, color = 'blue', dot, grow, gap, p, ...rest }: DProps & { value: number, color?: Color, dot?: boolean }) => {
|
|
233
|
+
const c = rcn(rest as DProps)
|
|
234
|
+
const v = Math.max(0, Math.min(1, value))
|
|
235
|
+
const fill = color === 'red' ? 'bg-red-500'
|
|
236
|
+
: color === 'orange' ? 'bg-orange-500'
|
|
237
|
+
: color === 'purple' ? 'bg-purple-500'
|
|
238
|
+
: color === 'yellow' ? 'bg-yellow-500'
|
|
239
|
+
: color === 'green' ? 'bg-green-500'
|
|
240
|
+
: 'bg-blue-500'
|
|
241
|
+
|
|
242
|
+
if (dot) return <div
|
|
243
|
+
className={CN("rounded-full border border-zinc-700 bg-zinc-800 overflow-hidden", grow && "flex-1", c)}
|
|
244
|
+
style={dStyle({ gap, p, wd: rest.wd, style: { aspectRatio: '1 / 1', ...rest.style } })}
|
|
245
|
+
>
|
|
246
|
+
<div className={CN("h-full rounded-full", fill)} style={{ opacity: v ? 1 : 0.2 }} />
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
return <div
|
|
250
|
+
className={CN("h-2 overflow-hidden rounded-full bg-zinc-800 border border-zinc-700", grow && "flex-1", c)}
|
|
251
|
+
style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
|
|
252
|
+
>
|
|
253
|
+
<div className={CN("h-full rounded-full", fill)} style={{ width: `${v * 100}%` }} />
|
|
254
|
+
</div>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export const Dropzone = ({ children, onFiles, multiple, accept, click, ...rest }: DProps & { onFiles: (files: File[]) => void, multiple?: boolean, accept?: string, click?: () => void }) => {
|
|
258
|
+
const input = useRef<HTMLInputElement>(null)
|
|
259
|
+
const c = rcn(rest)
|
|
260
|
+
const push = (list: FileList | null) => {
|
|
261
|
+
const files = list ? Array.from(list) : []
|
|
262
|
+
if (files.length) onFiles(files)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return <div
|
|
266
|
+
className={CN("contents", c)}
|
|
267
|
+
onClick={e => {
|
|
268
|
+
click?.()
|
|
269
|
+
input.current?.click()
|
|
270
|
+
rest.onClick?.(e as any)
|
|
271
|
+
}}
|
|
272
|
+
onDragOver={e => {
|
|
273
|
+
e.preventDefault()
|
|
274
|
+
rest.onDragOver?.(e)
|
|
275
|
+
}}
|
|
276
|
+
onDrop={e => {
|
|
277
|
+
e.preventDefault()
|
|
278
|
+
push(e.dataTransfer.files)
|
|
279
|
+
rest.onDrop?.(e)
|
|
280
|
+
}}
|
|
281
|
+
>
|
|
282
|
+
<input
|
|
283
|
+
ref={input}
|
|
284
|
+
hidden
|
|
285
|
+
type="file"
|
|
286
|
+
multiple={multiple}
|
|
287
|
+
accept={accept}
|
|
288
|
+
onChange={e => push(e.target.files)}
|
|
289
|
+
/>
|
|
290
|
+
{children}
|
|
291
|
+
</div>
|
|
292
|
+
}
|
|
293
|
+
|
|
165
294
|
export const Code = (props: DProps & { highlight?: string }) => {
|
|
166
295
|
const { children, highlight } = props
|
|
167
296
|
const c = rcn(props)
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -28,15 +28,21 @@ export default defineConfig({
|
|
|
28
28
|
|-----------|-------|-------------|
|
|
29
29
|
| `App` | `center`, `width`, `cn` | Root layout, dark background, wraps strings in `Md` |
|
|
30
30
|
| `Centered` | `width`, `cn`, `grow` | Centered max-width container |
|
|
31
|
-
| `D` | `cn`, `style`, `grow` | Plain div with `cn` |
|
|
32
|
-
| `Row` | `align`, `ratio`, `cn`, `grow` | Flex row, `align`: `start \| mid \| end` |
|
|
33
|
-
| `Col` | `cn`, `grow` | Flex column |
|
|
31
|
+
| `D` | `cn`, `style`, `grow`, `wd` | Plain div with `cn` |
|
|
32
|
+
| `Row` | `align`, `ratio`, `cn`, `grow`, `wd` | Flex row, `align`: `start \| mid \| end` |
|
|
33
|
+
| `Col` | `cn`, `grow`, `wd` | Flex column |
|
|
34
34
|
| `Code` | `highlight` | Code block |
|
|
35
35
|
| `Grid` | `cols`, `cn`, `grow` | CSS grid, defaults to one column per child |
|
|
36
36
|
| `Card` | `cn`, `grow` | Bordered zinc-900 card |
|
|
37
37
|
| `Block` | `label`, `row`, `dashed`, `cn`, `grow` | Padded container with optional label |
|
|
38
38
|
| `BlockSm` | `dashed`, `cn`, `grow` | Smaller padded container |
|
|
39
|
+
| `TabList` | `gap`, `p`, `wd` | Simple tab row wrapper |
|
|
40
|
+
| `Tab` | `title`, `active`, `click`, `wd` | String-first tab primitive |
|
|
41
|
+
| `Hr` | `vertical`, `color`, `wd` | Horizontal or vertical divider |
|
|
42
|
+
| `Progress` | `value`, `color`, `dot`, `wd` | Progress bar or dot |
|
|
43
|
+
| `Dropzone` | `onFiles`, `multiple`, `accept` | Hidden file input wrapper for click/drop |
|
|
39
44
|
| `Btn` | `click`, `color`, `ghost`, `cn`, `grow` | Button, supports `Color` and ghost style |
|
|
45
|
+
| `A` | `href`, `click`, `cn`, `grow` | Link-styled anchor, works with `onClick` or `href` |
|
|
40
46
|
| `Chip` | `click`, `cn` | Small inline badge, clickable if `click` provided |
|
|
41
47
|
| `Tint` | `color`, `cn`, `grow` | Tinted background block |
|
|
42
48
|
| `Muted` | `cn`, `grow` | Small muted text |
|
|
@@ -47,6 +53,6 @@ export default defineConfig({
|
|
|
47
53
|
| `Popover` | `text`, `color`, `cn` | Hover popover |
|
|
48
54
|
| `Md` | `className` | Markdown renderer with syntax highlighting |
|
|
49
55
|
|
|
50
|
-
`Color`: `red \| blue \| orange \| purple \| yellow`
|
|
56
|
+
`Color`: `red \| blue \| orange \| purple \| yellow \| green`
|
|
51
57
|
|
|
52
58
|
All components accept `cn` for additional Tailwind classes (merged via `tailwind-merge`).
|