posts: rewrite tag counter widget in React.

Rewrite the tag counter widget (the one above the tag edit box) to use
React. This is a trial run for using React elsewhere.

We actually use Preact instead of React because it's lighter weight.
We use Mobx for state management because it's cleaner than React's
setState or useState.

This incidentally fixes a couple bugs:

* The tag counter wasn't updated when deleting tags with backspace
* The tag counter wasn't updated when pasting in new tags.
This commit is contained in:
evazion
2020-08-02 10:56:36 -05:00
parent baf0cf87af
commit b3a4c7aa43
9 changed files with 168 additions and 35 deletions

View File

@@ -43,6 +43,7 @@ export { default as PostTooltip } from '../src/javascripts/post_tooltips.js';
export { default as PostVersion } from '../src/javascripts/post_version.js';
export { default as RelatedTag } from '../src/javascripts/related_tag.js';
export { default as Shortcuts } from '../src/javascripts/shortcuts.js';
export { default as TagCounter } from '../src/javascripts/tag_counter.js';
export { default as Upload } from '../src/javascripts/uploads.js.erb';
export { default as UserTooltip } from '../src/javascripts/user_tooltips.js';
export { default as Utility } from '../src/javascripts/utility.js';

View File

@@ -42,8 +42,6 @@ Post.initialize_all = function() {
}
var $fields_multiple = $('[data-autocomplete="tag-edit"]');
$fields_multiple.on("keypress.danbooru", Post.update_tag_count);
$fields_multiple.on("click", Post.update_tag_count);
$(window).on('danbooru:initialize_saved_seraches', () => {
Post.initialize_saved_searches();
@@ -402,7 +400,6 @@ Post.initialize_post_sections = function() {
$("#post_tag_string").focus().selectEnd().height($("#post_tag_string")[0].scrollHeight);
$("#recommended").hide();
$(document).trigger("danbooru:open-post-edit-tab");
Post.update_tag_count({target: $("#post_tag_string")});
} else if (e.target.hash === "#recommended") {
$("#comments").hide();
$("#edit").hide();
@@ -513,32 +510,6 @@ Post.initialize_recommended = function() {
});
};
Post.update_tag_count = function(event) {
let string = "0 tags";
let count = 0;
if (event) {
let tags = Utility.regexp_split($(event.target).val());
if (tags.length) {
count = tags.length;
string = (count === 1) ? (count + " tag") : (count + " tags")
}
}
$("#tags-container .count").html(string);
let klass = "";
if (count < Post.LOW_TAG_COUNT) {
klass = "frown";
} else if (count >= Post.LOW_TAG_COUNT && count < Post.HIGH_TAG_COUNT) {
klass = "meh";
} else {
klass = "smile";
}
$("#tags-container .options #face").removeClass().addClass(`far fa-${klass}`);
}
$(document).ready(function() {
Post.initialize_all();
});

View File

@@ -0,0 +1,49 @@
import { h, Component, render } from "preact";
import { observable, computed, action } from "mobx";
import { observer } from "mobx-react";
import Utility from "./utility";
export default @observer class TagCounter extends Component {
static lowCount = 10;
static highCount = 20;
@observable tagCount = 0;
componentDidMount() {
$(this.props.tags).on("input", this.updateCount);
this.updateCount();
}
render() {
return (
<span class="tag-counter">
<span class="tag-count">{this.tagCount}</span> / {TagCounter.highCount} tags
<i class={`far fa-${this.emoji}`}></i>
</span>
);
}
@action.bound updateCount() {
this.tagCount = Utility.regexp_split($(this.props.tags).val()).length;
}
@computed get emoji() {
if (this.tagCount < TagCounter.lowCount) {
return "frown";
} else if (this.tagCount >= TagCounter.lowCount && this.tagCount < TagCounter.highCount) {
return "meh";
} else {
return "smile";
}
}
static initialize() {
$("[data-tag-counter]").toArray().forEach(element => {
let target = $($(element).attr("data-for")).get(0);
render(h(TagCounter, { tags: target }), element);
});
}
}
$(TagCounter.initialize);

View File

@@ -50,8 +50,7 @@
<%= f.label :tag_string, "Tags" %>
<span class="options">
<i id="face" class="fas"></i>
<span class="count"></span>
<span data-tag-counter data-for="#post_tag_string"></span>
<a href="javascript:void(0)"><i id="open-edit-dialog" class="fas fa-arrows-alt" title="detach" data-shortcut="shift+e"></i></a>
</span>
</div>

View File

@@ -66,8 +66,7 @@
<%= f.label :tag_string, "Tags" %>
<span class="options">
<i id="face" class="fas"></i>
<span class="count"></span>
<span data-tag-counter data-for="#upload_tag_string"></span>
<a href="javascript:void(0)"><i id="open-edit-dialog" class="fas fa-arrows-alt" title="detach" data-shortcut="shift+e"></i></a>
</span>
</div>

View File

@@ -41,6 +41,13 @@ module.exports = function(api) {
'@babel/plugin-syntax-dynamic-import',
isTestEnv && 'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-destructuring',
["@babel/plugin-proposal-decorators", {
legacy: true
}],
["@babel/plugin-transform-react-jsx", {
pragma: "h",
pragmaFrag: "Fragment",
}],
[
'@babel/plugin-proposal-class-properties',
{

View File

@@ -7,7 +7,9 @@ environment.loaders.append('erb', erb);
environment.config.output.library = ["Danbooru"];
environment.config.set("resolve.alias", {
"jquery": "jquery/src/jquery.js"
"jquery": "jquery/src/jquery.js",
"react": "preact/compat",
"react-dom": "preact/compat",
});
module.exports = environment

View File

@@ -1,6 +1,8 @@
{
"license": "MIT",
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.10.5",
"@babel/plugin-transform-react-jsx": "^7.10.4",
"@fortawesome/fontawesome-free": "^5.11.2",
"@rails/ujs": "^6.0.2-1",
"@rails/webpacker": "^5.0.0",
@@ -9,6 +11,9 @@
"jquery": "3.5.1",
"jquery-hotkeys": "^0.2.2",
"jquery-ui": "^1.12.1",
"mobx": "^5.15.4",
"mobx-react": "^6.2.5",
"preact": "^10.4.6",
"rails-erb-loader": "^5.5.0",
"spark-md5": "^3.0.0",
"tippy.js": "^6.2.3",

102
yarn.lock
View File

@@ -65,6 +65,23 @@
"@babel/helper-explode-assignable-expression" "^7.10.4"
"@babel/types" "^7.10.4"
"@babel/helper-builder-react-jsx-experimental@^7.10.4":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.5.tgz#f35e956a19955ff08c1258e44a515a6d6248646b"
integrity sha512-Buewnx6M4ttG+NLkKyt7baQn7ScC/Td+e99G914fRU8fGIUivDDgVIQeDHFa5e4CRSJQt58WpNHhsAZgtzVhsg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.10.4"
"@babel/helper-module-imports" "^7.10.4"
"@babel/types" "^7.10.5"
"@babel/helper-builder-react-jsx@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d"
integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.10.4"
"@babel/types" "^7.10.4"
"@babel/helper-compilation-targets@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2"
@@ -88,6 +105,18 @@
"@babel/helper-replace-supers" "^7.10.4"
"@babel/helper-split-export-declaration" "^7.10.4"
"@babel/helper-create-class-features-plugin@^7.10.5":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
dependencies:
"@babel/helper-function-name" "^7.10.4"
"@babel/helper-member-expression-to-functions" "^7.10.5"
"@babel/helper-optimise-call-expression" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/helper-replace-supers" "^7.10.4"
"@babel/helper-split-export-declaration" "^7.10.4"
"@babel/helper-create-regexp-features-plugin@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
@@ -144,6 +173,13 @@
dependencies:
"@babel/types" "^7.10.4"
"@babel/helper-member-expression-to-functions@^7.10.5":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
dependencies:
"@babel/types" "^7.11.0"
"@babel/helper-module-imports@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
@@ -274,6 +310,15 @@
"@babel/helper-create-class-features-plugin" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-proposal-decorators@^7.10.5":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4"
integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ==
dependencies:
"@babel/helper-create-class-features-plugin" "^7.10.5"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-decorators" "^7.10.4"
"@babel/plugin-proposal-dynamic-import@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e"
@@ -361,6 +406,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-decorators@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c"
integrity sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
@@ -375,6 +427,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-syntax-jsx@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c"
integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
@@ -602,6 +661,16 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-transform-react-jsx@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2"
integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A==
dependencies:
"@babel/helper-builder-react-jsx" "^7.10.4"
"@babel/helper-builder-react-jsx-experimental" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-jsx" "^7.10.4"
"@babel/plugin-transform-regenerator@^7.10.4", "@babel/plugin-transform-regenerator@^7.8.7":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
@@ -799,6 +868,15 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.10.5", "@babel/types@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d"
integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==
dependencies:
"@babel/helper-validator-identifier" "^7.10.4"
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@csstools/convert-colors@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
@@ -4969,7 +5047,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.10:
lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
@@ -5374,6 +5452,23 @@ mixin-deep@^1.2.0:
dependencies:
minimist "^1.2.5"
mobx-react-lite@>=2.0.6:
version "2.0.7"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.0.7.tgz#1bfb3b4272668e288047cf0c7940b14e91cba284"
integrity sha512-YKAh2gThC6WooPnVZCoC+rV1bODAKFwkhxikzgH18wpBjkgTkkR9Sb0IesQAH5QrAEH/JQVmy47jcpQkf2Au3Q==
mobx-react@^6.2.5:
version "6.2.5"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.2.5.tgz#9020a17b79cc6dc3d124ad89ab36eb9ea540a45b"
integrity sha512-LxtXXW0GkOAO6VOIg2m/6WL6ZuKlzOWwESIFdrWelI0ZMIvtKCMZVUuulcO5GAWSDsH0ApaMkGLoaPqKjzyziQ==
dependencies:
mobx-react-lite ">=2.0.6"
mobx@^5.15.4:
version "5.15.4"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.15.4.tgz#9da1a84e97ba624622f4e55a0bf3300fb931c2ab"
integrity sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -6851,6 +6946,11 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.1
source-map "^0.6.1"
supports-color "^6.1.0"
preact@^10.4.6:
version "10.4.6"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.4.6.tgz#86cc43396e4bdd755726a2b4b1f0529e78067cd3"
integrity sha512-80WJfXH53yyINig5Wza/8MD9n4lMg9G6aN00ws0ptsAaY/Fu/M7xW4zICf7OLfocVltxS30wvNQ8oIbUyZS1tw==
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"