Skip to content

Commit

Permalink
feat(app): add support for switching between search engines, and spec…
Browse files Browse the repository at this point in the history
…ifying custom one (#12)
  • Loading branch information
codeswhite authored Oct 28, 2024
1 parent f0eaf8e commit 73e574c
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 86 deletions.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from 'react'
import { Global, ThemeProvider } from '@emotion/react'

import BackgroundCanvas from '@components/BackgroundCanvas'
import Settings from '@components/Settings'
import Settings from '@components/settings/Settings'
import Clock from '@components/Clock'

import spacing from '@utils/spacing'
Expand Down
47 changes: 30 additions & 17 deletions src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,72 @@
import React from 'react'
import { RiOpenaiFill, RiGoogleFill } from 'react-icons/ri'
import { RiOpenaiFill, RiSearch2Line } from 'react-icons/ri'

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

import * as Styled from './SearchBar.styled'
import useAppStore from '@stores/app'
import { SEARCH_ENGINES } from '@constants/searchEngines'

type SearchEngine = 'google' | 'chatgpt'
type QueryMode = 'websearch' | 'chatgpt'

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

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

function SearchBar() {
const [query, setQuery] = React.useState('')
const [searchEngine, setSearchEngine] = React.useState<SearchEngine>('google')
const [queryMode, setQueryMode] = React.useState<QueryMode>('websearch')
const { searchEngine, customSearchEngineURL } = useAppStore()

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}`
}
if (event.key !== 'Enter') return

if (queryMode === 'chatgpt') {
location.href = `https://chat.openai.com/?q=${query}`
return
}

if (queryMode === 'websearch') {
const searchUrl =
searchEngine === 'custom'
? customSearchEngineURL
: SEARCH_ENGINES[searchEngine].url

if (!searchUrl) return alert('Please set a custom search engine URL.')

location.href = searchUrl.replace('{}', encodeURIComponent(query))
}
}

return (
<Styled.Root>
<Styled.Input
type="text"
placeholder={PLACEHOLDER_TEXT[searchEngine]}
placeholder={PLACEHOLDER_TEXT[queryMode]}
value={query}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<Styled.IconTabWrapper>
<IconTab
value={searchEngine}
onChange={setSearchEngine}
value={queryMode}
onChange={setQueryMode}
items={SEARCH_ENGiNE_ITEMS}
/>
</Styled.IconTabWrapper>
Expand Down
43 changes: 43 additions & 0 deletions src/components/settings/Settings.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import styled from '@emotion/styled'
import IconButton from '@components/IconButton'

export const Button = styled(IconButton)``

export const Root = styled.div`
position: absolute;
right: ${({ theme }) => theme.spacing(2)};
bottom: ${({ theme }) => theme.spacing(2)};
z-index: 3;
`

export const Content = styled.div`
width: 300px;
height: 300px;
`

export const TabHeader = styled.div`
display: flex;
justify-content: space-around;
margin-bottom: 16px;
gap: 8px;
border-radius: 4px;
`

export const TabButton = styled.button<{ isActive?: boolean }>`
flex: 1;
padding: 10px 0;
background-color: ${({ isActive }) => (isActive ? '#444' : 'transparent')};
color: ${({ isActive }) => (isActive ? '#fff' : '#ccc')};
border: ${() => '1px solid #969696'};
border-radius: 4px;
cursor: pointer;
transition:
color 0.3s,
background-color 0.3s;
&:hover {
color: #fff;
background-color: #444;
}
`
54 changes: 54 additions & 0 deletions src/components/settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useState } from 'react'
import { MdSettings } from 'react-icons/md'

import Panel from '@components/Panel'

import * as Styled from './Settings.styled'
import SettingsBackground from './background/SettingsBackground'
import SettingsSearch from './search/SettingsSearch'

type Tabs = 'background' | 'search'

