diff --git a/README.md b/README.md index 309436d..6f03a95 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ - structural helper utils and mdast typing extensions ```shell -npm i -S @content-ui/md @content-ui/react @content-ui/md-mui +npm i -S @content-ui/md @content-ui/react @content-ui/struct @content-ui/md-mui # peer-dependencies: npm i -S @mui/material @mui/icons-material npm i -D @types/mdast # input component with CodeMirror: -npm i -S @content-ui/md @content-ui/react @content-ui/md-mui @content-ui/input +npm i -S @content-ui/md @content-ui/react @content-ui/struct @content-ui/md-mui @content-ui/input # peer-dependencies: npm i -S react-progress-state @ui-controls/progress @ui-schema/kit-codemirror @codemirror/state ``` diff --git a/apps/demo/src/App.tsx b/apps/demo/src/App.tsx index 3c038f9..e45fad3 100644 --- a/apps/demo/src/App.tsx +++ b/apps/demo/src/App.tsx @@ -1,4 +1,4 @@ -import { contentUIDecorators, ContentLeafsProvider } from '@content-ui/react/ContentLeaf' +import { contentUIDecorators, ContentLeafsProvider } from '@content-ui/react/ContentLeafsContext' import React from 'react' import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles' import { DndProvider } from 'react-dnd' diff --git a/apps/demo/src/components/ContentUI.ts b/apps/demo/src/components/ContentUI.ts index 949b384..45d9034 100644 --- a/apps/demo/src/components/ContentUI.ts +++ b/apps/demo/src/components/ContentUI.ts @@ -1,5 +1,5 @@ import { MuiContentRenderComponents, renderMapping } from '@content-ui/md-mui/LeafsMarkdown' -import { ContentLeafsNodeMapping, LeafsRenderMapping, ContentLeafMatchParams } from '@content-ui/react/ContentLeaf' +import { ContentLeafsNodeMapping, LeafsRenderMapping, ContentLeafMatchParams } from '@content-ui/react/ContentLeafsContext' import { CustomCodeMirror } from './CustomCodeMirror.js' export const contentUIMapping: LeafsRenderMapping = { diff --git a/apps/demo/src/components/CustomCodeMirror.tsx b/apps/demo/src/components/CustomCodeMirror.tsx index a8bd266..0770303 100644 --- a/apps/demo/src/components/CustomCodeMirror.tsx +++ b/apps/demo/src/components/CustomCodeMirror.tsx @@ -19,7 +19,7 @@ import { lintKeymap } from '@codemirror/lint' import { Compartment, EditorState, Extension } from '@codemirror/state' import { CodeMirrorComponentProps, CodeMirrorProps } from '@ui-schema/kit-codemirror/CodeMirror' import { EditorThemeCustomStyles, useEditorTheme } from '@ui-schema/material-code/useEditorTheme' -import { useHighlightStyle } from './useHighlightStyle' +import { useHighlightStyle } from '@ui-schema/material-code/useHighlightStyle' import { json } from '@codemirror/lang-json' import { javascript } from '@codemirror/lang-javascript' import { html } from '@codemirror/lang-html' @@ -257,7 +257,7 @@ export const CustomCodeMirror: React.FC = ( }), [palette.mode, paddingBottom]) const theme = useEditorTheme(typeof onChange === 'undefined', dense, customVariant, customStyles as EditorThemeCustomStyles) - const highlightStyle = useHighlightStyle() + const highlightStyle = useHighlightStyle({headlineUnderline: false}) const {init: initHighlightExt, effects: effectsHighlightExt} = useExtension( () => syntaxHighlighting(highlightStyle || defaultHighlightStyle, {fallback: true}), [highlightStyle], diff --git a/apps/demo/src/components/CustomWidgets/WidgetMarkdownEditor.tsx b/apps/demo/src/components/CustomWidgets/WidgetMarkdownEditor.tsx index effb955..cf3018b 100644 --- a/apps/demo/src/components/CustomWidgets/WidgetMarkdownEditor.tsx +++ b/apps/demo/src/components/CustomWidgets/WidgetMarkdownEditor.tsx @@ -1,4 +1,5 @@ import { ContentParser } from '@content-ui/md/parser/ContentParser' +import { ContentSelectionProvider } from '@content-ui/react/ContentSelectionContext' import { UIMetaReadContextType } from '@ui-schema/ui-schema/UIMetaReadContext' import React from 'react' import { TransTitle, WidgetProps, WithScalarValue } from '@ui-schema/ui-schema' @@ -15,7 +16,7 @@ import { CustomCodeMirror, getHighlight } from '../CustomCodeMirror' import { ContentInput } from '@content-ui/input/ContentInput' import { useContentEditor } from '@content-ui/input/useContentEditor' import { useContent } from '@content-ui/react/useContent' -import { ContentFileProvider } from '@content-ui/react/ContentFileProvider' +import { ContentFileProvider } from '@content-ui/react/ContentFileContext' export const WidgetMarkdownEditor: React.ComponentType = ( { @@ -118,22 +119,23 @@ export const WidgetMarkdownEditor: React.ComponentType - + + + diff --git a/apps/demo/src/components/useHighlightStyle.ts b/apps/demo/src/components/useHighlightStyle.ts deleted file mode 100644 index 7102746..0000000 --- a/apps/demo/src/components/useHighlightStyle.ts +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react' -import { tags } from '@lezer/highlight' -import { HighlightStyle } from '@codemirror/language' -import { useTheme } from '@mui/material/styles' - -export const useHighlightStyle = (): HighlightStyle => { - const {palette} = useTheme() - return React.useMemo(() => HighlightStyle.define([ - { - tag: tags.link, - textDecoration: 'underline', - }, - { - tag: tags.heading, - // textDecoration: 'underline', - color: palette.mode === 'dark' ? '#e4e7e8' : '#011d24', - fontWeight: 'bold', - }, - { - tag: [tags.meta], - color: palette.mode === 'dark' ? '#57b1a8' : '#008074', - }, - { - tag: tags.emphasis, - fontStyle: 'italic', - }, - { - tag: tags.strong, - fontWeight: 'bold', - }, - { - tag: tags.strikethrough, - textDecoration: 'line-through', - }, - { - tag: tags.keyword, - color: palette.mode === 'dark' ? '#d55d9b' : '#c232ab', - }, - { - tag: [tags.atom, tags.bool, tags.null, tags.url, tags.contentSeparator, tags.labelName], - // tags.operator, - color: palette.mode === 'dark' ? '#b167e4' : '#851cce', - }, - { - tag: [tags.literal], // numbers in json+yaml - // tag: [tags.literal, tags.inserted], - color: palette.mode === 'dark' ? '#73a3ce' : '#125f77', - }, - { - tag: [tags.inserted], - // tag: [tags.literal, tags.inserted], - color: palette.mode === 'dark' ? '#1a9544' : '#068248', - }, - { - tag: [tags.deleted], - color: palette.mode === 'dark' ? '#d22c2c' : '#aa1111', - }, - { - tag: [tags.brace], - color: palette.text.secondary, - }, - { - tag: [tags.bracket], - color: palette.mode === 'dark' ? '#608bb1' : '#22758f', - }, - { - tag: [tags.string], - color: palette.mode === 'dark' ? '#83ca69' : '#067326', - }, - { - tag: [tags.regexp, tags.escape, tags.special(tags.string)], - color: palette.mode === 'dark' ? '#ec7242' : '#ee4400', - }, - { - tag: [ - tags.definition(tags.variableName), - // e.g. sass-vars - tags.special(tags.variableName), - tags.variableName, - tags.attributeName, - ], - color: palette.mode === 'dark' ? '#6789ec' : '#1a3ab9', - }, - { - tag: tags.local(tags.variableName), - color: '#3300aa', - }, - { - tag: [tags.typeName, tags.namespace], - color: palette.mode === 'dark' ? '#41aea4' : '#008074', - // color: palette.mode === 'dark' ? '#ec4837' : '#b7382b', - }, - { - tag: tags.className, - // color: '#116677', - color: palette.mode === 'dark' ? '#388c83' : '#207e75', - }, - { - tag: [tags.macroName], - color: '#225566', - }, - /*{ - tag: tags.definition(tags.propertyName), - color: '#0000cc', - },*/ - { - tag: [ - tags.comment, - // tags.blockComment, - ], - color: palette.mode === 'dark' ? '#738284' : '#6b7677', - // backgroundColor: palette.mode === 'dark' ? '#738284' : '#6b7677', - }, - // { - // tag: [ - // tags.blockComment, - // ], - // opacity: 0.75, - // '&:hover, &:active, &:focus': { - // opacity: 1, - // }, - // // backgroundColor: palette.mode === 'dark' ? '#1f2626' : '#e1ebec', - // // display: 'block', - // }, - { - tag: tags.invalid, - color: palette.error.main, - // color: '#ff0000', - }, - ]), [palette]) -} diff --git a/apps/demo/src/pages/PageComplex.tsx b/apps/demo/src/pages/PageComplex.tsx index 289b5d9..42fd095 100644 --- a/apps/demo/src/pages/PageComplex.tsx +++ b/apps/demo/src/pages/PageComplex.tsx @@ -1,4 +1,5 @@ import { ContentParser } from '@content-ui/md/parser/ContentParser' +import { SettingsProvider } from '@content-ui/react/LeafSettings' import Paper from '@mui/material/Paper' import React from 'react' import Helmet from 'react-helmet' @@ -122,12 +123,18 @@ export const PageComplex: React.ComponentType = () => { {contentDetails?.file ? - + + + : null} diff --git a/apps/demo/src/pages/PageInput.tsx b/apps/demo/src/pages/PageInput.tsx index a771513..20b2ea1 100644 --- a/apps/demo/src/pages/PageInput.tsx +++ b/apps/demo/src/pages/PageInput.tsx @@ -1,4 +1,5 @@ import { ContentParser } from '@content-ui/md/parser/ContentParser' +import { ContentSelectionProvider } from '@content-ui/react/ContentSelectionContext' import useMediaQuery from '@mui/material/useMediaQuery' import Button from '@mui/material/Button' import IcVisibility from '@mui/icons-material/Visibility' @@ -15,7 +16,7 @@ import { Viewer } from '@content-ui/md-mui/Viewer' import { SettingsProvider } from '@content-ui/react/LeafSettings' import { useContentEditor } from '@content-ui/input/useContentEditor' import { useContent } from '@content-ui/react/useContent' -import { ContentFileProvider } from '@content-ui/react/ContentFileProvider' +import { ContentFileProvider } from '@content-ui/react/ContentFileContext' const md = `# About a Note @@ -97,60 +98,62 @@ export const PageInput: React.ComponentType = () => { - - - - - - - - - {showAst ? - : null} + + + {showAst ? + : null} + - - + + diff --git a/apps/demo/tests/PageHome.test.tsx b/apps/demo/tests/PageHome.test.tsx index 1377d30..9d60d8f 100644 --- a/apps/demo/tests/PageHome.test.tsx +++ b/apps/demo/tests/PageHome.test.tsx @@ -1,7 +1,7 @@ /** * @jest-environment jsdom */ -import { ContentLeafsProvider, contentUIDecorators } from '@content-ui/react/ContentLeaf' +import { ContentLeafsProvider, contentUIDecorators } from '@content-ui/react/ContentLeafsContext' import { it, expect, describe } from '@jest/globals' import '@testing-library/jest-dom/jest-globals' import { render, screen } from '@testing-library/react' diff --git a/apps/sandbox/src/components/CustomCodeMirror.tsx b/apps/sandbox/src/components/CustomCodeMirror.tsx index cc24c65..21e3624 100644 --- a/apps/sandbox/src/components/CustomCodeMirror.tsx +++ b/apps/sandbox/src/components/CustomCodeMirror.tsx @@ -50,7 +50,7 @@ import { EditorThemeCustomStyles, useEditorTheme, } from "@ui-schema/material-code/useEditorTheme"; -import { useHighlightStyle } from "./useHighlightStyle"; +import { useHighlightStyle } from "@ui-schema/material-code/useHighlightStyle"; import { json } from "@codemirror/lang-json"; import { javascript } from "@codemirror/lang-javascript"; import { html } from "@codemirror/lang-html"; @@ -343,7 +343,7 @@ export const CustomCodeMirror: React.FC = ({ customStyles as EditorThemeCustomStyles, ); - const highlightStyle = useHighlightStyle(); + const highlightStyle = useHighlightStyle({headlineUnderline: false}); const { init: initHighlightExt, effects: effectsHighlightExt } = useExtension( () => syntaxHighlighting(highlightStyle || defaultHighlightStyle, { diff --git a/apps/sandbox/src/components/useHighlightStyle.ts b/apps/sandbox/src/components/useHighlightStyle.ts deleted file mode 100644 index 9d15b99..0000000 --- a/apps/sandbox/src/components/useHighlightStyle.ts +++ /dev/null @@ -1,142 +0,0 @@ -import React from "react"; -import { tags } from "@lezer/highlight"; -import { HighlightStyle } from "@codemirror/language"; -import useTheme from "@mui/material/styles/useTheme"; - -export const useHighlightStyle = (): HighlightStyle => { - const { palette } = useTheme(); - return React.useMemo( - () => - HighlightStyle.define([ - { - tag: tags.link, - textDecoration: "underline", - }, - { - tag: tags.heading, - // textDecoration: 'underline', - color: palette.mode === "dark" ? "#e4e7e8" : "#011d24", - fontWeight: "bold", - }, - { - tag: [tags.meta], - color: palette.mode === "dark" ? "#57b1a8" : "#008074", - }, - { - tag: tags.emphasis, - fontStyle: "italic", - }, - { - tag: tags.strong, - fontWeight: "bold", - }, - { - tag: tags.strikethrough, - textDecoration: "line-through", - }, - { - tag: tags.keyword, - color: palette.mode === "dark" ? "#d55d9b" : "#c232ab", - }, - { - tag: [ - tags.atom, - tags.bool, - tags.null, - tags.url, - tags.contentSeparator, - tags.labelName, - ], - // tags.operator, - color: palette.mode === "dark" ? "#b167e4" : "#851cce", - }, - { - tag: [tags.literal], // numbers in json+yaml - // tag: [tags.literal, tags.inserted], - color: palette.mode === "dark" ? "#73a3ce" : "#125f77", - }, - { - tag: [tags.inserted], - // tag: [tags.literal, tags.inserted], - color: palette.mode === "dark" ? "#1a9544" : "#068248", - }, - { - tag: [tags.deleted], - color: palette.mode === "dark" ? "#d22c2c" : "#aa1111", - }, - { - tag: [tags.brace], - color: palette.text.secondary, - }, - { - tag: [tags.bracket], - color: palette.mode === "dark" ? "#608bb1" : "#22758f", - }, - { - tag: [tags.string], - color: palette.mode === "dark" ? "#83ca69" : "#067326", - }, - { - tag: [tags.regexp, tags.escape, tags.special(tags.string)], - color: palette.mode === "dark" ? "#ec7242" : "#ee4400", - }, - { - tag: [ - tags.definition(tags.variableName), - // e.g. sass-vars - tags.special(tags.variableName), - tags.variableName, - tags.attributeName, - ], - color: palette.mode === "dark" ? "#6789ec" : "#1a3ab9", - }, - { - tag: tags.local(tags.variableName), - color: "#3300aa", - }, - { - tag: [tags.typeName, tags.namespace], - color: palette.mode === "dark" ? "#41aea4" : "#008074", - // color: palette.mode === 'dark' ? '#ec4837' : '#b7382b', - }, - { - tag: tags.className, - // color: '#116677', - color: palette.mode === "dark" ? "#388c83" : "#207e75", - }, - { - tag: [tags.macroName], - color: "#225566", - }, - /*{ - tag: tags.definition(tags.propertyName), - color: '#0000cc', - },*/ - { - tag: [ - tags.comment, - // tags.blockComment, - ], - color: palette.mode === "dark" ? "#738284" : "#6b7677", - // backgroundColor: palette.mode === 'dark' ? '#738284' : '#6b7677', - }, - // { - // tag: [ - // tags.blockComment, - // ], - // opacity: 0.75, - // '&:hover, &:active, &:focus': { - // opacity: 1, - // }, - // // backgroundColor: palette.mode === 'dark' ? '#1f2626' : '#e1ebec', - // // display: 'block', - // }, - { - tag: tags.invalid, - color: palette.error.main, - // color: '#ff0000', - }, - ]), - [palette], - ); -}; diff --git a/apps/sandbox/src/main.tsx b/apps/sandbox/src/main.tsx index 3134e41..2caa75a 100644 --- a/apps/sandbox/src/main.tsx +++ b/apps/sandbox/src/main.tsx @@ -5,7 +5,7 @@ import { LeafsRenderMapping, ContentLeafsNodeMapping, ContentLeafMatchParams, -} from '@content-ui/react/ContentLeaf' +} from '@content-ui/react/ContentLeafsContext' import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles' import CssBaseline from '@mui/material/CssBaseline' import { MuiContentRenderComponents, renderMapping } from '@content-ui/md-mui/LeafsMarkdown' diff --git a/apps/sandbox/src/pages/PageInput.tsx b/apps/sandbox/src/pages/PageInput.tsx index cdeb437..89cd99f 100644 --- a/apps/sandbox/src/pages/PageInput.tsx +++ b/apps/sandbox/src/pages/PageInput.tsx @@ -1,16 +1,17 @@ -import { ContentParser } from "@content-ui/md/parser/ContentParser"; -import React from "react"; -import Grid2 from "@mui/material/Unstable_Grid2"; -import { ContentInput } from "@content-ui/input/ContentInput"; -import { CustomCodeMirror, getHighlight } from "../components/CustomCodeMirror"; -import Box from "@mui/material/Box"; -import { Viewer } from "@content-ui/md-mui/Viewer"; -import { SettingsProvider } from "@content-ui/react/LeafSettings"; -import { useContentEditor } from "@content-ui/input/useContentEditor"; -import { useContent } from "@content-ui/react/useContent"; -import { ContentFileProvider } from "@content-ui/react/ContentFileProvider"; -import useTheme from "@mui/material/styles/useTheme"; -import { useMediaQuery } from "@mui/material"; +import { ContentParser } from '@content-ui/md/parser/ContentParser' +import { ContentSelectionProvider } from '@content-ui/react/ContentSelectionContext' +import React from 'react' +import Grid2 from '@mui/material/Unstable_Grid2' +import { ContentInput } from '@content-ui/input/ContentInput' +import { CustomCodeMirror, getHighlight } from '../components/CustomCodeMirror' +import Box from '@mui/material/Box' +import { Viewer } from '@content-ui/md-mui/Viewer' +import { SettingsProvider } from '@content-ui/react/LeafSettings' +import { useContentEditor } from '@content-ui/input/useContentEditor' +import { useContent } from '@content-ui/react/useContent' +import { ContentFileProvider } from '@content-ui/react/ContentFileContext' +import useTheme from '@mui/material/styles/useTheme' +import { useMediaQuery } from '@mui/material' const md = `# About a Note @@ -44,107 +45,109 @@ With even more sentences, words and other things. | 6pm | Evening | | 10pm | Night | -`; +` export const PageInput: React.ComponentType = () => { - const [value, setValue] = React.useState(md); - const { breakpoints } = useTheme(); - const isMediumScreen = useMediaQuery(breakpoints.up("md")); - const { - textValue, - handleOnChange, - editorSelection, - bigSize, - autoProcess, - setAutoProcess, - } = useContentEditor(typeof value === "string" ? value : "", setValue); - const { processing, outdated, root, file } = useContent({ - textValue, - // for direct preview, the parseDelay should be as low as possible, - // with disabled preview it's better to use `600` for less unnecessary processing - parseDelay: - textValue.length > 10000 - ? 460 - : textValue.length > 1200 - ? 160 - : textValue.length > 3500 - ? 280 - : 40, - autoProcess, - onMount: true, - processor: ContentParser, - }); + const [value, setValue] = React.useState(md) + const {breakpoints} = useTheme() + const isMediumScreen = useMediaQuery(breakpoints.up('md')) + const { + textValue, + handleOnChange, + editorSelection, + bigSize, + autoProcess, + setAutoProcess, + } = useContentEditor(typeof value === 'string' ? value : '', setValue) + const {processing, outdated, root, file} = useContent({ + textValue, + // for direct preview, the parseDelay should be as low as possible, + // with disabled preview it's better to use `600` for less unnecessary processing + parseDelay: + textValue.length > 10000 + ? 460 + : textValue.length > 1200 + ? 160 + : textValue.length > 3500 + ? 280 + : 40, + autoProcess, + onMount: true, + processor: ContentParser, + }) - const extensions = React.useMemo(() => { - const highlight = getHighlight("md"); - return [...(highlight ? [highlight] : [])]; - }, []); + const extensions = React.useMemo(() => { + const highlight = getHighlight('md') + return [...(highlight ? [highlight] : [])] + }, []) - return ( - <> - - - - + - - - - - - - - - - - - ); -}; + + + + + + + + + + + + + + + + + ) +} diff --git a/babelImportDefaultPlugin.js b/babelImportDefaultPlugin.js index c628e1e..6846c45 100644 --- a/babelImportDefaultPlugin.js +++ b/babelImportDefaultPlugin.js @@ -57,7 +57,6 @@ export default function({types: t}) { } function isLikelyCommonJS(source, state) { - // Log the check // Check if the source is from a package with type: module const isModulePackage = state.file.opts.filename.includes('node_modules') && state.file.opts.packageData diff --git a/packages/input/ContentInput.tsx b/packages/input/ContentInput.tsx index e131801..0ca6922 100644 --- a/packages/input/ContentInput.tsx +++ b/packages/input/ContentInput.tsx @@ -1,3 +1,4 @@ +import { useContentSelection } from '@content-ui/react/ContentSelectionContext' import React from 'react' import { CodeMirrorComponentProps } from '@ui-schema/kit-codemirror/CodeMirror' import { Extension } from '@codemirror/state' @@ -10,7 +11,8 @@ import { InputBottomBar } from '@content-ui/input/InputBottomBar' import { IconButtonProgress } from '@ui-controls/progress/IconButtonProgress' import IcAutoProcess from '@mui/icons-material/ModelTraining' import { CodeMirrorOnChange } from '@ui-schema/kit-codemirror/useCodeMirror' -import { useContentContext, useContentSelection, WithContent } from '@content-ui/react/useContent' +import { useContentContext } from '@content-ui/react/ContentFileContext' +import { WithContent } from '@content-ui/react/useContent' import { Viewer, ViewerProps } from '@content-ui/md-mui/Viewer' export interface ViewEditorProps extends Pick, Omit { @@ -52,7 +54,6 @@ export const ContentInput: React.ComponentType : > + editorSelection: ContentSelection | undefined + setEditorSelection: React.Dispatch> lines: number textValue: string @@ -19,7 +19,7 @@ export const useContentEditor = ( textValue: string, onChange: (newValue: string) => void, ): WithContentEditor => { - const [editorSelection, setEditorSelection] = React.useState(undefined) + const [editorSelection, setEditorSelection] = React.useState(undefined) const bigSize = textValue.length > 50000 const [autoProcess, setAutoProcess] = React.useState(bigSize ? 0 : -1) diff --git a/packages/md-mui/LeafChildNodes.tsx b/packages/md-mui/LeafChildNodes.tsx index 375f063..4d2a82b 100644 --- a/packages/md-mui/LeafChildNodes.tsx +++ b/packages/md-mui/LeafChildNodes.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Parents } from 'mdast' -import { useContentSelection } from '@content-ui/react/useContent' +import { useContentSelection } from '@content-ui/react/ContentSelectionContext' import { ContentLeaf } from '@content-ui/react/ContentLeaf' import { isLeafSelected } from '@content-ui/react/isLeafSelected' @@ -24,7 +24,7 @@ export const LeafChildNodes =

( elem={childNext.type} child={childNext} // todo: add support for multiple selections, e.g. multiple lines with unselected lines in between - selected={isLeafSelected(childNext.position, editorSelection?.startLine, editorSelection?.endLine)} + selected={editorSelection ? isLeafSelected(childNext.position, editorSelection.startLine, editorSelection.endLine) : false} isFirst={i === 0} isLast={i === length - 1} />, diff --git a/packages/md-mui/Leafs/HTMLLeafs.tsx b/packages/md-mui/Leafs/HTMLLeafs.tsx index ca8ed5b..bf725a0 100644 --- a/packages/md-mui/Leafs/HTMLLeafs.tsx +++ b/packages/md-mui/Leafs/HTMLLeafs.tsx @@ -1,7 +1,7 @@ import React from 'react' import { LeafChildNodes } from '@content-ui/md-mui/LeafChildNodes' import Box from '@mui/material/Box' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { WithMdAstChild } from '@content-ui/struct/Ast' export const LeafBr: React.FC = () =>
diff --git a/packages/md-mui/Leafs/LeafBlockquote.tsx b/packages/md-mui/Leafs/LeafBlockquote.tsx index f673780..3e8a50b 100644 --- a/packages/md-mui/Leafs/LeafBlockquote.tsx +++ b/packages/md-mui/Leafs/LeafBlockquote.tsx @@ -1,7 +1,7 @@ import React from 'react' import Box from '@mui/material/Box' import { LeafChildNodes } from '@content-ui/md-mui/LeafChildNodes' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { useLeafFollower } from '@content-ui/react/useLeafFollower' export const LeafBlockquote: React.FC = ({child, selected}) => { diff --git a/packages/md-mui/Leafs/LeafCode.tsx b/packages/md-mui/Leafs/LeafCode.tsx index 41e8d70..be051b8 100644 --- a/packages/md-mui/Leafs/LeafCode.tsx +++ b/packages/md-mui/Leafs/LeafCode.tsx @@ -1,11 +1,11 @@ import React from 'react' import Box from '@mui/material/Box' -import { ContentLeafProps, ContentLeafsPropsMapping, useContentLeafs } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps, ContentLeafsPropsMapping, useContentLeafs } from '@content-ui/react/ContentLeafsContext' import { useLeafFollower } from '@content-ui/react/useLeafFollower' import { useTheme } from '@mui/material/styles' import type { Theme } from '@mui/material/styles' import { TypographyWithExtras } from '@content-ui/md-mui/MuiComponents/Theme' -import { MuiContentRenderComponents } from '../LeafsMarkdown' +import { MuiContentRenderComponents } from '@content-ui/md-mui/LeafsMarkdown' export const LeafCode: React.FC = ({child, selected}) => { const code = child.type === 'code' ? child : undefined diff --git a/packages/md-mui/Leafs/LeafDefList.tsx b/packages/md-mui/Leafs/LeafDefList.tsx index 3fcd31a..f045e21 100644 --- a/packages/md-mui/Leafs/LeafDefList.tsx +++ b/packages/md-mui/Leafs/LeafDefList.tsx @@ -2,7 +2,7 @@ import React from 'react' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' import { useTheme } from '@mui/material/styles' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { LeafChildNodes } from '@content-ui/md-mui/LeafChildNodes' import { useLeafFollower } from '@content-ui/react/useLeafFollower' diff --git a/packages/md-mui/Leafs/LeafFootnote.tsx b/packages/md-mui/Leafs/LeafFootnote.tsx index 25c2738..723250b 100644 --- a/packages/md-mui/Leafs/LeafFootnote.tsx +++ b/packages/md-mui/Leafs/LeafFootnote.tsx @@ -4,7 +4,7 @@ import { LeafChildNodes } from '@content-ui/md-mui/LeafChildNodes' import { MuiLink } from '@content-ui/md-mui/MuiComponents/MuiLink' import IcGoTo from '@mui/icons-material/SubdirectoryArrowLeft' import Typography from '@mui/material/Typography' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { useTheme } from '@mui/material/styles' import type { Theme } from '@mui/material/styles' import { TypographyWithExtras } from '@content-ui/md-mui/MuiComponents/Theme' diff --git a/packages/md-mui/Leafs/LeafImage.tsx b/packages/md-mui/Leafs/LeafImage.tsx index d7e97fc..7885fe7 100644 --- a/packages/md-mui/Leafs/LeafImage.tsx +++ b/packages/md-mui/Leafs/LeafImage.tsx @@ -1,6 +1,6 @@ import React from 'react' import Typography from '@mui/material/Typography' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { WithMdAstChild } from '@content-ui/struct/Ast' export const LeafImage: React.FC = ({child}) => diff --git a/packages/md-mui/Leafs/LeafList.tsx b/packages/md-mui/Leafs/LeafList.tsx index cb78e4b..c3d9ee2 100644 --- a/packages/md-mui/Leafs/LeafList.tsx +++ b/packages/md-mui/Leafs/LeafList.tsx @@ -4,7 +4,7 @@ import Box from '@mui/material/Box' import IcChecked from '@mui/icons-material/CheckBox' import IcUnchecked from '@mui/icons-material/CheckBoxOutlineBlank' import { LeafChildNodes } from '@content-ui/md-mui/LeafChildNodes' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' export const LeafList: React.FC> = ({child}) => { const component = child.type === 'list' && child.ordered ? 'ol' : 'ul' diff --git a/packages/md-mui/Leafs/LeafTable.tsx b/packages/md-mui/Leafs/LeafTable.tsx index dc9cf3e..ad89d95 100644 --- a/packages/md-mui/Leafs/LeafTable.tsx +++ b/packages/md-mui/Leafs/LeafTable.tsx @@ -8,7 +8,7 @@ import TableCell from '@mui/material/TableCell' // import { TableCellProps } from '@mui/material/TableCell/TableCell' import TableBody from '@mui/material/TableBody' import { useTheme } from '@mui/material/styles' -import { ContentLeafProps } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps } from '@content-ui/react/ContentLeafsContext' import { useLeafFollower } from '@content-ui/react/useLeafFollower' export const LeafTable: React.FC = ({child}) => { diff --git a/packages/md-mui/Leafs/LeafToc.tsx b/packages/md-mui/Leafs/LeafToc.tsx index b808e8f..d1b9e8e 100644 --- a/packages/md-mui/Leafs/LeafToc.tsx +++ b/packages/md-mui/Leafs/LeafToc.tsx @@ -1,10 +1,11 @@ +import { useContentSelection } from '@content-ui/react/ContentSelectionContext' import React from 'react' import { Heading, ListItem, Root } from 'mdast' import { MuiLink } from '@content-ui/md-mui/MuiComponents/MuiLink' import Typography from '@mui/material/Typography' import Box from '@mui/material/Box' -import { ContentLeaf, ContentLeafProps, ContentLeafsPropsMapping } from '@content-ui/react/ContentLeaf' -import { EditorSelection } from '@content-ui/react/useContent' +import { ContentLeaf } from '@content-ui/react/ContentLeaf' +import { ContentLeafProps, ContentLeafsPropsMapping } from '@content-ui/react/ContentLeafsContext' import { useSettings } from '@content-ui/react/LeafSettings' import { flattenText } from '@content-ui/struct/flattenText' import { textToId } from '@content-ui/struct/textToId' @@ -15,7 +16,8 @@ import { TocHNode, TocListItem, WithMdAstChild } from '@content-ui/struct/Ast' export const LeafTocListItem: React.FC & { textVariant?: 'body1' | 'body2' | 'caption' }> = ({child, textVariant}) => { const c = child as TocListItem - const {smallList, showLines, editorSelection, onClick} = useToc() + const editorSelection = useContentSelection() + const {smallList, showLines, onClick} = useToc() // const c = child.type === 'tocListItem' ? child : undefined // todo: is injected in `ContentRenderer`, move to props const {headlineLinkable} = useSettings() @@ -112,7 +114,6 @@ export const LeafTocList: React.FC<{ export interface LeafTocContextType { smallList?: boolean showLines?: boolean - editorSelection?: EditorSelection onClick?: (hNode: TocHNode) => void } @@ -120,10 +121,10 @@ export const LeafTocContext = React.createContext({}) export const useToc = (): LeafTocContextType => React.useContext(LeafTocContext) -export const TocProvider: React.FC> = ({children, showLines, editorSelection, smallList, onClick}) => { +export const TocProvider: React.FC> = ({children, showLines, smallList, onClick}) => { const ctx = React.useMemo( - () => ({showLines, editorSelection, smallList, onClick}), - [showLines, editorSelection, smallList, onClick], + () => ({showLines, smallList, onClick}), + [showLines, smallList, onClick], ) return {children} } @@ -184,7 +185,7 @@ export interface LeafTocProps { export const LeafToc: React.FC = ( { - smallList, showLines, editorSelection, onClick, + smallList, showLines, onClick, tocInject, headlines, }, ) => { @@ -198,7 +199,6 @@ export const LeafToc: React.FC = ( return = ({child}) => { diff --git a/packages/md-mui/LeafsMarkdown.ts b/packages/md-mui/LeafsMarkdown.ts index a260631..d291ecf 100644 --- a/packages/md-mui/LeafsMarkdown.ts +++ b/packages/md-mui/LeafsMarkdown.ts @@ -15,7 +15,7 @@ import { LeafTable, LeafTableCell, LeafTableRow } from '@content-ui/md-mui/Leafs import { LeafYaml } from '@content-ui/md-mui/Leafs/LeafYaml' import { LeafTocListItem } from '@content-ui/md-mui/Leafs/LeafToc' import { LeafImage } from '@content-ui/md-mui/Leafs/LeafImage' -import { ContentRenderComponents, ContentLeafsNodeMapping, LeafsRenderMapping, ContentLeafMatchParams } from '@content-ui/react/ContentLeaf' +import { ContentRenderComponents, ContentLeafsNodeMapping, LeafsRenderMapping, ContentLeafMatchParams } from '@content-ui/react/ContentLeafsContext' import { LeafDefList, LeafDefListDescription, LeafDefListTerm } from '@content-ui/md-mui/Leafs/LeafDefList' import { ComponentType } from 'react' diff --git a/packages/md-mui/Renderer.tsx b/packages/md-mui/Renderer.tsx index 2e6d9cd..9b179f6 100644 --- a/packages/md-mui/Renderer.tsx +++ b/packages/md-mui/Renderer.tsx @@ -1,5 +1,6 @@ +import { useContentSelection } from '@content-ui/react/ContentSelectionContext' import { Fragment, memo, ReactNode } from 'react' -import { useContentContext, EditorSelection } from '@content-ui/react/useContent' +import { useContentContext } from '@content-ui/react/ContentFileContext' import Typography from '@mui/material/Typography' import { defaultTocIds, LeafToc, LeafTocContextType, useLeafToc } from '@content-ui/md-mui/Leafs/LeafToc' import { isLeafSelected } from '@content-ui/react/isLeafSelected' @@ -7,12 +8,12 @@ import { ContentLeaf } from '@content-ui/react/ContentLeaf' import { FootnoteSection } from '@content-ui/md-mui/Leafs/LeafFootnoteSection' export interface RendererProps { - editorSelection?: EditorSelection handleTocClick?: LeafTocContextType['onClick'] } -export const Renderer = ({handleTocClick, editorSelection}: RendererProps): ReactNode => { +export const Renderer = ({handleTocClick}: RendererProps): ReactNode => { const {root} = useContentContext() + const editorSelection = useContentSelection() const {headlines, tocInject} = useLeafToc(root, defaultTocIds) const bodyNodes = root?.children?.filter(c => c.type !== 'footnoteDefinition') diff --git a/packages/md-mui/Viewer.tsx b/packages/md-mui/Viewer.tsx index 3043ed0..926a362 100644 --- a/packages/md-mui/Viewer.tsx +++ b/packages/md-mui/Viewer.tsx @@ -1,14 +1,14 @@ import React from 'react' import Box from '@mui/material/Box' -import { ContentProcessor, EditorSelection, useContent, useContentContext, WithContent } from '@content-ui/react/useContent' +import { useContentContext } from '@content-ui/react/ContentFileContext' +import { ContentProcessor, useContent, WithContent } from '@content-ui/react/useContent' import Typography from '@mui/material/Typography' import { useLocation } from 'react-router-dom' import LinearProgress from '@mui/material/LinearProgress' -import { ContentFileProvider } from '@content-ui/react/ContentFileProvider' +import { ContentFileProvider } from '@content-ui/react/ContentFileContext' import { RendererMemo } from '@content-ui/md-mui/Renderer' export interface ViewerProps { - editorSelection?: EditorSelection outdated?: boolean processing: WithContent['processing'] m?: number @@ -29,7 +29,6 @@ export interface ViewerProps { export const Viewer =

( { - editorSelection, processing, outdated, ...props @@ -56,7 +55,6 @@ export const Viewer =

( {processing === 'success' || isReady ? : null} {(processing === 'loading' || outdated) && !isReady ? @@ -80,7 +78,7 @@ export interface ViewerFromTextProps extends Omit = ( { textValue, - editorSelection, processor, + processor, parseDelay, onMount = false, ...props @@ -98,7 +96,6 @@ export const ViewerFromText: React.ComponentType = ( file={file} > ({}) + +export const useContentContext = () => React.useContext(ContentContext) + export const ContentFileProvider: React.FC> = ({root, file, editorSelection, children}) => { +}>> = ({root, file, children}) => { const cmCtx = React.useMemo((): ContentFileContextType => ({ root, file, }), [root, file]) return - - {children} - + {children} } diff --git a/packages/react/ContentLeaf.tsx b/packages/react/ContentLeaf.tsx index fe720b3..8b2604d 100644 --- a/packages/react/ContentLeaf.tsx +++ b/packages/react/ContentLeaf.tsx @@ -1,169 +1,10 @@ -import { RootContent } from 'mdast' -import React, { useMemo, memo, createContext, useContext } from 'react' -import { EditorSelection } from '@content-ui/react/useContent' -import { useSettings } from '@content-ui/react/LeafSettings' +import { ReactElement } from 'react' import { DecoratorProps, DecoratorPropsNext, ReactBaseDecorator, ReactDeco } from '@content-ui/react/EngineDecorator' - -export type GenericLeafsDataSpec = { - [k: string]: D -} - -export interface LeafsRenderMapping< - TLeafsMapping extends {} = {}, - TComponentsMapping extends {} = {}, - /** - * The match params should be wider than the params each leaf expects, - * to improve portability of matcher to work with similar leafs, - * as mostly the matcher should work for more leafs than initially known. - * - * @example - * if some leaf param is: `{ elem: 'headline' | 'input' }` - * the match params should be: `{ elem: string }` - */ - TMatchParams extends {} = {}, - /** - * @experimental - */ - TMatchResult = any, - /** - * @experimental - */ - THooks extends {} = {} -> { - components: TComponentsMapping - leafs: TLeafsMapping - /** - * Responsible to match leafs of this mapping. - */ - matchLeaf:

(params: P, leafs: TLeafsMapping) => TMatchResult - children?: never - hooks?: THooks -} - -/** - * A wider `React.ComponentType`, as the remapping had a lot of issues when `React.ComponentType` was used internally, somehow not reproducible here or in others with React18. - * But in ui-schema with the latest React 18 setup, `React.ComponentType` won't work without the `React.ComponentClass

` - */ -export type ReactLeafDefaultNodeType

= React.ComponentClass

| ((props: P, context?: any) => React.ReactNode) -export type ReactLeafsNodeSpec = { - [K in keyof LDS]: ReactLeafDefaultNodeType>; -} - -export type ContentLeafMatchParams = { elem: string } - -export interface LeafsEngine, TRender extends {}> { - renderMap: TRender - deco?: TDeco -} - -export interface ContentLeafPayload { - elem: string - selection?: EditorSelection | undefined - selected?: boolean - // `true` when first Leaf inside the parent level - isFirst?: boolean - // `true` when last Leaf inside the parent level - isLast?: boolean -} - -type MdAstNodes = RootContent - -/** - * @todo make generic and easy to add further mdast types - */ -export type ContentLeafsPropsMapping = { - // [K in CustomMdAstContent['type']]: { elem: K, child: CustomMdAstContent } & ContentLeafPayload - // [K in Content['type']]: { elem: K, child: Content extends { type: K } ? Extract : never } & ContentLeafPayload - [K in MdAstNodes['type']]: { elem: K, child: Extract } & ContentLeafPayload -} - -export type ContentLeafsNodeMapping = ReactLeafsNodeSpec - -export type ContentRenderComponents = {} - -export type ContentLeafProps = ContentLeafsPropsMapping[S] - -export type ContentRendererProps = { - renderMap: LeafsRenderMapping, ContentRenderComponents, ContentLeafMatchParams> - elem: string -} - -export function ContentRenderer

( - { - renderMap, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - next, - ...p - }: P & ContentRendererProps, -): React.ReactElement

| null { - const settings = useSettings() - const leafs = renderMap.leafs - const Leaf = renderMap.matchLeaf(p, leafs) - - if(!Leaf) { - console.error('No LeafNode found for ' + p.elem, p) - return null - } - - return -} - -export const ContentRendererMemo = memo(ContentRenderer) - -export const contentUIDecorators = new ReactDeco< - DecoratorPropsNext & - ContentRendererProps ->() - .use(ContentRendererMemo as typeof ContentRenderer) - -export const contentLeafsContext: React.Context< - LeafsEngine< - ReactDeco<{}, {}>, - LeafsRenderMapping, ContentRenderComponents, ContentLeafMatchParams> - > -> = createContext(undefined as any) - -export const useContentLeafs = < - TLeafsDataMapping extends ContentLeafsPropsMapping = ContentLeafsPropsMapping, - TComponents extends ContentRenderComponents = ContentRenderComponents, - TDeco extends ReactDeco<{}, {}> = ReactDeco<{}, {}>, - TRender extends LeafsRenderMapping, TComponents, ContentLeafMatchParams> = LeafsRenderMapping, TComponents, ContentLeafMatchParams>, ->() => { - return useContext>( - contentLeafsContext as unknown as React.Context>, - ) -} - -export function ContentLeafsProvider< - TLeafsDataMapping extends ContentLeafsPropsMapping = ContentLeafsPropsMapping, - TComponents extends ContentRenderComponents = ContentRenderComponents, - TDeco extends ReactDeco<{}, {}, {}> = ReactDeco<{}, {}, {}>, - TRender extends LeafsRenderMapping, TComponents, ContentLeafMatchParams> = LeafsRenderMapping, TComponents, ContentLeafMatchParams>, - // todo: integrate a typing which validates that the provided deco-result-props are compatible with props of `TRender2['leafs']` ->( - { - children, - deco, renderMap, - }: React.PropsWithChildren>, -) { - const ctx = useMemo((): LeafsEngine => ({ - deco: deco, - renderMap: renderMap, - }), [deco, renderMap]) - - const LeafsContextProvider = contentLeafsContext.Provider - return , TComponents, ContentLeafMatchParams, any, {}> - > - } - >{children} -} +import { + ContentLeafMatchParams, ContentLeafsPropsMapping, ContentRenderComponents, + LeafsEngine, LeafsRenderMapping, ReactLeafsNodeSpec, + useContentLeafs, +} from '@content-ui/react/ContentLeafsContext' export type ContentLeafInjected = 'decoIndex' | 'next' | keyof LeafsEngine @@ -175,7 +16,7 @@ export function ContentLeaf< TProps extends DecoratorProps = DecoratorProps, >( props: Omit, -): React.JSX.Element | null { +): ReactElement | null { const {renderMap, deco} = useContentLeafs< TLeafDataMapping, ContentRenderComponents, TDeco, LeafsRenderMapping, ContentRenderComponents, ContentLeafMatchParams> diff --git a/packages/react/ContentLeafsContext.tsx b/packages/react/ContentLeafsContext.tsx new file mode 100644 index 0000000..56a90a0 --- /dev/null +++ b/packages/react/ContentLeafsContext.tsx @@ -0,0 +1,166 @@ +import { RootContent } from 'mdast' +import React, { useMemo, memo, createContext, useContext } from 'react' +import { useSettings } from '@content-ui/react/LeafSettings' +import { DecoratorPropsNext, ReactDeco } from '@content-ui/react/EngineDecorator' +import { ContentSelection } from '@content-ui/react/ContentSelectionContext' + +export type GenericLeafsDataSpec = { + [k: string]: D +} + +export interface LeafsRenderMapping< + TLeafsMapping extends {} = {}, + TComponentsMapping extends {} = {}, + /** + * The match params should be wider than the params each leaf expects, + * to improve portability of matcher to work with similar leafs, + * as mostly the matcher should work for more leafs than initially known. + * + * @example + * if some leaf param is: `{ elem: 'headline' | 'input' }` + * the match params should be: `{ elem: string }` + */ + TMatchParams extends {} = {}, + /** + * @experimental + */ + TMatchResult = any, + /** + * @experimental + */ + THooks extends {} = {} +> { + components: TComponentsMapping + leafs: TLeafsMapping + /** + * Responsible to match leafs of this mapping. + */ + matchLeaf:

(params: P, leafs: TLeafsMapping) => TMatchResult + children?: never + hooks?: THooks +} + +/** + * A wider `React.ComponentType`, as the remapping had a lot of issues when `React.ComponentType` was used internally, somehow not reproducible here or in others with React18. + * But in ui-schema with the latest React 18 setup, `React.ComponentType` won't work without the `React.ComponentClass

` + */ +export type ReactLeafDefaultNodeType

= React.ComponentClass

| ((props: P, context?: any) => React.ReactNode) +export type ReactLeafsNodeSpec = { + [K in keyof LDS]: ReactLeafDefaultNodeType>; +} + +export type ContentLeafMatchParams = { elem: string } + +export interface LeafsEngine, TRender extends {}> { + renderMap: TRender + deco?: TDeco +} + +export interface ContentLeafPayload { + elem: string + selection?: ContentSelection + selected?: boolean + // `true` when first Leaf inside the parent level + isFirst?: boolean + // `true` when last Leaf inside the parent level + isLast?: boolean +} + +type MdAstNodes = RootContent + +/** + * @todo make generic and easy to add further mdast types + */ +export type ContentLeafsPropsMapping = { + // [K in CustomMdAstContent['type']]: { elem: K, child: CustomMdAstContent } & ContentLeafPayload + // [K in Content['type']]: { elem: K, child: Content extends { type: K } ? Extract : never } & ContentLeafPayload + [K in MdAstNodes['type']]: { elem: K, child: Extract } & ContentLeafPayload +} + +export type ContentLeafsNodeMapping = ReactLeafsNodeSpec + +export type ContentRenderComponents = {} + +export type ContentLeafProps = ContentLeafsPropsMapping[S] + +export type ContentRendererProps = { + renderMap: LeafsRenderMapping, ContentRenderComponents, ContentLeafMatchParams> + elem: string +} + +export function ContentRenderer

( + { + renderMap, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + next, + ...p + }: P & ContentRendererProps, +): React.ReactElement

| null { + const settings = useSettings() + const leafs = renderMap.leafs + const Leaf = renderMap.matchLeaf(p, leafs) + + if(!Leaf) { + console.error('No LeafNode found for ' + p.elem, p) + return null + } + + return +} + +export const ContentRendererMemo = memo(ContentRenderer) + +export const contentUIDecorators = new ReactDeco< + DecoratorPropsNext & + ContentRendererProps +>() + .use(ContentRendererMemo as typeof ContentRenderer) + +export const contentLeafsContext: React.Context< + LeafsEngine< + ReactDeco<{}, {}>, + LeafsRenderMapping, ContentRenderComponents, ContentLeafMatchParams> + > +> = createContext(undefined as any) + +export const useContentLeafs = < + TLeafsDataMapping extends ContentLeafsPropsMapping = ContentLeafsPropsMapping, + TComponents extends ContentRenderComponents = ContentRenderComponents, + TDeco extends ReactDeco<{}, {}> = ReactDeco<{}, {}>, + TRender extends LeafsRenderMapping, TComponents, ContentLeafMatchParams> = LeafsRenderMapping, TComponents, ContentLeafMatchParams>, +>() => { + return useContext>( + contentLeafsContext as unknown as React.Context>, + ) +} + +export function ContentLeafsProvider< + TLeafsDataMapping extends ContentLeafsPropsMapping = ContentLeafsPropsMapping, + TComponents extends ContentRenderComponents = ContentRenderComponents, + TDeco extends ReactDeco<{}, {}, {}> = ReactDeco<{}, {}, {}>, + TRender extends LeafsRenderMapping, TComponents, ContentLeafMatchParams> = LeafsRenderMapping, TComponents, ContentLeafMatchParams>, + // todo: integrate a typing which validates that the provided deco-result-props are compatible with props of `TRender2['leafs']` +>( + { + children, + deco, renderMap, + }: React.PropsWithChildren>, +) { + const ctx = useMemo((): LeafsEngine => ({ + deco: deco, + renderMap: renderMap, + }), [deco, renderMap]) + + const LeafsContextProvider = contentLeafsContext.Provider + return , TComponents, ContentLeafMatchParams, any, {}> + > + } + >{children} +} diff --git a/packages/react/ContentSelectionContext.tsx b/packages/react/ContentSelectionContext.tsx new file mode 100644 index 0000000..66ef6af --- /dev/null +++ b/packages/react/ContentSelectionContext.tsx @@ -0,0 +1,39 @@ +import React, { createContext, useContext } from 'react' + +export interface EditorSelectionPosition { + start: number + startLine: number + startLineStart: number + startLineEnd: number + + end: number + endLine: number + endLineStart: number + endLineEnd: number +} + +export interface EditorSelectionFilled extends EditorSelectionPosition { + selected: true +} + +export interface EditorSelectionEmpty extends Partial { + selected?: false +} + +export type ContentSelection = EditorSelectionEmpty | EditorSelectionFilled +/** + * @deprecated use `ContentSelection` instead + */ +export type EditorSelection = ContentSelection + +export const ContentSelectionContext = createContext(undefined) + +export const useContentSelection = () => useContext(ContentSelectionContext) + +export const ContentSelectionProvider: React.FC> = ({children, selection}) => { + return + {children} + +} diff --git a/packages/react/isLeafSelected.ts b/packages/react/isLeafSelected.ts index a26afd8..1127511 100644 --- a/packages/react/isLeafSelected.ts +++ b/packages/react/isLeafSelected.ts @@ -2,7 +2,7 @@ export const isLeafSelected = ( position: { start: { line: number }, end: { line: number } } | undefined, startLine: number | undefined, endLine: number | undefined, -) => +): boolean => typeof position?.start.line === 'number' && typeof startLine === 'number' && typeof endLine === 'number' && ( diff --git a/packages/react/package.json b/packages/react/package.json index 764ed99..697887f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -24,14 +24,22 @@ "import": "./Utils/copyToClipboard.js", "types": "./Utils/copyToClipboard.d.ts" }, - "./ContentFileProvider": { - "import": "./ContentFileProvider.js", - "types": "./ContentFileProvider.d.ts" + "./ContentFileContext": { + "import": "./ContentFileContext.js", + "types": "./ContentFileContext.d.ts" }, "./ContentLeaf": { "import": "./ContentLeaf.js", "types": "./ContentLeaf.d.ts" }, + "./ContentLeafsContext": { + "import": "./ContentLeafsContext.js", + "types": "./ContentLeafsContext.d.ts" + }, + "./ContentSelectionContext": { + "import": "./ContentSelectionContext.js", + "types": "./ContentSelectionContext.d.ts" + }, "./EngineDecorator": { "import": "./EngineDecorator.js", "types": "./EngineDecorator.d.ts" diff --git a/packages/react/useContent.ts b/packages/react/useContent.ts index ab31fe6..df0639d 100644 --- a/packages/react/useContent.ts +++ b/packages/react/useContent.ts @@ -1,44 +1,8 @@ -import React, { useCallback, useRef, useState } from 'react' +import { useEffect, useCallback, useRef, useState } from 'react' import { Processor } from 'unified' import { VFile } from 'vfile' import { Root } from 'mdast' -export interface EditorSelectionPosition { - start: number - startLine: number - startLineStart: number - startLineEnd: number - - end: number - endLine: number - endLineStart: number - endLineEnd: number -} - -export interface EditorSelectionFilled extends EditorSelectionPosition { - selected: true -} - -export interface EditorSelectionEmpty extends Partial { - selected?: false -} - -export type EditorSelection = EditorSelectionEmpty | EditorSelectionFilled - -export interface ContentFileContextType { - // root?: OrderedMap - root?: Root - file?: VFile -} - -export const ContentContext = React.createContext({}) - -export const useContentContext = () => React.useContext(ContentContext) - -export const ContentSelectionContext = React.createContext(undefined) - -export const useContentSelection = () => React.useContext(ContentSelectionContext) - export type ContentProcessor = Processor export interface WithContent { @@ -151,11 +115,11 @@ export const useContent = ( const delayRefs = useRef({parseDelay, forceAfter}) delayRefs.current = {parseDelay, forceAfter} - React.useEffect(() => { + useEffect(() => { return () => window.clearTimeout(timer2.current) }, []) - React.useEffect(() => { + useEffect(() => { if(onMount && !mountedRef.current) { mountedRef.current = true return diff --git a/packages/struct/textToId.ts b/packages/struct/textToId.ts index f5f0f49..a03495d 100644 --- a/packages/struct/textToId.ts +++ b/packages/struct/textToId.ts @@ -2,11 +2,11 @@ export const textToId = (text: string): string => text .toLowerCase() - // replace any non-word characters (except `:` `.` `-`) with a hyphen - .replace(/[^\w:.-]+/g, '-') + // replace any non-word characters (except `.` `-`) with a hyphen + .replace(/[^\w.-]+/g, '-') // remove maybe generated leading hyphens, including numbers .replace(/^[-\d]+/, '') // replace multiple hyphens with a single one .replace(/-+/g, '-') - // trim any trailing hyphens - .replace(/-$/, '') + // remove trailing hyphens, dots + .replace(/[-.]+$/, '') diff --git a/server/feed/src/handler/ReactHandler.tsx b/server/feed/src/handler/ReactHandler.tsx index 6868f7b..c1d0a65 100644 --- a/server/feed/src/handler/ReactHandler.tsx +++ b/server/feed/src/handler/ReactHandler.tsx @@ -1,7 +1,7 @@ import { Viewer } from '@content-ui/md-mui/Viewer' import { ContentParser } from '@content-ui/md/parser/ContentParser' -import { ContentFileProvider } from '@content-ui/react/ContentFileProvider' -import { ContentLeafsProvider, contentUIDecorators } from '@content-ui/react/ContentLeaf' +import { ContentFileProvider } from '@content-ui/react/ContentFileContext' +import { ContentLeafsProvider, contentUIDecorators } from '@content-ui/react/ContentLeafsContext' import { Express } from 'express' import { renderToStaticMarkup } from 'react-dom/server' import { StaticRouter } from 'react-router-dom/server' @@ -35,7 +35,6 @@ This is **rendered static on server**. file={file} >