obsidian-book-tracker/src/ui/components/RatingInput.svelte

109 lines
2.4 KiB
Svelte

<script lang="ts">
import { Star, StarHalf } from "lucide-svelte";
interface Props {
value?: number;
name?: string;
}
const maxV = 5;
let { value = $bindable(), name }: Props = $props();
let ctrl: HTMLElement | null = $state(null);
let w = $state(0);
let h = $state(0);
$effect(() => {
if (ctrl) {
ctrl.style.width = `${w}px`;
ctrl.style.height = `${h}px`;
}
});
let hovering = $state(false);
let valueHover = $state(0);
let displayVal = $derived(hovering ? valueHover : (value ?? 0));
let items = $derived.by(() => {
const full = Number.isInteger(displayVal);
return Array.from({ length: maxV }, (_, i) => i + 1).map((index) => ({
index,
state:
index <= Math.ceil(displayVal)
? full || index != Math.ceil(displayVal)
? "active"
: "partial"
: "inactive",
}));
});
function calcSliderPos(e: MouseEvent) {
return (e.offsetX / (e.target as HTMLElement).clientWidth) * maxV;
}
function onclick() {
value = valueHover;
}
function onmouseout() {
hovering = false;
valueHover = 0;
}
function onmousemove(e: MouseEvent) {
hovering = true;
valueHover = Math.ceil(calcSliderPos(e) * 2) / 2;
}
</script>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<div class="rating-input" {onclick} {onmouseout}>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="ctrl" {onmousemove} bind:this={ctrl}></div>
<div class="cont m-1" bind:clientWidth={w} bind:clientHeight={h}>
{#each items as item (item.index)}
<div class="rating-item">
{#if item.state === "inactive"}
<Star fill="var(--interactive-normal)" />
{:else if item.state === "active"}
<Star fill="var(--interactive-accent)" />
{:else if item.state === "partial"}
<Star fill="var(--interactive-normal)" />
<StarHalf fill="var(--interactive-accent)" />
{/if}
</div>
{/each}
</div>
</div>
<style lang="scss">
.rating-input {
cursor: pointer;
.ctrl {
position: absolute;
z-index: 2;
}
.cont {
display: flex;
}
:global(svg) {
position: absolute;
width: 100%;
height: 100%;
}
.rating-item {
position: relative;
width: var(--size-4-16);
height: var(--size-4-16);
}
}
</style>