titlebar.ts

  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}