@appforgeapps/uiforge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1241 -0
- package/dist/index.d.ts +847 -0
- package/dist/uiforge.cjs +6 -0
- package/dist/uiforge.css +1 -0
- package/dist/uiforge.js +1882 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
# UIForge
|
|
2
|
+
|
|
3
|
+
A rich user interface library for ReactJS developers written by a seasoned user interface developer who loves working with ReactJS.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://reactjs.org/)
|
|
8
|
+
|
|
9
|
+
> **๐ฆ Package Name Change**: The package has been renamed from `@chriscase/uiforge` to `@appforgeapps/uiforge`. If you're upgrading from an older version, please see the [Migration Guide](./MIGRATION_GUIDE.md).
|
|
10
|
+
|
|
11
|
+
> **โ ๏ธ Early Stage Project**: UIForge is a very new project under active development. The API is subject to rapid changes and breaking changes may occur frequently as we refine the components and their interfaces. We recommend pinning to specific versions in production and reviewing the CHANGELOG before upgrading.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- ๐จ **Beautiful Components** - Carefully crafted, customizable UI components
|
|
16
|
+
- ๐ช **TypeScript First** - Full TypeScript support with type definitions
|
|
17
|
+
- โก **Modern Stack** - Built with React, TypeScript, and Vite
|
|
18
|
+
- ๐งช **Well Tested** - Comprehensive test coverage with Vitest
|
|
19
|
+
- ๐ฆ **Tree Shakeable** - Only import what you need
|
|
20
|
+
- ๐ฏ **Developer Friendly** - Easy to use and customize
|
|
21
|
+
- ๐ **Free & Open Source** - MIT licensed
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
### From NPM (Recommended)
|
|
26
|
+
|
|
27
|
+
Install UIForge from NPM:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @appforgeapps/uiforge
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or with yarn:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn add @appforgeapps/uiforge
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or with pnpm:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add @appforgeapps/uiforge
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Using UIForge in Your Project
|
|
46
|
+
|
|
47
|
+
After installation, you'll need to import both the components and the CSS styles in your application.
|
|
48
|
+
|
|
49
|
+
#### Method 1: Import in your main entry file (Recommended)
|
|
50
|
+
|
|
51
|
+
This is the most common approach - import the styles once in your application's entry point:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// src/main.tsx or src/index.tsx
|
|
55
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
56
|
+
import React from 'react'
|
|
57
|
+
import ReactDOM from 'react-dom/client'
|
|
58
|
+
import App from './App'
|
|
59
|
+
|
|
60
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
61
|
+
<React.StrictMode>
|
|
62
|
+
<App />
|
|
63
|
+
</React.StrictMode>
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then import and use components in your app:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// src/App.tsx
|
|
71
|
+
import { Button, UIForgeGrid, UIForgeComboBox } from '@appforgeapps/uiforge'
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
return (
|
|
75
|
+
<div>
|
|
76
|
+
<h1>My Application</h1>
|
|
77
|
+
<Button variant="primary" onClick={() => alert('Hello!')}>
|
|
78
|
+
Click Me
|
|
79
|
+
</Button>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default App
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Method 2: Import styles in your component file
|
|
88
|
+
|
|
89
|
+
If you prefer, you can import the styles directly in the component file where you use UIForge components:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// src/components/MyComponent.tsx
|
|
93
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
94
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
95
|
+
|
|
96
|
+
export function MyComponent() {
|
|
97
|
+
return <Button variant="primary">Click Me</Button>
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Method 3: Import in your global CSS file
|
|
102
|
+
|
|
103
|
+
You can also import UIForge styles in your main CSS file:
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
/* src/index.css or src/App.css */
|
|
107
|
+
@import '@appforgeapps/uiforge/styles.css';
|
|
108
|
+
|
|
109
|
+
/* Your other styles */
|
|
110
|
+
body {
|
|
111
|
+
margin: 0;
|
|
112
|
+
font-family: sans-serif;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### TypeScript Configuration
|
|
117
|
+
|
|
118
|
+
UIForge is written in TypeScript and includes full type definitions. If you're using TypeScript, the types will be automatically picked up. Ensure your `tsconfig.json` includes:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"compilerOptions": {
|
|
123
|
+
"moduleResolution": "bundler", // or "node16" / "nodenext"
|
|
124
|
+
"jsx": "react-jsx",
|
|
125
|
+
"esModuleInterop": true
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Bundler Configuration
|
|
131
|
+
|
|
132
|
+
UIForge works with all modern bundlers. Here are specific notes for common setups:
|
|
133
|
+
|
|
134
|
+
#### Vite
|
|
135
|
+
|
|
136
|
+
No additional configuration needed. Just import and use:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
140
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Next.js (App Router)
|
|
144
|
+
|
|
145
|
+
For Next.js 13+ with the App Router, import styles in your root layout:
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// app/layout.tsx
|
|
149
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
150
|
+
import type { Metadata } from 'next'
|
|
151
|
+
|
|
152
|
+
export const metadata: Metadata = {
|
|
153
|
+
title: 'My App',
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default function RootLayout({
|
|
157
|
+
children,
|
|
158
|
+
}: {
|
|
159
|
+
children: React.ReactNode
|
|
160
|
+
}) {
|
|
161
|
+
return (
|
|
162
|
+
<html lang="en">
|
|
163
|
+
<body>{children}</body>
|
|
164
|
+
</html>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Then use components in your pages:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
// app/page.tsx
|
|
173
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
174
|
+
|
|
175
|
+
export default function Home() {
|
|
176
|
+
return <Button variant="primary">Click Me</Button>
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Next.js (Pages Router)
|
|
181
|
+
|
|
182
|
+
For Next.js with the Pages Router, import styles in `_app.tsx`:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
// pages/_app.tsx
|
|
186
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
187
|
+
import type { AppProps } from 'next/app'
|
|
188
|
+
|
|
189
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
190
|
+
return <Component {...pageProps} />
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Create React App
|
|
195
|
+
|
|
196
|
+
Import styles in your `index.tsx` or `App.tsx`:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
// src/index.tsx
|
|
200
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
201
|
+
import React from 'react'
|
|
202
|
+
import ReactDOM from 'react-dom/client'
|
|
203
|
+
import App from './App'
|
|
204
|
+
|
|
205
|
+
const root = ReactDOM.createRoot(document.getElementById('root')!)
|
|
206
|
+
root.render(
|
|
207
|
+
<React.StrictMode>
|
|
208
|
+
<App />
|
|
209
|
+
</React.StrictMode>
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Webpack
|
|
214
|
+
|
|
215
|
+
If using a custom Webpack setup, ensure you have CSS loaders configured:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm install --save-dev style-loader css-loader
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Then in your webpack.config.js:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
module.exports = {
|
|
225
|
+
module: {
|
|
226
|
+
rules: [
|
|
227
|
+
{
|
|
228
|
+
test: /\.css$/i,
|
|
229
|
+
use: ['style-loader', 'css-loader'],
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Verifying Installation
|
|
237
|
+
|
|
238
|
+
To verify UIForge is properly installed, you can check:
|
|
239
|
+
|
|
240
|
+
1. **Package is installed:**
|
|
241
|
+
```bash
|
|
242
|
+
npm list @appforgeapps/uiforge
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
2. **Types are available** (TypeScript projects):
|
|
246
|
+
```tsx
|
|
247
|
+
import type { ButtonProps } from '@appforgeapps/uiforge'
|
|
248
|
+
// If this imports without errors, types are working
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
3. **Create a simple test component:**
|
|
252
|
+
```tsx
|
|
253
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
254
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
255
|
+
|
|
256
|
+
export function Test() {
|
|
257
|
+
return <Button variant="primary">Test</Button>
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Troubleshooting
|
|
262
|
+
|
|
263
|
+
**Issue: "Cannot find module '@appforgeapps/uiforge'"**
|
|
264
|
+
- Run `npm install` to ensure dependencies are installed
|
|
265
|
+
- Check that `@appforgeapps/uiforge` is in your `package.json` dependencies
|
|
266
|
+
- Try deleting `node_modules` and `package-lock.json`, then run `npm install` again
|
|
267
|
+
|
|
268
|
+
**Issue: Styles not loading**
|
|
269
|
+
- Ensure you've imported the CSS: `import '@appforgeapps/uiforge/styles.css'`
|
|
270
|
+
- Check that your bundler supports CSS imports
|
|
271
|
+
- For Webpack, ensure css-loader and style-loader are configured
|
|
272
|
+
|
|
273
|
+
**Issue: TypeScript errors**
|
|
274
|
+
- Ensure TypeScript 4.7+ is installed
|
|
275
|
+
- Check that your `tsconfig.json` has proper module resolution settings
|
|
276
|
+
- Try running `npm install @types/react @types/react-dom` if not already installed
|
|
277
|
+
|
|
278
|
+
### Alternative: Install from GitHub
|
|
279
|
+
|
|
280
|
+
For development or to use the latest unreleased features, you can install directly from GitHub. Note that GitHub installations require building the project after installation.
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npm install github:chriscase/UIForge
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Or specify a specific branch, tag, or commit:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm install github:chriscase/UIForge#main
|
|
290
|
+
npm install github:chriscase/UIForge#v0.1.0
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Important for GitHub installations:**
|
|
294
|
+
|
|
295
|
+
After installing from GitHub, you'll need to build the project:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
cd node_modules/@appforgeapps/uiforge
|
|
299
|
+
npm install
|
|
300
|
+
npm run build
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Or use a `postinstall` script in your project to automate this:
|
|
304
|
+
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"scripts": {
|
|
308
|
+
"postinstall": "cd node_modules/@appforgeapps/uiforge && npm install && npm run build"
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Recommendation:** Use NPM installation for production projects. GitHub installation is primarily intended for:
|
|
314
|
+
- Contributing to UIForge development
|
|
315
|
+
- Testing unreleased features
|
|
316
|
+
- Debugging issues with the latest code
|
|
317
|
+
|
|
318
|
+
## Usage
|
|
319
|
+
|
|
320
|
+
### Basic Example
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
324
|
+
import '@appforgeapps/uiforge/styles.css' // Import styles
|
|
325
|
+
|
|
326
|
+
function App() {
|
|
327
|
+
return (
|
|
328
|
+
<Button variant="primary" onClick={() => console.log('Clicked!')}>
|
|
329
|
+
Click me
|
|
330
|
+
</Button>
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Complete Setup Example
|
|
336
|
+
|
|
337
|
+
Here's a complete example of setting up UIForge in a React + TypeScript + Vite project:
|
|
338
|
+
|
|
339
|
+
**1. Install UIForge:**
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
npm install @appforgeapps/uiforge react react-dom
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**2. Import components and styles in your app:**
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
// src/App.tsx
|
|
349
|
+
import { useState } from 'react'
|
|
350
|
+
import {
|
|
351
|
+
Button,
|
|
352
|
+
UIForgeGrid,
|
|
353
|
+
UIForgeComboBox,
|
|
354
|
+
UIForgeActivityStream
|
|
355
|
+
} from '@appforgeapps/uiforge'
|
|
356
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
357
|
+
|
|
358
|
+
function App() {
|
|
359
|
+
const [selectedValue, setSelectedValue] = useState(null)
|
|
360
|
+
|
|
361
|
+
const options = [
|
|
362
|
+
{ value: 1, label: 'Option 1', icon: '๐ ' },
|
|
363
|
+
{ value: 2, label: 'Option 2', icon: 'โญ' },
|
|
364
|
+
{ value: 3, label: 'Option 3', icon: 'โ๏ธ' },
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<div className="app">
|
|
369
|
+
<h1>UIForge Demo</h1>
|
|
370
|
+
|
|
371
|
+
<Button variant="primary" onClick={() => alert('Clicked!')}>
|
|
372
|
+
Click Me
|
|
373
|
+
</Button>
|
|
374
|
+
|
|
375
|
+
<UIForgeComboBox
|
|
376
|
+
options={options}
|
|
377
|
+
value={selectedValue}
|
|
378
|
+
onChange={(val) => setSelectedValue(val)}
|
|
379
|
+
placeholder="Select an option..."
|
|
380
|
+
clearable
|
|
381
|
+
/>
|
|
382
|
+
</div>
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export default App
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**3. Ensure peer dependencies are satisfied:**
|
|
390
|
+
|
|
391
|
+
UIForge requires React 18+ or React 19+ as peer dependencies:
|
|
392
|
+
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"dependencies": {
|
|
396
|
+
"@appforgeapps/uiforge": "^0.1.0",
|
|
397
|
+
"react": "^18.0.0",
|
|
398
|
+
"react-dom": "^18.0.0"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### TypeScript Support
|
|
404
|
+
|
|
405
|
+
UIForge is written in TypeScript and includes full type definitions. TypeScript will automatically pick up the types when you import components:
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { Button, ButtonProps, UIForgeComboBox, ComboBoxOption } from '@appforgeapps/uiforge'
|
|
409
|
+
|
|
410
|
+
// Type inference works automatically
|
|
411
|
+
const options: ComboBoxOption[] = [
|
|
412
|
+
{ value: 1, label: 'Item 1' },
|
|
413
|
+
{ value: 2, label: 'Item 2' },
|
|
414
|
+
]
|
|
415
|
+
|
|
416
|
+
// Component props are fully typed
|
|
417
|
+
const MyButton: React.FC<ButtonProps> = (props) => {
|
|
418
|
+
return <Button {...props} />
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Importing Styles
|
|
423
|
+
|
|
424
|
+
UIForge components require CSS to be imported. You have several options:
|
|
425
|
+
|
|
426
|
+
**Option 1: Import in your main entry file (recommended)**
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
// src/main.tsx or src/index.tsx
|
|
430
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
431
|
+
import App from './App'
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Option 2: Import in your component file**
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
// src/App.tsx
|
|
438
|
+
import '@appforgeapps/uiforge/styles.css'
|
|
439
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Option 3: Import in your global CSS file**
|
|
443
|
+
|
|
444
|
+
```css
|
|
445
|
+
/* src/index.css */
|
|
446
|
+
@import '@appforgeapps/uiforge/styles.css';
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Components
|
|
450
|
+
|
|
451
|
+
### Button
|
|
452
|
+
|
|
453
|
+
A customizable button component with multiple variants and sizes.
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
import { Button } from '@appforgeapps/uiforge'
|
|
457
|
+
|
|
458
|
+
// Variants
|
|
459
|
+
<Button variant="primary">Primary</Button>
|
|
460
|
+
<Button variant="secondary">Secondary</Button>
|
|
461
|
+
<Button variant="outline">Outline</Button>
|
|
462
|
+
|
|
463
|
+
// Sizes
|
|
464
|
+
<Button size="small">Small</Button>
|
|
465
|
+
<Button size="medium">Medium</Button>
|
|
466
|
+
<Button size="large">Large</Button>
|
|
467
|
+
|
|
468
|
+
// Disabled state
|
|
469
|
+
<Button disabled>Disabled</Button>
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### UIForgeBlocksEditor
|
|
473
|
+
|
|
474
|
+
A rich, block-based content editor for flexible layouts and content creation.
|
|
475
|
+
|
|
476
|
+
**Features:**
|
|
477
|
+
|
|
478
|
+
- **Block-based editing** - Create, move, and delete content blocks (text, headings, images, quotes, code)
|
|
479
|
+
- **Rich formatting** - WYSIWYG controls for bold, italic, underline, inline code, and more
|
|
480
|
+
- **Drag-and-drop** - Intuitive reordering of content blocks
|
|
481
|
+
- **Multiple block types** - Paragraphs, headings, lists, quotes, code blocks, and images
|
|
482
|
+
- **Export capabilities** - Export content to JSON, HTML, or Markdown
|
|
483
|
+
- **No HTML/CSS knowledge required** - User-friendly interface for non-technical users
|
|
484
|
+
- **Reusable component** - Easy integration into any React application
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
import {
|
|
488
|
+
UIForgeBlocksEditor,
|
|
489
|
+
blocksToHTML,
|
|
490
|
+
blocksToMarkdown,
|
|
491
|
+
ContentBlock,
|
|
492
|
+
} from '@appforgeapps/uiforge'
|
|
493
|
+
|
|
494
|
+
function MyEditor() {
|
|
495
|
+
const [blocks, setBlocks] = useState<ContentBlock[]>([])
|
|
496
|
+
|
|
497
|
+
const handleExport = () => {
|
|
498
|
+
// Export to HTML
|
|
499
|
+
const html = blocksToHTML(blocks)
|
|
500
|
+
console.log(html)
|
|
501
|
+
|
|
502
|
+
// Export to Markdown
|
|
503
|
+
const markdown = blocksToMarkdown(blocks)
|
|
504
|
+
console.log(markdown)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return (
|
|
508
|
+
<>
|
|
509
|
+
<UIForgeBlocksEditor
|
|
510
|
+
initialBlocks={blocks}
|
|
511
|
+
onChange={setBlocks}
|
|
512
|
+
placeholder="Start typing..."
|
|
513
|
+
/>
|
|
514
|
+
<button onClick={handleExport}>Export Content</button>
|
|
515
|
+
</>
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Props Reference:**
|
|
521
|
+
|
|
522
|
+
| Prop | Type | Default | Description |
|
|
523
|
+
| --------------- | ---------------------------------- | ------------------- | ---------------------------------------- |
|
|
524
|
+
| `initialBlocks` | `ContentBlock[]` | `[]` | Initial blocks to display |
|
|
525
|
+
| `onChange` | `(blocks: ContentBlock[]) => void` | - | Callback when blocks change |
|
|
526
|
+
| `placeholder` | `string` | `"Start typing..."` | Placeholder text for empty editor |
|
|
527
|
+
| `readOnly` | `boolean` | `false` | Whether the editor is read-only |
|
|
528
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
529
|
+
| `maxHeight` | `string` | - | Maximum height of the editor (CSS value) |
|
|
530
|
+
|
|
531
|
+
**Block Types:**
|
|
532
|
+
|
|
533
|
+
- `paragraph` - Standard text block
|
|
534
|
+
- `heading1`, `heading2`, `heading3` - Heading blocks
|
|
535
|
+
- `list` - List item
|
|
536
|
+
- `quote` - Blockquote
|
|
537
|
+
- `code` - Code block
|
|
538
|
+
- `image` - Image with URL and alt text
|
|
539
|
+
|
|
540
|
+
**Export Functions:**
|
|
541
|
+
|
|
542
|
+
- `blocksToHTML(blocks)` - Convert blocks to HTML string
|
|
543
|
+
- `blocksToMarkdown(blocks)` - Convert blocks to Markdown string
|
|
544
|
+
- `blocksToJSON(blocks)` - Convert blocks to JSON string
|
|
545
|
+
|
|
546
|
+
### UIForgeGrid
|
|
547
|
+
|
|
548
|
+
A feature-rich data grid component with selection, editing, search, pagination, and custom actions.
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
import { UIForgeGrid, GridColumn } from '@appforgeapps/uiforge'
|
|
552
|
+
|
|
553
|
+
interface User {
|
|
554
|
+
id: number
|
|
555
|
+
name: string
|
|
556
|
+
email: string
|
|
557
|
+
role: string
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const columns: GridColumn<User>[] = [
|
|
561
|
+
{ key: 'name', header: 'Name', field: 'name', editable: true },
|
|
562
|
+
{ key: 'email', header: 'Email', field: 'email' },
|
|
563
|
+
{ key: 'role', header: 'Role', field: 'role' },
|
|
564
|
+
]
|
|
565
|
+
|
|
566
|
+
const data: User[] = [
|
|
567
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'Developer' },
|
|
568
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'Designer' },
|
|
569
|
+
]
|
|
570
|
+
|
|
571
|
+
function MyGrid() {
|
|
572
|
+
return (
|
|
573
|
+
<UIForgeGrid
|
|
574
|
+
columns={columns}
|
|
575
|
+
data={data}
|
|
576
|
+
selectable
|
|
577
|
+
searchable
|
|
578
|
+
pagination={{ currentPage: 0, pageSize: 10 }}
|
|
579
|
+
/>
|
|
580
|
+
)
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**Key Features:**
|
|
585
|
+
|
|
586
|
+
- **Row Selection**: Enable with `selectable` prop. Includes master checkbox for select all/none.
|
|
587
|
+
- **Editable Cells**: Set `editable: true` on column definitions for inline editing.
|
|
588
|
+
- **Custom Renderers**: Use `render` function in column definitions for custom cell content.
|
|
589
|
+
- **Search**: Enable with `searchable` prop. Supports custom filter functions.
|
|
590
|
+
- **Pagination**: Supports both client-side and server-side pagination.
|
|
591
|
+
- **Action Buttons**: Add custom action buttons with event handlers.
|
|
592
|
+
- **Accessibility**: Full keyboard navigation and ARIA labels.
|
|
593
|
+
- **Responsive**: Mobile-friendly design that adapts to different screen sizes.
|
|
594
|
+
|
|
595
|
+
**Advanced Example:**
|
|
596
|
+
|
|
597
|
+
```tsx
|
|
598
|
+
import { UIForgeGrid, GridColumn, GridActionButton } from '@appforgeapps/uiforge'
|
|
599
|
+
|
|
600
|
+
const columns: GridColumn<User>[] = [
|
|
601
|
+
{
|
|
602
|
+
key: 'name',
|
|
603
|
+
header: 'Name',
|
|
604
|
+
field: 'name',
|
|
605
|
+
editable: true,
|
|
606
|
+
width: '200px',
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
key: 'email',
|
|
610
|
+
header: 'Email',
|
|
611
|
+
field: 'email',
|
|
612
|
+
render: (value) => <a href={`mailto:${value}`}>{value}</a>,
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
key: 'status',
|
|
616
|
+
header: 'Status',
|
|
617
|
+
field: 'status',
|
|
618
|
+
render: (value) => <span className={`status-badge status-${value}`}>{value}</span>,
|
|
619
|
+
},
|
|
620
|
+
]
|
|
621
|
+
|
|
622
|
+
const actionButtons: GridActionButton[] = [
|
|
623
|
+
{
|
|
624
|
+
label: 'Export',
|
|
625
|
+
variant: 'primary',
|
|
626
|
+
onClick: (selectedRows) => exportData(selectedRows),
|
|
627
|
+
requiresSelection: true,
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
label: 'Delete',
|
|
631
|
+
variant: 'secondary',
|
|
632
|
+
onClick: (selectedRows) => deleteRows(selectedRows),
|
|
633
|
+
requiresSelection: true,
|
|
634
|
+
},
|
|
635
|
+
]
|
|
636
|
+
|
|
637
|
+
function AdvancedGrid() {
|
|
638
|
+
const [currentPage, setCurrentPage] = useState(0)
|
|
639
|
+
const [pageSize, setPageSize] = useState(25)
|
|
640
|
+
|
|
641
|
+
return (
|
|
642
|
+
<UIForgeGrid
|
|
643
|
+
columns={columns}
|
|
644
|
+
data={data}
|
|
645
|
+
selectable
|
|
646
|
+
onSelectionChange={(keys, rows) => console.log('Selected:', rows)}
|
|
647
|
+
getRowKey={(row) => row.id}
|
|
648
|
+
onCellEdit={(rowKey, columnKey, newValue) => {
|
|
649
|
+
// Handle cell edit
|
|
650
|
+
console.log('Edit:', rowKey, columnKey, newValue)
|
|
651
|
+
}}
|
|
652
|
+
searchable
|
|
653
|
+
searchPlaceholder="Search users..."
|
|
654
|
+
customFilter={(row, searchTerm) => {
|
|
655
|
+
// Custom search logic
|
|
656
|
+
return row.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
657
|
+
}}
|
|
658
|
+
actionButtons={actionButtons}
|
|
659
|
+
pagination={{ currentPage, pageSize }}
|
|
660
|
+
onPageChange={setCurrentPage}
|
|
661
|
+
onPageSizeChange={setPageSize}
|
|
662
|
+
pageSizeOptions={[10, 25, 50, 100]}
|
|
663
|
+
/>
|
|
664
|
+
)
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**Props Reference:**
|
|
669
|
+
|
|
670
|
+
| Prop | Type | Default | Description |
|
|
671
|
+
| ------------------- | -------------------------------------------- | --------------------- | ---------------------------- |
|
|
672
|
+
| `columns` | `GridColumn<T>[]` | required | Column definitions |
|
|
673
|
+
| `data` | `T[]` | required | Data to display |
|
|
674
|
+
| `selectable` | `boolean` | `false` | Enable row selection |
|
|
675
|
+
| `selectedRows` | `Set<string \| number>` | - | Controlled selection state |
|
|
676
|
+
| `getRowKey` | `(row, index) => string \| number` | `(_, i) => i` | Function to get unique key |
|
|
677
|
+
| `onSelectionChange` | `(keys, rows) => void` | - | Selection change handler |
|
|
678
|
+
| `onCellEdit` | `(rowKey, columnKey, newValue, row) => void` | - | Cell edit handler |
|
|
679
|
+
| `searchable` | `boolean` | `false` | Enable search |
|
|
680
|
+
| `searchPlaceholder` | `string` | `"Search..."` | Search input placeholder |
|
|
681
|
+
| `onSearch` | `(searchTerm) => void` | - | Search change handler |
|
|
682
|
+
| `customFilter` | `(row, searchTerm) => boolean` | - | Custom filter function |
|
|
683
|
+
| `pagination` | `GridPaginationConfig` | - | Pagination configuration |
|
|
684
|
+
| `onPageChange` | `(page, pageSize) => void` | - | Page change handler |
|
|
685
|
+
| `onPageSizeChange` | `(pageSize) => void` | - | Page size change handler |
|
|
686
|
+
| `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Available page sizes |
|
|
687
|
+
| `actionButtons` | `GridActionButton[]` | `[]` | Action button configurations |
|
|
688
|
+
| `loading` | `boolean` | `false` | Show loading state |
|
|
689
|
+
| `emptyMessage` | `string` | `"No data available"` | Empty state message |
|
|
690
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
691
|
+
|
|
692
|
+
### UIForgeComboBox
|
|
693
|
+
|
|
694
|
+
A rich, powerful select/combo box component for ReactJS supporting static lists, dynamic server-backed data sources, hierarchical options, and advanced UX features.
|
|
695
|
+
|
|
696
|
+
**Features:**
|
|
697
|
+
|
|
698
|
+
- **Dynamic Suggestions** - Filter options as you type with client-side or server-side search
|
|
699
|
+
- **Static Data Support** - Simple dropdown selection from a fixed set of items
|
|
700
|
+
- **Icons per Option** - Display icons alongside option labels for better visual context
|
|
701
|
+
- **Hierarchical/Tree View** - Support for nested, multi-level option structures
|
|
702
|
+
- **Non-selectable Headers** - Group options with disabled header rows or section dividers
|
|
703
|
+
- **Async Callback Support** - Integrate with APIs for query-as-you-type autocomplete
|
|
704
|
+
- **Highly Customizable** - Custom rendering, styling, and behavior
|
|
705
|
+
- **Keyboard Navigation** - Full keyboard support (arrows, Enter, Escape, Tab)
|
|
706
|
+
- **Accessibility** - ARIA attributes and screen reader support
|
|
707
|
+
|
|
708
|
+
```tsx
|
|
709
|
+
import { UIForgeComboBox, ComboBoxOption } from '@appforgeapps/uiforge'
|
|
710
|
+
|
|
711
|
+
// Static dropdown
|
|
712
|
+
const options: ComboBoxOption[] = [
|
|
713
|
+
{ value: 1, label: 'Option 1' },
|
|
714
|
+
{ value: 2, label: 'Option 2' },
|
|
715
|
+
{ value: 3, label: 'Option 3' },
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
function MyCombo() {
|
|
719
|
+
const [value, setValue] = useState(null)
|
|
720
|
+
|
|
721
|
+
return (
|
|
722
|
+
<UIForgeComboBox
|
|
723
|
+
options={options}
|
|
724
|
+
value={value}
|
|
725
|
+
onChange={(val, option) => setValue(val)}
|
|
726
|
+
placeholder="Select an option..."
|
|
727
|
+
/>
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**With Icons:**
|
|
733
|
+
|
|
734
|
+
```tsx
|
|
735
|
+
const optionsWithIcons: ComboBoxOption[] = [
|
|
736
|
+
{ value: 'home', label: 'Home', icon: '๐ ' },
|
|
737
|
+
{ value: 'star', label: 'Favorites', icon: 'โญ' },
|
|
738
|
+
{ value: 'settings', label: 'Settings', icon: 'โ๏ธ' },
|
|
739
|
+
]
|
|
740
|
+
|
|
741
|
+
<UIForgeComboBox
|
|
742
|
+
options={optionsWithIcons}
|
|
743
|
+
value={value}
|
|
744
|
+
onChange={(val) => setValue(val)}
|
|
745
|
+
clearable
|
|
746
|
+
/>
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**Hierarchical Options (Tree View):**
|
|
750
|
+
|
|
751
|
+
```tsx
|
|
752
|
+
const hierarchicalOptions: ComboBoxOption[] = [
|
|
753
|
+
{
|
|
754
|
+
value: 'fruits',
|
|
755
|
+
label: 'Fruits',
|
|
756
|
+
disabled: true, // Non-selectable header
|
|
757
|
+
children: [
|
|
758
|
+
{ value: 'apple', label: 'Apple', icon: '๐' },
|
|
759
|
+
{ value: 'banana', label: 'Banana', icon: '๐' },
|
|
760
|
+
],
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
value: 'vegetables',
|
|
764
|
+
label: 'Vegetables',
|
|
765
|
+
disabled: true,
|
|
766
|
+
children: [
|
|
767
|
+
{ value: 'carrot', label: 'Carrot', icon: '๐ฅ' },
|
|
768
|
+
{ value: 'broccoli', label: 'Broccoli', icon: '๐ฅฆ' },
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
]
|
|
772
|
+
|
|
773
|
+
<UIForgeComboBox
|
|
774
|
+
options={hierarchicalOptions}
|
|
775
|
+
value={value}
|
|
776
|
+
onChange={(val) => setValue(val)}
|
|
777
|
+
/>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Async/Dynamic Search:**
|
|
781
|
+
|
|
782
|
+
````tsx
|
|
783
|
+
const handleSearch = async (searchText: string, signal?: AbortSignal) => {
|
|
784
|
+
// Call your API
|
|
785
|
+
const response = await fetch(`/api/search?q=${searchText}`)
|
|
786
|
+
const results = await response.json()
|
|
787
|
+
return results.map(item => ({
|
|
788
|
+
value: item.id,
|
|
789
|
+
label: item.name,
|
|
790
|
+
icon: item.iconUrl,
|
|
791
|
+
}))
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
<UIForgeComboBox
|
|
795
|
+
onSearch={handleSearch}
|
|
796
|
+
value={value}
|
|
797
|
+
onChange={(val) => setValue(val)}
|
|
798
|
+
searchable
|
|
799
|
+
debounceMs={300}
|
|
800
|
+
placeholder="Search..."
|
|
801
|
+
/>
|
|
802
|
+
|
|
803
|
+
**Caching and TTL:**
|
|
804
|
+
|
|
805
|
+
```tsx
|
|
806
|
+
const [clearCache, setClearCache] = useState<(() => void) | null>(null)
|
|
807
|
+
const [forceRefresh, setForceRefresh] = useState<(() => void) | null>(null)
|
|
808
|
+
|
|
809
|
+
<UIForgeComboBox
|
|
810
|
+
onSearch={handleSearch}
|
|
811
|
+
searchable
|
|
812
|
+
enableCache // default: false
|
|
813
|
+
cacheTTL={10000} // 10 seconds
|
|
814
|
+
onClearCache={(fn) => setClearCache(() => fn)}
|
|
815
|
+
onForceRefresh={(fn) => setForceRefresh(() => fn)}
|
|
816
|
+
/>
|
|
817
|
+
|
|
818
|
+
// Clear or force-refresh from parent
|
|
819
|
+
clearCache && clearCache()
|
|
820
|
+
forceRefresh && forceRefresh()
|
|
821
|
+
````
|
|
822
|
+
|
|
823
|
+
````
|
|
824
|
+
|
|
825
|
+
**Custom Rendering:**
|
|
826
|
+
|
|
827
|
+
```tsx
|
|
828
|
+
const renderOption = (option: ComboBoxOption) => (
|
|
829
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
830
|
+
{option.icon && <span>{option.icon}</span>}
|
|
831
|
+
<div>
|
|
832
|
+
<div style={{ fontWeight: 'bold' }}>{option.label}</div>
|
|
833
|
+
<div style={{ fontSize: '0.8em', color: '#666' }}>
|
|
834
|
+
{option.data?.description}
|
|
835
|
+
</div>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
<UIForgeComboBox
|
|
841
|
+
options={optionsWithDetails}
|
|
842
|
+
renderOption={renderOption}
|
|
843
|
+
value={value}
|
|
844
|
+
onChange={(val) => setValue(val)}
|
|
845
|
+
/>
|
|
846
|
+
````
|
|
847
|
+
|
|
848
|
+
**Props Reference:**
|
|
849
|
+
|
|
850
|
+
| Prop | Type | Default | Description |
|
|
851
|
+
| ------------------ | ------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------- |
|
|
852
|
+
| `options` | `ComboBoxOption[]` | `[]` | Static list of options |
|
|
853
|
+
| `value` | `string \| number \| null` | - | Selected value |
|
|
854
|
+
| `onChange` | `(value, option) => void` | - | Callback when selection changes |
|
|
855
|
+
| `onSearch` | `(searchText) => Promise<ComboBoxOption[]>` | - | Async callback for dynamic suggestions |
|
|
856
|
+
| `placeholder` | `string` | `"Select an option..."` | Placeholder text |
|
|
857
|
+
| `disabled` | `boolean` | `false` | Whether the combo box is disabled |
|
|
858
|
+
| `clearable` | `boolean` | `false` | Show clear button to deselect |
|
|
859
|
+
| `className` | `string` | - | Custom class name |
|
|
860
|
+
| `renderOption` | `(option) => ReactNode` | - | Custom option renderer |
|
|
861
|
+
| `renderValue` | `(option) => ReactNode` | - | Custom selected value renderer |
|
|
862
|
+
| `loading` | `boolean` | `false` | Show loading indicator |
|
|
863
|
+
| `maxHeight` | `string` | `"300px"` | Maximum height for dropdown |
|
|
864
|
+
| `debounceMs` | `number` | `300` | Debounce delay for async search (ms) |
|
|
865
|
+
| `searchable` | `boolean` | `true` | Enable search/filter input |
|
|
866
|
+
| `noOptionsMessage` | `string` | `"No options found"` | Message when no options match |
|
|
867
|
+
| `ariaLabel` | `string` | - | ARIA label for accessibility |
|
|
868
|
+
| `enableCache` | `boolean` | `false` | Enable in-memory caching of identical search queries |
|
|
869
|
+
| `cacheTTL` | `number` | - | Time-to-live for cache entries in milliseconds (no expiry if omitted) |
|
|
870
|
+
| `refreshOnOpen` | `boolean` | `false` | Re-fetch results on dropdown open even if search text hasn't changed |
|
|
871
|
+
| `onClearCache` | `(clearFn) => void` | - | Receives a function to clear the internal cache from the parent component |
|
|
872
|
+
| `onForceRefresh` | `(forceFn) => void` | - | Receives a function to force refresh the current search results from the parent |
|
|
873
|
+
|
|
874
|
+
**ComboBoxOption Interface:**
|
|
875
|
+
|
|
876
|
+
| Field | Type | Description |
|
|
877
|
+
| ---------- | ------------------ | -------------------------------------------------- |
|
|
878
|
+
| `value` | `string \| number` | Unique value for the option |
|
|
879
|
+
| `label` | `string` | Display label |
|
|
880
|
+
| `icon` | `React.ReactNode` | Optional icon (string/emoji or React component) |
|
|
881
|
+
| `disabled` | `boolean` | Whether the option is non-selectable (for headers) |
|
|
882
|
+
| `level` | `number` | Nesting level for hierarchical display |
|
|
883
|
+
| `children` | `ComboBoxOption[]` | Child options for tree structures |
|
|
884
|
+
| `data` | `unknown` | Optional custom data |
|
|
885
|
+
|
|
886
|
+
**Keyboard Navigation:**
|
|
887
|
+
|
|
888
|
+
- `โ` / `โ` - Navigate through options
|
|
889
|
+
- `Enter` - Select highlighted option / Toggle dropdown
|
|
890
|
+
- `Escape` - Close dropdown
|
|
891
|
+
- `Tab` - Close dropdown and move focus
|
|
892
|
+
|
|
893
|
+
**Accessibility:**
|
|
894
|
+
|
|
895
|
+
- Full ARIA support (`role="combobox"`, `aria-expanded`, `aria-selected`, etc.)
|
|
896
|
+
- Keyboard navigation
|
|
897
|
+
- Screen reader friendly
|
|
898
|
+
- Focus management
|
|
899
|
+
|
|
900
|
+
### UIForgeActivityStream / UIForgeActivityStreamEnhanced
|
|
901
|
+
|
|
902
|
+
A GitHub-inspired activity stream component for displaying user activities, events, and notifications with intelligent grouping, timeline visualization, and theming support.
|
|
903
|
+
|
|
904
|
+
**Key Features:**
|
|
905
|
+
|
|
906
|
+
- **Smart Event Grouping** - Automatically combines consecutive events of the same type
|
|
907
|
+
- **Nested Hierarchical Grouping** - Sub-groups events by repository or context
|
|
908
|
+
- **Timeline Visualization** - Vertical line with markers showing event flow
|
|
909
|
+
- **Date Separators** - Month/year labels between time periods
|
|
910
|
+
- **Monochrome Icons** - Clean, GitHub-style SVG icons for each event type
|
|
911
|
+
- **Expandable Content** - Click grouped events to see individual items
|
|
912
|
+
- **Infinite Scroll** - "Show more" bar for progressive loading
|
|
913
|
+
- **Light/Dark Themes** - Seamless theme switching with CSS variables
|
|
914
|
+
- **Fully Accessible** - Keyboard navigation, ARIA attributes, screen reader support
|
|
915
|
+
- **Responsive Design** - Adapts to mobile and desktop viewports
|
|
916
|
+
|
|
917
|
+
```tsx
|
|
918
|
+
import { UIForgeActivityStreamEnhanced, ActivityEvent } from '@appforgeapps/uiforge'
|
|
919
|
+
|
|
920
|
+
const events: ActivityEvent[] = [
|
|
921
|
+
{
|
|
922
|
+
id: 1,
|
|
923
|
+
type: 'pr',
|
|
924
|
+
title: 'Added dark mode support',
|
|
925
|
+
description: 'Implemented comprehensive dark mode theming',
|
|
926
|
+
timestamp: new Date(),
|
|
927
|
+
metadata: { repository: 'myapp/frontend' },
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
id: 2,
|
|
931
|
+
type: 'commit',
|
|
932
|
+
title: 'Fixed authentication bug',
|
|
933
|
+
timestamp: new Date(),
|
|
934
|
+
metadata: { repository: 'myapp/backend' },
|
|
935
|
+
},
|
|
936
|
+
// ... more events
|
|
937
|
+
]
|
|
938
|
+
|
|
939
|
+
<UIForgeActivityStreamEnhanced
|
|
940
|
+
events={events}
|
|
941
|
+
theme="dark"
|
|
942
|
+
enableGrouping={true}
|
|
943
|
+
showTimeline={true}
|
|
944
|
+
showDateSeparators={true}
|
|
945
|
+
groupingThreshold={2}
|
|
946
|
+
onLoadMore={() => loadMoreEvents()}
|
|
947
|
+
pagination={{ currentPage: 0, pageSize: 20, hasMore: true }}
|
|
948
|
+
/>
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
**Smart Grouping Example:**
|
|
952
|
+
|
|
953
|
+
When you have consecutive events of the same type, they're automatically grouped:
|
|
954
|
+
- Individual: "Created pull request #123", "Created pull request #124", "Created pull request #125"
|
|
955
|
+
- Grouped: "Created 3 pull requests in myapp/frontend" (expandable to see individual PRs)
|
|
956
|
+
|
|
957
|
+
**Nested Grouping Example:**
|
|
958
|
+
|
|
959
|
+
When grouped events span multiple repositories:
|
|
960
|
+
- Top level: "Created 6 pull requests in 3 repositories"
|
|
961
|
+
- Sub-group: "Created 2 pull requests in myapp/frontend"
|
|
962
|
+
- Sub-group: "Created 1 pull request in myapp/docs"
|
|
963
|
+
- Sub-group: "Created 3 pull requests in myapp/backend"
|
|
964
|
+
|
|
965
|
+
**Props Reference:**
|
|
966
|
+
|
|
967
|
+
| Prop | Type | Default | Description |
|
|
968
|
+
| ---------------------- | --------------------------- | ------- | ---------------------------------------------- |
|
|
969
|
+
| `events` | `ActivityEvent[]` | - | Array of activity events to display |
|
|
970
|
+
| `theme` | `'light' \| 'dark'` | `light` | Theme variant |
|
|
971
|
+
| `enableGrouping` | `boolean` | `true` | Enable smart event grouping |
|
|
972
|
+
| `groupingThreshold` | `number` | `2` | Minimum consecutive events to trigger grouping |
|
|
973
|
+
| `showTimeline` | `boolean` | `true` | Show vertical timeline line |
|
|
974
|
+
| `showDateSeparators` | `boolean` | `true` | Show month/year date separators |
|
|
975
|
+
| `showLoadMore` | `boolean` | `true` | Show "Show more" bar |
|
|
976
|
+
| `loading` | `boolean` | `false` | Display loading indicator |
|
|
977
|
+
| `onLoadMore` | `() => void` | - | Callback when "Show more" is clicked |
|
|
978
|
+
| `pagination` | `ActivityStreamPagination` | - | Pagination configuration |
|
|
979
|
+
| `maxHeight` | `string` | - | Maximum height (CSS value) |
|
|
980
|
+
| `showMoreThreshold` | `number` | `100` | Distance from bottom to show "Show more" (px) |
|
|
981
|
+
| `initiallyExpandedAll` | `boolean` | `false` | Expand all events initially |
|
|
982
|
+
| `emptyMessage` | `string` | - | Empty state message |
|
|
983
|
+
| `onToggleExpand` | `(id, expanded) => void` | - | Callback when event is expanded/collapsed |
|
|
984
|
+
|
|
985
|
+
**ActivityEvent Interface:**
|
|
986
|
+
|
|
987
|
+
| Field | Type | Description |
|
|
988
|
+
| -------------- | --------------------------- | ------------------------------------------ |
|
|
989
|
+
| `id` | `string \| number` | Unique identifier |
|
|
990
|
+
| `type` | `string` | Event type (e.g., 'commit', 'pr', 'issue') |
|
|
991
|
+
| `title` | `string` | Event title/description |
|
|
992
|
+
| `description` | `string` | Optional detailed description |
|
|
993
|
+
| `timestamp` | `Date \| string` | Event timestamp |
|
|
994
|
+
| `icon` | `React.ReactNode` | Optional custom icon |
|
|
995
|
+
| `metadata` | `Record<string, unknown>` | Optional metadata (e.g., repository name) |
|
|
996
|
+
|
|
997
|
+
**Supported Event Types (with default icons):**
|
|
998
|
+
|
|
999
|
+
- `commit` - Code commits
|
|
1000
|
+
- `pr` - Pull requests
|
|
1001
|
+
- `issue` - Issues
|
|
1002
|
+
- `comment` - Comments
|
|
1003
|
+
- `star` - Repository stars
|
|
1004
|
+
- `fork` - Repository forks
|
|
1005
|
+
- `merge` - Merged pull requests
|
|
1006
|
+
- `release` - Version releases
|
|
1007
|
+
- `deploy` - Deployments
|
|
1008
|
+
|
|
1009
|
+
See `examples/ActivityStreamExample.tsx` for a complete interactive demo.
|
|
1010
|
+
|
|
1011
|
+
### UIForgeVideo / UIForgeVideoPreview
|
|
1012
|
+
|
|
1013
|
+
Video components for embedding YouTube and Vimeo videos with interactive overlays and preview functionality.
|
|
1014
|
+
|
|
1015
|
+
**UIForgeVideo Features:**
|
|
1016
|
+
|
|
1017
|
+
- **Video Embedding** - Supports both YouTube and Vimeo video embeds
|
|
1018
|
+
- **Lazy Loading** - Video player loads only when user clicks to play
|
|
1019
|
+
- **Custom Thumbnails** - Use custom thumbnail images or provider defaults
|
|
1020
|
+
- **Overlay Interaction** - Clickable overlay with customizable play icon
|
|
1021
|
+
- **Event Emission** - Fires callback when video starts playing for analytics/tracking
|
|
1022
|
+
- **Responsive Design** - Adjustable aspect ratios and mobile-friendly
|
|
1023
|
+
- **Accessibility** - Proper ARIA labels and keyboard navigation
|
|
1024
|
+
|
|
1025
|
+
**UIForgeVideoPreview Features:**
|
|
1026
|
+
|
|
1027
|
+
- **Compact Display** - Small preview component with title and icon
|
|
1028
|
+
- **Interactive** - Optional click handler for navigation
|
|
1029
|
+
- **Customizable Icons** - Support for custom icons or emojis
|
|
1030
|
+
- **Lightweight** - Perfect for video lists and catalogs
|
|
1031
|
+
|
|
1032
|
+
```tsx
|
|
1033
|
+
import { UIForgeVideo, UIForgeVideoPreview } from '@appforgeapps/uiforge'
|
|
1034
|
+
|
|
1035
|
+
// YouTube video with event tracking
|
|
1036
|
+
<UIForgeVideo
|
|
1037
|
+
title="Introduction to React"
|
|
1038
|
+
description="Learn React basics in this comprehensive tutorial"
|
|
1039
|
+
youtubeId="dQw4w9WgXcQ"
|
|
1040
|
+
onPlay={(videoId, provider) => {
|
|
1041
|
+
console.log(`Playing ${provider} video: ${videoId}`)
|
|
1042
|
+
trackAnalytics('video_play', { videoId, provider })
|
|
1043
|
+
}}
|
|
1044
|
+
/>
|
|
1045
|
+
|
|
1046
|
+
// Vimeo video
|
|
1047
|
+
<UIForgeVideo
|
|
1048
|
+
title="Beautiful Nature Footage"
|
|
1049
|
+
description="Stunning visuals from around the world"
|
|
1050
|
+
vimeoId="76979871"
|
|
1051
|
+
onPlay={(videoId, provider) => {
|
|
1052
|
+
console.log('Video started')
|
|
1053
|
+
}}
|
|
1054
|
+
/>
|
|
1055
|
+
|
|
1056
|
+
// Custom thumbnail and aspect ratio
|
|
1057
|
+
<UIForgeVideo
|
|
1058
|
+
title="Classic Format Video"
|
|
1059
|
+
youtubeId="abc123"
|
|
1060
|
+
thumbnailUrl="https://example.com/custom-thumb.jpg"
|
|
1061
|
+
aspectRatio="4/3"
|
|
1062
|
+
onPlay={handlePlay}
|
|
1063
|
+
/>
|
|
1064
|
+
|
|
1065
|
+
// Custom overlay icon
|
|
1066
|
+
<UIForgeVideo
|
|
1067
|
+
title="Video with Custom Play Button"
|
|
1068
|
+
youtubeId="xyz789"
|
|
1069
|
+
overlayIcon={<span style={{ fontSize: '64px' }}>โถ๏ธ</span>}
|
|
1070
|
+
onPlay={handlePlay}
|
|
1071
|
+
/>
|
|
1072
|
+
|
|
1073
|
+
// Video preview component
|
|
1074
|
+
<UIForgeVideoPreview
|
|
1075
|
+
title="Tutorial: Getting Started"
|
|
1076
|
+
icon={<span>๐</span>}
|
|
1077
|
+
onClick={() => navigateToVideo('tutorial-123')}
|
|
1078
|
+
/>
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
**UIForgeVideo Props:**
|
|
1082
|
+
|
|
1083
|
+
| Prop | Type | Default | Description |
|
|
1084
|
+
| -------------- | ------------------------------------------- | ---------- | --------------------------------------------- |
|
|
1085
|
+
| `title` | `string` | required | Video title |
|
|
1086
|
+
| `description` | `string` | - | Optional video description |
|
|
1087
|
+
| `youtubeId` | `string` | - | YouTube video ID (required if no vimeoId) |
|
|
1088
|
+
| `vimeoId` | `string` | - | Vimeo video ID (required if no youtubeId) |
|
|
1089
|
+
| `thumbnailUrl` | `string` | - | Custom thumbnail URL (optional) |
|
|
1090
|
+
| `onPlay` | `(videoId, provider) => void` | - | Callback when video starts playing |
|
|
1091
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
1092
|
+
| `overlayIcon` | `React.ReactNode` | Play icon | Custom overlay icon |
|
|
1093
|
+
| `aspectRatio` | `string` | `"16/9"` | Video aspect ratio (CSS value) |
|
|
1094
|
+
|
|
1095
|
+
**UIForgeVideoPreview Props:**
|
|
1096
|
+
|
|
1097
|
+
| Prop | Type | Default | Description |
|
|
1098
|
+
| ----------- | ----------------- | -------- | ------------------------------ |
|
|
1099
|
+
| `title` | `string` | required | Video title |
|
|
1100
|
+
| `icon` | `React.ReactNode` | - | Optional custom icon |
|
|
1101
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
1102
|
+
| `onClick` | `() => void` | - | Click handler (makes it interactive) |
|
|
1103
|
+
|
|
1104
|
+
**Use Cases:**
|
|
1105
|
+
|
|
1106
|
+
- Video tutorials and educational content
|
|
1107
|
+
- Product demos and walkthroughs
|
|
1108
|
+
- Marketing videos and testimonials
|
|
1109
|
+
- Video galleries and catalogs
|
|
1110
|
+
- Course content and lessons
|
|
1111
|
+
- Conference talks and presentations
|
|
1112
|
+
|
|
1113
|
+
See `examples/VideoExample.tsx` for a complete interactive demo with multiple examples.
|
|
1114
|
+
|
|
1115
|
+
## Theming
|
|
1116
|
+
|
|
1117
|
+
UIForge components support comprehensive theming through CSS variables. See [THEMING.md](./THEMING.md) for a complete guide on:
|
|
1118
|
+
|
|
1119
|
+
- Light and dark theme support
|
|
1120
|
+
- Custom theme creation
|
|
1121
|
+
- CSS variable reference
|
|
1122
|
+
- System preference detection
|
|
1123
|
+
- Advanced customization techniques
|
|
1124
|
+
|
|
1125
|
+
Quick example:
|
|
1126
|
+
|
|
1127
|
+
```tsx
|
|
1128
|
+
import { useState } from 'react'
|
|
1129
|
+
|
|
1130
|
+
function App() {
|
|
1131
|
+
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
|
1132
|
+
|
|
1133
|
+
return (
|
|
1134
|
+
<div>
|
|
1135
|
+
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
|
|
1136
|
+
Toggle Theme
|
|
1137
|
+
</button>
|
|
1138
|
+
<UIForgeActivityStreamEnhanced events={events} theme={theme} />
|
|
1139
|
+
</div>
|
|
1140
|
+
)
|
|
1141
|
+
}
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
## Development
|
|
1145
|
+
|
|
1146
|
+
### Prerequisites
|
|
1147
|
+
|
|
1148
|
+
- Node.js 18+ and npm
|
|
1149
|
+
|
|
1150
|
+
### Getting Started
|
|
1151
|
+
|
|
1152
|
+
1. Clone the repository:
|
|
1153
|
+
|
|
1154
|
+
```bash
|
|
1155
|
+
git clone https://github.com/chriscase/UIForge.git
|
|
1156
|
+
cd UIForge
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
2. Install dependencies:
|
|
1160
|
+
|
|
1161
|
+
```bash
|
|
1162
|
+
npm install
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
3. Start the development server:
|
|
1166
|
+
|
|
1167
|
+
```bash
|
|
1168
|
+
npm run dev
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
This will start a local development server where you can see and interact with the components.
|
|
1172
|
+
|
|
1173
|
+
### Available Scripts
|
|
1174
|
+
|
|
1175
|
+
- `npm run dev` - Start the development server
|
|
1176
|
+
- `npm run build` - Build the library for production
|
|
1177
|
+
- `npm run preview` - Preview the production build
|
|
1178
|
+
- `npm test` - Run tests in watch mode
|
|
1179
|
+
- `npm run test:ui` - Run tests with UI
|
|
1180
|
+
- `npm run test:coverage` - Generate test coverage report
|
|
1181
|
+
- `npm run lint` - Lint the codebase
|
|
1182
|
+
- `npm run lint:fix` - Fix linting issues
|
|
1183
|
+
- `npm run format` - Format code with Prettier
|
|
1184
|
+
- `npm run format:check` - Check code formatting
|
|
1185
|
+
|
|
1186
|
+
### Testing
|
|
1187
|
+
|
|
1188
|
+
Tests are written using Vitest and React Testing Library:
|
|
1189
|
+
|
|
1190
|
+
```bash
|
|
1191
|
+
npm test
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
### Building
|
|
1195
|
+
|
|
1196
|
+
Build the library for production:
|
|
1197
|
+
|
|
1198
|
+
```bash
|
|
1199
|
+
npm run build
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
The built files will be in the `dist` directory.
|
|
1203
|
+
|
|
1204
|
+
### Development with GitHub Tools
|
|
1205
|
+
|
|
1206
|
+
This repository is optimized for development using:
|
|
1207
|
+
|
|
1208
|
+
- **GitHub Codespaces** - One-click cloud development environment
|
|
1209
|
+
- **GitHub Copilot** - AI-powered code completion
|
|
1210
|
+
|
|
1211
|
+
Simply open this repository in GitHub Codespaces to get started immediately with all dependencies pre-installed!
|
|
1212
|
+
|
|
1213
|
+
## Contributing
|
|
1214
|
+
|
|
1215
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines.
|
|
1216
|
+
|
|
1217
|
+
Quick start:
|
|
1218
|
+
|
|
1219
|
+
1. Fork the repository
|
|
1220
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
1221
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
1222
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1223
|
+
5. Open a Pull Request
|
|
1224
|
+
|
|
1225
|
+
## For Maintainers
|
|
1226
|
+
|
|
1227
|
+
If you're a maintainer looking to publish releases or manage the project, please see [MAINTAINER_INSTRUCTIONS.md](./MAINTAINER_INSTRUCTIONS.md) for comprehensive publishing and maintenance workflows.
|
|
1228
|
+
|
|
1229
|
+
## License
|
|
1230
|
+
|
|
1231
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1232
|
+
|
|
1233
|
+
## Author
|
|
1234
|
+
|
|
1235
|
+
**chriscase**
|
|
1236
|
+
|
|
1237
|
+
## Acknowledgments
|
|
1238
|
+
|
|
1239
|
+
- Built with [Vite](https://vitejs.dev/)
|
|
1240
|
+
- Tested with [Vitest](https://vitest.dev/)
|
|
1241
|
+
- Styled with CSS
|