@multiplatform.one/forms 7.77.1 → 7.78.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/Button/IconButtonGroup.cjs +58 -27
- package/dist/cjs/Button/IconButtonGroup.native.js +64 -29
- package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -1
- package/dist/cjs/Button/circleGlyph.cjs +12 -2
- package/dist/cjs/Button/circleGlyph.native.js +12 -1
- package/dist/cjs/Button/circleGlyph.native.js.map +1 -1
- package/dist/cjs/Button/glass.cjs +2 -1
- package/dist/cjs/Button/glass.native.js +44 -19
- package/dist/cjs/Button/glass.native.js.map +1 -1
- package/dist/cjs/Button/iconButtonGroupContext.cjs +7 -1
- package/dist/cjs/Button/iconButtonGroupContext.native.js +7 -1
- package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -1
- package/dist/cjs/Button/index.cjs +15 -23
- package/dist/cjs/Button/index.native.js +15 -24
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Button/webGlass.cjs +9 -5
- package/dist/cjs/Button/webGlass.native.js +9 -5
- package/dist/cjs/Button/webGlass.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/coverRect.cjs +50 -0
- package/dist/cjs/FloatingPanel/coverRect.native.js +54 -0
- package/dist/cjs/FloatingPanel/coverRect.native.js.map +1 -0
- package/dist/cjs/FloatingPanel/iconEcho.cjs +3 -1
- package/dist/cjs/FloatingPanel/iconEcho.native.js +3 -1
- package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.cjs +23 -13
- package/dist/cjs/FloatingPanel/index.native.js +41 -15
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/InputParts/index.cjs +5 -0
- package/dist/cjs/InputParts/index.native.js +7 -0
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/{Button/mediaPlate.cjs → InputParts/keyboardAppearance.cjs} +7 -8
- package/dist/{jsx/Button/mediaPlate.native.js → cjs/InputParts/keyboardAppearance.native.js} +8 -9
- package/dist/cjs/InputParts/keyboardAppearance.native.js.map +1 -0
- package/dist/cjs/{Button/mediaPlate.native.js → fields/Duration/DurationWheel.cjs} +11 -11
- package/dist/cjs/fields/Duration/DurationWheel.native.js +132 -0
- package/dist/cjs/fields/Duration/DurationWheel.native.js.map +1 -0
- package/dist/cjs/fields/Duration/durationMath.cjs +19 -1
- package/dist/cjs/fields/Duration/durationMath.native.js +19 -1
- package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/cjs/fields/Duration/durationWheelTypes.cjs +18 -0
- package/dist/cjs/fields/Duration/durationWheelTypes.native.js +21 -0
- package/dist/cjs/fields/Duration/durationWheelTypes.native.js.map +1 -0
- package/dist/cjs/fields/Duration/index.cjs +30 -10
- package/dist/cjs/fields/Duration/index.native.js +32 -12
- package/dist/cjs/fields/Duration/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +8 -7
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.cjs +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/sheetKeyboard.cjs +1 -1
- package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
- package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.native.js +0 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/Button/IconButtonGroup.mjs +65 -33
- package/dist/esm/Button/IconButtonGroup.mjs.map +1 -1
- package/dist/esm/Button/IconButtonGroup.native.js +71 -35
- package/dist/esm/Button/IconButtonGroup.native.js.map +1 -1
- package/dist/esm/Button/circleGlyph.mjs +9 -1
- package/dist/esm/Button/circleGlyph.mjs.map +1 -1
- package/dist/esm/Button/circleGlyph.native.js +10 -1
- package/dist/esm/Button/circleGlyph.native.js.map +1 -1
- package/dist/esm/Button/glass.mjs +2 -1
- package/dist/esm/Button/glass.mjs.map +1 -1
- package/dist/esm/Button/glass.native.js +43 -19
- package/dist/esm/Button/glass.native.js.map +1 -1
- package/dist/esm/Button/iconButtonGroupContext.mjs +5 -1
- package/dist/esm/Button/iconButtonGroupContext.mjs.map +1 -1
- package/dist/esm/Button/iconButtonGroupContext.native.js +5 -1
- package/dist/esm/Button/iconButtonGroupContext.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +15 -22
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +15 -23
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Button/webGlass.mjs +10 -6
- package/dist/esm/Button/webGlass.mjs.map +1 -1
- package/dist/esm/Button/webGlass.native.js +10 -6
- package/dist/esm/Button/webGlass.native.js.map +1 -1
- package/dist/esm/FloatingPanel/coverRect.mjs +23 -0
- package/dist/esm/FloatingPanel/coverRect.mjs.map +1 -0
- package/dist/esm/FloatingPanel/coverRect.native.js +24 -0
- package/dist/esm/FloatingPanel/coverRect.native.js.map +1 -0
- package/dist/esm/FloatingPanel/iconEcho.mjs +4 -2
- package/dist/esm/FloatingPanel/iconEcho.mjs.map +1 -1
- package/dist/esm/FloatingPanel/iconEcho.native.js +4 -2
- package/dist/esm/FloatingPanel/iconEcho.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.mjs +23 -13
- package/dist/esm/FloatingPanel/index.mjs.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +41 -15
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/InputParts/index.mjs +6 -1
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +8 -1
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/InputParts/keyboardAppearance.mjs +5 -0
- package/dist/esm/InputParts/keyboardAppearance.mjs.map +1 -0
- package/dist/esm/InputParts/keyboardAppearance.native.js +5 -0
- package/dist/esm/InputParts/keyboardAppearance.native.js.map +1 -0
- package/dist/esm/fields/Duration/DurationWheel.mjs +8 -0
- package/dist/esm/fields/Duration/DurationWheel.mjs.map +1 -0
- package/dist/esm/fields/Duration/DurationWheel.native.js +103 -0
- package/dist/esm/fields/Duration/DurationWheel.native.js.map +1 -0
- package/dist/esm/fields/Duration/durationMath.mjs +16 -1
- package/dist/esm/fields/Duration/durationMath.mjs.map +1 -1
- package/dist/esm/fields/Duration/durationMath.native.js +16 -1
- package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/esm/fields/Duration/durationWheelTypes.mjs +2 -0
- package/dist/esm/fields/Duration/durationWheelTypes.mjs.map +1 -0
- package/dist/esm/fields/Duration/durationWheelTypes.native.js +2 -0
- package/dist/esm/fields/Duration/durationWheelTypes.native.js.map +1 -0
- package/dist/esm/fields/Duration/index.mjs +28 -11
- package/dist/esm/fields/Duration/index.mjs.map +1 -1
- package/dist/esm/fields/Duration/index.native.js +30 -13
- package/dist/esm/fields/Duration/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
- package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
- package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/jsx/Button/IconButtonGroup.mjs +65 -33
- package/dist/jsx/Button/IconButtonGroup.mjs.map +1 -1
- package/dist/jsx/Button/IconButtonGroup.native.js +64 -29
- package/dist/jsx/Button/IconButtonGroup.native.js.map +1 -1
- package/dist/jsx/Button/circleGlyph.mjs +9 -1
- package/dist/jsx/Button/circleGlyph.mjs.map +1 -1
- package/dist/jsx/Button/circleGlyph.native.js +12 -1
- package/dist/jsx/Button/circleGlyph.native.js.map +1 -1
- package/dist/jsx/Button/glass.mjs +2 -1
- package/dist/jsx/Button/glass.mjs.map +1 -1
- package/dist/jsx/Button/glass.native.js +44 -19
- package/dist/jsx/Button/glass.native.js.map +1 -1
- package/dist/jsx/Button/iconButtonGroupContext.mjs +5 -1
- package/dist/jsx/Button/iconButtonGroupContext.mjs.map +1 -1
- package/dist/jsx/Button/iconButtonGroupContext.native.js +7 -1
- package/dist/jsx/Button/iconButtonGroupContext.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +15 -22
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +15 -24
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Button/webGlass.mjs +10 -6
- package/dist/jsx/Button/webGlass.mjs.map +1 -1
- package/dist/jsx/Button/webGlass.native.js +9 -5
- package/dist/jsx/Button/webGlass.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/coverRect.mjs +23 -0
- package/dist/jsx/FloatingPanel/coverRect.mjs.map +1 -0
- package/dist/jsx/FloatingPanel/coverRect.native.js +54 -0
- package/dist/jsx/FloatingPanel/coverRect.native.js.map +1 -0
- package/dist/jsx/FloatingPanel/iconEcho.mjs +4 -2
- package/dist/jsx/FloatingPanel/iconEcho.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/iconEcho.native.js +3 -1
- package/dist/jsx/FloatingPanel/iconEcho.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.mjs +23 -13
- package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +41 -15
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/InputParts/index.mjs +6 -1
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +7 -0
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/InputParts/keyboardAppearance.mjs +5 -0
- package/dist/jsx/InputParts/keyboardAppearance.mjs.map +1 -0
- package/dist/jsx/InputParts/keyboardAppearance.native.js +33 -0
- package/dist/jsx/InputParts/keyboardAppearance.native.js.map +1 -0
- package/dist/jsx/fields/Duration/DurationWheel.mjs +8 -0
- package/dist/jsx/fields/Duration/DurationWheel.mjs.map +1 -0
- package/dist/jsx/fields/Duration/DurationWheel.native.js +132 -0
- package/dist/jsx/fields/Duration/DurationWheel.native.js.map +1 -0
- package/dist/jsx/fields/Duration/durationMath.mjs +16 -1
- package/dist/jsx/fields/Duration/durationMath.mjs.map +1 -1
- package/dist/jsx/fields/Duration/durationMath.native.js +19 -1
- package/dist/jsx/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/jsx/fields/Duration/durationWheelTypes.mjs +2 -0
- package/dist/jsx/fields/Duration/durationWheelTypes.mjs.map +1 -0
- package/dist/jsx/fields/Duration/durationWheelTypes.native.js +21 -0
- package/dist/jsx/fields/Duration/durationWheelTypes.native.js.map +1 -0
- package/dist/jsx/fields/Duration/index.mjs +28 -11
- package/dist/jsx/fields/Duration/index.mjs.map +1 -1
- package/dist/jsx/fields/Duration/index.native.js +32 -12
- package/dist/jsx/fields/Duration/index.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
- package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
- package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
- package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
- package/dist/jsx/index.js +2 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +0 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/package.json +8 -8
- package/src/Button/Button.iconButton.spec.tsx +47 -7
- package/src/Button/IconButtonGroup.spec.tsx +103 -19
- package/src/Button/IconButtonGroup.tsx +84 -46
- package/src/Button/circleGlyph.ts +18 -0
- package/src/Button/glass.native.spec.ts +62 -0
- package/src/Button/glass.native.ts +58 -37
- package/src/Button/glass.spec.ts +64 -16
- package/src/Button/glass.ts +16 -12
- package/src/Button/iconButtonGroupContext.ts +27 -3
- package/src/Button/index.tsx +21 -33
- package/src/Button/webGlass.ts +25 -18
- package/src/FloatingPanel/FloatingPanel.iconEcho.spec.tsx +61 -1
- package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +104 -0
- package/src/FloatingPanel/coverRect.ts +42 -0
- package/src/FloatingPanel/iconEcho.tsx +9 -3
- package/src/FloatingPanel/index.native.tsx +63 -18
- package/src/FloatingPanel/index.tsx +35 -15
- package/src/InputParts/index.tsx +6 -0
- package/src/InputParts/keyboardAppearance.spec.ts +12 -0
- package/src/InputParts/keyboardAppearance.ts +4 -0
- package/src/fields/Duration/Duration.spec.tsx +19 -10
- package/src/fields/Duration/Duration.wheel.native.spec.tsx +98 -0
- package/src/fields/Duration/DurationWheel.native.tsx +101 -0
- package/src/fields/Duration/DurationWheel.tsx +11 -0
- package/src/fields/Duration/durationMath.spec.ts +43 -0
- package/src/fields/Duration/durationMath.ts +31 -0
- package/src/fields/Duration/durationWheelTypes.ts +18 -0
- package/src/fields/Duration/index.tsx +58 -27
- package/src/fields/nativeDateTime/OsDateTimeSheet.countdown.native.spec.tsx +121 -0
- package/src/fields/nativeDateTime/OsDateTimeSheet.spec.ts +4 -0
- package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +14 -9
- package/src/fields/nativeDateTime/osDateTimeIosDisplay.ts +3 -3
- package/src/fields/nativeDateTime/sheetKeyboard.spec.ts +6 -0
- package/src/fields/nativeDateTime/sheetKeyboard.ts +2 -2
- package/src/index.ts +0 -1
- package/types/Button/IconButtonGroup.d.ts +7 -12
- package/types/Button/IconButtonGroup.d.ts.map +1 -1
- package/types/Button/circleGlyph.d.ts +12 -0
- package/types/Button/circleGlyph.d.ts.map +1 -1
- package/types/Button/glass.d.ts +7 -11
- package/types/Button/glass.d.ts.map +1 -1
- package/types/Button/glass.native.d.ts +22 -10
- package/types/Button/glass.native.d.ts.map +1 -1
- package/types/Button/iconButtonGroupContext.d.ts +21 -3
- package/types/Button/iconButtonGroupContext.d.ts.map +1 -1
- package/types/Button/index.d.ts +3 -10
- package/types/Button/index.d.ts.map +1 -1
- package/types/Button/webGlass.d.ts +8 -16
- package/types/Button/webGlass.d.ts.map +1 -1
- package/types/FloatingPanel/coverRect.d.ts +25 -0
- package/types/FloatingPanel/coverRect.d.ts.map +1 -0
- package/types/FloatingPanel/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/InputParts/keyboardAppearance.d.ts +3 -0
- package/types/InputParts/keyboardAppearance.d.ts.map +1 -0
- package/types/fields/Duration/DurationWheel.d.ts +5 -0
- package/types/fields/Duration/DurationWheel.d.ts.map +1 -0
- package/types/fields/Duration/DurationWheel.native.d.ts +5 -0
- package/types/fields/Duration/DurationWheel.native.d.ts.map +1 -0
- package/types/fields/Duration/durationMath.d.ts +13 -0
- package/types/fields/Duration/durationMath.d.ts.map +1 -1
- package/types/fields/Duration/durationWheelTypes.d.ts +18 -0
- package/types/fields/Duration/durationWheelTypes.d.ts.map +1 -0
- package/types/fields/Duration/index.d.ts +8 -3
- package/types/fields/Duration/index.d.ts.map +1 -1
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts +1 -1
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
- package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts +2 -2
- package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts.map +1 -1
- package/types/fields/nativeDateTime/sheetKeyboard.d.ts +1 -1
- package/types/fields/nativeDateTime/sheetKeyboard.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/dist/cjs/Button/mediaPlate.native.js.map +0 -1
- package/dist/esm/Button/mediaPlate.mjs +0 -6
- package/dist/esm/Button/mediaPlate.mjs.map +0 -1
- package/dist/esm/Button/mediaPlate.native.js +0 -6
- package/dist/esm/Button/mediaPlate.native.js.map +0 -1
- package/dist/jsx/Button/mediaPlate.mjs +0 -6
- package/dist/jsx/Button/mediaPlate.mjs.map +0 -1
- package/dist/jsx/Button/mediaPlate.native.js.map +0 -1
- package/src/Button/mediaPlate.ts +0 -9
- package/types/Button/mediaPlate.d.ts +0 -10
- package/types/Button/mediaPlate.d.ts.map +0 -1
|
@@ -2,10 +2,13 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { sizeRecipeForToken } from "@multiplatform.one/theme";
|
|
3
3
|
import {
|
|
4
4
|
durationSegmentWidth,
|
|
5
|
+
durationToWheelDate,
|
|
6
|
+
formatDurationParts,
|
|
5
7
|
normalizeSeconds,
|
|
6
8
|
parseDurationText,
|
|
7
9
|
partsToSeconds,
|
|
8
10
|
secondsToParts,
|
|
11
|
+
wheelDateToDuration,
|
|
9
12
|
} from "./durationMath";
|
|
10
13
|
|
|
11
14
|
describe("durationMath", () => {
|
|
@@ -64,4 +67,44 @@ describe("durationMath", () => {
|
|
|
64
67
|
expect(durationSegmentWidth("$2", 2)).not.toBe(durationSegmentWidth("$6", 2));
|
|
65
68
|
expect(durationSegmentWidth("$2", 2)).toBeLessThan(durationSegmentWidth("$4", 2));
|
|
66
69
|
});
|
|
70
|
+
|
|
71
|
+
describe("countdown wheel (FZ-8111)", () => {
|
|
72
|
+
const at = (h: number, m: number) => {
|
|
73
|
+
const d = new Date(2026, 9, 29);
|
|
74
|
+
d.setHours(h, m, 0, 0);
|
|
75
|
+
return d;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
it("reads hours and minutes within the day onto the wheel", () => {
|
|
79
|
+
const date = durationToWheelDate(3720);
|
|
80
|
+
expect([date.getHours(), date.getMinutes(), date.getSeconds()]).toEqual([1, 2, 0]);
|
|
81
|
+
const late = durationToWheelDate(23 * 3600 + 59 * 60);
|
|
82
|
+
expect([late.getHours(), late.getMinutes()]).toEqual([23, 59]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("round-trips every wheel position back to the same seconds", () => {
|
|
86
|
+
for (const seconds of [0, 60, 900, 3720, 9000, 86340]) {
|
|
87
|
+
expect(wheelDateToDuration(durationToWheelDate(seconds), 0)).toBe(seconds);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("sets seconds from a wheel pick", () => {
|
|
92
|
+
expect(wheelDateToDuration(at(2, 30), 0)).toBe(9000);
|
|
93
|
+
expect(wheelDateToDuration(at(0, 15), 3720)).toBe(900);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("carries whole days and stray seconds the wheel cannot show", () => {
|
|
97
|
+
expect(wheelDateToDuration(at(2, 30), 86400 + 3600 + 7)).toBe(86400 + 9000 + 7);
|
|
98
|
+
const wheel = durationToWheelDate(90000);
|
|
99
|
+
expect([wheel.getHours(), wheel.getMinutes()]).toEqual([1, 0]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("formats the trigger text with or without days", () => {
|
|
103
|
+
const units = { d: "d", h: "h", m: "m" };
|
|
104
|
+
expect(formatDurationParts(secondsToParts(3720, true), true, units)).toBe("1 h 2 m");
|
|
105
|
+
expect(formatDurationParts(secondsToParts(90000, true), true, units)).toBe("25 h 0 m");
|
|
106
|
+
expect(formatDurationParts(secondsToParts(93784, false), false, units)).toBe("1 d 2 h 3 m");
|
|
107
|
+
expect(formatDurationParts(secondsToParts(3720, false), false, units)).toBe("1 h 2 m");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
67
110
|
});
|
|
@@ -76,3 +76,34 @@ export function durationSegmentWidth(sizeToken: string, digits: number): number
|
|
|
76
76
|
const recipe = sizeRecipeForToken(sizeToken);
|
|
77
77
|
return recipe.fontSize * digits;
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
const DAY = 86400;
|
|
81
|
+
|
|
82
|
+
/** The countdown wheel's reading of a duration: hours and minutes within one day, on today's date. */
|
|
83
|
+
export function durationToWheelDate(total: number): Date {
|
|
84
|
+
const s = normalizeSeconds(total) % DAY;
|
|
85
|
+
const date = new Date();
|
|
86
|
+
date.setHours(Math.floor(s / 3600), Math.floor((s % 3600) / 60), 0, 0);
|
|
87
|
+
return date;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A wheel pick back to seconds. The wheel spans 0 h 0 m to 23 h 59 m, so the
|
|
92
|
+
* whole days and the stray seconds of the previous value carry over untouched.
|
|
93
|
+
*/
|
|
94
|
+
export function wheelDateToDuration(picked: Date, previous: number): number {
|
|
95
|
+
const prev = normalizeSeconds(previous);
|
|
96
|
+
return (
|
|
97
|
+
Math.floor(prev / DAY) * DAY + picked.getHours() * 3600 + picked.getMinutes() * 60 + (prev % 60)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** "1 h 2 m", "1 d 2 h 3 m": the wheel trigger's text, units from the caller. */
|
|
102
|
+
export function formatDurationParts(
|
|
103
|
+
parts: DurationParts,
|
|
104
|
+
hideDays: boolean,
|
|
105
|
+
units: { d: string; h: string; m: string },
|
|
106
|
+
): string {
|
|
107
|
+
const head = hideDays || parts.days === 0 ? [] : [`${parts.days} ${units.d}`];
|
|
108
|
+
return [...head, `${parts.hours} ${units.h}`, `${parts.minutes} ${units.m}`].join(" ");
|
|
109
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { KnobProps } from "@multiplatform.one/theme";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { SizeTokens } from "tamagui";
|
|
4
|
+
|
|
5
|
+
export interface DurationWheelFieldProps {
|
|
6
|
+
seconds: number;
|
|
7
|
+
onChange: (seconds: number) => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
foldDays: boolean;
|
|
11
|
+
clockIcon?: ReactNode | null;
|
|
12
|
+
hasError?: boolean;
|
|
13
|
+
id?: string;
|
|
14
|
+
hasLabel?: boolean;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
size?: SizeTokens;
|
|
17
|
+
knobProps: KnobProps;
|
|
18
|
+
}
|
|
@@ -5,7 +5,7 @@ import type { ReactNode } from "react";
|
|
|
5
5
|
import { useCallback, useState } from "react";
|
|
6
6
|
import type { LabelProps, SizeTokens } from "tamagui";
|
|
7
7
|
import { Text, XStack, YStack, isWeb } from "tamagui";
|
|
8
|
-
import {
|
|
8
|
+
import { Chip } from "../../Chip";
|
|
9
9
|
import { Skeleton } from "../../Skeleton";
|
|
10
10
|
import { Field, FieldLayout } from "../../fieldLayout";
|
|
11
11
|
import { Input as InputParts } from "../../InputParts";
|
|
@@ -20,22 +20,29 @@ import {
|
|
|
20
20
|
useResolvedValidators,
|
|
21
21
|
} from "../../shared/utils";
|
|
22
22
|
import type { AnyFormApi } from "../../types";
|
|
23
|
+
import { DurationWheelField, durationWheelAvailable } from "./DurationWheel";
|
|
23
24
|
import {
|
|
24
25
|
durationSegmentWidth,
|
|
26
|
+
durationToWheelDate,
|
|
27
|
+
formatDurationParts,
|
|
25
28
|
normalizeSeconds,
|
|
26
29
|
parseDurationText,
|
|
27
30
|
partsToSeconds,
|
|
28
31
|
secondsToParts,
|
|
32
|
+
wheelDateToDuration,
|
|
29
33
|
type DurationParts,
|
|
30
34
|
} from "./durationMath";
|
|
31
35
|
|
|
32
36
|
export type { DurationParts };
|
|
33
37
|
export {
|
|
34
38
|
durationSegmentWidth,
|
|
39
|
+
durationToWheelDate,
|
|
40
|
+
formatDurationParts,
|
|
35
41
|
normalizeSeconds,
|
|
36
42
|
parseDurationText,
|
|
37
43
|
partsToSeconds,
|
|
38
44
|
secondsToParts,
|
|
45
|
+
wheelDateToDuration,
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
export interface DurationPreset {
|
|
@@ -374,16 +381,15 @@ function DurationPresets({
|
|
|
374
381
|
{presets.map((preset) => {
|
|
375
382
|
const selected = seconds === preset.seconds;
|
|
376
383
|
return (
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
</Button>
|
|
384
|
+
<XStack key={preset.seconds} data-duration-preset={preset.label}>
|
|
385
|
+
<Chip
|
|
386
|
+
size={DURATION_PRESET_SIZE}
|
|
387
|
+
selected={selected}
|
|
388
|
+
onPress={() => onChange(preset.seconds)}
|
|
389
|
+
>
|
|
390
|
+
{preset.label}
|
|
391
|
+
</Chip>
|
|
392
|
+
</XStack>
|
|
387
393
|
);
|
|
388
394
|
})}
|
|
389
395
|
</XStack>
|
|
@@ -425,6 +431,11 @@ export interface DurationProps {
|
|
|
425
431
|
clockIcon?: ReactNode | null;
|
|
426
432
|
/** When true, renders a skeleton placeholder instead of the duration picker */
|
|
427
433
|
skeleton?: boolean;
|
|
434
|
+
/**
|
|
435
|
+
* On iOS and Android a tap opens the system hours/minutes wheel (LC-266).
|
|
436
|
+
* `false` keeps the typed segments there too. Web always types.
|
|
437
|
+
*/
|
|
438
|
+
native?: boolean;
|
|
428
439
|
}
|
|
429
440
|
|
|
430
441
|
export function Duration({
|
|
@@ -450,6 +461,7 @@ export function Duration({
|
|
|
450
461
|
placeholder = t("Select duration"),
|
|
451
462
|
clockIcon,
|
|
452
463
|
skeleton,
|
|
464
|
+
native,
|
|
453
465
|
}: DurationProps) {
|
|
454
466
|
const hydrationTouch = useTouchSurface();
|
|
455
467
|
const { resolvedForm, knobProps, id } = useFormField({
|
|
@@ -489,24 +501,43 @@ export function Duration({
|
|
|
489
501
|
);
|
|
490
502
|
}
|
|
491
503
|
|
|
504
|
+
const wheel = native !== false && !showSeconds && durationWheelAvailable();
|
|
505
|
+
|
|
492
506
|
const fieldBody = (current: number, onValueChange: (s: number) => void, hasError?: boolean) => (
|
|
493
507
|
<YStack {...knobProps.gap}>
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
508
|
+
{wheel ? (
|
|
509
|
+
<DurationWheelField
|
|
510
|
+
seconds={current}
|
|
511
|
+
onChange={onValueChange}
|
|
512
|
+
disabled={disabled}
|
|
513
|
+
readOnly={readOnly}
|
|
514
|
+
foldDays={hideDays}
|
|
515
|
+
clockIcon={clockIcon}
|
|
516
|
+
hasError={hasError}
|
|
517
|
+
id={id}
|
|
518
|
+
hasLabel={Boolean(label)}
|
|
519
|
+
required={required}
|
|
520
|
+
size={size}
|
|
521
|
+
knobProps={knobProps}
|
|
522
|
+
/>
|
|
523
|
+
) : (
|
|
524
|
+
<DurationControl
|
|
525
|
+
seconds={current}
|
|
526
|
+
onChange={onValueChange}
|
|
527
|
+
disabled={disabled}
|
|
528
|
+
readOnly={readOnly}
|
|
529
|
+
hideDays={hideDays}
|
|
530
|
+
showSeconds={showSeconds}
|
|
531
|
+
clockIcon={clockIcon}
|
|
532
|
+
hasError={hasError}
|
|
533
|
+
id={id}
|
|
534
|
+
hasLabel={Boolean(label)}
|
|
535
|
+
placeholder={placeholder}
|
|
536
|
+
required={required}
|
|
537
|
+
size={size}
|
|
538
|
+
knobProps={knobProps}
|
|
539
|
+
/>
|
|
540
|
+
)}
|
|
510
541
|
{readOnly || disabled || inTableCell ? null : (
|
|
511
542
|
<DurationPresets
|
|
512
543
|
seconds={current}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
+
import { fireEvent } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { OsDateTimeSheet } from "./OsDateTimeSheet";
|
|
5
|
+
import { DATE_PICKER_WHEEL_HEIGHT } from "./sheetKeyboard";
|
|
6
|
+
|
|
7
|
+
const os = vi.hoisted(() => ({ name: "ios" as "ios" | "android" }));
|
|
8
|
+
const seen = vi.hoisted(() => ({ props: [] as Record<string, unknown>[] }));
|
|
9
|
+
|
|
10
|
+
vi.mock("react-native-web", async (importOriginal) => ({
|
|
11
|
+
...(await importOriginal<typeof import("react-native")>()),
|
|
12
|
+
useWindowDimensions: () => ({ width: 390, height: 844, scale: 3, fontScale: 1 }),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@multiplatform.one/platform", async (importOriginal) => {
|
|
16
|
+
const actual = (await importOriginal()) as Record<string, unknown>;
|
|
17
|
+
const real = actual.platform as Record<string, unknown>;
|
|
18
|
+
return {
|
|
19
|
+
...actual,
|
|
20
|
+
platform: new Proxy(
|
|
21
|
+
{},
|
|
22
|
+
{
|
|
23
|
+
get(_target, prop) {
|
|
24
|
+
if (prop === "isIos") return os.name === "ios";
|
|
25
|
+
if (prop === "isAndroid") return os.name === "android";
|
|
26
|
+
return real[prop as string];
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
vi.mock("./dateTimePickerLoader", () => ({
|
|
34
|
+
loadDateTimePicker: () => (props: Record<string, unknown>) => {
|
|
35
|
+
seen.props.push(props);
|
|
36
|
+
return <div data-testid="os-picker" />;
|
|
37
|
+
},
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const at = (h: number, m: number) => {
|
|
41
|
+
const d = new Date(2026, 9, 29);
|
|
42
|
+
d.setHours(h, m, 0, 0);
|
|
43
|
+
return d;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
describe("OsDateTimeSheet countdown (FZ-8111)", () => {
|
|
47
|
+
it("opens the iOS countdown-timer wheel in the app's scheme", () => {
|
|
48
|
+
os.name = "ios";
|
|
49
|
+
seen.props = [];
|
|
50
|
+
renderWithProviders(
|
|
51
|
+
<OsDateTimeSheet
|
|
52
|
+
open
|
|
53
|
+
mode="countdown"
|
|
54
|
+
value={at(1, 2)}
|
|
55
|
+
onCancel={vi.fn()}
|
|
56
|
+
onConfirm={vi.fn()}
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
const props = seen.props.at(-1)!;
|
|
60
|
+
expect(props.mode).toBe("countdown");
|
|
61
|
+
expect(props.display).toBe("spinner");
|
|
62
|
+
expect(props.themeVariant).toBe("light");
|
|
63
|
+
expect((props.value as Date).getHours()).toBe(1);
|
|
64
|
+
expect((props.value as Date).getMinutes()).toBe(2);
|
|
65
|
+
expect((props.style as { height: number }).height).toBe(DATE_PICKER_WHEEL_HEIGHT);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("confirms the wheel's hours and minutes", () => {
|
|
69
|
+
os.name = "ios";
|
|
70
|
+
seen.props = [];
|
|
71
|
+
const onConfirm = vi.fn();
|
|
72
|
+
const onCancel = vi.fn();
|
|
73
|
+
const result = renderWithProviders(
|
|
74
|
+
<OsDateTimeSheet
|
|
75
|
+
open
|
|
76
|
+
mode="countdown"
|
|
77
|
+
value={at(1, 2)}
|
|
78
|
+
onCancel={onCancel}
|
|
79
|
+
onConfirm={onConfirm}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
fireEvent.click(result.getByRole("button", { name: "Done" }));
|
|
83
|
+
expect(onConfirm).toHaveBeenCalledOnce();
|
|
84
|
+
const picked = onConfirm.mock.calls[0]![0] as Date;
|
|
85
|
+
expect([picked.getHours(), picked.getMinutes()]).toEqual([1, 2]);
|
|
86
|
+
fireEvent.click(result.getByRole("button", { name: "Cancel" }));
|
|
87
|
+
expect(onCancel).toHaveBeenCalledOnce();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("opens Android's 24-hour spinner, never the AM/PM clock dial", () => {
|
|
91
|
+
os.name = "android";
|
|
92
|
+
seen.props = [];
|
|
93
|
+
renderWithProviders(
|
|
94
|
+
<OsDateTimeSheet
|
|
95
|
+
open
|
|
96
|
+
mode="countdown"
|
|
97
|
+
value={at(2, 30)}
|
|
98
|
+
onCancel={vi.fn()}
|
|
99
|
+
onConfirm={vi.fn()}
|
|
100
|
+
/>,
|
|
101
|
+
);
|
|
102
|
+
const props = seen.props.at(-1)!;
|
|
103
|
+
expect(props.mode).toBe("time");
|
|
104
|
+
expect(props.display).toBe("spinner");
|
|
105
|
+
expect(props.is24Hour).toBe(true);
|
|
106
|
+
os.name = "ios";
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("leaves the Android date dialog as it was", () => {
|
|
110
|
+
os.name = "android";
|
|
111
|
+
seen.props = [];
|
|
112
|
+
renderWithProviders(
|
|
113
|
+
<OsDateTimeSheet open mode="date" value={at(0, 0)} onCancel={vi.fn()} onConfirm={vi.fn()} />,
|
|
114
|
+
);
|
|
115
|
+
const props = seen.props.at(-1)!;
|
|
116
|
+
expect(props.mode).toBe("date");
|
|
117
|
+
expect(props.display).toBe("default");
|
|
118
|
+
expect(props.is24Hour).toBeUndefined();
|
|
119
|
+
os.name = "ios";
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -9,4 +9,8 @@ describe("osDateTimeIosDisplay", () => {
|
|
|
9
9
|
it("keeps the time wheel", () => {
|
|
10
10
|
expect(osDateTimeIosDisplay("time")).toBe("spinner");
|
|
11
11
|
});
|
|
12
|
+
|
|
13
|
+
it("keeps the wheel for a countdown (FZ-8111)", () => {
|
|
14
|
+
expect(osDateTimeIosDisplay("countdown")).toBe("spinner");
|
|
15
|
+
});
|
|
12
16
|
});
|
|
@@ -33,7 +33,7 @@ import { osDateTimeIosDisplay } from "./osDateTimeIosDisplay";
|
|
|
33
33
|
import { datePickerHeight, dateSheetBottomPadding, dateSheetMaxHeight } from "./sheetKeyboard";
|
|
34
34
|
import { useKeyboardInset } from "../../shared/useKeyboardInset";
|
|
35
35
|
|
|
36
|
-
export type OsDateTimeMode = "date" | "time" | "datetime";
|
|
36
|
+
export type OsDateTimeMode = "date" | "time" | "datetime" | "countdown";
|
|
37
37
|
|
|
38
38
|
export type OsDateTimeSheetProps = {
|
|
39
39
|
open: boolean;
|
|
@@ -74,8 +74,12 @@ export function OsDateTimeSheet({
|
|
|
74
74
|
const maxHeight = dateSheetMaxHeight(windowHeight, keyboardInset, topInset);
|
|
75
75
|
const [showTimePicker, setShowTimePicker] = useState(false);
|
|
76
76
|
const [temp, setTemp] = useState(value);
|
|
77
|
-
const pickerMode: "date" | "time" =
|
|
78
|
-
mode === "
|
|
77
|
+
const pickerMode: "date" | "time" | "countdown" =
|
|
78
|
+
mode === "countdown"
|
|
79
|
+
? "countdown"
|
|
80
|
+
: mode === "time" || (mode === "datetime" && showTimePicker)
|
|
81
|
+
? "time"
|
|
82
|
+
: "date";
|
|
79
83
|
const iosDisplay = osDateTimeIosDisplay(pickerMode);
|
|
80
84
|
|
|
81
85
|
const reset = useCallback(() => {
|
|
@@ -182,11 +186,12 @@ export function OsDateTimeSheet({
|
|
|
182
186
|
return (
|
|
183
187
|
<DateTimePicker
|
|
184
188
|
value={temp}
|
|
185
|
-
mode={pickerMode}
|
|
186
|
-
display="default"
|
|
189
|
+
mode={pickerMode === "countdown" ? "time" : pickerMode}
|
|
190
|
+
display={pickerMode === "countdown" ? "spinner" : "default"}
|
|
191
|
+
is24Hour={pickerMode === "countdown" ? true : undefined}
|
|
187
192
|
onChange={handleAndroidChange}
|
|
188
|
-
minimumDate={mode
|
|
189
|
-
maximumDate={mode
|
|
193
|
+
minimumDate={mode === "date" || mode === "datetime" ? minDate : undefined}
|
|
194
|
+
maximumDate={mode === "date" || mode === "datetime" ? maxDate : undefined}
|
|
190
195
|
/>
|
|
191
196
|
);
|
|
192
197
|
}
|
|
@@ -242,8 +247,8 @@ export function OsDateTimeSheet({
|
|
|
242
247
|
themeVariant={themeVariant}
|
|
243
248
|
accentColor={accentColor}
|
|
244
249
|
onChange={handleIOSChange}
|
|
245
|
-
minimumDate={mode
|
|
246
|
-
maximumDate={mode
|
|
250
|
+
minimumDate={mode === "date" || mode === "datetime" ? minDate : undefined}
|
|
251
|
+
maximumDate={mode === "date" || mode === "datetime" ? maxDate : undefined}
|
|
247
252
|
style={{ width: "100%", height: datePickerHeight(pickerMode, maxHeight) }}
|
|
248
253
|
/>
|
|
249
254
|
</YStack>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** iOS date is the inline calendar; time
|
|
2
|
-
export function osDateTimeIosDisplay(mode: "date" | "time"): "inline" | "spinner" {
|
|
3
|
-
return mode === "
|
|
1
|
+
/** iOS date is the inline calendar; time and countdown keep the wheel users expect. */
|
|
2
|
+
export function osDateTimeIosDisplay(mode: "date" | "time" | "countdown"): "inline" | "spinner" {
|
|
3
|
+
return mode === "date" ? "inline" : "spinner";
|
|
4
4
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
dateSheetBottomPadding,
|
|
11
11
|
dateSheetMaxHeight,
|
|
12
12
|
DATE_PICKER_CALENDAR_HEIGHT,
|
|
13
|
+
DATE_PICKER_WHEEL_HEIGHT,
|
|
13
14
|
DATE_SHEET_BACKDROP_STRIP,
|
|
14
15
|
DATE_SHEET_MIN_HEIGHT,
|
|
15
16
|
} from "./sheetKeyboard";
|
|
@@ -49,6 +50,11 @@ describe("datePickerHeight", () => {
|
|
|
49
50
|
expect(height).toBeLessThan(DATE_PICKER_CALENDAR_HEIGHT);
|
|
50
51
|
expect(height).toBeLessThanOrEqual(maxHeight);
|
|
51
52
|
});
|
|
53
|
+
|
|
54
|
+
it("gives the countdown the wheel height, not the calendar's (FZ-8111)", () => {
|
|
55
|
+
expect(datePickerHeight("countdown", 700)).toBe(DATE_PICKER_WHEEL_HEIGHT);
|
|
56
|
+
expect(datePickerHeight("countdown", 700)).toBe(datePickerHeight("time", 700));
|
|
57
|
+
});
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
describe("dateSheetBottomPadding", () => {
|
|
@@ -39,7 +39,7 @@ export function dateSheetBottomPadding(keyboardInset: number, isIos: boolean): n
|
|
|
39
39
|
return isIos ? 24 : 16;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export function datePickerHeight(mode: "date" | "time", maxHeight: number): number {
|
|
43
|
-
const preferred = mode === "
|
|
42
|
+
export function datePickerHeight(mode: "date" | "time" | "countdown", maxHeight: number): number {
|
|
43
|
+
const preferred = mode === "date" ? DATE_PICKER_CALENDAR_HEIGHT : DATE_PICKER_WHEEL_HEIGHT;
|
|
44
44
|
return Math.min(preferred, Math.max(minPickerHeight, maxHeight - DATE_SHEET_CHROME));
|
|
45
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import { type XStackProps } from "tamagui";
|
|
3
|
-
/**
|
|
4
|
-
* LC-176: how far apart two Liquid Glass circles may sit and still merge. Half
|
|
5
|
-
* the 44pt target, so neighbours on the bar's gap always join.
|
|
6
|
-
*/
|
|
7
|
-
export declare const ICON_BUTTON_GLASS_MERGE_PX = 22;
|
|
8
3
|
export interface IconButtonGroupProps extends XStackProps {
|
|
9
|
-
/** Over imagery or video: the group shares one
|
|
4
|
+
/** Over imagery or video: the group shares one dark capsule. */
|
|
10
5
|
plate?: "media";
|
|
11
6
|
children?: ReactNode;
|
|
12
7
|
}
|
|
13
8
|
/**
|
|
14
9
|
* LC-176: icon buttons that sit side by side. Grounds merge, they never stand
|
|
15
|
-
* as a row of separate circles: on iOS 26 the bar's group
|
|
16
|
-
* capsule, as the system draws a bar's item group (merging each
|
|
17
|
-
* the glass container left a waist between them, FZ-8158)
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* capsule (FZ-8494).
|
|
10
|
+
* as a row of separate circles: on iOS 26 the bar's group sits in one system
|
|
11
|
+
* Liquid Glass capsule, as the system draws a bar's item group (merging each
|
|
12
|
+
* circle through the glass container left a waist between them, FZ-8158), so
|
|
13
|
+
* press and merge are the system's; over media the group shares one dark
|
|
14
|
+
* capsule; a bare row stays bare. A lone button keeps its own circle, and a
|
|
15
|
+
* bar group with no icon button in it paints no capsule (FZ-8494).
|
|
21
16
|
*/
|
|
22
17
|
export declare function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
23
18
|
//# sourceMappingURL=IconButtonGroup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconButtonGroup.d.ts","sourceRoot":"","sources":["../../src/Button/IconButtonGroup.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"IconButtonGroup.d.ts","sourceRoot":"","sources":["../../src/Button/IconButtonGroup.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAA4C,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAmBrF,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,oBAAoB,2CAyIjF"}
|
|
@@ -4,4 +4,16 @@
|
|
|
4
4
|
* ratio, never a small glyph lost in a big plate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const ICON_BUTTON_CIRCLE_GLYPH_RATIO = 0.58;
|
|
7
|
+
/**
|
|
8
|
+
* LC-176: Phosphor bold's stem is 0.12 of its ink, an SF Symbols bold plus's
|
|
9
|
+
* 0.18 (measured at 17 and 24pt). An outline stroke this wide, in the 256-unit
|
|
10
|
+
* grid, brings a bold glyph to SF semibold-bold (0.154); a wider one closes
|
|
11
|
+
* Phosphor bold's 16-unit counters (the trash lid, copy's overlap).
|
|
12
|
+
*/
|
|
13
|
+
export declare const GLYPH_BOLD_STROKE_UNITS = 8;
|
|
14
|
+
export declare function boldStroke(weight: "regular" | "bold", color?: string): {
|
|
15
|
+
stroke: string;
|
|
16
|
+
strokeWidth: number;
|
|
17
|
+
strokeLinejoin: "round";
|
|
18
|
+
} | undefined;
|
|
7
19
|
//# sourceMappingURL=circleGlyph.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circleGlyph.d.ts","sourceRoot":"","sources":["../../src/Button/circleGlyph.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC"}
|
|
1
|
+
{"version":3,"file":"circleGlyph.d.ts","sourceRoot":"","sources":["../../src/Button/circleGlyph.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,KAAK,SAAiB;;;;cAQ5E"}
|
package/types/Button/glass.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import type { ComponentType, ReactNode } from "react";
|
|
|
2
2
|
/** The slice of expo-glass-effect's `GlassView` an icon button draws. */
|
|
3
3
|
export type GlassViewComponent = ComponentType<{
|
|
4
4
|
style?: unknown;
|
|
5
|
-
glassEffectStyle?: "regular" | "clear";
|
|
5
|
+
glassEffectStyle?: "regular" | "clear" | "none";
|
|
6
6
|
isInteractive?: boolean;
|
|
7
|
-
|
|
7
|
+
colorScheme?: "auto" | "light" | "dark";
|
|
8
|
+
children?: ReactNode;
|
|
8
9
|
}>;
|
|
9
10
|
/** The slice of expo-glass-effect's `GlassContainer` an icon button group draws. */
|
|
10
11
|
export type GlassContainerComponent = ComponentType<{
|
|
@@ -14,16 +15,11 @@ export type GlassContainerComponent = ComponentType<{
|
|
|
14
15
|
}>;
|
|
15
16
|
export { isAppleTouchWeb } from "./webGlass";
|
|
16
17
|
/**
|
|
17
|
-
* LC-176: the
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* rv_3bb3f1f65d6b981f: "Is there a way to have the buttons at the bottom be
|
|
21
|
-
* liquid glass if it's an iPhone"); every other browser draws the bare glyph.
|
|
18
|
+
* LC-176: the system Liquid Glass an icon button sits in on a bar. Only native
|
|
19
|
+
* iOS 26 has it, from expo-glass-effect (`glass.native.ts`; Android probes
|
|
20
|
+
* null), so the web draws the plain glyph.
|
|
22
21
|
*/
|
|
23
22
|
export declare function loadGlassView(): GlassViewComponent | null;
|
|
24
|
-
/**
|
|
25
|
-
* LC-176: the container that merges neighbouring Liquid Glass circles into one
|
|
26
|
-
* shape, as iOS 26 groups bar buttons. Only iOS has it.
|
|
27
|
-
*/
|
|
23
|
+
/** LC-176: the system's Liquid Glass container. Only iOS has it. */
|
|
28
24
|
export declare function loadGlassContainer(): GlassContainerComponent | null;
|
|
29
25
|
//# sourceMappingURL=glass.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../../src/Button/glass.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../../src/Button/glass.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IAChD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,uBAAuB,GAAG,aAAa,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC,CAAC;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAU7C;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CAEzD;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAEnE"}
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
2
2
|
export type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
3
|
+
export interface GlassSources {
|
|
4
|
+
core: () => {
|
|
5
|
+
requireOptionalNativeModule?: (moduleName: string) => unknown;
|
|
6
|
+
};
|
|
7
|
+
glass: () => {
|
|
8
|
+
GlassView?: GlassViewComponent;
|
|
9
|
+
GlassContainer?: GlassContainerComponent;
|
|
10
|
+
isLiquidGlassAvailable?: () => boolean;
|
|
11
|
+
isGlassEffectAPIAvailable?: () => boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export interface SystemGlass {
|
|
15
|
+
view: GlassViewComponent;
|
|
16
|
+
container: GlassContainerComponent | null;
|
|
17
|
+
}
|
|
3
18
|
/**
|
|
4
|
-
* LC-176: expo-glass-effect's
|
|
5
|
-
*
|
|
6
|
-
* `requireOptionalNativeModule` BEFORE the guarded `require`,
|
|
7
|
-
* binary built without it, or an older iOS, falls back to the
|
|
8
|
-
* glyph instead of throwing mid-render (the `nativePickerLoader.ts`
|
|
19
|
+
* LC-176: expo-glass-effect's views, when this binary links the native module
|
|
20
|
+
* and the OS draws Liquid Glass (iOS 26+); Android never links it. The module
|
|
21
|
+
* is probed through `requireOptionalNativeModule` BEFORE the guarded `require`,
|
|
22
|
+
* so an OTA on a binary built without it, or an older iOS, falls back to the
|
|
23
|
+
* bare tinted glyph instead of throwing mid-render (the `nativePickerLoader.ts`
|
|
24
|
+
* contract). A binary whose module predates the container keeps its capsule.
|
|
9
25
|
*/
|
|
26
|
+
export declare function resolveSystemGlass(sources?: GlassSources): SystemGlass | null;
|
|
10
27
|
export declare function loadGlassView(): GlassViewComponent | null;
|
|
11
|
-
/**
|
|
12
|
-
* LC-176: expo-glass-effect's `GlassContainer`, present exactly when
|
|
13
|
-
* `loadGlassView` found Liquid Glass. A binary whose module predates the
|
|
14
|
-
* container keeps separate circles rather than throwing.
|
|
15
|
-
*/
|
|
16
28
|
export declare function loadGlassContainer(): GlassContainerComponent | null;
|
|
17
29
|
//# sourceMappingURL=glass.native.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glass.native.d.ts","sourceRoot":"","sources":["../../src/Button/glass.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"glass.native.d.ts","sourceRoot":"","sources":["../../src/Button/glass.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM;QAAE,2BAA2B,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAA;KAAE,CAAC;IAC9E,KAAK,EAAE,MAAM;QACX,SAAS,CAAC,EAAE,kBAAkB,CAAC;QAC/B,cAAc,CAAC,EAAE,uBAAuB,CAAC;QACzC,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC;QACvC,yBAAyB,CAAC,EAAE,MAAM,OAAO,CAAC;KAC3C,CAAC;CACH;AAqBD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,YAA0B,GAAG,WAAW,GAAG,IAAI,CAU1F;AASD,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAEnE"}
|