1import { ColorScheme } from "../common"
2import { icon_button, toggleable_icon_button } from "../component/icon_button"
3import { toggleable_text_button } from "../component/text_button"
4import { interactive, toggleable } from "../element"
5import { with_opacity } from "../theme/color"
6import { background, border, foreground, text } from "./components"
7
8const ITEM_SPACING = 8
9const TITLEBAR_HEIGHT = 32
10
11function build_spacing(
12 container_height: number,
13 element_height: number,
14 spacing: number
15) {
16 return {
17 group: spacing,
18 item: spacing / 2,
19 half_item: spacing / 4,
20 margin_y: (container_height - element_height) / 2,
21 margin_x: (container_height - element_height) / 2,
22 }
23}
24
25function call_controls(theme: ColorScheme) {
26 const button_height = 18
27
28 const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
29 const margin_y = {
30 top: space.margin_y,
31 bottom: space.margin_y,
32 }
33
34 return {
35 toggle_microphone_button: toggleable_icon_button(theme, {
36 margin: {
37 ...margin_y,
38 left: space.group,
39 right: space.half_item,
40 },
41 active_color: "negative",
42 }),
43
44 toggle_speakers_button: toggleable_icon_button(theme, {
45 margin: {
46 ...margin_y,
47 left: space.half_item,
48 right: space.half_item,
49 },
50 }),
51
52 screen_share_button: toggleable_icon_button(theme, {
53 margin: {
54 ...margin_y,
55 left: space.half_item,
56 right: space.group,
57 },
58 active_color: "accent",
59 }),
60
61 muted: foreground(theme.lowest, "negative"),
62 speaking: foreground(theme.lowest, "accent"),
63 }
64}
65
66/**
67 * Opens the User Menu when toggled
68 *
69 * When logged in shows the user's avatar and a chevron,
70 * When logged out only shows a chevron.
71 */
72function user_menu(theme: ColorScheme) {
73 const button_height = 18
74
75 const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
76
77 const build_button = ({ online }: { online: boolean }) => {
78 const button = toggleable({
79 base: interactive({
80 base: {
81 corner_radius: 6,
82 height: button_height,
83 width: online ? 37 : 24,
84 padding: {
85 top: 2,
86 bottom: 2,
87 left: 6,
88 right: 6,
89 },
90 margin: {
91 left: space.item,
92 right: space.item,
93 },
94 ...text(theme.lowest, "sans", { size: "xs" }),
95 background: background(theme.lowest),
96 },
97 state: {
98 hovered: {
99 ...text(theme.lowest, "sans", "hovered", {
100 size: "xs",
101 }),
102 background: background(theme.lowest, "hovered"),
103 },
104 clicked: {
105 ...text(theme.lowest, "sans", "pressed", {
106 size: "xs",
107 }),
108 background: background(theme.lowest, "pressed"),
109 },
110 },
111 }),
112 state: {
113 active: {
114 default: {
115 ...text(theme.lowest, "sans", "active", { size: "xs" }),
116 background: background(theme.middle),
117 },
118 hovered: {
119 ...text(theme.lowest, "sans", "active", { size: "xs" }),
120 background: background(theme.middle, "hovered"),
121 },
122 clicked: {
123 ...text(theme.lowest, "sans", "active", { size: "xs" }),
124 background: background(theme.middle, "pressed"),
125 },
126 },
127 },
128 })
129
130 return {
131 user_menu: button,
132 avatar: {
133 icon_width: 16,
134 icon_height: 16,
135 corner_radius: 4,
136 outer_width: 16,
137 outer_corner_radius: 16,
138 },
139 icon: {
140 margin: {
141 top: 2,
142 left: online ? space.item : 0,
143 right: space.group,
144 bottom: 2,
145 },
146 width: 11,
147 height: 11,
148 color: foreground(theme.lowest),
149 },
150 }
151 }
152 return {
153 user_menu_button_online: build_button({ online: true }),
154 user_menu_button_offline: build_button({ online: false }),
155 }
156}
157
158export function titlebar(theme: ColorScheme): any {
159 const avatar_width = 15
160 const avatar_outer_width = avatar_width + 4
161 const follower_avatar_width = 14
162 const follower_avatar_outer_width = follower_avatar_width + 4
163
164 return {
165 item_spacing: ITEM_SPACING,
166 face_pile_spacing: 2,
167 height: TITLEBAR_HEIGHT,
168 background: background(theme.lowest),
169 border: border(theme.lowest, { bottom: true }),
170 padding: {
171 left: 80,
172 right: 0,
173 },
174
175 // Project
176 title: text(theme.lowest, "sans", "active"),
177 project_name_divider: text(theme.lowest, "sans", "variant"),
178 git_branch: text(theme.lowest, "sans", "variant"),
179
180 // Collaborators
181 leader_avatar: {
182 width: avatar_width,
183 outer_width: avatar_outer_width,
184 corner_radius: avatar_width / 2,
185 outer_corner_radius: avatar_outer_width / 2,
186 },
187 follower_avatar: {
188 width: follower_avatar_width,
189 outer_width: follower_avatar_outer_width,
190 corner_radius: follower_avatar_width / 2,
191 outer_corner_radius: follower_avatar_outer_width / 2,
192 },
193 inactive_avatar_grayscale: true,
194 follower_avatar_overlap: 8,
195 leader_selection: {
196 margin: {
197 top: 4,
198 bottom: 4,
199 },
200 padding: {
201 left: 2,
202 right: 2,
203 top: 2,
204 bottom: 2,
205 },
206 corner_radius: 6,
207 },
208 avatar_ribbon: {
209 height: 3,
210 width: 14,
211 // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
212 },
213
214 sign_in_button: toggleable_text_button(theme, {}),
215 offline_icon: {
216 color: foreground(theme.lowest, "variant"),
217 width: 16,
218 margin: {
219 left: ITEM_SPACING,
220 },
221 padding: {
222 right: 4,
223 },
224 },
225
226 // When the collaboration server is out of date, show a warning
227 outdated_warning: {
228 ...text(theme.lowest, "sans", "warning", { size: "xs" }),
229 background: with_opacity(background(theme.lowest, "warning"), 0.3),
230 border: border(theme.lowest, "warning"),
231 margin: {
232 left: ITEM_SPACING,
233 },
234 padding: {
235 left: 8,
236 right: 8,
237 },
238 corner_radius: 6,
239 },
240
241 leave_call_button: icon_button(theme, {
242 margin: {
243 left: ITEM_SPACING / 2,
244 right: ITEM_SPACING,
245 },
246 }),
247
248 ...call_controls(theme),
249
250 toggle_contacts_button: toggleable_icon_button(theme, {
251 margin: {
252 left: ITEM_SPACING,
253 },
254 }),
255
256 // Jewel that notifies you that there are new contact requests
257 toggle_contacts_badge: {
258 corner_radius: 3,
259 padding: 2,
260 margin: { top: 3, left: 3 },
261 border: border(theme.lowest),
262 background: foreground(theme.lowest, "accent"),
263 },
264 share_button: toggleable_text_button(theme, {}),
265 user_menu: user_menu(theme),
266 }
267}