File size: 3,980 Bytes
518343a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { useState, useRef, useEffect } from 'react';
import { useOrg } from '../../context/OrgContext';
import type { OrgId } from '../../context/OrgContext';
import { ChevronDown } from 'lucide-react';

export function TopBar() {
  const { currentOrg, setOrg } = useOrg();
  const [isOpen, setIsOpen] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);
  const triggerRef = useRef<HTMLButtonElement>(null);

  const orgs: { id: OrgId; name: string }[] = [
    { id: 'szl', name: 'a11oy' },
    { id: 'acme', name: 'Acme Industries' },
    { id: 'northwind', name: 'Northwind Labs' }
  ];

  useEffect(() => {
    function handleOutsideClick(e: MouseEvent) {
      if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
        setIsOpen(false);
      }
    }
    if (isOpen) {
      document.addEventListener('mousedown', handleOutsideClick);
    }
    return () => document.removeEventListener('mousedown', handleOutsideClick);
  }, [isOpen]);

  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === 'Escape') {
      setIsOpen(false);
      triggerRef.current?.focus();
    }
  }

  function handleOrgSelect(id: OrgId) {
    setOrg(id);
    setIsOpen(false);
    triggerRef.current?.focus();
  }

  return (
    <header className="h-14 border-b border-[var(--color-a11oy-border)] bg-[var(--color-a11oy-deep)] flex items-center justify-between px-6 shrink-0 z-30 sticky top-0">
      <div className="flex items-center gap-5">
        <div className="flex items-baseline gap-2">
          <span className="font-display font-semibold text-lg tracking-tight text-[var(--color-a11oy-text)]">
            A<span className="font-mono text-[var(--color-a11oy-blue)] font-bold">11</span>oy
          </span>
        </div>

        <div className="h-4 w-px bg-[var(--color-a11oy-border)]" aria-hidden="true"></div>

        <div className="relative" ref={menuRef} onKeyDown={handleKeyDown}>
          <button
            ref={triggerRef}
            type="button"
            onClick={() => setIsOpen(prev => !prev)}
            aria-haspopup="listbox"
            aria-expanded={isOpen}
            aria-label={`Current organization: ${orgs.find(o => o.id === currentOrg)?.name}. Change organization`}
            className="flex items-center gap-2 text-sm text-[var(--color-a11oy-text-sub)] hover:text-[var(--color-a11oy-text)] transition-colors cursor-pointer"
          >
            <span className="font-medium">{orgs.find(o => o.id === currentOrg)?.name}</span>
            <ChevronDown className="w-3.5 h-3.5 opacity-50" aria-hidden="true" />
          </button>

          {isOpen && (
            <ul
              role="listbox"
              aria-label="Select organization"
              className="absolute top-full left-0 mt-2 w-48 bg-[var(--color-a11oy-card)] border border-[var(--color-a11oy-border)] rounded shadow-xl z-50 py-1"
            >
              {orgs.map(org => (
                <li key={org.id} role="option" aria-selected={currentOrg === org.id}>
                  <button
                    type="button"
                    onClick={() => handleOrgSelect(org.id)}
                    className={`w-full text-left px-4 py-2 text-sm transition-colors ${currentOrg === org.id ? 'bg-[var(--color-a11oy-surface)] text-[var(--color-a11oy-blue)]' : 'text-[var(--color-a11oy-text-sub)] hover:bg-[var(--color-a11oy-surface)] hover:text-[var(--color-a11oy-text)]'}`}
                  >
                    {org.name}
                  </button>
                </li>
              ))}
            </ul>
          )}
        </div>
      </div>

      <div className="flex items-center gap-2">
        <span className="w-1.5 h-1.5 rounded-full bg-[var(--color-a11oy-blue)] animate-pulse" aria-hidden="true" />
        <span className="text-[11px] font-mono" style={{ color: 'var(--color-a11oy-text-ghost)', letterSpacing: '0.04em' }}>
          Governed Environment
        </span>
      </div>
    </header>
  );
}