@astryxdesign/core 0.4.4-canary.39d8a5a → 0.4.4-canary.46b0532

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.
Files changed (140) hide show
  1. package/dist/BottomSheet/BottomSheet.d.ts +11 -2
  2. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  3. package/dist/BottomSheet/BottomSheet.js +4 -0
  4. package/dist/BottomSheet/BottomSheetPanel.d.ts +5 -2
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetPanel.js +56 -13
  7. package/dist/BottomSheet/index.d.ts +1 -1
  8. package/dist/BottomSheet/index.d.ts.map +1 -1
  9. package/dist/BottomSheet/snapOffsets.d.ts +57 -20
  10. package/dist/BottomSheet/snapOffsets.d.ts.map +1 -1
  11. package/dist/BottomSheet/snapOffsets.js +106 -30
  12. package/dist/BottomSheet/useSheetGestures.d.ts +13 -8
  13. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  14. package/dist/BottomSheet/useSheetGestures.js +111 -62
  15. package/dist/Calendar/Calendar.d.ts +17 -0
  16. package/dist/Calendar/Calendar.d.ts.map +1 -1
  17. package/dist/Calendar/Calendar.js +34 -3
  18. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts +14 -0
  19. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts.map +1 -0
  20. package/dist/Calendar/getStandaloneShortWeekdayNames.js +26 -0
  21. package/dist/Calendar/hooks/useCalendarConstraints.d.ts +22 -1
  22. package/dist/Calendar/hooks/useCalendarConstraints.d.ts.map +1 -1
  23. package/dist/Calendar/hooks/useCalendarConstraints.js +26 -4
  24. package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
  25. package/dist/Calendar/hooks/useCalendarDays.js +14 -10
  26. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts +47 -0
  27. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts.map +1 -0
  28. package/dist/Calendar/standaloneShortWeekdayNames.generated.js +40 -0
  29. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  30. package/dist/CheckboxInput/CheckboxInput.js +9 -0
  31. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  32. package/dist/ComplexSelector/ComplexSelector.js +6 -1
  33. package/dist/DateInput/DateInput.d.ts.map +1 -1
  34. package/dist/DateInput/DateInput.js +6 -1
  35. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  36. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  37. package/dist/DateRangeInput/DateRangeInput.js +38 -3
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +7 -2
  40. package/dist/Field/FieldLabel.d.ts +2 -1
  41. package/dist/Field/FieldLabel.d.ts.map +1 -1
  42. package/dist/Field/FieldLabel.js +21 -3
  43. package/dist/FormLayout/FormLayout.d.ts +24 -2
  44. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  45. package/dist/FormLayout/FormLayout.js +7 -2
  46. package/dist/FormLayout/FormLayoutContext.d.ts +15 -2
  47. package/dist/FormLayout/FormLayoutContext.d.ts.map +1 -1
  48. package/dist/FormLayout/FormLayoutContext.js +19 -4
  49. package/dist/FormLayout/index.d.ts +1 -1
  50. package/dist/FormLayout/index.d.ts.map +1 -1
  51. package/dist/Markdown/Markdown.d.ts +8 -0
  52. package/dist/Markdown/Markdown.d.ts.map +1 -1
  53. package/dist/Markdown/Markdown.js +31 -3
  54. package/dist/Markdown/parser.d.ts +14 -2
  55. package/dist/Markdown/parser.d.ts.map +1 -1
  56. package/dist/Markdown/parser.js +50 -2
  57. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  58. package/dist/MultiSelector/MultiSelector.js +6 -1
  59. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  60. package/dist/NumberInput/NumberInput.js +6 -1
  61. package/dist/Outline/parseOutlineFromMarkdown.d.ts +2 -0
  62. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  63. package/dist/Outline/parseOutlineFromMarkdown.js +5 -31
  64. package/dist/RadioList/RadioList.d.ts.map +1 -1
  65. package/dist/RadioList/RadioList.js +12 -1
  66. package/dist/Selector/Selector.d.ts.map +1 -1
  67. package/dist/Selector/Selector.js +6 -1
  68. package/dist/StatusDot/StatusDot.d.ts +43 -3
  69. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  70. package/dist/StatusDot/StatusDot.js +42 -5
  71. package/dist/Switch/Switch.d.ts.map +1 -1
  72. package/dist/Switch/Switch.js +9 -0
  73. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  74. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  75. package/dist/TextArea/TextArea.d.ts.map +1 -1
  76. package/dist/TextArea/TextArea.js +6 -1
  77. package/dist/TextInput/TextInput.d.ts.map +1 -1
  78. package/dist/TextInput/TextInput.js +6 -1
  79. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  80. package/dist/TimeInput/TimeInput.js +6 -1
  81. package/dist/hooks/useResolvedRequired.d.ts +19 -0
  82. package/dist/hooks/useResolvedRequired.d.ts.map +1 -0
  83. package/dist/hooks/useResolvedRequired.js +40 -0
  84. package/dist/utils/plainDate.d.ts +6 -0
  85. package/dist/utils/plainDate.d.ts.map +1 -1
  86. package/dist/utils/plainDate.js +12 -0
  87. package/locales/en.json +4 -0
  88. package/locales/pseudo.json +3 -0
  89. package/package.json +2 -2
  90. package/src/BottomSheet/BottomSheet.doc.mjs +36 -3
  91. package/src/BottomSheet/BottomSheet.test.tsx +153 -2
  92. package/src/BottomSheet/BottomSheet.tsx +16 -2
  93. package/src/BottomSheet/BottomSheetPanel.tsx +67 -13
  94. package/src/BottomSheet/index.ts +5 -1
  95. package/src/BottomSheet/snapOffsets.test.ts +114 -47
  96. package/src/BottomSheet/snapOffsets.ts +119 -33
  97. package/src/BottomSheet/useSheetGestures.test.ts +68 -15
  98. package/src/BottomSheet/useSheetGestures.ts +142 -98
  99. package/src/Calendar/Calendar.doc.mjs +16 -0
  100. package/src/Calendar/Calendar.test.tsx +137 -0
  101. package/src/Calendar/Calendar.tsx +65 -2
  102. package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +54 -0
  103. package/src/Calendar/getStandaloneShortWeekdayNames.ts +38 -0
  104. package/src/Calendar/hooks/useCalendarConstraints.ts +54 -3
  105. package/src/Calendar/hooks/useCalendarDays.ts +13 -10
  106. package/src/Calendar/standaloneShortWeekdayNames.generated.ts +50 -0
  107. package/src/CheckboxInput/CheckboxInput.tsx +6 -0
  108. package/src/ComplexSelector/ComplexSelector.tsx +3 -1
  109. package/src/DateInput/DateInput.tsx +3 -1
  110. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  111. package/src/DateRangeInput/DateRangeInput.test.tsx +81 -1
  112. package/src/DateRangeInput/DateRangeInput.tsx +72 -1
  113. package/src/DateTimeInput/DateTimeInput.tsx +4 -2
  114. package/src/Field/FieldLabel.tsx +20 -4
  115. package/src/FormLayout/FormLayout.doc.mjs +13 -0
  116. package/src/FormLayout/FormLayout.test.tsx +181 -4
  117. package/src/FormLayout/FormLayout.tsx +33 -2
  118. package/src/FormLayout/FormLayoutContext.ts +22 -7
  119. package/src/FormLayout/index.ts +1 -1
  120. package/src/Markdown/Markdown.doc.mjs +3 -0
  121. package/src/Markdown/Markdown.test.tsx +78 -0
  122. package/src/Markdown/Markdown.tsx +41 -1
  123. package/src/Markdown/parser.ts +60 -2
  124. package/src/MultiSelector/MultiSelector.tsx +3 -1
  125. package/src/NumberInput/NumberInput.tsx +3 -1
  126. package/src/Outline/Outline.doc.mjs +2 -0
  127. package/src/Outline/parseOutlineFromMarkdown.ts +10 -40
  128. package/src/RadioList/RadioList.tsx +9 -1
  129. package/src/Selector/Selector.tsx +3 -1
  130. package/src/StatusDot/StatusDot.doc.mjs +13 -0
  131. package/src/StatusDot/StatusDot.test.tsx +115 -8
  132. package/src/StatusDot/StatusDot.tsx +74 -9
  133. package/src/Switch/Switch.tsx +6 -0
  134. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +20 -2
  135. package/src/TextArea/TextArea.tsx +3 -1
  136. package/src/TextInput/TextInput.tsx +3 -1
  137. package/src/TimeInput/TimeInput.tsx +3 -1
  138. package/src/hooks/useResolvedRequired.ts +42 -0
  139. package/src/utils/plainDate.test.ts +50 -0
  140. package/src/utils/plainDate.ts +12 -0
