mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
ui-default: init
将ui-default合并至主存储库便于维护
This commit is contained in:
1 parent
6317c9057f
commit
5faa54c7ca
564 files changed
+23083
-14
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import _ from 'lodash';
|
||||
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
|
||||
import request from 'vj/utils/request';
|
||||
import TextareaHandler from 'vj/components/cmeditor/textareaHandler';
|
||||
import Notification from 'vj/components/notification';
|
||||
|
||||
let initialized = false;
|
||||
const $template = $('.dczcomments__box').eq(0).clone();
|
||||
|
||||
function getClosestInstance($dom) {
|
||||
return $dom.closest('.dczcomments__box').data('instance');
|
||||
}
|
||||
|
||||
function onBoxCancel(ev) {
|
||||
const commentBox = getClosestInstance($(ev.currentTarget));
|
||||
if (commentBox) commentBox.onCancel(ev);
|
||||
}
|
||||
|
||||
function onBoxSubmit(ev) {
|
||||
const commentBox = getClosestInstance($(ev.currentTarget));
|
||||
if (commentBox) commentBox.onSubmit(ev);
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (initialized) return;
|
||||
$(document).on('submit', '[name="dczcomments__box__form"]', onBoxSubmit);
|
||||
$(document).on('vjCommentBoxCancel', '[name="dczcomments__box__form"]', onBoxCancel);
|
||||
$(document).on('click', '[name="dczcomments__box__cancel"]', onBoxCancel);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
export default class CommentBox extends DOMAttachedObject {
|
||||
static DOMAttachKey = 'vjCommentBoxInstance';
|
||||
|
||||
constructor($dom, options = {}) {
|
||||
super($dom);
|
||||
init();
|
||||
this.$box = $template.clone();
|
||||
this.$box.data('instance', this);
|
||||
this.options = {
|
||||
initialText: null,
|
||||
mode: null,
|
||||
form: null,
|
||||
onCancel: () => null,
|
||||
...options,
|
||||
};
|
||||
if (this.options.initialText) this.setText(this.options.initialText);
|
||||
if (this.options.mode) {
|
||||
const submitButton = this.$box.find('.dczcomments__box__submit');
|
||||
submitButton.val(submitButton.attr(`data-value-${this.options.mode}`));
|
||||
}
|
||||
}
|
||||
|
||||
getTextareaHandler() {
|
||||
const $textarea = this.$box.find('textarea');
|
||||
return TextareaHandler.getOrConstruct($textarea);
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.getTextareaHandler().focus();
|
||||
return this;
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
this.getTextareaHandler().val(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this.getTextareaHandler().val();
|
||||
}
|
||||
|
||||
insertText(text) {
|
||||
const handler = this.getTextareaHandler();
|
||||
handler.val(handler.val() + text);
|
||||
return this;
|
||||
}
|
||||
|
||||
appendTo($dom) {
|
||||
this.$box.appendTo($dom);
|
||||
this.$box.trigger('vjContentNew');
|
||||
return this;
|
||||
}
|
||||
|
||||
async onSubmit() {
|
||||
try {
|
||||
await request.post('', {
|
||||
...this.options.form,
|
||||
content: this.getText(),
|
||||
});
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
Notification.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async onCancel(ev) {
|
||||
await this.options.onCancel(ev);
|
||||
this.$box.remove();
|
||||
this.detach();
|
||||
}
|
||||
}
|
||||
|
||||
_.assign(CommentBox, DOMAttachedObject);
|
||||
@@ -0,0 +1,206 @@
|
||||
import 'jquery.easing';
|
||||
|
||||
import { AutoloadPage } from 'vj/misc/Page';
|
||||
import CommentBox from 'vj/components/discussion/CommentBox';
|
||||
import { ConfirmDialog } from 'vj/components/dialog';
|
||||
|
||||
import delay from 'vj/utils/delay';
|
||||
import { slideDown, slideUp } from 'vj/utils/slide';
|
||||
import request from 'vj/utils/request';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import tpl from 'vj/utils/tpl';
|
||||
|
||||
const $replyTemplate = $('.commentbox-container').eq(0).clone();
|
||||
|
||||
function createReplyContainer($parent) {
|
||||
const $container = $replyTemplate
|
||||
.clone()
|
||||
.hide()
|
||||
.prependTo($parent.find('.commentbox-reply-target').eq(0))
|
||||
.trigger('vjContentNew');
|
||||
return $container.find('.commentbox-placeholder');
|
||||
}
|
||||
|
||||
async function showReplyContainer($parent) {
|
||||
const $container = $parent.find('.commentbox-container');
|
||||
// TODO: fix ugly hack. cannot get $container rect because it is invisible
|
||||
const rect = $container.parent()[0].getBoundingClientRect();
|
||||
const rectBody = document.body.getBoundingClientRect();
|
||||
if (rect.top < 100 || rect.top + 100 > window.innerHeight) {
|
||||
const targetScrollTop = rect.top - rectBody.top - window.innerHeight * 0.382;
|
||||
$('html, body').stop().animate({ scrollTop: targetScrollTop }, 400, 'easeOutCubic');
|
||||
await delay(300);
|
||||
// delay duration is set smaller than animation duration intentionally
|
||||
}
|
||||
$container.css('opacity', 0);
|
||||
await slideDown($container, 300);
|
||||
await $container.transition({ opacity: 1 }, { duration: 200 }).promise();
|
||||
$container.removeAttr('style');
|
||||
}
|
||||
|
||||
async function destroyReplyContainer($parent) {
|
||||
const $container = $parent.find('.commentbox-container');
|
||||
$container.css('opacity', 1);
|
||||
await $container.transition({ opacity: 0 }, { duration: 200 }).promise();
|
||||
await slideUp($container, 300);
|
||||
$container.remove();
|
||||
}
|
||||
|
||||
function onClickDummyBox(ev) {
|
||||
const $evTarget = $(ev.currentTarget);
|
||||
|
||||
if (CommentBox.get($evTarget)) {
|
||||
CommentBox
|
||||
.get($evTarget)
|
||||
.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const $mediaBody = $evTarget.closest('.media__body');
|
||||
|
||||
const opt = {
|
||||
form: JSON.parse($evTarget.attr('data-form')),
|
||||
mode: 'comment',
|
||||
onCancel: () => {
|
||||
$mediaBody.removeClass('is-editing');
|
||||
},
|
||||
};
|
||||
|
||||
$mediaBody.addClass('is-editing');
|
||||
|
||||
CommentBox
|
||||
.getOrConstruct($evTarget, opt)
|
||||
.appendTo($mediaBody.find('.commentbox-placeholder').eq(0))
|
||||
.focus();
|
||||
}
|
||||
|
||||
async function onCommentClickReplyComment(ev, options = {}) {
|
||||
const $evTarget = $(ev.currentTarget);
|
||||
|
||||
if (CommentBox.get($evTarget)) {
|
||||
// If comment box is already expanded,
|
||||
// we should insert "initialText"
|
||||
CommentBox
|
||||
.get($evTarget)
|
||||
.insertText(options.initialText || '')
|
||||
.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const $mediaBody = $evTarget.closest('.media__body');
|
||||
|
||||
const opt = {
|
||||
initialText: '',
|
||||
mode: 'reply',
|
||||
...options,
|
||||
onCancel: async () => {
|
||||
await destroyReplyContainer($mediaBody);
|
||||
},
|
||||
};
|
||||
|
||||
const cbox = CommentBox
|
||||
.getOrConstruct($evTarget, {
|
||||
form: JSON.parse($evTarget.attr('data-form')),
|
||||
...opt,
|
||||
})
|
||||
.appendTo(createReplyContainer($mediaBody));
|
||||
await showReplyContainer($mediaBody);
|
||||
cbox.focus();
|
||||
}
|
||||
|
||||
async function onCommentClickReplyReply(ev) {
|
||||
const $evTarget = $(ev.currentTarget);
|
||||
const $mediaBody = $evTarget.closest('.media__body');
|
||||
const username = $mediaBody
|
||||
.find('.user-profile-name').eq(0)
|
||||
.text();
|
||||
|
||||
$evTarget
|
||||
.closest('.dczcomments__item')
|
||||
.find('[name="dczcomments__op-reply-comment"]').eq(0)
|
||||
.trigger('click', { initialText: `@${username}: ` });
|
||||
}
|
||||
|
||||
async function onCommentClickEdit(mode, ev) {
|
||||
const $evTarget = $(ev.currentTarget);
|
||||
|
||||
if (CommentBox.get($evTarget)) {
|
||||
CommentBox
|
||||
.get($evTarget)
|
||||
.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const $mediaBody = $evTarget.closest('.media__body');
|
||||
|
||||
const raw = await request.get(
|
||||
$mediaBody.find('.typo').eq(0).attr('data-raw-url'),
|
||||
{},
|
||||
{ dataType: 'text' },
|
||||
);
|
||||
|
||||
const opt = {
|
||||
initialText: raw,
|
||||
form: JSON.parse($evTarget.attr('data-form')),
|
||||
mode,
|
||||
onCancel: () => {
|
||||
$mediaBody.removeClass('is-editing');
|
||||
},
|
||||
};
|
||||
|
||||
$mediaBody.addClass('is-editing');
|
||||
|
||||
CommentBox
|
||||
.getOrConstruct($evTarget, opt)
|
||||
.appendTo($mediaBody.find('.commentbox-edit-target').eq(0))
|
||||
.focus();
|
||||
}
|
||||
|
||||
function onCommentClickEditComment(ev) {
|
||||
return onCommentClickEdit('comment-update', ev);
|
||||
}
|
||||
|
||||
function onCommentClickEditReply(ev) {
|
||||
return onCommentClickEdit('reply-update', ev);
|
||||
}
|
||||
|
||||
async function onCommentClickDelete(type, ev) {
|
||||
const message = (type === 'comment')
|
||||
? 'Confirm deleting this comment? Its replies will be deleted as well.'
|
||||
: 'Confirm deleting this reply?';
|
||||
const action = await new ConfirmDialog({
|
||||
$body: tpl`
|
||||
<div class="typo">
|
||||
<p>${i18n(message)}</p>
|
||||
</div>`,
|
||||
}).open();
|
||||
if (action !== 'yes') {
|
||||
return;
|
||||
}
|
||||
|
||||
const $evTarget = $(ev.currentTarget);
|
||||
const form = JSON.parse($evTarget.attr('data-form'));
|
||||
|
||||
await request.post('', form);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function onCommentClickDeleteComment(ev) {
|
||||
onCommentClickDelete('comment', ev);
|
||||
}
|
||||
|
||||
function onCommentClickDeleteReply(ev) {
|
||||
onCommentClickDelete('reply', ev);
|
||||
}
|
||||
|
||||
const commentsPage = new AutoloadPage('commentsPage', () => {
|
||||
$(document).on('click', '[name="dczcomments__dummy-box"]', onClickDummyBox);
|
||||
$(document).on('click', '[name="dczcomments__op-reply-comment"]', onCommentClickReplyComment);
|
||||
$(document).on('click', '[name="dczcomments__op-reply-reply"]', onCommentClickReplyReply);
|
||||
$(document).on('click', '[name="dczcomments__op-edit-comment"]', onCommentClickEditComment);
|
||||
$(document).on('click', '[name="dczcomments__op-edit-reply"]', onCommentClickEditReply);
|
||||
$(document).on('click', '[name="dczcomments__op-delete-comment"]', onCommentClickDeleteComment);
|
||||
$(document).on('click', '[name="dczcomments__op-delete-reply"]', onCommentClickDeleteReply);
|
||||
});
|
||||
|
||||
export default commentsPage;
|
||||
@@ -0,0 +1,125 @@
|
||||
$upvote-color = green
|
||||
$downvote-color = red
|
||||
|
||||
.dczcomments__item .is-editing
|
||||
[name="dczcomments__dummy-box"]
|
||||
display: none
|
||||
> .typo
|
||||
display: none
|
||||
|
||||
.dczcomments__box .textbox
|
||||
height: rem(180px)
|
||||
resize: vertical
|
||||
|
||||
.textbox[name="dczcomments__dummy-box"]
|
||||
height: rem(60px)
|
||||
resize: none
|
||||
font-size: rem($font-size-title)
|
||||
color: $comment-placeholder-color
|
||||
padding: rem(10px)
|
||||
|
||||
.dczcomments__item,
|
||||
.dczcomments__reply
|
||||
border-top: 1px solid $table-border-color
|
||||
|
||||
>.media
|
||||
padding: rem(20px 0)
|
||||
|
||||
&:first-child
|
||||
border-top: 0
|
||||
|
||||
.dczcomments__reply
|
||||
&:last-child
|
||||
>.media
|
||||
padding-bottom: 0
|
||||
|
||||
.dczcomments__supplementary
|
||||
float: left
|
||||
margin-bottom: rem(5px)
|
||||
|
||||
.dczcomments__operations
|
||||
font-size: rem($font-size)
|
||||
float: right
|
||||
vertical-align: top
|
||||
color: $comment-op-color
|
||||
|
||||
a, a:visited
|
||||
color: $comment-op-link-color
|
||||
margin-left: rem(3px)
|
||||
a:hover
|
||||
color: $primary-color
|
||||
text-decoration: none
|
||||
|
||||
.view--solution .dczcomments__replies
|
||||
margin-left: rem(50px)
|
||||
|
||||
.dczcomments__item
|
||||
.vote-number
|
||||
width: rem(82px)
|
||||
text-align: center
|
||||
font-size: rem(25px)
|
||||
font-weight: 300
|
||||
margin-bottom: rem(10px)
|
||||
|
||||
.vote-op
|
||||
position: relative
|
||||
border: 1px solid transparent
|
||||
border-radius: rem(14px)
|
||||
transition: border-color .2s linear, opacity .2s linear
|
||||
opacity: 0.5
|
||||
|
||||
&>form
|
||||
width: rem(40px)
|
||||
float: left
|
||||
position: relative
|
||||
|
||||
&:hover .vote-op
|
||||
opacity: 1
|
||||
border-color: $supplementary-border-color
|
||||
|
||||
.vote-button
|
||||
appearance: none
|
||||
border: 0
|
||||
background: none
|
||||
width: 100%
|
||||
outline: 0
|
||||
font-size: rem($font-size-icon)
|
||||
line-height: rem(26px)
|
||||
color: #AAA
|
||||
transition: color .2s linear, background-color .2s linear, box-shadow .2s linear
|
||||
|
||||
&.upvote
|
||||
border-radius: rem(13px 0 0 13px)
|
||||
padding-left: rem(7px)
|
||||
|
||||
&:hover
|
||||
background: $upvote-color
|
||||
color: #FFF
|
||||
box-shadow: 0 2px 10px rgba($upvote-color, 0.4)
|
||||
|
||||
&.active
|
||||
color: $upvote-color
|
||||
|
||||
|
||||
&.downvote
|
||||
border-radius: rem(0 13px 13px 0)
|
||||
padding-right: rem(7px)
|
||||
|
||||
&:hover
|
||||
background: $downvote-color
|
||||
color: #FFF
|
||||
box-shadow: 0 2px 10px rgba($downvote-color, 0.4)
|
||||
|
||||
&.active
|
||||
color: $downvote-color
|
||||
|
||||
&.active
|
||||
background: transparent !important
|
||||
box-shadow: none !important
|
||||
cursor: default
|
||||
|
||||
|
||||
+mobile()
|
||||
.dczcomments__item
|
||||
.media__left
|
||||
display: none
|
||||
@@ -0,0 +1,17 @@
|
||||
.discussion-node-tag
|
||||
font-size: rem($font-size-small)
|
||||
display: inline-block
|
||||
line-height: rem(22px)
|
||||
padding: rem(0 10px)
|
||||
vertical-align: middle
|
||||
border-radius: rem(12px)
|
||||
border: 1px solid $supplementary-border-color
|
||||
|
||||
&:hover
|
||||
background: $primary-color
|
||||
color: #FFF !important
|
||||
text-decoration: none
|
||||
border-color: $primary-color
|
||||
|
||||
.icon
|
||||
margin-right: rem(5px)
|
||||
Reference in new issue
Block a user