More comment create and textarea improvements

This commit is contained in:
Rafael 2022-02-02 09:49:02 -03:00 committed by Thomas Zarebczan
parent b3ed0027ff
commit eef6691557
16 changed files with 250 additions and 135 deletions

View file

@ -484,8 +484,8 @@ export function CommentCreate(props: Props) {
disabled={isFetchingChannels || disableInput} disabled={isFetchingChannels || disableInput}
isLivestream={isLivestream} isLivestream={isLivestream}
label={ label={
<div className="commentCreate__labelWrapper"> <div className="comment-create__label-wrapper">
<span className="commentCreate__label"> <span className="comment-create__label">
{(isReply ? __('Replying as') : isLivestream ? __('Chat as') : __('Comment as')) + ' '} {(isReply ? __('Replying as') : isLivestream ? __('Chat as') : __('Comment as')) + ' '}
</span> </span>
<SelectChannel tiny /> <SelectChannel tiny />

View file

@ -253,7 +253,7 @@ function CommentMenuList(props: Props) {
</MenuItem> </MenuItem>
)} )}
{isPinned && ( {isPinned && isLiveComment && (
<MenuItem className="comment__menu-option menu__link" onSelect={handleDismissPin}> <MenuItem className="comment__menu-option menu__link" onSelect={handleDismissPin}>
<Icon aria-hidden icon={ICONS.DISMISS_ALL} /> <Icon aria-hidden icon={ICONS.DISMISS_ALL} />
{__('Dismiss Pin')} {__('Dismiss Pin')}

View file

@ -272,8 +272,8 @@ export class FormField extends React.PureComponent<Props> {
</React.Suspense> </React.Suspense>
)} )}
<div className="form-field__textarea-info">
{!noEmojis && openEmoteMenu && ( {!noEmojis && openEmoteMenu && (
<div className="form-field__textarea-info">
<Button <Button
type="alt" type="alt"
className="button--file-action" className="button--file-action"
@ -282,8 +282,8 @@ export class FormField extends React.PureComponent<Props> {
icon={ICONS.EMOJI} icon={ICONS.EMOJI}
iconSize={20} iconSize={20}
/> />
)}
</div> </div>
)}
</fieldset-section> </fieldset-section>
); );
default: default:

View file

@ -297,7 +297,6 @@ export default function LivestreamChatLayout(props: Props) {
key={pinnedComment.comment_id} key={pinnedComment.comment_id}
uri={uri} uri={uri}
pushMention={setMention} pushMention={setMention}
handleDismissPin={() => setShowPinned(false)}
/> />
<Button <Button

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import LivestreamLayout from './view'; import SwipeableDrawer from './view';
import { selectTheme } from 'redux/selectors/settings'; import { selectTheme } from 'redux/selectors/settings';
import { selectMobilePlayerDimensions } from 'redux/selectors/app'; import { selectMobilePlayerDimensions } from 'redux/selectors/app';
@ -8,4 +8,4 @@ const select = (state) => ({
mobilePlayerDimensions: selectMobilePlayerDimensions(state), mobilePlayerDimensions: selectMobilePlayerDimensions(state),
}); });
export default connect(select)(LivestreamLayout); export default connect(select)(SwipeableDrawer);

View file

