Skip to content

Commit

Permalink
feat(search): implement searching feature (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
async3619 authored Oct 27, 2024
1 parent 6d05ead commit 8e3e5cd
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 1 deletion.
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default tseslint.config(
},
rules: {
...reactHooks.configs.recommended.rules,
'no-empty-pattern': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
Expand Down
1 change: 1 addition & 0 deletions src/App.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const Content = styled.div<{ isReady: boolean }>`
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useSessionAppStore from '@stores/session-app'
import useAppStore from '@stores/app'

import * as Styled from './App.styled'
import SearchBar from '@components/SearchBar'

function App() {
const isReady = useSessionAppStore((store) => store.isReady)
Expand Down Expand Up @@ -51,6 +52,7 @@ function App() {
<Styled.Content isReady={isReady}>
<Clock />
<Settings />
<SearchBar />
</Styled.Content>
</Styled.Root>
</ThemeProvider>
Expand Down
7 changes: 7 additions & 0 deletions src/components/Box.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from '@emotion/styled'

export const Root = styled.div`
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.3);
`
11 changes: 11 additions & 0 deletions src/components/Box.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'

import * as Styled from './Box.styled'

interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {}

function Box(props: BoxProps) {
return <Styled.Root {...props} />
}

export default Box
25 changes: 25 additions & 0 deletions src/components/IconTab.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from '@emotion/styled'

export const Root = styled.div`
display: flex;
`

export const Item = styled.button<{ selected: boolean }>`
margin: 0;
padding: ${({ theme }) => theme.spacing(0.75)};
border: 0;
background-color: rgba(255, 255, 255, 0.3);
cursor: pointer;
opacity: ${({ selected }) => (selected ? 1 : 0.5)};
> svg {
width: 20px;
height: 20px;
display: block;
fill: white;
}
`
36 changes: 36 additions & 0 deletions src/components/IconTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'

import * as Styled from './IconTab.styled'

export interface IconTabItem<TValue extends string> {
value: TValue
icon: React.ComponentType
}

interface IconTabProps<TValue extends string> {
value: TValue
onChange: (value: TValue) => void
items: IconTabItem<TValue>[]
}

function IconTab<TValue extends string>({
value: currentValue,
items,
onChange,
}: IconTabProps<TValue>) {
return (
<Styled.Root>
{items.map(({ icon: Icon, value }) => (
<Styled.Item
key={value}
onClick={() => onChange(value)}
selected={value === currentValue}
>
<Icon />
</Styled.Item>
))}
</Styled.Root>
)
}

export default IconTab
48 changes: 48 additions & 0 deletions src/components/SearchBar.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import styled from '@emotion/styled'

import Box from '@components/Box'

export const Root = styled(Box)`
width: 100%;
max-width: 764px;
margin-top: ${({ theme }) => theme.spacing(6)};
border-top-left-radius: 0;
position: relative;
`

export const Input = styled.input`
width: 100%;
margin: 0;
padding: ${({ theme }) => theme.spacing(1.5, 2)};
border: 0;
display: block;
box-sizing: border-box;
font-family: 'SUITE Variable', sans-serif;
color: white;
background-color: transparent;
outline: none;
::placeholder {
color: rgba(255, 255, 255, 0.5);
}
`

export const IconTabWrapper = styled.div`
border-top-left-radius: 4px;
border-top-right-radius: 4px;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
transform: translateY(-100%);
`
64 changes: 64 additions & 0 deletions src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react'
import { RiOpenaiFill, RiGoogleFill } from 'react-icons/ri'

import IconTab, { IconTabItem } from '@components/IconTab'

import * as Styled from './SearchBar.styled'

type SearchEngine = 'google' | 'chatgpt'

const SEARCH_ENGiNE_ITEMS: IconTabItem<SearchEngine>[] = [
{
value: 'google',
icon: RiGoogleFill,
},
{
value: 'chatgpt',
icon: RiOpenaiFill,
},
]

const PLACEHOLDER_TEXT: Record<SearchEngine, string> = {
google: 'Search Google...',
chatgpt: 'Ask ChatGPT...',
}

function SearchBar() {
const [query, setQuery] = React.useState('')
const [searchEngine, setSearchEngine] = React.useState<SearchEngine>('google')

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value)
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
if (searchEngine === 'google') {
location.href = `https://www.google.com/search?q=${query}`
} else if (searchEngine === 'chatgpt') {
location.href = `https://chat.openai.com/?q=${query}`
}
}
}

return (
<Styled.Root>
<Styled.Input
type="text"
placeholder={PLACEHOLDER_TEXT[searchEngine]}
value={query}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<Styled.IconTabWrapper>
<IconTab
value={searchEngine}
onChange={setSearchEngine}
items={SEARCH_ENGiNE_ITEMS}
/>
</Styled.IconTabWrapper>
</Styled.Root>
)
}

export default SearchBar
2 changes: 1 addition & 1 deletion tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"allowImportingTsExtensions": false,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
Expand Down

0 comments on commit 8e3e5cd

Please sign in to comment.