You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

114 lines
2.9 KiB

/** * PersonCard.vue - Individual person display component * * Extracted from
GiftedDialog.vue to handle person entity display * with selection states and
conflict detection. * * @author Matthew Raymer */
<template>
<li :class="cardClasses" @click="handleClick">
<div class="relative w-fit mx-auto">
<EntityIcon
v-if="person.did"
:contact="person"
class="!size-[3rem] mx-auto border border-slate-300 bg-white overflow-hidden rounded-full mb-1"
/>
<font-awesome
v-else
icon="circle-question"
class="text-slate-400 text-5xl mb-1"
/>
<!-- Time icon overlay for contacts -->
<div
v-if="person.did && showTimeIcon"
class="rounded-full bg-slate-400 absolute bottom-0 right-0 p-1 translate-x-1/3"
>
<font-awesome icon="clock" class="block text-white text-xs w-[1em]" />
</div>
</div>
<h3 :class="nameClasses">
{{ person.name || person.did || "Unnamed" }}
</h3>
</li>
</template>
<script lang="ts">
import { Component, Prop, Vue, Emit } from "vue-facing-decorator";
import EntityIcon from "./EntityIcon.vue";
import { Contact } from "../db/tables/contacts";
/**
* PersonCard - Individual person display with selection capability
*
* Features:
* - Person avatar using EntityIcon
* - Selection states (selectable, conflicted, disabled)
* - Time icon overlay for contacts
* - Click event handling
* - Emits click events for parent handling
*/
@Component({
components: {
EntityIcon,
},
})
export default class PersonCard extends Vue {
/** Contact data to display */
@Prop({ required: true })
person!: Contact;
/** Whether this person can be selected */
@Prop({ default: true })
selectable!: boolean;
/** Whether this person would create a conflict if selected */
@Prop({ default: false })
conflicted!: boolean;
/** Whether to show time icon overlay */
@Prop({ default: false })
showTimeIcon!: boolean;
/**
* Computed CSS classes for the card
*/
get cardClasses(): string {
if (!this.selectable || this.conflicted) {
return "opacity-50 cursor-not-allowed";
}
return "cursor-pointer hover:bg-slate-50";
}
/**
* Computed CSS classes for the person name
*/
get nameClasses(): string {
const baseClasses =
"text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden";
if (this.conflicted) {
return `${baseClasses} text-slate-400`;
}
return baseClasses;
}
/**
* Handle card click - only emit if selectable and not conflicted
*/
handleClick(): void {
if (this.selectable && !this.conflicted) {
this.emitPersonSelected(this.person);
}
}
// Emit methods using @Emit decorator
@Emit("person-selected")
emitPersonSelected(person: Contact): Contact {
return person;
}
}
</script>
<style scoped>
/* Component-specific styles if needed */
</style>