titlebar.ts

  1import { icon_button, toggleable_icon_button } from "../component/icon_button"
  2import { toggleable_text_button } from "../component/text_button"
  3import { interactive, toggleable } from "../element"
  4import { useTheme } from "../theme"
  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() {
 26    const theme = useTheme()
 27
 28    const button_height = 18
 29
 30    const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
 31    const margin_y = {
 32        top: space.margin_y,
 33        bottom: space.margin_y,
 34    }
 35
 36    return {
 37        toggle_microphone_button: toggleable_icon_button(theme, {
 38            margin: {
 39                ...margin_y,
 40                left: space.group,
 41                right: space.half_item,
 42            },
 43            active_color: "negative",
 44        }),
 45
 46        toggle_speakers_button: toggleable_icon_button(theme, {
 47            margin: {
 48                ...margin_y,
 49                left: space.half_item,
 50                right: space.half_item,
 51            },
 52        }),
 53
 54        screen_share_button: toggleable_icon_button(theme, {
 55            margin: {
 56                ...margin_y,
 57                left: space.half_item,
 58                right: space.group,
 59            },
 60            active_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
187        project_name_divider: text(theme.lowest, "sans", "variant"),
188
189        project_menu_button: toggleable_text_button(theme, {
190            color: 'base',
191        }),
192        git_menu_button: toggleable_text_button(theme, {
193            color: 'variant',
194        }),
195
196        // Collaborators
197        leader_avatar: {
198            width: avatar_width,
199            outer_width: avatar_outer_width,
200            corner_radius: avatar_width / 2,
201            outer_corner_radius: avatar_outer_width / 2,
202        },
203        follower_avatar: {
204            width: follower_avatar_width,
205            outer_width: follower_avatar_outer_width,
206            corner_radius: follower_avatar_width / 2,
207            outer_corner_radius: follower_avatar_outer_width / 2,
208        },
209        inactive_avatar_grayscale: true,
210        follower_avatar_overlap: 8,
211        leader_selection: {
212            margin: {
213                top: 4,
214                bottom: 4,
215            },
216            padding: {
217                left: 2,
218                right: 2,
219                top: 2,
220                bottom: 2,
221            },
222            corner_radius: 6,
223        },
224        avatar_ribbon: {
225            height: 3,
226            width: 14,
227            // TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
228        },
229
230        sign_in_button: toggleable_text_button(theme, {}),
231        offline_icon: {
232            color: foreground(theme.lowest, "variant"),
233            width: 16,
234            margin: {
235                left: ITEM_SPACING,
236            },
237            padding: {
238                right: 4,
239            },
240        },
241
242        // When the collaboration server is out of date, show a warning
243        outdated_warning: {
244            ...text(theme.lowest, "sans", "warning", { size: "xs" }),
245            background: with_opacity(background(theme.lowest, "warning"), 0.3),
246            border: border(theme.lowest, "warning"),
247            margin: {
248                left: ITEM_SPACING,
249            },
250            padding: {
251                left: 8,
252                right: 8,
253            },
254            corner_radius: 6,
255        },
256
257        leave_call_button: icon_button({
258            margin: {
259                left: ITEM_SPACING / 2,
260                right: ITEM_SPACING,
261            },
262        }),
263
264        ...call_controls(),
265
266        toggle_contacts_button: toggleable_icon_button(theme, {
267            margin: {
268                left: ITEM_SPACING,
269            },
270        }),
271
272        // Jewel that notifies you that there are new contact requests
273        toggle_contacts_badge: {
274            corner_radius: 3,
275            padding: 2,
276            margin: { top: 3, left: 3 },
277            border: border(theme.lowest),
278            background: foreground(theme.lowest, "accent"),
279        },
280        share_button: toggleable_text_button(theme, {}),
281        user_menu: user_menu(),
282    }
283}