Sobes.tech
Middle

Тапсырма 1 Бізде 10 000 объектілерден тұратын массив бар (мысалы, «Қызметкерлер» немесе «Тауарлар»). Біз браузерді «қалмайтындай» етіп, пайдаланушыға ыңғайлы таңдау компонентін жүзеге асыруымыз керек. Талаптар: 1. Мәліметтер типін анықтау: id: string, label: string және category: string өрістері бар Item интерфейсін жасау. 2. Іздеу (Input): Мәтін енгізілген сайын тізім сүзгіленуі керек. 3. DOM және оқиға өңдеу (Клавиатурамен басқару): * Пайдаланушы Up және Down стрелкаларымен тізімді басқара алуы керек. 4. Enter пернесі басылғанда, таңдалған элемент «расталады». 5. Маңызды нюанс: Стрелкамен таңдалған элемент әрқашан көрінетін болуы керек (контейнерді программалық түрде айналдыру керек, егер курсор шекарадан асып кетсе). 6. Оптимизация (Өнімділік): * Мәлімет көп болғандықтан (10 мың), қарастырыңыз немесе қарапайым «мұздату» (тек алғашқы 20-30 элементті көрсету немесе IntersectionObserver пайдалану) жүзеге асырыңыз. Компоненттің нәтижесі.

sobes.tech AI

AI-дан жауап

Үлкен массивтен (10 000 элемент) таңдау компонентін іске асыру үшін іздеу және пернелер арқылы навигация жасау тәсілі:

interface Item {
  id: string;
  label: string;
  category: string;
}

const ITEMS_PER_PAGE = 30;

function ItemSelector({ items }: { items: Item[] }) {
  const [query, setQuery] = React.useState('');
  const [filteredItems, setFilteredItems] = React.useState<Item[]>(items);
  const [highlightedIndex, setHighlightedIndex] = React.useState(0);
  const containerRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    const filtered = items.filter(item =>
      item.label.toLowerCase().includes(query.toLowerCase())
    );
    setFilteredItems(filtered);
    setHighlightedIndex(0);
  }, [query, items]);

  // Пернелерді өңдеу
  function onKeyDown(e: React.KeyboardEvent) {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setHighlightedIndex(i => Math.min(i + 1, filteredItems.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setHighlightedIndex(i => Math.max(i - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      const selected = filteredItems[highlightedIndex];
      if (selected) alert(`Таңдалды: ${selected.label}`);
    }
  }

  // Таңдалған элементке скролл жасау
  React.useEffect(() => {
    const container = containerRef.current;
    if (!container) return;
    const itemElements = container.querySelectorAll('.item');
    const current = itemElements[highlightedIndex] as HTMLElement | undefined;
    if (current) {
      const containerTop = container.scrollTop;
      const containerBottom = containerTop + container.clientHeight;
      const elemTop = current.offsetTop;
      const elemBottom = elemTop + current.offsetHeight;

      if (elemTop < containerTop) {
        container.scrollTop = elemTop;
      } else if (elemBottom > containerBottom) {
        container.scrollTop = elemBottom - container.clientHeight;
      }
    }
  }, [highlightedIndex]);

  // Бірінші ITEMS_PER_PAGE элементтерін ленивый түрде көрсету
  const visibleItems = filteredItems.slice(0, ITEMS_PER_PAGE);

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={e => setQuery(e.target.value)}
        onKeyDown={onKeyDown}
        placeholder="Іздеу..."
      />
      <div
        ref={containerRef}
        style={{ maxHeight: 300, overflowY: 'auto', border: '1px solid #ccc' }}
        tabIndex={0}
        onKeyDown={onKeyDown}
      >
        {visibleItems.map((item, index) => (
          <div
            key={item.id}
            className="item"
            style={{
              padding: '4px 8px',
              backgroundColor: index === highlightedIndex ? '#bde4ff' : 'transparent',
            }}
          >
            {item.label} ({item.category})
          </div>
        ))}
        {filteredItems.length > ITEMS_PER_PAGE && (
          <div style={{ padding: 8, fontStyle: 'italic' }}>
            {ITEMS_PER_PAGE} элементтен көрсетілді, барлығы {filteredItems.length}.
          </div>
        )}
      </div>
    </div>
  );
}