@multiplatform.one/forms 8.3.0 → 8.4.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/dist/cjs/ControlGroup/index.cjs +2 -1
- package/dist/cjs/ControlGroup/index.native.js +2 -1
- package/dist/cjs/ControlGroup/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.native.js +20 -2
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/InputParts/index.cjs +1 -0
- package/dist/cjs/InputParts/index.native.js +1 -0
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/Wheel/index.cjs +22 -6
- package/dist/cjs/Wheel/index.native.js +26 -8
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +33 -30
- package/dist/cjs/fields/DatePicker/parts.native.js +34 -30
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.cjs +32 -15
- package/dist/cjs/fields/FileUpload/index.native.js +40 -19
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/ImageField/index.cjs +35 -20
- package/dist/cjs/fields/ImageField/index.native.js +37 -20
- package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
- package/dist/cjs/fields/Progress/index.cjs +5 -0
- package/dist/cjs/fields/Progress/index.native.js +5 -0
- package/dist/cjs/fields/Progress/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.cjs +38 -12
- package/dist/cjs/fields/Slider/index.native.js +39 -11
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/Switch/index.cjs +10 -6
- package/dist/cjs/fields/Switch/index.native.js +10 -6
- package/dist/cjs/fields/Switch/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/parts.cjs +9 -10
- package/dist/cjs/fields/TimePicker/parts.native.js +9 -10
- package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.cjs +1 -0
- package/dist/cjs/fields/ToggleGroup/index.native.js +1 -0
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/pickerHandle.cjs +18 -0
- package/dist/cjs/fields/pickerHandle.native.js +21 -0
- package/dist/cjs/fields/pickerHandle.native.js.map +1 -0
- package/dist/cjs/hooks/useHoldCue.cjs +54 -0
- package/dist/cjs/hooks/useHoldCue.native.js +62 -0
- package/dist/cjs/hooks/useHoldCue.native.js.map +1 -0
- package/dist/cjs/index.cjs +4 -0
- package/dist/cjs/index.gnome.cjs +4 -0
- package/dist/cjs/index.gnome.native.js +4 -0
- package/dist/cjs/index.gnome.native.js.map +1 -1
- package/dist/cjs/index.native.js +4 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/colorRamps.cjs +9 -7
- package/dist/cjs/shared/colorRamps.native.js +9 -7
- package/dist/cjs/shared/colorRamps.native.js.map +1 -1
- package/dist/cjs/shared/controlTrack.cjs +32 -0
- package/dist/cjs/shared/controlTrack.native.js +35 -0
- package/dist/cjs/shared/controlTrack.native.js.map +1 -0
- package/dist/esm/ControlGroup/index.mjs +2 -1
- package/dist/esm/ControlGroup/index.mjs.map +1 -1
- package/dist/esm/ControlGroup/index.native.js +2 -1
- package/dist/esm/ControlGroup/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +20 -2
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/InputParts/index.mjs +1 -0
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +1 -0
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/Wheel/index.mjs +23 -7
- package/dist/esm/Wheel/index.mjs.map +1 -1
- package/dist/esm/Wheel/index.native.js +27 -9
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +35 -32
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +36 -32
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/index.mjs +33 -16
- package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js +41 -20
- package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
- package/dist/esm/fields/ImageField/index.mjs +36 -21
- package/dist/esm/fields/ImageField/index.mjs.map +1 -1
- package/dist/esm/fields/ImageField/index.native.js +38 -21
- package/dist/esm/fields/ImageField/index.native.js.map +1 -1
- package/dist/esm/fields/Progress/index.mjs +5 -0
- package/dist/esm/fields/Progress/index.mjs.map +1 -1
- package/dist/esm/fields/Progress/index.native.js +5 -0
- package/dist/esm/fields/Progress/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/index.mjs +38 -12
- package/dist/esm/fields/Slider/index.mjs.map +1 -1
- package/dist/esm/fields/Slider/index.native.js +39 -11
- package/dist/esm/fields/Slider/index.native.js.map +1 -1
- package/dist/esm/fields/Switch/index.mjs +10 -6
- package/dist/esm/fields/Switch/index.mjs.map +1 -1
- package/dist/esm/fields/Switch/index.native.js +10 -6
- package/dist/esm/fields/Switch/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/parts.mjs +9 -10
- package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/parts.native.js +9 -10
- package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.mjs +2 -1
- package/dist/esm/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.native.js +2 -1
- package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/esm/fields/pickerHandle.mjs +2 -0
- package/dist/esm/fields/pickerHandle.mjs.map +1 -0
- package/dist/esm/fields/pickerHandle.native.js +2 -0
- package/dist/esm/fields/pickerHandle.native.js.map +1 -0
- package/dist/esm/hooks/useHoldCue.mjs +27 -0
- package/dist/esm/hooks/useHoldCue.mjs.map +1 -0
- package/dist/esm/hooks/useHoldCue.native.js +32 -0
- package/dist/esm/hooks/useHoldCue.native.js.map +1 -0
- package/dist/esm/index.gnome.mjs +2 -1
- package/dist/esm/index.gnome.mjs.map +1 -1
- package/dist/esm/index.gnome.native.js +2 -1
- package/dist/esm/index.gnome.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/colorRamps.mjs +9 -7
- package/dist/esm/shared/colorRamps.mjs.map +1 -1
- package/dist/esm/shared/colorRamps.native.js +9 -7
- package/dist/esm/shared/colorRamps.native.js.map +1 -1
- package/dist/esm/shared/controlTrack.mjs +7 -0
- package/dist/esm/shared/controlTrack.mjs.map +1 -0
- package/dist/esm/shared/controlTrack.native.js +7 -0
- package/dist/esm/shared/controlTrack.native.js.map +1 -0
- package/dist/esm/shared/nativeOverlayWindow.mjs.map +1 -1
- package/dist/esm/shared/systemControls.mjs.map +1 -1
- package/dist/esm/shared/systemControls.native.js.map +1 -1
- package/dist/jsx/ControlGroup/index.mjs +2 -1
- package/dist/jsx/ControlGroup/index.mjs.map +1 -1
- package/dist/jsx/ControlGroup/index.native.js +2 -1
- package/dist/jsx/ControlGroup/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +20 -2
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/InputParts/index.mjs +1 -0
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +1 -0
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/Wheel/index.mjs +23 -7
- package/dist/jsx/Wheel/index.mjs.map +1 -1
- package/dist/jsx/Wheel/index.native.js +26 -8
- package/dist/jsx/Wheel/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +35 -32
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +34 -30
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload/index.mjs +33 -16
- package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload/index.native.js +40 -19
- package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
- package/dist/jsx/fields/ImageField/index.mjs +36 -21
- package/dist/jsx/fields/ImageField/index.mjs.map +1 -1
- package/dist/jsx/fields/ImageField/index.native.js +37 -20
- package/dist/jsx/fields/ImageField/index.native.js.map +1 -1
- package/dist/jsx/fields/Progress/index.mjs +5 -0
- package/dist/jsx/fields/Progress/index.mjs.map +1 -1
- package/dist/jsx/fields/Progress/index.native.js +5 -0
- package/dist/jsx/fields/Progress/index.native.js.map +1 -1
- package/dist/jsx/fields/Slider/index.mjs +38 -12
- package/dist/jsx/fields/Slider/index.mjs.map +1 -1
- package/dist/jsx/fields/Slider/index.native.js +39 -11
- package/dist/jsx/fields/Slider/index.native.js.map +1 -1
- package/dist/jsx/fields/Switch/index.mjs +10 -6
- package/dist/jsx/fields/Switch/index.mjs.map +1 -1
- package/dist/jsx/fields/Switch/index.native.js +10 -6
- package/dist/jsx/fields/Switch/index.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.mjs +9 -10
- package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.native.js +9 -10
- package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.mjs +2 -1
- package/dist/jsx/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.native.js +1 -0
- package/dist/jsx/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/jsx/fields/pickerHandle.mjs +2 -0
- package/dist/jsx/fields/pickerHandle.mjs.map +1 -0
- package/dist/jsx/fields/pickerHandle.native.js +21 -0
- package/dist/jsx/fields/pickerHandle.native.js.map +1 -0
- package/dist/jsx/hooks/useHoldCue.mjs +27 -0
- package/dist/jsx/hooks/useHoldCue.mjs.map +1 -0
- package/dist/jsx/hooks/useHoldCue.native.js +62 -0
- package/dist/jsx/hooks/useHoldCue.native.js.map +1 -0
- package/dist/jsx/index.gnome.mjs +2 -1
- package/dist/jsx/index.gnome.mjs.map +1 -1
- package/dist/jsx/index.gnome.native.js +4 -0
- package/dist/jsx/index.gnome.native.js.map +1 -1
- package/dist/jsx/index.js +2 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +4 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/colorRamps.mjs +9 -7
- package/dist/jsx/shared/colorRamps.mjs.map +1 -1
- package/dist/jsx/shared/colorRamps.native.js +9 -7
- package/dist/jsx/shared/colorRamps.native.js.map +1 -1
- package/dist/jsx/shared/controlTrack.mjs +7 -0
- package/dist/jsx/shared/controlTrack.mjs.map +1 -0
- package/dist/jsx/shared/controlTrack.native.js +35 -0
- package/dist/jsx/shared/controlTrack.native.js.map +1 -0
- package/dist/jsx/shared/nativeOverlayWindow.mjs.map +1 -1
- package/dist/jsx/shared/nativeOverlayWindow.native.js.map +1 -1
- package/dist/jsx/shared/systemControls.mjs.map +1 -1
- package/package.json +11 -10
- package/src/Button/Button.controlTiers.spec.tsx +2 -2
- package/src/ControlGroup/index.tsx +2 -1
- package/src/FloatingPanel/FloatingPanel.holdCue.native.spec.tsx +72 -0
- package/src/FloatingPanel/index.native.tsx +9 -1
- package/src/InputParts/index.tsx +1 -0
- package/src/Wheel/Wheel.edgeFade.spec.tsx +74 -0
- package/src/Wheel/index.tsx +25 -11
- package/src/fields/DatePicker/dateFieldMarks.spec.tsx +154 -0
- package/src/fields/DatePicker/parts.tsx +33 -30
- package/src/fields/FileUpload/index.tsx +45 -16
- package/src/fields/ImageField/index.tsx +42 -19
- package/src/fields/Progress/index.tsx +3 -0
- package/src/fields/Slider/Slider.system.native.spec.tsx +44 -0
- package/src/fields/Slider/index.tsx +40 -12
- package/src/fields/Stepper/Stepper.stories.tsx +42 -0
- package/src/fields/Switch/Switch.spec.tsx +14 -10
- package/src/fields/Switch/index.tsx +11 -7
- package/src/fields/TimePicker/parts.tsx +8 -8
- package/src/fields/ToggleGroup/index.tsx +7 -1
- package/src/fields/fieldTriggerSlot.spec.tsx +89 -0
- package/src/fields/pickerHandle.ts +13 -0
- package/src/hooks/useHoldCue.ts +26 -0
- package/src/index.gnome.ts +1 -0
- package/src/index.ts +2 -0
- package/src/overlayGround.spec.ts +5 -3
- package/src/shared/OverlayWindow.tsx +2 -2
- package/src/shared/colorRamps.ts +9 -6
- package/src/shared/controlTrack.ts +7 -0
- package/src/shared/nativeOverlayWindow.tsx +1 -1
- package/src/shared/systemControls.ts +10 -2
- package/types/ControlGroup/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/InputParts/index.d.ts +4 -4
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/Wheel/index.d.ts.map +1 -1
- package/types/fields/DatePicker/parts.d.ts +4 -3
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/FileUpload/index.d.ts +6 -1
- package/types/fields/FileUpload/index.d.ts.map +1 -1
- package/types/fields/ImageField/index.d.ts +7 -2
- package/types/fields/ImageField/index.d.ts.map +1 -1
- package/types/fields/Progress/index.d.ts.map +1 -1
- package/types/fields/Slider/index.d.ts.map +1 -1
- package/types/fields/Switch/index.d.ts.map +1 -1
- package/types/fields/ToggleGroup/index.d.ts.map +1 -1
- package/types/fields/pickerHandle.d.ts +12 -0
- package/types/fields/pickerHandle.d.ts.map +1 -0
- package/types/hooks/useHoldCue.d.ts +10 -0
- package/types/hooks/useHoldCue.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/index.gnome.d.ts +1 -0
- package/types/index.gnome.d.ts.map +1 -1
- package/types/shared/OverlayWindow.d.ts +2 -2
- package/types/shared/OverlayWindow.d.ts.map +1 -1
- package/types/shared/colorRamps.d.ts +8 -6
- package/types/shared/colorRamps.d.ts.map +1 -1
- package/types/shared/controlTrack.d.ts +3 -0
- package/types/shared/controlTrack.d.ts.map +1 -0
- package/types/shared/nativeOverlayWindow.d.ts +1 -1
- package/types/shared/nativeOverlayWindow.d.ts.map +1 -1
- package/types/shared/systemControls.d.ts +2 -1
- package/types/shared/systemControls.d.ts.map +1 -1
|
@@ -2,7 +2,16 @@ import { transitionProps } from "@multiplatform.one/theme";
|
|
|
2
2
|
import { useTouchSurface } from "@multiplatform.one/theme";
|
|
3
3
|
import { FileIcon, UploadIcon, XIcon } from "@phosphor-icons/react";
|
|
4
4
|
import type { DeepKeys, DeepValue } from "@tanstack/form-core";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type DragEvent,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
type Ref,
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useImperativeHandle,
|
|
12
|
+
useRef,
|
|
13
|
+
useState,
|
|
14
|
+
} from "react";
|
|
6
15
|
import { useMedia, useProps, type SizeTokens } from "tamagui";
|
|
7
16
|
import { Image, isWeb, Text, XStack, YStack, styled } from "tamagui";
|
|
8
17
|
import { Button, IconButton, IconButtonGroup } from "../../Button";
|
|
@@ -26,6 +35,7 @@ import {
|
|
|
26
35
|
import { formCommonColors, formDropzoneColors, formInputColors } from "../../shared/colorRamps";
|
|
27
36
|
import { t } from "../../shared/t";
|
|
28
37
|
import type { FieldComponentProps, Validator } from "../../types";
|
|
38
|
+
import type { FieldPickerHandle, FieldPickerTrigger } from "../pickerHandle";
|
|
29
39
|
import type { NativePickedFile, NativePickerMode } from "./filePickerTypes";
|
|
30
40
|
// Platform-split: the web twin is inert, the `.native` twin opens
|
|
31
41
|
// expo-document-picker / expo-image-picker (Geolocation map-surface pattern).
|
|
@@ -114,6 +124,10 @@ export type FileUploadProps<
|
|
|
114
124
|
existing?: { src: string; name?: string; isImage?: boolean } | null;
|
|
115
125
|
/** Removes the existing file; given, its tile carries a remove disc. */
|
|
116
126
|
onExistingRemove?: () => void;
|
|
127
|
+
/** LC-290: `open()` runs the picker from the app's own control. */
|
|
128
|
+
ref?: Ref<FieldPickerHandle>;
|
|
129
|
+
/** LC-290: the app's own control, drawn in place of the dropzone; call `open` from its press. */
|
|
130
|
+
trigger?: FieldPickerTrigger;
|
|
117
131
|
};
|
|
118
132
|
|
|
119
133
|
const UploadAreaFrame = styled(YStack, {
|
|
@@ -203,6 +217,7 @@ export function FileUpload<
|
|
|
203
217
|
>(props: FileUploadProps<TParentData, TName, TFieldValidator, TFormValidator, TData>) {
|
|
204
218
|
const hydrationTouch = useTouchSurface();
|
|
205
219
|
const media = useMedia();
|
|
220
|
+
const { ref: pickerRef, trigger } = props;
|
|
206
221
|
const {
|
|
207
222
|
defaultValue,
|
|
208
223
|
form,
|
|
@@ -230,6 +245,8 @@ export function FileUpload<
|
|
|
230
245
|
existing,
|
|
231
246
|
onExistingRemove,
|
|
232
247
|
id: idProp,
|
|
248
|
+
ref: _ref,
|
|
249
|
+
trigger: _trigger,
|
|
233
250
|
...fieldProps
|
|
234
251
|
} = useProps(props);
|
|
235
252
|
|
|
@@ -615,6 +632,9 @@ export function FileUpload<
|
|
|
615
632
|
fileInputRef.current?.click();
|
|
616
633
|
}, [disabled, readOnly]);
|
|
617
634
|
|
|
635
|
+
const requestFilesRef = useRef<() => void>(openFileDialog);
|
|
636
|
+
useImperativeHandle(pickerRef, () => ({ open: () => requestFilesRef.current() }), []);
|
|
637
|
+
|
|
618
638
|
// Native has no <input type="file">. The same press opens the OS picker and
|
|
619
639
|
// routes its result through the identical validate → setState → field path,
|
|
620
640
|
// so `accept`, `maxSize`, `multiple` and the removal list behave as on web.
|
|
@@ -912,6 +932,29 @@ export function FileUpload<
|
|
|
912
932
|
: () => {
|
|
913
933
|
void openNativePicker(onValueChange, onBlurHandler);
|
|
914
934
|
};
|
|
935
|
+
requestFilesRef.current = requestFiles;
|
|
936
|
+
const hiddenInput = isWeb ? (
|
|
937
|
+
<input
|
|
938
|
+
ref={fileInputRef}
|
|
939
|
+
type="file"
|
|
940
|
+
accept={accept}
|
|
941
|
+
multiple={multiple}
|
|
942
|
+
tabIndex={-1}
|
|
943
|
+
onChange={(e) => {
|
|
944
|
+
handleFileSelect(e.target.files, onValueChange, e.target.files);
|
|
945
|
+
onBlurHandler?.();
|
|
946
|
+
}}
|
|
947
|
+
style={{ display: "none" }}
|
|
948
|
+
disabled={!canInteract}
|
|
949
|
+
/>
|
|
950
|
+
) : null;
|
|
951
|
+
if (trigger)
|
|
952
|
+
return (
|
|
953
|
+
<YStack data-file-upload="trigger" alignItems="flex-start">
|
|
954
|
+
{trigger(requestFiles)}
|
|
955
|
+
{hiddenInput}
|
|
956
|
+
</YStack>
|
|
957
|
+
);
|
|
915
958
|
|
|
916
959
|
// Drag-and-drop, keyboard activation and the focus ring are DOM concerns;
|
|
917
960
|
// native gets a plain press target on the same surface instead.
|
|
@@ -1150,21 +1193,7 @@ export function FileUpload<
|
|
|
1150
1193
|
</Text>
|
|
1151
1194
|
)}
|
|
1152
1195
|
|
|
1153
|
-
{
|
|
1154
|
-
<input
|
|
1155
|
-
ref={fileInputRef}
|
|
1156
|
-
type="file"
|
|
1157
|
-
accept={accept}
|
|
1158
|
-
multiple={multiple}
|
|
1159
|
-
tabIndex={-1}
|
|
1160
|
-
onChange={(e) => {
|
|
1161
|
-
handleFileSelect(e.target.files, onValueChange, e.target.files);
|
|
1162
|
-
onBlurHandler?.();
|
|
1163
|
-
}}
|
|
1164
|
-
style={{ display: "none" }}
|
|
1165
|
-
disabled={!canInteract}
|
|
1166
|
-
/>
|
|
1167
|
-
) : null}
|
|
1196
|
+
{hiddenInput}
|
|
1168
1197
|
</YStack>
|
|
1169
1198
|
|
|
1170
1199
|
{((multiple && hasRows) || transfers.length > 0) && (
|
|
@@ -18,8 +18,9 @@ import {
|
|
|
18
18
|
type DisabledSurfaceProps,
|
|
19
19
|
type KnobProps,
|
|
20
20
|
} from "@multiplatform.one/theme";
|
|
21
|
-
import type { DragEvent, ReactNode } from "react";
|
|
22
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
21
|
+
import type { DragEvent, ReactNode, Ref } from "react";
|
|
22
|
+
import { useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
23
|
+
import type { FieldPickerHandle, FieldPickerTrigger } from "../pickerHandle";
|
|
23
24
|
import type { LabelProps, SizeTokens } from "tamagui";
|
|
24
25
|
import { Image, Text as TamaguiText, View, YStack, isWeb, styled } from "tamagui";
|
|
25
26
|
import { IconButton } from "../../Button";
|
|
@@ -78,6 +79,10 @@ export interface ImageFieldProps {
|
|
|
78
79
|
objectFit?: "cover" | "contain" | "fill" | "none";
|
|
79
80
|
/** When true, renders a skeleton placeholder instead of the image field */
|
|
80
81
|
skeleton?: boolean;
|
|
82
|
+
/** LC-290: `open()` runs the picker from the app's own control. */
|
|
83
|
+
ref?: Ref<FieldPickerHandle>;
|
|
84
|
+
/** LC-290: the app's own control, drawn in place of the image frame; call `open` from its press. */
|
|
85
|
+
trigger?: FieldPickerTrigger;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
const ImageFrame = styled(View, {
|
|
@@ -112,6 +117,8 @@ type ImageControlProps = {
|
|
|
112
117
|
onChangeProp?: (val: string | File | null) => void;
|
|
113
118
|
onInvalid?: (message: string | undefined) => void;
|
|
114
119
|
blurHandler?: (...args: any[]) => void;
|
|
120
|
+
pickerRef?: Ref<FieldPickerHandle>;
|
|
121
|
+
trigger?: FieldPickerTrigger;
|
|
115
122
|
};
|
|
116
123
|
|
|
117
124
|
function isImageFile(file: File, accept: string): boolean {
|
|
@@ -148,6 +155,8 @@ function ImageControl({
|
|
|
148
155
|
onChangeProp,
|
|
149
156
|
onInvalid,
|
|
150
157
|
blurHandler,
|
|
158
|
+
pickerRef,
|
|
159
|
+
trigger,
|
|
151
160
|
}: ImageControlProps) {
|
|
152
161
|
const describedBy = useFieldDescribedBy();
|
|
153
162
|
const fieldA11y = useFieldA11y();
|
|
@@ -230,6 +239,7 @@ function ImageControl({
|
|
|
230
239
|
if (!canInteract) return;
|
|
231
240
|
fileInputRef.current?.click();
|
|
232
241
|
}, [canInteract]);
|
|
242
|
+
useImperativeHandle(pickerRef, () => ({ open: openFileDialog }), [openFileDialog]);
|
|
233
243
|
|
|
234
244
|
const isInteractiveChild = useCallback((e: { target: unknown; currentTarget: unknown }) => {
|
|
235
245
|
const target = e.target as HTMLElement | null;
|
|
@@ -313,6 +323,31 @@ function ImageControl({
|
|
|
313
323
|
const emptyName =
|
|
314
324
|
typeof label === "string" && label ? t("{{label}}: add image", { label }) : t("Add image");
|
|
315
325
|
|
|
326
|
+
const hiddenInput = isWeb ? (
|
|
327
|
+
<input
|
|
328
|
+
ref={fileInputRef}
|
|
329
|
+
type="file"
|
|
330
|
+
accept={accept}
|
|
331
|
+
tabIndex={-1}
|
|
332
|
+
aria-hidden={true}
|
|
333
|
+
onChange={(e) => {
|
|
334
|
+
const files = e.target.files;
|
|
335
|
+
e.target.value = "";
|
|
336
|
+
if (!files || files.length === 0) return;
|
|
337
|
+
applyFile(files[0]);
|
|
338
|
+
}}
|
|
339
|
+
style={{ display: "none" }}
|
|
340
|
+
disabled={!canInteract}
|
|
341
|
+
/>
|
|
342
|
+
) : null;
|
|
343
|
+
if (trigger)
|
|
344
|
+
return (
|
|
345
|
+
<YStack alignItems="flex-start" data-image-field="trigger">
|
|
346
|
+
{trigger(openFileDialog)}
|
|
347
|
+
{hiddenInput}
|
|
348
|
+
</YStack>
|
|
349
|
+
);
|
|
350
|
+
|
|
316
351
|
return (
|
|
317
352
|
<YStack alignItems="flex-start" {...knobProps.gap}>
|
|
318
353
|
<ImageFrame
|
|
@@ -398,23 +433,7 @@ function ImageControl({
|
|
|
398
433
|
</View>
|
|
399
434
|
)}
|
|
400
435
|
|
|
401
|
-
{
|
|
402
|
-
<input
|
|
403
|
-
ref={fileInputRef}
|
|
404
|
-
type="file"
|
|
405
|
-
accept={accept}
|
|
406
|
-
tabIndex={-1}
|
|
407
|
-
aria-hidden={true}
|
|
408
|
-
onChange={(e) => {
|
|
409
|
-
const files = e.target.files;
|
|
410
|
-
e.target.value = "";
|
|
411
|
-
if (!files || files.length === 0) return;
|
|
412
|
-
applyFile(files[0]);
|
|
413
|
-
}}
|
|
414
|
-
style={{ display: "none" }}
|
|
415
|
-
disabled={!canInteract}
|
|
416
|
-
/>
|
|
417
|
-
)}
|
|
436
|
+
{hiddenInput}
|
|
418
437
|
</ImageFrame>
|
|
419
438
|
|
|
420
439
|
{showRemove && hasValue && canInteract && !inTableCell && (
|
|
@@ -453,6 +472,8 @@ export function ImageField({
|
|
|
453
472
|
placeholder,
|
|
454
473
|
objectFit = "cover",
|
|
455
474
|
skeleton,
|
|
475
|
+
ref,
|
|
476
|
+
trigger,
|
|
456
477
|
}: ImageFieldProps) {
|
|
457
478
|
const { resolvedForm, knobProps, disabledState, id } = useFormField({
|
|
458
479
|
form: formProp,
|
|
@@ -486,6 +507,8 @@ export function ImageField({
|
|
|
486
507
|
disabledSurface: disabled ? disabledState.surfaceKnobProps : undefined,
|
|
487
508
|
onChangeProp,
|
|
488
509
|
onInvalid: setValidationError,
|
|
510
|
+
pickerRef: ref,
|
|
511
|
+
trigger,
|
|
489
512
|
};
|
|
490
513
|
|
|
491
514
|
if (!resolvedForm || !name) {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { Field, FieldLayout, useFieldDescribedBy } from "../../fieldLayout";
|
|
26
26
|
import { Skeleton } from "../../Skeleton";
|
|
27
27
|
import { formControlColors, formSelectedColors } from "../../shared/colorRamps";
|
|
28
|
+
import { useControlTrackFill } from "../../shared/controlTrack";
|
|
28
29
|
import { useIsInTableCell } from "../../shared/tableCellContext";
|
|
29
30
|
import { bidiIsolate, t } from "../../shared/t";
|
|
30
31
|
import {
|
|
@@ -186,6 +187,7 @@ function ProgressTrack({
|
|
|
186
187
|
blurHandler,
|
|
187
188
|
}: ProgressTrackProps) {
|
|
188
189
|
const describedBy = useFieldDescribedBy();
|
|
190
|
+
const trackFill = useControlTrackFill();
|
|
189
191
|
const trackSize = size ?? sizeToken;
|
|
190
192
|
const percent = Math.round((progressValue / max) * 100);
|
|
191
193
|
const motion = indicatorMotion(knobProps.motion.slide);
|
|
@@ -197,6 +199,7 @@ function ProgressTrack({
|
|
|
197
199
|
size={trackSize}
|
|
198
200
|
overflow="hidden"
|
|
199
201
|
outlined={knobProps.outlined}
|
|
202
|
+
{...(knobProps.outlined ? undefined : { backgroundColor: trackFill })}
|
|
200
203
|
{...knobProps.borderRadius}
|
|
201
204
|
{...(knobProps.outlined ? knobProps.inputSurface : { borderWidth: 0 })}
|
|
202
205
|
{...(knobProps.outlined ? { borderColor: formControlColors.boundary } : undefined)}
|
|
@@ -25,6 +25,17 @@ const SystemSliderProbe = vi.hoisted(
|
|
|
25
25
|
data-rail={props.maximumTrackTintColor}
|
|
26
26
|
data-thumb={props.thumbTintColor ?? ""}
|
|
27
27
|
data-label={props.accessibilityLabel ?? ""}
|
|
28
|
+
data-accessible={String(props.accessible ?? "")}
|
|
29
|
+
data-role={props.accessibilityRole ?? ""}
|
|
30
|
+
data-a11y-value={JSON.stringify(props.accessibilityValue ?? null)}
|
|
31
|
+
data-actions={(props.accessibilityActions ?? [])
|
|
32
|
+
.map((a: { name: string }) => a.name)
|
|
33
|
+
.join(",")}
|
|
34
|
+
onKeyDown={(event) => {
|
|
35
|
+
const actionName =
|
|
36
|
+
event.key === "ArrowUp" ? "increment" : event.key === "ArrowDown" ? "decrement" : "";
|
|
37
|
+
if (actionName) props.onAccessibilityAction?.({ nativeEvent: { actionName } });
|
|
38
|
+
}}
|
|
28
39
|
data-min={props.minimumValue}
|
|
29
40
|
data-max={props.maximumValue}
|
|
30
41
|
data-step={props.step}
|
|
@@ -101,6 +112,39 @@ describe("Slider on iOS and Android", () => {
|
|
|
101
112
|
},
|
|
102
113
|
);
|
|
103
114
|
|
|
115
|
+
it("is an adjustable element named by its label, valued, stepping on VoiceOver swipes (FZ-8905)", () => {
|
|
116
|
+
const onChange = vi.fn();
|
|
117
|
+
const result = renderWithProviders(
|
|
118
|
+
<Slider
|
|
119
|
+
label="Brightness"
|
|
120
|
+
defaultValue={[80]}
|
|
121
|
+
min={0}
|
|
122
|
+
max={100}
|
|
123
|
+
step={1}
|
|
124
|
+
onChange={onChange}
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
const view = result.getByTestId("system-slider");
|
|
128
|
+
expect(view.dataset).toMatchObject({
|
|
129
|
+
accessible: "true",
|
|
130
|
+
role: "adjustable",
|
|
131
|
+
label: "Brightness",
|
|
132
|
+
actions: "increment,decrement",
|
|
133
|
+
});
|
|
134
|
+
expect(JSON.parse(view.dataset.a11yValue ?? "null")).toEqual({
|
|
135
|
+
min: 0,
|
|
136
|
+
max: 100,
|
|
137
|
+
now: 80,
|
|
138
|
+
text: "80",
|
|
139
|
+
});
|
|
140
|
+
fireEvent.keyDown(view, { key: "ArrowUp" });
|
|
141
|
+
expect(onChange).toHaveBeenLastCalledWith([81]);
|
|
142
|
+
expect(JSON.parse(view.dataset.a11yValue ?? "null")).toMatchObject({ now: 81 });
|
|
143
|
+
fireEvent.keyDown(view, { key: "ArrowDown" });
|
|
144
|
+
fireEvent.keyDown(view, { key: "ArrowDown" });
|
|
145
|
+
expect(onChange).toHaveBeenLastCalledWith([79]);
|
|
146
|
+
});
|
|
147
|
+
|
|
104
148
|
it("tints the Android thumb with the fill", () => {
|
|
105
149
|
system.os = "android";
|
|
106
150
|
const result = renderInScheme(<Slider label="Volume" />, "light");
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
import { Skeleton } from "../../Skeleton";
|
|
35
35
|
import { Field, FieldLayout, useFieldA11y, useFieldDescribedBy } from "../../fieldLayout";
|
|
36
36
|
import { formCommonColors, formControlColors, formSelectedColors } from "../../shared/colorRamps";
|
|
37
|
+
import { useControlTrackFill } from "../../shared/controlTrack";
|
|
37
38
|
import { loadSystemSlider } from "../../shared/systemControlModules";
|
|
38
39
|
import {
|
|
39
40
|
type SystemPlatform,
|
|
@@ -74,12 +75,16 @@ function radiusStopFromToken(token: number | string): BorderRadius {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
// Track styling helper - returns props to pass to TamaguiSlider.Track
|
|
77
|
-
const getTrackProps = (
|
|
78
|
+
const getTrackProps = (
|
|
79
|
+
outlined: boolean | undefined,
|
|
80
|
+
borderRadius: number,
|
|
81
|
+
trackFill: string = formControlColors.background,
|
|
82
|
+
) => ({
|
|
78
83
|
unstyled: false,
|
|
79
84
|
height: TRACK_HEIGHT,
|
|
80
85
|
width: "100%",
|
|
81
86
|
alignSelf: "center" as const,
|
|
82
|
-
backgroundColor: outlined ? "transparent" :
|
|
87
|
+
backgroundColor: outlined ? "transparent" : trackFill,
|
|
83
88
|
borderWidth: outlined ? 1 : 0,
|
|
84
89
|
borderColor: outlined ? formControlColors.border : undefined,
|
|
85
90
|
borderRadius,
|
|
@@ -270,9 +275,26 @@ function SystemSliderControl({
|
|
|
270
275
|
onBlur?: (...args: any[]) => void;
|
|
271
276
|
}) {
|
|
272
277
|
const literal = useThemeLiteral();
|
|
278
|
+
const trackFill = useControlTrackFill();
|
|
273
279
|
const [displayValue, setDisplayValue] = useState(value);
|
|
274
280
|
useEffect(() => setDisplayValue(value), [value]);
|
|
275
281
|
const fill = literal(hasError ? formCommonColors.error : formSelectedColors.mark);
|
|
282
|
+
// A Fabric view is an accessibility element only when `accessible` says so:
|
|
283
|
+
// without it UIKit drops the UISlider from the tree (FZ-8905). It takes the
|
|
284
|
+
// MPO thumb's VoiceOver contract: adjustable, named by its field, valued,
|
|
285
|
+
// and one step per swipe.
|
|
286
|
+
const fieldA11y = useFieldA11y();
|
|
287
|
+
const move = (next: number) => {
|
|
288
|
+
setDisplayValue(next);
|
|
289
|
+
if (disabled || readOnly) return;
|
|
290
|
+
onChangeValue(next);
|
|
291
|
+
};
|
|
292
|
+
const adjust = (direction: 1 | -1) => {
|
|
293
|
+
if (disabled || readOnly) return;
|
|
294
|
+
const next = snapSliderValue(displayValue + direction * step, min, max, step);
|
|
295
|
+
if (next !== displayValue) move(next);
|
|
296
|
+
};
|
|
297
|
+
const readout = formatSliderOutput([displayValue], step);
|
|
276
298
|
return (
|
|
277
299
|
<XStack height={height} alignItems="center" width="100%" gap="$3">
|
|
278
300
|
<View
|
|
@@ -291,15 +313,20 @@ function SystemSliderControl({
|
|
|
291
313
|
step={step}
|
|
292
314
|
disabled={disabled}
|
|
293
315
|
minimumTrackTintColor={fill}
|
|
294
|
-
maximumTrackTintColor={literal(
|
|
316
|
+
maximumTrackTintColor={literal(trackFill)}
|
|
295
317
|
{...(platform === "android" && { thumbTintColor: fill })}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
318
|
+
accessible
|
|
319
|
+
accessibilityRole="adjustable"
|
|
320
|
+
accessibilityLabel={accessibilityLabel ?? fieldA11y?.label ?? t("Slider")}
|
|
321
|
+
accessibilityHint={accessibilityHint ?? fieldA11y?.description}
|
|
322
|
+
accessibilityValue={{ min, max, now: displayValue, text: readout }}
|
|
323
|
+
accessibilityState={{ disabled: Boolean(disabled) }}
|
|
324
|
+
accessibilityActions={A11Y_ADJUSTABLE_ACTIONS}
|
|
325
|
+
onAccessibilityAction={(event: { nativeEvent: { actionName: string } }) => {
|
|
326
|
+
if (event.nativeEvent.actionName === "increment") adjust(1);
|
|
327
|
+
else if (event.nativeEvent.actionName === "decrement") adjust(-1);
|
|
302
328
|
}}
|
|
329
|
+
onValueChange={move}
|
|
303
330
|
onSlidingComplete={() => onBlur?.()}
|
|
304
331
|
/>
|
|
305
332
|
</View>
|
|
@@ -310,7 +337,7 @@ function SystemSliderControl({
|
|
|
310
337
|
textAlign="right"
|
|
311
338
|
aria-hidden
|
|
312
339
|
>
|
|
313
|
-
{
|
|
340
|
+
{readout}
|
|
314
341
|
</Paragraph>
|
|
315
342
|
</XStack>
|
|
316
343
|
);
|
|
@@ -383,6 +410,7 @@ function SliderControl({
|
|
|
383
410
|
// with their thumbs (RULINGS-2026-08-28 §R2).
|
|
384
411
|
const radiusStop = radiusStopFromToken(borderRadius);
|
|
385
412
|
const railRadius = resolveRadiusClass("BINARY", radiusStop, { heightPx: TRACK_HEIGHT });
|
|
413
|
+
const trackFill = useControlTrackFill();
|
|
386
414
|
|
|
387
415
|
// Native jump tween rides transform (FLIP) — the RN driver cannot animate
|
|
388
416
|
// the thumb's `left` percent (see ./jumpMotion.ts). Web keeps the css
|
|
@@ -634,12 +662,12 @@ function SliderControl({
|
|
|
634
662
|
height={TRACK_HEIGHT}
|
|
635
663
|
borderRadius={railRadius}
|
|
636
664
|
>
|
|
637
|
-
<TamaguiSlider.Track {...getTrackProps(outlined, railRadius)}>
|
|
665
|
+
<TamaguiSlider.Track {...getTrackProps(outlined, railRadius, trackFill)}>
|
|
638
666
|
{trackActive}
|
|
639
667
|
</TamaguiSlider.Track>
|
|
640
668
|
</View>
|
|
641
669
|
) : (
|
|
642
|
-
<TamaguiSlider.Track {...getTrackProps(outlined, railRadius)}>
|
|
670
|
+
<TamaguiSlider.Track {...getTrackProps(outlined, railRadius, trackFill)}>
|
|
643
671
|
{trackActive}
|
|
644
672
|
</TamaguiSlider.Track>
|
|
645
673
|
)}
|
|
@@ -188,3 +188,45 @@ export const InTable: Story = {
|
|
|
188
188
|
);
|
|
189
189
|
},
|
|
190
190
|
};
|
|
191
|
+
|
|
192
|
+
/** LC-114: a buttonless Stepper with `hug` is the number field. It is as wide as its value plus the currency glyph or unit, and a long signed value with decimals still fits (FZ-7598). */
|
|
193
|
+
export const HuggingCurrency: Story = {
|
|
194
|
+
render: () => (
|
|
195
|
+
<YStack gap="$4" width="100%">
|
|
196
|
+
<Stepper
|
|
197
|
+
label="Daily loss cap"
|
|
198
|
+
aria-label="Daily loss cap"
|
|
199
|
+
currency="USD"
|
|
200
|
+
hug
|
|
201
|
+
showButtons={false}
|
|
202
|
+
min={0}
|
|
203
|
+
max={50000}
|
|
204
|
+
precision={2}
|
|
205
|
+
defaultValue={2500}
|
|
206
|
+
/>
|
|
207
|
+
<Stepper
|
|
208
|
+
label="Position limit"
|
|
209
|
+
aria-label="Position limit"
|
|
210
|
+
unit="%"
|
|
211
|
+
hug
|
|
212
|
+
showButtons={false}
|
|
213
|
+
min={0}
|
|
214
|
+
max={100}
|
|
215
|
+
precision={1}
|
|
216
|
+
defaultValue={12.5}
|
|
217
|
+
/>
|
|
218
|
+
<Stepper
|
|
219
|
+
label="Adjustment"
|
|
220
|
+
aria-label="Adjustment"
|
|
221
|
+
currency="USD"
|
|
222
|
+
hug
|
|
223
|
+
showButtons={false}
|
|
224
|
+
min={-9999999}
|
|
225
|
+
max={9999999}
|
|
226
|
+
precision={2}
|
|
227
|
+
defaultValue={-1234567.89}
|
|
228
|
+
/>
|
|
229
|
+
<Stepper label="Contracts" aria-label="Contracts" hug min={1} max={500} defaultValue={12} />
|
|
230
|
+
</YStack>
|
|
231
|
+
),
|
|
232
|
+
};
|
|
@@ -499,16 +499,20 @@ describe("Switch thumb and pointer paint", () => {
|
|
|
499
499
|
expect(paint(track, "_bg-")).toBe("var(--switchTrackOff)");
|
|
500
500
|
});
|
|
501
501
|
|
|
502
|
-
it.each([false, true])(
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
502
|
+
it.each([false, true])(
|
|
503
|
+
"grown=%s keeps the outlined ring on its 3:1 edge and washes the track on hover (LC-299)",
|
|
504
|
+
(grown) => {
|
|
505
|
+
const { frame, track } = mount({ grown, outlined: true });
|
|
506
|
+
expect(track.className).toContain("_bg-transparent");
|
|
507
|
+
expect(paint(track, "_btc-")).toBe("var(--switchTrackOffBorder)");
|
|
508
|
+
fireEvent.mouseEnter(frame);
|
|
509
|
+
expect(paint(track, "_bg-")).toBe("var(--switchTrackOffHover)");
|
|
510
|
+
expect(paint(track, "_btc-")).toBe("var(--switchTrackOffBorder)");
|
|
511
|
+
fireEvent.mouseLeave(frame);
|
|
512
|
+
expect(track.className).toContain("_bg-transparent");
|
|
513
|
+
expect(paint(track, "_btc-")).toBe("var(--switchTrackOffBorder)");
|
|
514
|
+
},
|
|
515
|
+
);
|
|
512
516
|
|
|
513
517
|
it("composes the caller's mouse handlers once", () => {
|
|
514
518
|
const onMouseEnter = vi.fn();
|
|
@@ -198,7 +198,7 @@ const SwitchFrameStyled = styled(View, {
|
|
|
198
198
|
borderRadius: 1000,
|
|
199
199
|
backgroundColor: formSwitchColors.track.off,
|
|
200
200
|
borderWidth: 0,
|
|
201
|
-
borderColor: formSwitchColors.track.
|
|
201
|
+
borderColor: formSwitchColors.track.offBorder,
|
|
202
202
|
outlineWidth: 0,
|
|
203
203
|
focusStyle: { outlineWidth: 0 },
|
|
204
204
|
// LC-71: ring is painted from keyboard modality on the track, never
|
|
@@ -214,7 +214,7 @@ const SwitchFrameStyled = styled(View, {
|
|
|
214
214
|
},
|
|
215
215
|
false: {
|
|
216
216
|
backgroundColor: formSwitchColors.track.off,
|
|
217
|
-
borderColor: formSwitchColors.track.
|
|
217
|
+
borderColor: formSwitchColors.track.offBorder,
|
|
218
218
|
},
|
|
219
219
|
},
|
|
220
220
|
|
|
@@ -406,6 +406,8 @@ function SwitchControl({
|
|
|
406
406
|
: hovered
|
|
407
407
|
? formSwitchColors.track.offHover
|
|
408
408
|
: formSwitchColors.track.off;
|
|
409
|
+
const offFill = outlined && !hovered && !pressed ? "transparent" : offPaint;
|
|
410
|
+
const offRing = outlined ? undefined : knobProps.trackRing;
|
|
409
411
|
const { thumbInset, thumbSize, travel: dragDistance } = footprint;
|
|
410
412
|
const motion = resolveSwitchMotion(transition);
|
|
411
413
|
const [dragX, setDragX] = useState<number | null>(null);
|
|
@@ -562,8 +564,9 @@ function SwitchControl({
|
|
|
562
564
|
borderColor: formSwitchColors.track.on,
|
|
563
565
|
}
|
|
564
566
|
: {
|
|
565
|
-
backgroundColor:
|
|
566
|
-
borderColor:
|
|
567
|
+
backgroundColor: offFill,
|
|
568
|
+
borderColor: formSwitchColors.track.offBorder,
|
|
569
|
+
...offRing,
|
|
567
570
|
})}
|
|
568
571
|
{...(kbFocus ? { "data-kb-focus": "true" } : undefined)}
|
|
569
572
|
{...(frameClassName ? { className: frameClassName } : undefined)}
|
|
@@ -653,10 +656,11 @@ function SwitchControl({
|
|
|
653
656
|
borderRadius={cellTrack.borderRadius}
|
|
654
657
|
{...radiusResolution}
|
|
655
658
|
borderWidth={cellTrack.borderWidth}
|
|
656
|
-
borderColor={
|
|
657
|
-
|
|
658
|
-
displayChecked ? formSwitchColors.track.on : outlined ? "transparent" : offPaint
|
|
659
|
+
borderColor={
|
|
660
|
+
displayChecked ? formSwitchColors.track.on : formSwitchColors.track.offBorder
|
|
659
661
|
}
|
|
662
|
+
backgroundColor={displayChecked ? formSwitchColors.track.on : offFill}
|
|
663
|
+
{...(displayChecked ? undefined : offRing)}
|
|
660
664
|
{...transitionProps({ ...knobProps.motion.control, transition: motion }, ["borderWidth"])}
|
|
661
665
|
pointerEvents="none"
|
|
662
666
|
className={kbFocus ? SWITCH_KB_RING_CLASS : undefined}
|
|
@@ -160,11 +160,6 @@ export function TimePickerInputTrigger({
|
|
|
160
160
|
onPress={isWeb && interactive ? onOpen : undefined}
|
|
161
161
|
{...(disabled ? { opacity: 0.5, pointerEvents: "none" as const } : undefined)}
|
|
162
162
|
>
|
|
163
|
-
<InputParts.Section data-testid="time-picker-glyph">
|
|
164
|
-
<InputParts.Icon adornment="leading" color={formCommonColors.muted}>
|
|
165
|
-
{clockIcon ?? <ClockIcon />}
|
|
166
|
-
</InputParts.Icon>
|
|
167
|
-
</InputParts.Section>
|
|
168
163
|
<InputParts.Section>
|
|
169
164
|
<InputParts.Area
|
|
170
165
|
readOnly
|
|
@@ -174,7 +169,6 @@ export function TimePickerInputTrigger({
|
|
|
174
169
|
placeholder={placeholder}
|
|
175
170
|
disabled={disabled}
|
|
176
171
|
color={formCommonColors.text}
|
|
177
|
-
placeholderTextColor={formCommonColors.muted}
|
|
178
172
|
aria-label={ariaLabel}
|
|
179
173
|
aria-required={ariaRequired}
|
|
180
174
|
aria-invalid={ariaInvalid}
|
|
@@ -196,11 +190,17 @@ export function TimePickerInputTrigger({
|
|
|
196
190
|
}
|
|
197
191
|
/>
|
|
198
192
|
</InputParts.Section>
|
|
199
|
-
{value && isWeb && !readOnly ? (
|
|
193
|
+
{value && isWeb && !readOnly && !disabled ? (
|
|
200
194
|
<InputParts.Section>
|
|
201
195
|
<TimePickerClearButton onClear={onClear} disabled={disabled} />
|
|
202
196
|
</InputParts.Section>
|
|
203
|
-
) :
|
|
197
|
+
) : (
|
|
198
|
+
<InputParts.Section data-testid="time-picker-glyph">
|
|
199
|
+
<InputParts.Icon adornment="trailing" color={formCommonColors.muted}>
|
|
200
|
+
{clockIcon ?? <ClockIcon />}
|
|
201
|
+
</InputParts.Icon>
|
|
202
|
+
</InputParts.Section>
|
|
203
|
+
)}
|
|
204
204
|
</InputParts.Box>
|
|
205
205
|
</View>
|
|
206
206
|
);
|
|
@@ -25,7 +25,12 @@ import {
|
|
|
25
25
|
} from "tamagui";
|
|
26
26
|
import { Skeleton } from "../../Skeleton";
|
|
27
27
|
import { Field, FieldLayout } from "../../fieldLayout";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
formButtonColors,
|
|
30
|
+
formCommonColors,
|
|
31
|
+
formControlColors,
|
|
32
|
+
formInputColors,
|
|
33
|
+
} from "../../shared/colorRamps";
|
|
29
34
|
import { warnBareDisabled } from "../../shared/devWarn";
|
|
30
35
|
import { choiceItemA11y, composeA11yName } from "../../shared/nativeA11y";
|
|
31
36
|
import { loadSystemSegmentedControl } from "../../shared/systemControlModules";
|
|
@@ -693,6 +698,7 @@ export function ToggleGroup({
|
|
|
693
698
|
{...toggleProps}
|
|
694
699
|
{...knobProps.borderRadius}
|
|
695
700
|
{...knobProps.inputSurface}
|
|
701
|
+
borderColor={formControlColors.edge}
|
|
696
702
|
overflow="hidden"
|
|
697
703
|
backgroundColor={knobProps.outlined ? "transparent" : formInputColors.background.base}
|
|
698
704
|
{...(isWeb
|