jfmow / noti

A online note storage/editor using a open source backend (pocketbase) and a next js frontend.
https://noti-priv.vercel.app
GNU General Public License v2.0
2 stars 0 forks source link

Tab bar #26

Closed jfmow closed 11 months ago

jfmow commented 11 months ago

Concept image

concept1
jfmow commented 11 months ago

Working prototype

Image

jfmow commented 11 months ago

Drag and drop functionality

function onDragStart(e, id) {
        setDraggedItemId(id);
    }

    function onDragOver(e) {
        e.preventDefault();

    }

    function onDrop(e, id) {
        e.preventDefault();
        const updatedItems = tabBarItems.slice();
        console.log(updatedItems)
        const draggedItemIndex = updatedItems.findIndex((item) => item.id === draggedItemId);
        const dropItemIndex = updatedItems.findIndex((item) => item.id === id);
        [updatedItems[draggedItemIndex], updatedItems[dropItemIndex]] = [
            updatedItems[dropItemIndex],
            updatedItems[draggedItemIndex],
        ];
        setTabBarItems(updatedItems);
        setDraggedItemId(null);
        window.localStorage.setItem('_tabbar', JSON.stringify(updatedItems))
    }
jfmow commented 11 months ago

Full code, it stores the pages in localstorage so is per device and doesn't require network:

import { ContextMenuDropMenu, ContextMenuDropMenuSection, ContextMenuDropMenuSectionItem } from '@/lib/ContextMenu';
import styles from '@/styles/TabBar.module.css';
import Router from 'next/router';
import { useEffect, useRef, useState } from 'react';

export default function TabBar({ pb, page }) {
    const [tabBarItems, setTabBarItems] = useState([]);
    const [draggedItemId, setDraggedItemId] = useState(null);

    useEffect(() => {
        async function fetchData() {
            let tabBarStore = window.localStorage.getItem('_tabbar')
            if (!tabBarStore) {
                tabBarStore = window.localStorage.setItem('_tabbar', JSON.stringify([]))
                return
            }
            if (!JSON.parse(tabBarStore).some(item => item.id === page)) {
                const updatedStore = [...JSON.parse(tabBarStore), { id: page }]
                window.localStorage.setItem('_tabbar', JSON.stringify(updatedStore))
                tabBarStore = JSON.stringify(updatedStore)
            }
            const records = await pb.collection('pages_Bare').getFullList({
                sort: '-created',
            });
            //console.log(records)
            //console.log(tabBarStore)
            let tempArrayItem = []
            const updatedItems = await JSON.parse(tabBarStore).map((item) => {
                const toPush = records.filter(rec => rec.id === item.id)[0]
                if (toPush) {
                    tempArrayItem.push(toPush)
                }
            });
            setTabBarItems(tempArrayItem);

            //console.log(tempArrayItem)
        }
        fetchData()
    }, [page])

    function RemoveTabItem(id) {
        const tabBarStore = window.localStorage.getItem('_tabbar')
        window.localStorage.setItem('_tabbar', JSON.stringify(JSON.parse(tabBarStore).filter(item => item.id !== id)))
        setTabBarItems(tabBarItems.filter(item => item.id !== id))
        return
    }

    function onDragStart(e, id) {
        setDraggedItemId(id);
    }

    function onDragOver(e) {
        e.preventDefault();

    }

    function onDrop(e, id) {
        e.preventDefault();
        const updatedItems = tabBarItems.slice();
        console.log(updatedItems)
        const draggedItemIndex = updatedItems.findIndex((item) => item.id === draggedItemId);
        const dropItemIndex = updatedItems.findIndex((item) => item.id === id);
        [updatedItems[draggedItemIndex], updatedItems[dropItemIndex]] = [
            updatedItems[dropItemIndex],
            updatedItems[draggedItemIndex],
        ];
        setTabBarItems(updatedItems);
        setDraggedItemId(null);
        window.localStorage.setItem('_tabbar', JSON.stringify(updatedItems))
    }

    return (
        <>
            <div className={styles.tabbar}>
                {tabBarItems.map((item) => {
                    if (item === undefined) {
                        return <></>;
                    }
                    return (
                        <TabBarItem
                            key={item.id}
                            name={item.title}
                            icon={item.icon}
                            active={page === item.id}
                            id={item.id}
                            RemoveTabItem={RemoveTabItem}
                            onDragStart={onDragStart}
                            onDragOver={onDragOver}
                            onDrop={onDrop}
                        />
                    );
                })}
            </div>
        </>
    );
}

function TabBarItem({ name, icon, active, id, RemoveTabItem, onDragStart, onDragOver, onDrop }) {
    const selfRef = useRef(null);
    const [contextMenuEvent, setContextMenuEvent] = useState(null)

    function openContextMenu(e, id) {

    }

    return (
        <>

            <ContextMenuDropMenu event={contextMenuEvent}>
                <ContextMenuDropMenuSection>
                    <ContextMenuDropMenuSectionItem onClick={() => RemoveTabItem(contextMenuEvent.data.filter(item => item.key === 'pageId')[0].value)}>
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-minus-square"><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M8 12h8" /></svg>
                        <p>Remove item</p>
                    </ContextMenuDropMenuSectionItem>
                </ContextMenuDropMenuSection>
            </ContextMenuDropMenu>
            <div
                ref={selfRef}
                onContextMenu={(e) => {
                    e.preventDefault()
                    setContextMenuEvent({ eventData: e, data: [{ key: "pageId", value: id }] })
                }}
                className={`${active && styles.tabbaritem_active} ${styles.tabbaritem}`}
                onClick={() => {
                    Router.push(`/page/${id}`);
                }}
                draggable
                onDragStart={(e) => onDragStart(e, id)}
                onDragOver={(e) => onDragOver(e)}
                onDrop={(e) => onDrop(e, id)}
            >
                <div className={styles.tabbaritememoji}>
                    {icon && icon.includes('.png') ? (
                        <img className={styles.page_icon} src={`/emoji/twitter/64/${icon}`} alt={name} />
                    ) : (
                        !isNaN(parseInt(icon, 16)) && String.fromCodePoint(parseInt(icon, 16))
                    )}
                </div>

                <div className={styles.tabbaritemname}>{name || id}</div>
                <div className={styles.tabbaritemicon}>
                    {active && (
                        <svg
                            onClick={() => RemoveTabItem(id)}
                            xmlns="http://www.w3.org/2000/svg"
                            width="24"
                            height="24"
                            viewBox="0 0 24 24"
                            fill="none"
                            stroke="currentColor"
                            strokeWidth="2"
                            strokeLinecap="round"
                            strokeLinejoin="round"
                        >
                            <path d="M18 6 6 18" />
                            <path d="m6 6 12 12" />
                        </svg>
                    )}
                </div>
            </div>
        </>
    );
}