1import { css } from '@emotion/react';
2import { borderRadius, iconSize, shadows, spacing, theme, ChevronDownIcon } from '@expo/styleguide';
3import React from 'react';
4
5import { VERSIONS, LATEST_VERSION, BETA_VERSION } from '~/constants/versions';
6import { usePageApiVersion } from '~/providers/page-api-version';
7import { LABEL } from '~/ui/components/Text';
8
9// TODO(cedric): move this to a generic select input, so we can reuse it in the color scheme selector
10
11export function ApiVersionSelect() {
12  const { version, hasVersion, setVersion } = usePageApiVersion();
13
14  if (!hasVersion) {
15    return null;
16  }
17
18  return (
19    <div css={containerStyle}>
20      <label css={labelStyle} htmlFor="api-version-select">
21        <LABEL css={labelTextStyle}>{versionToText(version)}</LABEL>
22        <ChevronDownIcon css={labelIconStyle} size={iconSize.small} />
23      </label>
24      <select
25        id="api-version-select"
26        css={selectStyle}
27        value={version}
28        onChange={event => setVersion(event.target.value)}>
29        {VERSIONS.map(version => (
30          <option key={version} value={version}>
31            {versionToText(version)}
32          </option>
33        ))}
34      </select>
35      {/* Changing versions is a JS only mechanism. To help crawlers find other versions, we add hidden links. */}
36      {VERSIONS.map(version => (
37        <a css={crawlerLinkStyle} key={version} href={`/versions/${version}`} />
38      ))}
39    </div>
40  );
41}
42
43function versionToText(version: string): string {
44  if (version === 'unversioned') {
45    return 'Unversioned';
46  } else if (version === 'latest') {
47    return `${versionToText(LATEST_VERSION)} (latest)`;
48  } else if (version === BETA_VERSION) {
49    return `${versionToText(BETA_VERSION)} (beta)`;
50  }
51  return `SDK ${version.substring(1, 3)}`;
52}
53
54const containerStyle = css({
55  position: 'relative',
56  background: theme.background.default,
57  border: `1px solid ${theme.border.default}`,
58  borderRadius: borderRadius.medium,
59  boxShadow: shadows.input,
60  margin: spacing[4],
61  padding: `${spacing[2]}px ${spacing[3]}px`,
62});
63
64const labelStyle = css({
65  display: 'flex',
66  flexDirection: 'row',
67  alignItems: 'center',
68});
69
70const labelTextStyle = css({
71  flex: 1,
72});
73
74const labelIconStyle = css({
75  flexShrink: 0,
76});
77
78const crawlerLinkStyle = css({
79  display: 'none',
80});
81
82const selectStyle = css({
83  borderRadius: 0,
84  position: 'absolute',
85  width: '100%',
86  height: '100%',
87  top: 0,
88  left: 0,
89  right: 0,
90  bottom: 0,
91  opacity: 0,
92  cursor: 'pointer',
93});
94