@nextcloud/vue
Version:
Nextcloud vue components
1 lines • 57.2 kB
Source Map (JSON)
{"version":3,"file":"NcProfileHoverCard.mjs","sources":["../../node_modules/vue-material-design-icons/BriefcaseOutline.vue","../../node_modules/vue-material-design-icons/ClockOutline.vue","../../node_modules/vue-material-design-icons/MapMarkerOutline.vue","../../node_modules/vue-material-design-icons/PencilOutline.vue","../../src/components/NcProfileHoverCard/profileCache.ts","../../src/components/NcProfileHoverCard/NcProfileHoverCard.vue"],"sourcesContent":["<template>\n <span v-bind=\"$attrs\"\n :aria-hidden=\"title ? null : 'true'\"\n :aria-label=\"title\"\n class=\"material-design-icon briefcase-outline-icon\"\n role=\"img\"\n @click=\"$emit('click', $event)\">\n <svg :fill=\"fillColor\"\n class=\"material-design-icon__svg\"\n :width=\"size\"\n :height=\"size\"\n viewBox=\"0 0 24 24\">\n <path d=\"M20,6C20.58,6 21.05,6.2 21.42,6.59C21.8,7 22,7.45 22,8V19C22,19.55 21.8,20 21.42,20.41C21.05,20.8 20.58,21 20,21H4C3.42,21 2.95,20.8 2.58,20.41C2.2,20 2,19.55 2,19V8C2,7.45 2.2,7 2.58,6.59C2.95,6.2 3.42,6 4,6H8V4C8,3.42 8.2,2.95 8.58,2.58C8.95,2.2 9.42,2 10,2H14C14.58,2 15.05,2.2 15.42,2.58C15.8,2.95 16,3.42 16,4V6H20M4,8V19H20V8H4M14,6V4H10V6H14Z\">\n <title v-if=\"title\">{{ title }}</title>\n </path>\n </svg>\n </span>\n</template>\n\n<script>\nexport default {\n name: \"BriefcaseOutlineIcon\",\n emits: ['click'],\n props: {\n title: {\n type: String,\n },\n fillColor: {\n type: String,\n default: \"currentColor\"\n },\n size: {\n type: Number,\n default: 24\n }\n }\n}\n</script>","<template>\n <span v-bind=\"$attrs\"\n :aria-hidden=\"title ? null : 'true'\"\n :aria-label=\"title\"\n class=\"material-design-icon clock-outline-icon\"\n role=\"img\"\n @click=\"$emit('click', $event)\">\n <svg :fill=\"fillColor\"\n class=\"material-design-icon__svg\"\n :width=\"size\"\n :height=\"size\"\n viewBox=\"0 0 24 24\">\n <path d=\"M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\">\n <title v-if=\"title\">{{ title }}</title>\n </path>\n </svg>\n </span>\n</template>\n\n<script>\nexport default {\n name: \"ClockOutlineIcon\",\n emits: ['click'],\n props: {\n title: {\n type: String,\n },\n fillColor: {\n type: String,\n default: \"currentColor\"\n },\n size: {\n type: Number,\n default: 24\n }\n }\n}\n</script>","<template>\n <span v-bind=\"$attrs\"\n :aria-hidden=\"title ? null : 'true'\"\n :aria-label=\"title\"\n class=\"material-design-icon map-marker-outline-icon\"\n role=\"img\"\n @click=\"$emit('click', $event)\">\n <svg :fill=\"fillColor\"\n class=\"material-design-icon__svg\"\n :width=\"size\"\n :height=\"size\"\n viewBox=\"0 0 24 24\">\n <path d=\"M12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5M12,2A7,7 0 0,1 19,9C19,14.25 12,22 12,22C12,22 5,14.25 5,9A7,7 0 0,1 12,2M12,4A5,5 0 0,0 7,9C7,10 7,12 12,18.71C17,12 17,10 17,9A5,5 0 0,0 12,4Z\">\n <title v-if=\"title\">{{ title }}</title>\n </path>\n </svg>\n </span>\n</template>\n\n<script>\nexport default {\n name: \"MapMarkerOutlineIcon\",\n emits: ['click'],\n props: {\n title: {\n type: String,\n },\n fillColor: {\n type: String,\n default: \"currentColor\"\n },\n size: {\n type: Number,\n default: 24\n }\n }\n}\n</script>","<template>\n <span v-bind=\"$attrs\"\n :aria-hidden=\"title ? null : 'true'\"\n :aria-label=\"title\"\n class=\"material-design-icon pencil-outline-icon\"\n role=\"img\"\n @click=\"$emit('click', $event)\">\n <svg :fill=\"fillColor\"\n class=\"material-design-icon__svg\"\n :width=\"size\"\n :height=\"size\"\n viewBox=\"0 0 24 24\">\n <path d=\"M14.06,9L15,9.94L5.92,19H5V18.08L14.06,9M17.66,3C17.41,3 17.15,3.1 16.96,3.29L15.13,5.12L18.88,8.87L20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18.17,3.09 17.92,3 17.66,3M14.06,6.19L3,17.25V21H6.75L17.81,9.94L14.06,6.19Z\">\n <title v-if=\"title\">{{ title }}</title>\n </path>\n </svg>\n </span>\n</template>\n\n<script>\nexport default {\n name: \"PencilOutlineIcon\",\n emits: ['click'],\n props: {\n title: {\n type: String,\n },\n fillColor: {\n type: String,\n default: \"currentColor\"\n },\n size: {\n type: Number,\n default: 24\n }\n }\n}\n</script>","/*!\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IProfileData } from './NcProfileHoverCard.vue'\n\n/** Default TTL for cached profile payloads (30 minutes). */\nexport const PROFILE_CACHE_TTL_MS = 30 * 60 * 1000\n\ninterface CacheEntry {\n\tdata: IProfileData\n\texpiresAt: number\n}\n\nconst cache = new Map<string, CacheEntry>()\nconst inflight = new Map<string, Promise<IProfileData | null>>()\n\n/**\n * Return a non-expired cached profile for the user, or null.\n *\n * @param userId - User id\n */\nexport function getCachedProfile(userId: string): IProfileData | null {\n\tconst entry = cache.get(userId)\n\tif (!entry) {\n\t\treturn null\n\t}\n\tif (Date.now() >= entry.expiresAt) {\n\t\tcache.delete(userId)\n\t\treturn null\n\t}\n\treturn entry.data\n}\n\n/**\n * Store profile data in the shared TTL cache.\n *\n * @param userId - User id\n * @param data - Profile payload\n * @param ttlMs - Time to live in milliseconds\n */\nexport function setCachedProfile(userId: string, data: IProfileData, ttlMs = PROFILE_CACHE_TTL_MS): void {\n\tcache.set(userId, {\n\t\tdata,\n\t\texpiresAt: Date.now() + ttlMs,\n\t})\n}\n\n/**\n * Clear all cached profiles and in-flight fetches. Used by tests.\n *\n * @internal\n */\nexport function clearProfileCache(): void {\n\tcache.clear()\n\tinflight.clear()\n}\n\n/**\n * Fetch a profile with shared TTL cache and in-flight request deduplication.\n *\n * @param userId - User id\n * @param fetcher - Network loader invoked on cache miss\n * @param ttlMs - Time to live for a successful result\n */\nexport async function fetchProfileCached(\n\tuserId: string,\n\tfetcher: () => Promise<IProfileData | null>,\n\tttlMs = PROFILE_CACHE_TTL_MS,\n): Promise<IProfileData | null> {\n\tconst cached = getCachedProfile(userId)\n\tif (cached) {\n\t\treturn cached\n\t}\n\n\tconst pending = inflight.get(userId)\n\tif (pending) {\n\t\treturn pending\n\t}\n\n\tconst promise = (async () => {\n\t\ttry {\n\t\t\tconst data = await fetcher()\n\t\t\tif (data) {\n\t\t\t\tsetCachedProfile(userId, data, ttlMs)\n\t\t\t}\n\t\t\treturn data\n\t\t} finally {\n\t\t\tinflight.delete(userId)\n\t\t}\n\t})()\n\n\tinflight.set(userId, promise)\n\treturn promise\n}\n","<!--\n - SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script setup lang=\"ts\">\nimport type { RouteLocationRaw } from 'vue-router'\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { getCapabilities } from '@nextcloud/capabilities'\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { generateOcsUrl, generateUrl } from '@nextcloud/router'\nimport { computed, onMounted, ref, watch } from 'vue'\nimport BriefcaseIcon from 'vue-material-design-icons/BriefcaseOutline.vue'\nimport ClockIcon from 'vue-material-design-icons/ClockOutline.vue'\nimport MapMarkerIcon from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport PencilIcon from 'vue-material-design-icons/PencilOutline.vue'\nimport NcActionButton from '../NcActionButton/NcActionButton.vue'\nimport NcActionLink from '../NcActionLink/NcActionLink.vue'\nimport NcActionRouter from '../NcActionRouter/NcActionRouter.vue'\nimport NcActions from '../NcActions/NcActions.vue'\nimport NcAvatar from '../NcAvatar/NcAvatar.vue'\nimport NcButton from '../NcButton/NcButton.vue'\nimport NcLoadingIcon from '../NcLoadingIcon/NcLoadingIcon.vue'\nimport NcRichText from '../NcRichText/NcRichText.vue'\nimport { useIsSmallMobile } from '../../composables/useIsMobile/index.ts'\nimport { t } from '../../l10n.ts'\nimport { logger } from '../../utils/logger.ts'\nimport NcIconSvgWrapper from '../NcIconSvgWrapper/index.ts'\nimport { fetchProfileCached, getCachedProfile, setCachedProfile } from './profileCache.ts'\n\nexport interface IProfileAction {\n\tid: string\n\tappId?: string\n\ticon: string\n\ttitle: string\n\ttarget: string | null\n}\n\nexport interface IProfileData {\n\tuserId: string\n\taddress?: string | null\n\tbiography?: string | null\n\tdisplayname?: string | null\n\theadline?: string | null\n\tisUserAvatarVisible?: boolean\n\torganisation?: string | null\n\tpronouns?: string | null\n\trole?: string | null\n\ttimezone?: string | null\n\ttimezoneOffset?: number | null\n\tactions: IProfileAction[]\n}\n\n/**\n * Action shown in the hover card sidebar (typically from NcAvatar's contacts menu).\n */\nexport interface IProfileHoverCardAction {\n\tid?: string\n\tappId?: string\n\ttext: string\n\thref?: string\n\tto?: RouteLocationRaw\n\ttarget?: string\n\trel?: string\n\ticon?: string\n\ticonSvg?: string\n\tonClick?: (event: MouseEvent) => void\n}\n\nexport interface IUserStatus {\n\ticon?: string | null\n\tmessage?: string | null\n\tstatus?: string | null\n}\n\nconst props = withDefaults(defineProps<{\n\t/**\n\t * User id whose profile to show.\n\t */\n\tuser?: string | null\n\n\t/**\n\t * Optional display name when already known (skips `/displaynames` for limited profiles).\n\t * When omitted and the full profile is unavailable, the card looks the name up itself.\n\t */\n\tdisplayName?: string | null\n\n\t/**\n\t * Optional preloaded profile data (skips the profile API request when set).\n\t */\n\tprofile?: IProfileData | null\n\n\t/**\n\t * Optional preloaded user status (skips the status API request when set).\n\t * Pass an object with `status`, `icon`, and `message` set to null for a known empty status.\n\t */\n\tpreloadedUserStatus?: IUserStatus | null\n\n\t/**\n\t * Whether the popover that hosts this card is currently shown.\n\t * Profile data and user status are loaded when this becomes true.\n\t */\n\topen?: boolean\n\n\t/**\n\t * Actions to show in the card (e.g. NcAvatar contacts menu actions).\n\t * When omitted or empty, actions from the profile API are used.\n\t */\n\tactions?: IProfileHoverCardAction[] | null\n\n\t/**\n\t * True while parent actions (e.g. contacts menu) are still loading.\n\t */\n\tactionsLoading?: boolean\n}>(), {\n\tuser: null,\n\tdisplayName: null,\n\tprofile: null,\n\tpreloadedUserStatus: null,\n\topen: false,\n\tactions: null,\n\tactionsLoading: false,\n})\n\nconst loading = ref(false)\nconst error = ref(false)\nconst profileData = ref<IProfileData | null>(props.profile)\nconst userStatus = ref<IUserStatus>(props.preloadedUserStatus ?? {})\n/**\n * False when the full profile is disabled or unavailable; card shows avatar + name only.\n */\nconst profileEnabled = ref(!!props.profile)\n/**\n * True once status is known for the current user (preloaded or fetched, including empty).\n * The card stays on the loading state until this is true to avoid a status flash.\n */\nconst userStatusReady = ref(props.preloadedUserStatus !== null)\nconst isMobile = useIsSmallMobile()\n\n/**\n * Show the spinner until profile (or limited fallback) and status are both ready.\n */\nconst showLoading = computed(() => loading.value\n\t|| (!!profileData.value && !error.value && !userStatusReady.value))\n\nif (props.user && props.profile) {\n\tsetCachedProfile(props.user, props.profile)\n}\n\nconst isCurrentUser = computed(() => !!props.user && getCurrentUser()?.uid === props.user)\nconst settingsUrl = generateUrl('/settings/user')\n\n/**\n * Full profile page URL — only when the profile is enabled and reachable.\n */\nconst profileUrl = computed(() => {\n\tif (!profileEnabled.value) {\n\t\treturn null\n\t}\n\tconst userId = profileData.value?.userId || props.user\n\tif (!userId) {\n\t\treturn null\n\t}\n\treturn generateUrl('/u/{userId}', { userId })\n})\n\n/**\n * Display name for the header (full profile, prop, or user id).\n */\nconst resolvedDisplayName = computed(() => profileData.value?.displayname\n\t|| props.displayName\n\t|| profileData.value?.userId\n\t|| props.user\n\t|| '')\n\n/**\n * Props for avatar wrappers that link to the full profile when available.\n */\nconst profileLinkProps = computed(() => {\n\tif (!profileUrl.value) {\n\t\treturn {}\n\t}\n\treturn {\n\t\thref: profileUrl.value,\n\t\t'aria-label': t('View profile'),\n\t}\n})\n\n/**\n * Drop actions that do not belong in the hover-card sidebar\n * (timezone info and the separate \"View profile\" entry).\n *\n * @param action - Hover card or profile API action\n */\nfunction isSidebarAction(action: Pick<IProfileHoverCardAction, 'appId' | 'id'>) {\n\treturn action.appId !== 'timezone'\n\t\t&& action.appId !== 'profile'\n\t\t&& action.id !== 'profile'\n}\n\n/**\n * Display label for an action (short Talk labels without the redundant name).\n *\n * @param action - Hover card action\n */\nfunction actionDisplayText(action: IProfileHoverCardAction) {\n\t// Keep self-profile labels such as \"Open Talk\" unchanged\n\tif (isCurrentUser.value) {\n\t\treturn action.text\n\t}\n\tif (action.id === 'talk-call' || isSpreedDirectCallAction(action)) {\n\t\treturn t('Start call')\n\t}\n\tif (action.id === 'talk' || action.appId === 'spreed') {\n\t\treturn t('Send message')\n\t}\n\treturn action.text\n}\n\n/**\n * Contacts-menu Talk \"direct call\" action (vs chat), identified by URL fragment.\n *\n * @param action - Hover card action\n */\nfunction isSpreedDirectCallAction(action: IProfileHoverCardAction) {\n\tif (action.appId !== 'spreed') {\n\t\treturn false\n\t}\n\tconst href = action.href ?? (typeof action.to === 'string' ? action.to : '')\n\treturn typeof href === 'string' && href.includes('#direct-call')\n}\n\n/**\n * Actions shown in the card sidebar, excluding timezone and View profile.\n */\nconst resolvedActions = computed((): IProfileHoverCardAction[] => {\n\tif (props.actions && props.actions.length > 0) {\n\t\treturn props.actions.filter(isSidebarAction)\n\t}\n\treturn (profileData.value?.actions ?? [])\n\t\t.filter(isSidebarAction)\n\t\t.map((action) => ({\n\t\t\tid: action.id,\n\t\t\tappId: action.appId,\n\t\t\ttext: action.title,\n\t\t\thref: action.target || undefined,\n\t\t\ticon: action.icon,\n\t\t\ttarget: action.id === 'phone' ? '_self' : '_blank',\n\t\t\trel: action.id === 'fediverse' ? 'me' : undefined,\n\t\t}))\n})\n\n/**\n * Local clock for the profile user (same format as Talk's right sidebar).\n */\nconst localTimeLabel = computed(() => {\n\tconst time = formatProfileLocalTime(new Date())\n\tif (!time) {\n\t\treturn null\n\t}\n\treturn t('Local time: {time}', { time })\n})\n\nconst primaryAction = computed(() => resolvedActions.value[0])\nconst otherActions = computed(() => resolvedActions.value.slice(1))\n\nconst primaryActionButtonProps = computed(() => {\n\tconst action = primaryAction.value\n\tif (!action) {\n\t\treturn {}\n\t}\n\tif (action.to) {\n\t\treturn { to: action.to }\n\t}\n\tif (action.href) {\n\t\treturn {\n\t\t\thref: action.href,\n\t\t\ttarget: action.target ?? (action.href.startsWith('tel:') ? '_self' : '_blank'),\n\t\t\trel: action.rel,\n\t\t}\n\t}\n\treturn {}\n})\n\n/**\n * Pass status to NcAvatar so the badge matches the loaded status.\n * Always pass an object (null fields if empty) so NcAvatar does not fetch again.\n */\nconst avatarUserStatus = computed(() => ({\n\tstatus: userStatus.value.status ?? null,\n\ticon: userStatus.value.icon ?? null,\n\tmessage: userStatus.value.message ?? null,\n}))\n\n/**\n * Format the profile user's current time like Talk (`HH:MM` in their timezone)\n *\n * @param date - Instant to format\n */\nfunction formatProfileLocalTime(date: Date): string | null {\n\tconst timeZone = profileData.value?.timezone\n\tif (timeZone) {\n\t\ttry {\n\t\t\treturn new Intl.DateTimeFormat(getCanonicalLocale(), {\n\t\t\t\ttimeZone,\n\t\t\t\thour: '2-digit',\n\t\t\t\tminute: '2-digit',\n\t\t\t}).format(date)\n\t\t} catch {\n\t\t\t// Invalid timezone id from the server\n\t\t}\n\t}\n\n\tif (typeof profileData.value?.timezoneOffset === 'number') {\n\t\tconst shifted = new Date(date.getTime() + profileData.value.timezoneOffset * 1000)\n\t\treturn new Intl.DateTimeFormat(getCanonicalLocale(), {\n\t\t\ttimeZone: 'UTC',\n\t\t\thour: '2-digit',\n\t\t\tminute: '2-digit',\n\t\t}).format(shifted)\n\t}\n\n\treturn null\n}\n\n/**\n * Whether an action icon is an image URL rather than a CSS class\n *\n * @param icon - Icon value\n */\nfunction isIconUrl(icon?: string) {\n\treturn !!icon && (/^(https?:)?\\/\\//.test(icon) || icon.startsWith('/') || icon.startsWith('data:'))\n}\n\n/**\n * Resolve the NcAction component for a secondary hover-card action\n *\n * @param action - Hover card action\n */\nfunction otherActionComponent(action: IProfileHoverCardAction) {\n\tif (action.to) {\n\t\treturn NcActionRouter\n\t}\n\tif (action.onClick && !action.href) {\n\t\treturn NcActionButton\n\t}\n\treturn NcActionLink\n}\n\n/**\n * Props for a secondary hover-card action component\n *\n * @param action - Hover card action\n */\nfunction otherActionProps(action: IProfileHoverCardAction) {\n\tif (action.to) {\n\t\treturn { to: action.to, closeAfterClick: true }\n\t}\n\tif (action.onClick && !action.href) {\n\t\treturn { onClick: action.onClick, closeAfterClick: true }\n\t}\n\treturn {\n\t\thref: action.href,\n\t\ttarget: action.target ?? (action.href?.startsWith('tel:') ? '_self' : '_blank'),\n\t\trel: action.rel,\n\t\tcloseAfterClick: true,\n\t}\n}\n\nwatch(() => props.profile, (value) => {\n\tif (value) {\n\t\tprofileData.value = value\n\t\tprofileEnabled.value = true\n\t\tif (props.user) {\n\t\t\tsetCachedProfile(props.user, value)\n\t\t}\n\t\terror.value = false\n\t}\n})\n\nwatch(() => props.preloadedUserStatus, (value) => {\n\tif (value) {\n\t\tuserStatus.value = value\n\t\tuserStatusReady.value = true\n\t}\n})\n\nwatch(() => props.displayName, (value) => {\n\tif (value && profileData.value && !profileEnabled.value) {\n\t\tprofileData.value = { ...profileData.value, displayname: value }\n\t}\n})\n\nwatch(() => props.user, () => {\n\tprofileData.value = props.profile\n\tuserStatus.value = props.preloadedUserStatus ?? {}\n\tuserStatusReady.value = props.preloadedUserStatus !== null\n\tprofileEnabled.value = !!props.profile\n\terror.value = false\n\tif (props.user && props.profile) {\n\t\tsetCachedProfile(props.user, props.profile)\n\t}\n\tif (props.open) {\n\t\tloadProfile()\n\t}\n})\n\nwatch(() => props.open, (isOpen) => {\n\tif (isOpen) {\n\t\tloadProfile()\n\t}\n})\n\nonMounted(() => {\n\tif (props.open) {\n\t\tloadProfile()\n\t}\n})\n\n/**\n * Resolve a display name for limited profiles: prop first, then `/displaynames`.\n *\n * @param userId - User id\n */\nasync function resolveDisplayName(userId: string): Promise<string> {\n\tif (props.displayName) {\n\t\treturn props.displayName\n\t}\n\ttry {\n\t\tconst { data } = await axios.post(generateUrl('/displaynames'), { users: [userId] })\n\t\treturn data?.users?.[userId] || userId\n\t} catch (e) {\n\t\tlogger.debug('Failed to load display name for hovercard', { error: e, userId })\n\t\treturn userId\n\t}\n}\n\n/**\n * Minimal profile when the full profile is disabled or unavailable.\n * Matches Talk / sharing sidebars: avatar + display name (+ status/actions).\n *\n * @param userId - User id\n */\nasync function createLimitedProfile(userId: string): Promise<IProfileData> {\n\treturn {\n\t\tuserId,\n\t\tdisplayname: await resolveDisplayName(userId),\n\t\tactions: [],\n\t\tisUserAvatarVisible: true,\n\t}\n}\n\n/**\n * Ensure user status is loaded when missing.\n *\n * @param userId - User id\n */\nasync function ensureUserStatus(userId: string) {\n\t// Skip the status request when already known (e.g. from NcAvatar)\n\tif (!userStatusReady.value) {\n\t\tuserStatus.value = await fetchUserStatus(userId)\n\t\tuserStatusReady.value = true\n\t}\n}\n\n/**\n * Load profile fields and user status when the hovercard opens\n */\nasync function loadProfile() {\n\tif (!props.user || loading.value) {\n\t\treturn\n\t}\n\n\tconst userId = props.user\n\n\t// Use preloaded, instance, or shared-cache profile; only refresh status if needed\n\tif (!profileData.value) {\n\t\tconst cached = getCachedProfile(userId)\n\t\tif (cached) {\n\t\t\tprofileData.value = cached\n\t\t\tprofileEnabled.value = true\n\t\t}\n\t}\n\tif (profileData.value && profileEnabled.value) {\n\t\tif (!userStatusReady.value) {\n\t\t\tloading.value = true\n\t\t\ttry {\n\t\t\t\tawait ensureUserStatus(userId)\n\t\t\t} finally {\n\t\t\t\tloading.value = false\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tloading.value = true\n\terror.value = false\n\n\tconst statusPromise = userStatusReady.value\n\t\t? Promise.resolve(userStatus.value)\n\t\t: fetchUserStatus(userId)\n\n\ttry {\n\t\tconst profile = await fetchProfileCached(userId, async () => {\n\t\t\tconst { data } = await axios.get(generateOcsUrl('/profile/{userId}', { userId }))\n\t\t\treturn data.ocs?.data ?? null\n\t\t})\n\t\tif (profile) {\n\t\t\tprofileData.value = profile\n\t\t\tprofileEnabled.value = true\n\t\t} else {\n\t\t\t// Profile disabled or empty — show avatar + name like Talk / sharing\n\t\t\tprofileData.value = await createLimitedProfile(userId)\n\t\t\tprofileEnabled.value = false\n\t\t}\n\t} catch (e) {\n\t\tlogger.debug('Failed to load profile for hovercard, showing limited info', { error: e, user: userId })\n\t\tprofileData.value = await createLimitedProfile(userId)\n\t\tprofileEnabled.value = false\n\t}\n\n\ttry {\n\t\tuserStatus.value = await statusPromise\n\t\tuserStatusReady.value = true\n\t} finally {\n\t\tloading.value = false\n\t}\n}\n\n/**\n * Fetch user status for the given user\n *\n * @param userId - User id\n */\nasync function fetchUserStatus(userId: string): Promise<IUserStatus> {\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tif (!(getCapabilities() as any)?.user_status?.enabled || !getCurrentUser()) {\n\t\t\treturn {}\n\t\t}\n\t\tconst { data } = await axios.get(generateOcsUrl('/apps/user_status/api/v1/statuses/{userId}', { userId }))\n\t\treturn data.ocs?.data ?? {}\n\t} catch (e) {\n\t\tlogger.debug('Failed to load user status for hovercard', { error: e, userId })\n\t\treturn {}\n\t}\n}\n</script>\n\n<template>\n\t<div\n\t\tclass=\"profile-hover-card\"\n\t\t:class=\"{ 'profile-hover-card--limited': !showLoading && !!profileData && !error && !profileEnabled }\"\n\t\trole=\"dialog\"\n\t\t:aria-label=\"t('Profile of {user}', { user: resolvedDisplayName })\">\n\t\t<div v-if=\"showLoading\" class=\"profile-hover-card__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" :name=\"t('Loading profile')\" />\n\t\t</div>\n\n\t\t<template v-else-if=\"profileData && !error\">\n\t\t\t<!-- Header band: name, pronouns, status (mirrors ProfileApp.vue) -->\n\t\t\t<div class=\"profile-hover-card__header\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"isCurrentUser\"\n\t\t\t\t\tclass=\"profile-hover-card__header__edit\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"t('Edit profile')\"\n\t\t\t\t\t:title=\"t('Edit profile')\"\n\t\t\t\t\t:href=\"settingsUrl\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<PencilIcon :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<div class=\"profile-hover-card__header__container\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"profileUrl ? 'a' : 'div'\"\n\t\t\t\t\t\tv-if=\"isMobile\"\n\t\t\t\t\t\tclass=\"profile-hover-card__header__container__avatar\"\n\t\t\t\t\t\t:class=\"{ 'profile-hover-card__profile-link': !!profileUrl }\"\n\t\t\t\t\t\tv-bind=\"profileLinkProps\">\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\tclass=\"avatar\"\n\t\t\t\t\t\t\t:user=\"profileData.userId\"\n\t\t\t\t\t\t\t:displayName=\"resolvedDisplayName\"\n\t\t\t\t\t\t\t:size=\"96\"\n\t\t\t\t\t\t\tdisableMenu\n\t\t\t\t\t\t\tdisableTooltip\n\t\t\t\t\t\t\t:preloadedUserStatus=\"avatarUserStatus\"\n\t\t\t\t\t\t\t:isNoUser=\"!profileData.isUserAvatarVisible\" />\n\t\t\t\t\t</component>\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"profile-hover-card__header__container__placeholder\" />\n\t\t\t\t\t<div class=\"profile-hover-card__header__container__identity\">\n\t\t\t\t\t\t<div class=\"profile-hover-card__header__container__displayname\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tv-if=\"profileUrl\"\n\t\t\t\t\t\t\t\tclass=\"profile-hover-card__profile-link\"\n\t\t\t\t\t\t\t\t:href=\"profileUrl\">\n\t\t\t\t\t\t\t\t<h2>{{ resolvedDisplayName }}</h2>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<h2 v-else>{{ resolvedDisplayName }}</h2>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"profileData.pronouns\"\n\t\t\t\t\t\t\t\tclass=\"profile-hover-card__header__container__pronoun-separator\">·</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"profileData.pronouns\"\n\t\t\t\t\t\t\t\tclass=\"profile-hover-card__header__container__pronouns\">{{ profileData.pronouns }}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"userStatus.icon || userStatus.message\"\n\t\t\t\t\t\t\tclass=\"profile-hover-card__header__container__status\">\n\t\t\t\t\t\t\t<span class=\"profile-hover-card__header__container__status-content\">\n\t\t\t\t\t\t\t\t<span>{{ userStatus.icon }} {{ userStatus.message }}</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div class=\"profile-hover-card__body\">\n\t\t\t\t<div class=\"profile-hover-card__content\">\n\t\t\t\t\t<!-- Sidebar: avatar + actions (mirrors ProfileApp.vue) -->\n\t\t\t\t\t<div class=\"profile-hover-card__sidebar\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"profileUrl ? 'a' : 'div'\"\n\t\t\t\t\t\t\tv-if=\"!isMobile\"\n\t\t\t\t\t\t\t:class=\"{ 'profile-hover-card__profile-link': !!profileUrl }\"\n\t\t\t\t\t\t\tv-bind=\"profileLinkProps\">\n\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\tclass=\"avatar\"\n\t\t\t\t\t\t\t\t:user=\"profileData.userId\"\n\t\t\t\t\t\t\t\t:displayName=\"resolvedDisplayName\"\n\t\t\t\t\t\t\t\t:size=\"135\"\n\t\t\t\t\t\t\t\tdisableMenu\n\t\t\t\t\t\t\t\tdisableTooltip\n\t\t\t\t\t\t\t\t:preloadedUserStatus=\"avatarUserStatus\"\n\t\t\t\t\t\t\t\t:isNoUser=\"!profileData.isUserAvatarVisible\" />\n\t\t\t\t\t\t</component>\n\n\t\t\t\t\t\t<div class=\"user-actions\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"actionsLoading && resolvedActions.length === 0\"\n\t\t\t\t\t\t\t\tclass=\"user-actions__loading\">\n\t\t\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" :name=\"t('Loading actions')\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\t\tv-if=\"primaryAction\"\n\t\t\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\t\t\tclass=\"user-actions__primary\"\n\t\t\t\t\t\t\t\t\tv-bind=\"primaryActionButtonProps\"\n\t\t\t\t\t\t\t\t\t@click=\"primaryAction.onClick\">\n\t\t\t\t\t\t\t\t\t<template v-if=\"primaryAction.iconSvg || primaryAction.icon\" #icon>\n\t\t\t\t\t\t\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t\t\t\t\t\t\tv-if=\"primaryAction.iconSvg\"\n\t\t\t\t\t\t\t\t\t\t\t:svg=\"primaryAction.iconSvg\" />\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tv-else-if=\"isIconUrl(primaryAction.icon)\"\n\t\t\t\t\t\t\t\t\t\t\t:src=\"primaryAction.icon\"\n\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"user-actions__primary__icon\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"user-actions__primary__icon-class\"\n\t\t\t\t\t\t\t\t\t\t\t:class=\"primaryAction.icon\" />\n\t\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t\t{{ actionDisplayText(primaryAction) }}\n\t\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t\t\t<NcActions\n\t\t\t\t\t\t\t\t\tv-if=\"otherActions.length > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"user-actions__other\"\n\t\t\t\t\t\t\t\t\t:inline=\"isMobile ? 5 : 4\">\n\t\t\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t\t\t:is=\"otherActionComponent(action)\"\n\t\t\t\t\t\t\t\t\t\tv-for=\"(action, index) in otherActions\"\n\t\t\t\t\t\t\t\t\t\t:key=\"action.id || action.href || index\"\n\t\t\t\t\t\t\t\t\t\tv-bind=\"otherActionProps(action)\">\n\t\t\t\t\t\t\t\t\t\t<template v-if=\"action.iconSvg || action.icon\" #icon>\n\t\t\t\t\t\t\t\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t\t\t\t\t\t\t\tv-if=\"action.iconSvg\"\n\t\t\t\t\t\t\t\t\t\t\t\t:svg=\"action.iconSvg\" />\n\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\tv-else-if=\"isIconUrl(action.icon)\"\n\t\t\t\t\t\t\t\t\t\t\t\t:src=\"action.icon\"\n\t\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"user-actions__other__icon\">\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"user-actions__other__icon-class\"\n\t\t\t\t\t\t\t\t\t\t\t\t:class=\"action.icon\" />\n\t\t\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t\t\t{{ actionDisplayText(action) }}\n\t\t\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t\t</NcActions>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Content blocks: details + bio (mirrors ProfileApp.vue) -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"profileEnabled\"\n\t\t\t\t\t\tclass=\"profile-hover-card__blocks\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"profileData.organisation || profileData.role || profileData.address || localTimeLabel\"\n\t\t\t\t\t\t\tclass=\"profile-hover-card__blocks-details\">\n\t\t\t\t\t\t\t<div v-if=\"profileData.organisation || profileData.role\" class=\"detail\">\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t<BriefcaseIcon\n\t\t\t\t\t\t\t\t\t\tclass=\"detail-icon\"\n\t\t\t\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\t\t\t\ttitle=\"\" />\n\t\t\t\t\t\t\t\t\t{{ profileData.organisation }} <span v-if=\"profileData.organisation && profileData.role\">•</span> {{ profileData.role }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"profileData.address\" class=\"detail\">\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t<MapMarkerIcon\n\t\t\t\t\t\t\t\t\t\tclass=\"detail-icon\"\n\t\t\t\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\t\t\t\ttitle=\"\" />\n\t\t\t\t\t\t\t\t\t{{ profileData.address }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div v-if=\"localTimeLabel\" class=\"detail\">\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t<ClockIcon\n\t\t\t\t\t\t\t\t\t\tclass=\"detail-icon\"\n\t\t\t\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\t\t\t\ttitle=\"\" />\n\t\t\t\t\t\t\t\t\t{{ localTimeLabel }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<template v-if=\"profileData.headline || profileData.biography\">\n\t\t\t\t\t\t\t<h3 v-if=\"profileData.headline\" class=\"profile-hover-card__blocks-headline\">\n\t\t\t\t\t\t\t\t{{ profileData.headline }}\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t<div v-if=\"profileData.biography\" class=\"profile-hover-card__blocks-biography\">\n\t\t\t\t\t\t\t\t<NcRichText :text=\"profileData.biography\" useExtendedMarkdown />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<style lang=\"scss\" scoped>\n// Card-scaled mirror of apps/profile ProfileApp.vue layout\n$card-width: 560px;\n$card-height: 300px;\n$header-height: 127px;\n$sidebar-width: 180px;\n\n.profile-hover-card {\n\tbox-sizing: border-box;\n\tposition: relative;\n\twidth: $card-width;\n\tmax-width: min(#{$card-width}, 90vw);\n\tmin-height: $card-height;\n\toverflow: hidden;\n\tcolor: var(--color-main-text);\n\tbackground-color: var(--color-main-background);\n\tborder-radius: var(--border-radius-large);\n\tdisplay: flex;\n\tflex-direction: column;\n\n\t&__profile-link {\n\t\tcolor: inherit;\n\t\ttext-decoration: none;\n\t\toutline-offset: 2px;\n\n\t\t&:hover,\n\t\t&:focus-visible {\n\t\t\th2 {\n\t\t\t\ttext-decoration: underline;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__header {\n\t\tdisplay: flex;\n\t\tflex-shrink: 0;\n\t\tposition: relative;\n\t\theight: $header-height;\n\t\tbackground-color: var(--color-primary-element-light);\n\n\t\t&__edit {\n\t\t\tposition: absolute;\n\t\t\tinset-block-start: 8px;\n\t\t\tinset-inline-end: 8px;\n\t\t\tz-index: 1;\n\t\t}\n\n\t\t&__container {\n\t\t\talign-self: flex-end;\n\t\t\tdisplay: flex;\n\t\t\talign-items: flex-end;\n\t\t\t// Match sidebar margin-inline-end so name/status line up with role/bio\n\t\t\tgap: 12px;\n\t\t\twidth: 100%;\n\t\t\tmargin: 8px 0;\n\t\t\tpadding-inline: 16px;\n\t\t\tbox-sizing: border-box;\n\n\t\t\t&__placeholder {\n\t\t\t\tflex: 0 0 $sidebar-width;\n\t\t\t\twidth: $sidebar-width;\n\t\t\t}\n\n\t\t\t&__avatar {\n\t\t\t\tflex-shrink: 0;\n\t\t\t}\n\n\t\t\t&__identity {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\tjustify-content: flex-end;\n\t\t\t\talign-items: flex-start;\n\t\t\t\tgap: 4px;\n\t\t\t\tmin-width: 0;\n\t\t\t\tflex: 1;\n\t\t\t\tpadding-inline: 0;\n\t\t\t}\n\n\t\t\t&__displayname {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: baseline;\n\t\t\t\tgap: 8px;\n\t\t\t\tmin-width: 0;\n\n\t\t\t\th2 {\n\t\t\t\t\tfont-size: 24px;\n\t\t\t\t\tmargin: 0;\n\t\t\t\t\toverflow-wrap: anywhere;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&__pronouns {\n\t\t\t\tfont-size: 15px;\n\t\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t\t}\n\n\t\t\t&__status {\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t&__status-content {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 8px;\n\t\t\t\tfont-size: 14px;\n\t\t\t\toverflow-wrap: anywhere;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__body {\n\t\tflex: 1;\n\t\tmin-height: 0;\n\t\tbackground-color: var(--color-main-background);\n\t}\n\n\t&__content {\n\t\tdisplay: flex;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tpadding-inline: 16px 16px;\n\t\tbox-sizing: border-box;\n\t}\n\n\t&__sidebar {\n\t\talign-self: flex-start;\n\t\tpadding-top: 8px;\n\t\twidth: $sidebar-width;\n\t\tmin-width: $sidebar-width;\n\t\tmargin-block: -100px 0;\n\t\tmargin-inline: 0 12px;\n\t}\n\n\t&__header,\n\t&__sidebar {\n\t\t// Specificity hack is needed to override Avatar component styles\n\t\t:deep(.avatar.avatardiv) {\n\t\t\ttext-align: center;\n\t\t\tmargin: auto;\n\t\t\tdisplay: block;\n\t\t\tpadding: 8px;\n\n\t\t\t.avatardiv__user-status {\n\t\t\t\tinset-inline-end: 10px;\n\t\t\t\tbottom: 10px;\n\t\t\t\twidth: 26px;\n\t\t\t\theight: 26px;\n\t\t\t\tbackground-size: 22px;\n\t\t\t\tborder: none;\n\t\t\t\tbackground-color: var(--color-main-background);\n\t\t\t\tline-height: 26px;\n\t\t\t\tfont-size: 16px;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__blocks {\n\t\tmargin: 12px 0 12px 0;\n\t\tdisplay: grid;\n\t\tgap: 8px 0;\n\t\tmin-width: 0;\n\t\tflex: 1;\n\t\toverflow: hidden;\n\t\talign-content: start;\n\n\t\tp, h3 {\n\t\t\toverflow-wrap: anywhere;\n\t\t}\n\n\t\t&-details {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: 2px 0;\n\n\t\t\t.detail {\n\t\t\t\tdisplay: inline-block;\n\t\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t\t\tfont-size: 14px;\n\n\t\t\t\tp .detail-icon {\n\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\tvertical-align: middle;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t&-headline {\n\t\t\tmargin-inline: 0;\n\t\t\tmargin-block: 4px 0;\n\t\t\tfont-weight: bold;\n\t\t\tfont-size: 16px;\n\t\t}\n\n\t\t&-biography {\n\t\t\toverflow: hidden;\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1.4;\n\t\t\tdisplay: -webkit-box;\n\t\t\t-webkit-box-orient: vertical;\n\t\t\t-webkit-line-clamp: 4;\n\t\t\tline-clamp: 4;\n\t\t\ttext-overflow: ellipsis;\n\t\t\toverflow-wrap: anywhere;\n\n\t\t\t:deep(p) {\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__loading {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\theight: 100%;\n\t\tmin-height: $card-height;\n\t\tpadding: 16px;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t// Avatar + name (+ actions) only — drop the empty content column height\n\t&--limited {\n\t\tmin-height: 0;\n\n\t\t.profile-hover-card__body {\n\t\t\tflex: 0 0 auto;\n\t\t}\n\n\t\t.profile-hover-card__content {\n\t\t\theight: auto;\n\t\t\tpadding-block-end: 24px;\n\t\t}\n\n\t\t.profile-hover-card__sidebar {\n\t\t\tmargin-block-end: 0;\n\t\t}\n\n\t\t.user-actions {\n\t\t\tmargin-top: 8px;\n\t\t}\n\t}\n}\n\n.user-actions {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px 0;\n\tmargin-top: 12px;\n\tmax-width: $sidebar-width;\n\n\t&__loading {\n\t\tdisplay: flex;\n\t\tjustify-content: center;\n\t\tpadding: 8px 0;\n\t}\n\n\t&__primary {\n\t\tmargin: 0 auto;\n\t\twidth: $sidebar-width !important;\n\n\t\t&__icon {\n\t\t\theight: 20px;\n\t\t\twidth: 20px;\n\t\t\tobject-fit: contain;\n\t\t\tfilter: var(--primary-invert-if-dark);\n\t\t}\n\n\t\t&__icon-class {\n\t\t\tdisplay: block;\n\t\t\theight: 20px;\n\t\t\twidth: 20px;\n\t\t\tbackground-size: 20px;\n\t\t\tbackground-repeat: no-repeat;\n\t\t\tbackground-position: center;\n\t\t\tfilter: var(--primary-invert-if-dark);\n\t\t}\n\t}\n\n\t&__other {\n\t\tdisplay: flex;\n\t\tjustify-content: center;\n\t\tgap: 0 4px;\n\n\t\t&__icon {\n\t\t\theight: 20px;\n\t\t\twidth: 20px;\n\t\t\tobject-fit: contain;\n\t\t\tfilter: var(--background-invert-if-dark);\n\t\t\talign-self: center;\n\t\t\tmargin: 8px; // compact 44px-ish hit target for the card\n\t\t}\n\n\t\t&__icon-class {\n\t\t\tdisplay: block;\n\t\t\theight: 20px;\n\t\t\twidth: 20px;\n\t\t\tmargin: 8px;\n\t\t\tbackground-size: 20px;\n\t\t\tbackground-repeat: no-repeat;\n\t\t\tbackground-position: center;\n\t\t\tfilter: var(--background-invert-if-dark);\n\t\t}\n\t}\n}\n\n// Mobile layout — uses MOBILE_SMALL_BREAKPOINT (512px), same as useIsSmallMobile()\n@media only screen and (max-width: 512px) {\n\t.profile-hover-card {\n\t\twidth: min(#{$card-width}, calc(100vw - 24px));\n\t\tmax-width: calc(100vw - 24px);\n\t\theight: auto;\n\t\tmin-height: 0;\n\t\tmax-height: min(80vh, 520px);\n\t\toverflow: auto;\n\n\t\t&__header {\n\t\t\theight: auto;\n\t\t\tmin-height: 112px;\n\n\t\t\t&__container {\n\t\t\t\talign-self: flex-end;\n\t\t\t\talign-items: flex-end;\n\t\t\t\tgap: 16px;\n\t\t\t\tmargin: 12px auto;\n\t\t\t\tpadding-inline: 12px;\n\n\t\t\t\t&__avatar {\n\t\t\t\t\talign-self: flex-end;\n\t\t\t\t}\n\n\t\t\t\t&__identity {\n\t\t\t\t\tgap: 6px;\n\t\t\t\t\tpadding-inline: 0;\n\t\t\t\t\t// Stack from the bottom: name, then pronouns, then status\n\t\t\t\t\tjustify-content: flex-end;\n\t\t\t\t}\n\n\t\t\t\t&__displayname {\n\t\t\t\t\tflex-direction: column;\n\t\t\t\t\talign-items: flex-start;\n\t\t\t\t\tgap: 4px;\n\n\t\t\t\t\th2 {\n\t\t\t\t\t\tfont-size: 20px;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t&__pronouns {\n\t\t\t\t\tfont-size: 16px;\n\t\t\t\t}\n\n\t\t\t\t&__pronoun-separator {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\n\t\t\t\t&__status-content {\n\t\t\t\t\tfont-size: 16px;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t&__content {\n\t\t\tdisplay: block;\n\t\t\theight: auto;\n\t\t\tpadding-inline: 12px;\n\t\t\tpadding-block-end: 12px;\n\t\t}\n\n\t\t&__sidebar {\n\t\t\tmargin: unset;\n\t\t\twidth: auto;\n\t\t\tmin-width: 0;\n\t\t\tpadding-top: 0;\n\t\t}\n\n\t\t&__blocks {\n\t\t\twidth: unset;\n\t\t\tmargin: 0;\n\t\t\tpadding: 8px 4px 12px;\n\t\t}\n\n\t\t&__header,\n\t\t&__sidebar {\n\t\t\t:deep(.avatar.avatardiv) {\n\t\t\t\t.avatardiv__user-status {\n\t\t\t\t\tinset-inline-end: 0;\n\t\t\t\t\tbottom: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t&__loading {\n\t\t\tmin-height: 200px;\n\t\t}\n\n\t\t// Disabled profile: avatar + name are in the header; no content blocks\n\t\t&--limited {\n\t\t\t.profile-hover-card__body {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\t}\n\n\t.user-actions {\n\t\twidth: unset;\n\t\tmax-width: none;\n\t\tmargin: 8px auto 0;\n\t\tpadding: 0 4px;\n\n\t\t&__primary {\n\t\t\twidth: 100% !important;\n\t\t\tmax-width: 220px;\n\t\t}\n\n\t\t&__other {\n\t\t\tflex-wrap: wrap;\n\t\t}\n\t}\n}\n\n@media only screen and (max-width: 480px) {\n\t.profile-hover-card {\n\t\twidth: calc(100vw - 16px);\n\t\tmax-width: calc(100vw - 16px);\n\t\tborder-radius: var(--border-radius);\n\n\t\t&__header {\n\t\t\t&__container {\n\t\t\t\tgap: 8px;\n\t\t\t\tpadding-inline: 8px;\n\t\t\t}\n\t\t}\n\t}\n}\n</style>\n\n<docs>\n\n### Description\n\n`NcProfileHoverCard` is the **content** of a profile popover. It is not a trigger and does not wrap `NcPopover`.\n\nWhen `open` becomes `true`, the card loads the user's profile and user status itself.\nThe card content is shown only after both are ready (or preloaded), so status does not flash in late.\nIf the profile is disabled or unavailable, the card still shows avatar, display name, status, and actions\ninstead of an error.\nPass `profile` to skip the profile request.\nPass `displayName` when already known to skip the `/displaynames` lookup for limited profiles;\notherwise the card fetches it when the full profile cannot be loaded.\nPass `preloadedUserStatus` to skip the status request (e.g. when `NcAvatar` already loaded it).\n\n`NcAvatar` already hosts this card on hover or focus for real users (unless `disable-menu` is set)\nand can pass contacts menu actions via `actions` / `actions-loading`.\nIf `actions` is omitted, actions from the profile API are used.\n\n### With NcAvatar\n\n```vue\n\t<NcAvatar user=\"alice\" display-name=\"Alice\" />\n```\n\n### Standalone as popover content\n\nBind `open` to the popover visibility so data is loaded only while the card is shown.\n\n```vue\n<template>\n\t<NcPopover v-model:shown=\"shown\"\n\t\t:triggers=\"['hover', 'focus', 'click']\"\n\t\t:popover-triggers=\"['hover']\"\n\t\t:delay=\"{ show: 400, hide: 200 }\"\n\t\tno-focus-trap\n\t\tpopup-role=\"dialog\">\n\t\t<template #trigger>\n\t\t\t<span tabindex=\"0\">Hover me</span>\n\t\t</template>\n\t\t<NcProfileHoverCard user=\"alice\" :open=\"shown\" />\n\t</NcPopover>\n</template>\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tshown: false,\n\t\t}\n\t},\n}\n</script>\n```\n\n</docs>\n"],"names":["_sfc_main","_hoisted_3","_createElementBlock","_mergeProps","_createElementVNode","_openBlock","_normalizeClass","_unref","_createVNode","_Fragment","_createBlock","_resolveDynamicComponent","_toDisplayString","_renderList","_createSlots","_createTextVNode"],"mappings":"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","x_google_ignoreList":[0,1,2,3]}