component Rating { outputs { input(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) change(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) } props { size: string = "default" color: string = "primary" title: string = "Rating" description: string = "" items: unknown[] = [] variant: string = "default" value: number = 0 max: number = 5 step: number = 1 readonly: boolean = false disabled: boolean = false class: string = "" } state selectedValue = value functions { shared function stars() { return Array.from({ length: Math.max(1, Math.min(10, max)) }) } client function choose(index, sourceEvent) { if (readonly || disabled) { return } var rect = sourceEvent.currentTarget.getBoundingClientRect() var fraction = sourceEvent.detail > 0 && rect.width ? Math.max(0, Math.min(1, (sourceEvent.clientX - rect.left) / rect.width)) : 1 var increment = Math.max(0.01, Number(step)) var next = Math.ceil((index + fraction) / increment) * increment selectedValue = Number(Math.min(max, Math.max(0, next)).toFixed(2)) output.input({ value: selectedValue, sourceEvent: sourceEvent }) output.change({ value: selectedValue, sourceEvent: sourceEvent }) } } view {
{#if title}{title}{/if} {#if description}

{description}

{/if}
{#each stars() as star, index}{/each} {selectedValue} / {max}
} style { .wrn-rating__precise { position:absolute; opacity:0; width:1px; height:1px; } .wrn-rating__precise:focus-visible { position:static; opacity:1; width:5rem; height:auto; accent-color:var(--wrn-color-primary); } .wrn-rating { display:grid; gap:.6rem; color:var(--wrn-color-text); } .wrn-rating p { margin:0; color:var(--wrn-color-muted); font-size:.875rem; } .wrn-rating__stars { display:flex; align-items:center; gap:.25rem; } .wrn-rating button { display: grid; place-items: center; width: 2rem; height: 2rem; padding: .2rem; border: 0; border-radius: calc(var(--wrn-radius-sm) - 2px); background: transparent; color: var(--wrn-color-border-strong, var(--wrn-color-muted)); cursor: pointer; transition: color 120ms ease, transform 120ms ease; } .wrn-rating svg { width:1.5rem; height:1.5rem; fill:none; stroke:currentColor; stroke-width:1.5; } .wrn-rating button[data-filled="true"] { color: var(--wrn-color-warning); } .wrn-rating svg { grid-area:1 / 1; } .wrn-rating .wrn-rating__fill { fill:currentColor; clip-path:inset(0 calc(100% - var(--fill)) 0 0); } .wrn-rating output { margin-inline-start: .5rem; font-family: var(--wrn-font-mono, monospace); font-size: .75rem; font-variant-numeric: tabular-nums; color: var(--wrn-color-muted); } .wrn-rating button:focus-visible { outline:2px solid var(--wrn-color-focus); outline-offset:2px; border-radius:4px; } } }