diff --git a/app/assets/javascripts/character_count.js b/app/assets/javascripts/character_count.js index 13287284b..cf3a8b7a9 100644 --- a/app/assets/javascripts/character_count.js +++ b/app/assets/javascripts/character_count.js @@ -1,60 +1,89 @@ $(() => { - const setIcon = (el, icon) => { + /** + * @typedef {'fa-ellipsis-h'|'fa-times'|'fa-exclamation-circle'|'fa-check'} CounterIcon + * @typedef {'info'|'warning'|'error'|'default'} CounterState + * @typedef {'valid'|'invalid'} InputValidationState + * @typedef {'disabled'|'enabled'} SubmitButtonDisabledState + */ + + /** + * Sets the icon to show before the counter, if any + * @param {CounterIcon} icon name of the icon to show + */ + const setCounterIcon = (el, icon) => { const icons = ['fa-ellipsis-h', 'fa-check', 'fa-exclamation-circle', 'fa-times']; el.removeClass(icons.join(' ')).addClass(icon); }; - $(document).on('keyup change paste', '[data-character-count]', ev => { + /** + * Sets the counter's state + * @param {CounterState} state the state to set + */ + const setCounterState = (el, state) => { + if (state === 'info') { + el.removeClass('has-color-yellow-700 has-color-red-500').addClass('has-color-primary'); + } + else if (state === 'warning') { + el.removeClass('has-color-red-500 has-color-primary').addClass('has-color-yellow-700'); + } + else if (state === 'error') { + el.removeClass('has-color-yellow-700 has-color-primary').addClass('has-color-red-500'); + } + else { + el.removeClass('has-color-red-500 has-color-yellow-700 has-color-primary'); + } + }; + + /** + * Sets the input's validation state + * @param {InputValidationState} state the state to set + */ + const setInputValidationState = (el, state) => { + const isInvalid = state === 'invalid'; + el.toggleClass('failed-validation', isInvalid); + }; + + /** + * Sets the submit button's disabled state + * @param {SubmitButtonDisabledState} state the state to set + */ + const setSubmitButtonDisabledState = (el, state) => { + const isDisabled = state === 'disabled'; + el.attr('disabled', isDisabled).toggleClass('is-muted', isDisabled); + }; + + $(document).on('keyup change paste', '[data-character-count]', (ev) => { const $tgt = $(ev.target); const $counter = $($tgt.attr('data-character-count')); const $button = $counter.parents('form').find('input[type="submit"]'); const $count = $counter.find('.js-character-count__count'); const $icon = $counter.find('.js-character-count__icon'); - const displayAt = parseFloat($counter.attr('data-display-at')); + const count = $tgt.val().length; const max = parseInt($counter.attr('data-max'), 10); const min = parseInt($counter.attr('data-min'), 10); - const count = $tgt.val().length; - const text = `${count} / ${max}`; + const threshold = parseFloat($counter.attr('data-threshold')); - if (displayAt) { - if (count >= displayAt * max) { - $counter.removeClass('hide'); - } - else { - $counter.addClass('hide'); - } - } + const gtnMax = count > max; + const ltnMin = count < min; + const gteThreshold = count >= threshold * max; - if (count > max) { - $counter.removeClass('has-color-yellow-700 has-color-primary').addClass('has-color-red-500'); - setIcon($icon, 'fa-times'); - if ($button) { - $button.attr('disabled', true).addClass('is-muted'); - } - } - else if (count > 0.75 * max) { - $counter.removeClass('has-color-red-500 has-color-primary').addClass('has-color-yellow-700'); - setIcon($icon, 'fa-exclamation-circle'); - if ($button) { - $button.attr('disabled', false).removeClass('is-muted'); - } - } - else if (min && count < min) { - $counter.removeClass('has-color-yellow-700 has-color-red-500').addClass('has-color-primary'); - setIcon($icon, 'fa-ellipsis-h'); - if ($button) { - $button.attr('disabled', true).addClass('is-muted'); - } - $tgt.addClass('failed-validation'); - } - else { - $counter.removeClass('has-color-red-500 has-color-yellow-700 has-color-primary'); - setIcon($icon, 'fa-check'); - if ($button) { - $button.attr('disabled', false).removeClass('is-muted'); - } - $tgt.removeClass('failed-validation'); + const text = `${count} / ${ltnMin ? min : max}`; + + if (gtnMax || ltnMin) { + setCounterState($counter, 'error'); + setCounterIcon($icon, 'fa-times'); + setSubmitButtonDisabledState($button, 'disabled'); + setInputValidationState($tgt, 'invalid'); + } else if (gteThreshold) { + setCounterState($counter, 'warning'); + setCounterIcon($icon, 'fa-exclamation-circle'); + setSubmitButtonDisabledState($button, 'enabled'); + } else { + setCounterState($counter, 'default'); + setCounterIcon($icon, 'fa-check'); + setSubmitButtonDisabledState($button, 'enabled'); + setInputValidationState($tgt, 'valid'); } $count.text(text); diff --git a/app/views/comments/_new_thread_modal.html.erb b/app/views/comments/_new_thread_modal.html.erb index 5ba122f28..8ea24575f 100644 --- a/app/views/comments/_new_thread_modal.html.erb +++ b/app/views/comments/_new_thread_modal.html.erb @@ -20,11 +20,7 @@
<%= text_area_tag :body, '', class: 'form-element js-comment-field', required: true, data: { post: post.id, thread: '-1', character_count: ".js-character-count-#{post.id}" } %> - + <%= render 'shared/char_count', type: post.id, min: 15, max: 1000 %> <%= label_tag :title, 'Comment thread title (optional)', class: 'form-element' %> <%= text_field_tag :title, '', class: 'form-element', data: { character_count: ".js-character-count-thread-title" } %> - - + <%= render 'shared/char_count', type: 'thread-title' %> <%= submit_tag 'Create thread', class: 'button is-filled', id: "create_thread_button_#{post.id}", disabled: true %> <% end %> diff --git a/app/views/comments/thread.html.erb b/app/views/comments/thread.html.erb index a9a2faf2f..98aa32ebd 100644 --- a/app/views/comments/thread.html.erb +++ b/app/views/comments/thread.html.erb @@ -121,12 +121,8 @@ <%= text_area_tag :content, '', class: 'form-element js-comment-field', data: { thread: @comment_thread.id, post: @comment_thread.post_id, character_count: ".js-character-count-#{@post.id}" } %> - - + <%= render 'shared/char_count', type: @post.id, min: 15, max: 1000 %> + <%= submit_tag 'Add reply', class: 'button is-muted is-filled', disabled:true %> <% end %> <% end %> diff --git a/app/views/posts/_form.html.erb b/app/views/posts/_form.html.erb index 92a345698..f126fd627 100644 --- a/app/views/posts/_form.html.erb +++ b/app/views/posts/_form.html.erb @@ -73,14 +73,9 @@ <%= f.text_field :title, class: 'form-element post_title', data: { character_count: ".js-character-count-post-title" } %>