InputDrawer.tsx 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { X, Check } from 'lucide-react';
  3. interface InputDrawerProps {
  4. isOpen: boolean;
  5. onClose: () => void;
  6. title: string;
  7. onSubmit: (value: string) => void;
  8. placeholder?: string;
  9. defaultValue?: string;
  10. submitLabel?: string;
  11. }
  12. export const InputDrawer: React.FC<InputDrawerProps> = ({
  13. isOpen,
  14. onClose,
  15. title,
  16. onSubmit,
  17. placeholder = '',
  18. defaultValue = '',
  19. submitLabel = '确定'
  20. }) => {
  21. const [value, setValue] = useState(defaultValue);
  22. const inputRef = useRef<HTMLInputElement>(null);
  23. useEffect(() => {
  24. if (isOpen) {
  25. setValue(defaultValue);
  26. setTimeout(() => {
  27. inputRef.current?.focus();
  28. }, 50);
  29. }
  30. }, [isOpen, defaultValue]);
  31. const handleSubmit = (e?: React.FormEvent) => {
  32. e?.preventDefault();
  33. if (value.trim()) {
  34. onSubmit(value.trim());
  35. onClose();
  36. }
  37. };
  38. if (!isOpen) return null;
  39. return (
  40. <div className="fixed inset-0 z-50 overflow-hidden">
  41. <div className="absolute inset-0 bg-black bg-opacity-30 transition-opacity" onClick={onClose} />
  42. <div className="absolute inset-y-0 right-0 max-w-sm w-full flex pointer-events-none">
  43. {/* pointer-events-none on wrapper, auto on content to allow closing by clicking left side */}
  44. <div className="flex-1 flex flex-col bg-white shadow-xl animate-in slide-in-from-right duration-300 pointer-events-auto">
  45. <div className="flex items-center justify-between px-4 py-3 border-b border-gray-200">
  46. <h2 className="text-lg font-medium text-gray-900">{title}</h2>
  47. <button
  48. onClick={onClose}
  49. className="text-gray-400 hover:text-gray-500 focus:outline-none"
  50. >
  51. <X size={24} />
  52. </button>
  53. </div>
  54. <form onSubmit={handleSubmit} className="p-6 flex-1 flex flex-col">
  55. <label className="block text-sm font-medium text-gray-700 mb-2">
  56. {title}
  57. </label>
  58. <input
  59. ref={inputRef}
  60. type="text"
  61. value={value}
  62. onChange={(e) => setValue(e.target.value)}
  63. placeholder={placeholder}
  64. className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all"
  65. />
  66. <div className="mt-6 flex justify-end gap-3">
  67. <button
  68. type="button"
  69. onClick={onClose}
  70. className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50"
  71. >
  72. 取消
  73. </button>
  74. <button
  75. type="submit"
  76. disabled={!value.trim()}
  77. className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 flex items-center gap-2"
  78. >
  79. {submitLabel}
  80. </button>
  81. </div>
  82. </form>
  83. </div>
  84. </div>
  85. </div>
  86. );
  87. };