@tamagui/select 1.0.1-beta.103 → 1.0.1-beta.106

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.
@@ -48,9 +48,11 @@ import {
48
48
  } from "@floating-ui/react-dom-interactions";
49
49
  import { usePrevious } from "@radix-ui/react-use-previous";
50
50
  import { useComposedRefs } from "@tamagui/compose-refs";
51
+ import { isWeb, useMedia } from "@tamagui/core";
51
52
  import {
52
53
  Theme,
53
54
  styled,
55
+ useGet,
54
56
  useIsomorphicLayoutEffect,
55
57
  useThemeName,
56
58
  withStaticProperties
@@ -58,12 +60,20 @@ import {
58
60
  import { useId } from "@tamagui/core";
59
61
  import { createContextScope } from "@tamagui/create-context";
60
62
  import { ListItem } from "@tamagui/list-item";
63
+ import { PortalHost, PortalItem } from "@tamagui/portal";
61
64
  import { Separator } from "@tamagui/separator";
65
+ import { Sheet, SheetController } from "@tamagui/sheet";
62
66
  import { ThemeableStack, XStack, YStack } from "@tamagui/stacks";
63
67
  import { Paragraph } from "@tamagui/text";
64
68
  import { useControllableState } from "@tamagui/use-controllable-state";
65
69
  import * as React from "react";
66
70
  import * as ReactDOM from "react-dom";
71
+ const SELECT_NAME = "Select";
72
+ const WINDOW_PADDING = 8;
73
+ const SCROLL_ARROW_VELOCITY = 8;
74
+ const SCROLL_ARROW_THRESHOLD = 8;
75
+ const MIN_HEIGHT = 80;
76
+ const FALLBACK_THRESHOLD = 16;
67
77
  const userAgent = typeof navigator !== "undefined" && navigator.userAgent || "";
68
78
  const isFirefox = userAgent.toLowerCase().includes("firefox");
69
79
  if (isFirefox) {
@@ -72,12 +82,6 @@ if (isFirefox) {
72
82
  function getVisualOffsetTop() {
73
83
  return !/^((?!chrome|android).)*safari/i.test(userAgent) ? (visualViewport == null ? void 0 : visualViewport.offsetTop) ?? 0 : 0;
74
84
  }
75
- const SELECT_NAME = "Select";
76
- const WINDOW_PADDING = 8;
77
- const SCROLL_ARROW_VELOCITY = 8;
78
- const SCROLL_ARROW_THRESHOLD = 8;
79
- const MIN_HEIGHT = 80;
80
- const FALLBACK_THRESHOLD = 16;
81
85
  const [createSelectContext, createSelectScope] = createContextScope(SELECT_NAME);
82
86
  const [SelectProvider, useSelectContext] = createSelectContext(SELECT_NAME);
83
87
  const TRIGGER_NAME = "SelectTrigger";
@@ -92,6 +96,7 @@ const SelectTrigger = React.forwardRef((props, forwardedRef) => {
92
96
  "aria-labelledby"
93
97
  ]);
94
98
  const context = useSelectContext(TRIGGER_NAME, __scopeSelect);
99
+ const labelledBy = ariaLabelledby;
95
100
  return /* @__PURE__ */ React.createElement(ListItem, __spreadValues(__spreadProps(__spreadValues({
96
101
  backgrounded: true,
97
102
  radiused: true,
@@ -104,11 +109,17 @@ const SelectTrigger = React.forwardRef((props, forwardedRef) => {
104
109
  size: context.size,
105
110
  "aria-expanded": context.open,
106
111
  "aria-autocomplete": "none",
112
+ "aria-labelledby": labelledBy,
113
+ dir: context.dir,
107
114
  disabled,
108
115
  "data-disabled": disabled ? "" : void 0
109
116
  }, triggerProps), {
110
117
  ref: forwardedRef
111
- }), context.interactions.getReferenceProps()));
118
+ }), context.interactions ? context.interactions.getReferenceProps() : {
119
+ onPress() {
120
+ context.setOpen(!context.open);
121
+ }
122
+ }));
112
123
  });
