diff --git a/src/components/Containers/Main/Main.js b/src/components/Containers/Main/Main.js index f915f8c..32f4a1e 100644 --- a/src/components/Containers/Main/Main.js +++ b/src/components/Containers/Main/Main.js @@ -97,6 +97,34 @@ const Main = () => { }); }; + const setTabEditableHandler = id => { + tabs.setTabEditable(id); + }; + + const handleTabTitleClick = (id, event) => { + switch (event.detail) { + case 1: + if (tabs.activeTabId !== id) { + setTabEditableHandler(null); + } + setTabActiveHandler(id); + break; + case 2: + setTabActiveHandler(id); + setTabEditableHandler(id); + break; + default: + return; + } + }; + + const handleTabTitleBlur = (id, newTabName, prevTabName) => { + setTabEditableHandler(null); + if (newTabName && newTabName !== prevTabName) { + tabs.editTabName(id, newTabName); + } + }; + const renderEmptyScreen = () => { return (
@@ -123,8 +151,12 @@ const Main = () => { setTabActiveHandler(tab.id)} + onBlur={tabNewName => + handleTabTitleBlur(tab.id, tabNewName, tab.name) + } + onClick={e => handleTabTitleClick(tab.id, e)} onDelete={e => removeTabHandler(e, tab.id)} /> )); diff --git a/src/components/UI/NavItem/NavItem.js b/src/components/UI/NavItem/NavItem.js index f4e0058..4397a4d 100644 --- a/src/components/UI/NavItem/NavItem.js +++ b/src/components/UI/NavItem/NavItem.js @@ -1,16 +1,62 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, {useState} from 'react'; import {ReactComponent as XIcon} from '../../../assets/svg/x.svg'; import styles from './NavItem.module.scss'; -const NavItem = ({text, isActive, onClick, onDelete}) => { - const classes = [styles.navItem, isActive && styles.isActive].join( - ' ' - ); +const NavItem = ({ + text, + isActive, + isEditable, + onBlur, + onClick, + onDelete +}) => { + const [tabNameInput, setTabNameInput] = useState(text); + const classes = [ + styles.navItem, + isActive && styles.isActive, + isEditable && styles.isEditable + ].join(' '); + const handleKeyPress = event => { + if (event?.key === 'Enter') { + handleBlur(tabNameInput); + } + }; + const editingStateStyle = { + maxWidth: 200 + }; + const viewingStateStyle = { + maxWidth: 142 + }; + const getInputSize = () => { + return tabNameInput?.length + ? (tabNameInput.length * 0.75).toFixed(0) + : 5; + }; + const handleBlur = tabNameInput => { + if (tabNameInput) { + onBlur(tabNameInput); + } else { + setTabNameInput(text); + onBlur(text); + } + }; return ( ); @@ -19,6 +65,8 @@ const NavItem = ({text, isActive, onClick, onDelete}) => { NavItem.propTypes = { text: PropTypes.string, isActive: PropTypes.bool, + isEditable: PropTypes.bool, + onBlur: PropTypes.func, onClick: PropTypes.func, onDelete: PropTypes.func }; diff --git a/src/components/UI/NavItem/NavItem.module.scss b/src/components/UI/NavItem/NavItem.module.scss index 0564cd9..bc1438a 100644 --- a/src/components/UI/NavItem/NavItem.module.scss +++ b/src/components/UI/NavItem/NavItem.module.scss @@ -2,13 +2,41 @@ .navItem { color: $--color-1; - border: none; + border: 0; + border-bottom: 3px solid transparent; + border-right: 1px solid transparent; background: transparent; font-weight: bold; font-size: 15px; cursor: pointer; - transition: 0.3s; + transition: 0.1s; padding-left: 10px; + display: flex; + flex-flow: row nowrap; + align-items: center; + + > div { + overflow: hidden; + height: 20px; + display: inline-block; + margin-top: 2px; + border-bottom-color: transparent; + } + + input { + border: 0; + outline: 0; + padding: 0; + appearance: none; + color: inherit; + height: inherit; + font-size: inherit; + background: inherit; + font-weight: inherit; + cursor: text; + width: auto; + transition: max-width 0.2s ease-in-out; + } &:hover { background-color: $--bg-color-memory; @@ -16,10 +44,16 @@ &.isActive { background-color: $--bg-color-editor; - border-bottom: 3px solid $--color-5-active; + border-bottom-color: $--color-5-active; cursor: default; } + &.isEditable { + > div { + border-bottom: 1px solid #7c87ff; + } + } + svg { margin-left: 10px; cursor: pointer; diff --git a/src/context/tabs/TabsProvider.js b/src/context/tabs/TabsProvider.js index b09378d..ea3fe56 100644 --- a/src/context/tabs/TabsProvider.js +++ b/src/context/tabs/TabsProvider.js @@ -18,6 +18,14 @@ const TabsProvider = ({children}) => { dispatch({type: actions.REMOVE_TAB, payload: id}); }; + const editTabNameHandler = (id, name) => { + dispatch({type: actions.EDIT_TAB_NAME, payload: {id, name}}); + }; + + const setTabEditableHandler = id => { + dispatch({type: actions.SET_TAB_NAME_EDITABLE, payload: id}); + }; + const setTabActiveHandler = id => { const tab = getTabById(id); setParam(tab.type, tab.file); @@ -31,11 +39,14 @@ const TabsProvider = ({children}) => { const context = { tabs: state.tabs, activeTabId: state.activeTabId, + editableTabNameId: state.editableTabNameId, tabsCount: state.tabsCount, removeTab: removeTabHandler, setTabActive: setTabActiveHandler, addTab: addTabHandler, - getActiveTab: getActiveTabHandler + getActiveTab: getActiveTabHandler, + setTabEditable: setTabEditableHandler, + editTabName: editTabNameHandler }; return ( diff --git a/src/context/tabs/tabs-context.js b/src/context/tabs/tabs-context.js index 24bd757..7f114ea 100644 --- a/src/context/tabs/tabs-context.js +++ b/src/context/tabs/tabs-context.js @@ -6,7 +6,8 @@ const TabsContext = createContext({ removeTab: () => {}, setTabActive: () => {}, addTab: () => {}, - getActiveTab: () => {} + getActiveTab: () => {}, + setTabEditable: () => {} }); export {TabsContext}; diff --git a/src/context/tabs/tabs-reducer.js b/src/context/tabs/tabs-reducer.js index 764ed6f..b1f53b3 100644 --- a/src/context/tabs/tabs-reducer.js +++ b/src/context/tabs/tabs-reducer.js @@ -3,13 +3,16 @@ import {id} from '../../utils/id'; const actions = { ADD_TAB: 'Tab/ADD_TAB', REMOVE_TAB: 'Tab/REMOVE_TAB', - SET_TAB_ACTIVE: 'Tab/SET_TAB_ACTIVE' + SET_TAB_ACTIVE: 'Tab/SET_TAB_ACTIVE', + SET_TAB_NAME_EDITABLE: 'Tab/SET_TAB_NAME_EDITABLE', + EDIT_TAB_NAME: 'Tab/EDIT_TAB_NAME' }; const initialState = { tabs: [], activeTabId: null, - tabsCount: 0 + tabsCount: 0, + editableTabNameId: null }; const reducer = (state, action) => { @@ -26,6 +29,20 @@ const reducer = (state, action) => { }; } + case actions.EDIT_TAB_NAME: { + const {id, name} = action.payload; + let {tabs} = state; + const indexToEditName = tabs.findIndex(tab => tab.id === id); + return { + ...state, + tabs: [ + ...tabs.slice(0, indexToEditName), + {...tabs[indexToEditName], name}, + ...tabs.slice(indexToEditName + 1) + ] + }; + } + case actions.REMOVE_TAB: { const id = action.payload; let {activeTabId, tabs} = state; @@ -52,6 +69,12 @@ const reducer = (state, action) => { activeTabId: action.payload }; + case actions.SET_TAB_NAME_EDITABLE: + return { + ...state, + editableTabNameId: action.payload + }; + default: return initialState; }