Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

Improved search language select (#7591)

* replace with Menu

* new icon for native

* hackfix radix dropdown height

* fix jsx

* reduce language names with lots of variants to what firefox returns from Intl.DisplayNames

* more language label simplifications

* add collision padding

* adjust spacing around and left align title

authored by samuel.fm and committed by

GitHub db25f95c b37199a5

+140 -134
+1
assets/icons/chevronTopBottom_stroke2_corner0_rounded.svg
···
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M11.293 4.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L12 6.414 8.707 9.707a1 1 0 0 1-1.414-1.414l4-4Zm-4 10a1 1 0 0 1 1.414 0L12 17.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414Z" clip-rule="evenodd"/></svg>
+6
src/alf/atoms.ts
··· 50 overflow_hidden: { 51 overflow: 'hidden', 52 }, 53 54 /* 55 * Width
··· 50 overflow_hidden: { 51 overflow: 'hidden', 52 }, 53 + /** 54 + * @platform web 55 + */ 56 + overflow_auto: web({ 57 + overflow: 'auto', 58 + }), 59 60 /* 61 * Width
+4 -3
src/components/Menu/index.web.tsx
··· 184 <DropdownMenu.Portal> 185 <DropdownMenu.Content 186 sideOffset={5} 187 loop 188 aria-label="Test" 189 className="dropdown-menu-transform-origin"> ··· 195 t.name === 'light' ? t.atoms.bg : t.atoms.bg_contrast_25, 196 t.atoms.shadow_md, 197 t.atoms.border_contrast_low, 198 !reduceMotionEnabled && a.zoom_fade_in, 199 style, 200 ]}> ··· 380 style={flatten([ 381 a.my_xs, 382 t.atoms.bg_contrast_100, 383 - { 384 - height: 1, 385 - }, 386 ])} 387 /> 388 )
··· 184 <DropdownMenu.Portal> 185 <DropdownMenu.Content 186 sideOffset={5} 187 + collisionPadding={{left: 5, right: 5, bottom: 5}} 188 loop 189 aria-label="Test" 190 className="dropdown-menu-transform-origin"> ··· 196 t.name === 'light' ? t.atoms.bg : t.atoms.bg_contrast_25, 197 t.atoms.shadow_md, 198 t.atoms.border_contrast_low, 199 + a.overflow_auto, 200 !reduceMotionEnabled && a.zoom_fade_in, 201 style, 202 ]}> ··· 382 style={flatten([ 383 a.my_xs, 384 t.atoms.bg_contrast_100, 385 + a.flex_shrink_0, 386 + {height: 1}, 387 ])} 388 /> 389 )
+4
src/components/icons/Chevron.tsx
··· 15 export const ChevronBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({ 16 path: 'M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z', 17 })
··· 15 export const ChevronBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({ 16 path: 'M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z', 17 }) 18 + 19 + export const ChevronTopBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({ 20 + path: 'M11.293 4.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L12 6.414 8.707 9.707a1 1 0 0 1-1.414-1.414l4-4Zm-4 10a1 1 0 0 1 1.414 0L12 17.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414Z', 21 + })
+11 -19
src/locale/languages.ts
··· 179 {code3: 'cho', code2: '', name: 'Choctaw'}, 180 {code3: 'chp', code2: '', name: 'Chipewyan; Dene Suline'}, 181 {code3: 'chr', code2: '', name: 'Cherokee'}, 182 - { 183 - code3: 'chu', 184 - code2: 'cu', 185 - name: 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', 186 - }, 187 {code3: 'chv', code2: 'cv', name: 'Chuvash'}, 188 {code3: 'chy', code2: '', name: 'Cheyenne'}, 189 {code3: 'cmc', code2: '', name: 'Chamic languages'}, ··· 301 {code3: 'iii', code2: 'ii', name: 'Sichuan Yi; Nuosu'}, 302 {code3: 'ijo', code2: '', name: 'Ijo languages'}, 303 {code3: 'iku', code2: 'iu', name: 'Inuktitut'}, 304 - {code3: 'ile', code2: 'ie', name: 'Interlingue; Occidental'}, 305 {code3: 'ilo', code2: '', name: 'Iloko'}, 306 - { 307 - code3: 'ina', 308 - code2: 'ia', 309 - name: 'Interlingua (International Auxiliary Language Association)', 310 - }, 311 {code3: 'inc', code2: '', name: 'Indic languages'}, 312 {code3: 'ind', code2: 'id', name: 'Indonesian'}, 313 {code3: 'ine', code2: '', name: 'Indo-European languages'}, ··· 325 {code3: 'kaa', code2: '', name: 'Kara-Kalpak'}, 326 {code3: 'kab', code2: '', name: 'Kabyle'}, 327 {code3: 'kac', code2: '', name: 'Kachin; Jingpho'}, 328 - {code3: 'kal', code2: 'kl', name: 'Kalaallisut; Greenlandic'}, 329 {code3: 'kam', code2: '', name: 'Kamba'}, 330 {code3: 'kan', code2: 'kn', name: 'Kannada'}, 331 {code3: 'kar', code2: '', name: 'Karen languages'}, ··· 364 {code3: 'lat', code2: 'la', name: 'Latin'}, 365 {code3: 'lav', code2: 'lv', name: 'Latvian'}, 366 {code3: 'lez', code2: '', name: 'Lezghian'}, 367 - {code3: 'lim', code2: 'li', name: 'Limburgan; Limburger; Limburgish'}, 368 {code3: 'lin', code2: 'ln', name: 'Lingala'}, 369 {code3: 'lit', code2: 'lt', name: 'Lithuanian'}, 370 {code3: 'lol', code2: '', name: 'Mongo'}, ··· 425 {code3: 'nai', code2: '', name: 'North American Indian languages'}, 426 {code3: 'nap', code2: '', name: 'Neapolitan'}, 427 {code3: 'nau', code2: 'na', name: 'Nauru'}, 428 - {code3: 'nav', code2: 'nv', name: 'Navajo; Navaho'}, 429 - {code3: 'nbl', code2: 'nr', name: 'Ndebele, South; South Ndebele'}, 430 - {code3: 'nde', code2: 'nd', name: 'Ndebele, North; North Ndebele'}, 431 {code3: 'ndo', code2: 'ng', name: 'Ndonga'}, 432 { 433 code3: 'nds', ··· 440 {code3: 'nic', code2: '', name: 'Niger-Kordofanian languages'}, 441 {code3: 'niu', code2: '', name: 'Niuean'}, 442 {code3: 'nld', code2: 'nl', name: 'Dutch; Flemish'}, 443 - {code3: 'nno', code2: 'nn', name: 'Norwegian Nynorsk; Nynorsk, Norwegian'}, 444 - {code3: 'nob', code2: 'nb', name: 'Bokmål, Norwegian; Norwegian Bokmål'}, 445 {code3: 'nog', code2: '', name: 'Nogai'}, 446 {code3: 'non', code2: '', name: 'Norse, Old'}, 447 {code3: 'nor', code2: 'no', name: 'Norwegian'}, ··· 463 {code3: 'ori', code2: 'or', name: 'Oriya'}, 464 {code3: 'orm', code2: 'om', name: 'Oromo'}, 465 {code3: 'osa', code2: '', name: 'Osage'}, 466 - {code3: 'oss', code2: 'os', name: 'Ossetian; Ossetic'}, 467 {code3: 'ota', code2: '', name: 'Turkish, Ottoman (1500-1928)'}, 468 {code3: 'oto', code2: '', name: 'Otomian languages'}, 469 {code3: 'paa', code2: '', name: 'Papuan languages'},
··· 179 {code3: 'cho', code2: '', name: 'Choctaw'}, 180 {code3: 'chp', code2: '', name: 'Chipewyan; Dene Suline'}, 181 {code3: 'chr', code2: '', name: 'Cherokee'}, 182 + {code3: 'chu', code2: 'cu', name: 'Church Slavic'}, 183 {code3: 'chv', code2: 'cv', name: 'Chuvash'}, 184 {code3: 'chy', code2: '', name: 'Cheyenne'}, 185 {code3: 'cmc', code2: '', name: 'Chamic languages'}, ··· 297 {code3: 'iii', code2: 'ii', name: 'Sichuan Yi; Nuosu'}, 298 {code3: 'ijo', code2: '', name: 'Ijo languages'}, 299 {code3: 'iku', code2: 'iu', name: 'Inuktitut'}, 300 + {code3: 'ile', code2: 'ie', name: 'Interlingue'}, 301 {code3: 'ilo', code2: '', name: 'Iloko'}, 302 + {code3: 'ina', code2: 'ia', name: 'Interlingua'}, 303 {code3: 'inc', code2: '', name: 'Indic languages'}, 304 {code3: 'ind', code2: 'id', name: 'Indonesian'}, 305 {code3: 'ine', code2: '', name: 'Indo-European languages'}, ··· 317 {code3: 'kaa', code2: '', name: 'Kara-Kalpak'}, 318 {code3: 'kab', code2: '', name: 'Kabyle'}, 319 {code3: 'kac', code2: '', name: 'Kachin; Jingpho'}, 320 + {code3: 'kal', code2: 'kl', name: 'Kalaallisut'}, 321 {code3: 'kam', code2: '', name: 'Kamba'}, 322 {code3: 'kan', code2: 'kn', name: 'Kannada'}, 323 {code3: 'kar', code2: '', name: 'Karen languages'}, ··· 356 {code3: 'lat', code2: 'la', name: 'Latin'}, 357 {code3: 'lav', code2: 'lv', name: 'Latvian'}, 358 {code3: 'lez', code2: '', name: 'Lezghian'}, 359 + {code3: 'lim', code2: 'li', name: 'Limburgish'}, 360 {code3: 'lin', code2: 'ln', name: 'Lingala'}, 361 {code3: 'lit', code2: 'lt', name: 'Lithuanian'}, 362 {code3: 'lol', code2: '', name: 'Mongo'}, ··· 417 {code3: 'nai', code2: '', name: 'North American Indian languages'}, 418 {code3: 'nap', code2: '', name: 'Neapolitan'}, 419 {code3: 'nau', code2: 'na', name: 'Nauru'}, 420 + {code3: 'nav', code2: 'nv', name: 'Navajo'}, 421 + {code3: 'nbl', code2: 'nr', name: 'South Ndebele'}, 422 + {code3: 'nde', code2: 'nd', name: 'North Ndebele'}, 423 {code3: 'ndo', code2: 'ng', name: 'Ndonga'}, 424 { 425 code3: 'nds', ··· 432 {code3: 'nic', code2: '', name: 'Niger-Kordofanian languages'}, 433 {code3: 'niu', code2: '', name: 'Niuean'}, 434 {code3: 'nld', code2: 'nl', name: 'Dutch; Flemish'}, 435 + {code3: 'nno', code2: 'nn', name: 'Norwegian Nynorsk'}, 436 + {code3: 'nob', code2: 'nb', name: 'Norwegian Bokmål'}, 437 {code3: 'nog', code2: '', name: 'Nogai'}, 438 {code3: 'non', code2: '', name: 'Norse, Old'}, 439 {code3: 'nor', code2: 'no', name: 'Norwegian'}, ··· 455 {code3: 'ori', code2: 'or', name: 'Oriya'}, 456 {code3: 'orm', code2: 'om', name: 'Oromo'}, 457 {code3: 'osa', code2: '', name: 'Osage'}, 458 + {code3: 'oss', code2: 'os', name: 'Ossetic'}, 459 {code3: 'ota', code2: '', name: 'Turkish, Ottoman (1500-1928)'}, 460 {code3: 'oto', code2: '', name: 'Otomian languages'}, 461 {code3: 'paa', code2: '', name: 'Papuan languages'},
+109 -110
src/view/screens/Search/Search.tsx
··· 1 - import React, {useCallback, useLayoutEffect} from 'react' 2 import { 3 ActivityIndicator, 4 Image, ··· 10 View, 11 } from 'react-native' 12 import {ScrollView as RNGHScrollView} from 'react-native-gesture-handler' 13 - import RNPickerSelect from 'react-native-picker-select' 14 import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api' 15 import { 16 FontAwesomeIcon, ··· 57 import {Explore} from '#/view/screens/Search/Explore' 58 import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search' 59 import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils' 60 - import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf' 61 - import {Button, ButtonText} from '#/components/Button' 62 import * as FeedCard from '#/components/FeedCard' 63 import {SearchInput} from '#/components/forms/SearchInput' 64 - import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' 65 import * as Layout from '#/components/Layout' 66 import {account, useStorage} from '#/storage' 67 68 function Loader() { ··· 315 value: string 316 onChange(value: string): void 317 }) { 318 - const t = useTheme() 319 const {_} = useLingui() 320 const {appLanguage, contentLanguages} = useLanguagePrefs() 321 322 - const items = React.useMemo(() => { 323 - return [ 324 - { 325 - label: _(msg`Any language`), 326 - inputLabel: _(msg`Any language`), 327 - value: '', 328 - key: '*', 329 - }, 330 - ].concat( 331 - LANGUAGES.filter( 332 - (lang, index, self) => 333 - Boolean(lang.code2) && // reduce to the code2 varieties 334 - index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen) 335 - ) 336 - .map(l => ({ 337 - label: languageName(l, appLanguage), 338 - inputLabel: languageName(l, appLanguage), 339 - value: l.code2, 340 - key: l.code2 + l.code3, 341 - })) 342 - .sort((a, b) => { 343 - // prioritize user's languages 344 - const aIsUser = contentLanguages.includes(a.value) 345 - const bIsUser = contentLanguages.includes(b.value) 346 - if (aIsUser && !bIsUser) return -1 347 - if (bIsUser && !aIsUser) return 1 348 - // prioritize "common" langs in the network 349 - const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value) 350 - const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value) 351 - if (aIsCommon && !bIsCommon) return -1 352 - if (bIsCommon && !aIsCommon) return 1 353 - // fall back to alphabetical 354 - return a.label.localeCompare(b.label) 355 - }), 356 ) 357 - }, [_, appLanguage, contentLanguages]) 358 359 - const style = { 360 - backgroundColor: t.atoms.bg_contrast_25.backgroundColor, 361 - color: t.atoms.text.color, 362 - fontSize: a.text_xs.fontSize, 363 - fontFamily: 'inherit', 364 - fontWeight: a.font_bold.fontWeight, 365 - paddingHorizontal: 14, 366 - paddingRight: 32, 367 - paddingVertical: 8, 368 - borderRadius: a.rounded_full.borderRadius, 369 - borderWidth: a.border.borderWidth, 370 - borderColor: t.atoms.border_contrast_low.borderColor, 371 - } 372 373 return ( 374 - <RNPickerSelect 375 - darkTheme={t.scheme === 'dark'} 376 - placeholder={{}} 377 - value={value} 378 - onValueChange={onChange} 379 - items={items} 380 - Icon={() => ( 381 - <ChevronDown fill={t.atoms.text_contrast_low.color} size="sm" /> 382 - )} 383 - useNativeAndroidPickerStyle={false} 384 - style={{ 385 - iconContainer: { 386 - pointerEvents: 'none', 387 - right: a.px_sm.paddingRight, 388 - top: 0, 389 - bottom: 0, 390 - display: 'flex', 391 - justifyContent: 'center', 392 - }, 393 - inputAndroid: { 394 - ...style, 395 - paddingVertical: 2, 396 - }, 397 - inputIOS: { 398 - ...style, 399 - }, 400 - inputWeb: web({ 401 - ...style, 402 - cursor: 'pointer', 403 - // @ts-ignore web only 404 - '-moz-appearance': 'none', 405 - '-webkit-appearance': 'none', 406 - appearance: 'none', 407 - outline: 0, 408 - borderWidth: 0, 409 - overflow: 'hidden', 410 - whiteSpace: 'nowrap', 411 - textOverflow: 'ellipsis', 412 - }), 413 - }} 414 - /> 415 ) 416 } 417 ··· 795 // HACK: shift up search input. we can't remove the top padding 796 // on the search input because it messes up the layout animation 797 // if we add it only when the header is hidden 798 - style={{marginBottom: tokens.space.sm * -1}}> 799 <Layout.Header.Outer noBottomBorder> 800 <Layout.Header.MenuButton /> 801 - <Layout.Header.Content 802 - align={showFilters ? 'left' : 'platform'}> 803 <Layout.Header.TitleText> 804 <Trans>Search</Trans> 805 </Layout.Header.TitleText> 806 </Layout.Header.Content> 807 {showFilters ? ( 808 - <View style={[{minWidth: 140}]}> 809 - <SearchLanguageDropdown 810 - value={params.lang} 811 - onChange={params.setLang} 812 - /> 813 - </View> 814 ) : ( 815 <Layout.Header.Slot /> 816 )} ··· 856 a.justify_between, 857 a.gap_sm, 858 ]}> 859 - <View style={[{width: 140}]}> 860 - <SearchLanguageDropdown 861 - value={params.lang} 862 - onChange={params.setLang} 863 - /> 864 - </View> 865 </View> 866 )} 867 </View>
··· 1 + import React, {useCallback, useLayoutEffect, useMemo} from 'react' 2 import { 3 ActivityIndicator, 4 Image, ··· 10 View, 11 } from 'react-native' 12 import {ScrollView as RNGHScrollView} from 'react-native-gesture-handler' 13 import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api' 14 import { 15 FontAwesomeIcon, ··· 56 import {Explore} from '#/view/screens/Search/Explore' 57 import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search' 58 import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils' 59 + import { 60 + atoms as a, 61 + native, 62 + platform, 63 + tokens, 64 + useBreakpoints, 65 + useTheme, 66 + web, 67 + } from '#/alf' 68 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 69 import * as FeedCard from '#/components/FeedCard' 70 import {SearchInput} from '#/components/forms/SearchInput' 71 + import { 72 + ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon, 73 + ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon, 74 + } from '#/components/icons/Chevron' 75 + import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe' 76 import * as Layout from '#/components/Layout' 77 + import * as Menu from '#/components/Menu' 78 import {account, useStorage} from '#/storage' 79 80 function Loader() { ··· 327 value: string 328 onChange(value: string): void 329 }) { 330 const {_} = useLingui() 331 const {appLanguage, contentLanguages} = useLanguagePrefs() 332 333 + const languages = useMemo(() => { 334 + return LANGUAGES.filter( 335 + (lang, index, self) => 336 + Boolean(lang.code2) && // reduce to the code2 varieties 337 + index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen) 338 ) 339 + .map(l => ({ 340 + label: languageName(l, appLanguage), 341 + value: l.code2, 342 + key: l.code2 + l.code3, 343 + })) 344 + .sort((a, b) => { 345 + // prioritize user's languages 346 + const aIsUser = contentLanguages.includes(a.value) 347 + const bIsUser = contentLanguages.includes(b.value) 348 + if (aIsUser && !bIsUser) return -1 349 + if (bIsUser && !aIsUser) return 1 350 + // prioritize "common" langs in the network 351 + const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value) 352 + const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value) 353 + if (aIsCommon && !bIsCommon) return -1 354 + if (bIsCommon && !aIsCommon) return 1 355 + // fall back to alphabetical 356 + return a.label.localeCompare(b.label) 357 + }) 358 + }, [appLanguage, contentLanguages]) 359 360 + const currentLanguageLabel = 361 + languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`) 362 363 return ( 364 + <Menu.Root> 365 + <Menu.Trigger 366 + label={_( 367 + msg`Filter search by language (currently: ${currentLanguageLabel})`, 368 + )}> 369 + {({props}) => ( 370 + <Button 371 + {...props} 372 + label={props.accessibilityLabel} 373 + size="small" 374 + color={platform({native: 'primary', default: 'secondary'})} 375 + variant={platform({native: 'ghost', default: 'solid'})} 376 + style={native([ 377 + a.py_sm, 378 + a.px_sm, 379 + {marginRight: tokens.space.sm * -1}, 380 + ])}> 381 + <ButtonIcon icon={EarthIcon} /> 382 + <ButtonText>{currentLanguageLabel}</ButtonText> 383 + <ButtonIcon 384 + icon={platform({ 385 + native: ChevronUpDownIcon, 386 + default: ChevronDownIcon, 387 + })} 388 + /> 389 + </Button> 390 + )} 391 + </Menu.Trigger> 392 + <Menu.Outer 393 + // HACKFIX: Currently there is no height limit for Radix dropdowns, 394 + // so if it's too tall it just goes off screen. TODO: fix internally -sfn 395 + style={web({maxHeight: '70vh'})}> 396 + <Menu.LabelText> 397 + <Trans>Filter search by language</Trans> 398 + </Menu.LabelText> 399 + <Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}> 400 + <Menu.ItemText> 401 + <Trans>All languages</Trans> 402 + </Menu.ItemText> 403 + <Menu.ItemRadio selected={value === ''} /> 404 + </Menu.Item> 405 + <Menu.Divider /> 406 + <Menu.Group> 407 + {languages.map(lang => ( 408 + <Menu.Item 409 + key={lang.key} 410 + label={lang.label} 411 + onPress={() => onChange(lang.value)}> 412 + <Menu.ItemText>{lang.label}</Menu.ItemText> 413 + <Menu.ItemRadio selected={value === lang.value} /> 414 + </Menu.Item> 415 + ))} 416 + </Menu.Group> 417 + </Menu.Outer> 418 + </Menu.Root> 419 ) 420 } 421 ··· 799 // HACK: shift up search input. we can't remove the top padding 800 // on the search input because it messes up the layout animation 801 // if we add it only when the header is hidden 802 + style={{marginBottom: tokens.space.xs * -1}}> 803 <Layout.Header.Outer noBottomBorder> 804 <Layout.Header.MenuButton /> 805 + <Layout.Header.Content align="left"> 806 <Layout.Header.TitleText> 807 <Trans>Search</Trans> 808 </Layout.Header.TitleText> 809 </Layout.Header.Content> 810 {showFilters ? ( 811 + <SearchLanguageDropdown 812 + value={params.lang} 813 + onChange={params.setLang} 814 + /> 815 ) : ( 816 <Layout.Header.Slot /> 817 )} ··· 857 a.justify_between, 858 a.gap_sm, 859 ]}> 860 + <SearchLanguageDropdown 861 + value={params.lang} 862 + onChange={params.setLang} 863 + /> 864 </View> 865 )} 866 </View>
+5 -2
src/view/shell/desktop/LeftNav.tsx
··· 33 import {PressableWithHover} from '#/view/com/util/PressableWithHover' 34 import {UserAvatar} from '#/view/com/util/UserAvatar' 35 import {NavSignupCard} from '#/view/shell/NavSignupCard' 36 - import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf' 37 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 38 import {DialogControlProps} from '#/components/Dialog' 39 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' ··· 235 closeEverything() 236 } 237 return ( 238 - <Menu.Outer> 239 {accounts && accounts.length > 0 && ( 240 <> 241 <Menu.Group>
··· 33 import {PressableWithHover} from '#/view/com/util/PressableWithHover' 34 import {UserAvatar} from '#/view/com/util/UserAvatar' 35 import {NavSignupCard} from '#/view/shell/NavSignupCard' 36 + import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf' 37 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 38 import {DialogControlProps} from '#/components/Dialog' 39 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' ··· 235 closeEverything() 236 } 237 return ( 238 + <Menu.Outer 239 + // HACKFIX: Currently there is no height limit for Radix dropdowns, 240 + // so if it's too tall it just goes off screen. TODO: fix internally -sfn 241 + style={web({maxHeight: '70vh'})}> 242 {accounts && accounts.length > 0 && ( 243 <> 244 <Menu.Group>