You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/soma/components/meter/meter-provider.svelte.ts

148 lines
4.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { context, type WithRefOpts } from '../../provider';
import { type ActiveProps } from '$libs/reactive';
import { Soma } from '../../core/soma.svelte';
import type { MeterZone } from './types';
import { meterMorfo } from '../../../morfo/components/meter';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
function computeZone(value: number, low: number, high: number): MeterZone {
if (value < low) return 'below';
if (value > high) return 'above';
return 'optimum';
}
// ── Root provider ──────────────────────────────────────────────────────────
interface MeterOpts
extends
WithRefOpts,
ActiveProps<{
value: number;
min: number;
max: number;
low: number | undefined;
high: number | undefined;
optimum: number | undefined;
ariaLabelledby: string | undefined;
valueText: string | undefined;
}> {}
export class MeterProvider {
readonly opts: MeterOpts;
readonly runtimePart: SomaRuntimePart<typeof meterMorfo, 'provider'>;
readonly soma: Soma;
readonly runtime: SomaRuntime<typeof meterMorfo>;
static readonly ctx = context<MeterProvider>('Meter');
static get(): MeterProvider | undefined {
return this.ctx.getOr(undefined) as MeterProvider | undefined;
}
static require(): MeterProvider {
return this.ctx.get();
}
static create(opts: MeterOpts) {
return new MeterProvider(opts);
}
private constructor(opts: MeterOpts) {
this.opts = opts;
this.soma = Soma.require();
this.runtime = this.soma.runtime(meterMorfo, {
states: {
optimum: () => this.state
},
props: {
min: () => this.opts.min.current,
max: () => this.opts.max.current,
low: () => this.resolvedLow,
high: () => this.resolvedHigh,
optimum: () => this.resolvedOptimum,
value: () => this.opts.value.current,
valueText: () => this.resolvedValueText,
ariaLabelledby: () => this.opts.ariaLabelledby.current
}
});
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: MeterProvider.ctx
});
}
readonly resolvedLow = $derived.by(() => this.opts.low.current ?? this.opts.min.current);
readonly resolvedHigh = $derived.by(() => this.opts.high.current ?? this.opts.max.current);
readonly resolvedOptimum = $derived.by(
() => this.opts.optimum.current ?? (this.resolvedLow + this.resolvedHigh) / 2
);
readonly state: MeterZone = $derived.by(() =>
computeZone(this.opts.value.current, this.resolvedLow, this.resolvedHigh)
);
private percentageFor(value: number): number {
const min = this.opts.min.current;
const max = this.opts.max.current;
if (max <= min) return 0;
return Math.max(0, Math.min(100, ((value - min) / (max - min)) * 100));
}
/** 0–100 percentage for CSS vars. Clamped to the min/max range. */
readonly percentage = $derived.by(() => this.percentageFor(this.opts.value.current));
readonly lowPercentage = $derived.by(() => this.percentageFor(this.resolvedLow));
readonly highPercentage = $derived.by(() => this.percentageFor(this.resolvedHigh));
readonly optimumPercentage = $derived.by(() => this.percentageFor(this.resolvedOptimum));
readonly resolvedValueText = $derived.by(() => {
const explicit = this.opts.valueText.current;
if (explicit) return explicit;
return `${this.opts.value.current} / ${this.opts.max.current}`;
});
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
style: {
'--_meter-value-pct': `${this.percentage}`,
'--_meter-low-pct': `${this.lowPercentage}`,
'--_meter-high-pct': `${this.highPercentage}`,
'--_meter-optimum-pct': `${this.optimumPercentage}`
}
} as const)
);
}
// ── Indicator ──────────────────────────────────────────────────────────────
interface MeterIndicatorOpts extends WithRefOpts {}
export class MeterIndicatorProvider {
readonly opts: MeterIndicatorOpts;
readonly runtimePart: SomaRuntimePart<typeof meterMorfo, 'indicator'>;
static create(opts: MeterIndicatorOpts) {
return new MeterIndicatorProvider(opts);
}
readonly provider: MeterProvider;
private constructor(opts: MeterIndicatorOpts) {
this.opts = opts;
this.provider = MeterProvider.require();
this.runtimePart = this.provider.runtime.part('indicator', {
id: opts.id,
ref: opts.ref,
states: {
optimum: () => this.provider.state
},
owner: this
});
}
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props
} as const)
);
}

Powered by TurnKey Linux.