@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.
- package/README.md +24 -7
- package/build/angular/slider/index.android.js +9 -1
- package/build/angular/slider/index.ios.js +10 -2
- package/build/angular/slider/shared.d.ts +6 -1
- package/build/angular/slider/shared.js +23 -1
- package/build/core/index.d.ts +2 -2
- package/build/core/render-slider.js +5 -1
- package/build/core/render-steps-indicator.js +13 -4
- package/build/core/slider-props.js +4 -6
- package/build/react/slider/index.ios.js +1 -1
- package/build/react/slider/shared.js +11 -4
- package/build/register/index.js +5 -2
- package/build/solid/index.d.ts +4 -0
- package/build/solid/index.js +8 -0
- package/build/solid/slider/index.android.d.ts +2 -0
- package/build/solid/slider/index.android.js +10 -0
- package/build/solid/slider/index.d.ts +1 -0
- package/build/solid/slider/index.ios.d.ts +2 -0
- package/build/solid/slider/index.ios.js +11 -0
- package/build/solid/slider/index.js +4 -0
- package/build/solid/slider/shared.d.ts +12 -0
- package/build/solid/slider/shared.js +296 -0
- package/build/svelte/index.d.ts +3 -0
- package/build/svelte/index.js +9 -0
- package/build/svelte/slider/index.svelte +326 -0
- package/build/svelte/slider/index.svelte.d.ts +5 -0
- package/build/svelte/slider/slider-platform.android.d.ts +2 -0
- package/build/svelte/slider/slider-platform.android.js +11 -0
- package/build/svelte/slider/slider-platform.d.ts +1 -0
- package/build/svelte/slider/slider-platform.ios.d.ts +2 -0
- package/build/svelte/slider/slider-platform.ios.js +12 -0
- package/build/svelte/slider/slider-platform.js +5 -0
- package/build/svelte/slider/slider-props.d.ts +7 -0
- package/build/svelte/slider/slider-props.js +1 -0
- package/build/vue/slider/index.ios.js +1 -1
- package/build/vue/slider/shared.js +11 -5
- package/build-ngc/angular/slider/index.android.js +18 -4
- package/build-ngc/angular/slider/index.android.js.map +1 -1
- package/build-ngc/angular/slider/index.ios.js +19 -5
- package/build-ngc/angular/slider/index.ios.js.map +1 -1
- package/build-ngc/angular/slider/shared.d.ts +6 -1
- package/build-ngc/angular/slider/shared.js +26 -4
- package/build-ngc/angular/slider/shared.js.map +1 -1
- package/build-ngc/core/index.d.ts +2 -2
- package/build-ngc/core/index.js.map +1 -1
- package/build-ngc/core/render-slider.js +5 -1
- package/build-ngc/core/render-slider.js.map +1 -1
- package/build-ngc/core/render-steps-indicator.js +13 -4
- package/build-ngc/core/render-steps-indicator.js.map +1 -1
- package/build-ngc/core/slider-props.js +4 -6
- package/build-ngc/core/slider-props.js.map +1 -1
- package/build-ngc/core/slider-state.js.map +1 -1
- package/build-ngc/register/index.js +5 -2
- package/build-ngc/register/index.js.map +1 -1
- package/package.json +48 -15
- package/src/angular/slider/index.android.ts +9 -1
- package/src/angular/slider/index.ios.ts +13 -2
- package/src/angular/slider/shared.ts +55 -10
- package/src/core/index.ts +9 -2
- package/src/core/render-slider.ts +24 -6
- package/src/core/render-steps-indicator.ts +38 -10
- package/src/core/slider-props.ts +17 -17
- package/src/core/slider-state.ts +16 -5
- package/src/react/slider/index.ios.ts +4 -1
- package/src/react/slider/shared.ts +66 -15
- package/src/register/index.ts +9 -2
- package/src/solid/index.ts +12 -0
- package/src/solid/slider/index.android.ts +15 -0
- package/src/solid/slider/index.ios.ts +19 -0
- package/src/solid/slider/index.ts +5 -0
- package/src/solid/slider/shared.ts +468 -0
- package/src/svelte/index.ts +12 -0
- package/src/svelte/slider/index.svelte +326 -0
- package/src/svelte/slider/slider-platform.android.ts +14 -0
- package/src/svelte/slider/slider-platform.ios.ts +18 -0
- package/src/svelte/slider/slider-platform.ts +5 -0
- package/src/svelte/slider/slider-props.ts +17 -0
- package/src/vue/slider/index.ios.ts +4 -1
- package/src/vue/slider/shared.ts +72 -19
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Slider on Android: the native view sizes itself (no default height), lays out implicit steps at
|
|
2
2
|
// 128-point resolution, and keeps the step row at the top. Mirrors the library's Android branches.
|
|
3
3
|
|
|
4
|
-
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
|
|
5
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
6
|
import { DescriptorOutlet } from '@symbiote-native/angular';
|
|
6
7
|
import { SliderBase } from './shared';
|
|
7
8
|
import { SLIDER_STEP_RESOLUTION_ANDROID } from '../../core';
|
|
@@ -14,6 +15,13 @@ const ANDROID_STEPS_CONTAINER_TOP = 0;
|
|
|
14
15
|
standalone: true,
|
|
15
16
|
imports: [DescriptorOutlet],
|
|
16
17
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
18
|
+
providers: [
|
|
19
|
+
{
|
|
20
|
+
provide: NG_VALUE_ACCESSOR,
|
|
21
|
+
useExisting: forwardRef(() => Slider),
|
|
22
|
+
multi: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
17
25
|
template: `<symbiote-descriptor-outlet [node]="descriptor" />`,
|
|
18
26
|
})
|
|
19
27
|
export class Slider extends SliderBase {
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
// default), lays out implicit steps at 1000-point resolution, and nudges the step row down 10pt.
|
|
3
3
|
// Also the base (./index re-exports it) for headless. Mirrors the library's iOS branches.
|
|
4
4
|
|
|
5
|
-
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
|
+
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
|
|
6
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
6
7
|
import { DescriptorOutlet } from '@symbiote-native/angular';
|
|
7
8
|
import { SliderBase } from './shared';
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
SLIDER_IOS_DEFAULT_HEIGHT,
|
|
11
|
+
SLIDER_STEP_RESOLUTION_IOS,
|
|
12
|
+
} from '../../core';
|
|
9
13
|
export type { ISliderProps } from './shared';
|
|
10
14
|
|
|
11
15
|
const IOS_STEPS_CONTAINER_TOP = 10;
|
|
@@ -15,6 +19,13 @@ const IOS_STEPS_CONTAINER_TOP = 10;
|
|
|
15
19
|
standalone: true,
|
|
16
20
|
imports: [DescriptorOutlet],
|
|
17
21
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
22
|
+
providers: [
|
|
23
|
+
{
|
|
24
|
+
provide: NG_VALUE_ACCESSOR,
|
|
25
|
+
useExisting: forwardRef(() => Slider),
|
|
26
|
+
multi: true,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
18
29
|
template: `<symbiote-descriptor-outlet [node]="descriptor" />`,
|
|
19
30
|
})
|
|
20
31
|
export class Slider extends SliderBase {
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
Output,
|
|
34
34
|
type OnChanges,
|
|
35
35
|
} from '@angular/core';
|
|
36
|
+
import type { ControlValueAccessor } from '@angular/forms';
|
|
36
37
|
import { anchorHostStyle } from '@symbiote-native/angular';
|
|
37
38
|
import { resolveAccessibilityProps } from '@symbiote-native/components';
|
|
38
39
|
import type {
|
|
@@ -71,7 +72,10 @@ import {
|
|
|
71
72
|
|
|
72
73
|
export type ISliderProps = Omit<
|
|
73
74
|
ISliderBaseProps,
|
|
74
|
-
|
|
75
|
+
| 'onValueChange'
|
|
76
|
+
| 'onSlidingStart'
|
|
77
|
+
| 'onSlidingComplete'
|
|
78
|
+
| 'onAccessibilityAction'
|
|
75
79
|
>;
|
|
76
80
|
|
|
77
81
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -87,7 +91,9 @@ function asStyle(value: unknown): ISliderProps['style'] {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
@Directive()
|
|
90
|
-
export abstract class SliderBase
|
|
94
|
+
export abstract class SliderBase
|
|
95
|
+
implements ISliderProps, OnChanges, ControlValueAccessor
|
|
96
|
+
{
|
|
91
97
|
@Input() value?: number;
|
|
92
98
|
@Input() minimumValue?: number;
|
|
93
99
|
@Input() maximumValue?: number;
|
|
@@ -121,8 +127,10 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
121
127
|
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
122
128
|
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
123
129
|
@Input() accessibilityLabelledBy?: string | string[];
|
|
124
|
-
@Input()
|
|
125
|
-
|
|
130
|
+
@Input()
|
|
131
|
+
importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
132
|
+
@Input()
|
|
133
|
+
accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
126
134
|
@Input() screenReaderFocusable?: boolean;
|
|
127
135
|
@Input() accessibilityViewIsModal?: boolean;
|
|
128
136
|
@Input() accessibilityElementsHidden?: boolean;
|
|
@@ -199,7 +207,9 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
199
207
|
if (value !== undefined) this.slidingComplete.emit(value);
|
|
200
208
|
};
|
|
201
209
|
|
|
202
|
-
protected readonly handleAccessibilityAction = (
|
|
210
|
+
protected readonly handleAccessibilityAction = (
|
|
211
|
+
event: ISymbioteEvent,
|
|
212
|
+
): void => {
|
|
203
213
|
this.accessibilityAction.emit(event);
|
|
204
214
|
};
|
|
205
215
|
|
|
@@ -215,6 +225,29 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
215
225
|
}
|
|
216
226
|
};
|
|
217
227
|
|
|
228
|
+
// ControlValueAccessor — `value`/`disabled` are plain @Input() fields feeding the `descriptor`
|
|
229
|
+
// getter (re-evaluated on every OnPush check, no fold/commit dance), so writeValue()/
|
|
230
|
+
// setDisabledState() can set them straight, no TextInput-style stale-safe seam needed.
|
|
231
|
+
writeValue(value: number | null): void {
|
|
232
|
+
this.value = value ?? undefined;
|
|
233
|
+
this.changeDetector.markForCheck();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
registerOnChange(fn: (value: number) => void): void {
|
|
237
|
+
this.valueChange.subscribe(fn);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
registerOnTouched(fn: () => void): void {
|
|
241
|
+
// Slider has no blur concept (it's a drag control, not a text field) — the thumb release is
|
|
242
|
+
// the closest "the user is done interacting" signal.
|
|
243
|
+
this.slidingComplete.subscribe(() => fn());
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
setDisabledState(isDisabled: boolean): void {
|
|
247
|
+
this.disabled = isDisabled;
|
|
248
|
+
this.changeDetector.markForCheck();
|
|
249
|
+
}
|
|
250
|
+
|
|
218
251
|
private inputProps(): ISliderProps {
|
|
219
252
|
return {
|
|
220
253
|
value: this.value,
|
|
@@ -256,8 +289,10 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
256
289
|
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
257
290
|
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
258
291
|
accessibilityLanguage: this.accessibilityLanguage,
|
|
259
|
-
accessibilityRespondsToUserInteraction:
|
|
260
|
-
|
|
292
|
+
accessibilityRespondsToUserInteraction:
|
|
293
|
+
this.accessibilityRespondsToUserInteraction,
|
|
294
|
+
accessibilityShowsLargeContentViewer:
|
|
295
|
+
this.accessibilityShowsLargeContentViewer,
|
|
261
296
|
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
262
297
|
onAccessibilityTap: this.onAccessibilityTap,
|
|
263
298
|
onMagicTap: this.onMagicTap,
|
|
@@ -325,9 +360,16 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
325
360
|
upperLimit,
|
|
326
361
|
disabled: resolveSliderDisabled(disabled, accessibilityState),
|
|
327
362
|
inverted,
|
|
328
|
-
thumbTintColor: resolveThumbTintColor(
|
|
363
|
+
thumbTintColor: resolveThumbTintColor(
|
|
364
|
+
thumbTintColor,
|
|
365
|
+
false,
|
|
366
|
+
hasThumbImage,
|
|
367
|
+
),
|
|
329
368
|
thumbImage: nativeThumbImage,
|
|
330
|
-
accessibilityState: resolveSliderAccessibilityState(
|
|
369
|
+
accessibilityState: resolveSliderAccessibilityState(
|
|
370
|
+
disabled,
|
|
371
|
+
accessibilityState,
|
|
372
|
+
),
|
|
331
373
|
width: this.width,
|
|
332
374
|
style,
|
|
333
375
|
passthrough: {
|
|
@@ -360,6 +402,9 @@ export abstract class SliderBase implements ISliderProps, OnChanges {
|
|
|
360
402
|
inverted,
|
|
361
403
|
platform: this.platform,
|
|
362
404
|
});
|
|
363
|
-
return renderSlider(view, this.platform, {
|
|
405
|
+
return renderSlider(view, this.platform, {
|
|
406
|
+
steps,
|
|
407
|
+
onLayout: this.handleLayout,
|
|
408
|
+
});
|
|
364
409
|
}
|
|
365
410
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -22,7 +22,11 @@ export {
|
|
|
22
22
|
} from './slider-state';
|
|
23
23
|
export type { ISliderAccessibilityState } from './slider-state';
|
|
24
24
|
|
|
25
|
-
export type {
|
|
25
|
+
export type {
|
|
26
|
+
ISliderProps,
|
|
27
|
+
ISliderPlatform,
|
|
28
|
+
ISliderViewProps,
|
|
29
|
+
} from './slider-props';
|
|
26
30
|
|
|
27
31
|
export {
|
|
28
32
|
renderSlider,
|
|
@@ -42,4 +46,7 @@ export {
|
|
|
42
46
|
THUMB_IMAGE_STYLE,
|
|
43
47
|
STEP_NUMBER_CONTAINER_STYLE,
|
|
44
48
|
} from './render-steps-indicator';
|
|
45
|
-
export type {
|
|
49
|
+
export type {
|
|
50
|
+
IStepsIndicatorParams,
|
|
51
|
+
IStepMarkerProps,
|
|
52
|
+
} from './render-steps-indicator';
|
|
@@ -7,9 +7,16 @@
|
|
|
7
7
|
// width measurement that feeds `view.width`.
|
|
8
8
|
|
|
9
9
|
import { dlog } from '@symbiote-native/engine';
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
IStyleProp,
|
|
12
|
+
IViewStyle,
|
|
13
|
+
ISymbioteEvent,
|
|
14
|
+
} from '@symbiote-native/engine';
|
|
11
15
|
import { el } from '@symbiote-native/components';
|
|
12
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
IDescriptor,
|
|
18
|
+
IDescriptorChild,
|
|
19
|
+
} from '@symbiote-native/components';
|
|
13
20
|
import { RNC_SLIDER_VIEW_NAME } from './constants';
|
|
14
21
|
import type { ISliderPlatform, ISliderViewProps } from './slider-props';
|
|
15
22
|
|
|
@@ -27,21 +34,32 @@ export function resolveSliderNativeStyle(
|
|
|
27
34
|
width: number | undefined,
|
|
28
35
|
platform: ISliderPlatform,
|
|
29
36
|
): IStyleProp<IViewStyle> {
|
|
30
|
-
const base: IViewStyle = {
|
|
31
|
-
|
|
37
|
+
const base: IViewStyle = {
|
|
38
|
+
zIndex: 1,
|
|
39
|
+
alignContent: 'center',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
};
|
|
42
|
+
const sized: IStyleProp<IViewStyle> =
|
|
43
|
+
width === undefined ? base : [base, { width }];
|
|
32
44
|
return [platform.defaultStyle, sized];
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
// The outer wrapper centers the native slider and gives it the platform default height; the
|
|
36
48
|
// caller's style layers on top, exactly as the library composes [defaultStyle, props.style]. The
|
|
37
49
|
// caller's style is opaque (`unknown`) — the engine flattens the array — so it is not narrowed.
|
|
38
|
-
export function resolveSliderWrapperStyle(
|
|
50
|
+
export function resolveSliderWrapperStyle(
|
|
51
|
+
style: unknown,
|
|
52
|
+
platform: ISliderPlatform,
|
|
53
|
+
): unknown {
|
|
39
54
|
return [platform.defaultStyle, style, { justifyContent: 'center' }];
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
// The native `RNCSlider` leaf alone (no wrapper). Adapters that compose a custom step overlay
|
|
43
58
|
// (a per-adapter StepMarker element) assemble the wrapper themselves and drop this leaf in.
|
|
44
|
-
export function renderSliderNative(
|
|
59
|
+
export function renderSliderNative(
|
|
60
|
+
view: ISliderViewProps,
|
|
61
|
+
platform: ISliderPlatform,
|
|
62
|
+
): IDescriptor {
|
|
45
63
|
dlog(
|
|
46
64
|
`Slider render value=${String(view.value)} disabled=${String(view.disabled)} width=${String(view.width)}`,
|
|
47
65
|
);
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
// assembles the overlay itself from these same helpers, dropping its element into each mark.
|
|
7
7
|
|
|
8
8
|
import { el, txt } from '@symbiote-native/components';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
IDescriptor,
|
|
11
|
+
IDescriptorChild,
|
|
12
|
+
} from '@symbiote-native/components';
|
|
10
13
|
import type { IViewStyle, ITextStyle } from '@symbiote-native/engine';
|
|
11
14
|
import {
|
|
12
15
|
SLIDER_MARGIN_HORIZONTAL_PADDING,
|
|
@@ -54,7 +57,10 @@ export const STEP_NUMBER_CONTAINER_STYLE: IViewStyle = {
|
|
|
54
57
|
|
|
55
58
|
// The mark row: flex row, evenly spaced, inset by a fraction of the measured width. `top` is
|
|
56
59
|
// platform-specific (iOS nudges the row down 10pt), supplied via the platform piece.
|
|
57
|
-
export function resolveStepsContainerStyle(
|
|
60
|
+
export function resolveStepsContainerStyle(
|
|
61
|
+
width: number,
|
|
62
|
+
platform: ISliderPlatform,
|
|
63
|
+
): IViewStyle {
|
|
58
64
|
return {
|
|
59
65
|
flex: 1,
|
|
60
66
|
flexDirection: 'row',
|
|
@@ -114,29 +120,51 @@ function renderStepCell(
|
|
|
114
120
|
trackMarkChildren.push(
|
|
115
121
|
el(
|
|
116
122
|
'symbiote-view',
|
|
117
|
-
{
|
|
118
|
-
|
|
123
|
+
{
|
|
124
|
+
style: THUMB_IMAGE_CONTAINER_STYLE,
|
|
125
|
+
testID: 'sliderTrackMark-thumbImage',
|
|
126
|
+
},
|
|
127
|
+
[
|
|
128
|
+
el('symbiote-image', {
|
|
129
|
+
source: params.thumbImage,
|
|
130
|
+
style: THUMB_IMAGE_STYLE,
|
|
131
|
+
}),
|
|
132
|
+
],
|
|
119
133
|
),
|
|
120
134
|
);
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
const cellChildren: IDescriptorChild[] = [
|
|
124
|
-
el(
|
|
138
|
+
el(
|
|
139
|
+
'symbiote-view',
|
|
140
|
+
{ style: TRACK_MARK_CONTAINER_STYLE },
|
|
141
|
+
trackMarkChildren,
|
|
142
|
+
),
|
|
125
143
|
];
|
|
126
144
|
if (params.renderStepNumber === true) {
|
|
127
145
|
cellChildren.push(
|
|
128
146
|
el('symbiote-view', { style: STEP_NUMBER_CONTAINER_STYLE }, [
|
|
129
|
-
txt(
|
|
130
|
-
|
|
131
|
-
|
|
147
|
+
txt(
|
|
148
|
+
{
|
|
149
|
+
testID: `${displayIndex}th-step`,
|
|
150
|
+
style: { fontSize } satisfies ITextStyle,
|
|
151
|
+
},
|
|
152
|
+
[String(value)],
|
|
153
|
+
),
|
|
132
154
|
]),
|
|
133
155
|
);
|
|
134
156
|
}
|
|
135
|
-
return el(
|
|
157
|
+
return el(
|
|
158
|
+
'symbiote-view',
|
|
159
|
+
{ style: STEP_INDICATOR_ELEMENT_STYLE },
|
|
160
|
+
cellChildren,
|
|
161
|
+
);
|
|
136
162
|
}
|
|
137
163
|
|
|
138
164
|
// The default overlay (no custom StepMarker). pointerEvents none so it never eats the drag.
|
|
139
|
-
export function renderStepsIndicator(
|
|
165
|
+
export function renderStepsIndicator(
|
|
166
|
+
params: IStepsIndicatorParams,
|
|
167
|
+
): IDescriptor {
|
|
140
168
|
const fontSize = stepNumberFontSize(params.options.length);
|
|
141
169
|
const ordered = orderStepOptions(params.options, params.inverted);
|
|
142
170
|
const cells = ordered.map((value, displayIndex) =>
|
package/src/core/slider-props.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
// Slider prop types.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// every adapter re-exports and extends; ISliderViewProps is the pre-resolved input the render fn
|
|
6
|
-
// paints.
|
|
1
|
+
// Slider prop types. ISliderProps is the shared agnostic base every adapter re-exports and
|
|
2
|
+
// extends; ISliderViewProps is the pre-resolved input the render fn paints. The custom
|
|
3
|
+
// `StepMarker` field is per-adapter (a framework element return type can't live here) - see
|
|
4
|
+
// CLAUDE.md's <prop_types_split_agnostic_vs_per_adapter>.
|
|
7
5
|
|
|
8
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
IColorValue,
|
|
8
|
+
IStyleProp,
|
|
9
|
+
IViewStyle,
|
|
10
|
+
} from '@symbiote-native/engine';
|
|
9
11
|
import type {
|
|
10
12
|
IAccessibilityProps,
|
|
11
13
|
IAriaProps,
|
|
@@ -14,8 +16,7 @@ import type {
|
|
|
14
16
|
import type { ISliderAccessibilityState } from './slider-state';
|
|
15
17
|
|
|
16
18
|
// The agnostic public surface, shared by every adapter. Callbacks take a plain number (the
|
|
17
|
-
// adapter unwraps nativeEvent.value)
|
|
18
|
-
// `StepMarker` component is deliberately absent — adapters add it to their own flavored type.
|
|
19
|
+
// adapter unwraps nativeEvent.value); colors/images/style are agnostic value types.
|
|
19
20
|
export interface ISliderProps extends IAccessibilityProps, IAriaProps {
|
|
20
21
|
value?: number;
|
|
21
22
|
minimumValue?: number;
|
|
@@ -55,14 +56,13 @@ export type ISliderPlatform = {
|
|
|
55
56
|
stepsContainerTop: number;
|
|
56
57
|
};
|
|
57
58
|
|
|
58
|
-
// Pre-resolved inputs the native render paints from. Only
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// width:0 first-paint flash.
|
|
59
|
+
// Pre-resolved inputs the native render paints from. Only fields that need FOLDING are explicit
|
|
60
|
+
// here (value sanitized, disabled/accessibilityState resolved, limits defaulted, thumb
|
|
61
|
+
// tint/image resolved against the StepMarker decision); everything else - track colors/images,
|
|
62
|
+
// thumbSize, tapToSeek, vertical, accessibility*/testID/aria-*, and the native event handlers -
|
|
63
|
+
// rides in `passthrough` verbatim (the engine routes on*-keyed handlers and ViewConfig color
|
|
64
|
+
// processors). `width` is set only when a step overlay needs it, avoiding a width:0 first-paint
|
|
65
|
+
// flash in the common case.
|
|
66
66
|
export type ISliderViewProps = {
|
|
67
67
|
value?: number;
|
|
68
68
|
minimumValue: number;
|
package/src/core/slider-state.ts
CHANGED
|
@@ -18,7 +18,9 @@ export type ISliderAccessibilityState = {
|
|
|
18
18
|
// Mirror the library's `passedValue`: NaN or a falsy value (incl. 0) becomes undefined, so the
|
|
19
19
|
// native view falls back to its own default initial value rather than receiving NaN/0 as a
|
|
20
20
|
// write. Entered once it still acts as the initial value (the slider is uncontrolled after).
|
|
21
|
-
export function sanitizeSliderValue(
|
|
21
|
+
export function sanitizeSliderValue(
|
|
22
|
+
value: number | undefined,
|
|
23
|
+
): number | undefined {
|
|
22
24
|
if (value === undefined) return undefined;
|
|
23
25
|
return Number.isNaN(value) || !value ? undefined : value;
|
|
24
26
|
}
|
|
@@ -44,17 +46,23 @@ export function resolveSliderAccessibilityState(
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// Default the slide limits to the unbounded sentinels when unset (the native, non-web case).
|
|
47
|
-
export function resolveSliderLowerLimit(
|
|
49
|
+
export function resolveSliderLowerLimit(
|
|
50
|
+
lowerLimit: number | undefined,
|
|
51
|
+
): number {
|
|
48
52
|
return lowerLimit ?? SLIDER_LIMIT_MIN_VALUE;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
export function resolveSliderUpperLimit(
|
|
55
|
+
export function resolveSliderUpperLimit(
|
|
56
|
+
upperLimit: number | undefined,
|
|
57
|
+
): number {
|
|
52
58
|
return upperLimit ?? SLIDER_LIMIT_MAX_VALUE;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
// The value the native value/sliding events carry as nativeEvent.value. nativeEvent is an
|
|
56
62
|
// untyped Record, so narrow; a non-number payload yields undefined (no callback fired upstream).
|
|
57
|
-
export function valueFromSliderEvent(
|
|
63
|
+
export function valueFromSliderEvent(
|
|
64
|
+
event: ISymbioteEvent,
|
|
65
|
+
): number | undefined {
|
|
58
66
|
const value = event.nativeEvent.value;
|
|
59
67
|
return typeof value === 'number' ? value : undefined;
|
|
60
68
|
}
|
|
@@ -91,7 +99,10 @@ export function shouldPassNativeThumbImage(
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
// The library warns when the limits cross; surfaced as a pure predicate so the adapter logs it.
|
|
94
|
-
export function isInvalidLimitConfig(
|
|
102
|
+
export function isInvalidLimitConfig(
|
|
103
|
+
lowerLimit: number,
|
|
104
|
+
upperLimit: number,
|
|
105
|
+
): boolean {
|
|
95
106
|
return lowerLimit >= upperLimit;
|
|
96
107
|
}
|
|
97
108
|
|
|
@@ -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 {
|
|
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
|
|
|
@@ -9,7 +9,13 @@
|
|
|
9
9
|
// library's React Slider component — that component internally calls React hooks off the React
|
|
10
10
|
// dispatcher, so a non-React adapter rendering it directly would crash on a null dispatcher.
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
createElement,
|
|
14
|
+
forwardRef,
|
|
15
|
+
useCallback,
|
|
16
|
+
useEffect,
|
|
17
|
+
useState,
|
|
18
|
+
} from 'react';
|
|
13
19
|
import type {
|
|
14
20
|
FC,
|
|
15
21
|
ForwardRefExoticComponent,
|
|
@@ -19,8 +25,16 @@ import type {
|
|
|
19
25
|
} from 'react';
|
|
20
26
|
import { descriptorToReact, Image } from '@symbiote-native/react';
|
|
21
27
|
import { resolveAccessibilityProps } from '@symbiote-native/components';
|
|
22
|
-
import type {
|
|
23
|
-
|
|
28
|
+
import type {
|
|
29
|
+
IDescriptor,
|
|
30
|
+
IDescriptorChild,
|
|
31
|
+
IImageSourceProp,
|
|
32
|
+
} from '@symbiote-native/components';
|
|
33
|
+
import {
|
|
34
|
+
dlog,
|
|
35
|
+
type ISymbioteEvent,
|
|
36
|
+
type ISymbioteNode,
|
|
37
|
+
} from '@symbiote-native/engine';
|
|
24
38
|
import {
|
|
25
39
|
sanitizeSliderValue,
|
|
26
40
|
resolveSliderDisabled,
|
|
@@ -86,7 +100,10 @@ function toChild(child: IDescriptorChild): ReactElement | string {
|
|
|
86
100
|
// renderSlider's Descriptor IS the outer wrapper (symbiote-view hosting the native leaf +
|
|
87
101
|
// optional steps overlay); className is reapplied on it here, exactly like ImageBackground's
|
|
88
102
|
// wrapper.
|
|
89
|
-
function withClassName(
|
|
103
|
+
function withClassName(
|
|
104
|
+
descriptor: IDescriptor,
|
|
105
|
+
className: string | undefined,
|
|
106
|
+
): ReactElement {
|
|
90
107
|
return createElement(
|
|
91
108
|
descriptor.type,
|
|
92
109
|
{ key: descriptor.key, ...descriptor.props, className },
|
|
@@ -123,7 +140,9 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
123
140
|
...passthrough
|
|
124
141
|
} = props;
|
|
125
142
|
|
|
126
|
-
const [reportedValue, setReportedValue] = useState<number | undefined>(
|
|
143
|
+
const [reportedValue, setReportedValue] = useState<number | undefined>(
|
|
144
|
+
undefined,
|
|
145
|
+
);
|
|
127
146
|
const [width, setWidth] = useState(0);
|
|
128
147
|
|
|
129
148
|
const handleValueChange = useCallback(
|
|
@@ -157,7 +176,8 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
157
176
|
);
|
|
158
177
|
const handleLayout = useCallback((event: ISymbioteEvent): void => {
|
|
159
178
|
const layout = event.nativeEvent.layout;
|
|
160
|
-
if (isRecord(layout) && typeof layout.width === 'number')
|
|
179
|
+
if (isRecord(layout) && typeof layout.width === 'number')
|
|
180
|
+
setWidth(layout.width);
|
|
161
181
|
}, []);
|
|
162
182
|
|
|
163
183
|
const minimum = minimumValue ?? SLIDER_DEFAULT_MINIMUM_VALUE;
|
|
@@ -173,10 +193,16 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
173
193
|
|
|
174
194
|
const hasStepMarker = StepMarker !== undefined;
|
|
175
195
|
const hasThumbImage = thumbImage !== undefined;
|
|
176
|
-
const showSteps = shouldRenderStepsIndicator(
|
|
196
|
+
const showSteps = shouldRenderStepsIndicator(
|
|
197
|
+
hasStepMarker,
|
|
198
|
+
renderStepNumber,
|
|
199
|
+
);
|
|
177
200
|
// Passed to the native view raw — the engine runs the image processor derived from RNCSlider's
|
|
178
201
|
// ViewConfig — and only when no custom marker draws its own thumb (matches the library).
|
|
179
|
-
const nativeThumbImage = shouldPassNativeThumbImage(
|
|
202
|
+
const nativeThumbImage = shouldPassNativeThumbImage(
|
|
203
|
+
hasStepMarker,
|
|
204
|
+
hasThumbImage,
|
|
205
|
+
)
|
|
180
206
|
? thumbImage
|
|
181
207
|
: undefined;
|
|
182
208
|
|
|
@@ -189,9 +215,16 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
189
215
|
upperLimit: upper,
|
|
190
216
|
disabled: resolveSliderDisabled(disabled, accessibilityState),
|
|
191
217
|
inverted: inverted ?? false,
|
|
192
|
-
thumbTintColor: resolveThumbTintColor(
|
|
218
|
+
thumbTintColor: resolveThumbTintColor(
|
|
219
|
+
thumbTintColor,
|
|
220
|
+
hasStepMarker,
|
|
221
|
+
hasThumbImage,
|
|
222
|
+
),
|
|
193
223
|
thumbImage: nativeThumbImage,
|
|
194
|
-
accessibilityState: resolveSliderAccessibilityState(
|
|
224
|
+
accessibilityState: resolveSliderAccessibilityState(
|
|
225
|
+
disabled,
|
|
226
|
+
accessibilityState,
|
|
227
|
+
),
|
|
195
228
|
width,
|
|
196
229
|
style,
|
|
197
230
|
passthrough: {
|
|
@@ -206,10 +239,18 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
206
239
|
};
|
|
207
240
|
|
|
208
241
|
if (!showSteps) {
|
|
209
|
-
return withClassName(
|
|
242
|
+
return withClassName(
|
|
243
|
+
renderSlider(view, platform, { onLayout: handleLayout }),
|
|
244
|
+
className,
|
|
245
|
+
);
|
|
210
246
|
}
|
|
211
247
|
|
|
212
|
-
const options = computeStepOptions(
|
|
248
|
+
const options = computeStepOptions(
|
|
249
|
+
minimum,
|
|
250
|
+
maximum,
|
|
251
|
+
stepValue,
|
|
252
|
+
platform.stepResolution,
|
|
253
|
+
);
|
|
213
254
|
const currentValue = reportedValue ?? view.value ?? minimum;
|
|
214
255
|
|
|
215
256
|
if (StepMarker !== undefined) {
|
|
@@ -225,7 +266,11 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
225
266
|
});
|
|
226
267
|
return createElement(
|
|
227
268
|
'symbiote-view',
|
|
228
|
-
{
|
|
269
|
+
{
|
|
270
|
+
style: resolveStepsWrapperStyle(style, platform),
|
|
271
|
+
onLayout: handleLayout,
|
|
272
|
+
className,
|
|
273
|
+
},
|
|
229
274
|
overlay,
|
|
230
275
|
descriptorToReact(renderSliderNative(view, platform)),
|
|
231
276
|
);
|
|
@@ -248,7 +293,10 @@ export function createSlider(platform: ISliderPlatform): ISliderComponent {
|
|
|
248
293
|
}
|
|
249
294
|
|
|
250
295
|
// The wrapper style for the custom-marker steps path mirrors renderSlider's wrapper.
|
|
251
|
-
function resolveStepsWrapperStyle(
|
|
296
|
+
function resolveStepsWrapperStyle(
|
|
297
|
+
style: unknown,
|
|
298
|
+
platform: ISliderPlatform,
|
|
299
|
+
): unknown {
|
|
252
300
|
return [platform.defaultStyle, style, { justifyContent: 'center' }];
|
|
253
301
|
}
|
|
254
302
|
|
|
@@ -291,7 +339,10 @@ function renderCustomStepsOverlay(params: ICustomStepsParams): ReactElement {
|
|
|
291
339
|
style: THUMB_IMAGE_CONTAINER_STYLE,
|
|
292
340
|
testID: 'sliderTrackMark-thumbImage',
|
|
293
341
|
},
|
|
294
|
-
createElement(Image, {
|
|
342
|
+
createElement(Image, {
|
|
343
|
+
source: params.thumbImage,
|
|
344
|
+
style: THUMB_IMAGE_STYLE,
|
|
345
|
+
}),
|
|
295
346
|
),
|
|
296
347
|
);
|
|
297
348
|
}
|
package/src/register/index.ts
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
// barrel imports it; the component modules and their headless tests stay free of the spec.
|
|
8
8
|
|
|
9
9
|
import '@react-native-community/slider/dist/RNCSliderNativeComponent';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
processColor,
|
|
12
|
+
registerComponent,
|
|
13
|
+
type IColorValue,
|
|
14
|
+
} from '@symbiote-native/engine';
|
|
11
15
|
import {
|
|
12
16
|
RNC_SLIDER_VIEW_NAME,
|
|
13
17
|
SLIDER_ON_CHANGE,
|
|
@@ -33,7 +37,10 @@ function processSliderColor(value: unknown): unknown {
|
|
|
33
37
|
registerComponent(RNC_SLIDER_VIEW_NAME, {
|
|
34
38
|
events: [
|
|
35
39
|
{ raw: 'topChange', listener: listenerName(SLIDER_ON_CHANGE) },
|
|
36
|
-
{
|
|
40
|
+
{
|
|
41
|
+
raw: 'topRNCSliderValueChange',
|
|
42
|
+
listener: listenerName(SLIDER_ON_VALUE_CHANGE),
|
|
43
|
+
},
|
|
37
44
|
{
|
|
38
45
|
raw: 'topRNCSliderSlidingStart',
|
|
39
46
|
listener: listenerName(SLIDER_ON_SLIDING_START),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// @symbiote-native/slider/solid: the Solid wrapper over the @react-native-community/slider native
|
|
2
|
+
// view. The bare import below is FIRST and is a side effect, not a re-export: it registers the
|
|
3
|
+
// native view's ViewConfig (the codegen spec, never the library's React component) before any
|
|
4
|
+
// Slider can mount. Metro's production-only `inlineRequires` moves a require down to the first
|
|
5
|
+
// place its binding is USED and compiles a barrel's re-export into a lazy getter, so a registration
|
|
6
|
+
// reached through one silently never runs in RELEASE builds only. Do not restructure it.
|
|
7
|
+
|
|
8
|
+
import '../register';
|
|
9
|
+
|
|
10
|
+
export { Slider } from './slider';
|
|
11
|
+
export type { ISliderProps, ISliderStepMarker } from './slider';
|
|
12
|
+
export type { IStepMarkerProps } from '../core';
|