@grasco/profile-picture 0.1.2 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue.d.ts CHANGED
@@ -7,7 +7,7 @@ import { LitElement } from 'lit';
7
7
  */
8
8
  type Size = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
9
9
  type Variant = "circle" | "rounded" | "squircle" | "square";
10
- type Position = "top-left" | "top-right" | "bottom-left" | "bottom-right";
10
+ type Position = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
11
11
  type LoadingStrategy = "lazy" | "eager";
12
12
  type PlaceholderType = "shimmer" | "pulse" | "blur" | "skeleton" | "none";
13
13
  type PresenceStatus = "online" | "away" | "busy" | "offline" | "dnd";
@@ -19,18 +19,6 @@ interface PresenceConfig {
19
19
  /** Ring thickness (1-3) */
20
20
  thickness?: 1 | 2 | 3;
21
21
  }
22
- interface RibbonConfig {
23
- /** Text displayed on the ribbon */
24
- text: string;
25
- /** Position of the ribbon */
26
- position?: Position;
27
- /** Text color (CSS color) */
28
- color?: string;
29
- /** Background color (CSS color or gradient) */
30
- bgColor?: string;
31
- /** Icon before text (emoji or symbol) */
32
- icon?: string;
33
- }
34
22
  interface BadgeConfig {
35
23
  /** Badge content (text, number, or empty for dot) */
36
24
  content?: string | number;
@@ -40,6 +28,8 @@ interface BadgeConfig {
40
28
  color?: string;
41
29
  /** Background color */
42
30
  bgColor?: string;
31
+ /** Border radius (CSS value, e.g., "8px", "50%", "9999px") */
32
+ borderRadius?: string;
43
33
  /** Enable pulse animation */
44
34
  pulse?: boolean;
45
35
  /** Enable glow effect */
@@ -86,6 +76,130 @@ interface InteractionConfig {
86
76
  cursor?: "pointer" | "default" | "zoom-in";
87
77
  }
88
78
  type ShadowPreset = "none" | "sm" | "md" | "lg" | "glow";
79
+ /**
80
+ * Base props interface shared across all framework wrappers.
81
+ * This ensures type consistency between React, Vue, Svelte, and Angular.
82
+ */
83
+ interface ProfilePictureProps {
84
+ /** Image source URL */
85
+ src?: string;
86
+ /** Alt text for accessibility and fallback initials */
87
+ alt?: string;
88
+ /** External customer ID for CDN image loading */
89
+ extCustomerId?: string;
90
+ /** Predefined size or custom pixel value */
91
+ size?: Size | number;
92
+ /** Shape variant */
93
+ variant?: Variant;
94
+ /** Shadow preset */
95
+ shadow?: ShadowPreset;
96
+ /** Enable border */
97
+ border?: boolean;
98
+ /** Border width in pixels */
99
+ borderWidth?: 1 | 2 | 3 | 4 | 5 | 6 | 8;
100
+ /** Border color (CSS color value) */
101
+ borderColor?: string;
102
+ /** Rotation angle in degrees */
103
+ rotation?: number;
104
+ /** Background color (CSS color value) */
105
+ bgColor?: string;
106
+ /** Background gradient (CSS gradient value) */
107
+ bgGradient?: string;
108
+ /** Glow effect configuration */
109
+ glow?: GlowConfig;
110
+ /** Ring effect configuration (Instagram-style) */
111
+ ring?: RingConfig;
112
+ /** Presence indicator configuration */
113
+ presence?: PresenceConfig;
114
+ /** Badge configuration */
115
+ badge?: BadgeConfig;
116
+ /** Loading strategy */
117
+ loading?: LoadingStrategy;
118
+ /** Placeholder type while loading */
119
+ placeholder?: PlaceholderType;
120
+ /** Placeholder background color */
121
+ placeholderColor?: string;
122
+ /** Custom fallback text (overrides initials from alt) */
123
+ fallback?: string;
124
+ /** Interactive behavior configuration */
125
+ interactive?: InteractionConfig;
126
+ }
127
+ /** Direction for stacking avatars */
128
+ type StackDirection = "ltr" | "rtl";
129
+ /** Tooltip position relative to avatar */
130
+ type TooltipPosition = "top" | "bottom" | "left" | "right";
131
+ /** Tooltip configuration */
132
+ interface TooltipConfig {
133
+ /** Enable/disable tooltip (default: true) */
134
+ enabled?: boolean;
135
+ /** Position of tooltip relative to avatar */
136
+ position?: TooltipPosition;
137
+ /** Delay before showing tooltip in ms (default: 300) */
138
+ delay?: number;
139
+ }
140
+ /** User data extracted from profile-picture elements */
141
+ interface GroupUserData {
142
+ /** User ID from data-user-id attribute */
143
+ id?: string;
144
+ /** User name from alt attribute */
145
+ name: string;
146
+ /** Image source URL */
147
+ src?: string;
148
+ /** External customer ID for CDN image loading */
149
+ extCustomerId?: string;
150
+ /** Presence status from data-status attribute */
151
+ status?: PresenceStatus;
152
+ /** Reference to the profile-picture element */
153
+ element: HTMLElement;
154
+ /** Index in the group */
155
+ index: number;
156
+ /** Visual variant (circle, rounded, squircle, square) */
157
+ variant?: Variant;
158
+ /** Shadow preset */
159
+ shadow?: ShadowPreset;
160
+ /** Show border */
161
+ border?: boolean;
162
+ /** Border width (1-8) */
163
+ borderWidth?: 1 | 2 | 3 | 4 | 5 | 6 | 8;
164
+ /** Border color */
165
+ borderColor?: string;
166
+ /** Background color */
167
+ bgColor?: string;
168
+ /** Background gradient */
169
+ bgGradient?: string;
170
+ }
171
+ /** Dropdown configuration for overflow users */
172
+ interface DropdownConfig {
173
+ /** Max height of dropdown in px (default: 280) */
174
+ maxHeight?: number;
175
+ /** Show presence indicator dots (default: true) */
176
+ showPresence?: boolean;
177
+ /** Dropdown position (default: 'bottom') */
178
+ position?: "bottom" | "top";
179
+ }
180
+ /** Profile Picture Group Props */
181
+ interface ProfilePictureGroupProps {
182
+ /** Maximum visible avatars before showing counter (default: 4) */
183
+ max?: number;
184
+ /** Stack direction - ltr shows first on left (default: 'ltr') */
185
+ direction?: StackDirection;
186
+ /** Overlap amount as percentage 0-1 (default: 0.3) */
187
+ overlap?: number;
188
+ /** Avatar size - inherited by children (default: 'md') */
189
+ size?: Size | number;
190
+ /** Spacing between avatars in px (overrides overlap calculation) */
191
+ spacing?: number;
192
+ /** Tooltip configuration */
193
+ tooltip?: TooltipConfig;
194
+ /** Dropdown configuration for overflow users */
195
+ dropdown?: DropdownConfig;
196
+ /** Show add button (default: false) */
197
+ showAddButton?: boolean;
198
+ /** Add button label for accessibility */
199
+ addButtonLabel?: string;
200
+ /** Enable hover lift animation (default: true) */
201
+ animated?: boolean;
202
+ }
89
203
 
90
204
  declare class ProfilePicture extends LitElement {
91
205
  private static stylesInjected;
@@ -117,7 +231,6 @@ declare class ProfilePicture extends LitElement {
117
231
  ring?: RingConfig;
118
232
  presence?: PresenceConfig;
119
233
  glow?: GlowConfig;
120
- ribbon?: RibbonConfig;
121
234
  badge?: BadgeConfig;
122
235
  loading: LoadingStrategy;
123
236
  placeholder: PlaceholderType;
@@ -150,9 +263,13 @@ declare class ProfilePicture extends LitElement {
150
263
  private renderFallback;
151
264
  private renderImage;
152
265
  private renderRing;
266
+ /**
267
+ * Calculate corner offset based on variant
268
+ * Returns the inset from container edge where badges/presence should be positioned
269
+ */
270
+ private getCornerOffset;
153
271
  private renderPresence;
154
272
  private renderBadge;
155
- private renderRibbon;
156
273
  render(): lit_html.TemplateResult<1>;
157
274
  }
158
275
 
@@ -162,4 +279,64 @@ declare global {
162
279
  }
163
280
  }
164
281
 
165
- export type { BadgeConfig, LoadingStrategy, PlaceholderType, Position, RibbonConfig, Size, Variant };
282
+ declare class ProfilePictureGroup extends LitElement {
283
+ protected createRenderRoot(): this;
284
+ max: 4;
285
+ direction: StackDirection;
286
+ overlap: 0.3;
287
+ size: Size | string;
288
+ spacing?: number;
289
+ tooltip?: TooltipConfig;
290
+ dropdown?: DropdownConfig;
291
+ showAddButton: boolean;
292
+ addButtonLabel: string;
293
+ animated: true;
294
+ rotationAmount: number;
295
+ private users;
296
+ private dropdownOpen;
297
+ private tooltipData;
298
+ private tooltipTimeout;
299
+ private slotObserver;
300
+ private portalContainer;
301
+ private counterRef;
302
+ private get pixelSize();
303
+ private get tooltipEnabled();
304
+ private get tooltipPosition();
305
+ private get tooltipDelay();
306
+ private get dropdownMaxHeight();
307
+ private get showPresence();
308
+ private get dropdownPosition();
309
+ connectedCallback(): void;
310
+ disconnectedCallback(): void;
311
+ private setupSlotObserver;
312
+ private updateUsers;
313
+ private createPortal;
314
+ private destroyPortal;
315
+ private updatePortalContent;
316
+ private handleAvatarClick;
317
+ private handleAvatarHover;
318
+ private handleAvatarLeave;
319
+ private clearTooltipTimeout;
320
+ private handleCounterClick;
321
+ private readonly handleBackdropClick;
322
+ private handleDropdownItemClick;
323
+ private handleAddClick;
324
+ private handleKeyDown;
325
+ private renderAvatar;
326
+ private renderProfilePicture;
327
+ private renderCounter;
328
+ private renderAddButton;
329
+ private renderTooltip;
330
+ private renderDropdownItem;
331
+ private formatStatus;
332
+ protected updated(changedProperties: Map<PropertyKey, unknown>): void;
333
+ render(): lit_html.TemplateResult<1>;
334
+ }
335
+
336
+ declare global {
337
+ interface HTMLElementTagNameMap {
338
+ "profile-picture-group": ProfilePictureGroup;
339
+ }
340
+ }
341
+
342
+ export type { BadgeConfig, DropdownConfig, GlowConfig, GroupUserData, InteractionConfig, LoadingStrategy, PlaceholderType, Position, PresenceConfig, PresenceStatus, ProfilePictureGroupProps, ProfilePictureProps, RingConfig, ShadowPreset, Size, StackDirection, TooltipConfig, TooltipPosition, Variant };
package/dist/vue.js CHANGED
@@ -1,371 +1,2 @@
1
- import {LitElement,nothing,html}from'lit';import {property,state,customElement}from'lit/decorators.js';import {styleMap}from'lit/directives/style-map.js';var Q=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var s=(t,r,e,n)=>{for(var i=n>1?void 0:n?_(r,e):r,a=t.length-1,d;a>=0;a--)(d=t[a])&&(i=(n?d(r,e,i):d(i))||i);return n&&i&&Q(r,e,i),i};var I="grasco-profile-picture-styles",w=false;function $(){if(w||typeof document>"u")return;if(document.getElementById(I)){w=true;return}if(document.querySelector('link[href*="profile-picture"][href$="styles.css"]')){w=true;return}let r=P();if(!r)return;let e=document.createElement("link");e.id=I,e.rel="stylesheet",e.href=r,document.head.appendChild(e),w=true;}function P(){if(typeof window<"u"&&window.__GRASCO_PROFILE_PICTURE_CSS__)return window.__GRASCO_PROFILE_PICTURE_CSS__;try{let r=import.meta.url;if(r)return `${r.substring(0,r.lastIndexOf("/")+1)}dist/styles.css`}catch{}let t=document.currentScript;if(t?.src){let r=new URL(t.src);return `${r.href.substring(0,r.href.lastIndexOf("/")+1)}dist/styles.css`}return null}var v={"2xs":20,xs:24,sm:32,md:40,lg:48,xl:64,"2xl":80,"3xl":120},R={online:"#30D158",away:"#FFD60A",busy:"#FF453A",offline:"#8E8E93",dnd:"#FF453A"},z={none:"none",sm:"0 1px 2px 0 rgba(0, 0, 0, 0.05)",md:"0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",lg:"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",glow:"0 0 20px 0 rgba(99, 102, 241, 0.3)"},u={size:"md",variant:"circle",loading:"lazy",placeholder:"shimmer",borderWidth:2,borderColor:"#ffffff",placeholderColor:"#f3f4f6",shadow:"sm"};var x={circle:"9999px",rounded:"12px",squircle:"30%",square:"0px"};function b(...t){return t.filter(Boolean).join(" ")}function y(t){return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(t)}function E(t){return {"top-left":"np:top-0 np:left-0","top-right":"np:top-0 np:right-0","bottom-left":"np:bottom-0 np:left-0","bottom-right":"np:bottom-0 np:right-0"}[t]}function A(t){return {"top-left":"np:-rotate-45 np:-translate-x-1/4 np:-translate-y-1/2","top-right":"np:rotate-45 np:translate-x-1/4 np:-translate-y-1/2","bottom-left":"np:rotate-45 np:-translate-x-1/4 np:translate-y-1/2","bottom-right":"np:-rotate-45 np:translate-x-1/4 np:translate-y-1/2"}[t]}function T(t){let e=t.trim().replace(/[^\w\s]/g,"").split(/\s+/).filter(Boolean);return e.length===0?"?":e.length===1?e[0].slice(0,1).toUpperCase():(e[0][0]+e[e.length-1][0]).toUpperCase()}function S(t){return Math.round(t*.38)}function L(t,r){let n=Math.max(r?18:10,Math.round(t*(r?.32:.22))),i=Math.round(n*.6);return {size:n,fontSize:i}}function B(t,r){return Math.max(8,Math.round(t*.25))+(r-1)*2}function U(t,r){return typeof t=="string"?t:r&&t>r?`${r}+`:t.toString()}function F(t){return t.length===0?"transparent":t.length===1?t[0]:`conic-gradient(${t.map((e,n)=>{let i=n/t.length*360,a=(n+1)/t.length*360;return `${e} ${i}deg ${a}deg`}).join(", ")})`}function ee(t){let r=0;for(let e=0;e<t.length;e++){let n=t.charCodeAt(e);r=(r<<5)-r+n,r&=r;}return Math.abs(r)}function M(t,r,e){let i=Math.max(0,Math.min(100,t))/100*360;return `conic-gradient(from 270deg, ${r} 0deg ${i}deg, ${e} ${i}deg 360deg)`}function D(t){let r=["linear-gradient(135deg, #667eea 0%, #764ba2 100%)","linear-gradient(135deg, #f093fb 0%, #f5576c 100%)","linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)","linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)","linear-gradient(135deg, #fa709a 0%, #fee140 100%)","linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)","linear-gradient(135deg, #d299c2 0%, #fef9d7 100%)","linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%)","linear-gradient(135deg, #cd9cf2 0%, #f6f3ff 100%)","linear-gradient(135deg, #fddb92 0%, #d1fdff 100%)"],e=ee(t)%r.length;return r[e]}function N(t){return t<=80?"80":t<=120?"120":t<=240?"240":t<=480?"480":"960"}function O(t){if(!(t&&y(t)))return "light";let r=t.replace("#","");r.length===3&&(r=r.split("").map(d=>d+d).join(""));let e=Number.parseInt(r.slice(0,2),16),n=Number.parseInt(r.slice(2,4),16),i=Number.parseInt(r.slice(4,6),16);return (.299*e+.587*n+.114*i)/255>.5?"light":"dark"}function G(t,r,e,n,i){let a=t.endsWith("/")?t.slice(0,-1):t,d=`${r}_${e}_${n}.webp`;return `${a}/api/profile-picture/cdn/${d}?hostname=${i}`}function j(t){return {circle:"np:rounded-full",rounded:"np:rounded-xl",squircle:"np:rounded-[30%]",square:"np:rounded-none"}[t]}function V(t,r){return {className:{1:"np:border",2:"np:border-2",3:"np:border-[3px]",4:"np:border-4",5:"np:border-[5px]",6:"np:border-[6px]",8:"np:border-8"}[t],style:{borderColor:r,borderStyle:"solid"}}}function Y(t,r){return r?{className:"",style:{background:r}}:t?t.includes("gradient")?{className:"",style:{background:t}}:{className:"",style:{backgroundColor:t}}:{className:"",style:{backgroundColor:"#e7e7e7"}}}function H(t){return {boxShadow:z[t]}}var te={shimmer:`
2
- @keyframes pp-shimmer {
3
- 0% { transform: translateX(-100%); }
4
- 100% { transform: translateX(100%); }
5
- }`,pulse:`
6
- @keyframes pp-pulse {
7
- 0%, 100% { opacity: 1; transform: scale(1); }
8
- 50% { opacity: 0.7; transform: scale(0.98); }
9
- }`,skeleton:`
10
- @keyframes pp-skeleton {
11
- 0%, 100% { opacity: 1; }
12
- 50% { opacity: 0.5; }
13
- }`,badgeBounce:`
14
- @keyframes pp-badge-bounce {
15
- 0% { transform: scale(0) translateY(10px); opacity: 0; }
16
- 50% { transform: scale(1.2) translateY(-2px); }
17
- 100% { transform: scale(1) translateY(0); opacity: 1; }
18
- }`,ringRotate:`
19
- @keyframes pp-ring-rotate {
20
- from { transform: rotate(0deg); }
21
- to { transform: rotate(360deg); }
22
- }`,presencePulse:`
23
- @keyframes pp-presence-pulse {
24
- 0%, 100% { box-shadow: 0 0 0 0 currentColor; }
25
- 50% { box-shadow: 0 0 0 4px transparent; }
26
- }`,glow:`
27
- @keyframes pp-glow {
28
- 0%, 100% { box-shadow: 0 0 20px 0 var(--pp-glow-color, rgba(99, 102, 241, 0.3)); }
29
- 50% { box-shadow: 0 0 30px 5px var(--pp-glow-color, rgba(99, 102, 241, 0.5)); }
30
- }`,fadeIn:`
31
- @keyframes pp-fade-in {
32
- from { opacity: 0; }
33
- to { opacity: 1; }
34
- }`,scaleIn:`
35
- @keyframes pp-scale-in {
36
- 0% { transform: scale(0.8); opacity: 0; }
37
- 50% { transform: scale(1.05); }
38
- 100% { transform: scale(1); opacity: 1; }
39
- }`},ne=Object.values(te).join(`
40
- `),re=`
41
- /* Profile Picture Component Styles */
42
- .pp-container {
43
- --pp-transition-duration: 200ms;
44
- --pp-transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
45
- --pp-spring-timing: cubic-bezier(0.34, 1.56, 0.64, 1);
46
- position: relative;
47
- display: inline-flex;
48
- align-items: center;
49
- justify-content: center;
50
- user-select: none;
51
- -webkit-user-select: none;
52
- flex-shrink: 0;
53
- }
54
-
55
- /* Inner container for image clipping - allows badges/rings to overflow */
56
- .pp-inner {
57
- position: absolute;
58
- inset: 0;
59
- overflow: hidden;
60
- border-radius: inherit;
61
- }
62
-
63
- /* Interactive states */
64
- .pp-interactive {
65
- cursor: pointer;
66
- transition: transform var(--pp-transition-duration) var(--pp-spring-timing),
67
- box-shadow var(--pp-transition-duration) var(--pp-transition-timing);
68
- }
69
-
70
- .pp-interactive:hover {
71
- transform: scale(1.05);
72
- }
73
-
74
- .pp-interactive:active {
75
- transform: scale(0.95);
76
- }
77
-
78
- .pp-interactive:focus-visible {
79
- outline: none;
80
- box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.5);
81
- }
82
-
83
- /* Image styles */
84
- .pp-image {
85
- position: absolute;
86
- inset: 0;
87
- width: 100%;
88
- height: 100%;
89
- object-fit: cover;
90
- transition: opacity var(--pp-transition-duration) var(--pp-transition-timing);
91
- }
92
-
93
- .pp-image-loading {
94
- opacity: 0;
95
- }
96
-
97
- .pp-image-loaded {
98
- opacity: 1;
99
- }
100
-
101
- /* Fallback styles */
102
- .pp-fallback {
103
- display: flex;
104
- align-items: center;
105
- justify-content: center;
106
- width: 100%;
107
- height: 100%;
108
- font-weight: 500;
109
- color: rgba(255, 255, 255, 0.9);
110
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
111
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
112
- letter-spacing: 0.02em;
113
- }
114
-
115
- .pp-fallback-icon {
116
- color: rgba(156, 163, 175, 0.8);
117
- }
118
-
119
- /* Shimmer placeholder */
120
- .pp-shimmer {
121
- position: absolute;
122
- inset: 0;
123
- overflow: hidden;
124
- }
125
-
126
- .pp-shimmer::after {
127
- content: '';
128
- position: absolute;
129
- inset: 0;
130
- background: linear-gradient(
131
- 90deg,
132
- transparent 0%,
133
- rgba(255, 255, 255, 0.4) 50%,
134
- transparent 100%
135
- );
136
- animation: pp-shimmer 1.5s infinite;
137
- }
138
-
139
- /* Pulse placeholder */
140
- .pp-pulse {
141
- animation: pp-pulse 2s ease-in-out infinite;
142
- }
143
-
144
- /* Skeleton placeholder */
145
- .pp-skeleton {
146
- background: linear-gradient(90deg, #e5e7eb 0%, #f3f4f6 50%, #e5e7eb 100%);
147
- background-size: 200% 100%;
148
- animation: pp-skeleton 1.5s ease-in-out infinite;
149
- }
150
-
151
- /* Badge styles */
152
- .pp-badge {
153
- position: absolute;
154
- display: flex;
155
- align-items: center;
156
- justify-content: center;
157
- border-radius: 9999px;
158
- font-weight: 600;
159
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif;
160
- line-height: 1;
161
- animation: pp-badge-bounce 0.4s var(--pp-spring-timing);
162
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
163
- }
164
-
165
- .pp-badge-with-icon {
166
- gap: 4px;
167
- }
168
-
169
- .pp-badge-icon {
170
- display: inline-flex;
171
- align-items: center;
172
- justify-content: center;
173
- line-height: 1;
174
- flex-shrink: 0;
175
- }
176
-
177
- .pp-badge-pulse {
178
- animation: pp-badge-bounce 0.4s var(--pp-spring-timing),
179
- pp-presence-pulse 2s ease-in-out infinite 0.4s;
180
- }
181
-
182
- .pp-badge-glow {
183
- box-shadow: 0 0 10px 2px var(--pp-badge-glow-color, currentColor);
184
- }
185
-
186
- /* Ribbon styles */
187
- .pp-ribbon-container {
188
- position: absolute;
189
- z-index: 10;
190
- overflow: hidden;
191
- }
192
-
193
- .pp-ribbon {
194
- position: absolute;
195
- width: 100%;
196
- text-align: center;
197
- font-weight: 600;
198
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif;
199
- text-transform: uppercase;
200
- letter-spacing: 0.05em;
201
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
202
- }
203
-
204
- /* Ring effect (Instagram-style) */
205
- .pp-ring {
206
- position: absolute;
207
- inset: -4px;
208
- border-radius: inherit;
209
- padding: 3px;
210
- background: var(--pp-ring-color, linear-gradient(45deg, #f09433, #e6683c, #dc2743, #cc2366, #bc1888));
211
- -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
212
- mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
213
- -webkit-mask-composite: xor;
214
- mask-composite: exclude;
215
- transition: background 0.3s ease;
216
- }
217
-
218
- .pp-ring-animated {
219
- animation: pp-ring-rotate 3s linear infinite;
220
- }
221
-
222
- .pp-ring-progress {
223
- /* Progress rings should not animate rotation */
224
- animation: none;
225
- }
226
-
227
- /* Presence indicator */
228
- .pp-presence {
229
- position: absolute;
230
- bottom: 0;
231
- right: 0;
232
- border-radius: 9999px;
233
- border: 2px solid white;
234
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
235
- }
236
-
237
- .pp-presence-animated {
238
- animation: pp-presence-pulse 2s ease-in-out infinite;
239
- }
240
-
241
- /* Glow effect */
242
- .pp-glow {
243
- animation: pp-glow 2s ease-in-out infinite;
244
- }
245
-
246
- /* Reduced motion support */
247
- @media (prefers-reduced-motion: reduce) {
248
- .pp-container,
249
- .pp-interactive,
250
- .pp-image,
251
- .pp-shimmer::after,
252
- .pp-pulse,
253
- .pp-skeleton,
254
- .pp-badge,
255
- .pp-ring-animated,
256
- .pp-presence-animated,
257
- .pp-glow {
258
- animation: none !important;
259
- transition: none !important;
260
- }
261
- }
262
- `,W=`${ne}
263
- ${re}`,J=`
264
- @keyframes np-shimmer {
265
- 0% { background-position: -200% 0; }
266
- 100% { background-position: 200% 0; }
267
- }
268
- .np-shimmer {
269
- background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.4) 50%, transparent 100%);
270
- background-size: 200% 100%;
271
- animation: np-shimmer 1.5s infinite;
272
- }
273
- `;function q(t,r=.3){if(y(t)){let e=Number.parseInt(t.slice(1,3),16),n=Number.parseInt(t.slice(3,5),16),i=Number.parseInt(t.slice(5,7),16);return `0 0 20px 0 rgba(${e}, ${n}, ${i}, ${r})`}return `0 0 20px 0 ${t}`}var o=class extends LitElement{constructor(){super(...arguments);this.src="";this.alt="";this.size=u.size;this.variant=u.variant;this.shadow=u.shadow;this.border=false;this.borderWidth=u.borderWidth;this.borderColor=u.borderColor;this.rotation=0;this.loading=u.loading;this.placeholder=u.placeholder;this.placeholderColor=u.placeholderColor;this.isLoaded=false;this.hasError=false;this.cdnLoadFailed=false;this.previousSrc="";this.RETRY_DELAY_MS=3e4;}static setCdnBaseUrl(e){o.cdnBaseUrl=e,o.instances.forEach(n=>{n.extCustomerId&&!n.cdnImageUrl&&(n.cdnLoadFailed=false,n.retryTimeoutId&&(clearTimeout(n.retryTimeoutId),n.retryTimeoutId=void 0),n.previousExtCustomerId||(n.previousExtCustomerId=n.extCustomerId),n.loadCdnImage());});}static getCdnBaseUrl(){return o.cdnBaseUrl}createRenderRoot(){return o.injectStylesOnce(),this}static injectStylesOnce(){if(o.stylesInjected||typeof document>"u")return;let e=document.createElement("style");e.textContent=W,document.head.appendChild(e),o.stylesInjected=true;}get pixelSize(){let e=this.size;return typeof e=="number"?e:v[e]??v[u.size]}connectedCallback(){super.connectedCallback(),o.instances.add(this);}disconnectedCallback(){super.disconnectedCallback(),o.instances.delete(this),this.retryTimeoutId&&(clearTimeout(this.retryTimeoutId),this.retryTimeoutId=void 0);}firstUpdated(){this.extCustomerId&&o.cdnBaseUrl&&(this.previousExtCustomerId=this.extCustomerId,this.loadCdnImage());}updated(e){super.updated(e),this.extCustomerId&&o.cdnBaseUrl&&!this.cdnImageUrl&&(this.cdnLoadFailed||!this.previousExtCustomerId)&&(this.previousExtCustomerId||(this.previousExtCustomerId=this.extCustomerId),this.cdnLoadFailed=false,this.loadCdnImage());}willUpdate(e){e.has("src")&&this.src!==this.previousSrc&&(this.isLoaded=false,this.hasError=false,this.previousSrc=this.src),(e.has("extCustomerId")||e.has("size")||e.has("bgColor"))&&this.extCustomerId!==this.previousExtCustomerId&&(this.previousExtCustomerId=this.extCustomerId,this.cdnImageUrl=void 0,this.cdnLoadFailed=false,this.isLoaded=false,this.hasError=false,this.retryTimeoutId&&(clearTimeout(this.retryTimeoutId),this.retryTimeoutId=void 0),this.loadCdnImage());}handleLoad(){this.isLoaded=true,this.dispatchEvent(new CustomEvent("load",{bubbles:true,composed:true}));}handleError(){if(this.cdnImageUrl&&!this.cdnLoadFailed){this.cdnLoadFailed=true,this.cdnImageUrl=void 0,this.dispatchEvent(new CustomEvent("cdn-error",{bubbles:true,composed:true,detail:{error:"Image load failed"}})),this.retryTimeoutId&&clearTimeout(this.retryTimeoutId),this.retryTimeoutId=setTimeout(()=>{this.retryTimeoutId=void 0,this.extCustomerId&&o.cdnBaseUrl&&(this.cdnLoadFailed=false,this.hasError=false,this.isLoaded=false,this.loadCdnImage());},this.RETRY_DELAY_MS);return}this.hasError=true,this.isLoaded=true,this.dispatchEvent(new CustomEvent("error",{bubbles:true,composed:true}));}loadCdnImage(){if(!(this.extCustomerId&&o.cdnBaseUrl)){this.cdnLoadFailed=true;return}let e=N(this.pixelSize),n=O(this.bgColor),i=typeof window<"u"?window.location.hostname:"localhost";this.cdnImageUrl=G(o.cdnBaseUrl,this.extCustomerId,e,n,i);}getContainerStyles(){let e=Y(this.bgColor,this.bgGradient),n=this.border?V(this.borderWidth,this.borderColor):null,i=H(this.shadow),a={};if(this.glow){let f=this.glow.color??this.borderColor??"#6366f1";a={"--pp-glow-color":f,boxShadow:q(f,this.glow.intensity??.3)};}let d=this.interactive?.hoverable||this.interactive?.pressable;return {classes:b("pp-container",j(this.variant),e.className,n?.className,d&&"pp-interactive",this.glow?.animate&&"pp-glow"),styles:{width:`${this.pixelSize}px`,height:`${this.pixelSize}px`,borderRadius:x[this.variant],...e.style,...n?.style,...i,...a,cursor:this.interactive?.cursor??(d?"pointer":"default"),transform:this.rotation?`rotate(${this.rotation}deg)`:void 0}}}renderPlaceholder(){if(this.isLoaded||this.placeholder==="none")return nothing;let e={shimmer:"pp-shimmer",pulse:"pp-pulse",skeleton:"pp-skeleton",blur:"",none:""}[this.placeholder];return html`
274
- ${this.placeholder==="shimmer"?html`<style>${J}</style>`:nothing}
275
- <div
276
- class=${b("np:absolute np:inset-0",e)}
277
- style=${styleMap({backgroundColor:this.placeholderColor,borderRadius:"inherit"})}>
278
- </div>
279
- `}renderFallback(){if(this.fallback)return html`
280
- <span
281
- class="pp-fallback"
282
- style=${styleMap({fontSize:`${S(this.pixelSize)}px`})}>
283
- ${this.fallback}
284
- </span>
285
- `;if(this.alt){let n=D(this.alt);return html`
286
- <div
287
- class="pp-fallback np:absolute np:inset-0"
288
- style=${styleMap({background:this.bgColor??n,fontSize:`${S(this.pixelSize)}px`})}>
289
- ${T(this.alt)}
290
- </div>
291
- `}let e=this.pixelSize*.5;return html`
292
- <svg
293
- class="pp-fallback-icon"
294
- style="width: ${e}px; height: ${e}px;"
295
- fill="currentColor"
296
- viewBox="0 0 24 24">
297
- <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
298
- </svg>
299
- `}renderImage(){let e=!!(this.extCustomerId&&o.cdnBaseUrl),n;return this.cdnImageUrl?n=this.cdnImageUrl:(!e||this.cdnLoadFailed)&&(n=this.src),this.hasError||!n?this.renderFallback():html`
300
- <img
301
- src=${n}
302
- alt=${this.alt}
303
- loading=${this.loading}
304
- decoding="async"
305
- @load=${this.handleLoad}
306
- @error=${this.handleError}
307
- class=${b("pp-image",this.isLoaded?"pp-image-loaded":"pp-image-loading")}
308
- draggable="false" />
309
- `}renderRing(){if(!this.ring?.show)return nothing;let e=this.ring.width??3,n=this.ring.gap??3,i=e+n,a,d=this.ring.progress!==void 0;if(d){let f=this.ring.progress??0,m=this.ring.color??"#30D158",h=this.ring.emptyColor??"#8E8E93";a=M(f,m,h);}else this.ring.gradient&&this.ring.gradient.length>0?a=F(this.ring.gradient):a=this.ring.color??"linear-gradient(45deg, #f09433, #e6683c, #dc2743, #cc2366, #bc1888)";return html`
310
- <div
311
- class=${b("pp-ring",this.ring.animate&&!d&&"pp-ring-animated",d&&"pp-ring-progress")}
312
- style=${styleMap({inset:`-${i}px`,padding:`${e}px`,background:a,borderRadius:x[this.variant]})}>
313
- </div>
314
- `}renderPresence(){if(!this.presence)return nothing;let e=this.presence.thickness??2,n=B(this.pixelSize,e),i=R[this.presence.status],a=Math.max(0,this.pixelSize*.02);return html`
315
- <div
316
- class=${b("pp-presence",this.presence.animate&&"pp-presence-animated")}
317
- style=${styleMap({width:`${n}px`,height:`${n}px`,backgroundColor:i,bottom:`${a}px`,right:`${a}px`,color:i})}
318
- title=${this.presence.status}>
319
- </div>
320
- `}renderBadge(){if(!this.badge)return nothing;let e=this.badge.position??"bottom-right",n=this.badge.content!==void 0,i=this.badge.icon!==void 0,a=n||i,{size:d,fontSize:f}=L(this.pixelSize,a),m=this.badge.bgColor??"#22c55e",h=this.badge.color??"#ffffff",Z=n&&this.badge.content!==void 0?U(this.badge.content,this.badge.max):null,k=i?this.badge.icon:null,K=f*.9,X={"top-left":{top:"-4px",left:"-4px"},"top-right":{top:"-4px",right:"-4px"},"bottom-left":{bottom:"-4px",left:"-4px"},"bottom-right":{bottom:"-4px",right:"-4px"}};return html`
321
- <div
322
- class=${b("pp-badge",this.badge.pulse&&"pp-badge-pulse",this.badge.glow&&"pp-badge-glow",i&&"pp-badge-with-icon")}
323
- style=${styleMap({width:a?"auto":`${d}px`,minWidth:`${d}px`,height:`${d}px`,padding:a?"0 6px":"0",fontSize:`${f}px`,backgroundColor:m,color:h,"--pp-badge-glow-color":m,gap:i&&n?"4px":"0",...X[e]})}>
324
- ${k?html`<span class="pp-badge-icon" style=${styleMap({fontSize:`${K}px`})}>${k}</span>`:nothing}
325
- ${Z??nothing}
326
- </div>
327
- `}renderRibbon(){if(!this.ribbon)return nothing;let e=this.ribbon.position??"top-right",n=this.ribbon.bgColor??"#ef4444",i=this.ribbon.color??"#ffffff",a=y(n)?{backgroundColor:n}:{background:n},d={color:i},f=this.pixelSize*.9,m=this.pixelSize*.4,h=Math.max(8,this.pixelSize*.11);return html`
328
- <div
329
- class=${b("pp-ribbon-container",E(e))}
330
- style=${styleMap({width:`${f}px`,height:`${m}px`})}>
331
- <div
332
- class=${b("pp-ribbon np:origin-center np:transform",A(e))}
333
- style=${styleMap({fontSize:`${h}px`,padding:`${h*.3}px 0`,...a,...d})}>
334
- ${this.ribbon.icon?html`<span style="margin-right: 2px">${this.ribbon.icon}</span>`:nothing}
335
- ${this.ribbon.text}
336
- </div>
337
- </div>
338
- `}render(){let e=this.getContainerStyles(),n=this.interactive?.focusable||this.interactive?.pressable?0:void 0;return html`
339
- <div
340
- class=${e.classes}
341
- style=${styleMap(e.styles)}
342
- tabindex=${n??nothing}
343
- role=${this.interactive?.pressable?"button":nothing}
344
- aria-label=${this.alt||nothing}
345
- data-profile-picture>
346
-
347
- <!-- Ring Effect (behind everything) -->
348
- ${this.renderRing()}
349
-
350
- <!-- Inner container for image clipping -->
351
- <div
352
- class="pp-inner"
353
- style=${styleMap({borderRadius:x[this.variant]})}>
354
- <!-- Placeholder -->
355
- ${this.renderPlaceholder()}
356
-
357
- <!-- Main Image or Fallback -->
358
- ${this.renderImage()}
359
- </div>
360
-
361
- <!-- Ribbon -->
362
- ${this.renderRibbon()}
363
-
364
- <!-- Badge -->
365
- ${this.renderBadge()}
366
-
367
- <!-- Presence Indicator -->
368
- ${this.renderPresence()}
369
- </div>
370
- `}};o.stylesInjected=false,o.cdnBaseUrl="",o.instances=new Set,s([property({type:String})],o.prototype,"src",2),s([property({type:String})],o.prototype,"alt",2),s([property({type:String,attribute:"ext-customer-id"})],o.prototype,"extCustomerId",2),s([property({type:String})],o.prototype,"size",2),s([property({type:String})],o.prototype,"variant",2),s([property({type:String})],o.prototype,"shadow",2),s([property({type:Boolean})],o.prototype,"border",2),s([property({type:Number,attribute:"border-width"})],o.prototype,"borderWidth",2),s([property({type:String,attribute:"border-color"})],o.prototype,"borderColor",2),s([property({type:Number})],o.prototype,"rotation",2),s([property({type:String,attribute:"bg-color"})],o.prototype,"bgColor",2),s([property({type:String,attribute:"bg-gradient"})],o.prototype,"bgGradient",2),s([property({type:Object,attribute:"ring",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"ring",2),s([property({type:Object,attribute:"presence",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"presence",2),s([property({type:Object,attribute:"glow",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"glow",2),s([property({type:Object,attribute:"ribbon",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"ribbon",2),s([property({type:Object,attribute:"badge",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"badge",2),s([property({type:String})],o.prototype,"loading",2),s([property({type:String})],o.prototype,"placeholder",2),s([property({type:String,attribute:"placeholder-color"})],o.prototype,"placeholderColor",2),s([property({type:String})],o.prototype,"fallback",2),s([property({type:Object,attribute:"interactive",converter:{fromAttribute:e=>{if(e)try{return JSON.parse(e)}catch{return}},toAttribute:e=>e?JSON.stringify(e):null}})],o.prototype,"interactive",2),s([state()],o.prototype,"isLoaded",2),s([state()],o.prototype,"hasError",2),s([state()],o.prototype,"cdnImageUrl",2),s([state()],o.prototype,"cdnLoadFailed",2),o=s([customElement("profile-picture")],o);$();//# sourceMappingURL=vue.js.map
1
+ var r="grasco-profile-picture-styles",n=false,s=false;function i(){if(n||typeof document>"u")return;if(document.getElementById(r)){n=true;return}if(document.querySelector('link[href*="profile-picture"][href$="styles.css"]')){n=true;return}s||(s=true,l());}function l(){(typeof requestIdleCallback<"u"?requestIdleCallback:e=>setTimeout(e,1))(()=>u());}function u(){if(n||typeof document>"u")return;if(document.getElementById(r)){n=true;return}let t=c();if(!t)return;let e=document.createElement("link");e.id=r,e.rel="stylesheet",e.href=t,document.head.appendChild(e),n=true;}function c(){if(typeof window<"u"&&window.__GRASCO_PROFILE_PICTURE_CSS__)return window.__GRASCO_PROFILE_PICTURE_CSS__;try{let e=import.meta.url;if(e)return `${e.substring(0,e.lastIndexOf("/")+1)}dist/styles.css`}catch{}let t=document.currentScript;if(t?.src){let e=new URL(t.src);return `${e.href.substring(0,e.href.lastIndexOf("/")+1)}dist/styles.css`}return null}i();//# sourceMappingURL=vue.js.map
371
2
  //# sourceMappingURL=vue.js.map