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 { 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}