file-search.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import { Portal } from "solid-js/web"
  2. import { useI18n } from "@kirincode-ai/ui/context/i18n"
  3. import { Icon } from "@kirincode-ai/ui/icon"
  4. export function FileSearchBar(props: {
  5. pos: () => { top: number; right: number }
  6. query: () => string
  7. index: () => number
  8. count: () => number
  9. setInput: (el: HTMLInputElement) => void
  10. onInput: (value: string) => void
  11. onKeyDown: (event: KeyboardEvent) => void
  12. onClose: () => void
  13. onPrev: () => void
  14. onNext: () => void
  15. }) {
  16. const i18n = useI18n()
  17. return (
  18. <Portal>
  19. <div
  20. class="fixed z-50 flex h-8 items-center gap-2 rounded-md border border-border-base bg-background-base px-3 shadow-md"
  21. style={{
  22. top: `${props.pos().top}px`,
  23. right: `${props.pos().right}px`,
  24. }}
  25. onPointerDown={(e) => e.stopPropagation()}
  26. >
  27. <Icon name="magnifying-glass" size="small" class="text-text-weak shrink-0" />
  28. <input
  29. ref={props.setInput}
  30. placeholder={i18n.t("ui.fileSearch.placeholder")}
  31. value={props.query()}
  32. class="w-40 bg-transparent outline-none text-14-regular text-text-strong placeholder:text-text-weak"
  33. onInput={(e) => props.onInput(e.currentTarget.value)}
  34. onKeyDown={(e) => props.onKeyDown(e as KeyboardEvent)}
  35. />
  36. <div class="shrink-0 text-12-regular text-text-weak tabular-nums text-right" style={{ width: "10ch" }}>
  37. {props.count() ? `${props.index() + 1}/${props.count()}` : "0/0"}
  38. </div>
  39. <div class="flex items-center">
  40. <button
  41. type="button"
  42. class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
  43. disabled={props.count() === 0}
  44. aria-label={i18n.t("ui.fileSearch.previousMatch")}
  45. onClick={props.onPrev}
  46. >
  47. <Icon name="chevron-down" size="small" class="rotate-180" />
  48. </button>
  49. <button
  50. type="button"
  51. class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
  52. disabled={props.count() === 0}
  53. aria-label={i18n.t("ui.fileSearch.nextMatch")}
  54. onClick={props.onNext}
  55. >
  56. <Icon name="chevron-down" size="small" />
  57. </button>
  58. </div>
  59. <button
  60. type="button"
  61. class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
  62. aria-label={i18n.t("ui.fileSearch.close")}
  63. onClick={props.onClose}
  64. >
  65. <Icon name="close-small" size="small" />
  66. </button>
  67. </div>
  68. </Portal>
  69. )
  70. }