diff --git a/frontend/src/components/modals/AssignItemForModal.jsx b/frontend/src/components/modals/AssignItemForModal.jsx index f23d928..5b16973 100644 --- a/frontend/src/components/modals/AssignItemForModal.jsx +++ b/frontend/src/components/modals/AssignItemForModal.jsx @@ -1,10 +1,16 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import "../../styles/components/AssignItemForModal.css"; function getMemberLabel(member) { return member.display_name || member.name || member.username || `User ${member.id}`; } +function getMemberOptionLabel(member, maxLength = 28) { + const label = getMemberLabel(member); + if (label.length <= maxLength) return label; + return `${label.slice(0, maxLength - 3)}...`; +} + export default function AssignItemForModal({ isOpen, members, @@ -12,6 +18,8 @@ export default function AssignItemForModal({ onConfirm }) { const [selectedUserId, setSelectedUserId] = useState(""); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const dropdownRef = useRef(null); const hasMembers = members.length > 0; const selectedMember = useMemo( @@ -22,6 +30,7 @@ export default function AssignItemForModal({ useEffect(() => { if (!isOpen) return; setSelectedUserId(members[0] ? String(members[0].id) : ""); + setIsDropdownOpen(false); }, [isOpen, members]); useEffect(() => { @@ -29,13 +38,31 @@ export default function AssignItemForModal({ const handleEscape = (event) => { if (event.key === "Escape") { - onCancel(); + if (isDropdownOpen) { + setIsDropdownOpen(false); + } else { + onCancel(); + } } }; window.addEventListener("keydown", handleEscape); return () => window.removeEventListener("keydown", handleEscape); - }, [isOpen, onCancel]); + }, [isDropdownOpen, isOpen, onCancel]); + + useEffect(() => { + if (!isOpen || !isDropdownOpen) return undefined; + + const handlePointerDown = (event) => { + if (!dropdownRef.current) return; + if (!dropdownRef.current.contains(event.target)) { + setIsDropdownOpen(false); + } + }; + + window.addEventListener("pointerdown", handlePointerDown); + return () => window.removeEventListener("pointerdown", handlePointerDown); + }, [isDropdownOpen, isOpen]); if (!isOpen) return null; @@ -48,27 +75,54 @@ export default function AssignItemForModal({
- Who should this item be assigned to? -
{hasMembers ? (diff --git a/frontend/src/styles/components/AssignItemForModal.css b/frontend/src/styles/components/AssignItemForModal.css index 479dc9c..a3ae81b 100644 --- a/frontend/src/styles/components/AssignItemForModal.css +++ b/frontend/src/styles/components/AssignItemForModal.css @@ -1,15 +1,91 @@ .assign-item-for-modal { + width: min(420px, calc(100vw - (2 * var(--spacing-md)))); max-width: 420px; -} - -.assign-item-for-modal-description { - margin: 0 0 var(--spacing-md) 0; - color: var(--color-text-secondary); - font-size: var(--font-size-sm); + overflow-x: hidden; } .assign-item-for-modal-field { margin-bottom: var(--spacing-sm); + width: 100%; + min-width: 0; +} + +.assign-item-for-dropdown { + position: relative; + width: 100%; + min-width: 0; +} + +.assign-item-for-dropdown-trigger { + width: 100%; + min-width: 0; + max-width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-xs); + padding: var(--input-padding-y) var(--input-padding-x); + border: var(--border-width-thin) solid var(--input-border-color); + border-radius: var(--input-border-radius); + background: var(--color-bg-surface); + color: var(--color-text-primary); + font-size: var(--font-size-base); + text-align: left; + cursor: pointer; +} + +.assign-item-for-dropdown-trigger.is-open, +.assign-item-for-dropdown-trigger:focus-visible { + outline: none; + border-color: var(--input-focus-border-color); + box-shadow: var(--input-focus-shadow); +} + +.assign-item-for-dropdown-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.assign-item-for-dropdown-caret { + flex-shrink: 0; + font-size: 0.75rem; + color: var(--color-text-secondary); +} + +.assign-item-for-dropdown-menu { + position: absolute; + top: calc(100% + 6px); + left: 0; + right: 0; + z-index: 3; + max-height: 180px; + overflow-y: auto; + background: var(--color-bg-surface); + border: var(--border-width-thin) solid var(--input-border-color); + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-lg); +} + +.assign-item-for-dropdown-option { + width: 100%; + display: block; + text-align: left; + margin: 0; + border: 0; + border-radius: 0; + padding: 10px var(--input-padding-x); + background: transparent; + color: var(--color-text-primary); + cursor: pointer; +} + +.assign-item-for-dropdown-option:hover { + background: var(--color-bg-hover); +} + +.assign-item-for-dropdown-option.is-selected { + background: var(--color-primary-light); } .assign-item-for-modal-empty {