@spark-ui/components
Version:
Spark (Leboncoin design system) components.
267 lines (258 loc) • 7.95 kB
JavaScript
import {
IconButton
} from "../chunk-QNYSDG6F.mjs";
import "../chunk-7A5MVJB3.mjs";
import "../chunk-GAK4SC2F.mjs";
import {
Icon
} from "../chunk-UMUMFMFB.mjs";
import "../chunk-KEGAAGJW.mjs";
import {
Slot
} from "../chunk-6QCEPQ3U.mjs";
// src/avatar/Avatar.tsx
import { cx } from "class-variance-authority";
import * as React2 from "react";
// src/avatar/context.ts
import * as React from "react";
var AvatarContext = React.createContext(void 0);
var useAvatarContext = () => {
const context = React.useContext(AvatarContext);
if (!context) {
throw new Error("useAvatarContext must be used within an Avatar component");
}
return context;
};
// src/avatar/Avatar.tsx
import { jsx } from "react/jsx-runtime";
var sizeMap = {
xs: 24,
sm: 32,
md: 40,
lg: 56,
xl: 64,
// default
"2xl": 96
};
var Avatar = React2.forwardRef(
({
className,
size = "xl",
isOnline = false,
onlineText,
username,
asChild,
children,
design = "circle",
...props
}, ref) => {
const Comp = asChild ? Slot : "div";
const contextValue = React2.useMemo(
() => ({
size,
isOnline,
onlineText,
username,
design,
pixelSize: sizeMap[size]
}),
[size, isOnline, username, design, onlineText]
);
return /* @__PURE__ */ jsx(AvatarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
Comp,
{
ref,
style: {
width: sizeMap[size],
height: sizeMap[size]
},
"data-spark-component": "avatar",
className: cx("relative inline-flex items-center justify-center", className),
...props,
children
}
) });
}
);
Avatar.displayName = "Avatar";
// src/avatar/AvatarImage.tsx
import { cx as cx2 } from "class-variance-authority";
import { useState } from "react";
import { jsx as jsx2 } from "react/jsx-runtime";
var AvatarImage = ({ className, asChild, src, ...props }) => {
const { username, isOnline, onlineText } = useAvatarContext();
const Comp = asChild ? Slot : "img";
const [isVisible, setIsVisible] = useState(false);
const accessibleName = isOnline && onlineText ? `${username} (${onlineText})` : username;
return /* @__PURE__ */ jsx2(
Comp,
{
"aria-hidden": true,
className: cx2(
"absolute inset-0 size-full",
"object-cover",
{ "transition-all duration-300 group-hover:scale-120": props.onClick },
"focus-visible:u-outline",
isVisible ? "block" : "hidden",
className
),
alt: accessibleName,
src,
onLoad: () => {
setIsVisible(true);
},
...props
}
);
};
AvatarImage.displayName = "AvatarImage";
// src/avatar/AvatarAction.tsx
import { PenOutline } from "@spark-ui/icons/PenOutline";
import { cx as cx3 } from "class-variance-authority";
import { jsx as jsx3 } from "react/jsx-runtime";
var AvatarAction = ({
className,
children,
asChild,
angle = 135,
ariaLabel,
...props
}) => {
const Comp = asChild ? Slot : IconButton;
const { pixelSize, design } = useAvatarContext();
function getBadgePosition(circleSize) {
const angleRad = (90 - angle) * Math.PI / 180;
const circleRadius = circleSize / 2;
return {
x: circleRadius + circleRadius * Math.cos(angleRad),
y: circleRadius - circleRadius * Math.sin(angleRad)
};
}
const position = getBadgePosition(pixelSize);
const isCustomElement = asChild;
return /* @__PURE__ */ jsx3(
Comp,
{
"data-spark-component": "avatar-action",
style: {
position: "absolute",
...design === "circle" ? { left: `${position.x}px`, top: `${position.y}px` } : {},
...design === "square" ? { right: "0px", bottom: "0px" } : {}
},
className: cx3(
"z-raised",
design === "circle" ? "-translate-x-1/2 -translate-y-1/2" : "translate-x-1/4 translate-y-1/4",
{ "shadow-sm": !isCustomElement },
className
),
"aria-label": ariaLabel,
title: ariaLabel,
...!isCustomElement ? { size: "sm", intent: "support", design: "contrast" } : {},
...props,
children: children || /* @__PURE__ */ jsx3(Icon, { size: "sm", children: /* @__PURE__ */ jsx3(PenOutline, {}) })
}
);
};
AvatarAction.displayName = "AvatarAction";
// src/avatar/AvatarOnlineBadge.tsx
import { cx as cx4 } from "class-variance-authority";
import { jsx as jsx4 } from "react/jsx-runtime";
var AvatarOnlineBadge = ({ angle = 135, ...props }) => {
const { isOnline, pixelSize, design, onlineText, size } = useAvatarContext();
if (!isOnline) return null;
function getBadgePosition(circleSize) {
const angleRad = (90 - angle) * Math.PI / 180;
const circleRadius = circleSize / 2;
return {
x: circleRadius + circleRadius * Math.cos(angleRad),
y: circleRadius - circleRadius * Math.sin(angleRad)
};
}
const badgePosition = getBadgePosition(pixelSize);
return /* @__PURE__ */ jsx4(
"div",
{
"data-spark-component": "avatar-online-badge",
role: "status",
"aria-label": onlineText,
style: {
...design === "circle" ? { left: `${badgePosition.x}px`, top: `${badgePosition.y}px` } : { right: "0px", bottom: "0px" }
},
className: cx4(
"bg-success outline-surface absolute rounded-full",
design === "circle" ? "-translate-x-1/2 -translate-y-1/2" : "translate-x-1/4 translate-y-1/4",
["lg", "xl", "2xl"].includes(size) ? cx4("size-sz-12 outline-4") : cx4("size-sz-8 outline-2")
),
...props
}
);
};
AvatarOnlineBadge.displayName = "AvatarOnlineBadge";
// src/avatar/AvatarUser.tsx
import { cx as cx5 } from "class-variance-authority";
import { jsx as jsx5 } from "react/jsx-runtime";
var AvatarUser = ({ asChild, children, className, ...props }) => {
const { design, isOnline, onlineText, username } = useAvatarContext();
const Comp = asChild ? Slot : "div";
const accessibleName = isOnline && onlineText ? `${username} (${onlineText})` : username;
return /* @__PURE__ */ jsx5(
Comp,
{
...!asChild && { role: "img" },
"aria-label": accessibleName,
title: accessibleName,
className: cx5(
"group default:border-outline relative size-full overflow-hidden",
"focus-visible:u-outline",
{
"default:rounded-full": design === "circle",
"default:rounded-md": design === "square",
"hover:opacity-dim-1 cursor-pointer": asChild || props.onClick
},
className
),
...props,
children
}
);
};
AvatarUser.displayName = "AvatarUser";
// src/avatar/AvatarPlaceholder.tsx
import { cx as cx6 } from "class-variance-authority";
import { jsx as jsx6 } from "react/jsx-runtime";
var AvatarPlaceholder = ({ className, children, ...props }) => {
const { size, username } = useAvatarContext();
const firstLetter = username?.charAt(0);
return /* @__PURE__ */ jsx6(
"div",
{
"aria-hidden": true,
className: cx6(
"absolute inset-0 flex size-full items-center justify-center",
"default:bg-neutral default:text-on-neutral",
{
"text-display-1": size === "2xl",
"text-display-2": ["xl", "lg"].includes(size),
"text-display-3": size === "md",
"text-headline-2": size === "sm",
"text-body-2 font-bold": size === "xs"
},
className
),
...props,
children: children || firstLetter
}
);
};
AvatarPlaceholder.displayName = "AvatarPlaceholder";
// src/avatar/index.ts
var AvatarComponent = Avatar;
AvatarComponent.Image = AvatarImage;
AvatarComponent.Action = AvatarAction;
AvatarComponent.OnlineBadge = AvatarOnlineBadge;
AvatarComponent.User = AvatarUser;
AvatarComponent.Placeholder = AvatarPlaceholder;
export {
AvatarComponent as Avatar
};
//# sourceMappingURL=index.mjs.map