chore: show Super demo modal if user doesn't have subscription when switching editor to Super
This commit is contained in:
@@ -1,6 +1,14 @@
|
|||||||
export interface SuperConverterServiceInterface {
|
export interface SuperConverterServiceInterface {
|
||||||
isValidSuperString(superString: string): boolean
|
isValidSuperString(superString: string): boolean
|
||||||
convertSuperStringToOtherFormat: (superString: string, toFormat: 'txt' | 'md' | 'html' | 'json') => Promise<string>
|
convertSuperStringToOtherFormat: (superString: string, toFormat: 'txt' | 'md' | 'html' | 'json') => Promise<string>
|
||||||
convertOtherFormatToSuperString: (otherFormatString: string, fromFormat: 'txt' | 'md' | 'html' | 'json') => string
|
convertOtherFormatToSuperString: (
|
||||||
|
otherFormatString: string,
|
||||||
|
fromFormat: 'txt' | 'md' | 'html' | 'json',
|
||||||
|
options?: {
|
||||||
|
html?: {
|
||||||
|
addLineBreaks?: boolean
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) => string
|
||||||
getEmbeddedFileIDsFromSuperString(superString: string): string[]
|
getEmbeddedFileIDsFromSuperString(superString: string): string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,7 +736,7 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||||
FBLazyVector: 71803c074f6325f10b5ec891c443b6bbabef0ca7
|
FBLazyVector: 71803c074f6325f10b5ec891c443b6bbabef0ca7
|
||||||
@@ -756,7 +756,7 @@ SPEC CHECKSUMS:
|
|||||||
MMKV: 9c4663aa7ca255d478ff10f2f5cb7d17c1651ccd
|
MMKV: 9c4663aa7ca255d478ff10f2f5cb7d17c1651ccd
|
||||||
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
|
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
|
||||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||||
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
|
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||||
RCTRequired: df81ab637d35fac9e6eb94611cfd20f0feb05455
|
RCTRequired: df81ab637d35fac9e6eb94611cfd20f0feb05455
|
||||||
RCTTypeSafety: 4636e4a36c7c2df332bda6d59b19b41c443d4287
|
RCTTypeSafety: 4636e4a36c7c2df332bda6d59b19b41c443d4287
|
||||||
React: e0cc5197a804031a6c53fb38483c3485fcb9d6f3
|
React: e0cc5197a804031a6c53fb38483c3485fcb9d6f3
|
||||||
|
|||||||
@@ -991,6 +991,7 @@
|
|||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
" ",
|
" ",
|
||||||
|
"-Wl -ld_classic ",
|
||||||
);
|
);
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1059,6 +1060,7 @@
|
|||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
" ",
|
" ",
|
||||||
|
"-Wl -ld_classic ",
|
||||||
);
|
);
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
@@ -154,6 +154,11 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
|||||||
const handleMenuSelection = useCallback(
|
const handleMenuSelection = useCallback(
|
||||||
async (menuItem: EditorMenuItem) => {
|
async (menuItem: EditorMenuItem) => {
|
||||||
if (!menuItem.isEntitled) {
|
if (!menuItem.isEntitled) {
|
||||||
|
if (menuItem.uiFeature.featureIdentifier === NativeFeatureIdentifier.TYPES.SuperEditor) {
|
||||||
|
premiumModal.showSuperDemo()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
premiumModal.activate(menuItem.uiFeature.displayName)
|
premiumModal.activate(menuItem.uiFeature.displayName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -249,7 +254,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
|||||||
<>
|
<>
|
||||||
<Menu className="pb-1 pt-0.5" a11yLabel="Change note type menu">
|
<Menu className="pb-1 pt-0.5" a11yLabel="Change note type menu">
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
<div className="flex items-center justify-between pr-4 py-3 md:pt-0 md:pb-1">
|
<div className="flex items-center justify-between py-3 pr-4 md:pb-1 md:pt-0">
|
||||||
<div className="px-3">
|
<div className="px-3">
|
||||||
<h2 className="text-base font-bold">Choose a note type</h2>
|
<h2 className="text-base font-bold">Choose a note type</h2>
|
||||||
{unableToFindEditor && (
|
{unableToFindEditor && (
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
NoteMutator,
|
NoteMutator,
|
||||||
NoteType,
|
NoteType,
|
||||||
SNNote,
|
SNNote,
|
||||||
|
NativeFeatureIdentifier,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
import Icon from '../Icon/Icon'
|
import Icon from '../Icon/Icon'
|
||||||
@@ -60,6 +61,11 @@ const ChangeEditorMultipleMenu = ({ application, notes, setDisableClickOutside }
|
|||||||
const handleMenuSelection = useCallback(
|
const handleMenuSelection = useCallback(
|
||||||
async (itemToBeSelected: EditorMenuItem) => {
|
async (itemToBeSelected: EditorMenuItem) => {
|
||||||
if (!itemToBeSelected.isEntitled) {
|
if (!itemToBeSelected.isEntitled) {
|
||||||
|
if (itemToBeSelected.uiFeature.featureIdentifier === NativeFeatureIdentifier.TYPES.SuperEditor) {
|
||||||
|
premiumModal.showSuperDemo()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
premiumModal.activate(itemToBeSelected.uiFeature.displayName)
|
premiumModal.activate(itemToBeSelected.uiFeature.displayName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const PositionedPopoverContent = ({
|
|||||||
hideOnClickInModal = false,
|
hideOnClickInModal = false,
|
||||||
setAnimationElement,
|
setAnimationElement,
|
||||||
containerClassName,
|
containerClassName,
|
||||||
|
documentElement,
|
||||||
}: PopoverContentProps) => {
|
}: PopoverContentProps) => {
|
||||||
const [popoverElement, setPopoverElement] = useState<HTMLDivElement | null>(null)
|
const [popoverElement, setPopoverElement] = useState<HTMLDivElement | null>(null)
|
||||||
const popoverRect = useAutoElementRect(popoverElement)
|
const popoverRect = useAutoElementRect(popoverElement)
|
||||||
@@ -45,13 +46,13 @@ const PositionedPopoverContent = ({
|
|||||||
y: anchorPoint?.y,
|
y: anchorPoint?.y,
|
||||||
})
|
})
|
||||||
const anchorRect = anchorPoint ? anchorPointRect : anchorElementRect
|
const anchorRect = anchorPoint ? anchorPointRect : anchorElementRect
|
||||||
const documentRect = useDocumentRect()
|
const _documentRect = useDocumentRect()
|
||||||
const isDesktopScreen = useMediaQuery(MediaQueryBreakpoints.md)
|
const isDesktopScreen = useMediaQuery(MediaQueryBreakpoints.md)
|
||||||
|
|
||||||
const styles = getPositionedPopoverStyles({
|
const styles = getPositionedPopoverStyles({
|
||||||
align,
|
align,
|
||||||
anchorRect,
|
anchorRect,
|
||||||
documentRect,
|
documentRect: documentElement?.getBoundingClientRect() ?? _documentRect,
|
||||||
popoverRect: popoverRect ?? popoverElement?.getBoundingClientRect(),
|
popoverRect: popoverRect ?? popoverElement?.getBoundingClientRect(),
|
||||||
side,
|
side,
|
||||||
disableMobileFullscreenTakeover,
|
disableMobileFullscreenTakeover,
|
||||||
@@ -73,7 +74,7 @@ const PositionedPopoverContent = ({
|
|||||||
let adjustedStyles: PopoverCSSProperties | undefined = undefined
|
let adjustedStyles: PopoverCSSProperties | undefined = undefined
|
||||||
|
|
||||||
if (!portal && popoverElement && styles) {
|
if (!portal && popoverElement && styles) {
|
||||||
adjustedStyles = getAdjustedStylesForNonPortalPopover(popoverElement, styles)
|
adjustedStyles = getAdjustedStylesForNonPortalPopover(popoverElement, styles, documentElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
usePopoverCloseOnClickOutside({
|
usePopoverCloseOnClickOutside({
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ type CommonPopoverProps = {
|
|||||||
offset?: number
|
offset?: number
|
||||||
hideOnClickInModal?: boolean
|
hideOnClickInModal?: boolean
|
||||||
open: boolean
|
open: boolean
|
||||||
|
documentElement?: HTMLElement
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PopoverContentProps = CommonPopoverProps & {
|
export type PopoverContentProps = CommonPopoverProps & {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const getAdjustedStylesForNonPortalPopover = (
|
|||||||
styles: PopoverCSSProperties,
|
styles: PopoverCSSProperties,
|
||||||
parent?: HTMLElement,
|
parent?: HTMLElement,
|
||||||
) => {
|
) => {
|
||||||
const absoluteParent = parent || getAbsolutePositionedParent(popoverElement)
|
const absoluteParent = parent || getAbsolutePositionedParent(popoverElement) || popoverElement.parentElement
|
||||||
const translateXProperty = styles?.['--translate-x']
|
const translateXProperty = styles?.['--translate-x']
|
||||||
const translateYProperty = styles?.['--translate-y']
|
const translateYProperty = styles?.['--translate-y']
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ const NewNoteDefaults = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (application.features.getFeatureStatus(identifier) !== FeatureStatus.Entitled) {
|
if (application.features.getFeatureStatus(identifier) !== FeatureStatus.Entitled) {
|
||||||
|
if (feature.getValue().value === NativeFeatureIdentifier.TYPES.SuperEditor) {
|
||||||
|
premiumModal.showSuperDemo()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const editorItem = editorItems.find((item) => item.value === value)
|
const editorItem = editorItems.find((item) => item.value === value)
|
||||||
if (editorItem) {
|
if (editorItem) {
|
||||||
premiumModal.activate(editorItem.label)
|
premiumModal.activate(editorItem.label)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum PremiumFeatureModalType {
|
export enum PremiumFeatureModalType {
|
||||||
UpgradePrompt,
|
UpgradePrompt,
|
||||||
UpgradeSuccess,
|
UpgradeSuccess,
|
||||||
|
SuperDemo,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { FeatureName } from '@/Controllers/FeatureName'
|
|||||||
import { SuccessPrompt } from './Subviews/SuccessPrompt'
|
import { SuccessPrompt } from './Subviews/SuccessPrompt'
|
||||||
import { UpgradePrompt } from './Subviews/UpgradePrompt'
|
import { UpgradePrompt } from './Subviews/UpgradePrompt'
|
||||||
import Modal from '../Modal/Modal'
|
import Modal from '../Modal/Modal'
|
||||||
|
import SuperDemo from './Subviews/SuperDemo'
|
||||||
|
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -23,23 +25,41 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const ctaButtonRef = useRef<HTMLButtonElement>(null)
|
const ctaButtonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
return (
|
const isShowingSuperDemo = type === PremiumFeatureModalType.SuperDemo
|
||||||
<Modal close={onClose} title="Upgrade" className="px-6 py-5" customHeader={<></>}>
|
|
||||||
<div tabIndex={-1} className="sn-component">
|
|
||||||
<div tabIndex={0}>
|
|
||||||
{type === PremiumFeatureModalType.UpgradePrompt && (
|
|
||||||
<UpgradePrompt
|
|
||||||
featureName={featureName}
|
|
||||||
ctaRef={ctaButtonRef}
|
|
||||||
application={application}
|
|
||||||
hasSubscription={hasSubscription}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{type === PremiumFeatureModalType.UpgradeSuccess && <SuccessPrompt ctaRef={ctaButtonRef} onClose={onClose} />}
|
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||||
</div>
|
|
||||||
</div>
|
return (
|
||||||
|
<Modal
|
||||||
|
close={onClose}
|
||||||
|
title={isShowingSuperDemo ? 'Try out Super' : 'Upgrade'}
|
||||||
|
className={isShowingSuperDemo ? '' : 'px-6 py-5'}
|
||||||
|
customHeader={isShowingSuperDemo ? undefined : <></>}
|
||||||
|
actions={
|
||||||
|
isShowingSuperDemo
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: 'Done',
|
||||||
|
type: 'primary',
|
||||||
|
onClick: onClose,
|
||||||
|
hidden: !isMobileScreen,
|
||||||
|
mobileSlot: 'right',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{type === PremiumFeatureModalType.UpgradePrompt && (
|
||||||
|
<UpgradePrompt
|
||||||
|
featureName={featureName}
|
||||||
|
ctaRef={ctaButtonRef}
|
||||||
|
application={application}
|
||||||
|
hasSubscription={hasSubscription}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{type === PremiumFeatureModalType.UpgradeSuccess && <SuccessPrompt ctaRef={ctaButtonRef} onClose={onClose} />}
|
||||||
|
{type === PremiumFeatureModalType.SuperDemo && <SuperDemo hasSubscription={hasSubscription} onClose={onClose} />}
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { BlocksEditor } from '@/Components/SuperEditor/BlocksEditor'
|
||||||
|
import { BlocksEditorComposer } from '@/Components/SuperEditor/BlocksEditorComposer'
|
||||||
|
import BlockPickerMenuPlugin from '@/Components/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin'
|
||||||
|
import usePreference from '@/Hooks/usePreference'
|
||||||
|
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
|
||||||
|
import { EditorLineHeightValues, PrefKey, classNames } from '@standardnotes/snjs'
|
||||||
|
import { CSSProperties, useRef, useState } from 'react'
|
||||||
|
import { SuperDemoInitialValue } from './SuperDemoInitialValue'
|
||||||
|
import { UpgradePrompt } from './UpgradePrompt'
|
||||||
|
import { useApplication } from '@/Components/ApplicationProvider'
|
||||||
|
import { useAutoElementRect } from '@/Hooks/useElementRect'
|
||||||
|
|
||||||
|
const SuperDemo = ({ hasSubscription, onClose }: { hasSubscription: boolean; onClose: () => void }) => {
|
||||||
|
const application = useApplication()
|
||||||
|
|
||||||
|
const lineHeight = usePreference(PrefKey.EditorLineHeight)
|
||||||
|
const fontSize = usePreference(PrefKey.EditorFontSize)
|
||||||
|
const responsiveFontSize = useResponsiveEditorFontSize(fontSize, false)
|
||||||
|
|
||||||
|
const ctaRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
const [demoContainer, setDemoContainer] = useState<HTMLDivElement | null>(null)
|
||||||
|
const demoContainerRect = useAutoElementRect(demoContainer, {
|
||||||
|
updateOnWindowResize: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full flex-col" ref={setDemoContainer}>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'flex-shrink-0 border-b border-border p-4',
|
||||||
|
demoContainerRect && demoContainerRect.height < 500 ? 'hidden md:block' : '',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<UpgradePrompt
|
||||||
|
featureName="Super notes"
|
||||||
|
ctaRef={ctaRef}
|
||||||
|
application={application}
|
||||||
|
hasSubscription={hasSubscription}
|
||||||
|
inline
|
||||||
|
preferHorizontalLayout
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="relative flex h-full min-h-0 flex-col"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--line-height': EditorLineHeightValues[lineHeight],
|
||||||
|
'--font-size': responsiveFontSize,
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BlocksEditorComposer initialValue={SuperDemoInitialValue()}>
|
||||||
|
<BlocksEditor className="blocks-editor h-full bg-default">
|
||||||
|
<BlockPickerMenuPlugin popoverZIndex="z-modal" />
|
||||||
|
</BlocksEditor>
|
||||||
|
</BlocksEditorComposer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SuperDemo
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter'
|
||||||
|
|
||||||
|
const InitialHTML = `<div>
|
||||||
|
<h1>This is a demo of Super notes</h1>
|
||||||
|
<p><br></p>
|
||||||
|
<p>Super notes are our new <b>rich text</b> experience. With Super notes, you can create <b>rich</b>, <i>dynamic</i> text with powerful options.</p>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Images</span></h2>
|
||||||
|
<p><br></p>
|
||||||
|
<p>You can add images to your note by selecting the "Image from URL" option from the <code spellcheck="false"><span>/</span></code> menu or Insert menu in the toolbar.</p>
|
||||||
|
<p><br></p>
|
||||||
|
<p><img src="https://standardnotes.com/static/292c6ba50c69a3ae4f8b1883e7f505f6/1f7f6/vault-wide.jpg" /></p>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Lists</span></h2>
|
||||||
|
<p><br></p>
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>Type </span><code spellcheck="false"><span>-</span></code><span> followed by a space in begin a
|
||||||
|
list</span></li>
|
||||||
|
<li value="2"><span>Type </span><code spellcheck="false"><span>1.</span></code><span> followed by a space in begin a numbered
|
||||||
|
list</span></li>
|
||||||
|
<li value="3"><span>Type </span><code spellcheck="false"><span>[]</span></code><span> followed by a space
|
||||||
|
to begin a checklist </span></li>
|
||||||
|
</ul>
|
||||||
|
<p><br></p>
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>A list</span></li>
|
||||||
|
<li value="2">
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>Indent the list</span></li>
|
||||||
|
<li value="2">
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>And even more</span></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><br></p>
|
||||||
|
<ol>
|
||||||
|
<li value="1"><span>A numbered list</span></li>
|
||||||
|
<li value="2"><span>With multiple levels</span></li>
|
||||||
|
<li value="3"><span>And even more</span></li>
|
||||||
|
</ol>
|
||||||
|
<p><br></p>
|
||||||
|
<ul __lexicallisttype="check">
|
||||||
|
<li role="checkbox" tabindex="-1" aria-checked="false" value="1">
|
||||||
|
<span>Create</span>
|
||||||
|
</li>
|
||||||
|
<li role="checkbox" tabindex="-1" aria-checked="true" value="2">
|
||||||
|
<span>a</span>
|
||||||
|
</li>
|
||||||
|
<li role="checkbox" tabindex="-1" aria-checked="true" value="3">
|
||||||
|
<span>checklist</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Collapsible sections</span></h2>
|
||||||
|
<p><br></p>
|
||||||
|
<details open="">
|
||||||
|
<summary><span>Collapsible section</span></summary>
|
||||||
|
<div data-lexical-collapsible-content="true">
|
||||||
|
<p><span>Collapsible sections can include all
|
||||||
|
other types of content like</span></p>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Heading</span></h2>
|
||||||
|
<p><br></p>
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>a list</span></li>
|
||||||
|
</ul>
|
||||||
|
<ol>
|
||||||
|
<li value="1"><span>numbered</span></li>
|
||||||
|
</ol>
|
||||||
|
<ul __lexicallisttype="check">
|
||||||
|
<li role="checkbox" tabindex="-1" aria-checked="false" value="1"><span>check
|
||||||
|
list</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><br></p>
|
||||||
|
<pre spellcheck="false" data-highlight-language="javascript"><span>A</span><span> code block</span></pre>
|
||||||
|
<p><br></p>
|
||||||
|
<p><span>You can even nest collapsible
|
||||||
|
sections.</span></p>
|
||||||
|
<p><br></p>
|
||||||
|
<details open="">
|
||||||
|
<summary><span>Nested collapsible section</span></summary>
|
||||||
|
<div data-lexical-collapsible-content="true">
|
||||||
|
<blockquote><span>Quote</span></blockquote>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Code blocks</span></h2>
|
||||||
|
<p><br></p>
|
||||||
|
<p><span>Type </span><code spellcheck="false"><span >\`\`\`</span></code><span> followed by a space to create a code
|
||||||
|
block. You can choose the language when your
|
||||||
|
cursor is within the code block.</span></p>
|
||||||
|
<p><br></p>
|
||||||
|
<pre spellcheck="false"
|
||||||
|
data-highlight-language="js"><span >function</span><span> </span><span >main</span><span >(</span><span >)</span><span> </span><span >{</span><br><span> </span><span >const</span><span> variable </span><span >=</span><span> </span><span >"string"</span><span >;</span><br><span> </span><span >return</span><span> </span><span >TEST</span><span >;</span><br><span >}</span></pre>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Tables</span></h2>
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<p><span>Header</span></p>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<p><span>Column 1</span></p>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<p><span>Column 2</span></p>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<p><span>Row 1</span></p>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<p><span>Row 1 x Column 1</span></p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p><span>Row 1 x Column 2</span></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<p><span>Row 2</span></p>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<p><span>Row 2 x Column 1</span></p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p><span>Row 2 x Column 2</span></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><br></p>
|
||||||
|
<h2><span>Passwords</span></h2>
|
||||||
|
<p><span>You can generate a secure password using
|
||||||
|
the "Generate password" command using the </span><code spellcheck="false"><span >/</span></code><span>
|
||||||
|
menu.</span></p>
|
||||||
|
<p><br></p>
|
||||||
|
<ul>
|
||||||
|
<li value="1"><span>}:hcMrIFgaijpkyz</span></li>
|
||||||
|
<li value="2"><span>*raF/qi$m?y?iiBS</span></li>
|
||||||
|
<li value="3"><span>YuVmWf(gOD&=vjbB</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
export function SuperDemoInitialValue() {
|
||||||
|
return new HeadlessSuperConverter().convertOtherFormatToSuperString(InitialHTML, 'html', {
|
||||||
|
html: {
|
||||||
|
addLineBreaks: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ import { useCallback } from 'react'
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
|
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
|
||||||
|
import { classNames } from '@standardnotes/snjs'
|
||||||
|
import { requestCloseAllOpenModalsAndPopovers } from '@/Utils/CloseOpenModalsAndPopovers'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
featureName?: string
|
featureName?: string
|
||||||
@@ -12,10 +14,12 @@ type Props = {
|
|||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
inline: true
|
inline: true
|
||||||
|
preferHorizontalLayout?: boolean
|
||||||
onClose?: never
|
onClose?: never
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
inline?: false
|
inline?: false
|
||||||
|
preferHorizontalLayout?: never
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -28,11 +32,13 @@ export const UpgradePrompt = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onClick,
|
onClick,
|
||||||
inline,
|
inline,
|
||||||
|
preferHorizontalLayout = false,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick()
|
onClick()
|
||||||
}
|
}
|
||||||
|
requestCloseAllOpenModalsAndPopovers()
|
||||||
if (hasSubscription && !application.isNativeIOS()) {
|
if (hasSubscription && !application.isNativeIOS()) {
|
||||||
void application.openSubscriptionDashboard.execute()
|
void application.openSubscriptionDashboard.execute()
|
||||||
} else {
|
} else {
|
||||||
@@ -44,65 +50,76 @@ export const UpgradePrompt = ({
|
|||||||
}, [application, hasSubscription, onClose, onClick])
|
}, [application, hasSubscription, onClose, onClick])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={preferHorizontalLayout ? 'flex flex-wrap items-center gap-4 md:flex-nowrap' : ''}>
|
||||||
<div>
|
{!inline && (
|
||||||
<div className="flex justify-end p-1">
|
<div className="flex justify-end p-1">
|
||||||
{!inline && (
|
<button
|
||||||
<button
|
className="flex cursor-pointer border-0 bg-transparent p-0"
|
||||||
className="flex cursor-pointer border-0 bg-transparent p-0"
|
onClick={onClose}
|
||||||
onClick={onClose}
|
aria-label="Close modal"
|
||||||
aria-label="Close modal"
|
>
|
||||||
>
|
<Icon className="text-neutral" type="close" />
|
||||||
<Icon className="text-neutral" type="close" />
|
</button>
|
||||||
</button>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center justify-center rounded-[50%] bg-contrast',
|
||||||
|
preferHorizontalLayout ? 'h-12 w-12 flex-shrink-0' : 'mx-auto mb-5 h-24 w-24',
|
||||||
|
)}
|
||||||
|
aria-hidden={true}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={classNames(preferHorizontalLayout ? 'h-8 w-8' : 'h-12 w-12', PremiumFeatureIconClass)}
|
||||||
|
size={'custom'}
|
||||||
|
type={PremiumFeatureIconName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={preferHorizontalLayout ? '' : 'mb-2'}>
|
||||||
|
<div className={classNames('mb-1 text-lg font-bold', preferHorizontalLayout ? 'text-left' : 'text-center')}>
|
||||||
|
Enable Advanced Features
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
|
className={classNames('text-sm text-passive-1', preferHorizontalLayout ? 'text-left' : 'px-4.5 text-center')}
|
||||||
aria-hidden={true}
|
|
||||||
>
|
>
|
||||||
<Icon className={`h-12 w-12 ${PremiumFeatureIconClass}`} size={'custom'} type={PremiumFeatureIconName} />
|
{featureName && (
|
||||||
|
<span>
|
||||||
|
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features,
|
||||||
|
upgrade your current plan.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!featureName && (
|
||||||
|
<span>
|
||||||
|
To take advantage of all the advanced features Standard Notes has to offer, upgrade your current plan.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{application.isNativeIOS() && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<div className="mb-2 font-bold">The Professional Plan costs $119.99/year and includes benefits like</div>
|
||||||
|
<ul className="list-inside list-[circle]">
|
||||||
|
<li>100GB encrypted file storage</li>
|
||||||
|
<li>
|
||||||
|
Access to all note types, including Super, markdown, rich text, authenticator, tasks, and spreadsheets
|
||||||
|
</li>
|
||||||
|
<li>Access to Daily Notebooks and Moments journals</li>
|
||||||
|
<li>Note history going back indefinitely</li>
|
||||||
|
<li>Nested folders for your tags</li>
|
||||||
|
<li>Premium support</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-1 text-center text-lg font-bold">Enable Advanced Features</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-2 px-4.5 text-center text-sm text-passive-1">
|
<button
|
||||||
{featureName && (
|
onClick={handleClick}
|
||||||
<span>
|
className={classNames(
|
||||||
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features,
|
'no-border cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125',
|
||||||
upgrade your current plan.
|
preferHorizontalLayout ? 'w-full px-4 md:ml-auto md:w-auto' : 'my-2 w-full',
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
{!featureName && (
|
ref={ctaRef}
|
||||||
<span>
|
>
|
||||||
To take advantage of all the advanced features Standard Notes has to offer, upgrade your current plan.
|
Upgrade
|
||||||
</span>
|
</button>
|
||||||
)}
|
</div>
|
||||||
{application.isNativeIOS() && (
|
|
||||||
<div className="mt-2">
|
|
||||||
<div className="mb-2 font-bold">The Professional Plan costs $119.99/year and includes benefits like</div>
|
|
||||||
<ul className="list-inside list-[circle]">
|
|
||||||
<li>100GB encrypted file storage</li>
|
|
||||||
<li>
|
|
||||||
Access to all note types, including Super, markdown, rich text, authenticator, tasks, and spreadsheets
|
|
||||||
</li>
|
|
||||||
<li>Access to Daily Notebooks and Moments journals</li>
|
|
||||||
<li>Note history going back indefinitely</li>
|
|
||||||
<li>Nested folders for your tags</li>
|
|
||||||
<li>Premium support</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-4">
|
|
||||||
<button
|
|
||||||
onClick={handleClick}
|
|
||||||
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
|
|
||||||
ref={ctaRef}
|
|
||||||
>
|
|
||||||
Upgrade
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const StyledTooltip = ({
|
|||||||
interactive = false,
|
interactive = false,
|
||||||
type = 'label',
|
type = 'label',
|
||||||
side,
|
side,
|
||||||
|
documentElement,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
@@ -28,6 +29,7 @@ const StyledTooltip = ({
|
|||||||
interactive?: boolean
|
interactive?: boolean
|
||||||
type?: TooltipStoreProps['type']
|
type?: TooltipStoreProps['type']
|
||||||
side?: PopoverSide
|
side?: PopoverSide
|
||||||
|
documentElement?: HTMLElement
|
||||||
} & Partial<TooltipOptions>) => {
|
} & Partial<TooltipOptions>) => {
|
||||||
const [forceOpen, setForceOpen] = useState<boolean | undefined>()
|
const [forceOpen, setForceOpen] = useState<boolean | undefined>()
|
||||||
|
|
||||||
@@ -123,15 +125,15 @@ const StyledTooltip = ({
|
|||||||
popoverElement.style.pointerEvents = 'none'
|
popoverElement.style.pointerEvents = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentElement = document.querySelector('.main-ui-view')
|
const documentElementForPopover = documentElement || document.querySelector('.main-ui-view')
|
||||||
|
|
||||||
if (!popoverElement || !anchorElement || !documentElement || !open) {
|
if (!popoverElement || !anchorElement || !documentElementForPopover || !open) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const anchorRect = anchorElement.getBoundingClientRect()
|
const anchorRect = anchorElement.getBoundingClientRect()
|
||||||
const popoverRect = popoverElement.getBoundingClientRect()
|
const popoverRect = popoverElement.getBoundingClientRect()
|
||||||
const documentRect = documentElement.getBoundingClientRect()
|
const documentRect = documentElementForPopover.getBoundingClientRect()
|
||||||
|
|
||||||
const styles = getPositionedPopoverStyles({
|
const styles = getPositionedPopoverStyles({
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -151,7 +153,11 @@ const StyledTooltip = ({
|
|||||||
Object.assign(popoverElement.style, styles)
|
Object.assign(popoverElement.style, styles)
|
||||||
|
|
||||||
if (!props.portal) {
|
if (!props.portal) {
|
||||||
const adjustedStyles = getAdjustedStylesForNonPortalPopover(popoverElement, styles)
|
const adjustedStyles = getAdjustedStylesForNonPortalPopover(
|
||||||
|
popoverElement,
|
||||||
|
styles,
|
||||||
|
props.portalElement instanceof HTMLElement ? props.portalElement : undefined,
|
||||||
|
)
|
||||||
popoverElement.style.setProperty('--translate-x', adjustedStyles['--translate-x'])
|
popoverElement.style.setProperty('--translate-x', adjustedStyles['--translate-x'])
|
||||||
popoverElement.style.setProperty('--translate-y', adjustedStyles['--translate-y'])
|
popoverElement.style.setProperty('--translate-y', adjustedStyles['--translate-y'])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ import TableActionMenuPlugin from './Plugins/TableCellActionMenuPlugin'
|
|||||||
import ToolbarPlugin from './Plugins/ToolbarPlugin/ToolbarPlugin'
|
import ToolbarPlugin from './Plugins/ToolbarPlugin/ToolbarPlugin'
|
||||||
import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
|
||||||
import { CheckListPlugin } from './Plugins/List/CheckListPlugin'
|
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 = {
|
type BlocksEditorProps = {
|
||||||
onChange?: (value: string, preview: string) => void
|
onChange?: (value: string, preview: string) => void
|
||||||
@@ -83,7 +90,10 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
|
|||||||
<div className="editor z-0 overflow-hidden" ref={onRef}>
|
<div className="editor z-0 overflow-hidden" ref={onRef}>
|
||||||
<ContentEditable
|
<ContentEditable
|
||||||
id={SuperEditorContentId}
|
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}
|
spellCheck={spellcheck}
|
||||||
/>
|
/>
|
||||||
<div className="search-highlight-container pointer-events-none absolute left-0 top-0 h-full w-full" />
|
<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 />
|
<CollapsiblePlugin />
|
||||||
<TabIndentationPlugin />
|
<TabIndentationPlugin />
|
||||||
<RemoveBrokenTablesPlugin />
|
<RemoveBrokenTablesPlugin />
|
||||||
|
<RemoteImagePlugin />
|
||||||
|
<CodeOptionsPlugin />
|
||||||
|
<SuperSearchContextProvider>
|
||||||
|
<SearchPlugin />
|
||||||
|
</SuperSearchContextProvider>
|
||||||
|
<DatetimePlugin />
|
||||||
|
<PasswordPlugin />
|
||||||
|
<AutoLinkPlugin />
|
||||||
{!readonly && floatingAnchorElem && (
|
{!readonly && floatingAnchorElem && (
|
||||||
<>
|
<>
|
||||||
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { FunctionComponent } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
import { LexicalComposer, InitialEditorStateType } from '@lexical/react/LexicalComposer'
|
||||||
import BlocksEditorTheme from './Lexical/Theme/Theme'
|
import BlocksEditorTheme from './Lexical/Theme/Theme'
|
||||||
import { BlockEditorNodes } from './Lexical/Nodes/AllNodes'
|
import { BlockEditorNodes } from './Lexical/Nodes/AllNodes'
|
||||||
import { Klass, LexicalNode } from 'lexical'
|
import { Klass, LexicalNode } from 'lexical'
|
||||||
|
|
||||||
type BlocksEditorComposerProps = {
|
type BlocksEditorComposerProps = {
|
||||||
initialValue: string | undefined
|
initialValue: InitialEditorStateType | undefined
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
nodes?: Array<Klass<LexicalNode>>
|
nodes?: Array<Klass<LexicalNode>>
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
@@ -24,7 +24,7 @@ export const BlocksEditorComposer: FunctionComponent<BlocksEditorComposerProps>
|
|||||||
theme: BlocksEditorTheme,
|
theme: BlocksEditorTheme,
|
||||||
editable: !readonly,
|
editable: !readonly,
|
||||||
onError: (error: Error) => console.error(error),
|
onError: (error: Error) => console.error(error),
|
||||||
editorState: initialValue && initialValue.length > 0 ? initialValue : undefined,
|
editorState: initialValue,
|
||||||
nodes: [...nodes, ...BlockEditorNodes],
|
nodes: [...nodes, ...BlockEditorNodes],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||||
import './Modal.css'
|
import './Modal.css'
|
||||||
|
|
||||||
import { ReactNode, useEffect, useRef } from 'react'
|
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
function PortalImpl({
|
function PortalImpl({
|
||||||
@@ -83,11 +84,22 @@ export default function Modal({
|
|||||||
closeOnClickOutside?: boolean
|
closeOnClickOutside?: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
title: string
|
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(
|
return createPortal(
|
||||||
<PortalImpl onClose={onClose} title={title} closeOnClickOutside={closeOnClickOutside}>
|
<PortalImpl onClose={onClose} title={title} closeOnClickOutside={closeOnClickOutside}>
|
||||||
{children}
|
{children}
|
||||||
</PortalImpl>,
|
</PortalImpl>,
|
||||||
document.body,
|
containerElement,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function BlockPickerMenuItem({
|
|||||||
<li
|
<li
|
||||||
key={option.key}
|
key={option.key}
|
||||||
tabIndex={-1}
|
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 : ''
|
isSelected ? PopoverItemSelectedClassNames : ''
|
||||||
}`}
|
}`}
|
||||||
ref={option.setRefElement}
|
ref={option.setRefElement}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { GetDividerBlockOption } from '../Blocks/Divider'
|
|||||||
import { GetCollapsibleBlockOption } from '../Blocks/Collapsible'
|
import { GetCollapsibleBlockOption } from '../Blocks/Collapsible'
|
||||||
import { GetEmbedsBlockOptions } from '../Blocks/Embeds'
|
import { GetEmbedsBlockOptions } from '../Blocks/Embeds'
|
||||||
|
|
||||||
export default function BlockPickerMenuPlugin(): JSX.Element {
|
export default function BlockPickerMenuPlugin({ popoverZIndex }: { popoverZIndex?: string }): JSX.Element {
|
||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
const application = useApplication()
|
const application = useApplication()
|
||||||
const [modal, showModal] = useModal()
|
const [modal, showModal] = useModal()
|
||||||
@@ -130,6 +130,7 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
|
|||||||
disableMobileFullscreenTakeover={true}
|
disableMobileFullscreenTakeover={true}
|
||||||
side={isMobileScreen() ? 'top' : 'bottom'}
|
side={isMobileScreen() ? 'top' : 'bottom'}
|
||||||
maxHeight={(mh) => mh / 2}
|
maxHeight={(mh) => mh / 2}
|
||||||
|
overrideZIndex={popoverZIndex}
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
{options.map((option, i: number) => (
|
{options.map((option, i: number) => (
|
||||||
|
|||||||
@@ -113,8 +113,19 @@ const ToolbarButton = forwardRef(
|
|||||||
) => {
|
) => {
|
||||||
const [editor] = useLexicalComposerContext()
|
const [editor] = useLexicalComposerContext()
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||||
|
const parentElement = editor.getRootElement()?.parentElement ?? document.body
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTooltip showOnMobile showOnHover label={name} side="top">
|
<StyledTooltip
|
||||||
|
showOnMobile
|
||||||
|
showOnHover
|
||||||
|
label={name}
|
||||||
|
side="top"
|
||||||
|
portal={false}
|
||||||
|
portalElement={isMobile ? parentElement : undefined}
|
||||||
|
documentElement={parentElement}
|
||||||
|
>
|
||||||
<ToolbarItem
|
<ToolbarItem
|
||||||
className={classNames(
|
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]',
|
'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,
|
toolbarStore,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const popoverDocumentElement = editor.getRootElement()?.parentElement ?? document.body
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{modal}
|
{modal}
|
||||||
@@ -746,6 +759,8 @@ const ToolbarPlugin = () => {
|
|||||||
className="py-1"
|
className="py-1"
|
||||||
disableMobileFullscreenTakeover
|
disableMobileFullscreenTakeover
|
||||||
disableFlip
|
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>
|
<div className="mb-1.5 mt-1 px-3 text-sm font-semibold uppercase text-text">Table of Contents</div>
|
||||||
<LexicalTableOfContents>
|
<LexicalTableOfContents>
|
||||||
@@ -800,6 +815,8 @@ const ToolbarPlugin = () => {
|
|||||||
disableMobileFullscreenTakeover
|
disableMobileFullscreenTakeover
|
||||||
disableFlip
|
disableFlip
|
||||||
containerClassName="md:!min-w-60 md:!w-auto"
|
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)}>
|
<Menu a11yLabel="Text formatting options" className="!px-0" onClick={() => setIsTextFormatMenuOpen(false)}>
|
||||||
<ToolbarMenuItem
|
<ToolbarMenuItem
|
||||||
@@ -839,6 +856,8 @@ const ToolbarPlugin = () => {
|
|||||||
disableMobileFullscreenTakeover
|
disableMobileFullscreenTakeover
|
||||||
disableFlip
|
disableFlip
|
||||||
containerClassName="md:!min-w-60 md:!w-auto"
|
containerClassName="md:!min-w-60 md:!w-auto"
|
||||||
|
portal={false}
|
||||||
|
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||||
>
|
>
|
||||||
<Menu a11yLabel="Text style" className="!px-0" onClick={() => setIsTextStyleMenuOpen(false)}>
|
<Menu a11yLabel="Text style" className="!px-0" onClick={() => setIsTextStyleMenuOpen(false)}>
|
||||||
<ToolbarMenuItem
|
<ToolbarMenuItem
|
||||||
@@ -910,6 +929,8 @@ const ToolbarPlugin = () => {
|
|||||||
disableMobileFullscreenTakeover
|
disableMobileFullscreenTakeover
|
||||||
disableFlip
|
disableFlip
|
||||||
containerClassName="md:!min-w-60 md:!w-auto"
|
containerClassName="md:!min-w-60 md:!w-auto"
|
||||||
|
portal={false}
|
||||||
|
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||||
>
|
>
|
||||||
<Menu a11yLabel="Alignment" className="!px-0" onClick={() => setIsAlignmentMenuOpen(false)}>
|
<Menu a11yLabel="Alignment" className="!px-0" onClick={() => setIsAlignmentMenuOpen(false)}>
|
||||||
<ToolbarMenuItem
|
<ToolbarMenuItem
|
||||||
@@ -949,6 +970,8 @@ const ToolbarPlugin = () => {
|
|||||||
disableMobileFullscreenTakeover
|
disableMobileFullscreenTakeover
|
||||||
disableFlip
|
disableFlip
|
||||||
containerClassName="md:!min-w-60 md:!w-auto"
|
containerClassName="md:!min-w-60 md:!w-auto"
|
||||||
|
portal={false}
|
||||||
|
documentElement={isMobile ? popoverDocumentElement : undefined}
|
||||||
>
|
>
|
||||||
<Menu a11yLabel="Insert" className="!px-0" onClick={() => setIsInsertMenuOpen(false)}>
|
<Menu a11yLabel="Insert" className="!px-0" onClick={() => setIsInsertMenuOpen(false)}>
|
||||||
<ToolbarMenuItem
|
<ToolbarMenuItem
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import {
|
import {
|
||||||
classNames,
|
|
||||||
isPayloadSourceRetrieved,
|
isPayloadSourceRetrieved,
|
||||||
PrefKey,
|
PrefKey,
|
||||||
NativeFeatureIdentifier,
|
NativeFeatureIdentifier,
|
||||||
@@ -14,7 +13,6 @@ import { BlocksEditorComposer } from './BlocksEditorComposer'
|
|||||||
import { ItemSelectionPlugin } from './Plugins/ItemSelectionPlugin/ItemSelectionPlugin'
|
import { ItemSelectionPlugin } from './Plugins/ItemSelectionPlugin/ItemSelectionPlugin'
|
||||||
import { FileNode } from './Plugins/EncryptedFilePlugin/Nodes/FileNode'
|
import { FileNode } from './Plugins/EncryptedFilePlugin/Nodes/FileNode'
|
||||||
import FilePlugin from './Plugins/EncryptedFilePlugin/FilePlugin'
|
import FilePlugin from './Plugins/EncryptedFilePlugin/FilePlugin'
|
||||||
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
|
|
||||||
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
|
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
|
||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
import LinkingControllerProvider from '../../Controllers/LinkingControllerProvider'
|
import LinkingControllerProvider from '../../Controllers/LinkingControllerProvider'
|
||||||
@@ -23,28 +21,22 @@ import ItemBubblePlugin from './Plugins/ItemBubblePlugin/ItemBubblePlugin'
|
|||||||
import { NodeObserverPlugin } from './Plugins/NodeObserverPlugin/NodeObserverPlugin'
|
import { NodeObserverPlugin } from './Plugins/NodeObserverPlugin/NodeObserverPlugin'
|
||||||
import { FilesController } from '@/Controllers/FilesController'
|
import { FilesController } from '@/Controllers/FilesController'
|
||||||
import FilesControllerProvider from '@/Controllers/FilesControllerProvider'
|
import FilesControllerProvider from '@/Controllers/FilesControllerProvider'
|
||||||
import DatetimePlugin from './Plugins/DateTimePlugin/DateTimePlugin'
|
|
||||||
import AutoLinkPlugin from './Plugins/AutoLinkPlugin/AutoLinkPlugin'
|
|
||||||
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
||||||
import {
|
import {
|
||||||
ChangeContentCallbackPlugin,
|
ChangeContentCallbackPlugin,
|
||||||
ChangeEditorFunction,
|
ChangeEditorFunction,
|
||||||
} from './Plugins/ChangeContentCallback/ChangeContentCallback'
|
} from './Plugins/ChangeContentCallback/ChangeContentCallback'
|
||||||
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
|
|
||||||
import { useCommandService } from '@/Components/CommandProvider'
|
import { useCommandService } from '@/Components/CommandProvider'
|
||||||
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
|
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
|
||||||
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
|
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
|
||||||
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
|
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
|
||||||
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
|
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
|
||||||
import ReadonlyPlugin from './Plugins/ReadonlyPlugin/ReadonlyPlugin'
|
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 ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||||
import CodeOptionsPlugin from './Plugins/CodeOptionsPlugin/CodeOptions'
|
|
||||||
import RemoteImagePlugin from './Plugins/RemoteImagePlugin/RemoteImagePlugin'
|
|
||||||
import NotEntitledBanner from '../ComponentView/NotEntitledBanner'
|
import NotEntitledBanner from '../ComponentView/NotEntitledBanner'
|
||||||
import AutoFocusPlugin from './Plugins/AutoFocusPlugin'
|
import AutoFocusPlugin from './Plugins/AutoFocusPlugin'
|
||||||
import usePreference from '@/Hooks/usePreference'
|
import usePreference from '@/Hooks/usePreference'
|
||||||
|
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
|
||||||
|
|
||||||
export const SuperNotePreviewCharLimit = 160
|
export const SuperNotePreviewCharLimit = 160
|
||||||
|
|
||||||
@@ -207,10 +199,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
|||||||
<BlocksEditorComposer readonly={note.current.locked || readonly} initialValue={note.current.text}>
|
<BlocksEditorComposer readonly={note.current.locked || readonly} initialValue={note.current.text}>
|
||||||
<BlocksEditor
|
<BlocksEditor
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={classNames(
|
className="blocks-editor h-full resize-none"
|
||||||
'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]',
|
|
||||||
)}
|
|
||||||
previewLength={SuperNotePreviewCharLimit}
|
previewLength={SuperNotePreviewCharLimit}
|
||||||
spellcheck={spellcheck}
|
spellcheck={spellcheck}
|
||||||
readonly={note.current.locked || readonly}
|
readonly={note.current.locked || readonly}
|
||||||
@@ -218,11 +207,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
|||||||
<ItemSelectionPlugin currentNote={note.current} />
|
<ItemSelectionPlugin currentNote={note.current} />
|
||||||
<FilePlugin currentNote={note.current} />
|
<FilePlugin currentNote={note.current} />
|
||||||
<ItemBubblePlugin />
|
<ItemBubblePlugin />
|
||||||
<BlockPickerMenuPlugin />
|
|
||||||
<GetMarkdownPlugin ref={getMarkdownPlugin} />
|
<GetMarkdownPlugin ref={getMarkdownPlugin} />
|
||||||
<DatetimePlugin />
|
|
||||||
<PasswordPlugin />
|
|
||||||
<AutoLinkPlugin />
|
|
||||||
<ChangeContentCallbackPlugin
|
<ChangeContentCallbackPlugin
|
||||||
providerCallback={(callback) => (changeEditorFunction.current = callback)}
|
providerCallback={(callback) => (changeEditorFunction.current = callback)}
|
||||||
/>
|
/>
|
||||||
@@ -230,11 +215,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
|
|||||||
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
|
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
|
||||||
{readonly === undefined && <ReadonlyPlugin note={note.current} />}
|
{readonly === undefined && <ReadonlyPlugin note={note.current} />}
|
||||||
<AutoFocusPlugin isEnabled={controller.isTemplateNote} />
|
<AutoFocusPlugin isEnabled={controller.isTemplateNote} />
|
||||||
<SuperSearchContextProvider>
|
<BlockPickerMenuPlugin />
|
||||||
<SearchPlugin />
|
|
||||||
</SuperSearchContextProvider>
|
|
||||||
<CodeOptionsPlugin />
|
|
||||||
<RemoteImagePlugin />
|
|
||||||
</BlocksEditor>
|
</BlocksEditor>
|
||||||
</BlocksEditorComposer>
|
</BlocksEditorComposer>
|
||||||
</FilesControllerProvider>
|
</FilesControllerProvider>
|
||||||
|
|||||||
@@ -155,7 +155,11 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
convertOtherFormatToSuperString(otherFormatString: string, fromFormat: 'txt' | 'md' | 'html' | 'json'): string {
|
convertOtherFormatToSuperString: SuperConverterServiceInterface['convertOtherFormatToSuperString'] = (
|
||||||
|
otherFormatString,
|
||||||
|
fromFormat,
|
||||||
|
options,
|
||||||
|
) => {
|
||||||
if (otherFormatString.length === 0) {
|
if (otherFormatString.length === 0) {
|
||||||
return otherFormatString
|
return otherFormatString
|
||||||
}
|
}
|
||||||
@@ -175,6 +179,10 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
|||||||
|
|
||||||
let didThrow = false
|
let didThrow = false
|
||||||
if (fromFormat === 'html') {
|
if (fromFormat === 'html') {
|
||||||
|
const htmlOptions = options?.html || {
|
||||||
|
addLineBreaks: true,
|
||||||
|
}
|
||||||
|
|
||||||
this.importEditor.update(
|
this.importEditor.update(
|
||||||
() => {
|
() => {
|
||||||
try {
|
try {
|
||||||
@@ -203,7 +211,9 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
|||||||
nodesToInsert.push(node)
|
nodesToInsert.push(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesToInsert.push($createParagraphNode())
|
if (htmlOptions.addLineBreaks) {
|
||||||
|
nodesToInsert.push($createParagraphNode())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
$getRoot().selectEnd()
|
$getRoot().selectEnd()
|
||||||
$insertNodes(nodesToInsert.concat($createParagraphNode()))
|
$insertNodes(nodesToInsert.concat($createParagraphNode()))
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ export class FeaturesController extends AbstractViewController implements Intern
|
|||||||
this.premiumAlertType = PremiumFeatureModalType.UpgradeSuccess
|
this.premiumAlertType = PremiumFeatureModalType.UpgradeSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSuperDemoModal = () => {
|
||||||
|
this.premiumAlertType = PremiumFeatureModalType.SuperDemo
|
||||||
|
}
|
||||||
|
|
||||||
public closePremiumAlert() {
|
public closePremiumAlert() {
|
||||||
this.premiumAlertType = undefined
|
this.premiumAlertType = undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent, createContext, useCallback, useContext, ReactNode } from 'react'
|
import { FunctionComponent, createContext, useCallback, useContext, ReactNode, useMemo } from 'react'
|
||||||
import PremiumFeaturesModal from '@/Components/PremiumFeaturesModal/PremiumFeaturesModal'
|
import PremiumFeaturesModal from '@/Components/PremiumFeaturesModal/PremiumFeaturesModal'
|
||||||
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||||
|
import { classNames } from '@standardnotes/snjs'
|
||||||
|
import { PremiumFeatureModalType } from '@/Components/PremiumFeaturesModal/PremiumFeatureModalType'
|
||||||
|
|
||||||
type PremiumModalContextData = {
|
type PremiumModalContextData = {
|
||||||
activate: (featureName: string) => void
|
activate: (featureName: string) => void
|
||||||
|
showSuperDemo: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PremiumModalContext = createContext<PremiumModalContextData | null>(null)
|
const PremiumModalContext = createContext<PremiumModalContextData | null>(null)
|
||||||
@@ -43,12 +46,23 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(({ application,
|
|||||||
application.featuresController.closePremiumAlert()
|
application.featuresController.closePremiumAlert()
|
||||||
}, [application.featuresController])
|
}, [application.featuresController])
|
||||||
|
|
||||||
|
const showSuperDemo = useCallback(() => {
|
||||||
|
application.featuresController.showSuperDemoModal()
|
||||||
|
}, [application.featuresController])
|
||||||
|
|
||||||
|
const value: PremiumModalContextData = useMemo(() => ({ activate, showSuperDemo }), [activate, showSuperDemo])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ModalOverlay
|
<ModalOverlay
|
||||||
isOpen={application.featuresController.premiumAlertType != undefined}
|
isOpen={application.featuresController.premiumAlertType != undefined}
|
||||||
close={close}
|
close={close}
|
||||||
className="w-full max-w-[90vw] !h-auto md:max-w-89"
|
className={classNames(
|
||||||
|
'w-full',
|
||||||
|
application.featuresController.premiumAlertType === PremiumFeatureModalType.SuperDemo
|
||||||
|
? 'md:!h-full md:w-full md:max-w-[70vw]'
|
||||||
|
: '!h-auto max-w-[90vw] md:max-w-89',
|
||||||
|
)}
|
||||||
backdropClassName="!opacity-50"
|
backdropClassName="!opacity-50"
|
||||||
>
|
>
|
||||||
<PremiumFeaturesModal
|
<PremiumFeaturesModal
|
||||||
@@ -59,7 +73,7 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(({ application,
|
|||||||
type={application.featuresController.premiumAlertType!}
|
type={application.featuresController.premiumAlertType!}
|
||||||
/>
|
/>
|
||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
<PremiumModalProvider_ value={{ activate }}>{children}</PremiumModalProvider_>
|
<PremiumModalProvider_ value={value}>{children}</PremiumModalProvider_>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user