From cde839dc5deaf74153f1473f8c6463577eb7981a Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 15 May 2026 03:27:14 +0200 Subject: [PATCH] Consolidate Soma color picker runtime --- src/uix/morfo/components/color-picker.ts | 10 + .../color-picker-provider.svelte.ts | 196 ++++++++++-------- 2 files changed, 119 insertions(+), 87 deletions(-) diff --git a/src/uix/morfo/components/color-picker.ts b/src/uix/morfo/components/color-picker.ts index 689ec0f85..f56a98d1a 100644 --- a/src/uix/morfo/components/color-picker.ts +++ b/src/uix/morfo/components/color-picker.ts @@ -102,6 +102,16 @@ export const colorPickerMorfo = { data: [], aria: [] }, + { + name: 'HiddenInput', + kebab: 'hidden-input', + archetype: 'input', + kind: 'public', + defaultElement: 'input', + optional: true, + data: [], + aria: [{ attr: 'aria-hidden', value: v.literal('true') }] + }, { name: 'Content', kebab: 'content', diff --git a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts index aac84f363..58cee14a7 100644 --- a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts +++ b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts @@ -23,6 +23,7 @@ import type { ColorValidator, ColorOnInvalid } from '../color-field/types'; // ── Contract ──────────────────────────────────────────────────────────────── import { colorPickerMorfo } from '../../../morfo/components/color-picker'; +import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte'; const attrs = createAttrs(colorPickerMorfo); @@ -151,6 +152,8 @@ export interface ColorPickerOpts export class ColorPickerProvider { readonly opts: ColorPickerOpts; readonly runtimePart: SomaRuntimePart; + readonly soma: Soma; + readonly runtime: SomaRuntime; static readonly ctx = context('ColorPicker'); static get(): ColorPickerProvider | undefined { return this.ctx.getOr(undefined) as ColorPickerProvider | undefined; @@ -162,7 +165,6 @@ export class ColorPickerProvider { return new ColorPickerProvider(opts); } - readonly soma = Soma.get(); readonly field = FieldProvider.get(); triggerNode = $state(null); @@ -171,14 +173,14 @@ export class ColorPickerProvider { private constructor(opts: ColorPickerOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('provider', { - id: opts.id, - ref: opts.ref, - owner: this, - context: ColorPickerProvider.ctx - }); + this.soma = Soma.require(); + this.runtime = this.soma.runtime(colorPickerMorfo, {}); + this.runtimePart = this.runtime.part('provider', { + id: opts.id, + ref: opts.ref, + owner: this, + context: ColorPickerProvider.ctx + }); // Fire `onValueChangeEnd` only on committed transitions (drag end / swatch // click). `onValueChange` fires on every intermediate change via the @@ -282,7 +284,7 @@ export class ColorPickerProvider { } langs(idlangref: string): string { - return this.soma?.langs.ts(idlangref) ?? idlangref; + return this.soma.langs.ts(idlangref) ?? idlangref; } // ── Root props ────────────────────────────────────────────────────────── @@ -316,15 +318,13 @@ export class ColorPickerLabelProvider { private constructor(opts: ColorPickerLabelOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('label', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (v) => this.provider.setLabelNode(v) - }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('label', { + id: opts.id, + ref: opts.ref, + owner: this, + onRefChange: (v) => this.provider.setLabelNode(v) + }); } readonly onclick = () => { @@ -356,10 +356,12 @@ export class ColorPickerControlProvider { private constructor(opts: ColorPickerControlOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('control', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('control', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly props = $derived.by(() => @@ -386,15 +388,13 @@ export class ColorPickerTriggerProvider { private constructor(opts: ColorPickerTriggerOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('trigger', { - id: opts.id, - ref: opts.ref, - owner: this, - onRefChange: (v) => this.provider.setTriggerNode(v) - }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('trigger', { + id: opts.id, + ref: opts.ref, + owner: this, + onRefChange: (v) => this.provider.setTriggerNode(v) + }); } readonly resolvedLabel = $derived.by( @@ -430,10 +430,12 @@ export class ColorPickerValueSwatchProvider { private constructor(opts: ColorPickerValueSwatchOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('value-swatch', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('value-swatch', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly backgroundStyle = $derived.by( @@ -465,10 +467,12 @@ export class ColorPickerValueTextProvider { private constructor(opts: ColorPickerValueTextOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('value-text', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('value-text', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly displayValue = $derived.by(() => { @@ -495,13 +499,13 @@ export class ColorPickerHiddenInputProvider { readonly provider: ColorPickerProvider; private constructor(opts: ColorPickerHiddenInputOpts) { - // `input` isn't a registered part — we borrow the root part attr so the - // contract stays minimal (and the element is aria-hidden anyway). this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('provider', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('hidden-input', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly props = $derived.by(() => { @@ -550,15 +554,13 @@ export class ColorPickerAreaProvider { private constructor(opts: ColorPickerAreaOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('area', { - id: opts.id, - ref: opts.ref, - owner: this, - context: ColorPickerAreaProvider.ctx - }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('area', { + id: opts.id, + ref: opts.ref, + owner: this, + context: ColorPickerAreaProvider.ctx + }); } readonly xChannel: ColorChannel = $derived.by(() => this.provider.opts.areaChannels.current.x); @@ -666,10 +668,12 @@ export class ColorPickerAreaBackgroundProvider { private constructor(opts: ColorPickerAreaBackgroundOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('area-background', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('area-background', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly backgroundStyle: string = $derived.by( @@ -703,11 +707,13 @@ export class ColorPickerAreaThumbProvider { private constructor(opts: ColorPickerAreaThumbOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('area-thumb', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); this.area = ColorPickerAreaProvider.require(); + this.runtimePart = this.provider.runtime.part('area-thumb', { + id: opts.id, + ref: opts.ref, + owner: this + }); } /** Keyboard-driven Area editing — both axes respond to arrows. */ @@ -849,15 +855,13 @@ export class ColorPickerChannelSliderProvider { private constructor(opts: ColorPickerChannelSliderOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('channel-slider', { - id: opts.id, - ref: opts.ref, - owner: this, - context: ColorPickerChannelSliderProvider.ctx - }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('channel-slider', { + id: opts.id, + ref: opts.ref, + owner: this, + context: ColorPickerChannelSliderProvider.ctx + }); } readonly channel: ColorChannel = $derived.by(() => this.opts.channel.current); @@ -942,10 +946,12 @@ export class ColorPickerChannelSliderTrackProvider { private constructor(opts: ColorPickerChannelSliderTrackOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('channel-slider-track', { id: opts.id, ref: opts.ref, owner: this }); this.slider = ColorPickerChannelSliderProvider.require(); + this.runtimePart = this.slider.provider.runtime.part('channel-slider-track', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly style: string = $derived.by(() => `background: ${this.slider.backgroundStyle};`); @@ -976,11 +982,13 @@ export class ColorPickerChannelSliderThumbProvider { private constructor(opts: ColorPickerChannelSliderThumbOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('channel-slider-thumb', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); this.slider = ColorPickerChannelSliderProvider.require(); + this.runtimePart = this.provider.runtime.part('channel-slider-thumb', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly onkeydown = (e: SomaKeyboardEvent) => { @@ -1048,9 +1056,12 @@ export class ColorPickerTransparencyGridProvider { private constructor(opts: ColorPickerTransparencyGridOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('transparency-grid', { id: opts.id, ref: opts.ref, owner: this }); + const provider = ColorPickerProvider.require(); + this.runtimePart = provider.runtime.part('transparency-grid', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly style: string = $derived.by(() => { @@ -1075,7 +1086,6 @@ export class ColorPickerTransparencyGridProvider { // Swatches — SwatchGroup, SwatchTrigger, Swatch, SwatchIndicator. import { parseColor, hsvToHex } from '../../external/colors'; -import type { SomaRuntimePart } from '../../runtime.svelte'; export interface ColorPickerSwatchGroupOpts extends WithRefOpts, ActiveProps<{ ariaLabel: string | undefined }> {} @@ -1091,10 +1101,12 @@ export class ColorPickerSwatchGroupProvider { private constructor(opts: ColorPickerSwatchGroupOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('swatch-group', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('swatch-group', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly resolvedLabel = $derived.by( @@ -1124,10 +1136,12 @@ export class ColorPickerSwatchTriggerProvider { private constructor(opts: ColorPickerSwatchTriggerOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('swatch-trigger', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('swatch-trigger', { + id: opts.id, + ref: opts.ref, + owner: this + }); } /** `true` when this swatch matches the current value by hex comparison. */ @@ -1173,9 +1187,12 @@ export class ColorPickerSwatchProvider { private constructor(opts: ColorPickerSwatchOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('swatch', { id: opts.id, ref: opts.ref, owner: this }); + const provider = ColorPickerProvider.require(); + this.runtimePart = provider.runtime.part('swatch', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly effectiveColor: string = $derived.by(() => this.opts.color.current ?? ''); @@ -1201,9 +1218,12 @@ export class ColorPickerSwatchIndicatorProvider { private constructor(opts: ColorPickerSwatchIndicatorOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('swatch-indicator', { id: opts.id, ref: opts.ref, owner: this }); + const provider = ColorPickerProvider.require(); + this.runtimePart = provider.runtime.part('swatch-indicator', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly props = $derived.by(() => @@ -1235,10 +1255,12 @@ export class ColorPickerEyeDropperProvider { private constructor(opts: ColorPickerEyeDropperOpts) { this.opts = opts; - this.runtimePart = Soma.require() - .runtime(colorPickerMorfo, {}) - .part('eye-dropper', { id: opts.id, ref: opts.ref, owner: this }); this.provider = ColorPickerProvider.require(); + this.runtimePart = this.provider.runtime.part('eye-dropper', { + id: opts.id, + ref: opts.ref, + owner: this + }); } readonly isSupported: boolean =