mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Start adding a search modal thingy
This commit is contained in:
parent
7f6c0e9e0a
commit
3b132490c9
3 changed files with 163 additions and 35 deletions
|
|
@ -0,0 +1,62 @@
|
|||
import { ReactNode, useMemo, useRef, useState } from 'react'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { Button, IconButton } from '../../elements/Button'
|
||||
import {
|
||||
CaretRight,
|
||||
Circle,
|
||||
DotsThree,
|
||||
MagnifyingGlass,
|
||||
Plus,
|
||||
X,
|
||||
} from 'phosphor-react'
|
||||
import { useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery'
|
||||
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import {
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalRoot,
|
||||
} from '../../elements/ModalPrimitives'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { SearchBox } from '../../templates/homeFeed/LibraryHeader'
|
||||
|
||||
type SearchModalProps = {
|
||||
searchTerm: string | undefined
|
||||
applySearchQuery: (searchTerm: string) => void
|
||||
|
||||
onOpenChange: (open: boolean) => void
|
||||
}
|
||||
|
||||
export function SearchModal(props: SearchModalProps): JSX.Element {
|
||||
return (
|
||||
<ModalRoot defaultOpen onOpenChange={props.onOpenChange} css={{}}>
|
||||
<ModalOverlay onClick={(e) => e.preventDefault()} />
|
||||
<ModalContent
|
||||
css={{
|
||||
bg: '$grayBg',
|
||||
width: '100%',
|
||||
p: '10px',
|
||||
height: '120px',
|
||||
top: '70px',
|
||||
}}
|
||||
onInteractOutside={() => {
|
||||
// remove focus from modal
|
||||
;(document.activeElement as HTMLElement).blur()
|
||||
}}
|
||||
>
|
||||
<VStack distribution="start" css={{ p: '0px' }}>
|
||||
<Box css={{ width: '100%' }}>
|
||||
<SearchBox
|
||||
{...props}
|
||||
compact={true}
|
||||
onClose={() => props.onOpenChange(false)}
|
||||
/>
|
||||
</Box>
|
||||
</VStack>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
)
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ import { uploadFileRequestMutation } from '../../../lib/networking/mutations/upl
|
|||
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
import { LibraryFilterMenu } from './LibraryFilterMenu'
|
||||
import { SearchModal } from '../../patterns/LibraryCards/SearchModal'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
|
||||
|
|
@ -733,6 +734,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
}
|
||||
|
||||
const [showFilterMenu, setShowFilterMenu] = useState(false)
|
||||
const [showSearchModal, setShowSearchModal] = useState(false)
|
||||
|
||||
return (
|
||||
<VStack css={{ width: '100%', height: '100%' }}>
|
||||
|
|
@ -746,6 +748,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
}}
|
||||
showFilterMenu={showFilterMenu}
|
||||
setShowFilterMenu={setShowFilterMenu}
|
||||
setShowSearchModal={setShowSearchModal}
|
||||
/>
|
||||
<HStack css={{ width: '100%', height: '100%' }}>
|
||||
<LibraryFilterMenu
|
||||
|
|
@ -1062,6 +1065,13 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
{showSearchModal && (
|
||||
<SearchModal
|
||||
searchTerm={props.searchTerm}
|
||||
applySearchQuery={props.applySearchQuery}
|
||||
onOpenChange={(open) => setShowSearchModal(open)}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { LayoutType } from './HomeFeedContainer'
|
|||
import { PrimaryDropdown } from '../PrimaryDropdown'
|
||||
import { LogoBox } from '../../elements/LogoBox'
|
||||
import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo'
|
||||
import { SearchModal } from '../../patterns/LibraryCards/SearchModal'
|
||||
|
||||
type LibraryHeaderProps = {
|
||||
layout: LayoutType
|
||||
|
|
@ -22,9 +23,9 @@ type LibraryHeaderProps = {
|
|||
|
||||
showFilterMenu: boolean
|
||||
setShowFilterMenu: (show: boolean) => void
|
||||
}
|
||||
|
||||
const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)'
|
||||
setShowSearchModal: (show: boolean) => void
|
||||
}
|
||||
|
||||
const HEADER_HEIGHT = '105px'
|
||||
const MOBILE_HEIGHT = '48px'
|
||||
|
|
@ -89,6 +90,7 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
|||
<ControlButtonBox
|
||||
layout={props.layout}
|
||||
updateLayout={props.updateLayout}
|
||||
setShowSearchModal={props.setShowSearchModal}
|
||||
/>
|
||||
</HStack>
|
||||
)
|
||||
|
|
@ -111,6 +113,7 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element {
|
|||
<ControlButtonBox
|
||||
layout={props.layout}
|
||||
updateLayout={props.updateLayout}
|
||||
setShowSearchModal={props.setShowSearchModal}
|
||||
/>
|
||||
</HStack>
|
||||
)
|
||||
|
|
@ -147,12 +150,15 @@ export function MenuHeaderButton(props: MenuHeaderButtonProps): JSX.Element {
|
|||
)
|
||||
}
|
||||
|
||||
type SearchBoxProps = {
|
||||
export type SearchBoxProps = {
|
||||
searchTerm: string | undefined
|
||||
applySearchQuery: (searchQuery: string) => void
|
||||
|
||||
compact?: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
export function SearchBox(props: SearchBoxProps): JSX.Element {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null)
|
||||
const [focused, setFocused] = useState(false)
|
||||
const [searchTerm, setSearchTerm] = useState(props.searchTerm ?? '')
|
||||
|
|
@ -174,7 +180,7 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
mr: '15px',
|
||||
bg: '#F3F3F3',
|
||||
borderRadius: '6px',
|
||||
boxShadow: focused ? FOCUSED_BOXSHADOW : 'unset',
|
||||
border: focused ? '1px solid $omnivoreCtaYellow' : '1px solid #D9D9D9',
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
|
|
@ -185,14 +191,18 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
<HStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
css={{ height: '100%', px: '15px' }}
|
||||
css={{
|
||||
height: '100%',
|
||||
pl: props.compact ? '10px' : '15px',
|
||||
pr: props.compact ? '5px' : '10px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
inputRef.current?.focus()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<MagnifyingGlass
|
||||
size={20}
|
||||
size={props.compact ? 15 : 20}
|
||||
color={theme.colors.graySolid.toString()}
|
||||
/>
|
||||
</HStack>
|
||||
|
|
@ -201,6 +211,9 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
event.preventDefault()
|
||||
props.applySearchQuery(searchTerm || '')
|
||||
inputRef.current?.blur()
|
||||
if (props.onClose) {
|
||||
props.onClose()
|
||||
}
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
|
|
@ -221,7 +234,7 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
}}
|
||||
/>
|
||||
</form>
|
||||
{searchTerm && searchTerm.length > 0 ? (
|
||||
{searchTerm && searchTerm.length && props.compact && (
|
||||
<Button
|
||||
style="plainIcon"
|
||||
onClick={(event) => {
|
||||
|
|
@ -233,17 +246,74 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
css={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
mr: '8px',
|
||||
height: '100%',
|
||||
p: '4px',
|
||||
mx: '4px',
|
||||
fontSize: '12px',
|
||||
alignItems: 'center',
|
||||
// bg: '#E1E1E1',
|
||||
}}
|
||||
>
|
||||
<X
|
||||
width={16}
|
||||
height={16}
|
||||
color={theme.colors.grayTextContrast.toString()}
|
||||
/>
|
||||
clear
|
||||
</Button>
|
||||
)}
|
||||
{focused || props.compact ? (
|
||||
// <Button
|
||||
// style="plainIcon"
|
||||
// onClick={(event) => {
|
||||
// event.preventDefault()
|
||||
// setSearchTerm('')
|
||||
// props.applySearchQuery('')
|
||||
// inputRef.current?.blur()
|
||||
// }}
|
||||
// css={{
|
||||
// display: 'flex',
|
||||
// flexDirection: 'row',
|
||||
// mr: '8px',
|
||||
// height: '100%',
|
||||
// alignItems: 'center',
|
||||
// minWidth: '28px',
|
||||
// }}
|
||||
// >
|
||||
// <X
|
||||
// width={16}
|
||||
// height={16}
|
||||
// color={theme.colors.grayTextContrast.toString()}
|
||||
// />
|
||||
// </Button>
|
||||
<Box
|
||||
css={{
|
||||
py: '15px',
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
css={{
|
||||
p: '0px',
|
||||
mr: '5px',
|
||||
width: '28px',
|
||||
height: '28px',
|
||||
color: '#898989',
|
||||
}}
|
||||
onClick={(event) => {
|
||||
// In compact mode this closes the search, in fullscreen mode it clears the search
|
||||
if (props.compact) {
|
||||
props.onClose && props.onClose()
|
||||
} else {
|
||||
event.preventDefault()
|
||||
setSearchTerm('')
|
||||
props.applySearchQuery('')
|
||||
inputRef.current?.blur()
|
||||
}
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<X
|
||||
width={16}
|
||||
height={16}
|
||||
color={theme.colors.grayTextContrast.toString()}
|
||||
/>
|
||||
</IconButton>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
css={{
|
||||
|
|
@ -258,9 +328,9 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
height: '28px',
|
||||
color: '#898989',
|
||||
}}
|
||||
onClick={() =>
|
||||
requestAnimationFrame(() => inputRef?.current?.focus())
|
||||
}
|
||||
// onClick={() =>
|
||||
// requestAnimationFrame(() => inputRef?.current?.focus())
|
||||
// }
|
||||
// we can make it unreachable via keyboard as we have the same message for the SR label
|
||||
tabIndex={-1}
|
||||
>
|
||||
|
|
@ -276,6 +346,7 @@ function SearchBox(props: SearchBoxProps): JSX.Element {
|
|||
type ControlButtonBoxProps = {
|
||||
layout: LayoutType
|
||||
updateLayout: (layout: LayoutType) => void
|
||||
setShowSearchModal: (show: boolean) => void
|
||||
}
|
||||
|
||||
function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
|
|
@ -340,26 +411,11 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
|||
},
|
||||
}}
|
||||
>
|
||||
{showInlineSearch && (
|
||||
<SpanBox
|
||||
css={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
bg: 'red',
|
||||
top: '0px',
|
||||
left: '0px',
|
||||
p: '10px',
|
||||
}}
|
||||
onClick={() => setShowInlineSearch(false)}
|
||||
>
|
||||
{/* <SearchBox {...props} /> */}
|
||||
</SpanBox>
|
||||
)}
|
||||
<Button
|
||||
style="ghost"
|
||||
onClick={() => {
|
||||
setShowInlineSearch(true)
|
||||
console.log('showing search modal')
|
||||
props.setShowSearchModal(true)
|
||||
}}
|
||||
>
|
||||
<MagnifyingGlass
|
||||
|
|
|
|||
Loading…
Reference in a new issue