<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ayesha Munir</title>
    <description>The latest articles on Forem by Ayesha Munir (@ashmunir).</description>
    <link>https://forem.com/ashmunir</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F954881%2F417e5ccf-960f-41ac-a120-0705a7bb032a.jpg</url>
      <title>Forem: Ayesha Munir</title>
      <link>https://forem.com/ashmunir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ashmunir"/>
    <language>en</language>
    <item>
      <title>Creating a Custom Select Component with Tree View in React</title>
      <dc:creator>Ayesha Munir</dc:creator>
      <pubDate>Sun, 17 Mar 2024 16:32:35 +0000</pubDate>
      <link>https://forem.com/ashmunir/creating-a-custom-select-component-with-tree-view-in-react-l80</link>
      <guid>https://forem.com/ashmunir/creating-a-custom-select-component-with-tree-view-in-react-l80</guid>
      <description>&lt;p&gt;In this blog post, we'll walk through the process of creating a custom select component in React that includes a tree view for displaying options and sub-options. 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Select Component&lt;/strong&gt; 🎛️&lt;br&gt;
The custom select component is a dropdown menu that allows users to select an option from a list. Here's the basic structure of our custom select component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import PropTypes from 'prop-types'
import TreeView from './treeView'
import React, { useState, useEffect, useRef } from 'react'
import { iconExtraSmall } from '@/lib/utils/helperUtils/constants'
import { IconChevronDown, IconChevronUp } from '@tabler/icons-react'

