@symbiote-native/slider 5.0.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +24 -7
  2. package/build/angular/slider/index.android.js +9 -1
  3. package/build/angular/slider/index.ios.js +10 -2
  4. package/build/angular/slider/shared.d.ts +6 -1
  5. package/build/angular/slider/shared.js +23 -1
  6. package/build/core/index.d.ts +2 -2
  7. package/build/core/render-slider.js +5 -1
  8. package/build/core/render-steps-indicator.js +13 -4
  9. package/build/core/slider-props.js +4 -6
  10. package/build/react/slider/index.ios.js +1 -1
  11. package/build/react/slider/shared.js +11 -4
  12. package/build/register/index.js +5 -2
  13. package/build/solid/index.d.ts +4 -0
  14. package/build/solid/index.js +8 -0
  15. package/build/solid/slider/index.android.d.ts +2 -0
  16. package/build/solid/slider/index.android.js +10 -0
  17. package/build/solid/slider/index.d.ts +1 -0
  18. package/build/solid/slider/index.ios.d.ts +2 -0
  19. package/build/solid/slider/index.ios.js +11 -0
  20. package/build/solid/slider/index.js +4 -0
  21. package/build/solid/slider/shared.d.ts +12 -0
  22. package/build/solid/slider/shared.js +296 -0
  23. package/build/svelte/index.d.ts +3 -0
  24. package/build/svelte/index.js +9 -0
  25. package/build/svelte/slider/index.svelte +326 -0
  26. package/build/svelte/slider/index.svelte.d.ts +5 -0
  27. package/build/svelte/slider/slider-platform.android.d.ts +2 -0
  28. package/build/svelte/slider/slider-platform.android.js +11 -0
  29. package/build/svelte/slider/slider-platform.d.ts +1 -0
  30. package/build/svelte/slider/slider-platform.ios.d.ts +2 -0
  31. package/build/svelte/slider/slider-platform.ios.js +12 -0
  32. package/build/svelte/slider/slider-platform.js +5 -0
  33. package/build/svelte/slider/slider-props.d.ts +7 -0
  34. package/build/svelte/slider/slider-props.js +1 -0
  35. package/build/vue/slider/index.ios.js +1 -1
  36. package/build/vue/slider/shared.js +11 -5
  37. package/build-ngc/angular/slider/index.android.js +18 -4
  38. package/build-ngc/angular/slider/index.android.js.map +1 -1
  39. package/build-ngc/angular/slider/index.ios.js +19 -5
  40. package/build-ngc/angular/slider/index.ios.js.map +1 -1
  41. package/build-ngc/angular/slider/shared.d.ts +6 -1
  42. package/build-ngc/angular/slider/shared.js +26 -4
  43. package/build-ngc/angular/slider/shared.js.map +1 -1
  44. package/build-ngc/core/index.d.ts +2 -2
  45. package/build-ngc/core/index.js.map +1 -1
  46. package/build-ngc/core/render-slider.js +5 -1
  47. package/build-ngc/core/render-slider.js.map +1 -1
  48. package/build-ngc/core/render-steps-indicator.js +13 -4
  49. package/build-ngc/core/render-steps-indicator.js.map +1 -1
  50. package/build-ngc/core/slider-props.js +4 -6
  51. package/build-ngc/core/slider-props.js.map +1 -1
  52. package/build-ngc/core/slider-state.js.map +1 -1
  53. package/build-ngc/register/index.js +5 -2
  54. package/build-ngc/register/index.js.map +1 -1
  55. package/package.json +48 -15
  56. package/src/angular/slider/index.android.ts +9 -1
  57. package/src/angular/slider/index.ios.ts +13 -2
  58. package/src/angular/slider/shared.ts +55 -10
  59. package/src/core/index.ts +9 -2
  60. package/src/core/render-slider.ts +24 -6
  61. package/src/core/render-steps-indicator.ts +38 -10
  62. package/src/core/slider-props.ts +17 -17
  63. package/src/core/slider-state.ts +16 -5
  64. package/src/react/slider/index.ios.ts +4 -1
  65. package/src/react/slider/shared.ts +66 -15
  66. package/src/register/index.ts +9 -2
  67. package/src/solid/index.ts +12 -0
  68. package/src/solid/slider/index.android.ts +15 -0
  69. package/src/solid/slider/index.ios.ts +19 -0
  70. package/src/solid/slider/index.ts +5 -0
  71. package/src/solid/slider/shared.ts +468 -0
  72. package/src/svelte/index.ts +12 -0
  73. package/src/svelte/slider/index.svelte +326 -0
  74. package/src/svelte/slider/slider-platform.android.ts +14 -0
  75. package/src/svelte/slider/slider-platform.ios.ts +18 -0
  76. package/src/svelte/slider/slider-platform.ts +5 -0
  77. package/src/svelte/slider/slider-props.ts +17 -0
  78. package/src/vue/slider/index.ios.ts +4 -1
  79. package/src/vue/slider/shared.ts +72 -19
