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