diff --git a/assets/styles.css b/assets/styles.css index 6a3a3af..91549d2 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -138,7 +138,7 @@ button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible .workspace.table-workspace { width: min(1480px, 94vw); padding-top: 22px; - grid-template-columns: minmax(0, 1fr) 390px; + grid-template-columns: minmax(0, 1fr); align-items: start; } .table-main-column { min-width: 0; display: grid; gap: 24px; } @@ -161,6 +161,36 @@ button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible } .dice-chat-heading .section-heading { margin-bottom: 0; } .dice-chat-heading > button { flex: 0 0 auto; min-height: 40px; } +.global-dice-button { + position: fixed; + z-index: 98; + right: 24px; + bottom: 24px; + width: 62px; + height: 54px; + padding: 6px; + display: grid; + place-content: center; + gap: 1px; + border: 1px solid var(--accent); + border-radius: 14px; + color: #111; + background: var(--accent); + box-shadow: 0 12px 36px rgba(0,0,0,.48); +} +.global-dice-button:hover { + transform: translateY(-2px); + box-shadow: 0 16px 42px rgba(0,0,0,.58); +} +.global-dice-button span { + font: 800 15px var(--font-display); + line-height: 1; +} +.global-dice-button strong { + font: 800 7px var(--font-mono); + letter-spacing: .08em; + text-transform: uppercase; +} .health-choice-window { position: fixed; z-index: 99; @@ -241,20 +271,26 @@ button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible margin-bottom: 14px; } .dice-window .dice-roll-button { width: 100%; } -.dice-window-result { +.dice-window-history { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); } -.dice-window-result > span { - display: block; - margin-bottom: 8px; +.dice-window-history-heading { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; +} +.dice-window-history-heading strong { + font: 700 12px var(--font-display); +} +.dice-window-history-heading span { color: var(--faint); font: 9px var(--font-mono); - letter-spacing: .07em; text-transform: uppercase; } -.dice-window-result .dice-roll { background: var(--surface-raised); } +.dice-window .dice-history { max-height: 240px; } .roll-mode-group { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -844,12 +880,11 @@ footer { width: min(1480px, 94vw); margin: 0 auto; padding: 20px 0 34px; display .topbar { grid-template-columns: 1fr 1fr; } .data-status { display: none; } } -@media (max-width: 1100px) { - .workspace.table-workspace { - grid-template-columns: minmax(0, 1fr) 340px; - } -} @media (max-width: 600px) { + .global-dice-button { + right: 12px; + bottom: 12px; + } .dice-window-layer { padding: 12px; } .dice-window { max-height: calc(100vh - 24px); diff --git a/src/sheets.gleam b/src/sheets.gleam index fc6214b..257886b 100644 --- a/src/sheets.gleam +++ b/src/sheets.gleam @@ -1960,7 +1960,6 @@ fn view(model: Model) -> Element(Message) { html.main([attribute.class("workspace master-workspace")], [ html.div([attribute.class("builder master-console")], [ view_collaboration(model), - view_dice_chat(model), ]), view_inspected_character(model), ]) @@ -1977,6 +1976,10 @@ fn view(model: Model) -> Element(Message) { _ -> view_page_navigation(model) }, workspace, + case model.dice_open { + True -> html.text("") + False -> view_global_dice_button() + }, case pending_health_level(model) { Ok(level) -> view_health_choice(model, level) Error(Nil) -> html.text("") @@ -1989,6 +1992,21 @@ fn view(model: Model) -> Element(Message) { ]) } +fn view_global_dice_button() -> Element(Message) { + html.button( + [ + attribute.type_("button"), + attribute.class("global-dice-button"), + attribute.aria_label("Open dice roller"), + event.on_click(UserOpenedDiceRoll("", 0)), + ], + [ + html.span([], [html.text("d20")]), + html.strong([], [html.text("Roll")]), + ], + ) +} + fn view_page_navigation(model: Model) -> Element(Message) { html.nav( [ @@ -2051,7 +2069,6 @@ fn view_table_workspace(model: Model) -> Element(Message) { _ -> view_inspected_character(model) }, ]), - view_dice_chat(model), ]) } @@ -2512,37 +2529,6 @@ fn view_health_choice(model: Model, level: Int) -> Element(Message) { ) } -fn view_dice_chat(model: Model) -> Element(Message) { - html.section([attribute.class("panel dice-chat-panel")], [ - html.div([attribute.class("dice-chat-heading")], [ - section_heading( - "DICE", - "Roll chat", - "Shared checks, damage, saves, and attacks", - ), - html.button( - [ - attribute.type_("button"), - attribute.class("primary-button"), - event.on_click(UserOpenedDiceRoll("", 0)), - ], - [html.text("Open dice roller")], - ), - ]), - case model.dice_rolls { - [] -> - html.p([attribute.class("dice-empty")], [ - html.text("Rolls from everyone at the table will appear here."), - ]) - rolls -> - html.div( - [attribute.class("dice-history"), attribute.aria_live("polite")], - list.map(rolls, view_dice_roll), - ) - }, - ]) -} - fn view_dice_window(model: Model) -> Element(Message) { let dice_count = parse_bounded(model.dice_count, 1, 1, 20) let health_roll = case model.dice_target { @@ -2672,14 +2658,27 @@ fn view_dice_window(model: Model) -> Element(Message) { ), ], ), - case model.dice_rolls { - [latest, ..] -> - html.div([attribute.class("dice-window-result")], [ - html.span([], [html.text("Latest roll")]), - view_dice_roll(latest), - ]) - [] -> html.text("") - }, + html.div([attribute.class("dice-window-history")], [ + html.div([attribute.class("dice-window-history-heading")], [ + html.strong([], [html.text("Roll history")]), + html.span([], [ + html.text( + int.to_string(list.length(model.dice_rolls)) <> " rolls", + ), + ]), + ]), + case model.dice_rolls { + [] -> + html.p([attribute.class("dice-empty")], [ + html.text("Rolls from everyone at the table will appear here."), + ]) + rolls -> + html.div( + [attribute.class("dice-history"), attribute.aria_live("polite")], + list.map(rolls, view_dice_roll), + ) + }, + ]), ], ), ])