diff --git a/apps/extension/src/Components/Dibba/index.tsx b/apps/extension/src/Components/Dibba/index.tsx index e3de0ac38..30bbc1794 100644 --- a/apps/extension/src/Components/Dibba/index.tsx +++ b/apps/extension/src/Components/Dibba/index.tsx @@ -1,13 +1,17 @@ -import { LinkCapture, parseSnippet, Snippet } from '@mexit/core' +import { LinkCapture, parseSnippet, QuickLinkType, Snippet } from '@mexit/core' import React, { useEffect, useRef, useState } from 'react' import { Icon } from '@iconify/react' import fuzzysort from 'fuzzysort' import { useSnippets } from '../../Hooks/useSnippets' import { useSputlitContext, VisualState } from '../../Hooks/useSputlitContext' -import { ComboboxItem, ComboboxRoot, Img, ItemCenterWrapper, ItemDesc, ItemTitle } from './styled' +import { ComboboxItem, ComboboxRoot, ItemCenterWrapper, ItemDesc, ItemRightIcons, ItemTitle } from './styled' import { useShortenerStore } from '../../Hooks/useShortener' import { getDibbaText } from '../../Utils/getDibbaText' +import { ComboboxShortcuts, ComboSeperator, DisplayShortcut, ShortcutText } from '@mexit/shared' +import { ElementTypeBasedShortcut } from '../../Editor/components/ComboBox' +import EditorPreviewRenderer from '../EditorPreviewRenderer' +import usePointerMovedSinceMount from '../../Hooks/usePointerMovedSinceMount' // This functions provides the 'to be' range and text content // Needed because keydown event happens before there is a selection or content change @@ -40,16 +44,21 @@ export default function Dibba() { const linkCaptures = useShortenerStore((store) => store.linkCaptures) const snippets = useSnippets().getSnippets() + const pointerMoved = usePointerMovedSinceMount() const data = [ + // TODO: fix link captures after discussion ...linkCaptures.map((item) => ({ id: item.shortenedURL, title: item.short, - // TODO: find a way to use favicons but single array of results icon: 'ri:link', content: item.shortenedURL })), - ...snippets + ...snippets.map((item) => ({ + type: QuickLinkType.snippet, + icon: item?.icon || 'ri:quill-pen-line', + ...item + })) ] const insertSnippet = (item: Snippet) => { @@ -86,7 +95,7 @@ export default function Dibba() { console.log(error) } - if (item.icon === 'ri:quill-pen-line') { + if (item.type === QuickLinkType.snippet) { insertSnippet(item as Snippet) } else if (item.icon === 'ri:link') { // TODO: transform again to type linkCapture @@ -174,6 +183,9 @@ export default function Dibba() { setOffsetTop(window.innerHeight < top + dibbaRef.current.clientHeight) }) + const listItem = results[activeIndex] + const itemShortcut = listItem?.type ? ElementTypeBasedShortcut[listItem?.type] : undefined + return ( - {results.map((item, index) => { - return ( - { - handleClick(item) - }} - > - - - {item.title} - - - ) - })} +
+ {results.map((item, index) => { + return ( + { + handleClick(item) + }} + onPointerMove={() => pointerMoved && setActiveIndex(index)} + > + + + {item.title} + {item.desc && {item.desc}} + + {item.rightIcons && ( + + {item.rightIcons.map((i: string) => ( + + ))} + + )} + + ) + })} + {itemShortcut && ( + + {Object.entries(itemShortcut).map(([key, shortcut]) => { + return ( + +
{shortcut.title}
+
+ ) + })} +
+ )} +
+ + {listItem?.content && ( + +
+ +
+
+ )}
) } diff --git a/apps/extension/src/Components/Dibba/styled.tsx b/apps/extension/src/Components/Dibba/styled.tsx index 03d7181b1..a6a903239 100644 --- a/apps/extension/src/Components/Dibba/styled.tsx +++ b/apps/extension/src/Components/Dibba/styled.tsx @@ -10,25 +10,35 @@ export const ComboboxRoot = styled.ul<{ ${({ isOpen, theme, top, left, offsetTop, offsetRight }) => isOpen && css` + display: flex; top: calc(${top}px + 1em); left: ${left}px; position: absolute; padding: 0; margin: 0; z-index: 9999999; - background: ${theme.colors.background.modal}; - width: 225px; + /* background: ${theme.colors.background.modal}; */ + /* width: 225px; */ overflow: hidden; border-radius: 8px; - box-shadow: rgba(0, 0, 0, 0.133) 0 3.2px 7.2px 0, rgba(0, 0, 0, 0.11) 0 0.6px 1.8px 0; + /* box-shadow: rgba(0, 0, 0, 0.133) 0 3.2px 7.2px 0, rgba(0, 0, 0, 0.11) 0 0.6px 1.8px 0; */ transform: ${offsetTop ? css`translateY(calc(-100% - 1em))` : ''} ${offsetRight ? css`translateX(-100%)` : ''}; - `} -` -export const Img = styled.img` - width: 18px; - aspect-ratio: 1/1; + > div { + background: ${theme.colors.background.modal}; + height: fit-content; + /* max-height: 400px; */ + box-shadow: rgba(0, 0, 0, 0.133) 0 3.2px 7.2px 0, rgba(0, 0, 0, 0.11) 0 0.6px 1.8px 0; + border-radius: ${theme.borderRadius.small}; + + > section { + max-height: 30vh; + overflow-y: auto; + overflow-x: hidden; + } + } + `} ` export const ItemTitle = styled.div`` @@ -40,7 +50,7 @@ export const ItemRightIcons = styled.div` export const ItemDesc = styled.div` margin-top: ${({ theme }) => theme.spacing.tiny}; color: ${({ theme }) => theme.colors.text.fade}; - font-size: 0.8em; + font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/apps/extension/src/Components/Editor/Components.tsx b/apps/extension/src/Components/Editor/Components.tsx index 0d48e2c18..adfd38d62 100644 --- a/apps/extension/src/Components/Editor/Components.tsx +++ b/apps/extension/src/Components/Editor/Components.tsx @@ -14,14 +14,14 @@ import { ELEMENT_TAG } from '@mexit/core' -export const components = { +export const editorPreviewComponents = { [ELEMENT_LINK]: withProps(LinkElement, { as: 'a' }), [ELEMENT_PARAGRAPH]: withProps(StyledElement, { styles: { root: { - margin: '0.1em 0 0' + margin: '0.1rem 0 0' } } }), @@ -31,8 +31,8 @@ export const components = { [ELEMENT_TABLE]: TableWrapper } -// const components = createPlateUI({ -// ...editorPreviewComponents -// }) +const components = createPlateUI({ + ...editorPreviewComponents +}) export default components diff --git a/apps/extension/src/Components/Editor/index.tsx b/apps/extension/src/Components/Editor/index.tsx index 2166925b2..fb11a5c23 100644 --- a/apps/extension/src/Components/Editor/index.tsx +++ b/apps/extension/src/Components/Editor/index.tsx @@ -5,11 +5,9 @@ import { useDebouncedCallback } from 'use-debounce' import React, { useState, useMemo } from 'react' import { EditorStyles, useEditorChange } from '@mexit/shared' -import generatePlugins from '../../Utils/plugins' import { useAuthStore } from '../../Hooks/useAuth' import { EditorWrapper } from './styled' import { useSputlitContext } from '../../Hooks/useSputlitContext' -import { useTagStore } from '../../Hooks/useTags' import components from './Components' import BallonMarkToolbarButtons from './BalloonToolbar/EditorBalloonToolbar' @@ -46,10 +44,8 @@ const commands = [ export const Editor: React.FC = ({ readOnly, onChange }) => { const { searchResults, activeIndex, activeItem } = useSputlitContext() const { previewMode, nodeContent, node, setPreviewMode } = useEditorContext() - const ilinks = useDataStore((store) => store.ilinks) - const addTags = useTagStore((store) => store.addTags) - const tags = useTagStore((store) => store.tags) + const { tags, addTag, ilinks, addILink } = useDataStore() useEditorChange(node.nodeid, nodeContent, onChange) @@ -58,11 +54,11 @@ export const Editor: React.FC = ({ readOnly, onChange }) => { keys: { tag: { slateElementType: ELEMENT_TAG, - newItemHandler: (tag: string) => addTags({ id: 'TAG_1234', text: tag }) + newItemHandler: (newItem) => addTag(newItem) }, ilink: { slateElementType: ELEMENT_ILINK, - newItemHandler: (ilink: string, parentId?: string) => console.log(`ilink: ${ilink} | ParentID: ${parentId}`) + newItemHandler: (newItem, parentId?) => addILink({ ilink: newItem, parentId }) }, slash_command: { slateElementType: 'slash_command', @@ -88,13 +84,18 @@ export const Editor: React.FC = ({ readOnly, onChange }) => { cbKey: ComboboxKey.TAG, trigger: '#', data: tags.map((t) => ({ ...t, text: t.text })), - icon: 'add-something-here' + icon: 'ri:hashtag' }, ilink: { cbKey: ComboboxKey.ILINK, trigger: '[[', - data: ilinks.map((l) => ({ ...l, value: l.path, text: l.path })), - icon: 'add-something-here' + data: ilinks.map((l) => ({ + ...l, + value: l.nodeid, + text: l.path, + type: QuickLinkType.backlink + })), + icon: 'ri:file-list-2-line' }, slash_command: { cbKey: ComboboxKey.SLASH_COMMAND, diff --git a/apps/extension/src/Components/EditorPreviewRenderer.tsx b/apps/extension/src/Components/EditorPreviewRenderer.tsx new file mode 100644 index 000000000..0567f4eee --- /dev/null +++ b/apps/extension/src/Components/EditorPreviewRenderer.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useMemo } from 'react' +import styled from 'styled-components' + +import { EditorStyles, FadeContainer, TodoContainer } from '@mexit/shared' + +import { useEditorChange } from '@mexit/shared' + +import { Plate, PlatePlugin } from '@udecode/plate' +import components from './Editor/Components' +import useMemoizedPlugins from '../Editor/plugins' + +interface EditorPreviewRendererProps { + content: any[] // eslint-disable-line @typescript-eslint/no-explicit-any + editorId: string + noStyle?: boolean + /** + * Block that will be focused on render + */ + blockId?: string + noMouseEvents?: boolean + onDoubleClick?: (ev: React.MouseEvent) => void + plugins?: PlatePlugin[] +} + +const PreviewStyles = styled(EditorStyles)<{ noMouseEvents: boolean }>` + ${({ noMouseEvents }) => noMouseEvents && 'pointer-events: none;'}; + /* user-select: none; */ + font-size: 0.9rem; + + ${TodoContainer}, button, input, textarea, select, option { + pointer-events: none; + } +` + +const EditorPreviewRenderer = ({ + content, + editorId, + blockId, + noStyle, + noMouseEvents, + onDoubleClick +}: EditorPreviewRendererProps) => { + const editableProps = { + placeholder: 'Murmuring the mex hype... ', + spellCheck: false, + style: noStyle + ? {} + : { + padding: '15px' + }, + readOnly: true + } + + // We get memoized plugins + const plugins = useMemoizedPlugins(components, { exclude: { dnd: true } }) + // const setHighlights = useBlockHighlightStore((s) => s.setHighlightedBlockIds) + // const { focusBlock } = useFocusBlock() + + useEffect(() => { + const timeoutId = setTimeout(() => { + if (blockId) { + // mog('editorPreviewRenderer', { blockId, editorId }) + // focusBlock(blockId, editorId) + // setHighlights([blockId], 'preview') + } + }, 300) + + return () => { + clearTimeout(timeoutId) + } + }, [blockId, editorId, content]) + + useEditorChange(editorId, content) + + return ( + { + if (onDoubleClick && ev.detail === 2) { + onDoubleClick(ev) + } + }} + > + + + + + ) +} +export default EditorPreviewRenderer diff --git a/apps/extension/src/Editor/components/ComboBox/config.tsx b/apps/extension/src/Editor/components/ComboBox/config.tsx index 211c51d3c..5cfb2d008 100644 --- a/apps/extension/src/Editor/components/ComboBox/config.tsx +++ b/apps/extension/src/Editor/components/ComboBox/config.tsx @@ -58,7 +58,7 @@ export const useComboboxConfig = ( ...(config.onChangeConfig as any) } - const prePlugins = useMemoizedPlugins(customPlugins ?? generatePlugins(), components) + const prePlugins = useMemoizedPlugins(components) const plugins = [ ...prePlugins, diff --git a/apps/extension/src/Editor/components/ComboBox/index.tsx b/apps/extension/src/Editor/components/ComboBox/index.tsx index 5ad26afa9..0010fb63e 100644 --- a/apps/extension/src/Editor/components/ComboBox/index.tsx +++ b/apps/extension/src/Editor/components/ComboBox/index.tsx @@ -1,114 +1,277 @@ -import { Icon } from '@iconify/react'; -import useMergedRef from '@react-hook/merged-ref'; +import { Icon } from '@iconify/react' +import useMergedRef from '@react-hook/merged-ref' +import { getPreventDefaultHandler, PortalBody, useEditorState } from '@udecode/plate' +import React, { useEffect, useState } from 'react' +import { useComboboxControls } from '../../hooks/useComboboxControls' +import { useComboboxIsOpen } from '../../hooks/useComboboxIsOpen' +import { useComboboxStore } from '../../store/combobox' +import { ComboboxRoot, ItemCenterWrapper, ItemDesc, ItemRightIcons, ItemTitle } from '@mexit/shared' +import { setElementPositionByRange } from '../../utils/setElementPositionByRange' +import { ComboboxProps } from './types' +import { CategoryType, NodeEditorContent, QuickLinkType, Shortcut, mog } from '@mexit/core' import { - getPreventDefaultHandler, - PortalBody, - useEditorState, -} from '@udecode/plate'; -import React, { useEffect } from 'react'; -import { useComboboxControls } from '../../hooks/useComboboxControls'; -import { useComboboxIsOpen } from '../../hooks/useComboboxIsOpen'; -import { useComboboxStore } from '../../store/combobox'; -import { - ComboboxItem, - ComboboxRoot, - ItemCenterWrapper, - ItemDesc, - ItemRightIcons, - ItemTitle, -} from './styled'; -import { setElementPositionByRange } from '../../utils/setElementPositionByRange'; -import { ComboboxProps } from './types'; -import { mog } from '../../utils'; - -export const Combobox = ({ - onSelectItem, - onRenderItem, - isSlash, - portalElement, -}: ComboboxProps) => { + ActionTitle, + ComboboxShortcuts, + ComboSeperator, + DisplayShortcut, + MexIcon, + PreviewMeta, + ShortcutText +} from '@mexit/shared' +import { useTheme } from 'styled-components' +import EditorPreviewRenderer from '../../../Components/EditorPreviewRenderer' +import { PrimaryText } from '../../../Components/Action/styled' +import { useContentStore } from '../../../Stores/useContentStore' +import { useSnippets } from '../../../Hooks/useSnippets' +import { replaceFragment } from '../../hooks/useComboboxOnKeyDown' +import { ComboboxItem } from '../../../Components/Dibba/styled' + +export const spotlightShortcuts = { + save: { + title: 'Save changes', + keystrokes: '$mod+Enter', + category: 'Action' + }, + open: { + title: 'Open item', + keystrokes: 'Enter', + category: 'Action' + }, + escape: { + title: 'Save and Escape', + keystrokes: 'Escape', + category: 'Navigation' + }, + Tab: { + title: 'Create new quick note', + keystrokes: 'Tab', + category: 'Action' + } +} + +export const ElementTypeBasedShortcut: Record> = { + [QuickLinkType.backlink]: { + link: { + ...spotlightShortcuts.open, + title: 'to Link' + }, + inlineBlock: { + ...spotlightShortcuts.Tab, + title: 'to Embed' + } + }, + [QuickLinkType.snippet]: { + snippet: { + ...spotlightShortcuts.open, + title: 'to Insert' + } + }, + [CategoryType.action]: { + action: { + ...spotlightShortcuts.open, + title: 'to Insert' + } + } +} + +export const Combobox = ({ onSelectItem, onRenderItem, isSlash, portalElement }: ComboboxProps) => { // TODO clear the error-esque warnings for 'type inference' - const at = useComboboxStore((state) => state.targetRange); - const items = useComboboxStore((state) => state.items); - const closeMenu = useComboboxStore((state) => state.closeMenu); - const itemIndex = useComboboxStore((state) => state.itemIndex); - const combobox = useComboboxControls(true); - const isOpen = useComboboxIsOpen(); - const search = useComboboxStore((state) => state.search); - const ref = React.useRef(null); // eslint-disable-line @typescript-eslint/no -explicit-any - const editor = useEditorState(); + const at = useComboboxStore((state) => state.targetRange) + const items = useComboboxStore((state) => state.items) + const closeMenu = useComboboxStore((state) => state.closeMenu) + const itemIndex = useComboboxStore((state) => state.itemIndex) + const targetRange = useComboboxStore((state) => state.targetRange) + const setItemIndex = useComboboxStore((state) => state.setItemIndex) + const isBlockTriggered = useComboboxStore((store) => store.isBlockTriggered) + const activeBlock = useComboboxStore((store) => store.activeBlock) + const preview = useComboboxStore((store) => store.preview) + const setPreview = useComboboxStore((store) => store.setPreview) + const search = useComboboxStore((store) => store.search) + const combobox = useComboboxControls(true) + const isOpen = useComboboxIsOpen() + + const { textAfterTrigger, textAfterBlockTrigger } = useComboboxStore((store) => store.search) + const getContent = useContentStore((store) => store.getContent) + const { getSnippetContent } = useSnippets() + const setIsSlash = useComboboxStore((store) => store.setIsSlash) + const [metaData, setMetaData] = useState(undefined) + const ref = React.useRef(null) // eslint-disable-line @typescript-eslint/no-explicit-any + const editor = useEditorState() + const theme = useTheme() useEffect(() => { // Throws error when the combobox is open and editor is switched or removed try { - if (editor) setElementPositionByRange(editor, { ref, at }); + if (editor) setElementPositionByRange(editor, { ref, at }) } catch (e) { - closeMenu(); - console.error(e); + closeMenu() + console.error(e) } - }, [at, editor]); + }, [at, editor]) - const menuProps = combobox ? combobox.getMenuProps() : { ref: null }; + const menuProps = combobox ? combobox.getMenuProps() : { ref: null } - const multiRef = useMergedRef(menuProps.ref, ref); - - if (!combobox) return null; + const multiRef = useMergedRef(menuProps.ref, ref) const comboProps = (item, index) => { if (combobox) { return combobox.getItemProps({ item, - index, - }); + index + }) + } + } + + useEffect(() => { + if (items?.[itemIndex]?.type === QuickLinkType.snippet) { + setIsSlash(true) + } else { + setIsSlash(false) + } + }, [itemIndex]) + + useEffect(() => { + if (editor && items?.[itemIndex] && textAfterTrigger.trim() && isBlockTriggered) { + replaceFragment(editor, targetRange, `[[${items[itemIndex].text}:`) + setItemIndex(0) + } + + if (isBlockTriggered) { + setPreview(undefined) + } + }, [isBlockTriggered]) + + useEffect(() => { + const comboItem = items[itemIndex] + + if (comboItem && comboItem.type && isOpen) { + const { key, type } = comboItem + + let content: NodeEditorContent | undefined + + if (type === QuickLinkType.backlink) { + const nodeContent = getContent(key) + console.log('nodeContent', nodeContent) + content = nodeContent?.content + + setMetaData(nodeContent?.metadata) + } else if (type === QuickLinkType.snippet) { + content = getSnippetContent(key) + } + /* + * else if (key === 'remind') { + // mog('reminderItem', { comboItem, search }) + const searchTerm = search.textAfterTrigger.slice(key.length) + const parsed = getTimeInText(searchTerm) + if (parsed) { + const time = toLocaleString(parsed.time) + const text = parsed.textWithoutTime + const newContent = getReminderPreview(time, text) + mog('getCommandExtendedInRenderItem', { parsed, search, newContent }) + content = newContent + } + } + */ + + // TODO: fix when adding block linking + if (!activeBlock) setPreview(content) + + if (isBlockTriggered && !textAfterBlockTrigger) { + setPreview(undefined) + } } - }; + }, [itemIndex, items, activeBlock, isOpen, search]) + + if (!combobox) return null + + const listItem = items[itemIndex] + const itemShortcut = listItem?.type ? ElementTypeBasedShortcut[listItem?.type] : undefined return ( - {isOpen && - items.map((item, index) => { - mog('ITEM', { item }); - const Item = onRenderItem ? onRenderItem({ item }) : item.text; - const text = item.text; - - return ( - - {item.icon && ( - - )} - - {Item} - {item.desc && {item.desc}} - - {item.rightIcons && ( - - {item.rightIcons.map((i: string) => ( - - ))} - + {isOpen && ( + <> + {!isBlockTriggered && ( +
+
+ {items.map((item, index) => { + const Item = onRenderItem ? onRenderItem({ item }) : item.text + const lastItem = index > 0 ? items[index - 1] : undefined + + return ( + + {item.type !== lastItem?.type && {item.type}} + { + setItemIndex(index) + }} + onMouseDown={() => { + editor && onSelectItem(editor, item) + }} + > + {item.icon && ( + + )} + + {!item.prefix ? ( + {Item} + ) : ( + + {item.prefix} {Item} + + )} + {item.desc && {item.desc}} + + {item.rightIcons && ( + + {item.rightIcons.map((i: string) => ( + + ))} + + )} + + + ) + })} +
+ {itemShortcut && ( + + {Object.entries(itemShortcut).map(([key, shortcut]) => { + return ( + + {' '} +
{shortcut.title}
+
+ ) + })} +
)} - - ); - })} +
+ )} + {preview && listItem?.type && ( + +
+ +
+ {preview && } +
+ )} + + )}
- ); -}; + ) +} diff --git a/apps/extension/src/Editor/components/ComboBox/styled.tsx b/apps/extension/src/Editor/components/ComboBox/styled.tsx deleted file mode 100644 index 4a04dbf78..000000000 --- a/apps/extension/src/Editor/components/ComboBox/styled.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import styled, { css } from 'styled-components' - -export const ComboboxRoot = styled.ul<{ isOpen: boolean }>` - ${({ isOpen, theme }) => - isOpen && - css` - top: -9999px; - left: -9999px; - position: absolute; - padding: 0; - margin: 0; - z-index: 9999999998; - background: ${theme.colors.background.modal}; - width: 300px; - border-radius: 8px; - box-shadow: rgba(0, 0, 0, 0.133) 0 3.2px 7.2px 0, rgba(0, 0, 0, 0.11) 0 0.6px 1.8px 0; - `} -` - -export const ItemTitle = styled.div`` -export const ItemRightIcons = styled.div` - display: flex; - flex-gap: ${({ theme }) => theme.spacing.tiny}; -` - -export const ItemDesc = styled.div` - margin-top: ${({ theme }) => theme.spacing.tiny}; - color: ${({ theme }) => theme.colors.text.fade}; - font-size: 0.8rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -` -export const ItemCenterWrapper = styled.div` - width: 90%; -` - -export const ComboboxItem = styled.div<{ highlighted: boolean }>` - display: flex; - align-items: center; - font-size: 14px; - gap: ${({ theme }) => theme.spacing.tiny}; - - :first-child { - border-radius: 6px 6px 0 0; - } - - :last-child { - border-radius: 0 0 6px 6px; - } - - font-weight: 400; - padding: 0 8px; - min-height: 36px; - user-select: none; - color: ${({ theme }) => theme.colors.text.subheading}; - background: ${({ highlighted, theme }) => (!highlighted ? 'transparent' : theme.colors.background.highlight)}; - cursor: pointer; - - :hover { - background-color: ${({ theme }) => theme.colors.background.highlight}; - } - - & > svg { - color: ${({ theme }) => theme.colors.gray[4]}; - } -` diff --git a/apps/extension/src/Editor/components/MultiCombobox/multiComboboxContainer.tsx b/apps/extension/src/Editor/components/MultiCombobox/multiComboboxContainer.tsx index 73b65cde7..1a20601a3 100644 --- a/apps/extension/src/Editor/components/MultiCombobox/multiComboboxContainer.tsx +++ b/apps/extension/src/Editor/components/MultiCombobox/multiComboboxContainer.tsx @@ -1,36 +1,28 @@ -import { RenderFunction } from '@udecode/plate'; -import React from 'react'; -import { useComboboxControls } from '../../hooks/useComboboxControls'; -import { getCreateableOnSelect } from '../../hooks/useComboboxOnKeyDown'; -import { useComboboxStore } from '../../store/combobox'; -import { Combobox } from '../ComboBox'; -import { ComboboxItemProps, ComboboxKeyDownConfig } from '../ComboBox/types'; +import { RenderFunction } from '@udecode/plate' +import React from 'react' +import { useComboboxControls } from '../../hooks/useComboboxControls' +import { getCreateableOnSelect } from '../../hooks/useComboboxOnKeyDown' +import { useComboboxStore } from '../../store/combobox' +import { Combobox } from '../ComboBox' +import { ComboboxItemProps, ComboboxKeyDownConfig } from '../ComboBox/types' -import { useOnSelectItem } from './useMultiComboboxOnKeyDown'; +import { useOnSelectItem } from './useMultiComboboxOnKeyDown' export interface SingleComboboxConfig { - slateElementType: string; - newItemHandler: (newItem: string, parentId?: any) => any; // eslint-disable-line @typescript-eslint/no-explicit-any - renderElement: RenderFunction; + slateElementType: string + newItemHandler: (newItem: string, parentId?: any) => any // eslint-disable-line @typescript-eslint/no-explicit-any + renderElement: RenderFunction } -export const ElementComboboxComponent = ({ - keys, - slashCommands, - portalElement, -}: ComboboxKeyDownConfig) => { - const comboboxKey: string = useComboboxStore.getState().key; - const comboRenderType = keys[comboboxKey]; - const { changeHandler: onSelectItem, isSlash } = useOnSelectItem( - comboboxKey, - slashCommands, - comboRenderType - ); +export const ElementComboboxComponent = ({ keys, slashCommands, portalElement }: ComboboxKeyDownConfig) => { + const comboboxKey: string = useComboboxStore.getState().key + const comboRenderType = keys[comboboxKey] + const { changeHandler: onSelectItem, isSlash } = useOnSelectItem(comboboxKey, slashCommands, comboRenderType) // console.log({ slashCommands }) const onNewItem = (newItem, parentId?) => { - comboRenderType.newItemHandler(newItem, parentId); - }; + comboRenderType.newItemHandler(newItem, parentId) + } - const creatableOnSelectItem = getCreateableOnSelect(onSelectItem, onNewItem); + const creatableOnSelectItem = getCreateableOnSelect(onSelectItem, onNewItem) return ( - ); -}; + ) +} // Handle multiple combobox export const MultiComboboxContainer = (props: ComboboxKeyDownConfig) => { - useComboboxControls(true); + useComboboxControls(true) - return ; -}; + return +} diff --git a/apps/extension/src/Editor/components/MultiCombobox/types.ts b/apps/extension/src/Editor/components/MultiCombobox/types.ts index 8af03d9a6..010b67c9d 100644 --- a/apps/extension/src/Editor/components/MultiCombobox/types.ts +++ b/apps/extension/src/Editor/components/MultiCombobox/types.ts @@ -1,14 +1,14 @@ -import { ComboboxKey } from '../../types'; +import { ComboboxKey } from '../../types' export interface ComboboxItem { - text: string; - value: string; - icon?: string; + text: string + value: string + icon?: string } export interface ComboboxType { - cbKey: ComboboxKey; - icon?: string; - trigger: string; - data: ComboboxItem[]; + cbKey: ComboboxKey + icon?: string + trigger: string + data: ComboboxItem[] } diff --git a/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxChange.ts b/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxChange.ts index 0c27e1aeb..553d1d507 100644 --- a/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxChange.ts +++ b/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxChange.ts @@ -1,83 +1,82 @@ -import { OnChange, usePlateEditorRef } from '@udecode/plate'; -import { useCallback } from 'react'; -import { useComboboxOnChange } from '../../hooks/useComboOnChange'; -import { useComboboxStore } from '../../store/combobox'; -import { fuzzySearch } from '../../utils/lib'; -import { ComboboxKey, IComboboxItem } from '../ComboBox/types'; -import { ComboboxType } from './types'; +import { OnChange, usePlateEditorRef } from '@udecode/plate' +import { useCallback } from 'react' +import { useComboboxOnChange } from '../../hooks/useComboOnChange' +import { useComboboxStore } from '../../store/combobox' +import { fuzzySearch } from '../../utils/lib' +import { ComboboxKey, IComboboxItem } from '../ComboBox/types' +import { ComboboxType } from './types' // * Handle multiple combobox const useMultiComboboxOnChange = ( editorId: string, keys: { - [type: string]: ComboboxType; + [type: string]: ComboboxType } ): OnChange => { - const editor = usePlateEditorRef(editorId)!; // eslint-disable-line @typescript-eslint/no-non-null-assertion + const editor = usePlateEditorRef(editorId)! // eslint-disable-line @typescript-eslint/no-non-null-assertion - const closeMenu = useComboboxStore((state) => state.closeMenu); + const closeMenu = useComboboxStore((state) => state.closeMenu) - const setItems = useComboboxStore((state) => state.setItems); + const setItems = useComboboxStore((state) => state.setItems) const comboboxOnChange = useComboboxOnChange({ editor, - keys, - }); + keys + }) // * Construct the correct change handler const changeHandler = useCallback(() => { - const res = comboboxOnChange(); - if (!res) return false; - const { search } = res; + const res = comboboxOnChange() + if (!res) return false + const { search } = res - if (!search && search !== '') return false; + if (!search && search !== '') return false - const key = useComboboxStore.getState().key; - const maxSuggestions = useComboboxStore.getState().maxSuggestions; + const key = useComboboxStore.getState().key + const maxSuggestions = useComboboxStore.getState().maxSuggestions - const ct = keys[key]; - const data = ct.data; + const ct = keys[key] + const data = ct.data - if (!data) return false; + if (!data) return false - const searchItems = fuzzySearch(data, search, { keys: ['text'] }); + const searchItems = fuzzySearch(data, search, { keys: ['text'] }) const items: IComboboxItem[] = ( - search !== '' - ? searchItems.slice(0, maxSuggestions) - : keys[key].data.slice(0, maxSuggestions) + search !== '' ? searchItems.slice(0, maxSuggestions) : keys[key].data.slice(0, maxSuggestions) ).map((item) => ({ key: item.value, icon: item.icon ?? ct.icon ?? undefined, text: item.text, - })); + type: item?.type + })) // TODO: Disable new item if key exists. if (key !== ComboboxKey.SLASH_COMMAND && search !== '') { items.push({ key: '__create_new', icon: 'ri:add-circle-line', - text: `Create New ${search}`, - }); + text: `Create New ${search}` + }) } - setItems(items); + setItems(items) - return true; - }, [comboboxOnChange, setItems, keys]); + return true + }, [comboboxOnChange, setItems, keys]) return useCallback( () => () => { - const isOpen = !!useComboboxStore.getState().targetRange; + const isOpen = !!useComboboxStore.getState().targetRange - let changed: boolean | undefined = false; - changed = changeHandler !== undefined ? changeHandler() : false; - if (changed) return; + let changed: boolean | undefined = false + changed = changeHandler !== undefined ? changeHandler() : false + if (changed) return if (!changed && isOpen) { - closeMenu(); + closeMenu() } }, [closeMenu, changeHandler, keys] - ); -}; + ) +} -export default useMultiComboboxOnChange; +export default useMultiComboboxOnChange diff --git a/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxOnKeyDown.ts b/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxOnKeyDown.ts index 5f80af73b..18d7db212 100644 --- a/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxOnKeyDown.ts +++ b/apps/extension/src/Editor/components/MultiCombobox/useMultiComboboxOnKeyDown.ts @@ -1,17 +1,17 @@ -import { PlateEditor } from '@udecode/plate'; -import { useComboboxOnKeyDown } from '../../hooks/useComboboxOnKeyDown'; -import { useElementOnChange } from '../../hooks/useElementOnChange'; +import { PlateEditor } from '@udecode/plate' +import { useComboboxOnKeyDown } from '../../hooks/useComboboxOnKeyDown' +import { useElementOnChange } from '../../hooks/useElementOnChange' import { ComboboxItemType, ComboboxKey, ComboboxKeyDownConfig, IComboboxItem, - SlashCommandConfig, -} from '../ComboBox/types'; -import { useSlashCommandOnChange } from '../SlashCommands/useSlashCommandOnChange'; + SlashCommandConfig +} from '../ComboBox/types' +import { useSlashCommandOnChange } from '../SlashCommands/useSlashCommandOnChange' export interface ComboTypeHandlers { - slateElementType: string; - newItemHandler: (newItem: string, parentId?) => any; // eslint-disable-line @typescript-eslint/no-explicit-any + slateElementType: string + newItemHandler: (newItem: string, parentId?) => any // eslint-disable-line @typescript-eslint/no-explicit-any } export const useOnSelectItem = ( @@ -19,19 +19,18 @@ export const useOnSelectItem = ( slashCommands: Record, singleComboConfig: ComboboxItemType ) => { - const slashCommandOnChange = useSlashCommandOnChange(slashCommands); - const elementOnChange = useElementOnChange(singleComboConfig); + const slashCommandOnChange = useSlashCommandOnChange(slashCommands) + const elementOnChange = useElementOnChange(singleComboConfig) - const isSlash = comboboxKey === ComboboxKey.SLASH_COMMAND; + const isSlash = comboboxKey === ComboboxKey.SLASH_COMMAND - const changeHandler = (editor: PlateEditor, item: IComboboxItem) => - isSlash ? slashCommandOnChange : elementOnChange; + const changeHandler = (editor: PlateEditor, item: IComboboxItem) => (isSlash ? slashCommandOnChange : elementOnChange) - return { changeHandler, isSlash }; -}; + return { changeHandler, isSlash } +} const useMultiComboboxOnKeyDown = (config: ComboboxKeyDownConfig) => { - return useComboboxOnKeyDown(config); -}; + return useComboboxOnKeyDown(config) +} -export default useMultiComboboxOnKeyDown; +export default useMultiComboboxOnKeyDown diff --git a/apps/extension/src/Editor/hooks/useComboboxOnKeyDown.ts b/apps/extension/src/Editor/hooks/useComboboxOnKeyDown.ts index 5134737d7..b6c67c58c 100644 --- a/apps/extension/src/Editor/hooks/useComboboxOnKeyDown.ts +++ b/apps/extension/src/Editor/hooks/useComboboxOnKeyDown.ts @@ -1,121 +1,113 @@ -import { getNextWrappingIndex, PlateEditor } from '@udecode/plate'; -import { KeyboardHandler } from '@udecode/plate-core'; -import { ComboboxKey, IComboboxItem } from '../components/ComboBox/types'; -import { useSlashCommandOnChange } from '../components/SlashCommands/useSlashCommandOnChange'; -import { useComboboxStore } from '../store/combobox'; -import { useMexEditorStore } from '../store/editor'; -import { useElementOnChange } from './useElementOnChange'; +import { getNextWrappingIndex, PlateEditor } from '@udecode/plate' +import { KeyboardHandler } from '@udecode/plate-core' +import { ComboboxKey, IComboboxItem } from '../components/ComboBox/types' +import { useSlashCommandOnChange } from '../components/SlashCommands/useSlashCommandOnChange' +import { useComboboxStore } from '../store/combobox' +import { useMexEditorStore } from '../store/editor' +import { useElementOnChange } from './useElementOnChange' +import { Transforms, Editor } from 'slate' const pure = (id: string) => { if (id.endsWith(']]')) { - return id.substr(0, id.length - 2); + return id.substr(0, id.length - 2) } - return id; -}; + return id +} -export type OnSelectItem = (editor: PlateEditor, item: IComboboxItem) => any; // eslint-disable-line @typescript-eslint/no-explicit-any -export type OnNewItem = (name: string, parentId?) => void; +export type OnSelectItem = (editor: PlateEditor, item: IComboboxItem) => any // eslint-disable-line @typescript-eslint/no-explicit-any +export type OnNewItem = (name: string, parentId?) => void -export const getCreateableOnSelect = ( - onSelectItem: OnSelectItem, - onNewItem: OnNewItem, - creatable?: boolean -) => { +export const getCreateableOnSelect = (onSelectItem: OnSelectItem, onNewItem: OnNewItem, creatable?: boolean) => { const creatableOnSelect = (editor: any, textVal: string) => { - const items = useComboboxStore.getState().items; - const currentNodeKey = useMexEditorStore.getState().metaData.path; - const itemIndex = useComboboxStore.getState().itemIndex; + const items = useComboboxStore.getState().items + const currentNodeKey = useMexEditorStore.getState().metaData.path + const itemIndex = useComboboxStore.getState().itemIndex - const val = pure(textVal); + const val = pure(textVal) if (items[itemIndex]) { - const item = items[itemIndex]; + const item = items[itemIndex] if (item.key === '__create_new' && val !== '') { - onSelectItem(editor, { key: String(items.length), text: val }); - onNewItem(val, currentNodeKey); - } else onSelectItem(editor, item); + onSelectItem(editor, { key: String(items.length), text: val }) + onNewItem(val, currentNodeKey) + } else onSelectItem(editor, item) } else if (val && creatable) { - onSelectItem(editor, { key: String(items.length), text: val }); - onNewItem(val, currentNodeKey); + onSelectItem(editor, { key: String(items.length), text: val }) + onNewItem(val, currentNodeKey) } - }; + } + + return creatableOnSelect +} - return creatableOnSelect; -}; +export const replaceFragment = (editor: any, range: any, text: string) => { + const sel = editor.selection + + if (sel) { + Transforms.select(editor, range) + Editor.insertText(editor, text) + } +} /** * If the combobox is open, handle keyboard */ export const useComboboxOnKeyDown = (config: any): KeyboardHandler => { - const setItemIndex = useComboboxStore((state) => state.setItemIndex); - const closeMenu = useComboboxStore((state) => state.closeMenu); + const setItemIndex = useComboboxStore((state) => state.setItemIndex) + const closeMenu = useComboboxStore((state) => state.closeMenu) // We need to create the select handlers ourselves here - const { keys, slashCommands } = config; - const slashCommandOnChange = useSlashCommandOnChange(slashCommands); - const comboboxKey: string = useComboboxStore.getState().key; + const { keys, slashCommands } = config + const slashCommandOnChange = useSlashCommandOnChange(slashCommands) + const comboboxKey: string = useComboboxStore.getState().key - const elementOnChange = useElementOnChange(keys[comboboxKey], keys); + const elementOnChange = useElementOnChange(keys[comboboxKey], keys) return (editor) => (e) => { - const comboboxKey: string = useComboboxStore.getState().key; - const comboType = keys[comboboxKey]; + const comboboxKey: string = useComboboxStore.getState().key + const comboType = keys[comboboxKey] const onSelectItemHandler = - comboType.slateElementType === ComboboxKey.SLASH_COMMAND - ? slashCommandOnChange - : elementOnChange; + comboType.slateElementType === ComboboxKey.SLASH_COMMAND ? slashCommandOnChange : elementOnChange const creatabaleOnSelect = getCreateableOnSelect( onSelectItemHandler, (newItem, parentId?) => { - comboType.newItemHandler(newItem, parentId); + comboType.newItemHandler(newItem, parentId) }, comboboxKey !== ComboboxKey.SLASH_COMMAND - ); + ) - const itemIndex = useComboboxStore.getState().itemIndex; - const search = useComboboxStore.getState().search; - const items = useComboboxStore.getState().items; - const isOpen = !!useComboboxStore.getState().targetRange; + const itemIndex = useComboboxStore.getState().itemIndex + const search = useComboboxStore.getState().search + const items = useComboboxStore.getState().items + const isOpen = !!useComboboxStore.getState().targetRange if (isOpen) { if (e.key === 'ArrowDown') { - e.preventDefault(); - - const newIndex = getNextWrappingIndex( - 1, - itemIndex, - items.length, - () => undefined, - true - ); - return setItemIndex(newIndex); + e.preventDefault() + + const newIndex = getNextWrappingIndex(1, itemIndex, items.length, () => undefined, true) + return setItemIndex(newIndex) } if (e.key === 'ArrowUp') { - e.preventDefault(); - - const newIndex = getNextWrappingIndex( - -1, - itemIndex, - items.length, - () => undefined, - true - ); - return setItemIndex(newIndex); + e.preventDefault() + + const newIndex = getNextWrappingIndex(-1, itemIndex, items.length, () => undefined, true) + return setItemIndex(newIndex) } if (e.key === 'Escape') { - e.preventDefault(); - return closeMenu(); + e.preventDefault() + return closeMenu() } if (['Tab', 'Enter', ' ', ']'].includes(e.key)) { - e.preventDefault(); - creatabaleOnSelect(editor, search); - return false; + e.preventDefault() + creatabaleOnSelect(editor, search) + return false } } - return false; - }; -}; + return false + } +} diff --git a/apps/extension/src/Editor/plugins/QuickLink/components/QuickLinkElement.tsx b/apps/extension/src/Editor/plugins/QuickLink/components/QuickLinkElement.tsx index 2304f399c..377e4e64d 100644 --- a/apps/extension/src/Editor/plugins/QuickLink/components/QuickLinkElement.tsx +++ b/apps/extension/src/Editor/plugins/QuickLink/components/QuickLinkElement.tsx @@ -73,6 +73,7 @@ const QuickLinkElement = ({ ]] ) : ( + // TODO: uncomment this when the id from address bar issue is fixed in webapp // { +export type PluginOptionType = { + exclude: { + dnd: boolean + } +} + +/** + * Plugin generator + * @param config Configurations for the plugins, event handlers etc. + * @returns Array of PlatePlugin + */ + +export const generatePlugins = (options: PluginOptionType) => { const Plugins: PlatePlugin[] = [ + // editor + // elements - createParagraphPlugin(), - createBlockquotePlugin(), - createCodeBlockPlugin(), - createHeadingPlugin(), + createParagraphPlugin(), // paragraph element + createBlockquotePlugin(), // blockquote element + createCodeBlockPlugin(), // code block element + createHeadingPlugin(), // heading elements // Marks - createBoldPlugin(), - createItalicPlugin(), - createUnderlinePlugin(), - createStrikethroughPlugin(), - createCodePlugin(), - createHighlightPlugin(), - createTodoListPlugin(), + createBoldPlugin(), // bold mark + createItalicPlugin(), // italic mark + createUnderlinePlugin(), // underline mark + createStrikethroughPlugin(), // strikethrough mark + createCodePlugin(), // code mark + createHighlightPlugin(), // highlight mark + // createTodoListPlugin(), // Special Elements - createImagePlugin(), - createLinkPlugin(), - createListPlugin(), - createTablePlugin(), + // createImagePlugin(optionsImagePlugin), // Image + createLinkPlugin(), // Link + createListPlugin(), // List createTodoPlugin(), + createTablePlugin({ component: TableWrapper }), // Table // Editing Plugins createSoftBreakPlugin(optionsSoftBreakPlugin), createExitBreakPlugin(optionsExitBreakPlugin), createResetNodePlugin(optionsResetBlockTypePlugin), createHorizontalRulePlugin(), - createSelectOnBackspacePlugin({ - options: { query: { allow: [ELEMENT_HR] } } - }), + createSelectOnBackspacePlugin({ options: { query: { allow: [ELEMENT_HR, ELEMENT_EXCALIDRAW] } } }), createAlignPlugin({ inject: { props: { @@ -99,17 +113,15 @@ export const generatePlugins = () => { } } }), - // Autoformat markdown syntax to elements (**, #(n)) createAutoformatPlugin({ options: { rules: [ ...autoformatSmartQuotes, - ...autoformatPunctuation, ...autoformatLegal, ...autoformatLegalHtml, - ...autoformatArrow, ...autoformatMath, + ...autoformatArrow, ...optionsAutoFormatRule, { mode: 'block', @@ -126,28 +138,43 @@ export const generatePlugins = () => { ] } }), - createDndPlugin(), + createNodeIdPlugin(optionsCreateNodeIdPlugin), + + // serialization / deseriailization + + // Convert pasted markdown to contents of the editor + // createDeserializeMDPlugin(), + + // Media and link embed createMediaEmbedPlugin(), - // createNodeIdPlugin(optionsCreateNodeIdPlugin), + // Custom Plugins + // createBlurSelectionPlugin() as PlatePlugin, + + // Comboboxes + createTagPlugin(), // Tags + createQuickLinkPlugin(), // Internal Links ILinks - // // mex custom plugins - createTagPlugin(), - createQuickLinkPlugin(), + // // For Inline Blocks + // createInlineBlockPlugin(), createSelectOnBackspacePlugin(optionsSelectOnBackspacePlugin) + // createHighlightTextPlugin() ] - return Plugins + const withPlugins = !options?.exclude?.dnd ? [...Plugins, createDndPlugin()] : Plugins + + return withPlugins } -const useMemoizedPlugins = ( - plugins: Array, - components: Record> -) => { - return createPlugins(plugins, { - components: createPlateUI(components) +const useMemoizedPlugins = (components: Record, options?: PluginOptionType) => { + const wrappedComponents = components + + const plugins = createPlugins(generatePlugins(options), { + components: wrappedComponents }) + + return plugins } export default useMemoizedPlugins diff --git a/apps/extension/src/Editor/plugins/options.tsx b/apps/extension/src/Editor/plugins/options.tsx index ec2a514ef..1336c0fd4 100644 --- a/apps/extension/src/Editor/plugins/options.tsx +++ b/apps/extension/src/Editor/plugins/options.tsx @@ -38,6 +38,7 @@ import { } from '@udecode/plate' import { generateTempId } from '@mexit/core' +import { uploadImageToWDCDN } from '@mexit/shared' const preFormat = (editor: TEditor) => unwrapList(editor as PlateEditor) @@ -255,3 +256,9 @@ export const optionsCreateNodeIdPlugin = { // exclude: [ELEMENT_SYNC_BLOCK], } } + +export const optionsImagePlugin = { + options: { + uploadImage: uploadImageToWDCDN + } +} diff --git a/apps/extension/src/Editor/store/combobox.ts b/apps/extension/src/Editor/store/combobox.ts index dee09c99b..338512d07 100644 --- a/apps/extension/src/Editor/store/combobox.ts +++ b/apps/extension/src/Editor/store/combobox.ts @@ -1,47 +1,79 @@ -import { UseComboboxReturnValue } from 'downshift'; -import { Range } from 'slate'; -import { ComboboxKey, IComboboxItem } from '../components/ComboBox/types'; -import { createStore, setStoreValue } from '../utils/store.utils'; +import { UseComboboxReturnValue } from 'downshift' +import { BaseRange, Point, Range } from 'slate' +import { ComboboxKey, IComboboxItem } from '../components/ComboBox/types' +import { ComboboxType } from '../types' +import { createStore, setStoreValue } from '../utils/store.utils' + +export type ComboTriggerType = ComboboxType & { at?: Point; blockAt?: Point } export type ComboboxState = { // Combobox key - key: string; - setKey: (value: string) => void; + key: string + setKey: (value: string) => void // Maximum number of suggestions - maxSuggestions: number; - setMaxSuggestions: (value: number) => void; + maxSuggestions: number + setMaxSuggestions: (value: number) => void + + activeBlock: any + setActiveBlock: (block: any) => void // Tag search value - search: string; - setSearch: (value: string) => void; + search: string + setSearch: (value: string) => void // Fetched tags - items: IComboboxItem[]; - setItems: (value: IComboboxItem[]) => void; + items: IComboboxItem[] + setItems: (value: IComboboxItem[]) => void + + isBlockTriggered: boolean + setIsBlockTriggered: (value: boolean) => void + + blockRange: BaseRange | null + setBlockRange: (value: BaseRange) => void // Range from the tag trigger to the cursor - targetRange: Range | null; - setTargetRange: (value: Range | null) => void; + targetRange: Range | null + setTargetRange: (value: Range | null) => void // Highlighted index - itemIndex: number; - setItemIndex: (value: number) => void; + itemIndex: number + setItemIndex: (value: number) => void + + isSlash: boolean + setIsSlash: (value: boolean) => void + + preview?: any + setPreview: (value: any) => void - combobox: UseComboboxReturnValue | null; - setCombobox: (value: UseComboboxReturnValue) => void; + showPreview: boolean + setShowPreview: (value: boolean) => void - closeMenu: () => void; -}; + combobox: UseComboboxReturnValue | null + setCombobox: (value: UseComboboxReturnValue) => void + + closeMenu: () => void +} export const useComboboxStore = createStore()((set) => ({ - key: ComboboxKey.ILINK, + key: ComboboxKey.TAG, setKey: setStoreValue(set, 'key', 'setKey'), + setBlockRange: setStoreValue(set, 'blockRange', 'setBlockRange'), + + isSlash: false, + setIsSlash: setStoreValue(set, 'isSlash', 'setIsSlash'), + + isBlockTriggered: false, + setIsBlockTriggered: setStoreValue(set, 'isBlockTriggered', 'setIsBlockTriggered'), + maxSuggestions: 10, setMaxSuggestions: setStoreValue(set, 'maxSuggestions', 'setMaxSuggestions'), - search: '', + setActiveBlock: setStoreValue(set, 'activeBlock', 'setActiveBlock'), + setPreview: setStoreValue(set, 'preview', 'setPreview'), + + search: { textAfterTrigger: '' }, setSearch: setStoreValue(set, 'search', 'setSearch'), items: [], @@ -58,10 +90,10 @@ export const useComboboxStore = createStore()((set) => ({ closeMenu: () => { set((state) => { - state.targetRange = null; - state.items = []; - state.search = ''; - state.itemIndex = 0; - }); - }, -})); + state.targetRange = null + state.items = [] + state.search = '' + state.itemIndex = 0 + }) + } +})) diff --git a/apps/extension/src/Editor/utils/constants.ts b/apps/extension/src/Editor/utils/constants.ts deleted file mode 100644 index 74f51914a..000000000 --- a/apps/extension/src/Editor/utils/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const SEPARATOR = '.' \ No newline at end of file diff --git a/apps/extension/src/Editor/utils/helper.ts b/apps/extension/src/Editor/utils/helper.ts index 3d2c13cd1..08e3c2c73 100644 --- a/apps/extension/src/Editor/utils/helper.ts +++ b/apps/extension/src/Editor/utils/helper.ts @@ -1,42 +1,38 @@ -import { deepEqual } from 'fast-equals'; -import { SEPARATOR } from './constants'; +import { SEPARATOR } from '@mexit/core' +import { deepEqual } from 'fast-equals' export const withoutDelimiter = (text: string, delimiter = '.') => { const key = text .split(delimiter) .filter((ch) => ch !== '') - .join(delimiter); + .join(delimiter) - if (text?.startsWith(delimiter) && key.length > 0) - return { key: `.${key}`, isChild: true }; - return { key, isChild: false }; -}; + if (text?.startsWith(delimiter) && key.length > 0) return { key: `.${key}`, isChild: true } + return { key, isChild: false } +} export const removeNulls = (obj: any): any => { if (obj === null) { - return undefined; + return undefined } if (typeof obj === 'object') { for (const key in obj) { - obj[key] = removeNulls(obj[key]); + obj[key] = removeNulls(obj[key]) } } - return obj; -}; + return obj +} export const removeLink = (item: T, list: T[]): T[] => { - return list.filter((l) => !deepEqual(l, item)); -}; + return list.filter((l) => !deepEqual(l, item)) +} export const getAllParentIds = (id: string) => id .split(SEPARATOR) - .reduce( - (p, c) => [...p, p.length > 0 ? `${p[p.length - 1]}${SEPARATOR}${c}` : c], - [] as Array - ); + .reduce((p, c) => [...p, p.length > 0 ? `${p[p.length - 1]}${SEPARATOR}${c}` : c], [] as Array) -export const typeInvert = (type: string) => (type === 'from' ? 'to' : 'from'); +export const typeInvert = (type: string) => (type === 'from' ? 'to' : 'from') // * Returns an array of unique values via Set -export const Settify = (arr: T[]): T[] => Array.from(new Set(arr)); +export const Settify = (arr: T[]): T[] => Array.from(new Set(arr)) diff --git a/apps/extension/src/Editor/utils/idGenerators.ts b/apps/extension/src/Editor/utils/idGenerators.ts deleted file mode 100644 index 598a6d15c..000000000 --- a/apps/extension/src/Editor/utils/idGenerators.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { customAlphabet } from 'nanoid'; -import { SEPARATOR } from './constants'; - -const nolookalikes = '346789ABCDEFGHJKLMNPQRTUVWXYabcdefghijkmnpqrtwxyz'; -const nanoid = customAlphabet(nolookalikes, 21); - -export const ID_SEPARATOR = '_'; -export const NODE_ID_PREFIX = 'NODE'; -export const WORKSPACE_ID_PREFIX = 'WORKSPACE'; -export const IG_ID_PREFIX = 'INTENTGROUP'; -export const SYNC_BLOCK_ID_PREFIX = 'SYNC'; -export const TEMP_ID_PREFIX = 'TEMP'; -export const SNIPPET_PREFIX = 'SNIPPET'; -export const SYNCTEMP_PREFIX = 'SYNCTEMP'; -export const TODO_PREFIX = 'TODO'; -export const DRAFT_PREFIX = 'Draft'; -export const MEETING_PREFIX = 'Meeting'; -export const QUESTION_ID_PREFIX = 'WD_MEX_QUESTION'; -export const DRAFT_NODE = 'Untitled'; - -export const generateNodeUID = () => - `${NODE_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateNodeId = () => - `${NODE_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateWorkspaceId = () => - `${WORKSPACE_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateIgId = () => `${IG_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateSyncBlockId = () => - `${SYNC_BLOCK_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateTempId = () => - `${TEMP_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateSnippetId = () => - `${SNIPPET_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateSyncTempId = () => - `${SYNCTEMP_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateTodoId = () => `${TODO_PREFIX}${ID_SEPARATOR}${nanoid()}`; -export const generateQuestionId = () => - `${QUESTION_ID_PREFIX}${ID_SEPARATOR}${nanoid()}`; - -export const cleanString = (str: string) => - str.startsWith(`${DRAFT_PREFIX}${SEPARATOR}`) - ? str.replace(`${DRAFT_PREFIX}${SEPARATOR}`, '') - : str; diff --git a/apps/extension/src/Editor/utils/index.ts b/apps/extension/src/Editor/utils/index.ts index e34183a60..f4a149a8f 100644 --- a/apps/extension/src/Editor/utils/index.ts +++ b/apps/extension/src/Editor/utils/index.ts @@ -1,47 +1,6 @@ -export { SEPARATOR } from './constants'; -export { getHugeDocument } from './content'; -export { getTextFromTrigger } from './getTextFromTrigger'; -export { - Settify, - getAllParentIds, - removeLink, - removeNulls, - typeInvert, - withoutDelimiter, -} from './helper'; -export { - ID_SEPARATOR, - NODE_ID_PREFIX, - WORKSPACE_ID_PREFIX, - IG_ID_PREFIX, - SYNC_BLOCK_ID_PREFIX, - TEMP_ID_PREFIX, - SNIPPET_PREFIX, - SYNCTEMP_PREFIX, - TODO_PREFIX, - DRAFT_PREFIX, - MEETING_PREFIX, - QUESTION_ID_PREFIX, - DRAFT_NODE, - generateNodeUID, - generateNodeId, - generateWorkspaceId, - generateIgId, - generateSyncBlockId, - generateTempId, - generateSnippetId, - generateSyncTempId, - generateTodoId, - generateQuestionId, -} from './idGenerators'; -export { fuzzySearch } from './lib'; -export { mog } from './mog'; -export { setElementPositionByRange } from './setElementPositionByRange'; -export { - action, - combineAndImmer, - createStore, - immer, - immerMutable, - setStoreValue, -} from './store.utils'; +export { getHugeDocument } from './content' +export { getTextFromTrigger } from './getTextFromTrigger' +export { Settify, getAllParentIds, removeLink, removeNulls, typeInvert, withoutDelimiter } from './helper' +export { fuzzySearch } from './lib' +export { setElementPositionByRange } from './setElementPositionByRange' +export { action, combineAndImmer, createStore, immer, immerMutable, setStoreValue } from './store.utils' diff --git a/apps/extension/src/Editor/utils/mog.ts b/apps/extension/src/Editor/utils/mog.ts deleted file mode 100644 index 4b1b41717..000000000 --- a/apps/extension/src/Editor/utils/mog.ts +++ /dev/null @@ -1,19 +0,0 @@ -type MogOptions = { - pretty: boolean; - collapsed: boolean; -}; - -export const mog = ( - title: string, - propertiesToLog: Record, - options: Partial = { pretty: false, collapsed: false } -) => { - options.collapsed ? console.groupCollapsed(title) : console.group(title); - Object.entries(propertiesToLog).forEach(([key, value]) => { - console.info( - `${key}: `, - options?.pretty ? JSON.stringify(value, null, 2) : value - ); - }); - console.groupEnd(); -}; diff --git a/apps/extension/src/Hooks/useSnippets.tsx b/apps/extension/src/Hooks/useSnippets.tsx index 7a79a54b5..4b0c304e1 100644 --- a/apps/extension/src/Hooks/useSnippets.tsx +++ b/apps/extension/src/Hooks/useSnippets.tsx @@ -1,3 +1,4 @@ +import { SEPARATOR } from '@mexit/core' import { useSnippetStore } from '../Stores/useSnippetStore' export const useSnippets = () => { @@ -13,5 +14,17 @@ export const useSnippets = () => { return undefined } - return { getSnippets, getSnippet } + // Replacer that will provide new fresh and different content each time + const getSnippetContent = (command: string) => { + const snippets = useSnippetStore.getState().snippets + const snippet = snippets.filter((c) => getSnippetCommand(c.title) === command) + + if (snippet.length > 0) return snippet[0].content + return undefined + } + + return { getSnippets, getSnippet, getSnippetContent } } + +export const SnippetCommandPrefix = `snip` +export const getSnippetCommand = (title: string) => `${SnippetCommandPrefix}${SEPARATOR}${title}` diff --git a/apps/extension/src/Utils/plugins.ts b/apps/extension/src/Utils/plugins.ts deleted file mode 100644 index b3a2f830d..000000000 --- a/apps/extension/src/Utils/plugins.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Plugins for Plate Editor - -import { - createBlockquotePlugin, - createBoldPlugin, - createCodeBlockPlugin, - createCodePlugin, - createHeadingPlugin, - createHighlightPlugin, - createItalicPlugin, - createParagraphPlugin, - createPlugins, - createStrikethroughPlugin, - createUnderlinePlugin, - PlatePlugin -} from '@udecode/plate' - -const generatePlugins = () => { - const Plugins: PlatePlugin[] = [ - // editor - - // elements - createParagraphPlugin(), // paragraph element - createBlockquotePlugin(), // blockquote element - createCodeBlockPlugin(), // code block element - createHeadingPlugin(), // heading elements - - // Marks - createBoldPlugin(), // bold mark - createItalicPlugin(), // italic mark - createUnderlinePlugin(), // underline mark - createStrikethroughPlugin(), // strikethrough mark - createCodePlugin(), // code mark - createHighlightPlugin() // highlight mark - ] - - return Plugins -} - -const useMemoizedPlugins = (components?: any) => { - return createPlugins(generatePlugins()) -} - -export default useMemoizedPlugins diff --git a/apps/extension/webpack.config.js b/apps/extension/webpack.config.js index 5dc8b86c0..ac3d8bda2 100644 --- a/apps/extension/webpack.config.js +++ b/apps/extension/webpack.config.js @@ -66,7 +66,7 @@ module.exports = (config, context) => { filename: '[name].js' }, optimization: { - minimize: true, + minimize: !!process.env.NO_SOURCE_MAP, minimizer: [new ESBuildMinifyPlugin()], runtimeChunk: false }, diff --git a/apps/webapp/src/Components/Editor/Editor.tsx b/apps/webapp/src/Components/Editor/Editor.tsx index 1d8b20ea8..3dae09b99 100644 --- a/apps/webapp/src/Components/Editor/Editor.tsx +++ b/apps/webapp/src/Components/Editor/Editor.tsx @@ -20,7 +20,6 @@ import { useSnippets } from '../../Hooks/useSnippets' import { CategoryType, QuickLinkType } from '../../Editor/constants' import { SlashComboboxItem } from '../../Editor/Components/SlashCommands/SlashComboboxItem' import { TagComboboxItem } from '../../Editor/Components/Tags/TagComboboxItem' -import { QuickLinkElement } from '../../Editor/Components/QuickLink/QuickLinkElement' import { QuickLinkComboboxItem } from '../../Editor/Components/QuickLink/QuickLinkComboboxItem' import components from '../../Editor/Components/EditorPreviewComponents' diff --git a/apps/webapp/src/Components/EditorInfobar/Metadata.tsx b/apps/webapp/src/Components/EditorInfobar/Metadata.tsx index c59a154fe..f72f0f8c7 100644 --- a/apps/webapp/src/Components/EditorInfobar/Metadata.tsx +++ b/apps/webapp/src/Components/EditorInfobar/Metadata.tsx @@ -10,7 +10,7 @@ import { Label } from '../../Style/Form' import { CardShadow, HoverFade } from '../../Style/Helpers' import { ProfileIcon } from '../../Style/UserPage' import { NodeMetadata, NodeProperties } from '@mexit/core' -import { RelativeTime } from '../RelativeTime' +import { RelativeTime } from '@mexit/shared' import { ProfileImageWithToolTip } from '../User/ProfileImage' import { useContentStore } from '../../Stores/useContentStore' diff --git a/apps/webapp/src/Components/EditorInfobar/PublicNodeMetadata.tsx b/apps/webapp/src/Components/EditorInfobar/PublicNodeMetadata.tsx index 7e1ab35e6..3bc753f7c 100644 --- a/apps/webapp/src/Components/EditorInfobar/PublicNodeMetadata.tsx +++ b/apps/webapp/src/Components/EditorInfobar/PublicNodeMetadata.tsx @@ -7,7 +7,7 @@ import { focusStyles, FocusModeProp } from '@mexit/shared' import { Label } from '../../Style/Form' import { HoverFade } from '../../Style/Helpers' import { ProfileIcon } from '../../Style/UserPage' -import { RelativeTime } from '../RelativeTime' +import { RelativeTime } from '@mexit/shared' import { ProfileImageWithToolTip } from '../User/ProfileImage' import { PublicNode } from '../../Stores/usePublicNodes' diff --git a/apps/webapp/src/Components/Todo/PrioritySelect.tsx b/apps/webapp/src/Components/Todo/PrioritySelect.tsx index 88c516667..749a4ead8 100644 --- a/apps/webapp/src/Components/Todo/PrioritySelect.tsx +++ b/apps/webapp/src/Components/Todo/PrioritySelect.tsx @@ -1,10 +1,9 @@ import { PriorityType, PriorityDataType, Priority } from '@mexit/core' -import { MexIcon } from '@mexit/shared' +import { MexIcon, TodoActionButton, TodoActionWrapper } from '@mexit/shared' import Tippy from '@tippyjs/react' import React from 'react' import { useContextMenu } from 'react-contexify' -import { TodoActionWrapper, TodoActionButton } from '../../Style/Todo.style' -import PriorityMenu from "./PriorityMenu" +import PriorityMenu from './PriorityMenu' interface PriorityMenuSelect { id: string diff --git a/apps/webapp/src/Components/Todo/Todo.tsx b/apps/webapp/src/Components/Todo/Todo.tsx index ac270a862..dba51704c 100644 --- a/apps/webapp/src/Components/Todo/Todo.tsx +++ b/apps/webapp/src/Components/Todo/Todo.tsx @@ -1,8 +1,7 @@ import { TodoStatus, PriorityType, TodoType, PriorityDataType, getNextStatus } from '@mexit/core' -import { MexIcon } from '@mexit/shared' +import { CheckBoxWrapper, MexIcon, StyledTodoStatus, TodoContainer, TodoOptions, TodoText } from '@mexit/shared' import React, { useEffect, useMemo, useState } from 'react' import { useTodoStore } from '../../Stores/useTodoStore' -import { TodoContainer, CheckBoxWrapper, StyledTodoStatus, TodoText, TodoOptions } from '../../Style/Todo.style' import PrioritySelect from './PrioritySelect' export interface TodoControls { diff --git a/apps/webapp/src/Components/Welcome.tsx b/apps/webapp/src/Components/Welcome.tsx index 85041aa0b..aed00673a 100644 --- a/apps/webapp/src/Components/Welcome.tsx +++ b/apps/webapp/src/Components/Welcome.tsx @@ -1,63 +1,7 @@ -import { Center } from '@mexit/shared' +import { Center, Height, StyledKeyCap, StyledTypography, Wave, WelcomeHeader } from '@mexit/shared' import React from 'react' -import styled, { keyframes, useTheme } from 'styled-components' -import { StyledKey } from '../Style/Shortcut' - -const WelcomeHeader = styled.section` - display: flex; - flex-direction: column; - align-items: center; - padding: 1rem 2rem; - background-color: ${({ theme }) => theme.colors.background}; - border-radius: 0.5rem; ; -` - -export const Height = styled.section` - display: flex; - flex-direction: column; -` - -export const StyledKeyCap = styled(StyledKey)` - padding: 4px 8px; - font-size: 0.8rem; -` - -export const StyledTypography = styled.div<{ margin: string; maxWidth: string; color: string; size: string }>` - margin: ${({ margin }) => margin}; - max-width: ${({ maxWidth }) => maxWidth}; - color: ${({ color }) => color}; - font-size: ${({ size }) => size}; -` - -const waveAnimation = keyframes` - 0% { transform: rotate(0.0deg) } - 10% { transform: rotate(12.0deg) } - 20% { transform: rotate(-7.0deg) } - 30% { transform: rotate(12.0deg) } - 40% { transform: rotate(-3.0deg) } - 50% { transform: rotate(9.0deg) } - 60% { transform: rotate(0.0deg) } - 100% { transform: rotate(0.0deg) } -` - -export const WaterWave = keyframes` - 0% { transform: translateY(1.2rem) rotateZ(0deg)} - 50% {transform: translateY(0.8rem) rotateZ(180deg)} - 100% {transform: translateY(0.6rem) rotateZ(360deg)} -` - -export const CompleteWave = keyframes` - 0% { transform: translateY(0.6rem) rotateZ(0deg)} - 50% {transform: translateY(0.3rem) rotateZ(180deg)} - 100% { transform: translateY(0rem) rotateZ(360deg)} - -` -const Wave = styled.span` - display: inline-block; - animation: ${waveAnimation} 2.5s infinite; - transform-origin: 70% 70%; -` +import { useTheme } from 'styled-components' const WelcomeSection = () => { const theme = useTheme() diff --git a/apps/webapp/src/Editor/Actions/withDraggables.tsx b/apps/webapp/src/Editor/Actions/withDraggables.tsx index 495d8218a..256ce9680 100644 --- a/apps/webapp/src/Editor/Actions/withDraggables.tsx +++ b/apps/webapp/src/Editor/Actions/withDraggables.tsx @@ -29,7 +29,7 @@ import checkboxBlankCircleLine from '@iconify-icons/ri/checkbox-blank-circle-lin import { ProfileImage } from '../../Components/User/ProfileImage' import useBlockStore from '../../Stores/useBlockStore' import { useEditorStore } from '../../Stores/useEditorStore' -import { RelativeTime } from '../../Components/RelativeTime' +import { RelativeTime } from '@mexit/shared' // import { RelativeTime } from '../../components/mex/RelativeTime' // import { ProfileImage } from '../../components/mex/User/ProfileImage' diff --git a/apps/webapp/src/Editor/Components/Combobox/BlockCombo.tsx b/apps/webapp/src/Editor/Components/Combobox/BlockCombo.tsx index 030980d0c..de7b1dd73 100644 --- a/apps/webapp/src/Editor/Components/Combobox/BlockCombo.tsx +++ b/apps/webapp/src/Editor/Components/Combobox/BlockCombo.tsx @@ -10,7 +10,7 @@ import { useComboboxStore } from '../../../Stores/useComboboxStore' import { replaceFragment } from '../../Hooks/useComboboxOnKeyDown' import { useSearch } from '../../../Hooks/useSearch' import { KEYBOARD_KEYS } from '../../constants' -import { ActionTitle, ComboboxShortcuts, ComboSeperator, StyledComboHeader, ShortcutText } from '../../Styles/Combobox' +import { ActionTitle, ComboboxShortcuts, ComboSeperator, StyledComboHeader, ShortcutText } from '@mexit/shared' import { getPathFromNodeIdHookless } from '../../../Hooks/useLinks' import { ComboboxItem, ItemTitle, ItemDesc, ItemCenterWrapper } from '../../Styles/TagCombobox.styles' import { PrimaryText } from '../../../Components/EditorInfobar/BlockInfobar' diff --git a/apps/webapp/src/Editor/Components/Combobox/index.tsx b/apps/webapp/src/Editor/Components/Combobox/index.tsx index 3b38db3b5..653a9abfb 100644 --- a/apps/webapp/src/Editor/Components/Combobox/index.tsx +++ b/apps/webapp/src/Editor/Components/Combobox/index.tsx @@ -4,8 +4,8 @@ import { PortalBody, useEditorState } from '@udecode/plate' import { Icon } from '@iconify/react' import useMergedRef from '@react-hook/merged-ref' -import { MexIcon } from '@mexit/shared' -import { NodeEditorContent } from '@mexit/core' +import { ComboboxItem, MexIcon, PreviewMeta } from '@mexit/shared' +import { mog, NodeEditorContent } from '@mexit/core' import { CategoryType, QuickLinkType } from '../../constants' import { useComboboxStore } from '../../../Stores/useComboboxStore' @@ -17,7 +17,6 @@ import { setElementPositionByRange } from '../../Utils/setElementPositionByRange import { ComboboxProps } from '../../Types/Combobox' import { ActionTitle, - ComboboxItem, ComboboxRoot, ComboboxShortcuts, ComboSeperator, @@ -26,14 +25,13 @@ import { ItemRightIcons, ItemTitle, ShortcutText -} from '../../Styles/Combobox' +} from '@mexit/shared' import { Shortcut } from '../../../Stores/useHelpStore' import { PrimaryText } from '../../../Components/EditorInfobar/BlockInfobar' import { DisplayShortcut } from '../../../Components/Shortcuts' import { replaceFragment } from '../../Hooks/useComboboxOnKeyDown' import EditorPreviewRenderer from '../../EditorPreviewRenderer' import BlockCombo from './BlockCombo' -import PreviewMeta from './PreviewMeta' export const spotlightShortcuts = { save: { @@ -161,7 +159,6 @@ export const Combobox = ({ onSelectItem, onRenderItem, isSlash, portalElement }: if (type === QuickLinkType.backlink) { const nodeContent = getContent(key) - console.log('nodeContent', nodeContent) content = nodeContent?.content setMetaData(nodeContent?.metadata) diff --git a/apps/webapp/src/Editor/Components/QuickLink/QuickLinkElement.tsx b/apps/webapp/src/Editor/Components/QuickLink/QuickLinkElement.tsx index b42ed396f..b8d4c616f 100644 --- a/apps/webapp/src/Editor/Components/QuickLink/QuickLinkElement.tsx +++ b/apps/webapp/src/Editor/Components/QuickLink/QuickLinkElement.tsx @@ -16,7 +16,7 @@ import { useLinks } from '../../../Hooks/useLinks' import { useNodes } from '../../../Hooks/useNodes' import { useOnMouseClick } from '../../../Hooks/useOnMouseClick' import { useContentStore } from '../../../Stores/useContentStore' -import { SILinkRoot, SILink } from '../../Styles/QuickLinkElement' +import { SILinkRoot, SILink } from './styled' import { ILinkElementProps } from '../../Types/QuickLink' import EditorPreview from '../EditorPreview/EditorPreview' import { getBlock } from '../../../Utils/parseData' diff --git a/apps/webapp/src/Editor/Styles/QuickLinkElement.ts b/apps/webapp/src/Editor/Components/QuickLink/styled.ts similarity index 100% rename from apps/webapp/src/Editor/Styles/QuickLinkElement.ts rename to apps/webapp/src/Editor/Components/QuickLink/styled.ts diff --git a/apps/webapp/src/Editor/EditorPreviewRenderer.tsx b/apps/webapp/src/Editor/EditorPreviewRenderer.tsx index e3750fcd0..764a3a3a1 100644 --- a/apps/webapp/src/Editor/EditorPreviewRenderer.tsx +++ b/apps/webapp/src/Editor/EditorPreviewRenderer.tsx @@ -1,26 +1,12 @@ import React, { useEffect, useMemo } from 'react' import styled from 'styled-components' -import { ComboboxItem, ComboboxType, EditorStyles, FadeContainer } from '@mexit/shared' +import { EditorStyles, FadeContainer, TodoContainer } from '@mexit/shared' import { useBlockHighlightStore, useFocusBlock } from '../Stores/useFocusBlock' import { useEditorChange } from '@mexit/shared' -import MexEditor from './MexEditor' -import { TodoContainer } from '../Style/Todo.style' -import generatePlugins from './Plugins' import { editorPreviewComponents } from './Components/EditorPreviewComponents' -import { useRouting } from '../Hooks/useRouting' -import { useSnippets } from '../Hooks/useSnippets' -import { useDataStore } from '../Stores/useDataStore' -import { QuickLinkComboboxItem } from './Components/QuickLink/QuickLinkComboboxItem' -import { SlashComboboxItem } from './Components/SlashCommands/SlashComboboxItem' -import { TagComboboxItem } from './Components/Tags/TagComboboxItem' -import { QuickLinkType, CategoryType } from './constants' -import { ELEMENT_INLINE_BLOCK, ELEMENT_TAG, ELEMENT_ILINK, ELEMENT_MEDIA_EMBED, ELEMENT_TABLE } from './elements' -import { ComboboxKey } from './Types/Combobox' -import { ComboConfigData, ComboboxConfig } from './Types/MultiCombobox' -import { useEditorStore } from '../Stores/useEditorStore' import { Plate, PlatePlugin } from '@udecode/plate' import useMemoizedPlugins from './Plugins' diff --git a/apps/webapp/src/Editor/Plugins/options.tsx b/apps/webapp/src/Editor/Plugins/options.tsx index db4c03e11..1336c0fd4 100644 --- a/apps/webapp/src/Editor/Plugins/options.tsx +++ b/apps/webapp/src/Editor/Plugins/options.tsx @@ -38,7 +38,7 @@ import { } from '@udecode/plate' import { generateTempId } from '@mexit/core' -import { uploadImageToWDCDN } from '../../Utils/uploadToCDN' +import { uploadImageToWDCDN } from '@mexit/shared' const preFormat = (editor: TEditor) => unwrapList(editor as PlateEditor) diff --git a/apps/webapp/src/Style/Todo.tsx b/apps/webapp/src/Style/Todo.tsx index 395d9d9b9..25a4bd2f5 100644 --- a/apps/webapp/src/Style/Todo.tsx +++ b/apps/webapp/src/Style/Todo.tsx @@ -1,8 +1,7 @@ -import { MainHeader } from '@mexit/shared' +import { MainHeader, TodoContainer } from '@mexit/shared' import { mix, transparentize } from 'polished' import styled, { css } from 'styled-components' import { Title } from './Elements' -import { TodoContainer } from './Todo.style' /* * Todos diff --git a/apps/webapp/src/Themes/neoDark.custom.tsx b/apps/webapp/src/Themes/neoDark.custom.tsx index 08b6684dc..ccbf93bde 100644 --- a/apps/webapp/src/Themes/neoDark.custom.tsx +++ b/apps/webapp/src/Themes/neoDark.custom.tsx @@ -1,9 +1,9 @@ -import { EditorStyles, Widget, MenuTrigger } from '@mexit/shared' +import { EditorStyles, Widget, MenuTrigger, TodoContainer } from '@mexit/shared' import { transparentize } from 'polished' import { css } from 'styled-components' import { BalloonToolbarBase } from '../Components/Editor/BalloonToolbar' import { DataInfobarWrapper } from '../Components/Infobar/DataInfobar' -import { EditorPreviewWrapper } from "../Editor/Components/EditorPreview/EditorPreview.styles" +import { EditorPreviewWrapper } from '../Editor/Components/EditorPreview/EditorPreview.styles' import { BackCard } from '../Style/Card' import { ComboboxRoot, ComboboxItem } from '../Style/Combobox' import { Title } from '../Style/Elements' @@ -13,7 +13,6 @@ import { NavWrapper, NavButton } from '../Style/Nav' import { Result, ResultHeader, SearchContainer, SplitSearchPreviewWrapper } from '../Style/Search' import { SidebarDiv } from '../Style/Sidebar' import { CreateSnippet } from '../Style/Snippets' -import { TodoContainer } from '../Style/Todo.style' import { ArchivedNode } from '../Views/Archive' import { SettingsOptions, SettingTitle } from '../Views/Settings' import { SpaceBlocksCss } from './spaceBlocks' diff --git a/apps/webapp/src/Themes/neoLight.custom.tsx b/apps/webapp/src/Themes/neoLight.custom.tsx index ab691be41..9d82624ce 100644 --- a/apps/webapp/src/Themes/neoLight.custom.tsx +++ b/apps/webapp/src/Themes/neoLight.custom.tsx @@ -1,4 +1,4 @@ -import { MenuTrigger, EditorStyles, Widget } from '@mexit/shared' +import { MenuTrigger, EditorStyles, Widget, TodoContainer } from '@mexit/shared' import { transparentize } from 'polished' import { css } from 'styled-components' @@ -14,7 +14,6 @@ import { NavWrapper, NavButton } from '../Style/Nav' import { Result, ResultHeader, SearchContainer, SplitSearchPreviewWrapper } from '../Style/Search' import { SidebarDiv } from '../Style/Sidebar' import { CreateSnippet } from '../Style/Snippets' -import { TodoContainer } from '../Style/Todo.style' import { ArchivedNode } from '../Views/Archive' import { SettingsOptions, SettingTitle } from '../Views/Settings' import { SpaceBlocksCss } from './spaceBlocks' diff --git a/apps/webapp/src/Themes/spaceBlocks.tsx b/apps/webapp/src/Themes/spaceBlocks.tsx index 2d77090e9..32375fee0 100644 --- a/apps/webapp/src/Themes/spaceBlocks.tsx +++ b/apps/webapp/src/Themes/spaceBlocks.tsx @@ -1,4 +1,4 @@ -import { StyledEditor, NodeInfo, EditorWrapper, EditorStyles } from '@mexit/shared' +import { StyledEditor, NodeInfo, EditorWrapper, EditorStyles, TodoContainer } from '@mexit/shared' import { transparentize } from 'polished' import { css, DefaultTheme, FlattenInterpolation, ThemeProps } from 'styled-components' @@ -13,7 +13,6 @@ import { Result, SearchFilterListWrap } from '../Style/Search' import { SidebarDiv } from '../Style/Sidebar' import { SSnippet, CreateSnippet } from '../Style/Snippets' import { StyledBoard } from '../Style/Todo' -import { TodoContainer } from '../Style/Todo.style' import { ArchivedNode } from '../Views/Archive' import { SettingsOptions } from '../Views/Settings' diff --git a/apps/webapp/src/Themes/vertigoTheme.custom.tsx b/apps/webapp/src/Themes/vertigoTheme.custom.tsx index 2607e7ec6..9fa7f150f 100644 --- a/apps/webapp/src/Themes/vertigoTheme.custom.tsx +++ b/apps/webapp/src/Themes/vertigoTheme.custom.tsx @@ -1,7 +1,7 @@ import { transparentize } from 'polished' import { css } from 'styled-components' -import { EditorStyles, MenuTrigger, Widget } from '@mexit/shared' +import { EditorStyles, MenuTrigger, TodoContainer, Widget } from '@mexit/shared' import { BalloonToolbarBase } from '../Components/Editor/BalloonToolbar' import { DataInfobarWrapper } from '../Components/Infobar/DataInfobar' @@ -14,7 +14,6 @@ import { NavWrapper, NavButton } from '../Style/Nav' import { Result, ResultHeader, SearchContainer, SplitSearchPreviewWrapper } from '../Style/Search' import { SidebarDiv } from '../Style/Sidebar' import { CreateSnippet } from '../Style/Snippets' -import { TodoContainer } from '../Style/Todo.style' import { ArchivedNode } from '../Views/Archive' import { SettingsOptions, SettingTitle } from '../Views/Settings' import { SpaceBlocksCss } from './spaceBlocks' diff --git a/apps/webapp/src/Editor/Components/Combobox/PreviewMeta.tsx b/libs/shared/src/Components/PreviewMeta.tsx similarity index 89% rename from apps/webapp/src/Editor/Components/Combobox/PreviewMeta.tsx rename to libs/shared/src/Components/PreviewMeta.tsx index 5d78c804e..245aa7ff7 100644 --- a/apps/webapp/src/Editor/Components/Combobox/PreviewMeta.tsx +++ b/libs/shared/src/Components/PreviewMeta.tsx @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import { RelativeTime } from '../../../Components/RelativeTime' +import { RelativeTime } from './RelativeTime' export const Data = styled.div` color: ${({ theme }) => theme.colors.text.fade}; @@ -39,4 +39,4 @@ const PreviewMeta: React.FC = ({ meta }) => { ) } -export default PreviewMeta +export { PreviewMeta } diff --git a/apps/webapp/src/Components/RelativeTime.tsx b/libs/shared/src/Components/RelativeTime.tsx similarity index 100% rename from apps/webapp/src/Components/RelativeTime.tsx rename to libs/shared/src/Components/RelativeTime.tsx diff --git a/apps/webapp/src/Hooks/useRelativeTime.tsx b/libs/shared/src/Hooks/useRelativeTime.tsx similarity index 100% rename from apps/webapp/src/Hooks/useRelativeTime.tsx rename to libs/shared/src/Hooks/useRelativeTime.tsx diff --git a/apps/webapp/src/Editor/Styles/Combobox.tsx b/libs/shared/src/Style/Combobox.tsx similarity index 98% rename from apps/webapp/src/Editor/Styles/Combobox.tsx rename to libs/shared/src/Style/Combobox.tsx index 51f5decce..e1d746c84 100644 --- a/apps/webapp/src/Editor/Styles/Combobox.tsx +++ b/libs/shared/src/Style/Combobox.tsx @@ -1,6 +1,5 @@ import styled, { css } from 'styled-components' - -import { Button } from '@mexit/shared' +import { Button } from './Buttons' export const ComboboxItem = styled.div` display: flex; @@ -62,7 +61,7 @@ export const ComboboxRoot = styled.div<{ isOpen: boolean }>` background: none !important; display: flex; margin: 0; - z-index: 11; + z-index: 9999999998; height: fit-content; > div { diff --git a/apps/webapp/src/Style/Shortcut.tsx b/libs/shared/src/Style/Shortcut.tsx similarity index 100% rename from apps/webapp/src/Style/Shortcut.tsx rename to libs/shared/src/Style/Shortcut.tsx diff --git a/apps/webapp/src/Style/Todo.style.tsx b/libs/shared/src/Style/Todo.style.tsx similarity index 97% rename from apps/webapp/src/Style/Todo.style.tsx rename to libs/shared/src/Style/Todo.style.tsx index 81db82763..d475d9b35 100644 --- a/apps/webapp/src/Style/Todo.style.tsx +++ b/libs/shared/src/Style/Todo.style.tsx @@ -1,7 +1,7 @@ import styled, { css } from 'styled-components' import { transparentize } from 'polished' import { TodoStatus } from '@mexit/core' -import { WaterWave, CompleteWave } from '../Components/Welcome' +import { CompleteWave, WaterWave } from './Welcome' export const TodoContainer = styled.div<{ checked?: boolean }>` display: flex; diff --git a/libs/shared/src/Style/Welcome.ts b/libs/shared/src/Style/Welcome.ts new file mode 100644 index 000000000..8e3c13fad --- /dev/null +++ b/libs/shared/src/Style/Welcome.ts @@ -0,0 +1,57 @@ +import styled, { keyframes } from 'styled-components' +import { StyledKey } from './Shortcut' + +export const WelcomeHeader = styled.section` + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem 2rem; + background-color: ${({ theme }) => theme.colors.background}; + border-radius: 0.5rem; ; +` + +export const Height = styled.section` + display: flex; + flex-direction: column; +` + +export const StyledKeyCap = styled(StyledKey)` + padding: 4px 8px; + font-size: 0.8rem; +` + +export const StyledTypography = styled.div<{ margin: string; maxWidth: string; color: string; size: string }>` + margin: ${({ margin }) => margin}; + max-width: ${({ maxWidth }) => maxWidth}; + color: ${({ color }) => color}; + font-size: ${({ size }) => size}; +` + +export const waveAnimation = keyframes` + 0% { transform: rotate(0.0deg) } + 10% { transform: rotate(12.0deg) } + 20% { transform: rotate(-7.0deg) } + 30% { transform: rotate(12.0deg) } + 40% { transform: rotate(-3.0deg) } + 50% { transform: rotate(9.0deg) } + 60% { transform: rotate(0.0deg) } + 100% { transform: rotate(0.0deg) } +` + +export const WaterWave = keyframes` + 0% { transform: translateY(1.2rem) rotateZ(0deg)} + 50% {transform: translateY(0.8rem) rotateZ(180deg)} + 100% {transform: translateY(0.6rem) rotateZ(360deg)} +` + +export const CompleteWave = keyframes` + 0% { transform: translateY(0.6rem) rotateZ(0deg)} + 50% {transform: translateY(0.3rem) rotateZ(180deg)} + 100% { transform: translateY(0rem) rotateZ(360deg)} + +` +export const Wave = styled.span` + display: inline-block; + animation: ${waveAnimation} 2.5s infinite; + transform-origin: 70% 70%; +` diff --git a/apps/webapp/src/Utils/uploadToCDN.ts b/libs/shared/src/Utils/uploadToCDN.ts similarity index 100% rename from apps/webapp/src/Utils/uploadToCDN.ts rename to libs/shared/src/Utils/uploadToCDN.ts diff --git a/libs/shared/src/index.ts b/libs/shared/src/index.ts index 1c214eef2..709f052a9 100644 --- a/libs/shared/src/index.ts +++ b/libs/shared/src/index.ts @@ -1,31 +1,39 @@ export * from './Components/Icons' export * from './Components/Link' export * from './Components/Notification' +export * from './Components/PreviewMeta' +export * from './Components/RelativeTime' export * from './Components/ToggleButton' export * from './Components/Tooltips' export * from './Components/MediaEmbed' export * from './Components/TableWrapper' export * from './Hooks/useEditorActions' +export * from './Hooks/useRelativeTime' export * from './Stores/themeStoreConstructor' export * from './Stores/dataStoreConstructor' export * from './Style/Buttons' -export * from './Style/Form' +export * from './Style/Card' +export * from './Style/Combobox' export * from './Style/Editor' +export * from './Style/fade' +export * from './Style/Form' export * from './Style/Helpers' -export * from './Style/Normalize' -export * from './Style/Card' export * from './Style/Layouts' -export * from './Style/Typography' -export * from './Style/Toolbar' export * from './Style/Loading' -export * from './Style/fade' +export * from './Style/Normalize' +export * from './Style/Shortcut' +export * from './Style/Todo.style' +export * from './Style/Toolbar' +export * from './Style/Tooltip' +export * from './Style/Typography' +export * from './Style/Welcome' export * from './Themes' -export * from './Types/Combobox' +// export * from './Types/Combobox' export * from './Types/Tree' export * from './Types/Theme' @@ -37,3 +45,4 @@ export * from './Utils/shortcuts' export * from './Utils/tabInfo' export * from './Utils/themeGenerator' export * from './Utils/treeUtils' +export * from './Utils/uploadToCDN'