refactor: format and lint codebase (#971)

This commit is contained in:
Aman Harwara
2022-04-13 22:02:34 +05:30
committed by GitHub
parent dc9c1ea0fc
commit 8e467f9e6d
367 changed files with 13778 additions and 16093 deletions

View File

@@ -0,0 +1,75 @@
import { RefObject } from 'preact'
import { StateUpdater } from 'preact/hooks'
import { JSXInternal } from 'preact/src/jsx'
export const quickSettingsKeyDownHandler = (
closeQuickSettingsMenu: () => void,
event: JSXInternal.TargetedKeyboardEvent<HTMLDivElement>,
quickSettingsMenuRef: RefObject<HTMLDivElement>,
themesMenuOpen: boolean,
) => {
if (quickSettingsMenuRef?.current) {
const items: NodeListOf<HTMLButtonElement> =
quickSettingsMenuRef.current.querySelectorAll(':scope > button')
const currentFocusedIndex = Array.from(items).findIndex((btn) => btn === document.activeElement)
if (!themesMenuOpen) {
switch (event.key) {
case 'Escape':
closeQuickSettingsMenu()
break
case 'ArrowDown':
if (items[currentFocusedIndex + 1]) {
items[currentFocusedIndex + 1].focus()
} else {
items[0].focus()
}
break
case 'ArrowUp':
if (items[currentFocusedIndex - 1]) {
items[currentFocusedIndex - 1].focus()
} else {
items[items.length - 1].focus()
}
break
}
}
}
}
export const themesMenuKeyDownHandler = (
event: React.KeyboardEvent<HTMLDivElement>,
themesMenuRef: RefObject<HTMLDivElement>,
setThemesMenuOpen: StateUpdater<boolean>,
themesButtonRef: RefObject<HTMLButtonElement>,
) => {
if (themesMenuRef?.current) {
const themes = themesMenuRef.current.querySelectorAll('button')
const currentFocusedIndex = Array.from(themes).findIndex(
(themeBtn) => themeBtn === document.activeElement,
)
switch (event.key) {
case 'Escape':
case 'ArrowLeft':
event.stopPropagation()
setThemesMenuOpen(false)
themesButtonRef.current?.focus()
break
case 'ArrowDown':
if (themes[currentFocusedIndex + 1]) {
themes[currentFocusedIndex + 1].focus()
} else {
themes[0].focus()
}
break
case 'ArrowUp':
if (themes[currentFocusedIndex - 1]) {
themes[currentFocusedIndex - 1].focus()
} else {
themes[themes.length - 1].focus()
}
break
}
}
}

View File

@@ -0,0 +1,61 @@
import { WebApplication } from '@/UIModels/Application'
import { FeatureStatus, FeatureIdentifier } from '@standardnotes/snjs'
import { FunctionComponent } from 'preact'
import { useCallback } from 'preact/hooks'
import { JSXInternal } from 'preact/src/jsx'
import { Icon } from '@/Components/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { Switch } from '@/Components/Switch'
type Props = {
application: WebApplication
onToggle: (value: boolean) => void
onClose: () => void
isEnabled: boolean
}
export const FocusModeSwitch: FunctionComponent<Props> = ({
application,
onToggle,
onClose,
isEnabled,
}) => {
const premiumModal = usePremiumModal()
const isEntitled =
application.features.getFeatureStatus(FeatureIdentifier.FocusMode) === FeatureStatus.Entitled
const toggle = useCallback(
(e: JSXInternal.TargetedMouseEvent<HTMLButtonElement>) => {
e.preventDefault()
if (isEntitled) {
onToggle(!isEnabled)
onClose()
} else {
premiumModal.activate('Focused Writing')
}
},
[isEntitled, onToggle, isEnabled, onClose, premiumModal],
)
return (
<>
<button
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none justify-between"
onClick={toggle}
>
<div className="flex items-center">
<Icon type="menu-close" className="color-neutral mr-2" />
Focused Writing
</div>
{isEntitled ? (
<Switch className="px-0" checked={isEnabled} />
) : (
<div title="Premium feature">
<Icon type="premium-feature" />
</div>
)}
</button>
</>
)
}

View File

@@ -0,0 +1,7 @@
import { FeatureIdentifier, SNTheme } from '@standardnotes/snjs'
export type ThemeItem = {
name: string
identifier: FeatureIdentifier
component?: SNTheme
}

View File

@@ -0,0 +1,87 @@
import { WebApplication } from '@/UIModels/Application'
import { FeatureStatus } from '@standardnotes/snjs'
import { FunctionComponent } from 'preact'
import { useMemo } from 'preact/hooks'
import { JSXInternal } from 'preact/src/jsx'
import { Icon } from '@/Components/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { Switch } from '@/Components/Switch'
import { ThemeItem } from './ThemeItem'
type Props = {
item: ThemeItem
application: WebApplication
onBlur: (event: { relatedTarget: EventTarget | null }) => void
}
export const ThemesMenuButton: FunctionComponent<Props> = ({ application, item, onBlur }) => {
const premiumModal = usePremiumModal()
const isThirdPartyTheme = useMemo(
() => application.features.isThirdPartyFeature(item.identifier),
[application, item.identifier],
)
const isEntitledToTheme = useMemo(
() => application.features.getFeatureStatus(item.identifier) === FeatureStatus.Entitled,
[application, item.identifier],
)
const canActivateTheme = useMemo(
() => isEntitledToTheme || isThirdPartyTheme,
[isEntitledToTheme, isThirdPartyTheme],
)
const toggleTheme: JSXInternal.MouseEventHandler<HTMLButtonElement> = (e) => {
e.preventDefault()
if (item.component && canActivateTheme) {
const themeIsLayerableOrNotActive = item.component.isLayerable() || !item.component.active
if (themeIsLayerableOrNotActive) {
application.mutator.toggleTheme(item.component).catch(console.error)
}
} else {
premiumModal.activate(`${item.name} theme`)
}
}
return (
<button
className={'sn-dropdown-item focus:bg-info-backdrop focus:shadow-none justify-between'}
onClick={toggleTheme}
onBlur={onBlur}
>
{item.component?.isLayerable() ? (
<>
<div className="flex items-center">
<Switch className="px-0 mr-2" checked={item.component?.active} />
{item.name}
</div>
{!canActivateTheme && <Icon type="premium-feature" />}
</>
) : (
<>
<div className="flex items-center">
<div
className={`pseudo-radio-btn ${
item.component?.active ? 'pseudo-radio-btn--checked' : ''
} mr-2`}
></div>
<span className={item.component?.active ? 'font-semibold' : undefined}>
{item.name}
</span>
</div>
{item.component && canActivateTheme ? (
<div
className="w-5 h-5 rounded-full"
style={{
backgroundColor: item.component.package_info?.dock_icon?.background_color,
}}
></div>
) : (
<Icon type="premium-feature" />
)}
</>
)}
</button>
)
}

View File

@@ -0,0 +1,313 @@
import { WebApplication } from '@/UIModels/Application'
import { AppState } from '@/UIModels/AppState'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure'
import {
ComponentArea,
ContentType,
FeatureIdentifier,
GetFeatures,
SNComponent,
SNTheme,
} from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'preact'
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
import { JSXInternal } from 'preact/src/jsx'
import { Icon } from '@/Components/Icon'
import { Switch } from '@/Components/Switch'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import { quickSettingsKeyDownHandler, themesMenuKeyDownHandler } from './EventHandlers'
import { FocusModeSwitch } from './FocusModeSwitch'
import { ThemesMenuButton } from './ThemesMenuButton'
import { useCloseOnClickOutside } from '@/Hooks/useCloseOnClickOutside'
import { ThemeItem } from './ThemeItem'
import { sortThemes } from '@/Utils/SortThemes'
const focusModeAnimationDuration = 1255
const MENU_CLASSNAME =
'sn-menu-border sn-dropdown min-w-80 max-h-120 max-w-xs flex flex-col py-2 overflow-y-auto'
type MenuProps = {
appState: AppState
application: WebApplication
onClickOutside: () => void
}
const toggleFocusMode = (enabled: boolean) => {
if (enabled) {
document.body.classList.add('focus-mode')
} else {
if (document.body.classList.contains('focus-mode')) {
document.body.classList.add('disable-focus-mode')
document.body.classList.remove('focus-mode')
setTimeout(() => {
document.body.classList.remove('disable-focus-mode')
}, focusModeAnimationDuration)
}
}
}
export const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
({ application, appState, onClickOutside }) => {
const {
closeQuickSettingsMenu,
shouldAnimateCloseMenu,
focusModeEnabled,
setFocusModeEnabled,
} = appState.quickSettingsMenu
const [themes, setThemes] = useState<ThemeItem[]>([])
const [toggleableComponents, setToggleableComponents] = useState<SNComponent[]>([])
const [themesMenuOpen, setThemesMenuOpen] = useState(false)
const [themesMenuPosition, setThemesMenuPosition] = useState({})
const [defaultThemeOn, setDefaultThemeOn] = useState(false)
const themesMenuRef = useRef<HTMLDivElement>(null)
const themesButtonRef = useRef<HTMLButtonElement>(null)
const prefsButtonRef = useRef<HTMLButtonElement>(null)
const quickSettingsMenuRef = useRef<HTMLDivElement>(null)
const defaultThemeButtonRef = useRef<HTMLButtonElement>(null)
const mainRef = useRef<HTMLDivElement>(null)
useCloseOnClickOutside(mainRef, () => {
onClickOutside()
})
useEffect(() => {
toggleFocusMode(focusModeEnabled)
}, [focusModeEnabled])
const reloadThemes = useCallback(() => {
const themes = application.items
.getDisplayableItems<SNTheme>(ContentType.Theme)
.map((item) => {
return {
name: item.name,
identifier: item.identifier,
component: item,
}
}) as ThemeItem[]
GetFeatures()
.filter((feature) => feature.content_type === ContentType.Theme && !feature.layerable)
.forEach((theme) => {
if (themes.findIndex((item) => item.identifier === theme.identifier) === -1) {
themes.push({
name: theme.name as string,
identifier: theme.identifier,
})
}
})
setThemes(themes.sort(sortThemes))
setDefaultThemeOn(
!themes
.map((item) => item?.component)
.find((theme) => theme?.active && !theme.isLayerable()),
)
}, [application])
const reloadToggleableComponents = useCallback(() => {
const toggleableComponents = application.items
.getDisplayableItems<SNComponent>(ContentType.Component)
.filter(
(component) =>
[ComponentArea.EditorStack].includes(component.area) &&
component.identifier !== FeatureIdentifier.DeprecatedFoldersComponent,
)
setToggleableComponents(toggleableComponents)
}, [application])
useEffect(() => {
if (!themes.length) {
reloadThemes()
}
}, [reloadThemes, themes.length])
useEffect(() => {
const cleanupItemStream = application.streamItems(ContentType.Theme, () => {
reloadThemes()
})
return () => {
cleanupItemStream()
}
}, [application, reloadThemes])
useEffect(() => {
const cleanupItemStream = application.streamItems(ContentType.Component, () => {
reloadToggleableComponents()
})
return () => {
cleanupItemStream()
}
}, [application, reloadToggleableComponents])
useEffect(() => {
if (themesMenuOpen) {
defaultThemeButtonRef.current?.focus()
}
}, [themesMenuOpen])
useEffect(() => {
prefsButtonRef.current?.focus()
}, [])
const [closeOnBlur] = useCloseOnBlur(themesMenuRef, setThemesMenuOpen)
const toggleThemesMenu = () => {
if (!themesMenuOpen && themesButtonRef.current) {
const themesButtonRect = themesButtonRef.current.getBoundingClientRect()
setThemesMenuPosition({
left: themesButtonRect.right,
bottom: document.documentElement.clientHeight - themesButtonRect.bottom,
})
setThemesMenuOpen(true)
} else {
setThemesMenuOpen(false)
}
}
const openPreferences = () => {
closeQuickSettingsMenu()
appState.preferences.openPreferences()
}
const toggleComponent = (component: SNComponent) => {
if (component.isTheme()) {
application.mutator.toggleTheme(component).catch(console.error)
} else {
application.mutator.toggleComponent(component).catch(console.error)
}
}
const handleBtnKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (event) => {
switch (event.key) {
case 'Escape':
setThemesMenuOpen(false)
themesButtonRef.current?.focus()
break
case 'ArrowRight':
if (!themesMenuOpen) {
toggleThemesMenu()
}
}
}
const handleQuickSettingsKeyDown: JSXInternal.KeyboardEventHandler<HTMLDivElement> = (
event,
) => {
quickSettingsKeyDownHandler(
closeQuickSettingsMenu,
event,
quickSettingsMenuRef,
themesMenuOpen,
)
}
const handlePanelKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
themesMenuKeyDownHandler(event, themesMenuRef, setThemesMenuOpen, themesButtonRef)
}
const toggleDefaultTheme = () => {
const activeTheme = themes
.map((item) => item.component)
.find((theme) => theme?.active && !theme.isLayerable())
if (activeTheme) {
application.mutator.toggleTheme(activeTheme).catch(console.error)
}
}
return (
<div ref={mainRef} className="sn-component">
<div
className={`sn-quick-settings-menu absolute ${MENU_CLASSNAME} ${
shouldAnimateCloseMenu ? 'slide-up-animation' : 'sn-dropdown--animated'
}`}
ref={quickSettingsMenuRef}
onKeyDown={handleQuickSettingsKeyDown}
>
<div className="px-3 mt-1 mb-2 font-semibold color-text uppercase">Quick Settings</div>
<Disclosure open={themesMenuOpen} onChange={toggleThemesMenu}>
<DisclosureButton
onKeyDown={handleBtnKeyDown}
onBlur={closeOnBlur}
ref={themesButtonRef}
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
>
<div className="flex items-center">
<Icon type="themes" className="color-neutral mr-2" />
Themes
</div>
<Icon type="chevron-right" className="color-neutral" />
</DisclosureButton>
<DisclosurePanel
onBlur={closeOnBlur}
ref={themesMenuRef}
onKeyDown={handlePanelKeyDown}
style={{
...themesMenuPosition,
}}
className={`${MENU_CLASSNAME} fixed sn-dropdown--animated`}
>
<div className="px-3 my-1 font-semibold color-text uppercase">Themes</div>
<button
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
onClick={toggleDefaultTheme}
onBlur={closeOnBlur}
ref={defaultThemeButtonRef}
>
<div
className={`pseudo-radio-btn ${
defaultThemeOn ? 'pseudo-radio-btn--checked' : ''
} mr-2`}
></div>
Default
</button>
{themes.map((theme) => (
<ThemesMenuButton
item={theme}
application={application}
key={theme.component?.uuid ?? theme.identifier}
onBlur={closeOnBlur}
/>
))}
</DisclosurePanel>
</Disclosure>
{toggleableComponents.map((component) => (
<button
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
toggleComponent(component)
}}
>
<div className="flex items-center">
<Icon type="window" className="color-neutral mr-2" />
{component.name}
</div>
<Switch checked={component.active} className="px-0" />
</button>
))}
<FocusModeSwitch
application={application}
onToggle={setFocusModeEnabled}
onClose={closeQuickSettingsMenu}
isEnabled={focusModeEnabled}
/>
<div className="h-1px my-2 bg-border"></div>
<button
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
onClick={openPreferences}
ref={prefsButtonRef}
>
<Icon type="more" className="color-neutral mr-2" />
Open Preferences
</button>
</div>
</div>
)
},
)