diff options
author | Thomas F. K. Jorna <[email protected]> | 2021-11-02 23:24:07 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-11-02 23:24:07 +0100 |
commit | 78f15c54c2e0d1f61abc9eec4d88ee020b191e95 (patch) | |
tree | 5aa007482d9e6f9e1aeb32429dcedf4f48073e33 /components | |
parent | d7d75c295209acbb9c0f48676b6059ae2fa76aeb (diff) |
Feat/collapse: add collapsible/toggleable headers and outline layout in the preview panel (#139)
* feat(preview): collapsible headings
* feat(preview): collapsible headings
* feat(collapse): change icons for headings
* feat(collapse):
* feat(collapse): use new uniorg and better looks
* feat(collapse): fix typescript errors
* fix(ci): better filter
* feat(collapse): more small adjustments
* feat(collapse): collapse all button
* fix(collapse): fix global css
* fix(cd): remove yarn and add export
* fix(collapse): type-errors
* fix(cd): fix format-all fucking up yml
Diffstat (limited to 'components')
-rw-r--r-- | components/Sidebar/Backlinks.tsx | 12 | ||||
-rw-r--r-- | components/Sidebar/Link.tsx | 92 | ||||
-rw-r--r-- | components/Sidebar/Note.tsx | 22 | ||||
-rw-r--r-- | components/Sidebar/Section.tsx | 106 | ||||
-rw-r--r-- | components/Sidebar/Title.tsx | 2 | ||||
-rw-r--r-- | components/Sidebar/Toolbar.tsx | 30 | ||||
-rw-r--r-- | components/Sidebar/index.tsx | 80 | ||||
-rw-r--r-- | components/Sidebar/noteStyle.ts | 93 |
8 files changed, 330 insertions, 107 deletions
diff --git a/components/Sidebar/Backlinks.tsx b/components/Sidebar/Backlinks.tsx index d82fbba..e115225 100644 --- a/components/Sidebar/Backlinks.tsx +++ b/components/Sidebar/Backlinks.tsx @@ -15,10 +15,12 @@ export interface BacklinksProps { nodeByCite: NodeByCite setSidebarHighlightedNode: OrgRoamNode openContextMenu: any + outline: boolean } import { PreviewLink } from './Link' import { OrgRoamNode } from '../../api' +import { Section } from './Section' export const Backlinks = (props: BacklinksProps) => { const { @@ -29,6 +31,7 @@ export const Backlinks = (props: BacklinksProps) => { linksByNodeId, nodeByCite, openContextMenu, + outline, } = props const links = linksByNodeId[previewNode?.id] ?? [] @@ -40,8 +43,8 @@ export const Backlinks = (props: BacklinksProps) => { .map((l) => l.source) return ( - <Box> - <Heading pt={4}>{`Backlinks (${backLinks.length})`}</Heading> + <Box className="backlinks" borderRadius="sm" mt={6} p={4} bg="white" mb={10}> + <p style={{ fontSize: 16, fontWeight: 600 }}>{`Linked references (${backLinks.length})`}</p> <VStack py={2} spacing={3} @@ -54,14 +57,17 @@ export const Backlinks = (props: BacklinksProps) => { backLinks.map((link) => { const title = nodeById[link as string]?.title ?? '' return ( - <Box overflow="hidden" p={3} bg="gray.300" width="100%" key={link}> + <Box overflow="hidden" py={1} borderRadius="sm" width="100%" key={link}> <PreviewLink nodeByCite={nodeByCite} setSidebarHighlightedNode={setSidebarHighlightedNode} href={`id:${link as string}`} nodeById={nodeById} + previewNode={previewNode} setPreviewNode={setPreviewNode} openContextMenu={openContextMenu} + outline={outline} + noUnderline > {nodeById[link as string]?.title} </PreviewLink> diff --git a/components/Sidebar/Link.tsx b/components/Sidebar/Link.tsx index 7e966a4..644078d 100644 --- a/components/Sidebar/Link.tsx +++ b/components/Sidebar/Link.tsx @@ -16,6 +16,7 @@ import { } from '@chakra-ui/react' import React, { ReactElement, useContext, useEffect, useMemo, useState } from 'react' +import { ProcessedOrg } from '../../util/processOrg' import unified from 'unified' //import createStream from 'unified-stream' import uniorgParse from 'uniorg-parse' @@ -36,6 +37,8 @@ export interface LinkProps { nodeByCite: NodeByCite nodeById: NodeById openContextMenu: any + outline: boolean + noUnderline?: boolean } export interface NodeLinkProps { @@ -46,6 +49,7 @@ export interface NodeLinkProps { children: any setSidebarHighlightedNode: any openContextMenu: any + noUnderline?: boolean id?: string } export interface NormalLinkProps { @@ -54,13 +58,15 @@ export interface NormalLinkProps { } import { hexToRGBA, getThemeColor } from '../../pages/index' -import noteStyle from './noteStyle' +import { defaultNoteStyle, viewerNoteStyle, outlineNoteStyle } from './noteStyle' import { OrgImage } from './OrgImage' import { Scrollbars } from 'react-custom-scrollbars-2' import { ExternalLinkIcon } from '@chakra-ui/icons' +import { Section } from './Section' export const NodeLink = (props: NodeLinkProps) => { const { + noUnderline, id, setSidebarHighlightedNode, setPreviewNode, @@ -85,7 +91,7 @@ export const NodeLink = (props: NodeLinkProps) => { overflow="hidden" fontWeight={500} color={highlightColor} - textDecoration="underline" + textDecoration={noUnderline ? undefined : 'underline'} onContextMenu={(e) => { e.preventDefault() openContextMenu(nodeById[uri], e) @@ -121,6 +127,8 @@ export const PreviewLink = (props: LinkProps) => { setPreviewNode, nodeByCite, openContextMenu, + outline, + noUnderline, } = props // TODO figure out how to properly type this // see https://github.com/rehypejs/rehype-react/issues/25 @@ -128,6 +136,7 @@ export const PreviewLink = (props: LinkProps) => { const [hover, setHover] = useState(false) const type = href.replaceAll(/(.*?)\:.*/g, '$1') + const extraNoteStyle = outline ? outlineNoteStyle : viewerNoteStyle const getText = () => { fetch(`http://localhost:35901/file/${file}`) .then((res) => { @@ -135,8 +144,7 @@ export const PreviewLink = (props: LinkProps) => { }) .then((res) => { if (res !== 'error') { - const text = processor.processSync(res).result - setOrgText(text) + setOrgText(res) return } }) @@ -187,32 +195,39 @@ export const PreviewLink = (props: LinkProps) => { const id = getId(type, uri) const file = encodeURIComponent(encodeURIComponent(nodeById[id]?.file as string)) - const processor = unified() - .use(uniorgParse) - .use(uniorg2rehype) - .use(katex) - .use(rehype2react, { - createElement: React.createElement, - components: { - // eslint-disable-next-line react/display-name - a: ({ children, href }) => ( - <PreviewLink - nodeByCite={nodeByCite} - setSidebarHighlightedNode={setSidebarHighlightedNode} - href={href} - nodeById={nodeById} - setPreviewNode={setPreviewNode} - openContextMenu={openContextMenu} - > - {children} - </PreviewLink> - ), - img: ({ src }) => { - return <OrgImage src={src as string} file={nodeById[id]?.file as string} /> - }, - }, - }) - + /* const processor = unified() + * .use(uniorgParse) + * .use(uniorg2rehype) + * .use(katex) + * .use(rehype2react, { + * createElement: React.createElement, + * components: { + * // eslint-disable-next-line react/display-name + * a: ({ children, href }) => ( + * <PreviewLink + * outline={outline} + * nodeByCite={nodeByCite} + * setSidebarHighlightedNode={setSidebarHighlightedNode} + * href={href} + * nodeById={nodeById} + * setPreviewNode={setPreviewNode} + * openContextMenu={openContextMenu} + * > + * {children} + * </PreviewLink> + * ), + * img: ({ src }) => { + * return <OrgImage src={src as string} file={nodeById[id]?.file as string} /> + * }, + * section: ({ children, className }) => ( + * <Section {...{ outline, className }}>{children}</Section> + * ), + * p: ({ children }) => { + * return <p lang="en">{children}</p> + * }, + * }, + * }) + */ if (id) { return ( <> @@ -234,6 +249,7 @@ export const PreviewLink = (props: LinkProps) => { children, nodeByCite, openContextMenu, + noUnderline, }} /> </Box> @@ -281,10 +297,22 @@ export const PreviewLink = (props: LinkProps) => { w="100%" color="black" px={3} - sx={noteStyle} + sx={{ ...defaultNoteStyle, ...extraNoteStyle }} //overflowY="scroll" > - {orgText} + <ProcessedOrg + previewText={orgText} + {...{ + nodeById, + setSidebarHighlightedNode, + setPreviewNode, + nodeByCite, + previewNode, + openContextMenu, + outline, + }} + collapse={false} + /> </Box> </Scrollbars> </PopoverBody> diff --git a/components/Sidebar/Note.tsx b/components/Sidebar/Note.tsx index ec267a3..197fa76 100644 --- a/components/Sidebar/Note.tsx +++ b/components/Sidebar/Note.tsx @@ -5,7 +5,7 @@ import { NodeById, NodeByCite, LinksByNodeId } from '../../pages' import { Box, Flex } from '@chakra-ui/react' import { UniOrg } from '../../util/uniorg' import { Backlinks } from '../../components/Sidebar/Backlinks' -import { noteStyle } from './noteStyle' +import { defaultNoteStyle, viewerNoteStyle, outlineNoteStyle } from './noteStyle' export interface NoteProps { setPreviewNode: any @@ -17,6 +17,8 @@ export interface NoteProps { justificationList: string[] linksByNodeId: LinksByNodeId openContextMenu: any + outline: boolean + collapse: boolean } export const Note = (props: NoteProps) => { @@ -30,20 +32,29 @@ export const Note = (props: NoteProps) => { setSidebarHighlightedNode, linksByNodeId, openContextMenu, + outline, + collapse, } = props + const extraStyle = outline ? outlineNoteStyle : viewerNoteStyle return ( <Box pr={8} + pt={2} height="100%" className="org" sx={{ - ...noteStyle, - + ...defaultNoteStyle, + ...extraStyle, textAlign: justificationList[justification], }} > {previewNode?.id && ( - <Flex height="100%" flexDirection="column" justifyContent="space-between"> + <Flex + className="wrapClass" + height="100%" + flexDirection="column" + justifyContent="space-between" + > <UniOrg {...{ setPreviewNode, @@ -52,6 +63,8 @@ export const Note = (props: NoteProps) => { nodeByCite, setSidebarHighlightedNode, openContextMenu, + outline, + collapse, }} /> <Backlinks @@ -63,6 +76,7 @@ export const Note = (props: NoteProps) => { nodeByCite, setSidebarHighlightedNode, openContextMenu, + outline, }} /> </Flex> diff --git a/components/Sidebar/Section.tsx b/components/Sidebar/Section.tsx new file mode 100644 index 0000000..adaf00e --- /dev/null +++ b/components/Sidebar/Section.tsx @@ -0,0 +1,106 @@ +import { Box, Collapse, Flex, IconButton } from '@chakra-ui/react' +import React, { + JSXElementConstructor, + ReactChild, + ReactElement, + ReactNode, + useContext, + useEffect, + useState, +} from 'react' +import { BiCaretDownCircle, BiChevronDownCircle, BiCircle } from 'react-icons/bi' +import { ComponentLike, ComponentPropsWithoutNode } from 'rehype-react' +import { VscCircleFilled, VscCircleOutline } from 'react-icons/vsc' +import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } from '@chakra-ui/icons' +import { NoteContext } from '../../util/NoteContext' + +export interface SectionProps { + children: any + className: string +} + +export const Section = (props: SectionProps) => { + const { + children, + className, // outline + } = props + const [open, setOpen] = useState(true) + const { collapse } = useContext(NoteContext) + useEffect(() => { + setOpen(!collapse) + }, [collapse]) + + if (className === 'h0Wrapper headingWrapper') { + return <Box className="preHeadingContent"> {children}</Box> + } + const kids = children as ReactChild[] + return ( + <Box className={'sec'}> + <Box display="block"> + <Flex className="headingFlex" alignItems="baseline"> + {open && kids.length > 0 ? ( + <> + <IconButton + className="viewerHeadingButton" + _focus={{}} + _active={{}} + aria-label="Expand heading" + //mr={1} + size="xs" + variant="subtle" + icon={<ChevronUpIcon />} + onClick={() => setOpen(!open)} + height={2} + width={2} + /> + <IconButton + className="outlineHeadingButton" + _focus={{}} + _active={{}} + aria-label="Expand heading" + //mr={1} + size="xs" + variant="subtle" + icon={<VscCircleOutline />} + onClick={() => setOpen(!open)} + height={2} + width={2} + /> + </> + ) : ( + <> + <IconButton + className="viewerHeadingButton" + _active={{}} + _focus={{}} + aria-label="Collapse heading" + //mr={1} + height={2} + width={2} + size="xs" + variant="subtle" + icon={<ChevronDownIcon />} + onClick={() => setOpen(!open)} + /> + <IconButton + className="outlineHeadingButton" + _active={{}} + _focus={{}} + aria-label="Collapse heading" + //mr={1} + height={2} + width={2} + size="xs" + variant="subtle" + icon={<VscCircleFilled />} + onClick={() => setOpen(!open)} + /> + </> + )} + {kids[0]} + </Flex> + </Box> + {open && <Box className="sectionContent">{kids.slice(1)}</Box>} + </Box> + ) +} diff --git a/components/Sidebar/Title.tsx b/components/Sidebar/Title.tsx index 5d39190..2c8d8b7 100644 --- a/components/Sidebar/Title.tsx +++ b/components/Sidebar/Title.tsx @@ -4,7 +4,7 @@ import { BiFile } from 'react-icons/bi' import { OrgRoamNode } from '../../api' export interface TitleProps { - previewNode: OrgRoamNode + previewNode: OrgRoamNode | undefined } export const Title = (props: TitleProps) => { diff --git a/components/Sidebar/Toolbar.tsx b/components/Sidebar/Toolbar.tsx index 6cbecae..71f3807 100644 --- a/components/Sidebar/Toolbar.tsx +++ b/components/Sidebar/Toolbar.tsx @@ -8,7 +8,9 @@ import { BiFont, BiRightIndent, } from 'react-icons/bi' +import { MdOutlineExpand, MdOutlineCompress } from 'react-icons/md' import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons' +import { IoIosListBox, IoMdListBox } from 'react-icons/io' import { NodeObject } from 'force-graph' export interface ToolbarProps { @@ -22,6 +24,10 @@ export interface ToolbarProps { resetPreviewNode: any previousPreviewNode: any nextPreviewNode: any + outline: boolean + setOutline: any + collapse: boolean + setCollapse: any } export const Toolbar = (props: ToolbarProps) => { @@ -36,13 +42,18 @@ export const Toolbar = (props: ToolbarProps) => { resetPreviewNode, previousPreviewNode, nextPreviewNode, + outline, + setOutline, + collapse, + setCollapse, } = props return ( - <Flex flex="0 1 40px" pb={3} alignItems="center" justifyContent="space-between" pl={1} pr={1}> + <Flex flex="0 1 40px" pb={3} alignItems="center" justifyContent="space-between" pr={1}> <Flex> <ButtonGroup isAttached> <Tooltip label="Go backward"> <IconButton + _focus={{}} variant="subtle" icon={<ChevronLeftIcon />} aria-label="Previous node" @@ -52,6 +63,7 @@ export const Toolbar = (props: ToolbarProps) => { </Tooltip> <Tooltip label="Go forward"> <IconButton + _focus={{}} variant="subtle" icon={<ChevronRightIcon />} aria-label="Next node" @@ -77,6 +89,22 @@ export const Toolbar = (props: ToolbarProps) => { onClick={() => setJustification((curr: number) => (curr + 1) % 4)} /> </Tooltip> + <Tooltip label="Toggle outline view"> + <IconButton + variant="subtle" + aria-label="Justify content" + icon={outline ? <IoIosListBox /> : <IoMdListBox />} + onClick={() => setOutline((curr: boolean) => !curr)} + /> + </Tooltip> + <Tooltip label="Toggle headers"> + <IconButton + variant="subtle" + aria-label="Toggle headers" + icon={collapse ? <MdOutlineExpand /> : <MdOutlineCompress />} + onClick={() => setCollapse((curr: boolean) => !curr)} + /> + </Tooltip> {/* <Tooltip label="Indent trees"> <IconButton variant="subtle" diff --git a/components/Sidebar/index.tsx b/components/Sidebar/index.tsx index 33af598..21465c6 100644 --- a/components/Sidebar/index.tsx +++ b/components/Sidebar/index.tsx @@ -3,21 +3,9 @@ import React, { useContext, useEffect, useRef, useState } from 'react' import { Toolbar } from './Toolbar' import { TagBar } from './TagBar' import { Note } from './Note' +import { Title } from './Title' -import { - Button, - Slide, - VStack, - Flex, - Heading, - Box, - IconButton, - Tooltip, - HStack, - TagLabel, - Tag, - TagRightIcon, -} from '@chakra-ui/react' +import { VStack, Flex, Box, IconButton } from '@chakra-ui/react' import { Collapse } from './Collapse' import { Scrollbars } from 'react-custom-scrollbars-2' import { @@ -29,7 +17,6 @@ import { ViewOffIcon, } from '@chakra-ui/icons' import { BiDotsVerticalRounded, BiFile, BiNetworkChart } from 'react-icons/bi' -import { BsReverseLayoutSidebarInsetReverse } from 'react-icons/bs' import { GraphData, NodeObject, LinkObject } from 'force-graph' import { OrgRoamNode } from '../../api' @@ -91,7 +78,7 @@ const Sidebar = (props: SidebarProps) => { } = props const { highlightColor } = useContext(ThemeContext) - const [previewRoamNode, setPreviewRoamNode] = useState<OrgRoamNode>() + const [previewRoamNode, setPreviewRoamNode] = useState<OrgRoamNode | undefined>() const [sidebarWidth, setSidebarWidth] = usePersistantState<number>('sidebarWidth', 400) useEffect(() => { @@ -103,10 +90,12 @@ const Sidebar = (props: SidebarProps) => { setPreviewRoamNode(previewNode as OrgRoamNode) }, [previewNode?.id]) - const [justification, setJustification] = useState(1) + const [justification, setJustification] = usePersistantState('justification', 1) + const [outline, setOutline] = usePersistantState('outline', false) const justificationList = ['justify', 'start', 'end', 'center'] const [font, setFont] = useState('sans serif') const [indent, setIndent] = useState(0) + const [collapse, setCollapse] = useState(false) //maybe want to close it when clicking outside, but not sure //const outsideClickRef = useRef(); return ( @@ -142,16 +131,28 @@ const Sidebar = (props: SidebarProps) => { //whiteSpace="nowrap" // overflow="hidden" // textOverflow="ellipsis" - pl={4} + pl={2} alignItems="center" color="black" width="100%" > - <Flex flexShrink={0}> - <BiFile - onContextMenu={(e) => { - e.preventDefault() - openContextMenu(previewNode, e) + <Flex pt={1} flexShrink={0}> + <Toolbar + {...{ + setJustification, + setIndent, + setFont, + justification, + setPreviewNode, + canUndo, + canRedo, + resetPreviewNode, + previousPreviewNode, + nextPreviewNode, + outline, + setOutline, + collapse, + setCollapse, }} /> </Flex> @@ -163,20 +164,7 @@ const Sidebar = (props: SidebarProps) => { e.preventDefault() openContextMenu(previewNode, e) }} - > - <Heading - pl={2} - whiteSpace="nowrap" - textOverflow="ellipsis" - overflow="hidden" - lineHeight={1} - size="sm" - fontWeight={600} - color={'gray.800'} - > - {previewRoamNode?.title} - </Heading> - </Flex> + ></Flex> <Flex flexDir="row" ml="auto"> <IconButton // eslint-disable-next-line react/jsx-no-undef @@ -195,20 +183,6 @@ const Sidebar = (props: SidebarProps) => { /> </Flex> </Flex> - <Toolbar - {...{ - setJustification, - setIndent, - setFont, - justification, - setPreviewNode, - canUndo, - canRedo, - resetPreviewNode, - previousPreviewNode, - nextPreviewNode, - }} - /> <Scrollbars //autoHeight //autoHeightMax={600} @@ -232,6 +206,7 @@ const Sidebar = (props: SidebarProps) => { bg="alt.100" paddingLeft={4} > + <Title previewNode={previewRoamNode} /> <TagBar {...{ filter, setFilter, tagColors, setTagColors, openContextMenu, previewNode }} /> @@ -246,6 +221,9 @@ const Sidebar = (props: SidebarProps) => { justificationList, linksByNodeId, openContextMenu, + outline, + setOutline, + collapse, }} /> </VStack> diff --git a/components/Sidebar/noteStyle.ts b/components/Sidebar/noteStyle.ts index 42ecaf6..895009f 100644 --- a/components/Sidebar/noteStyle.ts +++ b/components/Sidebar/noteStyle.ts @@ -1,27 +1,91 @@ -export const noteStyle = { - '.katex': { overflowX: 'scroll' }, - h1: { color: 'black', lineHeight: '1.2', fontSize: '20', fontWeight: 'bold', marginBottom: 3 }, +export const viewerNoteStyle = { + '.headingFlex': { + flexDirection: 'row-reverse', + justifyContent: 'flex-end', + }, + '.outlineHeadingButton': { + display: 'none', + }, + h1: { color: 'black', lineHeight: '1.2', fontSize: '16', fontWeight: 'bold', paddingTop: 2 }, h2: { - fontSize: '18', - marginBottom: 2, + fontSize: '14', color: 'black', + fontWeight: 'bold', + fontStyle: 'bold italic', + paddingTop: 2, }, h3: { - fontSize: '16', - fontWeight: '600 !important', - marginBottom: '.5em', - + fontSize: '13', color: 'black', + paddingTop: 2, }, h4: { - fontSize: '14', - fontWeight: '500 !important', - marginBottom: '.25em', + fontSize: '12', fontStyle: 'italic', color: 'black', + paddingTop: 2, + }, + + '.sectionContent': { + paddingTop: 2, }, +} + +export const outlineNoteStyle = { + '.headingFlex': { + flexDirection: 'row', + justifyContent: 'flex-start', + }, + '.viewerHeadingButton': { + display: 'none', + }, + '.sectionContent': { + //pt: 2, + mt: 3, + paddingLeft: 4, + ml: '11px', + borderLeftWidth: '1px', + borderLeftColor: 'gray.500', + }, + '.preHeadingContent': { + //paddingTop: 2, + //paddingLeft: 4, + // ml: 3, + //borderLeftWidth: '1px', + //borderLeftColor: 'gray.600', + }, + 'h1,h2,h3,h4,h5,h6,h7,h8': { + pl: 1, + lineHeight: '1.25', + color: 'black', + fontSize: 15, + fontWeight: 700, + }, + '.sec': { + pt: 1, + }, + '.wrapClass > div > p': { + mb: 2, + }, + p: { + fontWeight: 500, + fontSize: 14, + pb: 2, + }, +} + +export const defaultNoteStyle = { + '.katex': { overflowX: 'scroll' }, ol: { - paddingLeft: '5', + paddingLeft: 4, + py: 1, + }, + 'li::marker': { + fontSize: 12, + fontWeight: 'bold', + }, + li: { + pt: 1, }, ul: { paddingLeft: '5', @@ -29,7 +93,7 @@ export const noteStyle = { p: { fontSize: '14', fontWeight: '500 !important', - paddingBottom: '.5em', + pb: 2, }, div: { hyphens: 'auto !important', @@ -136,4 +200,3 @@ export const noteStyle = { '.figure': { padding: '1em' }, '.figure p': { textAlign: 'center' }, } -export default noteStyle |