انتقل إلى المحتوى

منتقي جهة في نموذج

المستخدم يكتب، والنموذج يقترح ويحفظ المعرّف.

ما يحفظه النموذج

احفظ slug للجهة المختارة، لا اسمها ولا رابط شعارها: الاسم قد يُصحّح، والشعار قد يتغيّر، والمعرّف يبقى. واقرأ السجل حين تحتاج الشعار والاسم.

الطلب

brand-picker.js

const base = 'https://new-wasmenia-4zjd6l.cranl.net';let pending; // the request still in flight, if any/** Suggestions for what the user has typed so far. */export async function suggest(text) {  const q = text.trim();  if (q.length < 2) return []; // shorter is answered empty and not counted  pending?.abort(); // a newer keystroke makes the older answer useless  pending = new AbortController();  const url = new URL('/api/v1/search/suggest', base);  url.searchParams.set('q', q);  const res = await fetch(url, { signal: pending.signal });  if (res.status === 429) {    // Over 90 a minute from this address: wait, then try again.    const wait = Number(res.headers.get('Retry-After') ?? 60);    throw Object.assign(new Error('rate_limited'), { wait });  }  if (!res.ok) throw new Error(`HTTP ${res.status}`);  const { items } = await res.json();  return items.map((b) => ({    slug: b.slug,          // what you store    label: b.nameAr,    secondary: b.nameEn,   // may be null    image: b.thumbUrl,     // may be null: show the name alone    verified: b.isVerified,  }));}

بين الحروف

لا تطلب مع كل حرف: انتظر قرابة 250 ملي ثانية بعد آخر حرف، وألغِ الطلب السابق حين يصل حرف جديد. هكذا يبقى المستخدم العادي بعيدًا عن حد الـ 90 في الدقيقة.

مع حقل الإدخال

let timer;input.addEventListener('input', () => {  clearTimeout(timer);  timer = setTimeout(async () => {    try {      render(await suggest(input.value));    } catch (e) {      if (e.name === 'AbortError') return; // replaced by a newer request      if (e.message === 'rate_limited') return pauseFor(e.wait);      showError();    }  }, 250);});

لوحة المفاتيح وقارئ الشاشة

  • الحقل role="combobox" والقائمة role="listbox"، وكل نتيجة role="option".
  • الأسهم تتنقل بين النتائج، وEnter يختار، وEsc يغلق القائمة.
  • aria-activedescendant يشير إلى النتيجة الحالية، وعدد النتائج يُعلن في منطقة aria-live.
  • نتيجة بلا thumbUrl تعرض الاسم وحده.