@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
|
@@ -1,40 +1,64 @@
|
|
|
1
|
-
import { radiusClassProps, useBarSurface } from "@multiplatform.one/theme";
|
|
2
|
-
import { useCallback, useState, type ReactNode } from "react";
|
|
3
|
-
import { isWeb, Theme, View, XStack, type XStackProps } from "tamagui";
|
|
4
|
-
import { loadGlassView } from "./glass";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
1
|
+
import { isDarkThemeName, radiusClassProps, useBarSurface } from "@multiplatform.one/theme";
|
|
2
|
+
import { Children, useCallback, useMemo, useRef, useState, type ReactNode } from "react";
|
|
3
|
+
import { isWeb, Theme, useThemeName, View, XStack, type XStackProps } from "tamagui";
|
|
4
|
+
import { loadGlassContainer, loadGlassView } from "./glass";
|
|
5
|
+
import { isAppleTouchWeb } from "./webGlass";
|
|
6
|
+
import {
|
|
7
|
+
IconButtonGroupContext,
|
|
8
|
+
IconButtonGroupRegistry,
|
|
9
|
+
IconButtonGroupSurfaceContext,
|
|
10
|
+
type IconButtonGroupSurface,
|
|
11
|
+
} from "./iconButtonGroupContext";
|
|
8
12
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/** UIGlassContainerEffect's own default: the system decides when glass merges. */
|
|
14
|
+
const SYSTEM_GLASS_SPACING = 0;
|
|
15
|
+
|
|
16
|
+
type MeasurableView = {
|
|
17
|
+
measureInWindow?: (
|
|
18
|
+
callback: (x: number, y: number, width: number, height: number) => void,
|
|
19
|
+
) => void;
|
|
20
|
+
};
|
|
14
21
|
|
|
15
22
|
export interface IconButtonGroupProps extends XStackProps {
|
|
16
|
-
/** Over imagery or video: the group shares one
|
|
23
|
+
/** Over imagery or video: the group shares one dark capsule. */
|
|
17
24
|
plate?: "media";
|
|
18
25
|
children?: ReactNode;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
29
|
* LC-176: icon buttons that sit side by side. Grounds merge, they never stand
|
|
23
|
-
* as a row of separate circles: on iOS 26 the bar's group
|
|
24
|
-
* capsule, as the system draws a bar's item group (merging each
|
|
25
|
-
* the glass container left a waist between them, FZ-8158)
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* capsule (FZ-8494).
|
|
30
|
+
* as a row of separate circles: on iOS 26 the bar's group sits in one system
|
|
31
|
+
* Liquid Glass capsule, as the system draws a bar's item group (merging each
|
|
32
|
+
* circle through the glass container left a waist between them, FZ-8158), so
|
|
33
|
+
* press and merge are the system's; over media the group shares one dark
|
|
34
|
+
* capsule; a bare row stays bare. A lone button keeps its own circle, and a
|
|
35
|
+
* bar group with no icon button in it paints no capsule (FZ-8494).
|
|
29
36
|
*/
|
|
30
37
|
export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupProps) {
|
|
31
38
|
const bar = useBarSurface();
|
|
39
|
+
const dark = isDarkThemeName(useThemeName());
|
|
32
40
|
const [capsuleHeight, setCapsuleHeight] = useState(0);
|
|
33
41
|
const [members, setMembers] = useState(0);
|
|
34
42
|
const register = useCallback(() => {
|
|
35
43
|
setMembers((count) => count + 1);
|
|
36
44
|
return () => setMembers((count) => count - 1);
|
|
37
45
|
}, []);
|
|
46
|
+
const capsuleRef = useRef<MeasurableView | null>(null);
|
|
47
|
+
const surface = useMemo<IconButtonGroupSurface>(
|
|
48
|
+
() => ({
|
|
49
|
+
measure(callback) {
|
|
50
|
+
const node = capsuleRef.current;
|
|
51
|
+
if (typeof node?.measureInWindow !== "function") {
|
|
52
|
+
callback(null);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
node.measureInWindow((x, y, width, height) =>
|
|
56
|
+
callback(width > 0 && height > 0 ? { x, y, width, height, radius: height / 2 } : null),
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
[],
|
|
61
|
+
);
|
|
38
62
|
if (plate === "media") {
|
|
39
63
|
return (
|
|
40
64
|
<IconButtonGroupContext.Provider value="media">
|
|
@@ -47,7 +71,6 @@ export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupPro
|
|
|
47
71
|
overflow="hidden"
|
|
48
72
|
data-mp-icon-button-group="media"
|
|
49
73
|
{...radiusClassProps("R-IDENTITY", "icon button plate")}
|
|
50
|
-
{...(isWeb ? { style: MEDIA_PLATE_WEB_STYLE } : undefined)}
|
|
51
74
|
{...rest}
|
|
52
75
|
>
|
|
53
76
|
{children}
|
|
@@ -56,7 +79,8 @@ export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupPro
|
|
|
56
79
|
</IconButtonGroupContext.Provider>
|
|
57
80
|
);
|
|
58
81
|
}
|
|
59
|
-
|
|
82
|
+
const GlassView = bar ? loadGlassView() : null;
|
|
83
|
+
if (GlassView && isWeb && isAppleTouchWeb()) {
|
|
60
84
|
// The caller's layout props stay on the outer row; the capsule hugs the
|
|
61
85
|
// buttons inside it, so a stretched slot never draws a wide empty pill.
|
|
62
86
|
return (
|
|
@@ -76,7 +100,7 @@ export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupPro
|
|
|
76
100
|
data-mp-glass-capsule=""
|
|
77
101
|
>
|
|
78
102
|
{members > 0 ? (
|
|
79
|
-
<
|
|
103
|
+
<GlassView
|
|
80
104
|
style={{
|
|
81
105
|
position: "absolute",
|
|
82
106
|
top: 0,
|
|
@@ -94,42 +118,56 @@ export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupPro
|
|
|
94
118
|
</IconButtonGroupRegistry.Provider>
|
|
95
119
|
);
|
|
96
120
|
}
|
|
97
|
-
const
|
|
121
|
+
const capsule = GlassView != null && members > 0;
|
|
122
|
+
// The capsule hugs its buttons with the web twin's $2 end inset and $1 gap,
|
|
123
|
+
// so its first and last items never sit on its curve (FZ-8550). A group of
|
|
124
|
+
// one button is a lone button, and keeps its circle.
|
|
125
|
+
const lone = members === 1 && Children.toArray(children).length === 1;
|
|
98
126
|
const row = (
|
|
99
127
|
<XStack
|
|
100
128
|
alignItems="center"
|
|
101
|
-
data-mp-icon-button-group={
|
|
129
|
+
data-mp-icon-button-group={capsule ? "glass" : bar ? "bar" : "row"}
|
|
102
130
|
{...rest}
|
|
131
|
+
{...(capsule && !lone ? { paddingHorizontal: "$2" as const, gap: "$1" as const } : null)}
|
|
103
132
|
>
|
|
104
133
|
{children}
|
|
105
134
|
</XStack>
|
|
106
135
|
);
|
|
107
136
|
if (!GlassView) return row;
|
|
137
|
+
// The buttons sit inside the glass, so touches reach it and the system
|
|
138
|
+
// answers the press. It stays mounted with no effect until a button
|
|
139
|
+
// registers, or the swap would remount the buttons that are registering.
|
|
140
|
+
const glass = (
|
|
141
|
+
<GlassView
|
|
142
|
+
glassEffectStyle={capsule ? "regular" : "none"}
|
|
143
|
+
isInteractive={capsule}
|
|
144
|
+
colorScheme={dark ? "dark" : "light"}
|
|
145
|
+
style={{ borderRadius: capsuleHeight / 2, flexShrink: 1, minWidth: 0 }}
|
|
146
|
+
>
|
|
147
|
+
{row}
|
|
148
|
+
</GlassView>
|
|
149
|
+
);
|
|
150
|
+
const GlassContainer = loadGlassContainer();
|
|
108
151
|
return (
|
|
109
152
|
<IconButtonGroupRegistry.Provider value={register}>
|
|
110
153
|
<IconButtonGroupContext.Provider value="glass">
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}}
|
|
129
|
-
/>
|
|
130
|
-
) : null}
|
|
131
|
-
{row}
|
|
132
|
-
</View>
|
|
154
|
+
<IconButtonGroupSurfaceContext.Provider value={members > 0 ? surface : null}>
|
|
155
|
+
<View
|
|
156
|
+
ref={capsuleRef as never}
|
|
157
|
+
collapsable={false}
|
|
158
|
+
flexShrink={1}
|
|
159
|
+
minWidth={0}
|
|
160
|
+
onLayout={(event) => setCapsuleHeight(event.nativeEvent.layout.height)}
|
|
161
|
+
>
|
|
162
|
+
{GlassContainer ? (
|
|
163
|
+
<GlassContainer spacing={SYSTEM_GLASS_SPACING} style={{ flexShrink: 1, minWidth: 0 }}>
|
|
164
|
+
{glass}
|
|
165
|
+
</GlassContainer>
|
|
166
|
+
) : (
|
|
167
|
+
glass
|
|
168
|
+
)}
|
|
169
|
+
</View>
|
|
170
|
+
</IconButtonGroupSurfaceContext.Provider>
|
|
133
171
|
</IconButtonGroupContext.Provider>
|
|
134
172
|
</IconButtonGroupRegistry.Provider>
|
|
135
173
|
);
|
|
@@ -4,3 +4,21 @@
|
|
|
4
4
|
* ratio, never a small glyph lost in a big plate.
|
|
5
5
|
*/
|
|
6
6
|
export const ICON_BUTTON_CIRCLE_GLYPH_RATIO = 0.58;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* LC-176: Phosphor bold's stem is 0.12 of its ink, an SF Symbols bold plus's
|
|
10
|
+
* 0.18 (measured at 17 and 24pt). An outline stroke this wide, in the 256-unit
|
|
11
|
+
* grid, brings a bold glyph to SF semibold-bold (0.154); a wider one closes
|
|
12
|
+
* Phosphor bold's 16-unit counters (the trash lid, copy's overlap).
|
|
13
|
+
*/
|
|
14
|
+
export const GLYPH_BOLD_STROKE_UNITS = 8;
|
|
15
|
+
|
|
16
|
+
export function boldStroke(weight: "regular" | "bold", color = "currentColor") {
|
|
17
|
+
return weight === "bold"
|
|
18
|
+
? {
|
|
19
|
+
stroke: color,
|
|
20
|
+
strokeWidth: GLYPH_BOLD_STROKE_UNITS,
|
|
21
|
+
strokeLinejoin: "round" as const,
|
|
22
|
+
}
|
|
23
|
+
: undefined;
|
|
24
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LC-176 (FZ-8666): real Liquid Glass or none. An Android binary, a binary
|
|
3
|
+
* without the module and an iOS before 26 get no glass at all, so the icon
|
|
4
|
+
* button is its plain glyph.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it, vi } from "vitest";
|
|
7
|
+
import type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
8
|
+
import { resolveSystemGlass, type GlassSources } from "./glass.native";
|
|
9
|
+
|
|
10
|
+
const view = (() => null) as unknown as GlassViewComponent;
|
|
11
|
+
const container = (() => null) as unknown as GlassContainerComponent;
|
|
12
|
+
|
|
13
|
+
function sources(
|
|
14
|
+
nativeModule: unknown,
|
|
15
|
+
overrides: Partial<ReturnType<GlassSources["glass"]>> = {},
|
|
16
|
+
): GlassSources {
|
|
17
|
+
return {
|
|
18
|
+
core: () => ({ requireOptionalNativeModule: () => nativeModule }),
|
|
19
|
+
glass: () => ({
|
|
20
|
+
GlassView: view,
|
|
21
|
+
GlassContainer: container,
|
|
22
|
+
isLiquidGlassAvailable: () => true,
|
|
23
|
+
isGlassEffectAPIAvailable: () => true,
|
|
24
|
+
...overrides,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("resolveSystemGlass", () => {
|
|
30
|
+
it("iOS 26 with the module gets the system's views", () => {
|
|
31
|
+
expect(resolveSystemGlass(sources({}))).toEqual({ view, container });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("no native module (Android, a binary built without it) never requires the package", () => {
|
|
35
|
+
const glass = vi.fn();
|
|
36
|
+
const source = { ...sources(null), glass };
|
|
37
|
+
expect(resolveSystemGlass(source as unknown as GlassSources)).toBeNull();
|
|
38
|
+
expect(glass).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("an iOS before 26 and a beta without the API get no glass", () => {
|
|
42
|
+
expect(resolveSystemGlass(sources({}, { isLiquidGlassAvailable: () => false }))).toBeNull();
|
|
43
|
+
expect(resolveSystemGlass(sources({}, { isGlassEffectAPIAvailable: () => false }))).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("a package that throws on load gets no glass", () => {
|
|
47
|
+
const source = {
|
|
48
|
+
...sources({}),
|
|
49
|
+
glass: () => {
|
|
50
|
+
throw new Error("no module");
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
expect(resolveSystemGlass(source)).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("a module with no container keeps the view", () => {
|
|
57
|
+
expect(resolveSystemGlass(sources({}, { GlassContainer: undefined }))).toEqual({
|
|
58
|
+
view,
|
|
59
|
+
container: null,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -2,50 +2,71 @@ import type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
|
2
2
|
|
|
3
3
|
export type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export interface GlassSources {
|
|
6
|
+
core: () => { requireOptionalNativeModule?: (moduleName: string) => unknown };
|
|
7
|
+
glass: () => {
|
|
8
|
+
GlassView?: GlassViewComponent;
|
|
9
|
+
GlassContainer?: GlassContainerComponent;
|
|
10
|
+
isLiquidGlassAvailable?: () => boolean;
|
|
11
|
+
isGlassEffectAPIAvailable?: () => boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Each require sits in its own try: Metro only treats a require as optional
|
|
16
|
+
// inside one, and an app without the package must still bundle.
|
|
17
|
+
const liveSources: GlassSources = {
|
|
18
|
+
core: () => {
|
|
19
|
+
try {
|
|
20
|
+
return require("expo-modules-core");
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
glass: () => {
|
|
26
|
+
try {
|
|
27
|
+
return require("expo-glass-effect");
|
|
28
|
+
} catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export interface SystemGlass {
|
|
35
|
+
view: GlassViewComponent;
|
|
36
|
+
container: GlassContainerComponent | null;
|
|
37
|
+
}
|
|
7
38
|
|
|
8
39
|
/**
|
|
9
|
-
* LC-176: expo-glass-effect's
|
|
10
|
-
*
|
|
11
|
-
* `requireOptionalNativeModule` BEFORE the guarded `require`,
|
|
12
|
-
* binary built without it, or an older iOS, falls back to the
|
|
13
|
-
* glyph instead of throwing mid-render (the `nativePickerLoader.ts`
|
|
40
|
+
* LC-176: expo-glass-effect's views, when this binary links the native module
|
|
41
|
+
* and the OS draws Liquid Glass (iOS 26+); Android never links it. The module
|
|
42
|
+
* is probed through `requireOptionalNativeModule` BEFORE the guarded `require`,
|
|
43
|
+
* so an OTA on a binary built without it, or an older iOS, falls back to the
|
|
44
|
+
* bare tinted glyph instead of throwing mid-render (the `nativePickerLoader.ts`
|
|
45
|
+
* contract). A binary whose module predates the container keeps its capsule.
|
|
14
46
|
*/
|
|
15
|
-
export function
|
|
16
|
-
if (cachedGlassView !== undefined) return cachedGlassView;
|
|
17
|
-
cachedGlassView = null;
|
|
47
|
+
export function resolveSystemGlass(sources: GlassSources = liveSources): SystemGlass | null {
|
|
18
48
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
GlassView?: GlassViewComponent;
|
|
25
|
-
GlassContainer?: GlassContainerComponent;
|
|
26
|
-
isLiquidGlassAvailable?: () => boolean;
|
|
27
|
-
isGlassEffectAPIAvailable?: () => boolean;
|
|
28
|
-
};
|
|
29
|
-
if (
|
|
30
|
-
glass.GlassView &&
|
|
31
|
-
glass.isLiquidGlassAvailable?.() &&
|
|
32
|
-
glass.isGlassEffectAPIAvailable?.()
|
|
33
|
-
) {
|
|
34
|
-
cachedGlassView = glass.GlassView;
|
|
35
|
-
cachedGlassContainer = glass.GlassContainer ?? null;
|
|
36
|
-
}
|
|
49
|
+
if (sources.core().requireOptionalNativeModule?.("ExpoGlassEffect") == null) return null;
|
|
50
|
+
const glass = sources.glass();
|
|
51
|
+
if (!glass.GlassView || !glass.isLiquidGlassAvailable?.()) return null;
|
|
52
|
+
if (!glass.isGlassEffectAPIAvailable?.()) return null;
|
|
53
|
+
return { view: glass.GlassView, container: glass.GlassContainer ?? null };
|
|
37
54
|
} catch {
|
|
38
|
-
|
|
55
|
+
return null;
|
|
39
56
|
}
|
|
40
|
-
return cachedGlassView;
|
|
41
57
|
}
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
let cached: SystemGlass | null | undefined;
|
|
60
|
+
|
|
61
|
+
function systemGlass(): SystemGlass | null {
|
|
62
|
+
if (cached === undefined) cached = resolveSystemGlass();
|
|
63
|
+
return cached;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function loadGlassView(): GlassViewComponent | null {
|
|
67
|
+
return systemGlass()?.view ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
export function loadGlassContainer(): GlassContainerComponent | null {
|
|
49
|
-
|
|
50
|
-
return cachedGlassContainer ?? null;
|
|
71
|
+
return systemGlass()?.container ?? null;
|
|
51
72
|
}
|
package/src/Button/glass.spec.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment jsdom
|
|
3
3
|
*
|
|
4
|
-
* LC-176 / LC-266:
|
|
5
|
-
*
|
|
4
|
+
* LC-176 / LC-266 (FZ-8666): no browser can draw Liquid Glass, so no browser
|
|
5
|
+
* draws glass, iOS Safari included. The drawn face stays behind one switch in
|
|
6
|
+
* `glass.ts` until Clay answers the compare.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
import { createDefaultThemeConfig } from "@multiplatform.one/theme";
|
|
9
|
-
import { cleanup, render } from "@testing-library/react";
|
|
9
|
+
import { createDefaultThemeConfig, Surface } from "@multiplatform.one/theme";
|
|
10
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
10
11
|
import { createElement } from "react";
|
|
11
12
|
import { TamaguiProvider } from "tamagui";
|
|
12
13
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
13
|
-
import { isAppleTouchWeb, loadGlassView } from "./glass";
|
|
14
|
+
import { isAppleTouchWeb, loadGlassContainer, loadGlassView } from "./glass";
|
|
15
|
+
import { IconButton, IconButtonGroup } from "./index";
|
|
16
|
+
import { WebGlassFace } from "./webGlass";
|
|
14
17
|
|
|
15
18
|
const IPHONE =
|
|
16
19
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1";
|
|
@@ -44,30 +47,75 @@ describe("isAppleTouchWeb", () => {
|
|
|
44
47
|
});
|
|
45
48
|
});
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
function Glyph({ size }: { size?: number }) {
|
|
51
|
+
return createElement("svg", { "data-glyph": "", width: size, height: size });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function renderBar(ui: ReturnType<typeof createElement>) {
|
|
55
|
+
return render(
|
|
56
|
+
createElement(
|
|
57
|
+
TamaguiProvider,
|
|
58
|
+
{ config: houseConfig.tamagui, defaultTheme: "light", disableInjectCSS: true },
|
|
59
|
+
createElement(Surface, { bar: true }, ui),
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("no glass on the web (LC-176)", () => {
|
|
48
65
|
afterEach(() => {
|
|
49
66
|
cleanup();
|
|
50
67
|
vi.unstubAllGlobals();
|
|
51
68
|
});
|
|
52
69
|
|
|
53
|
-
it(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
it.each([
|
|
71
|
+
["iOS Safari", { userAgent: IPHONE, platform: "iPhone", maxTouchPoints: 5 }],
|
|
72
|
+
["iPadOS Safari", { userAgent: MAC_SAFARI, platform: "MacIntel", maxTouchPoints: 5 }],
|
|
73
|
+
["desktop Safari", { userAgent: MAC_SAFARI, platform: "MacIntel", maxTouchPoints: 0 }],
|
|
74
|
+
["Android Chrome", { userAgent: PIXEL, platform: "Linux armv8l", maxTouchPoints: 5 }],
|
|
75
|
+
])("%s loads no glass and draws the plain glyph", (_browser, nav) => {
|
|
76
|
+
vi.stubGlobal("navigator", nav);
|
|
77
|
+
expect(loadGlassView()).toBeNull();
|
|
78
|
+
expect(loadGlassContainer()).toBeNull();
|
|
79
|
+
renderBar(
|
|
80
|
+
createElement(
|
|
81
|
+
"div",
|
|
82
|
+
null,
|
|
83
|
+
createElement(
|
|
84
|
+
IconButtonGroup,
|
|
85
|
+
null,
|
|
86
|
+
createElement(IconButton, { icon: Glyph, "aria-label": "Search" }),
|
|
87
|
+
createElement(IconButton, { icon: Glyph, "aria-label": "More" }),
|
|
88
|
+
),
|
|
89
|
+
createElement(IconButton, { icon: Glyph, "aria-label": "Back" }),
|
|
90
|
+
),
|
|
91
|
+
);
|
|
92
|
+
expect(document.querySelector("[data-mp-glass]")).toBeNull();
|
|
93
|
+
expect(document.querySelector("[data-mp-glass-capsule]")).toBeNull();
|
|
94
|
+
expect(document.querySelector('[data-mp-icon-button-group="bar"]')).not.toBeNull();
|
|
95
|
+
for (const name of ["Search", "More", "Back"])
|
|
96
|
+
expect(screen.getByLabelText(name).getAttribute("data-mp-icon-button")).toBe("glyph");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("the drawn web face behind the switch", () => {
|
|
101
|
+
afterEach(cleanup);
|
|
102
|
+
|
|
103
|
+
it("sits under the glyph it backs, which stays its sibling", () => {
|
|
57
104
|
const { container } = render(
|
|
58
105
|
createElement(
|
|
59
106
|
TamaguiProvider,
|
|
60
107
|
{ config: houseConfig.tamagui, defaultTheme: "light", disableInjectCSS: true },
|
|
61
|
-
createElement(
|
|
108
|
+
createElement(
|
|
109
|
+
WebGlassFace,
|
|
110
|
+
{ style: { position: "absolute" } },
|
|
111
|
+
createElement("svg", { "data-glyph": "" }),
|
|
112
|
+
),
|
|
62
113
|
),
|
|
63
114
|
);
|
|
64
115
|
const face = container.querySelector('[data-mp-glass="web"]') as HTMLElement | null;
|
|
65
116
|
expect(face).not.toBeNull();
|
|
66
117
|
expect(face!.style.zIndex).toBe("-1");
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
it("every other browser draws the bare glyph", () => {
|
|
70
|
-
vi.stubGlobal("navigator", { userAgent: PIXEL, platform: "Linux armv8l", maxTouchPoints: 5 });
|
|
71
|
-
expect(loadGlassView()).toBeNull();
|
|
118
|
+
expect(face!.querySelector("[data-glyph]")).toBeNull();
|
|
119
|
+
expect(face!.nextElementSibling?.hasAttribute("data-glyph")).toBe(true);
|
|
72
120
|
});
|
|
73
121
|
});
|
package/src/Button/glass.ts
CHANGED
|
@@ -4,9 +4,10 @@ import { isAppleTouchWeb, WebGlassFace } from "./webGlass";
|
|
|
4
4
|
/** The slice of expo-glass-effect's `GlassView` an icon button draws. */
|
|
5
5
|
export type GlassViewComponent = ComponentType<{
|
|
6
6
|
style?: unknown;
|
|
7
|
-
glassEffectStyle?: "regular" | "clear";
|
|
7
|
+
glassEffectStyle?: "regular" | "clear" | "none";
|
|
8
8
|
isInteractive?: boolean;
|
|
9
|
-
|
|
9
|
+
colorScheme?: "auto" | "light" | "dark";
|
|
10
|
+
children?: ReactNode;
|
|
10
11
|
}>;
|
|
11
12
|
|
|
12
13
|
/** The slice of expo-glass-effect's `GlassContainer` an icon button group draws. */
|
|
@@ -19,20 +20,23 @@ export type GlassContainerComponent = ComponentType<{
|
|
|
19
20
|
export { isAppleTouchWeb } from "./webGlass";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* LC-176:
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* liquid glass if it's an iPhone"); every other browser draws the bare glyph.
|
|
23
|
+
* LC-176 (FZ-8666): iOS Safari cannot draw Liquid Glass, so it draws no glass.
|
|
24
|
+
* This is the one switch for the drawn face Clay asked for on
|
|
25
|
+
* rv_3bb3f1f65d6b981f, off since his note on rv_d481d34557e702ef, until he
|
|
26
|
+
* answers rv_c6369c2bab2ffea0.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
return isAppleTouchWeb() ? (WebGlassFace as GlassViewComponent) : null;
|
|
30
|
-
}
|
|
28
|
+
const iosSafariGlassFace = false;
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
|
-
* LC-176: the
|
|
34
|
-
*
|
|
31
|
+
* LC-176: the system Liquid Glass an icon button sits in on a bar. Only native
|
|
32
|
+
* iOS 26 has it, from expo-glass-effect (`glass.native.ts`; Android probes
|
|
33
|
+
* null), so the web draws the plain glyph.
|
|
35
34
|
*/
|
|
35
|
+
export function loadGlassView(): GlassViewComponent | null {
|
|
36
|
+
return iosSafariGlassFace && isAppleTouchWeb() ? (WebGlassFace as GlassViewComponent) : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** LC-176: the system's Liquid Glass container. Only iOS has it. */
|
|
36
40
|
export function loadGlassContainer(): GlassContainerComponent | null {
|
|
37
41
|
return null;
|
|
38
42
|
}
|
|
@@ -4,9 +4,8 @@ const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : us
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* LC-176: the ground an `IconButtonGroup` paints under its buttons. `media`
|
|
7
|
-
* is one shared
|
|
8
|
-
*
|
|
9
|
-
* so a button inside draws its bare glyph instead of its own circle.
|
|
7
|
+
* is one shared dark capsule and `glass` one shared Liquid Glass capsule, so
|
|
8
|
+
* a button inside draws its bare glyph instead of its own circle.
|
|
10
9
|
*/
|
|
11
10
|
export type IconButtonGroupGround = "media" | "glass" | null;
|
|
12
11
|
|
|
@@ -30,3 +29,28 @@ export function useIconButtonGroupMember(iconOnly: boolean): void {
|
|
|
30
29
|
[iconOnly, register],
|
|
31
30
|
);
|
|
32
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The window frame and corner radius of the plate an `IconButtonGroup` paints
|
|
35
|
+
* (a native Liquid Glass capsule). Native has no DOM to walk, so a panel
|
|
36
|
+
* opening from a grouped button reads the plate it covers here (LC-112,
|
|
37
|
+
* FZ-8550).
|
|
38
|
+
*/
|
|
39
|
+
export interface IconButtonGroupPlate {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
radius: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IconButtonGroupSurface {
|
|
48
|
+
/** Answers once: the plate, or null when it cannot be measured. */
|
|
49
|
+
measure(callback: (plate: IconButtonGroupPlate | null) => void): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const IconButtonGroupSurfaceContext = createContext<IconButtonGroupSurface | null>(null);
|
|
53
|
+
|
|
54
|
+
export function useIconButtonGroupSurface(): IconButtonGroupSurface | null {
|
|
55
|
+
return useContext(IconButtonGroupSurfaceContext);
|
|
56
|
+
}
|