function Settings() {
const [open, setOpen] = useState(false)
const [tab, setTab] = useState<Tabs>('background')

const handleButtonClick = () => {
setOpen((prev) => !prev)
}

const renderTabContent = () => {
if (tab === 'search') {
return <SettingsSearch />
}

return <SettingsBackground />
}

return (
<Styled.Root>
<Panel open={open}>
<Styled.TabHeader>
<Styled.TabButton
onClick={() => setTab('background')}
isActive={tab === 'background'}
>
Background
</Styled.TabButton>
<Styled.TabButton
onClick={() => setTab('search')}
isActive={tab === 'search'}
>
Search
</Styled.TabButton>
</Styled.TabHeader>
<Styled.Content>{renderTabContent()}</Styled.Content>
</Panel>
<Styled.Button onClick={handleButtonClick}>
<MdSettings />
</Styled.Button>
</Styled.Root>
)
}

export default Settings
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import styled from '@emotion/styled'
import IconButton from '@components/IconButton'

export const Root = styled.div`
position: absolute;
right: ${({ theme }) => theme.spacing(2)};
bottom: ${({ theme }) => theme.spacing(2)};
z-index: 3;
`

export const Content = styled.div`
width: 300px;
`

export const FileInput = styled.input`
display: none;
`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import React, { useRef, useState } from 'react'
import { MdSettings } from 'react-icons/md'
import React, { useRef } from 'react'

import Panel from '@components/Panel'
import FormSection from '@components/FormSection'

import useAppStore from '@stores/app'
import readFileAsBase64 from '@utils/readFileAsBase64'

import * as Styled from './Settings.styled'
import * as Styled from './SettingsBackground.styled'

const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB

function Settings() {
const [open, setOpen] = useState(false)
function SettingsBackground() {
const fileInputRef = useRef<HTMLInputElement>(null)
const changingTimeout = useRef<number | null>(null)

Expand Down Expand Up @@ -40,10 +37,6 @@ function Settings() {
}, 500)
}

const handleButtonClick = () => {
setOpen((prev) => !prev)
}

const handleUploadClick = () => {
fileInputRef.current?.click()
}
Expand Down Expand Up @@ -80,52 +73,45 @@ function Settings() {
}

return (
<Styled.Root>
<Panel open={open}>
<Styled.Content>
<FormSection
title="Background Image"
description="Background image should be maximum 5MB in size."
>
<Styled.FileInput
onChange={handleFileChange}
ref={fileInputRef}
type="file"
/>
<Styled.UploadButton
type="button"
onClick={handleUploadClick}
>
Upload Image
</Styled.UploadButton>
</FormSection>
<FormSection title="Dimming">
<Styled.RangeInput
type="range"
min={0}
max={1}
value={dimmingAmount}
step={0.01}
onChange={handleDimmingAmountChange}
/>
</FormSection>
<FormSection title="Blur">
<Styled.RangeInput
type="range"
min={1}
max={5}
value={blurAmount}
step={1}
onChange={handleBlurAmountChange}
/>
</FormSection>
</Styled.Content>
</Panel>
<Styled.Button onClick={handleButtonClick}>
<MdSettings />
</Styled.Button>
</Styled.Root>
<>
<FormSection
title="Background Image"
description="Background image should be maximum 5MB in size."
>
<Styled.FileInput
onChange={handleFileChange}
ref={fileInputRef}
type="file"
/>
<Styled.UploadButton
type="button"
onClick={handleUploadClick}
>
Upload Image
</Styled.UploadButton>
</FormSection>
<FormSection title="Dimming">
<Styled.RangeInput
type="range"
min={0}
max={1}
value={dimmingAmount}
step={0.01}
onChange={handleDimmingAmountChange}
/>
</FormSection>
<FormSection title="Blur">
<Styled.RangeInput
type="range"
min={1}
max={5}
value={blurAmount}
step={1}
onChange={handleBlurAmountChange}
/>
</FormSection>
</>
)
}

export default Settings
export default SettingsBackground
Loading

0 comments on commit 73e574c

Please sign in to comment.