Files
Artemis-Eapil/apps/showcase_ui/src/app/components/setting-slider/setting-slider.component.ts
T

47 lines
2.9 KiB
TypeScript

import { Component, computed, input, output } from '@angular/core';
@Component({
selector: 'app-setting-slider', standalone: true,
template: `
<div class="slider-heading"><span>{{label()}}</span><output>{{display()}}</output></div>
<input type="range" min="0" [max]="values().length - 1" step="1" [value]="index()"
[disabled]="disabled()" [attr.aria-label]="label()" [attr.aria-valuetext]="display()"
[style.--fill]="(index() / (values().length - 1) * 100) + '%'"
(input)="change($any($event.target).value)" />
<div class="slider-ends"><span>{{labels()[0] || values()[0]}}</span><span>{{labels().at(-1) || values().at(-1)}}</span></div>
@if(hint()){<p>{{hint()}}</p>}
`,
styles: [`
:host{display:block;min-width:0;padding:14px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:14px;font-size:13px;color:#334155}
.slider-heading,.slider-ends{display:flex;align-items:center;justify-content:space-between;gap:10px}
.slider-heading{font-weight:600}output{color:#4f46e5;font-size:12px;white-space:nowrap}.slider-ends{font-size:11px;color:#64748b}
input{appearance:none;display:block;box-sizing:border-box;width:100%;height:6px;border:0;border-radius:10px;margin:20px 0 12px;background:linear-gradient(to right,#6366f1 var(--fill),#dbe3ee var(--fill));cursor:pointer}
input::-webkit-slider-thumb{appearance:none;width:18px;height:18px;border-radius:50%;background:#fff;border:2px solid #6366f1;box-shadow:0 2px 6px #33415526}
input::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:#fff;border:2px solid #6366f1}
input:focus-visible{outline:2px solid #a5b4fc;outline-offset:5px}input:disabled{opacity:.5;cursor:default}p{margin:10px 0 0;font-size:12px;line-height:1.6;color:#64748b}
`]
})
export class SettingSliderComponent {
label = input.required<string>();
value = input.required<string | number>();
values = input.required<(string | number)[]>();
labels = input<string[]>([]);
hint = input('');
disabled = input(false);
valueChange = output<any>();
index = computed(() => {
const exact = this.values().indexOf(this.value());
if(exact >= 0) return exact;
if(typeof this.value() === 'number') return this.values().reduce<number>((best, v, i) => Math.abs(Number(v)-Number(this.value())) < Math.abs(Number(this.values()[best])-Number(this.value())) ? i : best, 0);
return 0;
});
display = computed(() => {
const exact = this.values().indexOf(this.value());
return exact >= 0 ? this.labels()[exact] || String(this.value()) : String(this.value()) + (typeof this.value() === 'number' ? ' 秒' : '');
});
change(index: string) { this.valueChange.emit(this.values()[Number(index)]); }
}
export const REASONING_VALUES = ['default','none','minimal','low','medium','high','xhigh','max'];
export const REASONING_LABELS = ['模型默认','关闭 · none','最低 · minimal','低 · low','中 · medium','高 · high','极高 · xhigh','最高 · max'];