1import { css } from '@emotion/react';
2import { ExpoGoLogo, shadows, theme, typography } from '@expo/styleguide';
3import { breakpoints, spacing } from '@expo/styleguide-base';
4import { ChevronDownIcon, Monitor01DuotoneIcon, Phone01DuotoneIcon } from '@expo/styleguide-icons';
5import { useEffect, useState } from 'react';
6
7type PopupActionProps<T extends string> = {
8  items: { name: string; id: T }[];
9  selected: string;
10  onSelect: (value: T) => void;
11};
12export function RuntimePopup<T extends string>({ items, selected, onSelect }: PopupActionProps<T>) {
13  const Icon = [ExpoGoLogo, Phone01DuotoneIcon, Monitor01DuotoneIcon][
14    items.findIndex(item => item.id === selected)
15  ];
16  const [isLoaded, setLoaded] = useState(false);
17
18  useEffect(function didMount() {
19    setLoaded(true);
20  }, []);
21
22  return (
23    <div className="relative">
24      <select
25        aria-label="Runtime URL format selector"
26        title="Select runtime URL format"
27        css={selectStyle}
28        className="focus-visible:-outline-offset-2 border-0 rounded-none border-l border-l-default h-10 leading-10 px-10 hocus:bg-subtle hocus:shadow-none"
29        value={selected}
30        onChange={e => {
31          onSelect(e.target.value as T);
32        }}>
33        {items.map((item, index) => (
34          <option key={String(index)} value={item.id}>
35            {item.name}
36          </option>
37        ))}
38      </select>
39      {isLoaded && (
40        <div
41          style={{ lineHeight: 1.3 }}
42          className="absolute inset-x-2.5 inset-y-0 flex items-center justify-between gap-2 text-icon-secondary pointer-events-none select-none">
43          <Icon className={ICON_CLASSES} />
44          <ChevronDownIcon className="icon-xs text-icon-secondary pointer-events-none" />
45        </div>
46      )}
47    </div>
48  );
49}
50
51const ICON_CLASSES = 'icon-sm text-icon-secondary pointer-events-none inline-block';
52
53const selectStyle = css`
54  ${typography.fontSizes[14]}
55  display: flex;
56  align-items: center;
57  justify-content: center;
58  color: ${theme.text.default};
59  line-height: 1.3;
60  padding: 0 ${spacing[8]}px;
61  color: ${theme.text.default};
62  text-indent: 0;
63
64  box-shadow: ${shadows.xs};
65  -moz-appearance: none;
66  -webkit-appearance: none;
67  appearance: none;
68  background-color: ${theme.background.default};
69  cursor: pointer;
70
71  :hover {
72    background-color: ${theme.background.element};
73  }
74
75  :focus-visible {
76    background-color: ${theme.background.element};
77  }
78
79  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
80    padding: 0 0;
81    text-indent: -9999px;
82  }
83`;
84