Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cope-settings-sync 337 lines 8.6 kB view raw
1import {createContext, forwardRef, Fragment, useContext, useMemo} from 'react' 2import {View} from 'react-native' 3import {Select as RadixSelect} from 'radix-ui' 4 5import {useA11y} from '#/state/a11y' 6import {atoms as a, flatten, useTheme, web} from '#/alf' 7import {useInteractionState} from '#/components/hooks/useInteractionState' 8import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' 9import { 10 ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon, 11 ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon, 12} from '#/components/icons/Chevron' 13import {Text} from '#/components/Typography' 14import { 15 type ContentProps, 16 type IconProps, 17 type ItemIndicatorProps, 18 type ItemProps, 19 type ItemTextProps, 20 type RadixPassThroughTriggerProps, 21 type RootProps, 22 type TriggerProps, 23 type ValueProps, 24} from './types' 25 26const SelectedValueContext = createContext<string | undefined | null>(null) 27SelectedValueContext.displayName = 'SelectSelectedValueContext' 28 29export function Root(props: RootProps) { 30 return ( 31 <SelectedValueContext.Provider value={props.value}> 32 <RadixSelect.Root {...props} /> 33 </SelectedValueContext.Provider> 34 ) 35} 36 37const RadixTriggerPassThrough = forwardRef( 38 ( 39 props: { 40 children: ( 41 props: RadixPassThroughTriggerProps & { 42 ref: React.Ref<any> 43 }, 44 ) => React.ReactNode 45 }, 46 ref, 47 ) => { 48 // @ts-expect-error Radix provides no types of this stuff 49 50 return props.children?.({...props, ref}) 51 }, 52) 53RadixTriggerPassThrough.displayName = 'RadixTriggerPassThrough' 54 55export function Trigger({children, label}: TriggerProps) { 56 const t = useTheme() 57 const { 58 state: hovered, 59 onIn: onMouseEnter, 60 onOut: onMouseLeave, 61 } = useInteractionState() 62 const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() 63 64 if (typeof children === 'function') { 65 return ( 66 <RadixSelect.Trigger asChild> 67 <RadixTriggerPassThrough> 68 {props => 69 children({ 70 IS_NATIVE: false, 71 state: { 72 hovered, 73 focused, 74 pressed: false, 75 }, 76 props: { 77 ...props, 78 onPress: props.onClick, 79 onFocus: onFocus, 80 onBlur: onBlur, 81 onMouseEnter, 82 onMouseLeave, 83 accessibilityLabel: label, 84 }, 85 }) 86 } 87 </RadixTriggerPassThrough> 88 </RadixSelect.Trigger> 89 ) 90 } else { 91 return ( 92 <RadixSelect.Trigger 93 onFocus={onFocus} 94 onBlur={onBlur} 95 onMouseEnter={onMouseEnter} 96 onMouseLeave={onMouseLeave} 97 style={flatten([ 98 a.flex, 99 a.relative, 100 t.atoms.bg_contrast_50, 101 a.align_center, 102 a.gap_sm, 103 a.justify_between, 104 a.py_sm, 105 a.px_md, 106 a.pointer, 107 { 108 borderRadius: 10, 109 maxWidth: 400, 110 outline: 0, 111 borderWidth: 1, 112 borderStyle: 'solid', 113 borderColor: focused 114 ? t.palette.primary_500 115 : t.palette.contrast_50, 116 }, 117 ])}> 118 {children} 119 </RadixSelect.Trigger> 120 ) 121 } 122} 123 124export function ValueText({ 125 children, 126 webOverrideValue, 127 style, 128 ...props 129}: ValueProps) { 130 let content 131 132 if (webOverrideValue && children) { 133 content = children(webOverrideValue) 134 } 135 136 return ( 137 <Text style={style}> 138 <RadixSelect.Value {...props}>{content}</RadixSelect.Value> 139 </Text> 140 ) 141} 142 143export function Icon({style}: IconProps) { 144 const t = useTheme() 145 return ( 146 <RadixSelect.Icon> 147 <ChevronDownIcon style={[t.atoms.text, style]} size="xs" /> 148 </RadixSelect.Icon> 149 ) 150} 151 152export function Content<T>({ 153 items, 154 renderItem, 155 valueExtractor = defaultItemValueExtractor, 156}: ContentProps<T>) { 157 const t = useTheme() 158 const selectedValue = useContext(SelectedValueContext) 159 const {reduceMotionEnabled} = useA11y() 160 161 const scrollBtnStyles: React.CSSProperties[] = [ 162 a.absolute, 163 a.flex, 164 a.align_center, 165 a.justify_center, 166 a.rounded_sm, 167 a.z_10, 168 ] 169 const up: React.CSSProperties[] = [ 170 ...scrollBtnStyles, 171 a.pt_sm, 172 a.pb_lg, 173 { 174 top: 0, 175 left: 0, 176 right: 0, 177 borderBottomLeftRadius: 0, 178 borderBottomRightRadius: 0, 179 background: `linear-gradient(to bottom, ${t.atoms.bg.backgroundColor} 0%, transparent 100%)`, 180 }, 181 ] 182 const down: React.CSSProperties[] = [ 183 ...scrollBtnStyles, 184 a.pt_lg, 185 a.pb_sm, 186 { 187 bottom: 0, 188 left: 0, 189 right: 0, 190 borderBottomLeftRadius: 0, 191 borderBottomRightRadius: 0, 192 background: `linear-gradient(to top, ${t.atoms.bg.backgroundColor} 0%, transparent 100%)`, 193 }, 194 ] 195 196 return ( 197 <RadixSelect.Portal> 198 <RadixSelect.Content 199 style={flatten([t.atoms.bg, a.rounded_sm, a.overflow_hidden])} 200 position="popper" 201 align="center" 202 sideOffset={5} 203 className="radix-select-content" 204 // prevent the keyboard shortcut for opening the composer 205 onKeyDown={evt => evt.stopPropagation()}> 206 <View 207 style={[ 208 a.flex_1, 209 a.border, 210 t.atoms.border_contrast_low, 211 a.rounded_sm, 212 a.overflow_hidden, 213 !reduceMotionEnabled && a.zoom_fade_in, 214 ]}> 215 <RadixSelect.ScrollUpButton style={flatten(up)}> 216 <ChevronUpIcon style={[t.atoms.text]} size="xs" /> 217 </RadixSelect.ScrollUpButton> 218 <RadixSelect.Viewport style={flatten([a.p_xs])}> 219 {items.map((item, index) => ( 220 <Fragment key={valueExtractor(item)}> 221 {renderItem(item, index, selectedValue)} 222 </Fragment> 223 ))} 224 </RadixSelect.Viewport> 225 <RadixSelect.ScrollDownButton style={flatten(down)}> 226 <ChevronDownIcon style={[t.atoms.text]} size="xs" /> 227 </RadixSelect.ScrollDownButton> 228 </View> 229 </RadixSelect.Content> 230 </RadixSelect.Portal> 231 ) 232} 233 234function defaultItemValueExtractor(item: any) { 235 return item.value 236} 237 238const ItemContext = createContext<{ 239 hovered: boolean 240 focused: boolean 241 pressed: boolean 242 selected: boolean 243}>({ 244 hovered: false, 245 focused: false, 246 pressed: false, 247 selected: false, 248}) 249ItemContext.displayName = 'SelectItemContext' 250 251export function useItemContext() { 252 return useContext(ItemContext) 253} 254 255export function Item({ref, value, style, children}: ItemProps) { 256 const t = useTheme() 257 const { 258 state: hovered, 259 onIn: onMouseEnter, 260 onOut: onMouseLeave, 261 } = useInteractionState() 262 const selected = useContext(SelectedValueContext) === value 263 const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() 264 const ctx = useMemo( 265 () => ({hovered, focused, pressed: false, selected}), 266 [hovered, focused, selected], 267 ) 268 return ( 269 <RadixSelect.Item 270 ref={ref} 271 value={value} 272 onMouseEnter={onMouseEnter} 273 onMouseLeave={onMouseLeave} 274 onFocus={onFocus} 275 onBlur={onBlur} 276 style={flatten([ 277 t.atoms.text, 278 a.relative, 279 a.flex, 280 {minHeight: 25, paddingLeft: 30, paddingRight: 8}, 281 a.user_select_none, 282 a.align_center, 283 a.rounded_xs, 284 a.py_2xs, 285 a.text_sm, 286 {outline: 0}, 287 (hovered || focused) && {backgroundColor: t.palette.primary_50}, 288 selected && [a.font_semi_bold], 289 a.transition_color, 290 style, 291 ])}> 292 <ItemContext.Provider value={ctx}>{children}</ItemContext.Provider> 293 </RadixSelect.Item> 294 ) 295} 296 297export const ItemText = function ItemText({children, style}: ItemTextProps) { 298 return ( 299 <RadixSelect.ItemText asChild> 300 <Text style={flatten([style, web({pointerEvents: 'inherit'})])}> 301 {children} 302 </Text> 303 </RadixSelect.ItemText> 304 ) 305} 306 307export function ItemIndicator({icon: Icon = CheckIcon}: ItemIndicatorProps) { 308 return ( 309 <RadixSelect.ItemIndicator 310 style={flatten([ 311 a.absolute, 312 {left: 0, width: 30}, 313 a.flex, 314 a.align_center, 315 a.justify_center, 316 ])}> 317 <Icon size="sm" /> 318 </RadixSelect.ItemIndicator> 319 ) 320} 321 322export function Separator() { 323 const t = useTheme() 324 325 return ( 326 <RadixSelect.Separator 327 style={flatten([ 328 { 329 height: 1, 330 backgroundColor: t.atoms.border_contrast_low.borderColor, 331 }, 332 a.my_xs, 333 a.w_full, 334 ])} 335 /> 336 ) 337}