@@ -0,0 +1,326 @@
1
+ <script lang="ts" module>
2
+ // Slider, the Svelte lifecycle half. Logic (value/limit/disabled folds, step-option layout) and
3
+ // the native render live in @symbiote-native/slider core, shared with every adapter; Svelte
4
+ // supplies reactivity (`$state`/`$derived`) and the descriptor bridge for the native
5
+ // `RNCSlider` leaf. The native view carries no symbiote metadata - the engine derives its
6
+ // events/color/image processors from the library's ViewConfig at runtime, registered by the
7
+ // side-effect import in ../index (not here, so this module and its tests stay free of the
8
+ // third-party spec).
9
+ //
10
+ // The leaf is bridged rather than written as `<RNCSlider>` because 'RNCSlider' is neither
11
+ // hyphenated nor lowercase-leading, so Svelte's compiler parses a literal tag as a COMPONENT
12
+ // reference, not an element (confirmed against the compiled output). The descriptor bridge
13
+ // (`native-view-bridge`'s `mountDescriptorChildren`, svelte-adapter-dom-shim skill §19)
14
+ // sidesteps this by calling `document.createElement(child.type)` programmatically, which the
15
+ // compiler never inspects. Imported from `native-view-bridge`, not the package's main barrel -
16
+ // the main barrel pulls in the whole `.svelte` module graph, fatal under vitest's plain
17
+ // svelte-plugin-free transform (this component's own smoke test).
18
+ //
19
+ // The custom-marker overlay's thumb-image cell uses the raw `symbiote-image` host tag rather
20
+ // than the framework's `Image` component: core's `render-steps-indicator.ts` paints the same
21
+ // default-overlay thumb with a bare `el('symbiote-image', {source, style})` Descriptor, and
22
+ // importing another `.svelte` component here would reintroduce the same main-barrel problem
23
+ // one level down.
24
+ import type { ISliderProps } from './slider-props';
25
+
26
+ export type { ISliderProps };
27
+ </script>
28
+
29
+ <script lang="ts">
30
+ import { dlog, type ISymbioteEvent } from '@symbiote-native/engine';
31
+ import {
32
+ mountDescriptorChildren,
33
+ createDescriptorChildrenSync,
34
+ type IDescriptorChildrenMount,
35
+ type ShimElement,
36
+ } from '@symbiote-native/svelte/native-view-bridge';
37
+ import type { IDescriptorChild } from '@symbiote-native/components';
38
+ import {
39
+ sanitizeSliderValue,
40
+ resolveSliderDisabled,
41
+ resolveSliderAccessibilityState,
42
+ resolveSliderLowerLimit,
43
+ resolveSliderUpperLimit,
44
+ valueFromSliderEvent,
45
+ shouldRenderStepsIndicator,
46
+ resolveThumbTintColor,
47
+ shouldPassNativeThumbImage,
48
+ isInvalidLimitConfig,
49
+ computeStepOptions,
50
+ orderStepOptions,
51
+ stepNumberFontSize,
52
+ renderSlider,
53
+ renderSliderNative,
54
+ resolveSliderWrapperStyle,
55
+ resolveStepsContainerStyle,
56
+ renderStepsIndicator,
57
+ STEP_INDICATOR_ELEMENT_STYLE,
58
+ TRACK_MARK_CONTAINER_STYLE,
59
+ THUMB_IMAGE_CONTAINER_STYLE,
60
+ THUMB_IMAGE_STYLE,
61
+ STEP_NUMBER_CONTAINER_STYLE,
62
+ SLIDER_DEFAULT_MINIMUM_VALUE,
63
+ SLIDER_DEFAULT_MAXIMUM_VALUE,
64
+ SLIDER_DEFAULT_STEP,
65
+ SLIDER_ON_CHANGE,
66
+ SLIDER_ON_VALUE_CHANGE,
67
+ SLIDER_ON_SLIDING_START,
68
+ SLIDER_ON_SLIDING_COMPLETE,
69
+ SLIDER_ON_ACCESSIBILITY_ACTION,
70
+ type ISliderViewProps,
71
+ } from '../../core';
72
+ import { PLATFORM } from './slider-platform';
73
+
74
+ function isRecord(value: unknown): value is Record<string, unknown> {
75
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
76
+ }
77
+
78
+ let {
79
+ value = $bindable(),
80
+ minimumValue,
81
+ maximumValue,
82
+ step,
83
+ lowerLimit,
84
+ upperLimit,
85
+ disabled,
86
+ inverted = false,
87
+ thumbTintColor,
88
+ thumbImage,
89
+ accessibilityState,
90
+ renderStepNumber,
91
+ style,
92
+ class: className,
93
+ stepMarker,
94
+ onValueChange,
95
+ onSlidingStart,
96
+ onSlidingComplete,
97
+ onAccessibilityAction,
98
+ ...passthrough
99
+ }: ISliderProps = $props();
100
+
101
+ // Last value native reported, kept only to mark the active step - not the controlled value (the
102
+ // slider is uncontrolled during a drag, unlike Switch: native owns the thumb position). A plain
103
+ // number, not an engine-node reference, so `$state` (not `.raw`) is fine here; only `hostShim*`
104
+ // below needs `.raw`'s no-deep-proxy guarantee for the engine's WeakMap-keyed identity lookups.
105
+ let reportedValue = $state<number | undefined>(undefined);
106
+ // The measured wrapper width the step indicator lays out against; 0 until the first layout.
107
+ let width = $state(0);
108
+
109
+ function handleValueChange(event: ISymbioteEvent): void {
110
+ const next = valueFromSliderEvent(event);
111
+ if (next === undefined) return;
112
+ reportedValue = next;
113
+ // Additive `bind:value` sugar (svelte-adapter-dom-shim skill): reassigning a `$bindable()`
114
+ // prop pushes it to the caller's bound variable, on the same report path as `onValueChange`
115
+ // below (matches the Vue adapter's `emitModelUpdate` timing) - never replacing the
116
+ // `value`/`onValueChange` contract.
117
+ value = next;
118
+ onValueChange?.(next);
119
+ }
120
+ function handleSlidingStart(event: ISymbioteEvent): void {
121
+ const next = valueFromSliderEvent(event);
122
+ if (next !== undefined) onSlidingStart?.(next);
123
+ }
124
+ function handleSlidingComplete(event: ISymbioteEvent): void {
125
+ const next = valueFromSliderEvent(event);
126
+ if (next !== undefined) onSlidingComplete?.(next);
127
+ }
128
+ function handleAccessibilityAction(event: ISymbioteEvent): void {
129
+ onAccessibilityAction?.(event);
130
+ }
131
+ function handleLayout(event: ISymbioteEvent): void {
132
+ const layout = event.nativeEvent.layout;
133
+ if (isRecord(layout) && typeof layout.width === 'number')
134
+ width = layout.width;
135
+ }
136
+
137
+ const minimum = $derived(minimumValue ?? SLIDER_DEFAULT_MINIMUM_VALUE);
138
+ const maximum = $derived(maximumValue ?? SLIDER_DEFAULT_MAXIMUM_VALUE);
139
+ const resolvedStep = $derived(step ?? SLIDER_DEFAULT_STEP);
140
+ const lower = $derived(resolveSliderLowerLimit(lowerLimit));
141
+ const upper = $derived(resolveSliderUpperLimit(upperLimit));
142
+
143
+ $effect(() => {
144
+ if (isInvalidLimitConfig(lower, upper)) {
145
+ dlog('Slider: lowerLimit must be smaller than upperLimit');
146
+ }
147
+ });
148
+
149
+ const hasStepMarker = $derived(stepMarker !== undefined);
150
+ const hasThumbImage = $derived(thumbImage !== undefined);
151
+ const showSteps = $derived(
152
+ shouldRenderStepsIndicator(hasStepMarker, renderStepNumber),
153
+ );
154
+
155
+ // The native view gets a thumbImage only when there is one AND no custom marker (which draws
156
+ // its own thumb), matching the library. Passed raw — the engine runs the image processor
157
+ // derived from RNCSlider's ViewConfig (same path as the color tints).
158
+ const nativeThumbImage = $derived(
159
+ shouldPassNativeThumbImage(hasStepMarker, hasThumbImage)
160
+ ? thumbImage
161
+ : undefined,
162
+ );
163
+
164
+ const view = $derived<ISliderViewProps>({
165
+ value: sanitizeSliderValue(value),
166
+ minimumValue: minimum,
167
+ maximumValue: maximum,
168
+ step: resolvedStep,
169
+ lowerLimit: lower,
170
+ upperLimit: upper,
171
+ disabled: resolveSliderDisabled(disabled, accessibilityState),
172
+ inverted,
173
+ thumbTintColor: resolveThumbTintColor(
174
+ thumbTintColor,
175
+ hasStepMarker,
176
+ hasThumbImage,
177
+ ),
178
+ thumbImage: nativeThumbImage,
179
+ accessibilityState: resolveSliderAccessibilityState(
180
+ disabled,
181
+ accessibilityState,
182
+ ),
183
+ width,
184
+ style,
185
+ passthrough: {
186
+ ...passthrough,
187
+ [SLIDER_ON_CHANGE]: handleValueChange,
188
+ [SLIDER_ON_VALUE_CHANGE]: handleValueChange,
189
+ [SLIDER_ON_SLIDING_START]: handleSlidingStart,
190
+ [SLIDER_ON_SLIDING_COMPLETE]: handleSlidingComplete,
191
+ [SLIDER_ON_ACCESSIBILITY_ACTION]: handleAccessibilityAction,
192
+ },
193
+ });
194
+
195
+ const options = $derived(
196
+ computeStepOptions(minimum, maximum, resolvedStep, PLATFORM.stepResolution),
197
+ );
198
+ const currentValue = $derived(reportedValue ?? view.value ?? minimum);
199
+
200
+ // --- No custom marker: renderSlider() is the single source of truth (wrapper + optional
201
+ // default steps overlay + the native leaf), ALL pure Descriptor — mounted via the bridge.
202
+ // Unlike every fixed-shape component elsewhere in this adapter, this Descriptor's own shape
203
+ // genuinely varies with `renderStepNumber` (1 child with no overlay, 2 with one), so the sync
204
+ // below tolerates a child-count change with a clean rebuild instead of assuming constant shape.
205
+ const defaultDescriptor = $derived(
206
+ hasStepMarker
207
+ ? undefined
208
+ : renderSlider(
209
+ view,
210
+ PLATFORM,
211
+ showSteps
212
+ ? {
213
+ steps: renderStepsIndicator({
214
+ options,
215
+ currentValue,
216
+ width,
217
+ renderStepNumber: renderStepNumber === true,
218
+ thumbImage,
219
+ inverted,
220
+ platform: PLATFORM,
221
+ }),
222
+ onLayout: handleLayout,
223
+ }
224
+ : { onLayout: handleLayout },
225
+ ),
226
+ );
227
+
228
+ let hostShimDefault = $state.raw<ShimElement | null>(null);
229
+ let mountedDefault: IDescriptorChildrenMount | undefined;
230
+ let mountedDefaultCount = -1;
231
+
232
+ function syncDefaultChildren(children: IDescriptorChild[]): void {
233
+ if (hostShimDefault === null) return;
234
+ if (
235
+ mountedDefault === undefined ||
236
+ mountedDefaultCount !== children.length
237
+ ) {
238
+ for (const child of hostShimDefault.children.slice())
239
+ hostShimDefault.removeChild(child);
240
+ mountedDefault = mountDescriptorChildren(hostShimDefault, children);
241
+ mountedDefaultCount = children.length;
242
+ } else {
243
+ mountedDefault.update(children);
244
+ }
245
+ }
246
+
247
+ $effect(() => {
248
+ const descriptor = defaultDescriptor;
249
+ if (descriptor === undefined) return;
250
+ syncDefaultChildren(descriptor.children);
251
+ });
252
+
253
+ // --- Custom marker: the overlay is hand-authored live template content below (it hosts the
254
+ // caller's `stepMarker` Snippet, a live child no Descriptor can carry — component-render-fn-
255
+ // boundary rule). Only the native leaf (always exactly one, constant shape) is bridged.
256
+ const fontSize = $derived(stepNumberFontSize(options.length));
257
+ const orderedOptions = $derived(orderStepOptions(options, inverted));
258
+ const minOption = $derived(options[0]);
259
+ const maxOption = $derived(options[options.length - 1]);
260
+ const nativeLeafDescriptor = $derived(renderSliderNative(view, PLATFORM));
261
+ const markerWrapperBag = $derived({
262
+ style: resolveSliderWrapperStyle(style, PLATFORM),
263
+ class: className,
264
+ onLayout: handleLayout,
265
+ });
266
+
267
+ let hostShimMarker = $state.raw<ShimElement | null>(null);
268
+ const syncMarkerLeaf = createDescriptorChildrenSync();
269
+
270
+ $effect(() => {
271
+ if (!hasStepMarker) return;
272
+ syncMarkerLeaf(hostShimMarker, [nativeLeafDescriptor]);
273
+ });
274
+ </script>
275
+
276
+ {#if hasStepMarker}
277
+ <symbiote-view p={markerWrapperBag} bind:this={hostShimMarker}>
278
+ <symbiote-view
279
+ p={{
280
+ pointerEvents: 'none',
281
+ testID: 'StepsIndicator-Container',
282
+ style: resolveStepsContainerStyle(width, PLATFORM),
283
+ }}
284
+ >
285
+ {#each orderedOptions as optionValue, index (optionValue)}
286
+ <symbiote-view p={{ style: STEP_INDICATOR_ELEMENT_STYLE }}>
287
+ <symbiote-view p={{ style: TRACK_MARK_CONTAINER_STYLE }}>
288
+ {@render stepMarker?.({
289
+ stepMarked: optionValue === currentValue,
290
+ currentValue,
291
+ index,
292
+ min: minOption,
293
+ max: maxOption,
294
+ })}
295
+ {#if thumbImage !== undefined && optionValue === currentValue}
296
+ <symbiote-view
297
+ p={{
298
+ style: THUMB_IMAGE_CONTAINER_STYLE,
299
+ testID: 'sliderTrackMark-thumbImage',
300
+ }}
301
+ >
302
+ <symbiote-image
303
+ p={{ source: thumbImage, style: THUMB_IMAGE_STYLE }}
304
+ />
305
+ </symbiote-view>
306
+ {/if}
307
+ </symbiote-view>
308
+ {#if renderStepNumber}
309
+ <symbiote-view p={{ style: STEP_NUMBER_CONTAINER_STYLE }}>
310
+ <symbiote-text
311
+ p={{ testID: `${index}th-step`, style: { fontSize } }}
312
+ >
313
+ {String(optionValue)}
314
+ </symbiote-text>
315
+ </symbiote-view>
316
+ {/if}
317
+ </symbiote-view>
318
+ {/each}
319
+ </symbiote-view>
320
+ </symbiote-view>
321
+ {:else}
322
+ <symbiote-view
323
+ p={{ ...defaultDescriptor?.props, class: className }}
324
+ bind:this={hostShimDefault}
325
+ />
326
+ {/if}
@@ -0,0 +1,14 @@
1
+ // Slider on Android: the native view sizes itself (no default height), lays out implicit steps
2
+ // at 128-point resolution, and keeps the step row at the top. Mirrors
3
+ // packages/slider/src/vue/slider/index.android.ts exactly, one level down (platform CONSTANT
4
+ // here — see index.svelte's header).
5
+ import { SLIDER_STEP_RESOLUTION_ANDROID } from '../../core';
6
+ import type { ISliderPlatform } from '../../core';
7
+
8
+ const ANDROID_STEPS_CONTAINER_TOP = 0;
9
+
10
+ export const PLATFORM: ISliderPlatform = {
11
+ defaultStyle: {},
12
+ stepResolution: SLIDER_STEP_RESOLUTION_ANDROID,
13
+ stepsContainerTop: ANDROID_STEPS_CONTAINER_TOP,
14
+ };
@@ -0,0 +1,18 @@
1
+ // Slider on iOS: the native view has an intrinsic 40pt height (the wrapper applies it as the
2
+ // default), lays out implicit steps at 1000-point resolution, and nudges the step row down 10pt.
3
+ // Also the base (./slider-platform re-exports it) for headless. Mirrors
4
+ // packages/slider/src/vue/slider/index.ios.ts exactly, one level down (platform CONSTANT here,
5
+ // not a per-platform component — see index.svelte's header).
6
+ import {
7
+ SLIDER_IOS_DEFAULT_HEIGHT,
8
+ SLIDER_STEP_RESOLUTION_IOS,
9
+ } from '../../core';
10
+ import type { ISliderPlatform } from '../../core';
11
+
12
+ const IOS_STEPS_CONTAINER_TOP = 10;
13
+
14
+ export const PLATFORM: ISliderPlatform = {
15
+ defaultStyle: { height: SLIDER_IOS_DEFAULT_HEIGHT },
16
+ stepResolution: SLIDER_STEP_RESOLUTION_IOS,
17
+ stepsContainerTop: IOS_STEPS_CONTAINER_TOP,
18
+ };
@@ -0,0 +1,5 @@
1
+ // Base / default: re-exports the iOS build. Metro overrides this with slider-platform.ios.ts /
2
+ // slider-platform.android.ts on a real host; under tsc/vitest the host config resolves here.
3
+ // Filename is the selector, no Platform.OS read — same convention as adapters/svelte's own
4
+ // switch-platform.ts.
5
+ export * from './slider-platform.ios';
@@ -0,0 +1,17 @@
1
+ // `ISliderProps`'s canonical home: reuses the shared @symbiote-native/slider core base verbatim
2
+ // (already framework-agnostic - no children, no ref-as-prop) and adds two Svelte-only fields,
3
+ // per CLAUDE.md's <prop_types_split_agnostic_vs_per_adapter>:
4
+ // - `class`, matching every component in this adapter (View/Text/Switch/…).
5
+ // - `stepMarker`, a Snippet<[IStepMarkerProps]> — the framework-specific render-callback field
6
+ // Vue takes as a `#stepMarker` scoped slot, React as `StepMarker: FC<IStepMarkerProps>`.
7
+ import type { Snippet } from 'svelte';
8
+ import type { IClassNameValue } from '@symbiote-native/engine';
9
+ import type {
10
+ ISliderProps as ISliderBaseProps,
11
+ IStepMarkerProps,
12
+ } from '../../core';
13
+
14
+ export type ISliderProps = ISliderBaseProps & {
15
+ class?: IClassNameValue;
16
+ stepMarker?: Snippet<[IStepMarkerProps]>;
17
+ };
@@ -3,7 +3,10 @@
3
3
  // Also the base (./index re-exports it) for headless. Mirrors the library's iOS branches.
4
4
 
5
5
  import { createSlider } from './shared';
6
- import { SLIDER_IOS_DEFAULT_HEIGHT, SLIDER_STEP_RESOLUTION_IOS } from '../../core';
6
+ import {
7
+ SLIDER_IOS_DEFAULT_HEIGHT,
8
+ SLIDER_STEP_RESOLUTION_IOS,
9
+ } from '../../core';
7
10
 
8
11
  const IOS_STEPS_CONTAINER_TOP = 10;
9
12
 
@@ -7,7 +7,13 @@
7
7
  // the side-effect import in ../register (pulled in by the package barrel, NOT here, so this module
8
8
  // and its tests stay free of the third-party spec).
9
9
 
10
- import { defineComponent, h, ref, type SetupContext, type VNode } from '@vue/runtime-core';
10
+ import {
11
+ defineComponent,
12
+ h,
13
+ ref,
14
+ type SetupContext,
15
+ type VNode,
16
+ } from '@vue/runtime-core';
11
17
  import {
12
18
  descriptorToVue,
13
19
  normalizeVueAttrs,
@@ -16,7 +22,11 @@ import {
16
22
  Image,
17
23
  type IImageSourceProp,
18
24
  } from '@symbiote-native/vue';
19
- import { dlog, type IClassNameValue, type ISymbioteEvent } from '@symbiote-native/engine';
25
+ import {
26
+ dlog,
27
+ type IClassNameValue,
28
+ type ISymbioteEvent,
29
+ } from '@symbiote-native/engine';
20
30
  import {
21
31
  sanitizeSliderValue,
22
32
  resolveSliderDisabled,
@@ -60,7 +70,10 @@ import {
60
70
  // the class-routing note on HANDLED_ATTRS below.
61
71
  export type ISliderProps = Omit<
62
72
  ISliderBaseProps,
63
- 'onValueChange' | 'onSlidingStart' | 'onSlidingComplete' | 'onAccessibilityAction'
73
+ | 'onValueChange'
74
+ | 'onSlidingStart'
75
+ | 'onSlidingComplete'
76
+ | 'onAccessibilityAction'
64
77
  > & {
65
78
  modelValue?: number;
66
79
  class?: IClassNameValue;
@@ -92,14 +105,21 @@ function asBoolean(value: unknown): boolean | undefined {
92
105
  }
93
106
 
94
107
  function isImageSourceProp(value: unknown): value is IImageSourceProp {
95
- return typeof value === 'number' || (typeof value === 'object' && value !== null);
108
+ return (
109
+ typeof value === 'number' || (typeof value === 'object' && value !== null)
110
+ );
96
111
  }
97
112
 
98
113
  // accessibilityState arrives untyped; narrow its disabled flag for the fold while preserving the
99
114
  // other entries at runtime (the resolve spread carries them onto the native node).
100
- function asAccessibilityState(value: unknown): ISliderAccessibilityState | undefined {
115
+ function asAccessibilityState(
116
+ value: unknown,
117
+ ): ISliderAccessibilityState | undefined {
101
118
  if (!isRecord(value)) return undefined;
102
- return { ...value, disabled: typeof value.disabled === 'boolean' ? value.disabled : undefined };
119
+ return {
120
+ ...value,
121
+ disabled: typeof value.disabled === 'boolean' ? value.disabled : undefined,
122
+ };
103
123
  }
104
124
 
105
125
  // The props/handlers the lifecycle consumes itself; everything else (the track tints + images,
@@ -180,14 +200,17 @@ export function createSlider(platform: ISliderPlatform) {
180
200
  };
181
201
  const handleLayout = (event: ISymbioteEvent): void => {
182
202
  const layout = event.nativeEvent.layout;
183
- if (isRecord(layout) && typeof layout.width === 'number') width.value = layout.width;
203
+ if (isRecord(layout) && typeof layout.width === 'number')
204
+ width.value = layout.width;
184
205
  };
185
206
 
186
207
  return () => {
187
208
  const attrs = normalizeVueAttrs(rawAttrs);
188
209
 
189
- const minimumValue = asNumber(attrs.minimumValue) ?? SLIDER_DEFAULT_MINIMUM_VALUE;
190
- const maximumValue = asNumber(attrs.maximumValue) ?? SLIDER_DEFAULT_MAXIMUM_VALUE;
210
+ const minimumValue =
211
+ asNumber(attrs.minimumValue) ?? SLIDER_DEFAULT_MINIMUM_VALUE;
212
+ const maximumValue =
213
+ asNumber(attrs.maximumValue) ?? SLIDER_DEFAULT_MAXIMUM_VALUE;
191
214
  const step = asNumber(attrs.step) ?? SLIDER_DEFAULT_STEP;
192
215
  const lowerLimit = resolveSliderLowerLimit(asNumber(attrs.lowerLimit));
193
216
  const upperLimit = resolveSliderUpperLimit(asNumber(attrs.upperLimit));
@@ -195,19 +218,27 @@ export function createSlider(platform: ISliderPlatform) {
195
218
  dlog('Slider: lowerLimit must be smaller than upperLimit');
196
219
  }
197
220
  const disabledAttr = asBoolean(attrs.disabled);
198
- const accessibilityState = asAccessibilityState(attrs.accessibilityState);
221
+ const accessibilityState = asAccessibilityState(
222
+ attrs.accessibilityState,
223
+ );
199
224
  const inverted = asBoolean(attrs.inverted) ?? false;
200
225
 
201
226
  const hasStepMarker = slots.stepMarker !== undefined;
202
227
  const renderStepNumber = asBoolean(attrs.renderStepNumber);
203
228
  const hasThumbImage = attrs.thumbImage !== undefined;
204
- const showSteps = shouldRenderStepsIndicator(hasStepMarker, renderStepNumber);
229
+ const showSteps = shouldRenderStepsIndicator(
230
+ hasStepMarker,
231
+ renderStepNumber,
232
+ );
205
233
 
206
234
  // The native view gets a thumbImage only when there is one AND no custom marker (which
207
235
  // draws its own thumb), matching the library. We pass the source raw — the engine runs the
208
236
  // image processor derived from RNCSlider's ViewConfig (same path as the color tints), so
209
237
  // unlike the plain-RN library wrapper we do NOT pre-call Image.resolveAssetSource here.
210
- const nativeThumbImage = shouldPassNativeThumbImage(hasStepMarker, hasThumbImage)
238
+ const nativeThumbImage = shouldPassNativeThumbImage(
239
+ hasStepMarker,
240
+ hasThumbImage,
241
+ )
211
242
  ? attrs.thumbImage
212
243
  : undefined;
213
244
 
@@ -220,9 +251,16 @@ export function createSlider(platform: ISliderPlatform) {
220
251
  upperLimit,
221
252
  disabled: resolveSliderDisabled(disabledAttr, accessibilityState),
222
253
  inverted,
223
- thumbTintColor: resolveThumbTintColor(attrs.thumbTintColor, hasStepMarker, hasThumbImage),
254
+ thumbTintColor: resolveThumbTintColor(
255
+ attrs.thumbTintColor,
256
+ hasStepMarker,
257
+ hasThumbImage,
258
+ ),
224
259
  thumbImage: nativeThumbImage,
225
- accessibilityState: resolveSliderAccessibilityState(disabledAttr, accessibilityState),
260
+ accessibilityState: resolveSliderAccessibilityState(
261
+ disabledAttr,
262
+ accessibilityState,
263
+ ),
226
264
  width: width.value,
227
265
  style: attrs.style,
228
266
  passthrough: {
@@ -314,7 +352,10 @@ export function createSlider(platform: ISliderPlatform) {
314
352
 
315
353
  // The wrapper style for the steps path mirrors renderSlider's wrapper (kept here because the
316
354
  // custom-marker path builds the wrapper itself rather than going through renderSlider).
317
- function resolveStepsWrapperStyle(style: unknown, platform: ISliderPlatform): unknown {
355
+ function resolveStepsWrapperStyle(
356
+ style: unknown,
357
+ platform: ISliderPlatform,
358
+ ): unknown {
318
359
  return [platform.defaultStyle, style, { justifyContent: 'center' }];
319
360
  }
320
361
 
@@ -348,12 +389,16 @@ function renderCustomStepsOverlay(params: ICustomStepsParams): VNode {
348
389
  min,
349
390
  max,
350
391
  });
351
- if (marker !== undefined) trackChildren.push(h('symbiote-view', {}, marker));
392
+ if (marker !== undefined)
393
+ trackChildren.push(h('symbiote-view', {}, marker));
352
394
  if (isImageSourceProp(params.thumbImage) && value === params.currentValue) {
353
395
  trackChildren.push(
354
396
  h(
355
397
  'symbiote-view',
356
- { style: THUMB_IMAGE_CONTAINER_STYLE, testID: 'sliderTrackMark-thumbImage' },
398
+ {
399
+ style: THUMB_IMAGE_CONTAINER_STYLE,
400
+ testID: 'sliderTrackMark-thumbImage',
401
+ },
357
402
  [h(Image, { source: params.thumbImage, style: THUMB_IMAGE_STYLE })],
358
403
  ),
359
404
  );
@@ -364,11 +409,19 @@ function renderCustomStepsOverlay(params: ICustomStepsParams): VNode {
364
409
  if (params.renderStepNumber) {
365
410
  cellChildren.push(
366
411
  h('symbiote-view', { style: STEP_NUMBER_CONTAINER_STYLE }, [
367
- h('symbiote-text', { testID: `${index}th-step`, style: { fontSize } }, String(value)),
412
+ h(
413
+ 'symbiote-text',
414
+ { testID: `${index}th-step`, style: { fontSize } },
415
+ String(value),
416
+ ),
368
417
  ]),
369
418
  );
370
419
  }
371
- return h('symbiote-view', { style: STEP_INDICATOR_ELEMENT_STYLE }, cellChildren);
420
+ return h(
421
+ 'symbiote-view',
422
+ { style: STEP_INDICATOR_ELEMENT_STYLE },
423
+ cellChildren,
424
+ );
372
425
  });
373
426
  return h(
374
427
  'symbiote-view',