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 { withOpacity } 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 marginY: (container_height - element_height) / 2,
21 marginX: (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 marginY = {
30 top: space.marginY,
31 bottom: space.marginY,
32 }
33
34 return {
35 toggle_microphone_button: toggleable_icon_button(theme, {
36 margin: {
37 ...marginY,
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 ...marginY,
47 left: space.half_item,
48 right: space.half_item
49 },
50 }),
51
52 screen_share_button: toggleable_icon_button(theme, {
53 margin: {
54 ...marginY,
55 left: space.half_item,
56 right: space.group
57 },
58 active_color: 'accent'
59 }),
60 }
61}
62
63/**
64* Opens the User Menu when toggled
65*
66* When logged in shows the user's avatar and a chevron,
67* When logged out only shows a chevron.
68*/
69function user_menu(theme: ColorScheme) {
70 const button_height = 18
71
72 const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING);
73
74 const build_button = ({ online }: { online: boolean }) => {
75 const button = toggleable({
76 base: interactive({
77 base: {
78 cornerRadius: 6,
79 height: button_height,
80 width: online ? 37 : 24,
81 padding: {
82 top: 2,
83 bottom: 2,
84 left: 6,
85 right: 6,
86 },
87 margin: {
88 left: space.item,
89 right: space.item,
90 },
91 ...text(theme.lowest, "sans", { size: "xs" }),
92 background: background(theme.lowest),
93 },
94 state: {
95 hovered: {
96 ...text(theme.lowest, "sans", "hovered", {
97 size: "xs",
98 }),
99 background: background(theme.lowest, "hovered"),
100 },
101 clicked: {
102 ...text(theme.lowest, "sans", "pressed", {
103 size: "xs",
104 }),
105 background: background(theme.lowest, "pressed"),
106 },
107 },
108 }),
109 state: {
110 active: {
111 default: {
112 ...text(theme.lowest, "sans", "active", { size: "xs" }),
113 background: background(theme.middle),
114 },
115 hovered: {
116 ...text(theme.lowest, "sans", "active", { size: "xs" }),
117 background: background(theme.middle, "hovered"),
118 },
119 clicked: {
120 ...text(theme.lowest, "sans", "active", { size: "xs" }),
121 background: background(theme.middle, "pressed"),
122 },
123 },
124 }
125 });
126
127 return {
128 user_menu: button,
129 avatar: {
130 icon_width: 16,
131 icon_height: 16,
132 corner_radius: 4,
133 outer_width: 16,
134 outer_corner_radius: 16
135 },
136 icon: {
137 margin: {
138 top: 2,
139 left: online ? space.item : 0,
140 right: space.group,
141 bottom: 2,
142 },
143 width: 11,
144 height: 11,
145 color: foreground(theme.lowest)
146 }
147 }
148 }
149 return {
150 userMenuButtonOnline: build_button({ online: true }),
151 userMenuButtonOffline: build_button({ online: false }),
152 }
153}
154
155export function titlebar(theme: ColorScheme) {
156 const avatarWidth = 18
157 const avatarOuterWidth = avatarWidth + 4
158 const followerAvatarWidth = 14
159 const followerAvatarOuterWidth = followerAvatarWidth + 4
160
161 return {
162 item_spacing: ITEM_SPACING,
163 facePileSpacing: 2,
164 height: TITLEBAR_HEIGHT,
165 background: background(theme.lowest),
166 border: border(theme.lowest, { bottom: true }),
167 padding: {
168 left: 80,
169 right: 0,
170 },
171
172 // Project
173 title: text(theme.lowest, "sans", "variant"),
174 highlight_color: text(theme.lowest, "sans", "active").color,
175
176 // Collaborators
177 leaderAvatar: {
178 width: avatarWidth,
179 outerWidth: avatarOuterWidth,
180 cornerRadius: avatarWidth / 2,
181 outerCornerRadius: avatarOuterWidth / 2,
182 },
183 followerAvatar: {
184 width: followerAvatarWidth,
185 outerWidth: followerAvatarOuterWidth,
186 cornerRadius: followerAvatarWidth / 2,
187 outerCornerRadius: followerAvatarOuterWidth / 2,
188 },
189 inactiveAvatarGrayscale: true,
190 followerAvatarOverlap: 8,
191 leaderSelection: {
192 margin: {
193 top: 4,
194 bottom: 4,
195 },
196 padding: {
197 left: 2,
198 right: 2,
199 top: 2,
200 bottom: 2,
201 },
202 cornerRadius: 6,
203 },
204 avatarRibbon: {
205 height: 3,
206 width: 14,
207 // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
208 },
209
210 sign_in_button: toggleable_text_button(theme, {}),
211 offlineIcon: {
212 color: foreground(theme.lowest, "variant"),
213 width: 16,
214 margin: {
215 left: ITEM_SPACING,
216 },
217 padding: {
218 right: 4,
219 },
220 },
221
222 // When the collaboration server is out of date, show a warning
223 outdatedWarning: {
224 ...text(theme.lowest, "sans", "warning", { size: "xs" }),
225 background: withOpacity(background(theme.lowest, "warning"), 0.3),
226 border: border(theme.lowest, "warning"),
227 margin: {
228 left: ITEM_SPACING,
229 },
230 padding: {
231 left: 8,
232 right: 8,
233 },
234 cornerRadius: 6,
235 },
236
237 leave_call_button: icon_button(theme, {
238 margin: {
239 left: ITEM_SPACING / 2,
240 right: ITEM_SPACING
241 },
242 }),
243
244 ...call_controls(theme),
245
246 toggle_contacts_button: toggleable_icon_button(theme, {
247 margin: {
248 left: ITEM_SPACING,
249 },
250 }),
251
252 // Jewel that notifies you that there are new contact requests
253 toggleContactsBadge: {
254 cornerRadius: 3,
255 padding: 2,
256 margin: { top: 3, left: 3 },
257 border: border(theme.lowest),
258 background: foreground(theme.lowest, "accent"),
259 },
260 shareButton: toggleable_text_button(theme, {}),
261 user_menu: user_menu(theme)
262 }
263}