@sruim/nexus-design 0.0.1
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 +195 -0
- package/dist/assets/grid.webp.js +3 -0
- package/dist/components/credits-button/index.d.ts +8 -0
- package/dist/components/credits-button/index.js +40 -0
- package/dist/components/icon/index.d.ts +7 -0
- package/dist/components/icon/index.js +32 -0
- package/dist/components/icon-button/index.d.ts +9 -0
- package/dist/components/icon-button/index.js +36 -0
- package/dist/components/img-uploader/index.d.ts +19 -0
- package/dist/components/img-uploader/index.js +107 -0
- package/dist/components/img-viewer/index.d.ts +16 -0
- package/dist/components/img-viewer/index.js +80 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/loadable/index.d.ts +8 -0
- package/dist/components/loadable/index.js +67 -0
- package/dist/components/loading/assets/loading.webp.js +3 -0
- package/dist/components/loading/index.d.ts +16 -0
- package/dist/components/loading/index.js +85 -0
- package/dist/components/model-uploader/index.d.ts +17 -0
- package/dist/components/model-uploader/index.js +93 -0
- package/dist/components/tree/index.d.ts +14 -0
- package/dist/components/tree/index.js +50 -0
- package/dist/components/tree/node.d.ts +23 -0
- package/dist/components/tree/node.js +185 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +43 -0
- package/dist/style.css +1193 -0
- package/dist/theme.d.ts +2 -0
- package/dist/theme.js +96 -0
- package/dist/tokens/materials.d.ts +4 -0
- package/dist/tokens/materials.js +4 -0
- package/dist/tokens/nexus.d.ts +51 -0
- package/dist/tokens/nexus.js +42 -0
- package/dist/ui/avatar/index.d.ts +11 -0
- package/dist/ui/avatar/index.js +55 -0
- package/dist/ui/badge/index.d.ts +2 -0
- package/dist/ui/badge/index.js +45 -0
- package/dist/ui/button.d.ts +8 -0
- package/dist/ui/button.js +61 -0
- package/dist/ui/carousel/index.d.ts +43 -0
- package/dist/ui/carousel/index.js +186 -0
- package/dist/ui/checkbox/index.d.ts +4 -0
- package/dist/ui/checkbox/index.js +34 -0
- package/dist/ui/collapsible/index.d.ts +9 -0
- package/dist/ui/collapsible/index.js +7 -0
- package/dist/ui/dialog/confirm.d.ts +20 -0
- package/dist/ui/dialog/confirm.js +80 -0
- package/dist/ui/dialog/dialog.d.ts +26 -0
- package/dist/ui/dialog/dialog.js +97 -0
- package/dist/ui/dialog/index.d.ts +2 -0
- package/dist/ui/drawer.d.ts +22 -0
- package/dist/ui/drawer.js +98 -0
- package/dist/ui/form.d.ts +33 -0
- package/dist/ui/form.js +138 -0
- package/dist/ui/index.d.ts +24 -0
- package/dist/ui/input-otp.d.ts +14 -0
- package/dist/ui/input-otp.js +73 -0
- package/dist/ui/label.d.ts +4 -0
- package/dist/ui/label.js +32 -0
- package/dist/ui/masonry/index.d.ts +13 -0
- package/dist/ui/masonry/index.js +45 -0
- package/dist/ui/popover/index.d.ts +15 -0
- package/dist/ui/popover/index.js +78 -0
- package/dist/ui/progress.d.ts +6 -0
- package/dist/ui/progress.js +48 -0
- package/dist/ui/select/index.d.ts +21 -0
- package/dist/ui/select/index.js +127 -0
- package/dist/ui/slider/index.d.ts +9 -0
- package/dist/ui/slider/index.js +87 -0
- package/dist/ui/snap-input.d.ts +7 -0
- package/dist/ui/snap-input.js +38 -0
- package/dist/ui/sonner.d.ts +5 -0
- package/dist/ui/sonner.js +50 -0
- package/dist/ui/switch.d.ts +4 -0
- package/dist/ui/switch.js +33 -0
- package/dist/ui/table/index.d.ts +22 -0
- package/dist/ui/table/index.js +70 -0
- package/dist/ui/tabs/index.d.ts +12 -0
- package/dist/ui/tabs/index.js +60 -0
- package/dist/ui/toggle/index.d.ts +2 -0
- package/dist/ui/toggle/toggle-group.d.ts +9 -0
- package/dist/ui/toggle/toggle-group.js +54 -0
- package/dist/ui/toggle/toggle.d.ts +11 -0
- package/dist/ui/toggle/toggle.js +45 -0
- package/dist/ui/tooltip/index.d.ts +17 -0
- package/dist/ui/tooltip/index.js +68 -0
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +48 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/utils.d.ts +8 -0
- package/dist/utils/utils.js +91 -0
- package/package.json +148 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Nexus Design System
|
|
2
|
+
|
|
3
|
+
A modern React component library built with Radix UI primitives and UnoCSS.
|
|
4
|
+
|
|
5
|
+
[中文文档](./README_CN.md) | [API Reference](./docs/api-reference.md)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Radix UI Primitives** - Accessible, unstyled components as foundation
|
|
10
|
+
- **UnoCSS Styling** - Atomic CSS with custom theme tokens
|
|
11
|
+
- **TypeScript** - Full type safety with strict mode
|
|
12
|
+
- **Dark Theme** - Obsidian-based dark mode design
|
|
13
|
+
- **Glass Effects** - Frosted glass materials (FrostGlass, DeepGlass)
|
|
14
|
+
- **Compound Components** - Flexible composition patterns
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @sruim/nexus-design
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Peer Dependencies:**
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add react react-dom
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Configure UnoCSS
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// uno.config.ts
|
|
34
|
+
import { theme } from '@sruim/nexus-design/theme';
|
|
35
|
+
import { defineConfig, presetUno } from 'unocss';
|
|
36
|
+
|
|
37
|
+
export default defineConfig({
|
|
38
|
+
presets: [presetUno()],
|
|
39
|
+
theme,
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Import Styles
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// main.tsx
|
|
47
|
+
import '@sruim/nexus-design/style.css';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Setup Dialoger
|
|
51
|
+
|
|
52
|
+
Required for imperative dialog APIs (`Dialog.show`, `Confirm.show`):
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { Dialoger } from '@sruim/nexus-design/ui';
|
|
56
|
+
|
|
57
|
+
function App() {
|
|
58
|
+
return (
|
|
59
|
+
<>
|
|
60
|
+
<YourApp />
|
|
61
|
+
<Dialoger />
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 4. Use Components
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Button, Dialog, Select } from '@sruim/nexus-design/ui';
|
|
71
|
+
import { IconButton } from '@sruim/nexus-design/components';
|
|
72
|
+
|
|
73
|
+
function Example() {
|
|
74
|
+
return (
|
|
75
|
+
<div>
|
|
76
|
+
<Button variant="solid">Click Me</Button>
|
|
77
|
+
<IconButton icon="i-nexus:download" text="Download" />
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Import Paths
|
|
84
|
+
|
|
85
|
+
| Path | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `@sruim/nexus-design` | All exports |
|
|
88
|
+
| `@sruim/nexus-design/ui` | UI primitives (Button, Dialog, Select...) |
|
|
89
|
+
| `@sruim/nexus-design/components` | Composite components (Icon, IconButton...) |
|
|
90
|
+
| `@sruim/nexus-design/utils` | Utilities (cn, sleep, copy...) |
|
|
91
|
+
| `@sruim/nexus-design/theme` | UnoCSS theme configuration |
|
|
92
|
+
| `@sruim/nexus-design/style.css` | Global styles |
|
|
93
|
+
|
|
94
|
+
## Components
|
|
95
|
+
|
|
96
|
+
### UI Primitives
|
|
97
|
+
|
|
98
|
+
| Component | Description |
|
|
99
|
+
|-----------|-------------|
|
|
100
|
+
| `Button` | Primary button with solid/hollow/plain variants |
|
|
101
|
+
| `Dialog` | Modal dialog with imperative API |
|
|
102
|
+
| `Confirm` | Confirmation dialog |
|
|
103
|
+
| `Select` | Dropdown select |
|
|
104
|
+
| `Tabs` | Tab navigation |
|
|
105
|
+
| `Popover` | Floating popover |
|
|
106
|
+
| `Tooltip` | Hover tooltip |
|
|
107
|
+
| `Checkbox` | Checkbox input |
|
|
108
|
+
| `Switch` | Toggle switch |
|
|
109
|
+
| `Slider` | Range slider with optional input |
|
|
110
|
+
| `Progress` | Progress bar |
|
|
111
|
+
| `Toggle` | Toggle button |
|
|
112
|
+
| `ToggleGroup` | Toggle button group |
|
|
113
|
+
| `Avatar` | User avatar |
|
|
114
|
+
| `Badge` | Notification badge |
|
|
115
|
+
| `Drawer` | Bottom sheet drawer |
|
|
116
|
+
| `Form` | Form with Zod validation |
|
|
117
|
+
|
|
118
|
+
### Composite Components
|
|
119
|
+
|
|
120
|
+
| Component | Description |
|
|
121
|
+
|-----------|-------------|
|
|
122
|
+
| `Icon` | UnoCSS icon wrapper |
|
|
123
|
+
| `IconButton` | Button with icon |
|
|
124
|
+
| `ImgUploader` | Image upload component |
|
|
125
|
+
| `ImgViewer` | Image viewer |
|
|
126
|
+
| `ModelUploader` | 3D model upload |
|
|
127
|
+
| `Tree` | Tree view |
|
|
128
|
+
| `Loading` | Loading indicator |
|
|
129
|
+
| `Loadable` | Async content wrapper |
|
|
130
|
+
|
|
131
|
+
## Theme Tokens
|
|
132
|
+
|
|
133
|
+
### Colors
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
// Dark backgrounds
|
|
137
|
+
bg-obsidian-100 // #020617
|
|
138
|
+
bg-obsidian-200 // #0F172A
|
|
139
|
+
bg-obsidian-300 // #1E293B
|
|
140
|
+
|
|
141
|
+
// Primary accent
|
|
142
|
+
bg-core-blue // #3B82F6
|
|
143
|
+
|
|
144
|
+
// Status
|
|
145
|
+
text-status-error // #F43F5E
|
|
146
|
+
text-status-success // #10B981
|
|
147
|
+
text-status-warning // #F59E0B
|
|
148
|
+
|
|
149
|
+
// Text
|
|
150
|
+
text-text-primary // #FFFFFF
|
|
151
|
+
text-text-secondary // #CBD5E1
|
|
152
|
+
text-text-disabled // #64748B
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Glass Materials
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { FrostGlass, DeepGlass } from '@sruim/nexus-design/tokens/materials';
|
|
159
|
+
|
|
160
|
+
// FrostGlass: backdrop-blur-12 bg-slate-900/70 border border-white/10
|
|
161
|
+
// DeepGlass: backdrop-blur-12 bg-slate-950/90
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Install dependencies
|
|
168
|
+
pnpm install
|
|
169
|
+
|
|
170
|
+
# Start Storybook
|
|
171
|
+
pnpm storybook
|
|
172
|
+
|
|
173
|
+
# Build library
|
|
174
|
+
pnpm build
|
|
175
|
+
|
|
176
|
+
# Type check
|
|
177
|
+
pnpm typecheck
|
|
178
|
+
|
|
179
|
+
# Lint
|
|
180
|
+
pnpm lint
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Tech Stack
|
|
184
|
+
|
|
185
|
+
- React 19
|
|
186
|
+
- TypeScript 5.8
|
|
187
|
+
- Radix UI
|
|
188
|
+
- UnoCSS
|
|
189
|
+
- Vite
|
|
190
|
+
- Storybook
|
|
191
|
+
- Zod + React Hook Form
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const grid = "data:image/webp;base64,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";
|
|
2
|
+
|
|
3
|
+
export { grid as default };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import '@radix-ui/react-portal';
|
|
3
|
+
import '../../ui/avatar/index.js';
|
|
4
|
+
import '../../utils/config.js';
|
|
5
|
+
import { Button } from '../../ui/button.js';
|
|
6
|
+
import '../../ui/carousel/index.js';
|
|
7
|
+
import '../../ui/checkbox/index.js';
|
|
8
|
+
import '../../ui/collapsible/index.js';
|
|
9
|
+
import '../../ui/dialog/confirm.js';
|
|
10
|
+
import '../../ui/dialog/dialog.js';
|
|
11
|
+
import '../../ui/drawer.js';
|
|
12
|
+
import '../../ui/form.js';
|
|
13
|
+
import '../../ui/input-otp.js';
|
|
14
|
+
import '../../ui/label.js';
|
|
15
|
+
import 'react';
|
|
16
|
+
import '../../ui/popover/index.js';
|
|
17
|
+
import '../../ui/progress.js';
|
|
18
|
+
import '../../ui/select/index.js';
|
|
19
|
+
import '../../ui/slider/index.js';
|
|
20
|
+
import '../../ui/sonner.js';
|
|
21
|
+
import '../../ui/switch.js';
|
|
22
|
+
import '../../ui/table/index.js';
|
|
23
|
+
import '../../ui/tabs/index.js';
|
|
24
|
+
import '../../ui/toggle/toggle.js';
|
|
25
|
+
import '../../ui/toggle/toggle-group.js';
|
|
26
|
+
import '../../ui/tooltip/index.js';
|
|
27
|
+
import { Icon } from '../icon/index.js';
|
|
28
|
+
|
|
29
|
+
const CreditsButton = (props) => {
|
|
30
|
+
return /* @__PURE__ */ jsxs(Button, { variant: "solid", size: "large", ...props, children: [
|
|
31
|
+
props.children,
|
|
32
|
+
props.text,
|
|
33
|
+
props.count ? /* @__PURE__ */ jsxs("div", { className: "flex items-center pl-1.5", children: [
|
|
34
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-5", icon: "i-nexus-bolt-monotone" }),
|
|
35
|
+
/* @__PURE__ */ jsx("span", { className: "pl-1.5", children: props.count })
|
|
36
|
+
] }) : null
|
|
37
|
+
] });
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { CreditsButton };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { cn } from '../../utils/config.js';
|
|
3
|
+
import '@radix-ui/react-portal';
|
|
4
|
+
import '../../ui/avatar/index.js';
|
|
5
|
+
import '../../ui/button.js';
|
|
6
|
+
import '../../ui/carousel/index.js';
|
|
7
|
+
import '../../ui/checkbox/index.js';
|
|
8
|
+
import '../../ui/collapsible/index.js';
|
|
9
|
+
import '../../ui/dialog/confirm.js';
|
|
10
|
+
import '../../ui/dialog/dialog.js';
|
|
11
|
+
import '../../ui/drawer.js';
|
|
12
|
+
import '../../ui/form.js';
|
|
13
|
+
import '../../ui/input-otp.js';
|
|
14
|
+
import '../../ui/label.js';
|
|
15
|
+
import 'react';
|
|
16
|
+
import '../../ui/popover/index.js';
|
|
17
|
+
import '../../ui/progress.js';
|
|
18
|
+
import '../../ui/select/index.js';
|
|
19
|
+
import '../../ui/slider/index.js';
|
|
20
|
+
import '../../ui/sonner.js';
|
|
21
|
+
import '../../ui/switch.js';
|
|
22
|
+
import '../../ui/table/index.js';
|
|
23
|
+
import '../../ui/tabs/index.js';
|
|
24
|
+
import '../../ui/toggle/toggle.js';
|
|
25
|
+
import '../../ui/toggle/toggle-group.js';
|
|
26
|
+
import '../../ui/tooltip/index.js';
|
|
27
|
+
|
|
28
|
+
const Icon = ({ icon, className, ...props }) => {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { className: cn(icon, "v-mid", className), ...props });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { Icon };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Button } from '../../ui';
|
|
2
|
+
import { default as React } from 'react';
|
|
3
|
+
interface Props extends Omit<React.ComponentProps<typeof Button>, 'children'> {
|
|
4
|
+
icon?: string;
|
|
5
|
+
iconClassName?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const IconButton: React.FC<Props>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import '@radix-ui/react-portal';
|
|
3
|
+
import '../../ui/avatar/index.js';
|
|
4
|
+
import { cn } from '../../utils/config.js';
|
|
5
|
+
import { Button } from '../../ui/button.js';
|
|
6
|
+
import '../../ui/carousel/index.js';
|
|
7
|
+
import '../../ui/checkbox/index.js';
|
|
8
|
+
import '../../ui/collapsible/index.js';
|
|
9
|
+
import '../../ui/dialog/confirm.js';
|
|
10
|
+
import '../../ui/dialog/dialog.js';
|
|
11
|
+
import '../../ui/drawer.js';
|
|
12
|
+
import '../../ui/form.js';
|
|
13
|
+
import '../../ui/input-otp.js';
|
|
14
|
+
import '../../ui/label.js';
|
|
15
|
+
import 'react';
|
|
16
|
+
import '../../ui/popover/index.js';
|
|
17
|
+
import '../../ui/progress.js';
|
|
18
|
+
import '../../ui/select/index.js';
|
|
19
|
+
import '../../ui/slider/index.js';
|
|
20
|
+
import '../../ui/sonner.js';
|
|
21
|
+
import '../../ui/switch.js';
|
|
22
|
+
import '../../ui/table/index.js';
|
|
23
|
+
import '../../ui/tabs/index.js';
|
|
24
|
+
import '../../ui/toggle/toggle.js';
|
|
25
|
+
import '../../ui/toggle/toggle-group.js';
|
|
26
|
+
import '../../ui/tooltip/index.js';
|
|
27
|
+
import { Icon } from '../icon/index.js';
|
|
28
|
+
|
|
29
|
+
const IconButton = ({ icon, text, iconClassName, ...props }) => {
|
|
30
|
+
return /* @__PURE__ */ jsxs(Button, { variant: "solid", size: "large", ...props, children: [
|
|
31
|
+
icon && /* @__PURE__ */ jsx(Icon, { className: cn("mr-1 size-5", iconClassName), icon }),
|
|
32
|
+
text
|
|
33
|
+
] });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { IconButton };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
interface InputProps {
|
|
3
|
+
onChange: (file: File | File[]) => void;
|
|
4
|
+
multiple?: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface Props {
|
|
7
|
+
className?: string;
|
|
8
|
+
icon?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
onChange: (file: File | File[]) => void;
|
|
13
|
+
multiple?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface IImgUploader extends React.FC<Props> {
|
|
16
|
+
Input: React.FC<InputProps>;
|
|
17
|
+
}
|
|
18
|
+
export declare const ImgUploader: IImgUploader;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FrostGlass } from '../../tokens/materials.js';
|
|
3
|
+
import '@radix-ui/react-portal';
|
|
4
|
+
import '../../ui/avatar/index.js';
|
|
5
|
+
import { cn } from '../../utils/config.js';
|
|
6
|
+
import { checkSize, checkType } from '../../utils/utils.js';
|
|
7
|
+
import '../../ui/button.js';
|
|
8
|
+
import '../../ui/carousel/index.js';
|
|
9
|
+
import '../../ui/checkbox/index.js';
|
|
10
|
+
import '../../ui/collapsible/index.js';
|
|
11
|
+
import '../../ui/dialog/confirm.js';
|
|
12
|
+
import '../../ui/dialog/dialog.js';
|
|
13
|
+
import '../../ui/drawer.js';
|
|
14
|
+
import '../../ui/form.js';
|
|
15
|
+
import '../../ui/input-otp.js';
|
|
16
|
+
import '../../ui/label.js';
|
|
17
|
+
import { useState, useCallback } from 'react';
|
|
18
|
+
import '../../ui/popover/index.js';
|
|
19
|
+
import '../../ui/progress.js';
|
|
20
|
+
import '../../ui/select/index.js';
|
|
21
|
+
import '../../ui/slider/index.js';
|
|
22
|
+
import '../../ui/sonner.js';
|
|
23
|
+
import '../../ui/switch.js';
|
|
24
|
+
import '../../ui/table/index.js';
|
|
25
|
+
import '../../ui/tabs/index.js';
|
|
26
|
+
import '../../ui/toggle/toggle.js';
|
|
27
|
+
import '../../ui/toggle/toggle-group.js';
|
|
28
|
+
import '../../ui/tooltip/index.js';
|
|
29
|
+
import grid from '../../assets/grid.webp.js';
|
|
30
|
+
import { Icon } from '../icon/index.js';
|
|
31
|
+
import { toast } from 'sonner';
|
|
32
|
+
|
|
33
|
+
const Input = ({ onChange, multiple = false }) => {
|
|
34
|
+
const handleChange = (e) => {
|
|
35
|
+
const image = e.target.files?.[0];
|
|
36
|
+
if (!image) return;
|
|
37
|
+
if (!checkSize(image, 5)) {
|
|
38
|
+
toast.error("The uploaded file size cannot exceed 5 MB");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!checkType(image, "image")) {
|
|
42
|
+
toast.error("This file type is not supported.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
onChange(multiple ? Array.from(e.target.files || []) : image);
|
|
46
|
+
e.target.value = "";
|
|
47
|
+
};
|
|
48
|
+
return /* @__PURE__ */ jsx(
|
|
49
|
+
"input",
|
|
50
|
+
{
|
|
51
|
+
className: "absolute inset-0 cursor-pointer opacity-0",
|
|
52
|
+
type: "file",
|
|
53
|
+
max: 10,
|
|
54
|
+
multiple,
|
|
55
|
+
accept: "image/png,image/webp,image/jpg,image/jpeg",
|
|
56
|
+
onChange: handleChange
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
const BaseStyle = "relative size-full flex flex-col items-center justify-center overflow-hidden transition-colors duration-fast ease-smooth hover:bg-surface-hover";
|
|
61
|
+
const BorderStyle = "box-border border border-dashed border-white/20";
|
|
62
|
+
const DragOverStyle = cn("bg-core-blue/10 w-full hover:bg-core-blue/10", FrostGlass);
|
|
63
|
+
const ImgUploader = ({
|
|
64
|
+
className,
|
|
65
|
+
icon,
|
|
66
|
+
title,
|
|
67
|
+
description,
|
|
68
|
+
required,
|
|
69
|
+
onChange,
|
|
70
|
+
multiple
|
|
71
|
+
}) => {
|
|
72
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
73
|
+
const handleDragEnter = useCallback(() => setIsDragOver(true), []);
|
|
74
|
+
const handleDragLeave = useCallback(() => setIsDragOver(false), []);
|
|
75
|
+
return /* @__PURE__ */ jsxs(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
className: cn(BaseStyle, BorderStyle, className, isDragOver && DragOverStyle),
|
|
79
|
+
onDragOver: handleDragEnter,
|
|
80
|
+
onDragLeave: handleDragLeave,
|
|
81
|
+
onDrop: handleDragLeave,
|
|
82
|
+
children: [
|
|
83
|
+
icon && /* @__PURE__ */ jsx(Icon, { className: "size-5", icon }),
|
|
84
|
+
title && /* @__PURE__ */ jsx("div", { className: "my-1.5 max-w-2/3 w-37.5 text-center text-wrap text-2.5 text-text-primary", children: title }),
|
|
85
|
+
description && /* @__PURE__ */ jsx("div", { className: "max-w-2/3 w-37.5 text-center text-wrap text-2.5 text-text-secondary", children: description }),
|
|
86
|
+
required && /* @__PURE__ */ jsx(
|
|
87
|
+
Icon,
|
|
88
|
+
{
|
|
89
|
+
className: "absolute right-3 top-3 size-3 text-status-error",
|
|
90
|
+
icon: "i-nexus:required-monotone"
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
"div",
|
|
95
|
+
{
|
|
96
|
+
className: "absolute left-0 top-0 z-0 size-full bg-contain bg-repeat",
|
|
97
|
+
style: { backgroundImage: `url(${grid})` }
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsx(Input, { onChange, multiple })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
ImgUploader.Input = Input;
|
|
106
|
+
|
|
107
|
+
export { ImgUploader };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
type ImgSource = string | File;
|
|
3
|
+
interface ImgViewerProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
src: ImgSource | ImgSource[];
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
interface ImgViewerComponent extends React.FC<ImgViewerProps> {
|
|
9
|
+
Delete: typeof Delete;
|
|
10
|
+
}
|
|
11
|
+
interface DeleteProps extends React.HTMLAttributes<HTMLButtonElement> {
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
}
|
|
14
|
+
declare const Delete: React.ForwardRefExoticComponent<DeleteProps & React.RefAttributes<HTMLButtonElement>>;
|
|
15
|
+
declare const ImgViewer: ImgViewerComponent;
|
|
16
|
+
export { ImgViewer };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { cn } from '../../utils/config.js';
|
|
3
|
+
import '@radix-ui/react-portal';
|
|
4
|
+
import '../../ui/avatar/index.js';
|
|
5
|
+
import '../../ui/button.js';
|
|
6
|
+
import '../../ui/carousel/index.js';
|
|
7
|
+
import '../../ui/checkbox/index.js';
|
|
8
|
+
import '../../ui/collapsible/index.js';
|
|
9
|
+
import '../../ui/dialog/confirm.js';
|
|
10
|
+
import '../../ui/dialog/dialog.js';
|
|
11
|
+
import '../../ui/drawer.js';
|
|
12
|
+
import '../../ui/form.js';
|
|
13
|
+
import '../../ui/input-otp.js';
|
|
14
|
+
import '../../ui/label.js';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
17
|
+
import '../../ui/popover/index.js';
|
|
18
|
+
import '../../ui/progress.js';
|
|
19
|
+
import '../../ui/select/index.js';
|
|
20
|
+
import '../../ui/slider/index.js';
|
|
21
|
+
import '../../ui/sonner.js';
|
|
22
|
+
import '../../ui/switch.js';
|
|
23
|
+
import '../../ui/table/index.js';
|
|
24
|
+
import '../../ui/tabs/index.js';
|
|
25
|
+
import '../../ui/toggle/toggle.js';
|
|
26
|
+
import '../../ui/toggle/toggle-group.js';
|
|
27
|
+
import '../../ui/tooltip/index.js';
|
|
28
|
+
import { Icon } from '../icon/index.js';
|
|
29
|
+
|
|
30
|
+
const BaseStyle = "group relative size-full overflow-hidden";
|
|
31
|
+
const BorderStyle = "box-border border border-white/8";
|
|
32
|
+
const Delete = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33
|
+
"button",
|
|
34
|
+
{
|
|
35
|
+
ref,
|
|
36
|
+
className: cn(
|
|
37
|
+
"absolute bottom-3 right-3 opacity-0 rounded-1/2 transition-opacity duration-base ease-smooth group-hover:opacity-100 group-hover:bg-black/60",
|
|
38
|
+
className
|
|
39
|
+
),
|
|
40
|
+
...props,
|
|
41
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "m-2 size-5 text-text-primary", icon: "i-nexus:delete-monotone" })
|
|
42
|
+
}
|
|
43
|
+
));
|
|
44
|
+
const ImgViewer = ({ src, className, children }) => {
|
|
45
|
+
const [images, setImages] = useState([]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const array = Array.isArray(src) ? src : [src];
|
|
48
|
+
const images2 = array.filter(Boolean).map((source) => {
|
|
49
|
+
if (source instanceof File) return URL.createObjectURL(source);
|
|
50
|
+
return source;
|
|
51
|
+
});
|
|
52
|
+
setImages(images2);
|
|
53
|
+
}, [src]);
|
|
54
|
+
const Inner = useMemo(() => {
|
|
55
|
+
if (images.length === 1) {
|
|
56
|
+
return /* @__PURE__ */ jsx("img", { src: images[0], alt: "preview-img", className: "size-full object-contain" });
|
|
57
|
+
}
|
|
58
|
+
if (images.length > 1) {
|
|
59
|
+
const rotation = [-15, 0, 15];
|
|
60
|
+
return images.slice(-3).map((url, index) => /* @__PURE__ */ jsx(
|
|
61
|
+
"img",
|
|
62
|
+
{
|
|
63
|
+
src: url,
|
|
64
|
+
alt: `preview-img-${index}`,
|
|
65
|
+
className: "absolute left-1/2 top-1/2 size-4/5 rounded-3.5 object-contain shadow-2xl",
|
|
66
|
+
style: { transform: `translate(-50%, -50%) rotate(${rotation[index]}deg)` }
|
|
67
|
+
},
|
|
68
|
+
`${url}${index}`
|
|
69
|
+
));
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}, [images]);
|
|
73
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(BaseStyle, BorderStyle, className), children: [
|
|
74
|
+
Inner,
|
|
75
|
+
children
|
|
76
|
+
] });
|
|
77
|
+
};
|
|
78
|
+
ImgViewer.Delete = Delete;
|
|
79
|
+
|
|
80
|
+
export { ImgViewer };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './credits-button';
|
|
2
|
+
export * from './icon';
|
|
3
|
+
export * from './icon-button';
|
|
4
|
+
export * from './img-uploader';
|
|
5
|
+
export * from './img-viewer';
|
|
6
|
+
export * from './loadable';
|
|
7
|
+
export * from './loading';
|
|
8
|
+
export * from './model-uploader';
|
|
9
|
+
export * from './tree';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { cn } from '../../utils/config.js';
|
|
3
|
+
import '@radix-ui/react-portal';
|
|
4
|
+
import '../../ui/avatar/index.js';
|
|
5
|
+
import '../../ui/button.js';
|
|
6
|
+
import '../../ui/carousel/index.js';
|
|
7
|
+
import '../../ui/checkbox/index.js';
|
|
8
|
+
import '../../ui/collapsible/index.js';
|
|
9
|
+
import '../../ui/dialog/confirm.js';
|
|
10
|
+
import '../../ui/dialog/dialog.js';
|
|
11
|
+
import '../../ui/drawer.js';
|
|
12
|
+
import '../../ui/form.js';
|
|
13
|
+
import '../../ui/input-otp.js';
|
|
14
|
+
import '../../ui/label.js';
|
|
15
|
+
import { useRef, useState, useLayoutEffect } from 'react';
|
|
16
|
+
import '../../ui/popover/index.js';
|
|
17
|
+
import '../../ui/progress.js';
|
|
18
|
+
import '../../ui/select/index.js';
|
|
19
|
+
import '../../ui/slider/index.js';
|
|
20
|
+
import '../../ui/sonner.js';
|
|
21
|
+
import '../../ui/switch.js';
|
|
22
|
+
import '../../ui/table/index.js';
|
|
23
|
+
import '../../ui/tabs/index.js';
|
|
24
|
+
import '../../ui/toggle/toggle.js';
|
|
25
|
+
import '../../ui/toggle/toggle-group.js';
|
|
26
|
+
import '../../ui/tooltip/index.js';
|
|
27
|
+
import { Loading } from '../loading/index.js';
|
|
28
|
+
|
|
29
|
+
const Loadable = ({ children, onLoadMore, className }) => {
|
|
30
|
+
const ref = useRef(null);
|
|
31
|
+
const loadMoreRef = useRef(onLoadMore);
|
|
32
|
+
const loadingRef = useRef(false);
|
|
33
|
+
const [loading, setLoading] = useState(false);
|
|
34
|
+
loadMoreRef.current = onLoadMore;
|
|
35
|
+
loadingRef.current = loading;
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
if (!ref.current) return;
|
|
38
|
+
const observer = new IntersectionObserver(async ([entry]) => {
|
|
39
|
+
if (entry.intersectionRatio <= 0) return;
|
|
40
|
+
if (loadingRef.current) return;
|
|
41
|
+
try {
|
|
42
|
+
setLoading(true);
|
|
43
|
+
loadingRef.current = true;
|
|
44
|
+
await loadMoreRef.current();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("加载更多数据时发生错误:", error);
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false);
|
|
49
|
+
loadingRef.current = false;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
observer.observe(ref.current);
|
|
53
|
+
return () => observer.disconnect();
|
|
54
|
+
}, []);
|
|
55
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
56
|
+
children,
|
|
57
|
+
/* @__PURE__ */ jsx("div", { ref, className: "min-h-10 w-full", children: loading && /* @__PURE__ */ jsx(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
className: cn("w-full flex flex-col items-center justify-center gap-2 mt-5", className),
|
|
61
|
+
children: /* @__PURE__ */ jsx(Loading.Icon, { className: "max-w-10" })
|
|
62
|
+
}
|
|
63
|
+
) })
|
|
64
|
+
] });
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export { Loadable };
|