Skip to content

Commit

Permalink
Added search places functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
silversonicaxel committed May 15, 2024
1 parent 4930605 commit 123edb5
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 4 deletions.
3 changes: 3 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ReactNode } from 'react'

import { font } from 'src/config/font'
import { meta } from 'src/config/meta'
import { DialogProvider } from 'src/providers/dialog'
import { Header } from 'src/views/header'


Expand All @@ -28,6 +29,8 @@ export default function RootLayout({ children }: RootLayoutProps) {
<html lang="en">
<head />
<body className={font.className}>
<DialogProvider />

<main>
<Header />

Expand Down
3 changes: 3 additions & 0 deletions app/places/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getPlacesList, getTotalPlaces } from 'src/api/place'
import { PAGINATION_LIMIT } from 'src/config/pagination'
import { ApiQuery } from 'src/types/api'
import { Pagination } from 'src/views/pagination'
import { SearchPlacesBox } from 'src/views/search-places-box'


export const dynamic = 'force-dynamic'
Expand All @@ -33,6 +34,8 @@ export default async function PlacesPage(props: PlacePageProps) {

return (
<>
<SearchPlacesBox />

{places.map((place) => (
<div key={`place-${place.id}`} className={styles.uaplaces}>
<Link href={`places/${place.id}`}>
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "vitest"
},
"dependencies": {
"exenv": "^1.2.2",
"fetch-meta-tags": "^1.0.12",
"mongodb": "^6.6.1",
"next": "^14.2.3",
Expand All @@ -24,6 +25,7 @@
"devDependencies": {
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^15.0.7",
"@types/exenv": "^1.2.2",
"@types/fetch-meta-tags": "^1.0.3",
"@types/node": "^20.12.11",
"@types/react": "^18.3.2",
Expand Down
8 changes: 8 additions & 0 deletions src/providers/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { memo } from 'react'


export const DialogProvider = memo(() => {
return <div id="dialog-root" />
})

DialogProvider.displayName = 'DialogProvider'
10 changes: 6 additions & 4 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
--color-text-primary: white;
--color-background-primary: black;
--color-background-secondary: #2d2d2d;
--color-border-primary: white;
--color-border-primary: #d3d3d3;
--zindex-dialog: 9;
}

html,
body {
body,
dialog {
background-color: var(--color-background-primary);
border: 0;
color: var(--color-text-primary);
font-family: 'Space Mono', 'Helvetica Neue', sans-serif;
margin: 0 var(--spacing-element);
margin: 0;
padding: 0;
}

Expand Down
8 changes: 8 additions & 0 deletions src/views/dialog/dialog.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.uadialog {
background-color: black;
height: 100%;
opacity: 0.85;
position: absolute;
width: 100%;
z-index: var(--zindex-dialog);
}
48 changes: 48 additions & 0 deletions src/views/dialog/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client'

import { canUseDOM } from 'exenv'
import type { FC, HTMLAttributes, PropsWithChildren } from 'react'
import { createPortal } from 'react-dom'

import styles from './dialog.module.css'


export type DialogProps =
& PropsWithChildren<{
id: string
toRender?: boolean
open?: boolean
}>
& HTMLAttributes<HTMLDialogElement>

export const Dialog: FC<DialogProps> = ({
id,
toRender,
open,
children,
...props
}) => {
if (!toRender || !canUseDOM) {
return null
}

const dialogPortal = document.getElementById('dialog-root')
if (!dialogPortal) {
return null
}

return createPortal(
(
<dialog
className={styles.uadialog}
open={open}
{...props}
>
{children}
</dialog>
),
dialogPortal
)
}

Dialog.displayName = 'Dialog'
69 changes: 69 additions & 0 deletions src/views/dialog/hooks/useDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'

import { canUseDOM } from 'exenv'
import { useCallback, useEffect, useMemo, useState, } from 'react'

import type { DialogProps } from '../dialog'


export type UseDialogParams = {
id: string;
open?: boolean;
onOpen?: () => void;
onClose?: () => void;
}

export type UseDialogHookResult = {
toRender: boolean
dialogProps: DialogProps
openDialog: () => void
closeDialog: () => void
}

export type UseDialogHook = (options: UseDialogParams) => UseDialogHookResult

export const useDialog: UseDialogHook = (props) => {
const [toRender, setTodRender] = useState(false)
const [isOpen, setIsOpen] = useState(props?.open ?? false)

const openDialog = useCallback(() => {
setIsOpen(true)
}, [])

const closeDialog = useCallback(() => {
setIsOpen(false)
}, [])

const dialogProps = useMemo(() => {
return {
...props,
open: isOpen,
'aria-labelledby': `${props.id}-title`,
'aria-describedby': `${props.id}-description`,
onOpen(): void {
props.onOpen?.()
},
onClose(): void {
props.onClose?.()
},
}
}, [props, isOpen])

useEffect(() => {
if (!canUseDOM) {
setTodRender(isOpen)
return
}

if (isOpen) {
setTodRender(true)
}
}, [isOpen])

return {
toRender,
dialogProps,
openDialog,
closeDialog,
}
}
1 change: 1 addition & 0 deletions src/views/dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dialog'
1 change: 1 addition & 0 deletions src/views/search-places-box/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './search-places-box'
20 changes: 20 additions & 0 deletions src/views/search-places-box/search-places-box.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.uasearchplacesbox {
border: 1px solid var(--color-border-primary);
display: flex;
margin-bottom: var(--spacing-element);
margin-left: auto;
padding: var(--spacing-text);
width: max-content;
}

.uasearchplacesbox__cta {
font-size: smaller;
}

.uasearchplacesbox__dialog {
align-items: center;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
}
46 changes: 46 additions & 0 deletions src/views/search-places-box/search-places-box.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client'

import type { FC } from 'react'
import { useCallback } from 'react'

import styles from './search-places-box.module.css'

import { Dialog } from 'src/views/dialog'
import { useDialog } from 'src/views/dialog/hooks/useDialog'



export const SearchPlacesBox: FC = () => {
const id = 'search-places'
const title = 'Search places'
const description = 'Filter the list of places'

const { dialogProps, openDialog, closeDialog, toRender } = useDialog({ id })

const onSubmit = useCallback(() => {
closeDialog()
}, [closeDialog])

return (
<div className={styles.uasearchplacesbox}>
<a role="button" className={styles.uasearchplacesbox__cta} onClick={openDialog}>search</a>

<Dialog {...dialogProps} toRender={toRender}>
<div className={styles.uasearchplacesbox__dialog}>
<button onClick={closeDialog}>close</button>

<h3 id={dialogProps['aria-labelledby']}>{title}</h3>
<p id={dialogProps['aria-describedby']}>{description}</p>

<form>
<input name="search-places-name" placeholder="name" />

<button onClick={onSubmit}>search</button>
</form>
</div>
</Dialog>
</div>
)
}

SearchPlacesBox.displayName = 'SearchPlacesBox'

0 comments on commit 123edb5

Please sign in to comment.