@manyrows/appkit-react 0.1.5 → 0.1.7
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 +372 -37
- package/dist/index.cjs +4321 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +444 -4
- package/dist/index.d.ts +444 -4
- package/dist/index.js +4298 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,59 +1,394 @@
|
|
|
1
|
-
|
|
2
|
-
import { AppKit, AppKitAuthed, useAppKit } from "@manyrows/appkit-react";
|
|
1
|
+
# @manyrows/appkit-react
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
const { snapshot, logout } = useAppKit();
|
|
3
|
+
React SDK for Manyrows AppKit. Provides authentication, real-time data, and image management for customer-facing apps.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @manyrows/appkit-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { AppKit, AppKitAuthed, useUser, UserButton } from "@manyrows/appkit-react";
|
|
15
|
+
|
|
16
|
+
function MyApp() {
|
|
17
|
+
const user = useUser();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
<header style={{ display: "flex", justifyContent: "space-between", padding: 16 }}>
|
|
22
|
+
<h1>My App</h1>
|
|
23
|
+
<UserButton />
|
|
24
|
+
</header>
|
|
25
|
+
{user && <p>Welcome, {user.name || user.email}</p>}
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
13
28
|
}
|
|
14
29
|
|
|
15
30
|
export default function Page() {
|
|
16
|
-
return (
|
|
17
|
-
<
|
|
18
|
-
<AppKit
|
|
19
|
-
src={`${baseURL}/appkit/assets/appkit.js`}
|
|
20
|
-
workspace="acme"
|
|
21
|
-
project="drums"
|
|
22
|
-
/>
|
|
23
|
-
|
|
31
|
+
return (
|
|
32
|
+
<AppKit workspace="acme" appId="your-app-id">
|
|
24
33
|
<AppKitAuthed fallback={null}>
|
|
25
|
-
<
|
|
34
|
+
<MyApp />
|
|
26
35
|
</AppKitAuthed>
|
|
36
|
+
</AppKit>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Only `workspace` and `appId` are required. The runtime script loads automatically.
|
|
42
|
+
|
|
43
|
+
### Convenience Hooks
|
|
44
|
+
|
|
45
|
+
Access common data without drilling into `snapshot`:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import {
|
|
49
|
+
useUser, // user's account (email, name)
|
|
50
|
+
useRoles, // string[] of roles
|
|
51
|
+
usePermissions, // string[] of permissions
|
|
52
|
+
usePermission, // check a single permission: usePermission("edit")
|
|
53
|
+
useRole, // check a single role: useRole("admin")
|
|
54
|
+
useFeatureFlags, // all feature flags
|
|
55
|
+
useFeatureFlag, // check one flag: useFeatureFlag("dark-mode")
|
|
56
|
+
useConfig, // all config values
|
|
57
|
+
useConfigValue, // single config: useConfigValue("api-url", "default")
|
|
58
|
+
useToken, // JWT token for API calls
|
|
59
|
+
} from "@manyrows/appkit-react";
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Auth Patterns
|
|
63
|
+
|
|
64
|
+
**Option A (recommended):** Use `<AppKitAuthed>` to gate authenticated content:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<AppKit workspace="acme" appId="your-app-id">
|
|
68
|
+
<AppKitAuthed fallback={null}>
|
|
69
|
+
<MyApp />
|
|
70
|
+
</AppKitAuthed>
|
|
71
|
+
</AppKit>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Option B:** Gate manually with `useAppKit()`:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<AppKit workspace="acme" appId="your-app-id">
|
|
78
|
+
{useAppKit().isAuthenticated ? <MyApp /> : null}
|
|
79
|
+
</AppKit>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Images
|
|
85
|
+
|
|
86
|
+
The SDK includes a full image management system — upload, browse, display, edit, and delete — backed by the `manyrows-images` service.
|
|
87
|
+
|
|
88
|
+
### Setup
|
|
89
|
+
|
|
90
|
+
Images work automatically when the workspace has an `imagesBaseURL` configured. All image hooks and components read auth and config from the AppKit context, so they must be rendered inside `<AppKitAuthed>`.
|
|
91
|
+
|
|
92
|
+
### useImages
|
|
93
|
+
|
|
94
|
+
Hook for listing, searching, editing, and deleting images.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { useImages } from "@manyrows/appkit-react";
|
|
98
|
+
|
|
99
|
+
function Gallery() {
|
|
100
|
+
const {
|
|
101
|
+
images, // ImageResource[]
|
|
102
|
+
loading,
|
|
103
|
+
error,
|
|
104
|
+
page,
|
|
105
|
+
pageCount,
|
|
106
|
+
total,
|
|
107
|
+
setPage,
|
|
108
|
+
setQuery, // search by title
|
|
109
|
+
refetch,
|
|
110
|
+
removeImage, // (imageId) => Promise<void>
|
|
111
|
+
updateImage, // (imageId, { title, description }) => Promise<void>
|
|
112
|
+
available, // false if images service not configured
|
|
113
|
+
} = useImages({ page: 0, pageSize: 20 });
|
|
114
|
+
|
|
115
|
+
if (!available) return null;
|
|
116
|
+
// ...
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Options:**
|
|
121
|
+
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
|------|------|---------|-------------|
|
|
124
|
+
| `page` | number | 0 | Starting page (zero-indexed) |
|
|
125
|
+
| `pageSize` | number | 50 | Items per page |
|
|
126
|
+
| `q` | string | — | Search query |
|
|
127
|
+
| `enabled` | boolean | true | Enable/disable fetching |
|
|
128
|
+
|
|
129
|
+
### useImageUpload
|
|
130
|
+
|
|
131
|
+
Hook for uploading images with progress tracking.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { useImageUpload } from "@manyrows/appkit-react";
|
|
135
|
+
|
|
136
|
+
function Uploader() {
|
|
137
|
+
const { upload, cancel, progress, reset, available } = useImageUpload();
|
|
138
|
+
|
|
139
|
+
const handleUpload = async (file: File) => {
|
|
140
|
+
await upload({
|
|
141
|
+
file,
|
|
142
|
+
title: "My image",
|
|
143
|
+
description: "Optional description",
|
|
144
|
+
variants: "sq200,w400,w800",
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div>
|
|
150
|
+
<p>Status: {progress.status}</p>
|
|
151
|
+
<p>Progress: {progress.progress}%</p>
|
|
152
|
+
<button onClick={cancel}>Cancel</button>
|
|
27
153
|
</div>
|
|
28
|
-
);
|
|
154
|
+
);
|
|
29
155
|
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Progress state:**
|
|
159
|
+
|
|
160
|
+
| Field | Type | Description |
|
|
161
|
+
|-------|------|-------------|
|
|
162
|
+
| `status` | `"idle" \| "uploading" \| "success" \| "error"` | Current state |
|
|
163
|
+
| `progress` | number | 0–100 percentage |
|
|
164
|
+
| `bytesUploaded` | number | Bytes transferred |
|
|
165
|
+
| `bytesTotal` | number | Total file size |
|
|
166
|
+
| `error` | string \| null | Error message |
|
|
30
167
|
|
|
31
|
-
|
|
168
|
+
### MrImage
|
|
32
169
|
|
|
33
|
-
|
|
170
|
+
Responsive image component with automatic variant selection.
|
|
34
171
|
|
|
35
|
-
|
|
172
|
+
```tsx
|
|
173
|
+
import { MrImage } from "@manyrows/appkit-react";
|
|
36
174
|
|
|
37
|
-
|
|
175
|
+
// Auto-selects best variant for dimensions + device DPR
|
|
176
|
+
<MrImage image={image} width={400} height={300} />
|
|
38
177
|
|
|
39
|
-
|
|
178
|
+
// Explicit variant
|
|
179
|
+
<MrImage image={image} variant="sq200" />
|
|
40
180
|
|
|
41
|
-
|
|
181
|
+
// Full-size original
|
|
182
|
+
<MrImage image={image} />
|
|
183
|
+
```
|
|
42
184
|
|
|
43
|
-
|
|
44
|
-
<AppKitAuthed fallback={null}>
|
|
45
|
-
<CustomerApp />
|
|
46
|
-
</AppKitAuthed>
|
|
185
|
+
**Props:**
|
|
47
186
|
|
|
48
|
-
|
|
187
|
+
| Prop | Type | Default | Description |
|
|
188
|
+
|------|------|---------|-------------|
|
|
189
|
+
| `image` | ImageResource | required | Image to display |
|
|
190
|
+
| `width` | number | — | Display width (used for variant selection) |
|
|
191
|
+
| `height` | number | — | Display height |
|
|
192
|
+
| `variant` | string | — | Explicit variant name (disables auto-selection) |
|
|
193
|
+
| `alt` | string | image.title | Alt text |
|
|
194
|
+
| `className` | string | — | CSS class |
|
|
195
|
+
| `style` | CSSProperties | — | Inline styles |
|
|
196
|
+
| `loading` | `"lazy" \| "eager"` | `"lazy"` | Browser loading strategy |
|
|
197
|
+
| `objectFit` | CSSProperties["objectFit"] | `"cover"` | CSS object-fit |
|
|
198
|
+
| `sizes` | string | — | Responsive sizes attribute |
|
|
199
|
+
| `onLoad` | () => void | — | Load callback |
|
|
200
|
+
| `onError` | () => void | — | Error callback |
|
|
49
201
|
|
|
50
|
-
|
|
202
|
+
Generates a `srcSet` from available width-based variants for responsive loading.
|
|
51
203
|
|
|
52
|
-
|
|
204
|
+
### ImageUploader
|
|
53
205
|
|
|
54
|
-
|
|
206
|
+
Drop-in upload form with drag-and-drop, preview, variant selection, and progress.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { ImageUploader } from "@manyrows/appkit-react";
|
|
210
|
+
|
|
211
|
+
<ImageUploader
|
|
212
|
+
onUpload={() => console.log("Done!")}
|
|
213
|
+
onError={(err) => console.error(err)}
|
|
214
|
+
defaultTitle="Untitled"
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Props:**
|
|
219
|
+
|
|
220
|
+
| Prop | Type | Default | Description |
|
|
221
|
+
|------|------|---------|-------------|
|
|
222
|
+
| `onUpload` | () => void | — | Called on successful upload |
|
|
223
|
+
| `onError` | (error: string) => void | — | Called on upload error |
|
|
224
|
+
| `defaultTitle` | string | — | Pre-fill title |
|
|
225
|
+
| `defaultDescription` | string | `""` | Pre-fill description |
|
|
226
|
+
| `variants` | string | `"sq200,w400,w800"` | Variant names to generate |
|
|
227
|
+
| `showFields` | boolean | true | Show title/description inputs |
|
|
228
|
+
| `showVariantPicker` | boolean | false | Show advanced variant selector |
|
|
229
|
+
| `accept` | string | `"image/png,image/jpeg,..."` | Accepted MIME types |
|
|
230
|
+
| `maxSize` | number | 4194304 | Max file size in bytes |
|
|
231
|
+
| `label` | string | `"Drop an image here or click to select"` | Drop zone text |
|
|
232
|
+
| `disabled` | boolean | false | Disable all interactions |
|
|
233
|
+
| `className` | string | — | CSS class |
|
|
234
|
+
| `style` | CSSProperties | — | Inline styles |
|
|
235
|
+
|
|
236
|
+
Features:
|
|
237
|
+
- Drag-and-drop or click to select
|
|
238
|
+
- Image preview before upload
|
|
239
|
+
- File size validation
|
|
240
|
+
- Progress bar during upload
|
|
241
|
+
- Success toast (auto-dismisses after 4s)
|
|
242
|
+
- Auto-populates title from filename
|
|
243
|
+
- Collapsible "Advanced" section for variant selection
|
|
244
|
+
|
|
245
|
+
### ImagePicker
|
|
246
|
+
|
|
247
|
+
Image grid browser with search, pagination, and optional inline actions.
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { ImagePicker } from "@manyrows/appkit-react";
|
|
251
|
+
|
|
252
|
+
// Inline grid
|
|
253
|
+
<ImagePicker
|
|
254
|
+
onSelect={(image) => setSelected(image)}
|
|
255
|
+
pageSize={20}
|
|
256
|
+
showSearch
|
|
257
|
+
showActions
|
|
258
|
+
/>
|
|
259
|
+
|
|
260
|
+
// Modal overlay
|
|
261
|
+
<ImagePicker
|
|
262
|
+
mode="modal"
|
|
263
|
+
onSelect={handleSelect}
|
|
264
|
+
onClose={() => setOpen(false)}
|
|
265
|
+
/>
|
|
266
|
+
|
|
267
|
+
// Refresh after upload
|
|
268
|
+
const [uploadCount, setUploadCount] = useState(0);
|
|
269
|
+
<ImageUploader onUpload={() => setUploadCount(c => c + 1)} />
|
|
270
|
+
<ImagePicker refreshSignal={uploadCount} />
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Props:**
|
|
274
|
+
|
|
275
|
+
| Prop | Type | Default | Description |
|
|
276
|
+
|------|------|---------|-------------|
|
|
277
|
+
| `onSelect` | (image: ImageResource) => void | — | Called when an image is clicked |
|
|
278
|
+
| `onClose` | () => void | — | Called when modal is closed |
|
|
279
|
+
| `mode` | `"modal" \| "inline"` | `"inline"` | Display mode |
|
|
280
|
+
| `pageSize` | number | 20 | Images per page |
|
|
281
|
+
| `showSearch` | boolean | true | Show search bar |
|
|
282
|
+
| `searchPlaceholder` | string | `"Search images..."` | Placeholder text |
|
|
283
|
+
| `selectedImageId` | string | — | Highlight selected image |
|
|
284
|
+
| `columns` | number | 4 | Grid columns |
|
|
285
|
+
| `showActions` | boolean | false | Show Info/Edit/Delete on hover |
|
|
286
|
+
| `refreshSignal` | number | — | Increment to trigger refetch |
|
|
287
|
+
| `className` | string | — | CSS class |
|
|
288
|
+
| `style` | CSSProperties | — | Inline styles |
|
|
289
|
+
|
|
290
|
+
### ImageDetails
|
|
291
|
+
|
|
292
|
+
Full-screen detail modal for a single image. Shows variant selector, metadata, copy-to-clipboard, and action buttons.
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
import { ImageDetails } from "@manyrows/appkit-react";
|
|
296
|
+
|
|
297
|
+
<ImageDetails
|
|
298
|
+
image={selectedImage}
|
|
299
|
+
onClose={() => setSelected(null)}
|
|
300
|
+
onEdit={(img) => openEditor(img)}
|
|
301
|
+
onDelete={(img) => confirmDelete(img)}
|
|
302
|
+
/>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Props:**
|
|
306
|
+
|
|
307
|
+
| Prop | Type | Default | Description |
|
|
308
|
+
|------|------|---------|-------------|
|
|
309
|
+
| `image` | ImageResource | required | Image to display |
|
|
310
|
+
| `onClose` | () => void | required | Close callback |
|
|
311
|
+
| `onEdit` | (image: ImageResource) => void | — | Edit button callback |
|
|
312
|
+
| `onDelete` | (image: ImageResource) => void | — | Delete button callback |
|
|
313
|
+
| `className` | string | — | CSS class |
|
|
314
|
+
| `style` | CSSProperties | — | Inline styles |
|
|
315
|
+
|
|
316
|
+
Displays:
|
|
317
|
+
- Large image preview with variant selector tabs
|
|
318
|
+
- File size, dimensions, format
|
|
319
|
+
- Image ID and URL (both copyable)
|
|
320
|
+
- Upload date and description
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Types
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
interface ImageResource {
|
|
328
|
+
imageId: string;
|
|
329
|
+
title: string;
|
|
330
|
+
description: string;
|
|
331
|
+
originalName: string;
|
|
332
|
+
uploadedAt: string;
|
|
333
|
+
uploadedBy?: string;
|
|
334
|
+
variants: ImageVariant[];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
interface ImageVariant {
|
|
338
|
+
id: string;
|
|
339
|
+
variant: string; // "sq200", "w800", "orig", etc.
|
|
340
|
+
s3Key: string;
|
|
341
|
+
content_type: string;
|
|
342
|
+
format: string;
|
|
343
|
+
size_bytes: number;
|
|
344
|
+
sha256_hex: string;
|
|
345
|
+
ext: string;
|
|
346
|
+
width: number;
|
|
347
|
+
height: number;
|
|
348
|
+
etag: string;
|
|
349
|
+
versionId: string;
|
|
350
|
+
objectURL: string; // presigned CloudFront URL
|
|
351
|
+
expiresAt: string;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
interface ImageListResponse {
|
|
355
|
+
images: ImageResource[];
|
|
356
|
+
page: number;
|
|
357
|
+
pageSize: number;
|
|
358
|
+
total: number;
|
|
359
|
+
pageCount: number;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
interface ImageUploadOptions {
|
|
363
|
+
file: File;
|
|
364
|
+
title: string;
|
|
365
|
+
description?: string;
|
|
366
|
+
variants?: string; // comma-separated, e.g. "sq200,w400,w800"
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
interface ImageUpdateOptions {
|
|
370
|
+
title: string;
|
|
371
|
+
description?: string;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
interface UploadProgress {
|
|
375
|
+
status: "idle" | "uploading" | "success" | "error";
|
|
376
|
+
progress: number;
|
|
377
|
+
bytesUploaded: number;
|
|
378
|
+
bytesTotal: number;
|
|
379
|
+
error: string | null;
|
|
380
|
+
}
|
|
381
|
+
```
|
|
55
382
|
|
|
56
|
-
|
|
383
|
+
## Available Variants
|
|
57
384
|
|
|
58
|
-
|
|
59
|
-
|
|
385
|
+
| Name | Description |
|
|
386
|
+
|------|-------------|
|
|
387
|
+
| `sq200` | Thumbnail (200x200 square crop) |
|
|
388
|
+
| `sq400` | Square (400x400) |
|
|
389
|
+
| `w400` | Small (400px wide) |
|
|
390
|
+
| `w800` | Medium (800px wide) |
|
|
391
|
+
| `w1200` | Large (1200px wide) |
|
|
392
|
+
| `w1600` | Retina (1600px wide) |
|
|
393
|
+
| `w1920` | Full HD (1920px wide) |
|
|
394
|
+
| `orig` | Original upload (always created) |
|