@ -42,29 +42,18 @@ export default function SwipeableDrawer(props: Props) {
} }
}, [coverHeight, mobilePlayerDimensions, open]); }, [coverHeight, mobilePlayerDimensions, open]);
const DrawerGlobalStyles = () => ( // Reset scroll position when opening: avoid broken position where
<Global // the drawer is lower than the video
styles={{ React.useEffect(() => {
'.main-wrapper__inner--filepage': { if (open) {
overflow: open ? 'hidden' : 'unset', const htmlEl = document.querySelector('html');
maxHeight: open ? '100vh' : 'unset', if (htmlEl) htmlEl.scrollTop = 0;
}, }
'.MuiDrawer-root': { }, [open]);
top: `calc(${HEADER_HEIGHT_MOBILE}px + ${videoHeight}px) !important`,
},
'.MuiDrawer-root > .MuiPaper-root': {
overflow: 'visible',
color: 'var(--color-text)',
position: 'absolute',
height: `calc(100% - ${DRAWER_PULLER_HEIGHT}px)`,
},
}}
/>
);
return ( return (
<> <>
<DrawerGlobalStyles /> <DrawerGlobalStyles open={open} videoHeight={videoHeight} />
<MUIDrawer <MUIDrawer
anchor="bottom" anchor="bottom"
@ -90,6 +79,35 @@ export default function SwipeableDrawer(props: Props) {
); );
} }
type GlobalStylesProps = {
open?: boolean,
videoHeight: number,
};
const DrawerGlobalStyles = (globalStylesProps: GlobalStylesProps) => {
const { open, videoHeight } = globalStylesProps;
return (
<Global
styles={{
'.main-wrapper__inner--filepage': {
overflow: open ? 'hidden' : 'unset',
maxHeight: open ? '100vh' : 'unset',
},
'.main-wrapper .MuiDrawer-root': {
top: `calc(${HEADER_HEIGHT_MOBILE}px + ${videoHeight}px) !important`,
},
'.main-wrapper .MuiDrawer-root > .MuiPaper-root': {
overflow: 'visible',
color: 'var(--color-text)',
position: 'absolute',
height: `calc(100% - ${DRAWER_PULLER_HEIGHT}px)`,
},
}}
/>
);
};
type PullerProps = { type PullerProps = {
theme: string, theme: string,
}; };

View file

@ -1,9 +1,18 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectClaimForUri } from 'redux/selectors/claims'; import { selectClaimForUri } from 'redux/selectors/claims';
import TextareaSuggestionsItem from './view'; import TextareaSuggestionsItem from './view';
import { formatLbryChannelName } from 'util/url';
import { getClaimTitle } from 'util/claim';
const select = (state, props) => ({ const select = (state, props) => {
claim: props.uri && selectClaimForUri(state, props.uri), const { uri } = props;
});
const claim = uri && selectClaimForUri(state, uri);
return {
claimLabel: claim && formatLbryChannelName(claim.canonical_url),
claimTitle: claim && getClaimTitle(claim.canonical_url),
};
};
export default connect(select)(TextareaSuggestionsItem); export default connect(select)(TextareaSuggestionsItem);

View file

