b44ui 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core.tsx ADDED
@@ -0,0 +1,89 @@
1
+ import React, { useState, type CSSProperties, type ReactNode, type Ref } from "react"
2
+
3
+ export type Color = 'purple'
4
+
5
+ const units = {
6
+ p: 'padding', m: 'margin', wd: 'width', ht: 'height',
7
+ minHt: 'minHeight', maxHt: 'maxHeight', minWd: 'minWidth', maxWd: 'maxWidth'
8
+ } as const
9
+
10
+ const passthrough = [
11
+ 'font', 'fontSize', 'fontFamily', 'fontWeight', 'lineHeight',
12
+ 'border', 'borderLeft', 'borderTop', 'borderRadius',
13
+ 'background', 'color', 'opacity',
14
+ 'gap', 'cursor', 'overflow', 'overflowX', 'overflowY',
15
+ 'transition', 'userSelect', 'resize',
16
+ 'position', 'inset', 'top', 'bottom', 'left', 'right', 'zIndex',
17
+ 'alignSelf', 'flex', 'flexDirection', 'outline',
18
+ 'display', 'gridTemplateColumns',
19
+ ] as const
20
+
21
+ type Passthrough = typeof passthrough[number]
22
+
23
+ // props that are consumed by Core and must NOT be forwarded to the DOM
24
+ const consumed = new Set(['children', 'style', 'ref', 'contentEditable', 'click', 'hover', 'input', 'href', 'grow', 'row', 'col', 'align', 'textAlign'])
25
+
26
+ export type Props = {
27
+ children?: ReactNode
28
+ ref?: Ref<HTMLDivElement>
29
+ contentEditable?: boolean
30
+ click?: (e: MouseEvent) => void
31
+ hover?: Partial<Record<Passthrough, string | number>>
32
+ input?: (v: string) => void
33
+ href?: string
34
+ grow?: boolean
35
+ row?: boolean
36
+ col?: boolean
37
+ align?: string
38
+ [key: string]: unknown
39
+ } & { [u in keyof typeof units]?: string | number }
40
+ & { [p in Passthrough]?: string | number }
41
+
42
+ const maybePx = (v: string | number) => typeof v == 'number' ? `${v}px` : v
43
+
44
+ export const Core = (props: Props) => {
45
+ const [isHover, setIsHover] = useState(false)
46
+
47
+ const style: CSSProperties = { ...(props.style as CSSProperties ?? {}) }
48
+ const rest: Record<string, unknown> = {}
49
+
50
+ if (props.contentEditable) rest.suppressContentEditableWarning = true
51
+
52
+ rest.onClick = (e: MouseEvent) => {
53
+ if (props.click) props.click(e)
54
+ if (props.href) window.location.href = props.href
55
+ }
56
+ rest.onInput = (e: Event) => {
57
+ if (props.input) props.input((e.currentTarget as HTMLElement).textContent ?? '')
58
+ }
59
+ rest.onMouseEnter = () => setIsHover(true)
60
+ rest.onMouseLeave = () => setIsHover(false)
61
+
62
+ for (const key in props) {
63
+ if (consumed.has(key)) continue
64
+ const keyu = key as keyof typeof units
65
+ const keyp = key as Passthrough
66
+ if (keyu in units) style[units[keyu]] = maybePx(props[keyu] as string | number)
67
+ else if (passthrough.includes(keyp)) style[keyp as keyof CSSProperties] = props[keyp] as never
68
+ else rest[key] = props[key]
69
+ }
70
+
71
+ if (props.grow) style.flex = 1, style.minHeight = 0
72
+ if (props.row) style.display = 'flex', style.flexDirection = 'row'
73
+ if (props.col) style.display = 'flex', style.flexDirection = 'column'
74
+
75
+ for (const word of (props.align ?? '').split(' ')) {
76
+ if (word === 'between') style.justifyContent = 'space-between'
77
+ const x = props.row ? 'justifyContent' : 'alignItems', y = props.row ? 'alignItems' : 'justifyContent'
78
+ if (word === 'left') style[x] = 'flex-start'
79
+ if (word === 'center') style[x] = 'center'
80
+ if (word === 'right') style[x] = 'flex-end'
81
+ if (word === 'top') style[y] = 'flex-start'
82
+ if (word === 'mid') style[y] = 'center'
83
+ if (word === 'bot') style[y] = 'flex-end'
84
+ }
85
+
86
+ if (isHover && props.hover) Object.assign(style, props.hover)
87
+
88
+ return <div style={style} {...rest}>{props.children as ReactNode}</div>
89
+ }
package/lib.tsx ADDED
@@ -0,0 +1,123 @@
1
+ import React from 'react'
2
+ import { Core, type Props } from "./core"
3
+ import { useEffect, useRef, type ReactNode } from "react"
4
+ import Markdown from 'marked-react'
5
+ import { Prism } from 'react-syntax-highlighter'
6
+
7
+ export const D = (props: Props) => <Core gap={8} {...props} />
8
+
9
+ export const A = (props: Props & { href: string }) => <D {...props} />
10
+
11
+ export const App = (props: Props) =>
12
+ <D col wd='100vw' p={32} gap={16} minHt='100vh' background='#111' color='#eee' fontFamily='Inter, sans-serif' align='center mid' {...props}>{props.children}</D>
13
+
14
+ export const Card = (props: Props) =>
15
+ <D col borderRadius={4} border='1px solid #333' background='#222' gap={16} p={16} {...props} />
16
+
17
+ export const Btn = ({ purple, sm, ...props }: Props & { purple?: boolean, sm?: boolean }) => {
18
+ if (sm) props.gap = 6, props.p = '4px 12px', props.fontSize = 13
19
+ else props.gap = 8, props.p = '8px 16px', props.fontSize = 14
20
+ props.border = '1px solid ' + (purple ? '#63a' : '#444')
21
+ props.background = purple ? '#63a' : '#333'
22
+ props.hover = { background: purple ? '#74b' : '#444', border: '1px solid ' + (purple ? '#74b' : '#444') } as Props
23
+ return <D row borderRadius={4} cursor='pointer' align='center mid' transition='background 0.15s, border-color 0.15s' {...props} />
24
+ }
25
+
26
+ export const Chip = ({ ...props }: Props) =>
27
+ <D row align='center mid' p='4px 10px' borderRadius={999}
28
+ border='1px solid #3f3f46' background='#222' fontSize={13}
29
+ cursor='pointer' userSelect='none' transition='background 0.15s'
30
+ hover={{ background: '#333' } as Props} {...props} />
31
+
32
+ export const Code = ({ lang, children }: Props & { lang: string }) =>
33
+ <Prism language={lang}>{children as string}</Prism>
34
+
35
+ export const Dropzone = ({ onFiles, accept, multiple, dashed, ...props }:
36
+ Props & { onFiles?: (f: File[]) => void, accept?: string, multiple?: boolean, dashed?: boolean }) => {
37
+ const ref = useRef<HTMLInputElement>(null)
38
+ const push = (l: FileList | null) => { if (l?.length) onFiles?.(Array.from(l)) }
39
+ return <D cursor='pointer' transition='border-color 0.2s' border={dashed ? '2px dashed #3f3f46' : undefined} borderRadius={dashed ? 6 : undefined}
40
+ hover={dashed ? { border: '2px dashed #6633aa' } : {}} click={() => ref.current?.click()} {...props}>
41
+ <input ref={ref} type='file' hidden accept={accept} multiple={multiple} onChange={e => push(e.target.files)} />
42
+ {props.children}
43
+ </D>
44
+ }
45
+
46
+ export const Grid = ({ cols, ...props }: Props & { cols?: number }) =>
47
+ <D display='grid' gridTemplateColumns={`repeat(${cols || 2}, 1fr)`} gap={16} {...props} />
48
+
49
+ export const Hr = ({ vertical: vert, ...props }: Props & { vertical?: boolean }) =>
50
+ vert ? <D alignSelf='stretch' borderLeft='1px solid #444' wd='1.5px' {...props} />
51
+ : <D wd='100%' borderTop='1px solid #444' {...props} />
52
+
53
+ export const Input = ({ state: [x, setX], ...props }: Props & { state: [string, (v: string) => void] }) => {
54
+ const ref = useRef<HTMLDivElement>(null)
55
+ useEffect(() => { if (ref.current && document.activeElement != ref.current) ref.current.textContent = x }, [x])
56
+ return <Core p='8px 12px' borderRadius={4} border='1px solid #333' background='#111' color='#eee' fontSize={14} outline='none' minHt='1em' {...props} ref={ref} contentEditable input={setX} />
57
+ }
58
+
59
+ export const Md = (props: Props) =>
60
+ <D {...props}><Markdown>{props.children as string}</Markdown></D>
61
+
62
+ export const Modal = ({ open, onClose, bare, ...props }: Props & { open: boolean, onClose?: () => void, bare?: boolean }) =>
63
+ open && <D position='fixed' inset='0' background={bare ? 'transparent' : '#0004'} zIndex={50} wd='100vw' ht='100%' row align='center mid' click={() => onClose?.()} {...(bare ? props : {})}>
64
+ {bare ? props.children : <Card p='24px' {...props}>{props.children}</Card>}
65
+ </D>
66
+
67
+ export const Muted = (props: Props) =>
68
+ <D color='#71717a' fontSize={13} {...props} />
69
+
70
+ export const Popover = ({ text, ...props }: Props & { text: ReactNode }) =>
71
+ <D position='relative' display='inline-flex' {...props}>
72
+ {props.children}
73
+ {props.hover && <D position='absolute' top='100%' left='0' zIndex={20} p='6px 0 0'>
74
+ <Card p='8px 12px' maxWd='260px' fontSize={13}>{text}</Card>
75
+ </D>}
76
+ </D>
77
+
78
+ export const Progress = ({ dot, value, ht = 6 }: { ht?: number, value: number | boolean, dot?: number }) =>
79
+ <D ht={ht} wd={dot ? ht : '100%'} borderRadius={ht} background='#222' overflow='hidden'>
80
+ <D ht='100%' borderRadius={ht} background='#63a' transition='width 0.3s'
81
+ wd={`${Math.max(0, Math.min(1, Number(value))) * 100}%`} />
82
+ </D>
83
+
84
+ export const Scroll = (props: Props) =>
85
+ <D grow overflow='auto' minHt='0' {...props} />
86
+
87
+ export const Select = <T,>({ state: [x, setX], options, ...props }: Props & { options: Record<string, T>, state: [T, (v: T) => void] }) =>
88
+ <select value={String(x)} onChange={e => { const entry = Object.entries(options).find(([, v]) => String(v) === e.target.value); if (entry) setX(entry[1] as T) }} style={{
89
+ background: '#111', color: '#eee', border: '1px solid #333',
90
+ borderRadius: 4, padding: '6px 10px', fontSize: 14, outline: 'none', cursor: 'pointer',
91
+ }}>
92
+ {Object.entries(options).map(([k, v]) => <option key={k} value={String(v)}>{k}</option>)}
93
+ </select>
94
+
95
+ export const Tab = ({ active, click, ...props }: Props & { active?: boolean, click?: () => void }) =>
96
+ <D p='6px 14px' borderRadius={4} cursor='pointer' fontSize={14}
97
+ userSelect='none' transition='all 0.15s' click={click}
98
+ background={active ? '#222' : 'transparent'}
99
+ border={active ? '1px solid #333' : '1px solid transparent'}
100
+ hover={!active ? { background: '#111' } as Props : undefined}
101
+ {...props} />
102
+
103
+ export const Tabs = ({ state: [active, setActive], list, ...props }: Props & {
104
+ state: [string, (v: string) => void], list: string[]
105
+ }) =>
106
+ <D row gap={4} p='4px' border='1px solid #222' {...props}>
107
+ {list.map(t => <Btn purple={active == t} click={() => setActive(t)}>{t}</Btn>)}
108
+ </D>
109
+
110
+ export const Textarea = ({ state: [x, setX] = ['', () => { }], ...props }: Props & { state?: [string, (v: string) => void] }) =>
111
+ <textarea value={x} onChange={e => setX(e.target.value)}
112
+ style={{
113
+ background: '#111', color: '#eee', border: '1px solid #333',
114
+ borderRadius: 4, padding: '8px 12px', fontSize: 14, outline: 'none',
115
+ resize: 'vertical', fontFamily: 'inherit', lineHeight: 1.6
116
+ }} />
117
+
118
+ export const H1 = (props: Props) => <D fontWeight={800} fontSize={32} {...props} />
119
+ export const H2 = (props: Props) => <D fontWeight={800} fontSize={24} {...props} />
120
+ export const H3 = (props: Props) => <D fontWeight={700} fontSize={20} {...props} />
121
+ export const H4 = (props: Props) => <D fontWeight={700} fontSize={18} {...props} />
122
+ export const H5 = (props: Props) => <D fontWeight={700} fontSize={16} {...props} />
123
+ export const H6 = (props: Props) => <D fontWeight={700} fontSize={14} {...props} />
package/package.json CHANGED
@@ -1,48 +1,26 @@
1
1
  {
2
2
  "name": "b44ui",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
- "sideEffects": [
6
- "./index.js",
7
- "./dist/styles.css"
8
- ],
9
- "files": [
10
- "index.js",
11
- "dist",
12
- "tailwind.css"
13
- ],
14
- "scripts": {
15
- "build": "npm run build:js && npm run build:css",
16
- "build:css": "node scripts/build-css.mjs",
17
- "build:js": "tsc -p tsconfig.json",
18
- "dev": "vite",
19
- "prepare": "npm run build",
20
- "prepublishOnly": "npm run build"
21
- },
22
5
  "exports": {
23
- ".": {
24
- "types": "./dist/index.d.ts",
25
- "import": "./index.js"
26
- },
27
- "./tailwind.css": "./tailwind.css",
28
- "./styles.css": "./dist/styles.css"
29
- },
30
- "dependencies": {
31
- "@vitejs/plugin-react": "^6.0.1",
32
- "clsx": "^2.1.1",
33
- "highlight.js": "^11.11.0",
34
- "marked": "^17.0.0",
35
- "marked-highlight": "^2.2.3",
36
- "tailwind-merge": "^3.0.0",
37
- "vite": "^8.0.2"
6
+ ".": "./lib.tsx",
7
+ "./server": "./server.tsx"
38
8
  },
39
9
  "peerDependencies": {
40
- "react": "^19.0.0"
10
+ "react": ">=19",
11
+ "react-dom": ">=19"
12
+ },
13
+ "dependencies": {
14
+ "esbuild": "^0.28.0",
15
+ "marked": "^18.0.3",
16
+ "marked-react": "^4.0.0",
17
+ "react-syntax-highlighter": "^16.1.1"
41
18
  },
42
19
  "devDependencies": {
20
+ "@types/node": "^25.6.2",
43
21
  "@types/react": "^19.2.14",
44
22
  "@types/react-dom": "^19.2.3",
45
- "react-dom": "^19.2.4",
46
- "tailwindcss": "^4.0.0"
23
+ "@types/react-syntax-highlighter": "^15.5.13",
24
+ "typescript": "^6.0.3"
47
25
  }
48
26
  }
