chore: show Super demo modal if user doesn't have subscription when switching editor to Super
This commit is contained in:
@@ -28,6 +28,13 @@ import TableActionMenuPlugin from './Plugins/TableCellActionMenuPlugin'
|
||||
import ToolbarPlugin from './Plugins/ToolbarPlugin/ToolbarPlugin'
|
||||
import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
||||
import { CheckListPlugin } from './Plugins/List/CheckListPlugin'
|
||||
import RemoteImagePlugin from './Plugins/RemoteImagePlugin/RemoteImagePlugin'
|
||||
import CodeOptionsPlugin from './Plugins/CodeOptionsPlugin/CodeOptions'
|
||||
import { SuperSearchContextProvider } from './Plugins/SearchPlugin/Context'
|
||||
import { SearchPlugin } from './Plugins/SearchPlugin/SearchPlugin'
|
||||
import AutoLinkPlugin from './Plugins/AutoLinkPlugin/AutoLinkPlugin'
|
||||
import DatetimePlugin from './Plugins/DateTimePlugin/DateTimePlugin'
|
||||
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
|
||||
|
||||
type BlocksEditorProps = {
|
||||
onChange?: (value: string, preview: string) => void
|
||||
@@ -83,7 +90,10 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
<div className="editor z-0 overflow-hidden" ref={onRef}>
|
||||
<ContentEditable
|
||||
id={SuperEditorContentId}
|
||||
className={classNames('ContentEditable__root overflow-y-auto', className)}
|
||||
className={classNames(
|
||||
'ContentEditable__root relative overflow-y-auto p-4 text-[length:--font-size] leading-[--line-height] focus:shadow-none focus:outline-none',
|
||||
className,
|
||||
)}
|
||||
spellCheck={spellcheck}
|
||||
/>
|
||||
<div className="search-highlight-container pointer-events-none absolute left-0 top-0 h-full w-full" />
|
||||
@@ -116,6 +126,14 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
||||
<CollapsiblePlugin />
|
||||
<TabIndentationPlugin />
|
||||
<RemoveBrokenTablesPlugin />
|
||||
<RemoteImagePlugin />
|
||||
<CodeOptionsPlugin />
|
||||
<SuperSearchContextProvider>
|
||||
<SearchPlugin />
|
||||
</SuperSearchContextProvider>
|
||||
<DatetimePlugin />
|
||||
<PasswordPlugin />
|
||||
<AutoLinkPlugin />
|
||||
{!readonly && floatingAnchorElem && (
|
||||
<>
|
||||
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { FunctionComponent } from 'react'
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { LexicalComposer, InitialEditorStateType } from '@lexical/react/LexicalComposer'
|
||||
import BlocksEditorTheme from './Lexical/Theme/Theme'
|
||||
import { BlockEditorNodes } from './Lexical/Nodes/AllNodes'
|
||||
import { Klass, LexicalNode } from 'lexical'
|
||||
|
||||
type BlocksEditorComposerProps = {
|
||||
initialValue: string | undefined
|
||||
initialValue: InitialEditorStateType | undefined
|
||||
children: React.ReactNode
|
||||
nodes?: Array<Klass<LexicalNode>>
|
||||
readonly?: boolean
|
||||
@@ -24,7 +24,7 @@ export const BlocksEditorComposer: FunctionComponent<BlocksEditorComposerProps>
|
||||
theme: BlocksEditorTheme,
|
||||
editable: !readonly,
|
||||
onError: (error: Error) => console.error(error),
|
||||
editorState: initialValue && initialValue.length > 0 ? initialValue : undefined,
|
||||
editorState: initialValue,
|
||||
nodes: [...nodes, ...BlockEditorNodes],
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
flex-direction: column;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import './Modal.css'
|
||||
|
||||
import { ReactNode, useEffect, useRef } from 'react'
|
||||
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
function PortalImpl({
|
||||
@@ -83,11 +84,22 @@ export default function Modal({
|
||||
closeOnClickOutside?: boolean
|
||||
onClose: () => void
|
||||
title: string
|
||||
}): JSX.Element {
|
||||
}): ReactNode {
|
||||
const [containerElement, setContainerElement] = useState<HTMLElement | undefined>()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
useEffect(() => {
|
||||
setContainerElement(editor.getRootElement()?.parentElement ?? document.body)
|
||||
}, [editor])
|
||||
|
||||
if (!containerElement) {
|
||||
return null
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<PortalImpl onClose={onClose} title={title} closeOnClickOutside={closeOnClickOutside}>
|
||||
{children}
|
||||
</PortalImpl>,
|
||||
document.body,
|
||||
containerElement,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function BlockPickerMenuItem({
|
||||
<li
|
||||
key={option.key}
|
||||
tabIndex={-1}
|
||||
className={`border-bottom gap-3 border-[0.5px] border-border ${PopoverItemClassNames} ${
|
||||
className={`gap-3 border-b-[0.5px] border-border ${PopoverItemClassNames} ${
|
||||
isSelected ? PopoverItemSelectedClassNames : ''
|
||||
}`}
|
||||
ref={option.setRefElement}
|
||||
|
||||
@@ -30,7 +30,7 @@ import { GetDividerBlockOption } from '../Blocks/Divider'
|
||||
import { GetCollapsibleBlockOption } from '../Blocks/Collapsible'
|
||||
import { GetEmbedsBlockOptions } from '../Blocks/Embeds'
|
||||
|
||||
export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
export default function BlockPickerMenuPlugin({ popoverZIndex }: { popoverZIndex?: string }): JSX.Element {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const application = useApplication()
|
||||
const [modal, showModal] = useModal()
|
||||
@@ -130,6 +130,7 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
||||
disableMobileFullscreenTakeover={true}
|
||||
side={isMobileScreen() ? 'top' : 'bottom'}
|
||||
maxHeight={(mh) => mh / 2}
|
||||
overrideZIndex={popoverZIndex}
|
||||
>
|
||||
<ul>
|
||||
{options.map((option, i: number) => (
|
||||
|
||||
@@ -113,8 +113,19 @@ const ToolbarButton = forwardRef(
|
||||
) => {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
const parentElement = editor.getRootElement()?.parentElement ?? document.body
|
||||
|
||||
return (
|
||||
<StyledTooltip showOnMobile showOnHover label={name} side="top">
|
||||
<StyledTooltip
|
||||
showOnMobile
|
||||
showOnHover
|
||||
label={name}
|
||||
side="top"
|
||||
portal={false}
|
||||
portalElement={isMobile ? parentElement : undefined}
|
||||
documentElement={parentElement}
|
||||
>
|
||||
<ToolbarItem
|
||||
className={classNames(
|
||||
'flex select-none items-center justify-center rounded p-0.5 focus:shadow-none focus:outline-none enabled:hover:bg-default enabled:focus-visible:bg-default disabled:opacity-50 md:border md:border-transparent enabled:hover:md:translucent-ui:border-[--popover-border-color]',
|
||||
@@ -572,6 +583,8 @@ const ToolbarPlugin = () => {
|
||||
toolbarStore,
|
||||
])
|
||||
|
||||
const popoverDocumentElement = editor.getRootElement()?.parentElement ?? document.body
|
||||
|
||||
return (
|
||||
<>
|
||||
{modal}
|
||||
@@ -746,6 +759,8 @@ const ToolbarPlugin = () => {
|
||||
className="py-1"
|
||||
disableMobileFullscreenTakeover
|
||||
disableFlip
|
||||
portal={false}
|
||||
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||
>
|
||||
<div className="mb-1.5 mt-1 px-3 text-sm font-semibold uppercase text-text">Table of Contents</div>
|
||||
<LexicalTableOfContents>
|
||||
@@ -800,6 +815,8 @@ const ToolbarPlugin = () => {
|
||||
disableMobileFullscreenTakeover
|
||||
disableFlip
|
||||
containerClassName="md:!min-w-60 md:!w-auto"
|
||||
portal={false}
|
||||
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||
>
|
||||
<Menu a11yLabel="Text formatting options" className="!px-0" onClick={() => setIsTextFormatMenuOpen(false)}>
|
||||
<ToolbarMenuItem
|
||||
@@ -839,6 +856,8 @@ const ToolbarPlugin = () => {
|
||||
disableMobileFullscreenTakeover
|
||||
disableFlip
|
||||
containerClassName="md:!min-w-60 md:!w-auto"
|
||||
portal={false}
|
||||
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||
>
|
||||
<Menu a11yLabel="Text style" className="!px-0" onClick={() => setIsTextStyleMenuOpen(false)}>
|
||||
<ToolbarMenuItem
|
||||
@@ -910,6 +929,8 @@ const ToolbarPlugin = () => {
|
||||
disableMobileFullscreenTakeover
|
||||
disableFlip
|
||||
containerClassName="md:!min-w-60 md:!w-auto"
|
||||
portal={false}
|
||||
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||
>
|
||||
<Menu a11yLabel="Alignment" className="!px-0" onClick={() => setIsAlignmentMenuOpen(false)}>
|
||||
<ToolbarMenuItem
|
||||
@@ -949,6 +970,8 @@ const ToolbarPlugin = () => {
|
||||
disableMobileFullscreenTakeover
|
||||
disableFlip
|
||||
containerClassName="md:!min-w-60 md:!w-auto"
|
||||
portal={false}
|
||||
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||
>
|
||||
<Menu a11yLabel="Insert" className="!px-0" onClick={() => setIsInsertMenuOpen(false)}>
|
||||
<ToolbarMenuItem
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import {
|
||||
classNames,
|
||||
isPayloadSourceRetrieved,
|
||||
PrefKey,
|
||||
NativeFeatureIdentifier,
|
||||
@@ -14,7 +13,6 @@ import { BlocksEditorComposer } from './BlocksEditorComposer'
|
||||
import { ItemSelectionPlugin } from './Plugins/ItemSelectionPlugin/ItemSelectionPlugin'
|
||||
import { FileNode } from './Plugins/EncryptedFilePlugin/Nodes/FileNode'
|
||||
import FilePlugin from './Plugins/EncryptedFilePlugin/FilePlugin'
|
||||
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
|
||||
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import LinkingControllerProvider from '../../Controllers/LinkingControllerProvider'
|
||||
@@ -23,28 +21,22 @@ import ItemBubblePlugin from './Plugins/ItemBubblePlugin/ItemBubblePlugin'
|
||||
import { NodeObserverPlugin } from './Plugins/NodeObserverPlugin/NodeObserverPlugin'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import FilesControllerProvider from '@/Controllers/FilesControllerProvider'
|
||||
import DatetimePlugin from './Plugins/DateTimePlugin/DateTimePlugin'
|
||||
import AutoLinkPlugin from './Plugins/AutoLinkPlugin/AutoLinkPlugin'
|
||||
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
||||
import {
|
||||
ChangeContentCallbackPlugin,
|
||||
ChangeEditorFunction,
|
||||
} from './Plugins/ChangeContentCallback/ChangeContentCallback'
|
||||
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
|
||||
import { useCommandService } from '@/Components/CommandProvider'
|
||||
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
|
||||
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
|
||||
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
|
||||
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
|
||||
import ReadonlyPlugin from './Plugins/ReadonlyPlugin/ReadonlyPlugin'
|
||||
import { SuperSearchContextProvider } from './Plugins/SearchPlugin/Context'
|
||||
import { SearchPlugin } from './Plugins/SearchPlugin/SearchPlugin'
|
||||
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||
import CodeOptionsPlugin from './Plugins/CodeOptionsPlugin/CodeOptions'
|
||||
import RemoteImagePlugin from './Plugins/RemoteImagePlugin/RemoteImagePlugin'
|
||||
import NotEntitledBanner from '../ComponentView/NotEntitledBanner'
|
||||
import AutoFocusPlugin from './Plugins/AutoFocusPlugin'
|
||||
import usePreference from '@/Hooks/usePreference'
|
||||
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
|
||||
|
||||
export const SuperNotePreviewCharLimit = 160
|
||||
|
||||
@@ -207,10 +199,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
<BlocksEditorComposer readonly={note.current.locked || readonly} initialValue={note.current.text}>
|
||||
<BlocksEditor
|
||||
onChange={handleChange}
|
||||
className={classNames(
|
||||
'blocks-editor relative h-full resize-none px-4 py-4 text-[length:--font-size] focus:shadow-none focus:outline-none',
|
||||
lineHeight && 'leading-[--line-height]',
|
||||
)}
|
||||
className="blocks-editor h-full resize-none"
|
||||
previewLength={SuperNotePreviewCharLimit}
|
||||
spellcheck={spellcheck}
|
||||
readonly={note.current.locked || readonly}
|
||||
@@ -218,11 +207,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
<ItemSelectionPlugin currentNote={note.current} />
|
||||
<FilePlugin currentNote={note.current} />
|
||||
<ItemBubblePlugin />
|
||||
<BlockPickerMenuPlugin />
|
||||
<GetMarkdownPlugin ref={getMarkdownPlugin} />
|
||||
<DatetimePlugin />
|
||||
<PasswordPlugin />
|
||||
<AutoLinkPlugin />
|
||||
<ChangeContentCallbackPlugin
|
||||
providerCallback={(callback) => (changeEditorFunction.current = callback)}
|
||||
/>
|
||||
@@ -230,11 +215,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
||||
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
|
||||
{readonly === undefined && <ReadonlyPlugin note={note.current} />}
|
||||
<AutoFocusPlugin isEnabled={controller.isTemplateNote} />
|
||||
<SuperSearchContextProvider>
|
||||
<SearchPlugin />
|
||||
</SuperSearchContextProvider>
|
||||
<CodeOptionsPlugin />
|
||||
<RemoteImagePlugin />
|
||||
<BlockPickerMenuPlugin />
|
||||
</BlocksEditor>
|
||||
</BlocksEditorComposer>
|
||||
</FilesControllerProvider>
|
||||
|
||||
@@ -155,7 +155,11 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
||||
return content
|
||||
}
|
||||
|
||||
convertOtherFormatToSuperString(otherFormatString: string, fromFormat: 'txt' | 'md' | 'html' | 'json'): string {
|
||||
convertOtherFormatToSuperString: SuperConverterServiceInterface['convertOtherFormatToSuperString'] = (
|
||||
otherFormatString,
|
||||
fromFormat,
|
||||
options,
|
||||
) => {
|
||||
if (otherFormatString.length === 0) {
|
||||
return otherFormatString
|
||||
}
|
||||
@@ -175,6 +179,10 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
||||
|
||||
let didThrow = false
|
||||
if (fromFormat === 'html') {
|
||||
const htmlOptions = options?.html || {
|
||||
addLineBreaks: true,
|
||||
}
|
||||
|
||||
this.importEditor.update(
|
||||
() => {
|
||||
try {
|
||||
@@ -203,7 +211,9 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
||||
nodesToInsert.push(node)
|
||||
}
|
||||
|
||||
nodesToInsert.push($createParagraphNode())
|
||||
if (htmlOptions.addLineBreaks) {
|
||||
nodesToInsert.push($createParagraphNode())
|
||||
}
|
||||
})
|
||||
$getRoot().selectEnd()
|
||||
$insertNodes(nodesToInsert.concat($createParagraphNode()))
|
||||
|
||||
Reference in New Issue
Block a user