@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
|
@@ -37,9 +37,9 @@ const SCHEMES = ["light", "dark"] as const;
|
|
|
37
37
|
type Scheme = (typeof SCHEMES)[number];
|
|
38
38
|
const WASH: Record<Scheme, { rest: string; hover: string; press: string }> = {
|
|
39
39
|
light: { rest: "color3", hover: "color4", press: "color5" },
|
|
40
|
-
dark: { rest: "
|
|
40
|
+
dark: { rest: "color5", hover: "color6", press: "color7" },
|
|
41
41
|
};
|
|
42
|
-
const ELEVATED_DARK_WASH = { rest: "
|
|
42
|
+
const ELEVATED_DARK_WASH = { rest: "color6", hover: "color7", press: "color8" };
|
|
43
43
|
|
|
44
44
|
function houseRender(ui: ReactElement, scheme: Scheme = "light", knobs?: object) {
|
|
45
45
|
return render(
|
|
@@ -186,7 +186,7 @@ function ControlGroupImpl({ children, orientation = "horizontal", ...props }: Co
|
|
|
186
186
|
<View
|
|
187
187
|
key={`seam-${index}`}
|
|
188
188
|
{...(isVertical ? hairline.line : hairline.vline)}
|
|
189
|
-
backgroundColor={
|
|
189
|
+
backgroundColor={formCommonColors.divider}
|
|
190
190
|
flexGrow={0}
|
|
191
191
|
flexShrink={0}
|
|
192
192
|
alignSelf="stretch"
|
|
@@ -235,6 +235,7 @@ function ControlGroupImpl({ children, orientation = "horizontal", ...props }: Co
|
|
|
235
235
|
backgroundColor={knobProps.outlined ? "transparent" : formInputColors.background.base}
|
|
236
236
|
{...knobProps.inputSurface}
|
|
237
237
|
{...knobProps.borderRadius}
|
|
238
|
+
borderColor={formInputColors.border.base}
|
|
238
239
|
{...elevationWrapperProps}
|
|
239
240
|
{...transitionProps(knobProps.motion.control, [
|
|
240
241
|
"borderRadius",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
+
import { act, screen } from "@testing-library/react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { Text } from "tamagui";
|
|
6
|
+
import { holdCueStartMs } from "../hooks/useHoldCue";
|
|
7
|
+
import { FloatingPanel } from "./index.native";
|
|
8
|
+
|
|
9
|
+
vi.mock("react-native-web", async (importOriginal) => {
|
|
10
|
+
const actual = await importOriginal<typeof import("react-native")>();
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
ActionSheetIOS: undefined,
|
|
14
|
+
Modal: ({ children, visible }: { children: import("react").ReactNode; visible: boolean }) =>
|
|
15
|
+
visible ? children : null,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.useFakeTimers();
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.useRealTimers();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function ContextPanel() {
|
|
27
|
+
const [open, setOpen] = useState(false);
|
|
28
|
+
return (
|
|
29
|
+
<FloatingPanel
|
|
30
|
+
open={open}
|
|
31
|
+
onOpenChange={setOpen}
|
|
32
|
+
openOn="contextmenu"
|
|
33
|
+
trigger={<Text>Row</Text>}
|
|
34
|
+
triggerA11y={{ label: "Row" }}
|
|
35
|
+
>
|
|
36
|
+
<Text>Copy</Text>
|
|
37
|
+
</FloatingPanel>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function press(target: Element, type: "mousedown" | "mouseup") {
|
|
42
|
+
act(() => {
|
|
43
|
+
target.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, button: 0 }));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("FloatingPanel native context trigger hold cue (LC-197, FZ-8902)", () => {
|
|
48
|
+
it("shows the cue partway to the 500ms hold and clears it on release", () => {
|
|
49
|
+
renderWithProviders(<ContextPanel />);
|
|
50
|
+
const trigger = screen.getByRole("button", { name: "Row" });
|
|
51
|
+
press(trigger, "mousedown");
|
|
52
|
+
expect(trigger).not.toHaveAttribute("data-hold");
|
|
53
|
+
act(() => {
|
|
54
|
+
vi.advanceTimersByTime(holdCueStartMs);
|
|
55
|
+
});
|
|
56
|
+
expect(trigger).toHaveAttribute("data-hold", "progress");
|
|
57
|
+
expect(screen.queryByText("Copy")).toBeNull();
|
|
58
|
+
press(trigger, "mouseup");
|
|
59
|
+
expect(trigger).not.toHaveAttribute("data-hold");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("drops the cue when the hold opens the menu", () => {
|
|
63
|
+
renderWithProviders(<ContextPanel />);
|
|
64
|
+
const trigger = screen.getByRole("button", { name: "Row" });
|
|
65
|
+
press(trigger, "mousedown");
|
|
66
|
+
act(() => {
|
|
67
|
+
vi.advanceTimersByTime(600);
|
|
68
|
+
});
|
|
69
|
+
expect(screen.getByText("Copy")).toBeTruthy();
|
|
70
|
+
expect(trigger).not.toHaveAttribute("data-hold");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -124,6 +124,7 @@ import {
|
|
|
124
124
|
scrollArrowBand,
|
|
125
125
|
scrollArrowThreshold,
|
|
126
126
|
} from "../shared/checkedOnField";
|
|
127
|
+
import { holdCueScale, useHoldCue } from "../hooks/useHoldCue";
|
|
127
128
|
import { useInsideOverlay, useOverlayZIndex } from "../shared/overlayStack";
|
|
128
129
|
import { OverlayWindow } from "../shared/OverlayWindow";
|
|
129
130
|
import { t } from "../shared/t";
|
|
@@ -244,8 +245,10 @@ export function FloatingPanel({
|
|
|
244
245
|
const handleTriggerPress = useCallback(() => {
|
|
245
246
|
if (!disabled) onOpenChange(!open);
|
|
246
247
|
}, [disabled, onOpenChange, open]);
|
|
248
|
+
const hold = useHoldCue();
|
|
247
249
|
const handleTriggerLongPress = useCallback(
|
|
248
250
|
(event?: GestureResponderEvent) => {
|
|
251
|
+
hold.stop();
|
|
249
252
|
if (disabled) return;
|
|
250
253
|
const { pageX, pageY } = event?.nativeEvent ?? {};
|
|
251
254
|
setPressPoint(
|
|
@@ -253,7 +256,7 @@ export function FloatingPanel({
|
|
|
253
256
|
);
|
|
254
257
|
onOpenChange(true);
|
|
255
258
|
},
|
|
256
|
-
[disabled, onOpenChange],
|
|
259
|
+
[disabled, onOpenChange, hold.stop],
|
|
257
260
|
);
|
|
258
261
|
const isContextMenu = openOn === "contextmenu";
|
|
259
262
|
// A long-press menu never eats its trigger's tap: the web lets the primary
|
|
@@ -629,6 +632,10 @@ export function FloatingPanel({
|
|
|
629
632
|
? {
|
|
630
633
|
onLongPress: handleTriggerLongPress,
|
|
631
634
|
delayLongPress: 500,
|
|
635
|
+
...(disabled
|
|
636
|
+
? undefined
|
|
637
|
+
: { delayPressIn: 0, onPressIn: hold.start, onPressOut: hold.stop }),
|
|
638
|
+
...(hold.holding ? { dataSet: { hold: "progress" } } : undefined),
|
|
632
639
|
...(triggerOwn?.onPress && !disabled ? { onPress: triggerOwn.onPress } : undefined),
|
|
633
640
|
}
|
|
634
641
|
: { onPress: handleTriggerPress })}
|
|
@@ -648,6 +655,7 @@ export function FloatingPanel({
|
|
|
648
655
|
alignSelf: "stretch",
|
|
649
656
|
width: triggerSizing === "content" ? undefined : "100%",
|
|
650
657
|
opacity: disabled ? 0.5 : 1,
|
|
658
|
+
...(hold.holding ? { transform: [{ scale: holdCueScale }] } : undefined),
|
|
651
659
|
}}
|
|
652
660
|
>
|
|
653
661
|
{/* Own the hit target — see file header for why pointerEvents="none". */}
|
package/src/InputParts/index.tsx
CHANGED
|
@@ -634,6 +634,7 @@ const InputGroupImpl = InputGroupFrame.styleable<{
|
|
|
634
634
|
{...(knobProps.elevation == null ? { elevation: undefined } : knobProps.elevationChrome)}
|
|
635
635
|
{...knobProps.borderRadius}
|
|
636
636
|
borderWidth={knobProps.fieldEdge.borderWidth}
|
|
637
|
+
borderColor={formInputColors.border.base}
|
|
637
638
|
{...hiddenEdge}
|
|
638
639
|
{...sizeTableRadius}
|
|
639
640
|
{...transitionProps(knobProps.motion.control)}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The web wheel's edge fade (FZ-8080): a LinearGradient veil of the surface the
|
|
3
|
+
* wheel sits on, never a CSS mask on the drum.
|
|
4
|
+
*/
|
|
5
|
+
import { SurfaceContext } from "@multiplatform.one/theme";
|
|
6
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
7
|
+
import { cleanup, screen } from "@testing-library/react";
|
|
8
|
+
import { createElement } from "react";
|
|
9
|
+
import { Theme, useTheme } from "tamagui";
|
|
10
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import { Wheel } from "./index";
|
|
12
|
+
|
|
13
|
+
vi.mock("tamagui/linear-gradient", () => ({
|
|
14
|
+
LinearGradient: (props: Record<string, unknown>) =>
|
|
15
|
+
createElement("span", {
|
|
16
|
+
"data-testid": props["data-testid"],
|
|
17
|
+
"data-colors": JSON.stringify(props.colors),
|
|
18
|
+
"data-locations": JSON.stringify(props.locations),
|
|
19
|
+
"data-opacity": props.opacity,
|
|
20
|
+
"data-pointer-events": props.pointerEvents,
|
|
21
|
+
}),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
afterEach(cleanup);
|
|
25
|
+
|
|
26
|
+
const items = Array.from({ length: 24 }, (_, i) => i);
|
|
27
|
+
const seen = { background: "" };
|
|
28
|
+
|
|
29
|
+
function Probe() {
|
|
30
|
+
seen.background = String(useTheme().background?.val);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("web Wheel edge fade (FZ-8080)", () => {
|
|
35
|
+
it("paints no CSS mask on the drum", () => {
|
|
36
|
+
renderWithProviders(<Wheel items={items} value={6} onChange={() => {}} />);
|
|
37
|
+
const drum = screen.getByTestId("wheel-wrapper");
|
|
38
|
+
expect(drum.style.maskImage).toBe("");
|
|
39
|
+
expect(drum.style.getPropertyValue("-webkit-mask-image")).toBe("");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("veils both ends in the page surface and leaves the selection band clear", () => {
|
|
43
|
+
renderWithProviders(
|
|
44
|
+
<>
|
|
45
|
+
<Probe />
|
|
46
|
+
<Wheel items={items} value={6} onChange={() => {}} />
|
|
47
|
+
</>,
|
|
48
|
+
);
|
|
49
|
+
const fade = screen.getByTestId("wheel-fade");
|
|
50
|
+
expect(JSON.parse(fade.getAttribute("data-colors")!)).toEqual([
|
|
51
|
+
seen.background,
|
|
52
|
+
"transparent",
|
|
53
|
+
"transparent",
|
|
54
|
+
seen.background,
|
|
55
|
+
]);
|
|
56
|
+
expect(JSON.parse(fade.getAttribute("data-locations")!)).toEqual([0, 0.32, 0.68, 1]);
|
|
57
|
+
expect(fade.getAttribute("data-opacity")).toBe("0.72");
|
|
58
|
+
expect(fade.getAttribute("data-pointer-events")).toBe("none");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("veils in the elevated surface on a dark overlay", () => {
|
|
62
|
+
renderWithProviders(
|
|
63
|
+
<Theme name="dark">
|
|
64
|
+
<SurfaceContext.Provider elevated>
|
|
65
|
+
<Probe />
|
|
66
|
+
<Wheel items={items} value={6} onChange={() => {}} />
|
|
67
|
+
</SurfaceContext.Provider>
|
|
68
|
+
</Theme>,
|
|
69
|
+
);
|
|
70
|
+
const colors = JSON.parse(screen.getByTestId("wheel-fade").getAttribute("data-colors")!);
|
|
71
|
+
expect(colors[0]).not.toBe(seen.background);
|
|
72
|
+
expect(colors[0]).toBe(colors[3]);
|
|
73
|
+
});
|
|
74
|
+
});
|
package/src/Wheel/index.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useElevatedSurface } from "@multiplatform.one/theme";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import type { GetProps } from "tamagui";
|
|
4
|
-
import { View, isWeb, styled, Text } from "tamagui";
|
|
4
|
+
import { View, isWeb, styled, Text, useTheme } from "tamagui";
|
|
5
|
+
import { LinearGradient } from "tamagui/linear-gradient";
|
|
5
6
|
import { t } from "../shared/t";
|
|
6
7
|
import { fireSelectionHaptic } from "./haptic";
|
|
7
8
|
import { NativeWheel } from "./NativeWheel";
|
|
@@ -49,14 +50,8 @@ function ensureWheelFocusStyles() {
|
|
|
49
50
|
document.head.appendChild(style);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
WebkitMaskImage:
|
|
55
|
-
"linear-gradient(to bottom, rgba(0,0,0,0.28) 0%, #000 32%, #000 68%, rgba(0,0,0,0.28) 100%)",
|
|
56
|
-
maskImage:
|
|
57
|
-
"linear-gradient(to bottom, rgba(0,0,0,0.28) 0%, #000 32%, #000 68%, rgba(0,0,0,0.28) 100%)",
|
|
58
|
-
}
|
|
59
|
-
: undefined;
|
|
53
|
+
const wheelFadeStops = [0, 0.32, 0.68, 1];
|
|
54
|
+
const wheelFadeOpacity = 0.72;
|
|
60
55
|
|
|
61
56
|
// ── Styled components ─────────────────────────────────────────
|
|
62
57
|
|
|
@@ -164,6 +159,12 @@ function WebWheel<T = string | number>(props: WheelProps<T>) {
|
|
|
164
159
|
highlightClassName,
|
|
165
160
|
} = useWheelLayout(props);
|
|
166
161
|
const columnWidth = widthProp ?? defaultColumnWidth;
|
|
162
|
+
const theme = useTheme();
|
|
163
|
+
const elevatedGround = useElevatedSurface();
|
|
164
|
+
const fadeKey = (
|
|
165
|
+
elevatedGround ? knobProps.elevatedSurface.backgroundColor : "$background"
|
|
166
|
+
) as string;
|
|
167
|
+
const fadeSurface = String(theme[fadeKey.replace(/^\$/, "") as "background"]?.val ?? fadeKey);
|
|
167
168
|
ensureWheelFocusStyles();
|
|
168
169
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
169
170
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
@@ -714,7 +715,6 @@ function WebWheel<T = string | number>(props: WheelProps<T>) {
|
|
|
714
715
|
userSelect: "none",
|
|
715
716
|
touchAction: "none",
|
|
716
717
|
cursor: disabled ? "not-allowed" : "grab",
|
|
717
|
-
...wheelMaskStyle,
|
|
718
718
|
}}
|
|
719
719
|
onPointerDown={handlePointerDown as any}
|
|
720
720
|
onPointerMove={handlePointerMove as any}
|
|
@@ -757,6 +757,20 @@ function WebWheel<T = string | number>(props: WheelProps<T>) {
|
|
|
757
757
|
);
|
|
758
758
|
})}
|
|
759
759
|
</div>
|
|
760
|
+
<LinearGradient
|
|
761
|
+
data-testid="wheel-fade"
|
|
762
|
+
pointerEvents="none"
|
|
763
|
+
position="absolute"
|
|
764
|
+
top={0}
|
|
765
|
+
right={0}
|
|
766
|
+
bottom={0}
|
|
767
|
+
left={0}
|
|
768
|
+
opacity={wheelFadeOpacity}
|
|
769
|
+
colors={[fadeSurface, "transparent", "transparent", fadeSurface]}
|
|
770
|
+
locations={wheelFadeStops}
|
|
771
|
+
start={[0, 0]}
|
|
772
|
+
end={[0, 1]}
|
|
773
|
+
/>
|
|
760
774
|
</WheelContainer>
|
|
761
775
|
);
|
|
762
776
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { CalendarBlankIcon, ClockIcon } from "@phosphor-icons/react";
|
|
2
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
3
|
+
import {
|
|
4
|
+
createThemesBuilder,
|
|
5
|
+
defaultAccentTheme,
|
|
6
|
+
defaultBaseTheme,
|
|
7
|
+
defaultBuilderOptions,
|
|
8
|
+
} from "@multiplatform.one/theme";
|
|
9
|
+
import { configWithoutAnimations } from "@tamagui/config";
|
|
10
|
+
import { animationsCSS } from "@tamagui/config/v5-css";
|
|
11
|
+
import { render } from "@testing-library/react";
|
|
12
|
+
import type { ReactElement } from "react";
|
|
13
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
14
|
+
import { TamaguiProvider, createTamagui } from "tamagui";
|
|
15
|
+
import { describe, expect, it, vi } from "vitest";
|
|
16
|
+
import { Input } from "../Input";
|
|
17
|
+
import { TimePicker } from "../TimePicker";
|
|
18
|
+
import { DatePicker, DatetimePicker } from "./index";
|
|
19
|
+
import { CalendarDays, MonthGrid, YearGrid, isSameDay, useCalendarData } from "./parts";
|
|
20
|
+
|
|
21
|
+
vi.mock("@multiplatform.one/storybook", () => ({ action: () => () => {} }));
|
|
22
|
+
vi.mock("@multiplatform.one/table", () => ({ DataTable: () => null }));
|
|
23
|
+
|
|
24
|
+
const houseThemes = createThemesBuilder(
|
|
25
|
+
defaultBaseTheme,
|
|
26
|
+
defaultAccentTheme,
|
|
27
|
+
defaultBuilderOptions,
|
|
28
|
+
).themes() as Record<string, Record<string, string>>;
|
|
29
|
+
|
|
30
|
+
const houseConfig = createTamagui({
|
|
31
|
+
...configWithoutAnimations,
|
|
32
|
+
animations: animationsCSS,
|
|
33
|
+
themes: houseThemes as never,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function placeholderInk(node: ReactElement) {
|
|
37
|
+
const { container, unmount } = renderWithProviders(node);
|
|
38
|
+
const input = container.querySelector("input");
|
|
39
|
+
expect(input).toBeTruthy();
|
|
40
|
+
const ink = input!.style.getPropertyValue("--t_placeholderColor");
|
|
41
|
+
unmount();
|
|
42
|
+
return ink;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function pathOf(icon: ReactElement) {
|
|
46
|
+
return renderToStaticMarkup(icon).match(/ d="([^"]+)"/)![1];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function June() {
|
|
50
|
+
const calendarData = useCalendarData(new Date(2024, 5, 15));
|
|
51
|
+
return (
|
|
52
|
+
<CalendarDays
|
|
53
|
+
calendarData={calendarData}
|
|
54
|
+
isSelected={(day) => isSameDay(day, new Date(2024, 5, 15))}
|
|
55
|
+
onSelect={() => {}}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function selectedCell(node: ReactElement, scheme: "light" | "dark") {
|
|
61
|
+
const { container } = render(
|
|
62
|
+
<TamaguiProvider config={houseConfig} defaultTheme={scheme} disableInjectCSS>
|
|
63
|
+
{node}
|
|
64
|
+
</TamaguiProvider>,
|
|
65
|
+
);
|
|
66
|
+
const cell = container.querySelector<HTMLElement>("[aria-pressed='true']");
|
|
67
|
+
expect(cell).toBeTruthy();
|
|
68
|
+
return cell!;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe("date and time fields read empty, name their kind and mark the pick (FZ-8433)", () => {
|
|
72
|
+
const stock = placeholderInk(<Input label="Name" placeholder="Your name" />);
|
|
73
|
+
|
|
74
|
+
it.each([
|
|
75
|
+
["DatePicker", <DatePicker key="d" label="Date" placeholder="Pick a date" />],
|
|
76
|
+
["DatetimePicker", <DatetimePicker key="dt" label="When" placeholder="Pick a time" />],
|
|
77
|
+
["TimePicker", <TimePicker key="t" label="Time" placeholder="Select time" />],
|
|
78
|
+
])("an empty %s draws its placeholder in the theme's placeholder ink", (_name, node) => {
|
|
79
|
+
const ink = placeholderInk(node);
|
|
80
|
+
expect(stock).not.toBe("");
|
|
81
|
+
expect(ink).toBe(stock);
|
|
82
|
+
expect(ink).not.toBe("var(--color11)");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each([
|
|
86
|
+
[
|
|
87
|
+
"DatetimePicker",
|
|
88
|
+
<DatetimePicker key="dt" label="When" />,
|
|
89
|
+
"date-picker-glyph",
|
|
90
|
+
CalendarBlankIcon,
|
|
91
|
+
],
|
|
92
|
+
["DatePicker", <DatePicker key="d" label="Date" />, "date-picker-glyph", CalendarBlankIcon],
|
|
93
|
+
["TimePicker", <TimePicker key="t" label="Time" />, "time-picker-glyph", ClockIcon],
|
|
94
|
+
[
|
|
95
|
+
"a read-only DatetimePicker",
|
|
96
|
+
<DatetimePicker key="ro" label="When" value={new Date(2026, 9, 31, 9, 30)} readOnly />,
|
|
97
|
+
"date-picker-glyph",
|
|
98
|
+
CalendarBlankIcon,
|
|
99
|
+
],
|
|
100
|
+
] as const)("%s draws one glyph, on the input's right", (_name, node, testId, Icon) => {
|
|
101
|
+
const { container, unmount } = renderWithProviders(node);
|
|
102
|
+
const glyphs = container.querySelectorAll(`[data-testid='${testId}']`);
|
|
103
|
+
expect(glyphs).toHaveLength(1);
|
|
104
|
+
const paths = Array.from(glyphs[0]!.querySelectorAll("svg path")).map((p) =>
|
|
105
|
+
p.getAttribute("d"),
|
|
106
|
+
);
|
|
107
|
+
expect(paths).toEqual([pathOf(<Icon />)]);
|
|
108
|
+
const input = container.querySelector("input")!;
|
|
109
|
+
expect(
|
|
110
|
+
input.compareDocumentPosition(glyphs[0]!) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
111
|
+
).toBeTruthy();
|
|
112
|
+
unmount();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("a filled web field swaps its glyph for the clear action, so the right holds one icon", () => {
|
|
116
|
+
const { container } = renderWithProviders(
|
|
117
|
+
<DatetimePicker label="When" value={new Date(2026, 9, 31, 9, 30)} />,
|
|
118
|
+
);
|
|
119
|
+
expect(container.querySelector("[data-testid='date-picker-glyph']")).toBeNull();
|
|
120
|
+
expect(container.querySelector("[data-testid='date-picker-clear']")).toBeTruthy();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it.each(
|
|
124
|
+
(["light", "dark"] as const).flatMap((scheme) => [
|
|
125
|
+
["day", scheme, <June key="d" />],
|
|
126
|
+
[
|
|
127
|
+
"month",
|
|
128
|
+
scheme,
|
|
129
|
+
<MonthGrid key="m" currentYear={2024} selectedMonth={5} onSelectMonth={() => {}} />,
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
"year",
|
|
133
|
+
scheme,
|
|
134
|
+
<YearGrid
|
|
135
|
+
key="y"
|
|
136
|
+
yearPageStart={2020}
|
|
137
|
+
selectedYear={2024}
|
|
138
|
+
onSelectYear={() => {}}
|
|
139
|
+
onPrevPage={() => {}}
|
|
140
|
+
onNextPage={() => {}}
|
|
141
|
+
/>,
|
|
142
|
+
],
|
|
143
|
+
]) as [string, "light" | "dark", ReactElement][],
|
|
144
|
+
)(
|
|
145
|
+
"the picked %s fills with the accent mark in %s, inked for that fill",
|
|
146
|
+
(_name, scheme, node) => {
|
|
147
|
+
const cell = selectedCell(node, scheme);
|
|
148
|
+
expect(cell.className).toMatch(/(?:^|\s)_bg-accentBackg/);
|
|
149
|
+
expect(cell.className).not.toMatch(/t_accent/);
|
|
150
|
+
const ink = cell.querySelector<HTMLElement>("span")!.className;
|
|
151
|
+
expect(ink).toMatch(/(?:^|\s)_col-(?:hsla?|rgba?)/);
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
});
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getMonthNames,
|
|
13
13
|
useAccentOnSurface,
|
|
14
14
|
useGlyphColor,
|
|
15
|
+
useReadableTextOn,
|
|
15
16
|
useResolvedKnobs,
|
|
16
17
|
} from "@multiplatform.one/theme";
|
|
17
18
|
import {
|
|
@@ -29,7 +30,7 @@ import { FloatingPanel, floatingPanelActionProps, isNestedPanelAction } from "..
|
|
|
29
30
|
import type { PanelActionEvent } from "../../FloatingPanel/nestedAction";
|
|
30
31
|
import type { PanelWidth } from "../../FloatingPanel/panelWidth";
|
|
31
32
|
import { Input as InputParts } from "../../InputParts";
|
|
32
|
-
import { formCommonColors } from "../../shared/colorRamps";
|
|
33
|
+
import { formCommonColors, formSelectedColors } from "../../shared/colorRamps";
|
|
33
34
|
import { t } from "../../shared/t";
|
|
34
35
|
|
|
35
36
|
// ---------------------------------------------------------------------------
|
|
@@ -341,6 +342,7 @@ export function CalendarDayCell({
|
|
|
341
342
|
}) {
|
|
342
343
|
const { knobProps } = useCalendarKnobs();
|
|
343
344
|
const accentText = useAccentOnSurface();
|
|
345
|
+
const onMark = useReadableTextOn(formSelectedColors.mark);
|
|
344
346
|
const { px, hitSlop, box } = splitNestedControl(knobProps.nestedControl);
|
|
345
347
|
|
|
346
348
|
if (!visible) {
|
|
@@ -373,8 +375,7 @@ export function CalendarDayCell({
|
|
|
373
375
|
// identical to a choosable cell; we do not adopt that.
|
|
374
376
|
opacity={disabled ? 0.5 : dimmed ? 0.45 : 1}
|
|
375
377
|
pointerEvents={disabled ? "none" : "auto"}
|
|
376
|
-
backgroundColor={selected ? (selectedBackground ??
|
|
377
|
-
theme={selected ? ("accent" as any) : undefined}
|
|
378
|
+
backgroundColor={selected ? (selectedBackground ?? formSelectedColors.mark) : "transparent"}
|
|
378
379
|
borderWidth={showTodayMark ? 2 : 0}
|
|
379
380
|
borderColor={showTodayMark ? accentText : "transparent"}
|
|
380
381
|
zIndex={1}
|
|
@@ -398,7 +399,7 @@ export function CalendarDayCell({
|
|
|
398
399
|
{...knobProps.label}
|
|
399
400
|
{...knobProps.textWeight}
|
|
400
401
|
fontWeight={selected ? "bold" : knobProps.textWeight.fontWeight}
|
|
401
|
-
color={selected ?
|
|
402
|
+
color={selected ? onMark : today ? accentText : formCommonColors.text}
|
|
402
403
|
>
|
|
403
404
|
{day.getDate()}
|
|
404
405
|
</Text>
|
|
@@ -675,6 +676,7 @@ interface MonthGridProps {
|
|
|
675
676
|
|
|
676
677
|
export function MonthGrid({ currentYear, selectedMonth, onSelectMonth }: MonthGridProps) {
|
|
677
678
|
const { knobProps } = useCalendarKnobs();
|
|
679
|
+
const onMark = useReadableTextOn(formSelectedColors.mark);
|
|
678
680
|
const { animation, animationKey } = useDateAnimation(currentYear);
|
|
679
681
|
const { px, hitSlop, box } = splitNestedControl(knobProps.nestedControl);
|
|
680
682
|
const cellW = (px * 7) / 3;
|
|
@@ -706,8 +708,7 @@ export function MonthGrid({ currentYear, selectedMonth, onSelectMonth }: MonthGr
|
|
|
706
708
|
cursor="pointer"
|
|
707
709
|
alignItems="center"
|
|
708
710
|
justifyContent="center"
|
|
709
|
-
|
|
710
|
-
backgroundColor={isSelected ? "$background" : "transparent"}
|
|
711
|
+
backgroundColor={isSelected ? formSelectedColors.mark : "transparent"}
|
|
711
712
|
onPress={() => onSelectMonth(index)}
|
|
712
713
|
outlineWidth={0}
|
|
713
714
|
focusStyle={{ outlineWidth: 0 }}
|
|
@@ -725,7 +726,7 @@ export function MonthGrid({ currentYear, selectedMonth, onSelectMonth }: MonthGr
|
|
|
725
726
|
>
|
|
726
727
|
<Text
|
|
727
728
|
{...knobProps.label}
|
|
728
|
-
color={isSelected ?
|
|
729
|
+
color={isSelected ? onMark : formCommonColors.text}
|
|
729
730
|
fontWeight={isSelected ? "bold" : "400"}
|
|
730
731
|
>
|
|
731
732
|
{t(name.slice(0, 3))}
|
|
@@ -758,6 +759,7 @@ export function YearGrid({
|
|
|
758
759
|
onNextPage,
|
|
759
760
|
}: YearGridProps) {
|
|
760
761
|
const { knobProps } = useCalendarKnobs();
|
|
762
|
+
const onMark = useReadableTextOn(formSelectedColors.mark);
|
|
761
763
|
const glyphColor = useGlyphColor();
|
|
762
764
|
const { px, hitSlop, box } = splitNestedControl(knobProps.nestedControl);
|
|
763
765
|
const cellW = (px * 7) / 3;
|
|
@@ -810,8 +812,7 @@ export function YearGrid({
|
|
|
810
812
|
cursor="pointer"
|
|
811
813
|
alignItems="center"
|
|
812
814
|
justifyContent="center"
|
|
813
|
-
|
|
814
|
-
backgroundColor={isSelected ? "$background" : "transparent"}
|
|
815
|
+
backgroundColor={isSelected ? formSelectedColors.mark : "transparent"}
|
|
815
816
|
onPress={() => onSelectYear(year)}
|
|
816
817
|
outlineWidth={0}
|
|
817
818
|
focusStyle={{ outlineWidth: 0 }}
|
|
@@ -829,7 +830,7 @@ export function YearGrid({
|
|
|
829
830
|
>
|
|
830
831
|
<Text
|
|
831
832
|
{...knobProps.label}
|
|
832
|
-
color={isSelected ?
|
|
833
|
+
color={isSelected ? onMark : formCommonColors.text}
|
|
833
834
|
fontWeight={isSelected ? "bold" : "400"}
|
|
834
835
|
>
|
|
835
836
|
{year}
|
|
@@ -855,8 +856,8 @@ interface DatePickerInputTriggerProps {
|
|
|
855
856
|
onReset: () => void;
|
|
856
857
|
onButtonPress: () => void;
|
|
857
858
|
id?: string;
|
|
858
|
-
/**
|
|
859
|
-
glyph?:
|
|
859
|
+
/** Trailing glyph: a clock for a time, else a calendar. */
|
|
860
|
+
glyph?: TriggerGlyphKind;
|
|
860
861
|
"aria-label"?: string;
|
|
861
862
|
"aria-labelledby"?: string;
|
|
862
863
|
"aria-describedby"?: string;
|
|
@@ -864,6 +865,16 @@ interface DatePickerInputTriggerProps {
|
|
|
864
865
|
"aria-invalid"?: boolean;
|
|
865
866
|
}
|
|
866
867
|
|
|
868
|
+
type TriggerGlyphKind = "calendar" | "clock";
|
|
869
|
+
|
|
870
|
+
function TriggerGlyph({ glyph, ...groupItem }: { glyph: TriggerGlyphKind }) {
|
|
871
|
+
return (
|
|
872
|
+
<InputParts.Icon adornment="trailing" color={formCommonColors.muted} {...groupItem}>
|
|
873
|
+
{glyph === "clock" ? <ClockIcon /> : <CalendarBlankIcon />}
|
|
874
|
+
</InputParts.Icon>
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
867
878
|
export function DatePickerClearButton({
|
|
868
879
|
onReset,
|
|
869
880
|
disabled,
|
|
@@ -918,8 +929,6 @@ export function DatePickerInputTrigger({
|
|
|
918
929
|
}
|
|
919
930
|
};
|
|
920
931
|
|
|
921
|
-
const Glyph = glyph === "clock" ? ClockIcon : CalendarBlankIcon;
|
|
922
|
-
|
|
923
932
|
return (
|
|
924
933
|
// LC-40: the disabled Box below is pointerEvents-none, so the cursor
|
|
925
934
|
// affordance must ride this wrapper — the fragment's not-allowed never
|
|
@@ -961,11 +970,6 @@ export function DatePickerInputTrigger({
|
|
|
961
970
|
}
|
|
962
971
|
: undefined)}
|
|
963
972
|
>
|
|
964
|
-
<InputParts.Section data-testid="date-picker-glyph">
|
|
965
|
-
<InputParts.Icon adornment="leading" color={formCommonColors.muted}>
|
|
966
|
-
<Glyph />
|
|
967
|
-
</InputParts.Icon>
|
|
968
|
-
</InputParts.Section>
|
|
969
973
|
<InputParts.Section>
|
|
970
974
|
<InputParts.Area
|
|
971
975
|
readOnly
|
|
@@ -975,14 +979,17 @@ export function DatePickerInputTrigger({
|
|
|
975
979
|
placeholder={placeholder}
|
|
976
980
|
disabled={disabled}
|
|
977
981
|
color={formCommonColors.text}
|
|
978
|
-
placeholderTextColor={formCommonColors.muted}
|
|
979
982
|
/>
|
|
980
983
|
</InputParts.Section>
|
|
981
|
-
{value && isWeb ? (
|
|
984
|
+
{value && isWeb && !disabled ? (
|
|
982
985
|
<InputParts.Section>
|
|
983
986
|
<DatePickerClearButton onReset={onReset} disabled={disabled} />
|
|
984
987
|
</InputParts.Section>
|
|
985
|
-
) :
|
|
988
|
+
) : (
|
|
989
|
+
<InputParts.Section data-testid="date-picker-glyph">
|
|
990
|
+
<TriggerGlyph glyph={glyph} />
|
|
991
|
+
</InputParts.Section>
|
|
992
|
+
)}
|
|
986
993
|
</InputParts.Box>
|
|
987
994
|
</View>
|
|
988
995
|
);
|
|
@@ -992,7 +999,7 @@ interface DatePickerReadOnlyFieldProps {
|
|
|
992
999
|
value: string;
|
|
993
1000
|
placeholder?: string;
|
|
994
1001
|
id?: string;
|
|
995
|
-
glyph?:
|
|
1002
|
+
glyph?: TriggerGlyphKind;
|
|
996
1003
|
"aria-label"?: string;
|
|
997
1004
|
"aria-required"?: boolean;
|
|
998
1005
|
"aria-invalid"?: boolean;
|
|
@@ -1011,7 +1018,6 @@ export function DatePickerReadOnlyField({
|
|
|
1011
1018
|
"aria-required": ariaRequired,
|
|
1012
1019
|
"aria-invalid": ariaInvalid,
|
|
1013
1020
|
}: DatePickerReadOnlyFieldProps) {
|
|
1014
|
-
const Glyph = glyph === "clock" ? ClockIcon : CalendarBlankIcon;
|
|
1015
1021
|
return (
|
|
1016
1022
|
<View flexDirection="row" alignItems="center" width="100%">
|
|
1017
1023
|
<InputParts.Box
|
|
@@ -1020,11 +1026,6 @@ export function DatePickerReadOnlyField({
|
|
|
1020
1026
|
elevation={undefined}
|
|
1021
1027
|
data-testid="date-picker-readonly"
|
|
1022
1028
|
>
|
|
1023
|
-
<InputParts.Section data-testid="date-picker-glyph">
|
|
1024
|
-
<InputParts.Icon adornment="leading" color={formCommonColors.muted}>
|
|
1025
|
-
<Glyph />
|
|
1026
|
-
</InputParts.Icon>
|
|
1027
|
-
</InputParts.Section>
|
|
1028
1029
|
<InputParts.Section>
|
|
1029
1030
|
<InputParts.Area
|
|
1030
1031
|
readOnly
|
|
@@ -1032,13 +1033,15 @@ export function DatePickerReadOnlyField({
|
|
|
1032
1033
|
value={value}
|
|
1033
1034
|
placeholder={placeholder}
|
|
1034
1035
|
color={formCommonColors.text}
|
|
1035
|
-
placeholderTextColor={formCommonColors.muted}
|
|
1036
1036
|
aria-label={ariaLabel}
|
|
1037
1037
|
aria-readonly="true"
|
|
1038
1038
|
aria-required={ariaRequired}
|
|
1039
1039
|
aria-invalid={ariaInvalid}
|
|
1040
1040
|
/>
|
|
1041
1041
|
</InputParts.Section>
|
|
1042
|
+
<InputParts.Section data-testid="date-picker-glyph">
|
|
1043
|
+
<TriggerGlyph glyph={glyph} />
|
|
1044
|
+
</InputParts.Section>
|
|
1042
1045
|
</InputParts.Box>
|
|
1043
1046
|
</View>
|
|
1044
1047
|
);
|