@grasco/profile-picture 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/README.md +385 -0
- package/dist/angular.cjs +353 -0
- package/dist/angular.cjs.map +1 -0
- package/dist/angular.d.cts +132 -0
- package/dist/angular.d.ts +132 -0
- package/dist/angular.js +353 -0
- package/dist/angular.js.map +1 -0
- package/dist/index.cjs +885 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +389 -0
- package/dist/index.d.ts +389 -0
- package/dist/index.js +885 -0
- package/dist/index.js.map +1 -0
- package/dist/standalone.d.ts +260 -0
- package/dist/standalone.standalone.js +952 -0
- package/dist/standalone.standalone.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/svelte.cjs +353 -0
- package/dist/svelte.cjs.map +1 -0
- package/dist/svelte.d.cts +132 -0
- package/dist/svelte.d.ts +132 -0
- package/dist/svelte.js +353 -0
- package/dist/svelte.js.map +1 -0
- package/dist/vue.cjs +353 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +132 -0
- package/dist/vue.d.ts +132 -0
- package/dist/vue.js +353 -0
- package/dist/vue.js.map +1 -0
- package/package.json +130 -0
- package/tailwind.safelist.js +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# @grasco/profile-picture
|
|
2
|
+
|
|
3
|
+
A lightweight, framework-agnostic profile picture component built with Lit Web Components. Features ribbon and badge support, optimized for S3-hosted transparent background images.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Tiny bundle** (~6KB gzipped including Lit runtime)
|
|
8
|
+
- **Zero framework dependencies** Works with React, Vue, Angular, Svelte, and vanilla JS
|
|
9
|
+
- **Tree-shakeable** ESM exports
|
|
10
|
+
- **Performance-first** Native lazy loading, async decoding, CSS shimmer
|
|
11
|
+
- **Fully customizable** Variants, ribbons, badges, borders, backgrounds
|
|
12
|
+
- **TypeScript** Full type definitions included
|
|
13
|
+
- **Tailwind compatible** Uses Light DOM for seamless Tailwind integration
|
|
14
|
+
- **shadcn compatible** Install via CLI
|
|
15
|
+
|
|
16
|
+
## Why Lit?
|
|
17
|
+
|
|
18
|
+
This component uses **Lit web components** as its core, providing:
|
|
19
|
+
- ✅ Works natively in all frameworks (React, Vue, Angular, Svelte)
|
|
20
|
+
- ✅ No peer dependencies (unlike React-based components)
|
|
21
|
+
- ✅ Smaller bundle size for non-React users
|
|
22
|
+
- ✅ No wrapper code complexity
|
|
23
|
+
- ✅ Full Tailwind CSS support via Light DOM
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### npm
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @grasco/profile-picture
|
|
31
|
+
# or
|
|
32
|
+
bun add @grasco/profile-picture
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### shadcn CLI
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bunx shadcn add @grasco/profile-picture-03
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start (Plug-and-Play)
|
|
42
|
+
|
|
43
|
+
Simply import the component and its styles - no Tailwind configuration required:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Import the CSS (required once, at app entry point)
|
|
47
|
+
import '@grasco/profile-picture/styles.css';
|
|
48
|
+
|
|
49
|
+
// Import the component
|
|
50
|
+
import { ProfilePicture } from '@grasco/profile-picture';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The CSS file includes all Tailwind utility classes used by the component. No additional setup needed.
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### React
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import '@grasco/profile-picture/styles.css';
|
|
61
|
+
import { ProfilePicture } from '@grasco/profile-picture';
|
|
62
|
+
|
|
63
|
+
function App() {
|
|
64
|
+
return (
|
|
65
|
+
<ProfilePicture
|
|
66
|
+
src="https://your-bucket.s3.amazonaws.com/avatar.png"
|
|
67
|
+
alt="John Doe"
|
|
68
|
+
size="lg"
|
|
69
|
+
variant="circle"
|
|
70
|
+
border
|
|
71
|
+
borderColor="white"
|
|
72
|
+
bgColor="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
73
|
+
ribbon={{ text: 'PRO', position: 'top-right', bgColor: 'bg-amber-500' }}
|
|
74
|
+
badge={{ content: '3', position: 'bottom-right', pulse: true }}
|
|
75
|
+
onLoad={() => console.log('loaded')}
|
|
76
|
+
onError={() => console.error('failed')}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Vue
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<script setup lang="ts">
|
|
86
|
+
import '@grasco/profile-picture/styles.css';
|
|
87
|
+
import '@grasco/profile-picture/vue';
|
|
88
|
+
|
|
89
|
+
const avatarUrl = 'https://example.com/avatar.png';
|
|
90
|
+
const ribbon = { text: 'PRO', position: 'top-right', bgColor: 'bg-amber-500' };
|
|
91
|
+
const badge = { content: '3', position: 'bottom-right', pulse: true };
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<profile-picture
|
|
96
|
+
:src="avatarUrl"
|
|
97
|
+
alt="John Doe"
|
|
98
|
+
size="lg"
|
|
99
|
+
variant="circle"
|
|
100
|
+
:border="true"
|
|
101
|
+
border-color="white"
|
|
102
|
+
bg-color="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
103
|
+
:ribbon="ribbon"
|
|
104
|
+
:badge="badge"
|
|
105
|
+
@load="handleLoad"
|
|
106
|
+
@error="handleError"
|
|
107
|
+
/>
|
|
108
|
+
</template>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Note:** Add to vite.config.ts:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export default {
|
|
115
|
+
plugins: [
|
|
116
|
+
vue({
|
|
117
|
+
template: {
|
|
118
|
+
compilerOptions: {
|
|
119
|
+
isCustomElement: (tag) => tag === 'profile-picture'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Angular
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
131
|
+
import '@grasco/profile-picture/styles.css';
|
|
132
|
+
import '@grasco/profile-picture/angular';
|
|
133
|
+
|
|
134
|
+
@Component({
|
|
135
|
+
selector: 'app-example',
|
|
136
|
+
standalone: true,
|
|
137
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
138
|
+
template: `
|
|
139
|
+
<profile-picture
|
|
140
|
+
[src]="avatarUrl"
|
|
141
|
+
alt="John Doe"
|
|
142
|
+
size="lg"
|
|
143
|
+
variant="circle"
|
|
144
|
+
[border]="true"
|
|
145
|
+
border-color="white"
|
|
146
|
+
bg-color="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
147
|
+
[ribbon]="ribbon"
|
|
148
|
+
[badge]="badge"
|
|
149
|
+
(load)="onLoad()"
|
|
150
|
+
(error)="onError()">
|
|
151
|
+
</profile-picture>
|
|
152
|
+
`
|
|
153
|
+
})
|
|
154
|
+
export class ExampleComponent {
|
|
155
|
+
avatarUrl = 'https://example.com/avatar.png';
|
|
156
|
+
ribbon = { text: 'PRO', position: 'top-right', bgColor: 'bg-amber-500' };
|
|
157
|
+
badge = { content: '3', position: 'bottom-right', pulse: true };
|
|
158
|
+
|
|
159
|
+
onLoad() {
|
|
160
|
+
console.log('Image loaded');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
onError() {
|
|
164
|
+
console.error('Image failed');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Svelte
|
|
170
|
+
|
|
171
|
+
```svelte
|
|
172
|
+
<script lang="ts">
|
|
173
|
+
import '@grasco/profile-picture/styles.css';
|
|
174
|
+
import '@grasco/profile-picture/svelte';
|
|
175
|
+
|
|
176
|
+
let avatarUrl = 'https://example.com/avatar.png';
|
|
177
|
+
const ribbon = { text: 'PRO', position: 'top-right', bgColor: 'bg-amber-500' };
|
|
178
|
+
const badge = { content: '3', position: 'bottom-right', pulse: true };
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<profile-picture
|
|
182
|
+
src={avatarUrl}
|
|
183
|
+
alt="John Doe"
|
|
184
|
+
size="lg"
|
|
185
|
+
variant="circle"
|
|
186
|
+
border={true}
|
|
187
|
+
border-color="white"
|
|
188
|
+
bg-color="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
189
|
+
ribbon={ribbon}
|
|
190
|
+
badge={badge}
|
|
191
|
+
on:load={handleLoad}
|
|
192
|
+
on:error={handleError}
|
|
193
|
+
/>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Vanilla JavaScript
|
|
197
|
+
|
|
198
|
+
```html
|
|
199
|
+
<!DOCTYPE html>
|
|
200
|
+
<html>
|
|
201
|
+
<head>
|
|
202
|
+
<link rel="stylesheet" href="node_modules/@grasco/profile-picture/dist/styles.css">
|
|
203
|
+
<script type="module">
|
|
204
|
+
import '@grasco/profile-picture';
|
|
205
|
+
</script>
|
|
206
|
+
</head>
|
|
207
|
+
<body>
|
|
208
|
+
<profile-picture
|
|
209
|
+
src="https://example.com/avatar.png"
|
|
210
|
+
alt="John Doe"
|
|
211
|
+
size="lg"
|
|
212
|
+
variant="circle"
|
|
213
|
+
border
|
|
214
|
+
border-color="white"
|
|
215
|
+
bg-color="bg-gradient-to-br from-purple-500 to-pink-500">
|
|
216
|
+
</profile-picture>
|
|
217
|
+
|
|
218
|
+
<script>
|
|
219
|
+
const pic = document.querySelector('profile-picture');
|
|
220
|
+
pic.ribbon = { text: 'PRO', position: 'top-right', bgColor: 'bg-amber-500' };
|
|
221
|
+
pic.badge = { content: '3', position: 'bottom-right', pulse: true };
|
|
222
|
+
pic.addEventListener('load', () => console.log('loaded'));
|
|
223
|
+
</script>
|
|
224
|
+
</body>
|
|
225
|
+
</html>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Props / Attributes
|
|
229
|
+
|
|
230
|
+
| Prop | Type | Default | Description |
|
|
231
|
+
|------|------|---------|-------------|
|
|
232
|
+
| `src` | `string` | **required** | Image URL (S3 or any public URL) |
|
|
233
|
+
| `alt` | `string` | `''` | Alt text for accessibility |
|
|
234
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| number` | `'md'` | Size preset or custom pixels |
|
|
235
|
+
| `variant` | `'circle' \| 'rounded' \| 'square'` | `'circle'` | Shape variant |
|
|
236
|
+
| `border` | `boolean` | `false` | Enable border |
|
|
237
|
+
| `border-width` | `1 \| 2 \| 3 \| 4` | `2` | Border width in pixels |
|
|
238
|
+
| `border-color` | `string` | `'white'` | Tailwind class or hex color |
|
|
239
|
+
| `bg-color` | `string` | `'bg-gray-100'` | Background color for transparent images |
|
|
240
|
+
| `bg-gradient` | `string` | - | Tailwind gradient class |
|
|
241
|
+
| `ribbon` | `RibbonConfig` | - | Ribbon configuration (object) |
|
|
242
|
+
| `badge` | `BadgeConfig` | - | Badge configuration (object) |
|
|
243
|
+
| `loading` | `'lazy' \| 'eager'` | `'lazy'` | Image loading strategy |
|
|
244
|
+
| `placeholder` | `'shimmer' \| 'blur' \| 'none'` | `'shimmer'` | Placeholder type |
|
|
245
|
+
| `placeholder-color` | `string` | `'#e5e7eb'` | Placeholder background color |
|
|
246
|
+
| `fallback` | `string` | - | Custom fallback text |
|
|
247
|
+
|
|
248
|
+
### RibbonConfig
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
interface RibbonConfig {
|
|
252
|
+
text: string;
|
|
253
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
254
|
+
color?: string; // Text color (Tailwind or hex)
|
|
255
|
+
bgColor?: string; // Background color (Tailwind or hex)
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### BadgeConfig
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
interface BadgeConfig {
|
|
263
|
+
content?: string | number; // Text, number, or empty for dot
|
|
264
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
265
|
+
color?: string; // Text color (Tailwind or hex)
|
|
266
|
+
bgColor?: string; // Background color (Tailwind or hex)
|
|
267
|
+
pulse?: boolean; // Enable pulse animation
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Events
|
|
272
|
+
|
|
273
|
+
| Event | Description |
|
|
274
|
+
|-------|-------------|
|
|
275
|
+
| `load` | Fired when image loads successfully |
|
|
276
|
+
| `error` | Fired when image fails to load |
|
|
277
|
+
|
|
278
|
+
## Size Presets
|
|
279
|
+
|
|
280
|
+
| Size | Pixels |
|
|
281
|
+
|------|--------|
|
|
282
|
+
| `xs` | 24px |
|
|
283
|
+
| `sm` | 32px |
|
|
284
|
+
| `md` | 40px |
|
|
285
|
+
| `lg` | 56px |
|
|
286
|
+
| `xl` | 80px |
|
|
287
|
+
|
|
288
|
+
## Tailwind Setup (Optional)
|
|
289
|
+
|
|
290
|
+
> **Note:** If you imported `@grasco/profile-picture/styles.css`, you already have all the styles needed. The setup below is only for projects that want to use their own Tailwind configuration.
|
|
291
|
+
|
|
292
|
+
This component uses **Light DOM** (no Shadow DOM) so Tailwind classes work seamlessly.
|
|
293
|
+
|
|
294
|
+
### Tailwind v4 (Recommended)
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// tailwind.config.js
|
|
298
|
+
import { safelist } from '@grasco/profile-picture/tailwind.safelist';
|
|
299
|
+
|
|
300
|
+
export default {
|
|
301
|
+
content: [
|
|
302
|
+
'./src/**/*.{html,js,ts,jsx,tsx,vue,svelte}',
|
|
303
|
+
'./node_modules/@grasco/profile-picture/dist/**/*.{js,ts}'
|
|
304
|
+
],
|
|
305
|
+
safelist, // Optional: only if you pass incomplete class names
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Tailwind v3
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
// tailwind.config.js
|
|
313
|
+
module.exports = {
|
|
314
|
+
content: [
|
|
315
|
+
'./src/**/*.{html,js,ts,jsx,tsx,vue,svelte}',
|
|
316
|
+
'./node_modules/@grasco/profile-picture/**/*.{js,ts}'
|
|
317
|
+
],
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Tree Shaking Best Practices
|
|
322
|
+
|
|
323
|
+
For optimal tree shaking, **always use complete Tailwind class names**:
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
// ✅ GOOD - Classes detected by Tailwind scanner
|
|
327
|
+
<ProfilePicture
|
|
328
|
+
bgColor="bg-blue-500"
|
|
329
|
+
borderColor="border-white"
|
|
330
|
+
bgGradient="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
// ⚠️ WORKS but requires safelist configuration
|
|
334
|
+
<ProfilePicture
|
|
335
|
+
bgColor="blue-500" // Missing "bg-" prefix
|
|
336
|
+
borderColor="white" // Missing "border-" prefix
|
|
337
|
+
/>
|
|
338
|
+
|
|
339
|
+
// ✅ BEST - Uses inline styles, no Tailwind needed
|
|
340
|
+
<ProfilePicture
|
|
341
|
+
bgColor="#3b82f6"
|
|
342
|
+
borderColor="#ffffff"
|
|
343
|
+
/>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Recommendation**: Use hex colors for dynamic values and complete class names for static values.
|
|
347
|
+
|
|
348
|
+
## Performance Tips
|
|
349
|
+
|
|
350
|
+
1. **Use lazy loading** (default) for below-the-fold images
|
|
351
|
+
2. **Set explicit sizes** to prevent layout shift
|
|
352
|
+
3. **Leverage S3 caching** with proper cache headers
|
|
353
|
+
4. **Use `placeholder="none"`** if you have instant images
|
|
354
|
+
5. **Preload critical images** with `<link rel="preload">`
|
|
355
|
+
|
|
356
|
+
## Bundle Size
|
|
357
|
+
|
|
358
|
+
| Package | Size (gzipped) |
|
|
359
|
+
|---------|----------------|
|
|
360
|
+
| Lit runtime | ~6KB |
|
|
361
|
+
| Component code | <1KB |
|
|
362
|
+
| **Total** | **~6KB** |
|
|
363
|
+
|
|
364
|
+
Compare to React-based alternatives:
|
|
365
|
+
- React + ReactDOM: ~40KB
|
|
366
|
+
- This component: **6KB** (85% smaller)
|
|
367
|
+
|
|
368
|
+
## Browser Support
|
|
369
|
+
|
|
370
|
+
- Chrome/Edge 67+
|
|
371
|
+
- Firefox 63+
|
|
372
|
+
- Safari 13.1+
|
|
373
|
+
- All modern browsers with Web Components support
|
|
374
|
+
|
|
375
|
+
For older browsers, include the [webcomponents polyfill](https://github.com/webcomponents/polyfills).
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
MIT
|
|
380
|
+
|
|
381
|
+
## Links
|
|
382
|
+
|
|
383
|
+
- [GitHub Repository](https://github.com/grasco-lab/main-backend/tree/main/sdk/profile-picture)
|
|
384
|
+
- [npm Package](https://www.npmjs.com/package/@grasco/profile-picture)
|
|
385
|
+
- [Lit Documentation](https://lit.dev)
|