@astryxdesign/core 0.7.7-canary.4a8a76e → 0.7.7-canary.4e495ec
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/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +79 -9
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -1
- package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
- package/dist/BottomSheet/bottomSheetStack.js +5 -4
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +7 -1
- package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +5 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +5 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +16 -14
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +15 -11
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
- package/dist/Drawer/useDrawerDialogPresence.js +5 -1
- package/dist/Field/PanelSearchInput.d.ts +9 -1
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +3 -1
- package/dist/InfoTip/InfoTip.d.ts +2 -2
- package/dist/InfoTip/InfoTip.js +4 -4
- package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +25 -20
- package/dist/Layer/useBackdropPress.d.ts +27 -0
- package/dist/Layer/useBackdropPress.d.ts.map +1 -0
- package/dist/Layer/useBackdropPress.js +53 -0
- package/dist/Layer/usePressPin.d.ts +3 -5
- package/dist/Layer/usePressPin.d.ts.map +1 -1
- package/dist/Layer/usePressPin.js +4 -6
- package/dist/MobileNav/MobileNav.js +0 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +2 -4
- package/dist/Outline/Outline.d.ts +2 -4
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +4 -52
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +1 -0
- package/dist/Outline/useScrollSpy.js +3 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +134 -179
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +5 -0
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +118 -260
- package/dist/Resizable/resizeGesture.d.ts +91 -0
- package/dist/Resizable/resizeGesture.d.ts.map +1 -0
- package/dist/Resizable/resizeGesture.js +251 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +115 -8
- package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
- package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
- package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
- package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
- package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
- package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
- package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
- package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +96 -67
- package/dist/Selector/optionShape.d.ts +24 -0
- package/dist/Selector/optionShape.d.ts.map +1 -0
- package/dist/Selector/optionShape.js +48 -0
- package/dist/Selector/selectableOptionEntries.d.ts +20 -0
- package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
- package/dist/Selector/selectableOptionEntries.js +38 -0
- package/dist/Selector/utils.d.ts +2 -17
- package/dist/Selector/utils.d.ts.map +1 -1
- package/dist/Selector/utils.js +3 -51
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +3 -9
- package/dist/Switch/switchTouch.d.ts +0 -7
- package/dist/Switch/switchTouch.d.ts.map +1 -1
- package/dist/Switch/switchTouch.js +2 -10
- package/dist/Switch/useSwitchTouch.d.ts +2 -11
- package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
- package/dist/Switch/useSwitchTouch.js +26 -85
- package/dist/Token/Token.d.ts +2 -2
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +12 -4
- package/dist/Token/TokenNamingContext.d.ts +14 -0
- package/dist/Token/TokenNamingContext.d.ts.map +1 -0
- package/dist/Token/TokenNamingContext.js +23 -0
- package/dist/Tokenizer/Tokenizer.d.ts +46 -59
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +35 -58
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
- package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
- package/dist/Tooltip/Tooltip.d.ts +25 -30
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +24 -5
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +28 -29
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +20 -8
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +4 -7
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -7
- package/dist/astryx.css +76 -5
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +53 -5
- package/dist/hooks/actionGroupTracker.d.ts +3 -3
- package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
- package/dist/hooks/actionGroupTracker.js +6 -53
- package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
- package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
- package/dist/hooks/useHighlightedOptionScroll.js +112 -11
- package/dist/hooks/useMediaQuery.d.ts +2 -1
- package/dist/hooks/useMediaQuery.d.ts.map +1 -1
- package/dist/hooks/useMediaQuery.js +7 -4
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +4 -2
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +1 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
- package/dist/internal/SheetConfirmation.d.ts +11 -0
- package/dist/internal/SheetConfirmation.d.ts.map +1 -0
- package/dist/internal/SheetConfirmation.js +92 -0
- package/dist/internal/sheetDraft.d.ts +35 -0
- package/dist/internal/sheetDraft.d.ts.map +1 -0
- package/dist/internal/sheetDraft.js +65 -0
- package/dist/internal/touch-sheet.d.ts +21 -0
- package/dist/internal/touch-sheet.d.ts.map +1 -0
- package/dist/internal/touch-sheet.js +19 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
- package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
- package/dist/motion/roles.d.ts +7 -1
- package/dist/motion/roles.d.ts.map +1 -1
- package/dist/motion/roles.js +20 -1
- package/dist/motion/uikit/index.d.ts +6 -5
- package/dist/motion/uikit/index.d.ts.map +1 -1
- package/dist/motion/uikit/index.js +6 -5
- package/dist/motion/uikit/segmented.d.ts +55 -0
- package/dist/motion/uikit/segmented.d.ts.map +1 -0
- package/dist/motion/uikit/segmented.js +77 -0
- package/dist/motion/uikit/springs.d.ts +0 -18
- package/dist/motion/uikit/springs.d.ts.map +1 -1
- package/dist/motion/uikit/springs.js +5 -21
- package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
- package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
- package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
- package/dist/utils/browserClickWait.d.ts +35 -0
- package/dist/utils/browserClickWait.d.ts.map +1 -0
- package/dist/utils/browserClickWait.js +123 -0
- package/dist/utils/clickGuard.d.ts +8 -1
- package/dist/utils/clickGuard.d.ts.map +1 -1
- package/dist/utils/clickGuard.js +24 -1
- package/locales/en.json +4 -0
- package/locales/pseudo.json +3 -0
- package/package.json +8 -8
- package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
- package/src/BottomSheet/BottomSheet.tsx +95 -10
- package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
- package/src/BottomSheet/bottomSheetStack.ts +8 -4
- package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
- package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
- package/src/CheckboxList/CheckboxList.tsx +6 -0
- package/src/CheckboxList/CheckboxListContext.tsx +6 -0
- package/src/CheckboxList/CheckboxListItem.tsx +11 -2
- package/src/DateInput/DateInput.tsx +8 -2
- package/src/DateTimeInput/DateTimeInput.tsx +8 -2
- package/src/Dialog/Dialog.tsx +44 -14
- package/src/Drawer/Drawer.tsx +28 -11
- package/src/Drawer/useDrawerDialogPresence.ts +5 -1
- package/src/Field/PanelSearchInput.tsx +11 -0
- package/src/InfoTip/InfoTip.doc.mjs +3 -3
- package/src/InfoTip/InfoTip.tsx +5 -5
- package/src/Layer/layerAnimations.stylex.ts +24 -16
- package/src/Layer/useBackdropPress.ts +81 -0
- package/src/Layer/usePressPin.ts +5 -11
- package/src/MobileNav/MobileNav.tsx +1 -1
- package/src/MultiSelector/hooks.ts +2 -4
- package/src/Outline/Outline.doc.mjs +177 -51
- package/src/Outline/Outline.tsx +5 -57
- package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
- package/src/Outline/useOutlineFromMarkdown.ts +1 -0
- package/src/Outline/useScrollSpy.ts +3 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
- package/src/PowerSearch/PowerSearch.tsx +196 -183
- package/src/RadioList/RadioList.doc.mjs +4 -2
- package/src/RadioList/RadioList.tsx +4 -0
- package/src/Resizable/ResizeHandle.tsx +133 -328
- package/src/Resizable/resizeGesture.ts +368 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
- package/src/SegmentedControl/SegmentedControl.tsx +179 -5
- package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
- package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1637 -0
- package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
- package/src/Selector/Selector.tsx +136 -89
- package/src/Selector/optionShape.ts +64 -0
- package/src/Selector/selectableOptionEntries.ts +39 -0
- package/src/Selector/utils.ts +9 -68
- package/src/SideNav/SideNavItem.tsx +3 -7
- package/src/Switch/switchTouch.ts +2 -10
- package/src/Switch/useSwitchTouch.ts +26 -96
- package/src/Token/Token.tsx +13 -4
- package/src/Token/TokenNamingContext.ts +28 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
- package/src/Tokenizer/Tokenizer.tsx +81 -86
- package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
- package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
- package/src/Tooltip/Tooltip.doc.mjs +123 -54
- package/src/Tooltip/Tooltip.tsx +36 -39
- package/src/Tooltip/index.ts +0 -1
- package/src/Tooltip/useTooltip.doc.mjs +147 -33
- package/src/Tooltip/useTooltip.tsx +39 -32
- package/src/TopNav/TopNavItem.tsx +6 -5
- package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
- package/src/TopNav/TopNavMenu.tsx +6 -5
- package/src/gestures/GestureHost.ts +73 -4
- package/src/hooks/actionGroupTracker.ts +12 -56
- package/src/hooks/useHighlightedOptionScroll.ts +123 -15
- package/src/hooks/useMediaQuery.doc.mjs +2 -2
- package/src/hooks/useMediaQuery.ts +13 -4
- package/src/hooks/useMenuPress.ts +8 -2
- package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
- package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
- package/src/i18n/generated-locales/en.generated.ts +1 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
- package/src/internal/SheetConfirmation.tsx +101 -0
- package/src/internal/sheetDraft.ts +80 -0
- package/src/internal/touch-sheet.ts +21 -0
- package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
- package/src/motion/MEASUREMENTS.md +7 -0
- package/src/motion/README.md +1 -0
- package/src/motion/roles.ts +20 -1
- package/src/motion/uikit/index.ts +5 -5
- package/src/motion/uikit/segmented.ts +74 -0
- package/src/motion/uikit/springs.ts +5 -21
- package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
- package/src/utils/browserClickWait.ts +133 -0
- package/src/utils/clickGuard.ts +37 -1
- package/dist/internal/tokenizer-presentation.d.ts +0 -21
- package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
- package/dist/internal/tokenizer-presentation.js +0 -21
- package/src/internal/tokenizer-presentation.ts +0 -25
|
@@ -483,7 +483,10 @@ export function useMenuPress(options: UseMenuPressOptions): UseMenuPressReturn {
|
|
|
483
483
|
// Detach before acting: the activation usually closes the menu.
|
|
484
484
|
endGesture();
|
|
485
485
|
// Once armed, the one swallower stays installed.
|
|
486
|
-
armClickGuard(doc, {
|
|
486
|
+
armClickGuard(doc, {
|
|
487
|
+
staysInstalled: true,
|
|
488
|
+
cancelsTouchEnd: native?.pointerType === 'touch',
|
|
489
|
+
});
|
|
487
490
|
if (native != null) {
|
|
488
491
|
activate(effect.row, native);
|
|
489
492
|
}
|
|
@@ -492,7 +495,10 @@ export function useMenuPress(options: UseMenuPressOptions): UseMenuPressReturn {
|
|
|
492
495
|
case 'settle': {
|
|
493
496
|
endGesture();
|
|
494
497
|
if (effect.stray) {
|
|
495
|
-
armClickGuard(doc, {
|
|
498
|
+
armClickGuard(doc, {
|
|
499
|
+
staysInstalled: true,
|
|
500
|
+
cancelsTouchEnd: native?.pointerType === 'touch',
|
|
501
|
+
});
|
|
496
502
|
}
|
|
497
503
|
if (effect.dismiss) {
|
|
498
504
|
optionsRef.current.onDismiss?.();
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const english: Readonly<Record<string, string>> = {
|
|
10
10
|
"@astryx.powersearch.allFiltersRemoved": "All filters removed",
|
|
11
|
+
"@astryx.powersearch.editableFiltersRemoved": "Editable filters removed",
|
|
11
12
|
"@astryx.powersearch.editor.apply": "Apply",
|
|
12
13
|
"@astryx.powersearch.label": "Search",
|
|
13
14
|
"@astryx.powersearch.placeholder": "Search…",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const english: Readonly<Record<string, string>> = {
|
|
10
|
+
"@astryx.alertDialog.cancel": "Cancel",
|
|
10
11
|
"@astryx.textInput.clearLabel": "Clear {label}",
|
|
11
12
|
"@astryx.tokenizer.add": "Add",
|
|
12
13
|
"@astryx.tokenizer.addValue": "Add {value}",
|
|
@@ -16,11 +17,16 @@ const english: Readonly<Record<string, string>> = {
|
|
|
16
17
|
"@astryx.tokenizer.clearAllTitle": "Clear all selected items?",
|
|
17
18
|
"@astryx.tokenizer.clearSelection": "Clear selection",
|
|
18
19
|
"@astryx.tokenizer.create": "Create \"{value}\"",
|
|
19
|
-
"@astryx.tokenizer.
|
|
20
|
+
"@astryx.tokenizer.discardChanges": "Discard changes",
|
|
21
|
+
"@astryx.tokenizer.discardChangesDescription": "Your changes to {label} haven't been saved.",
|
|
22
|
+
"@astryx.tokenizer.discardChangesTitle": "Discard changes?",
|
|
23
|
+
"@astryx.tokenizer.keepEditing": "Keep editing",
|
|
20
24
|
"@astryx.tokenizer.noResultsDescription": "Try a different search.",
|
|
21
25
|
"@astryx.tokenizer.nothingToClear": "Nothing selected",
|
|
26
|
+
"@astryx.tokenizer.save": "Save",
|
|
22
27
|
"@astryx.tokenizer.search": "Search {label}",
|
|
23
28
|
"@astryx.tokenizer.searchPlaceholder": "Search…",
|
|
29
|
+
"@astryx.tokenizer.selectionLimit": "Select up to {count, plural, one {# item} other {# items}}",
|
|
24
30
|
"@astryx.tokenizer.tokenAdded": "Added {label}",
|
|
25
31
|
"@astryx.tokenizer.tokenRemoved": "Removed {label}",
|
|
26
32
|
"@astryx.typeahead.emptySearchResults": "No results found",
|
|
@@ -315,6 +315,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
|
|
|
315
315
|
'@astryx.powersearch.label': 'Search',
|
|
316
316
|
'@astryx.powersearch.placeholder': 'Search…',
|
|
317
317
|
'@astryx.powersearch.allFiltersRemoved': 'All filters removed',
|
|
318
|
+
'@astryx.powersearch.editableFiltersRemoved': 'Editable filters removed',
|
|
318
319
|
'@astryx.resizable.collapsed': 'Collapsed',
|
|
319
320
|
'@astryx.resizable.handle.label': 'Resize handle',
|
|
320
321
|
'@astryx.selector.placeholder': 'Select…',
|
|
@@ -322,6 +322,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
|
|
|
322
322
|
'@astryx.powersearch.label': '⟦Šéàřçĥ⟧',
|
|
323
323
|
'@astryx.powersearch.placeholder': '⟦Šéàřçĥ…⟧',
|
|
324
324
|
'@astryx.powersearch.allFiltersRemoved': '⟦Àłł ƒíłţéřš řéɱóṽéð⟧',
|
|
325
|
+
'@astryx.powersearch.editableFiltersRemoved': '⟦Éðíţàƀłé ƒíłţéřš řéɱóṽéð⟧',
|
|
325
326
|
'@astryx.resizable.collapsed': '⟦Çółłàþšéð⟧',
|
|
326
327
|
'@astryx.resizable.handle.label': '⟦Řéšížé ĥàñðłé⟧',
|
|
327
328
|
'@astryx.selector.placeholder': '⟦Šéłéçţ…⟧',
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file SheetConfirmation.tsx
|
|
7
|
+
* @input Dialog, Layout, Heading, Text, Button; caller-owned copy
|
|
8
|
+
* @output SheetConfirmation — the confirmation a touch sheet opens over
|
|
9
|
+
* itself for Clear all and for discarding a changed draft
|
|
10
|
+
* @position Core-internal, shared by the Tokenizer sheet and the PowerSearch
|
|
11
|
+
* sheets; not a public export
|
|
12
|
+
*
|
|
13
|
+
* AlertDialog fixes its purpose to form and its safe action to ghost. A touch
|
|
14
|
+
* sheet's confirmation allows scrim dismissal. Its two full-width large
|
|
15
|
+
* actions are stacked and separated: the destructive action first in reading
|
|
16
|
+
* order, the safe action focused first. The alert-dialog theme target stays.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {useId} from 'react';
|
|
20
|
+
import * as stylex from '@stylexjs/stylex';
|
|
21
|
+
import {Button} from '../Button/Button';
|
|
22
|
+
import {Dialog} from '../Dialog/Dialog';
|
|
23
|
+
import {Heading} from '../Heading/Heading';
|
|
24
|
+
import {Layout} from '../Layout/Layout';
|
|
25
|
+
import {LayoutContent} from '../Layout/LayoutContent';
|
|
26
|
+
import {LayoutFooter} from '../Layout/LayoutFooter';
|
|
27
|
+
import {Stack} from '../Stack/Stack';
|
|
28
|
+
import {Text} from '../Text/Text';
|
|
29
|
+
import {themeProps} from '../utils/themeProps';
|
|
30
|
+
|
|
31
|
+
export interface SheetConfirmationProps {
|
|
32
|
+
isOpen: boolean;
|
|
33
|
+
onOpenChange: (isOpen: boolean) => void;
|
|
34
|
+
title: string;
|
|
35
|
+
description: string;
|
|
36
|
+
cancelLabel: string;
|
|
37
|
+
actionLabel: string;
|
|
38
|
+
onAction: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const styles = stylex.create({
|
|
42
|
+
action: {width: '100%'},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export function SheetConfirmation({
|
|
46
|
+
isOpen,
|
|
47
|
+
onOpenChange,
|
|
48
|
+
title,
|
|
49
|
+
description,
|
|
50
|
+
cancelLabel,
|
|
51
|
+
actionLabel,
|
|
52
|
+
onAction,
|
|
53
|
+
}: SheetConfirmationProps) {
|
|
54
|
+
const titleId = useId();
|
|
55
|
+
const descriptionId = useId();
|
|
56
|
+
return (
|
|
57
|
+
<Dialog
|
|
58
|
+
{...themeProps('alert-dialog')}
|
|
59
|
+
isOpen={isOpen}
|
|
60
|
+
onOpenChange={onOpenChange}
|
|
61
|
+
purpose="info"
|
|
62
|
+
role="alertdialog"
|
|
63
|
+
aria-labelledby={titleId}
|
|
64
|
+
aria-describedby={descriptionId}
|
|
65
|
+
width={400}>
|
|
66
|
+
<Layout
|
|
67
|
+
content={
|
|
68
|
+
<LayoutContent>
|
|
69
|
+
<Heading level={2} id={titleId}>
|
|
70
|
+
{title}
|
|
71
|
+
</Heading>
|
|
72
|
+
<Text type="body" color="secondary" id={descriptionId}>
|
|
73
|
+
{description}
|
|
74
|
+
</Text>
|
|
75
|
+
</LayoutContent>
|
|
76
|
+
}
|
|
77
|
+
footer={
|
|
78
|
+
<LayoutFooter>
|
|
79
|
+
<Stack direction="vertical" gap={4} hAlign="stretch">
|
|
80
|
+
<Button
|
|
81
|
+
label={actionLabel}
|
|
82
|
+
variant="destructive"
|
|
83
|
+
size="lg"
|
|
84
|
+
onClick={onAction}
|
|
85
|
+
xstyle={styles.action}
|
|
86
|
+
/>
|
|
87
|
+
<Button
|
|
88
|
+
label={cancelLabel}
|
|
89
|
+
variant="secondary"
|
|
90
|
+
size="lg"
|
|
91
|
+
onClick={() => onOpenChange(false)}
|
|
92
|
+
data-autofocus
|
|
93
|
+
xstyle={styles.action}
|
|
94
|
+
/>
|
|
95
|
+
</Stack>
|
|
96
|
+
</LayoutFooter>
|
|
97
|
+
}
|
|
98
|
+
/>
|
|
99
|
+
</Dialog>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
/**
|
|
3
|
+
* @file sheetDraft.ts
|
|
4
|
+
* @input A sheet's selection at open, its draft, and the last edit made
|
|
5
|
+
* @output SheetDraftChange, hasSameSelection, nameSheetDraftChange — the
|
|
6
|
+
* rules a touch sheet's Save and dismissal share
|
|
7
|
+
* @position Core-internal, shared by the Tokenizer sheet and the Lab sheets
|
|
8
|
+
* through `internal/touch-sheet`; not a public export
|
|
9
|
+
*
|
|
10
|
+
* A touch sheet edits a draft and reports it once, on Save, with one change:
|
|
11
|
+
* the last edit that still holds, else the nearest net add or remove. A
|
|
12
|
+
* dismissal asks before discarding only while the draft differs from the
|
|
13
|
+
* selection at open, as a set: a row unchecked and checked again is no change.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface SheetDraftItem {
|
|
17
|
+
id: string | number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** An edit of a sheet's draft, and the shape Save reports. */
|
|
21
|
+
export type SheetDraftChange<T> = {
|
|
22
|
+
item: T;
|
|
23
|
+
type: 'add' | 'create' | 'remove';
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Whether two selections hold the same items, in any order. */
|
|
27
|
+
export function hasSameSelection<T extends SheetDraftItem>(
|
|
28
|
+
a: ReadonlyArray<T>,
|
|
29
|
+
b: ReadonlyArray<T>,
|
|
30
|
+
): boolean {
|
|
31
|
+
if (a.length !== b.length) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const ids = new Set(a.map(item => item.id));
|
|
35
|
+
return b.every(item => ids.has(item.id));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The change Save reports for a draft that differs from the selection at
|
|
40
|
+
* open, or `null` when nothing differs. The last edit names it when it still
|
|
41
|
+
* holds: its item is in the draft and was not selected at open, or was
|
|
42
|
+
* selected at open and is gone. Otherwise, after a removal, a `remove` names
|
|
43
|
+
* an item that is gone; otherwise an `add` (`create` when the last edit
|
|
44
|
+
* created that item) names an item that is new; otherwise a `remove` names
|
|
45
|
+
* an item that is gone.
|
|
46
|
+
*/
|
|
47
|
+
export function nameSheetDraftChange<T extends SheetDraftItem>(
|
|
48
|
+
valueAtOpen: ReadonlyArray<T>,
|
|
49
|
+
draftValue: ReadonlyArray<T>,
|
|
50
|
+
lastDraftChange: SheetDraftChange<T> | null,
|
|
51
|
+
): SheetDraftChange<T> | null {
|
|
52
|
+
const wasSelected = (id: T['id']) => valueAtOpen.some(item => item.id === id);
|
|
53
|
+
const isSelected = (id: T['id']) => draftValue.some(item => item.id === id);
|
|
54
|
+
const added = draftValue.find(item => !wasSelected(item.id));
|
|
55
|
+
const removed = valueAtOpen.find(item => !isSelected(item.id));
|
|
56
|
+
const lastChangeIsNet =
|
|
57
|
+
lastDraftChange != null &&
|
|
58
|
+
(lastDraftChange.type === 'remove'
|
|
59
|
+
? wasSelected(lastDraftChange.item.id) &&
|
|
60
|
+
!isSelected(lastDraftChange.item.id)
|
|
61
|
+
: isSelected(lastDraftChange.item.id) &&
|
|
62
|
+
!wasSelected(lastDraftChange.item.id));
|
|
63
|
+
if (lastChangeIsNet) {
|
|
64
|
+
return lastDraftChange;
|
|
65
|
+
}
|
|
66
|
+
if (lastDraftChange?.type === 'remove' && removed != null) {
|
|
67
|
+
return {item: removed, type: 'remove'};
|
|
68
|
+
}
|
|
69
|
+
if (added != null) {
|
|
70
|
+
const type: SheetDraftChange<T>['type'] =
|
|
71
|
+
lastDraftChange?.item.id === added.id && lastDraftChange.type === 'create'
|
|
72
|
+
? 'create'
|
|
73
|
+
: 'add';
|
|
74
|
+
return {item: added, type};
|
|
75
|
+
}
|
|
76
|
+
if (removed != null) {
|
|
77
|
+
return {item: removed, type: 'remove'};
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
/**
|
|
3
|
+
* @file touch-sheet.ts
|
|
4
|
+
* @input The touch sheets' shared confirmation, search-drag dismissal and
|
|
5
|
+
* draft rules
|
|
6
|
+
* @output `@astryxdesign/core/internal/touch-sheet`: SheetConfirmation,
|
|
7
|
+
* useDismissSearchOnTouchDrag, hasSameSelection and nameSheetDraftChange,
|
|
8
|
+
* re-exported by name for Lab's sheets
|
|
9
|
+
* @position An internal entry reserved for Astryx's own packages:
|
|
10
|
+
* undocumented, outside the public API and its compatibility promise,
|
|
11
|
+
* versioned in lockstep with Core, and allowed only in Core and Lab by the
|
|
12
|
+
* `@astryx/no-core-internal-import` lint rule. Core components import the
|
|
13
|
+
* same modules by relative path. Named re-exports only.
|
|
14
|
+
*
|
|
15
|
+
* SYNC: When modified, update scripts/sync-exports.js.
|
|
16
|
+
*/
|
|
17
|
+
export {SheetConfirmation} from './SheetConfirmation';
|
|
18
|
+
export type {SheetConfirmationProps} from './SheetConfirmation';
|
|
19
|
+
export {useDismissSearchOnTouchDrag} from './useDismissSearchOnTouchDrag';
|
|
20
|
+
export {hasSameSelection, nameSheetDraftChange} from './sheetDraft';
|
|
21
|
+
export type {SheetDraftChange} from './sheetDraft';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useDismissSearchOnTouchDrag.ts
|
|
7
|
+
* @input Caller-owned search inputs, a focus target, and touch events from a
|
|
8
|
+
* separate results list
|
|
9
|
+
* @output useDismissSearchOnTouchDrag — touch handlers for the results list
|
|
10
|
+
* @position Core-internal, shared by the touch sheets; not a public export
|
|
11
|
+
*
|
|
12
|
+
* A direct vertical touch drag on the results moves focus off a focused
|
|
13
|
+
* search input. The on-screen keyboard then leaves and the rows come into
|
|
14
|
+
* view; the query stays. Focus goes to the caller's target (the sheet's
|
|
15
|
+
* panel), never to the page body behind an open modal. Scroll events are not
|
|
16
|
+
* observed: filtering can clamp scrollTop and fire scroll while someone is
|
|
17
|
+
* typing. Small moves, taps and multi-touch gestures leave focus alone.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {useRef, type TouchEvent} from 'react';
|
|
21
|
+
|
|
22
|
+
const DRAG_THRESHOLD_PX = 8;
|
|
23
|
+
|
|
24
|
+
type SearchInput = HTMLInputElement | null | undefined;
|
|
25
|
+
|
|
26
|
+
export function useDismissSearchOnTouchDrag(
|
|
27
|
+
getSearchInputs: () => ReadonlyArray<SearchInput>,
|
|
28
|
+
getFocusTarget?: () => HTMLElement | null | undefined,
|
|
29
|
+
) {
|
|
30
|
+
const startYRef = useRef<number | null>(null);
|
|
31
|
+
const reset = () => {
|
|
32
|
+
startYRef.current = null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
onTouchStart: (event: TouchEvent<HTMLElement>) => {
|
|
37
|
+
startYRef.current =
|
|
38
|
+
event.touches.length === 1 ? event.touches[0].clientY : null;
|
|
39
|
+
},
|
|
40
|
+
onTouchMove: (event: TouchEvent<HTMLElement>) => {
|
|
41
|
+
if (event.touches.length !== 1) {
|
|
42
|
+
reset();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const startY = startYRef.current;
|
|
46
|
+
if (
|
|
47
|
+
startY == null ||
|
|
48
|
+
Math.abs(event.touches[0].clientY - startY) < DRAG_THRESHOLD_PX
|
|
49
|
+
) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
reset();
|
|
53
|
+
const focusedSearch = getSearchInputs().find(
|
|
54
|
+
input => input != null && input === document.activeElement,
|
|
55
|
+
);
|
|
56
|
+
if (focusedSearch == null) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const target = getFocusTarget?.();
|
|
60
|
+
if (target != null) {
|
|
61
|
+
target.focus({preventScroll: true});
|
|
62
|
+
} else {
|
|
63
|
+
focusedSearch.blur();
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
onTouchEnd: reset,
|
|
67
|
+
onTouchCancel: reset,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -219,6 +219,13 @@ Bookmarks sheet.
|
|
|
219
219
|
| M-SG18 | iPad S1 (touch; pointer if available) | tap, hold, drag; hover | differences from iPhone | B |
|
|
220
220
|
| M-SG19 | Safari showing the segmented stories vs S1 | the segmented storyboards side by side | synced recordings (parity proof) | B |
|
|
221
221
|
|
|
222
|
+
Done (UIKit lab, iOS 26.2 simulator, Oct 23–24 2026; in the segmented control's
|
|
223
|
+
own table, `uikit/segmented.ts`, tagged `measured:`): M-SG02 (the selection
|
|
224
|
+
spring and the drag clamp; the tracking spring is the shared
|
|
225
|
+
`springs.fingerTracking`; the lift was measured too and is not in the table,
|
|
226
|
+
since a pressed thumb's lift is a theme's) and M-SG03 (the tap's frames,
|
|
227
|
+
fitted per frame).
|
|
228
|
+
|
|
222
229
|
## Sheets (UISheetPresentationController)
|
|
223
230
|
|
|
224
231
|
Probe app "SheetProbe" on an iPhone 16 Pro (iPad Pro 11" for M-SH11 / M-SH20 /
|
package/src/motion/README.md
CHANGED
|
@@ -22,6 +22,7 @@ roles through `resolveMotion`.
|
|
|
22
22
|
| `uikit/navigation.ts` | UINavigationController: push velocity, parallax, dimming, transition clock, interactive pop finish rule. |
|
|
23
23
|
| `uikit/progressBar.ts` | UIProgressView's fill: the whole-track duration and curve, its minimum length and empty alpha; the reduced-motion pulse (three roles). |
|
|
24
24
|
| `uikit/gestures.ts` | Gesture recognizer thresholds, the velocity filter and haptic stand-ins, as `{value, unit}` entries (sources in JSDoc). |
|
|
25
|
+
| `uikit/segmented.ts` | UISegmentedControl: the selection indicator's spring; under Reduce Motion the animator's cross-fade replaces it. |
|
|
25
26
|
| `uikit/switch.ts` | UISwitch on iOS 26.2: geometry, the lift, settle, travel and tracking springs, the flip distance, the track colour. |
|
|
26
27
|
| `roles.ts` | The internal motion roles: the springs, durations and curves `spec:AST-094` FR4 names, registered by the shared tables, and a role's CSS form read back. |
|
|
27
28
|
| `roleValue.ts` | A role's CSS form, written: a spring's duration and bounce, a duration in ms, a curve's `cubic-bezier()`. |
|
package/src/motion/roles.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* @input The motion tables (motion/uikit and each component's own table)
|
|
6
6
|
* @output registerMotionTable, motionRoleOf, motionRoleIn, listMotionRoles,
|
|
7
7
|
* motionRoleVariable, and the CSS form of a role's value
|
|
8
|
-
* (formatMotionRoleValue, parseMotionRoleValue)
|
|
8
|
+
* (formatMotionRoleValue, parseMotionRoleValue), and parseCssEasing for a
|
|
9
|
+
* CSS transition's easing
|
|
9
10
|
* @position Motion foundation, internal to core: read by the code that
|
|
10
11
|
* resolves a role when a motion starts. No public API writes a role: touch
|
|
11
12
|
* motion stays internal until the public motion-token set is decided.
|
|
@@ -260,3 +261,21 @@ export function parseMotionRoleValue(
|
|
|
260
261
|
? points
|
|
261
262
|
: null;
|
|
262
263
|
}
|
|
264
|
+
|
|
265
|
+
/** The CSS easing keywords, as the control points CSS gives them. */
|
|
266
|
+
const EASING_KEYWORDS: Readonly<Record<string, MotionCurvePoints>> = {
|
|
267
|
+
linear: [0, 0, 1, 1],
|
|
268
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
269
|
+
'ease-in': [0.42, 0, 1, 1],
|
|
270
|
+
'ease-out': [0, 0, 0.58, 1],
|
|
271
|
+
'ease-in-out': [0.42, 0, 0.58, 1],
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Reads a CSS easing, a keyword or a `cubic-bezier()`, as a curve role's value
|
|
276
|
+
* is read. Returns null for any other easing, such as `steps()`.
|
|
277
|
+
*/
|
|
278
|
+
export function parseCssEasing(text: string): MotionCurvePoints | null {
|
|
279
|
+
const value = text.trim();
|
|
280
|
+
return EASING_KEYWORDS[value] ?? parseMotionRoleValue('curve', value);
|
|
281
|
+
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @file index.ts
|
|
5
5
|
* @input The domain files of the UIKit constants table
|
|
6
6
|
* @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`,
|
|
7
|
-
* …), `scroll`, `timing`, `navigation`, `progressBar`,
|
|
8
|
-
* switch (`switchControl`) domains, and the gesture
|
|
9
|
-
* `UIKIT_*` group per recognizer, their union
|
|
10
|
-
* haptic tables)
|
|
7
|
+
* …), `scroll`, `timing`, `navigation`, `progressBar`, `segmentedControl`,
|
|
8
|
+
* the `slider` and switch (`switchControl`) domains, and the gesture
|
|
9
|
+
* thresholds (one `UIKIT_*` group per recognizer, their union
|
|
10
|
+
* `UIKIT_GESTURES`, and the haptic tables)
|
|
11
11
|
* @position Entry of the UIKit constants table (motion/uikit). Internal to
|
|
12
12
|
* core: not exported from the package entry point
|
|
13
13
|
*
|
|
@@ -42,6 +42,7 @@ export {
|
|
|
42
42
|
export {navigation, registerNavigationTable} from './navigation';
|
|
43
43
|
export {progressBar, registerProgressBarTable} from './progressBar';
|
|
44
44
|
export {registerScrollTable, scroll} from './scroll';
|
|
45
|
+
export {segmentedControl} from './segmented';
|
|
45
46
|
export {slider} from './slider';
|
|
46
47
|
export {
|
|
47
48
|
ACTION_SHEET_DISMISS_SPRING,
|
|
@@ -64,7 +65,6 @@ export {
|
|
|
64
65
|
NAVIGATION_POP_SPRING,
|
|
65
66
|
NAVIGATION_PUSH_SPRING,
|
|
66
67
|
QUICK_CHANGE_SPRING,
|
|
67
|
-
SEGMENTED_THUMB_SPRING,
|
|
68
68
|
SETTLE_SPRING,
|
|
69
69
|
SHEET_DETENT_SPRING,
|
|
70
70
|
SHEET_DISMISS_SPRING,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file segmented.ts
|
|
5
|
+
* @input Nothing; data only
|
|
6
|
+
* @output Exports `segmentedControl`, UISegmentedControl's selection slide
|
|
7
|
+
* spring and its drag overshoot; and `registerSegmentedTable`, which
|
|
8
|
+
* registers the spring as an internal motion role
|
|
9
|
+
* @position The segmented-control domain of the UIKit constants table
|
|
10
|
+
* (motion/uikit); read by SegmentedControl's touch presentation
|
|
11
|
+
*
|
|
12
|
+
* SYNC: When modified, update:
|
|
13
|
+
* - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
|
|
14
|
+
* - /packages/core/src/motion/README.md (domain list)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {registerMotionTable} from '../roles';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* UIKit's own UISegmentedControl, measured with three text segments in a
|
|
21
|
+
* 300 × 31 pt control. A tap and a programmatic change follow the same
|
|
22
|
+
* spring. Every leaf carries a `@source`, and uikit.test.ts checks it.
|
|
23
|
+
*
|
|
24
|
+
* Some UIKit motion is left out on purpose. UIKit cross-fades the two titles
|
|
25
|
+
* that change, over 0.2 s. Astryx switches them at once, because a
|
|
26
|
+
* cross-fade needs a second copy of each title. Under Reduce Motion, UIKit's
|
|
27
|
+
* lens fades in at the new segment and scales from 0.88. Astryx allows no
|
|
28
|
+
* scaling there, so the animator's Reduce Motion cross-fade replaces it.
|
|
29
|
+
* UIKit's lens also lifts and stretches under a finger. A theme role owns
|
|
30
|
+
* the lift, and the stretch is not modeled. The lens chases a dragging
|
|
31
|
+
* finger on the shared `springs.fingerTracking` role, which matches the
|
|
32
|
+
* values UIKit reports for this control.
|
|
33
|
+
*/
|
|
34
|
+
export const segmentedControl = {
|
|
35
|
+
/**
|
|
36
|
+
* The selection indicator's slide from the old segment to the new one,
|
|
37
|
+
* after a tap or a programmatic change. Its response is 0.40 s and its
|
|
38
|
+
* damping ratio 0.85. That gives about 1.25 pt of overshoot on a 200 pt
|
|
39
|
+
* move, settled by about 0.6 s. The width follows the same spring when the
|
|
40
|
+
* segments differ in width.
|
|
41
|
+
* @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
|
|
42
|
+
* iPhone 17 Pro, UIViewSpringAnimationBehavior read on the lens position
|
|
43
|
+
* for a tap and a programmatic set (dampingRatio 0.85, response 0.40,
|
|
44
|
+
* tracking off), Oct 23 2026
|
|
45
|
+
* @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
|
|
46
|
+
* iPhone 17 Pro, per-frame log of the lens's presentation position and
|
|
47
|
+
* velocity (method C) for a tap, a programmatic set and an animated set,
|
|
48
|
+
* least-squares spring fit (response 0.395 / 0.405 / 0.400 s, damping
|
|
49
|
+
* ratio 0.855 / 0.845 / 0.850, rms at most 0.65 pt over 200 pt),
|
|
50
|
+
* Oct 23 2026
|
|
51
|
+
*/
|
|
52
|
+
selectionSpring: {response: 0.4, dampingRatio: 0.85},
|
|
53
|
+
/**
|
|
54
|
+
* How far past the first or last segment's center the lens follows a
|
|
55
|
+
* dragging finger, in pt. Beyond that, the lens stays put while the finger
|
|
56
|
+
* goes on.
|
|
57
|
+
* @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
|
|
58
|
+
* iPhone 17 Pro, a drag 60 pt past the last segment: the lens stops about
|
|
59
|
+
* 11 pt past its center (312 vs 301), Oct 23 2026
|
|
60
|
+
*/
|
|
61
|
+
dragOvershoot: 11,
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Registers this table with the motion registry (motion/roles.ts). The
|
|
66
|
+
* slide's spring becomes an internal motion role. The drag overshoot is not
|
|
67
|
+
* motion, so it stays an internal constant. Nothing registers on import, and
|
|
68
|
+
* a second call does nothing.
|
|
69
|
+
*/
|
|
70
|
+
export function registerSegmentedTable(): void {
|
|
71
|
+
registerMotionTable('segmented', segmentedControl, {
|
|
72
|
+
roles: ['selectionSpring'],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
* that will confirm or replace them (MEASUREMENTS.md lists them all).
|
|
16
16
|
* uikit.test.ts enforces both.
|
|
17
17
|
*
|
|
18
|
-
* Most springs are measured on iOS 26.2 or documented by Apple;
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* Most springs are measured on iOS 26.2 or documented by Apple; one is still
|
|
19
|
+
* a GUESS (`banner`). `switchThumbMove` is the switch's measured travel,
|
|
20
|
+
* which uikit/switch.ts reads. A guess starts from the nearest sourced
|
|
21
|
+
* spring and names it. A component built on the table gets the measured
|
|
22
|
+
* value the moment it lands, without changing.
|
|
23
23
|
*
|
|
24
24
|
* Press highlight in/out are fades, not springs: see timing.ts.
|
|
25
25
|
*
|
|
@@ -400,21 +400,6 @@ export const SWITCH_THUMB_MOVE_SPRING = {
|
|
|
400
400
|
dampingRatio: 1,
|
|
401
401
|
} as const;
|
|
402
402
|
|
|
403
|
-
/**
|
|
404
|
-
* UISegmentedControl selection thumb sliding to a new segment.
|
|
405
|
-
* @source GUESS — WebKit's UIKit-matched switch thumb spring
|
|
406
|
-
* (RenderThemeCocoa.mm `thumbMoveSpring`: response 0.30 s, ratio 0.75),
|
|
407
|
-
* kept only until UISegmentedControl's measured spring replaces it;
|
|
408
|
-
* UISwitch's measured travel (`SWITCH_THUMB_MOVE_SPRING`)
|
|
409
|
-
* belongs to another control
|
|
410
|
-
* @measure M-SG02
|
|
411
|
-
* @measure M-SG03
|
|
412
|
-
*/
|
|
413
|
-
export const SEGMENTED_THUMB_SPRING = {
|
|
414
|
-
response: 0.3,
|
|
415
|
-
dampingRatio: 0.75,
|
|
416
|
-
} as const;
|
|
417
|
-
|
|
418
403
|
// ---------------------------------------------------------------------
|
|
419
404
|
// System
|
|
420
405
|
// ---------------------------------------------------------------------
|
|
@@ -504,7 +489,6 @@ export const springs = {
|
|
|
504
489
|
contextMenuMorph: CONTEXT_MENU_MORPH_SPRING,
|
|
505
490
|
menuReducedMotion: MENU_REDUCED_MOTION_SPRING,
|
|
506
491
|
switchThumbMove: SWITCH_THUMB_MOVE_SPRING,
|
|
507
|
-
segmentedThumb: SEGMENTED_THUMB_SPRING,
|
|
508
492
|
keyboard: KEYBOARD_SPRING,
|
|
509
493
|
banner: BANNER_SPRING,
|
|
510
494
|
sliderRelease: SLIDER_RELEASE_SPRING,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file privateAttributes/segmentTitle.stylex.ts
|
|
5
|
+
* @input Nothing
|
|
6
|
+
* @output segmentTitleProps, which marks a SegmentedControl segment's title,
|
|
7
|
+
* and titleOfSegment, which finds that title in a segment
|
|
8
|
+
* @position Owner module of the private segment-title attribute: the only
|
|
9
|
+
* file that writes its name. No package entry exports it.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const SEGMENT_TITLE = 'data-astryx-segment-title';
|
|
13
|
+
|
|
14
|
+
/** Spread on a segment's title: marks it for the touch layer. */
|
|
15
|
+
export const segmentTitleProps: Readonly<Record<string, string>> = {
|
|
16
|
+
[SEGMENT_TITLE]: '',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** The title inside `segment`, or null when the label is hidden. */
|
|
20
|
+
export function titleOfSegment(segment: Element): HTMLElement | null {
|
|
21
|
+
return segment.querySelector<HTMLElement>(`[${SEGMENT_TITLE}]`);
|
|
22
|
+
}
|