@satinhq/kit 0.1.0-beta.3 → 0.1.0-beta.4
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/NOTICE +1 -1
- package/README.md +1 -2
- package/dist/acknowledgements/inventory.json +1 -1
- package/dist/actions/RowActions.d.ts +2 -2
- package/dist/actions/RowActions.js +91 -25
- package/dist/actions/fullSwipeContact.d.ts +73 -0
- package/dist/actions/fullSwipeContact.js +186 -0
- package/dist/actions/rowScrub.d.ts +7 -4
- package/dist/actions/rowScrub.js +39 -37
- package/dist/actions/types.d.ts +13 -7
- package/dist/app/InstallComponents.d.ts +4 -7
- package/dist/app/InstallComponents.js +7 -7
- package/dist/app/InstallationDiagnostics.d.ts +2 -1
- package/dist/app/InstallationDiagnostics.js +2 -1
- package/dist/app/appearance.d.ts +5 -3
- package/dist/app/appearance.js +5 -3
- package/dist/app/appearanceStore.d.ts +3 -2
- package/dist/app/buildInfo.d.ts +4 -4
- package/dist/app/buildInfo.js +2 -2
- package/dist/app/definition.d.ts +11 -3
- package/dist/app/definition.js +2 -0
- package/dist/app/head.d.ts +2 -1
- package/dist/app/head.js +13 -16
- package/dist/app/inlineCSP.d.ts +6 -0
- package/dist/app/inlineCSP.js +25 -0
- package/dist/app/install.d.ts +1 -0
- package/dist/app/install.js +5 -2
- package/dist/app/installCheck.js +32 -86
- package/dist/app/installContext.d.ts +10 -5
- package/dist/app/installContext.js +2 -0
- package/dist/app/installCustom.js +1 -0
- package/dist/app/installExperience.js +0 -60
- package/dist/app/installationFailure.d.ts +3 -2
- package/dist/app/installationFailure.js +1 -1
- package/dist/app/launchScreen.d.ts +6 -2
- package/dist/app/launchScreen.js +104 -1
- package/dist/app/launchScreenRuntime.d.ts +31 -4
- package/dist/app/launchScreenRuntime.js +150 -22
- package/dist/app/launchWatchdog.js +25 -65
- package/dist/app/platformScripts.js +11 -0
- package/dist/app/styles.d.ts +3 -0
- package/dist/app/styles.js +42 -0
- package/dist/app/viewportValue.d.ts +2 -1
- package/dist/app/vite.d.ts +4 -0
- package/dist/app/vite.js +22 -24
- package/dist/content/content.d.ts +35 -30
- package/dist/content/content.js +19 -16
- package/dist/content/layout.d.ts +10 -2
- package/dist/content/layout.js +4 -2
- package/dist/content/scaledMetric.d.ts +2 -1
- package/dist/content/scaledMetric.js +2 -1
- package/dist/controls/ColorPicker.d.ts +2 -4
- package/dist/controls/ColorPicker.js +2 -4
- package/dist/controls/DatePicker.d.ts +4 -4
- package/dist/controls/DatePicker.js +2 -2
- package/dist/controls/ProgressView.d.ts +4 -4
- package/dist/controls/ProgressView.js +1 -2
- package/dist/data/database.d.ts +9 -8
- package/dist/data/database.js +4 -4
- package/dist/docs/reference.md +982 -964
- package/dist/docs/tailwind.md +76 -0
- package/dist/docs/tokens.md +1 -1
- package/dist/gestures/GestureView.d.ts +3 -1
- package/dist/gestures/GestureView.js +2 -1
- package/dist/gestures/gesture.d.ts +22 -2
- package/dist/gestures/gesture.js +7 -2
- package/dist/glass/GlassEffect.d.ts +2 -1
- package/dist/glass/GlassEffect.js +2 -1
- package/dist/glass/GlassPresentation.d.ts +1 -0
- package/dist/glass/GlassPresentation.js +21 -4
- package/dist/glass/TabBar.js +23 -1
- package/dist/glass/errorAlert.js +2 -1
- package/dist/glass/errors.d.ts +3 -1
- package/dist/glass/fallback/runtime.d.ts +1 -0
- package/dist/glass/fallback/runtime.js +8 -1
- package/dist/glass/liquidContext.d.ts +2 -0
- package/dist/glass/menuPresenter.d.ts +2 -0
- package/dist/glass/regions.d.ts +2 -1
- package/dist/glass/renderSpecs.d.ts +1 -0
- package/dist/glass/renderSpecs.js +1 -0
- package/dist/glass/webgpu/controlRim.d.ts +29 -0
- package/dist/glass/webgpu/controlRim.js +68 -0
- package/dist/glass/webgpu/passes.d.ts +2 -2
- package/dist/glass/webgpu/production.d.ts +1 -0
- package/dist/glass/webgpu/production.js +27 -1
- package/dist/glass/webgpu/rasterUniforms.js +9 -0
- package/dist/glass/webgpu/shaders.d.ts +2 -2
- package/dist/glass/webgpu/shaders.js +5 -1
- package/dist/haptics/SensoryButton.d.ts +4 -4
- package/dist/haptics/SensoryButton.js +3 -3
- package/dist/image/Image.d.ts +6 -3
- package/dist/image/Image.js +4 -3
- package/dist/image/asset.d.ts +2 -1
- package/dist/keyboard/adapter.d.ts +3 -4
- package/dist/keyboard/geometry.d.ts +1 -1
- package/dist/list-form/ListForm.d.ts +8 -7
- package/dist/list-form/ListForm.js +2 -2
- package/dist/list-form/editing.d.ts +5 -6
- package/dist/list-form/editing.js +4 -5
- package/dist/list-form/offsets.d.ts +2 -2
- package/dist/list-form/offsets.js +2 -2
- package/dist/media/react.d.ts +2 -1
- package/dist/media/react.js +2 -1
- package/dist/media/types.d.ts +6 -3
- package/dist/motion/TransitionView.d.ts +4 -2
- package/dist/motion/TransitionView.js +3 -2
- package/dist/motion/animationValue.d.ts +1 -1
- package/dist/motion/matchedGeometry.d.ts +6 -5
- package/dist/motion/matchedGeometry.js +2 -1
- package/dist/motion/transition.d.ts +4 -1
- package/dist/motion/transition.js +2 -0
- package/dist/navigation/DetailList.d.ts +1 -1
- package/dist/navigation/DetailList.js +1 -1
- package/dist/navigation/Sidebar.d.ts +3 -1
- package/dist/navigation/Sidebar.js +1 -1
- package/dist/navigation/SidebarSymbol.d.ts +2 -1
- package/dist/navigation/SidebarSymbol.js +2 -1
- package/dist/navigation/navigationContext.d.ts +2 -1
- package/dist/navigation/navigationContext.js +2 -1
- package/dist/navigation/splitContext.d.ts +2 -1
- package/dist/navigation/splitContext.js +2 -1
- package/dist/navigation/toolbarMinimization.d.ts +4 -3
- package/dist/navigation/types.d.ts +7 -5
- package/dist/presentation/Presentation.d.ts +8 -7
- package/dist/presentation/Presentation.js +3 -2
- package/dist/presentation/model.d.ts +2 -1
- package/dist/press/Pressable.d.ts +5 -1
- package/dist/press/Pressable.js +2 -0
- package/dist/press/stateMachine.d.ts +1 -1
- package/dist/public/Label.d.ts +2 -1
- package/dist/public/Label.js +2 -1
- package/dist/public/Screen.d.ts +4 -6
- package/dist/public/actions.d.ts +2 -1
- package/dist/public/actions.js +2 -1
- package/dist/public/buttonBorderShape.d.ts +2 -1
- package/dist/public/colors.d.ts +7 -4
- package/dist/public/colors.js +3 -2
- package/dist/public/controlStyle.d.ts +17 -14
- package/dist/public/controls.d.ts +27 -26
- package/dist/public/controls.js +20 -19
- package/dist/public/deviceActions.d.ts +6 -4
- package/dist/public/deviceActions.js +4 -2
- package/dist/public/environment.d.ts +16 -13
- package/dist/public/environment.js +11 -9
- package/dist/public/filePickers.d.ts +6 -4
- package/dist/public/filePickers.js +3 -2
- package/dist/public/glass.d.ts +6 -3
- package/dist/public/glass.js +2 -1
- package/dist/public/glassValue.d.ts +3 -4
- package/dist/public/glassValue.js +3 -4
- package/dist/public/keyboard.d.ts +4 -1
- package/dist/public/keyboard.js +2 -1
- package/dist/public/navigationTransition.d.ts +5 -5
- package/dist/public/navigationTransition.js +2 -2
- package/dist/public/presentation.d.ts +8 -7
- package/dist/public/presentation.js +5 -5
- package/dist/public/runtime.d.ts +9 -8
- package/dist/public/runtime.js +7 -7
- package/dist/public/scroll.d.ts +6 -3
- package/dist/public/scroll.js +3 -1
- package/dist/public/structure.d.ts +2 -2
- package/dist/public/toolbar.d.ts +15 -11
- package/dist/public/toolbar.js +12 -7
- package/dist/reachability/model.d.ts +4 -3
- package/dist/reachability/react.d.ts +4 -1
- package/dist/reachability/react.js +2 -1
- package/dist/root/SatinRoot.d.ts +3 -3
- package/dist/root/SatinRoot.js +12 -36
- package/dist/root/mountSatin.d.ts +1 -1
- package/dist/root/mountSatin.js +26 -35
- package/dist/root/runtimeContext.d.ts +2 -1
- package/dist/root/runtimeContext.js +2 -1
- package/dist/safe-area/Overlay.d.ts +3 -2
- package/dist/safe-area/Overlay.js +2 -1
- package/dist/safe-area/SafeAreaInset.d.ts +11 -7
- package/dist/safe-area/SafeAreaInset.js +7 -6
- package/dist/safe-area/context.d.ts +3 -3
- package/dist/safe-area/context.js +1 -1
- package/dist/safe-area/displayCorners.d.ts +4 -2
- package/dist/safe-area/displayCorners.js +4 -2
- package/dist/safe-area/modifiers.d.ts +14 -10
- package/dist/safe-area/modifiers.js +8 -4
- package/dist/scroll/ScrollSurface.js +5 -1
- package/dist/scroll/ScrollTarget.d.ts +1 -1
- package/dist/scroll/ScrollTarget.js +1 -1
- package/dist/scroll/edgePolicy.d.ts +2 -2
- package/dist/scroll/position.d.ts +5 -3
- package/dist/scroll/types.d.ts +1 -1
- package/dist/search/PadSearchDrawer.d.ts +7 -0
- package/dist/search/PadSearchDrawer.js +8 -0
- package/dist/search/PadSearchPresentationContext.d.ts +6 -0
- package/dist/search/PadSearchPresentationContext.js +4 -0
- package/dist/search/SearchDrawer.d.ts +1 -1
- package/dist/search/SearchDrawer.js +6 -1
- package/dist/search/SearchSuggestionsPopover.d.ts +6 -0
- package/dist/search/SearchSuggestionsPopover.js +42 -0
- package/dist/search/SearchToolbarField.d.ts +2 -1
- package/dist/search/SearchToolbarField.js +28 -7
- package/dist/search/padSearchBarMotion.d.ts +69 -0
- package/dist/search/padSearchBarMotion.js +73 -0
- package/dist/search/padSearchFieldSamples.json +491 -0
- package/dist/search/searchPlacement.d.ts +3 -0
- package/dist/search/searchPlacement.js +7 -0
- package/dist/search/searchable.d.ts +14 -12
- package/dist/search/usePadSearchFieldMotion.d.ts +4 -0
- package/dist/search/usePadSearchFieldMotion.js +100 -0
- package/dist/search/useSearchPresentation.d.ts +2 -1
- package/dist/search/useSearchPresentation.js +23 -5
- package/dist/server.d.ts +4 -4
- package/dist/server.js +4 -4
- package/dist/shapes/corners.d.ts +3 -3
- package/dist/shapes/corners.js +1 -2
- package/dist/styles.css +8289 -8244
- package/dist/sw/vite.d.ts +4 -0
- package/dist/sw/vite.js +2 -0
- package/dist/symbols/Symbol.d.ts +7 -5
- package/dist/symbols/Symbol.js +5 -3
- package/dist/symbols/catalogue.d.ts +1 -1
- package/dist/symbols/types.d.ts +6 -4
- package/dist/tabs/TabSection.d.ts +3 -2
- package/dist/tabs/TabSection.js +2 -1
- package/dist/tabs/TabView.js +54 -51
- package/dist/tasks/useTask.d.ts +5 -4
- package/dist/tasks/useTask.js +4 -3
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +3 -2
- package/dist/text-input/focusChain.d.ts +4 -3
- package/dist/text-input/focusChain.js +3 -2
- package/dist/text-input/kinds.d.ts +1 -1
- package/dist/web/WebView.d.ts +10 -3
- package/dist/web/WebView.js +3 -2
- package/dist/web/authentication.d.ts +12 -1
- package/dist/web/authentication.js +2 -1
- package/dist/web/openURL.d.ts +10 -4
- package/dist/web/openURL.js +4 -2
- package/dist/web/useWebAuthentication.d.ts +2 -1
- package/dist/web/useWebAuthentication.js +2 -1
- package/dist/zoom/Zoomable.d.ts +3 -3
- package/dist/zoom/Zoomable.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Using Tailwind with Satin
|
|
2
|
+
|
|
3
|
+
Add Tailwind v4 to a Satin starter:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install -D tailwindcss@4 @tailwindcss/vite@4
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Keep `satinApp()` in `vite.config.ts` and add the Tailwind plugin:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import react from '@vitejs/plugin-react'
|
|
13
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
14
|
+
import { defineApp, satinApp } from '@satinhq/kit/vite'
|
|
15
|
+
import { defineConfig } from 'vite'
|
|
16
|
+
import { app } from './src/app'
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: [react(), tailwindcss(), satinApp(defineApp(app))],
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Put this in your app's `src/styles.css`, which the starter already imports:
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
@layer theme, base, satin, components, utilities;
|
|
27
|
+
@import 'tailwindcss/theme.css' layer(theme);
|
|
28
|
+
@import 'tailwindcss/utilities.css' layer(utilities);
|
|
29
|
+
|
|
30
|
+
@custom-variant dark (&:where([data-satin-appearance='dark'], [data-satin-appearance='dark'] *));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This follows Tailwind's [Preflight opt-out](https://tailwindcss.com/docs/preflight#disabling-preflight).
|
|
34
|
+
Importing all of `tailwindcss` also installs a global reset. Omit that reset:
|
|
35
|
+
Satin supplies control defaults, and Preflight changes plain headings, images
|
|
36
|
+
and other app content. Keep app resets out of the framework's document and controls.
|
|
37
|
+
|
|
38
|
+
`satinApp()` includes the framework stylesheet automatically, before app CSS.
|
|
39
|
+
Satin reserves the layer order shown above and puts its rules in `satin`.
|
|
40
|
+
Utilities therefore win over framework stylesheet declarations; ordinary unlayered
|
|
41
|
+
app CSS wins over both. Inline component styles and explicit component props
|
|
42
|
+
still take precedence over ordinary utility classes. Prefer the component's
|
|
43
|
+
props for control appearance, sizing and row geometry.
|
|
44
|
+
|
|
45
|
+
Use utilities on app content or components that accept `className`:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { Button, ListRow, Text } from '@satinhq/kit'
|
|
49
|
+
|
|
50
|
+
export function Example() {
|
|
51
|
+
return (
|
|
52
|
+
<ListRow>
|
|
53
|
+
<Text className="text-fuchsia-600">A colored label</Text>
|
|
54
|
+
<Button className="text-fuchsia-600" onPress={() => {}}>Save</Button>
|
|
55
|
+
</ListRow>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`ListRow` does not accept `className`. Use `listRowBackground`, `listRowInsets`
|
|
61
|
+
and `listRowSeparator`, or apply utilities to content inside the row.
|
|
62
|
+
|
|
63
|
+
The custom `dark` variant follows Satin's effective appearance, including an
|
|
64
|
+
in-app override. Tailwind's default media-query variant follows only the system.
|
|
65
|
+
For safe areas, continue using Satin containers and geometry tokens. Do not add
|
|
66
|
+
viewport-height, raw scrolling or `env(safe-area-inset-*)` utilities to the shell.
|
|
67
|
+
|
|
68
|
+
Run your app's typecheck, fidelity lint and build after adding classes.
|
|
69
|
+
Fidelity lint does not expand dependency imports or utility names, so a passing
|
|
70
|
+
lint cannot approve a reset or a geometry utility.
|
|
71
|
+
`satin check <url>` checks a deployment's files and headers; it does not inspect
|
|
72
|
+
the CSS cascade or certify the appearance of utility classes.
|
|
73
|
+
|
|
74
|
+
For a custom build integration that does not use `satinApp()`, the stylesheet
|
|
75
|
+
fallback is `import '@satinhq/kit/styles.css'` once, before app styles. Apps using
|
|
76
|
+
`satinApp()` need no framework CSS import; existing imports remain compatible.
|
package/dist/docs/tokens.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Geometry and appearance tokens
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`satinApp()` includes framework styles automatically. Satin supplies these properties inside its mounted root and may override geometry in adaptive pane descendants. Consume the nearest value; do not copy root measurements into a sidebar or detail pane. Unknown custom-property names do not produce a TypeScript error and can leave a CSS declaration invalid.
|
|
4
4
|
|
|
5
5
|
| Property | Value | Use |
|
|
6
6
|
| --- | --- | --- |
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { Gesture } from './gesture.js';
|
|
3
|
+
/** The gestures a `GestureView` recognizes: a normal, a simultaneous and a high-priority gesture. */
|
|
3
4
|
export type GestureViewProps = Readonly<{
|
|
4
5
|
gesture?: Gesture;
|
|
5
6
|
simultaneousGesture?: Gesture;
|
|
6
7
|
highPriorityGesture?: Gesture;
|
|
7
8
|
children: ReactNode;
|
|
8
9
|
}>;
|
|
9
|
-
/** A
|
|
10
|
+
/** A region that recognizes custom gestures on its children. Scrolling and the back swipe still
|
|
11
|
+
* win the touches they own.
|
|
10
12
|
* @experimental Real-touch arbitration and native thresholds remain unqualified.
|
|
11
13
|
*/
|
|
12
14
|
export declare function GestureView({ gesture, simultaneousGesture, highPriorityGesture, children, }: GestureViewProps): import("react").JSX.Element;
|
|
@@ -6,7 +6,8 @@ import { cancelPointerPressForOwnedGesture, observePointerPressCancellation, } f
|
|
|
6
6
|
import { createGestureArena, failGesture, reduceGestureArena, } from './arena.js';
|
|
7
7
|
import { interactivePopRecognizer, nativeScrollRecognizer } from './existingCompetitors.js';
|
|
8
8
|
const regions = new WeakMap();
|
|
9
|
-
/** A
|
|
9
|
+
/** A region that recognizes custom gestures on its children. Scrolling and the back swipe still
|
|
10
|
+
* win the touches they own.
|
|
10
11
|
* @experimental Real-touch arbitration and native thresholds remain unqualified.
|
|
11
12
|
*/
|
|
12
13
|
export function GestureView({ gesture, simultaneousGesture, highPriorityGesture, children, }) {
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { type GestureDecision, type GestureRecognizerContext } from './arena.js';
|
|
2
|
+
/** A point in CSS pixels. */
|
|
2
3
|
export type GesturePoint = Readonly<{
|
|
3
4
|
x: number;
|
|
4
5
|
y: number;
|
|
5
6
|
}>;
|
|
7
|
+
/** A drag's current and start locations, its translation and its velocity. */
|
|
6
8
|
export type DragGestureValue = Readonly<{
|
|
7
9
|
location: GesturePoint;
|
|
8
10
|
startLocation: GesturePoint;
|
|
9
11
|
translation: GesturePoint;
|
|
10
12
|
velocity: GesturePoint;
|
|
11
13
|
}>;
|
|
14
|
+
/** A pinch's scale relative to its start, and the point it started around. */
|
|
12
15
|
export type MagnifyGestureValue = Readonly<{
|
|
13
16
|
magnification: number;
|
|
14
17
|
startAnchor: GesturePoint;
|
|
15
18
|
}>;
|
|
19
|
+
/** The binding `useGestureState` returns. A gesture updates it while active and resets it when it
|
|
20
|
+
* ends. */
|
|
16
21
|
export type GestureState<Value> = Readonly<{
|
|
17
22
|
update(value: Value): void;
|
|
18
23
|
reset(): void;
|
|
@@ -27,6 +32,8 @@ export type GestureSample = Readonly<{
|
|
|
27
32
|
drag: DragGestureValue;
|
|
28
33
|
magnify: MagnifyGestureValue | undefined;
|
|
29
34
|
}>;
|
|
35
|
+
/** A gesture made by `DragGesture`, `LongPressGesture`, `TapGesture` or `MagnifyGesture`, for a
|
|
36
|
+
* `GestureView`. */
|
|
30
37
|
export type Gesture = Readonly<{
|
|
31
38
|
kind: 'drag' | 'longPress' | 'tap' | 'magnify';
|
|
32
39
|
axis: 'horizontal' | 'vertical' | 'any';
|
|
@@ -37,26 +44,39 @@ export type Gesture = Readonly<{
|
|
|
37
44
|
ended(sample: GestureSample): void;
|
|
38
45
|
cancelled(): void;
|
|
39
46
|
}>;
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
47
|
+
/** Returns a value that a gesture drives, and the binding to pass as the gesture's `state`. The
|
|
48
|
+
* value goes back to `initialValue` when the gesture ends or is cancelled. */
|
|
42
49
|
export declare function useGestureState<Value>(initialValue: Value): readonly [Value, GestureState<Value>];
|
|
50
|
+
/** A drag's callbacks and state, the distance it must travel before it starts (10 by default) and
|
|
51
|
+
* the axis it accepts. */
|
|
43
52
|
export type DragGestureOptions = Callbacks<DragGestureValue> & Readonly<{
|
|
44
53
|
minimumDistance?: number;
|
|
45
54
|
axis?: Gesture['axis'];
|
|
46
55
|
}>;
|
|
56
|
+
/** A drag that starts after the finger moves `minimumDistance` along the accepted axis. */
|
|
47
57
|
export declare function DragGesture({ minimumDistance, axis, ...options }?: DragGestureOptions): Gesture;
|
|
58
|
+
/** A long press's callbacks and state, how long it must be held (500 ms by default) and how far
|
|
59
|
+
* the finger may move (10 by default). */
|
|
48
60
|
export type LongPressGestureOptions = Callbacks<boolean> & Readonly<{
|
|
49
61
|
minimumDurationMs?: number;
|
|
50
62
|
maximumDistance?: number;
|
|
51
63
|
}>;
|
|
64
|
+
/** A press held for `minimumDurationMs` without moving more than `maximumDistance`. */
|
|
52
65
|
export declare function LongPressGesture({ minimumDurationMs, maximumDistance, ...options }?: LongPressGestureOptions): Gesture;
|
|
66
|
+
/** A tap's callbacks and state, the number of taps (1 by default) and how far the finger may
|
|
67
|
+
* move (10 by default). */
|
|
53
68
|
export type TapGestureOptions = Callbacks<GesturePoint> & Readonly<{
|
|
54
69
|
count?: number;
|
|
55
70
|
maximumDistance?: number;
|
|
56
71
|
}>;
|
|
72
|
+
/** A tap, or `count` taps, that ends without moving more than `maximumDistance`. It reports where
|
|
73
|
+
* the finger lifted. */
|
|
57
74
|
export declare function TapGesture({ count, maximumDistance, ...options }?: TapGestureOptions): Gesture;
|
|
75
|
+
/** A pinch's callbacks and state, and how far the scale must change before it starts (0.01 by
|
|
76
|
+
* default). */
|
|
58
77
|
export type MagnifyGestureOptions = Callbacks<MagnifyGestureValue> & Readonly<{
|
|
59
78
|
minimumScaleDelta?: number;
|
|
60
79
|
}>;
|
|
80
|
+
/** A two-finger pinch that starts once the scale changes by `minimumScaleDelta`. */
|
|
61
81
|
export declare function MagnifyGesture({ minimumScaleDelta, ...options }?: MagnifyGestureOptions): Gesture;
|
|
62
82
|
export {};
|
package/dist/gestures/gesture.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { deferGesture, failGesture, recognizeGesture, } from './arena.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/** Returns a value that a gesture drives, and the binding to pass as the gesture's `state`. The
|
|
4
|
+
* value goes back to `initialValue` when the gesture ends or is cancelled. */
|
|
5
5
|
export function useGestureState(initialValue) {
|
|
6
6
|
const [value, setValue] = useState(initialValue);
|
|
7
7
|
const initial = useRef(initialValue);
|
|
@@ -38,6 +38,7 @@ function distance(value, name) {
|
|
|
38
38
|
throw new Error(`${name} must be finite and non-negative.`);
|
|
39
39
|
return value;
|
|
40
40
|
}
|
|
41
|
+
/** A drag that starts after the finger moves `minimumDistance` along the accepted axis. */
|
|
41
42
|
export function DragGesture({ minimumDistance = 10, axis = 'any', ...options } = {}) {
|
|
42
43
|
distance(minimumDistance, 'Drag minimumDistance');
|
|
43
44
|
return Object.freeze({
|
|
@@ -56,6 +57,7 @@ export function DragGesture({ minimumDistance = 10, axis = 'any', ...options } =
|
|
|
56
57
|
},
|
|
57
58
|
});
|
|
58
59
|
}
|
|
60
|
+
/** A press held for `minimumDurationMs` without moving more than `maximumDistance`. */
|
|
59
61
|
export function LongPressGesture({ minimumDurationMs = 500, maximumDistance = 10, ...options } = {}) {
|
|
60
62
|
distance(minimumDurationMs, 'Long press minimumDurationMs');
|
|
61
63
|
distance(maximumDistance, 'Long press maximumDistance');
|
|
@@ -74,6 +76,8 @@ export function LongPressGesture({ minimumDurationMs = 500, maximumDistance = 10
|
|
|
74
76
|
},
|
|
75
77
|
});
|
|
76
78
|
}
|
|
79
|
+
/** A tap, or `count` taps, that ends without moving more than `maximumDistance`. It reports where
|
|
80
|
+
* the finger lifted. */
|
|
77
81
|
export function TapGesture({ count = 1, maximumDistance = 10, ...options } = {}) {
|
|
78
82
|
if (!Number.isInteger(count) || count < 1)
|
|
79
83
|
throw new Error('Tap count must be a positive integer.');
|
|
@@ -90,6 +94,7 @@ export function TapGesture({ count = 1, maximumDistance = 10, ...options } = {})
|
|
|
90
94
|
},
|
|
91
95
|
});
|
|
92
96
|
}
|
|
97
|
+
/** A two-finger pinch that starts once the scale changes by `minimumScaleDelta`. */
|
|
93
98
|
export function MagnifyGesture({ minimumScaleDelta = 0.01, ...options } = {}) {
|
|
94
99
|
distance(minimumScaleDelta, 'Magnify minimumScaleDelta');
|
|
95
100
|
return Object.freeze({
|
|
@@ -28,7 +28,8 @@ export type GlassEffectProps = HTMLAttributes<HTMLDivElement> & Readonly<{
|
|
|
28
28
|
}>;
|
|
29
29
|
/** Local content remains DOM-owned; only the material canvas is painted by reference. */
|
|
30
30
|
export declare function GlassEffect({ glass, shape, touchLight, glassEffectID, namespace, union, transition, press, emerge, onEmergeSettled, onEmergeInk, children, style, onPointerDown, onPointerUp, onClick, ...props }: GlassEffectProps): import("react").JSX.Element;
|
|
31
|
-
/**
|
|
31
|
+
/** Groups nearby glass effects so they render together and blend or morph into one another.
|
|
32
|
+
* `spacing`, 20 pt by default, sets how close effects must be to merge. */
|
|
32
33
|
export declare function GlassEffectContainer({ spacing, children, ...props }: HTMLAttributes<HTMLDivElement> & {
|
|
33
34
|
spacing?: number;
|
|
34
35
|
}): import("react").JSX.Element;
|
|
@@ -245,7 +245,8 @@ export function GlassEffect({ glass = Glass.regular, shape = 'capsule', touchLig
|
|
|
245
245
|
justifyContent: style?.justifyContent,
|
|
246
246
|
}, children: children })) : (children)] }));
|
|
247
247
|
}
|
|
248
|
-
/**
|
|
248
|
+
/** Groups nearby glass effects so they render together and blend or morph into one another.
|
|
249
|
+
* `spacing`, 20 pt by default, sets how close effects must be to merge. */
|
|
249
250
|
export function GlassEffectContainer({ spacing = 20, children, ...props }) {
|
|
250
251
|
const id = useId(), element = useRef(null), canvas = useRef(null);
|
|
251
252
|
const liquid = useContext(MaterialEffectContext);
|
|
@@ -14,6 +14,7 @@ export type GlassPresentationConsumer = Readonly<{
|
|
|
14
14
|
intrinsicWidth?: boolean;
|
|
15
15
|
placement?: 'bottom' | 'top';
|
|
16
16
|
itemFrames?: readonly TabItemFrame[];
|
|
17
|
+
searchProgress?: number;
|
|
17
18
|
items: readonly TabBarItem[];
|
|
18
19
|
selected: string;
|
|
19
20
|
onSelect: (id: string) => void;
|
|
@@ -182,6 +182,8 @@ export function GlassPresentation({ consumer }) {
|
|
|
182
182
|
? consumer.tabs.itemFrames
|
|
183
183
|
: undefined;
|
|
184
184
|
const tabBarPlacement = consumer?.tabs?.placement;
|
|
185
|
+
const searchProgress = tabBarPlacement === 'top' ? (consumer?.tabs?.searchProgress ?? 0) : 0;
|
|
186
|
+
const tabBarOpacity = 1 - searchProgress;
|
|
185
187
|
const compactTabs = readDeviceIdiom() === 'phone' && width > height;
|
|
186
188
|
const intrinsicTabs = Boolean(consumer?.tabs?.intrinsicWidth) && !compactTabs;
|
|
187
189
|
const intrinsicGeometry = useMemo(() => (intrinsicTabs ? intrinsicTabGeometry(dockWidth, tabCount) : undefined), [intrinsicTabs, dockWidth, tabCount]);
|
|
@@ -232,8 +234,11 @@ export function GlassPresentation({ consumer }) {
|
|
|
232
234
|
: 7;
|
|
233
235
|
// The measured TabView material occupies the bottom safe-area band; its
|
|
234
236
|
// glyphs remain above the Home indicator inside that material.
|
|
235
|
-
const dockY = insetFrame?.y ??
|
|
236
|
-
(isDragPage
|
|
237
|
+
const dockY = (insetFrame?.y ??
|
|
238
|
+
(isDragPage
|
|
239
|
+
? dragFrames.capsule.y
|
|
240
|
+
: height - GLASS_DOCK_BOTTOM_GAP_PX - fallbackDockHeight)) -
|
|
241
|
+
64 * searchProgress;
|
|
237
242
|
const dockRadius = isDragPage
|
|
238
243
|
? dragFrames.capsule.height / 2
|
|
239
244
|
: topFrames
|
|
@@ -276,6 +281,7 @@ export function GlassPresentation({ consumer }) {
|
|
|
276
281
|
tabBarPillWidth,
|
|
277
282
|
tabBarPillWidths,
|
|
278
283
|
tabBarPlacement,
|
|
284
|
+
tabBarOpacity,
|
|
279
285
|
tabBarPillHeight,
|
|
280
286
|
tabBarPillCenterY,
|
|
281
287
|
tabBarSelectedIndex: tabBarVisible ? selectedTab : undefined,
|
|
@@ -654,12 +660,13 @@ export function GlassPresentation({ consumer }) {
|
|
|
654
660
|
// DOM, selected state and renderer-neutral region registrations mounted.
|
|
655
661
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
656
662
|
}, [consumer?.backend]);
|
|
657
|
-
|
|
663
|
+
const synchronizeGeometry = useCallback(() => {
|
|
658
664
|
runtimeRef.current?.updateGeometry({
|
|
659
665
|
tabBarCentres,
|
|
660
666
|
tabBarPillWidth,
|
|
661
667
|
tabBarPillWidths,
|
|
662
668
|
tabBarPlacement,
|
|
669
|
+
tabBarOpacity,
|
|
663
670
|
tabBarMinimizedOffsetX,
|
|
664
671
|
tabBarPillHeight,
|
|
665
672
|
tabBarPillCenterY,
|
|
@@ -683,6 +690,7 @@ export function GlassPresentation({ consumer }) {
|
|
|
683
690
|
tabBarCentres,
|
|
684
691
|
tabBarPillWidths,
|
|
685
692
|
tabBarPlacement,
|
|
693
|
+
tabBarOpacity,
|
|
686
694
|
tabBarMinimizedOffsetX,
|
|
687
695
|
tabBarPillWidth,
|
|
688
696
|
tabBarPillHeight,
|
|
@@ -703,6 +711,14 @@ export function GlassPresentation({ consumer }) {
|
|
|
703
711
|
rightX,
|
|
704
712
|
width,
|
|
705
713
|
]);
|
|
714
|
+
useLayoutEffect(() => {
|
|
715
|
+
if (readDeviceIdiom() === 'pad')
|
|
716
|
+
synchronizeGeometry();
|
|
717
|
+
}, [synchronizeGeometry]);
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
if (readDeviceIdiom() !== 'pad')
|
|
720
|
+
synchronizeGeometry();
|
|
721
|
+
}, [synchronizeGeometry]);
|
|
706
722
|
useEffect(() => runtimeRef.current?.setTabSelection(tabBarVisible ? selectedTab : undefined), [selectedTab, tabBarVisible]);
|
|
707
723
|
useLayoutEffect(() => runtimeRef.current?.commitAppearance(appearance), [appearance]);
|
|
708
724
|
// Settings and reference backgrounds are different DOM roots. Hand the newly
|
|
@@ -864,12 +880,13 @@ export function GlassPresentation({ consumer }) {
|
|
|
864
880
|
height: liquidEnvelope.frameHeight,
|
|
865
881
|
}
|
|
866
882
|
: {
|
|
883
|
+
opacity: tabBarOpacity,
|
|
867
884
|
borderRadius: dockRadius,
|
|
868
885
|
left: dockX,
|
|
869
886
|
top: dockY,
|
|
870
887
|
width: dockWidth,
|
|
871
888
|
height: dockHeight,
|
|
872
|
-
}, "data-satin-tab-layout": topFrames ? 'top' : compactTabs ? 'compact' : undefined, role: liquidMotion !== 'none' ? 'presentation' : tabBarVisible ? 'tablist' : undefined, "aria-label": liquidMotion !== 'none'
|
|
889
|
+
}, inert: searchProgress > 0 || undefined, "aria-hidden": searchProgress > 0 || undefined, "data-satin-tab-layout": topFrames ? 'top' : compactTabs ? 'compact' : undefined, role: liquidMotion !== 'none' ? 'presentation' : tabBarVisible ? 'tablist' : undefined, "aria-label": liquidMotion !== 'none'
|
|
873
890
|
? `Liquid ${liquidMotion} surface`
|
|
874
891
|
: tabBarVisible
|
|
875
892
|
? probe
|
package/dist/glass/TabBar.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PadSearchBarMotion } from '../search/padSearchBarMotion.js';
|
|
3
|
+
import { PadSearchPresentationContext } from '../search/PadSearchPresentationContext.js';
|
|
2
4
|
import { useControlStyle } from '../public/controlStyle.js';
|
|
3
5
|
import { orderedTabItems, TopTabLabel } from '../tabs/presentation.js';
|
|
4
6
|
import { handleTabKeyDown } from '../tabs/keyboard.js';
|
|
@@ -20,6 +22,8 @@ import { useGlassMaterialContext } from './context.js';
|
|
|
20
22
|
import { resolveTabBarModel, TabBarAccessoryContext } from './tabBarModel.js';
|
|
21
23
|
import {} from './tokens.js';
|
|
22
24
|
export function TabBar({ className, adaptive = true, onShowSidebar, items: declaredItems, onSelect, selected, style, variant = 'regular', ...props }) {
|
|
25
|
+
const searchPresentation = useContext(PadSearchPresentationContext);
|
|
26
|
+
const searchProgress = searchPresentation?.owner === selected ? 1 : 0;
|
|
23
27
|
const { tint } = useControlStyle();
|
|
24
28
|
const liquid = useContext(MaterialEffectContext);
|
|
25
29
|
const layoutClass = useLayoutClass();
|
|
@@ -37,6 +41,19 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
37
41
|
const { chromeLayer, registerRegion } = useGlassMaterialContext();
|
|
38
42
|
const regionId = useId();
|
|
39
43
|
const elementRef = useRef(null);
|
|
44
|
+
const padBarMotion = useRef(null);
|
|
45
|
+
const previousSearchProgress = useRef(searchProgress);
|
|
46
|
+
useLayoutEffect(() => {
|
|
47
|
+
if (!sidebar || liquidTop || previousSearchProgress.current === searchProgress)
|
|
48
|
+
return;
|
|
49
|
+
previousSearchProgress.current = searchProgress;
|
|
50
|
+
const element = elementRef.current;
|
|
51
|
+
if (!element)
|
|
52
|
+
return;
|
|
53
|
+
padBarMotion.current ??= new PadSearchBarMotion();
|
|
54
|
+
padBarMotion.current.start(element, searchProgress, undefined);
|
|
55
|
+
}, [sidebar, liquidTop, searchProgress]);
|
|
56
|
+
useLayoutEffect(() => () => padBarMotion.current?.dispose(), []);
|
|
40
57
|
const hasAccessory = useContext(TabBarAccessoryContext);
|
|
41
58
|
const compact = readDeviceIdiom() === 'phone' && metrics.layoutViewport.width > metrics.layoutViewport.height;
|
|
42
59
|
const intrinsicWidth = readDeviceIdiom() === 'phone' && !compact && items.length < 4 && !hasAccessory;
|
|
@@ -88,6 +105,7 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
88
105
|
tint,
|
|
89
106
|
insetHost: elementRef.current ?? undefined,
|
|
90
107
|
placement: 'top',
|
|
108
|
+
searchProgress,
|
|
91
109
|
itemFrames,
|
|
92
110
|
});
|
|
93
111
|
return () => liquid?.registerTabs(regionId, undefined);
|
|
@@ -102,6 +120,7 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
102
120
|
tint,
|
|
103
121
|
liquidTop,
|
|
104
122
|
itemFrames,
|
|
123
|
+
searchProgress,
|
|
105
124
|
]);
|
|
106
125
|
useLayoutEffect(() => {
|
|
107
126
|
if (sidebar)
|
|
@@ -152,12 +171,15 @@ export function TabBar({ className, adaptive = true, onShowSidebar, items: decla
|
|
|
152
171
|
const tabBarStyle = {
|
|
153
172
|
...style,
|
|
154
173
|
...(sidebar ? sidebarCssTokens : {}),
|
|
174
|
+
...(sidebar && searchProgress
|
|
175
|
+
? { translate: `0 ${-64 * searchProgress}px`, opacity: 1 - searchProgress }
|
|
176
|
+
: {}),
|
|
155
177
|
'--satin-tab-tint': tint,
|
|
156
178
|
'--satin-tab-count': items.length,
|
|
157
179
|
'--satin-tab-selection-offset': `${selectedIndex * 100}%`,
|
|
158
180
|
'--satin-tab-selected-index': selectedIndex,
|
|
159
181
|
};
|
|
160
|
-
return createPortal(_jsxs("nav", { ...props, ref: elementRef, role: "tablist", onKeyDown: handleTabKeyDown, "aria-label": props['aria-label'] ?? 'App tabs', className: className, "data-satin-glass-region": sidebar ? undefined : regionId, "data-satin-tab-placement": sidebar ? 'top' : 'bottom', "data-satin-glass-variant": variant, "data-satin-role": "chrome", "data-satin-tab-bar": "true", style: tabBarStyle, children: [_jsx(GlassBackground, { glass: variant === 'clear' ? Glass.clear : Glass.regular }), _jsxs("div", { "data-satin-tab-items": "true", children: [onShowSidebar && (_jsx(Pressable, { "data-satin-tab-sidebar-show": "true", "aria-label": "Show Tab Sidebar", onPress: onShowSidebar, children: _jsx(SymbolImage, { name: "sidebar.left" }) })), items.map((item, index) => {
|
|
182
|
+
return createPortal(_jsxs("nav", { ...props, ref: elementRef, role: "tablist", inert: props.inert || (sidebar && searchProgress > 0) || undefined, "aria-hidden": props['aria-hidden'] || (sidebar && searchProgress > 0) || undefined, onKeyDown: handleTabKeyDown, "aria-label": props['aria-label'] ?? 'App tabs', className: className, "data-satin-glass-region": sidebar ? undefined : regionId, "data-satin-tab-placement": sidebar ? 'top' : 'bottom', "data-satin-glass-variant": variant, "data-satin-role": "chrome", "data-satin-tab-bar": "true", style: tabBarStyle, children: [_jsx(GlassBackground, { glass: variant === 'clear' ? Glass.clear : Glass.regular }), _jsxs("div", { "data-satin-tab-items": "true", children: [onShowSidebar && (_jsx(Pressable, { "data-satin-tab-sidebar-show": "true", "aria-label": "Show Tab Sidebar", onPress: onShowSidebar, children: _jsx(SymbolImage, { name: "sidebar.left" }) })), items.map((item, index) => {
|
|
161
183
|
const isSelected = item.id === selected;
|
|
162
184
|
return (_jsxs(Pressable, { role: "tab", id: item.tabId, "aria-controls": item.panelId, "aria-selected": isSelected, tabIndex: isSelected || (selectedIndex < 0 && index === 0) ? 0 : -1, "aria-label": item.accessibilityLabel ??
|
|
163
185
|
(sidebar && item.role === 'search'
|
package/dist/glass/errorAlert.js
CHANGED
|
@@ -33,7 +33,8 @@ export function installGlassErrorAlert() {
|
|
|
33
33
|
const errors = getGlassErrors().filter(error => !error.transient);
|
|
34
34
|
if (!errors.length)
|
|
35
35
|
return;
|
|
36
|
-
|
|
36
|
+
const prelaunch = window.__SATIN_PRELAUNCH__;
|
|
37
|
+
if (prelaunch && ['preparing', 'failed'].includes(prelaunch.snapshot().state)) {
|
|
37
38
|
document.querySelector(':root > pre[data-satin-glass-error]')?.remove();
|
|
38
39
|
return;
|
|
39
40
|
}
|
package/dist/glass/errors.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Why the glass renderer failed, such as `gpu-unavailable` or `device-lost`. In a release build a
|
|
2
|
+
* failure can switch glass to the fallback material. */
|
|
2
3
|
export type GlassErrorCode = 'gpu-unavailable' | 'adapter-unavailable' | 'device-request' | 'context-configure' | 'device-lost' | 'validation' | 'out-of-memory' | 'shader-compilation' | 'worker-startup' | 'readiness-timeout' | 'presentation' | 'unsupported-configuration';
|
|
4
|
+
/** One glass renderer failure: its code, message, the stage it happened in and when. */
|
|
3
5
|
export type GlassError = Readonly<{
|
|
4
6
|
code: GlassErrorCode;
|
|
5
7
|
message: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PadSearchBarMotion } from '../../search/padSearchBarMotion.js';
|
|
1
2
|
import { getGlassErrors } from '../errors.js';
|
|
2
3
|
import { createRenderSpecs, } from '../renderSpecs.js';
|
|
3
4
|
import { TabMotion, TAB_INTERACTIVE_MOTION } from '../lens/tabMotion.js';
|
|
@@ -13,6 +14,7 @@ export class FallbackGlassRuntime {
|
|
|
13
14
|
regions;
|
|
14
15
|
dock;
|
|
15
16
|
thumb;
|
|
17
|
+
padSearch;
|
|
16
18
|
minimize = new TabMinimizeIntent();
|
|
17
19
|
minimizeDom;
|
|
18
20
|
tab = new TabMotion(TAB_INTERACTIVE_MOTION);
|
|
@@ -93,7 +95,7 @@ export class FallbackGlassRuntime {
|
|
|
93
95
|
dockSpec() {
|
|
94
96
|
const spec = createRenderSpecs(this.options.geometry, this.options.appearance).find(spec => spec.id === 'dock');
|
|
95
97
|
// Chrome's DOM host already owns the viewport placement.
|
|
96
|
-
return { ...spec, frameX: 0, frameY: 0 };
|
|
98
|
+
return { ...spec, opacity: 1, frameX: 0, frameY: 0 };
|
|
97
99
|
}
|
|
98
100
|
minimizeTrack(direction, path = 'tap') {
|
|
99
101
|
const track = this.minimizeDom.start(direction, this.dockSpec(), path);
|
|
@@ -131,9 +133,13 @@ export class FallbackGlassRuntime {
|
|
|
131
133
|
this.thumb.paint(this.thumbSpec(selection), this.options.appearance);
|
|
132
134
|
}
|
|
133
135
|
updateGeometry(next) {
|
|
136
|
+
const previousOpacity = this.options.geometry.tabBarOpacity;
|
|
134
137
|
const previousPlacement = this.options.geometry.tabBarPlacement;
|
|
135
138
|
this.options = { ...this.options, geometry: { ...this.options.geometry, ...next } };
|
|
136
139
|
const geometry = this.options.geometry;
|
|
140
|
+
if (previousPlacement === 'top' && geometry.tabBarOpacity !== undefined && previousOpacity !== geometry.tabBarOpacity) {
|
|
141
|
+
(this.padSearch ??= new PadSearchBarMotion()).start(this.options.elements.chrome.dock, 1 - geometry.tabBarOpacity, this.dockSpec());
|
|
142
|
+
}
|
|
137
143
|
this.tab.configure(geometry.tabBarCentres, geometry.tabBarPillWidth, geometry.tabBarPillHeight, geometry.dockWidth, {
|
|
138
144
|
selected: geometry.tabBarSelectedIndex ?? 0,
|
|
139
145
|
centerY: geometry.tabBarPillCenterY,
|
|
@@ -274,6 +280,7 @@ export class FallbackGlassRuntime {
|
|
|
274
280
|
this.dispose(owner);
|
|
275
281
|
}
|
|
276
282
|
dispose(_owner) {
|
|
283
|
+
this.padSearch?.dispose();
|
|
277
284
|
if (this.disposed)
|
|
278
285
|
return;
|
|
279
286
|
this.disposed = true;
|
|
@@ -7,6 +7,8 @@ export type RegisteredTabBar = TabBarProps & Readonly<{
|
|
|
7
7
|
/** 'top': the regular-width iPad bar, laid out by `itemFrames` inside `insetHost`. */
|
|
8
8
|
placement?: 'bottom' | 'top';
|
|
9
9
|
itemFrames?: readonly TabItemFrame[];
|
|
10
|
+
/** iPad search handoff; applied to material, foreground and controls together. */
|
|
11
|
+
searchProgress?: number;
|
|
10
12
|
}>;
|
|
11
13
|
export type MaterialEffectRegistration = Readonly<{
|
|
12
14
|
/** The one glass renderer. */
|
|
@@ -2,6 +2,8 @@ import type { MenuEntry } from './menuTypes.js';
|
|
|
2
2
|
import { type MenuContactSample } from '../menu/menuContact.js';
|
|
3
3
|
import { type ReactNode } from 'react';
|
|
4
4
|
import { type PressableProps } from '../press/Pressable.js';
|
|
5
|
+
/** The props a custom `Menu` trigger receives. Forward all of them to the element that opens the
|
|
6
|
+
* menu. */
|
|
5
7
|
export type MenuTriggerProps = Pick<PressableProps, 'onPress' | 'disabled' | 'aria-expanded' | 'aria-haspopup' | 'aria-controls'>;
|
|
6
8
|
export type MenuProps = Readonly<{
|
|
7
9
|
label: ReactNode;
|
package/dist/glass/regions.d.ts
CHANGED
|
@@ -45,7 +45,8 @@ type GlassRect = Readonly<{
|
|
|
45
45
|
width: number;
|
|
46
46
|
height: number;
|
|
47
47
|
}>;
|
|
48
|
-
/**
|
|
48
|
+
/** A glass tint as red, green, blue and alpha channels from 0 to 1. `Glass.tintRGBA` rejects
|
|
49
|
+
* values outside that range. */
|
|
49
50
|
export type GlassTint = readonly [red: number, green: number, blue: number, alpha: number];
|
|
50
51
|
export type GlassMaterial = Readonly<{
|
|
51
52
|
variant: 'regular' | 'clear';
|
|
@@ -202,6 +202,7 @@ export type RuntimeGeometry = Readonly<{
|
|
|
202
202
|
tabBarPillWidths?: readonly number[];
|
|
203
203
|
/** 'top' is the regular-width iPad tab bar; the phone dock is 'bottom' (the default). */
|
|
204
204
|
tabBarPlacement?: 'bottom' | 'top';
|
|
205
|
+
tabBarOpacity?: number;
|
|
205
206
|
tabBarPillHeight?: number;
|
|
206
207
|
tabBarPillCenterY?: number;
|
|
207
208
|
tabBarCentres?: readonly number[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Exterior rim measured from the iOS 27 stock 44 pt circle, at 3 device pixels/pt.
|
|
2
|
+
* See scripts/research/glass-blackroom/fit-rim.py and the round-2 evidence packet.
|
|
3
|
+
*
|
|
4
|
+
* The dark line is outside the material aperture. Its colour comes from the local
|
|
5
|
+
* backdrop, while the interior specular is a separate normal-dependent lobe.
|
|
6
|
+
* Both lateral normals have the same response; a signed horizontal term would
|
|
7
|
+
* introduce lighting that the native flat-background captures do not contain.
|
|
8
|
+
*/
|
|
9
|
+
export declare function controlRimUniform(appearance: 'light' | 'dark'): [number, number, number, number];
|
|
10
|
+
/** Native dark regular glass changes with the short side, independently of corner radius.
|
|
11
|
+
* The 44/50/62/64/70/80/100 pt sweep measured white interior 185/185/185/185/175/159/128.
|
|
12
|
+
* Grey and 75% grey deltas follow backdrop squared. Limit extrapolation to the measured range.
|
|
13
|
+
*/
|
|
14
|
+
export declare function controlSizeResponse(appearance: 'light' | 'dark', width: number, height: number): number;
|
|
15
|
+
/** White backdrop, bottom exterior at 2 pt: native 44/62 pt controls are 252,
|
|
16
|
+
* 80 pt controls are 248 light / 246 dark; the unscaled kernel is 245.
|
|
17
|
+
* Keep the broad shadow separate from the sub-point rim coverage.
|
|
18
|
+
*/
|
|
19
|
+
export declare function controlShadowScale(appearance: 'light' | 'dark', width: number, height: number): number;
|
|
20
|
+
/** Native 44 pt sweep at background code levels 32/64/96/128: first exterior pixel 6/17/32/52. */
|
|
21
|
+
export declare const CONTROL_RIM_LOW_LUMA_GAIN = 0.6891894;
|
|
22
|
+
/** Joint least-squares fit of the unclipped black/grey/white cardinal profiles.
|
|
23
|
+
* Final amplitudes include the 1.02493656 contrast fit over 32 unclipped flat-background
|
|
24
|
+
* vertical rays after the measured size response. Photo cases were held out.
|
|
25
|
+
* Shared across appearances: the native colour matrix supplies their different contrast.
|
|
26
|
+
* Narrow support/amount, diffuse support/amount (all support distances in points).
|
|
27
|
+
*/
|
|
28
|
+
export declare const CONTROL_SPECULAR: readonly [1.25, 1.5385214, 6.4, 0.38803352];
|
|
29
|
+
export declare const controlRimWGSL = "\nfn materialBoundary(distance: f32, normal: vec2<f32>, uv: vec2<f32>) -> vec4<f32> {\n let rim = global.uControlRim_slot;\n let angular = normal.x * normal.x + rim.w * normal.y * normal.y;\n let backdrop = materialMip(uv, 0.0);\n let luminance = dot(backdrop, vec3f(0.2126, 0.7152, 0.0722));\n let opacity = rim.y + 0.6891894 * max(0.5 - luminance, 0.0);\n let radial = opacity * clamp(1.0 - max(distance, 0.0) / rim.x, 0.0, 1.0);\n // Size sweep: exterior contrast changes linearly above mid-grey, while the face\n // changes quadratically. Normalizing by luminance expresses that contrast as coverage.\n let sizeContrast = global.uControlSize_slot.x * max(2.0 * luminance - 1.0, 0.0);\n let sizeCoverage = sizeContrast / max(0.6 * luminance, 0.001);\n let coverage = clamp(radial * angular * (1.0 + sizeCoverage), 0.0, 1.0);\n // Nine-level native sweep: light reflects a quadratic colour transform;\n // dark retains a small highlight in the upper half of the backdrop range.\n let lightColour = clamp(backdrop * (backdrop - vec3f(0.5)), vec3f(0.0), vec3f(1.0));\n let darkColour = 0.6 * max(backdrop - vec3f(0.5), vec3f(0.0)) * (vec3f(1.0) - backdrop);\n let colour = mix(lightColour, darkColour, rim.z);\n return vec4f(colour * coverage, coverage);\n}\nfn controlSpecular(distance: f32, normal: vec2<f32>) -> f32 {\n let profile = global.uControlSpecular_slot;\n let depth = max(-distance, 0.0);\n let narrow = profile.y * max(1.0 - depth / profile.x, 0.0);\n let diffuse = profile.w * max(1.0 - depth / profile.z, 0.0);\n let facing = clamp((abs(normal.y) - 0.16) / 0.84, 0.0, 1.0);\n return (narrow + diffuse) * facing * facing * facing;\n}\n";
|