| <script lang="ts"> |
| import { createEventDispatcher } from "svelte"; |
| |
| import type { IconName } from "@graphite/utility-functions/icons"; |
| |
| import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; |
| import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte"; |
| |
| const dispatch = createEventDispatcher<{ checked: boolean }>(); |
| |
| export let checked = false; |
| export let disabled = false; |
| export let icon: IconName = "Checkmark"; |
| export let tooltip: string | undefined = undefined; |
| export let forLabel: bigint | undefined = undefined; |
| |
| let inputElement: HTMLInputElement | undefined; |
| |
| const backupId = String(Math.random()).substring(2); |
| |
| $: id = forLabel !== undefined ? String(forLabel) : backupId; |
| $: displayIcon = (!checked && icon === "Checkmark" ? "Empty12px" : icon) as IconName; |
| |
| export function isChecked() { |
| return checked; |
| } |
| |
| export function input(): HTMLInputElement | undefined { |
| return inputElement; |
| } |
| |
| function toggleCheckboxFromLabel(e: KeyboardEvent) { |
| const target = (e.target || undefined) as HTMLLabelElement | undefined; |
| const previousSibling = (target?.previousSibling || undefined) as HTMLInputElement | undefined; |
| previousSibling?.click(); |
| } |
| </script> |
|
|
| <LayoutRow class="checkbox-input"> |
| <input |
| type="checkbox" |
| id={`checkbox-input-${id}`} |
| bind:checked |
| on:change={(_) => dispatch("checked", inputElement?.checked || false)} |
| {disabled} |
| tabindex={disabled ? -1 : 0} |
| bind:this={inputElement} |
| /> |
| <label class:disabled class:checked for={`checkbox-input-${id}`} on:keydown={(e) => e.key === "Enter" && toggleCheckboxFromLabel(e)} title={tooltip}> |
| <LayoutRow class="checkbox-box"> |
| <IconLabel icon={displayIcon} /> |
| </LayoutRow> |
| </label> |
| </LayoutRow> |
|
|
| <style lang="scss" global> |
| .checkbox-input { |
| flex: 0 0 auto; |
| align-items: center; |
|
|
| input { |
| // We can't use `display: none` because it must be visible to work as a tabbale input that accepts a space bar actuation |
| width: 0; |
| height: 0; |
| margin: 0; |
| opacity: 0; |
| // Firefox weirdly applies a 2px border which causes this element to take up a 4x4 square of space, so this removes it from the flow to prevent it from offsetting the label |
| position: absolute; |
| } |
|
|
| // Unchecked |
| label { |
| display: flex; |
| height: 16px; |
| // Provides rounded corners for the :focus outline |
| border-radius: 2px; |
|
|
| .checkbox-box { |
| flex: 0 0 auto; |
| background: var(--color-5-dullgray); |
| padding: 2px; |
| border-radius: 2px; |
|
|
| .icon-label { |
| fill: var(--color-8-uppergray); |
| } |
| } |
|
|
| // Hovered while unchecked |
| &:hover .checkbox-box { |
| background: var(--color-6-lowergray); |
| } |
|
|
| // Disabled while unchecked |
| &.disabled .checkbox-box { |
| background: var(--color-4-dimgray); |
| } |
| } |
|
|
| // Checked |
| input:checked + label { |
| .checkbox-box { |
| background: var(--color-e-nearwhite); |
|
|
| .icon-label { |
| fill: var(--color-2-mildblack); |
| } |
| } |
|
|
| // Hovered while checked |
| &:hover .checkbox-box { |
| background: var(--color-f-white); |
| } |
|
|
| // Disabled while checked |
| &.disabled .checkbox-box { |
| background: var(--color-8-uppergray); |
| } |
| } |
|
|
| + .text-label.text-label { |
| margin-left: 8px; |
| } |
| } |
| </style> |
|
|