Merge pull request #736 from Retrospring/feature/stimulus-character-count

This commit is contained in:
Andreas Nedbal 2022-11-03 19:22:57 +01:00 committed by GitHub
commit 8eb53d39c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 87 additions and 37 deletions

View file

@ -1,5 +1,8 @@
require('../styles/application.scss');
import { Application } from '@hotwired/stimulus';
import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers';
import start from 'retrospring/common';
import initAnnouncements from 'retrospring/features/announcement';
import initAnswerbox from 'retrospring/features/answerbox/index';
@ -27,3 +30,7 @@ document.addEventListener('DOMContentLoaded', initMemes);
document.addEventListener('turbo:load', initAnnouncements);
document.addEventListener('turbo:load', initLocales);
document.addEventListener('turbo:load', initFront);
window['Stimulus'] = Application.start();
const context = require.context('../retrospring/controllers', true, /\.ts$/);
window['Stimulus'].load(definitionsFromContext(context));

View file

@ -0,0 +1,36 @@
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['input', 'counter', 'action'];
declare readonly inputTarget: HTMLInputElement;
declare readonly counterTarget: HTMLElement;
declare readonly actionTarget: HTMLInputElement;
static values = {
max: Number
};
declare readonly maxValue: number;
connect(): void {
this.inputTarget.addEventListener('input', this.update.bind(this));
}
update(): void {
this.counterTarget.innerHTML = String(`${this.maxValue - this.inputTarget.value.length}`);
if (this.inputTarget.value.length > this.maxValue) {
if (!this.inputTarget.classList.contains('is-invalid') && !this.actionTarget.disabled) {
this.inputTarget.classList.add('is-invalid');
this.actionTarget.disabled = true;
}
}
else {
if (this.inputTarget.classList.contains('is-invalid') && this.actionTarget.disabled) {
this.inputTarget.classList.remove('is-invalid');
this.actionTarget.disabled = false;
}
}
}
}

View file

@ -1,19 +0,0 @@
export function commentCharacterCountHandler(event: Event): void {
const input = event.target as HTMLInputElement;
const id = input.dataset.aId;
const counter = document.querySelector(`#ab-comment-charcount-${id}`);
const group = document.querySelector(`[name=ab-comment-new-group][data-a-id="${id}"]`);
if (group.classList.contains('has-error')) {
group.classList.remove('has-error');
}
counter.innerHTML = String(160 - input.value.length);
if (Number(counter.innerHTML) < 0) {
counter.classList.remove('text-muted');
counter.classList.add('text-danger');
} else {
counter.classList.remove('text-danger');
counter.classList.add('text-muted');
}
}

View file

@ -1,5 +1,4 @@
import registerEvents from "retrospring/utilities/registerEvents"
import { commentCharacterCountHandler } from "./count";
import registerEvents from "retrospring/utilities/registerEvents";
import { commentDestroyHandler } from "./destroy";
import {commentComposeEnd, commentComposeStart, commentCreateHandler} from "./new";
import { commentReportHandler } from "./report";
@ -14,7 +13,6 @@ export default (): void => {
{ type: 'click', target: '[data-action=ab-comment-destroy]', handler: commentDestroyHandler, global: true },
{ type: 'compositionstart', target: '[name=ab-comment-new]', handler: commentComposeStart, global: true },
{ type: 'compositionend', target: '[name=ab-comment-new]', handler: commentComposeEnd, global: true },
{ type: 'keyup', target: '[name=ab-comment-new]', handler: commentCreateHandler, global: true },
{ type: 'input', target: '[name=ab-comment-new]', handler: commentCharacterCountHandler, global: true }
{ type: 'keyup', target: '[name=ab-comment-new]', handler: commentCreateHandler, global: true }
]);
}

View file

