@tamagui/select 1.0.1-beta.118 → 1.0.1-beta.121

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 (49) hide show
  1. package/dist/cjs/Select.js +36 -32
  2. package/dist/cjs/Select.js.map +2 -2
  3. package/dist/cjs/SelectContent.js +2 -1
  4. package/dist/cjs/SelectContent.js.map +2 -2
  5. package/dist/cjs/SelectImpl.js +154 -284
  6. package/dist/cjs/SelectImpl.js.map +2 -2
  7. package/dist/cjs/SelectScrollButton.js +54 -40
  8. package/dist/cjs/SelectScrollButton.js.map +2 -2
  9. package/dist/cjs/SelectViewport.js +10 -6
  10. package/dist/cjs/SelectViewport.js.map +2 -2
  11. package/dist/cjs/types.js.map +1 -1
  12. package/dist/esm/Select.js +36 -32
  13. package/dist/esm/Select.js.map +2 -2
  14. package/dist/esm/SelectContent.js +3 -2
  15. package/dist/esm/SelectContent.js.map +2 -2
  16. package/dist/esm/SelectImpl.js +158 -286
  17. package/dist/esm/SelectImpl.js.map +2 -2
  18. package/dist/esm/SelectScrollButton.js +54 -40
  19. package/dist/esm/SelectScrollButton.js.map +2 -2
  20. package/dist/esm/SelectViewport.js +10 -6
  21. package/dist/esm/SelectViewport.js.map +2 -2
  22. package/dist/jsx/Select.js +34 -34
  23. package/dist/jsx/Select.js.map +2 -2
  24. package/dist/jsx/SelectContent.js +3 -2
  25. package/dist/jsx/SelectContent.js.map +2 -2
  26. package/dist/jsx/SelectImpl.js +128 -266
  27. package/dist/jsx/SelectImpl.js.map +2 -2
  28. package/dist/jsx/SelectScrollButton.js +53 -39
  29. package/dist/jsx/SelectScrollButton.js.map +2 -2
  30. package/dist/jsx/SelectViewport.js +10 -6
  31. package/dist/jsx/SelectViewport.js.map +2 -2
  32. package/package.json +15 -15
  33. package/src/Select.tsx +47 -49
  34. package/src/SelectContent.tsx +4 -2
  35. package/src/SelectImpl.tsx +179 -369
  36. package/src/SelectScrollButton.tsx +72 -54
  37. package/src/SelectViewport.tsx +12 -6
  38. package/src/types.tsx +9 -1
  39. package/types/Select.d.ts +15 -15
  40. package/types/Select.d.ts.map +1 -1
  41. package/types/SelectContent.d.ts +0 -1
  42. package/types/SelectContent.d.ts.map +1 -1
  43. package/types/SelectImpl.d.ts.map +1 -1
  44. package/types/SelectViewport.d.ts +3 -3
  45. package/types/SelectViewport.d.ts.map +1 -1
  46. package/types/types.d.ts +9 -1
  47. package/types/types.d.ts.map +1 -1
  48. package/types/constants.d.ts +0 -8
  49. package/types/constants.d.ts.map +0 -1
@@ -2,7 +2,7 @@ import { autoUpdate, offset, useFloating } from "@floating-ui/react-dom-interact
2
2
  import { useComposedRefs } from "@tamagui/compose-refs";
3
3
  import { YStack } from "@tamagui/stacks";
4
4
  import * as React from "react";
5
- import { SCROLL_ARROW_THRESHOLD, SCROLL_ARROW_VELOCITY } from "./constants";
5
+ import { flushSync } from "react-dom";
6
6
  import { useSelectContext } from "./context";
7
7
  const SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton";
