diff --git a/app/soapbox/components/progress_circle.js b/app/soapbox/components/progress_circle.js
new file mode 100644
index 000000000..da20515c1
--- /dev/null
+++ b/app/soapbox/components/progress_circle.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+
+class ProgressCircle extends React.PureComponent {
+
+ static defaultProps = {
+ radius: 12,
+ stroke: 4,
+ }
+
+ render() {
+ const { progress, radius, stroke, title } = this.props;
+
+ const progressStroke = stroke + 0.5;
+ const actualRadius = radius + progressStroke;
+ const circumference = 2 * Math.PI * radius;
+ const dashoffset = circumference * (1 - Math.min(progress, 1));
+
+ return (
+
1 })} title={title}>
+
+
+ );
+ }
+
+}
+
+ProgressCircle.propTypes = {
+ progress: PropTypes.number.isRequired,
+ radius: PropTypes.number,
+ stroke: PropTypes.number,
+ title: PropTypes.text,
+};
+
+export default ProgressCircle;
diff --git a/app/soapbox/features/compose/components/character_counter.js b/app/soapbox/features/compose/components/character_counter.js
index d056a8240..4468b9cd6 100644
--- a/app/soapbox/features/compose/components/character_counter.js
+++ b/app/soapbox/features/compose/components/character_counter.js
@@ -1,25 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { injectIntl, defineMessages } from 'react-intl';
import { length } from 'stringz';
+import ProgressCircle from 'soapbox/components/progress_circle';
-export default class CharacterCounter extends React.PureComponent {
+const messages = defineMessages({
+ title: { id: 'compose.character_counter.title', defaultMessage: 'Used {chars} out of {maxChars} characters' },
+});
- static propTypes = {
- text: PropTypes.string.isRequired,
- max: PropTypes.number.isRequired,
- };
-
- checkRemainingText(diff) {
- if (diff < 0) {
- return {diff};
- }
-
- return {diff};
- }
+/**
+ * Renders a character counter
+ * @param {string} props.text - text to use to measure
+ * @param {number} props.max - max text allowed
+ */
+class CharacterCounter extends React.PureComponent {
render() {
- const diff = this.props.max - length(this.props.text);
- return this.checkRemainingText(diff);
+ const { intl, text, max } = this.props;
+
+ const textLength = length(text);
+ const progress = textLength / max;
+
+ return (
+
+ );
}
}
+
+CharacterCounter.propTypes = {
+ intl: PropTypes.object.isRequired,
+ text: PropTypes.string.isRequired,
+ max: PropTypes.number.isRequired,
+};
+
+export default injectIntl(CharacterCounter);
diff --git a/app/styles/application.scss b/app/styles/application.scss
index 3503b7044..fbb50eadf 100644
--- a/app/styles/application.scss
+++ b/app/styles/application.scss
@@ -88,6 +88,7 @@
@import 'components/aliases';
@import 'components/icon';
@import 'components/profile-stats';
+@import 'components/progress-circle';
// Holiday
@import 'holiday/halloween';
diff --git a/app/styles/components/compose-form.scss b/app/styles/components/compose-form.scss
index de46ea001..4b015b878 100644
--- a/app/styles/components/compose-form.scss
+++ b/app/styles/components/compose-form.scss
@@ -383,15 +383,6 @@
.character-counter__wrapper {
align-self: center;
margin: 0 10px 0 auto;
-
- .character-counter {
- cursor: default;
- font-family: var(--font-sans-serif), sans-serif;
- font-size: 14px;
- font-weight: 600;
- color: var(--primary-text-color--faint);
- &.character-counter--over { color: $warning-red; }
- }
}
}
diff --git a/app/styles/components/progress-circle.scss b/app/styles/components/progress-circle.scss
new file mode 100644
index 000000000..ee96af008
--- /dev/null
+++ b/app/styles/components/progress-circle.scss
@@ -0,0 +1,17 @@
+.progress-circle {
+ display: flex;
+
+ &__circle {
+ stroke: hsla(var(--primary-text-color_hsl), 0.2);
+ }
+
+ &__progress {
+ stroke: var(--brand-color);
+ }
+
+ &--over {
+ .progress-circle__progress {
+ stroke: $warning-red;
+ }
+ }
+}