@ -22,6 +22,10 @@ export function questionboxAllHandler(event: Event): void {
if (data.success) {
document.querySelector<HTMLInputElement>('textarea[name=qb-all-question]').value = '';
window['$']('#modal-ask-followers').modal('hide');
// FIXME: also solve this using a Stimulus controller
const characterCount = document.querySelector<HTMLElement>('#modal-ask-followers [data-character-count-max-value]').dataset.characterCountMaxValue;
document.querySelector<HTMLElement>('#modal-ask-followers [data-character-count-target="counter"]').innerHTML = characterCount;
}
showNotification(data.message, data.success);

View file

@ -19,7 +19,7 @@ export function questionboxUserHandler(event: Event): void {
body: {
rcpt: document.querySelector<HTMLInputElement>('input[name=qb-to]').value,
question: document.querySelector<HTMLInputElement>('textarea[name=qb-question]').value,
anonymousQuestion: String(anonymousQuestion)
anonymousQuestion: String(anonymousQuestion)
},
contentType: 'application/json'
})
@ -29,6 +29,10 @@ export function questionboxUserHandler(event: Event): void {
if (data.success) {
document.querySelector<HTMLInputElement>('textarea[name=qb-question]').value = '';
// FIXME: also solve this using a Stimulus controller
const characterCount = document.querySelector<HTMLElement>('#question-box[data-character-count-max-value]').dataset.characterCountMaxValue;
document.querySelector<HTMLElement>('#question-box [data-character-count-target="counter"]').innerHTML = characterCount;
if (promote) {
const questionbox = document.querySelector('#question-box');
questionbox.classList.toggle('d-none');
@ -37,7 +41,7 @@ export function questionboxUserHandler(event: Event): void {
promote.classList.toggle('d-none');
}
}
showNotification(data.message, data.success);
})
.catch(err => {
@ -62,4 +66,4 @@ export function questionboxUserInputHandler(event: KeyboardEvent): void {
if (event.keyCode == 13 && (event.ctrlKey || event.metaKey)) {
document.querySelector<HTMLButtonElement>(`button[name=qb-ask]`).click();
}
}
}

View file

@ -24,6 +24,10 @@
&__input {
z-index: 99;
padding-right: 2.5rem;
&.is-invalid {
background-image: none;
}
}
&__character-count {

View file

@ -25,6 +25,7 @@
%span.caret
= render "actions/comment", comment: comment, answer: a
- if user_signed_in?
.form-group.has-feedback.comment__input-group{ name: "ab-comment-new-group", data: { a_id: a.id } }
%input.form-control.comment__input{ type: :text, placeholder: t(".placeholder"), name: "ab-comment-new", data: { a_id: a.id } }
%span.text-muted.form-control-feedback.comment__character-count{ id: "ab-comment-charcount-#{a.id}" } 160
.form-group.has-feedback.comment__input-group.input-group{ name: "ab-comment-new-group", data: { a_id: a.id, controller: "character-count", "character-count-max-value": 160 } }
%input.form-control.comment__input{ type: :text, placeholder: t(".placeholder"), name: "ab-comment-new", data: { a_id: a.id, "character-count-target": "input" } }
%span.text-muted.form-control-feedback.comment__character-count{ id: "ab-comment-charcount-#{a.id}", data: { "character-count-target": "counter" } } 160
%button.btn.btn-primary.d-none{ type: :button, name: "ab-comment-new-submit", data: { a_id: a.id, "character-count-target": "action" } }= t(".action")

View file

@ -16,9 +16,9 @@
%strong= t(".status.blocked")
- else
- if user_signed_in? || user.privacy_allow_anonymous_questions?
#question-box
%textarea.form-control{ name: "qb-question", placeholder: t(".placeholder") }
.row{ style: "padding-top: 5px; padding-left: 5px; padding-right: 5px;" }
#question-box{ data: { controller: "character-count", "character-count-max-value": 512 }}
%textarea.form-control{ name: "qb-question", placeholder: t(".placeholder"), data: { "character-count-target": "input" } }
.row{ style: "padding-top: 5px;" }
.col-6
- if user_signed_in?
- if user.privacy_allow_anonymous_questions?
@ -30,10 +30,11 @@
%input{ name: "qb-anonymous", type: :hidden, value: false }/
.col-6
%p.pull-right
%span.text-muted.mr-1{ data: { "character-count-target": "counter" } } 512
%input{ name: "qb-to", type: "hidden", value: user.id }/
%button.btn.btn-primary{ name: "qb-ask",
type: :button,
data: { loading_text: t(".load"), promote: user_signed_in? ? "false" : "true" } }
data: { loading_text: t(".load"), promote: user_signed_in? ? "false" : "true", "character-count-target": "action" } }
Ask
- unless user_signed_in?
- if user.privacy_allow_anonymous_questions?

View file

@ -1,13 +1,15 @@
.modal.fade#modal-ask-followers{ aria: { hidden: true, labelledby: "modal-ask-followers-label" }, role: :dialog, tabindex: -1 }
.modal-dialog
.modal-content
.modal-content{ data: { controller: "character-count", "character-count-max-value": 512 }}
.modal-header
%h5.modal-title#modal-ask-followers-label= t(".title")
%button.close{ data: { dismiss: :modal }, type: :button }
%span{ aria: { hidden: true } } ×
%span.sr-only= t("voc.close")
.modal-body
%textarea.form-control{ name: "qb-all-question", placeholder: t(".placeholder") }
.form-group.has-feedback
%textarea.form-control{ name: "qb-all-question", placeholder: t(".placeholder"), data: { "character-count-target": "input" } }
%p.text-right.text-muted.form-control-feedback{ data: { "character-count-target": "counter" } } 512
.modal-footer
%button.btn.btn-default{ type: :button, data: { dismiss: :modal } }= t("voc.cancel")
%button.btn.btn-primary{ name: "qb-all-ask", type: :button, data: { loading_text: t(".loading") } }= t(".action")
%button.btn.btn-primary{ name: "qb-all-ask", type: :button, data: { "character-count-target": "action", loading_text: t(".loading") } }= t(".action")

View file

@ -35,12 +35,12 @@ module.exports = function(api) {
exclude: ['transform-typeof-symbol']
}
],
['@babel/preset-typescript', { 'allExtensions': true, 'isTSX': true }]
].filter(Boolean),
plugins: [
'babel-plugin-macros',
'@babel/plugin-syntax-dynamic-import',
isTestEnv && 'babel-plugin-dynamic-import-node',
["@babel/plugin-transform-typescript", { 'allExtensions': true, 'isTSX': true, 'allowDeclareFields': true }],
'@babel/plugin-transform-destructuring',
[
'@babel/plugin-proposal-class-properties',

View file

@ -5,6 +5,8 @@
},
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
"@hotwired/stimulus": "^3.1.0",
"@hotwired/stimulus-webpack-helpers": "^1.0.1",
"@fontsource/lexend": "^4.5.13",
"@hotwired/turbo-rails": "^7.1.3",
"@melloware/coloris": "^0.16.1",

View file

@ -1115,6 +1115,16 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
"@hotwired/stimulus-webpack-helpers@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz#4cd74487adeca576c9865ac2b9fe5cb20cef16dd"
integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ==
"@hotwired/stimulus@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.1.0.tgz#20215251e5afe6e0a3787285181ba1bfc9097df0"
integrity sha512-iDMHUhiEJ1xFeicyHcZQQgBzhtk5mPR0QZO3L6wtqzMsJEk2TKECuCQTGKjm+KJTHVY0dKq1dOOAWvODjpd2Mg==
"@hotwired/turbo-rails@^7.1.0", "@hotwired/turbo-rails@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-7.1.3.tgz#a4e04ecb800a06e7f9aa6e298170fa4580b74216"