From 81ef21a0c802f1d905f37a2a818544a8b400793c Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Sat, 25 Feb 2023 14:34:32 +0100 Subject: [Glitch] Rename JSX files with proper `.jsx` extension Port 44a7d87cb1f5df953b6c14c16c59e2e4ead1bcb9 to glitch-soc Signed-off-by: Claire --- .../compose/components/language_dropdown.jsx | 327 +++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx (limited to 'app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx') diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx new file mode 100644 index 000000000..3f8411ab1 --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx @@ -0,0 +1,327 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, defineMessages } from 'react-intl'; +import TextIconButton from './text_icon_button'; +import Overlay from 'react-overlays/Overlay'; +import { supportsPassiveEvents } from 'detect-passive-events'; +import classNames from 'classnames'; +import { languages as preloadedLanguages } from 'flavours/glitch/initial_state'; +import { loupeIcon, deleteIcon } from 'flavours/glitch/utils/icons'; +import fuzzysort from 'fuzzysort'; + +const messages = defineMessages({ + changeLanguage: { id: 'compose.language.change', defaultMessage: 'Change language' }, + search: { id: 'compose.language.search', defaultMessage: 'Search languages...' }, + clear: { id: 'emoji_button.clear', defaultMessage: 'Clear' }, +}); + +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; + +class LanguageDropdownMenu extends React.PureComponent { + + static propTypes = { + value: PropTypes.string.isRequired, + frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired, + onClose: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)), + intl: PropTypes.object, + }; + + static defaultProps = { + languages: preloadedLanguages, + }; + + state = { + searchValue: '', + }; + + handleDocumentClick = e => { + if (this.node && !this.node.contains(e.target)) { + this.props.onClose(); + } + }; + + componentDidMount () { + document.addEventListener('click', this.handleDocumentClick, false); + document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + + // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need + // to wait for a frame before focusing + requestAnimationFrame(() => { + if (this.node) { + const element = this.node.querySelector('input[type="search"]'); + if (element) element.focus(); + } + }); + } + + componentWillUnmount () { + document.removeEventListener('click', this.handleDocumentClick, false); + document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + setRef = c => { + this.node = c; + }; + + setListRef = c => { + this.listNode = c; + }; + + handleSearchChange = ({ target }) => { + this.setState({ searchValue: target.value }); + }; + + search () { + const { languages, value, frequentlyUsedLanguages } = this.props; + const { searchValue } = this.state; + + if (searchValue === '') { + return [...languages].sort((a, b) => { + // Push current selection to the top of the list + + if (a[0] === value) { + return -1; + } else if (b[0] === value) { + return 1; + } else { + // Sort according to frequently used languages + + const indexOfA = frequentlyUsedLanguages.indexOf(a[0]); + const indexOfB = frequentlyUsedLanguages.indexOf(b[0]); + + return ((indexOfA > -1 ? indexOfA : Infinity) - (indexOfB > -1 ? indexOfB : Infinity)); + } + }); + } + + return fuzzysort.go(searchValue, languages, { + keys: ['0', '1', '2'], + limit: 5, + threshold: -10000, + }).map(result => result.obj); + } + + frequentlyUsed () { + const { languages, value } = this.props; + const current = languages.find(lang => lang[0] === value); + const results = []; + + if (current) { + results.push(current); + } + + return results; + } + + handleClick = e => { + const value = e.currentTarget.getAttribute('data-index'); + + e.preventDefault(); + + this.props.onClose(); + this.props.onChange(value); + }; + + handleKeyDown = e => { + const { onClose } = this.props; + const index = Array.from(this.listNode.childNodes).findIndex(node => node === e.currentTarget); + + let element = null; + + switch(e.key) { + case 'Escape': + onClose(); + break; + case 'Enter': + this.handleClick(e); + break; + case 'ArrowDown': + element = this.listNode.childNodes[index + 1] || this.listNode.firstChild; + break; + case 'ArrowUp': + element = this.listNode.childNodes[index - 1] || this.listNode.lastChild; + break; + case 'Tab': + if (e.shiftKey) { + element = this.listNode.childNodes[index - 1] || this.listNode.lastChild; + } else { + element = this.listNode.childNodes[index + 1] || this.listNode.firstChild; + } + break; + case 'Home': + element = this.listNode.firstChild; + break; + case 'End': + element = this.listNode.lastChild; + break; + } + + if (element) { + element.focus(); + e.preventDefault(); + e.stopPropagation(); + } + }; + + handleSearchKeyDown = e => { + const { onChange, onClose } = this.props; + const { searchValue } = this.state; + + let element = null; + + switch(e.key) { + case 'Tab': + case 'ArrowDown': + element = this.listNode.firstChild; + + if (element) { + element.focus(); + e.preventDefault(); + e.stopPropagation(); + } + + break; + case 'Enter': + element = this.listNode.firstChild; + + if (element) { + onChange(element.getAttribute('data-index')); + onClose(); + } + break; + case 'Escape': + if (searchValue !== '') { + e.preventDefault(); + this.handleClear(); + } + + break; + } + }; + + handleClear = () => { + this.setState({ searchValue: '' }); + }; + + renderItem = lang => { + const { value } = this.props; + + return ( +
+ {lang[2]} ({lang[1]}) +
+ ); + }; + + render () { + const { intl } = this.props; + const { searchValue } = this.state; + const isSearching = searchValue !== ''; + const results = this.search(); + + return ( +
+
+ + +
+ +
+ {results.map(this.renderItem)} +
+
+ ); + } + +} + +export default @injectIntl +class LanguageDropdown extends React.PureComponent { + + static propTypes = { + value: PropTypes.string, + frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string), + intl: PropTypes.object.isRequired, + onChange: PropTypes.func, + onClose: PropTypes.func, + }; + + state = { + open: false, + placement: 'bottom', + }; + + handleToggle = () => { + if (this.state.open && this.activeElement) { + this.activeElement.focus({ preventScroll: true }); + } + + this.setState({ open: !this.state.open }); + }; + + handleClose = () => { + const { value, onClose } = this.props; + + if (this.state.open && this.activeElement) { + this.activeElement.focus({ preventScroll: true }); + } + + this.setState({ open: false }); + onClose(value); + }; + + handleChange = value => { + const { onChange } = this.props; + onChange(value); + }; + + setTargetRef = c => { + this.target = c; + }; + + findTarget = () => { + return this.target; + }; + + handleOverlayEnter = (state) => { + this.setState({ placement: state.placement }); + }; + + render () { + const { value, intl, frequentlyUsedLanguages } = this.props; + const { open, placement } = this.state; + + return ( +
+
+ +
+ + + {({ props, placement }) => ( +
+
+ +
+
+ )} +
+
+ ); + } + +} -- cgit From 31455f05fb361aa2f73a2af9704fff7bd64ff486 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 21 Feb 2023 09:23:39 +0100 Subject: [Glitch] Add `lang` tag to native language names in language picker Port 7207a98d3990124c6dc1df2187475695c800e65f to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/features/compose/components/language_dropdown.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx') diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx index 3f8411ab1..03fb899f8 100644 --- a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx @@ -210,7 +210,7 @@ class LanguageDropdownMenu extends React.PureComponent { return (
- {lang[2]} ({lang[1]}) + {lang[2]} ({lang[1]})
); }; -- cgit