From c94d0ed78bf184d3893a13819574663b6a8b0f02 Mon Sep 17 00:00:00 2001 From: Joshua Shoemaker Date: Fri, 24 Mar 2023 10:32:49 -0500 Subject: [PATCH] refact: breakup sidebar --- frontend/components/workspace/Navigation.tsx | 7 +- frontend/components/workspace/Sidebar.tsx | 492 ------------------ .../workspace/Sidebar/AddGroupInput.tsx | 89 ++++ .../workspace/Sidebar/AreaLineItem.tsx | 127 +++++ .../workspace/Sidebar/DocumentLineItem.tsx | 194 +++++++ .../workspace/Sidebar/GroupLineItem.tsx | 254 +++++++++ .../components/workspace/Sidebar/Sidebar.tsx | 33 ++ .../workspace/Sidebar/makeDefaultSidebar.ts | 21 + .../workspace/Sidebar/navigationProps.ts | 39 ++ .../components/workspace/Sidebar/provider.tsx | 53 ++ .../components/workspace/Sidebar/types.ts | 35 ++ .../Navigation/makeDefaultNavigation.ts | 6 +- frontend/context/Navigation/provider.tsx | 2 +- frontend/utils/onEnterHandler.ts | 5 + 14 files changed, 860 insertions(+), 497 deletions(-) delete mode 100644 frontend/components/workspace/Sidebar.tsx create mode 100644 frontend/components/workspace/Sidebar/AddGroupInput.tsx create mode 100644 frontend/components/workspace/Sidebar/AreaLineItem.tsx create mode 100644 frontend/components/workspace/Sidebar/DocumentLineItem.tsx create mode 100644 frontend/components/workspace/Sidebar/GroupLineItem.tsx create mode 100644 frontend/components/workspace/Sidebar/Sidebar.tsx create mode 100644 frontend/components/workspace/Sidebar/makeDefaultSidebar.ts create mode 100644 frontend/components/workspace/Sidebar/navigationProps.ts create mode 100644 frontend/components/workspace/Sidebar/provider.tsx create mode 100644 frontend/components/workspace/Sidebar/types.ts create mode 100644 frontend/utils/onEnterHandler.ts diff --git a/frontend/components/workspace/Navigation.tsx b/frontend/components/workspace/Navigation.tsx index 9c8015e..844d2f1 100644 --- a/frontend/components/workspace/Navigation.tsx +++ b/frontend/components/workspace/Navigation.tsx @@ -1,6 +1,7 @@ 'use client' -import Sidebar from './Sidebar' +import Sidebar from './Sidebar/Sidebar' +import { SidebarProvider } from './Sidebar/provider' import TopBar from './TopBar' function WorkspaceNavigation() { @@ -8,7 +9,9 @@ function WorkspaceNavigation() { return ( <> - + + + ) } diff --git a/frontend/components/workspace/Sidebar.tsx b/frontend/components/workspace/Sidebar.tsx deleted file mode 100644 index 03c136d..0000000 --- a/frontend/components/workspace/Sidebar.tsx +++ /dev/null @@ -1,492 +0,0 @@ -'use client' - -import React, { useRef, useState } from 'react' -import { PlusIcon, XMarkIcon, DocumentPlusIcon } from '@heroicons/react/20/solid' -import { ipc } from '../../wailsjs/wailsjs/go/models' -import { useProject } from '../../context/Project/provider' -import classNames from '../../utils/classNames' - -type GroupNavigationItem = { - id: string, - name: string, - documents: { - id: string, - name: string, - areas: { id: string, name: string, order: number }[] - }[] -} - -const getNavigationProps = (documents: ipc.Document[], groups: ipc.Group[]): GroupNavigationItem[] => { - const groupsWithDocuments = groups.map(g => { - const childrenDocuments = documents - .filter(d => d.groupId === g.id) - .map(d => ({ - id: d.id, - name: d.name, - areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) - })) - - return { - id: g.id, - name: g.name, - documents: childrenDocuments - } - }) - - const documentsWithoutGroup = documents - .filter(d => !d.groupId || d.groupId === 'Uncategorized') - .map(d => ({ - id: d.id, - name: d.name, - areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) - })) - - return [ - ...groupsWithDocuments, - { - id: '', - name: 'Uncategorized', - documents: documentsWithoutGroup - } - ] -} - -function Sidebar() { - const [selectedGroupId, setSelectedGroupId] = useState('') - const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false) - const [isEditDocumentNameInputShowing, setIsEditDocumentNameInputShowing] = useState(false) - const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false) - const [isEditAreaNameInputShowing, setIsEditAreaNameInputShowing] = useState(false) - const [dragOverAreaId, setDragOverAreaId] = useState('') - - const addDocumentTextInput = useRef(null) - const addGroupTextInput = useRef(null) - const editAreaNameTextInput = useRef(null) - const editDocumentNameTextInput = useRef(null) - - const { - documents, - groups, - getSelectedDocument, - getAreaById, - requestUpdateArea, - requestDeleteAreaById, - requestAddDocument, - requestAddDocumentGroup, - selectedAreaId, - setSelectedAreaId, - selectedDocumentId, - setSelectedDocumentId, - currentSession, - requestUpdateDocument, - requestChangeAreaOrder, - } = useProject() - - const navigation = getNavigationProps(documents, groups) - - const getGroupIdFromDocumentId = (itemId: string) => { - let groupId = '' - navigation.forEach(g => { - const foundDocument = g.documents.find(d => d.id === itemId) - if (foundDocument) groupId = g.id - }) - return groupId - } - - const getDocumentIdFromAreaId = (areaId: string) => { - let documentId = '' - navigation.map(g => g.documents).flat().forEach(d => { - const doesDocumentIncludeArea = d.areas.map(a => a.id).includes(areaId) - if (doesDocumentIncludeArea) documentId = d.id - }) - - return documentId - } - - const onAddNewDocumentLineItemClickHandler = (groupId: string) => { - setSelectedGroupId(groupId) - setIsAddNewDocumentInputShowing(true) - setIsAddNewGroupInputShowing(false) - } - - const onAddNewGroupLineItemClickHandler = () => { - setIsAddNewGroupInputShowing(true) - setIsAddNewDocumentInputShowing(false) - } - - const onAreaClick = (areaId: string) => { - getDocumentIdFromAreaId(areaId) - setSelectedDocumentId(getDocumentIdFromAreaId(areaId) || '') - setSelectedAreaId(areaId) - } - - const onAreaDoubleClick = (areaId: string) => { - setSelectedDocumentId(getDocumentIdFromAreaId(areaId) || '') - setIsEditAreaNameInputShowing(true) - } - - const onAreaInputBlur = () => { - setIsEditAreaNameInputShowing(false) - } - - const onAreaDragOver = (areaId: string) => { - setDragOverAreaId(areaId) - } - - const onAreaDragStart = (areaId: string) => { - setSelectedAreaId(areaId) - } - - const onAreaDropEnd = (areaId: string) => { - const areaDroppedOn = navigation.map(n => n.documents).flat().map(d => d.areas).flat().find(a => a.id === dragOverAreaId) - if (!areaDroppedOn) return - requestChangeAreaOrder(areaId, areaDroppedOn.order) - setDragOverAreaId('') - } - - const handleAreaDeleteButtonClick = (areaId: string) => { - requestDeleteAreaById(areaId) - } - - const onDocumentClickHandler = (itemId: string) => { - setSelectedDocumentId(itemId) - setSelectedGroupId(getGroupIdFromDocumentId(itemId)) - setIsAddNewDocumentInputShowing(false) - setIsAddNewGroupInputShowing(false) - } - - const onDocumentDoubleClickHandler = (docuemntId: string) => { - setIsEditDocumentNameInputShowing(true) - } - - const onDocumentInputBlur = () => { - setIsEditDocumentNameInputShowing(false) - } - - const onCancelAddGroupClickHandler = () => { - setIsAddNewGroupInputShowing(false) - } - - const onCancelAddItemClickHandler = () => { - setIsAddNewDocumentInputShowing(false) - } - - const onConfirmAreaNameChangeHandler = async (areaDetails: { areaId: string, areaName: string }) => { - const { areaId, areaName } = areaDetails - - const areaToUpdate = getAreaById(areaId) - if (areaToUpdate) { - areaToUpdate.name = areaName - requestUpdateArea(areaToUpdate) - .then(response => console.log(response)) - .catch(console.error) - } - setIsEditAreaNameInputShowing(false) - } - - const onConfirmDocumentNameChangeHandler = async (documentName: string) => { - const documentToUpdate = { ...getSelectedDocument() } - if (documentToUpdate) { - documentToUpdate.name = documentName - requestUpdateDocument(documentToUpdate) - .then(response => console.log('onConfirmDocumentNameChangeHandler response: ', response)) - .catch(console.error) - } - setIsEditDocumentNameInputShowing(false) - } - - const onConfirmAddDocumentClickHandler = async (groupId: string) => { - const documentName = addDocumentTextInput.current?.value - if (!documentName) return - - const response = await requestAddDocument(groupId, documentName) - if (!response.id) return - - setSelectedDocumentId(response.id) - setSelectedGroupId(groupId) - setIsAddNewDocumentInputShowing(false) - } - - const onConfirmAddGroupClickHandler = async (e: React.MouseEvent) => { - const groupName = addGroupTextInput.current?.value - if (!groupName) return - - const response = await requestAddDocumentGroup(groupName) - if (!response.id) return - - setSelectedGroupId(response.id) - setIsAddNewGroupInputShowing(false) - } - - const onEnterHandler = (event: React.KeyboardEvent, callback: Function) => { - if (event.key === 'Enter') callback() - } - - const renderAddGroupInput = () => { - return isAddNewGroupInputShowing - ?
-
- { - onEnterHandler(event, - onConfirmAddGroupClickHandler) - }} - ref={addGroupTextInput} - /> -
- - - -
- : - - } - - const renderAddNewDocument = (groupId: string) => { - return isAddNewDocumentInputShowing && selectedGroupId === groupId - ?
-
- { - onEnterHandler(event, - () => onConfirmAddDocumentClickHandler(groupId)) - }} - ref={addDocumentTextInput} - /> -
- - -
- : onAddNewDocumentLineItemClickHandler(groupId)} - > - - } - - const renderNavigationItems = () => ( - - ) - - return ( - <> -
-
-
- Textualize -

{currentSession?.project?.name}

-
-
- {renderNavigationItems()} -
-
-
- - ) -} - -export default Sidebar diff --git a/frontend/components/workspace/Sidebar/AddGroupInput.tsx b/frontend/components/workspace/Sidebar/AddGroupInput.tsx new file mode 100644 index 0000000..c9a9880 --- /dev/null +++ b/frontend/components/workspace/Sidebar/AddGroupInput.tsx @@ -0,0 +1,89 @@ + +import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline' +import React, { useRef } from 'react' +import { useProject } from '../../../context/Project/provider' +import classNames from '../../../utils/classNames' +import onEnterHandler from '../../../utils/onEnterHandler' +import { useSidebar } from './provider' + +const AddGroupInput = () => { + const { requestAddDocumentGroup } = useProject() + const { + isAddNewGroupInputShowing, + setSelectedGroupId, + setIsAddNewGroupInputShowing, + setIsAddNewDocumentInputShowing + } = useSidebar() + + const addGroupTextInput = useRef(null) + + + const onAddNewGroupLineItemClickHandler = () => { + setIsAddNewGroupInputShowing(true) + setIsAddNewDocumentInputShowing(false) + } + + const onConfirmAddGroupClickHandler = async (e: React.MouseEvent) => { + const groupName = addGroupTextInput.current?.value + if (!groupName) return + + const response = await requestAddDocumentGroup(groupName) + if (!response.id) return + + setSelectedGroupId(response.id) + setIsAddNewGroupInputShowing(false) + } + + const onCancelAddGroupClickHandler = () => { + setIsAddNewGroupInputShowing(false) + } + + return isAddNewGroupInputShowing + ?
+
+ { + onEnterHandler(event, + onConfirmAddGroupClickHandler) + }} + ref={addGroupTextInput} + /> +
+ + + +
+ : + +} + + +export default AddGroupInput diff --git a/frontend/components/workspace/Sidebar/AreaLineItem.tsx b/frontend/components/workspace/Sidebar/AreaLineItem.tsx new file mode 100644 index 0000000..cc44605 --- /dev/null +++ b/frontend/components/workspace/Sidebar/AreaLineItem.tsx @@ -0,0 +1,127 @@ +'use client' + +import React, { useRef, useState } from 'react' +import { XMarkIcon } from '@heroicons/react/20/solid' +import { useProject } from '../../../context/Project/provider' +import classNames from '../../../utils/classNames' +import { ArrowPathIcon } from '@heroicons/react/24/outline' +import { SidebarArea } from './types' +import { useSidebar } from './provider' +import onEnterHandler from '../../../utils/onEnterHandler' + + +const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: number }) => { + const { + getAreaById, + requestUpdateArea, + setSelectedDocumentId, + setSelectedAreaId, + requestChangeAreaOrder, + requestDeleteAreaById, + } = useProject() + + const { + selectedAreaId, + isEditAreaNameInputShowing, + setIsEditAreaNameInputShowing, + } = useSidebar() + + const editAreaNameTextInput = useRef(null) + + const [dragOverAreaId, setDragOverAreaId] = useState('') + + const onConfirmAreaNameChangeHandler = async (areaDetails: { areaId: string, areaName: string }) => { + const { areaId, areaName } = areaDetails + + const areaToUpdate = getAreaById(areaId) + if (areaToUpdate) { + areaToUpdate.name = areaName + requestUpdateArea(areaToUpdate) + .then(response => console.log(response)) + .catch(console.error) + } + setIsEditAreaNameInputShowing(false) + } + + const onAreaClick = (areaId: string) => { + setSelectedDocumentId(props.documentId) + setSelectedAreaId(areaId) + } + + const onAreaDoubleClick = (areaId: string) => { + setSelectedDocumentId(props.documentId) + setIsEditAreaNameInputShowing(true) + } + + const onAreaInputBlur = () => { + setIsEditAreaNameInputShowing(false) + } + + const onAreaDragOver = (areaId: string) => { + setDragOverAreaId(areaId) + } + + const onAreaDragStart = (areaId: string) => { + setSelectedAreaId(areaId) + } + + const onAreaDropEnd = (areaId: string) => { + const areaDroppedOn = getAreaById(areaId) + if (!areaDroppedOn) return + requestChangeAreaOrder(areaId, areaDroppedOn.order) + setDragOverAreaId('') + } + + const handleAreaDeleteButtonClick = (areaId: string) => { + requestDeleteAreaById(areaId) + } + + return
  • + {selectedAreaId === props.area.id && isEditAreaNameInputShowing + ? { + onEnterHandler(event, + () => onConfirmAreaNameChangeHandler({ areaId: props.area.id, areaName: event.currentTarget.value })) + }} + ref={editAreaNameTextInput} + /> + :
    onAreaDragOver(props.area.id)} + onDragStart={() => onAreaDragStart(props.area.id)} + onDragEnd={() => onAreaDropEnd(props.area.id)} + className={classNames('flex justify-between items-center cursor-pointer', + selectedAreaId === props.area.id ? 'bg-indigo-500 text-gray-200' : 'text-gray-300 hover:bg-gray-700 hover:text-white', + dragOverAreaId === props.area.id ? 'bg-gray-300 text-gray-700' : '', + selectedAreaId === props.area.id && dragOverAreaId === props.area.id ? 'bg-indigo-300' : '', + )}> + onAreaClick(props.area.id)} + onDoubleClick={() => onAreaDoubleClick(props.area.id)} + className={classNames('group w-full pr-2 py-2 text-left font-medium pl-8 text-xs', + 'rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 py-2 select-none', + )}> + {props.area.name || `Area ${props.area.order}`} + +
    + } +
  • +} + +export default AreaLineItem diff --git a/frontend/components/workspace/Sidebar/DocumentLineItem.tsx b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx new file mode 100644 index 0000000..97c4208 --- /dev/null +++ b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx @@ -0,0 +1,194 @@ +'use client' + +import React, { useRef } from 'react' +import { useProject } from '../../../context/Project/provider' +import classNames from '../../../utils/classNames' +import onEnterHandler from '../../../utils/onEnterHandler' +import AreaLineItem from './AreaLineItem' +import { useSidebar } from './provider' +import { SidebarDocument } from './types' + +const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, index: number }) => { + const { + getSelectedDocument, + requestUpdateDocument + } = useProject() + + const { + selectedDocumentId, + setSelectedDocumentId, + isEditDocumentNameInputShowing, + setIsAddNewDocumentInputShowing, + setSelectedGroupId, + setIsAddNewGroupInputShowing, + setIsEditDocumentNameInputShowing, + } = useSidebar() + + const editDocumentNameTextInput = useRef(null) + + const onDocumentClickHandler = (itemId: string) => { + setSelectedDocumentId(itemId) + setSelectedGroupId(props.groupId) + setIsAddNewDocumentInputShowing(false) + setIsAddNewGroupInputShowing(false) + } + + const onDocumentDoubleClickHandler = (docuemntId: string) => { + setIsEditDocumentNameInputShowing(true) + } + + const onDocumentInputBlur = () => { + setIsEditDocumentNameInputShowing(false) + } + + const onConfirmDocumentNameChangeHandler = async (documentName: string) => { + const documentToUpdate = { ...getSelectedDocument() } + if (documentToUpdate) { + documentToUpdate.name = documentName + requestUpdateDocument(documentToUpdate) + .then(response => console.log('onConfirmDocumentNameChangeHandler response: ', response)) + .catch(console.error) + } + setIsEditDocumentNameInputShowing(false) + } + + return ( +
  • + {!props.document.areas.length + ? +
    onDocumentClickHandler(props.document.id)} + onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)} + className={classNames( + props.document.id === selectedDocumentId + ? 'bg-gray-900 text-white' + : 'text-gray-300 hover:bg-gray-700 hover:text-white', + 'group items-center py-2 text-base font-medium rounded-b-md pl-10', + props.index !== 0 ? 'rounded-t-md' : '', + )}> + {selectedDocumentId === props.document.id && isEditDocumentNameInputShowing + ? { + onEnterHandler(event, + () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) + }} + ref={editDocumentNameTextInput} + /> + : + {props.document.name} + + } +
    + :
    + onDocumentClickHandler(props.document.id)} + onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)} + className={classNames( + props.document.id === selectedDocumentId + ? 'bg-gray-900 text-white' + : 'text-gray-300 hover:bg-gray-700 hover:text-white', + 'group items-center py-2 text-base font-medium rounded-b-md pl-6', + props.index !== 0 ? 'rounded-t-md' : '', + + )}> + {selectedDocumentId === props.document.id && isEditDocumentNameInputShowing + ? { + onEnterHandler(event, + () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) + }} + ref={editDocumentNameTextInput} + /> + : + {props.document.name} + + } + + +
    + } +
  • + ) +} + +export default DocumentLineItem diff --git a/frontend/components/workspace/Sidebar/GroupLineItem.tsx b/frontend/components/workspace/Sidebar/GroupLineItem.tsx new file mode 100644 index 0000000..492a34c --- /dev/null +++ b/frontend/components/workspace/Sidebar/GroupLineItem.tsx @@ -0,0 +1,254 @@ +'use client' + +import { DocumentPlusIcon, PlusIcon, XMarkIcon } from '@heroicons/react/24/outline' +import React, { useRef } from 'react' +import { useProject } from '../../../context/Project/provider' +import classNames from '../../../utils/classNames' +import onEnterHandler from '../../../utils/onEnterHandler' +import AddGroupInput from './AddGroupInput' +import DocumentLineItem from './DocumentLineItem' +import { useSidebar } from './provider' +import { SidebarGroup } from './types' + + + +const GroupLineItem = (props: { group: SidebarGroup }) => { + const { + requestAddDocument + } = useProject() + + const { + selectedGroupId, + isAddNewDocumentInputShowing, + setSelectedDocumentId, + setSelectedGroupId, + setIsAddNewDocumentInputShowing, + setIsAddNewGroupInputShowing, + } = useSidebar() + + const addDocumentTextInput = useRef(null) + + const onConfirmAddDocumentClickHandler = async (groupId: string) => { + const documentName = addDocumentTextInput.current?.value + if (!documentName) return + + const response = await requestAddDocument(groupId, documentName) + if (!response.id) return + + setSelectedDocumentId(response.id) + setSelectedGroupId(groupId) + setIsAddNewDocumentInputShowing(false) + } + + const onCancelAddItemClickHandler = () => { + setIsAddNewDocumentInputShowing(false) + } + + const onAddNewDocumentLineItemClickHandler = (groupId: string) => { + setSelectedGroupId(groupId) + setIsAddNewDocumentInputShowing(true) + setIsAddNewGroupInputShowing(false) + } + + const renderAddNewDocument = (groupId: string) => { + return isAddNewDocumentInputShowing && selectedGroupId === groupId + ?
    +
    + { + onEnterHandler(event, + () => onConfirmAddDocumentClickHandler(groupId)) + }} + ref={addDocumentTextInput} + /> +
    + + +
    + : onAddNewDocumentLineItemClickHandler(groupId)} + > + + } + + return (
    + + {props.group.name} + +
      + {props.group.documents.map((d, index) => ( + + //
    • + // {!d.areas.length + // ? + //
      onDocumentClickHandler(d.id)} + // onDoubleClick={() => onDocumentDoubleClickHandler(d.id)} + // className={classNames( + // d.id === selectedDocumentId + // ? 'bg-gray-900 text-white' + // : 'text-gray-300 hover:bg-gray-700 hover:text-white', + // 'group items-center py-2 text-base font-medium rounded-b-md pl-10', + // index !== 0 ? 'rounded-t-md' : '', + // )}> + // {selectedDocumentId === d.id && isEditDocumentNameInputShowing + // ? { + // onEnterHandler(event, + // () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) + // }} + // ref={editDocumentNameTextInput} + // /> + // : + // {d.name} + // + // } + //
      + // :
      + // onDocumentClickHandler(d.id)} + // onDoubleClick={() => onDocumentDoubleClickHandler(d.id)} + // className={classNames( + // d.id === selectedDocumentId + // ? 'bg-gray-900 text-white' + // : 'text-gray-300 hover:bg-gray-700 hover:text-white', + // 'group items-center py-2 text-base font-medium rounded-b-md pl-6', + // index !== 0 ? 'rounded-t-md' : '', + + // )}> + // {selectedDocumentId === d.id && isEditDocumentNameInputShowing + // ? { + // onEnterHandler(event, + // () => onConfirmDocumentNameChangeHandler(event.currentTarget.value)) + // }} + // ref={editDocumentNameTextInput} + // /> + // : + // {d.name} + // + // } + // + // + //
      + // } + //
    • + ))} + + {renderAddNewDocument(props.group.id)} +
    +
    + ) +} + +export default GroupLineItem diff --git a/frontend/components/workspace/Sidebar/Sidebar.tsx b/frontend/components/workspace/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..3a5ec46 --- /dev/null +++ b/frontend/components/workspace/Sidebar/Sidebar.tsx @@ -0,0 +1,33 @@ +'use client' + +import React from 'react' +import { useProject } from '../../../context/Project/provider' +import AddGroupInput from './AddGroupInput' +import GroupLineItem from './GroupLineItem' +import { useSidebar } from './provider' + +function Sidebar() { + const { currentSession } = useProject() + const { navigationProps } = useSidebar() + + return ( + <> +
    +
    +
    + Textualize +

    {currentSession?.project?.name}

    +
    +
    + +
    +
    +
    + + ) +} + +export default Sidebar diff --git a/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts b/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts new file mode 100644 index 0000000..cc45a09 --- /dev/null +++ b/frontend/components/workspace/Sidebar/makeDefaultSidebar.ts @@ -0,0 +1,21 @@ +import { SidebarContextType } from './types' + +const makeDefaultSidebar = (): SidebarContextType => ({ + navigationProps: [], + selectedGroupId: '', + setSelectedGroupId: (_: string) => {}, + selectedDocumentId: '', + setSelectedDocumentId: (_: string) => {}, + selectedAreaId: '', + setSelectedAreaId: (_: string) => {}, + isAddNewDocumentInputShowing: false, + setIsAddNewDocumentInputShowing: (_: boolean) => {}, + isEditDocumentNameInputShowing: false, + setIsEditDocumentNameInputShowing: (_: boolean) => {}, + isAddNewGroupInputShowing: false, + setIsAddNewGroupInputShowing: (_: boolean) => {}, + isEditAreaNameInputShowing: false, + setIsEditAreaNameInputShowing: (_: boolean) => {}, +}) + +export default makeDefaultSidebar \ No newline at end of file diff --git a/frontend/components/workspace/Sidebar/navigationProps.ts b/frontend/components/workspace/Sidebar/navigationProps.ts new file mode 100644 index 0000000..3c14fc1 --- /dev/null +++ b/frontend/components/workspace/Sidebar/navigationProps.ts @@ -0,0 +1,39 @@ +import { ipc } from '../../../wailsjs/wailsjs/go/models' +import { SidebarGroup } from './types' + +const getNavigationProps = (documents: ipc.Document[], groups: ipc.Group[]) : SidebarGroup[] => { + const groupsWithDocuments = groups.map(g => { + const childrenDocuments = documents + .filter(d => d.groupId === g.id) + .map(d => ({ + id: d.id, + name: d.name, + areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) + })) + + return { + id: g.id, + name: g.name, + documents: childrenDocuments + } + }) + + const documentsWithoutGroup = documents + .filter(d => !d.groupId || d.groupId === 'Uncategorized') + .map(d => ({ + id: d.id, + name: d.name, + areas: d.areas.map(a => ({ id: a.id, name: a.name, order: a.order }))//.sort((a, b) => a.order - b.order) + })) + + return [ + ...groupsWithDocuments, + { + id: '', + name: 'Uncategorized', + documents: documentsWithoutGroup + } + ] +} + +export { getNavigationProps } \ No newline at end of file diff --git a/frontend/components/workspace/Sidebar/provider.tsx b/frontend/components/workspace/Sidebar/provider.tsx new file mode 100644 index 0000000..7a72e10 --- /dev/null +++ b/frontend/components/workspace/Sidebar/provider.tsx @@ -0,0 +1,53 @@ +'use client' + +import { createContext, useContext, useState, ReactNode, } from 'react' +import { useProject } from '../../../context/Project/provider' +import makeDefaultSidebar from './makeDefaultSidebar' +import { getNavigationProps } from './navigationProps' +import { SidebarContextType } from './types' + +const SidebarContext = createContext(makeDefaultSidebar()) + +export function useSidebar() { + return useContext(SidebarContext) +} + +type Props = { children: ReactNode } +export function SidebarProvider({ children }: Props) { + const [selectedGroupId, setSelectedGroupId] = useState('') + const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false) + const [isEditDocumentNameInputShowing, setIsEditDocumentNameInputShowing] = useState(false) + const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false) + const [isEditAreaNameInputShowing, setIsEditAreaNameInputShowing] = useState(false) + + const { + selectedDocumentId, setSelectedDocumentId, + selectedAreaId, setSelectedAreaId, + documents, + groups, + } = useProject() + + const navigationProps = getNavigationProps(documents, groups) + + const value = { + navigationProps, + selectedGroupId, + setSelectedGroupId, + selectedDocumentId, + setSelectedDocumentId, + selectedAreaId, + setSelectedAreaId, + isAddNewDocumentInputShowing, + setIsAddNewDocumentInputShowing, + isEditDocumentNameInputShowing, + setIsEditDocumentNameInputShowing, + isAddNewGroupInputShowing, + setIsAddNewGroupInputShowing, + isEditAreaNameInputShowing, + setIsEditAreaNameInputShowing + } + + return + { children } + +} \ No newline at end of file diff --git a/frontend/components/workspace/Sidebar/types.ts b/frontend/components/workspace/Sidebar/types.ts new file mode 100644 index 0000000..a1c130e --- /dev/null +++ b/frontend/components/workspace/Sidebar/types.ts @@ -0,0 +1,35 @@ +export type SidebarContextType = { + navigationProps: SidebarGroup[], + selectedGroupId: string, + setSelectedGroupId: (_: string) => void, + selectedDocumentId: string, + setSelectedDocumentId: (_: string) => void, + selectedAreaId: string, + setSelectedAreaId: (_: string) => void, + isAddNewDocumentInputShowing: boolean, + setIsAddNewDocumentInputShowing: (_: boolean) => void, + isEditDocumentNameInputShowing: boolean, + setIsEditDocumentNameInputShowing: (_: boolean) => void, + isAddNewGroupInputShowing: boolean, + setIsAddNewGroupInputShowing: (_: boolean) => void, + isEditAreaNameInputShowing: boolean, + setIsEditAreaNameInputShowing: (_: boolean) => void, +} + +export type SidebarArea = { + id: string, + name: string, + order: number +} + +export type SidebarDocument = { + id: string, + name: string, + areas: SidebarArea[] +} + +export type SidebarGroup = { + id: string, + name: string, + documents: SidebarDocument[] +} \ No newline at end of file diff --git a/frontend/context/Navigation/makeDefaultNavigation.ts b/frontend/context/Navigation/makeDefaultNavigation.ts index fa9e663..fa39d04 100644 --- a/frontend/context/Navigation/makeDefaultNavigation.ts +++ b/frontend/context/Navigation/makeDefaultNavigation.ts @@ -1,8 +1,10 @@ -import { NavigationContextType, workspaces } from './types' +import { mainPages, NavigationContextType, workspaces } from './types' const makeDefaultNavigation = (): NavigationContextType => ({ selectedWorkspace: workspaces.PROCESSOR, - setSelectedWorkspace: (workspace) => {} + setSelectedWorkspace: (_) => {}, + selectedMainPage: mainPages.EDITUSER, + setSelectedMainPage: (_) => {}, }) export default makeDefaultNavigation diff --git a/frontend/context/Navigation/provider.tsx b/frontend/context/Navigation/provider.tsx index 5442995..046a66f 100644 --- a/frontend/context/Navigation/provider.tsx +++ b/frontend/context/Navigation/provider.tsx @@ -1,6 +1,6 @@ 'use client' -import { createContext, ReactNode, useContext, useEffect, useState } from 'react' +import { createContext, ReactNode, useContext, useState } from 'react' import makeDefaultNavigation from './makeDefaultNavigation' import { mainPages, NavigationContextType, NavigationProps, workspaces } from './types' diff --git a/frontend/utils/onEnterHandler.ts b/frontend/utils/onEnterHandler.ts new file mode 100644 index 0000000..9a84fc4 --- /dev/null +++ b/frontend/utils/onEnterHandler.ts @@ -0,0 +1,5 @@ +const onEnterHandler = (event: React.KeyboardEvent, callback: Function) => { + if (event.key === 'Enter') callback() +} + +export default onEnterHandler