@ -3,13 +3,14 @@ import ChannelThumbnail from 'component/channelThumbnail';
import React from 'react'; import React from 'react';
type Props = { type Props = {
claim?: Claim, claimLabel?: string,
claimTitle?: string,
emote?: any, emote?: any,
uri?: string, uri?: string,
}; };
export default function TextareaSuggestionsItem(props: Props) { export default function TextareaSuggestionsItem(props: Props) {
const { claim, emote, uri, ...autocompleteProps } = props; const { claimLabel, claimTitle, emote, uri, ...autocompleteProps } = props;
if (emote) { if (emote) {
const { name: value, url, unicode } = emote; const { name: value, url, unicode } = emote;
@ -18,8 +19,8 @@ export default function TextareaSuggestionsItem(props: Props) {
<div {...autocompleteProps} dispatch={undefined}> <div {...autocompleteProps} dispatch={undefined}>
{unicode ? <div className="emote">{unicode}</div> : <img className="emote" src={url} />} {unicode ? <div className="emote">{unicode}</div> : <img className="emote" src={url} />}
<div className="textareaSuggestion__label"> <div className="textarea-suggestion__label">
<span className="textareaSuggestion__title textareaSuggestion__value textareaSuggestion__value--emote"> <span className="textarea-suggestion__title textarea-suggestion__value textarea-suggestion__value--emote">
{value} {value}
</span> </span>
</div> </div>
@ -27,16 +28,16 @@ export default function TextareaSuggestionsItem(props: Props) {
); );
} }
if (claim) { if (claimLabel) {
const value = claim.canonical_url.replace('lbry://', '').replace('#', ':'); const value = claimLabel;
return ( return (
<div {...autocompleteProps} dispatch={undefined}> <div {...autocompleteProps} dispatch={undefined}>
<ChannelThumbnail xsmall uri={uri} /> <ChannelThumbnail xsmall uri={uri} />
<div className="textareaSuggestion__label"> <div className="textarea-suggestion__label">
<span className="textareaSuggestion__title">{(claim.value && claim.value.title) || value}</span> <span className="textarea-suggestion__title">{claimTitle || value}</span>
<span className="textareaSuggestion__value">{value}</span> <span className="textarea-suggestion__value">{value}</span>
</div> </div>
</div> </div>
); );

View file

@ -4,7 +4,6 @@ import { doSetMentionSearchResults } from 'redux/actions/search';
import { makeSelectWinningUriForQuery } from 'redux/selectors/search'; import { makeSelectWinningUriForQuery } from 'redux/selectors/search';
import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream'; import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream';
import { selectChannelMentionData } from 'redux/selectors/comments'; import { selectChannelMentionData } from 'redux/selectors/comments';
import { selectShowMatureContent } from 'redux/selectors/settings';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import TextareaWithSuggestions from './view'; import TextareaWithSuggestions from './view';
@ -32,13 +31,12 @@ const select = (state, props) => {
commentorUris, commentorUris,
hasNewResolvedResults, hasNewResolvedResults,
searchQuery: query, searchQuery: query,
showMature: selectShowMatureContent(state),
}; };
}; };
const perform = (dispatch) => ({ const perform = {
doResolveUris: (uris) => dispatch(doResolveUris(uris, true)), doResolveUris,
doSetMentionSearchResults: (query, uris) => dispatch(doSetMentionSearchResults(query, uris)), doSetMentionSearchResults,
}); };
export default withRouter(connect(select, perform)(TextareaWithSuggestions)); export default withRouter(connect(select, perform)(TextareaWithSuggestions));

View file

@ -0,0 +1,33 @@
// @flow
import LbcSymbol from 'component/common/lbc-symbol';
import React from 'react';
type Props = {
groupName: string,
suggestionTerm?: ?string,
searchQuery?: string,
children: any,
};
const TextareaSuggestionsGroup = (props: Props) => {
const { groupName, suggestionTerm, searchQuery, children } = props;
return (
<div key={groupName} className="textarea-suggestions__group">
<label className="textarea-suggestions__group-label">
{groupName === 'Top' ? (
<LbcSymbol prefix={__('Winning Search for %matching_term%', { matching_term: searchQuery })} />
) : suggestionTerm && suggestionTerm.length > 1 ? (
__('%group_name% matching %matching_term%', { group_name: groupName, matching_term: suggestionTerm })
) : (
groupName
)}
</label>
{children}
<hr className="textarea-suggestions__separator" />
</div>
);
};
export default TextareaSuggestionsGroup;

View file

@ -0,0 +1,51 @@
// @flow
import { useIsMobile } from 'effects/use-screensize';
import * as ICONS from 'constants/icons';
import React from 'react';
import TextField from '@mui/material/TextField';
import Button from 'component/button';
import Zoom from '@mui/material/Zoom';
type Props = {
params: any,
messageValue: string,
inputDefaultProps: any,
inputRef: any,
handleEmojis: () => any,
handleTip: (isLBC: boolean) => void,
handleSubmit: () => any,
};
const TextareaSuggestionsInput = (props: Props) => {
const { params, messageValue, inputRef, inputDefaultProps, handleEmojis, handleTip, handleSubmit } = props;
const isMobile = useIsMobile();
const { InputProps, disabled, fullWidth, id, inputProps: autocompleteInputProps } = params;
const inputProps = { ...autocompleteInputProps, ...inputDefaultProps };
const autocompleteProps = { InputProps, disabled, fullWidth, id, inputProps };
if (isMobile) {
InputProps.startAdornment = <Button icon={ICONS.STICKER} onClick={handleEmojis} />;
InputProps.endAdornment = (
<>
<Button icon={ICONS.LBC} onClick={() => handleTip(true)} />
<Button icon={ICONS.FINANCE} onClick={() => handleTip(false)} />
<Zoom in={messageValue && messageValue.length > 0} mountOnEnter unmountOnExit>
<div>
<Button button="primary" icon={ICONS.SUBMIT} iconColor="red" onClick={() => handleSubmit()} />
</div>
</Zoom>
</>
);
return (
<TextField inputRef={inputRef} variant="outlined" multiline minRows={1} select={false} {...autocompleteProps} />
);
}
return <TextField inputRef={inputRef} multiline select={false} {...autocompleteProps} />;
};
export default TextareaSuggestionsInput;

View file

@ -0,0 +1,24 @@
// @flow
import { EMOTES_48px as EMOTES } from 'constants/emotes';
import EMOJIS from 'emoji-dictionary';
import React from 'react';
import TextareaSuggestionsItem from 'component/textareaSuggestionsItem';
type Props = {
label: string,
isEmote?: boolean,
optionProps: any,
};
const TextareaSuggestionsOption = (props: Props) => {
const { label, isEmote, optionProps } = props;
const emoteFound = isEmote && EMOTES.find(({ name }) => name === label);
const emoteValue = emoteFound ? { name: label, url: emoteFound.url } : undefined;
const emojiFound = isEmote && EMOJIS.getUnicode(label);
const emojiValue = emojiFound ? { name: label, unicode: emojiFound } : undefined;
return <TextareaSuggestionsItem key={label} uri={label} emote={emoteValue || emojiValue} {...optionProps} />;
};
export default TextareaSuggestionsOption;

View file

@ -2,22 +2,18 @@
import { EMOTES_48px as EMOTES } from 'constants/emotes'; import { EMOTES_48px as EMOTES } from 'constants/emotes';
import { matchSorter } from 'match-sorter'; import { matchSorter } from 'match-sorter';
import { SEARCH_OPTIONS } from 'constants/search'; import { SEARCH_OPTIONS } from 'constants/search';
import * as ICONS from 'constants/icons';
import * as KEYCODES from 'constants/keycodes'; import * as KEYCODES from 'constants/keycodes';
import Autocomplete from '@mui/material/Autocomplete'; import Autocomplete from '@mui/material/Autocomplete';
import BusyIndicator from 'component/common/busy-indicator'; import BusyIndicator from 'component/common/busy-indicator';
import EMOJIS from 'emoji-dictionary'; import EMOJIS from 'emoji-dictionary';
import LbcSymbol from 'component/common/lbc-symbol';
import Popper from '@mui/material/Popper'; import Popper from '@mui/material/Popper';
import React from 'react'; import React from 'react';
import replaceAll from 'core-js-pure/features/string/replace-all';
import TextareaSuggestionsItem from 'component/textareaSuggestionsItem';
import TextField from '@mui/material/TextField';
import useLighthouse from 'effects/use-lighthouse'; import useLighthouse from 'effects/use-lighthouse';
import useThrottle from 'effects/use-throttle'; import useThrottle from 'effects/use-throttle';
import { parseURI } from 'util/lbryURI'; import { parseURI } from 'util/lbryURI';
import Button from 'component/button'; import TextareaSuggestionsOption from './render-option';
import { useIsMobile } from 'effects/use-screensize'; import TextareaSuggestionsInput from './render-input';
import TextareaSuggestionsGroup from './render-group';
const SUGGESTION_REGEX = new RegExp( const SUGGESTION_REGEX = new RegExp(
'((?:^| |\n)@[^\\s=&#$@%?:;/\\"<>%{}|^~[]*(?::[\\w]+)?)|((?:^| |\n):[\\w+-]*:?)', '((?:^| |\n)@[^\\s=&#$@%?:;/\\"<>%{}|^~[]*(?::[\\w]+)?)|((?:^| |\n):[\\w+-]*:?)',
@ -57,12 +53,11 @@ type Props = {
maxLength?: number, maxLength?: number,
placeholder?: string, placeholder?: string,
searchQuery?: string, searchQuery?: string,
showMature: boolean,
type?: string, type?: string,
uri?: string, uri?: string,
value: any, value: any,
doResolveUris: (Array<string>) => void, doResolveUris: (uris: Array<string>, cache: boolean) => void,
doSetMentionSearchResults: (string, Array<string>) => void, doSetMentionSearchResults: (query: string, uris: Array<string>) => void,
onBlur: (any) => any, onBlur: (any) => any,
onChange: (any) => any, onChange: (any) => any,
onFocus: (any) => any, onFocus: (any) => any,
@ -88,7 +83,6 @@ export default function TextareaWithSuggestions(props: Props) {
maxLength, maxLength,
placeholder, placeholder,
searchQuery, searchQuery,
showMature,
type, type,
value: messageValue, value: messageValue,
doResolveUris, doResolveUris,
@ -101,18 +95,15 @@ export default function TextareaWithSuggestions(props: Props) {
handleSubmit, handleSubmit,
} = props; } = props;
const isMobile = useIsMobile();
const inputDefaultProps = { className, placeholder, maxLength, type, disabled }; const inputDefaultProps = { className, placeholder, maxLength, type, disabled };
const [suggestionValue, setSuggestionValue] = React.useState(undefined); const [suggestionValue, setSuggestionValue] = React.useState(undefined);
const [highlightedSuggestion, setHighlightedSuggestion] = React.useState(''); const [highlightedSuggestion, setHighlightedSuggestion] = React.useState('');
const [shouldClose, setClose] = React.useState(); const [shouldClose, setClose] = React.useState();
const [debouncedTerm, setDebouncedTerm] = React.useState(''); const [debouncedTerm, setDebouncedTerm] = React.useState('');
// const [mostSupported, setMostSupported] = React.useState('');
const suggestionTerm = suggestionValue && suggestionValue.term; const suggestionTerm = suggestionValue && suggestionValue.term;
const isEmote = suggestionValue && suggestionValue.isEmote; const isEmote = Boolean(suggestionValue && suggestionValue.isEmote);
const isMention = suggestionValue && !suggestionValue.isEmote; const isMention = suggestionValue && !suggestionValue.isEmote;
let invalidTerm = suggestionTerm && isMention && suggestionTerm.charAt(1) === ':'; let invalidTerm = suggestionTerm && isMention && suggestionTerm.charAt(1) === ':';
@ -125,7 +116,7 @@ export default function TextareaWithSuggestions(props: Props) {
} }
const additionalOptions = { isBackgroundSearch: false, [SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_CHANNELS }; const additionalOptions = { isBackgroundSearch: false, [SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_CHANNELS };
const { results, loading } = useLighthouse(debouncedTerm, showMature, SEARCH_SIZE, additionalOptions, 0); const { results, loading } = useLighthouse(debouncedTerm, false, SEARCH_SIZE, additionalOptions, 0);
const stringifiedResults = JSON.stringify(results); const stringifiedResults = JSON.stringify(results);
const hasMinLength = suggestionTerm && isMention && suggestionTerm.length >= LIGHTHOUSE_MIN_CHARACTERS; const hasMinLength = suggestionTerm && isMention && suggestionTerm.length >= LIGHTHOUSE_MIN_CHARACTERS;
@ -180,7 +171,7 @@ export default function TextareaWithSuggestions(props: Props) {
let emoteLabel; let emoteLabel;
if (isEmote) { if (isEmote) {
// $FlowFixMe // $FlowFixMe
emoteLabel = `:${replaceAll(option, ':', '')}:`; emoteLabel = `:${option.replace(/:/g, '')}:`;
} }
return { return {
@ -316,14 +307,14 @@ export default function TextareaWithSuggestions(props: Props) {
const arrayResults = JSON.parse(stringifiedResults); const arrayResults = JSON.parse(stringifiedResults);
if (debouncedTerm && arrayResults && arrayResults.length > 0) { if (debouncedTerm && arrayResults && arrayResults.length > 0) {
doResolveUris([debouncedTerm, ...arrayResults]); doResolveUris([debouncedTerm, ...arrayResults], true);
doSetMentionSearchResults(debouncedTerm, arrayResults); doSetMentionSearchResults(debouncedTerm, arrayResults);
} }
}, [debouncedTerm, doResolveUris, doSetMentionSearchResults, stringifiedResults, suggestionTerm]); }, [debouncedTerm, doResolveUris, doSetMentionSearchResults, stringifiedResults, suggestionTerm]);
// Only resolve commentors on Livestreams when first trying to mention/looking for it // Only resolve commentors on Livestreams when first trying to mention/looking for it
React.useEffect(() => { React.useEffect(() => {
if (isLivestream && commentorUris && suggestionTerm) doResolveUris(commentorUris); if (isLivestream && commentorUris && suggestionTerm) doResolveUris(commentorUris, true);
}, [commentorUris, doResolveUris, isLivestream, suggestionTerm]); }, [commentorUris, doResolveUris, isLivestream, suggestionTerm]);
// Allow selecting with TAB key // Allow selecting with TAB key
@ -371,58 +362,6 @@ export default function TextareaWithSuggestions(props: Props) {
/** Render **/ /** Render **/
/** ------ **/ /** ------ **/
const renderGroup = (groupName: string, children: any) => (
<div key={groupName} className="textareaSuggestions__group">
<label className="textareaSuggestions__label">
{groupName === 'Top' ? (
<LbcSymbol prefix={__('Winning Search for %matching_term%', { matching_term: searchQuery })} />
) : suggestionTerm && suggestionTerm.length > 1 ? (
__('%group_name% matching %matching_term%', { group_name: groupName, matching_term: suggestionTerm })
) : (
groupName
)}
</label>
{children}
<hr className="textareaSuggestions__topSeparator" />
</div>
);
const renderInput = (params: any) => {
const { InputProps, disabled, fullWidth, id, inputProps: autocompleteInputProps } = params;
if (isMobile) {
InputProps.startAdornment = <Button icon={ICONS.STICKER} onClick={handleEmojis} />;
InputProps.endAdornment = (
<>
<Button icon={ICONS.LBC} onClick={() => handleTip(true)} />
<Button icon={ICONS.FINANCE} onClick={() => handleTip(false)} />
{messageValue && messageValue.length > 0 && (
<Button button="primary" icon={ICONS.SUBMIT} iconColor="red" onClick={() => handleSubmit()} />
)}
</>
);
}
const inputProps = { ...autocompleteInputProps, ...inputDefaultProps };
const autocompleteProps = { InputProps, disabled, fullWidth, id, inputProps };
return !isMobile ? (
<TextField inputRef={inputRef} multiline select={false} {...autocompleteProps} />
) : (
<TextField inputRef={inputRef} variant="outlined" multiline minRows={1} select={false} {...autocompleteProps} />
);
};
const renderOption = (optionProps: any, label: string) => {
const emoteFound = isEmote && EMOTES.find(({ name }) => name === label);
const emoteValue = emoteFound ? { name: label, url: emoteFound.url } : undefined;
const emojiFound = isEmote && EMOJIS.getUnicode(label);
const emojiValue = emojiFound ? { name: label, unicode: emojiFound } : undefined;
return <TextareaSuggestionsItem key={label} uri={label} emote={emoteValue || emojiValue} {...optionProps} />;
};
return ( return (
<Autocomplete <Autocomplete
PopperComponent={AutocompletePopper} PopperComponent={AutocompletePopper}
@ -450,16 +389,30 @@ export default function TextareaWithSuggestions(props: Props) {
or else it will be displayed all the time as empty (no options) */ or else it will be displayed all the time as empty (no options) */
open={!!suggestionTerm && !shouldClose} open={!!suggestionTerm && !shouldClose}
options={allOptionsGrouped} options={allOptionsGrouped}
renderGroup={({ group, children }) => renderGroup(group, children)} renderGroup={({ group, children }) => (
renderInput={(params) => renderInput(params)} <TextareaSuggestionsGroup groupName={group} suggestionTerm={suggestionTerm} searchQuery={searchQuery}>
renderOption={(optionProps, option) => renderOption(optionProps, option.label)} {children}
</TextareaSuggestionsGroup>
)}
renderInput={(params) => (
<TextareaSuggestionsInput
params={params}
messageValue={messageValue}
inputRef={inputRef}
inputDefaultProps={inputDefaultProps}
handleEmojis={handleEmojis}
handleTip={handleTip}
handleSubmit={handleSubmit}
/>
)}
renderOption={(optionProps, option) => (
<TextareaSuggestionsOption label={option.label} isEmote={isEmote} optionProps={optionProps} />
)}
/> />
); );
} }
function AutocompletePopper(props: any) { const AutocompletePopper = (props: any) => <Popper {...props} placement="top" />;
return <Popper {...props} placement="top" />;
}
function useSuggestionMatch(term: string, list: Array<string>) { function useSuggestionMatch(term: string, list: Array<string>) {
const throttledTerm = useThrottle(term); const throttledTerm = useThrottle(term);

View file

@ -23,6 +23,15 @@ $thumbnailWidthSmall: 1rem;
} }
} }
@media (max-width: $breakpoint-small) {
.commentCreate + .empty__wrap {
p {
font-size: var(--font-small);
text-align: center;
}
}
}
.commentCreate--reply { .commentCreate--reply {
margin-top: var(--spacing-m); margin-top: var(--spacing-m);
position: relative; position: relative;
@ -41,7 +50,7 @@ $thumbnailWidthSmall: 1rem;
padding-bottom: 0; padding-bottom: 0;
} }
.commentCreate__labelWrapper { .comment-create__label-wrapper {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
@ -49,15 +58,19 @@ $thumbnailWidthSmall: 1rem;
flex-wrap: wrap; flex-wrap: wrap;
width: 100%; width: 100%;
.commentCreate__label { .comment-create__label {
white-space: nowrap; white-space: nowrap;
margin-right: var(--spacing-xs); margin-right: var(--spacing-xs);
} }
@media (min-width: $breakpoint-small) {
fieldset-section { fieldset-section {
max-width: 10rem; max-width: 10rem;
} }
@media (max-width: $breakpoint-small) {
fieldset-section {
font-size: var(--font-xxsmall);
}
} }
} }

View file

@ -480,6 +480,12 @@ fieldset-group {
@media (min-width: $breakpoint-small) { @media (min-width: $breakpoint-small) {
column-count: 2; column-count: 2;
} }
@media (max-width: $breakpoint-small) {
span {
font-size: var(--font-xxsmall);
}
}
} }
.form-field__quick-action { .form-field__quick-action {

View file

@ -25,9 +25,19 @@
font-size: var(--font-xsmall) !important; font-size: var(--font-xsmall) !important;
flex-wrap: nowrap !important; flex-wrap: nowrap !important;
color: var(--color-text) !important; color: var(--color-text) !important;
padding: 0px 9px !important;
textarea { textarea {
border: none; border: none;
margin: 9px 0px;
}
button:not(:first-of-type):not(:last-of-type) {
margin: 0px var(--spacing-xxs);
}
button + div {
margin-left: var(--spacing-xxs);
} }
.button--primary { .button--primary {
@ -52,12 +62,12 @@
box-shadow: var(--card-box-shadow); box-shadow: var(--card-box-shadow);
color: var(--color-text) !important; color: var(--color-text) !important;
.textareaSuggestions__group { .textarea-suggestions__group {
&:last-child hr { &:last-child hr {
display: none; display: none;
} }
.textareaSuggestions__label { .textarea-suggestions__group-label {
@extend .wunderbar__label; @extend .wunderbar__label;
} }
@ -97,23 +107,23 @@
} }
} }
.textareaSuggestion__label { .textarea-suggestion__label {
@extend .wunderbar__suggestion-label; @extend .wunderbar__suggestion-label;
margin-left: var(--spacing-m); margin-left: var(--spacing-m);
display: block; display: block;
position: relative; position: relative;
.textareaSuggestion__title { .textarea-suggestion__title {
@extend .wunderbar__suggestion-title; @extend .wunderbar__suggestion-title;
} }
.textareaSuggestion__value { .textarea-suggestion__value {
@extend .wunderbar__suggestion-name; @extend .wunderbar__suggestion-name;
} }
} }
} }
.textareaSuggestions__topSeparator { .textarea-suggestions__separator {
@extend .wunderbar__top-separator; @extend .wunderbar__top-separator;
} }