@@ -22,8 +22,8 @@
22
22
  import { type ReactNode } from 'react';
23
23
  import type { BaseProps } from '../BaseProps';
24
24
  import type { DialogPurpose } from '../Dialog';
25
- export type { BottomSheetHeight } from './BottomSheetPanel';
26
- import type { BottomSheetHeight } from './BottomSheetPanel';
25
+ export type { BottomSheetHeight, BottomSheetSnapPoint } from './BottomSheetPanel';
26
+ import type { BottomSheetHeight, BottomSheetSnapPoint } from './BottomSheetPanel';
27
27
  interface BottomSheetSharedProps extends BaseProps<HTMLDivElement> {
28
28
  /** Ref forwarded to the visual sheet panel <div>. */
29
29
  ref?: React.Ref<HTMLDivElement>;
@@ -33,6 +33,15 @@ interface BottomSheetSharedProps extends BaseProps<HTMLDivElement> {
33
33
  children: ReactNode;
34
34
  /** Height budget or custom CSS length. Only fully expanded Tall is keyboard-aware. @default 'capped' */
35
35
  height?: BottomSheetHeight | number | string;
36
+ /**
37
+ * Extra heights the sheet can rest at when dragged; its own height is always
38
+ * the tallest stop, and omitting this gives a sheet that only opens and
39
+ * closes. Each stop is the sheet's visible height: a number is a viewport
40
+ * fraction (`0.5` is half the screen), `'50%'` the same in CSS, `'320px'` an
41
+ * absolute length. A stop of a quarter of the sheet or less is a peek — it
42
+ * slides away instead of reflowing, and thins the scrim.
43
+ */
44
+ snapPoints?: ReadonlyArray<BottomSheetSnapPoint>;
36
45
  /**
37
46
  * Configures implicit dismissal behavior, matching Dialog.
38
47
  * - required: Blocks swipe, scrim click, and Escape
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAc7C,YAAY,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AA2D1D,UAAU,sBAAuB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAChE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IAEd,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,CAAC;IAEpB,wGAAwG;IACxG,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAE7C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,UAAU,0BAA2B,SAAQ,sBAAsB;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,wBAAyB,SAAQ,sBAAsB;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAC1B,0BAA0B,GAAG,wBAAwB,CAAC;AAwWxD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA6BlD;yBA7Be,WAAW"}
1
+ {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAc7C,YAAY,EAAC,iBAAiB,EAAE,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAC,iBAAiB,EAAE,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AA2DhF,UAAU,sBAAuB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAChE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IAEd,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,CAAC;IAEpB,wGAAwG;IACxG,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAE7C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAEjD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,UAAU,0BAA2B,SAAQ,sBAAsB;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,wBAAyB,SAAQ,sBAAsB;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAC1B,0BAA0B,GAAG,wBAAwB,CAAC;AA4WxD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA6BlD;yBA7Be,WAAW"}
@@ -89,6 +89,7 @@ function StandaloneBottomSheet({
89
89
  label,
90
90
  children,
91
91
  height = 'capped',
92
+ snapPoints,
92
93
  hasScrim = true,
93
94
  purpose = 'info',
94
95
  xstyle,
@@ -224,6 +225,7 @@ function StandaloneBottomSheet({
224
225
  ref: ref,
225
226
  state: panelState,
226
227
  height: height,
228
+ snapPoints: snapPoints,
227
229
  isSwipeDismissAllowed: purpose === 'info',
228
230
  isPageScrollLocked: shouldPresent && hasScrim,
229
231
  xstyle: xstyle,
@@ -243,6 +245,7 @@ function SwitcherBottomSheetItem({
243
245
  label,
244
246
  children,
245
247
  height = 'capped',
248
+ snapPoints,
246
249
  purpose = 'info',
247
250
  xstyle,
248
251
  ...props
@@ -353,6 +356,7 @@ function SwitcherBottomSheetItem({
353
356
  ref: ref,
354
357
  state: panelState,
355
358
  height: height,
359
+ snapPoints: snapPoints,
356
360
  isSwipeDismissAllowed: purpose === 'info',
357
361
  isPageScrollLocked: hasScrim,
358
362
  xstyle: xstyle,
@@ -12,17 +12,20 @@
12
12
  * SYNC: When modified, update these files to stay in sync:
13
13
  * - /packages/core/src/BottomSheet/BottomSheet.tsx
14
14
  * - /packages/core/src/BottomSheet/BottomSheetPanel.test.tsx
15
+ * - /packages/core/src/BottomSheet/snapOffsets.ts
15
16
  * - /packages/core/src/BottomSheet/useMobileKeyboard.ts
16
17
  * - /packages/core/src/BottomSheet/useSheetGestures.ts
17
18
  */
18
19
  import { type ReactNode } from 'react';
19
20
  import type { BaseProps } from '../BaseProps';
21
+ import { type BottomSheetSnapPoint } from './snapOffsets';
20
22
  declare const HEIGHT_BUDGETS: {
21
23
  readonly hug: "92dvh";
22
24
  readonly capped: "62dvh";
23
25
  readonly tall: "92dvh";
24
26
  };
25
27
  export type BottomSheetHeight = keyof typeof HEIGHT_BUDGETS;
28
+ export type { BottomSheetSnapPoint };
26
29
  export type BottomSheetPanelMotion = 'entering' | 'aligning' | 'fading' | 'exiting';
27
30
  export type BottomSheetPanelState = {
28
31
  kind: 'hidden';
@@ -42,6 +45,7 @@ interface BottomSheetPanelProps extends BaseProps<HTMLDivElement> {
42
45
  state: BottomSheetPanelState;
43
46
  height: BottomSheetHeight | number | string;
44
47
  children: ReactNode;
48
+ snapPoints?: ReadonlyArray<BottomSheetSnapPoint>;
45
49
  isSwipeDismissAllowed?: boolean;
46
50
  /** Whether the host has locked page scrolling (a modal, scrim-backed sheet). */
47
51
  isPageScrollLocked?: boolean;
@@ -52,9 +56,8 @@ interface BottomSheetPanelProps extends BaseProps<HTMLDivElement> {
52
56
  onMotionComplete?: (motion: BottomSheetPanelMotion) => void;
53
57
  }
54
58
  /** Internal visual and gesture surface shared by every BottomSheet host. */
55
- export declare function BottomSheetPanel({ ref, state, height, children, className, style, tabIndex, xstyle, isSwipeDismissAllowed, isPageScrollLocked, onDismiss, onScrimOpacity, onElementChange, onMotionStart, onMotionComplete, ...props }: BottomSheetPanelProps): import("react").JSX.Element;
59
+ export declare function BottomSheetPanel({ ref, state, height, children, snapPoints, className, style, tabIndex, xstyle, isSwipeDismissAllowed, isPageScrollLocked, onDismiss, onScrimOpacity, onElementChange, onMotionStart, onMotionComplete, ...props }: BottomSheetPanelProps): import("react").JSX.Element;
56
60
  export declare namespace BottomSheetPanel {
57
61
  var displayName: string;
58
62
  }
59
- export {};
60
63
  //# sourceMappingURL=BottomSheetPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetPanel.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetPanel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,QAAA,MAAM,cAAc;;;;CAIV,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,cAAc,CAAC;AAqG5D,MAAM,MAAM,sBAAsB,GAChC,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEjD,MAAM,MAAM,qBAAqB,GAC7B;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC,GAChB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC,GACjC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;CACzB,GACD;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,CAAC;AAEtB,UAAU,qBAAsB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC/D,+CAA+C;IAC/C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAC5C,QAAQ,EAAE,SAAS,CAAC;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACzD,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC7D;AA+GD,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EACT,KAAK,EACL,QAAQ,EACR,MAAM,EACN,qBAA4B,EAC5B,kBAA0B,EAC1B,SAAS,EACT,cAAc,EACd,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,GAAG,KAAK,EACT,EAAE,qBAAqB,+BA0OvB;yBA3Pe,gBAAgB"}
1
+ {"version":3,"file":"BottomSheetPanel.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetPanel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAY5C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAIvB,QAAA,MAAM,cAAc;;;;CAIV,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,cAAc,CAAC;AAC5D,YAAY,EAAC,oBAAoB,EAAC,CAAC;AAoHnC,MAAM,MAAM,sBAAsB,GAChC,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEjD,MAAM,MAAM,qBAAqB,GAC7B;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC,GAChB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC,GACjC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;CACzB,GACD;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,CAAC;AAEtB,UAAU,qBAAsB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC/D,+CAA+C;IAC/C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAC5C,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACjD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACzD,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC7D;AA+GD,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,KAAK,EACL,QAAQ,EACR,MAAM,EACN,qBAA4B,EAC5B,kBAA0B,EAC1B,SAAS,EACT,cAAc,EACd,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,GAAG,KAAK,EACT,EAAE,qBAAqB,+BAwQvB;yBA1Re,gBAAgB"}
@@ -16,18 +16,20 @@
16
16
  * SYNC: When modified, update these files to stay in sync:
17
17
  * - /packages/core/src/BottomSheet/BottomSheet.tsx
18
18
  * - /packages/core/src/BottomSheet/BottomSheetPanel.test.tsx
19
+ * - /packages/core/src/BottomSheet/snapOffsets.ts
19
20
  * - /packages/core/src/BottomSheet/useMobileKeyboard.ts
20
21
  * - /packages/core/src/BottomSheet/useSheetGestures.ts
21
22
  */
22
- import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef } from 'react';
23
+ import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef } from 'react';
23
24
  import * as stylex from '@stylexjs/stylex';
25
+ import { useDevWarning } from "../hooks/index.js";
24
26
  import "../theme/tokens.stylex.js";
25
27
  import { colorVars, durationVars, easeVars, radiusVars, shadowVars, sizeVars, spacingVars } from "../theme/tokens.stylex.js";
26
28
  import { mergeProps, themeProps } from "../utils/index.js";
29
+ import { isValidSnapPoint, resolveSnapPoints } from "./snapOffsets.js";
27
30
  import { useMobileKeyboard } from "./useMobileKeyboard.js";
28
31
  import { useSheetGestures } from "./useSheetGestures.js";
29
32
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
30
- const SNAP_FRACTIONS = [0.14, 0.5, 0.92];
31
33
  const HEIGHT_BUDGETS = {
32
34
  hug: '92dvh',
33
35
  capped: '62dvh',
@@ -37,17 +39,31 @@ const HEIGHT_BUDGETS = {
37
39
  const OVERSCROLL_PADDING = 48;
38
40
  const MOBILE_KEYBOARD_BOTTOM_CLEARANCE = 48;
39
41
  const TRANSITION_BACKSTOP_BUFFER_MS = 50;
40
- function defaultSnapHeights() {
41
- if (typeof window === 'undefined') {
42
- return [];
42
+
43
+ // Measure the same viewport the height budgets are written against. Those are
44
+ // `dvh`, which the virtual keyboard does not shrink, so reading
45
+ // `visualViewport` here would make the two disagree by exactly the keyboard's
46
+ // height: every detent would move while the sheet it measures did not. A
47
+ // keyboard is `useMobileKeyboard`'s business — it holds the sheet still and
48
+ // scrolls the body — and it does not redefine the sheet's detents.
49
+ function layoutViewportHeight() {
50
+ return typeof window === 'undefined' ? 0 : window.innerHeight;
51
+ }
52
+
53
+ /**
54
+ * A stable identity for a set of snap points. Type-tagged, so the fraction
55
+ * `0.5` and the (invalid) string `'0.5'` cannot collide on one key.
56
+ *
57
+ * Recomputed every render — the panel re-renders on every frame of a drag — so
58
+ * it stays a single pass over a handful of values, and everything derived from
59
+ * the points hangs off it instead of being rebuilt per frame.
60
+ */
61
+ function snapPointsKeyFor(points) {
62
+ let key = '';
63
+ for (const point of points ?? []) {
64
+ key += `${typeof point}:${point}|`;
43
65
  }
44
- // Measure the same viewport the height budgets are written against. Those
45
- // are `dvh`, which the virtual keyboard does not shrink, so reading
46
- // `visualViewport` here made the two disagree by exactly the keyboard's
47
- // height: every detent moved while the sheet it was measuring did not. A
48
- // keyboard is `useMobileKeyboard`'s business — it holds the sheet still and
49
- // scrolls the body — and it does not redefine the sheet's detents.
50
- return SNAP_FRACTIONS.map(fraction => fraction * window.innerHeight);
66
+ return key;
51
67
  }
52
68
  const styles = {
53
69
  sheet: {
@@ -190,6 +206,7 @@ export function BottomSheetPanel({
190
206
  state,
191
207
  height,
192
208
  children,
209
+ snapPoints,
193
210
  className,
194
211
  style,
195
212
  tabIndex,
@@ -230,6 +247,32 @@ export function BottomSheetPanel({
230
247
  const isClosing = state.kind === 'exiting';
231
248
  const isFading = isRetained && state.motion === 'fading';
232
249
  const alignmentOffset = isRetained ? state.alignmentOffset : 0;
250
+
251
+ // Everything derived from the snap points keys off their identity, not the
252
+ // array's: the resolver's identity is the hook's signal that the stops
253
+ // changed, so it must not churn on a re-render the drag caused. The memo
254
+ // reads the points through a ref, so the resolver resolves them at call time
255
+ // — against whatever the viewport is then, not what it was at memo time.
256
+ const snapPointsKey = snapPointsKeyFor(snapPoints);
257
+ const snapPointsRef = useRef(snapPoints);
258
+ snapPointsRef.current = snapPoints;
259
+ const {
260
+ snapHeights,
261
+ ignoredSnapPointsMessage
262
+ } = useMemo(() => {
263
+ if (snapPointsKey === '') {
264
+ return {
265
+ snapHeights: undefined,
266
+ ignoredSnapPointsMessage: ''
267
+ };
268
+ }
269
+ const ignored = (snapPointsRef.current ?? []).filter(point => !isValidSnapPoint(point));
270
+ return {
271
+ snapHeights: () => resolveSnapPoints(snapPointsRef.current ?? [], layoutViewportHeight()),
272
+ ignoredSnapPointsMessage: ignored.length === 0 ? '' : `snapPoints ignored ${JSON.stringify(ignored)}. A snap point is a viewport fraction above 0 and up to 1 (0.5 is half the screen), a px length ('320px'), or a percentage ('50%').`
273
+ };
274
+ }, [snapPointsKey]);
275
+ useDevWarning('BottomSheet', ignoredSnapPointsMessage, ignoredSnapPointsMessage !== '');
233
276
  const {
234
277
  contentProps,
235
278
  handleProps,
@@ -249,7 +292,7 @@ export function BottomSheetPanel({
249
292
  canDismiss: isSwipeDismissAllowed,
250
293
  offscreenBlockEndInset: OVERSCROLL_PADDING,
251
294
  onDismiss,
252
- snapHeights: defaultSnapHeights,
295
+ snapHeights,
253
296
  onScrimOpacity
254
297
  });
255
298
  const setElement = useCallback(element => {
@@ -5,7 +5,7 @@
5
5
  * @position Component entry point; re-exported by /packages/core/src/index.ts
6
6
  */
7
7
  export { BottomSheet } from './BottomSheet';
8
- export type { BottomSheetHeight, BottomSheetProps } from './BottomSheet';
8
+ export type { BottomSheetHeight, BottomSheetProps, BottomSheetSnapPoint, } from './BottomSheet';
9
9
  export { BottomSheetSwitcher } from './BottomSheetSwitcher';
10
10
  export type { BottomSheetSwitcherProps } from './BottomSheetSwitcher';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/index.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,YAAY,EAAC,iBAAiB,EAAE,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACvE,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/index.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAC"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @file snapOffsets.ts
3
3
  * @input Pure geometry helpers — no React, no DOM.
4
- * @output Converts snap points to translateY offsets for the sheet surface.
5
- * @position Internal to BottomSheet; consumed by useSheetGestures, tested by
6
- * snapOffsets.test.ts.
4
+ * @output Resolves snap points and converts them to translateY offsets.
5
+ * @position Internal to BottomSheet; consumed by useSheetGestures and
6
+ * BottomSheetPanel, tested by snapOffsets.test.ts.
7
7
  *
8
8
  * A detent is represented as an offset in px from the fully-open position
9
9
  * (0 = fully open, larger = more collapsed). The panel may render that offset
@@ -11,6 +11,20 @@
11
11
  * helpers turn snap points into the shared offset list; they are pure so the
12
12
  * geometry can be unit-tested without a DOM.
13
13
  */
14
+ /**
15
+ * A height the sheet can rest at, expressed as the sheet's VISIBLE height:
16
+ *
17
+ * - a number in `(0, 1]` — a fraction of the viewport (`0.5` is half the
18
+ * screen), so the stop tracks rotation and window resizes;
19
+ * - a `'<n>%'` string — the same thing spelled in CSS;
20
+ * - a `'<n>px'` string — an absolute height that does not scale.
21
+ *
22
+ * Anything else — a unitless string, `calc()`, `vh`, `rem`, a number outside
23
+ * `(0, 1]` — is ignored with a dev warning. Percentages resolve against the
24
+ * layout viewport (`window.innerHeight`), the same box the height budgets are
25
+ * written against, so the mobile keyboard never moves a stop.
26
+ */
27
+ export type BottomSheetSnapPoint = number | string;
14
28
  /**
15
29
  * Detents whose resting offsets land within this many px of each other are
16
30
  * treated as the same stop (the taller one wins). Stops the sheet from having
@@ -18,8 +32,24 @@
18
32
  * a hair away from a fractional snap point.
19
33
  */
20
34
  export declare const DETENT_DEDUP_PX = 48;
21
- /** Resolve snap points given as viewport fractions (0, 1] to px heights. */
22
- export declare function snapFractionsToHeights(fractions: ReadonlyArray<number>, viewportPx: number): number[];
35
+ /**
36
+ * Largest share of the fully open sheet the shortest stop may fill and still
37
+ * be a peek. Above it the stop is a working surface — it lays its content out
38
+ * and keeps a full scrim — so a two-stop sheet like `[0.5]` behaves like the
39
+ * half-height panel it asks for rather than like a glance.
40
+ */
41
+ export declare const PEEK_MAX_HEIGHT_RATIO = 0.25;
42
+ /**
43
+ * Whether a snap point can be resolved at all. The viewport only scales the
44
+ * result, so validity is independent of it.
45
+ */
46
+ export declare function isValidSnapPoint(point: BottomSheetSnapPoint): boolean;
47
+ /**
48
+ * Resolve snap points to candidate visible heights in px, dropping the ones
49
+ * this sheet cannot honor. Callers warn about those separately — this stays
50
+ * silent because it runs on every drag frame.
51
+ */
52
+ export declare function resolveSnapPoints(points: ReadonlyArray<BottomSheetSnapPoint>, viewportPx: number): number[];
23
53
  /**
24
54
  * Given the sheet's full height (px, as rendered fully open) and a set of
25
55
  * candidate detent *visible heights* (px), return the resting offsets from
@@ -42,26 +72,33 @@ export declare function nearestOffset(value: number, offsets: ReadonlyArray<numb
42
72
  */
43
73
  export declare const MIN_PEEK_SCRIM_OPACITY = 0.3;
44
74
  /**
45
- * Whether `offset` is the peek detent: the shortest stop, where the sheet is a
46
- * glance rather than a working surface. A sheet with no collapsed stop at all
47
- * has no peek — the same rule `scrimOpacityForOffset` thins the scrim by.
75
+ * The peek detent's offset, or `null` when this sheet has no peek.
48
76
  *
49
- * The peek is the one detent the sheet does NOT express as layout height: at a
50
- * sliver of viewport there is nothing useful to lay out, and reflowing the
51
- * content into it (then back out on the way up) is churn the user sees. It
52
- * keeps the full layout height and slides below the viewport instead.
77
+ * A peek is the shortest stop AND a sliver — at most `PEEK_MAX_HEIGHT_RATIO`
78
+ * of the fully open sheet. It is the one detent the sheet does NOT express as
79
+ * layout height: at a sliver there is nothing useful to lay out, and reflowing
80
+ * the content into it (then back out on the way up) is churn the user sees, so
81
+ * it keeps the full layout height and slides below the viewport instead. It is
82
+ * also the only stop that thins the scrim.
83
+ *
84
+ * A sheet whose shortest stop is a working height — `[0.5]`, say — has no
85
+ * peek: half a screen of content deserves to be laid out at half a screen, and
86
+ * a scrim that thinned there would read as a dismissed sheet.
53
87
  */
54
- export declare function isPeekOffset(offset: number, offsets: ReadonlyArray<number>, tolerancePx?: number): boolean;
88
+ export declare function peekOffsetFor(offsets: ReadonlyArray<number>, visibleSheetHeight: number): number | null;
55
89
  /**
56
90
  * Scrim opacity (1 = fully visible) for a drag/settle `offset`.
57
- * The scrim stays full while the sheet is at or above its second-shortest
58
- * detent, then fades as it collapses onto the shortest ("peek") detent — a
59
- * glance state that thins the backdrop to `MIN_PEEK_SCRIM_OPACITY` (not fully
60
- * gone, since the sheet is still modal) and holds there below it.
61
- * A single-detent sheet has no peek, so it instead fades all the way to 0
62
- * across the dismiss overshoot toward `dismissOffset` (the sheet is leaving).
91
+ *
92
+ * The scrim is full down to the shortest stop that is still a working surface,
93
+ * and only thins past it — onto a peek, where it holds at
94
+ * `MIN_PEEK_SCRIM_OPACITY` (a glance state, and the sheet is still modal), or
95
+ * out through the dismiss overshoot toward `dismissOffset`, where it clears
96
+ * completely because the sheet is leaving.
97
+ *
98
+ * So a sheet whose stops are all working heights keeps a full scrim at every
99
+ * one of them; only the sliver of a peek, or a sheet on its way out, dims less.
63
100
  */
64
- export declare function scrimOpacityForOffset(offset: number, offsets: ReadonlyArray<number>, dismissOffset: number): number;
101
+ export declare function scrimOpacityForOffset(offset: number, offsets: ReadonlyArray<number>, dismissOffset: number, peekOffset: number | null): number;
65
102
  /**
66
103
  * Settle target for a released drag. Restricts candidates to the drag
67
104
  * direction so a committed drag never snaps *back past* where it started
@@ -1 +1 @@
1
- {"version":3,"file":"snapOffsets.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/snapOffsets.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,4EAA4E;AAC5E,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAEV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,EACpC,OAAO,GAAE,MAAwB,GAChC,MAAM,EAAE,CAcV;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC7B,MAAM,CAKR;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,WAAW,GAAE,MAAY,GACxB,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,aAAa,EAAE,MAAM,GACpB,MAAM,CAeR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,MAAM,CAcR"}
1
+ {"version":3,"file":"snapOffsets.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/snapOffsets.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAkC1C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAErE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,CAAC,oBAAoB,CAAC,EAC3C,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CASV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,EACpC,OAAO,GAAE,MAAwB,GAChC,MAAM,EAAE,CAcV;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC7B,MAAM,CAKR;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CAgBR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,MAAM,CAcR"}
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * @file snapOffsets.ts
5
5
  * @input Pure geometry helpers — no React, no DOM.
6
- * @output Converts snap points to translateY offsets for the sheet surface.
7
- * @position Internal to BottomSheet; consumed by useSheetGestures, tested by
8
- * snapOffsets.test.ts.
6
+ * @output Resolves snap points and converts them to translateY offsets.
7
+ * @position Internal to BottomSheet; consumed by useSheetGestures and
8
+ * BottomSheetPanel, tested by snapOffsets.test.ts.
9
9
  *
10
10
  * A detent is represented as an offset in px from the fully-open position
11
11
  * (0 = fully open, larger = more collapsed). The panel may render that offset
@@ -14,6 +14,20 @@
14
14
  * geometry can be unit-tested without a DOM.
15
15
  */
16
16
 
17
+ /**
18
+ * A height the sheet can rest at, expressed as the sheet's VISIBLE height:
19
+ *
20
+ * - a number in `(0, 1]` — a fraction of the viewport (`0.5` is half the
21
+ * screen), so the stop tracks rotation and window resizes;
22
+ * - a `'<n>%'` string — the same thing spelled in CSS;
23
+ * - a `'<n>px'` string — an absolute height that does not scale.
24
+ *
25
+ * Anything else — a unitless string, `calc()`, `vh`, `rem`, a number outside
26
+ * `(0, 1]` — is ignored with a dev warning. Percentages resolve against the
27
+ * layout viewport (`window.innerHeight`), the same box the height budgets are
28
+ * written against, so the mobile keyboard never moves a stop.
29
+ */
30
+
17
31
  /**
18
32
  * Detents whose resting offsets land within this many px of each other are
19
33
  * treated as the same stop (the taller one wins). Stops the sheet from having
@@ -22,9 +36,63 @@
22
36
  */
23
37
  export const DETENT_DEDUP_PX = 48;
24
38
 
25
- /** Resolve snap points given as viewport fractions (0, 1] to px heights. */
26
- export function snapFractionsToHeights(fractions, viewportPx) {
27
- return fractions.filter(f => f > 0 && f <= 1).map(f => f * viewportPx);
39
+ /**
40
+ * Largest share of the fully open sheet the shortest stop may fill and still
41
+ * be a peek. Above it the stop is a working surface — it lays its content out
42
+ * and keeps a full scrim — so a two-stop sheet like `[0.5]` behaves like the
43
+ * half-height panel it asks for rather than like a glance.
44
+ */
45
+ export const PEEK_MAX_HEIGHT_RATIO = 0.25;
46
+
47
+ // A snap point string: a positive number with a `px` or `%` unit. Deliberately
48
+ // narrow — every accepted unit has to be resolvable by arithmetic on the
49
+ // viewport height, because this runs inside the drag loop. Units that need the
50
+ // DOM to resolve (`rem`, `em`, `calc()`) would cost a layout per frame.
51
+ const SNAP_POINT_PATTERN = /^(\d+(?:\.\d+)?|\.\d+)(px|%)$/i;
52
+
53
+ /**
54
+ * Resolve one snap point to a visible height in px, or `null` when it is not a
55
+ * snap point this sheet can honor.
56
+ */
57
+ function parseSnapPoint(point, viewportPx) {
58
+ if (typeof point === 'number') {
59
+ // A bare number is a viewport fraction. Anything above 1 is a px value in
60
+ // disguise; the caller warns rather than guessing which was meant.
61
+ return Number.isFinite(point) && point > 0 && point <= 1 ? point * viewportPx : null;
62
+ }
63
+ const match = SNAP_POINT_PATTERN.exec(point.trim());
64
+ if (match === null) {
65
+ return null;
66
+ }
67
+ const value = Number.parseFloat(match[1]);
68
+ if (!(value > 0)) {
69
+ return null;
70
+ }
71
+ return match[2].toLowerCase() === '%' ? value / 100 * viewportPx : value;
72
+ }
73
+
74
+ /**
75
+ * Whether a snap point can be resolved at all. The viewport only scales the
76
+ * result, so validity is independent of it.
77
+ */
78
+ export function isValidSnapPoint(point) {
79
+ return parseSnapPoint(point, 1) !== null;
80
+ }
81
+
82
+ /**
83
+ * Resolve snap points to candidate visible heights in px, dropping the ones
84
+ * this sheet cannot honor. Callers warn about those separately — this stays
85
+ * silent because it runs on every drag frame.
86
+ */
87
+ export function resolveSnapPoints(points, viewportPx) {
88
+ const heights = [];
89
+ for (const point of points) {
90
+ const height = parseSnapPoint(point, viewportPx);
91
+ if (height !== null) {
92
+ heights.push(height);
93
+ }
94
+ }
95
+ return heights;
28
96
  }
29
97
 
30
98
  /**
@@ -65,38 +133,46 @@ export function nearestOffset(value, offsets) {
65
133
  export const MIN_PEEK_SCRIM_OPACITY = 0.3;
66
134
 
67
135
  /**
68
- * Whether `offset` is the peek detent: the shortest stop, where the sheet is a
69
- * glance rather than a working surface. A sheet with no collapsed stop at all
70
- * has no peek — the same rule `scrimOpacityForOffset` thins the scrim by.
136
+ * The peek detent's offset, or `null` when this sheet has no peek.
71
137
  *
72
- * The peek is the one detent the sheet does NOT express as layout height: at a
73
- * sliver of viewport there is nothing useful to lay out, and reflowing the
74
- * content into it (then back out on the way up) is churn the user sees. It
75
- * keeps the full layout height and slides below the viewport instead.
138
+ * A peek is the shortest stop AND a sliver — at most `PEEK_MAX_HEIGHT_RATIO`
139
+ * of the fully open sheet. It is the one detent the sheet does NOT express as
140
+ * layout height: at a sliver there is nothing useful to lay out, and reflowing
141
+ * the content into it (then back out on the way up) is churn the user sees, so
142
+ * it keeps the full layout height and slides below the viewport instead. It is
143
+ * also the only stop that thins the scrim.
144
+ *
145
+ * A sheet whose shortest stop is a working height — `[0.5]`, say — has no
146
+ * peek: half a screen of content deserves to be laid out at half a screen, and
147
+ * a scrim that thinned there would read as a dismissed sheet.
76
148
  */
77
- export function isPeekOffset(offset, offsets, tolerancePx = 0.5) {
78
- if (offsets.length < 2) {
79
- return false;
149
+ export function peekOffsetFor(offsets, visibleSheetHeight) {
150
+ if (offsets.length < 2 || visibleSheetHeight <= 0) {
151
+ return null;
80
152
  }
81
- return Math.abs(offset - offsets[offsets.length - 1]) <= tolerancePx;
153
+ const shortest = offsets[offsets.length - 1];
154
+ const heightAtShortest = visibleSheetHeight - shortest;
155
+ return heightAtShortest <= PEEK_MAX_HEIGHT_RATIO * visibleSheetHeight ? shortest : null;
82
156
  }
83
157
 
84
158
  /**
85
159
  * Scrim opacity (1 = fully visible) for a drag/settle `offset`.
86
- * The scrim stays full while the sheet is at or above its second-shortest
87
- * detent, then fades as it collapses onto the shortest ("peek") detent — a
88
- * glance state that thins the backdrop to `MIN_PEEK_SCRIM_OPACITY` (not fully
89
- * gone, since the sheet is still modal) and holds there below it.
90
- * A single-detent sheet has no peek, so it instead fades all the way to 0
91
- * across the dismiss overshoot toward `dismissOffset` (the sheet is leaving).
160
+ *
161
+ * The scrim is full down to the shortest stop that is still a working surface,
162
+ * and only thins past it — onto a peek, where it holds at
163
+ * `MIN_PEEK_SCRIM_OPACITY` (a glance state, and the sheet is still modal), or
164
+ * out through the dismiss overshoot toward `dismissOffset`, where it clears
165
+ * completely because the sheet is leaving.
166
+ *
167
+ * So a sheet whose stops are all working heights keeps a full scrim at every
168
+ * one of them; only the sliver of a peek, or a sheet on its way out, dims less.
92
169
  */
93
- export function scrimOpacityForOffset(offset, offsets, dismissOffset) {
94
- const shortest = offsets[offsets.length - 1];
95
- const hasPeek = offsets.length >= 2;
96
- const fadeStart = hasPeek ? offsets[offsets.length - 2] : 0;
97
- const fadeEnd = hasPeek ? shortest : dismissOffset;
98
- // Peek is a resting state on a still-modal sheet, so keep a floor; the
99
- // dismiss overshoot is a sheet on its way out, so let it clear completely.
170
+ export function scrimOpacityForOffset(offset, offsets, dismissOffset, peekOffset) {
171
+ const hasPeek = peekOffset !== null;
172
+ // The last stop that is still a working surface — the peek's neighbor when
173
+ // there is a peek, otherwise the shortest stop itself.
174
+ const fadeStart = hasPeek ? offsets[offsets.length - 2] : offsets[offsets.length - 1];
175
+ const fadeEnd = hasPeek ? peekOffset : dismissOffset;
100
176
  const floor = hasPeek ? MIN_PEEK_SCRIM_OPACITY : 0;
101
177
  if (offset <= fadeStart) {
102
178
  return 1;
@@ -15,14 +15,16 @@
15
15
  * the part the scrolling area gives up as layout height, and the remainder is
16
16
  * a transform. Gestures and snaps only ever move the transform, so they stay
17
17
  * on the compositor; layout height changes at rest, in one transition-free
18
- * render whose visible geometry is identical. The peek detent keeps the full
19
- * height and slides below the viewport instead of reflowing to a sliver.
18
+ * render whose visible geometry is identical. A peek detent — a stop that is
19
+ * only a sliver of the sheet — keeps the full height and slides below the
20
+ * viewport instead of reflowing to that sliver.
20
21
  *
21
- * Those are pixels, and the detents behind them are viewport fractions, so a
22
- * sheet at rest re-resolves its detent on `resize` / `orientationchange` and
23
- * re-anchors to the new geometry without animating. The detent it returns to
24
- * is tracked by index: the pixels stop meaning the same thing when the
25
- * viewport changes, the stop the user chose does not.
22
+ * Those are pixels, and the stops behind them are relative to the viewport, so
23
+ * a sheet at rest re-resolves its detent on `resize` / `orientationchange`,
24
+ * and whenever the host swaps the snap points, and re-anchors to the new
25
+ * geometry without animating. The detent it returns to is tracked by index:
26
+ * the pixels stop meaning the same thing when the viewport changes, the stop
27
+ * the user chose does not.
26
28
  *
27
29
  * On touch, the scrolling body hands the gesture to the sheet at a scroll
28
30
  * edge. Two shapes, because the browser only offers one of them a choice: a
@@ -70,6 +72,9 @@ export interface UseSheetGesturesOptions {
70
72
  * (rotation, a resized desktop window, collapsing browser chrome). The fully
71
73
  * open height is always the tallest detent. Omit for a single-height sheet
72
74
  * (a drag then only dismisses or springs back).
75
+ *
76
+ * Keep the identity stable: a new function is read as new stops, and
77
+ * re-anchors a resting sheet to them.
73
78
  */
74
79
  snapHeights?: () => number[];
75
80
  /** Notified when the settled visible detent height (px) changes. */
@@ -153,7 +158,7 @@ export interface UseSheetGesturesResult {
153
158
  /**
154
159
  * How much of `settledOffset` is expressed as layout height rather than as a
155
160
  * transform. Equals `settledOffset` at the resizing detents; 0 at fully open
156
- * and at the peek, which keep the sheet's full height (see isPeekOffset).
161
+ * and at the peek, which keep the sheet's full height (see peekOffsetFor).
157
162
  */
158
163
  settledLayoutOffset: number;
159
164
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useSheetGestures.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/useSheetGestures.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,UAAU,IAAI,eAAe,EAClC,KAAK,YAAY,IAAI,iBAAiB,EACtC,KAAK,SAAS,EACd,KAAK,OAAO,IAAI,YAAY,EAC7B,MAAM,OAAO,CAAC;AAuHf,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gFAAgF;IAChF,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAC7B,oEAAoE;IACpE,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,iBAAiB,CAAC;IAChC,gEAAgE;IAChE,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;OAKG;IACH,cAAc,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC9C,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,uBAAuB,EAAE,MAAM,CAAC;IAChC,qEAAqE;IACrE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;;;;;OAOG;IACH,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,UAAiB,EACjB,sBAA0B,EAC1B,SAAS,EACT,WAAW,EACX,MAAM,EACN,cAAc,GACf,EAAE,uBAAuB,GAAG,sBAAsB,CAqmClD"}
1
+ {"version":3,"file":"useSheetGestures.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/useSheetGestures.ts"],"names":[],"mappings":"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"}