package/readme.md CHANGED
@@ -1,64 +1,18 @@
1
- # b44ui
1
+ # b4ui
2
2
 
3
- minimal dark-mode react components, via tailwind
3
+ nice react components. also bundles a server cause why not
4
4
 
5
- ```bash
6
- npm add b44ui
5
+ ```tsx
6
+ import { Card, React, server } from "./more"
7
+ server(() => <Card>hello world</Card>)
7
8
  ```
8
9
 
9
- ```css
10
- @import "b44ui/tailwind.css";
11
- ```
12
-
13
- if your app already has `@import "tailwindcss";`, replace that line with the one above.
10
+ # components
14
11
 
15
- the host app still needs tailwind v4 processing. with vite:
12
+ ### D
16
13
 
17
- ```ts
18
- import { defineConfig } from "vite"
19
- import react from "@vitejs/plugin-react"
20
- import tailwindcss from "@tailwindcss/vite"
14
+ the div replacement. supports a bunch of shorthands:
21
15
 
22
- export default defineConfig({
23
- plugins: [react(), tailwindcss()],
24
- })
16
+ ```jsx
17
+ <D row wd="50%"></D>
25
18
  ```
26
-
27
- shared `d` props on `d`-based components:
28
-
29
- - layout: `grow`, `gap`, `p`, `wd`, `ht`, `row`, `col`, `align`
30
- - styling: `cn`
31
- - dom: native rest props like `style`, `onClick`, `data-*`
32
-
33
- `ratio` is only on `D`.
34
-
35
- | Component | Extra Props | Description |
36
- |-----------|-------------|-------------|
37
- | `App` | `center`, `width`, `htScreen` | Root layout, dark background, wraps strings in `Md` |
38
- | `Centered` | `width` | Centered max-width container |
39
- | `D` | `ratio` | Plain div, also handles row/col layout |
40
- | `H1` `H2` `H3` `H4` `B` `I` | none | Semantic html tags with shared `D` props |
41
- | `Scroll` | `grow=true` | Vertical scroll container for growable layouts |
42
- | `Code` | `highlight` | Code block |
43
- | `Grid` | `cols` | CSS grid, defaults to one column per child |
44
- | `Card` | none | Bordered zinc-900 card, column by default |
45
- | `Block` | `label`, `dashed` | Padded container with optional label |
46
- | `BlockSm` | `dashed` | Smaller padded container |
47
- | `TabList` | none | Simple tab row wrapper |
48
- | `Tab` | `title`, `active`, `click` | String-first tab primitive |
49
- | `Hr` | `vertical`, `color` | Horizontal or vertical divider |
50
- | `Progress` | `value`, `color`, `dot` | Progress bar or dot |
51
- | `Dropzone` | `onFiles`, `multiple`, `accept` | Hidden file input wrapper for click/drop |
52
- | `Btn` | `click`, `color`, `ghost`, `sm` | Button with optional layout props from `D` |
53
- | `A` | `href`, `click` | Link-styled anchor, works with `onClick` or `href` |
54
- | `Chip` | `click` | Small inline badge, clickable if `click` provided |
55
- | `Tint` | `color` | Tinted background block |
56
- | `Muted` | none | Small muted text |
57
- | `Input` | `state`, native input attrs | Styled text input, `state={[value, setValue]}` supported |
58
- | `Textarea` | native textarea attrs | Styled textarea |
59
- | `Select` | native select attrs | Styled select |
60
- | `Modal` | `open` | Fixed overlay modal |
61
- | `Popover` | `text`, `color` | Hover popover |
62
- | `Md` | `className` | Markdown renderer with syntax highlighting |
63
-
64
- `Color` = `red | blue | orange | purple | yellow | green`
package/server.tsx ADDED
@@ -0,0 +1,50 @@
1
+ import { createServer } from 'node:http'
2
+ import { build } from 'esbuild'
3
+ import { writeFileSync } from 'node:fs'
4
+ import { resolve } from 'node:path'
5
+ import type { ReactNode } from 'react'
6
+ import { App } from './lib'
7
+
8
+ export const server = async (Component: () => ReactNode, port = 6767, Wrapper = App) => {
9
+ const serverPath = resolve('./server')
10
+
11
+ writeFileSync(resolve('./.shim.tsx'), `
12
+ import React, { createElement } from 'react'
13
+ import { createRoot } from 'react-dom/client'
14
+ import { App } from './lib'
15
+
16
+ export const server = (C: () => React.ReactNode, p?: number, W: any = App) =>
17
+ createRoot(document.getElementById('root')!).render(createElement(W, null, createElement(C)))
18
+ `)
19
+
20
+ const shim = {
21
+ name: 'shim',
22
+ setup(build: any) {
23
+ build.onResolve({ filter: /.*/ }, (args: any) => {
24
+ const abs = resolve(args.resolveDir, args.path)
25
+ if (abs === serverPath || abs === serverPath + '.tsx')
26
+ return { path: resolve('./.shim.tsx') }
27
+ })
28
+ },
29
+ }
30
+
31
+ const result = await build({ entryPoints: [resolve(process.argv[1])], bundle: true, write: false, platform: 'browser', plugins: [shim] })
32
+
33
+ const clientJs = result.outputFiles[0].text
34
+ const html = `<html lang="en">
35
+ <head>
36
+ <meta charset="UTF-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38
+ <style>* { box-sizing: border-box; } body { margin: 0; padding: 0; }</style>
39
+ </head>
40
+ <body>
41
+ <div id="root"></div>
42
+ <script type="module">${clientJs}</script>
43
+ </body>
44
+ </html>`
45
+
46
+ createServer((req, res) => {
47
+ res.writeHead(200, { 'Content-Type': 'text/html' })
48
+ res.end(html)
49
+ }).listen(port, () => console.log(`listening on ${port}`))
50
+ }
package/showcase.tsx ADDED
@@ -0,0 +1,126 @@
1
+ import {
2
+ D, App, Card, Grid, Hr, Btn, Chip, Tabs, H1, H2, H3, H4, H5, H6, Muted,
3
+ Input, Textarea, Select, Progress, Modal, Dropzone
4
+ } from "./lib"
5
+ import { server } from "./server"
6
+ import React from "react"
7
+
8
+ server(() => {
9
+ const [tab, setTab] = React.useState('buttons')
10
+ const [inputVal, setInputVal] = React.useState('editable text')
11
+ const [textarea, setTextarea] = React.useState('some notes here')
12
+ const [select, setSelect] = React.useState(2)
13
+ const [progress, setProgress] = React.useState(0.6)
14
+ const [modal, setModal] = React.useState(false)
15
+ const [dropped, setDropped] = React.useState<string[]>([])
16
+ const [count, setCount] = React.useState(0)
17
+
18
+ return <App align='top' wd='50%'>
19
+ <H1>b44ui</H1>
20
+ <D color='#888'>component showcase</D>
21
+
22
+ <Hr />
23
+
24
+ <D color='#888'>typography</D>
25
+ <H1>heading no. 1</H1>
26
+ <H2>heading no. 2</H2>
27
+ <H3>heading no. 3</H3>
28
+ <H4>heading no. 4</H4>
29
+ <H5>heading no. 5</H5>
30
+ <H6>heading no. 6</H6>
31
+ <Muted>also, muted</Muted>
32
+
33
+ <Hr />
34
+
35
+ <D color='#888'>buttons</D>
36
+ <D row>
37
+ <Btn click={() => setCount(c => c + 1)}>default</Btn>
38
+ <Btn purple click={() => setCount(c => c + 1)}>purple</Btn>
39
+ <Btn sm click={() => setCount(c => c + 1)}>small</Btn>
40
+ <Btn sm purple click={() => setCount(c => c + 1)}>small purple</Btn>
41
+ </D>
42
+ <Muted>clicks: {count}</Muted>
43
+
44
+ <Hr />
45
+
46
+ <D color='#888'>chips</D>
47
+ <D row>
48
+ <Chip>design</Chip>
49
+ <Chip>react</Chip>
50
+ <Chip>typescript</Chip>
51
+ <Chip>ui library</Chip>
52
+ </D>
53
+
54
+ <Hr />
55
+
56
+ <D color='#888'>tabs</D>
57
+ <Tabs state={[tab, setTab]} list={['buttons', 'inputs', 'layout']} />
58
+ <Card>
59
+ {tab === 'buttons' && <Muted>Buttons tab content</Muted>}
60
+ {tab === 'inputs' && <Muted>Inputs tab content</Muted>}
61
+ {tab === 'layout' && <Muted>Layout tab content</Muted>}
62
+ </Card>
63
+
64
+ <Hr />
65
+
66
+ <D color='#888'>input / textarea / select</D>
67
+ <D col>
68
+ <Muted>Input</Muted>
69
+ <Input state={[inputVal, setInputVal]} />
70
+ </D>
71
+ <D col>
72
+ <Muted>Textarea</Muted>
73
+ <Textarea state={[textarea, setTextarea]} />
74
+ </D>
75
+ <D col>
76
+ <Muted>Select</Muted>
77
+ <Select state={[select, setSelect]} options={{ 'use 1': 1, 'use 2': 2, 'use 3': 3 }} />
78
+ <Muted>Selected: {select}</Muted>
79
+ </D>
80
+
81
+ <Hr />
82
+
83
+ <D color='#888'>progress</D>
84
+ <Progress value={progress} />
85
+ <D row >
86
+ <Btn sm click={() => setProgress(p => Math.max(0, p - 0.1))}>−10%</Btn>
87
+ <Btn sm click={() => setProgress(p => Math.min(1, p + 0.1))}>+10%</Btn>
88
+ <Muted>{Math.round(progress * 100)}%</Muted>
89
+ </D>
90
+
91
+ <Hr />
92
+
93
+ <D color='#888'>grid</D>
94
+ <Grid cols={3} gap={16}>
95
+ {['one', 'two', 'three', 'four', 'five', 'six'].map(n => <Card key={n}>{n}</Card>)}
96
+ </Grid>
97
+
98
+ <Hr />
99
+
100
+ <D color='#888'>dropzone</D>
101
+ <Dropzone dashed onFiles={files => setDropped(files.map(f => f.name))}
102
+ p={32} borderRadius={6} align='center mid' col >
103
+ <Muted>Drop files here or click to upload</Muted>
104
+ {dropped.length > 0 && dropped.map(n => <Chip key={n}>{n}</Chip>)}
105
+ </Dropzone>
106
+
107
+ <Hr />
108
+
109
+ <D color='#888'>modal</D>
110
+ <Btn click={() => setModal(true)}>Open Modal</Btn>
111
+ <Modal open={modal} onClose={() => setModal(false)} gap={16}>
112
+ <H3>my cool modal</H3>
113
+ <Muted>you can write things here if you want</Muted>
114
+ <Btn click={() => setModal(false)}>nice</Btn>
115
+ </Modal>
116
+
117
+ <Hr />
118
+
119
+ <D color='#888'>vertical hr</D>
120
+ <D row ht={40} gap={16} align='left mid'>
121
+ <Muted>left</Muted>
122
+ <Hr vertical />
123
+ <Muted>right</Muted>
124
+ </D>
125
+ </App>
126
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "types": ["node"],
10
+ "lib": ["esnext", "dom"]
11
+ },
12
+ "include": ["**/*.tsx", "**/*.ts"],
13
+ "exclude": ["node_modules"]
14
+ }
package/dist/example.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/example.js DELETED
@@ -1,9 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { App, Card, D, Input, Scroll } from './index';
4
- const Example = () => {
5
- let [text, setText] = useState('');
6
- return _jsxs(App, { htScreen: true, children: [_jsxs(Card, { row: true, children: [_jsx("h1", { children: "b44ui" }), _jsx(Input, { state: [text, setText], placeholder: 'input supports useState natively' })] }), _jsx(Card, { grow: true, children: _jsx(Scroll, { children: Array.from({ length: 24 }, (_, i) => _jsxs(D, { children: ["item ", i + 1] }, i)) }) })] });
7
- };
8
- import { createRoot } from 'react-dom/client';
9
- createRoot(document.getElementById('root')).render(_jsx(Example, {}));