fix(mobile): increase font sizes and other mobile-centric improvements (#1907)
This commit is contained in:
@@ -111,7 +111,7 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
|||||||
// For Mac inset window
|
// For Mac inset window
|
||||||
const sheet = document.styleSheets[0]
|
const sheet = document.styleSheets[0]
|
||||||
if (isMacOS) {
|
if (isMacOS) {
|
||||||
sheet.insertRule('#navigation { padding-top: 25px !important; }', sheet.cssRules.length)
|
sheet.insertRule('#navigation-content { padding-top: 25px !important; }', sheet.cssRules.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMacOS || useSystemMenuBar) {
|
if (isMacOS || useSystemMenuBar) {
|
||||||
|
|||||||
@@ -58,12 +58,34 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
|||||||
device.reloadStatusBarStyle(false)
|
device.reloadStatusBarStyle(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const keyboardWillChangeFrame = Keyboard.addListener('keyboardWillChangeFrame', (e) => {
|
||||||
|
webViewRef.current?.postMessage(
|
||||||
|
JSON.stringify({
|
||||||
|
reactNativeEvent: ReactNativeToWebEvent.KeyboardFrameWillChange,
|
||||||
|
messageType: 'event',
|
||||||
|
messageData: { height: e.endCoordinates.height, contentHeight: e.endCoordinates.screenY },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const keyboardDidChangeFrame = Keyboard.addListener('keyboardDidChangeFrame', (e) => {
|
||||||
|
webViewRef.current?.postMessage(
|
||||||
|
JSON.stringify({
|
||||||
|
reactNativeEvent: ReactNativeToWebEvent.KeyboardFrameDidChange,
|
||||||
|
messageType: 'event',
|
||||||
|
messageData: { height: e.endCoordinates.height, contentHeight: e.endCoordinates.screenY },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
removeStateServiceListener()
|
removeStateServiceListener()
|
||||||
removeBackHandlerServiceListener()
|
removeBackHandlerServiceListener()
|
||||||
removeColorSchemeServiceListener()
|
removeColorSchemeServiceListener()
|
||||||
keyboardShowListener.remove()
|
keyboardShowListener.remove()
|
||||||
keyboardHideListener.remove()
|
keyboardHideListener.remove()
|
||||||
|
keyboardWillChangeFrame.remove()
|
||||||
|
keyboardDidChangeFrame.remove()
|
||||||
}
|
}
|
||||||
}, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService])
|
}, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService])
|
||||||
|
|
||||||
@@ -198,7 +220,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
|||||||
|
|
||||||
const onFunctionMessage = async (functionName: string, messageId: string, args: any) => {
|
const onFunctionMessage = async (functionName: string, messageId: string, args: any) => {
|
||||||
const returnValue = await (device as any)[functionName](...args)
|
const returnValue = await (device as any)[functionName](...args)
|
||||||
if (LoggingEnabled) {
|
if (LoggingEnabled && functionName !== 'consoleLog') {
|
||||||
console.log(`Native device function ${functionName} called`)
|
console.log(`Native device function ${functionName} called`)
|
||||||
}
|
}
|
||||||
webViewRef.current?.postMessage(JSON.stringify({ messageId, returnValue, messageType: 'reply' }))
|
webViewRef.current?.postMessage(JSON.stringify({ messageId, returnValue, messageType: 'reply' }))
|
||||||
@@ -253,6 +275,12 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
|||||||
injectedJavaScriptBeforeContentLoaded={injectedJS}
|
injectedJavaScriptBeforeContentLoaded={injectedJS}
|
||||||
bounces={false}
|
bounces={false}
|
||||||
keyboardDisplayRequiresUserAction={false}
|
keyboardDisplayRequiresUserAction={false}
|
||||||
|
scalesPageToFit={true}
|
||||||
|
/**
|
||||||
|
* This disables the global window scroll but keeps scroll within div elements like lists and textareas.
|
||||||
|
* This is needed to prevent the keyboard from pushing the webview up and down when it appears and disappears.
|
||||||
|
*/
|
||||||
|
scrollEnabled={false}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export interface WebApplicationInterface extends ApplicationInterface {
|
|||||||
handleMobileLosingFocusEvent(): Promise<void>
|
handleMobileLosingFocusEvent(): Promise<void>
|
||||||
handleMobileResumingFromBackgroundEvent(): Promise<void>
|
handleMobileResumingFromBackgroundEvent(): Promise<void>
|
||||||
handleMobileColorSchemeChangeEvent(): void
|
handleMobileColorSchemeChangeEvent(): void
|
||||||
|
handleMobileKeyboardWillChangeFrameEvent(frame: { height: number; contentHeight: number }): void
|
||||||
|
handleMobileKeyboardDidChangeFrameEvent(frame: { height: number; contentHeight: number }): void
|
||||||
isNativeMobileWeb(): boolean
|
isNativeMobileWeb(): boolean
|
||||||
mobileDevice(): MobileDeviceInterface
|
mobileDevice(): MobileDeviceInterface
|
||||||
handleAndroidBackButtonPressed(): void
|
handleAndroidBackButtonPressed(): void
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ export enum WebAppEvent {
|
|||||||
PanelResized = 'PanelResized',
|
PanelResized = 'PanelResized',
|
||||||
WindowDidFocus = 'WindowDidFocus',
|
WindowDidFocus = 'WindowDidFocus',
|
||||||
WindowDidBlur = 'WindowDidBlur',
|
WindowDidBlur = 'WindowDidBlur',
|
||||||
|
MobileKeyboardDidChangeFrame = 'MobileKeyboardDidChangeFrame',
|
||||||
|
MobileKeyboardWillChangeFrame = 'MobileKeyboardWillChangeFrame',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ export enum ReactNativeToWebEvent {
|
|||||||
LosingFocus = 'LosingFocus',
|
LosingFocus = 'LosingFocus',
|
||||||
AndroidBackButtonPressed = 'AndroidBackButtonPressed',
|
AndroidBackButtonPressed = 'AndroidBackButtonPressed',
|
||||||
ColorSchemeChanged = 'ColorSchemeChanged',
|
ColorSchemeChanged = 'ColorSchemeChanged',
|
||||||
|
KeyboardFrameWillChange = 'KeyboardFrameWillChange',
|
||||||
|
KeyboardFrameDidChange = 'KeyboardFrameDidChange',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
||||||
.sk-panel-content {
|
|
||||||
// padding: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sk-panel-header {
|
.sk-panel-header {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
--sn-stylekit-base-font-size: 0.8125rem;
|
--sn-stylekit-base-font-size: 0.8125rem;
|
||||||
|
|
||||||
--sn-stylekit-font-size-p: 0.8125rem;
|
--sn-stylekit-font-size-p: 0.8125rem;
|
||||||
--sn-stylekit-font-size-editor: 0.983125rem;
|
--sn-stylekit-font-size-editor: 0.9375rem;
|
||||||
|
|
||||||
--sn-stylekit-font-size-h6: 0.65rem;
|
--sn-stylekit-font-size-h6: 0.65rem;
|
||||||
--sn-stylekit-font-size-h5: 0.73125rem;
|
--sn-stylekit-font-size-h5: 0.73125rem;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import {
|
|||||||
import { MobileWebReceiver, NativeMobileEventListener } from '../NativeMobileWeb/MobileWebReceiver'
|
import { MobileWebReceiver, NativeMobileEventListener } from '../NativeMobileWeb/MobileWebReceiver'
|
||||||
import { AndroidBackHandler } from '@/NativeMobileWeb/AndroidBackHandler'
|
import { AndroidBackHandler } from '@/NativeMobileWeb/AndroidBackHandler'
|
||||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||||
import { setViewportHeightWithFallback } from '@/setViewportHeightWithFallback'
|
import { setCustomViewportHeight, setViewportHeightWithFallback } from '@/setViewportHeightWithFallback'
|
||||||
import { WebServices } from './WebServices'
|
import { WebServices } from './WebServices'
|
||||||
|
|
||||||
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
||||||
@@ -293,6 +293,15 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
void this.getThemeService().handleMobileColorSchemeChangeEvent()
|
void this.getThemeService().handleMobileColorSchemeChangeEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMobileKeyboardWillChangeFrameEvent(frame: { height: number; contentHeight: number }): void {
|
||||||
|
setCustomViewportHeight(String(frame.contentHeight), 'px', true)
|
||||||
|
this.notifyWebEvent(WebAppEvent.MobileKeyboardWillChangeFrame, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMobileKeyboardDidChangeFrameEvent(frame: { height: number; contentHeight: number }): void {
|
||||||
|
this.notifyWebEvent(WebAppEvent.MobileKeyboardDidChangeFrame, frame)
|
||||||
|
}
|
||||||
|
|
||||||
private async lockApplicationAfterMobileEventIfApplicable(): Promise<void> {
|
private async lockApplicationAfterMobileEventIfApplicable(): Promise<void> {
|
||||||
const isLocked = await this.isLocked()
|
const isLocked = await this.isLocked()
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import WorkspaceSwitcherOption from './WorkspaceSwitcher/WorkspaceSwitcherOption
|
|||||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||||
import { formatLastSyncDate } from '@/Utils/DateUtils'
|
import { formatLastSyncDate } from '@/Utils/DateUtils'
|
||||||
import Spinner from '@/Components/Spinner/Spinner'
|
import Spinner from '@/Components/Spinner/Spinner'
|
||||||
|
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
viewControllerManager: ViewControllerManager
|
viewControllerManager: ViewControllerManager
|
||||||
@@ -23,7 +24,7 @@ type Props = {
|
|||||||
closeMenu: () => void
|
closeMenu: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconClassName = 'text-neutral mr-2'
|
const iconClassName = `text-neutral mr-2 ${MenuItemIconSize}`
|
||||||
|
|
||||||
const GeneralAccountMenu: FunctionComponent<Props> = ({
|
const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||||
application,
|
application,
|
||||||
@@ -90,48 +91,48 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mt-1 mb-1 flex items-center justify-between px-3">
|
<div className="mt-1 mb-1 flex items-center justify-between px-3">
|
||||||
<div className="text-base font-bold">Account</div>
|
<div className="text-lg font-bold lg:text-base">Account</div>
|
||||||
<div className="flex cursor-pointer" onClick={closeMenu}>
|
<div className="flex cursor-pointer" onClick={closeMenu}>
|
||||||
<Icon type="close" className="text-neutral" />
|
<Icon type="close" className="text-neutral" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{user ? (
|
{user ? (
|
||||||
<>
|
<>
|
||||||
<div className="mb-3 px-3 text-sm text-foreground">
|
<div className="mb-3 px-3 text-lg text-foreground lg:text-sm">
|
||||||
<div>You're signed in as:</div>
|
<div>You're signed in as:</div>
|
||||||
<div className="wrap my-0.5 font-bold">{user.email}</div>
|
<div className="wrap my-0.5 font-bold">{user.email}</div>
|
||||||
<span className="text-neutral">{application.getHost()}</span>
|
<span className="text-neutral">{application.getHost()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-2 flex items-start justify-between px-3">
|
<div className="mb-2 flex items-start justify-between px-3">
|
||||||
{isSyncingInProgress ? (
|
{isSyncingInProgress ? (
|
||||||
<div className="flex items-center text-sm font-semibold text-info">
|
<div className="flex items-center text-base font-semibold text-info lg:text-sm">
|
||||||
<Spinner className="mr-2 h-5 w-5" />
|
<Spinner className="mr-2 h-5 w-5" />
|
||||||
Syncing...
|
Syncing...
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-start">
|
<div className="flex items-start">
|
||||||
<Icon type="check-circle" className="mr-2 text-success" />
|
<Icon type="check-circle" className={`mr-2 text-success ${MenuItemIconSize}`} />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-semibold text-success">Last synced:</div>
|
<div className="text-base font-semibold text-success lg:text-sm">Last synced:</div>
|
||||||
<div className="text-sm text-text">{lastSyncDate}</div>
|
<div className="text-base text-text lg:text-sm">{lastSyncDate}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex cursor-pointer text-passive-1" onClick={doSynchronization}>
|
<div className="flex cursor-pointer text-passive-1" onClick={doSynchronization}>
|
||||||
<Icon type="sync" />
|
<Icon type="sync" className={`${MenuItemIconSize}`} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="mb-1 px-3">
|
<div className="mb-1 px-3">
|
||||||
<div className="mb-3 text-sm text-foreground">
|
<div className="mb-3 text-base text-foreground lg:text-sm">
|
||||||
You’re offline. Sign in to sync your notes and preferences across all your devices and enable end-to-end
|
You’re offline. Sign in to sync your notes and preferences across all your devices and enable end-to-end
|
||||||
encryption.
|
encryption.
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-passive-1">
|
<div className="flex items-center text-passive-1">
|
||||||
<Icon type="cloud-off" className="mr-2" />
|
<Icon type="cloud-off" className={`mr-2 ${MenuItemIconSize}`} />
|
||||||
<span className="text-sm font-semibold">Offline</span>
|
<span className="text-lg font-semibold lg:text-sm">Offline</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu'
|
|||||||
import MenuItem from '@/Components/Menu/MenuItem'
|
import MenuItem from '@/Components/Menu/MenuItem'
|
||||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||||
import Popover from '@/Components/Popover/Popover'
|
import Popover from '@/Components/Popover/Popover'
|
||||||
|
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
mainApplicationGroup: ApplicationGroup
|
mainApplicationGroup: ApplicationGroup
|
||||||
@@ -32,10 +33,10 @@ const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGrou
|
|||||||
className="justify-between"
|
className="justify-between"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon type="user-switch" className="mr-2 text-neutral" />
|
<Icon type="user-switch" className={`mr-2 text-neutral ${MenuItemIconSize}`} />
|
||||||
Switch workspace
|
Switch workspace
|
||||||
</div>
|
</div>
|
||||||
<Icon type="chevron-right" className="text-neutral" />
|
<Icon type="chevron-right" className={`text-neutral ${MenuItemIconSize}`} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Popover
|
<Popover
|
||||||
align="end"
|
align="end"
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class ApplicationGroupView extends Component<Props, State> {
|
|||||||
'challenge-modal shadow-overlay-light relative flex flex-col items-center rounded border border-solid border-border bg-default p-8'
|
'challenge-modal shadow-overlay-light relative flex flex-col items-center rounded border border-solid border-border bg-default p-8'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{message}
|
<div className="text-base lg:text-xs">{message}</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</DialogOverlay>
|
</DialogOverlay>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
base: 'active:border-info active:bg-info active:text-neutral-contrast flex-grow cursor-pointer rounded-full border border-solid px-2 py-1 text-center transition',
|
base: 'active:border-info active:bg-info active:text-neutral-contrast flex-grow cursor-pointer rounded-full border border-solid px-2 py-1 text-center transition text-sm',
|
||||||
unselected: 'text-neutral border-secondary-border bg-default',
|
unselected: 'text-neutral border-secondary-border bg-default',
|
||||||
selected: 'text-neutral-contrast border-info bg-info',
|
selected: 'text-neutral-contrast border-info bg-info',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const getClassName = (
|
|||||||
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
||||||
const width = fullWidth ? 'w-full' : 'w-fit'
|
const width = fullWidth ? 'w-full' : 'w-fit'
|
||||||
const padding = small ? 'px-3 py-1.5' : 'px-4 py-1.5'
|
const padding = small ? 'px-3 py-1.5' : 'px-4 py-1.5'
|
||||||
const textSize = small ? 'text-xs' : 'text-sm'
|
const textSize = small ? 'text-sm lg:text-xs' : 'text-base lg:text-sm'
|
||||||
const rounded = isRounded ? 'rounded' : ''
|
const rounded = isRounded ? 'rounded' : ''
|
||||||
|
|
||||||
let colors = primary ? getColorsForPrimaryVariant(style) : getColorsForNormalVariant(style)
|
let colors = primary ? getColorsForPrimaryVariant(style) : getColorsForNormalVariant(style)
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ const RoundIconButton = forwardRef(
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'bg-text-padding m-0 flex h-8.5 min-w-8.5 cursor-pointer items-center justify-center rounded-full border border-solid border-border bg-clip-padding text-neutral hover:bg-contrast hover:text-text focus:bg-contrast focus:text-text focus:outline-none focus:ring-info md:h-8 md:min-w-8',
|
'bg-text-padding m-0 flex h-10 min-w-10 cursor-pointer items-center justify-center rounded-full border',
|
||||||
|
'border-solid border-border bg-clip-padding text-neutral hover:bg-contrast hover:text-text focus:bg-contrast',
|
||||||
|
'focus:text-text focus:outline-none focus:ring-info md:h-8 md:min-w-8',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={click}
|
onClick={click}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
|||||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||||
import { ChallengeModalValues } from './ChallengeModalValues'
|
import { ChallengeModalValues } from './ChallengeModalValues'
|
||||||
import { InputValue } from './InputValue'
|
import { InputValue } from './InputValue'
|
||||||
|
import { isMobileScreen } from '@/Utils'
|
||||||
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -211,23 +213,24 @@ const ChallengeModal: FunctionComponent<Props> = ({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFullScreenBlocker = challenge.reason === ChallengeReason.ApplicationUnlock
|
||||||
|
const isMobileOverlay = isMobileScreen() && !isFullScreenBlocker
|
||||||
|
|
||||||
|
const contentClasses = classNames(
|
||||||
|
'challenge-modal relative flex flex-col items-center rounded border-solid border-border p-8 md:border',
|
||||||
|
!isMobileScreen() && 'shadow-overlay-light',
|
||||||
|
isMobileOverlay && 'border border-solid border-border shadow-overlay-light',
|
||||||
|
isFullScreenBlocker && isMobileScreen() ? 'bg-passive-5' : 'bg-default',
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogOverlay
|
<DialogOverlay
|
||||||
className={`sn-component ${
|
className={`sn-component ${isFullScreenBlocker ? 'bg-passive-5' : ''}`}
|
||||||
challenge.reason === ChallengeReason.ApplicationUnlock ? 'challenge-modal-overlay' : ''
|
|
||||||
}`}
|
|
||||||
onDismiss={cancelChallenge}
|
onDismiss={cancelChallenge}
|
||||||
dangerouslyBypassFocusLock={bypassModalFocusLock}
|
dangerouslyBypassFocusLock={bypassModalFocusLock}
|
||||||
key={challenge.id}
|
key={challenge.id}
|
||||||
>
|
>
|
||||||
<DialogContent
|
<DialogContent aria-label="Challenge modal" className={contentClasses}>
|
||||||
aria-label="Challenge modal"
|
|
||||||
className={`challenge-modal relative flex flex-col items-center rounded bg-default p-8 ${
|
|
||||||
challenge.reason !== ChallengeReason.ApplicationUnlock
|
|
||||||
? 'shadow-overlay-light border border-solid border-border'
|
|
||||||
: 'focus:shadow-none'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{challenge.cancelable && (
|
{challenge.cancelable && (
|
||||||
<button
|
<button
|
||||||
onClick={cancelChallenge}
|
onClick={cancelChallenge}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const PageSize = 2
|
|||||||
|
|
||||||
const InfiniteCalendar = forwardRef<InfiniteCalendarInterface, Props>(
|
const InfiniteCalendar = forwardRef<InfiniteCalendarInterface, Props>(
|
||||||
({ activities, onDateSelect, selectedDay, className }: Props, ref) => {
|
({ activities, onDateSelect, selectedDay, className }: Props, ref) => {
|
||||||
const [expanded, setExpanded] = useState(true)
|
const [expanded, setExpanded] = useState(isMobileScreen() ? false : true)
|
||||||
const [restoreScrollAfterExpand, setRestoreScrollAfterExpand] = useState(false)
|
const [restoreScrollAfterExpand, setRestoreScrollAfterExpand] = useState(false)
|
||||||
const scrollerRef = useRef<InfiniteScrollerInterface | null>(null)
|
const scrollerRef = useRef<InfiniteScrollerInterface | null>(null)
|
||||||
const previousSelectedDay = usePrevious(selectedDay)
|
const previousSelectedDay = usePrevious(selectedDay)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ListableContentItem } from '../Types/ListableContentItem'
|
|||||||
import { DailyItemsDay } from './DailyItemsDaySection'
|
import { DailyItemsDay } from './DailyItemsDaySection'
|
||||||
import { ListItemTitle } from '../ListItemTitle'
|
import { ListItemTitle } from '../ListItemTitle'
|
||||||
import { EmptyPlaceholderBars } from './EmptyPlaceholderBars'
|
import { EmptyPlaceholderBars } from './EmptyPlaceholderBars'
|
||||||
|
import { isMobileScreen } from '@/Utils'
|
||||||
|
|
||||||
type DaySquareProps = {
|
type DaySquareProps = {
|
||||||
day: number
|
day: number
|
||||||
@@ -22,7 +23,7 @@ const DaySquare: FunctionComponent<DaySquareProps> = ({ day, hasActivity, weekda
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
hasActivity ? 'bg-danger text-danger-contrast' : 'bg-neutral text-neutral-contrast'
|
hasActivity ? 'bg-danger text-danger-contrast' : 'bg-neutral text-neutral-contrast'
|
||||||
} h-15 w-18 rounded p-2 text-center`}
|
} h-19 w-18 rounded p-2 text-center`}
|
||||||
>
|
>
|
||||||
<div className="text-sm font-bold">{weekday}</div>
|
<div className="text-sm font-bold">{weekday}</div>
|
||||||
<div className="text-4xl font-bold">{day}</div>
|
<div className="text-4xl font-bold">{day}</div>
|
||||||
@@ -72,7 +73,7 @@ export const DailyItemCell = forwardRef(
|
|||||||
{!item && (
|
{!item && (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="break-word mr-2 font-semibold">{formatDateAndTimeForNote(section.date, false)}</div>
|
<div className="break-word mr-2 font-semibold">{formatDateAndTimeForNote(section.date, false)}</div>
|
||||||
<EmptyPlaceholderBars rows={4} />
|
<EmptyPlaceholderBars rows={isMobileScreen() ? 2 : 4} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { memo, useCallback, useRef, useState } from 'react'
|
import { memo, useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import Icon from '../../Icon/Icon'
|
import Icon from '../../Icon/Icon'
|
||||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import Popover from '@/Components/Popover/Popover'
|
import Popover from '@/Components/Popover/Popover'
|
||||||
@@ -8,6 +8,7 @@ import { NavigationMenuButton } from '@/Components/NavigationMenu/NavigationMenu
|
|||||||
import { IconType, isTag } from '@standardnotes/snjs'
|
import { IconType, isTag } from '@standardnotes/snjs'
|
||||||
import RoundIconButton from '@/Components/Button/RoundIconButton'
|
import RoundIconButton from '@/Components/Button/RoundIconButton'
|
||||||
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
||||||
|
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -34,30 +35,18 @@ const ContentListHeader = ({
|
|||||||
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||||
const isDailyEntry = isTag(selectedTag) && selectedTag.isDailyEntry
|
const isDailyEntry = isTag(selectedTag) && selectedTag.isDailyEntry
|
||||||
|
|
||||||
|
const matchesMd = useMediaQuery(MediaQueryBreakpoints.md)
|
||||||
|
const isTouchScreen = !useMediaQuery(MediaQueryBreakpoints.pointerFine)
|
||||||
|
const isTablet = matchesMd && isTouchScreen
|
||||||
|
|
||||||
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false)
|
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false)
|
||||||
|
|
||||||
const toggleDisplayOptionsMenu = useCallback(() => {
|
const toggleDisplayOptionsMenu = useCallback(() => {
|
||||||
setShowDisplayOptionsMenu((show) => !show)
|
setShowDisplayOptionsMenu((show) => !show)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
const OptionsMenu = useMemo(() => {
|
||||||
<div className="section-title-bar-header items-start gap-1 overflow-hidden">
|
return (
|
||||||
<NavigationMenuButton />
|
|
||||||
<div className="flex min-w-0 flex-grow flex-col break-words">
|
|
||||||
<div className={`flex min-w-0 flex-grow flex-row ${!optionsSubtitle ? 'items-center' : ''}`}>
|
|
||||||
{icon && (
|
|
||||||
<Icon
|
|
||||||
type={icon as IconType}
|
|
||||||
size={'large'}
|
|
||||||
className={`ml-0.5 mr-1.5 text-neutral ${optionsSubtitle ? 'mt-1' : ''}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="flex min-w-0 flex-grow flex-col break-words">
|
|
||||||
<div className="text-lg font-semibold text-text">{panelTitle}</div>
|
|
||||||
{optionsSubtitle && <div className="text-xs text-passive-0">{optionsSubtitle}</div>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="relative" ref={displayOptionsContainerRef}>
|
<div className="relative" ref={displayOptionsContainerRef}>
|
||||||
<RoundIconButton
|
<RoundIconButton
|
||||||
@@ -83,21 +72,90 @@ const ContentListHeader = ({
|
|||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className={classNames(
|
|
||||||
'absolute bottom-6 right-6 z-editor-title-bar ml-3 flex h-13 w-13 cursor-pointer items-center',
|
|
||||||
`justify-center rounded-full border border-solid border-transparent ${
|
|
||||||
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast'
|
|
||||||
}`,
|
|
||||||
'hover:brightness-125 md:static md:h-8 md:w-8',
|
|
||||||
)}
|
|
||||||
title={addButtonLabel}
|
|
||||||
aria-label={addButtonLabel}
|
|
||||||
onClick={addNewItem}
|
|
||||||
>
|
|
||||||
<Icon type="add" size="custom" className="h-6 w-6 md:h-5 md:w-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}, [
|
||||||
|
showDisplayOptionsMenu,
|
||||||
|
toggleDisplayOptionsMenu,
|
||||||
|
displayOptionsButtonRef,
|
||||||
|
application,
|
||||||
|
isFilesSmartView,
|
||||||
|
selectedTag,
|
||||||
|
])
|
||||||
|
|
||||||
|
const AddButton = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
'fixed bottom-6 right-6 z-editor-title-bar ml-3 flex h-15 w-15 cursor-pointer items-center',
|
||||||
|
`justify-center rounded-full border border-solid border-transparent ${
|
||||||
|
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast'
|
||||||
|
}`,
|
||||||
|
'hover:brightness-125 md:static md:h-8 md:w-8',
|
||||||
|
)}
|
||||||
|
title={addButtonLabel}
|
||||||
|
aria-label={addButtonLabel}
|
||||||
|
onClick={addNewItem}
|
||||||
|
>
|
||||||
|
<Icon type="add" size="custom" className="h-8 w-8 md:h-5 md:w-5" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}, [addButtonLabel, addNewItem, isDailyEntry])
|
||||||
|
|
||||||
|
const FolderName = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className="flex min-w-0 flex-grow flex-col break-words pt-1 lg:pt-0">
|
||||||
|
<div className={`flex min-w-0 flex-grow flex-row ${!optionsSubtitle ? 'items-center' : ''}`}>
|
||||||
|
{icon && (
|
||||||
|
<Icon
|
||||||
|
type={icon as IconType}
|
||||||
|
size={'custom'}
|
||||||
|
className={` ml-0.5 mr-1.5 h-7 w-7 text-2xl text-neutral lg:h-6 lg:w-6 lg:text-lg ${
|
||||||
|
optionsSubtitle ? 'mt-1' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="flex min-w-0 flex-grow flex-col break-words">
|
||||||
|
<div className=" text-2xl font-semibold text-text md:text-lg">{panelTitle}</div>
|
||||||
|
{optionsSubtitle && <div className="text-xs text-passive-0">{optionsSubtitle}</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [optionsSubtitle, icon, panelTitle])
|
||||||
|
|
||||||
|
const PhoneAndDesktopLayout = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className={'flex w-full justify-between md:flex'}>
|
||||||
|
<NavigationMenuButton />
|
||||||
|
{FolderName}
|
||||||
|
<div className="flex">
|
||||||
|
{OptionsMenu}
|
||||||
|
{AddButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [OptionsMenu, AddButton, FolderName])
|
||||||
|
|
||||||
|
const TabletLayout = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className={'w-full flex-col'}>
|
||||||
|
<div className="mb-2 flex justify-between">
|
||||||
|
<NavigationMenuButton />
|
||||||
|
<div className="flex">
|
||||||
|
{OptionsMenu}
|
||||||
|
{AddButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{FolderName}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [OptionsMenu, AddButton, FolderName])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="section-title-bar-header items-start gap-1 overflow-hidden">
|
||||||
|
{!isTablet && PhoneAndDesktopLayout}
|
||||||
|
{isTablet && TabletLayout}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ import { PreferenceMode } from './PreferenceMode'
|
|||||||
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
|
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
|
||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import { isDev } from '@/Utils'
|
|
||||||
|
|
||||||
const DailyEntryModeEnabled = isDev
|
const DailyEntryModeEnabled = true
|
||||||
|
|
||||||
const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||||
closeDisplayOptionsMenu,
|
closeDisplayOptionsMenu,
|
||||||
@@ -180,7 +179,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative cursor-pointer rounded-full border-2 border-solid border-transparent px-2 text-sm focus:shadow-none',
|
'relative cursor-pointer rounded-full border-2 border-solid border-transparent px-2 text-base focus:shadow-none lg:text-sm',
|
||||||
isSelected ? 'bg-info text-info-contrast' : 'bg-transparent text-text hover:bg-info-backdrop',
|
isSelected ? 'bg-info text-info-contrast' : 'bg-transparent text-text hover:bg-info-backdrop',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -209,7 +208,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<p className="col-start-1 col-end-3 m-0 mt-1 text-sm">
|
<p className="col-start-1 col-end-3 m-0 mt-1 text-sm">
|
||||||
{DailyEntryModeEnabled &&
|
{DailyEntryModeEnabled &&
|
||||||
'Create powerful workflows and organizational layouts with per-tag display preferences and the all-new Daily Notebook feature.'}
|
'Create powerful workflows and organizational layouts with per-tag display preferences and the all-new Daily Notebook calendar layout.'}
|
||||||
{!DailyEntryModeEnabled &&
|
{!DailyEntryModeEnabled &&
|
||||||
'Create powerful workflows and organizational layouts with per-tag display preferences.'}
|
'Create powerful workflows and organizational layouts with per-tag display preferences.'}
|
||||||
</p>
|
</p>
|
||||||
@@ -226,20 +225,24 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu className="text-sm" a11yLabel="Notes list options menu" closeMenu={closeDisplayOptionsMenu} isOpen={isOpen}>
|
<Menu className="text-sm" a11yLabel="Notes list options menu" closeMenu={closeDisplayOptionsMenu} isOpen={isOpen}>
|
||||||
<div className="my-1 px-3 text-xs font-semibold uppercase text-text">Preferences for</div>
|
<div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Preferences for</div>
|
||||||
<div className={classNames('mt-1.5 flex w-full justify-between px-3', !controlsDisabled && 'mb-3')}>
|
<div className={classNames('mt-1.5 flex w-full justify-between px-3', !controlsDisabled && 'mb-3')}>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<TabButton label="Global" mode="global" />
|
<TabButton label="Global" mode="global" />
|
||||||
{!isSystemTag && <TabButton label={selectedTag.title} icon={selectedTag.iconString} mode="tag" />}
|
{!isSystemTag && <TabButton label={selectedTag.title} icon={selectedTag.iconString} mode="tag" />}
|
||||||
</div>
|
</div>
|
||||||
{currentMode === 'tag' && <button onClick={resetTagPreferences}>Reset</button>}
|
{currentMode === 'tag' && (
|
||||||
|
<button className="text-base lg:text-sm" onClick={resetTagPreferences}>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{controlsDisabled && <NoSubscriptionBanner />}
|
{controlsDisabled && <NoSubscriptionBanner />}
|
||||||
|
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
|
|
||||||
<div className="my-1 px-3 text-xs font-semibold uppercase text-text">Sort by</div>
|
<div className="my-1 px-3 text-base font-semibold uppercase text-text lg:text-xs">Sort by</div>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
disabled={controlsDisabled || isDailyEntry}
|
disabled={controlsDisabled || isDailyEntry}
|
||||||
className="py-2"
|
className="py-2"
|
||||||
@@ -295,7 +298,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
<div className="px-3 py-1 text-xs font-semibold uppercase text-text">View</div>
|
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">View</div>
|
||||||
{!isFilesSmartView && (
|
{!isFilesSmartView && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
disabled={controlsDisabled}
|
disabled={controlsDisabled}
|
||||||
@@ -335,7 +338,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
Show icon
|
Show icon
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItemSeparator />
|
<MenuItemSeparator />
|
||||||
<div className="px-3 py-1 text-xs font-semibold uppercase text-text">Other</div>
|
<div className="px-3 py-1 text-base font-semibold uppercase text-text lg:text-xs">Other</div>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
disabled={controlsDisabled}
|
disabled={controlsDisabled}
|
||||||
type={MenuItemType.SwitchButton}
|
type={MenuItemType.SwitchButton}
|
||||||
@@ -384,7 +387,12 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
|||||||
onChange={toggleEntryMode}
|
onChange={toggleEntryMode}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col pr-5">
|
<div className="flex flex-col pr-5">
|
||||||
<div className="text-xs font-semibold uppercase text-text">Daily Notebook</div>
|
<div className="flex flex-row items-center">
|
||||||
|
<div className="text-base font-semibold uppercase text-text lg:text-xs">Daily Notebook</div>
|
||||||
|
<div className="ml-2 rounded bg-success px-1.5 py-[1px] text-[10px] font-bold text-success-contrast">
|
||||||
|
Experimental
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="mt-1">Capture new notes daily with a calendar-based layout</div>
|
<div className="mt-1">Capture new notes daily with a calendar-based layout</div>
|
||||||
</div>
|
</div>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -206,9 +206,9 @@ const NewNotePreferences: FunctionComponent<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-1 px-3 pb-2 pt-1">
|
<div className="my-1 px-3 pb-2 pt-1">
|
||||||
<div className="text-xs font-semibold uppercase text-text">New Note Defaults</div>
|
<div className="text-base font-semibold uppercase text-text lg:text-xs">New Note Defaults</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="mt-3">Note Type</div>
|
<div className="mt-3 text-mobile-menu-item md:text-menu-item">Note Type</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
portal={false}
|
portal={false}
|
||||||
@@ -223,7 +223,7 @@ const NewNotePreferences: FunctionComponent<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="mt-3">Title Format</div>
|
<div className="mt-3 text-mobile-menu-item md:text-menu-item">Title Format</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
portal={false}
|
portal={false}
|
||||||
@@ -249,10 +249,11 @@ const NewNotePreferences: FunctionComponent<Props> = ({
|
|||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-3 text-neutral">
|
||||||
<span className="font-bold">Preview:</span> {dayjs().format(customNoteTitleFormat)}
|
<span className="font-bold">Preview: </span>
|
||||||
|
<em>{dayjs().format(customNoteTitleFormat)}</em>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1">
|
<div className="mt-2 text-neutral">
|
||||||
<a
|
<a
|
||||||
className="underline"
|
className="underline"
|
||||||
href={HelpPageUrl}
|
href={HelpPageUrl}
|
||||||
@@ -267,7 +268,7 @@ const NewNotePreferences: FunctionComponent<Props> = ({
|
|||||||
>
|
>
|
||||||
Options
|
Options
|
||||||
</a>
|
</a>
|
||||||
. Use <code>[]</code> to escape date-time formatting.
|
. Use <code>[]</code> to escape formatting.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false })
|
|||||||
<div className="flex items-start border-b border-solid border-border p-4 pl-0">
|
<div className="flex items-start border-b border-solid border-border p-4 pl-0">
|
||||||
{item.locked && (
|
{item.locked && (
|
||||||
<span className="flex items-center" title="Editing Disabled">
|
<span className="flex items-center" title="Editing Disabled">
|
||||||
<Icon ariaLabel="Editing Disabled" type="pencil-off" className="text-info" size="small" />
|
<Icon ariaLabel="Editing Disabled" type="pencil-off" className="text-info" size="medium" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{item.trashed && (
|
{item.trashed && (
|
||||||
<span className="ml-1.5 flex items-center" title="Trashed">
|
<span className="ml-1.5 flex items-center" title="Trashed">
|
||||||
<Icon ariaLabel="Trashed" type="trash-filled" className="text-danger" size="small" />
|
<Icon ariaLabel="Trashed" type="trash-filled" className="text-danger" size="medium" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{item.archived && (
|
{item.archived && (
|
||||||
@@ -33,17 +33,17 @@ const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false })
|
|||||||
)}
|
)}
|
||||||
{item.pinned && (
|
{item.pinned && (
|
||||||
<span className="ml-1.5 flex items-center" title="Pinned">
|
<span className="ml-1.5 flex items-center" title="Pinned">
|
||||||
<Icon ariaLabel="Pinned" type="pin-filled" className="text-info" size="small" />
|
<Icon ariaLabel="Pinned" type="pin-filled" className="text-info" size="medium" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{hasFiles && (
|
{hasFiles && (
|
||||||
<span className="ml-1.5 flex items-center" title="Files">
|
<span className="ml-1.5 flex items-center" title="Files">
|
||||||
<Icon ariaLabel="Files" type="attachment-file" className="text-info" size="small" />
|
<Icon ariaLabel="Files" type="attachment-file" className="text-info" size="medium" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{item.starred && (
|
{item.starred && (
|
||||||
<span className="ml-1.5 flex items-center" title="Starred">
|
<span className="ml-1.5 flex items-center" title="Starred">
|
||||||
<Icon ariaLabel="Starred" type="star-filled" className="text-warning" size="small" />
|
<Icon ariaLabel="Starred" type="star-filled" className="text-warning" size="medium" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const ListItemMetadata: FunctionComponent<Props> = ({ item, hideDate, sortBy })
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="leading-1.4 mt-1 text-xs opacity-50">
|
<div className="leading-1.4 mt-1 text-sm opacity-50 lg:text-xs">
|
||||||
{item.protected && <span>Protected {hideDate ? '' : ' • '}</span>}
|
{item.protected && <span>Protected {hideDate ? '' : ' • '}</span>}
|
||||||
{!hideDate && showModifiedDate && <span>Modified {item.updatedAtString || 'Now'}</span>}
|
{!hideDate && showModifiedDate && <span>Modified {item.updatedAtString || 'Now'}</span>}
|
||||||
{!hideDate && !showModifiedDate && <span>{item.createdAtString || 'Now'}</span>}
|
{!hideDate && !showModifiedDate && <span>{item.createdAtString || 'Now'}</span>}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ListItemNotePreviewText: FunctionComponent<Props> = ({ item, hidePreview,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`overflow-hidden overflow-ellipsis text-sm ${item.archived ? 'opacity-60' : ''}`}>
|
<div className={`overflow-hidden overflow-ellipsis text-base lg:text-sm ${item.archived ? 'opacity-60' : ''}`}>
|
||||||
{item.preview_html && (
|
{item.preview_html && (
|
||||||
<div
|
<div
|
||||||
className="my-1"
|
className="my-1"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const ListItemTags: FunctionComponent<Props> = ({ hideTags, tags }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-1.5 flex flex-wrap gap-2 text-xs">
|
<div className="mt-1.5 flex flex-wrap gap-2 text-sm lg:text-xs">
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<span
|
<span
|
||||||
className="inline-flex items-center rounded-sm bg-passive-4-opacity-variant py-1 px-1.5 text-foreground"
|
className="inline-flex items-center rounded-sm bg-passive-4-opacity-variant py-1 px-1.5 text-foreground"
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import { ListableContentItem } from './Types/ListableContentItem'
|
|||||||
|
|
||||||
export const ListItemTitle: FunctionComponent<{ item: ListableContentItem }> = ({ item }) => {
|
export const ListItemTitle: FunctionComponent<{ item: ListableContentItem }> = ({ item }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start justify-between overflow-hidden text-base font-semibold leading-[1.3]">
|
<div
|
||||||
<div className={`break-word mr-2 ${item.archived ? 'opacity-60' : ''}`}>{item.title}</div>
|
className={`break-word mr-2 flex items-start justify-between overflow-hidden text-lg font-semibold leading-[1.3] lg:text-base lg:leading-[1.3] ${
|
||||||
|
item.archived ? 'opacity-60' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const CustomDropdownButton: FunctionComponent<ListboxButtonProps> = ({
|
|||||||
<Icon type={icon} className={iconClassName} size="small" />
|
<Icon type={icon} className={iconClassName} size="small" />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="dropdown-selected-label">{label}</div>
|
<div className="text-base lg:text-sm">{label}</div>
|
||||||
</div>
|
</div>
|
||||||
<ListboxArrow className={`flex ${isExpanded ? 'rotate-180' : ''}`}>
|
<ListboxArrow className={`flex ${isExpanded ? 'rotate-180' : ''}`}>
|
||||||
<Icon type="menu-arrow-down" className="text-passive-1" size="small" />
|
<Icon type="menu-arrow-down" className="text-passive-1" size="small" />
|
||||||
@@ -92,7 +92,7 @@ const Dropdown: FunctionComponent<DropdownProps> = ({
|
|||||||
<Icon type={item.icon} className={item.iconClassName ?? ''} size="small" />
|
<Icon type={item.icon} className={item.iconClassName ?? ''} size="small" />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="text-input">{item.label}</div>
|
<div className="text-base lg:text-sm">{item.label}</div>
|
||||||
</StyledListboxOption>
|
</StyledListboxOption>
|
||||||
))}
|
))}
|
||||||
</ListboxList>
|
</ListboxList>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const UpgradeNow = ({ application, featuresController }: Props) => {
|
|||||||
return shouldShowCTA ? (
|
return shouldShowCTA ? (
|
||||||
<div className="flex h-full items-center px-2">
|
<div className="flex h-full items-center px-2">
|
||||||
<button
|
<button
|
||||||
className="rounded bg-info py-0.5 px-1.5 text-xs font-bold uppercase text-info-contrast hover:brightness-125"
|
className="rounded bg-info py-0.5 px-1.5 text-sm font-bold uppercase text-info-contrast hover:brightness-125 lg:text-xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (hasAccount) {
|
if (hasAccount) {
|
||||||
void loadPurchaseFlowUrl(application)
|
void loadPurchaseFlowUrl(application)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className }: Props)
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`relative mr-2 cursor-pointer border-0 bg-default pb-1.5 text-sm focus:shadow-none ${
|
className={`relative mr-2 cursor-pointer border-0 bg-default pb-1.5 text-mobile-menu-item focus:shadow-none md:text-tablet-menu-item lg:text-menu-item ${
|
||||||
isSelected ? 'font-medium text-info' : 'text-text'
|
isSelected ? 'font-medium text-info' : 'text-text'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -114,14 +114,18 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className }: Props)
|
|||||||
onChange={({ target: input }) => handleEmojiChange((input as HTMLInputElement)?.value)}
|
onChange={({ target: input }) => handleEmojiChange((input as HTMLInputElement)?.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-xs text-passive-0">
|
<div className="mt-2 text-sm text-passive-0 lg:text-xs">
|
||||||
Use your keyboard to enter or paste in an emoji character.
|
Use your keyboard to enter or paste in an emoji character.
|
||||||
</div>
|
</div>
|
||||||
{isMacOS && (
|
{isMacOS && (
|
||||||
<div className="mt-2 text-xs text-passive-0">On macOS: ⌘ + ⌃ + Space bar to bring up emoji picker.</div>
|
<div className="mt-2 text-sm text-passive-0 lg:text-xs">
|
||||||
|
On macOS: ⌘ + ⌃ + Space bar to bring up emoji picker.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{isWindows && (
|
{isWindows && (
|
||||||
<div className="mt-2 text-xs text-passive-0">On Windows: Windows key + . to bring up emoji picker.</div>
|
<div className="mt-2 text-sm text-passive-0 lg:text-xs">
|
||||||
|
On Windows: Windows key + . to bring up emoji picker.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -119,8 +119,11 @@ const ItemLinkAutocompleteInput = ({ linkingController, focusPreviousItem, focus
|
|||||||
<Disclosure open={dropdownVisible} onChange={showDropdown}>
|
<Disclosure open={dropdownVisible} onChange={showDropdown}>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className={`${tags.length > 0 ? 'w-80' : 'mr-10 w-70'} no-border h-7
|
className={classNames(
|
||||||
bg-transparent text-xs text-text focus:border-b-2 focus:border-solid focus:border-info focus:shadow-none focus:outline-none`}
|
`${tags.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
|
||||||
|
'bg-transparent text-sm text-text focus:border-b-2 focus:border-solid focus:border-info lg:text-xs',
|
||||||
|
'no-border h-7 focus:shadow-none focus:outline-none',
|
||||||
|
)}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={onSearchQueryChange}
|
onChange={onSearchQueryChange}
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const LinkedItemBubble = ({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="group flex h-6 cursor-pointer items-center rounded border-0 bg-passive-4-opacity-variant py-2 pl-1 pr-2 text-xs text-text hover:bg-contrast focus:bg-contrast"
|
className="group flex h-6 cursor-pointer items-center rounded border-0 bg-passive-4-opacity-variant py-2 pl-1 pr-2 text-sm text-text hover:bg-contrast focus:bg-contrast lg:text-xs"
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const LinkedItemMeta = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Icon type={icon} className={classNames('flex-shrink-0', className)} />
|
<Icon type={icon} className={classNames('flex-shrink-0', className)} />
|
||||||
<div className="min-w-0 flex-grow break-words text-left text-sm">
|
<div className="min-w-0 flex-grow break-words text-left text-base lg:text-sm">
|
||||||
{tagTitle && <span className="text-passive-1">{tagTitle.titlePrefix}</span>}
|
{tagTitle && <span className="text-passive-1">{tagTitle.titlePrefix}</span>}
|
||||||
{searchQuery
|
{searchQuery
|
||||||
? splitQueryInString(title, searchQuery).map((substring, index) => (
|
? splitQueryInString(title, searchQuery).map((substring, index) => (
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ const LinkedItemsPanel = ({
|
|||||||
onChange={handleFileInputChange}
|
onChange={handleFileInputChange}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center gap-3 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-info-backdrop hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
className="flex w-full cursor-pointer items-center gap-3 bg-transparent px-3 py-2 text-left text-base text-text hover:bg-info-backdrop hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-sm"
|
||||||
onClick={selectAndUploadFiles}
|
onClick={selectAndUploadFiles}
|
||||||
>
|
>
|
||||||
<Icon type="add" />
|
<Icon type="add" />
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ const MenuItem = forwardRef(
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5',
|
'flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-2 md:py-1.5',
|
||||||
'text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
|
'text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
|
||||||
|
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onChange(!checked)
|
onChange(!checked)
|
||||||
@@ -68,9 +69,9 @@ const MenuItem = forwardRef(
|
|||||||
role={type === MenuItemType.RadioButton ? 'menuitemradio' : 'menuitem'}
|
role={type === MenuItemType.RadioButton ? 'menuitemradio' : 'menuitem'}
|
||||||
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left',
|
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left md:py-1.5',
|
||||||
'text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground',
|
'text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground',
|
||||||
'focus:bg-info-backdrop focus:shadow-none md:text-menu-item',
|
'focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const NoAccountWarningContent = ({ accountMenuController, noAccountWarningContro
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 grid grid-cols-1 rounded-md border border-border p-4">
|
<div className="mt-4 grid grid-cols-1 rounded-md border border-border p-4">
|
||||||
<h1 className="sk-h3 m-0 text-sm font-semibold">Data not backed up</h1>
|
<h1 className="sk-h3 m-0 text-base font-semibold lg:text-sm">Data not backed up</h1>
|
||||||
<p className="col-start-1 col-end-3 m-0 mt-1 text-sm">
|
<p className="col-start-1 col-end-3 m-0 mt-1 text-base lg:text-sm">
|
||||||
Sign in or register to sync your notes to your other devices with end-to-end encryption.
|
Sign in or register to sync your notes to your other devices with end-to-end encryption.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const MobileItemsListButton = () => {
|
|||||||
}}
|
}}
|
||||||
label={label}
|
label={label}
|
||||||
icon={iconType}
|
icon={iconType}
|
||||||
|
iconClassName={'h-6 w-6'}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ describe('NoteView', () => {
|
|||||||
application.getViewControllerManager = jest.fn().mockReturnValue(viewControllerManager)
|
application.getViewControllerManager = jest.fn().mockReturnValue(viewControllerManager)
|
||||||
application.hasProtectionSources = jest.fn().mockReturnValue(true)
|
application.hasProtectionSources = jest.fn().mockReturnValue(true)
|
||||||
application.authorizeNoteAccess = jest.fn()
|
application.authorizeNoteAccess = jest.fn()
|
||||||
|
application.addWebEventObserver = jest.fn()
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { ElementIds } from '@/Constants/ElementIDs'
|
|||||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||||
import { StringDeleteNote, STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT } from '@/Constants/Strings'
|
import { StringDeleteNote, STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT } from '@/Constants/Strings'
|
||||||
import { log, LoggingDomain } from '@/Logging'
|
import { log, LoggingDomain } from '@/Logging'
|
||||||
import { debounce, isDesktopApplication, isMobileScreen } from '@/Utils'
|
import { debounce, isDesktopApplication, isMobileScreen, isTabletScreen } from '@/Utils'
|
||||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import {
|
import {
|
||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
@@ -91,12 +91,36 @@ type State = {
|
|||||||
noteType?: NoteType
|
noteType?: NoteType
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlaintextFontSizeMapping: Record<EditorFontSize, string> = {
|
const getPlaintextFontSize = (key: EditorFontSize): string => {
|
||||||
ExtraSmall: 'text-xs',
|
const desktopMapping: Record<EditorFontSize, string> = {
|
||||||
Small: 'text-sm',
|
ExtraSmall: 'text-xs',
|
||||||
Normal: 'text-editor',
|
Small: 'text-sm',
|
||||||
Medium: 'text-lg',
|
Normal: 'text-editor',
|
||||||
Large: 'text-xl',
|
Medium: 'text-lg',
|
||||||
|
Large: 'text-xl',
|
||||||
|
}
|
||||||
|
|
||||||
|
const mobileMapping: Record<EditorFontSize, string> = {
|
||||||
|
ExtraSmall: 'text-sm',
|
||||||
|
Small: 'text-editor',
|
||||||
|
Normal: 'text-lg',
|
||||||
|
Medium: 'text-xl',
|
||||||
|
Large: 'text-xl2',
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabletMapping: Record<EditorFontSize, string> = {
|
||||||
|
ExtraSmall: 'text-sm',
|
||||||
|
Small: 'text-editor',
|
||||||
|
Normal: 'text-base',
|
||||||
|
Medium: 'text-xl',
|
||||||
|
Large: 'text-xl2',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTabletScreen()) {
|
||||||
|
return tabletMapping[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
return isMobileScreen() ? mobileMapping[key] : desktopMapping[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoteView extends AbstractComponent<NoteViewProps, State> {
|
class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||||
@@ -111,6 +135,10 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
private removeComponentStreamObserver?: () => void
|
private removeComponentStreamObserver?: () => void
|
||||||
private removeComponentManagerObserver?: () => void
|
private removeComponentManagerObserver?: () => void
|
||||||
private removeInnerNoteObserver?: () => void
|
private removeInnerNoteObserver?: () => void
|
||||||
|
private removeWebAppEventObserver: () => void
|
||||||
|
|
||||||
|
private needsAdjustMobileCursor = false
|
||||||
|
private isAdjustingMobileCursor = false
|
||||||
|
|
||||||
private protectionTimeoutId: ReturnType<typeof setTimeout> | null = null
|
private protectionTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
@@ -133,6 +161,12 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
|
|
||||||
this.textAreaChangeDebounceSave = debounce(this.textAreaChangeDebounceSave, TextareaDebounce)
|
this.textAreaChangeDebounceSave = debounce(this.textAreaChangeDebounceSave, TextareaDebounce)
|
||||||
|
|
||||||
|
this.removeWebAppEventObserver = props.application.addWebEventObserver((event) => {
|
||||||
|
if (event === WebAppEvent.MobileKeyboardWillChangeFrame) {
|
||||||
|
this.scrollMobileCursorIntoViewAfterWebviewResize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
availableStackComponents: [],
|
availableStackComponents: [],
|
||||||
editorStateDidLoad: false,
|
editorStateDidLoad: false,
|
||||||
@@ -160,6 +194,16 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
this.editorContentRef = createRef<HTMLDivElement>()
|
this.editorContentRef = createRef<HTMLDivElement>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollMobileCursorIntoViewAfterWebviewResize() {
|
||||||
|
if (this.needsAdjustMobileCursor) {
|
||||||
|
this.needsAdjustMobileCursor = false
|
||||||
|
this.isAdjustingMobileCursor = true
|
||||||
|
document.getElementById('note-text-editor')?.blur()
|
||||||
|
document.getElementById('note-text-editor')?.focus()
|
||||||
|
this.isAdjustingMobileCursor = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override deinit() {
|
override deinit() {
|
||||||
super.deinit()
|
super.deinit()
|
||||||
;(this.controller as unknown) = undefined
|
;(this.controller as unknown) = undefined
|
||||||
@@ -179,6 +223,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
this.clearNoteProtectionInactivityTimer()
|
this.clearNoteProtectionInactivityTimer()
|
||||||
;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined
|
;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined
|
||||||
|
|
||||||
|
this.removeWebAppEventObserver?.()
|
||||||
|
;(this.removeWebAppEventObserver as unknown) = undefined
|
||||||
|
|
||||||
this.removeTabObserver?.()
|
this.removeTabObserver?.()
|
||||||
this.removeTabObserver = undefined
|
this.removeTabObserver = undefined
|
||||||
this.onEditorComponentLoad = undefined
|
this.onEditorComponentLoad = undefined
|
||||||
@@ -671,9 +718,13 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onContentFocus = () => {
|
onContentFocus = () => {
|
||||||
|
if (!this.isAdjustingMobileCursor) {
|
||||||
|
this.needsAdjustMobileCursor = true
|
||||||
|
}
|
||||||
if (this.lastEditorFocusEventSource) {
|
if (this.lastEditorFocusEventSource) {
|
||||||
this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource })
|
this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastEditorFocusEventSource = undefined
|
this.lastEditorFocusEventSource = undefined
|
||||||
this.setState({ plaintextEditorFocused: true })
|
this.setState({ plaintextEditorFocused: true })
|
||||||
}
|
}
|
||||||
@@ -1114,7 +1165,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
'editable font-editor flex-grow',
|
'editable font-editor flex-grow',
|
||||||
this.state.lineHeight && `leading-${this.state.lineHeight.toLowerCase()}`,
|
this.state.lineHeight && `leading-${this.state.lineHeight.toLowerCase()}`,
|
||||||
this.state.fontSize && PlaintextFontSizeMapping[this.state.fontSize],
|
this.state.fontSize && getPlaintextFontSize(this.state.fontSize),
|
||||||
)}
|
)}
|
||||||
></textarea>
|
></textarea>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,14 +6,23 @@ import { NotesController } from '@/Controllers/NotesController'
|
|||||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||||
import Popover from '../Popover/Popover'
|
import Popover from '../Popover/Popover'
|
||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
|
import { IconType } from '@standardnotes/snjs'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigationController: NavigationController
|
navigationController: NavigationController
|
||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
|
className: string
|
||||||
|
iconClassName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesController, linkingController }) => {
|
const AddTagOption: FunctionComponent<Props> = ({
|
||||||
|
navigationController,
|
||||||
|
notesController,
|
||||||
|
linkingController,
|
||||||
|
className,
|
||||||
|
iconClassName,
|
||||||
|
}) => {
|
||||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
@@ -33,10 +42,10 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={className}
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon type="hashtag" className="mr-2 text-neutral" />
|
<Icon type="hashtag" className={`${iconClassName} mr-2 text-neutral`} />
|
||||||
Add tag
|
Add tag
|
||||||
</div>
|
</div>
|
||||||
<Icon type="chevron-right" className="text-neutral" />
|
<Icon type="chevron-right" className="text-neutral" />
|
||||||
@@ -52,13 +61,20 @@ const AddTagOption: FunctionComponent<Props> = ({ navigationController, notesCon
|
|||||||
{navigationController.tags.map((tag) => (
|
{navigationController.tags.map((tag) => (
|
||||||
<button
|
<button
|
||||||
key={tag.uuid}
|
key={tag.uuid}
|
||||||
className="max-w-80 flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={`max-w-80 ${className.replace('justify-between', 'justify-start')}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.isTagInSelectedNotes(tag)
|
notesController.isTagInSelectedNotes(tag)
|
||||||
? notesController.removeTagFromSelectedNotes(tag).catch(console.error)
|
? notesController.removeTagFromSelectedNotes(tag).catch(console.error)
|
||||||
: notesController.addTagToSelectedNotes(tag).catch(console.error)
|
: notesController.addTagToSelectedNotes(tag).catch(console.error)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{tag.iconString && (
|
||||||
|
<Icon
|
||||||
|
type={tag.iconString as IconType}
|
||||||
|
size={'custom'}
|
||||||
|
className={'ml-0.5 mr-1.5 h-7 w-7 text-2xl text-neutral lg:h-6 lg:w-6 lg:text-lg'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span
|
<span
|
||||||
className={`overflow-hidden overflow-ellipsis whitespace-nowrap
|
className={`overflow-hidden overflow-ellipsis whitespace-nowrap
|
||||||
${notesController.isTagInSelectedNotes(tag) ? 'font-bold' : ''}`}
|
${notesController.isTagInSelectedNotes(tag) ? 'font-bold' : ''}`}
|
||||||
|
|||||||
@@ -9,9 +9,16 @@ import Popover from '../Popover/Popover'
|
|||||||
type ChangeEditorOptionProps = {
|
type ChangeEditorOptionProps = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
note: SNNote
|
note: SNNote
|
||||||
|
className: string
|
||||||
|
iconClassName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ application, note }) => {
|
const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({
|
||||||
|
application,
|
||||||
|
note,
|
||||||
|
className,
|
||||||
|
iconClassName,
|
||||||
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||||
@@ -30,10 +37,10 @@ const ChangeEditorOption: FunctionComponent<ChangeEditorOptionProps> = ({ applic
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={className}
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon type="dashboard" className="mr-2 text-neutral" />
|
<Icon type="dashboard" className={`${iconClassName} mr-2 text-neutral`} />
|
||||||
Change note type
|
Change note type
|
||||||
</div>
|
</div>
|
||||||
<Icon type="chevron-right" className="text-neutral" />
|
<Icon type="chevron-right" className="text-neutral" />
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import Popover from '../Popover/Popover'
|
|||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
note: SNNote
|
note: SNNote
|
||||||
|
className: string
|
||||||
|
iconClassName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) => {
|
const ListedActionsOption: FunctionComponent<Props> = ({ application, note, className, iconClassName }) => {
|
||||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
@@ -37,10 +39,10 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) =>
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={className}
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon type="listed" className="mr-2 text-neutral" />
|
<Icon type="listed" className={`mr-2 text-neutral ${iconClassName}`} />
|
||||||
Listed actions
|
Listed actions
|
||||||
</div>
|
</div>
|
||||||
<Icon type="chevron-right" className="text-neutral" />
|
<Icon type="chevron-right" className="text-neutral" />
|
||||||
|
|||||||
@@ -41,17 +41,19 @@ const ListedMenuItem: FunctionComponent<ListedMenuItemProps> = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex w-full flex-row items-center justify-between">
|
||||||
<div className="font-semibold">{action.label}</div>
|
<div className="flex flex-col">
|
||||||
{action.access_type && (
|
<div className="font-semibold">{action.label}</div>
|
||||||
<div className="mt-0.5 text-xs text-passive-0">
|
{action.access_type && (
|
||||||
{'Uses '}
|
<div className="mt-0.5 text-xs text-passive-0">
|
||||||
<strong>{action.access_type}</strong>
|
{'Uses '}
|
||||||
{' access to this note.'}
|
<strong>{action.access_type}</strong>
|
||||||
</div>
|
{' access to this note.'}
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{isRunning && <Spinner className="h-3 w-3" />}
|
||||||
</div>
|
</div>
|
||||||
{isRunning && <Spinner className="h-3 w-3" />}
|
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
|
|||||||
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
|
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
|
||||||
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
|
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
|
||||||
import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel'
|
import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel'
|
||||||
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
|
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||||
|
|
||||||
type DeletePermanentlyButtonProps = {
|
type DeletePermanentlyButtonProps = {
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -34,10 +36,11 @@ const DeletePermanentlyButton = ({ onClick }: DeletePermanentlyButtonProps) => (
|
|||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const iconClass = 'text-neutral mr-2'
|
const iconSize = MenuItemIconSize
|
||||||
const iconClassDanger = 'text-danger mr-2'
|
const iconClass = `text-neutral mr-2 ${iconSize}`
|
||||||
const iconClassWarning = 'text-warning mr-2'
|
const iconClassDanger = `text-danger mr-2 ${iconSize}`
|
||||||
const iconClassSuccess = 'text-success mr-2'
|
const iconClassWarning = `text-warning mr-2 ${iconSize}`
|
||||||
|
const iconClassSuccess = `text-success mr-2 ${iconSize}`
|
||||||
|
|
||||||
const getWordCount = (text: string) => {
|
const getWordCount = (text: string) => {
|
||||||
if (text.trim().length === 0) {
|
if (text.trim().length === 0) {
|
||||||
@@ -99,7 +102,7 @@ const NoteAttributes: FunctionComponent<{
|
|||||||
const format = editor?.package_info?.file_type || 'txt'
|
const format = editor?.package_info?.file_type || 'txt'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="select-text px-3 py-1.5 text-xs font-medium text-neutral">
|
<div className="select-text px-3 py-1.5 text-sm font-medium text-neutral lg:text-xs">
|
||||||
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
|
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
|
||||||
<>
|
<>
|
||||||
<div className="mb-1">
|
<div className="mb-1">
|
||||||
@@ -127,7 +130,8 @@ const SpellcheckOptions: FunctionComponent<{
|
|||||||
editorForNote: SNComponent | undefined
|
editorForNote: SNComponent | undefined
|
||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
note: SNNote
|
note: SNNote
|
||||||
}> = ({ editorForNote, notesController, note }) => {
|
className: string
|
||||||
|
}> = ({ editorForNote, notesController, note, className }) => {
|
||||||
const spellcheckControllable = Boolean(!editorForNote || editorForNote.package_info.spellcheckControl)
|
const spellcheckControllable = Boolean(!editorForNote || editorForNote.package_info.spellcheckControl)
|
||||||
const noteSpellcheck = !spellcheckControllable
|
const noteSpellcheck = !spellcheckControllable
|
||||||
? true
|
? true
|
||||||
@@ -138,7 +142,7 @@ const SpellcheckOptions: FunctionComponent<{
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={className}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.toggleGlobalSpellcheckForNote(note).catch(console.error)
|
notesController.toggleGlobalSpellcheckForNote(note).catch(console.error)
|
||||||
}}
|
}}
|
||||||
@@ -273,14 +277,20 @@ const NotesOptions = ({
|
|||||||
return <ProtectedUnauthorizedLabel />
|
return <ProtectedUnauthorizedLabel />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const textClassNames = 'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item'
|
||||||
|
|
||||||
|
const defaultClassNames = classNames(
|
||||||
|
textClassNames,
|
||||||
|
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
|
||||||
|
)
|
||||||
|
|
||||||
|
const switchClassNames = classNames(textClassNames, defaultClassNames, 'justify-between')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{notes.length === 1 && (
|
{notes.length === 1 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button className={defaultClassNames} onClick={openRevisionHistoryModal}>
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
|
||||||
onClick={openRevisionHistoryModal}
|
|
||||||
>
|
|
||||||
<Icon type="history" className={iconClass} />
|
<Icon type="history" className={iconClass} />
|
||||||
Note history
|
Note history
|
||||||
</button>
|
</button>
|
||||||
@@ -288,7 +298,7 @@ const NotesOptions = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={switchClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setLockSelectedNotes(!locked)
|
notesController.setLockSelectedNotes(!locked)
|
||||||
}}
|
}}
|
||||||
@@ -300,7 +310,7 @@ const NotesOptions = ({
|
|||||||
<Switch className="px-0" checked={locked} />
|
<Switch className="px-0" checked={locked} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={switchClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setHideSelectedNotePreviews(!hidePreviews)
|
notesController.setHideSelectedNotePreviews(!hidePreviews)
|
||||||
}}
|
}}
|
||||||
@@ -312,7 +322,7 @@ const NotesOptions = ({
|
|||||||
<Switch className="px-0" checked={!hidePreviews} />
|
<Switch className="px-0" checked={!hidePreviews} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={switchClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setProtectSelectedNotes(!protect).catch(console.error)
|
notesController.setProtectSelectedNotes(!protect).catch(console.error)
|
||||||
}}
|
}}
|
||||||
@@ -326,12 +336,19 @@ const NotesOptions = ({
|
|||||||
{notes.length === 1 && (
|
{notes.length === 1 && (
|
||||||
<>
|
<>
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
<ChangeEditorOption application={application} note={notes[0]} />
|
<ChangeEditorOption
|
||||||
|
iconClassName={iconClass}
|
||||||
|
className={switchClassNames}
|
||||||
|
application={application}
|
||||||
|
note={notes[0]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
{navigationController.tagsCount > 0 && (
|
{navigationController.tagsCount > 0 && (
|
||||||
<AddTagOption
|
<AddTagOption
|
||||||
|
iconClassName={iconClass}
|
||||||
|
className={switchClassNames}
|
||||||
navigationController={navigationController}
|
navigationController={navigationController}
|
||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
@@ -339,7 +356,7 @@ const NotesOptions = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setStarSelectedNotes(!starred)
|
notesController.setStarSelectedNotes(!starred)
|
||||||
}}
|
}}
|
||||||
@@ -350,7 +367,7 @@ const NotesOptions = ({
|
|||||||
|
|
||||||
{unpinned && (
|
{unpinned && (
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setPinSelectedNotes(true)
|
notesController.setPinSelectedNotes(true)
|
||||||
}}
|
}}
|
||||||
@@ -361,7 +378,7 @@ const NotesOptions = ({
|
|||||||
)}
|
)}
|
||||||
{pinned && (
|
{pinned && (
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
notesController.setPinSelectedNotes(false)
|
notesController.setPinSelectedNotes(false)
|
||||||
}}
|
}}
|
||||||
@@ -371,7 +388,7 @@ const NotesOptions = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
application.isNativeMobileWeb() ? shareSelectedNotes(application, notes) : downloadSelectedItems()
|
application.isNativeMobileWeb() ? shareSelectedNotes(application, notes) : downloadSelectedItems()
|
||||||
}}
|
}}
|
||||||
@@ -380,24 +397,18 @@ const NotesOptions = ({
|
|||||||
{application.platform === Platform.Android ? 'Share' : 'Export'}
|
{application.platform === Platform.Android ? 'Share' : 'Export'}
|
||||||
</button>
|
</button>
|
||||||
{application.platform === Platform.Android && (
|
{application.platform === Platform.Android && (
|
||||||
<button
|
<button className={defaultClassNames} onClick={() => downloadSelectedNotesOnAndroid(application, notes)}>
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
|
||||||
onClick={() => downloadSelectedNotesOnAndroid(application, notes)}
|
|
||||||
>
|
|
||||||
<Icon type="download" className={iconClass} />
|
<Icon type="download" className={iconClass} />
|
||||||
Export
|
Export
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button className={defaultClassNames} onClick={duplicateSelectedItems}>
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
|
||||||
onClick={duplicateSelectedItems}
|
|
||||||
>
|
|
||||||
<Icon type="copy" className={iconClass} />
|
<Icon type="copy" className={iconClass} />
|
||||||
Duplicate
|
Duplicate
|
||||||
</button>
|
</button>
|
||||||
{unarchived && (
|
{unarchived && (
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await notesController.setArchiveSelectedNotes(true).catch(console.error)
|
await notesController.setArchiveSelectedNotes(true).catch(console.error)
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
@@ -409,7 +420,7 @@ const NotesOptions = ({
|
|||||||
)}
|
)}
|
||||||
{archived && (
|
{archived && (
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await notesController.setArchiveSelectedNotes(false).catch(console.error)
|
await notesController.setArchiveSelectedNotes(false).catch(console.error)
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
@@ -429,7 +440,7 @@ const NotesOptions = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await notesController.setTrashSelectedNotes(true)
|
await notesController.setTrashSelectedNotes(true)
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
@@ -442,7 +453,7 @@ const NotesOptions = ({
|
|||||||
{trashed && (
|
{trashed && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await notesController.setTrashSelectedNotes(false)
|
await notesController.setTrashSelectedNotes(false)
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
@@ -458,7 +469,7 @@ const NotesOptions = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-menu-item"
|
className={defaultClassNames}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await notesController.emptyTrash()
|
await notesController.emptyTrash()
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
@@ -474,14 +485,31 @@ const NotesOptions = ({
|
|||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{notes.length === 1 ? (
|
{notes.length === 1 ? (
|
||||||
<>
|
<>
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
<ListedActionsOption application={application} note={notes[0]} />
|
|
||||||
|
<ListedActionsOption
|
||||||
|
iconClassName={iconClass}
|
||||||
|
className={switchClassNames}
|
||||||
|
application={application}
|
||||||
|
note={notes[0]}
|
||||||
|
/>
|
||||||
|
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
<SpellcheckOptions editorForNote={editorForNote} notesController={notesController} note={notes[0]} />
|
|
||||||
|
<SpellcheckOptions
|
||||||
|
className={switchClassNames}
|
||||||
|
editorForNote={editorForNote}
|
||||||
|
notesController={notesController}
|
||||||
|
note={notes[0]}
|
||||||
|
/>
|
||||||
|
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
|
|
||||||
<NoteAttributes application={application} note={notes[0]} />
|
<NoteAttributes application={application} note={notes[0]} />
|
||||||
|
|
||||||
<NoteSizeWarning note={notes[0]} />
|
<NoteSizeWarning note={notes[0]} />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const NotesOptionsPanel = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RoundIconButton label="Note options menu" onClick={toggleMenu} ref={buttonRef} icon="more" />
|
<RoundIconButton label="Note options menu" onClick={toggleMenu} ref={buttonRef} icon="more" />
|
||||||
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="select-none py-2">
|
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="select-none">
|
||||||
<NotesOptions
|
<NotesOptions
|
||||||
application={application}
|
application={application}
|
||||||
navigationController={navigationController}
|
navigationController={navigationController}
|
||||||
|
|||||||
@@ -27,9 +27,8 @@ const PinNoteButton: FunctionComponent<Props> = ({ className = '', notesControll
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`sn-icon-button flex h-9 w-9 cursor-pointer items-center justify-center rounded-full border border-solid border-border text-neutral hover:bg-contrast focus:bg-contrast md:h-8 md:w-8 ${
|
className={`sn-icon-button flex h-10 min-w-10 cursor-pointer items-center justify-center rounded-full border border-solid border-border text-neutral hover:bg-contrast focus:bg-contrast
|
||||||
pinned ? 'toggled' : ''
|
md:h-8 md:min-w-8 ${pinned ? 'toggled' : ''} ${className}`}
|
||||||
} ${className}`}
|
|
||||||
onClick={togglePinned}
|
onClick={togglePinned}
|
||||||
>
|
>
|
||||||
<VisuallyHidden>Pin selected notes</VisuallyHidden>
|
<VisuallyHidden>Pin selected notes</VisuallyHidden>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useDocumentRect } from '@/Hooks/useDocumentRect'
|
import { useDocumentRect } from '@/Hooks/useDocumentRect'
|
||||||
import { useAutoElementRect } from '@/Hooks/useElementRect'
|
import { useAutoElementRect } from '@/Hooks/useElementRect'
|
||||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||||
import { useState } from 'react'
|
import { useCallback, useLayoutEffect, useState } from 'react'
|
||||||
import Icon from '../Icon/Icon'
|
import Icon from '../Icon/Icon'
|
||||||
import Portal from '../Portal/Portal'
|
import Portal from '../Portal/Portal'
|
||||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||||
@@ -54,6 +54,18 @@ const PositionedPopoverContent = ({
|
|||||||
|
|
||||||
useDisableBodyScrollOnMobile()
|
useDisableBodyScrollOnMobile()
|
||||||
|
|
||||||
|
const correctInitialScrollForOverflowedContent = useCallback(() => {
|
||||||
|
if (popoverElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
popoverElement.scrollTop = 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [popoverElement])
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
correctInitialScrollForOverflowedContent()
|
||||||
|
}, [popoverElement, correctInitialScrollForOverflowedContent])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<div
|
<div
|
||||||
@@ -70,15 +82,13 @@ const PositionedPopoverContent = ({
|
|||||||
: '',
|
: '',
|
||||||
top: !isDesktopScreen ? `${document.documentElement.scrollTop}px` : '',
|
top: !isDesktopScreen ? `${document.documentElement.scrollTop}px` : '',
|
||||||
}}
|
}}
|
||||||
ref={(node) => {
|
ref={setPopoverElement}
|
||||||
setPopoverElement(node)
|
|
||||||
}}
|
|
||||||
data-popover={id}
|
data-popover={id}
|
||||||
>
|
>
|
||||||
<div className="md:hidden">
|
<div className="md:hidden">
|
||||||
<div className="flex items-center justify-end px-3 pt-2">
|
<div className="flex items-center justify-end px-3 pt-2">
|
||||||
<button className="rounded-full border border-border p-1" onClick={togglePopover}>
|
<button className="rounded-full border border-border p-1" onClick={togglePopover}>
|
||||||
<Icon type="close" className="h-4 w-4" />
|
<Icon type="close" className="h-6 w-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const AccountPreferences = ({ application, viewControllerManager }: Props) => (
|
|||||||
{application.hasAccount() && viewControllerManager.featuresController.hasFiles && (
|
{application.hasAccount() && viewControllerManager.featuresController.hasFiles && (
|
||||||
<FilesSection application={application} />
|
<FilesSection application={application} />
|
||||||
)}
|
)}
|
||||||
<Email application={application} />
|
{application.hasAccount() && <Email application={application} />}
|
||||||
<SignOutWrapper application={application} viewControllerManager={viewControllerManager} />
|
<SignOutWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||||
<DeleteAccount application={application} viewControllerManager={viewControllerManager} />
|
<DeleteAccount application={application} viewControllerManager={viewControllerManager} />
|
||||||
</PreferencesPane>
|
</PreferencesPane>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
@@ -33,11 +33,11 @@ const Authentication: FunctionComponent<Props> = ({ viewControllerManager }) =>
|
|||||||
<div className="flex flex-col items-center px-4 md:px-12">
|
<div className="flex flex-col items-center px-4 md:px-12">
|
||||||
<AccountIllustration className="mb-3" />
|
<AccountIllustration className="mb-3" />
|
||||||
<Title>You're not signed in</Title>
|
<Title>You're not signed in</Title>
|
||||||
<Text className="mb-3 text-center">
|
<div className="mb-3 text-center text-base lg:text-sm">
|
||||||
Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.
|
Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.
|
||||||
</Text>
|
</div>
|
||||||
<Button primary label="Create free account" onClick={clickRegister} className="mb-3" />
|
<Button primary label="Create free account" onClick={clickRegister} className="mb-3" />
|
||||||
<div className="text-sm">
|
<div className="text-base lg:text-sm">
|
||||||
Already have an account?{' '}
|
Already have an account?{' '}
|
||||||
<button className="cursor-pointer border-0 bg-default p-0 text-info underline" onClick={clickSignIn}>
|
<button className="cursor-pointer border-0 bg-default p-0 text-info underline" onClick={clickSignIn}>
|
||||||
Sign in
|
Sign in
|
||||||
|
|||||||
@@ -19,12 +19,7 @@ const DeleteAccount = ({ application, viewControllerManager }: Props) => {
|
|||||||
<PreferencesGroup>
|
<PreferencesGroup>
|
||||||
<PreferencesSegment>
|
<PreferencesSegment>
|
||||||
<Title>Delete account</Title>
|
<Title>Delete account</Title>
|
||||||
<Text>
|
<Text>This action is irreversible. After deletion completes, you will be signed out on all devices.</Text>
|
||||||
This action is irreversible. After deletion completes, you will be signed out on all devices. If you have an
|
|
||||||
active paid subscription, cancel the subscription first. Otherwise, if you'd like to keep the subscription,
|
|
||||||
you can re-register with the same email after deletion, and your subscription will be linked back up with your
|
|
||||||
account.
|
|
||||||
</Text>
|
|
||||||
<div className="mt-3 flex flex-row flex-wrap gap-3">
|
<div className="mt-3 flex flex-row flex-wrap gap-3">
|
||||||
<Button
|
<Button
|
||||||
colorStyle="danger"
|
colorStyle="danger"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const EmailBackups = ({ application }: Props) => {
|
|||||||
const [emailFrequency, setEmailFrequency] = useState<EmailBackupFrequency>(EmailBackupFrequency.Disabled)
|
const [emailFrequency, setEmailFrequency] = useState<EmailBackupFrequency>(EmailBackupFrequency.Disabled)
|
||||||
const [emailFrequencyOptions, setEmailFrequencyOptions] = useState<DropdownItem[]>([])
|
const [emailFrequencyOptions, setEmailFrequencyOptions] = useState<DropdownItem[]>([])
|
||||||
const [isFailedBackupEmailMuted, setIsFailedBackupEmailMuted] = useState(true)
|
const [isFailedBackupEmailMuted, setIsFailedBackupEmailMuted] = useState(true)
|
||||||
|
const hasAccount = application.hasAccount()
|
||||||
|
|
||||||
const loadEmailFrequencySetting = useCallback(async () => {
|
const loadEmailFrequencySetting = useCallback(async () => {
|
||||||
if (!application.getUser()) {
|
if (!application.getUser()) {
|
||||||
@@ -104,12 +105,13 @@ const EmailBackups = ({ application }: Props) => {
|
|||||||
<PreferencesGroup>
|
<PreferencesGroup>
|
||||||
<PreferencesSegment>
|
<PreferencesSegment>
|
||||||
<Title>Email Backups</Title>
|
<Title>Email Backups</Title>
|
||||||
<div>
|
{!isDesktopApplication() && (
|
||||||
{!isDesktopApplication() && (
|
<Text className="mb-3">
|
||||||
<Text className="mb-3">
|
Receive daily encrypted email backups of all your notes directly in your email inbox.
|
||||||
Daily encrypted email backups of your entire data set delivered directly to your inbox.
|
</Text>
|
||||||
</Text>
|
)}
|
||||||
)}
|
|
||||||
|
<div className={`${!hasAccount ? 'pointer-events-none cursor-default opacity-50' : ''}`}>
|
||||||
<Subtitle>Email frequency</Subtitle>
|
<Subtitle>Email frequency</Subtitle>
|
||||||
<Text>How often to receive backups.</Text>
|
<Text>How often to receive backups.</Text>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
|
|||||||
@@ -78,11 +78,7 @@ const HelpAndFeedback = ({ application }: { application: WebApplication }) => {
|
|||||||
<Title>Community forum</Title>
|
<Title>Community forum</Title>
|
||||||
<Text>
|
<Text>
|
||||||
If you have an issue, found a bug or want to suggest a feature, you can browse or post to the forum. It’s
|
If you have an issue, found a bug or want to suggest a feature, you can browse or post to the forum. It’s
|
||||||
recommended for non-account related issues. Please read our{' '}
|
recommended for non-account related issues.
|
||||||
<a target="_blank" className="underline hover:no-underline" href="https://standardnotes.com/longevity/">
|
|
||||||
Longevity statement
|
|
||||||
</a>{' '}
|
|
||||||
before advocating for a feature request.
|
|
||||||
</Text>
|
</Text>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
@@ -97,14 +93,8 @@ const HelpAndFeedback = ({ application }: { application: WebApplication }) => {
|
|||||||
<Title>Community groups</Title>
|
<Title>Community groups</Title>
|
||||||
<Text>
|
<Text>
|
||||||
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your feedback with us? Join
|
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your feedback with us? Join
|
||||||
the Standard Notes community groups for discussions on security, themes, editors and more.
|
the Standard Notes Discord for discussions on security, themes, editors and more.
|
||||||
</Text>
|
</Text>
|
||||||
<LinkButton
|
|
||||||
className="mt-3"
|
|
||||||
link="https://standardnotes.com/slack"
|
|
||||||
label="Join our Slack"
|
|
||||||
onClick={handleClick}
|
|
||||||
/>
|
|
||||||
<LinkButton
|
<LinkButton
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
link="https://standardnotes.com/discord"
|
link="https://standardnotes.com/discord"
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ const Listed = ({ application }: Props) => {
|
|||||||
const [requestingAccount, setRequestingAccount] = useState<boolean>()
|
const [requestingAccount, setRequestingAccount] = useState<boolean>()
|
||||||
|
|
||||||
const reloadAccounts = useCallback(async () => {
|
const reloadAccounts = useCallback(async () => {
|
||||||
setAccounts(await application.listed.getListedAccounts())
|
if (application.hasAccount()) {
|
||||||
|
setAccounts(await application.listed.getListedAccounts())
|
||||||
|
}
|
||||||
}, [application])
|
}, [application])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -82,11 +84,11 @@ const Listed = ({ application }: Props) => {
|
|||||||
<Subtitle>What is Listed?</Subtitle>
|
<Subtitle>What is Listed?</Subtitle>
|
||||||
<Text>
|
<Text>
|
||||||
Listed is a free blogging platform that allows you to create a public journal published directly from your
|
Listed is a free blogging platform that allows you to create a public journal published directly from your
|
||||||
notes.{' '}
|
notes. {!application.getUser() && 'To get started, sign in or register for a Standard Notes account.'}
|
||||||
<a target="_blank" href="https://listed.to" rel="noreferrer noopener">
|
|
||||||
Learn more
|
|
||||||
</a>
|
|
||||||
</Text>
|
</Text>
|
||||||
|
<a className="mt-2 text-info" target="_blank" href="https://listed.to" rel="noreferrer noopener">
|
||||||
|
Learn more
|
||||||
|
</a>
|
||||||
</PreferencesSegment>
|
</PreferencesSegment>
|
||||||
{application.getUser() && (
|
{application.getUser() && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
|
||||||
import Switch from '@/Components/Switch/Switch'
|
import Switch from '@/Components/Switch/Switch'
|
||||||
import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
@@ -75,7 +74,6 @@ const Privacy: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
<PreferencesSegment>
|
<PreferencesSegment>
|
||||||
<Title>Privacy</Title>
|
<Title>Privacy</Title>
|
||||||
<div>
|
<div>
|
||||||
<HorizontalSeparator classes="my-4" />
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Subtitle>Session user agent logging</Subtitle>
|
<Subtitle>Session user agent logging</Subtitle>
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ export const Title: FunctionComponent<Props> = ({ children, className }) => (
|
|||||||
)
|
)
|
||||||
|
|
||||||
export const Subtitle: FunctionComponent<Props> = ({ children, className }) => (
|
export const Subtitle: FunctionComponent<Props> = ({ children, className }) => (
|
||||||
<h4 className={classNames('m-0 mb-1 text-sm font-medium', className)}>{children}</h4>
|
<h4 className={classNames('m-0 mb-1 text-base font-medium lg:text-sm', className)}>{children}</h4>
|
||||||
)
|
)
|
||||||
|
|
||||||
export const SubtitleLight: FunctionComponent<Props> = ({ children, className }) => (
|
export const SubtitleLight: FunctionComponent<Props> = ({ children, className }) => (
|
||||||
<h4 className={classNames('m-0 mb-1 text-sm font-normal', className)}>{children}</h4>
|
<h4 className={classNames('m-0 mb-1 text-base font-normal lg:text-sm', className)}>{children}</h4>
|
||||||
)
|
)
|
||||||
|
|
||||||
export const Text: FunctionComponent<Props> = ({ children, className }) => (
|
export const Text: FunctionComponent<Props> = ({ children, className }) => (
|
||||||
<p className={classNames('text-sm md:text-xs', className)}>{children}</p>
|
<p className={classNames('text-base lg:text-xs', className)}>{children}</p>
|
||||||
)
|
)
|
||||||
|
|
||||||
const buttonClasses =
|
const buttonClasses =
|
||||||
'block bg-default text-text rounded border-solid \
|
'block bg-default text-text rounded border-solid \
|
||||||
border px-4 py-1.5 font-bold text-sm w-fit \
|
border px-4 py-1.5 font-bold text-base lg:text-sm w-fit \
|
||||||
focus:bg-contrast hover:bg-contrast border-border'
|
focus:bg-contrast hover:bg-contrast border-border'
|
||||||
|
|
||||||
export const LinkButton: FunctionComponent<{
|
export const LinkButton: FunctionComponent<{
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const SearchBar = ({ itemListController, searchOptionsController }: Props) => {
|
|||||||
autocomplete={false}
|
autocomplete={false}
|
||||||
className={{
|
className={{
|
||||||
container: 'px-1',
|
container: 'px-1',
|
||||||
input: 'placeholder:text-passive-0',
|
input: 'text-base placeholder:text-passive-0 lg:text-sm',
|
||||||
}}
|
}}
|
||||||
placeholder={'Search...'}
|
placeholder={'Search...'}
|
||||||
value={noteFilterText}
|
value={noteFilterText}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
|
|||||||
>
|
>
|
||||||
<div className={'section-title-bar'}>
|
<div className={'section-title-bar'}>
|
||||||
<div className="section-title-bar-header">
|
<div className="section-title-bar-header">
|
||||||
<div className="title text-sm">
|
<div className="title text-base md:text-sm">
|
||||||
<span className="font-bold">Views</span>
|
<span className="font-bold">Views</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
|
|||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
className={classNames('tag py-2 px-3.5 md:py-1', isSelected && 'selected', isFaded && 'opacity-50')}
|
className={classNames('tag px-3.5', isSelected && 'selected', isFaded && 'opacity-50')}
|
||||||
onClick={selectCurrentTag}
|
onClick={selectCurrentTag}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
|
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
|
||||||
@@ -117,7 +117,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
|
|||||||
</div>
|
</div>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<input
|
<input
|
||||||
className={'title editing'}
|
className={'title editing text-mobile-navigation-list-item lg:text-navigation-list-item'}
|
||||||
id={`react-tag-${view.uuid}`}
|
id={`react-tag-${view.uuid}`}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onInput={onInput}
|
onInput={onInput}
|
||||||
@@ -127,18 +127,23 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
|
|||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={'title overflow-hidden text-left'} id={`react-tag-${view.uuid}`}>
|
<div
|
||||||
|
className={
|
||||||
|
'title overflow-hidden text-left text-mobile-navigation-list-item lg:text-navigation-list-item'
|
||||||
|
}
|
||||||
|
id={`react-tag-${view.uuid}`}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={'count'}>{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}</div>
|
<div className={'count text-base lg:text-sm'}>
|
||||||
|
{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isSystemView(view) && (
|
{!isSystemView(view) && (
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
{view.conflictOf && (
|
{view.conflictOf && <div className="-mt-1 text-[0.625rem] font-bold text-danger">Conflicted Copy</div>}
|
||||||
<div className="danger text-[0.625rem] font-bold">Conflicted Copy {view.conflictOf}</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isSelected && (
|
{isSelected && (
|
||||||
<div className="menu">
|
<div className="menu">
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
<HorizontalSeparator classes="my-2" />
|
<HorizontalSeparator classes="my-2" />
|
||||||
<div className="px-3 pt-1 pb-1.5 text-xs font-medium text-neutral">
|
<div className="px-3 pt-1 pb-1.5 text-sm font-medium text-neutral lg:text-xs">
|
||||||
<div className="mb-1">
|
<div className="mb-1">
|
||||||
<span className="font-semibold">Last modified:</span> {tagLastModified}
|
<span className="font-semibold">Last modified:</span> {tagLastModified}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, type }: Pro
|
|||||||
return (
|
return (
|
||||||
<DndProvider backend={backend}>
|
<DndProvider backend={backend}>
|
||||||
{allTags.length === 0 ? (
|
{allTags.length === 0 ? (
|
||||||
<div className="no-tags-placeholder">No tags or folders. Create one using the add button above.</div>
|
<div className="no-tags-placeholder text-base opacity-[0.4] lg:text-sm">
|
||||||
|
No tags or folders. Create one using the add button above.
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{allTags.map((tag) => {
|
{allTags.map((tag) => {
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||||
className={classNames('tag py-2 px-3.5 md:py-1', isSelected && 'selected', readyToDrop && 'is-drag-over')}
|
className={classNames('tag px-3.5', isSelected && 'selected', readyToDrop && 'is-drag-over')}
|
||||||
onClick={selectCurrentTag}
|
onClick={selectCurrentTag}
|
||||||
ref={mergeRefs([dragRef, tagRef])}
|
ref={mergeRefs([dragRef, tagRef])}
|
||||||
style={{
|
style={{
|
||||||
@@ -275,7 +275,9 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
</div>
|
</div>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<input
|
<input
|
||||||
className={'title editing focus:shadow-none focus:outline-none'}
|
className={
|
||||||
|
'title editing text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
|
||||||
|
}
|
||||||
id={`react-tag-${tag.uuid}-${type}`}
|
id={`react-tag-${tag.uuid}-${type}`}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onInput={onInput}
|
onInput={onInput}
|
||||||
@@ -286,7 +288,9 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={'title overflow-hidden text-left focus:shadow-none focus:outline-none'}
|
className={
|
||||||
|
'title overflow-hidden text-left text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
|
||||||
|
}
|
||||||
id={`react-tag-${tag.uuid}-${type}`}
|
id={`react-tag-${tag.uuid}-${type}`}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
@@ -303,12 +307,12 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
>
|
>
|
||||||
<Icon type="more" className="text-neutral" />
|
<Icon type="more" className="text-neutral" />
|
||||||
</a>
|
</a>
|
||||||
<div className="count">{noteCounts.get()}</div>
|
<div className="count text-base lg:text-sm">{noteCounts.get()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
|
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
|
||||||
{tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>}
|
{tag.conflictOf && <div className="-mt-1 text-[0.625rem] font-bold text-danger">Conflicted Copy</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isAddingSubtag && (
|
{isAddingSubtag && (
|
||||||
@@ -324,7 +328,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
|||||||
<Icon type="hashtag" className="mr-1 text-neutral" />
|
<Icon type="hashtag" className="mr-1 text-neutral" />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
className="title w-full focus:shadow-none focus:outline-none"
|
className="title w-full text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item"
|
||||||
type="text"
|
type="text"
|
||||||
ref={subtagInputRef}
|
ref={subtagInputRef}
|
||||||
onBlur={onSubtagInputBlur}
|
onBlur={onSubtagInputBlur}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
|
|||||||
<section>
|
<section>
|
||||||
<div className={'section-title-bar'}>
|
<div className={'section-title-bar'}>
|
||||||
<div className="section-title-bar-header">
|
<div className="section-title-bar-header">
|
||||||
<div className="title text-sm">
|
<div className="title text-base md:text-sm">
|
||||||
<span className="font-bold">Favorites</span>
|
<span className="font-bold">Favorites</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
|
|||||||
if (entitledToFolders) {
|
if (entitledToFolders) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="title text-sm">
|
<div className="title text-base md:text-sm">
|
||||||
<span className="font-bold">Folders</span>
|
<span className="font-bold">Folders</span>
|
||||||
{hasMigration && (
|
{hasMigration && (
|
||||||
<label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}>
|
<label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}>
|
||||||
@@ -36,7 +36,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="title text-sm">
|
<div className="title text-base md:text-sm">
|
||||||
<span className="font-bold">Tags</span>
|
<span className="font-bold">Tags</span>
|
||||||
<Tooltip label={TAG_FOLDERS_FEATURE_TOOLTIP}>
|
<Tooltip label={TAG_FOLDERS_FEATURE_TOOLTIP}>
|
||||||
<label className="ml-1 cursor-pointer font-bold text-passive-2" onClick={showPremiumAlert}>
|
<label className="ml-1 cursor-pointer font-bold text-passive-2" onClick={showPremiumAlert}>
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = 'Upgrade'
|
|||||||
|
|
||||||
export const STRING_REMOVE_OFFLINE_KEY_CONFIRMATION = 'This will delete the previously saved offline key.'
|
export const STRING_REMOVE_OFFLINE_KEY_CONFIRMATION = 'This will delete the previously saved offline key.'
|
||||||
|
|
||||||
export const STRING_DELETE_ACCOUNT_CONFIRMATION = 'Are you sure you want to permanently delete your account?'
|
export const STRING_DELETE_ACCOUNT_CONFIRMATION =
|
||||||
|
"Are you sure you want to permanently delete your account? You will be asked to confirm your account password in the next step. If you have an active paid subscription, cancel the subscription first. Otherwise, if you'd like to keep the subscription, you can re-register with the same email after deletion, and your subscription will be linked back up with your account."
|
||||||
|
|
||||||
export const STRING_FAILED_TO_UPDATE_USER_SETTING =
|
export const STRING_FAILED_TO_UPDATE_USER_SETTING =
|
||||||
'There was an error while trying to update your settings. Please try again.'
|
'There was an error while trying to update your settings. Please try again.'
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export const MenuItemIconSize = 'w-6 h-6 md:w-5 md:h-5'
|
||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
// Follows https://tailwindcss.com/docs/responsive-design
|
// Follows https://tailwindcss.com/docs/responsive-design
|
||||||
export const MediaQueryBreakpoints = {
|
export const MediaQueryBreakpoints = {
|
||||||
sm: '(min-width: 640px)',
|
sm: '(max-width: 640px)',
|
||||||
md: '(min-width: 768px)',
|
md: '(min-width: 768px)',
|
||||||
lg: '(min-width: 1024px)',
|
lg: '(min-width: 1024px)',
|
||||||
xl: '(min-width: 1280px)',
|
xl: '(min-width: 1280px)',
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum LoggingDomain {
|
|||||||
NoteView,
|
NoteView,
|
||||||
ItemsList,
|
ItemsList,
|
||||||
NavigationList,
|
NavigationList,
|
||||||
|
Viewport,
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoggingStatus: Record<LoggingDomain, boolean> = {
|
const LoggingStatus: Record<LoggingDomain, boolean> = {
|
||||||
@@ -13,6 +14,7 @@ const LoggingStatus: Record<LoggingDomain, boolean> = {
|
|||||||
[LoggingDomain.NoteView]: false,
|
[LoggingDomain.NoteView]: false,
|
||||||
[LoggingDomain.ItemsList]: false,
|
[LoggingDomain.ItemsList]: false,
|
||||||
[LoggingDomain.NavigationList]: false,
|
[LoggingDomain.NavigationList]: false,
|
||||||
|
[LoggingDomain.Viewport]: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function log(domain: LoggingDomain, ...args: any[]): void {
|
export function log(domain: LoggingDomain, ...args: any[]): void {
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ export class MobileWebReceiver {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(message)
|
const parsed = JSON.parse(message)
|
||||||
const { messageType, reactNativeEvent } = parsed
|
const { messageType, reactNativeEvent, messageData } = parsed
|
||||||
|
|
||||||
if (messageType === 'event' && reactNativeEvent) {
|
if (messageType === 'event' && reactNativeEvent) {
|
||||||
const nativeEvent = reactNativeEvent as ReactNativeToWebEvent
|
const nativeEvent = reactNativeEvent as ReactNativeToWebEvent
|
||||||
this.handleNativeEvent(nativeEvent)
|
this.handleNativeEvent(nativeEvent, messageData)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('[MobileWebReceiver] Error parsing message from React Native', error)
|
console.log('[MobileWebReceiver] Error parsing message from React Native', error)
|
||||||
@@ -51,7 +51,7 @@ export class MobileWebReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNativeEvent(event: ReactNativeToWebEvent) {
|
handleNativeEvent(event: ReactNativeToWebEvent, messageData?: unknown) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ReactNativeToWebEvent.EnteringBackground:
|
case ReactNativeToWebEvent.EnteringBackground:
|
||||||
void this.application.handleMobileEnteringBackgroundEvent()
|
void this.application.handleMobileEnteringBackgroundEvent()
|
||||||
@@ -71,6 +71,16 @@ export class MobileWebReceiver {
|
|||||||
case ReactNativeToWebEvent.ColorSchemeChanged:
|
case ReactNativeToWebEvent.ColorSchemeChanged:
|
||||||
void this.application.handleMobileColorSchemeChangeEvent()
|
void this.application.handleMobileColorSchemeChangeEvent()
|
||||||
break
|
break
|
||||||
|
case ReactNativeToWebEvent.KeyboardFrameWillChange:
|
||||||
|
void this.application.handleMobileKeyboardWillChangeFrameEvent(
|
||||||
|
messageData as { height: number; contentHeight: number },
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case ReactNativeToWebEvent.KeyboardFrameDidChange:
|
||||||
|
void this.application.handleMobileKeyboardDidChangeFrameEvent(
|
||||||
|
messageData as { height: number; contentHeight: number },
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ export const disableIosTextFieldZoom = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const isMobileScreen = () => !window.matchMedia(MediaQueryBreakpoints.md).matches
|
export const isMobileScreen = () => !window.matchMedia(MediaQueryBreakpoints.md).matches
|
||||||
|
export const isTabletScreen = () =>
|
||||||
|
!window.matchMedia(MediaQueryBreakpoints.sm).matches && !window.matchMedia(MediaQueryBreakpoints.lg).matches
|
||||||
|
|
||||||
export const getBase64FromBlob = (blob: Blob) => {
|
export const getBase64FromBlob = (blob: Blob) => {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
import { isDev } from '@/Utils'
|
import { log, LoggingDomain } from './Logging'
|
||||||
|
|
||||||
export const ViewportHeightKey = '--viewport-height'
|
export const ViewportHeightKey = '--viewport-height'
|
||||||
|
|
||||||
export const setViewportHeightWithFallback = () => {
|
export const setViewportHeightWithFallback = () => {
|
||||||
const currentHeight = parseInt(document.documentElement.style.getPropertyValue(ViewportHeightKey))
|
|
||||||
const newValue = visualViewport && visualViewport.height > 0 ? visualViewport.height : window.innerHeight
|
const newValue = visualViewport && visualViewport.height > 0 ? visualViewport.height : window.innerHeight
|
||||||
|
|
||||||
if (isDev) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`currentHeight: ${currentHeight}, newValue: ${newValue}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentHeight && newValue < currentHeight) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
document.documentElement.style.setProperty(ViewportHeightKey, '100vh')
|
setCustomViewportHeight('100', 'vh')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
document.documentElement.style.setProperty(ViewportHeightKey, `${newValue}px`)
|
setCustomViewportHeight(String(newValue), 'px')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param forceTriggerResizeEvent On iPad at least, setProperty(ViewportHeightKey) does not trigger a resize event
|
||||||
|
*/
|
||||||
|
export const setCustomViewportHeight = (height: string, suffix: 'px' | 'vh', forceTriggerResizeEvent = false) => {
|
||||||
|
log(LoggingDomain.Viewport, `setCustomViewportHeight: ${height}`)
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(ViewportHeightKey, `${height}${suffix}`)
|
||||||
|
|
||||||
|
if (forceTriggerResizeEvent) {
|
||||||
|
window.dispatchEvent(new Event('resize'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,11 +137,6 @@ body,
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background-color: var(--editor-header-bar-background-color);
|
background-color: var(--editor-header-bar-background-color);
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
min-height: 100vh;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$footer-height: 2rem;
|
$footer-height: 2rem;
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ $content-horizontal-padding: 16px;
|
|||||||
|
|
||||||
.no-tags-placeholder {
|
.no-tags-placeholder {
|
||||||
padding: 0px $content-horizontal-padding;
|
padding: 0px $content-horizontal-padding;
|
||||||
font-size: 12px;
|
|
||||||
opacity: 0.4;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.root-drop {
|
.root-drop {
|
||||||
@@ -68,10 +65,19 @@ $content-horizontal-padding: 16px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .tag-info {
|
> .tag-info {
|
||||||
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
min-height: 35px;
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
min-height: 31px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1024px) {
|
||||||
|
min-height: 29.5px;
|
||||||
|
}
|
||||||
|
|
||||||
.sn-icon {
|
.sn-icon {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -93,9 +99,6 @@ $content-horizontal-padding: 16px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .title {
|
> .title {
|
||||||
font-size: 14px;
|
|
||||||
line-height: 18px;
|
|
||||||
|
|
||||||
width: 80%;
|
width: 80%;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-reach-dialog-overlay]::before {
|
[data-reach-dialog-overlay]::before {
|
||||||
background-color: var(--sn-stylekit-contrast-background-color);
|
background-color: var(--sn-stylekit-passive-color-5);
|
||||||
content: '';
|
content: '';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
@@ -19,10 +19,6 @@
|
|||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
.challenge-modal-overlay::before {
|
|
||||||
background-color: var(--sn-stylekit-passive-color-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-reach-dialog-content] {
|
[data-reach-dialog-content] {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ module.exports = {
|
|||||||
4.5: '1.125rem',
|
4.5: '1.125rem',
|
||||||
8.5: '2.125rem',
|
8.5: '2.125rem',
|
||||||
13: '3.25rem',
|
13: '3.25rem',
|
||||||
|
15: '3.75rem',
|
||||||
18: '4.5rem',
|
18: '4.5rem',
|
||||||
26: '6.5rem',
|
26: '6.5rem',
|
||||||
30: '7.5rem',
|
30: '7.5rem',
|
||||||
@@ -28,9 +29,11 @@ module.exports = {
|
|||||||
3: '0.75rem',
|
3: '0.75rem',
|
||||||
4: '1rem',
|
4: '1rem',
|
||||||
5: '1.25rem',
|
5: '1.25rem',
|
||||||
|
6: '1.5rem',
|
||||||
8: '2rem',
|
8: '2rem',
|
||||||
8.5: '2.125rem',
|
8.5: '2.125rem',
|
||||||
9: '2.25rem',
|
9: '2.25rem',
|
||||||
|
10: '2.5rem',
|
||||||
15: '3.75rem',
|
15: '3.75rem',
|
||||||
20: '5rem',
|
20: '5rem',
|
||||||
24: '6rem',
|
24: '6rem',
|
||||||
@@ -87,7 +90,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
'menu-item': '0.813rem',
|
'menu-item': '0.813rem',
|
||||||
'mobile-menu-item': '0.9rem',
|
'mobile-menu-item': '1.1rem',
|
||||||
|
'tablet-menu-item': '0.95rem',
|
||||||
|
'navigation-list-item': '0.88rem',
|
||||||
|
'mobile-navigation-list-item': '1rem',
|
||||||
editor: 'var(--sn-stylekit-font-size-editor)',
|
editor: 'var(--sn-stylekit-font-size-editor)',
|
||||||
},
|
},
|
||||||
screens: {
|
screens: {
|
||||||
|
|||||||
Reference in New Issue
Block a user