Switch avatar rendering from canvas to SVG.
This delegates the calculation of the aspect ratio to the browser, and generally simplifies the code. Fixes #1156.
This commit is contained in:
parent
352c0797ad
commit
ab5dd4a146
@ -9631,7 +9631,7 @@ body.reset {
|
|||||||
#conversejs div, #conversejs span, #conversejs h1, #conversejs h2, #conversejs h3, #conversejs h4, #conversejs h5, #conversejs h6, #conversejs p, #conversejs blockquote,
|
#conversejs div, #conversejs span, #conversejs h1, #conversejs h2, #conversejs h3, #conversejs h4, #conversejs h5, #conversejs h6, #conversejs p, #conversejs blockquote,
|
||||||
#conversejs pre, #conversejs a, #conversejs em, #conversejs img, #conversejs strong, #conversejs dl, #conversejs dt, #conversejs dd, #conversejs ol, #conversejs ul, #conversejs li,
|
#conversejs pre, #conversejs a, #conversejs em, #conversejs img, #conversejs strong, #conversejs dl, #conversejs dt, #conversejs dd, #conversejs ol, #conversejs ul, #conversejs li,
|
||||||
#conversejs fieldset, #conversejs form, #conversejs legend, #conversejs table, #conversejs caption, #conversejs tbody,
|
#conversejs fieldset, #conversejs form, #conversejs legend, #conversejs table, #conversejs caption, #conversejs tbody,
|
||||||
#conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs canvas, #conversejs details,
|
#conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs details,
|
||||||
#conversejs embed, #conversejs figure, #conversejs figcaption, #conversejs footer, #conversejs header, #conversejs hgroup, #conversejs menu,
|
#conversejs embed, #conversejs figure, #conversejs figcaption, #conversejs footer, #conversejs header, #conversejs hgroup, #conversejs menu,
|
||||||
#conversejs nav, #conversejs output, #conversejs ruby, #conversejs section, #conversejs summary, #conversejs time, #conversejs mark, #conversejs audio, #conversejs video {
|
#conversejs nav, #conversejs output, #conversejs ruby, #conversejs section, #conversejs summary, #conversejs time, #conversejs mark, #conversejs audio, #conversejs video {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -9669,7 +9669,7 @@ body.reset {
|
|||||||
color: var(--subdued-color); }
|
color: var(--subdued-color); }
|
||||||
#conversejs a.fa:hover, #conversejs a.far:hover, #conversejs a.fas:hover, #conversejs a:visited.fa:hover, #conversejs a:visited.far:hover, #conversejs a:visited.fas:hover, #conversejs a:not([href]):not([tabindex]).fa:hover, #conversejs a:not([href]):not([tabindex]).far:hover, #conversejs a:not([href]):not([tabindex]).fas:hover {
|
#conversejs a.fa:hover, #conversejs a.far:hover, #conversejs a.fas:hover, #conversejs a:visited.fa:hover, #conversejs a:visited.far:hover, #conversejs a:visited.fas:hover, #conversejs a:not([href]):not([tabindex]).fa:hover, #conversejs a:not([href]):not([tabindex]).far:hover, #conversejs a:not([href]):not([tabindex]).fas:hover {
|
||||||
color: var(--gray-color); }
|
color: var(--gray-color); }
|
||||||
#conversejs canvas {
|
#conversejs svg {
|
||||||
border-radius: var(--chatbox-border-radius); }
|
border-radius: var(--chatbox-border-radius); }
|
||||||
#conversejs .fa, #conversejs .far, #conversejs .fas {
|
#conversejs .fa, #conversejs .far, #conversejs .fas {
|
||||||
color: var(--subdued-color); }
|
color: var(--subdued-color); }
|
||||||
|
77
dist/converse.js
vendored
77
dist/converse.js
vendored
@ -58492,8 +58492,10 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! backbone.overview */ "./node_modules/backbone.overview/backbone.overview.js");
|
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! backbone.overview */ "./node_modules/backbone.overview/backbone.overview.js");
|
||||||
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(backbone_overview__WEBPACK_IMPORTED_MODULE_2__);
|
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(backbone_overview__WEBPACK_IMPORTED_MODULE_2__);
|
||||||
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
|
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
|
||||||
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
|
/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/avatar.svg */ "./src/templates/avatar.svg");
|
||||||
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__);
|
/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__);
|
||||||
|
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
|
||||||
|
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__);
|
||||||
// Converse.js
|
// Converse.js
|
||||||
// http://conversejs.org
|
// http://conversejs.org
|
||||||
//
|
//
|
||||||
@ -58504,6 +58506,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env,
|
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env,
|
||||||
Backbone = _converse$env.Backbone,
|
Backbone = _converse$env.Backbone,
|
||||||
_ = _converse$env._,
|
_ = _converse$env._,
|
||||||
@ -58519,27 +58522,12 @@ const AvatarMixin = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const image_type = this.model.vcard.get('image_type'),
|
const image_type = this.model.vcard.get('image_type'),
|
||||||
image = this.model.vcard.get('image'),
|
image = this.model.vcard.get('image');
|
||||||
img_src = "data:" + image_type + ";base64," + image,
|
canvas_el.outerHTML = templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default()({
|
||||||
img = new Image();
|
'classes': canvas_el.getAttribute('class'),
|
||||||
return new Promise((resolve, reject) => {
|
'width': canvas_el.width,
|
||||||
img.onload = () => {
|
'height': canvas_el.height,
|
||||||
const ctx = canvas_el.getContext('2d'),
|
'image': "data:" + image_type + ";base64," + image
|
||||||
ratio = img.width / img.height;
|
|
||||||
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
|
|
||||||
|
|
||||||
if (ratio < 1) {
|
|
||||||
const scaled_img_with = canvas_el.width * ratio,
|
|
||||||
x = Math.floor((canvas_el.width - scaled_img_with) / 2);
|
|
||||||
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height * ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
img.src = img_src;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58618,11 +58606,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
try {
|
try {
|
||||||
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
|
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._ensureElement();
|
this._ensureElement();
|
||||||
|
|
||||||
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
|
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.row_el = this.el.querySelector('.row');
|
this.row_el = this.el.querySelector('.row');
|
||||||
@ -61766,14 +61754,13 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
msg_content.innerHTML = _.flow(_.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].geoUriToHttp, _, _converse.geouri_replacement), _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addMentionsMarkup, _, this.model.get('references'), this.model.collection.chatbox), utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addHyperlinks, utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderNewLines, _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addEmoji, _converse, _))(text);
|
msg_content.innerHTML = _.flow(_.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].geoUriToHttp, _, _converse.geouri_replacement), _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addMentionsMarkup, _, this.model.get('references'), this.model.collection.chatbox), utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addHyperlinks, utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderNewLines, _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addEmoji, _converse, _))(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises = [];
|
const promise = utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content);
|
||||||
promises.push(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content));
|
|
||||||
|
|
||||||
if (this.model.get('type') !== 'headline') {
|
if (this.model.get('type') !== 'headline') {
|
||||||
promises.push(this.renderAvatar(msg));
|
this.renderAvatar(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
await promise;
|
||||||
this.replaceElement(msg);
|
this.replaceElement(msg);
|
||||||
this.model.collection.trigger('rendered', this);
|
this.model.collection.trigger('rendered', this);
|
||||||
},
|
},
|
||||||
@ -100974,6 +100961,34 @@ return __p
|
|||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./src/templates/avatar.svg":
|
||||||
|
/*!**********************************!*\
|
||||||
|
!*** ./src/templates/avatar.svg ***!
|
||||||
|
\**********************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
|
||||||
|
module.exports = function(o) {
|
||||||
|
var __t, __p = '', __e = _.escape;
|
||||||
|
__p += '<!-- src/templates/avatar.svg -->\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="' +
|
||||||
|
__e(o.classes) +
|
||||||
|
'" width="' +
|
||||||
|
__e(o.width) +
|
||||||
|
'" height="' +
|
||||||
|
__e(o.height) +
|
||||||
|
'">\n <image width="' +
|
||||||
|
__e(o.width) +
|
||||||
|
'" height="' +
|
||||||
|
__e(o.height) +
|
||||||
|
'" preserveAspectRatio="xMidYMid meet" xlink:href="' +
|
||||||
|
__e(o.image) +
|
||||||
|
'"/>\n</svg>\n';
|
||||||
|
return __p
|
||||||
|
};
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ "./src/templates/bookmark.html":
|
/***/ "./src/templates/bookmark.html":
|
||||||
/*!*************************************!*\
|
/*!*************************************!*\
|
||||||
!*** ./src/templates/bookmark.html ***!
|
!*** ./src/templates/bookmark.html ***!
|
||||||
@ -102892,7 +102907,7 @@ __e(o.image) +
|
|||||||
} ;
|
} ;
|
||||||
__p += '\n ';
|
__p += '\n ';
|
||||||
if (!o.image) { ;
|
if (!o.image) { ;
|
||||||
__p += '\n <canvas class="avatar" height="100px" width="100px"/>\n ';
|
__p += '\n <canvas class="avatar" height="100px" width="100px"></canvas>\n ';
|
||||||
} ;
|
} ;
|
||||||
__p += '\n </a>\n <input class="hidden" name="image" type="file">\n </div>\n <div class="col">\n <div class="form-group">\n <label class="col-form-label">' +
|
__p += '\n </a>\n <input class="hidden" name="image" type="file">\n </div>\n <div class="col">\n <div class="form-group">\n <label class="col-form-label">' +
|
||||||
__e(o.label_jid) +
|
__e(o.label_jid) +
|
||||||
@ -102989,7 +103004,7 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
|
|||||||
module.exports = function(o) {
|
module.exports = function(o) {
|
||||||
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
|
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
|
||||||
function print() { __p += __j.call(arguments, '') }
|
function print() { __p += __j.call(arguments, '') }
|
||||||
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n <a class="show-profile" href="#">\n <canvas alt="o.__(\'Your avatar\')" class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
|
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n <a class="show-profile" href="#">\n <canvas class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
|
||||||
__e(o.fullname) +
|
__e(o.fullname) +
|
||||||
'</span>\n <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="' +
|
'</span>\n <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="' +
|
||||||
__e(o.info_details) +
|
__e(o.info_details) +
|
||||||
|
@ -196,7 +196,7 @@ body.reset {
|
|||||||
div, span, h1, h2, h3, h4, h5, h6, p, blockquote,
|
div, span, h1, h2, h3, h4, h5, h6, p, blockquote,
|
||||||
pre, a, em, img, strong, dl, dt, dd, ol, ul, li,
|
pre, a, em, img, strong, dl, dt, dd, ol, ul, li,
|
||||||
fieldset, form, legend, table, caption, tbody,
|
fieldset, form, legend, table, caption, tbody,
|
||||||
tfoot, thead, tr, th, td, article, aside, canvas, details,
|
tfoot, thead, tr, th, td, article, aside, details,
|
||||||
embed, figure, figcaption, footer, header, hgroup, menu,
|
embed, figure, figcaption, footer, header, hgroup, menu,
|
||||||
nav, output, ruby, section, summary, time, mark, audio, video {
|
nav, output, ruby, section, summary, time, mark, audio, video {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -254,7 +254,7 @@ body.reset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas {
|
svg {
|
||||||
border-radius: var(--chatbox-border-radius);
|
border-radius: var(--chatbox-border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import "@converse/headless/converse-chatboxes";
|
|||||||
import "backbone.nativeview";
|
import "backbone.nativeview";
|
||||||
import "backbone.overview";
|
import "backbone.overview";
|
||||||
import converse from "@converse/headless/converse-core";
|
import converse from "@converse/headless/converse-core";
|
||||||
|
import tpl_avatar from "templates/avatar.svg";
|
||||||
import tpl_chatboxes from "templates/chatboxes.html";
|
import tpl_chatboxes from "templates/chatboxes.html";
|
||||||
|
|
||||||
const { Backbone, _, utils } = converse.env;
|
const { Backbone, _, utils } = converse.env;
|
||||||
@ -22,25 +23,13 @@ const AvatarMixin = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const image_type = this.model.vcard.get('image_type'),
|
const image_type = this.model.vcard.get('image_type'),
|
||||||
image = this.model.vcard.get('image'),
|
image = this.model.vcard.get('image');
|
||||||
img_src = "data:" + image_type + ";base64," + image,
|
|
||||||
img = new Image();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
canvas_el.outerHTML = tpl_avatar({
|
||||||
img.onload = () => {
|
'classes': canvas_el.getAttribute('class'),
|
||||||
const ctx = canvas_el.getContext('2d'),
|
'width': canvas_el.width,
|
||||||
ratio = img.width / img.height;
|
'height': canvas_el.height,
|
||||||
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
|
'image': "data:" + image_type + ";base64," + image,
|
||||||
if (ratio < 1) {
|
|
||||||
const scaled_img_with = canvas_el.width*ratio,
|
|
||||||
x = Math.floor((canvas_el.width-scaled_img_with)/2);
|
|
||||||
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height*ratio);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
img.src = img_src;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -155,12 +155,11 @@ converse.plugins.add('converse-message-view', {
|
|||||||
_.partial(u.addEmoji, _converse, _)
|
_.partial(u.addEmoji, _converse, _)
|
||||||
)(text);
|
)(text);
|
||||||
}
|
}
|
||||||
const promises = [];
|
const promise = u.renderImageURLs(_converse, msg_content);
|
||||||
promises.push(u.renderImageURLs(_converse, msg_content));
|
|
||||||
if (this.model.get('type') !== 'headline') {
|
if (this.model.get('type') !== 'headline') {
|
||||||
promises.push(this.renderAvatar(msg));
|
this.renderAvatar(msg);
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await promise;
|
||||||
this.replaceElement(msg);
|
this.replaceElement(msg);
|
||||||
this.model.collection.trigger('rendered', this);
|
this.model.collection.trigger('rendered', this);
|
||||||
},
|
},
|
||||||
|
3
src/templates/avatar.svg
Normal file
3
src/templates/avatar.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="{{{o.classes}}}" width="{{{o.width}}}" height="{{{o.height}}}">
|
||||||
|
<image width="{{{o.width}}}" height="{{{o.height}}}" preserveAspectRatio="xMidYMid meet" xlink:href="{{{o.image}}}"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 283 B |
@ -26,7 +26,7 @@
|
|||||||
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||||
{[ } ]}
|
{[ } ]}
|
||||||
{[ if (!o.image) { ]}
|
{[ if (!o.image) { ]}
|
||||||
<canvas class="avatar" height="100px" width="100px"/>
|
<canvas class="avatar" height="100px" width="100px"></canvas>
|
||||||
{[ } ]}
|
{[ } ]}
|
||||||
</a>
|
</a>
|
||||||
<input class="hidden" name="image" type="file">
|
<input class="hidden" name="image" type="file">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="userinfo controlbox-padded">
|
<div class="userinfo controlbox-padded">
|
||||||
<div class="controlbox-section profile d-flex">
|
<div class="controlbox-section profile d-flex">
|
||||||
<a class="show-profile" href="#">
|
<a class="show-profile" href="#">
|
||||||
<canvas alt="o.__('Your avatar')" class="avatar align-self-center" height="40" width="40"></canvas>
|
<canvas class="avatar align-self-center" height="40" width="40"></canvas>
|
||||||
</a>
|
</a>
|
||||||
<span class="username w-100 align-self-center">{{{o.fullname}}}</span>
|
<span class="username w-100 align-self-center">{{{o.fullname}}}</span>
|
||||||
<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>
|
<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>
|
||||||
|
@ -36,7 +36,7 @@ const config = {
|
|||||||
use: "exports-loader?filterXSS,filterCSS"
|
use: "exports-loader?filterXSS,filterCSS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.(html|svg)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: [{
|
use: [{
|
||||||
loader: 'lodash-template-webpack-loader',
|
loader: 'lodash-template-webpack-loader',
|
||||||
|
Loading…
Reference in New Issue
Block a user