@m1kapp/ui 0.1.3 → 0.1.5
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 +327 -91
- package/dist/index.d.mts +155 -29
- package/dist/index.d.ts +155 -29
- package/dist/index.js +11 -4
- package/dist/index.mjs +11 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# @m1kapp/ui
|
|
2
2
|
|
|
3
3
|
> Mobile-first app shell for side projects.
|
|
4
|
-
> Build apps that feel
|
|
4
|
+
> Build apps that feel native — in minutes.
|
|
5
5
|
|
|
6
6
|
```
|
|
7
7
|
┌─────────────────────┐
|
|
8
|
-
│ Header (sticky) │
|
|
8
|
+
│ Header (sticky) │ ← AppShellHeader
|
|
9
9
|
├─────────────────────┤
|
|
10
10
|
│ │
|
|
11
|
-
│ Content (scroll) │
|
|
11
|
+
│ Content (scroll) │ ← AppShellContent
|
|
12
12
|
│ │
|
|
13
13
|
├─────────────────────┤
|
|
14
|
-
│ TabBar (sticky) │
|
|
14
|
+
│ TabBar (sticky) │ ← TabBar + Tab
|
|
15
15
|
└─────────────────────┘
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
rounded · shadow · floating on color
|
|
17
|
+
↑
|
|
18
|
+
Watermark (full-screen colored bg)
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
## Install
|
|
@@ -23,130 +24,365 @@
|
|
|
23
24
|
npm install @m1kapp/ui
|
|
24
25
|
```
|
|
25
26
|
|
|
27
|
+
**Requirements:** React 18+, Tailwind CSS 4+
|
|
28
|
+
|
|
29
|
+
Add the font (optional but recommended):
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<!-- index.html -->
|
|
33
|
+
<link rel="stylesheet" href="https://static.toss.im/dist/tps.css" />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// main.tsx
|
|
38
|
+
import { fontFamily } from "@m1kapp/ui";
|
|
39
|
+
document.body.style.fontFamily = fontFamily.toss;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
26
44
|
## Quick Start
|
|
27
45
|
|
|
28
46
|
```tsx
|
|
47
|
+
import { useState } from "react";
|
|
29
48
|
import {
|
|
30
|
-
Watermark,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
AppShellContent,
|
|
34
|
-
TabBar,
|
|
35
|
-
Tab,
|
|
36
|
-
Section,
|
|
37
|
-
SectionHeader,
|
|
38
|
-
Divider,
|
|
39
|
-
StatChip,
|
|
40
|
-
EmptyState,
|
|
49
|
+
Watermark, AppShell, AppShellHeader, AppShellContent,
|
|
50
|
+
TabBar, Tab, Section, StatChip, ThemeButton, ThemeDialog,
|
|
51
|
+
colors,
|
|
41
52
|
} from "@m1kapp/ui";
|
|
42
53
|
|
|
43
54
|
export default function App() {
|
|
44
55
|
const [tab, setTab] = useState("home");
|
|
56
|
+
const [themeColor, setThemeColor] = useState(colors.blue);
|
|
57
|
+
const [themeOpen, setThemeOpen] = useState(false);
|
|
58
|
+
const [dark, setDark] = useState(false);
|
|
45
59
|
|
|
46
60
|
return (
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
61
|
+
<>
|
|
62
|
+
<Watermark color={themeColor} text="myapp">
|
|
63
|
+
<AppShell>
|
|
64
|
+
<AppShellHeader>
|
|
65
|
+
<span className="text-xl font-black">myapp</span>
|
|
66
|
+
<ThemeButton
|
|
67
|
+
color={themeColor}
|
|
68
|
+
dark={dark}
|
|
69
|
+
onClick={() => setThemeOpen(true)}
|
|
70
|
+
/>
|
|
71
|
+
</AppShellHeader>
|
|
72
|
+
|
|
73
|
+
<AppShellContent>
|
|
74
|
+
<Section>
|
|
75
|
+
<StatChip label="Users" value={128} />
|
|
76
|
+
</Section>
|
|
77
|
+
</AppShellContent>
|
|
78
|
+
|
|
79
|
+
<TabBar>
|
|
80
|
+
<Tab
|
|
81
|
+
active={tab === "home"}
|
|
82
|
+
onClick={() => setTab("home")}
|
|
83
|
+
label="Home"
|
|
84
|
+
activeColor={themeColor}
|
|
85
|
+
icon={<HomeIcon />}
|
|
86
|
+
/>
|
|
87
|
+
</TabBar>
|
|
88
|
+
</AppShell>
|
|
89
|
+
</Watermark>
|
|
90
|
+
|
|
91
|
+
<ThemeDialog
|
|
92
|
+
open={themeOpen}
|
|
93
|
+
onClose={() => setThemeOpen(false)}
|
|
94
|
+
current={themeColor}
|
|
95
|
+
onSelect={setThemeColor}
|
|
96
|
+
dark={dark}
|
|
97
|
+
onDarkToggle={() => {
|
|
98
|
+
setDark((v) => !v);
|
|
99
|
+
document.documentElement.classList.toggle("dark");
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
</>
|
|
82
103
|
);
|
|
83
104
|
}
|
|
84
105
|
```
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Layout
|
|
107
|
+
---
|
|
89
108
|
|
|
90
|
-
|
|
91
|
-
|-----------|-------------|
|
|
92
|
-
| `Watermark` | Full-screen colored background with text pattern. The "floating app" look. |
|
|
93
|
-
| `AppShell` | Mobile app container (rounded, shadow, ring) |
|
|
94
|
-
| `AppShellHeader` | Sticky top header with blur backdrop |
|
|
95
|
-
| `AppShellContent` | Scrollable main content area |
|
|
96
|
-
| `TabBar` | Sticky bottom navigation |
|
|
97
|
-
| `Tab` | Individual tab button |
|
|
98
|
-
|
|
99
|
-
### Content
|
|
109
|
+
## Components
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
|-----------|-------------|
|
|
103
|
-
| `Section` | Padded content section (px-4) |
|
|
104
|
-
| `SectionHeader` | Small uppercase section title |
|
|
105
|
-
| `Divider` | Horizontal separator line |
|
|
106
|
-
| `StatChip` | Compact stat display (label + number) |
|
|
107
|
-
| `EmptyState` | Placeholder with icon and message |
|
|
111
|
+
### `Watermark`
|
|
108
112
|
|
|
109
|
-
|
|
113
|
+
Full-screen colored background with a repeating text pattern. The "floating app" aesthetic.
|
|
110
114
|
|
|
111
|
-
|
|
115
|
+
```tsx
|
|
116
|
+
<Watermark
|
|
117
|
+
color="#3b82f6" // background color
|
|
118
|
+
text="myapp" // repeating watermark text
|
|
119
|
+
speed={20} // animation speed in seconds. 0 = static
|
|
120
|
+
sponsor={{ name: "@m1kapp/ui", url: "https://github.com/m1kapp/ui" }}
|
|
121
|
+
>
|
|
122
|
+
<AppShell>...</AppShell>
|
|
123
|
+
</Watermark>
|
|
124
|
+
```
|
|
112
125
|
|
|
113
126
|
| Prop | Type | Default | Description |
|
|
114
127
|
|------|------|---------|-------------|
|
|
115
128
|
| `color` | `string` | `#0f172a` | Background color |
|
|
116
|
-
| `text` | `string` | `m1k` | Watermark pattern text |
|
|
129
|
+
| `text` | `string` | `"m1k"` | Watermark pattern text |
|
|
117
130
|
| `maxWidth` | `number` | `430` | Max width of content area |
|
|
118
|
-
| `padding` | `number` | `12` | Padding around the shell |
|
|
131
|
+
| `padding` | `number` | `12` | Padding around the shell (px) |
|
|
132
|
+
| `speed` | `number` | `20` | Drift animation speed (s). `0` = no animation |
|
|
133
|
+
| `sponsor` | `WatermarkSponsor` | — | 1k milestone sponsor — name shown interleaved with watermark text; entire bg becomes a link |
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
interface WatermarkSponsor {
|
|
137
|
+
name: string; // shown in background
|
|
138
|
+
url: string; // entire background becomes a clickable link
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
119
143
|
|
|
120
144
|
### `AppShell`
|
|
121
145
|
|
|
122
|
-
|
|
123
|
-
|------|------|---------|-------------|
|
|
124
|
-
| `maxWidth` | `number` | `430` | Max width |
|
|
125
|
-
| `className` | `string` | | Additional classes |
|
|
146
|
+
Mobile app container — rounded corners, drop shadow, ring. Centers within `Watermark`.
|
|
126
147
|
|
|
127
|
-
|
|
148
|
+
```tsx
|
|
149
|
+
<AppShell maxWidth={430}>
|
|
150
|
+
<AppShellHeader>...</AppShellHeader>
|
|
151
|
+
<AppShellContent>...</AppShellContent>
|
|
152
|
+
<TabBar>...</TabBar>
|
|
153
|
+
</AppShell>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Prop | Type | Default |
|
|
157
|
+
|------|------|---------|
|
|
158
|
+
| `maxWidth` | `number` | `430` |
|
|
159
|
+
| `className` | `string` | — |
|
|
160
|
+
| `style` | `CSSProperties` | — |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### `AppShellHeader`
|
|
165
|
+
|
|
166
|
+
Sticky top bar (h-14) with blur backdrop.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<AppShellHeader>
|
|
170
|
+
<span className="font-black">myapp</span>
|
|
171
|
+
<ThemeButton color={themeColor} dark={dark} onClick={...} />
|
|
172
|
+
</AppShellHeader>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### `AppShellContent`
|
|
178
|
+
|
|
179
|
+
Scrollable area between header and tab bar.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<AppShellContent>
|
|
183
|
+
<Section>...</Section>
|
|
184
|
+
<Divider />
|
|
185
|
+
<Section>...</Section>
|
|
186
|
+
</AppShellContent>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `TabBar` + `Tab`
|
|
192
|
+
|
|
193
|
+
Sticky bottom nav (h-16) with active color support.
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<TabBar>
|
|
197
|
+
<Tab
|
|
198
|
+
active={tab === "home"}
|
|
199
|
+
onClick={() => setTab("home")}
|
|
200
|
+
label="Home"
|
|
201
|
+
icon={<HomeIcon />}
|
|
202
|
+
activeColor={themeColor} // optional, defaults to current text color
|
|
203
|
+
/>
|
|
204
|
+
</TabBar>
|
|
205
|
+
```
|
|
128
206
|
|
|
129
207
|
| Prop | Type | Description |
|
|
130
208
|
|------|------|-------------|
|
|
131
|
-
| `active` | `boolean` |
|
|
132
|
-
| `onClick` | `() => void` |
|
|
133
|
-
| `icon` | `ReactNode` |
|
|
134
|
-
| `label` | `string` |
|
|
209
|
+
| `active` | `boolean` | Selected state |
|
|
210
|
+
| `onClick` | `() => void` | — |
|
|
211
|
+
| `icon` | `ReactNode` | Icon element |
|
|
212
|
+
| `label` | `string` | Label below icon |
|
|
135
213
|
| `activeColor` | `string?` | Color when active |
|
|
136
214
|
|
|
137
|
-
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### `ThemeButton`
|
|
218
|
+
|
|
219
|
+
Single circular button split diagonally — half light/dark indicator, half theme color dot. Put it in the header.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<ThemeButton
|
|
223
|
+
color={themeColor} // theme color (bottom-right half)
|
|
224
|
+
dark={dark} // light mode = white half, dark mode = black half
|
|
225
|
+
onClick={() => setThemeOpen(true)}
|
|
226
|
+
/>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### `ThemeDialog`
|
|
232
|
+
|
|
233
|
+
Bottom-sheet color picker + dark/light mode toggle.
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
<ThemeDialog
|
|
237
|
+
open={themeOpen}
|
|
238
|
+
onClose={() => setThemeOpen(false)}
|
|
239
|
+
current={themeColor}
|
|
240
|
+
onSelect={(color) => setThemeColor(color)}
|
|
241
|
+
dark={dark}
|
|
242
|
+
onDarkToggle={() => {
|
|
243
|
+
setDark((v) => !v);
|
|
244
|
+
document.documentElement.classList.toggle("dark");
|
|
245
|
+
}}
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
| Prop | Type | Description |
|
|
250
|
+
|------|------|-------------|
|
|
251
|
+
| `open` | `boolean` | Show/hide |
|
|
252
|
+
| `onClose` | `() => void` | Called on backdrop click or Escape |
|
|
253
|
+
| `current` | `string` | Currently selected color |
|
|
254
|
+
| `onSelect` | `(color: string) => void` | Called when a color is picked (closes dialog) |
|
|
255
|
+
| `dark` | `boolean?` | Current dark mode state |
|
|
256
|
+
| `onDarkToggle` | `() => void?` | Called when light/dark is toggled |
|
|
257
|
+
| `palette` | `Record<string, string>?` | Override color palette (default: built-in `colors`) |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### `Section` + `SectionHeader`
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
<Section className="pt-5">
|
|
265
|
+
<SectionHeader>Stats</SectionHeader>
|
|
266
|
+
<p>Content with px-4 padding applied.</p>
|
|
267
|
+
</Section>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### `Divider`
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<Divider />
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
### `StatChip`
|
|
281
|
+
|
|
282
|
+
Compact label + number display.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
<div className="flex gap-3">
|
|
286
|
+
<StatChip label="Users" value={1024} />
|
|
287
|
+
<StatChip label="Revenue" value={9800} />
|
|
288
|
+
</div>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### `EmptyState`
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
<EmptyState message="Nothing here yet" />
|
|
297
|
+
|
|
298
|
+
// Custom icon:
|
|
299
|
+
<EmptyState message="No results" icon={<SearchIcon />} />
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### `Typewriter`
|
|
305
|
+
|
|
306
|
+
Human-like typing effect with a blinking cursor.
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
<Typewriter
|
|
310
|
+
words={["side project", "weekend build", "바이브코딩"]}
|
|
311
|
+
color={themeColor}
|
|
312
|
+
speed={80} // ms per character
|
|
313
|
+
pause={2000} // ms between words
|
|
314
|
+
/>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
### `colors`
|
|
320
|
+
|
|
321
|
+
Curated palette. Use with `Watermark`, `Tab`, and `ThemeDialog`.
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import { colors } from "@m1kapp/ui";
|
|
325
|
+
|
|
326
|
+
colors.blue // #3b82f6
|
|
327
|
+
colors.purple // #8b5cf6
|
|
328
|
+
colors.green // #10b981
|
|
329
|
+
colors.orange // #f97316
|
|
330
|
+
colors.pink // #ec4899
|
|
331
|
+
colors.red // #ef4444
|
|
332
|
+
colors.yellow // #eab308
|
|
333
|
+
colors.cyan // #06b6d4
|
|
334
|
+
colors.slate // #0f172a
|
|
335
|
+
colors.zinc // #27272a
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### `fonts` + `fontFamily`
|
|
341
|
+
|
|
342
|
+
CDN font references — no files bundled.
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
import { fonts, fontFamily } from "@m1kapp/ui";
|
|
346
|
+
|
|
347
|
+
// In HTML:
|
|
348
|
+
// <link rel="stylesheet" href={fonts.toss} />
|
|
349
|
+
|
|
350
|
+
// In JS:
|
|
351
|
+
document.body.style.fontFamily = fontFamily.toss;
|
|
352
|
+
|
|
353
|
+
// Available:
|
|
354
|
+
fonts.toss // Toss Product Sans
|
|
355
|
+
fonts.pretendard // Pretendard Variable
|
|
356
|
+
fonts.inter // Inter
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Dark Mode
|
|
362
|
+
|
|
363
|
+
Uses Tailwind's class-based dark mode. Add this to your CSS:
|
|
364
|
+
|
|
365
|
+
```css
|
|
366
|
+
/* index.css */
|
|
367
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Then toggle with:
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
document.documentElement.classList.toggle("dark", isDark);
|
|
374
|
+
```
|
|
138
375
|
|
|
139
|
-
|
|
140
|
-
- Tailwind CSS 4+
|
|
376
|
+
---
|
|
141
377
|
|
|
142
378
|
## Philosophy
|
|
143
379
|
|
|
144
|
-
In the AI era,
|
|
380
|
+
In the AI era, a side project takes a day to build.
|
|
145
381
|
But making it *feel* like an app still takes effort.
|
|
146
382
|
|
|
147
|
-
`@m1kapp/ui` gives you the
|
|
383
|
+
`@m1kapp/ui` gives you the shell — header, scrollable content, bottom tabs, floating on a colored background — so you can focus on the idea.
|
|
148
384
|
|
|
149
|
-
Built and battle-tested
|
|
385
|
+
Built and battle-tested at [m1k.app](https://m1k.app).
|
|
150
386
|
|
|
151
387
|
## License
|
|
152
388
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
2
3
|
import { ReactNode, CSSProperties } from 'react';
|
|
3
4
|
|
|
4
5
|
interface AppShellProps {
|
|
@@ -93,22 +94,57 @@ interface EmptyStateProps {
|
|
|
93
94
|
*/
|
|
94
95
|
declare function EmptyState({ message, icon }: EmptyStateProps): react_jsx_runtime.JSX.Element;
|
|
95
96
|
|
|
97
|
+
interface WatermarkSponsor {
|
|
98
|
+
/** Service name displayed in the background as clickable text */
|
|
99
|
+
name: string;
|
|
100
|
+
/** URL to navigate to when the sponsor text is clicked */
|
|
101
|
+
url: string;
|
|
102
|
+
}
|
|
96
103
|
interface WatermarkProps {
|
|
97
104
|
children: ReactNode;
|
|
98
|
-
/** Background color */
|
|
105
|
+
/** Background fill color. Accepts any CSS color value. Default: "#0f172a" */
|
|
99
106
|
color?: string;
|
|
100
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Repeating watermark text shown across the background.
|
|
109
|
+
* Default: "m1k"
|
|
110
|
+
*/
|
|
101
111
|
text?: string;
|
|
102
|
-
/** Max width of content area. Default: 430 */
|
|
112
|
+
/** Max width of the center content area in px. Default: 430 */
|
|
103
113
|
maxWidth?: number;
|
|
104
|
-
/** Padding around the app shell. Default:
|
|
114
|
+
/** Padding around the app shell in px. Default: 12 */
|
|
105
115
|
padding?: number;
|
|
116
|
+
/**
|
|
117
|
+
* 1k milestone sponsor slot.
|
|
118
|
+
* The sponsor's name is interleaved with the watermark text across the
|
|
119
|
+
* background. Only the sponsor text is clickable (not the whole background).
|
|
120
|
+
*
|
|
121
|
+
* Font size auto-scales based on text length (14–28px).
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* <Watermark
|
|
125
|
+
* color={colors.blue}
|
|
126
|
+
* sponsor={{ name: "@m1kapp/ui", url: "https://github.com/m1kapp/ui" }}
|
|
127
|
+
* >
|
|
128
|
+
* <AppShell>...</AppShell>
|
|
129
|
+
* </Watermark>
|
|
130
|
+
*/
|
|
131
|
+
sponsor?: WatermarkSponsor;
|
|
132
|
+
/**
|
|
133
|
+
* Background drift animation speed in seconds per cycle.
|
|
134
|
+
* Set to `0` to disable animation (static background).
|
|
135
|
+
* Default: 20
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* <Watermark speed={0} /> // static
|
|
139
|
+
* <Watermark speed={10} /> // faster
|
|
140
|
+
* <Watermark speed={40} /> // very slow
|
|
141
|
+
*/
|
|
142
|
+
speed?: number;
|
|
106
143
|
}
|
|
107
144
|
/**
|
|
108
|
-
* Full-screen colored background with repeating text watermark pattern.
|
|
109
|
-
* Wraps the AppShell and provides the "floating app" look.
|
|
145
|
+
* Full-screen colored background with repeating animated text watermark pattern.
|
|
110
146
|
*/
|
|
111
|
-
declare function Watermark({ children, color, text, maxWidth, padding, }: WatermarkProps): react_jsx_runtime.JSX.Element;
|
|
147
|
+
declare function Watermark({ children, color, text, maxWidth, padding, sponsor, speed, }: WatermarkProps): react_jsx_runtime.JSX.Element;
|
|
112
148
|
|
|
113
149
|
declare const colors: {
|
|
114
150
|
readonly blue: "#3b82f6";
|
|
@@ -126,19 +162,21 @@ type ColorName = keyof typeof colors;
|
|
|
126
162
|
|
|
127
163
|
interface ThemeButtonProps {
|
|
128
164
|
color: string;
|
|
165
|
+
dark?: boolean;
|
|
129
166
|
onClick: () => void;
|
|
130
167
|
className?: string;
|
|
131
168
|
}
|
|
132
169
|
/**
|
|
133
|
-
*
|
|
134
|
-
* Designed for the AppShellHeader top-right slot.
|
|
170
|
+
* Single circular button split diagonally — half dark/light, half theme color.
|
|
135
171
|
*/
|
|
136
|
-
declare function ThemeButton({ color, onClick, className }: ThemeButtonProps): react_jsx_runtime.JSX.Element;
|
|
172
|
+
declare function ThemeButton({ color, dark, onClick, className }: ThemeButtonProps): react_jsx_runtime.JSX.Element;
|
|
137
173
|
interface ThemeDialogProps {
|
|
138
174
|
open: boolean;
|
|
139
175
|
onClose: () => void;
|
|
140
176
|
current: string;
|
|
141
177
|
onSelect: (color: string) => void;
|
|
178
|
+
dark?: boolean;
|
|
179
|
+
onDarkToggle?: () => void;
|
|
142
180
|
/** Override the color palette. Defaults to built-in colors. */
|
|
143
181
|
palette?: Record<string, string>;
|
|
144
182
|
}
|
|
@@ -146,40 +184,128 @@ interface ThemeDialogProps {
|
|
|
146
184
|
* Bottom-sheet style color picker dialog.
|
|
147
185
|
* Shows a 5-column grid of color circles with check on the active one.
|
|
148
186
|
*/
|
|
149
|
-
declare function ThemeDialog({ open, onClose, current, onSelect, palette, }: ThemeDialogProps): react_jsx_runtime.JSX.Element | null;
|
|
187
|
+
declare function ThemeDialog({ open, onClose, current, onSelect, dark, onDarkToggle, palette, }: ThemeDialogProps): react_jsx_runtime.JSX.Element | null;
|
|
150
188
|
|
|
151
189
|
/**
|
|
152
190
|
* Font presets for @m1kapp/ui.
|
|
153
|
-
*
|
|
154
|
-
*
|
|
191
|
+
* CDN links only — no font files bundled.
|
|
192
|
+
*
|
|
193
|
+
* ## Quick setup (recommended)
|
|
194
|
+
*
|
|
195
|
+
* Add to your `index.html` <head>:
|
|
196
|
+
* ```html
|
|
197
|
+
* <link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
|
198
|
+
* <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
|
199
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css" />
|
|
200
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" />
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* Add to your global CSS:
|
|
204
|
+
* ```css
|
|
205
|
+
* html {
|
|
206
|
+
* font-family: "Tossface", "Pretendard Variable", "Pretendard", system-ui, sans-serif;
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* That's it — Tossface handles emojis, Pretendard handles text.
|
|
155
211
|
*/
|
|
156
212
|
declare const fonts: {
|
|
157
|
-
/**
|
|
158
|
-
|
|
159
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Tossface — Toss emoji font (open source, jsDelivr CDN).
|
|
215
|
+
* Renders all emoji with the Toss design style.
|
|
216
|
+
* Add this to get consistent emoji across platforms.
|
|
217
|
+
*/
|
|
218
|
+
readonly tossface: "https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css";
|
|
219
|
+
/**
|
|
220
|
+
* Pretendard — best Korean variable web font (jsDelivr CDN).
|
|
221
|
+
* Recommended for all Korean UI text.
|
|
222
|
+
*/
|
|
160
223
|
readonly pretendard: "https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css";
|
|
161
|
-
/** Inter — clean Latin sans-serif */
|
|
224
|
+
/** Inter — clean Latin sans-serif (Google Fonts) */
|
|
162
225
|
readonly inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap";
|
|
163
226
|
};
|
|
164
227
|
type FontName = keyof typeof fonts;
|
|
165
228
|
/**
|
|
166
|
-
*
|
|
167
|
-
* Usage in your HTML head or via a <link> tag:
|
|
229
|
+
* Recommended font-family stacks.
|
|
168
230
|
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
231
|
+
* @example
|
|
232
|
+
* // In your global CSS:
|
|
233
|
+
* html { font-family: ${fontFamily.default}; }
|
|
172
234
|
*
|
|
173
|
-
* Or in
|
|
174
|
-
*
|
|
175
|
-
* @import url("https://static.toss.im/dist/tps.css");
|
|
176
|
-
* body { font-family: "Toss Product Sans", sans-serif; }
|
|
177
|
-
* ```
|
|
235
|
+
* // Or in JS (e.g. main.tsx):
|
|
236
|
+
* document.documentElement.style.fontFamily = fontFamily.default;
|
|
178
237
|
*/
|
|
179
238
|
declare const fontFamily: {
|
|
180
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Default recommended stack.
|
|
241
|
+
* Tossface for emoji + Pretendard for Korean/Latin text.
|
|
242
|
+
*/
|
|
243
|
+
readonly default: "\"Pretendard Variable\", \"Pretendard\", system-ui, -apple-system, sans-serif, \"Tossface\"";
|
|
244
|
+
/** Pretendard only */
|
|
181
245
|
readonly pretendard: "\"Pretendard Variable\", \"Pretendard\", system-ui, sans-serif";
|
|
246
|
+
/** Inter only */
|
|
182
247
|
readonly inter: "\"Inter\", system-ui, sans-serif";
|
|
183
248
|
};
|
|
184
249
|
|
|
185
|
-
|
|
250
|
+
interface TypewriterProps {
|
|
251
|
+
/** Words to cycle through */
|
|
252
|
+
words: string[];
|
|
253
|
+
/** Text color */
|
|
254
|
+
color?: string;
|
|
255
|
+
/** Typing speed in ms (base, actual varies ±50%). Default: 80 */
|
|
256
|
+
speed?: number;
|
|
257
|
+
/** Delete speed in ms. Default: 30 */
|
|
258
|
+
deleteSpeed?: number;
|
|
259
|
+
/** Pause after typing completes in ms. Default: 2200 */
|
|
260
|
+
pauseMs?: number;
|
|
261
|
+
/** Pause between words in ms. Default: 400 */
|
|
262
|
+
gapMs?: number;
|
|
263
|
+
/** Cursor color. Defaults to `color` prop */
|
|
264
|
+
cursorColor?: string;
|
|
265
|
+
className?: string;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Typewriter effect that cycles through words with human-like timing.
|
|
269
|
+
* Features irregular delays, space pauses, char pop animation, and blinking cursor.
|
|
270
|
+
*/
|
|
271
|
+
declare function Typewriter({ words, color, speed, deleteSpeed, pauseMs, gapMs, cursorColor, className, }: TypewriterProps): react_jsx_runtime.JSX.Element;
|
|
272
|
+
|
|
273
|
+
interface EmojiButtonProps {
|
|
274
|
+
emoji: string;
|
|
275
|
+
onClick: () => void;
|
|
276
|
+
className?: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Small button displaying the selected emoji.
|
|
280
|
+
* Use it anywhere — tab icons, headers, list items, etc.
|
|
281
|
+
*/
|
|
282
|
+
declare function EmojiButton({ emoji, onClick, className }: EmojiButtonProps): react_jsx_runtime.JSX.Element;
|
|
283
|
+
interface EmojiPickerProps {
|
|
284
|
+
open: boolean;
|
|
285
|
+
onClose: () => void;
|
|
286
|
+
current: string;
|
|
287
|
+
onSelect: (emoji: string) => void;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Bottom-sheet emoji picker with categories.
|
|
291
|
+
*/
|
|
292
|
+
declare function EmojiPicker({ open, onClose, current, onSelect }: EmojiPickerProps): react.ReactPortal | null;
|
|
293
|
+
|
|
294
|
+
interface TooltipProps {
|
|
295
|
+
label: string;
|
|
296
|
+
children: ReactNode;
|
|
297
|
+
/** Placement relative to the trigger. Default: "top" */
|
|
298
|
+
placement?: "top" | "bottom";
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Simple tooltip that appears above (or below) the trigger on hover/focus.
|
|
302
|
+
* Renders via portal so it's never clipped by overflow-hidden containers.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* <Tooltip label="복사하기">
|
|
306
|
+
* <button>복사</button>
|
|
307
|
+
* </Tooltip>
|
|
308
|
+
*/
|
|
309
|
+
declare function Tooltip({ label, children, placement }: TooltipProps): react_jsx_runtime.JSX.Element;
|
|
310
|
+
|
|
311
|
+
export { AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, type ColorName, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps, EmptyState, type EmptyStateProps, type FontName, Section, SectionHeader, type SectionHeaderProps, type SectionProps, StatChip, type StatChipProps, Tab, TabBar, type TabBarProps, type TabProps, ThemeButton, type ThemeButtonProps, ThemeDialog, type ThemeDialogProps, Tooltip, type TooltipProps, Typewriter, type TypewriterProps, Watermark, type WatermarkProps, type WatermarkSponsor, colors, fontFamily, fonts };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
2
3
|
import { ReactNode, CSSProperties } from 'react';
|
|
3
4
|
|
|
4
5
|
interface AppShellProps {
|
|
@@ -93,22 +94,57 @@ interface EmptyStateProps {
|
|
|
93
94
|
*/
|
|
94
95
|
declare function EmptyState({ message, icon }: EmptyStateProps): react_jsx_runtime.JSX.Element;
|
|
95
96
|
|
|
97
|
+
interface WatermarkSponsor {
|
|
98
|
+
/** Service name displayed in the background as clickable text */
|
|
99
|
+
name: string;
|
|
100
|
+
/** URL to navigate to when the sponsor text is clicked */
|
|
101
|
+
url: string;
|
|
102
|
+
}
|
|
96
103
|
interface WatermarkProps {
|
|
97
104
|
children: ReactNode;
|
|
98
|
-
/** Background color */
|
|
105
|
+
/** Background fill color. Accepts any CSS color value. Default: "#0f172a" */
|
|
99
106
|
color?: string;
|
|
100
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Repeating watermark text shown across the background.
|
|
109
|
+
* Default: "m1k"
|
|
110
|
+
*/
|
|
101
111
|
text?: string;
|
|
102
|
-
/** Max width of content area. Default: 430 */
|
|
112
|
+
/** Max width of the center content area in px. Default: 430 */
|
|
103
113
|
maxWidth?: number;
|
|
104
|
-
/** Padding around the app shell. Default:
|
|
114
|
+
/** Padding around the app shell in px. Default: 12 */
|
|
105
115
|
padding?: number;
|
|
116
|
+
/**
|
|
117
|
+
* 1k milestone sponsor slot.
|
|
118
|
+
* The sponsor's name is interleaved with the watermark text across the
|
|
119
|
+
* background. Only the sponsor text is clickable (not the whole background).
|
|
120
|
+
*
|
|
121
|
+
* Font size auto-scales based on text length (14–28px).
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* <Watermark
|
|
125
|
+
* color={colors.blue}
|
|
126
|
+
* sponsor={{ name: "@m1kapp/ui", url: "https://github.com/m1kapp/ui" }}
|
|
127
|
+
* >
|
|
128
|
+
* <AppShell>...</AppShell>
|
|
129
|
+
* </Watermark>
|
|
130
|
+
*/
|
|
131
|
+
sponsor?: WatermarkSponsor;
|
|
132
|
+
/**
|
|
133
|
+
* Background drift animation speed in seconds per cycle.
|
|
134
|
+
* Set to `0` to disable animation (static background).
|
|
135
|
+
* Default: 20
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* <Watermark speed={0} /> // static
|
|
139
|
+
* <Watermark speed={10} /> // faster
|
|
140
|
+
* <Watermark speed={40} /> // very slow
|
|
141
|
+
*/
|
|
142
|
+
speed?: number;
|
|
106
143
|
}
|
|
107
144
|
/**
|
|
108
|
-
* Full-screen colored background with repeating text watermark pattern.
|
|
109
|
-
* Wraps the AppShell and provides the "floating app" look.
|
|
145
|
+
* Full-screen colored background with repeating animated text watermark pattern.
|
|
110
146
|
*/
|
|
111
|
-
declare function Watermark({ children, color, text, maxWidth, padding, }: WatermarkProps): react_jsx_runtime.JSX.Element;
|
|
147
|
+
declare function Watermark({ children, color, text, maxWidth, padding, sponsor, speed, }: WatermarkProps): react_jsx_runtime.JSX.Element;
|
|
112
148
|
|
|
113
149
|
declare const colors: {
|
|
114
150
|
readonly blue: "#3b82f6";
|
|
@@ -126,19 +162,21 @@ type ColorName = keyof typeof colors;
|
|
|
126
162
|
|
|
127
163
|
interface ThemeButtonProps {
|
|
128
164
|
color: string;
|
|
165
|
+
dark?: boolean;
|
|
129
166
|
onClick: () => void;
|
|
130
167
|
className?: string;
|
|
131
168
|
}
|
|
132
169
|
/**
|
|
133
|
-
*
|
|
134
|
-
* Designed for the AppShellHeader top-right slot.
|
|
170
|
+
* Single circular button split diagonally — half dark/light, half theme color.
|
|
135
171
|
*/
|
|
136
|
-
declare function ThemeButton({ color, onClick, className }: ThemeButtonProps): react_jsx_runtime.JSX.Element;
|
|
172
|
+
declare function ThemeButton({ color, dark, onClick, className }: ThemeButtonProps): react_jsx_runtime.JSX.Element;
|
|
137
173
|
interface ThemeDialogProps {
|
|
138
174
|
open: boolean;
|
|
139
175
|
onClose: () => void;
|
|
140
176
|
current: string;
|
|
141
177
|
onSelect: (color: string) => void;
|
|
178
|
+
dark?: boolean;
|
|
179
|
+
onDarkToggle?: () => void;
|
|
142
180
|
/** Override the color palette. Defaults to built-in colors. */
|
|
143
181
|
palette?: Record<string, string>;
|
|
144
182
|
}
|
|
@@ -146,40 +184,128 @@ interface ThemeDialogProps {
|
|
|
146
184
|
* Bottom-sheet style color picker dialog.
|
|
147
185
|
* Shows a 5-column grid of color circles with check on the active one.
|
|
148
186
|
*/
|
|
149
|
-
declare function ThemeDialog({ open, onClose, current, onSelect, palette, }: ThemeDialogProps): react_jsx_runtime.JSX.Element | null;
|
|
187
|
+
declare function ThemeDialog({ open, onClose, current, onSelect, dark, onDarkToggle, palette, }: ThemeDialogProps): react_jsx_runtime.JSX.Element | null;
|
|
150
188
|
|
|
151
189
|
/**
|
|
152
190
|
* Font presets for @m1kapp/ui.
|
|
153
|
-
*
|
|
154
|
-
*
|
|
191
|
+
* CDN links only — no font files bundled.
|
|
192
|
+
*
|
|
193
|
+
* ## Quick setup (recommended)
|
|
194
|
+
*
|
|
195
|
+
* Add to your `index.html` <head>:
|
|
196
|
+
* ```html
|
|
197
|
+
* <link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
|
198
|
+
* <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
|
199
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css" />
|
|
200
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" />
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* Add to your global CSS:
|
|
204
|
+
* ```css
|
|
205
|
+
* html {
|
|
206
|
+
* font-family: "Tossface", "Pretendard Variable", "Pretendard", system-ui, sans-serif;
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* That's it — Tossface handles emojis, Pretendard handles text.
|
|
155
211
|
*/
|
|
156
212
|
declare const fonts: {
|
|
157
|
-
/**
|
|
158
|
-
|
|
159
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Tossface — Toss emoji font (open source, jsDelivr CDN).
|
|
215
|
+
* Renders all emoji with the Toss design style.
|
|
216
|
+
* Add this to get consistent emoji across platforms.
|
|
217
|
+
*/
|
|
218
|
+
readonly tossface: "https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css";
|
|
219
|
+
/**
|
|
220
|
+
* Pretendard — best Korean variable web font (jsDelivr CDN).
|
|
221
|
+
* Recommended for all Korean UI text.
|
|
222
|
+
*/
|
|
160
223
|
readonly pretendard: "https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css";
|
|
161
|
-
/** Inter — clean Latin sans-serif */
|
|
224
|
+
/** Inter — clean Latin sans-serif (Google Fonts) */
|
|
162
225
|
readonly inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap";
|
|
163
226
|
};
|
|
164
227
|
type FontName = keyof typeof fonts;
|
|
165
228
|
/**
|
|
166
|
-
*
|
|
167
|
-
* Usage in your HTML head or via a <link> tag:
|
|
229
|
+
* Recommended font-family stacks.
|
|
168
230
|
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
231
|
+
* @example
|
|
232
|
+
* // In your global CSS:
|
|
233
|
+
* html { font-family: ${fontFamily.default}; }
|
|
172
234
|
*
|
|
173
|
-
* Or in
|
|
174
|
-
*
|
|
175
|
-
* @import url("https://static.toss.im/dist/tps.css");
|
|
176
|
-
* body { font-family: "Toss Product Sans", sans-serif; }
|
|
177
|
-
* ```
|
|
235
|
+
* // Or in JS (e.g. main.tsx):
|
|
236
|
+
* document.documentElement.style.fontFamily = fontFamily.default;
|
|
178
237
|
*/
|
|
179
238
|
declare const fontFamily: {
|
|
180
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Default recommended stack.
|
|
241
|
+
* Tossface for emoji + Pretendard for Korean/Latin text.
|
|
242
|
+
*/
|
|
243
|
+
readonly default: "\"Pretendard Variable\", \"Pretendard\", system-ui, -apple-system, sans-serif, \"Tossface\"";
|
|
244
|
+
/** Pretendard only */
|
|
181
245
|
readonly pretendard: "\"Pretendard Variable\", \"Pretendard\", system-ui, sans-serif";
|
|
246
|
+
/** Inter only */
|
|
182
247
|
readonly inter: "\"Inter\", system-ui, sans-serif";
|
|
183
248
|
};
|
|
184
249
|
|
|
185
|
-
|
|
250
|
+
interface TypewriterProps {
|
|
251
|
+
/** Words to cycle through */
|
|
252
|
+
words: string[];
|
|
253
|
+
/** Text color */
|
|
254
|
+
color?: string;
|
|
255
|
+
/** Typing speed in ms (base, actual varies ±50%). Default: 80 */
|
|
256
|
+
speed?: number;
|
|
257
|
+
/** Delete speed in ms. Default: 30 */
|
|
258
|
+
deleteSpeed?: number;
|
|
259
|
+
/** Pause after typing completes in ms. Default: 2200 */
|
|
260
|
+
pauseMs?: number;
|
|
261
|
+
/** Pause between words in ms. Default: 400 */
|
|
262
|
+
gapMs?: number;
|
|
263
|
+
/** Cursor color. Defaults to `color` prop */
|
|
264
|
+
cursorColor?: string;
|
|
265
|
+
className?: string;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Typewriter effect that cycles through words with human-like timing.
|
|
269
|
+
* Features irregular delays, space pauses, char pop animation, and blinking cursor.
|
|
270
|
+
*/
|
|
271
|
+
declare function Typewriter({ words, color, speed, deleteSpeed, pauseMs, gapMs, cursorColor, className, }: TypewriterProps): react_jsx_runtime.JSX.Element;
|
|
272
|
+
|
|
273
|
+
interface EmojiButtonProps {
|
|
274
|
+
emoji: string;
|
|
275
|
+
onClick: () => void;
|
|
276
|
+
className?: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Small button displaying the selected emoji.
|
|
280
|
+
* Use it anywhere — tab icons, headers, list items, etc.
|
|
281
|
+
*/
|
|
282
|
+
declare function EmojiButton({ emoji, onClick, className }: EmojiButtonProps): react_jsx_runtime.JSX.Element;
|
|
283
|
+
interface EmojiPickerProps {
|
|
284
|
+
open: boolean;
|
|
285
|
+
onClose: () => void;
|
|
286
|
+
current: string;
|
|
287
|
+
onSelect: (emoji: string) => void;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Bottom-sheet emoji picker with categories.
|
|
291
|
+
*/
|
|
292
|
+
declare function EmojiPicker({ open, onClose, current, onSelect }: EmojiPickerProps): react.ReactPortal | null;
|
|
293
|
+
|
|
294
|
+
interface TooltipProps {
|
|
295
|
+
label: string;
|
|
296
|
+
children: ReactNode;
|
|
297
|
+
/** Placement relative to the trigger. Default: "top" */
|
|
298
|
+
placement?: "top" | "bottom";
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Simple tooltip that appears above (or below) the trigger on hover/focus.
|
|
302
|
+
* Renders via portal so it's never clipped by overflow-hidden containers.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* <Tooltip label="복사하기">
|
|
306
|
+
* <button>복사</button>
|
|
307
|
+
* </Tooltip>
|
|
308
|
+
*/
|
|
309
|
+
declare function Tooltip({ label, children, placement }: TooltipProps): react_jsx_runtime.JSX.Element;
|
|
310
|
+
|
|
311
|
+
export { AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, type ColorName, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps, EmptyState, type EmptyStateProps, type FontName, Section, SectionHeader, type SectionHeaderProps, type SectionProps, StatChip, type StatChipProps, Tab, TabBar, type TabBarProps, type TabProps, ThemeButton, type ThemeButtonProps, ThemeDialog, type ThemeDialogProps, Tooltip, type TooltipProps, Typewriter, type TypewriterProps, Watermark, type WatermarkProps, type WatermarkSponsor, colors, fontFamily, fonts };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
"use strict";var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"use strict";var A=Object.defineProperty;var me=Object.getOwnPropertyDescriptor;var fe=Object.getOwnPropertyNames;var ue=Object.prototype.hasOwnProperty;var xe=(e,t)=>{for(var o in t)A(e,o,{get:t[o],enumerable:!0})},be=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of fe(t))!ue.call(e,a)&&a!==o&&A(e,a,{get:()=>t[a],enumerable:!(n=me(t,a))||n.enumerable});return e};var he=e=>be(A({},"__esModule",{value:!0}),e);var ye={};xe(ye,{AppShell:()=>D,AppShellContent:()=>I,AppShellHeader:()=>H,Divider:()=>K,EmojiButton:()=>ie,EmojiPicker:()=>ae,EmptyState:()=>Z,Section:()=>O,SectionHeader:()=>_,StatChip:()=>V,Tab:()=>Y,TabBar:()=>F,ThemeButton:()=>Q,ThemeDialog:()=>U,Tooltip:()=>ce,Typewriter:()=>oe,Watermark:()=>J,colors:()=>E,fontFamily:()=>te,fonts:()=>ee});module.exports=he(ye);var C=require("react/jsx-runtime");function D({children:e,className:t="",maxWidth:o=430,style:n}){return(0,C.jsx)("div",{className:`w-full h-full flex flex-col bg-white dark:bg-zinc-950 shadow-2xl ring-1 ring-black/10 dark:ring-zinc-700 rounded-2xl overflow-hidden ${t}`,style:{maxWidth:o,...n},children:e})}function H({children:e,className:t=""}){return(0,C.jsx)("header",{className:`sticky top-0 z-20 h-14 px-4 flex items-center justify-between border-b border-zinc-100 dark:border-zinc-800 bg-white/90 dark:bg-zinc-950/90 backdrop-blur-md rounded-t-2xl ${t}`,children:e})}function I({children:e,className:t=""}){return(0,C.jsx)("div",{className:`flex-1 overflow-y-auto scrollbar-hide ${t}`,children:e})}var P=require("react/jsx-runtime");function F({children:e,className:t=""}){return(0,P.jsx)("nav",{className:`sticky bottom-0 z-20 h-16 border-t border-zinc-200 dark:border-zinc-800 flex bg-white/90 dark:bg-zinc-950/90 backdrop-blur-md rounded-b-2xl ${t}`,children:e})}function Y({active:e,onClick:t,icon:o,label:n,activeColor:a}){return(0,P.jsxs)("button",{onClick:t,className:`flex-1 flex flex-col items-center gap-0.5 py-2.5 transition-colors ${e?"":"text-zinc-300 dark:text-zinc-600"}`,style:e?{color:a}:void 0,children:[o,(0,P.jsx)("span",{className:"text-[10px] font-medium",children:n})]})}var W=require("react/jsx-runtime");function O({children:e,className:t=""}){return(0,W.jsx)("section",{className:`px-4 ${t}`,children:e})}function _({children:e}){return(0,W.jsx)("h2",{className:"text-[11px] font-semibold text-zinc-400 dark:text-zinc-500 uppercase tracking-wider mb-3",children:e})}var X=require("react/jsx-runtime");function K({className:e=""}){return(0,X.jsx)("div",{className:`mx-4 my-6 h-px bg-zinc-200 dark:bg-zinc-800 ${e}`})}var S=require("react/jsx-runtime");function V({label:e,value:t,className:o=""}){return(0,S.jsxs)("div",{className:`flex-1 rounded-xl bg-zinc-100 dark:bg-zinc-900 px-3 py-3 text-center ${o}`,children:[(0,S.jsx)("p",{className:"text-[10px] text-zinc-500 dark:text-zinc-400 font-medium mb-0.5",children:e}),(0,S.jsx)("p",{className:"text-lg font-bold tabular-nums text-zinc-900 dark:text-white",children:t.toLocaleString()})]})}var b=require("react/jsx-runtime");function Z({message:e,icon:t}){return(0,b.jsxs)("div",{className:"flex flex-col items-center justify-center py-12 gap-2",children:[t||(0,b.jsxs)("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-zinc-200 dark:text-zinc-700",children:[(0,b.jsx)("circle",{cx:"12",cy:"12",r:"10"}),(0,b.jsx)("path",{d:"M8 15h8"}),(0,b.jsx)("circle",{cx:"9",cy:"9",r:"1",fill:"currentColor",stroke:"none"}),(0,b.jsx)("circle",{cx:"15",cy:"9",r:"1",fill:"currentColor",stroke:"none"})]}),(0,b.jsx)("p",{className:"text-sm text-zinc-400 dark:text-zinc-500",children:e})]})}var w=require("react/jsx-runtime"),ge=`
|
|
3
|
+
@keyframes watermark-drift {
|
|
4
|
+
0% { transform: rotate(-12deg) scale(2) translate(0, 0); }
|
|
5
|
+
100% { transform: rotate(-12deg) scale(2) translate(180px, 100px); }
|
|
6
|
+
}
|
|
7
|
+
`,G=!1;function ke(){if(G||typeof document>"u")return;let e=document.createElement("style");e.textContent=ge,document.head.appendChild(e),G=!0}function J({children:e,color:t="#0f172a",text:o="m1k",maxWidth:n=430,padding:a=12,sponsor:c,speed:f=20}){ke();let l=Math.max(14,Math.min(28,Math.floor(160/o.length))),s=c?Math.max(14,Math.min(28,Math.floor(160/c.name.length))):l,i=180,p=100,z=16,x=16,T=z*i/2,M=x*p/2;return(0,w.jsxs)("div",{className:"h-dvh w-full relative overflow-hidden",style:{backgroundColor:t,transition:"background-color 0.5s ease"},children:[(0,w.jsx)("div",{className:"absolute inset-0 pointer-events-none select-none",style:{transformOrigin:"center center",animation:f>0?`watermark-drift ${f}s linear infinite`:void 0,transform:"rotate(-12deg) scale(2)"},children:Array.from({length:x}).flatMap((R,u)=>Array.from({length:z}).map((L,y)=>{let N=y*i-T+i/2,B=u*p-M+p/2,$=(u+y)%2===1&&c,m={position:"absolute",left:N,top:B,transform:"translate(-50%, -50%)",fontWeight:900,color:"rgba(255,255,255,0.12)",whiteSpace:"nowrap",lineHeight:1};return $?(0,w.jsx)("a",{href:c.url,target:"_blank",rel:"noopener noreferrer",className:"hover:opacity-30 transition-opacity",style:{...m,fontSize:s,textDecoration:"none",pointerEvents:"auto"},children:c.name},`${u}-${y}`):(0,w.jsx)("a",{href:"https://m1k.app",target:"_blank",rel:"noopener noreferrer",className:"hover:opacity-30 transition-opacity",style:{...m,fontSize:l,textDecoration:"none",pointerEvents:"auto"},children:o},`${u}-${y}`)}))}),(0,w.jsx)("div",{className:"relative z-10 h-full flex items-center justify-center mx-auto",style:{maxWidth:n,padding:a},children:e})]})}var E={blue:"#3b82f6",purple:"#8b5cf6",green:"#10b981",orange:"#f97316",pink:"#ec4899",red:"#ef4444",yellow:"#eab308",cyan:"#06b6d4",slate:"#0f172a",zinc:"#27272a"};var q=require("react");var r=require("react/jsx-runtime");function Q({color:e,dark:t=!1,onClick:o,className:n=""}){let a=t?"#000":"#fff",c=t?"rgba(255,255,255,0.15)":"rgba(0,0,0,0.12)";return(0,r.jsx)("button",{onClick:o,className:`w-7 h-7 rounded-full transition-all active:scale-90 hover:scale-110 overflow-hidden ${n}`,style:{boxShadow:`0 2px 10px ${e}55`},title:"Theme",children:(0,r.jsxs)("svg",{width:"100%",height:"100%",viewBox:"0 0 100 100",children:[(0,r.jsx)("path",{d:"M0 0 L100 0 L0 100 Z",fill:a}),(0,r.jsx)("path",{d:"M100 0 L100 100 L0 100 Z",fill:e}),(0,r.jsx)("circle",{cx:"50",cy:"50",r:"49",fill:"none",stroke:c,strokeWidth:"2"})]})})}function U({open:e,onClose:t,current:o,onSelect:n,dark:a=!1,onDarkToggle:c,palette:f=E}){if((0,q.useEffect)(()=>{if(!e)return;let s=i=>{i.key==="Escape"&&t()};return window.addEventListener("keydown",s),()=>window.removeEventListener("keydown",s)},[e,t]),!e)return null;let l=Object.entries(f);return(0,r.jsxs)("div",{className:"fixed inset-0 z-50 flex items-end justify-center",onClick:t,children:[(0,r.jsx)("div",{className:"absolute inset-0 bg-black/40 backdrop-blur-sm"}),(0,r.jsxs)("div",{className:"relative z-10 w-full max-w-101.5 mb-3 mx-3 rounded-2xl bg-white dark:bg-zinc-900 shadow-2xl overflow-hidden",onClick:s=>s.stopPropagation(),children:[(0,r.jsxs)("div",{className:"px-4 pt-4 pb-3",children:[(0,r.jsx)("p",{className:"text-sm font-bold text-zinc-900 dark:text-white mb-3",children:"\uD14C\uB9C8"}),c&&(0,r.jsx)("div",{className:"flex gap-2 mb-4",children:[{label:"\uB77C\uC774\uD2B8",isDark:!1},{label:"\uB2E4\uD06C",isDark:!0}].map(s=>{let i=a===s.isDark;return(0,r.jsxs)("button",{onClick:()=>{i||c()},className:`flex-1 flex items-center justify-center gap-2 py-2.5 rounded-2xl transition-all ${i?"bg-zinc-900 dark:bg-white ring-2 ring-zinc-900 dark:ring-white ring-offset-2 ring-offset-white dark:ring-offset-zinc-900":"bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:[s.isDark?(0,r.jsx)("svg",{width:"15",height:"15",viewBox:"-1 -1 26 26",fill:i?"#18181b":"#71717a",children:(0,r.jsx)("path",{d:"M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79z"})}):(0,r.jsxs)("svg",{width:"15",height:"15",viewBox:"-1 -1 26 26",fill:i?"white":"#71717a",children:[(0,r.jsx)("circle",{cx:"12",cy:"12",r:"5"}),(0,r.jsx)("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42",stroke:i?"white":"#71717a",strokeWidth:"2",strokeLinecap:"round"})]}),(0,r.jsx)("span",{className:`text-sm font-semibold ${i?"text-white dark:text-zinc-900":"text-zinc-400 dark:text-zinc-500"}`,children:s.label})]},s.label)})})]}),(0,r.jsx)("div",{className:"px-4 pb-3 grid grid-cols-5 gap-3 justify-items-center",children:l.map(([s,i])=>(0,r.jsx)("button",{onClick:()=>{n(i),t()},className:"relative w-11 h-11 rounded-full transition-all hover:scale-110",style:{backgroundColor:i,boxShadow:o===i?`0 0 0 2px #fff, 0 0 0 4px ${i}`:"0 0 0 1.5px rgba(255,255,255,0.5), 0 2px 8px rgba(255,255,255,0.2)"},children:o===i&&(0,r.jsx)("div",{className:"absolute inset-0 flex items-center justify-center",children:(0,r.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"white",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round",children:(0,r.jsx)("polyline",{points:"20 6 9 17 4 12"})})})},i))}),(0,r.jsx)("div",{className:"px-4 py-3 border-t border-zinc-100 dark:border-zinc-800",children:(0,r.jsx)("button",{onClick:t,className:"w-full py-2.5 rounded-xl bg-zinc-100 dark:bg-zinc-800 text-sm font-medium text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors",children:"Close"})})]})]})}var ee={tossface:"https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css",pretendard:"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css",inter:"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap"},te={default:'"Pretendard Variable", "Pretendard", system-ui, -apple-system, sans-serif, "Tossface"',pretendard:'"Pretendard Variable", "Pretendard", system-ui, sans-serif',inter:'"Inter", system-ui, sans-serif'};var g=require("react"),h=require("react/jsx-runtime");function oe({words:e,color:t="currentColor",speed:o=80,deleteSpeed:n=30,pauseMs:a=2200,gapMs:c=400,cursorColor:f,className:l=""}){let[s,i]=(0,g.useState)(0),[p,z]=(0,g.useState)(0),[x,T]=(0,g.useState)(!1),[M,R]=(0,g.useState)(!0),u=e[s]||"",L=f||t,y=x?p>0:p<u.length;(0,g.useEffect)(()=>{if(y){R(!0);return}let m=setInterval(()=>R(v=>!v),530);return()=>clearInterval(m)},[y]),(0,g.useEffect)(()=>{if(!x&&p<u.length){let m=Math.random()*o,v=u[p-1]===" "?o*.4:0,pe=setTimeout(()=>z(de=>de+1),o*.75+m+v);return()=>clearTimeout(pe)}if(!x&&p===u.length){let m=setTimeout(()=>T(!0),a);return()=>clearTimeout(m)}if(x&&p>0){let m=setTimeout(()=>z(v=>v-1),n+Math.random()*n*.6);return()=>clearTimeout(m)}if(x&&p===0){let m=setTimeout(()=>{T(!1),i(v=>(v+1)%e.length)},c);return()=>clearTimeout(m)}},[p,x,u,o,n,a,c,e.length]);let N=u.slice(0,p),B=N[N.length-1],$=N.slice(0,-1);return(0,h.jsxs)("span",{className:l,style:{color:t},children:[p>0&&(0,h.jsxs)(h.Fragment,{children:[$,(0,h.jsx)("span",{className:"inline-block",style:{animation:x?void 0:"m1k-char-pop 0.1s ease-out"},children:B},`${s}-${p}`)]}),(0,h.jsx)("span",{className:"inline-block w-0.5 h-[1em] ml-px align-middle rounded-full",style:{backgroundColor:L,opacity:M?1:0,transition:"opacity 0.1s"}}),(0,h.jsx)("style",{children:`
|
|
8
|
+
@keyframes m1k-char-pop {
|
|
9
|
+
0% { opacity: 0; transform: translateY(4px); }
|
|
10
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
11
|
+
}
|
|
12
|
+
`})]})}var j=require("react"),ne=require("react-dom"),d=require("react/jsx-runtime"),re=[{label:"\uC790\uC8FC \uC4F0\uB294",emojis:["\u{1F3E0}","\u{1F50D}","\u{1F464}","\u2B50","\u2764\uFE0F","\u{1F525}","\u2705","\u{1F4CC}","\u{1F3AF}","\u{1F4A1}","\u{1F680}","\u{1F4AC}","\u{1F44D}","\u{1F64C}","\u{1F4AA}","\u{1F389}","\u{1F4E2}","\u{1F511}","\u26A1","\u{1F31F}","\u{1F380}","\u{1F9E1}","\u{1FAF6}","\u{1F947}"]},{label:"\uAC10\uC815",emojis:["\u{1F600}","\u{1F604}","\u{1F606}","\u{1F60E}","\u{1F979}","\u{1F60D}","\u{1F929}","\u{1F605}","\u{1F602}","\u{1F972}","\u{1F62D}","\u{1F624}","\u{1F914}","\u{1F607}","\u{1FAF6}","\u{1F917}","\u{1F634}","\u{1F92F}","\u{1F973}","\u{1F62C}","\u{1FAE0}","\u{1F92B}","\u{1F636}","\u{1FAE1}"]},{label:"\uB3D9\uBB3C",emojis:["\u{1F436}","\u{1F431}","\u{1F42D}","\u{1F439}","\u{1F430}","\u{1F98A}","\u{1F43B}","\u{1F43C}","\u{1F428}","\u{1F42F}","\u{1F981}","\u{1F42E}","\u{1F437}","\u{1F438}","\u{1F435}","\u{1F414}","\u{1F427}","\u{1F426}","\u{1F986}","\u{1F989}","\u{1F98B}","\u{1F422}","\u{1F42C}","\u{1F433}"]},{label:"\uC0AC\uBB3C",emojis:["\u{1F4F1}","\u{1F4BB}","\u2328\uFE0F","\u{1F5A5}\uFE0F","\u{1F4F7}","\u{1F3B5}","\u{1F3AE}","\u{1F4DA}","\u{1F4B0}","\u{1F381}","\u{1F514}","\u{1F4CA}","\u{1F5D3}\uFE0F","\u26A1","\u{1F527}","\u{1F48A}","\u{1F9EA}","\u{1F52D}","\u{1F399}\uFE0F","\u{1F58B}\uFE0F","\u{1F4E6}","\u{1F6CD}\uFE0F","\u{1F4B3}","\u{1F510}"]},{label:"\uC790\uC5F0",emojis:["\u{1F308}","\u{1F338}","\u{1F33F}","\u{1F340}","\u{1F319}","\u2600\uFE0F","\u2B50","\u{1F30A}","\u{1F34E}","\u{1F33A}","\u2744\uFE0F","\u{1F334}","\u{1F335}","\u{1F344}","\u{1F33B}","\u{1F30D}","\u26C5","\u{1F32A}\uFE0F","\u{1F305}","\u{1F341}","\u{1F33E}","\u{1FAB8}","\u{1FAE7}","\u2604\uFE0F"]},{label:"\uD65C\uB3D9",emojis:["\u{1F3C3}","\u{1F9D8}","\u{1F3A8}","\u{1F373}","\u2708\uFE0F","\u{1F3D5}\uFE0F","\u{1F3A4}","\u{1F3CB}\uFE0F","\u{1F938}","\u{1F9E9}","\u{1F3AD}","\u{1F6D2}","\u{1F6B4}","\u{1F3CA}","\u26F7\uFE0F","\u{1F3B8}","\u{1F3B9}","\u{1F4F8}","\u{1F9D7}","\u{1F93F}","\u{1F3B2}","\u{1F3C6}","\u{1F3AF}","\u{1FA84}"]}];function ie({emoji:e,onClick:t,className:o=""}){return(0,d.jsx)("button",{onClick:t,className:`w-9 h-9 rounded-full flex items-center justify-center text-lg bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-all hover:scale-110 active:scale-90 ${o}`,title:"Pick emoji",children:e})}function ae({open:e,onClose:t,current:o,onSelect:n}){let[a,c]=(0,j.useState)(0);if((0,j.useEffect)(()=>{if(!e)return;let l=s=>{s.key==="Escape"&&t()};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[e,t]),!e)return null;let{emojis:f}=re[a];return(0,ne.createPortal)((0,d.jsxs)("div",{className:"fixed inset-0 z-50 flex items-end justify-center",onClick:t,children:[(0,d.jsx)("div",{className:"absolute inset-0 bg-black/40 backdrop-blur-sm"}),(0,d.jsxs)("div",{className:"relative z-10 w-full max-w-101.5 mb-3 mx-3 rounded-2xl bg-white dark:bg-zinc-900 shadow-2xl overflow-hidden",onClick:l=>l.stopPropagation(),children:[(0,d.jsx)("div",{className:"px-4 pt-4 pb-3",children:(0,d.jsx)("p",{className:"text-sm font-bold text-zinc-900 dark:text-white",children:"\uC774\uBAA8\uC9C0"})}),(0,d.jsx)("div",{className:"flex gap-1 px-4 pb-3 overflow-x-auto scrollbar-hide",children:re.map((l,s)=>(0,d.jsx)("button",{onClick:()=>c(s),className:`shrink-0 px-3 py-1.5 rounded-full text-xs font-semibold transition-colors ${a===s?"bg-zinc-900 dark:bg-white text-white dark:text-zinc-900":"bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:l.label},l.label))}),(0,d.jsx)("div",{className:"px-4 pb-3 grid grid-cols-6 gap-2",children:f.map(l=>(0,d.jsx)("button",{onClick:()=>{n(l),t()},className:`h-11 rounded-xl flex items-center justify-center text-2xl transition-all hover:scale-110 active:scale-90 ${o===l?"bg-zinc-900 dark:bg-white":"bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:l},l))}),(0,d.jsx)("div",{className:"px-4 py-3 border-t border-zinc-100 dark:border-zinc-800",children:(0,d.jsx)("button",{onClick:t,className:"w-full py-2.5 rounded-xl bg-zinc-100 dark:bg-zinc-800 text-sm font-medium text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors",children:"\uB2EB\uAE30"})})]})]}),document.body)}var se=require("react"),le=require("react-dom"),k=require("react/jsx-runtime");function ce({label:e,children:t,placement:o="top"}){let[n,a]=(0,se.useState)(null),c=i=>{a(i.currentTarget.getBoundingClientRect())},f=()=>a(null),l=n?o==="top"?n.top+window.scrollY-36:n.bottom+window.scrollY+8:0,s=n?n.left+window.scrollX+n.width/2:0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)("span",{className:"inline-flex",onMouseEnter:c,onMouseLeave:f,onFocus:c,onBlur:f,children:t}),n&&(0,le.createPortal)((0,k.jsx)("div",{className:"fixed z-[9999] pointer-events-none",style:{top:l,left:s,transform:"translateX(-50%)"},children:(0,k.jsx)("div",{className:"px-3 py-1.5 rounded-xl bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 text-xs font-semibold whitespace-nowrap shadow-lg animate-in fade-in zoom-in-95 duration-100",children:e})}),document.body)]})}0&&(module.exports={AppShell,AppShellContent,AppShellHeader,Divider,EmojiButton,EmojiPicker,EmptyState,Section,SectionHeader,StatChip,Tab,TabBar,ThemeButton,ThemeDialog,Tooltip,Typewriter,Watermark,colors,fontFamily,fonts});
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{jsx as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import{jsx as E}from"react/jsx-runtime";function K({children:e,className:t="",maxWidth:o=430,style:r}){return E("div",{className:`w-full h-full flex flex-col bg-white dark:bg-zinc-950 shadow-2xl ring-1 ring-black/10 dark:ring-zinc-700 rounded-2xl overflow-hidden ${t}`,style:{maxWidth:o,...r},children:e})}function X({children:e,className:t=""}){return E("header",{className:`sticky top-0 z-20 h-14 px-4 flex items-center justify-between border-b border-zinc-100 dark:border-zinc-800 bg-white/90 dark:bg-zinc-950/90 backdrop-blur-md rounded-t-2xl ${t}`,children:e})}function V({children:e,className:t=""}){return E("div",{className:`flex-1 overflow-y-auto scrollbar-hide ${t}`,children:e})}import{jsx as $,jsxs as J}from"react/jsx-runtime";function Z({children:e,className:t=""}){return $("nav",{className:`sticky bottom-0 z-20 h-16 border-t border-zinc-200 dark:border-zinc-800 flex bg-white/90 dark:bg-zinc-950/90 backdrop-blur-md rounded-b-2xl ${t}`,children:e})}function G({active:e,onClick:t,icon:o,label:r,activeColor:c}){return J("button",{onClick:t,className:`flex-1 flex flex-col items-center gap-0.5 py-2.5 transition-colors ${e?"":"text-zinc-300 dark:text-zinc-600"}`,style:e?{color:c}:void 0,children:[o,$("span",{className:"text-[10px] font-medium",children:r})]})}import{jsx as A}from"react/jsx-runtime";function q({children:e,className:t=""}){return A("section",{className:`px-4 ${t}`,children:e})}function Q({children:e}){return A("h2",{className:"text-[11px] font-semibold text-zinc-400 dark:text-zinc-500 uppercase tracking-wider mb-3",children:e})}import{jsx as ee}from"react/jsx-runtime";function U({className:e=""}){return ee("div",{className:`mx-4 my-6 h-px bg-zinc-200 dark:bg-zinc-800 ${e}`})}import{jsx as W,jsxs as oe}from"react/jsx-runtime";function te({label:e,value:t,className:o=""}){return oe("div",{className:`flex-1 rounded-xl bg-zinc-100 dark:bg-zinc-900 px-3 py-3 text-center ${o}`,children:[W("p",{className:"text-[10px] text-zinc-500 dark:text-zinc-400 font-medium mb-0.5",children:e}),W("p",{className:"text-lg font-bold tabular-nums text-zinc-900 dark:text-white",children:t.toLocaleString()})]})}import{jsx as v,jsxs as L}from"react/jsx-runtime";function re({message:e,icon:t}){return L("div",{className:"flex flex-col items-center justify-center py-12 gap-2",children:[t||L("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-zinc-200 dark:text-zinc-700",children:[v("circle",{cx:"12",cy:"12",r:"10"}),v("path",{d:"M8 15h8"}),v("circle",{cx:"9",cy:"9",r:"1",fill:"currentColor",stroke:"none"}),v("circle",{cx:"15",cy:"9",r:"1",fill:"currentColor",stroke:"none"})]}),v("p",{className:"text-sm text-zinc-400 dark:text-zinc-500",children:e})]})}import{jsx as z,jsxs as se}from"react/jsx-runtime";var ne=`
|
|
3
|
+
@keyframes watermark-drift {
|
|
4
|
+
0% { transform: rotate(-12deg) scale(2) translate(0, 0); }
|
|
5
|
+
100% { transform: rotate(-12deg) scale(2) translate(180px, 100px); }
|
|
6
|
+
}
|
|
7
|
+
`,D=!1;function ie(){if(D||typeof document>"u")return;let e=document.createElement("style");e.textContent=ne,document.head.appendChild(e),D=!0}function ae({children:e,color:t="#0f172a",text:o="m1k",maxWidth:r=430,padding:c=12,sponsor:l,speed:m=20}){ie();let a=Math.max(14,Math.min(28,Math.floor(160/o.length))),i=l?Math.max(14,Math.min(28,Math.floor(160/l.name.length))):a,n=180,p=100,k=16,u=16,w=k*n/2,P=u*p/2;return se("div",{className:"h-dvh w-full relative overflow-hidden",style:{backgroundColor:t,transition:"background-color 0.5s ease"},children:[z("div",{className:"absolute inset-0 pointer-events-none select-none",style:{transformOrigin:"center center",animation:m>0?`watermark-drift ${m}s linear infinite`:void 0,transform:"rotate(-12deg) scale(2)"},children:Array.from({length:u}).flatMap((S,f)=>Array.from({length:k}).map((B,b)=>{let y=b*n-w+n/2,T=f*p-P+p/2,C=(f+b)%2===1&&l,d={position:"absolute",left:y,top:T,transform:"translate(-50%, -50%)",fontWeight:900,color:"rgba(255,255,255,0.12)",whiteSpace:"nowrap",lineHeight:1};return C?z("a",{href:l.url,target:"_blank",rel:"noopener noreferrer",className:"hover:opacity-30 transition-opacity",style:{...d,fontSize:i,textDecoration:"none",pointerEvents:"auto"},children:l.name},`${f}-${b}`):z("a",{href:"https://m1k.app",target:"_blank",rel:"noopener noreferrer",className:"hover:opacity-30 transition-opacity",style:{...d,fontSize:a,textDecoration:"none",pointerEvents:"auto"},children:o},`${f}-${b}`)}))}),z("div",{className:"relative z-10 h-full flex items-center justify-center mx-auto",style:{maxWidth:r,padding:c},children:e})]})}var j={blue:"#3b82f6",purple:"#8b5cf6",green:"#10b981",orange:"#f97316",pink:"#ec4899",red:"#ef4444",yellow:"#eab308",cyan:"#06b6d4",slate:"#0f172a",zinc:"#27272a"};import{useEffect as le}from"react";import{jsx as s,jsxs as g}from"react/jsx-runtime";function ce({color:e,dark:t=!1,onClick:o,className:r=""}){let c=t?"#000":"#fff",l=t?"rgba(255,255,255,0.15)":"rgba(0,0,0,0.12)";return s("button",{onClick:o,className:`w-7 h-7 rounded-full transition-all active:scale-90 hover:scale-110 overflow-hidden ${r}`,style:{boxShadow:`0 2px 10px ${e}55`},title:"Theme",children:g("svg",{width:"100%",height:"100%",viewBox:"0 0 100 100",children:[s("path",{d:"M0 0 L100 0 L0 100 Z",fill:c}),s("path",{d:"M100 0 L100 100 L0 100 Z",fill:e}),s("circle",{cx:"50",cy:"50",r:"49",fill:"none",stroke:l,strokeWidth:"2"})]})})}function pe({open:e,onClose:t,current:o,onSelect:r,dark:c=!1,onDarkToggle:l,palette:m=j}){if(le(()=>{if(!e)return;let i=n=>{n.key==="Escape"&&t()};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e,t]),!e)return null;let a=Object.entries(m);return g("div",{className:"fixed inset-0 z-50 flex items-end justify-center",onClick:t,children:[s("div",{className:"absolute inset-0 bg-black/40 backdrop-blur-sm"}),g("div",{className:"relative z-10 w-full max-w-101.5 mb-3 mx-3 rounded-2xl bg-white dark:bg-zinc-900 shadow-2xl overflow-hidden",onClick:i=>i.stopPropagation(),children:[g("div",{className:"px-4 pt-4 pb-3",children:[s("p",{className:"text-sm font-bold text-zinc-900 dark:text-white mb-3",children:"\uD14C\uB9C8"}),l&&s("div",{className:"flex gap-2 mb-4",children:[{label:"\uB77C\uC774\uD2B8",isDark:!1},{label:"\uB2E4\uD06C",isDark:!0}].map(i=>{let n=c===i.isDark;return g("button",{onClick:()=>{n||l()},className:`flex-1 flex items-center justify-center gap-2 py-2.5 rounded-2xl transition-all ${n?"bg-zinc-900 dark:bg-white ring-2 ring-zinc-900 dark:ring-white ring-offset-2 ring-offset-white dark:ring-offset-zinc-900":"bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:[i.isDark?s("svg",{width:"15",height:"15",viewBox:"-1 -1 26 26",fill:n?"#18181b":"#71717a",children:s("path",{d:"M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79z"})}):g("svg",{width:"15",height:"15",viewBox:"-1 -1 26 26",fill:n?"white":"#71717a",children:[s("circle",{cx:"12",cy:"12",r:"5"}),s("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42",stroke:n?"white":"#71717a",strokeWidth:"2",strokeLinecap:"round"})]}),s("span",{className:`text-sm font-semibold ${n?"text-white dark:text-zinc-900":"text-zinc-400 dark:text-zinc-500"}`,children:i.label})]},i.label)})})]}),s("div",{className:"px-4 pb-3 grid grid-cols-5 gap-3 justify-items-center",children:a.map(([i,n])=>s("button",{onClick:()=>{r(n),t()},className:"relative w-11 h-11 rounded-full transition-all hover:scale-110",style:{backgroundColor:n,boxShadow:o===n?`0 0 0 2px #fff, 0 0 0 4px ${n}`:"0 0 0 1.5px rgba(255,255,255,0.5), 0 2px 8px rgba(255,255,255,0.2)"},children:o===n&&s("div",{className:"absolute inset-0 flex items-center justify-center",children:s("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"white",strokeWidth:"3",strokeLinecap:"round",strokeLinejoin:"round",children:s("polyline",{points:"20 6 9 17 4 12"})})})},n))}),s("div",{className:"px-4 py-3 border-t border-zinc-100 dark:border-zinc-800",children:s("button",{onClick:t,className:"w-full py-2.5 rounded-xl bg-zinc-100 dark:bg-zinc-800 text-sm font-medium text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors",children:"Close"})})]})]})}var de={tossface:"https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css",pretendard:"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css",inter:"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap"},me={default:'"Pretendard Variable", "Pretendard", system-ui, -apple-system, sans-serif, "Tossface"',pretendard:'"Pretendard Variable", "Pretendard", system-ui, sans-serif',inter:'"Inter", system-ui, sans-serif'};import{useState as N,useEffect as H}from"react";import{Fragment as ue,jsx as M,jsxs as I}from"react/jsx-runtime";function fe({words:e,color:t="currentColor",speed:o=80,deleteSpeed:r=30,pauseMs:c=2200,gapMs:l=400,cursorColor:m,className:a=""}){let[i,n]=N(0),[p,k]=N(0),[u,w]=N(!1),[P,S]=N(!0),f=e[i]||"",B=m||t,b=u?p>0:p<f.length;H(()=>{if(b){S(!0);return}let d=setInterval(()=>S(h=>!h),530);return()=>clearInterval(d)},[b]),H(()=>{if(!u&&p<f.length){let d=Math.random()*o,h=f[p-1]===" "?o*.4:0,O=setTimeout(()=>k(_=>_+1),o*.75+d+h);return()=>clearTimeout(O)}if(!u&&p===f.length){let d=setTimeout(()=>w(!0),c);return()=>clearTimeout(d)}if(u&&p>0){let d=setTimeout(()=>k(h=>h-1),r+Math.random()*r*.6);return()=>clearTimeout(d)}if(u&&p===0){let d=setTimeout(()=>{w(!1),n(h=>(h+1)%e.length)},l);return()=>clearTimeout(d)}},[p,u,f,o,r,c,l,e.length]);let y=f.slice(0,p),T=y[y.length-1],C=y.slice(0,-1);return I("span",{className:a,style:{color:t},children:[p>0&&I(ue,{children:[C,M("span",{className:"inline-block",style:{animation:u?void 0:"m1k-char-pop 0.1s ease-out"},children:T},`${i}-${p}`)]}),M("span",{className:"inline-block w-0.5 h-[1em] ml-px align-middle rounded-full",style:{backgroundColor:B,opacity:P?1:0,transition:"opacity 0.1s"}}),M("style",{children:`
|
|
8
|
+
@keyframes m1k-char-pop {
|
|
9
|
+
0% { opacity: 0; transform: translateY(4px); }
|
|
10
|
+
100% { opacity: 1; transform: translateY(0); }
|
|
11
|
+
}
|
|
12
|
+
`})]})}import{useEffect as xe,useState as be}from"react";import{createPortal as he}from"react-dom";import{jsx as x,jsxs as Y}from"react/jsx-runtime";var F=[{label:"\uC790\uC8FC \uC4F0\uB294",emojis:["\u{1F3E0}","\u{1F50D}","\u{1F464}","\u2B50","\u2764\uFE0F","\u{1F525}","\u2705","\u{1F4CC}","\u{1F3AF}","\u{1F4A1}","\u{1F680}","\u{1F4AC}","\u{1F44D}","\u{1F64C}","\u{1F4AA}","\u{1F389}","\u{1F4E2}","\u{1F511}","\u26A1","\u{1F31F}","\u{1F380}","\u{1F9E1}","\u{1FAF6}","\u{1F947}"]},{label:"\uAC10\uC815",emojis:["\u{1F600}","\u{1F604}","\u{1F606}","\u{1F60E}","\u{1F979}","\u{1F60D}","\u{1F929}","\u{1F605}","\u{1F602}","\u{1F972}","\u{1F62D}","\u{1F624}","\u{1F914}","\u{1F607}","\u{1FAF6}","\u{1F917}","\u{1F634}","\u{1F92F}","\u{1F973}","\u{1F62C}","\u{1FAE0}","\u{1F92B}","\u{1F636}","\u{1FAE1}"]},{label:"\uB3D9\uBB3C",emojis:["\u{1F436}","\u{1F431}","\u{1F42D}","\u{1F439}","\u{1F430}","\u{1F98A}","\u{1F43B}","\u{1F43C}","\u{1F428}","\u{1F42F}","\u{1F981}","\u{1F42E}","\u{1F437}","\u{1F438}","\u{1F435}","\u{1F414}","\u{1F427}","\u{1F426}","\u{1F986}","\u{1F989}","\u{1F98B}","\u{1F422}","\u{1F42C}","\u{1F433}"]},{label:"\uC0AC\uBB3C",emojis:["\u{1F4F1}","\u{1F4BB}","\u2328\uFE0F","\u{1F5A5}\uFE0F","\u{1F4F7}","\u{1F3B5}","\u{1F3AE}","\u{1F4DA}","\u{1F4B0}","\u{1F381}","\u{1F514}","\u{1F4CA}","\u{1F5D3}\uFE0F","\u26A1","\u{1F527}","\u{1F48A}","\u{1F9EA}","\u{1F52D}","\u{1F399}\uFE0F","\u{1F58B}\uFE0F","\u{1F4E6}","\u{1F6CD}\uFE0F","\u{1F4B3}","\u{1F510}"]},{label:"\uC790\uC5F0",emojis:["\u{1F308}","\u{1F338}","\u{1F33F}","\u{1F340}","\u{1F319}","\u2600\uFE0F","\u2B50","\u{1F30A}","\u{1F34E}","\u{1F33A}","\u2744\uFE0F","\u{1F334}","\u{1F335}","\u{1F344}","\u{1F33B}","\u{1F30D}","\u26C5","\u{1F32A}\uFE0F","\u{1F305}","\u{1F341}","\u{1F33E}","\u{1FAB8}","\u{1FAE7}","\u2604\uFE0F"]},{label:"\uD65C\uB3D9",emojis:["\u{1F3C3}","\u{1F9D8}","\u{1F3A8}","\u{1F373}","\u2708\uFE0F","\u{1F3D5}\uFE0F","\u{1F3A4}","\u{1F3CB}\uFE0F","\u{1F938}","\u{1F9E9}","\u{1F3AD}","\u{1F6D2}","\u{1F6B4}","\u{1F3CA}","\u26F7\uFE0F","\u{1F3B8}","\u{1F3B9}","\u{1F4F8}","\u{1F9D7}","\u{1F93F}","\u{1F3B2}","\u{1F3C6}","\u{1F3AF}","\u{1FA84}"]}];function ge({emoji:e,onClick:t,className:o=""}){return x("button",{onClick:t,className:`w-9 h-9 rounded-full flex items-center justify-center text-lg bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-all hover:scale-110 active:scale-90 ${o}`,title:"Pick emoji",children:e})}function ke({open:e,onClose:t,current:o,onSelect:r}){let[c,l]=be(0);if(xe(()=>{if(!e)return;let a=i=>{i.key==="Escape"&&t()};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[e,t]),!e)return null;let{emojis:m}=F[c];return he(Y("div",{className:"fixed inset-0 z-50 flex items-end justify-center",onClick:t,children:[x("div",{className:"absolute inset-0 bg-black/40 backdrop-blur-sm"}),Y("div",{className:"relative z-10 w-full max-w-101.5 mb-3 mx-3 rounded-2xl bg-white dark:bg-zinc-900 shadow-2xl overflow-hidden",onClick:a=>a.stopPropagation(),children:[x("div",{className:"px-4 pt-4 pb-3",children:x("p",{className:"text-sm font-bold text-zinc-900 dark:text-white",children:"\uC774\uBAA8\uC9C0"})}),x("div",{className:"flex gap-1 px-4 pb-3 overflow-x-auto scrollbar-hide",children:F.map((a,i)=>x("button",{onClick:()=>l(i),className:`shrink-0 px-3 py-1.5 rounded-full text-xs font-semibold transition-colors ${c===i?"bg-zinc-900 dark:bg-white text-white dark:text-zinc-900":"bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:a.label},a.label))}),x("div",{className:"px-4 pb-3 grid grid-cols-6 gap-2",children:m.map(a=>x("button",{onClick:()=>{r(a),t()},className:`h-11 rounded-xl flex items-center justify-center text-2xl transition-all hover:scale-110 active:scale-90 ${o===a?"bg-zinc-900 dark:bg-white":"bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,children:a},a))}),x("div",{className:"px-4 py-3 border-t border-zinc-100 dark:border-zinc-800",children:x("button",{onClick:t,className:"w-full py-2.5 rounded-xl bg-zinc-100 dark:bg-zinc-800 text-sm font-medium text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors",children:"\uB2EB\uAE30"})})]})]}),document.body)}import{useState as ye}from"react";import{createPortal as ve}from"react-dom";import{Fragment as ze,jsx as R,jsxs as Ne}from"react/jsx-runtime";function we({label:e,children:t,placement:o="top"}){let[r,c]=ye(null),l=n=>{c(n.currentTarget.getBoundingClientRect())},m=()=>c(null),a=r?o==="top"?r.top+window.scrollY-36:r.bottom+window.scrollY+8:0,i=r?r.left+window.scrollX+r.width/2:0;return Ne(ze,{children:[R("span",{className:"inline-flex",onMouseEnter:l,onMouseLeave:m,onFocus:l,onBlur:m,children:t}),r&&ve(R("div",{className:"fixed z-[9999] pointer-events-none",style:{top:a,left:i,transform:"translateX(-50%)"},children:R("div",{className:"px-3 py-1.5 rounded-xl bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 text-xs font-semibold whitespace-nowrap shadow-lg animate-in fade-in zoom-in-95 duration-100",children:e})}),document.body)]})}export{K as AppShell,V as AppShellContent,X as AppShellHeader,U as Divider,ge as EmojiButton,ke as EmojiPicker,re as EmptyState,q as Section,Q as SectionHeader,te as StatChip,G as Tab,Z as TabBar,ce as ThemeButton,pe as ThemeDialog,we as Tooltip,fe as Typewriter,ae as Watermark,j as colors,me as fontFamily,de as fonts};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m1kapp/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Mobile-first app shell UI components for side projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"license": "MIT",
|
|
48
48
|
"repository": {
|
|
49
49
|
"type": "git",
|
|
50
|
-
"url": "https://github.com/
|
|
50
|
+
"url": "https://github.com/m1kapp/ui"
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://m1k.app"
|
|
53
53
|
}
|