Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-21 17:00:29 +01:00
COMPATIBILITY: Convert composer editor to modern ember component
Dieser Commit ist enthalten in:
Ursprung
87b19ec364
Commit
5eec15160c
4 geänderte Dateien mit 134 neuen und 242 gelöschten Zeilen
|
@ -0,0 +1,97 @@
|
|||
import ComposerEditor from "discourse/components/composer-editor";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { uploadIcon } from "discourse/lib/uploads";
|
||||
import { dasherize } from "@ember/string";
|
||||
import InsertHyperlink from "discourse/components/modal/insert-hyperlink";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export const wizardComposerEdtiorEventPrefix = "wizard-editor";
|
||||
|
||||
export default class CustomWizardComposerEditor extends ComposerEditor {
|
||||
@service modal;
|
||||
allowUpload = true;
|
||||
showLink = false;
|
||||
topic = null;
|
||||
showToolbar = true;
|
||||
focusTarget = "reply";
|
||||
canWhisper = false;
|
||||
lastValidatedAt = "lastValidatedAt";
|
||||
popupMenuOptions = [];
|
||||
draftStatus = "null";
|
||||
@alias("topicList.loadingMore") loadingMore;
|
||||
wizardEventFieldId = null;
|
||||
composerEventPrefix = wizardComposerEdtiorEventPrefix;
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
this.fileUploadElementId = `file-uploader-${dasherize(this.field.id)}`;
|
||||
this.editorInputClass = `.${dasherize(this.field.type)}-${dasherize(
|
||||
this.field.id
|
||||
)} .d-editor-input`;
|
||||
}
|
||||
|
||||
@discourseComputed
|
||||
allowedFileTypes() {
|
||||
return this.siteSettings.authorized_extensions
|
||||
.split("|")
|
||||
.map((ext) => "." + ext)
|
||||
.join(",");
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
uploadIcon() {
|
||||
return uploadIcon(false, this.siteSettings);
|
||||
}
|
||||
|
||||
@bind
|
||||
_handleImageDeleteButtonClick() {
|
||||
this.session.set("wizardEventFieldId", this.field.id);
|
||||
super._handleImageDeleteButtonClick(...arguments);
|
||||
}
|
||||
|
||||
@action
|
||||
extraButtons(toolbar) {
|
||||
const component = this;
|
||||
|
||||
if (this.allowUpload && this.uploadIcon) {
|
||||
toolbar.addButton({
|
||||
id: "upload",
|
||||
group: "insertions",
|
||||
icon: this.uploadIcon,
|
||||
title: "upload",
|
||||
sendAction: (event) => component.send("showUploadModal", event),
|
||||
});
|
||||
}
|
||||
|
||||
toolbar.addButton({
|
||||
id: "link",
|
||||
icon: "link",
|
||||
group: "insertions",
|
||||
shortcut: "K",
|
||||
trimLeading: true,
|
||||
unshift: true,
|
||||
sendAction: (event) => component.send("showLinkModal", event),
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
showLinkModal(toolbarEvent) {
|
||||
let linkText = "";
|
||||
this._lastSel = toolbarEvent.selected;
|
||||
|
||||
if (this._lastSel) {
|
||||
linkText = this._lastSel.value;
|
||||
}
|
||||
this.modal.show(InsertHyperlink, {
|
||||
model: { linkText, toolbarEvent },
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
showUploadModal() {
|
||||
this.session.set("wizardEventFieldId", this.field.id);
|
||||
document.getElementById(this.fileUploadElementId).click();
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
import ComposerEditor from "discourse/components/composer-editor";
|
||||
import {
|
||||
bind,
|
||||
default as discourseComputed,
|
||||
on,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { caretPosition, inCodeBlock } from "discourse/lib/utilities";
|
||||
import highlightSyntax from "discourse/lib/highlight-syntax";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import Site from "discourse/models/site";
|
||||
import { uploadIcon } from "discourse/lib/uploads";
|
||||
import { dasherize } from "@ember/string";
|
||||
import InsertHyperlink from "discourse/components/modal/insert-hyperlink";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
const IMAGE_MARKDOWN_REGEX =
|
||||
/!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
|
||||
|
||||
export const wizardComposerEdtiorEventPrefix = "wizard-editor";
|
||||
|
||||
export default ComposerEditor.extend({
|
||||
modal: service(),
|
||||
|
||||
classNameBindings: ["fieldClass"],
|
||||
allowUpload: true,
|
||||
showLink: false,
|
||||
topic: null,
|
||||
showToolbar: true,
|
||||
focusTarget: "reply",
|
||||
canWhisper: false,
|
||||
lastValidatedAt: "lastValidatedAt",
|
||||
popupMenuOptions: [],
|
||||
draftStatus: "null",
|
||||
replyPlaceholder: alias("field.translatedPlaceholder"),
|
||||
wizardEventFieldId: null,
|
||||
|
||||
@on("didInsertElement")
|
||||
_composerEditorInit() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.composerEventPrefix = wizardComposerEdtiorEventPrefix;
|
||||
|
||||
if (this.siteSettings.mentionables_enabled) {
|
||||
const $input = $(this.element.querySelector(".d-editor-input"));
|
||||
|
||||
Site.currentProp("mentionable_items", this.wizard.mentionable_items);
|
||||
const { SEPARATOR } = requirejs(
|
||||
"discourse/plugins/discourse-mentionables/discourse/lib/discourse-markdown/mentionable-items"
|
||||
);
|
||||
const { searchMentionableItem } = requirejs(
|
||||
"discourse/plugins/discourse-mentionables/discourse/lib/mentionable-item-search"
|
||||
);
|
||||
|
||||
$input.autocomplete({
|
||||
template: findRawTemplate("javascripts/mentionable-item-autocomplete"),
|
||||
key: SEPARATOR,
|
||||
afterComplete: (value) => {
|
||||
this.composer.set("reply", value);
|
||||
scheduleOnce("afterRender", () => $input.blur().focus());
|
||||
},
|
||||
transformComplete: (item) => item.model.slug,
|
||||
dataSource: (term) =>
|
||||
term.match(/\s/)
|
||||
? null
|
||||
: searchMentionableItem(term, this.siteSettings),
|
||||
triggerRule: (textarea) =>
|
||||
!inCodeBlock(textarea.value, caretPosition(textarea)),
|
||||
});
|
||||
}
|
||||
|
||||
const field = this.field;
|
||||
this.editorInputClass = `.${dasherize(field.type)}-${dasherize(
|
||||
field.id
|
||||
)} .d-editor-input`;
|
||||
|
||||
this._uppyInstance.on("file-added", () => {
|
||||
this.session.set("wizardEventFieldId", field.id);
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("field.id")
|
||||
fileUploadElementId(fieldId) {
|
||||
return `file-uploader-${dasherize(fieldId)}`;
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
allowedFileTypes() {
|
||||
return this.siteSettings.authorized_extensions
|
||||
.split("|")
|
||||
.map((ext) => "." + ext)
|
||||
.join(",");
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
uploadIcon() {
|
||||
return uploadIcon(false, this.siteSettings);
|
||||
},
|
||||
|
||||
@bind
|
||||
_handleImageDeleteButtonClick(event) {
|
||||
if (!event.target.classList.contains("delete-image-button")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = parseInt(
|
||||
event.target.closest(".button-wrapper").dataset.imageIndex,
|
||||
10
|
||||
);
|
||||
const matchingPlaceholder =
|
||||
this.get("composer.reply").match(IMAGE_MARKDOWN_REGEX);
|
||||
|
||||
this.session.set("wizardEventFieldId", this.field.id);
|
||||
this.appEvents.trigger(
|
||||
"composer:replace-text",
|
||||
matchingPlaceholder[index],
|
||||
"",
|
||||
{ regex: IMAGE_MARKDOWN_REGEX, index }
|
||||
);
|
||||
},
|
||||
|
||||
@action
|
||||
extraButtons(toolbar) {
|
||||
const component = this;
|
||||
|
||||
if (this.allowUpload && this.uploadIcon) {
|
||||
toolbar.addButton({
|
||||
id: "upload",
|
||||
group: "insertions",
|
||||
icon: this.uploadIcon,
|
||||
title: "upload",
|
||||
sendAction: (event) => component.send("showUploadModal", event),
|
||||
});
|
||||
}
|
||||
|
||||
toolbar.addButton({
|
||||
id: "link",
|
||||
icon: "link",
|
||||
group: "insertions",
|
||||
shortcut: "K",
|
||||
trimLeading: true,
|
||||
unshift: true,
|
||||
sendAction: (event) => component.send("showLinkModal", event),
|
||||
});
|
||||
|
||||
if (this.siteSettings.mentionables_enabled) {
|
||||
const { SEPARATOR } = requirejs(
|
||||
"discourse/plugins/discourse-mentionables/discourse/lib/discourse-markdown/mentionable-items"
|
||||
);
|
||||
|
||||
toolbar.addButton({
|
||||
id: "insert-mentionable",
|
||||
group: "extras",
|
||||
icon: this.siteSettings.mentionables_composer_button_icon,
|
||||
title: "mentionables.composer.insert.title",
|
||||
perform: () => {
|
||||
this.appEvents.trigger("wizard-editor:insert-text", {
|
||||
fieldId: this.field.id,
|
||||
text: SEPARATOR,
|
||||
});
|
||||
const $textarea = $(
|
||||
document.querySelector(
|
||||
`.composer-field.${this.field.id} textarea.d-editor-input`
|
||||
)
|
||||
);
|
||||
$textarea.trigger("keyup.autocomplete");
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
previewUpdated(preview) {
|
||||
highlightSyntax(preview, this.siteSettings, this.session);
|
||||
|
||||
if (this.siteSettings.mentionables_enabled) {
|
||||
const { linkSeenMentionableItems } = requirejs(
|
||||
"discourse/plugins/discourse-mentionables/discourse/lib/mentionable-items-preview-styling"
|
||||
);
|
||||
linkSeenMentionableItems(preview, this.siteSettings);
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
@action
|
||||
showLinkModal(toolbarEvent) {
|
||||
let linkText = "";
|
||||
this._lastSel = toolbarEvent.selected;
|
||||
|
||||
if (this._lastSel) {
|
||||
linkText = this._lastSel.value;
|
||||
}
|
||||
this.modal.show(InsertHyperlink, {
|
||||
model: { linkText, toolbarEvent },
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
showUploadModal() {
|
||||
this.session.set("wizardEventFieldId", this.field.id);
|
||||
document.getElementById(this.fileUploadElementId).click();
|
||||
},
|
||||
});
|
|
@ -1,26 +1,28 @@
|
|||
<DEditor
|
||||
@tabindex={{field.tabindex}}
|
||||
@value={{composer.reply}}
|
||||
@placeholderOverride={{replyPlaceholder}}
|
||||
@previewUpdated={{this.previewUpdated}}
|
||||
@markdownOptions={{markdownOptions}}
|
||||
@extraButtons={{this.extraButtons}}
|
||||
@importQuote={{this.importQuote}}
|
||||
@showUploadModal={{this.showUploadModal}}
|
||||
@togglePreview={{this.togglePreview}}
|
||||
@validation={{validation}}
|
||||
@loading={{composer.loading}}
|
||||
@showLink={{showLink}}
|
||||
@wizardComposer={{true}}
|
||||
@fieldId={{field.id}}
|
||||
@disabled={{disableTextarea}}
|
||||
@outletArgs={{hash composer=composer editorType="composer"}}
|
||||
/>
|
||||
<div class={{this.fieldClass}}>
|
||||
<DEditor
|
||||
@tabindex={{field.tabindex}}
|
||||
@value={{composer.reply}}
|
||||
@placeholderOverride={{replyPlaceholder}}
|
||||
@previewUpdated={{this.previewUpdated}}
|
||||
@markdownOptions={{markdownOptions}}
|
||||
@extraButtons={{this.extraButtons}}
|
||||
@importQuote={{this.importQuote}}
|
||||
@showUploadModal={{this.showUploadModal}}
|
||||
@togglePreview={{this.togglePreview}}
|
||||
@validation={{validation}}
|
||||
@loading={{composer.loading}}
|
||||
@showLink={{showLink}}
|
||||
@wizardComposer={{true}}
|
||||
@fieldId={{field.id}}
|
||||
@disabled={{disableTextarea}}
|
||||
@outletArgs={{hash composer=composer editorType="composer"}}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
id={{fileUploadElementId}}
|
||||
class="wizard-composer-upload"
|
||||
accept={{allowedFileTypes}}
|
||||
multiple
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
id={{this.fileUploadElementId}}
|
||||
class="wizard-composer-upload"
|
||||
accept={{this.allowedFileTypes}}
|
||||
multiple
|
||||
/>
|
||||
</div>
|
|
@ -1,8 +1,8 @@
|
|||
<CustomWizardComposerEditor
|
||||
@field={{field}}
|
||||
@composer={{composer}}
|
||||
@wizard={{wizard}}
|
||||
@fieldClass={{fieldClass}}
|
||||
@field={{this.field}}
|
||||
@composer={{this.composer}}
|
||||
@wizard={{this.wizard}}
|
||||
@fieldClass={{this.fieldClass}}
|
||||
@groupsMentioned={{this.groupsMentioned}}
|
||||
@cannotSeeMention={{this.cannotSeeMention}}
|
||||
@importQuote={{this.importQuote}}
|
||||
|
@ -11,15 +11,13 @@
|
|||
/>
|
||||
|
||||
<div class="bottom-bar">
|
||||
<button
|
||||
<DButton
|
||||
@action={{this.togglePreview}}
|
||||
class="wizard-btn toggle-preview"
|
||||
{{action "togglePreview"}}
|
||||
type="button"
|
||||
>
|
||||
<span class="d-button-label">{{i18n togglePreviewLabel}}</span>
|
||||
</button>
|
||||
@label={{togglePreviewLabel}}
|
||||
/>
|
||||
|
||||
{{#if field.char_counter}}
|
||||
{{wizard-char-counter field.value field.max_length}}
|
||||
{{#if this.field.char_counter}}
|
||||
{{wizard-char-counter this.field.value field.max_length}}
|
||||
{{/if}}
|
||||
</div>
|
Laden …
In neuem Issue referenzieren