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