@bsky.app/sift 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @bsky.app/sift
2
2
 
3
+ ## 0.2.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [`9551019`](https://github.com/bluesky-social/toolbox/commit/955101995d44fa01b952632fd61b8baa3b6d99c7) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Handle recently unmounted popovers in Sift computeStyles
8
+
9
+ ## 0.2.9
10
+
11
+ ### Patch Changes
12
+
13
+ - [`e61b748`](https://github.com/bluesky-social/toolbox/commit/e61b748512e21a9c74f861768e682cb2284f9940) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add index, isFirst, isLast to Sift
14
+
3
15
  ## 0.2.8
4
16
 
5
17
  ### Patch Changes
package/build/Sift.d.ts CHANGED
@@ -7,6 +7,9 @@ export declare function Sift<Item extends {
7
7
  sift: UseSiftReturn;
8
8
  data: Item[];
9
9
  render: (props: {
10
+ index: number;
11
+ isFirst: boolean;
12
+ isLast: boolean;
10
13
  active: boolean;
11
14
  props: {
12
15
  role: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Sift.d.ts","sourceRoot":"","sources":["../src/Sift.tsx"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAC,KAAK,aAAa,EAAC,MAAM,WAAW,CAAA;AAG5C,cAAc,WAAW,CAAA;AAIzB,wBAAgB,IAAI,CAAC,IAAI,SAAS;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,EAAE,EAC/C,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,aAAa,CAAA;IACnB,IAAI,EAAE,IAAI,EAAE,CAAA;IACZ,MAAM,EAAE,CAAC,KAAK,EAAE;QACd,MAAM,EAAE,OAAO,CAAA;QACf,KAAK,EAAE;YACL,IAAI,EAAE,MAAM,CAAA;YACZ,eAAe,EAAE,OAAO,CAAA;YACxB,OAAO,EAAE,MAAM,IAAI,CAAA;SACpB,CAAA;QACD,IAAI,EAAE,IAAI,CAAA;KACX,KAAK,KAAK,CAAC,YAAY,CAAA;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC/B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,2CAkFA;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EACF,SAAS,CAAC,SAAS,CAAC,GACpB,CAAC,CAAC,KAAK,EAAE;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,KAAK,SAAS,CAAC,SAAS,CAAC,CAAC,GACvE,SAAS,CAAA;CACd,2CASA"}
1
+ {"version":3,"file":"Sift.d.ts","sourceRoot":"","sources":["../src/Sift.tsx"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAC,KAAK,aAAa,EAAC,MAAM,WAAW,CAAA;AAG5C,cAAc,WAAW,CAAA;AAIzB,wBAAgB,IAAI,CAAC,IAAI,SAAS;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,EAAE,EAC/C,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,aAAa,CAAA;IACnB,IAAI,EAAE,IAAI,EAAE,CAAA;IACZ,MAAM,EAAE,CAAC,KAAK,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,OAAO,CAAA;QACf,MAAM,EAAE,OAAO,CAAA;QACf,KAAK,EAAE;YACL,IAAI,EAAE,MAAM,CAAA;YACZ,eAAe,EAAE,OAAO,CAAA;YACxB,OAAO,EAAE,MAAM,IAAI,CAAA;SACpB,CAAA;QACD,IAAI,EAAE,IAAI,CAAA;KACX,KAAK,KAAK,CAAC,YAAY,CAAA;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC/B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,2CAqFA;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EACF,SAAS,CAAC,SAAS,CAAC,GACpB,CAAC,CAAC,KAAK,EAAE;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,KAAK,SAAS,CAAC,SAAS,CAAC,CAAC,GACvE,SAAS,CAAA;CACd,2CASA"}
package/build/Sift.js CHANGED
@@ -57,14 +57,17 @@ export function Sift({ sift, data, render, style, onSelect, onDismiss, inverted,
57
57
  // @ts-ignore web only
58
58
  onMouseDown: e => {
59
59
  e.preventDefault();
60
- }, children: _jsx(FlatList, { data: data, inverted: inverted, keyExtractor: keyExtractor, extraData: activeIndex, renderItem: useCallback((items) => renderRef.current({
61
- active: items.index === activeIndexRef.current,
60
+ }, children: _jsx(FlatList, { data: data, inverted: inverted, keyExtractor: keyExtractor, extraData: activeIndex, renderItem: useCallback((item) => renderRef.current({
61
+ index: item.index,
62
+ isFirst: item.index === 0,
63
+ isLast: item.index === data.length - 1,
64
+ active: item.index === activeIndexRef.current,
62
65
  props: {
63
66
  role: 'option',
64
- 'aria-selected': items.index === activeIndexRef.current,
65
- onPress: () => onSelectRef.current?.(items.item),
67
+ 'aria-selected': item.index === activeIndexRef.current,
68
+ onPress: () => onSelectRef.current?.(item.item),
66
69
  },
67
- item: items.item,
70
+ item: item.item,
68
71
  }), []), keyboardShouldPersistTaps: "handled" }) }));
69
72
  }
70
73
  /**
package/build/Sift.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Sift.js","sourceRoot":"","sources":["../src/Sift.tsx"],"names":[],"mappings":"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","sourcesContent":["import {useCallback, useEffect, useRef, useState} from 'react'\nimport {\n View,\n FlatList,\n Pressable,\n type StyleProp,\n type ViewStyle,\n type PressableProps,\n type Role,\n} from 'react-native'\nimport {type UseSiftReturn} from './useSift'\nimport {useKeyboardHandling} from './useKeyboardHandling'\n\nexport * from './useSift'\n\nconst keyExtractor = (item: {key: string}) => item.key\n\nexport function Sift<Item extends {key: string}>({\n sift,\n data,\n render,\n style,\n onSelect,\n onDismiss,\n inverted,\n}: {\n sift: UseSiftReturn\n data: Item[]\n render: (props: {\n active: boolean\n props: {\n role: string\n 'aria-selected': boolean\n onPress: () => void\n }\n item: Item\n }) => React.ReactElement\n style?: StyleProp<ViewStyle>\n onSelect?: (item: Item) => void\n onDismiss?: () => void\n inverted?: boolean\n}) {\n const [activeIndex, setActiveIndex] = useState(0)\n const activeIndexRef = useRef(activeIndex)\n activeIndexRef.current = activeIndex\n const dataLenRef = useRef(data.length)\n dataLenRef.current = data.length\n const updateRef = useRef(sift.updatePosition)\n updateRef.current = sift.updatePosition\n const renderRef = useRef(render)\n renderRef.current = render\n const onSelectRef = useRef(onSelect)\n onSelectRef.current = onSelect\n\n useEffect(() => {\n if (activeIndexRef.current !== 0) setActiveIndex(0)\n updateRef.current()\n }, [data.length])\n\n const next = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(i => (i + 1) % dataLenRef.current)\n }, [])\n const prev = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(i => (i - 1 + dataLenRef.current) % dataLenRef.current)\n }, [])\n const first = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(0)\n }, [])\n const last = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(dataLenRef.current - 1)\n }, [])\n\n useKeyboardHandling({\n sift,\n onArrowDown: inverted ? prev : next,\n onArrowUp: inverted ? next : prev,\n onHome: inverted ? last : first,\n onEnd: inverted ? first : last,\n onSelect: () => {\n onSelect?.(data[activeIndexRef.current])\n },\n onDismiss,\n })\n\n const hasStyles = sift.popoverStyles.top != null\n\n return (\n <View\n collapsable={false}\n ref={sift.refs.setPopover}\n role={'listbox' as any}\n id={sift.id}\n style={[style, sift.popoverStyles, !hasStyles && {opacity: 0}]}\n // @ts-ignore web only\n onMouseDown={e => {\n e.preventDefault()\n }}>\n <FlatList\n data={data}\n inverted={inverted}\n keyExtractor={keyExtractor}\n extraData={activeIndex}\n renderItem={useCallback(\n (items: {item: Item; index: number}) =>\n renderRef.current({\n active: items.index === activeIndexRef.current,\n props: {\n role: 'option',\n 'aria-selected': items.index === activeIndexRef.current,\n onPress: () => onSelectRef.current?.(items.item),\n },\n item: items.item,\n }),\n [],\n )}\n keyboardShouldPersistTaps=\"handled\"\n />\n </View>\n )\n}\n\n/**\n * A Pressable wrapper for items rendered in Sift. It applies the necessary\n * accessibility props for each item.\n */\nexport function SiftItem({\n children,\n role,\n style,\n ...props\n}: Omit<PressableProps, 'role' | 'style'> & {\n role?: string\n style?:\n | StyleProp<ViewStyle>\n | ((state: {pressed: boolean; hovered: boolean}) => StyleProp<ViewStyle>)\n | undefined\n}) {\n return (\n <Pressable\n role={role as Role}\n style={style as PressableProps['style']}\n {...props}>\n {children}\n </Pressable>\n )\n}\n"]}
1
+ {"version":3,"file":"Sift.js","sourceRoot":"","sources":["../src/Sift.tsx"],"names":[],"mappings":"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","sourcesContent":["import {useCallback, useEffect, useRef, useState} from 'react'\nimport {\n View,\n FlatList,\n Pressable,\n type StyleProp,\n type ViewStyle,\n type PressableProps,\n type Role,\n} from 'react-native'\nimport {type UseSiftReturn} from './useSift'\nimport {useKeyboardHandling} from './useKeyboardHandling'\n\nexport * from './useSift'\n\nconst keyExtractor = (item: {key: string}) => item.key\n\nexport function Sift<Item extends {key: string}>({\n sift,\n data,\n render,\n style,\n onSelect,\n onDismiss,\n inverted,\n}: {\n sift: UseSiftReturn\n data: Item[]\n render: (props: {\n index: number\n isFirst: boolean\n isLast: boolean\n active: boolean\n props: {\n role: string\n 'aria-selected': boolean\n onPress: () => void\n }\n item: Item\n }) => React.ReactElement\n style?: StyleProp<ViewStyle>\n onSelect?: (item: Item) => void\n onDismiss?: () => void\n inverted?: boolean\n}) {\n const [activeIndex, setActiveIndex] = useState(0)\n const activeIndexRef = useRef(activeIndex)\n activeIndexRef.current = activeIndex\n const dataLenRef = useRef(data.length)\n dataLenRef.current = data.length\n const updateRef = useRef(sift.updatePosition)\n updateRef.current = sift.updatePosition\n const renderRef = useRef(render)\n renderRef.current = render\n const onSelectRef = useRef(onSelect)\n onSelectRef.current = onSelect\n\n useEffect(() => {\n if (activeIndexRef.current !== 0) setActiveIndex(0)\n updateRef.current()\n }, [data.length])\n\n const next = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(i => (i + 1) % dataLenRef.current)\n }, [])\n const prev = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(i => (i - 1 + dataLenRef.current) % dataLenRef.current)\n }, [])\n const first = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(0)\n }, [])\n const last = useCallback(() => {\n if (dataLenRef.current === 0) return\n setActiveIndex(dataLenRef.current - 1)\n }, [])\n\n useKeyboardHandling({\n sift,\n onArrowDown: inverted ? prev : next,\n onArrowUp: inverted ? next : prev,\n onHome: inverted ? last : first,\n onEnd: inverted ? first : last,\n onSelect: () => {\n onSelect?.(data[activeIndexRef.current])\n },\n onDismiss,\n })\n\n const hasStyles = sift.popoverStyles.top != null\n\n return (\n <View\n collapsable={false}\n ref={sift.refs.setPopover}\n role={'listbox' as any}\n id={sift.id}\n style={[style, sift.popoverStyles, !hasStyles && {opacity: 0}]}\n // @ts-ignore web only\n onMouseDown={e => {\n e.preventDefault()\n }}>\n <FlatList\n data={data}\n inverted={inverted}\n keyExtractor={keyExtractor}\n extraData={activeIndex}\n renderItem={useCallback(\n (item: {item: Item; index: number}) =>\n renderRef.current({\n index: item.index,\n isFirst: item.index === 0,\n isLast: item.index === data.length - 1,\n active: item.index === activeIndexRef.current,\n props: {\n role: 'option',\n 'aria-selected': item.index === activeIndexRef.current,\n onPress: () => onSelectRef.current?.(item.item),\n },\n item: item.item,\n }),\n [],\n )}\n keyboardShouldPersistTaps=\"handled\"\n />\n </View>\n )\n}\n\n/**\n * A Pressable wrapper for items rendered in Sift. It applies the necessary\n * accessibility props for each item.\n */\nexport function SiftItem({\n children,\n role,\n style,\n ...props\n}: Omit<PressableProps, 'role' | 'style'> & {\n role?: string\n style?:\n | StyleProp<ViewStyle>\n | ((state: {pressed: boolean; hovered: boolean}) => StyleProp<ViewStyle>)\n | undefined\n}) {\n return (\n <Pressable\n role={role as Role}\n style={style as PressableProps['style']}\n {...props}>\n {children}\n </Pressable>\n )\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"computeStyles.d.ts","sourceRoot":"","sources":["../src/computeStyles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,cAAc,CAAA;AACvD,OAAO,EAAC,KAAK,SAAS,EAAC,MAAM,WAAW,CAAA;AAcxC,wBAAsB,aAAa,CACjC,EACE,MAAM,EACN,KAAK,EACL,OAAO,GACR,EAAE;IACD,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,GAAG,CAAA;IACV,OAAO,EAAE,GAAG,GAAG,IAAI,CAAA;CACpB,EACD,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,MAAM,CAAC,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAA;CACvC,GACA,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAwD3B"}
1
+ {"version":3,"file":"computeStyles.d.ts","sourceRoot":"","sources":["../src/computeStyles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,cAAc,CAAA;AACvD,OAAO,EAAC,KAAK,SAAS,EAAC,MAAM,WAAW,CAAA;AAkBxC,wBAAsB,aAAa,CACjC,EACE,MAAM,EACN,KAAK,EACL,OAAO,GACR,EAAE;IACD,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,GAAG,CAAA;IACV,OAAO,EAAE,GAAG,GAAG,IAAI,CAAA;CACpB,EACD,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,MAAM,CAAC,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAA;CACvC,GACA,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAwD3B"}
@@ -1,9 +1,14 @@
1
1
  import { Dimensions } from 'react-native';
2
2
  function measureInWindow(node) {
3
3
  return new Promise(resolve => {
4
- node.measureInWindow((x, y, width, height) => {
5
- resolve({ x, y, width, height });
6
- });
4
+ try {
5
+ node.measureInWindow((x, y, width, height) => {
6
+ resolve({ x, y, width, height });
7
+ });
8
+ }
9
+ catch {
10
+ resolve({ x: 0, y: 0, width: 0, height: 0 });
11
+ }
7
12
  });
8
13
  }
9
14
  export async function computeStyles({ anchor, input, popover, }, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"computeStyles.js","sourceRoot":"","sources":["../src/computeStyles.ts"],"names":[],"mappings":"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","sourcesContent":["import {Dimensions, type ViewStyle} from 'react-native'\nimport {type Placement} from './useSift'\n\nfunction measureInWindow(\n node: any,\n): Promise<{x: number; y: number; width: number; height: number}> {\n return new Promise(resolve => {\n node.measureInWindow(\n (x: number, y: number, width: number, height: number) => {\n resolve({x, y, width, height})\n },\n )\n })\n}\n\nexport async function computeStyles(\n {\n anchor,\n input,\n popover,\n }: {\n anchor: any\n input: any\n popover: any | null\n },\n options: {\n offset: number\n placement: Placement\n dynamicWidth?: boolean\n insets?: {top: number; bottom: number}\n },\n): Promise<ViewStyle | null> {\n const anchorRect = await measureInWindow(anchor)\n const inputRect = await measureInWindow(input)\n const popoverRect = popover ? await measureInWindow(popover) : null\n\n // If any measurement failed (view not in hierarchy yet), return null\n // so the caller keeps the previous styles.\n if (!anchorRect.width || !inputRect.width) return null\n if (popoverRect && !popoverRect.width && !popoverRect.height) return null\n\n const popoverWidth = popoverRect?.width ?? 0\n const [side, align] = options.placement.split('-') as [\n string,\n string | undefined,\n ]\n\n const insetTop = options.insets?.top ?? 0\n const insetBottom = options.insets?.bottom ?? 0\n\n let top: number | 'auto' = 'auto'\n let bottom: number | 'auto' = 'auto'\n let maxHeight: number | undefined\n if (side === 'top') {\n bottom = Dimensions.get('window').height - anchorRect.y + options.offset\n maxHeight = anchorRect.y - options.offset - insetTop\n } else {\n top = anchorRect.y + anchorRect.height + options.offset\n maxHeight =\n Dimensions.get('window').height -\n insetBottom -\n (anchorRect.y + anchorRect.height + options.offset)\n }\n\n let left: number\n if (align === 'start') {\n left = anchorRect.x\n } else if (align === 'end') {\n left = anchorRect.x + anchorRect.width - popoverWidth\n } else {\n left = anchorRect.x + (anchorRect.width - popoverWidth) / 2\n }\n\n let maxWidth: number | undefined\n if (options.dynamicWidth === false) {\n left = anchorRect.x\n maxWidth = anchorRect.width\n }\n\n return {\n position: 'absolute',\n top,\n bottom,\n left,\n maxWidth,\n maxHeight,\n }\n}\n"]}
1
+ {"version":3,"file":"computeStyles.js","sourceRoot":"","sources":["../src/computeStyles.ts"],"names":[],"mappings":"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","sourcesContent":["import {Dimensions, type ViewStyle} from 'react-native'\nimport {type Placement} from './useSift'\n\nfunction measureInWindow(\n node: any,\n): Promise<{x: number; y: number; width: number; height: number}> {\n return new Promise(resolve => {\n try {\n node.measureInWindow(\n (x: number, y: number, width: number, height: number) => {\n resolve({x, y, width, height})\n },\n )\n } catch {\n resolve({x: 0, y: 0, width: 0, height: 0})\n }\n })\n}\n\nexport async function computeStyles(\n {\n anchor,\n input,\n popover,\n }: {\n anchor: any\n input: any\n popover: any | null\n },\n options: {\n offset: number\n placement: Placement\n dynamicWidth?: boolean\n insets?: {top: number; bottom: number}\n },\n): Promise<ViewStyle | null> {\n const anchorRect = await measureInWindow(anchor)\n const inputRect = await measureInWindow(input)\n const popoverRect = popover ? await measureInWindow(popover) : null\n\n // If any measurement failed (view not in hierarchy yet), return null\n // so the caller keeps the previous styles.\n if (!anchorRect.width || !inputRect.width) return null\n if (popoverRect && !popoverRect.width && !popoverRect.height) return null\n\n const popoverWidth = popoverRect?.width ?? 0\n const [side, align] = options.placement.split('-') as [\n string,\n string | undefined,\n ]\n\n const insetTop = options.insets?.top ?? 0\n const insetBottom = options.insets?.bottom ?? 0\n\n let top: number | 'auto' = 'auto'\n let bottom: number | 'auto' = 'auto'\n let maxHeight: number | undefined\n if (side === 'top') {\n bottom = Dimensions.get('window').height - anchorRect.y + options.offset\n maxHeight = anchorRect.y - options.offset - insetTop\n } else {\n top = anchorRect.y + anchorRect.height + options.offset\n maxHeight =\n Dimensions.get('window').height -\n insetBottom -\n (anchorRect.y + anchorRect.height + options.offset)\n }\n\n let left: number\n if (align === 'start') {\n left = anchorRect.x\n } else if (align === 'end') {\n left = anchorRect.x + anchorRect.width - popoverWidth\n } else {\n left = anchorRect.x + (anchorRect.width - popoverWidth) / 2\n }\n\n let maxWidth: number | undefined\n if (options.dynamicWidth === false) {\n left = anchorRect.x\n maxWidth = anchorRect.width\n }\n\n return {\n position: 'absolute',\n top,\n bottom,\n left,\n maxWidth,\n maxHeight,\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsky.app/sift",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "license": "MIT",
5
5
  "description": "A little React Native library for building autocompletes.",
6
6
  "repository": "https://github.com/bluesky-social/toolbox",
package/src/Sift.tsx CHANGED
@@ -27,6 +27,9 @@ export function Sift<Item extends {key: string}>({
27
27
  sift: UseSiftReturn
28
28
  data: Item[]
29
29
  render: (props: {
30
+ index: number
31
+ isFirst: boolean
32
+ isLast: boolean
30
33
  active: boolean
31
34
  props: {
32
35
  role: string
@@ -105,15 +108,18 @@ export function Sift<Item extends {key: string}>({
105
108
  keyExtractor={keyExtractor}
106
109
  extraData={activeIndex}
107
110
  renderItem={useCallback(
108
- (items: {item: Item; index: number}) =>
111
+ (item: {item: Item; index: number}) =>
109
112
  renderRef.current({
110
- active: items.index === activeIndexRef.current,
113
+ index: item.index,
114
+ isFirst: item.index === 0,
115
+ isLast: item.index === data.length - 1,
116
+ active: item.index === activeIndexRef.current,
111
117
  props: {
112
118
  role: 'option',
113
- 'aria-selected': items.index === activeIndexRef.current,
114
- onPress: () => onSelectRef.current?.(items.item),
119
+ 'aria-selected': item.index === activeIndexRef.current,
120
+ onPress: () => onSelectRef.current?.(item.item),
115
121
  },
116
- item: items.item,
122
+ item: item.item,
117
123
  }),
118
124
  [],
119
125
  )}
@@ -5,11 +5,15 @@ function measureInWindow(
5
5
  node: any,
6
6
  ): Promise<{x: number; y: number; width: number; height: number}> {
7
7
  return new Promise(resolve => {
8
- node.measureInWindow(
9
- (x: number, y: number, width: number, height: number) => {
10
- resolve({x, y, width, height})
11
- },
12
- )
8
+ try {
9
+ node.measureInWindow(
10
+ (x: number, y: number, width: number, height: number) => {
11
+ resolve({x, y, width, height})
12
+ },
13
+ )
14
+ } catch {
15
+ resolve({x: 0, y: 0, width: 0, height: 0})
16
+ }
13
17
  })
14
18
  }
15
19