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