Style cleanup for channels panel

This commit is contained in:
Nate Butler
2023-08-16 12:38:44 -04:00
parent 0524abf114
commit 6c15636ccc
7 changed files with 241 additions and 45 deletions
+117 -5
View File
@@ -1,6 +1,118 @@
export const ButtonVariant = {
Default: 'default',
Ghost: 'ghost'
} as const
import { font_sizes, useTheme } from "../common"
import { Layer, Theme } from "../theme"
import { TextStyle, background } from "../style_tree/components"
export type Variant = typeof ButtonVariant[keyof typeof ButtonVariant]
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Button {
export type Options = {
layer: Layer,
background: keyof Theme["lowest"]
color: keyof Theme["lowest"]
variant: Button.Variant
size: Button.Size
shape: Button.Shape
margin: {
top?: number
bottom?: number
left?: number
right?: number
},
states: {
enabled?: boolean,
hovered?: boolean,
pressed?: boolean,
focused?: boolean,
disabled?: boolean,
}
}
export type ToggleableOptions = Options & {
active_background: keyof Theme["lowest"]
active_color: keyof Theme["lowest"]
}
/** Padding added to each side of a Shape.Rectangle button */
export const RECTANGLE_PADDING = 2
export const FONT_SIZE = font_sizes.sm
export const ICON_SIZE = 14
export const CORNER_RADIUS = 6
export const variant = {
Default: 'filled',
Outline: 'outline',
Ghost: 'ghost'
} as const
export type Variant = typeof variant[keyof typeof variant]
export const shape = {
Rectangle: 'rectangle',
Square: 'square'
} as const
export type Shape = typeof shape[keyof typeof shape]
export const size = {
Small: "sm",
Medium: "md"
} as const
export type Size = typeof size[keyof typeof size]
export type BaseStyle = {
corder_radius: number
background: string | null
padding: {
top: number
bottom: number
left: number
right: number
},
margin: Button.Options['margin']
button_height: number
}
export type LabelButtonStyle = BaseStyle & TextStyle
// export type IconButtonStyle = ButtonStyle
export const button_base = (
options: Partial<Button.Options> = {
variant: Button.variant.Default,
shape: Button.shape.Rectangle,
states: {
hovered: true,
pressed: true
}
}
): BaseStyle => {
const theme = useTheme()
const layer = options.layer ?? theme.middle
const color = options.color ?? "base"
const background_color = options.variant === Button.variant.Ghost ? null : background(layer, options.background ?? color)
const m = {
top: options.margin?.top ?? 0,
bottom: options.margin?.bottom ?? 0,
left: options.margin?.left ?? 0,
right: options.margin?.right ?? 0,
}
const size = options.size || Button.size.Medium
const padding = 2
const base: BaseStyle = {
background: background_color,
corder_radius: Button.CORNER_RADIUS,
padding: {
top: padding,
bottom: padding,
left: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding,
right: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding
},
margin: m,
button_height: 16,
}
return base
}
}
+22 -14
View File
@@ -1,7 +1,7 @@
import { interactive, toggleable } from "../element"
import { background, foreground } from "../style_tree/components"
import { useTheme, Theme } from "../theme"
import { ButtonVariant, Variant } from "./button"
import { useTheme, Theme, Layer } from "../theme"
import { Button } from "./button"
export type Margin = {
top: number
@@ -17,19 +17,24 @@ interface IconButtonOptions {
| Theme["highest"]
color?: keyof Theme["lowest"]
margin?: Partial<Margin>
variant?: Variant
variant?: Button.Variant
size?: Button.Size
}
type ToggleableIconButtonOptions = IconButtonOptions & {
active_color?: keyof Theme["lowest"]
active_layer?: Layer
}
export function icon_button({ color, margin, layer, variant = ButtonVariant.Default }: IconButtonOptions) {
export function icon_button({ color, margin, layer, variant, size }: IconButtonOptions = {
variant: Button.variant.Default,
size: Button.size.Medium,
}) {
const theme = useTheme()
if (!color) color = "base"
const background_color = variant === ButtonVariant.Ghost ? null : background(layer ?? theme.lowest, color)
const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color)
const m = {
top: margin?.top ?? 0,
@@ -38,15 +43,17 @@ export function icon_button({ color, margin, layer, variant = ButtonVariant.Defa
right: margin?.right ?? 0,
}
const padding = {
top: size === Button.size.Small ? 0 : 2,
bottom: size === Button.size.Small ? 0 : 2,
left: size === Button.size.Small ? 0 : 4,
right: size === Button.size.Small ? 0 : 4,
}
return interactive({
base: {
corner_radius: 6,
padding: {
top: 2,
bottom: 2,
left: 4,
right: 4,
},
padding: padding,
margin: m,
icon_width: 14,
icon_height: 14,
@@ -72,17 +79,18 @@ export function icon_button({ color, margin, layer, variant = ButtonVariant.Defa
export function toggleable_icon_button(
theme: Theme,
{ color, active_color, margin, variant }: ToggleableIconButtonOptions
{ color, active_color, margin, variant, size, active_layer }: ToggleableIconButtonOptions
) {
if (!color) color = "base"
return toggleable({
state: {
inactive: icon_button({ color, margin, variant }),
inactive: icon_button({ color, margin, variant, size }),
active: icon_button({
color: active_color ? active_color : color,
margin,
layer: theme.middle,
layer: active_layer,
size
}),
},
})
+78
View File
@@ -0,0 +1,78 @@
import { Interactive, interactive, toggleable, Toggleable } from "../element"
import { TextStyle, background, text } from "../style_tree/components"
import { useTheme } from "../theme"
import { Button } from "./button"
type LabelButtonStyle = {
corder_radius: number
background: string | null
padding: {
top: number
bottom: number
left: number
right: number
},
margin: Button.Options['margin']
button_height: number
} & TextStyle
/** Styles an Interactive&lt;ContainedText> */
export function label_button_style(
options: Partial<Button.Options> = {
variant: Button.variant.Default,
shape: Button.shape.Rectangle,
states: {
hovered: true,
pressed: true
}
}
): Interactive<LabelButtonStyle> {
const theme = useTheme()
const base = Button.button_base(options)
const layer = options.layer ?? theme.middle
const color = options.color ?? "base"
const default_state = {
...base,
...text(layer ?? theme.lowest, "sans", color),
font_size: Button.FONT_SIZE,
}
return interactive({
base: default_state,
state: {
hovered: {
background: background(layer, options.background ?? color, "hovered")
},
clicked: {
background: background(layer, options.background ?? color, "pressed")
}
}
})
}
/** Styles an Toggleable&lt;Interactive&lt;ContainedText>> */
export function toggle_label_button_style(
options: Partial<Button.ToggleableOptions> = {
variant: Button.variant.Default,
shape: Button.shape.Rectangle,
states: {
hovered: true,
pressed: true
}
}
): Toggleable<Interactive<LabelButtonStyle>> {
const activeOptions = {
...options,
color: options.active_color || options.color,
background: options.active_background || options.background
}
return toggleable({
state: {
inactive: label_button_style(options),
active: label_button_style(activeOptions),
},
})
}
+4 -4
View File
@@ -6,7 +6,7 @@ import {
text,
} from "../style_tree/components"
import { useTheme, Theme } from "../theme"
import { ButtonVariant, Variant } from "./button"
import { Button } from "./button"
import { Margin } from "./icon_button"
interface TextButtonOptions {
@@ -14,7 +14,7 @@ interface TextButtonOptions {
| Theme["lowest"]
| Theme["middle"]
| Theme["highest"]
variant?: Variant
variant?: Button.Variant
color?: keyof Theme["lowest"]
margin?: Partial<Margin>
text_properties?: TextProperties
@@ -25,7 +25,7 @@ type ToggleableTextButtonOptions = TextButtonOptions & {
}
export function text_button({
variant = ButtonVariant.Default,
variant = Button.variant.Default,
color,
layer,
margin,
@@ -34,7 +34,7 @@ export function text_button({
const theme = useTheme()
if (!color) color = "base"
const background_color = variant === ButtonVariant.Ghost ? null : background(layer ?? theme.lowest, color)
const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color)
const text_options: TextProperties = {
size: "xs",
+2 -2
View File
@@ -1,4 +1,4 @@
import { interactive, Interactive } from "./interactive"
import { toggleable } from "./toggle"
import { toggleable, Toggleable } from "./toggle"
export { interactive, Interactive, toggleable }
export { interactive, Interactive, toggleable, Toggleable }
+1 -1
View File
@@ -3,7 +3,7 @@ import { DeepPartial } from "utility-types"
type ToggleState = "inactive" | "active"
type Toggleable<T> = Record<ToggleState, T>
export type Toggleable<T> = Record<ToggleState, T>
export const NO_INACTIVE_OR_BASE_ERROR =
"A toggleable object must have an inactive state, or a base property."
+17 -19
View File
@@ -9,7 +9,7 @@ import { interactive, toggleable } from "../element"
import { useTheme } from "../theme"
import collab_modals from "./collab_modals"
import { text_button } from "../component/text_button"
import { toggleable_icon_button } from "../component/icon_button"
import { icon_button, toggleable_icon_button } from "../component/icon_button"
import { indicator } from "../component/indicator"
export default function contacts_panel(): any {
@@ -27,7 +27,7 @@ export default function contacts_panel(): any {
color: foreground(layer, "on"),
icon_width: 14,
button_width: 16,
corner_radius: 8,
corner_radius: 8
}
const project_row = {
@@ -62,8 +62,9 @@ export default function contacts_panel(): any {
}
const header_icon_button = toggleable_icon_button(theme, {
layer: theme.middle,
variant: "ghost",
size: "sm",
active_layer: theme.lowest,
})
const subheader_row = toggleable({
@@ -87,8 +88,8 @@ export default function contacts_panel(): any {
state: {
active: {
default: {
...text(layer, "ui_sans", "active", { size: "sm" }),
background: background(layer, "active"),
...text(theme.lowest, "ui_sans", { size: "sm" }),
background: background(theme.lowest),
},
clicked: {
background: background(layer, "pressed"),
@@ -140,8 +141,8 @@ export default function contacts_panel(): any {
},
active: {
default: {
...text(layer, "ui_sans", "active", { size: "sm" }),
background: background(layer, "active"),
...text(theme.lowest, "ui_sans", { size: "sm" }),
background: background(theme.lowest),
},
clicked: {
background: background(layer, "pressed"),
@@ -221,8 +222,8 @@ export default function contacts_panel(): any {
},
active: {
default: {
...text(layer, "ui_sans", "active", { size: "sm" }),
background: background(layer, "active"),
...text(theme.lowest, "ui_sans", { size: "sm" }),
background: background(theme.lowest),
},
clicked: {
background: background(layer, "pressed"),
@@ -271,8 +272,8 @@ export default function contacts_panel(): any {
},
active: {
default: {
...text(layer, "ui_sans", "active", { size: "sm" }),
background: background(layer, "active"),
...text(theme.lowest, "ui_sans", { size: "sm" }),
background: background(theme.lowest),
},
clicked: {
background: background(layer, "pressed"),
@@ -306,13 +307,10 @@ export default function contacts_panel(): any {
},
},
contact_button_spacing: NAME_MARGIN,
contact_button: interactive({
base: { ...contact_button },
state: {
hovered: {
background: background(layer, "hovered"),
},
},
contact_button: icon_button({
variant: "ghost",
color: "variant",
size: "sm",
}),
disabled_button: {
...contact_button,
@@ -364,7 +362,7 @@ export default function contacts_panel(): any {
}),
state: {
active: {
default: { background: background(layer, "active") },
default: { background: background(theme.lowest) },
},
},
}),