Search code examples
javascriptreactjsfrontendweb-frontend

How to apply the style of the rest of the items when I click item in REACT?


pic1

enter image description here

pic2

enter image description here

pic3

enter image description here

When I select one item, I want to apply the new style of one item and the original style of the rest of the items

Example

when I click SubBookmark33

Current

pic1 -> pic2

But I want to

pic1 -> pic3

BookmarksFolder.js

import BookmarksFolderNode from './BookmarksFolderNode';
import classes from './BookmarksFolder.module.css';

import { folders } from '../../resources/data';

function BookmarksFolder() {

  return (
    <div className={classes.bookmarksFolder}>
      {folders.map((folder) => (
        <BookmarksFolderNode
          key={folder.id}
          folder={folder}
        />
      ))}
    </div>
  );
}

export default BookmarksFolder;

BookmarksFolderNode.js

import { useState, useRef } from 'react';
import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
import Folder from '../../resources/img/folder.svg';
import OpenedFolder from '../../resources/img/opened_folder.svg';

import classes from './BookmarksFolderNode.module.css';

function BookmarksFolderNode(props) {
  const [folderIsOpen, setFolderIsOpen] = useState(false);
  const tab = useRef();
  const img = useRef();
  const title = useRef();

  const selectFolderHandler = () => {
    tab.current.style.backgroundColor = '#1a73eb';
    img.current.src = OpenedFolder;
    title.current.style.color = '#1a73eb';
  };

  const openFolderHandler = () => {
    setFolderIsOpen((prevState) => !prevState);
  };

  const paddingLeft = 20 * (props.folder.depth - 1);

return (
    <div className={classes.bookmarksFolderNode}>
      <div className={classes.bookmarksMainFolderNode}>
        <div className={classes.verticalTab} ref={tab}></div>
        <div className={classes.innerContainer} style={{paddingLeft}}>
          <div className={classes.icon} onClick={openFolderHandler}>
            {folderIsOpen ? (
              <AiFillCaretDown className={classes.ironIcon} />
            ) : (
              <AiFillCaretRight className={classes.ironIcon} />
            )}
          </div>
          <img src={Folder} className={classes.folderIcon} ref={img} />
          <div
            className={classes.menuLabel}
            onClick={selectFolderHandler}
            ref={title}
          >
            {props.folder.title}
          </div>
        </div>
      </div>
      <div className={classes.bookmarksSubFolderNode}>
        {props.folder.subFolder &&
          props.folder.subFolder.map((subFolder) => (
            <BookmarksFolderNode key={subFolder.id} folder={subFolder} />
          ))}
      </div>
    </div>
  );
}

export default BookmarksFolderNode;

data.js

export const folders = [
  {
    id: 1,
    depth: 1,
    title: 'Bookmark 1',
    subFolder: [
      {
        id: 1,
        depth: 2,
        title: 'SubBookmark 1',
        subFolder: [
          {
            id: 1,
            depth: 3,
            title: 'SubBookmark 11',
          },
          {
            id: 2,
            depth: 3,
            title: 'SubBookmark 22',
          },
          {
            id: 3,
            depth: 3,
            title: 'SubBookmark 33',
            subFolder: [
              {
                id: 1,
                depth: 4,
                title: 'SubBookmark 111',
              },
            ],
          },
        ],
      },
      {
        id: 2,
        depth: 2,
        title: 'SubBookmark 2',
      },
    ],
  },
];

Solution

  • The idea is that, when you select a folder, you should deselect the others from parent component BookmarksFolder. The solution is a little bit tricky because you select the folder using BookmarksFolderNode local state (I mean folderIsOpen).

    Well, lets start to use parent component's state to select/deselect folder:

    import BookmarksFolderNode from './BookmarksFolderNode';
    import classes from './BookmarksFolder.module.css';
    
    import { folders } from '../../resources/data';
    
    function BookmarksFolder() {
      const [foldersSelected, setFoldersSelected] = useState(new Array(folders.length).fill(false));
    
      const selectFolder = (index) => {
         let result = new Array(folders.length).fill(false);
         result[index] = true;
         setFoldersSelected(result);
      }
    
      return (
        <div className={classes.bookmarksFolder}>
          {folders.map((folder, index) => (
            <BookmarksFolderNode
              key={folder.id}
              folder={folder}
              isSelected={foldersSelected[index]}
              select={(index) => selectFolder(index)}
              index={index}
            />
          ))}
        </div>
      );
    }
    
    export default BookmarksFolder;
    

    So now in BookmarksFolderNode we have to use isSelected and select instead of folderIsOpen and openFolderHandler. Not only but we have to replicate the same logic for subfolders:

    import { useState, useRef, useEffect } from 'react';
    import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
    import Folder from '../../resources/img/folder.svg';
    import OpenedFolder from '../../resources/img/opened_folder.svg';
    
    import classes from './BookmarksFolderNode.module.css';
    
    function BookmarksFolderNode(props) {
      const [subfoldersSelected, setSubFoldersSelected] = useState(new Array(props.folder.subFolder.length).fill(false));
      const tab = useRef();
      const img = useRef();
      const title = useRef();
    
      useEffect(() => {
         if (props.isSelected) {
            tab.current.style.backgroundColor = '#1a73eb';
            img.current.src = OpenedFolder;
            title.current.style.color = '#1a73eb';
         }
         else {
            tab.current.style.backgroundColor = 'black';
            img.current.src = Folder;
            title.current.style.color = 'black';
         }
      }, [props.isSelected])
    
      const openFolderHandler = () => {
        props.select(props.index)
      };
    
      const paddingLeft = 20 * (props.folder.depth - 1);
    
      const selectSubfolder = (index) => {
         let result = new Array(props.folder.subFolder.length).fill(false);
         result[index] = true;
         setSubFoldersSelected(result);
      }
    
    return (
        <div className={classes.bookmarksFolderNode}>
          <div className={classes.bookmarksMainFolderNode}>
            <div className={classes.verticalTab} ref={tab}></div>
            <div className={classes.innerContainer} style={{paddingLeft}}>
              <div className={classes.icon} onClick={openFolderHandler}>
                {props.isSelected ? (
                  <AiFillCaretDown className={classes.ironIcon} />
                ) : (
                  <AiFillCaretRight className={classes.ironIcon} />
                )}
              </div>
              <img src={Folder} className={classes.folderIcon} ref={img} />
              <div
                className={classes.menuLabel}
                onClick={openFolderHandler}
                ref={title}
              >
                {props.folder.title}
              </div>
            </div>
          </div>
          <div className={classes.bookmarksSubFolderNode}>
            {props.folder.subFolder &&
              props.folder.subFolder.map((subFolder, index) => (
                <BookmarksFolderNode key={subFolder.id} folder={subFolder} index={index} isSelected={subfoldersSelected[index]} select={(index) => selectSubfolder(index)} />
              ))}
          </div>
        </div>
      );
    }
    
    export default BookmarksFolderNode;
    

    So now, if you select a folder (or a subfolder) the others folder (or subfolders) will be deselected and the useEffect will apply the desidered css.