UNPKG

@atlassian/aui

Version:

Atlassian User Interface library

208 lines (173 loc) 6.11 kB
import skate from './internal/skate'; const DEFAULT_SIZE = 'medium'; const VISIBLE_AVATAR_COUNT = 4; const OVERLAP_FACTOR = 0.25; const SIZES = { xsmall: 16, small: 24, medium: 32, large: 48, xlarge: 64, xxlarge: 96, xxxlarge: 128, }; const setAvatarGroupSize = (element, newValue, oldValue) => { if (oldValue) { element.classList.remove(`aui-avatar-group-${oldValue}`); } element.classList.add(`aui-avatar-group-${newValue}`); }; const getAvatarGroupSize = (value) => { return Object.keys(SIZES).includes(value) ? value : DEFAULT_SIZE; }; const updateAvatarGroup = (element, size) => { const avatars = element.querySelectorAll('aui-avatar'); const hiddenAvatars = [...avatars].slice(VISIBLE_AVATAR_COUNT); updateAvatarGroupItems(avatars, size); const oldDropdown = element.querySelector('.aui-avatar-group-dropdown'); if (oldDropdown) { oldDropdown.remove(); } if (hiddenAvatars.length === 0) { return; } const dropdown = createDropdown(size, hiddenAvatars); element.appendChild(dropdown); }; const createDropdown = (size, hiddenAvatars) => { const dropdown = document.createElement('div'); const button = createAvatarGroupDropdownButton(`+${hiddenAvatars.length}`); const menu = createAvatarGroupDropdownMenu(); hiddenAvatars.forEach((hiddenAvatar) => { menu.append(hiddenAvatar); }); dropdown.classList.add('aui-avatar-group-dropdown'); dropdown.style.left = getLeftPosition(size, VISIBLE_AVATAR_COUNT + 0.2); dropdown.appendChild(button); dropdown.appendChild(menu); return dropdown; }; const createAvatarGroupDropdownButton = (content) => { const button = document.createElement('button'); button.classList.add('aui-avatar-group-dropdown-button', 'aui-avatar-inner'); button.innerText = content; button.setAttribute('aria-expanded', false); button.setAttribute('aria-haspopup', 'dialog'); return button; }; const createAvatarGroupDropdownMenu = () => { const dropdown = document.createElement('div'); dropdown.classList.add('aui-avatar-group-dropdown-menu'); return dropdown; }; const updateAvatarGroupItems = (avatars, size) => { avatars.forEach((avatar, index) => { const isHidden = index >= VISIBLE_AVATAR_COUNT; avatar.classList.add( !isHidden ? 'aui-avatar-group-item' : 'aui-avatar-group-dropdown-item' ); if (!isHidden) { avatar.setAttribute('size', size); avatar.style.left = getLeftPosition(size, index); avatar.style.zIndex = `${VISIBLE_AVATAR_COUNT + 1 - index}`; } else { avatar.setAttribute('size', 'medium'); } }); }; const getLeftPosition = (size, index) => { const avatarSize = SIZES[size]; const overlapFactor = avatarSize * OVERLAP_FACTOR; const leftPosition = (avatarSize - overlapFactor) * index; return `${leftPosition}px`; }; document.addEventListener('click', (e) => { const isDropdownButton = e.target.matches('.aui-avatar-group-dropdown-button'); const closestDropdown = e.target.closest('.aui-avatar-group-dropdown'); if (!isDropdownButton && closestDropdown !== null) { return; } if (isDropdownButton) { closestDropdown.classList.contains('aui-avatar-group-dropdown-show') ? closeDropdown(closestDropdown) : openDropdown(closestDropdown); } document.querySelectorAll('.aui-avatar-group-dropdown-show').forEach((dropdown) => { if (dropdown === closestDropdown) { return; } closeDropdown(dropdown); }); }); document.addEventListener('keydown', (e) => { if (e.key !== 'Escape') { return; } const dropdown = document.querySelector('.aui-avatar-group-dropdown-show'); if (!dropdown) { return; } closeDropdown(dropdown); }); const closeDropdown = (dropdown) => { const button = dropdown.querySelector('.aui-avatar-group-dropdown-button'); dropdown.classList.remove('aui-avatar-group-dropdown-show'); button.setAttribute('aria-expanded', 'false'); }; const openDropdown = (dropdown) => { const button = dropdown.querySelector('.aui-avatar-group-dropdown-button'); dropdown.classList.add('aui-avatar-group-dropdown-show'); button.setAttribute('aria-expanded', 'true'); }; const AvatarGroupEl = skate('aui-avatar-group', { attributes: { size: { value: DEFAULT_SIZE, fallback(element, { newValue, oldValue }) { const size = getAvatarGroupSize(newValue); setAvatarGroupSize(element, size, oldValue); skate.init(element); updateAvatarGroup(element, size); }, }, }, created(element) { element.classList.add('aui-avatar-group'); }, }); const wasNodeRemoved = (mutation, target) => { return ( mutation.removedNodes.length > 0 && target.classList.contains('aui-avatar-group') && mutation.removedNodes[0].classList.contains('aui-avatar-group-item') ); }; const wasNodeAdded = (mutation, target) => { return ( mutation.addedNodes.length > 0 && target.classList.contains('aui-avatar-group') && mutation.addedNodes[0].classList.contains('aui-avatar') ); }; const mutationObserver = new MutationObserver(function (mutation) { mutation.forEach(function (mutation) { const target = mutation.target; if (wasNodeRemoved(mutation, target)) { updateAvatarGroup(target, target.getAttribute('size')); } if (wasNodeAdded(mutation, target)) { setTimeout(() => { updateAvatarGroup(target, target.getAttribute('size')); }, 0); } }); }); mutationObserver.observe(document.documentElement, { attributes: false, characterData: true, childList: true, subtree: true, attributeOldValue: false, characterDataOldValue: false, }); export { AvatarGroupEl };