1/**
2 * Copyright (c) 650 Industries.
3 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8import React from 'react';
9import { Image, Platform, StatusBar, StyleSheet, Text, View } from 'react-native';
10
11import type { LogLevel } from '../Data/LogBoxLog';
12import { useLogs } from '../Data/LogContext';
13import { LogBoxButton } from '../UI/LogBoxButton';
14import * as LogBoxStyle from '../UI/LogBoxStyle';
15
16type Props = {
17  onSelectIndex: (selectedIndex: number) => void;
18  level: LogLevel;
19};
20
21export function LogBoxInspectorHeader(props: Props) {
22  const { selectedLogIndex: selectedIndex, logs } = useLogs();
23  const total = logs.length;
24
25  if (props.level === 'syntax') {
26    return (
27      <View style={[styles.safeArea, styles[props.level]]}>
28        <View style={styles.header}>
29          <View style={styles.title}>
30            <Text style={styles.titleText}>Failed to compile</Text>
31          </View>
32        </View>
33      </View>
34    );
35  }
36
37  const prevIndex = selectedIndex - 1 < 0 ? total - 1 : selectedIndex - 1;
38  const nextIndex = selectedIndex + 1 > total - 1 ? 0 : selectedIndex + 1;
39
40  const titleText = `Log ${selectedIndex + 1} of ${total}`;
41
42  return (
43    <View style={[styles.safeArea, styles[props.level]]}>
44      <View style={styles.header}>
45        <LogBoxInspectorHeaderButton
46          disabled={total <= 1}
47          level={props.level}
48          image={require('@expo/metro-runtime/assets/chevron-left.png')}
49          onPress={() => props.onSelectIndex(prevIndex)}
50        />
51        <View style={styles.title}>
52          <Text style={styles.titleText}>{titleText}</Text>
53        </View>
54        <LogBoxInspectorHeaderButton
55          disabled={total <= 1}
56          level={props.level}
57          image={require('@expo/metro-runtime/assets/chevron-right.png')}
58          onPress={() => props.onSelectIndex(nextIndex)}
59        />
60      </View>
61    </View>
62  );
63}
64
65const backgroundForLevel = (level: LogLevel) =>
66  ({
67    warn: {
68      default: 'transparent',
69      pressed: LogBoxStyle.getWarningDarkColor(),
70    },
71    error: {
72      default: 'transparent',
73      pressed: LogBoxStyle.getErrorDarkColor(),
74    },
75    fatal: {
76      default: 'transparent',
77      pressed: LogBoxStyle.getFatalDarkColor(),
78    },
79    syntax: {
80      default: 'transparent',
81      pressed: LogBoxStyle.getFatalDarkColor(),
82    },
83    static: {
84      default: 'transparent',
85      pressed: LogBoxStyle.getFatalDarkColor(),
86    },
87  })[level];
88
89function LogBoxInspectorHeaderButton(props: {
90  disabled: boolean;
91  image: number;
92  level: LogLevel;
93  onPress?: () => void;
94}) {
95  return (
96    <LogBoxButton
97      backgroundColor={backgroundForLevel(props.level)}
98      onPress={props.disabled ? undefined : props.onPress}
99      style={headerStyles.button}>
100      {props.disabled ? null : (
101        <Image
102          source={props.image}
103          tintColor={LogBoxStyle.getTextColor()}
104          style={headerStyles.buttonImage}
105        />
106      )}
107    </LogBoxButton>
108  );
109}
110
111const headerStyles = StyleSheet.create({
112  button: {
113    alignItems: 'center',
114    justifyContent: 'center',
115    aspectRatio: 1,
116    marginRight: 6,
117    marginLeft: 6,
118    borderRadius: 3,
119  },
120  buttonImage: {
121    height: 14,
122    width: 8,
123  },
124});
125
126const styles = StyleSheet.create({
127  syntax: {
128    backgroundColor: LogBoxStyle.getFatalColor(),
129  },
130  static: {
131    backgroundColor: LogBoxStyle.getFatalColor(),
132  },
133  fatal: {
134    backgroundColor: LogBoxStyle.getFatalColor(),
135  },
136  warn: {
137    backgroundColor: LogBoxStyle.getWarningColor(),
138  },
139  error: {
140    backgroundColor: LogBoxStyle.getErrorColor(),
141  },
142  header: {
143    flexDirection: 'row',
144    alignItems: 'center',
145
146    paddingHorizontal: 8,
147    height: Platform.select({
148      default: 48,
149      ios: 44,
150    }),
151  },
152  title: {
153    alignItems: 'center',
154    flex: 1,
155    justifyContent: 'center',
156  },
157  titleText: {
158    color: LogBoxStyle.getTextColor(),
159    fontSize: 16,
160    fontWeight: '600',
161    includeFontPadding: false,
162    lineHeight: 20,
163  },
164  safeArea: {
165    paddingTop: Platform.OS !== 'ios' ? StatusBar.currentHeight : 40,
166  },
167});
168