parent
4f358b9632
commit
98ff406ddf
@ -1,20 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import Account from '../../../components/account';
|
|
||||||
import { makeGetAccount } from '../../../selectors';
|
|
||||||
|
|
||||||
const AutosuggestAccount = ({ id }) => {
|
|
||||||
const getAccount = makeGetAccount();
|
|
||||||
const account = useSelector((state) => getAccount(state, id));
|
|
||||||
|
|
||||||
return <Account account={account} hideActions showProfileHoverCard={false} />;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AutosuggestAccount.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AutosuggestAccount;
|
|
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Account from 'soapbox/components/account';
|
||||||
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
|
||||||
|
interface IAutosuggestAccount {
|
||||||
|
id: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AutosuggestAccount: React.FC<IAutosuggestAccount> = ({ id }) => {
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
const account = useAppSelector((state) => getAccount(state, id));
|
||||||
|
|
||||||
|
if (!account) return null;
|
||||||
|
|
||||||
|
return <Account account={account} hideActions showProfileHoverCard={false} />;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AutosuggestAccount;
|
@ -1,31 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { length } from 'stringz';
|
|
||||||
|
|
||||||
export default class TextCharacterCounter extends React.PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
text: PropTypes.string.isRequired,
|
|
||||||
max: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
checkRemainingText(diff) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={classNames('text-sm font-semibold', {
|
|
||||||
'text-gray-400': diff >= 0,
|
|
||||||
'text-danger-600': diff < 0,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{diff}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const diff = this.props.max - length(this.props.text);
|
|
||||||
return this.checkRemainingText(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,28 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
import { length } from 'stringz';
|
||||||
|
|
||||||
|
interface ITextCharacterCounter {
|
||||||
|
max: number,
|
||||||
|
text: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextCharacterCounter: React.FC<ITextCharacterCounter> = ({ text, max }) => {
|
||||||
|
const checkRemainingText = (diff: number) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classNames('text-sm font-semibold', {
|
||||||
|
'text-gray-400': diff >= 0,
|
||||||
|
'text-danger-600': diff < 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{diff}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const diff = max - length(text);
|
||||||
|
return checkRemainingText(diff);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextCharacterCounter;
|
@ -1,37 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
|
|
||||||
// import SensitiveButtonContainer from '../containers/sensitive_button_container';
|
|
||||||
import UploadProgress from '../components/upload-progress';
|
|
||||||
import UploadContainer from '../containers/upload_container';
|
|
||||||
|
|
||||||
export default class UploadForm extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
mediaIds: ImmutablePropTypes.list.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { mediaIds } = this.props;
|
|
||||||
const classes = classNames('compose-form__uploads-wrapper', {
|
|
||||||
'contains-media': mediaIds.size !== 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='compose-form__upload-wrapper'>
|
|
||||||
<UploadProgress />
|
|
||||||
|
|
||||||
<div className={classes}>
|
|
||||||
{mediaIds.map(id => (
|
|
||||||
<UploadContainer id={id} key={id} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* {!mediaIds.isEmpty() && <SensitiveButtonContainer />} */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import type { Attachment as AttachmentEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
// import SensitiveButtonContainer from '../containers/sensitive_button_container';
|
||||||
|
import UploadProgress from '../components/upload-progress';
|
||||||
|
import UploadContainer from '../containers/upload_container';
|
||||||
|
|
||||||
|
const UploadForm = () => {
|
||||||
|
const mediaIds = useAppSelector((state) => state.compose.get('media_attachments').map((item: AttachmentEntity) => item.get('id')));
|
||||||
|
const classes = classNames('compose-form__uploads-wrapper', {
|
||||||
|
'contains-media': mediaIds.size !== 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='compose-form__upload-wrapper'>
|
||||||
|
<UploadProgress />
|
||||||
|
|
||||||
|
<div className={classes}>
|
||||||
|
{mediaIds.map((id: string) => (
|
||||||
|
<UploadContainer id={id} key={id} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* {!mediaIds.isEmpty() && <SensitiveButtonContainer />} */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UploadForm;
|
@ -1,43 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
|
||||||
import { length } from 'stringz';
|
|
||||||
|
|
||||||
import ProgressCircle from 'soapbox/components/progress_circle';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
title: { id: 'compose.character_counter.title', defaultMessage: 'Used {chars} out of {maxChars} characters' },
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a character counter
|
|
||||||
* @param {string} props.text - text to use to measure
|
|
||||||
* @param {number} props.max - max text allowed
|
|
||||||
*/
|
|
||||||
class VisualCharacterCounter extends React.PureComponent {
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl, text, max } = this.props;
|
|
||||||
|
|
||||||
const textLength = length(text);
|
|
||||||
const progress = textLength / max;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ProgressCircle
|
|
||||||
title={intl.formatMessage(messages.title, { chars: textLength, maxChars: max })}
|
|
||||||
progress={progress}
|
|
||||||
radius={10}
|
|
||||||
stroke={3}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
VisualCharacterCounter.propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
text: PropTypes.string.isRequired,
|
|
||||||
max: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default injectIntl(VisualCharacterCounter);
|
|
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import { length } from 'stringz';
|
||||||
|
|
||||||
|
import ProgressCircle from 'soapbox/components/progress_circle';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'compose.character_counter.title', defaultMessage: 'Used {chars} out of {maxChars} characters' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IVisualCharacterCounter {
|
||||||
|
/** max text allowed */
|
||||||
|
max: number,
|
||||||
|
/** text to use to measure */
|
||||||
|
text: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Renders a character counter */
|
||||||
|
const VisualCharacterCounter: React.FC<IVisualCharacterCounter> = ({ text, max }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const textLength = length(text);
|
||||||
|
const progress = textLength / max;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProgressCircle
|
||||||
|
title={intl.formatMessage(messages.title, { chars: textLength, maxChars: max })}
|
||||||
|
progress={progress}
|
||||||
|
radius={10}
|
||||||
|
stroke={3}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VisualCharacterCounter;
|
@ -1,16 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { makeGetAccount } from '../../../selectors';
|
|
||||||
import AutosuggestAccount from '../components/autosuggest_account';
|
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
|
||||||
const getAccount = makeGetAccount();
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { id }) => ({
|
|
||||||
account: getAccount(state, id),
|
|
||||||
});
|
|
||||||
|
|
||||||
return mapStateToProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(makeMapStateToProps)(AutosuggestAccount);
|
|
@ -1,9 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import UploadForm from '../components/upload_form';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(UploadForm);
|
|
@ -1,59 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
collapse: { id: 'accordion.collapse', defaultMessage: 'Collapse' },
|
|
||||||
expand: { id: 'accordion.expand', defaultMessage: 'Expand' },
|
|
||||||
});
|
|
||||||
|
|
||||||
export default @injectIntl class Accordion extends React.PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
headline: PropTypes.node.isRequired,
|
|
||||||
children: PropTypes.oneOfType([PropTypes.string, PropTypes.element, PropTypes.node]),
|
|
||||||
menu: PropTypes.array,
|
|
||||||
expanded: PropTypes.bool,
|
|
||||||
onToggle: PropTypes.func,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
expanded: false,
|
|
||||||
onToggle: () => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
handleToggle = (e) => {
|
|
||||||
this.props.onToggle(!this.props.expanded);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { headline, children, menu, expanded, intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames('accordion', { 'accordion--expanded': expanded })}>
|
|
||||||
{menu && (
|
|
||||||
<div className='accordion__menu'>
|
|
||||||
<DropdownMenu items={menu} src={require('@tabler/icons/icons/dots-vertical.svg')} direction='right' />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
className='accordion__title'
|
|
||||||
onClick={this.handleToggle}
|
|
||||||
title={intl.formatMessage(expanded ? messages.collapse : messages.expand)}
|
|
||||||
>
|
|
||||||
{headline}
|
|
||||||
</button>
|
|
||||||
<div className='accordion__content'>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,52 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
|
||||||
|
|
||||||
|
import type { Menu } from 'soapbox/components/dropdown_menu';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
collapse: { id: 'accordion.collapse', defaultMessage: 'Collapse' },
|
||||||
|
expand: { id: 'accordion.expand', defaultMessage: 'Expand' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IAccordion {
|
||||||
|
headline: React.ReactNode,
|
||||||
|
children?: string | React.ReactNode,
|
||||||
|
menu?: Menu,
|
||||||
|
expanded?: boolean,
|
||||||
|
onToggle?: (value: boolean) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Accordion: React.FC<IAccordion> = ({ headline, children, menu, expanded = false, onToggle = () => {} }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleToggle = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
onToggle(!expanded);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('accordion', { 'accordion--expanded': expanded })}>
|
||||||
|
{menu && (
|
||||||
|
<div className='accordion__menu'>
|
||||||
|
<DropdownMenu items={menu} src={require('@tabler/icons/icons/dots-vertical.svg')} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='accordion__title'
|
||||||
|
onClick={handleToggle}
|
||||||
|
title={intl.formatMessage(expanded ? messages.collapse : messages.expand)}
|
||||||
|
>
|
||||||
|
{headline}
|
||||||
|
</button>
|
||||||
|
<div className='accordion__content'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Accordion;
|
Loading…
Reference in new issue