add ViewModeToggle component with element theming and neutral mode
This commit is contained in:
parent
04469ee202
commit
f20c99bdac
1 changed files with 152 additions and 0 deletions
152
src/lib/components/ui/ViewModeToggle.svelte
Normal file
152
src/lib/components/ui/ViewModeToggle.svelte
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import Icon from '../Icon.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import type { ViewMode } from '$lib/stores/viewMode.svelte'
|
||||
|
||||
interface Props {
|
||||
/** Current view mode */
|
||||
value?: ViewMode
|
||||
/** Callback when view mode changes */
|
||||
onValueChange?: (mode: ViewMode) => void
|
||||
/** Element color theme for active state */
|
||||
element?: string
|
||||
/** Use neutral gray styling instead of element colors */
|
||||
neutral?: boolean
|
||||
/** Additional class name */
|
||||
class?: string
|
||||
}
|
||||
|
||||
let { value = 'grid', onValueChange, element, neutral = false, class: className }: Props = $props()
|
||||
|
||||
function handleModeChange(mode: ViewMode) {
|
||||
onValueChange?.(mode)
|
||||
}
|
||||
|
||||
const containerClass = $derived(
|
||||
['view-mode-toggle', neutral ? 'neutral' : element, className].filter(Boolean).join(' ')
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class={containerClass} role="group" aria-label="View mode">
|
||||
<Tooltip content="Grid view">
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-btn"
|
||||
class:active={value === 'grid'}
|
||||
onclick={() => handleModeChange('grid')}
|
||||
aria-label="Grid view"
|
||||
aria-pressed={value === 'grid'}
|
||||
>
|
||||
<Icon name="grid" size={18} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip content="List view">
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-btn"
|
||||
class:active={value === 'list'}
|
||||
onclick={() => handleModeChange('list')}
|
||||
aria-label="List view"
|
||||
aria-pressed={value === 'list'}
|
||||
>
|
||||
<Icon name="list" size={18} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/layout' as *;
|
||||
@use '$src/themes/effects' as *;
|
||||
@use '$src/themes/colors' as *;
|
||||
|
||||
.view-mode-toggle {
|
||||
display: flex;
|
||||
gap: $unit-fourth;
|
||||
padding: $unit-fourth;
|
||||
background: var(--segmented-control-bg);
|
||||
border-radius: $item-corner;
|
||||
|
||||
// Default colors (null element)
|
||||
--toggle-active-bg: var(--null-button-bg);
|
||||
--toggle-active-bg-hover: var(--null-button-bg-hover);
|
||||
--toggle-active-icon: white;
|
||||
|
||||
// Element-specific colors
|
||||
&.wind {
|
||||
--toggle-active-bg: var(--wind-button-bg);
|
||||
--toggle-active-bg-hover: var(--wind-button-bg-hover);
|
||||
--toggle-active-icon: var(--wind-text-contrast);
|
||||
}
|
||||
|
||||
&.fire {
|
||||
--toggle-active-bg: var(--fire-button-bg);
|
||||
--toggle-active-bg-hover: var(--fire-button-bg-hover);
|
||||
--toggle-active-icon: var(--fire-text-contrast);
|
||||
}
|
||||
|
||||
&.water {
|
||||
--toggle-active-bg: var(--water-button-bg);
|
||||
--toggle-active-bg-hover: var(--water-button-bg-hover);
|
||||
--toggle-active-icon: var(--water-text-contrast);
|
||||
}
|
||||
|
||||
&.earth {
|
||||
--toggle-active-bg: var(--earth-button-bg);
|
||||
--toggle-active-bg-hover: var(--earth-button-bg-hover);
|
||||
--toggle-active-icon: var(--earth-text-contrast);
|
||||
}
|
||||
|
||||
&.dark {
|
||||
--toggle-active-bg: var(--dark-button-bg);
|
||||
--toggle-active-bg-hover: var(--dark-button-bg-hover);
|
||||
--toggle-active-icon: var(--dark-text-contrast);
|
||||
}
|
||||
|
||||
&.light {
|
||||
--toggle-active-bg: var(--light-button-bg);
|
||||
--toggle-active-bg-hover: var(--light-button-bg-hover);
|
||||
--toggle-active-icon: var(--light-text-contrast);
|
||||
}
|
||||
|
||||
// Neutral gray styling (no element colors)
|
||||
&.neutral {
|
||||
--toggle-active-bg: var(--button-bg);
|
||||
--toggle-active-bg-hover: var(--button-bg-hover);
|
||||
--toggle-active-icon: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $unit-half;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: $item-corner-small;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
@include smooth-transition($duration-instant, all);
|
||||
|
||||
&:hover:not(.active) {
|
||||
color: var(--text-tertiary);
|
||||
background: var(--button-bg-hover);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include focus-ring;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--toggle-active-bg);
|
||||
color: var(--toggle-active-icon);
|
||||
|
||||
&:hover {
|
||||
background: var(--toggle-active-bg-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue