docs,api-ref: added search filters (#4830)

* initial implementation of search modal

* added hit and search suggestions

* added support for multiple indices

* updated sample env

* added close when click outside dropdown

* test for mobile

* added mobile design

* added shortcut

* dark mode fixes

* added search to docs

* added plugins filter

* added React import

* moved filters to configurations

* handled error on page load

* change suggestion text

* removed hits limit

* handle select all

* open link in current tab

* change highlight colors

* added support for shortcuts + auto focus

* change header and footer

* redesigned search ui
This commit is contained in:
Shahed Nasser
2023-08-24 18:36:06 +03:00
committed by GitHub
parent f4bf9ee169
commit f07dc0384f
109 changed files with 4555 additions and 1648 deletions

View File

@@ -0,0 +1,29 @@
import React from "react"
import clsx from "clsx"
import Button, { ButtonProps } from "../../Button"
type ModalFooterProps = {
actions?: ButtonProps[]
children?: React.ReactNode
className?: string
}
const ModalFooter = ({ actions, className, children }: ModalFooterProps) => {
return (
<div
className={clsx(
"py-1.5 pl-0 pr-2",
"border-medusa-border-base dark:border-medusa-border-base-dark border-0 border-t border-solid",
"flex justify-end gap-0.5",
className
)}
>
{actions?.map((action, index) => (
<Button {...action} key={index} />
))}
{children}
</div>
)
}
export default ModalFooter

View File

@@ -0,0 +1,39 @@
import React from "react"
import clsx from "clsx"
import { useModal } from "../../../providers/Modal"
import Button from "../../Button"
import IconXMark from "../../../theme/Icon/XMark"
type ModalHeaderProps = {
title?: string
}
const ModalHeader = ({ title }: ModalHeaderProps) => {
const { closeModal } = useModal()
return (
<div
className={clsx(
"border-medusa-border-base dark:border-medusa-border-base-dark border-0 border-b border-solid py-1.5 px-2",
"flex items-center justify-between"
)}
>
<span
className={clsx(
"text-medusa-fg-base dark:text-medusa-fg-base-dark text-h2"
)}
>
{title}
</span>
<Button
variant="clear"
className="cursor-pointer"
onClick={() => closeModal()}
>
<IconXMark />
</Button>
</div>
)
}
export default ModalHeader

View File

@@ -1,73 +1,117 @@
import clsx from "clsx"
import React from "react"
import Button, { ButtonProps } from "../Button"
import IconXMark from "../../theme/Icon/XMark"
import React, { forwardRef, useCallback, useEffect, useRef } from "react"
import { ButtonProps } from "../Button"
import ModalHeader from "./Header"
import ModalFooter from "./Footer"
import useKeyboardShortcut from "../../hooks/use-keyboard-shortcut"
import { useModal } from "../../providers/Modal"
export type ModalProps = {
className?: string
title?: string
actions?: ButtonProps[]
} & React.DetailedHTMLProps<
React.DialogHTMLAttributes<HTMLDialogElement>,
HTMLDialogElement
>
modalContainerClassName?: string
contentClassName?: string
onClose?: React.ReactEventHandler<HTMLDialogElement>
open?: boolean
footerContent?: React.ReactNode
} & Omit<React.ComponentProps<"dialog">, "ref">
const Modal = forwardRef<HTMLDialogElement, ModalProps>(function Modal(
{
className,
title,
actions,
children,
contentClassName,
modalContainerClassName,
onClose,
open = true,
footerContent,
...props
},
passedRef
) {
const { closeModal } = useModal()
const ref = useRef<HTMLDialogElement | null>(null)
const setRefs = useCallback(
(node: HTMLDialogElement) => {
// Ref's from useRef needs to have the node assigned to `current`
ref.current = node
if (typeof passedRef === "function") {
passedRef(node)
} else if (passedRef && "current" in passedRef) {
passedRef.current = node
}
},
[passedRef]
)
useKeyboardShortcut({
metakey: false,
checkEditing: false,
shortcutKeys: ["escape"],
action: () => {
if (open) {
ref.current?.close()
}
},
})
const handleClick = (e: React.MouseEvent<HTMLDialogElement, MouseEvent>) => {
// close modal when the user clicks outside the content
if (e.target === ref.current) {
closeModal()
onClose?.(e)
}
}
const handleClose = (e: React.SyntheticEvent<HTMLDialogElement, Event>) => {
onClose?.(e)
closeModal()
}
useEffect(() => {
if (open) {
document.body.setAttribute("data-modal", "opened")
} else {
document.body.removeAttribute("data-modal")
}
}, [open])
const Modal: React.FC<ModalProps> = ({
className,
title,
actions,
children,
...props
}) => {
return (
<dialog
{...props}
className={clsx(
"absolute top-0 left-0 flex w-screen h-screen justify-center items-center",
"",
"fixed top-0 left-0 flex h-screen w-screen items-center justify-center",
"bg-medusa-bg-overlay dark:bg-medusa-bg-overlay-dark z-[500]",
"hidden open:flex border-0 p-0",
className
)}
onClick={handleClick}
ref={setRefs}
onClose={handleClose}
open={open}
>
<div
className={clsx(
"bg-medusa-bg-base dark:bg-medusa-bg-base-dark rounded",
"bg-medusa-bg-base dark:bg-medusa-bg-base-dark rounded-sm",
"border-medusa-border-base dark:border-medusa-border-base-dark border border-solid",
"shadow-modal dark:shadow-modal-dark",
"w-[90%] md:w-[75%] lg:w-1/2"
"w-[90%] md:h-auto md:w-[75%] lg:w-[560px]",
modalContainerClassName
)}
>
<div
className={clsx(
"py-1.5 px-2 border-0 border-solid border-b border-medusa-border-base dark:border-medusa-border-base-dark",
"flex justify-between items-center"
)}
>
<span
className={clsx(
"text-h1 text-medusa-fg-base dark:text-medusa-fg-base-dark"
)}
>
{title}
</span>
<IconXMark />
{title && <ModalHeader title={title} />}
<div className={clsx("overflow-auto py-1.5 px-2", contentClassName)}>
{children}
</div>
<div>{children}</div>
{actions && actions?.length > 0 && (
<div
className={clsx(
"pl-0 pr-2 px-1.5",
"border-0 border-solid border-t border-medusa-border-base dark:border-medusa-border-base-dark",
"flex justify-end gap-0.5"
)}
>
{actions.map((action, index) => (
<Button {...action} key={index} />
))}
</div>
)}
{actions && actions?.length > 0 && <ModalFooter actions={actions} />}
{footerContent && <ModalFooter>{footerContent}</ModalFooter>}
</div>
</dialog>
)
}
})
export default Modal