th-downstream
commit
7e54a30f06
@ -1,60 +0,0 @@
|
||||
// @ts-check
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
/**
|
||||
* Returns custom renderer for one of the common counter types
|
||||
* @param {"statuses" | "following" | "followers"} counterType
|
||||
* Type of the counter
|
||||
* @param {boolean} isBold Whether display number must be displayed in bold
|
||||
* @returns {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element}
|
||||
* Renderer function
|
||||
* @throws If counterType is not covered by this function
|
||||
*/
|
||||
export function counterRenderer(counterType, isBold = true) {
|
||||
/**
|
||||
* @type {(displayNumber: JSX.Element) => JSX.Element}
|
||||
*/
|
||||
const renderCounter = isBold
|
||||
? (displayNumber) => <strong>{displayNumber}</strong>
|
||||
: (displayNumber) => displayNumber;
|
||||
|
||||
switch (counterType) {
|
||||
case 'statuses': {
|
||||
return (displayNumber, pluralReady) => (
|
||||
<FormattedMessage
|
||||
id='account.statuses_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: renderCounter(displayNumber),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'following': {
|
||||
return (displayNumber, pluralReady) => (
|
||||
<FormattedMessage
|
||||
id='account.following_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: renderCounter(displayNumber),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'followers': {
|
||||
return (displayNumber, pluralReady) => (
|
||||
<FormattedMessage
|
||||
id='account.followers_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: renderCounter(displayNumber),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: throw Error(`Incorrect counter name: ${counterType}. Ensure it accepted by commonCounter function`);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export const StatusesCounter = (
|
||||
displayNumber: React.ReactNode,
|
||||
pluralReady: number
|
||||
) => (
|
||||
<FormattedMessage
|
||||
id='account.statuses_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: <strong>{displayNumber}</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FollowingCounter = (
|
||||
displayNumber: React.ReactNode,
|
||||
pluralReady: number
|
||||
) => (
|
||||
<FormattedMessage
|
||||
id='account.following_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: <strong>{displayNumber}</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const FollowersCounter = (
|
||||
displayNumber: React.ReactNode,
|
||||
pluralReady: number
|
||||
) => (
|
||||
<FormattedMessage
|
||||
id='account.followers_counter'
|
||||
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
||||
values={{
|
||||
count: pluralReady,
|
||||
counter: <strong>{displayNumber}</strong>,
|
||||
}}
|
||||
/>
|
||||
);
|
@ -1,55 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { bannerSettings } from 'mastodon/settings';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||
});
|
||||
|
||||
class DismissableBanner extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: !bannerSettings.get(this.props.id),
|
||||
};
|
||||
|
||||
handleDismiss = () => {
|
||||
const { id } = this.props;
|
||||
this.setState({ visible: false }, () => bannerSettings.set(id, true));
|
||||
};
|
||||
|
||||
render () {
|
||||
const { visible } = this.state;
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { children, intl } = this.props;
|
||||
|
||||
return (
|
||||
<div className='dismissable-banner'>
|
||||
<div className='dismissable-banner__message'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<div className='dismissable-banner__action'>
|
||||
<IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(DismissableBanner);
|
@ -0,0 +1,47 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { bannerSettings } from 'mastodon/settings';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||
});
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
||||
id,
|
||||
children,
|
||||
}) => {
|
||||
const [visible, setVisible] = useState(!bannerSettings.get(id));
|
||||
const intl = useIntl();
|
||||
|
||||
const handleDismiss = useCallback(() => {
|
||||
setVisible(false);
|
||||
bannerSettings.set(id, true);
|
||||
}, [id]);
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='dismissable-banner'>
|
||||
<div className='dismissable-banner__message'>{children}</div>
|
||||
|
||||
<div className='dismissable-banner__action'>
|
||||
<IconButton
|
||||
icon='times'
|
||||
title={intl.formatMessage(messages.dismiss)}
|
||||
onClick={handleDismiss}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,115 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
|
||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @callback ShortNumberRenderer
|
||||
* @param {JSX.Element} displayNumber Number to display
|
||||
* @param {number} pluralReady Number used for pluralization
|
||||
* @returns {JSX.Element} Final render of number
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ShortNumberProps
|
||||
* @property {number} value Number to display in short variant
|
||||
* @property {ShortNumberRenderer} [renderer]
|
||||
* Custom renderer for numbers, provided as a prop. If another renderer
|
||||
* passed as a child of this component, this prop won't be used.
|
||||
* @property {ShortNumberRenderer} [children]
|
||||
* Custom renderer for numbers, provided as a child. If another renderer
|
||||
* passed as a prop of this component, this one will be used instead.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Component that renders short big number to a shorter version
|
||||
* @param {ShortNumberProps} param0 Props for the component
|
||||
* @returns {JSX.Element} Rendered number
|
||||
*/
|
||||
function ShortNumber({ value, renderer, children }) {
|
||||
const shortNumber = toShortNumber(value);
|
||||
const [, division] = shortNumber;
|
||||
|
||||
if (children != null && renderer != null) {
|
||||
console.warn('Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.');
|
||||
}
|
||||
|
||||
const customRenderer = children != null ? children : renderer;
|
||||
|
||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
||||
|
||||
return customRenderer != null
|
||||
? customRenderer(displayNumber, pluralReady(value, division))
|
||||
: displayNumber;
|
||||
}
|
||||
|
||||
ShortNumber.propTypes = {
|
||||
value: PropTypes.number.isRequired,
|
||||
renderer: PropTypes.func,
|
||||
children: PropTypes.func,
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {object} ShortNumberCounterProps
|
||||
* @property {import('../utils/number').ShortNumber} value Short number
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders short number into corresponding localizable react fragment
|
||||
* @param {ShortNumberCounterProps} param0 Props for the component
|
||||
* @returns {JSX.Element} FormattedMessage ready to be embedded in code
|
||||
*/
|
||||
function ShortNumberCounter({ value }) {
|
||||
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
||||
|
||||
const count = (
|
||||
<FormattedNumber
|
||||
value={rawNumber}
|
||||
maximumFractionDigits={maxFractionDigits}
|
||||
/>
|
||||
);
|
||||
|
||||
let values = { count, rawNumber };
|
||||
|
||||
switch (unit) {
|
||||
case DECIMAL_UNITS.THOUSAND: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.thousand'
|
||||
defaultMessage='{count}K'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case DECIMAL_UNITS.MILLION: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.million'
|
||||
defaultMessage='{count}M'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case DECIMAL_UNITS.BILLION: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.billion'
|
||||
defaultMessage='{count}B'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// Not sure if we should go farther - @Sasha-Sorokin
|
||||
default: return count;
|
||||
}
|
||||
}
|
||||
|
||||
ShortNumberCounter.propTypes = {
|
||||
value: PropTypes.arrayOf(PropTypes.number),
|
||||
};
|
||||
|
||||
export default memo(ShortNumber);
|
@ -0,0 +1,90 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
|
||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
||||
|
||||
type ShortNumberRenderer = (
|
||||
displayNumber: JSX.Element,
|
||||
pluralReady: number
|
||||
) => JSX.Element;
|
||||
|
||||
interface ShortNumberProps {
|
||||
value: number;
|
||||
renderer?: ShortNumberRenderer;
|
||||
children?: ShortNumberRenderer;
|
||||
}
|
||||
|
||||
export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({
|
||||
value,
|
||||
renderer,
|
||||
children,
|
||||
}) => {
|
||||
const shortNumber = toShortNumber(value);
|
||||
const [, division] = shortNumber;
|
||||
|
||||
if (children && renderer) {
|
||||
console.warn(
|
||||
'Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.'
|
||||
);
|
||||
}
|
||||
|
||||
const customRenderer = children || renderer || null;
|
||||
|
||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
||||
|
||||
return (
|
||||
customRenderer?.(displayNumber, pluralReady(value, division)) ||
|
||||
displayNumber
|
||||
);
|
||||
};
|
||||
export const ShortNumber = memo(ShortNumberRenderer);
|
||||
|
||||
interface ShortNumberCounterProps {
|
||||
value: number[];
|
||||
}
|
||||
const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
|
||||
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
||||
|
||||
const count = (
|
||||
<FormattedNumber
|
||||
value={rawNumber}
|
||||
maximumFractionDigits={maxFractionDigits}
|
||||
/>
|
||||
);
|
||||
|
||||
const values = { count, rawNumber };
|
||||
|
||||
switch (unit) {
|
||||
case DECIMAL_UNITS.THOUSAND: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.thousand'
|
||||
defaultMessage='{count}K'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case DECIMAL_UNITS.MILLION: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.million'
|
||||
defaultMessage='{count}M'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case DECIMAL_UNITS.BILLION: {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='units.short.billion'
|
||||
defaultMessage='{count}B'
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// Not sure if we should go farther - @Sasha-Sorokin
|
||||
default:
|
||||
return count;
|
||||
}
|
||||
};
|
Loading…
Reference in new issue