@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
371 lines (370 loc) • 11.5 kB
JavaScript
"use strict";
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const common_utils = require("../../common/utils.cjs");
const avatar_constants = require("./avatar_constants.cjs");
const icon_constants = require("../icon/icon_constants.cjs");
const utils = require("./utils.cjs");
const vue = require("vue");
const _pluginVue_exportHelper = require("../../_virtual/_plugin-vue_export-helper.cjs");
const presence = require("../presence/presence.vue.cjs");
const _sfc_main = {
compatConfig: { MODE: 3 },
name: "DtAvatar",
components: { DtPresence: presence.default },
inheritAttrs: false,
props: {
/**
* Id of the avatar content wrapper element
*/
id: {
type: String,
default() {
return common_utils.getUniqueString();
}
},
/**
* Pass in a seed to get the random color generation based on that string. For example if you pass in a
* user ID as the string it will return the same randomly generated colors every time for that user.
*/
seed: {
type: String,
default: void 0
},
/**
* Set the avatar background to a specific color. If undefined will randomize the color which can be deterministic
* if the seed prop is set.
*/
color: {
type: String,
default: void 0
},
/**
* The size of the avatar
* @values xs, sm, md, lg, xl
*/
size: {
type: String,
default: "md",
validator: (size) => Object.keys(avatar_constants.AVATAR_SIZE_MODIFIERS).includes(size)
},
/**
* Used to customize the avatar container
*/
avatarClass: {
type: [String, Array, Object],
default: ""
},
/**
* Set classes on the avatar canvas. Wrapper around the core avatar image.
*/
canvasClass: {
type: [String, Array, Object],
default: ""
},
/**
* Pass through classes. Used to customize the avatar icon
*/
iconClass: {
type: [String, Array, Object],
default: ""
},
/**
* Determines whether to show the presence indicator for
* Avatar - accepts PRESENCE_STATES values: 'busy', 'away', 'offline',
* or 'active'. By default, it's null and nothing is shown.
* @values null, busy, away, offline, active
*/
presence: {
type: String,
default: avatar_constants.AVATAR_PRESENCE_STATES.NONE,
validator: (state) => {
return Object.values(avatar_constants.AVATAR_PRESENCE_STATES).includes(state);
}
},
/**
* A set of props to be passed into the presence component.
*/
presenceProps: {
type: Object,
default: () => ({})
},
/**
* Determines whether to show a group avatar.
* Limit to 2 digits max, more than 99 will be rendered as “99+”.
* if the number is 1 or less it would just show the regular avatar as if group had not been set.
*/
group: {
type: Number,
default: void 0,
validator: (group) => avatar_constants.AVATAR_GROUP_VALIDATOR(group)
},
/**
* The text that overlays the avatar
*/
overlayText: {
type: String,
default: ""
},
/**
* Used to customize the avatar overlay
*/
overlayClass: {
type: [String, Array, Object],
default: ""
},
/**
* Source of the image
*/
imageSrc: {
type: String,
default: ""
},
/**
* Alt attribute of the image, required if imageSrc is provided.
* Can be set to '' (empty string) if the image is described
* in text nearby
*/
imageAlt: {
type: String,
default: void 0
},
/**
* Icon size to be displayed on the avatar
* @values 100, 200, 300, 400, 500, 600, 700, 800
*/
iconSize: {
type: String,
default: "",
validator: (size) => !size || Object.keys(icon_constants.ICON_SIZE_MODIFIERS).includes(size)
},
/**
* Full name used to extract initials.
*/
fullName: {
type: String,
default: ""
},
/**
* Makes the avatar focusable and clickable,
* emits a click event when clicked.
*/
clickable: {
type: Boolean,
default: false
},
/**
* Descriptive label for the icon.
* To avoid a11y issues, set this prop if clickable and iconName are set.
*/
iconAriaLabel: {
type: String,
default: void 0
}
},
emits: [
/**
* Avatar click event
*
* @event click
* @type {PointerEvent | KeyboardEvent}
*/
"click"
],
data() {
return {
AVATAR_SIZE_MODIFIERS: avatar_constants.AVATAR_SIZE_MODIFIERS,
AVATAR_KIND_MODIFIERS: avatar_constants.AVATAR_KIND_MODIFIERS,
AVATAR_PRESENCE_SIZE_MODIFIERS: avatar_constants.AVATAR_PRESENCE_SIZE_MODIFIERS,
AVATAR_ICON_SIZES: avatar_constants.AVATAR_ICON_SIZES,
imageLoadedSuccessfully: null,
formattedInitials: "",
initializing: false,
hasSlotContent: common_utils.hasSlotContent
};
},
computed: {
hasOverlayIcon() {
return common_utils.hasSlotContent(this.$slots.overlayIcon);
},
iconDataQa() {
return "dt-avatar-icon";
},
avatarClasses() {
return [
"d-avatar",
this.$attrs.class,
avatar_constants.AVATAR_SIZE_MODIFIERS[this.validatedSize],
this.avatarClass,
{
"d-avatar--group": this.showGroup,
[`d-avatar--color-${this.getColor()}`]: !this.isIconType(),
"d-avatar--clickable": this.clickable
}
];
},
overlayClasses() {
return [
"d-avatar__overlay",
this.overlayClass,
{ "d-avatar__overlay-icon": this.hasOverlayIcon }
];
},
showGroup() {
return avatar_constants.AVATAR_GROUP_VALIDATOR(this.group);
},
formattedGroup() {
return this.group > 99 ? "99+" : this.group;
},
validatedSize() {
return this.group ? "xs" : this.size;
},
showImage() {
return this.imageLoadedSuccessfully !== false && this.imageSrc;
}
},
watch: {
fullName: {
immediate: true,
handler() {
this.formatInitials();
}
},
size: {
immediate: true,
handler() {
this.formatInitials();
}
},
group: {
immediate: true,
handler() {
this.formatInitials();
}
},
imageSrc(newSrc) {
this.imageLoadedSuccessfully = null;
if (!newSrc) return;
this.validateProps();
this.setImageListeners();
}
},
mounted() {
this.validateProps();
this.setImageListeners();
},
methods: {
isIconType() {
return common_utils.hasSlotContent(this.$slots.icon);
},
async setImageListeners() {
await this.$nextTick();
const el = this.$refs.avatarImage;
if (!el) return;
el.addEventListener("load", () => this._loadedImageEventHandler(el), { once: true });
el.addEventListener("error", () => this._erroredImageEventHandler(el), { once: true });
},
formatInitials() {
const initials = utils.extractInitialsFromName(this.fullName);
if (this.validatedSize === "xs") {
this.formattedInitials = "";
} else if (this.validatedSize === "sm") {
this.formattedInitials = initials[0];
} else {
this.formattedInitials = initials;
}
},
getColor() {
return this.color ?? common_utils.getRandomElement(avatar_constants.AVATAR_RANDOM_COLORS, this.seed);
},
_loadedImageEventHandler(el) {
this.imageLoadedSuccessfully = true;
el.classList.remove("d-d-none");
},
_erroredImageEventHandler(el) {
this.imageLoadedSuccessfully = false;
el.classList.add("d-d-none");
},
validateProps() {
if (this.imageSrc && this.imageAlt === void 0) {
console.error('image-alt required if image-src is provided. Can be set to "" (empty string) if the image is described in text nearby');
}
},
handleClick(e) {
if (!this.clickable) return;
this.$emit("click", e);
}
}
};
const _hoisted_1 = ["src", "alt"];
const _hoisted_2 = ["aria-label", "data-qa", "role"];
const _hoisted_3 = {
key: 1,
class: "d-avatar__overlay-text"
};
const _hoisted_4 = {
key: 1,
class: "d-avatar__count",
"data-qa": "dt-avatar-count"
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_dt_presence = vue.resolveComponent("dt-presence");
return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent($props.clickable ? "button" : "div"), {
id: $props.id,
class: vue.normalizeClass($options.avatarClasses),
style: vue.normalizeStyle(_ctx.$attrs.style),
"data-qa": "dt-avatar",
onClick: $options.handleClick
}, {
default: vue.withCtx(() => [
vue.createElementVNode("div", {
ref: "canvas",
class: vue.normalizeClass([
$props.canvasClass,
"d-avatar__canvas",
{ "d-avatar--image-loaded": $data.imageLoadedSuccessfully }
])
}, [
$options.showImage ? (vue.openBlock(), vue.createElementBlock("img", {
key: 0,
ref: "avatarImage",
class: "d-avatar__image",
"data-qa": "dt-avatar-image",
src: $props.imageSrc,
alt: $props.imageAlt
}, null, 8, _hoisted_1)) : $options.isIconType() ? (vue.openBlock(), vue.createElementBlock("div", {
key: 1,
class: vue.normalizeClass([$props.iconClass, $data.AVATAR_KIND_MODIFIERS.icon]),
"aria-label": $props.clickable ? $props.iconAriaLabel : "",
"data-qa": $options.iconDataQa,
role: $props.clickable ? "button" : ""
}, [
vue.renderSlot(_ctx.$slots, "icon", {
iconSize: $props.iconSize || $data.AVATAR_ICON_SIZES[$props.size]
})
], 10, _hoisted_2)) : (vue.openBlock(), vue.createElementBlock("span", {
key: 2,
class: vue.normalizeClass([$data.AVATAR_KIND_MODIFIERS.initials])
}, vue.toDisplayString($data.formattedInitials), 3))
], 2),
$options.hasOverlayIcon || $props.overlayText ? (vue.openBlock(), vue.createElementBlock("div", {
key: 0,
class: vue.normalizeClass($options.overlayClasses)
}, [
$options.hasOverlayIcon ? vue.renderSlot(_ctx.$slots, "overlayIcon", { key: 0 }) : $props.overlayText ? (vue.openBlock(), vue.createElementBlock("p", _hoisted_3, vue.toDisplayString($props.overlayText), 1)) : vue.createCommentVNode("", true)
], 2)) : vue.createCommentVNode("", true),
$options.showGroup ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_4, vue.toDisplayString($options.formattedGroup), 1)) : vue.createCommentVNode("", true),
$props.presence && !$options.showGroup ? (vue.openBlock(), vue.createBlock(_component_dt_presence, vue.mergeProps({
key: 2,
presence: $props.presence,
class: [
"d-avatar__presence",
$data.AVATAR_PRESENCE_SIZE_MODIFIERS[$props.size]
]
}, $props.presenceProps, { "data-qa": "dt-presence" }), null, 16, ["presence", "class"])) : vue.createCommentVNode("", true)
]),
_: 3
}, 8, ["id", "class", "style", "onClick"]);
}
const DtAvatar = /* @__PURE__ */ _pluginVue_exportHelper.default(_sfc_main, [["render", _sfc_render]]);
exports.default = DtAvatar;
//# sourceMappingURL=avatar.vue.cjs.map