mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #807 from omnivore-app/OMN-715
[OMN-715]: Add a command pallet to the web
This commit is contained in:
commit
ecde719b64
13 changed files with 636 additions and 198 deletions
85
packages/web/components/elements/KBar.tsx
Normal file
85
packages/web/components/elements/KBar.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { KBarResults, useMatches } from 'kbar'
|
||||
import { theme } from '../tokens/stitches.config'
|
||||
import { Box, SpanBox } from './LayoutPrimitives'
|
||||
|
||||
export const searchStyle = {
|
||||
padding: '14px 16px',
|
||||
fontSize: '16px',
|
||||
width: '100%',
|
||||
outline: 'none',
|
||||
border: 'none',
|
||||
boxSizing: 'border-box' as React.CSSProperties['boxSizing'],
|
||||
backgroundColor: theme.colors.grayBase.toString(),
|
||||
color: theme.colors.grayTextContrast.toString(),
|
||||
}
|
||||
|
||||
export const animatorStyle = {
|
||||
maxWidth: '600px',
|
||||
width: '100%',
|
||||
backgroundColor: theme.colors.grayBase.toString(),
|
||||
color: theme.colors.grayTextContrast.toString(),
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0px 6px 20px rgba(0, 0, 0, 0.2)',
|
||||
}
|
||||
|
||||
const groupNameStyle = {
|
||||
padding: '8px 16px',
|
||||
fontSize: '10px',
|
||||
textTransform: 'uppercase' as const,
|
||||
opacity: 0.5,
|
||||
}
|
||||
|
||||
export const KBarResultsComponents = () => {
|
||||
const { results } = useMatches()
|
||||
|
||||
return (
|
||||
<KBarResults
|
||||
items={results}
|
||||
onRender={({ item, active }) => {
|
||||
return typeof item === 'string' ? (
|
||||
<Box style={groupNameStyle}>{item.toLocaleUpperCase()}</Box>
|
||||
) : (
|
||||
<Box
|
||||
style={{
|
||||
background: active
|
||||
? theme.colors.grayBgActive.toString()
|
||||
: theme.colors.grayBase.toString(),
|
||||
color: theme.colors.grayTextContrast.toString(),
|
||||
padding: '0.8rem 1.5rem',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
cursor: 'pointer',
|
||||
height: '100',
|
||||
borderLeft: `2px solid ${
|
||||
active
|
||||
? theme.colors.grayTextContrast.toString()
|
||||
: 'transparent'
|
||||
}`,
|
||||
display: 'flex',
|
||||
gap: '0',
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
<Box>{item.name}</Box>
|
||||
<Box style={{ display: 'flex', gap: '4px' }}>
|
||||
{item.shortcut?.map((st, idx) => (
|
||||
<SpanBox
|
||||
key={idx}
|
||||
style={{
|
||||
padding: '4px 6px',
|
||||
background: 'rgba(0, 0, 0, 0.1)',
|
||||
borderRadius: '4px',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
{st}
|
||||
</SpanBox>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import { highlightBarKeyboardCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { isAndroid } from '../../lib/deviceType'
|
||||
|
||||
import { styled, theme } from '../tokens/stitches.config'
|
||||
|
|
@ -10,7 +8,7 @@ import { Button } from '../elements/Button'
|
|||
import { HStack, Box } from '../elements/LayoutPrimitives'
|
||||
import { TrashIcon } from '../elements/images/TrashIcon'
|
||||
import { PenWithColorIcon } from '../elements/images/PenWithColorIcon'
|
||||
import { Note } from 'phosphor-react'
|
||||
import { Note, Trash, TrashSimple } from 'phosphor-react'
|
||||
|
||||
type PageCoordinates = {
|
||||
pageX: number
|
||||
|
|
@ -82,21 +80,6 @@ export function HighlightBar(props: HighlightBarProps): JSX.Element {
|
|||
}
|
||||
|
||||
function BarContent(props: HighlightBarProps): JSX.Element {
|
||||
useKeyboardShortcuts(
|
||||
highlightBarKeyboardCommands((action) => {
|
||||
switch (action) {
|
||||
case 'createHighlight':
|
||||
props.handleButtonClick('create')
|
||||
break
|
||||
case 'openNoteModal':
|
||||
props.handleButtonClick('comment')
|
||||
break
|
||||
case 'openPostModal':
|
||||
break
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const Separator = styled('div', {
|
||||
width: '1px',
|
||||
maxWidth: '1px',
|
||||
|
|
@ -148,12 +131,26 @@ function BarContent(props: HighlightBarProps): JSX.Element {
|
|||
style="plainIcon"
|
||||
title="Remove Highlight"
|
||||
onClick={() => props.handleButtonClick('delete')}
|
||||
css={{ color: '$readerFont', height: '100%', m: 0, p: 0, pt: '6px'}}
|
||||
css={{ color: '$readerFont', height: '100%', m: 0, p: 0 }}
|
||||
>
|
||||
<TrashIcon
|
||||
size={28}
|
||||
strokeColor={theme.colors.readerFont.toString()}
|
||||
/>
|
||||
<HStack css={{ height: '100%', alignItems: 'center' }}>
|
||||
<Trash
|
||||
size={24}
|
||||
color={theme.colors.omnivoreRed.toString()}
|
||||
/>
|
||||
<StyledText
|
||||
style="body"
|
||||
css={{
|
||||
pl: '12px',
|
||||
m: '0px',
|
||||
color: '$readerFont',
|
||||
fontWeight: '400',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</StyledText>
|
||||
</HStack>
|
||||
</Button>
|
||||
)}
|
||||
<Separator />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Box, HStack } from '../elements/LayoutPrimitives'
|
||||
import { OmnivoreNameLogo } from './../elements/images/OmnivoreNameLogo'
|
||||
import { DropdownMenu, HeaderDropdownAction } from './../patterns/DropdownMenu'
|
||||
import { darkenTheme, lightenTheme, updateTheme } from '../../lib/themeUpdater'
|
||||
import { updateTheme } from '../../lib/themeUpdater'
|
||||
import { AvatarDropdown } from './../elements/AvatarDropdown'
|
||||
import { ThemeId } from './../tokens/stitches.config'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
|
@ -30,12 +30,12 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
|
|||
useKeyboardShortcuts(
|
||||
primaryCommands((action) => {
|
||||
switch (action) {
|
||||
case 'themeDarker':
|
||||
darkenTheme()
|
||||
break
|
||||
case 'themeLighter':
|
||||
lightenTheme()
|
||||
break
|
||||
// case 'themeDarker':
|
||||
// darkenTheme()
|
||||
// break
|
||||
// case 'themeLighter':
|
||||
// lightenTheme()
|
||||
// break
|
||||
case 'toggleShortcutHelpModalDisplay':
|
||||
props.setShowKeyboardCommandsModal(true)
|
||||
break
|
||||
|
|
@ -43,27 +43,6 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
|
|||
})
|
||||
)
|
||||
|
||||
/*
|
||||
useRegisterActions([
|
||||
{
|
||||
id: 'lightTheme',
|
||||
section: 'Preferences',
|
||||
name: 'Change theme (lighter) ',
|
||||
shortcut: ['v', 'l'],
|
||||
keywords: 'light theme',
|
||||
perform: () => lightenTheme(),
|
||||
},
|
||||
{
|
||||
id: 'darkTheme',
|
||||
section: 'Preferences',
|
||||
name: 'Change theme (darker) ',
|
||||
shortcut: ['v', 'd'],
|
||||
keywords: 'dark theme',
|
||||
perform: () => darkenTheme(),
|
||||
},
|
||||
])
|
||||
*/
|
||||
|
||||
const initAnalytics = useCallback(() => {
|
||||
setupAnalytics(props.user)
|
||||
}, [props.user])
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
|
|||
)}
|
||||
|
||||
{/* <MenuSeparator layout={props.layout} />
|
||||
|
||||
<Button style='articleActionIcon'>
|
||||
<DotsThree size={24} color={theme.colors.readerFont.toString()} />
|
||||
</Button> */}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
|
||||
import type {
|
||||
LibraryItem,
|
||||
LibraryItemsData,
|
||||
LibraryItemsQueryInput,
|
||||
} from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
|
|
@ -41,8 +42,11 @@ import {
|
|||
State,
|
||||
PageType,
|
||||
} from '../../../lib/networking/fragments/articleFragment'
|
||||
import { useRegisterActions, createAction, useKBar, Action } from 'kbar'
|
||||
import { EditTitleModal } from './EditTitleModal'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import { searchQuery } from '../../../lib/networking/queries/search'
|
||||
import debounce from 'lodash/debounce'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
|
||||
|
|
@ -60,11 +64,24 @@ const SAVED_SEARCHES: Record<string, string> = {
|
|||
Newsletters: `in:inbox label:Newsletter`,
|
||||
}
|
||||
|
||||
const fetchSearchResults = async (query: string, cb: any) => {
|
||||
if (!query.startsWith('#')) return
|
||||
const res = await searchQuery({ limit: 10, searchQuery: query.substring(1)})
|
||||
cb(res);
|
||||
};
|
||||
|
||||
const debouncedFetchSearchResults = debounce((query, cb) => {
|
||||
fetchSearchResults(query, cb);
|
||||
}, 300);
|
||||
|
||||
export function HomeFeedContainer(): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
const router = useRouter()
|
||||
const { queryValue } = useKBar((state) => ({queryValue: state.searchQuery}));
|
||||
const [searchResults, setSearchResults] = useState<LibraryItem[]>([]);
|
||||
|
||||
const defaultQuery = {
|
||||
limit: 10,
|
||||
sortDescending: true,
|
||||
|
|
@ -102,6 +119,16 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
const { itemsPages, size, setSize, isValidating, performActionOnItem } =
|
||||
useGetLibraryItemsQuery(queryInputs)
|
||||
|
||||
useEffect(() => {
|
||||
if (queryValue.startsWith('#')) {
|
||||
debouncedFetchSearchResults(queryValue, (data: LibraryItemsData) => {
|
||||
setSearchResults(data?.search.edges || [])
|
||||
})
|
||||
}
|
||||
else setSearchResults([])
|
||||
|
||||
}, [queryValue])
|
||||
|
||||
useEffect(() => {
|
||||
if (!router.isReady) return
|
||||
const q = router.query['q']
|
||||
|
|
@ -421,6 +448,72 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
})
|
||||
)
|
||||
|
||||
const ARCHIVE_ACTION = !activeItem?.node.isArchived ?
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Archive selected item',
|
||||
shortcut: ['e'],
|
||||
perform: () => handleCardAction('archive', activeItem),
|
||||
}) :
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'UnArchive selected item',
|
||||
shortcut: ['e'],
|
||||
perform: () => handleCardAction('unarchive', activeItem),
|
||||
})
|
||||
|
||||
const ACTIVE_ACTIONS = [
|
||||
ARCHIVE_ACTION,
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Remove item',
|
||||
shortcut: ['r'],
|
||||
perform: () => handleCardAction('delete', activeItem),
|
||||
}),
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Edit item labels',
|
||||
shortcut: ['l'],
|
||||
perform: () => handleCardAction('set-labels', activeItem),
|
||||
}),
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Mark item as read',
|
||||
shortcut: ['Shift', 'i'],
|
||||
perform: () => handleCardAction('mark-read', activeItem),
|
||||
}),
|
||||
createAction({
|
||||
section: 'Library',
|
||||
name: 'Mark item as unread',
|
||||
shortcut: ['Shift', 'u'],
|
||||
perform: () => handleCardAction('mark-unread', activeItem),
|
||||
}),
|
||||
]
|
||||
|
||||
const UNACTIVE_ACTIONS: Action[] = [
|
||||
// createAction({
|
||||
// section: 'Library',
|
||||
// name: 'Sort in ascending order',
|
||||
// shortcut: ['s', 'o'],
|
||||
// perform: () => setQueryInputs({ ...queryInputs, sortDescending: false }),
|
||||
// }),
|
||||
// createAction({
|
||||
// section: 'Library',
|
||||
// name: 'Sort in descending order',
|
||||
// shortcut: ['s', 'n'],
|
||||
// perform: () => setQueryInputs({ ...queryInputs, sortDescending: true }),
|
||||
// }),
|
||||
]
|
||||
|
||||
useRegisterActions(searchResults.map(link => ({
|
||||
id: link.node.id,
|
||||
section: 'Search Results',
|
||||
name: link.node.title,
|
||||
keywords: '#' + link.node.title,
|
||||
perform: () => handleCardAction('showDetail', link),
|
||||
})), [searchResults])
|
||||
|
||||
useRegisterActions(activeCardId ? [...ACTIVE_ACTIONS, ...UNACTIVE_ACTIONS] : UNACTIVE_ACTIONS, [activeCardId, activeItem]);
|
||||
useFetchMore(handleFetchMore)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useRegisterActions } from "kbar"
|
||||
import { useCallback, useState } from "react"
|
||||
import { userPersonalizationMutation } from "../networking/mutations/userPersonalizationMutation"
|
||||
import { useGetUserPreferences, UserPreferences } from "../networking/queries/useGetUserPreferences"
|
||||
|
|
@ -36,12 +37,12 @@ export const useReaderSettings = (): ReaderSettings => {
|
|||
const [showSetLabelsModal, setShowSetLabelsModal] = useState(false)
|
||||
const [showEditDisplaySettingsModal, setShowEditDisplaySettingsModal] = useState(false)
|
||||
|
||||
const actionHandler = useCallback(async(action: string, arg?: unknown) => {
|
||||
const updateFontSize = async(newFontSize: number) => {
|
||||
setFontSize(newFontSize)
|
||||
await userPersonalizationMutation({ fontSize: newFontSize })
|
||||
}
|
||||
const updateFontSize = async (newFontSize: number) => {
|
||||
setFontSize(newFontSize)
|
||||
await userPersonalizationMutation({ fontSize: newFontSize })
|
||||
}
|
||||
|
||||
const actionHandler = useCallback(async(action: string, arg?: unknown) => {
|
||||
switch (action) {
|
||||
case 'incrementFontSize':
|
||||
await updateFontSize(Math.min(fontSize + 2, 28))
|
||||
|
|
@ -91,6 +92,45 @@ export const useReaderSettings = (): ReaderSettings => {
|
|||
}
|
||||
}, [fontSize, setFontSize, lineHeight, fontFamily,
|
||||
setLineHeight, marginWidth, setMarginWidth, setFontFamily])
|
||||
|
||||
|
||||
useRegisterActions([
|
||||
{
|
||||
id: 'increaseFont',
|
||||
section: 'Article',
|
||||
name: 'Increase font size',
|
||||
shortcut: ['+'],
|
||||
perform: () => actionHandler('incrementFontSize'),
|
||||
},
|
||||
{
|
||||
id: 'decreaseFont',
|
||||
section: 'Article',
|
||||
name: 'Decrease font size',
|
||||
shortcut: ['-'],
|
||||
perform: () => actionHandler('decrementFontSize'),
|
||||
},
|
||||
{
|
||||
id: 'increaseMargin',
|
||||
section: 'Article',
|
||||
name: 'Increase margin width',
|
||||
shortcut: [']'],
|
||||
perform: () => actionHandler('incrementMarginWidth'),
|
||||
},
|
||||
{
|
||||
id: 'decreaseMargin',
|
||||
section: 'Article',
|
||||
name: 'Decrease margin width',
|
||||
shortcut: ['['],
|
||||
perform: () => actionHandler('decrementMarginWidth'),
|
||||
},
|
||||
{
|
||||
id: 'edit_a',
|
||||
section: 'Article',
|
||||
name: 'Edit labels',
|
||||
shortcut: ['l'],
|
||||
perform: () => setShowSetLabelsModal(true),
|
||||
},
|
||||
], [])
|
||||
|
||||
return {
|
||||
preferencesData,
|
||||
|
|
|
|||
|
|
@ -123,36 +123,36 @@ export function libraryListCommands(
|
|||
shortcutKeyDescription: 'k or left arrow',
|
||||
callback: () => actionHandler('moveFocusToPreviousListItem'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['e'],
|
||||
actionDescription: 'Archive item',
|
||||
shortcutKeyDescription: 'e',
|
||||
callback: () => actionHandler('archiveItem'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['r'],
|
||||
actionDescription: 'Remove item',
|
||||
shortcutKeyDescription: 'r',
|
||||
callback: () => actionHandler('removeItem'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['l'],
|
||||
actionDescription: 'Edit item labels',
|
||||
shortcutKeyDescription: 'l',
|
||||
callback: () => actionHandler('showEditLabelsModal'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['shift', 'i'],
|
||||
actionDescription: 'Mark item as read',
|
||||
shortcutKeyDescription: 'shift + i',
|
||||
callback: () => actionHandler('markItemAsRead'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['shift', 'u'],
|
||||
actionDescription: 'Mark item as unread',
|
||||
shortcutKeyDescription: 'shift + u',
|
||||
callback: () => actionHandler('markItemAsUnread'),
|
||||
},
|
||||
// {
|
||||
// shortcutKeys: ['e'],
|
||||
// actionDescription: 'Archive item',
|
||||
// shortcutKeyDescription: 'e',
|
||||
// callback: () => actionHandler('archiveItem'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['r'],
|
||||
// actionDescription: 'Remove item',
|
||||
// shortcutKeyDescription: 'r',
|
||||
// callback: () => actionHandler('removeItem'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['l'],
|
||||
// actionDescription: 'Edit item labels',
|
||||
// shortcutKeyDescription: 'l',
|
||||
// callback: () => actionHandler('showEditLabelsModal'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['shift', 'i'],
|
||||
// actionDescription: 'Mark item as read',
|
||||
// shortcutKeyDescription: 'shift + i',
|
||||
// callback: () => actionHandler('markItemAsRead'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['shift', 'u'],
|
||||
// actionDescription: 'Mark item as unread',
|
||||
// shortcutKeyDescription: 'shift + u',
|
||||
// callback: () => actionHandler('markItemAsUnread'),
|
||||
// },
|
||||
// Commented out until we re-enable highlight sharing
|
||||
// {
|
||||
// shortcutKeys: ['shift', 's'],
|
||||
|
|
@ -160,18 +160,18 @@ export function libraryListCommands(
|
|||
// shortcutKeyDescription: 'shift + s',
|
||||
// callback: () => actionHandler('shareItem'),
|
||||
// },
|
||||
{
|
||||
shortcutKeys: ['s', 'o'],
|
||||
actionDescription: 'Sort item in ascending order',
|
||||
shortcutKeyDescription: 's then o',
|
||||
callback: () => actionHandler('sortAscending'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['s', 'n'],
|
||||
actionDescription: 'Sort item in descending order',
|
||||
shortcutKeyDescription: 's then n',
|
||||
callback: () => actionHandler('sortDescending'),
|
||||
},
|
||||
// {
|
||||
// shortcutKeys: ['s', 'o'],
|
||||
// actionDescription: 'Sort item in ascending order',
|
||||
// shortcutKeyDescription: 's then o',
|
||||
// callback: () => actionHandler('sortAscending'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['s', 'n'],
|
||||
// actionDescription: 'Sort item in descending order',
|
||||
// shortcutKeyDescription: 's then n',
|
||||
// callback: () => actionHandler('sortDescending'),
|
||||
// },
|
||||
{
|
||||
shortcutKeys: ['arrowdown'],
|
||||
actionDescription: 'Move cursor to the next row',
|
||||
|
|
@ -231,53 +231,53 @@ export function articleKeyboardCommands(
|
|||
actionHandler: (action: ArticleKeyboardAction) => void
|
||||
): KeyboardCommand[] {
|
||||
return [
|
||||
{
|
||||
shortcutKeys: ['o'],
|
||||
actionDescription: 'Open original article page',
|
||||
shortcutKeyDescription: 'o',
|
||||
callback: () => actionHandler('openOriginalArticle'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['u'],
|
||||
actionDescription: 'Back to library',
|
||||
shortcutKeyDescription: 'u',
|
||||
callback: () => router?.push('/home'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['+'],
|
||||
actionDescription: 'Increase font size',
|
||||
shortcutKeyDescription: '+',
|
||||
callback: () => actionHandler('incrementFontSize'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['-'],
|
||||
actionDescription: 'Decrease font size',
|
||||
shortcutKeyDescription: '-',
|
||||
callback: () => actionHandler('decrementFontSize'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: [']'],
|
||||
actionDescription: 'Increase margin width',
|
||||
shortcutKeyDescription: ']',
|
||||
callback: () => actionHandler('incrementMarginWidth'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['['],
|
||||
actionDescription: 'Decrease margin width',
|
||||
shortcutKeyDescription: '[',
|
||||
callback: () => actionHandler('decrementMarginWidth'),
|
||||
},
|
||||
// {
|
||||
// shortcutKeys: ['o'],
|
||||
// actionDescription: 'Open original article page',
|
||||
// shortcutKeyDescription: 'o',
|
||||
// callback: () => actionHandler('openOriginalArticle'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['u'],
|
||||
// actionDescription: 'Back to library',
|
||||
// shortcutKeyDescription: 'u',
|
||||
// callback: () => router?.push('/home'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['+'],
|
||||
// actionDescription: 'Increase font size',
|
||||
// shortcutKeyDescription: '+',
|
||||
// callback: () => actionHandler('incrementFontSize'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['-'],
|
||||
// actionDescription: 'Decrease font size',
|
||||
// shortcutKeyDescription: '-',
|
||||
// callback: () => actionHandler('decrementFontSize'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: [']'],
|
||||
// actionDescription: 'Increase margin width',
|
||||
// shortcutKeyDescription: ']',
|
||||
// callback: () => actionHandler('incrementMarginWidth'),
|
||||
// },
|
||||
// {
|
||||
// shortcutKeys: ['['],
|
||||
// actionDescription: 'Decrease margin width',
|
||||
// shortcutKeyDescription: '[',
|
||||
// callback: () => actionHandler('decrementMarginWidth'),
|
||||
// },
|
||||
{
|
||||
shortcutKeys: ['d'],
|
||||
actionDescription: 'Edit Display Settings',
|
||||
shortcutKeyDescription: 'd',
|
||||
callback: () => actionHandler('editDisplaySettings'),
|
||||
},
|
||||
{
|
||||
shortcutKeys: ['l'],
|
||||
actionDescription: 'Edit labels',
|
||||
shortcutKeyDescription: 'l',
|
||||
callback: () => actionHandler('setLabels'),
|
||||
},
|
||||
// {
|
||||
// shortcutKeys: ['l'],
|
||||
// actionDescription: 'Edit labels',
|
||||
// shortcutKeyDescription: 'l',
|
||||
// callback: () => actionHandler('setLabels'),
|
||||
// },
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ const disabledTargets = ['INPUT', 'TEXTAREA']
|
|||
]
|
||||
*/
|
||||
|
||||
const KBAR_KEYS = ['control', 'meta', 'v', 'i', 's', 'n', '[', ']']
|
||||
|
||||
type KeyPressed = {
|
||||
[name: string]: boolean
|
||||
}
|
||||
|
|
@ -52,12 +54,17 @@ export const useKeyboardShortcuts = (commands: KeyboardCommand[]): void => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
KBAR_KEYS.map((key) => currentKeys[key.toLowerCase()] = false)
|
||||
return currentKeys
|
||||
}, [commands])
|
||||
|
||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping)
|
||||
|
||||
const metaPressed = useCallback(() => {
|
||||
return keys['meta'] === true
|
||||
}, [keys])
|
||||
|
||||
const applyCommands = useCallback(
|
||||
(updatedKeys: KeyPressed, disabled?: boolean) => {
|
||||
let commandApplied = false
|
||||
|
|
@ -66,6 +73,7 @@ export const useKeyboardShortcuts = (commands: KeyboardCommand[]): void => {
|
|||
return
|
||||
}
|
||||
const tempState = { ...updatedKeys }
|
||||
|
||||
const arePressed = command.shortcutKeys.every((key) => {
|
||||
const aliases = key.split('|')
|
||||
for (const k of aliases) {
|
||||
|
|
@ -104,6 +112,10 @@ export const useKeyboardShortcuts = (commands: KeyboardCommand[]): void => {
|
|||
|
||||
if (keys[key] === undefined) return
|
||||
if (keys[key] === false) {
|
||||
if (key === 'k' && metaPressed()) {
|
||||
// not setting the K value because meta is already pressed
|
||||
return
|
||||
}
|
||||
setKeys({ type: ActionType.SET_KEY_DOWN, key })
|
||||
}
|
||||
const commandApplied = applyCommands(
|
||||
|
|
|
|||
80
packages/web/lib/networking/queries/search.tsx
Normal file
80
packages/web/lib/networking/queries/search.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { LibraryItemsData } from './useGetLibraryItemsQuery'
|
||||
|
||||
export type LibraryItemsQueryInput = {
|
||||
limit?: number
|
||||
searchQuery?: string
|
||||
}
|
||||
|
||||
export async function searchQuery({
|
||||
limit = 10,
|
||||
searchQuery,
|
||||
}: LibraryItemsQueryInput): Promise<LibraryItemsData | undefined> {
|
||||
const query = gql`
|
||||
query Search($after: String, $first: Int, $query: String) {
|
||||
search(first: $first, after: $after, query: $query) {
|
||||
... on SearchSuccess {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
title
|
||||
slug
|
||||
url
|
||||
pageType
|
||||
contentReader
|
||||
createdAt
|
||||
isArchived
|
||||
readingProgressPercent
|
||||
readingProgressAnchorIndex
|
||||
author
|
||||
image
|
||||
description
|
||||
publishedAt
|
||||
ownedByViewer
|
||||
originalArticleUrl
|
||||
uploadFileId
|
||||
labels {
|
||||
id
|
||||
name
|
||||
color
|
||||
}
|
||||
pageId
|
||||
shortId
|
||||
quote
|
||||
annotation
|
||||
state
|
||||
siteName
|
||||
subscription
|
||||
readAt
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
... on SearchError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const variables = {
|
||||
first: limit,
|
||||
query: searchQuery,
|
||||
}
|
||||
|
||||
try {
|
||||
const data = (await gqlFetcher(query, {...variables}))
|
||||
return data as LibraryItemsData || undefined;
|
||||
} catch (error) {
|
||||
console.log('search error', error)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
"cookie": "^0.5.0",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"graphql-request": "^3.6.1",
|
||||
"kbar": "^0.1.0-beta.35",
|
||||
"nanoid": "^3.1.29",
|
||||
"next": "^12.1.0",
|
||||
"phosphor-react": "^1.4.0",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import { deleteHighlightMutation } from '../../../lib/networking/mutations/delet
|
|||
import { mergeHighlightMutation } from '../../../lib/networking/mutations/mergeHighlightMutation'
|
||||
import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation'
|
||||
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
|
||||
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
|
||||
import Script from 'next/script'
|
||||
import { theme } from '../../../components/tokens/stitches.config'
|
||||
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
|
||||
|
|
@ -29,6 +28,7 @@ import { SetLabelsModal } from '../../../components/templates/article/SetLabelsM
|
|||
import { DisplaySettingsModal } from '../../../components/templates/article/DisplaySettingsModal'
|
||||
import { useReaderSettings } from '../../../lib/hooks/useReaderSettings'
|
||||
import { SkeletonArticleContainer } from '../../../components/templates/article/SkeletonArticleContainer'
|
||||
import { useRegisterActions } from 'kbar'
|
||||
|
||||
|
||||
const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
|
||||
|
|
@ -137,6 +137,48 @@ export default function Home(): JSX.Element {
|
|||
})
|
||||
}
|
||||
}, [article, viewerData])
|
||||
|
||||
useRegisterActions([
|
||||
{
|
||||
id: 'open',
|
||||
section: 'Article',
|
||||
name: 'Open original article',
|
||||
shortcut: ['o'],
|
||||
perform: () => actionHandler('openOriginalArticle')
|
||||
},
|
||||
{
|
||||
id: 'back_home',
|
||||
section: 'Article',
|
||||
name: 'Return to library',
|
||||
shortcut: ['u'],
|
||||
perform: () => router.push(`/home`),
|
||||
},
|
||||
{
|
||||
id: 'archive',
|
||||
section: 'Article',
|
||||
name: 'Archive current item',
|
||||
shortcut: ['e'],
|
||||
perform: () => actionHandler('archive'),
|
||||
},
|
||||
{
|
||||
id: 'highlight',
|
||||
section: 'Article',
|
||||
name: 'Highlight selected text',
|
||||
shortcut: ['h'],
|
||||
perform: () => {
|
||||
document.dispatchEvent(new Event('highlight'));
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'note',
|
||||
section: 'Article',
|
||||
name: 'Highlight selected text and add a note',
|
||||
shortcut: ['n'],
|
||||
perform: () => {
|
||||
document.dispatchEvent(new Event('annotate'));
|
||||
},
|
||||
},
|
||||
], [])
|
||||
|
||||
if (articleFetchError && articleFetchError.indexOf('NOT_FOUND') > -1) {
|
||||
router.push('/404')
|
||||
|
|
@ -190,55 +232,55 @@ export default function Home(): JSX.Element {
|
|||
/>
|
||||
) : null}
|
||||
</VStack>
|
||||
{article && viewerData?.me && article.contentReader == 'PDF' ? (
|
||||
<PdfArticleContainerNoSSR
|
||||
article={article}
|
||||
showHighlightsModal={showHighlightsModal}
|
||||
setShowHighlightsModal={setShowHighlightsModal}
|
||||
viewerUsername={viewerData.me?.profile?.username}
|
||||
/>
|
||||
) : (
|
||||
<VStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
ref={scrollRef}
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
'@smDown': {
|
||||
background: theme.colors.grayBg.toString(),
|
||||
}
|
||||
}}
|
||||
>
|
||||
{article && viewerData?.me ? (
|
||||
<ArticleContainer
|
||||
article={article}
|
||||
isAppleAppEmbed={false}
|
||||
highlightBarDisabled={false}
|
||||
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
|
||||
fontSize={readerSettings.fontSize}
|
||||
margin={readerSettings.marginWidth}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
fontFamily={readerSettings.fontFamily}
|
||||
labels={labels}
|
||||
showHighlightsModal={showHighlightsModal}
|
||||
setShowHighlightsModal={setShowHighlightsModal}
|
||||
articleMutations={{
|
||||
createHighlightMutation,
|
||||
deleteHighlightMutation,
|
||||
mergeHighlightMutation,
|
||||
updateHighlightMutation,
|
||||
articleReadingProgressMutation,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<SkeletonArticleContainer
|
||||
margin={readerSettings.marginWidth}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
fontSize={readerSettings.fontSize}
|
||||
/>
|
||||
)}
|
||||
</VStack>
|
||||
{article && viewerData?.me && article.contentReader == 'PDF' ? (
|
||||
<PdfArticleContainerNoSSR
|
||||
article={article}
|
||||
showHighlightsModal={showHighlightsModal}
|
||||
setShowHighlightsModal={setShowHighlightsModal}
|
||||
viewerUsername={viewerData.me?.profile?.username}
|
||||
/>
|
||||
) : (
|
||||
<VStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
ref={scrollRef}
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
'@smDown': {
|
||||
background: theme.colors.grayBg.toString(),
|
||||
}
|
||||
}}
|
||||
>
|
||||
{article && viewerData?.me ? (
|
||||
<ArticleContainer
|
||||
article={article}
|
||||
isAppleAppEmbed={false}
|
||||
highlightBarDisabled={false}
|
||||
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
|
||||
fontSize={readerSettings.fontSize}
|
||||
margin={readerSettings.marginWidth}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
fontFamily={readerSettings.fontFamily}
|
||||
labels={labels}
|
||||
showHighlightsModal={showHighlightsModal}
|
||||
setShowHighlightsModal={setShowHighlightsModal}
|
||||
articleMutations={{
|
||||
createHighlightMutation,
|
||||
deleteHighlightMutation,
|
||||
mergeHighlightMutation,
|
||||
updateHighlightMutation,
|
||||
articleReadingProgressMutation,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<SkeletonArticleContainer
|
||||
margin={readerSettings.marginWidth}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
fontSize={readerSettings.fontSize}
|
||||
/>
|
||||
)}
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
{article && readerSettings.showSetLabelsModal && (
|
||||
<SetLabelsModal
|
||||
|
|
|
|||
|
|
@ -2,12 +2,21 @@ import '../styles/globals.css'
|
|||
import '../styles/articleInnerStyling.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import { IdProvider } from '@radix-ui/react-id'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextRouter, useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Analytics, AnalyticsBrowser } from '@segment/analytics-next'
|
||||
import { segmentApiKey } from '../lib/appConfig'
|
||||
import { TooltipProvider } from '../components/elements/Tooltip'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import {
|
||||
KBarProvider,
|
||||
KBarPortal,
|
||||
KBarPositioner,
|
||||
KBarAnimator,
|
||||
KBarSearch,
|
||||
} from 'kbar'
|
||||
import { animatorStyle, KBarResultsComponents, searchStyle } from '../components/elements/KBar'
|
||||
import { darkenTheme, lightenTheme } from '../lib/themeUpdater'
|
||||
|
||||
TopBarProgress.config({
|
||||
barColors: {
|
||||
|
|
@ -16,7 +25,38 @@ TopBarProgress.config({
|
|||
},
|
||||
shadowBlur: 0,
|
||||
barThickness: 2,
|
||||
});
|
||||
})
|
||||
|
||||
const generateActions = (router: NextRouter) => {
|
||||
const defaultActions = [
|
||||
{
|
||||
id: 'home',
|
||||
section: 'Navigation',
|
||||
name: 'Go to Home (Library) ',
|
||||
shortcut: ['g', 'h'],
|
||||
keywords: 'go home',
|
||||
perform: () => router?.push('/home'),
|
||||
},
|
||||
{
|
||||
id: 'lightTheme',
|
||||
section: 'Preferences',
|
||||
name: 'Change theme (light) ',
|
||||
shortcut: ['v', 'l'],
|
||||
keywords: 'light theme',
|
||||
perform: () => lightenTheme(),
|
||||
},
|
||||
{
|
||||
id: 'darkTheme',
|
||||
section: 'Preferences',
|
||||
name: 'Change theme (dark) ',
|
||||
shortcut: ['v', 'd'],
|
||||
keywords: 'dark theme',
|
||||
perform: () => darkenTheme(),
|
||||
},
|
||||
]
|
||||
|
||||
return defaultActions
|
||||
}
|
||||
|
||||
function OmnivoreApp({ Component, pageProps }: AppProps): JSX.Element {
|
||||
const router = useRouter()
|
||||
|
|
@ -51,11 +91,21 @@ function OmnivoreApp({ Component, pageProps }: AppProps): JSX.Element {
|
|||
}, [router.events, analytics])
|
||||
|
||||
return (
|
||||
<IdProvider>
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<Component {...pageProps} />
|
||||
</TooltipProvider>
|
||||
</IdProvider>
|
||||
<KBarProvider actions={generateActions(router)}>
|
||||
<KBarPortal>
|
||||
<KBarPositioner>
|
||||
<KBarAnimator style={animatorStyle}>
|
||||
<KBarSearch style={searchStyle} />
|
||||
<KBarResultsComponents />
|
||||
</KBarAnimator>
|
||||
</KBarPositioner>
|
||||
</KBarPortal>
|
||||
<IdProvider>
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<Component {...pageProps} />
|
||||
</TooltipProvider>
|
||||
</IdProvider>
|
||||
</KBarProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
60
yarn.lock
60
yarn.lock
|
|
@ -5039,6 +5039,28 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@reach/observe-rect@^1.1.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
|
||||
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==
|
||||
|
||||
"@reach/portal@^0.16.0":
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.16.2.tgz#ca83696215ee03acc2bb25a5ae5d8793eaaf2f64"
|
||||
integrity sha512-9ur/yxNkuVYTIjAcfi46LdKUvH0uYZPfEp4usWcpt6PIp+WDF57F/5deMe/uGi/B/nfDweQu8VVwuMVrCb97JQ==
|
||||
dependencies:
|
||||
"@reach/utils" "0.16.0"
|
||||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@reach/utils@0.16.0":
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.16.0.tgz#5b0777cf16a7cab1ddd4728d5d02762df0ba84ce"
|
||||
integrity sha512-PCggBet3qaQmwFNcmQ/GqHSefadAFyNCUekq9RrWoaU9hh/S4iaFgf2MBMdM47eQj5i/Bk0Mm07cP/XPFlkN+Q==
|
||||
dependencies:
|
||||
tiny-warning "^1.0.3"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@rushstack/eslint-patch@^1.0.8":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323"
|
||||
|
|
@ -10961,6 +10983,11 @@ comma-separated-tokens@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
|
||||
integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==
|
||||
|
||||
command-score@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/command-score/-/command-score-0.1.2.tgz#b986ad7e8c0beba17552a56636c44ae38363d381"
|
||||
integrity sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==
|
||||
|
||||
commander@^2.19.0, commander@^2.20.0, commander@^2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
|
|
@ -13376,6 +13403,11 @@ fast-diff@^1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||
|
||||
fast-equals@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.4.tgz#3add9410585e2d7364c2deeb6a707beadb24b927"
|
||||
integrity sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==
|
||||
|
||||
fast-glob@^2.2.6:
|
||||
version "2.2.7"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
|
||||
|
|
@ -17149,6 +17181,17 @@ jws@^4.0.0:
|
|||
jwa "^2.0.0"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kbar@^0.1.0-beta.35:
|
||||
version "0.1.0-beta.35"
|
||||
resolved "https://registry.yarnpkg.com/kbar/-/kbar-0.1.0-beta.35.tgz#48f6f021fa65793cdf9f5e8876db666f160eee47"
|
||||
integrity sha512-lXA/AFfmeTBIC0+6wsqHPpU1ULI4MMgzX9cRKkpHh9BVij5t1EAJOFTLzQQBr/4jVzSYNwRogRHHU4oQAlAduQ==
|
||||
dependencies:
|
||||
"@reach/portal" "^0.16.0"
|
||||
command-score "^0.1.2"
|
||||
fast-equals "^2.0.3"
|
||||
react-virtual "^2.8.2"
|
||||
tiny-invariant "^1.2.0"
|
||||
|
||||
keyv@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
|
||||
|
|
@ -21309,6 +21352,13 @@ react-twitter-widgets@^1.10.0:
|
|||
dependencies:
|
||||
loadjs "^4.2.0"
|
||||
|
||||
react-virtual@^2.8.2:
|
||||
version "2.10.4"
|
||||
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.10.4.tgz#08712f0acd79d7d6f7c4726f05651a13b24d8704"
|
||||
integrity sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==
|
||||
dependencies:
|
||||
"@reach/observe-rect" "^1.1.0"
|
||||
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
|
|
@ -23604,6 +23654,16 @@ tiny-hashes@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/tiny-hashes/-/tiny-hashes-1.0.1.tgz#ddbe9060312ddb4efe0a174bb3a27e1331c425a1"
|
||||
integrity sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==
|
||||
|
||||
tiny-invariant@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
|
||||
integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
|
||||
|
||||
tiny-warning@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
title-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
|
||||
|
|
|
|||
Loading…
Reference in a new issue