const CustomSelect = ({ options, selectedOption, setSelectedOption }) =&amp;gt; {
    const dropdownRef = useRef(null)
    const [expanded, setExpanded] = useState({})
    const [isOpen, setIsOpen] = useState(false)
    const [selectedOptionObject, setSelectedOptionObject] = useState(null)

    useEffect(() =&amp;gt; {
        const handleClickOutside = (event) =&amp;gt; {
            if (dropdownRef.current &amp;amp;&amp;amp; !dropdownRef.current.contains(event.target)) {
                setIsOpen(false)
            }
        }

        document.addEventListener('mousedown', handleClickOutside)
        return () =&amp;gt; {
            document.removeEventListener('mousedown', handleClickOutside)
        }
    }, [])

    const handleExpand = (id) =&amp;gt; {
        setExpanded((prev) =&amp;gt; ({ ...prev, [id]: !prev[id] }))
    }

    const handleSelect = (option) =&amp;gt; {
        setSelectedOption(option.id)
        setSelectedOptionObject(option)
    }

    return (
        &amp;lt;div className="relative" ref={dropdownRef}&amp;gt;
            &amp;lt;div className="w-full text-xs font-normal h-[40px] text-fieldsText rounded-md outline-none border-input bg-fieldsBg  z-50 max-h-96 min-w-[8rem] overflow-hidden"&amp;gt;
                &amp;lt;button
                    className="flex h-10 w-full items-center whitespace-nowrap rounded-md bg-transparent px-3 py-2 text-xs shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&amp;amp;&amp;gt;span]:line-clamp-1"
                    onClick={() =&amp;gt; setIsOpen(!isOpen)}
                    disabled={!options?.length}
                    type="button"
                &amp;gt;
                    {selectedOption &amp;amp;&amp;amp; selectedOptionObject?.name}
                    &amp;lt;div className="ml-auto"&amp;gt;
                        {isOpen ? (
                            &amp;lt;IconChevronUp size={iconExtraSmall} className={'cursor-pointer'} /&amp;gt;
                        ) : (
                            &amp;lt;IconChevronDown size={iconExtraSmall} className={'cursor-pointer'} /&amp;gt;
                        )}
                    &amp;lt;/div&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
            {isOpen &amp;amp;&amp;amp; (
                &amp;lt;div className="absolute top-full w-full mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-50 p-1 overflow-auto max-h-36"&amp;gt;
                    {options?.map((option) =&amp;gt; (
                        &amp;lt;TreeView
                            key={option.id}
                            option={option}
                            handleExpand={handleExpand}
                            handleSelect={handleSelect}
                            expanded={expanded}
                            selectedOption={selectedOption}
                        /&amp;gt;
                    ))}
                &amp;lt;/div&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}

CustomSelect.propTypes = {
    options: PropTypes.array,
    selectedOption: PropTypes.string,
    setSelectedOption: PropTypes.func,
}

export default CustomSelect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can see Select field with Chevron down icon on right. By clicking on field it will expand the dropdown and you will be able to see main options in one go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m96wdsmh60zuecx1zdw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m96wdsmh60zuecx1zdw.png" alt="Custom Select Component" width="762" height="90"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tree View Component&lt;/strong&gt;&lt;br&gt;
The tree view component is a visual representation of hierarchical data. It displays data as a tree-like structure with collapsible nodes. This is particularly useful when dealing with nested data, like a directory of folders and files or a nested menu structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import PropTypes from 'prop-types'

const TreeView = ({ option, handleExpand, handleSelect, expanded, selectedOption, level = 0 }) =&amp;gt; {
    const getIcon = (option) =&amp;gt; {
        if (option.subFolders.length &amp;gt; 0) {
            return (
                &amp;lt;div&amp;gt;
                    &amp;lt;button onClick={() =&amp;gt; handleExpand(option.id)} className="flex cursor-default items-center justify-center"&amp;gt;
                        &amp;lt;div className="flex items-center justify-center w-3 h-3 border border-fieldsText text-fieldsText text-xs"&amp;gt;
                            {expanded[option.id] ? '-' : '+'}
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            )
        } else {
            return (
                &amp;lt;div className="flex items-center justify-center w-3 h-3 border border-fieldsText text-fieldsText text-[0.6rem] "&amp;gt;
                    x
                &amp;lt;/div&amp;gt;
            )
        }
    }

    return (
        &amp;lt;div key={option.id} className="w-full"&amp;gt;
            &amp;lt;button
                type="button"
                onClick={() =&amp;gt; handleSelect(option)}
                style={{ marginLeft: `${level * 1.25}rem`, width: `calc(100% - ${level * 1.25}rem)` }}
                className={`relative flex py-2 px-1.5  cursor-default text-fieldsText select-none items-center rounded-sm  text-xs outline-none font-normal ${option === selectedOption ? 'bg-accent text-accent-foreground' : ''} hover:bg-accent focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 whitespace-nowrap`}
            &amp;gt;
                &amp;lt;div className="flex items-center"&amp;gt;
                    &amp;lt;div className="pr-1.5"&amp;gt;{getIcon(option)}&amp;lt;/div&amp;gt;
                    {option.name}
                &amp;lt;/div&amp;gt;
            &amp;lt;/button&amp;gt;
            {expanded[option.id] &amp;amp;&amp;amp;
                option.subFolders.map((subOption) =&amp;gt; (
                    &amp;lt;TreeView
                        key={subOption.id}
                        option={subOption}
                        handleExpand={handleExpand}
                        handleSelect={handleSelect}
                        expanded={expanded}
                        selectedOption={selectedOption}
                        level={level + 1}
                    /&amp;gt;
                ))}
        &amp;lt;/div&amp;gt;
    )
}

TreeView.propTypes = {
    option: PropTypes.object,
    handleExpand: PropTypes.func,
    handleSelect: PropTypes.func,
    expanded: PropTypes.object,
    selectedOption: PropTypes.string,
    level: PropTypes.number,
}

export default TreeView
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the tree view component, It allows users to interact with hierarchical data. It uses &lt;strong&gt;'+'&lt;/strong&gt; and &lt;strong&gt;'-'&lt;/strong&gt; icons to represent expandable and collapsible options respectively. Options without sub-options are represented by an &lt;strong&gt;'x'&lt;/strong&gt; icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vvfipitvpndq0j69b18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vvfipitvpndq0j69b18.png" alt="Tree view Component" width="786" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is Complete view of Select custom component and Tree view for showing and selecting from main options or sub options.&lt;/p&gt;

&lt;p&gt;In summary, the tree view component in React effectively displays hierarchical data, offering a user-friendly interface for navigation and selection, and enhancing interactivity with its expand/collapse features. 🎉&lt;/p&gt;




&lt;p&gt;If you have any thoughts or queries, don't hesitate to share them in the comments section. Your input is greatly appreciated! 😊&lt;/p&gt;

&lt;p&gt;If you found this post helpful, please consider giving it a thumbs up or a share to help others discover it.&lt;/p&gt;

&lt;p&gt;Keep coding and stay curious! 🎈&lt;/p&gt;

&lt;p&gt;🌐 Get in touch: &lt;a href="//ayeshadev.com"&gt;Ayesha Munir&lt;/a&gt;&lt;br&gt;
👥 Connect: &lt;a href="https://www.linkedin.com/in/ayesha-m-a2895b205/"&gt;Linkedin&lt;/a&gt; | &lt;a href="https://www.facebook.com/profile.php?id=100095293729900"&gt;Facebook&lt;/a&gt;| &lt;a href="https://www.instagram.com/ayes.hamunir98/"&gt;Instagram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>reactjsdevelopment</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>React - Building Chip and Chip Input Component from Scratch</title>
      <dc:creator>Ayesha Munir</dc:creator>
      <pubDate>Sat, 02 Mar 2024 19:06:35 +0000</pubDate>
      <link>https://forem.com/ashmunir/building-chips-and-chip-input-component-from-scratch-react-2gkk</link>
      <guid>https://forem.com/ashmunir/building-chips-and-chip-input-component-from-scratch-react-2gkk</guid>
      <description>&lt;p&gt;In this blog post, we will walk through creating a Chip and Chip Input component in React. These components are commonly used in modern web applications for tagging, filtering, and input selection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chip Component&lt;/strong&gt;&lt;br&gt;
The Chip component is a small, interactive element that represents an input, attribute, or action. It is a versatile component that can be used in a variety of ways.&lt;/p&gt;

&lt;p&gt;Here is the code for our Chip component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import PropTypes from 'prop-types'
import { Label } from './ui/label'
import { IconX } from '@tabler/icons-react'
import { getProfileIcon } from '@/lib/helpers/common'

const imageProps = {
    width: 20,
    height: 20,
}

const avatarStyles = 'w-[20px] h-[20px]'

const Chip = ({ label, color, containerClass, labelClass, isDelete, isImage, onDelete, chipStyles, chipImage }) =&amp;gt; {
    return (
        &amp;lt;div
            className={`px-2 ${color} rounded justify-center items-center gap-1 inline-flex ${chipStyles} ${containerClass}`}
        &amp;gt;
            &amp;lt;div className="justify-center items-center gap-1 flex"&amp;gt;
                {isImage &amp;amp;&amp;amp; getProfileIcon(chipImage, imageProps, label, avatarStyles, 'text-sm')}
                &amp;lt;Label className={`text-zinc-600 text-xs font-medium ${labelClass}`}&amp;gt;{label}&amp;lt;/Label&amp;gt;
            &amp;lt;/div&amp;gt;

            {isDelete &amp;amp;&amp;amp; (
                &amp;lt;button onClick={onDelete}&amp;gt;
                    &amp;lt;IconX size={16} stroke={2} color={'red'} /&amp;gt;
                &amp;lt;/button&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}

Chip.propTypes = {
    label: PropTypes.string.isRequired,
    isDelete: PropTypes.bool,
    onDelete: PropTypes.func,
    color: PropTypes.string,
    chipStyles: PropTypes.string,
    isImage: PropTypes.bool,
    chipImage: PropTypes.string,
    containerClass: PropTypes.string,
    labelClass: PropTypes.string,
}

export default Chip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;On the Right side you can see chips with different titles and you can also make it profile tags and show user images and avatars into it!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7fn77mogwxcvhoqhhrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7fn77mogwxcvhoqhhrc.png" alt="Chip for tags"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Chip Input Component&lt;/strong&gt;&lt;br&gt;
The Chip Input component is a text input that can contain multiple "chips" as input. It is useful for tagging, multi-select, or input suggestions.&lt;/p&gt;

&lt;p&gt;Here is the code for our Chip Input component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useRef } from 'react'
import Chip from './chip'
import PropTypes from 'prop-types'

function ChipsInput({ chips, setChips, chipColor }) {
    const [inputValue, setInputValue] = useState('')
    const [suggestions, setSuggestions] = useState(['Person', 'User'])
    const [removedSuggestions, setRemovedSuggestions] = useState([])
    const inputRef = useRef()

    const handleInputChange = (event) =&amp;gt; {
        setInputValue(event.target.value)
    }

    const handleDeleteChip = (chipToDelete) =&amp;gt; {
        setChips(chips.filter((chip) =&amp;gt; chip !== chipToDelete))
        if (removedSuggestions.includes(chipToDelete)) {
            setSuggestions((prevSuggestions) =&amp;gt; [...prevSuggestions, chipToDelete])
            setRemovedSuggestions((prevRemoved) =&amp;gt; prevRemoved.filter((item) =&amp;gt; item !== chipToDelete))
        }
        inputRef.current.focus()
    }

    const handleInputKeyDown = (event) =&amp;gt; {
        if (event.key === 'Enter') {
            event.preventDefault()
            const newChip = inputValue.trim()
            if (newChip !== '') {
                setChips([...chips, newChip])
                setInputValue('')
            }
        } else if (event.key === 'Backspace' &amp;amp;&amp;amp; inputValue === '') {
            const lastChip = chips[chips.length - 1]
            handleDeleteChip(lastChip)
        }
    }

    const handleSuggestionClick = (suggestion) =&amp;gt; {
        setChips([...chips, suggestion])
        setSuggestions((prevSuggestions) =&amp;gt; prevSuggestions.filter((prevSuggestion) =&amp;gt; prevSuggestion !== suggestion))
        setRemovedSuggestions((prevRemoved) =&amp;gt; [...prevRemoved, suggestion])
        setInputValue('')
    }

    return (
        &amp;lt;div className="relative"&amp;gt;
            &amp;lt;div className="flex flex-wrap gap-2 min-h-10 items-center bg-fieldsBg p-2 rounded"&amp;gt;
                {chips.map((chip) =&amp;gt; (
                    &amp;lt;Chip
                        key={chip}
                        containerClass="h-[26px]"
                        label={chip}
                        isDelete={true}
                        color={chipColor}
                        onDelete={() =&amp;gt; handleDeleteChip(chip)}
                    /&amp;gt;
                ))}
                &amp;lt;input
                    ref={inputRef}
                    type="text"
                    value={inputValue}
                    onChange={handleInputChange}
                    onKeyDown={handleInputKeyDown}
                    className="flex-grow bg-fieldsBg text-sm outline-none"
                /&amp;gt;
            &amp;lt;/div&amp;gt;
            {!!suggestions.length &amp;amp;&amp;amp; inputValue &amp;amp;&amp;amp; (
                &amp;lt;div className="absolute left-0 mt-1 w-full bg-white rounded shadow z-10"&amp;gt;
                    {!!suggestions.filter((suggestion) =&amp;gt; suggestion.toLowerCase().includes(inputValue.toLowerCase())).length &amp;amp;&amp;amp;
                        inputValue &amp;amp;&amp;amp; (
                            &amp;lt;div className="absolute left-0 mt-1 w-full bg-white border rounded shadow z-10"&amp;gt;
                                {suggestions
                                    .filter((suggestion) =&amp;gt; suggestion.toLowerCase().includes(inputValue.toLowerCase()))
                                    .map((suggestion) =&amp;gt; (
                                        &amp;lt;button
                                            key={suggestion}
                                            onClick={() =&amp;gt; handleSuggestionClick(suggestion)}
                                            className="p-2 hover:bg-gray-200 cursor-pointer w-full text-left"
                                        &amp;gt;
                                            {suggestion}
                                        &amp;lt;/button&amp;gt;
                                    ))}
                            &amp;lt;/div&amp;gt;
                        )}
                &amp;lt;/div&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}

ChipsInput.propTypes = {
    chips: PropTypes.arrayOf(PropTypes.string).isRequired,
    setChips: PropTypes.func.isRequired,
    chipColor: PropTypes.string.isRequired,
}

export default ChipsInput
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here you can see two chip inputs with different background chip color and cross icon to remove it and can also remove it through backspace🙌&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnti5we6u23xsc2baqzx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnti5we6u23xsc2baqzx0.png" alt="Chip input"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it for now, folks! 🎉 I hope you found this post useful and it helps you in your coding journey. Remember, practice is key when it comes to mastering programming. Keep coding and stay curious! 💻🚀&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, feel free to leave a comment below. I'd love to hear from you! 😊&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you enjoyed this post, please consider giving it a 🧡 or 🦄 to help more people find it. &lt;/p&gt;

&lt;p&gt;Happy coding! 🎈&lt;/p&gt;

&lt;p&gt;🌐 Get in touch: &lt;a href="//ayeshadev.com"&gt;Ayesha Munir&lt;/a&gt;&lt;br&gt;
👥 Connect: &lt;a href="https://www.linkedin.com/in/ayesha-m-a2895b205/" rel="noopener noreferrer"&gt;Linkedin &lt;/a&gt; | &lt;a href="https://www.facebook.com/profile.php?id=100095293729900" rel="noopener noreferrer"&gt;Facebook &lt;/a&gt;| &lt;a href="https://www.instagram.com/ayes.hamunir98/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;




</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
