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