8
8
  const SelectScrollUpButton = React.forwardRef((props, forwardedRef) => {
@@ -17,58 +17,72 @@ SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
17
17
  const SelectScrollButtonImpl = React.memo(React.forwardRef((props, forwardedRef) => {
18
18
  var _a, _b;
19
19
  const { __scopeSelect, dir, componentName, ...scrollIndicatorProps } = props;
20
- const { floatingRef, increaseHeight, forceUpdate, ...context } = useSelectContext(componentName, __scopeSelect);
21
- const intervalRef = React.useRef();
22
- const loopingRef = React.useRef(false);
20
+ const { floatingRef, forceUpdate, open, fallback, setScrollTop, setInnerOffset, ...context } = useSelectContext(componentName, __scopeSelect);
21
+ const [element, setElement] = React.useState(null);
22
+ const statusRef = React.useRef("idle");
23
23
  const isVisible = context[dir === "down" ? "canScrollDown" : "canScrollUp"];
24
+ const frameRef = React.useRef();
24
25
  const { x, y, reference, floating, strategy, update, refs } = useFloating({
25
26
  strategy: "fixed",
26
27
  placement: dir === "up" ? "top" : "bottom",
27
- middleware: [offset(({ rects }) => -rects.floating.height)]
28
+ middleware: [offset(({ rects }) => -rects.floating.height)],
29
+ whileElementsMounted: (...args) => autoUpdate(...args, { animationFrame: true })
28
30
  });
29
- const composedRefs = useComposedRefs(forwardedRef, floating);
30
- React.useLayoutEffect(() => {
31
- if (!floatingRef)
32
- return;
33
- reference(floatingRef.current);
34
- }, [reference, floatingRef == null ? void 0 : floatingRef.current]);
35
- React.useEffect(() => {
36
- if (!refs.reference.current || !refs.floating.current || !isVisible) {
37
- return;
31
+ const composedRef = useComposedRefs(forwardedRef, floating);
32
+ if (floatingRef) {
33
+ if (open) {
34
+ if (element !== floatingRef.current) {
35
+ setElement(floatingRef.current);
36
+ reference(floatingRef.current);
37
+ requestAnimationFrame(update);
38
+ }
39
+ } else {
40
+ cancelAnimationFrame(frameRef.current);
38
41
  }
39
- const cleanup = autoUpdate(refs.reference.current, refs.floating.current, update, {
40
- animationFrame: true
41
- });
42
+ }
43
+ React.useLayoutEffect(() => {
42
44
  return () => {
43
- clearInterval(intervalRef.current);
44
- loopingRef.current = false;
45
- cleanup();
45
+ cancelAnimationFrame(frameRef.current);
46
46
  };
47
- }, [isVisible, update, refs.floating, refs.reference]);
48
- if (!isVisible) {
47
+ }, []);
48
+ if (!isVisible || !floatingRef) {
49
49
  return null;
50
50
  }
51
- const handleScrollArrowChange = () => {
52
- if (!floatingRef)
53
- return;
54
- const floating2 = floatingRef.current;
55
- const isUp = dir === "up";
56
- if (floating2) {
57
- const value = isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY;
58
- const multi = isUp && floating2.scrollTop <= SCROLL_ARROW_THRESHOLD * 2 || !isUp && floating2.scrollTop >= floating2.scrollHeight - floating2.clientHeight - SCROLL_ARROW_THRESHOLD * 2 ? 2 : 1;
59
- floating2.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY);
60
- increaseHeight == null ? void 0 : increaseHeight(floating2, multi === 2 ? value * 2 : value);
61
- forceUpdate();
51
+ const onScroll = (amount) => {
52
+ if (fallback) {
53
+ if (refs.floating.current) {
54
+ refs.floating.current.scrollTop -= amount;
55
+ flushSync(() => {
56
+ var _a2, _b2;
57
+ return setScrollTop((_b2 = (_a2 = refs.floating.current) == null ? void 0 : _a2.scrollTop) != null ? _b2 : 0);
58
+ });
59
+ }
60
+ } else {
61
+ flushSync(() => setInnerOffset((value) => value - amount));
62
62
  }
63
63
  };
64
- return <YStack ref={composedRefs} componentName={componentName} aria-hidden {...scrollIndicatorProps} zIndex={1e3} position={strategy} left={x || 0} top={y || 0} width={`calc(${((_b = (_a = floatingRef == null ? void 0 : floatingRef.current) == null ? void 0 : _a.offsetWidth) != null ? _b : 0) - 2}px)`} onPointerMove={() => {
65
- if (!loopingRef.current) {
66
- intervalRef.current = setInterval(handleScrollArrowChange, 1e3 / 60);
67
- loopingRef.current = true;
64
+ return <YStack ref={composedRef} componentName={componentName} aria-hidden {...scrollIndicatorProps} zIndex={1e3} position={strategy} left={x || 0} top={y || 0} width={`calc(${((_b = (_a = floatingRef == null ? void 0 : floatingRef.current) == null ? void 0 : _a.offsetWidth) != null ? _b : 0) - 2}px)`} onPointerEnter={() => {
65
+ statusRef.current = "active";
66
+ let prevNow = Date.now();
67
+ function frame() {
68
+ if (element) {
69
+ const currentNow = Date.now();
70
+ const msElapsed = currentNow - prevNow;
71
+ prevNow = currentNow;
72
+ const pixelsToScroll = msElapsed / 2;
73
+ const remainingPixels = dir === "up" ? element.scrollTop : element.scrollHeight - element.clientHeight - element.scrollTop;
74
+ const scrollRemaining = dir === "up" ? element.scrollTop - pixelsToScroll > 0 : element.scrollTop + pixelsToScroll < element.scrollHeight - element.clientHeight;
75
+ onScroll(dir === "up" ? Math.min(pixelsToScroll, remainingPixels) : Math.max(-pixelsToScroll, -remainingPixels));
76
+ if (scrollRemaining) {
77
+ frameRef.current = requestAnimationFrame(frame);
78
+ }
79
+ }
68
80
  }
81
+ cancelAnimationFrame(frameRef.current);
82
+ frameRef.current = requestAnimationFrame(frame);
69
83
  }} onPointerLeave={() => {
70
- loopingRef.current = false;
71
- clearInterval(intervalRef.current);
84
+ statusRef.current = "idle";
85
+ cancelAnimationFrame(frameRef.current);
72
86
  }} />;
73
87
  }));
74
88
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/SelectScrollButton.tsx"],
4
- "sourcesContent": ["import { autoUpdate, offset, useFloating } from '@floating-ui/react-dom-interactions'\nimport { useComposedRefs } from '@tamagui/compose-refs'\nimport { TamaguiElement } from '@tamagui/core'\nimport { YStack } from '@tamagui/stacks'\nimport * as React from 'react'\n\nimport { SCROLL_ARROW_THRESHOLD, SCROLL_ARROW_VELOCITY } from './constants'\nimport { useSelectContext } from './context'\nimport { ScopedProps, SelectScrollButtonImplProps, SelectScrollButtonProps } from './types'\n\n/* -------------------------------------------------------------------------------------------------\n * SelectScrollUpButton\n * -----------------------------------------------------------------------------------------------*/\n\nconst SCROLL_UP_BUTTON_NAME = 'SelectScrollUpButton'\n\nexport const SelectScrollUpButton = React.forwardRef<TamaguiElement, SelectScrollButtonProps>(\n (props: ScopedProps<SelectScrollButtonProps>, forwardedRef) => {\n return (\n <SelectScrollButtonImpl\n componentName={SCROLL_UP_BUTTON_NAME}\n {...props}\n dir=\"up\"\n ref={forwardedRef}\n />\n )\n }\n)\n\nSelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * SelectScrollDownButton\n * -----------------------------------------------------------------------------------------------*/\n\nconst SCROLL_DOWN_BUTTON_NAME = 'SelectScrollDownButton'\n\nexport const SelectScrollDownButton = React.forwardRef<TamaguiElement, SelectScrollButtonProps>(\n (props: ScopedProps<SelectScrollButtonProps>, forwardedRef) => {\n return (\n <SelectScrollButtonImpl\n componentName={SCROLL_DOWN_BUTTON_NAME}\n {...props}\n dir=\"down\"\n ref={forwardedRef}\n />\n )\n }\n)\n\nSelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME\n\ntype SelectScrollButtonImplElement = TamaguiElement\n\nconst SelectScrollButtonImpl = React.memo(\n React.forwardRef<SelectScrollButtonImplElement, SelectScrollButtonImplProps>(\n (props: ScopedProps<SelectScrollButtonImplProps>, forwardedRef) => {\n const { __scopeSelect, dir, componentName, ...scrollIndicatorProps } = props\n const { floatingRef, increaseHeight, forceUpdate, ...context } = useSelectContext(\n componentName,\n __scopeSelect\n )\n const intervalRef = React.useRef<any>()\n const loopingRef = React.useRef(false)\n const isVisible = context[dir === 'down' ? 'canScrollDown' : 'canScrollUp']\n\n const { x, y, reference, floating, strategy, update, refs } = useFloating({\n strategy: 'fixed',\n placement: dir === 'up' ? 'top' : 'bottom',\n middleware: [offset(({ rects }) => -rects.floating.height)],\n })\n\n const composedRefs = useComposedRefs(forwardedRef, floating)\n\n React.useLayoutEffect(() => {\n if (!floatingRef) return\n reference(floatingRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [reference, floatingRef?.current])\n\n React.useEffect(() => {\n if (!refs.reference.current || !refs.floating.current || !isVisible) {\n return\n }\n\n const cleanup = autoUpdate(refs.reference.current, refs.floating.current, update, {\n animationFrame: true,\n })\n\n return () => {\n clearInterval(intervalRef.current)\n loopingRef.current = false\n cleanup()\n }\n }, [isVisible, update, refs.floating, refs.reference])\n\n if (!isVisible) {\n return null\n }\n\n const handleScrollArrowChange = () => {\n if (!floatingRef) return\n const floating = floatingRef.current\n const isUp = dir === 'up'\n\n if (floating) {\n const value = isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY\n const multi =\n (isUp && floating.scrollTop <= SCROLL_ARROW_THRESHOLD * 2) ||\n (!isUp &&\n floating.scrollTop >=\n floating.scrollHeight - floating.clientHeight - SCROLL_ARROW_THRESHOLD * 2)\n ? 2\n : 1\n\n floating.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY)\n\n increaseHeight?.(floating, multi === 2 ? value * 2 : value)\n // Ensure derived data (scroll arrows) is fresh\n forceUpdate()\n }\n }\n\n return (\n <YStack\n ref={composedRefs}\n componentName={componentName}\n aria-hidden\n {...scrollIndicatorProps}\n zIndex={1000}\n // @ts-expect-error\n position={strategy}\n left={x || 0}\n top={y || 0}\n width={`calc(${(floatingRef?.current?.offsetWidth ?? 0) - 2}px)`}\n onPointerMove={() => {\n if (!loopingRef.current) {\n intervalRef.current = setInterval(handleScrollArrowChange, 1000 / 60)\n loopingRef.current = true\n }\n }}\n onPointerLeave={() => {\n loopingRef.current = false\n clearInterval(intervalRef.current)\n }}\n />\n )\n }\n )\n)\n"],
5
- "mappings": "AAAA;AACA;AAEA;AACA;AAEA;AACA;AAOA,MAAM,wBAAwB;AAEvB,MAAM,uBAAuB,MAAM,WACxC,CAAC,OAA6C,iBAAiB;AAC7D,SACE,CAAC,uBACC,eAAe,2BACX,OACJ,IAAI,KACJ,KAAK,cACP;AAEJ,CACF;AAEA,qBAAqB,cAAc;AAMnC,MAAM,0BAA0B;AAEzB,MAAM,yBAAyB,MAAM,WAC1C,CAAC,OAA6C,iBAAiB;AAC7D,SACE,CAAC,uBACC,eAAe,6BACX,OACJ,IAAI,OACJ,KAAK,cACP;AAEJ,CACF;AAEA,uBAAuB,cAAc;AAIrC,MAAM,yBAAyB,MAAM,KACnC,MAAM,WACJ,CAAC,OAAiD,iBAAiB;AAxDvE;AAyDM,QAAM,EAAE,eAAe,KAAK,kBAAkB,yBAAyB;AACvE,QAAM,EAAE,aAAa,gBAAgB,gBAAgB,YAAY,iBAC/D,eACA,aACF;AACA,QAAM,cAAc,MAAM,OAAY;AACtC,QAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAM,YAAY,QAAQ,QAAQ,SAAS,kBAAkB;AAE7D,QAAM,EAAE,GAAG,GAAG,WAAW,UAAU,UAAU,QAAQ,SAAS,YAAY;AAAA,IACxE,UAAU;AAAA,IACV,WAAW,QAAQ,OAAO,QAAQ;AAAA,IAClC,YAAY,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,MAAM,SAAS,MAAM,CAAC;AAAA,EAC5D,CAAC;AAED,QAAM,eAAe,gBAAgB,cAAc,QAAQ;AAE3D,QAAM,gBAAgB,MAAM;AAC1B,QAAI,CAAC;AAAa;AAClB,cAAU,YAAY,OAAO;AAAA,EAE/B,GAAG,CAAC,WAAW,2CAAa,OAAO,CAAC;AAEpC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAK,UAAU,WAAW,CAAC,KAAK,SAAS,WAAW,CAAC,WAAW;AACnE;AAAA,IACF;AAEA,UAAM,UAAU,WAAW,KAAK,UAAU,SAAS,KAAK,SAAS,SAAS,QAAQ;AAAA,MAChF,gBAAgB;AAAA,IAClB,CAAC;AAED,WAAO,MAAM;AACX,oBAAc,YAAY,OAAO;AACjC,iBAAW,UAAU;AACrB,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,WAAW,QAAQ,KAAK,UAAU,KAAK,SAAS,CAAC;AAErD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,QAAM,0BAA0B,MAAM;AACpC,QAAI,CAAC;AAAa;AAClB,UAAM,YAAW,YAAY;AAC7B,UAAM,OAAO,QAAQ;AAErB,QAAI,WAAU;AACZ,YAAM,QAAQ,OAAO,CAAC,wBAAwB;AAC9C,YAAM,QACH,QAAQ,UAAS,aAAa,yBAAyB,KACvD,CAAC,QACA,UAAS,aACP,UAAS,eAAe,UAAS,eAAe,yBAAyB,IACzE,IACA;AAEN,gBAAS,aAAa,QAAS,QAAO,CAAC,wBAAwB;AAE/D,uDAAiB,WAAU,UAAU,IAAI,QAAQ,IAAI;AAErD,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SACE,CAAC,OACC,KAAK,cACL,eAAe,eACf,gBACI,sBACJ,QAAQ,KAER,UAAU,UACV,MAAM,KAAK,GACX,KAAK,KAAK,GACV,OAAO,QAAS,wDAAa,YAAb,mBAAsB,gBAAtB,YAAqC,KAAK,QAC1D,eAAe,MAAM;AACnB,QAAI,CAAC,WAAW,SAAS;AACvB,kBAAY,UAAU,YAAY,yBAAyB,MAAO,EAAE;AACpE,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GACA,gBAAgB,MAAM;AACpB,eAAW,UAAU;AACrB,kBAAc,YAAY,OAAO;AAAA,EACnC,GACF;AAEJ,CACF,CACF;",
4
+ "sourcesContent": ["import { autoUpdate, offset, useFloating } from '@floating-ui/react-dom-interactions'\nimport { useComposedRefs } from '@tamagui/compose-refs'\nimport { TamaguiElement } from '@tamagui/core'\nimport { YStack } from '@tamagui/stacks'\nimport * as React from 'react'\nimport { flushSync } from 'react-dom'\n\nimport { useSelectContext } from './context'\nimport { ScopedProps, SelectScrollButtonImplProps, SelectScrollButtonProps } from './types'\n\n/* -------------------------------------------------------------------------------------------------\n * SelectScrollUpButton\n * -----------------------------------------------------------------------------------------------*/\n\nconst SCROLL_UP_BUTTON_NAME = 'SelectScrollUpButton'\n\nexport const SelectScrollUpButton = React.forwardRef<TamaguiElement, SelectScrollButtonProps>(\n (props: ScopedProps<SelectScrollButtonProps>, forwardedRef) => {\n return (\n <SelectScrollButtonImpl\n componentName={SCROLL_UP_BUTTON_NAME}\n {...props}\n dir=\"up\"\n ref={forwardedRef}\n />\n )\n }\n)\n\nSelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * SelectScrollDownButton\n * -----------------------------------------------------------------------------------------------*/\n\nconst SCROLL_DOWN_BUTTON_NAME = 'SelectScrollDownButton'\n\nexport const SelectScrollDownButton = React.forwardRef<TamaguiElement, SelectScrollButtonProps>(\n (props: ScopedProps<SelectScrollButtonProps>, forwardedRef) => {\n return (\n <SelectScrollButtonImpl\n componentName={SCROLL_DOWN_BUTTON_NAME}\n {...props}\n dir=\"down\"\n ref={forwardedRef}\n />\n )\n }\n)\n\nSelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME\n\ntype SelectScrollButtonImplElement = TamaguiElement\n\nconst SelectScrollButtonImpl = React.memo(\n React.forwardRef<SelectScrollButtonImplElement, SelectScrollButtonImplProps>(\n (props: ScopedProps<SelectScrollButtonImplProps>, forwardedRef) => {\n const { __scopeSelect, dir, componentName, ...scrollIndicatorProps } = props\n const { floatingRef, forceUpdate, open, fallback, setScrollTop, setInnerOffset, ...context } =\n useSelectContext(componentName, __scopeSelect)\n\n const [element, setElement] = React.useState<HTMLElement | null>(null)\n const statusRef = React.useRef<'idle' | 'active'>('idle')\n const isVisible = context[dir === 'down' ? 'canScrollDown' : 'canScrollUp']\n const frameRef = React.useRef<any>()\n\n const { x, y, reference, floating, strategy, update, refs } = useFloating({\n strategy: 'fixed',\n placement: dir === 'up' ? 'top' : 'bottom',\n middleware: [offset(({ rects }) => -rects.floating.height)],\n whileElementsMounted: (...args) => autoUpdate(...args, { animationFrame: true }),\n })\n\n const composedRef = useComposedRefs(forwardedRef, floating)\n\n if (floatingRef) {\n if (open) {\n if (element !== floatingRef.current) {\n setElement(floatingRef.current)\n reference(floatingRef.current)\n requestAnimationFrame(update)\n }\n } else {\n cancelAnimationFrame(frameRef.current)\n }\n }\n\n React.useLayoutEffect(() => {\n return () => {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n cancelAnimationFrame(frameRef.current)\n }\n }, [])\n\n if (!isVisible || !floatingRef) {\n return null\n }\n\n const onScroll = (amount: number) => {\n if (fallback) {\n if (refs.floating.current) {\n refs.floating.current.scrollTop -= amount\n flushSync(() => setScrollTop!(refs.floating.current?.scrollTop ?? 0))\n }\n } else {\n flushSync(() => setInnerOffset!((value) => value - amount))\n }\n }\n\n return (\n <YStack\n ref={composedRef}\n componentName={componentName}\n aria-hidden\n {...scrollIndicatorProps}\n zIndex={1000}\n // @ts-expect-error\n position={strategy}\n left={x || 0}\n top={y || 0}\n width={`calc(${(floatingRef?.current?.offsetWidth ?? 0) - 2}px)`}\n onPointerEnter={() => {\n statusRef.current = 'active'\n let prevNow = Date.now()\n\n function frame() {\n if (element) {\n const currentNow = Date.now()\n const msElapsed = currentNow - prevNow\n prevNow = currentNow\n\n const pixelsToScroll = msElapsed / 2\n\n const remainingPixels =\n dir === 'up'\n ? element.scrollTop\n : element.scrollHeight - element.clientHeight - element.scrollTop\n\n const scrollRemaining =\n dir === 'up'\n ? element.scrollTop - pixelsToScroll > 0\n : element.scrollTop + pixelsToScroll <\n element.scrollHeight - element.clientHeight\n\n onScroll(\n dir === 'up'\n ? Math.min(pixelsToScroll, remainingPixels)\n : Math.max(-pixelsToScroll, -remainingPixels)\n )\n\n if (scrollRemaining) {\n frameRef.current = requestAnimationFrame(frame)\n }\n }\n }\n\n cancelAnimationFrame(frameRef.current)\n frameRef.current = requestAnimationFrame(frame)\n }}\n onPointerLeave={() => {\n statusRef.current = 'idle'\n cancelAnimationFrame(frameRef.current)\n }}\n />\n )\n }\n )\n)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -29,10 +29,8 @@ const SelectViewportFrame = styled(ThemeableStack, {
29
29
  }
30
30
  });
31
31
  const SelectViewport = React.forwardRef((props, forwardedRef) => {
32
- var _a, _b;
33
32
  const { __scopeSelect, children, ...viewportProps } = props;
34
33
  const context = useSelectContext(VIEWPORT_NAME, __scopeSelect);
35
- const { style, ...floatingProps } = (_b = (_a = context.interactions) == null ? void 0 : _a.getFloatingProps()) != null ? _b : {};
36
34
  const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint);
37
35
  if (breakpointActive || !isWeb) {
38
36
  return <PortalItem hostName={`${context.scopeKey}SheetContents`}>{children}</PortalItem>;
@@ -40,16 +38,22 @@ const SelectViewport = React.forwardRef((props, forwardedRef) => {
40
38
  if (!context.floatingContext) {
41
39
  return null;
42
40
  }
43
- delete style["scrollbarWidth"];
44
- delete style["listStyleType"];
41
+ if (!context.open) {
42
+ return children;
43
+ }
44
+ const {
45
+ style: { scrollbarWidth, listStyleType, ...restStyle },
46
+ ...floatingProps
47
+ } = context.interactions.getFloatingProps();
45
48
  return <>
46
49
  <style dangerouslySetInnerHTML={{
47
- __html: `[data-tamagui-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-tamagui-select-viewport]::-webkit-scrollbar{display:none}`
50
+ __html: disableScrollbarCSS
48
51
  }} />
49
- <FloatingFocusManager context={context.floatingContext} preventTabbing><SelectViewportFrame size={context.size} data-tamagui-select-viewport="" role="presentation" {...viewportProps} ref={forwardedRef} {...floatingProps} {...style}>{children}</SelectViewportFrame></FloatingFocusManager>
52
+ <FloatingFocusManager context={context.floatingContext} preventTabbing><SelectViewportFrame size={context.size} data-tamagui-select-viewport="" role="presentation" {...viewportProps} ref={forwardedRef} {...floatingProps} {...restStyle}>{children}</SelectViewportFrame></FloatingFocusManager>
50
53
  </>;
51
54
  });
52
55
  SelectViewport.displayName = VIEWPORT_NAME;
56
+ const disableScrollbarCSS = `[data-tamagui-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-tamagui-select-viewport]::-webkit-scrollbar{display:none}`;
53
57
  export {
54
58
  SelectViewport,
55
59
  SelectViewportFrame
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/SelectViewport.tsx"],
4
- "sourcesContent": ["import { FloatingFocusManager } from '@floating-ui/react-dom-interactions'\nimport { TamaguiElement, isWeb } from '@tamagui/core'\nimport { styled } from '@tamagui/core'\nimport { PortalItem } from '@tamagui/portal'\nimport { ThemeableStack } from '@tamagui/stacks'\nimport * as React from 'react'\n\nimport { VIEWPORT_NAME } from './constants'\nimport { useSelectContext } from './context'\nimport { useSelectBreakpointActive } from './Select'\nimport { ScopedProps, SelectViewportProps } from './types'\n\n/* -------------------------------------------------------------------------------------------------\n * SelectViewport\n * -----------------------------------------------------------------------------------------------*/\n\nexport const SelectViewportFrame = styled(ThemeableStack, {\n name: VIEWPORT_NAME,\n backgroundColor: '$background',\n elevate: true,\n bordered: true,\n overflow: 'scroll',\n userSelect: 'none',\n\n variants: {\n size: {\n '...size': (val, { tokens }) => {\n return {\n borderRadius: tokens.radius[val] ?? val,\n }\n },\n },\n },\n\n defaultVariants: {\n size: '$2',\n },\n})\n\nexport const SelectViewport = React.forwardRef<TamaguiElement, SelectViewportProps>(\n (props: ScopedProps<SelectViewportProps>, forwardedRef) => {\n const { __scopeSelect, children, ...viewportProps } = props\n const context = useSelectContext(VIEWPORT_NAME, __scopeSelect)\n const { style, ...floatingProps } = context.interactions?.getFloatingProps() ?? {}\n const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint)\n\n if (breakpointActive || !isWeb) {\n return <PortalItem hostName={`${context.scopeKey}SheetContents`}>{children}</PortalItem>\n }\n\n if (!context.floatingContext) {\n return null\n }\n\n // TODO\n delete style['scrollbarWidth']\n delete style['listStyleType']\n\n return (\n <>\n {/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */}\n <style\n dangerouslySetInnerHTML={{\n __html: `[data-tamagui-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-tamagui-select-viewport]::-webkit-scrollbar{display:none}`,\n }}\n />\n <FloatingFocusManager context={context.floatingContext} preventTabbing>\n <SelectViewportFrame\n size={context.size}\n data-tamagui-select-viewport=\"\"\n // @ts-ignore\n role=\"presentation\"\n {...viewportProps}\n ref={forwardedRef}\n {...floatingProps}\n {...style}\n >\n {children}\n </SelectViewportFrame>\n </FloatingFocusManager>\n </>\n )\n }\n)\n\nSelectViewport.displayName = VIEWPORT_NAME\n"],
5
- "mappings": "AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAOO,MAAM,sBAAsB,OAAO,gBAAgB;AAAA,EACxD,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EAEZ,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,WAAW,CAAC,KAAK,EAAE,aAAa;AA1BtC;AA2BQ,eAAO;AAAA,UACL,cAAc,aAAO,OAAO,SAAd,YAAsB;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAEM,MAAM,iBAAiB,MAAM,WAClC,CAAC,OAAyC,iBAAiB;AAxC7D;AAyCI,QAAM,EAAE,eAAe,aAAa,kBAAkB;AACtD,QAAM,UAAU,iBAAiB,eAAe,aAAa;AAC7D,QAAM,EAAE,UAAU,kBAAkB,oBAAQ,iBAAR,mBAAsB,uBAAtB,YAA4C,CAAC;AACjF,QAAM,mBAAmB,0BAA0B,QAAQ,eAAe;AAE1E,MAAI,oBAAoB,CAAC,OAAO;AAC9B,WAAO,CAAC,WAAW,UAAU,GAAG,QAAQ,0BAA0B,SAAS,EAAnE;AAAA,EACV;AAEA,MAAI,CAAC,QAAQ,iBAAiB;AAC5B,WAAO;AAAA,EACT;AAGA,SAAO,MAAM;AACb,SAAO,MAAM;AAEb,SACE;AAAA,IAEE,CAAC,MACC,yBAAyB;AAAA,MACvB,QAAQ;AAAA,IACV,GACF;AAAA,IACA,CAAC,qBAAqB,SAAS,QAAQ,iBAAiB,eACtD,CAAC,oBACC,MAAM,QAAQ,MACd,6BAA6B,GAE7B,KAAK,mBACD,eACJ,KAAK,kBACD,mBACA,QAEH,SACH,EAXC,oBAYH,EAbC;AAAA,EAcH;AAEJ,CACF;AAEA,eAAe,cAAc;",
4
+ "sourcesContent": ["import { FloatingFocusManager } from '@floating-ui/react-dom-interactions'\nimport { TamaguiElement, isWeb } from '@tamagui/core'\nimport { styled } from '@tamagui/core'\nimport { PortalItem } from '@tamagui/portal'\nimport { ThemeableStack } from '@tamagui/stacks'\nimport * as React from 'react'\n\nimport { VIEWPORT_NAME } from './constants'\nimport { useSelectContext } from './context'\nimport { useSelectBreakpointActive } from './Select'\nimport { ScopedProps, SelectViewportProps } from './types'\n\n/* -------------------------------------------------------------------------------------------------\n * SelectViewport\n * -----------------------------------------------------------------------------------------------*/\n\nexport const SelectViewportFrame = styled(ThemeableStack, {\n name: VIEWPORT_NAME,\n backgroundColor: '$background',\n elevate: true,\n bordered: true,\n overflow: 'scroll',\n userSelect: 'none',\n\n variants: {\n size: {\n '...size': (val, { tokens }) => {\n return {\n borderRadius: tokens.radius[val] ?? val,\n }\n },\n },\n },\n\n defaultVariants: {\n size: '$2',\n },\n})\n\nexport const SelectViewport = React.forwardRef<TamaguiElement, SelectViewportProps>(\n (props: ScopedProps<SelectViewportProps>, forwardedRef) => {\n const { __scopeSelect, children, ...viewportProps } = props\n const context = useSelectContext(VIEWPORT_NAME, __scopeSelect)\n const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint)\n\n if (breakpointActive || !isWeb) {\n return <PortalItem hostName={`${context.scopeKey}SheetContents`}>{children}</PortalItem>\n }\n\n if (!context.floatingContext) {\n return null\n }\n\n if (!context.open) {\n return children\n }\n\n const {\n style: { scrollbarWidth, listStyleType, ...restStyle },\n ...floatingProps\n } = context.interactions!.getFloatingProps()\n\n return (\n <>\n {/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */}\n <style\n dangerouslySetInnerHTML={{\n __html: disableScrollbarCSS,\n }}\n />\n <FloatingFocusManager context={context.floatingContext} preventTabbing>\n <SelectViewportFrame\n size={context.size}\n data-tamagui-select-viewport=\"\"\n // @ts-ignore\n role=\"presentation\"\n {...viewportProps}\n ref={forwardedRef}\n {...floatingProps}\n {...restStyle}\n >\n {children}\n </SelectViewportFrame>\n </FloatingFocusManager>\n </>\n )\n }\n)\n\nSelectViewport.displayName = VIEWPORT_NAME\n\nconst disableScrollbarCSS = `[data-tamagui-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-tamagui-select-viewport]::-webkit-scrollbar{display:none}`\n"],
5
+ "mappings": "AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAOO,MAAM,sBAAsB,OAAO,gBAAgB;AAAA,EACxD,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EAEZ,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,WAAW,CAAC,KAAK,EAAE,aAAa;AA1BtC;AA2BQ,eAAO;AAAA,UACL,cAAc,aAAO,OAAO,SAAd,YAAsB;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAEM,MAAM,iBAAiB,MAAM,WAClC,CAAC,OAAyC,iBAAiB;AACzD,QAAM,EAAE,eAAe,aAAa,kBAAkB;AACtD,QAAM,UAAU,iBAAiB,eAAe,aAAa;AAC7D,QAAM,mBAAmB,0BAA0B,QAAQ,eAAe;AAE1E,MAAI,oBAAoB,CAAC,OAAO;AAC9B,WAAO,CAAC,WAAW,UAAU,GAAG,QAAQ,0BAA0B,SAAS,EAAnE;AAAA,EACV;AAEA,MAAI,CAAC,QAAQ,iBAAiB;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,MAAM;AACjB,WAAO;AAAA,EACT;AAEA,QAAM;AAAA,IACJ,OAAO,EAAE,gBAAgB,kBAAkB;AAAA,OACxC;AAAA,MACD,QAAQ,aAAc,iBAAiB;AAE3C,SACE;AAAA,IAEE,CAAC,MACC,yBAAyB;AAAA,MACvB,QAAQ;AAAA,IACV,GACF;AAAA,IACA,CAAC,qBAAqB,SAAS,QAAQ,iBAAiB,eACtD,CAAC,oBACC,MAAM,QAAQ,MACd,6BAA6B,GAE7B,KAAK,mBACD,eACJ,KAAK,kBACD,mBACA,YAEH,SACH,EAXC,oBAYH,EAbC;AAAA,EAcH;AAEJ,CACF;AAEA,eAAe,cAAc;AAE7B,MAAM,sBAAsB;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/select",
3
- "version": "1.0.1-beta.118",
3
+ "version": "1.0.1-beta.121",
4
4
  "sideEffects": [
5
5
  "*.css"
6
6
  ],
@@ -21,20 +21,20 @@
21
21
  "clean:build": "tamagui-build clean:build"
22
22
  },
23
23
  "dependencies": {
24
- "@floating-ui/react-dom": "0.7.0",
25
- "@floating-ui/react-dom-interactions": "^0.5.0",
26
- "@floating-ui/react-native": "^0.7.0",
24
+ "@floating-ui/react-dom": "^1.0.0",
25
+ "@floating-ui/react-dom-interactions": "^0.8.1",
26
+ "@floating-ui/react-native": "^0.8.0",
27
27
  "@radix-ui/react-use-previous": "^0.1.1",
28
- "@tamagui/compose-refs": "^1.0.1-beta.118",
29
- "@tamagui/core": "^1.0.1-beta.118",
30
- "@tamagui/create-context": "^1.0.1-beta.118",
31
- "@tamagui/list-item": "^1.0.1-beta.118",
32
- "@tamagui/portal": "^1.0.1-beta.118",
33
- "@tamagui/separator": "^1.0.1-beta.118",
34
- "@tamagui/stacks": "^1.0.1-beta.118",
35
- "@tamagui/text": "^1.0.1-beta.118",
36
- "@tamagui/use-controllable-state": "^1.0.1-beta.118",
37
- "@tamagui/use-event": "^1.0.1-beta.118"
28
+ "@tamagui/compose-refs": "^1.0.1-beta.121",
29
+ "@tamagui/core": "^1.0.1-beta.121",
30
+ "@tamagui/create-context": "^1.0.1-beta.121",
31
+ "@tamagui/list-item": "^1.0.1-beta.121",
32
+ "@tamagui/portal": "^1.0.1-beta.121",
33
+ "@tamagui/separator": "^1.0.1-beta.121",
34
+ "@tamagui/stacks": "^1.0.1-beta.121",
35
+ "@tamagui/text": "^1.0.1-beta.121",
36
+ "@tamagui/use-controllable-state": "^1.0.1-beta.121",
37
+ "@tamagui/use-event": "^1.0.1-beta.121"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": "*",
@@ -42,7 +42,7 @@
42
42
  "react-native": "*"
43
43
  },
44
44
  "devDependencies": {
45
- "@tamagui/build": "^1.0.1-beta.118",
45
+ "@tamagui/build": "^1.0.1-beta.121",
46
46
  "@types/react-native": "^0.69.2",
47
47
  "react": "*",
48
48
  "react-dom": "*",
package/src/Select.tsx CHANGED
@@ -148,7 +148,6 @@ type SelectItemContextValue = {
148
148
  value: string
149
149
  textId: string
150
150
  isSelected: boolean
151
- onItemTextChange(node: TamaguiElement | null): void
152
151
  }
153
152
 
154
153
  const [SelectItemContextProvider, useSelectItemContext] =
@@ -173,8 +172,6 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
173
172
  } = props
174
173
  const context = useSelectContext(ITEM_NAME, __scopeSelect)
175
174
  const isSelected = context.value === value
176
- const [textValue, setTextValue] = React.useState(textValueProp ?? '')
177
- const [isFocused, setIsFocused] = React.useState(false)
178
175
  const textId = useId()
179
176
 
180
177
  const {
@@ -184,65 +181,69 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
184
181
  open,
185
182
  setOpen,
186
183
  onChange,
187
- activeIndex,
188
184
  setActiveIndex,
185
+ allowMouseUpRef,
186
+ allowSelectRef,
189
187
  setValueAtIndex,
188
+ selectTimeoutRef,
190
189
  dataRef,
191
190
  } = context
192
191
 
193
- const composedRefs = useComposedRefs(
194
- forwardedRef,
195
- (node) => {
196
- if (!isWeb) return
197
- if (node instanceof HTMLElement) {
198
- if (listRef) {
199
- listRef.current[index] = node
200
- }
192
+ const composedRefs = useComposedRefs(forwardedRef, (node) => {
193
+ if (!isWeb) return
194
+ if (node instanceof HTMLElement) {
195
+ if (listRef) {
196
+ listRef.current[index] = node
201
197
  }
202
198
  }
203
- // isSelected ? context.onSelectedItemChange : undefined
204
- )
199
+ })
205
200
 
206
201
  React.useEffect(() => {
207
202
  setValueAtIndex(index, value)
208
203
  }, [index, setValueAtIndex, value])
209
204
 
210
- const timeoutRef = React.useRef<any>()
211
- const [allowMouseUp, setAllowMouseUp] = React.useState(false)
212
-
213
205
  function handleSelect() {
214
206
  setSelectedIndex(index)
215
207
  onChange(value)
216
208
  setOpen(false)
217
209
  }
218
210
 
219
- React.useLayoutEffect(() => {
220
- clearTimeout(timeoutRef.current)
221
- if (open) {
222
- if (selectedIndex !== index) {
223
- setAllowMouseUp(true)
224
- } else {
225
- timeoutRef.current = setTimeout(() => {
226
- setAllowMouseUp(true)
227
- }, 300)
228
- }
229
- } else {
230
- setAllowMouseUp(false)
231
- }
232
- }, [open, index, setActiveIndex, selectedIndex])
233
-
234
- function handleKeyDown(event: React.KeyboardEvent) {
235
- if (event.key === 'Enter' || (event.key === ' ' && !dataRef?.current.typing)) {
236
- event.preventDefault()
237
- handleSelect()
238
- }
239
- }
240
-
241
211
  const selectItemProps = context.interactions
242
212
  ? context.interactions.getItemProps({
243
- onClick: allowMouseUp ? handleSelect : undefined,
244
- onMouseUp: allowMouseUp ? handleSelect : undefined,
245
- onKeyDown: handleKeyDown,
213
+ onTouchStart() {
214
+ allowSelectRef!.current = true
215
+ allowMouseUpRef!.current = false
216
+ },
217
+ onKeyDown(event) {
218
+ if (event.key === 'Enter' || (event.key === ' ' && !dataRef?.current.typing)) {
219
+ event.preventDefault()
220
+ handleSelect()
221
+ } else {
222
+ allowSelectRef!.current = true
223
+ }
224
+ },
225
+ onClick() {
226
+ if (allowSelectRef!.current) {
227
+ setSelectedIndex(index)
228
+ setOpen(false)
229
+ }
230
+ },
231
+ onMouseUp() {
232
+ if (!allowMouseUpRef!.current) {
233
+ return
234
+ }
235
+
236
+ if (allowSelectRef!.current) {
237
+ handleSelect()
238
+ }
239
+
240
+ // On touch devices, prevent the element from
241
+ // immediately closing `onClick` by deferring it
242
+ clearTimeout(selectTimeoutRef!.current)
243
+ selectTimeoutRef!.current = setTimeout(() => {
244
+ allowSelectRef!.current = true
245
+ })
246
+ },
246
247
  })
247
248
  : {
248
249
  onPress: handleSelect,
@@ -254,10 +255,6 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
254
255
  value={value}
255
256
  textId={textId || ''}
256
257
  isSelected={isSelected}
257
- onItemTextChange={React.useCallback((node) => {
258
- // @ts-ignore
259
- setTextValue((prevTextValue) => prevTextValue || (node?.textContent ?? '').trim())
260
- }, [])}
261
258
  >
262
259
  <ListItem
263
260
  backgrounded
@@ -266,8 +263,7 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
266
263
  componentName={ITEM_NAME}
267
264
  ref={composedRefs}
268
265
  aria-labelledby={textId}
269
- // `isFocused` caveat fixes stuttering in VoiceOver
270
- aria-selected={isSelected && isFocused}
266
+ aria-selected={isSelected}
271
267
  data-state={isSelected ? 'active' : 'inactive'}
272
268
  aria-disabled={disabled || undefined}
273
269
  data-disabled={disabled ? '' : undefined}
@@ -302,7 +298,7 @@ const SelectItemText = React.forwardRef<TamaguiElement, SelectItemTextProps>(
302
298
  const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect)
303
299
  const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect)
304
300
  const ref = React.useRef<TamaguiElement | null>(null)
305
- const composedRefs = useComposedRefs(forwardedRef, ref, itemContext.onItemTextChange)
301
+ const composedRefs = useComposedRefs(forwardedRef, ref)
306
302
  const isSelected = itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren
307
303
 
308
304
  const contents = (
@@ -560,7 +556,9 @@ export const Select = withStaticProperties(
560
556
  return (
561
557
  <SelectProvider
562
558
  dir={dir}
559
+ blockSelection={false}
563
560
  size={sizeProp}
561
+ fallback={false}
564
562
  selectedItem={selectedItem}
565
563
  setSelectedItem={setSelectedItem}
566
564
  forceUpdate={forceUpdate}
@@ -1,5 +1,5 @@
1
1
  import { FloatingOverlay, FloatingPortal } from '@floating-ui/react-dom-interactions'
2
- import { Theme, useThemeName } from '@tamagui/core'
2
+ import { Theme, useIsTouchDevice, useThemeName } from '@tamagui/core'
3
3
  import * as React from 'react'
4
4
 
5
5
  import { useSelectContext } from './context'
@@ -17,6 +17,7 @@ export const SelectContent = ({ children, __scopeSelect }: SelectContentProps) =
17
17
  const themeName = useThemeName()
18
18
  const showSheet = useShowSelectSheet(context)
19
19
  const contents = <Theme name={themeName}>{children}</Theme>
20
+ const touch = useIsTouchDevice()
20
21
 
21
22
  if (showSheet && context.open) {
22
23
  return contents
@@ -25,7 +26,8 @@ export const SelectContent = ({ children, __scopeSelect }: SelectContentProps) =
25
26
  return (
26
27
  <FloatingPortal>
27
28
  {context.open ? (
28
- <FloatingOverlay lockScroll>{contents}</FloatingOverlay>
29
+ // TODO inside overlay <FloatingFocusManager context={context} preventTabbing>
30
+ <FloatingOverlay lockScroll={!touch}>{contents}</FloatingOverlay>
29
31
  ) : (
30
32
  <div style={{ display: 'none' }}>{contents}</div>
31
33
  )}