dropdown.tsx 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { JSX, Show, createEffect, onCleanup } from "solid-js"
  2. import { createStore } from "solid-js/store"
  3. import { IconChevron } from "./icon"
  4. import "./dropdown.css"
  5. interface DropdownProps {
  6. trigger: JSX.Element | string
  7. children: JSX.Element
  8. open?: boolean
  9. onOpenChange?: (open: boolean) => void
  10. align?: "left" | "right"
  11. class?: string
  12. }
  13. export function Dropdown(props: DropdownProps) {
  14. const [store, setStore] = createStore({
  15. isOpen: props.open ?? false,
  16. })
  17. let dropdownRef: HTMLDivElement | undefined
  18. createEffect(() => {
  19. if (props.open !== undefined) {
  20. setStore("isOpen", props.open)
  21. }
  22. })
  23. createEffect(() => {
  24. const handleClickOutside = (event: MouseEvent) => {
  25. if (dropdownRef && !dropdownRef.contains(event.target as Node)) {
  26. setStore("isOpen", false)
  27. props.onOpenChange?.(false)
  28. }
  29. }
  30. document.addEventListener("click", handleClickOutside)
  31. onCleanup(() => document.removeEventListener("click", handleClickOutside))
  32. })
  33. const toggle = () => {
  34. const newValue = !store.isOpen
  35. setStore("isOpen", newValue)
  36. props.onOpenChange?.(newValue)
  37. }
  38. return (
  39. <div data-component="dropdown" class={props.class} ref={dropdownRef}>
  40. <button data-slot="trigger" type="button" onClick={toggle}>
  41. {typeof props.trigger === "string" ? <span>{props.trigger}</span> : props.trigger}
  42. <IconChevron data-slot="chevron" />
  43. </button>
  44. <Show when={store.isOpen}>
  45. <div data-slot="dropdown" data-align={props.align ?? "left"}>
  46. {props.children}
  47. </div>
  48. </Show>
  49. </div>
  50. )
  51. }
  52. interface DropdownItemProps {
  53. children: JSX.Element
  54. selected?: boolean
  55. onClick?: () => void
  56. type?: "button" | "submit" | "reset"
  57. }
  58. export function DropdownItem(props: DropdownItemProps) {
  59. return (
  60. <button
  61. data-slot="item"
  62. data-selected={props.selected ?? false}
  63. type={props.type ?? "button"}
  64. onClick={props.onClick}
  65. >
  66. {props.children}
  67. </button>
  68. )
  69. }