113
124
  SelectTrigger.displayName = TRIGGER_NAME;
114
125
  const VALUE_NAME = "SelectValue";
@@ -140,9 +151,13 @@ const CONTENT_NAME = "SelectContent";
140
151
  const SelectContent = ({ children, __scopeSelect }) => {
141
152
  const context = useSelectContext(CONTENT_NAME, __scopeSelect);
142
153
  const themeName = useThemeName();
154
+ const showSheet = useShowSelectSheet(context);
143
155
  const contents = /* @__PURE__ */ React.createElement(Theme, {
144
156
  name: themeName
145
157
  }, children);
158
+ if (showSheet && context.open) {
159
+ return contents;
160
+ }
146
161
  return /* @__PURE__ */ React.createElement(FloatingPortal, null, context.open ? /* @__PURE__ */ React.createElement(FloatingOverlay, {
147
162
  lockScroll: true
148
163
  }, contents) : /* @__PURE__ */ React.createElement("div", {
@@ -171,9 +186,19 @@ const SelectViewportFrame = styled(ThemeableStack, {
171
186
  }
172
187
  });
173
188
  const SelectViewport = React.forwardRef((props, forwardedRef) => {
189
+ var _b;
174
190
  const _a = props, { __scopeSelect, children } = _a, viewportProps = __objRest(_a, ["__scopeSelect", "children"]);
175
191
  const context = useSelectContext(VIEWPORT_NAME, __scopeSelect);
176
- const _b = context.interactions.getFloatingProps(), { style } = _b, floatingProps = __objRest(_b, ["style"]);
192
+ const _c = ((_b = context.interactions) == null ? void 0 : _b.getFloatingProps()) ?? {}, { style } = _c, floatingProps = __objRest(_c, ["style"]);
193
+ const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint);
194
+ if (breakpointActive || !isWeb) {
195
+ return /* @__PURE__ */ React.createElement(PortalItem, {
196
+ hostName: `${context.scopeKey}SheetContents`
197
+ }, children);
198
+ }
199
+ if (!context.floatingContext) {
200
+ return null;
201
+ }
177
202
  delete style["scrollbarWidth"];
178
203
  delete style["listStyleType"];
179
204
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", {
@@ -227,12 +252,14 @@ const SelectItem = React.forwardRef((props, forwardedRef) => {
227
252
  } = context;
228
253
  const composedRefs = useComposedRefs(forwardedRef, (node) => {
229
254
  if (node instanceof HTMLElement) {
230
- listRef.current[index] = node;
255
+ if (listRef) {
256
+ listRef.current[index] = node;
257
+ }
231
258
  }
232
259
  });
233
260
  React.useEffect(() => {
234
261
  setValueAtIndex(index, value);
235
- }, [index]);
262
+ }, [index, setValueAtIndex, value]);
236
263
  const timeoutRef = React.useRef();
237
264
  const [allowMouseUp, setAllowMouseUp] = React.useState(false);
238
265
  function handleSelect() {
@@ -255,16 +282,18 @@ const SelectItem = React.forwardRef((props, forwardedRef) => {
255
282
  }
256
283
  }, [open, index, setActiveIndex, selectedIndex]);
257
284
  function handleKeyDown(event) {
258
- if (event.key === "Enter" || event.key === " " && !dataRef.current.typing) {
285
+ if (event.key === "Enter" || event.key === " " && !(dataRef == null ? void 0 : dataRef.current.typing)) {
259
286
  event.preventDefault();
260
287
  handleSelect();
261
288
  }
262
289
  }
263
- const selectItemProps = context.interactions.getItemProps({
290
+ const selectItemProps = context.interactions ? context.interactions.getItemProps({
264
291
  onClick: allowMouseUp ? handleSelect : void 0,
265
292
  onMouseUp: allowMouseUp ? handleSelect : void 0,
266
293
  onKeyDown: handleKeyDown
267
- });
294
+ }) : {
295
+ onPress: handleSelect
296
+ };
268
297
  return /* @__PURE__ */ React.createElement(SelectItemContextProvider, {
269
298
  scope: __scopeSelect,
270
299
  value,
@@ -295,12 +324,13 @@ const SelectItemTextFrame = styled(Paragraph, {
295
324
  selectable: false
296
325
  });
297
326
  const SelectItemText = React.forwardRef((props, forwardedRef) => {
298
- const _a = props, { __scopeSelect, className, style } = _a, itemTextProps = __objRest(_a, ["__scopeSelect", "className", "style"]);
327
+ const _a = props, { __scopeSelect, className } = _a, itemTextProps = __objRest(_a, ["__scopeSelect", "className"]);
299
328
  const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect);
300
329
  const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect);
301
330
  const ref = React.useRef(null);
302
331
  const composedRefs = useComposedRefs(forwardedRef, ref, itemContext.onItemTextChange);
303
332
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SelectItemTextFrame, __spreadProps(__spreadValues({
333
+ className,
304
334
  size: context.size,
305
335
  id: itemContext.textId
306
336
  }, itemTextProps), {
@@ -377,7 +407,7 @@ const SelectScrollDownButton = React.forwardRef((props, forwardedRef) => {
377
407
  }));
378
408
  });
379
409
  SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
380
- const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
410
+ const SelectScrollButtonImpl = React.memo(React.forwardRef((props, forwardedRef) => {
381
411
  var _c;
382
412
  const _a = props, { __scopeSelect, dir, componentName } = _a, scrollIndicatorProps = __objRest(_a, ["__scopeSelect", "dir", "componentName"]);
383
413
  const _b = useSelectContext(componentName, __scopeSelect), { floatingRef, increaseHeight, forceUpdate } = _b, context = __objRest(_b, ["floatingRef", "increaseHeight", "forceUpdate"]);
@@ -391,8 +421,10 @@ const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
391
421
  });
392
422
  const composedRefs = useComposedRefs(forwardedRef, floating);
393
423
  React.useLayoutEffect(() => {
424
+ if (!floatingRef)
425
+ return;
394
426
  reference(floatingRef.current);
395
- }, [reference, floatingRef.current]);
427
+ }, [reference, floatingRef == null ? void 0 : floatingRef.current]);
396
428
  React.useEffect(() => {
397
429
  if (!refs.reference.current || !refs.floating.current || !isVisible) {
398
430
  return;
@@ -410,13 +442,15 @@ const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
410
442
  return null;
411
443
  }
412
444
  const handleScrollArrowChange = () => {
445
+ if (!floatingRef)
446
+ return;
413
447
  const floating2 = floatingRef.current;
414
448
  const isUp = dir === "up";
415
449
  if (floating2) {
416
450
  const value = isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY;
417
451
  const multi = isUp && floating2.scrollTop <= SCROLL_ARROW_THRESHOLD * 2 || !isUp && floating2.scrollTop >= floating2.scrollHeight - floating2.clientHeight - SCROLL_ARROW_THRESHOLD * 2 ? 2 : 1;
418
452
  floating2.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY);
419
- increaseHeight(floating2, multi === 2 ? value * 2 : value);
453
+ increaseHeight == null ? void 0 : increaseHeight(floating2, multi === 2 ? value * 2 : value);
420
454
  forceUpdate();
421
455
  }
422
456
  };
@@ -429,7 +463,7 @@ const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
429
463
  position: strategy,
430
464
  left: x || 0,
431
465
  top: y || 0,
432
- width: `calc(${(((_c = floatingRef.current) == null ? void 0 : _c.offsetWidth) ?? 0) - 2}px)`,
466
+ width: `calc(${(((_c = floatingRef == null ? void 0 : floatingRef.current) == null ? void 0 : _c.offsetWidth) ?? 0) - 2}px)`,
433
467
  onPointerMove: () => {
434
468
  if (!loopingRef.current) {
435
469
  intervalRef.current = setInterval(handleScrollArrowChange, 1e3 / 60);
@@ -441,51 +475,58 @@ const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
441
475
  clearInterval(intervalRef.current);
442
476
  }
443
477
  }));
444
- });
478
+ }));
445
479
  const SelectSeparator = styled(Separator, {
446
480
  name: "SelectSeparator"
447
481
  });
