|
<script lang="ts"> |
|
import { createEventDispatcher } from "svelte"; |
|
|
|
import { type RadioEntries, type RadioEntryData } from "@graphite/messages"; |
|
|
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; |
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte"; |
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte"; |
|
|
|
const dispatch = createEventDispatcher<{ selectedIndex: number }>(); |
|
|
|
export let entries: RadioEntries; |
|
export let selectedIndex: number | undefined = undefined; |
|
export let disabled = false; |
|
export let minWidth = 0; |
|
|
|
$: mixed = selectedIndex === undefined && !disabled; |
|
|
|
function handleEntryClick(radioEntryData: RadioEntryData) { |
|
const index = entries.indexOf(radioEntryData); |
|
dispatch("selectedIndex", index); |
|
|
|
radioEntryData.action?.(); |
|
} |
|
</script> |
|
|
|
<LayoutRow class="radio-input" classes={{ disabled, mixed }} styles={{ ...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}) }}> |
|
{#each entries as entry, index} |
|
<button class:active={!mixed ? index === selectedIndex : undefined} on:click={() => handleEntryClick(entry)} title={entry.tooltip} tabindex={index === selectedIndex ? -1 : 0} {disabled}> |
|
{#if entry.icon} |
|
<IconLabel icon={entry.icon} /> |
|
{/if} |
|
{#if entry.label} |
|
<TextLabel>{entry.label}</TextLabel> |
|
{/if} |
|
</button> |
|
{/each} |
|
</LayoutRow> |
|
|
|
<style lang="scss" global> |
|
.radio-input { |
|
background: var(--color-5-dullgray); |
|
border-radius: 2px; |
|
height: 24px; |
|
|
|
button { |
|
background: var(--color-5-dullgray); |
|
fill: var(--color-e-nearwhite); |
|
border-radius: 2px; |
|
height: 20px; |
|
padding: 0; |
|
margin: 2px 1px; |
|
border: none; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
// `min-width: fit-content` and `flex: 1 1 0` together allow us to occupy space such that we're always at least the content width, |
|
// but if the container is set wider, we distribute the space evenly (so buttons with short and long labels would have equal widths). |
|
min-width: fit-content; |
|
flex: 1 1 0; |
|
|
|
&:first-of-type { |
|
margin-left: 2px; |
|
} |
|
|
|
&:last-of-type { |
|
margin-right: 2px; |
|
} |
|
|
|
&:hover { |
|
background: var(--color-6-lowergray); |
|
color: var(--color-f-white); |
|
|
|
svg { |
|
fill: var(--color-f-white); |
|
} |
|
} |
|
|
|
&.active { |
|
background: var(--color-e-nearwhite); |
|
color: var(--color-2-mildblack); |
|
|
|
svg { |
|
fill: var(--color-2-mildblack); |
|
} |
|
} |
|
|
|
.icon-label { |
|
margin: 2px; |
|
|
|
+ .text-label { |
|
margin-left: 0; |
|
} |
|
} |
|
|
|
.text-label { |
|
margin: 0 8px; |
|
overflow: hidden; |
|
flex: 0 0 auto; |
|
} |
|
} |
|
|
|
&.mixed { |
|
background: var(--color-4-dimgray); |
|
|
|
button:not(:hover), |
|
&.disabled button:hover { |
|
background: var(--color-5-dullgray); |
|
} |
|
} |
|
|
|
&.disabled { |
|
background: var(--color-4-dimgray); |
|
|
|
button { |
|
background: var(--color-4-dimgray); |
|
color: var(--color-8-uppergray); |
|
|
|
svg { |
|
fill: var(--color-8-uppergray); |
|
} |
|
|
|
&.active { |
|
background: var(--color-8-uppergray); |
|
color: var(--color-2-mildblack); |
|
|
|
svg { |
|
fill: var(--color-2-mildblack); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
</style> |
|
|