diff --git a/src/App.scss b/src/App.scss index d5d8961..62de718 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,6 +1,8 @@ * { box-sizing: border-box; + font-family: Roboto; + -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ -khtml-user-select: none; /* Konqueror HTML */ diff --git a/src/components/Card/Card.jsx b/src/components/Card/Card.jsx index 739570b..5c6202e 100644 --- a/src/components/Card/Card.jsx +++ b/src/components/Card/Card.jsx @@ -39,12 +39,12 @@ export const Card = ({ return (
{ +const ProjectItem = ({ + closeProjectDropdown, + id, + name, +}) => { const { deleteBtnRef, isActiveProject, @@ -16,33 +18,47 @@ const ProjectItem = ({ closeProjectDropdown, id, name }) => { confirmDeleteProject, } = useProjectItemHooks({ closeProjectDropdown, id, name }); - const [deleteDisplayIcon, setDeleteDisplayIcon] = React.useState(DeleteIcon); + const { + onChange, + isEditable, + startEdit, + endEdit, + handleKeyPress, + } = useTitleHooks(); return ( - <li key={id} className='project-li'> - <div - className={'project-container' + (isActiveProject ? ' active-proj' : '')} - onClick={!isActiveProject ? switchProject : null} + <div + className={`project-item ${isActiveProject ? 'active-project' : ''}`} + onClick={!isActiveProject ? switchProject : null} + > + <input + className='project-name' + maxLength='300' + onBlur={endEdit} + onChange={event => onChange(event.target.value)} + onDoubleClick={startEdit} + onDragOver={event => event.preventDefault()} + onKeyDown={handleKeyPress} + readOnly={!isActiveProject || !isEditable} + spellCheck={false} + style={{ backgroundColor: isEditable ? '#C1E9FF' : 'transparent'}} + title={name} + type='text' + value={name} + /> + <button className='copy' onClick={copyProject}> + <img alt='Copy' src={CopyIcon} /> + <span className='tooltip'>Duplicate project</span> + </button> + <button + className='delete' + ref={deleteBtnRef} + onClick={confirmDeleteProject} > - <span className='project-name'> - {name} - </span> - <button className='copy' onClick={copyProject}> - <img alt='Copy' src={CopyIcon} /> - <span className='tooltip'>Duplicate project</span> - </button> - <button - className='delete' - ref={deleteBtnRef} - onClick={confirmDeleteProject} - onMouseOver={e => setDeleteDisplayIcon(DeleteRedIcon)} - onMouseOut={e => setDeleteDisplayIcon(DeleteIcon)} - > - <img alt='Delete' src={deleteDisplayIcon} /> - </button> - <div className={'back' + (isActiveProject ? ' active' : '')} /> - </div> - </li> + <img alt='Delete' /> + </button> + <div className={`back ${isActiveProject ? ' active' : ''}`} /> + </div> ); }; diff --git a/src/components/HeaderMenu/Projects.jsx b/src/components/HeaderMenu/Projects.jsx index 19e1795..71d3d96 100644 --- a/src/components/HeaderMenu/Projects.jsx +++ b/src/components/HeaderMenu/Projects.jsx @@ -14,31 +14,36 @@ const Projects = () => { openProjectDropdown, closeProjectDropdown, activeProject, + activeProjectName, projects, newProject, } = useProjectHooks(); - let projectsList = activeProject - ? [ + let projectsList = []; + if (!!activeProject) { + projectsList = [ + ...projectsList, + <li key={activeProject} className='project-li'> <ProjectItem id={activeProject} - key={activeProject} - name={projects[activeProject]} + name={activeProjectName} closeProjectDropdown={closeProjectDropdown} - />, - ] - : []; + /> + </li>, + ]; + } for (let project in projects) { if (project !== activeProject) { const name = projects[project]; projectsList = [ ...projectsList, - <ProjectItem - id={project} - key={project} - name={name} - closeProjectDropdown={closeProjectDropdown} - />, + <li key={project} className='project-li'> + <ProjectItem + id={project} + name={name} + closeProjectDropdown={closeProjectDropdown} + /> + </li>, ]; } } @@ -50,17 +55,22 @@ const Projects = () => { onClick={showProjectDropdown ? closeProjectDropdown : openProjectDropdown} ref={btnRef} > - <span>Projects</span> - <img /> + <span className='button-text'>Projects</span> + <img className='img-arrow' /> </button> <div - className='header-dropdown' + className='header-dropdown project-dropdown' ref={dropdownRef} style={{ display: showProjectDropdown ? 'block' : 'none' }} > - <ul className='projects-ul'> - {projectsList} - </ul> + <div + className='list-of-projects' + style={{ overflowY: (Object.keys(projects).length > 5) ? 'scroll' : 'hidden' }} + > + <ul className='projects-ul'> + {projectsList} + </ul> + </div> <div className='new-project' onClick={newProject} diff --git a/src/components/HeaderMenu/Title.jsx b/src/components/HeaderMenu/Title.jsx index e65e289..6e2a71c 100644 --- a/src/components/HeaderMenu/Title.jsx +++ b/src/components/HeaderMenu/Title.jsx @@ -6,27 +6,27 @@ import './index.scss'; const Title = () => { const { - titleRef, - titleValue, - changeTitleValue, - endTitleEdit, - handleTitleKeyPress, + title, + onChange, + startEdit, + endEdit, + handleKeyPress, } = useTitleHooks(); return ( <div className='usermenu-title'> <input maxLength='300' - onBlur={endTitleEdit} - onChange={e => changeTitleValue(e.target.value)} - onDragOver={e => e.preventDefault()} - onKeyDown={handleTitleKeyPress} - ref={titleRef} + onBlur={endEdit} + onClick={startEdit} + onChange={event => onChange(event.target.value)} + onDragOver={event => event.preventDefault()} + onKeyDown={handleKeyPress} required - size='' - title={titleValue} + spellCheck={false} + title={title} type='text' - value={titleValue ?? ''} + value={title} /> </div> ); diff --git a/src/components/HeaderMenu/UserOptions.jsx b/src/components/HeaderMenu/UserOptions.jsx index b91fb27..49f23b9 100644 --- a/src/components/HeaderMenu/UserOptions.jsx +++ b/src/components/HeaderMenu/UserOptions.jsx @@ -32,8 +32,8 @@ const UserOptions = () => { onClick={showUserOptionsDropdown ? closeUserOptionsDropdown : openUserOptionsDropdown} ref={btnRef} > - <span>{username}</span> - <img /> + <span className='button-text'>{username}</span> + <img className='img-arrow' /> </button> <div className='header-dropdown' diff --git a/src/components/HeaderMenu/hooks.js b/src/components/HeaderMenu/hooks.js index 013f3ba..b7dc7c4 100644 --- a/src/components/HeaderMenu/hooks.js +++ b/src/components/HeaderMenu/hooks.js @@ -24,30 +24,28 @@ export const useTitleHooks = () => { const dispatch = useDispatch(); const id = useSelector(state => state.session.activeCampaignId); - const value = useSelector(state => state.project.present.title || ''); - const [ titleValue, setTitleValue ] = useState(''); - const titleRef = useRef(); - - // Initialize title value - useEffect(() => { - setTitleValue(value); - }, [setTitleValue, value]); + const title = useSelector(state => state.project.present.title || ''); + const [ isEditable, setIsEditable ] = useState(false); + const startEdit = () => setIsEditable(true); const endEdit = () => { document.getSelection().removeAllRanges(); - if (titleValue !== value) { - dispatch(actions.project.updateProjectTitle({ title: titleValue })); - dispatch(actions.session.updateProjectTitle({ id, title: titleValue })); - } + setIsEditable(false); }; return { - titleRef, - titleValue, - changeTitleValue: (newValue) => setTitleValue(newValue), - endTitleEdit: endEdit, - handleTitleKeyPress: (event) => { - if(event.key === 'Enter' || event.key === 'Tab') { + title, + onChange: (newValue) => { + if (newValue !== title) { + dispatch(actions.project.updateProjectTitle({ title: newValue })); + dispatch(actions.session.updateProjectTitle({ id, title: newValue })); + } + }, + isEditable, + startEdit, + endEdit, + handleKeyPress: (event) => { + if (event.key === 'Enter' || event.key === 'Tab') { endEdit(); } }, @@ -95,6 +93,7 @@ export const useProjectHooks = () => { const projects = useSelector(state => state.session.campaignList || []); const projectData = useSelector(state => state.project.present || {}); const [ showProjectDropdown, setShowProjectDropdown ] = useState(false); + const [ deleteBtnIcon, setDeleteBtnIcon ] = useState(); const btnRef = useRef(); const dropdownRef = useRef(); @@ -117,6 +116,7 @@ export const useProjectHooks = () => { openProjectDropdown: () => setShowProjectDropdown(true), closeProjectDropdown: () => setShowProjectDropdown(false), activeProject, + activeProjectName: projectData.title, projects: sortedProjects, newProject: () => { if (!!activeProject) { diff --git a/src/components/HeaderMenu/index.scss b/src/components/HeaderMenu/index.scss index a95fe72..dee9d9d 100644 --- a/src/components/HeaderMenu/index.scss +++ b/src/components/HeaderMenu/index.scss @@ -79,83 +79,121 @@ $border-radius: 0.5rem; } } -$projects-height: 40px; +$project-height: 40px; +$scrollbar-width: 12px; .projects { position: relative; - .projects-ul { - height: ($projects-height + 1) * 7; - overflow-y: scroll; - overflow-x: hidden; + .project-dropdown { + display: flex; + flex-flow: column nowrap; - .project-li { - height: $projects-height; - - .project-container { - position: relative; - height: 100%; - border-bottom: 1px solid #F4F4F4; - - .project-name { - z-index: 1; - font-weight: 600; - .tooltip { right: -20%; top: 115%; white-space: nowrap; } - .tooltip::after { bottom: 100%; right: 8%; border-bottom-color: black; } - &:hover ~ .back { background-color: #C1E9FF; } - } - .copy { - margin: auto; - z-index: 1; - .tooltip { right: -20%; top: 115%; white-space: nowrap; } - .tooltip::after { bottom: 100%; right: 8%; border-bottom-color: black; } - &:hover { background-color: #C1E9FF; } - } - .delete { - margin: auto; - z-index: 1; - color: red; - &:hover { background-color: #FFE8E0; } - } - .back { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - border: 0; - &:hover { background-color: #C1E9FF; } + .list-of-projects { + max-height: ($project-height + 1) * 5; + + .projects-ul { + .project-li { + height: $project-height; } } - - .active-proj { - .project-name { font-weight: 700; } - .project-name:hover ~ .back { background-color: #DBE2EB; } - .copy:hover { background-color: white; } - .back { background-color: #DBE2EB; } - .back:hover { background-color: #DBE2EB; } + + &::-webkit-scrollbar { width: $scrollbar-width; } + &::-webkit-scrollbar-track { background-color: #00000000; } + &::-webkit-scrollbar-thumb { + background-color: #363B4EA6; + border: 4px solid white; + border-radius: 12px; } } - &::-webkit-scrollbar { width: 12px; } - &::-webkit-scrollbar-track { background-color: #00000000; } - &::-webkit-scrollbar-thumb { - background-color: #363B4EA6; - border: 4px solid white; - border-radius: 12px; + .new-project { + height: $project-height; + padding: 9px; + display: grid; + grid-template-columns: 1fr auto; + span { + padding: 0 10px; + font-weight: 600; + } + &:hover { background-color: #C1E9FF; } + img { margin: 4px; } } - } + + .project-item { + position: relative; + height: 100%; + padding: 9px; + border-bottom: 1px solid #F4F4F4; - .new-project { - padding: 9px; - font-weight: 600; - display: grid; - grid-template-columns: 1fr auto; - span { - padding: 0 10px; - font-weight: 600; + display: grid; + grid-template-columns: 1fr auto auto; + gap: 5px; + justify-content: end; + + .project-name { + padding: 0 10px; + border: 0; + background-color: transparent; + z-index: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + color: #353535; + font-size: 12px; + font-weight: 600; + line-height: 25px; + letter-spacing: 0.05em; + text-align: left; + + .tooltip { right: -20%; top: 115%; white-space: nowrap; } + .tooltip::after { bottom: 100%; right: 8%; border-bottom-color: black; } + &:hover ~ .back { background-color: #C1E9FF; } + } + button { + height: 23px; + width: 100%; + padding: 0; + margin: auto; + border-radius: 5px; + background-color: transparent; + text-align: left; + line-height: 30px; + z-index: 1; + img { margin: 4px; } + } + .copy { + .tooltip { right: -20%; top: 115%; white-space: nowrap; } + .tooltip::after { bottom: 100%; right: 8%; border-bottom-color: black; } + &:hover { background-color: #C1E9FF; } + } + .delete { + color: red; + img { content: url('../../assets/icons/delete.svg'); } + &:hover { background-color: #FFE8E0; } + &:hover > img { content: url('../../assets/icons/delete-red.svg')} + } + .back { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + border: 0; + &:hover { background-color: #C1E9FF; } + } + } + + .active-project { + // height: $project-height; + // padding-right: $scrollbar-width; + background-color: #DBE2EB; + + .project-name { font-weight: 700; } + .project-name:hover ~ .back { background-color: #DBE2EB; } + .copy:hover { background-color: white; } + .back { background-color: #DBE2EB; } + .back:hover { background-color: #DBE2EB; } } - &:hover { background-color: #C1E9FF; } - img { margin: 4px; } } } @@ -414,15 +452,15 @@ $projects-height: 40px; background-color: transparent; display: flex; flex-flow: row nowrap; - span { + .button-text { padding: 7px; white-space: nowrap; } - img { + .img-arrow { transition-duration: .5s; content: url('../../assets/icons/dropdown-arrow.svg'); } - &:hover > img { transform: translateY(3px); } + &:hover > .img-arrow { transform: translateY(3px); } } .header-dropdown { @@ -449,31 +487,6 @@ $projects-height: 40px; padding: 0; li { list-style: none; - div { - padding: 9px; - display: grid; - grid-template-columns: 1fr auto auto; - gap: 5px; - justify-content: end; - span { - padding: 0 10px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - button { - height: 23px; - width: 100%; - padding: 0; - margin: 0 1px; - border-radius: 5px; - background-color: transparent; - text-align: left; - line-height: 30px; - img { margin: 4px; } - &:hover { background-color: white; } - } - } } } } diff --git a/src/components/Library/SearchBar.jsx b/src/components/Library/SearchBar.jsx index 9db9627..898c79e 100644 --- a/src/components/Library/SearchBar.jsx +++ b/src/components/Library/SearchBar.jsx @@ -17,7 +17,7 @@ const SearchBar = ({ type='search' value={searchString} /> - <div className='search-count'> + <div className={`search-count ${!searchString ? 'shift-right' : ''}`}> {countDisplay} </div> <img diff --git a/src/components/Library/index.scss b/src/components/Library/index.scss index 931cadf..9ea537b 100644 --- a/src/components/Library/index.scss +++ b/src/components/Library/index.scss @@ -36,6 +36,12 @@ $library-width: 35vw; border-radius: 12px; box-shadow: 2px 2px 8px -3px #D7D6D6; + img { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%, -50%); + } + .tooltip { top: 27%; right: 120%; @@ -56,7 +62,7 @@ $search-img-offset: calc(($search-height - $search-img-height) / 2); $search-clear-height: 10px; $search-clear-offset: calc(($search-height - $search-clear-height) / 2); $search-count-height: 16px; -$search-count-top: calc(($search-height - $search-count-height) / 2); +$search-count-top: calc(($search-height - $search-count-height) / 2) + 1; //+1 for some reason here $search-count-offset: $search-clear-height + $search-clear-offset + $search-clear-offset; $search-text-left-offset: $search-img-height + $search-img-offset*2; $search-text-right-offset: $search-count-offset + 80px; @@ -105,15 +111,19 @@ $search-text-right-offset: $search-count-offset + 80px; top: $search-count-top; right: $search-count-offset; color: #7E849A9C; + + font-family: Roboto; + font-size: 14px; + font-weight: 400; + line-height: 16.41px; + letter-spacing: 0.05em; } + .shift-right { right: $search-clear-offset !important; } .search-clear { - // height: $search-clear-height; height: $search-height; position: absolute; padding: $search-clear-offset; - // top: $search-clear-offset; - // right: $search-clear-offset; top: 0; right: 0; content: url('../../assets/icons/search-clear-black.svg'); @@ -169,6 +179,7 @@ $search-text-right-offset: $search-count-offset + 80px; line-height: 14.06px; letter-spacing: 0.015em; text-align: left; + white-space: nowrap; } } @@ -193,9 +204,10 @@ $search-text-right-offset: $search-count-offset + 80px; } .divider { + width: 1px; height: 25px; margin: auto 10px; - border: 1px solid black; + background-color: black; } } } @@ -221,7 +233,7 @@ $search-text-right-offset: $search-count-offset + 80px; } } -$shift-right: 16px; +$shift-right: 18px; $scrollbar-width: 8px; $scrollbar-border: calc(($shift-right - $scrollbar-width) / 2); $scrollbar-radius: 6px + $scrollbar-border; @@ -230,8 +242,9 @@ $scrollbar-radius: 6px + $scrollbar-border; flex-flow: column nowrap; gap: 20px; overflow-y: scroll; - padding: 5px; + padding: 8px; margin-right: -$shift-right; + overflow-x: hidden; &::-webkit-scrollbar { position: absolute;