448
- const Select = withStaticProperties((props) => {
482
+ const useSelectBreakpointActive = (sheetBreakpoint) => {
483
+ const media = useMedia();
484
+ return sheetBreakpoint ? media[sheetBreakpoint] : false;
485
+ };
486
+ const useShowSelectSheet = (context) => {
487
+ const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint);
488
+ return context.open === false ? false : breakpointActive;
489
+ };
490
+ const SelectSheetController = (props) => {
491
+ const context = useSelectContext("SelectSheetController", props.__scopeSelect);
492
+ const showSheet = useShowSelectSheet(context);
493
+ const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint);
494
+ const getShowSheet = useGet(showSheet);
495
+ return /* @__PURE__ */ React.createElement(SheetController, {
496
+ onChangeOpen: (val) => {
497
+ if (getShowSheet()) {
498
+ props.onChangeOpen(val);
499
+ }
500
+ },
501
+ open: context.open,
502
+ hidden: breakpointActive === false
503
+ }, props.children);
504
+ };
505
+ const SHEET_CONTENTS_NAME = "SelectSheetContents";
506
+ const SelectSheetContents = ({ __scopeSelect }) => {
507
+ const context = useSelectContext(SHEET_CONTENTS_NAME, __scopeSelect);
508
+ return /* @__PURE__ */ React.createElement(PortalHost, {
509
+ name: `${context.scopeKey}SheetContents`
510
+ });
511
+ };
512
+ SelectSheetContents.displayName = SHEET_CONTENTS_NAME;
513
+ const SelectInlineImpl = (props) => {
449
514
  const {
450
- id,
451
515
  __scopeSelect,
452
516
  children,
453
- open: openProp,
454
- defaultOpen,
455
- onOpenChange,
456
- value: valueProp,
457
- defaultValue,
458
- onValueChange,
459
- size: sizeProp,
460
- dir,
461
- name,
462
- autoComplete
517
+ open = false,
518
+ activeIndexRef,
519
+ selectedIndexRef,
520
+ listContentRef
463
521
  } = props;
464
- const [open, setOpen] = useControllableState({
465
- prop: openProp,
466
- defaultProp: defaultOpen || false,
467
- onChange: onOpenChange
468
- });
469
- const [value, setValue] = useControllableState({
470
- prop: valueProp,
471
- defaultProp: defaultValue || "",
472
- onChange: onValueChange
473
- });
474
- const [activeIndex, setActiveIndex] = React.useState(null);
475
- const selectedIndexRef = React.useRef(null);
476
- const activeIndexRef = React.useRef(null);
477
- const prevActiveIndex = usePrevious(activeIndex);
522
+ const selectContext = useSelectContext("SelectSheetImpl", __scopeSelect);
523
+ const { setActiveIndex, setOpen, setSelectedIndex, selectedIndex, activeIndex, forceUpdate } = selectContext;
478
524
  const [showArrows, setShowArrows] = React.useState(false);
479
525
  const [scrollTop, setScrollTop] = React.useState(0);
526
+ const prevActiveIndex = usePrevious(activeIndex);
480
527
  const listItemsRef = React.useRef([]);
481
- const listContentRef = React.useRef([]);
482
- const [selectedIndex, setSelectedIndex] = React.useState(Math.max(0, listContentRef.current.indexOf(value)));
483
528
  const [controlledScrolling, setControlledScrolling] = React.useState(false);
484
529
  const [middlewareType, setMiddlewareType] = React.useState("align");
485
- useIsomorphicLayoutEffect(() => {
486
- selectedIndexRef.current = selectedIndex;
487
- activeIndexRef.current = activeIndex;
488
- });
489
530
  React.useEffect(() => {
490
531
  const frame = requestAnimationFrame(() => {
491
532
  setShowArrows(open);
@@ -499,7 +540,7 @@ const Select = withStaticProperties((props) => {
499
540
  return () => {
500
541
  cancelAnimationFrame(frame);
501
542
  };
502
- }, [open]);
543
+ }, [open, setActiveIndex]);
503
544
  function getFloatingPadding(floating2) {
504
545
  var _a;
505
546
  if (!floating2) {
@@ -580,7 +621,6 @@ const Select = withStaticProperties((props) => {
580
621
  ]
581
622
  });
582
623
  const floatingRef = refs.floating;
583
- const forceUpdate = React.useReducer(() => ({}), {})[1];
584
624
  const showUpArrow = showArrows && scrollTop > SCROLL_ARROW_THRESHOLD;
585
625
  const showDownArrow = showArrows && floatingRef.current && scrollTop < floatingRef.current.scrollHeight - floatingRef.current.clientHeight - SCROLL_ARROW_THRESHOLD;
586
626
  const interactions = useInteractions([
@@ -626,7 +666,7 @@ const Select = withStaticProperties((props) => {
626
666
  }
627
667
  return currentTop - nextTop;
628
668
  }
629
- }, [middlewareType]);
669
+ }, [middlewareType, selectedIndexRef]);
630
670
  const touchPageYRef = React.useRef(null);
631
671
  const handleWheel = React.useCallback((event) => {
632
672
  var _a;
@@ -659,7 +699,7 @@ const Select = withStaticProperties((props) => {
659
699
  setScrollTop(currentTarget.scrollTop);
660
700
  forceUpdate();
661
701
  }
662
- }, [increaseHeight, forceUpdate]);
702
+ }, [forceUpdate, increaseHeight]);
663
703
  React.useEffect(() => {
664
704
  function onTouchEnd() {
665
705
  touchPageYRef.current = null;
@@ -765,20 +805,13 @@ const Select = withStaticProperties((props) => {
765
805
  });
766
806
  return () => cancelAnimationFrame(frame);
767
807
  }, [open]);
768
- const [valueNode, setValueNode] = React.useState(null);
769
- const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false);
770
- return /* @__PURE__ */ React.createElement(SelectProvider, {
771
- size: sizeProp,
772
- scope: __scopeSelect,
808
+ return /* @__PURE__ */ React.createElement(SelectProvider, __spreadProps(__spreadValues({
809
+ scope: __scopeSelect
810
+ }, selectContext), {
773
811
  increaseHeight,
774
- forceUpdate,
775
812
  floatingRef,
776
- valueNode,
777
- onValueNodeChange: setValueNode,
778
- valueNodeHasChildren,
779
- onValueNodeHasChildrenChange: setValueNodeHasChildren,
780
- setValueAtIndex: (index, value2) => {
781
- listContentRef.current[index] = value2;
813
+ setValueAtIndex: (index, value) => {
814
+ listContentRef.current[index] = value;
782
815
  },
783
816
  interactions: __spreadProps(__spreadValues({}, interactions), {
784
817
  getReferenceProps() {
@@ -828,7 +861,65 @@ const Select = withStaticProperties((props) => {
828
861
  canScrollUp: !!showUpArrow,
829
862
  controlledScrolling: true,
830
863
  dataRef: context.dataRef,
831
- listRef: listItemsRef,
864
+ listRef: listItemsRef
865
+ }), children);
866
+ };
867
+ const SelectSheetImpl = (props) => {
868
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
869
+ };
870
+ const Select = withStaticProperties((props) => {
871
+ const {
872
+ __scopeSelect,
873
+ children,
874
+ open: openProp,
875
+ defaultOpen,
876
+ onOpenChange,
877
+ value: valueProp,
878
+ defaultValue,
879
+ onValueChange,
880
+ size: sizeProp,
881
+ sheetBreakpoint = false,
882
+ dir
883
+ } = props;
884
+ const isSheet = useSelectBreakpointActive(sheetBreakpoint);
885
+ const SelectImpl = isSheet ? SelectSheetImpl : SelectInlineImpl;
886
+ const forceUpdate = React.useReducer(() => ({}), {})[1];
887
+ const [open, setOpen] = useControllableState({
888
+ prop: openProp,
889
+ defaultProp: defaultOpen || false,
890
+ onChange: onOpenChange
891
+ });
892
+ const [value, setValue] = useControllableState({
893
+ prop: valueProp,
894
+ defaultProp: defaultValue || "",
895
+ onChange: onValueChange
896
+ });
897
+ const [activeIndex, setActiveIndex] = React.useState(null);
898
+ const selectedIndexRef = React.useRef(null);
899
+ const activeIndexRef = React.useRef(null);
900
+ const listContentRef = React.useRef([]);
901
+ const [selectedIndex, setSelectedIndex] = React.useState(Math.max(0, listContentRef.current.indexOf(value)));
902
+ const [valueNode, setValueNode] = React.useState(null);
903
+ const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false);
904
+ useIsomorphicLayoutEffect(() => {
905
+ selectedIndexRef.current = selectedIndex;
906
+ activeIndexRef.current = activeIndex;
907
+ });
908
+ return /* @__PURE__ */ React.createElement(SelectProvider, {
909
+ dir,
910
+ size: sizeProp,
911
+ forceUpdate,
912
+ valueNode,
913
+ onValueNodeChange: setValueNode,
914
+ onValueNodeHasChildrenChange: setValueNodeHasChildren,
915
+ valueNodeHasChildren,
916
+ scopeKey: __scopeSelect ? Object.keys(__scopeSelect)[0] : "",
917
+ sheetBreakpoint,
918
+ scope: __scopeSelect,
919
+ setValueAtIndex: (index, value2) => {
920
+ listContentRef.current[index] = value2;
921
+ },
922
+ activeIndex,
832
923
  onChange: setValue,
833
924
  selectedIndex,
834
925
  setActiveIndex,
@@ -836,7 +927,17 @@ const Select = withStaticProperties((props) => {
836
927
  setSelectedIndex,
837
928
  value,
838
929
  open
839
- }, children);
930
+ }, /* @__PURE__ */ React.createElement(SelectSheetController, {
931
+ onChangeOpen: setOpen,
932
+ __scopeSelect
933
+ }, /* @__PURE__ */ React.createElement(SelectImpl, __spreadProps(__spreadValues({
934
+ activeIndexRef,
935
+ listContentRef,
936
+ selectedIndexRef
937
+ }, props), {
938
+ open,
939
+ value
940
+ }), children)));
840
941
  }, {
841
942
  Content: SelectContent,
842
943
  Group: SelectGroup,
@@ -849,7 +950,9 @@ const Select = withStaticProperties((props) => {
849
950
  ScrollUpButton: SelectScrollUpButton,
850
951
  Trigger: SelectTrigger,
851
952
  Value: SelectValue,
852
- Viewport: SelectViewport
953
+ Viewport: SelectViewport,
954
+ SheetContents: SelectSheetContents,
955
+ Sheet
853
956
  });
854
957
  Select.displayName = SELECT_NAME;
855
958
  export {
@@ -859,6 +962,7 @@ export {
859
962
  SelectItem,
860
963
  SelectItemTextFrame,
861
964
  SelectSeparator,
965
+ SelectSheetContents,
862
966
  SelectTrigger,
863
967
  SelectViewport,
864
968
  SelectViewportFrame,