47 lines
2.9 KiB
TypeScript
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'];
|