import {
defaultKeymap, history, historyKeymap, indentWithTab,
} from '@codemirror/commands';
-import {EditorState} from '@codemirror/state';
+import {Compartment, EditorState} from '@codemirror/state';
import {getTheme} from './themes';
/**
return [
getTheme(parentEl),
lineNumbers(),
- highlightActiveLineGutter(),
drawSelection(),
dropCursor(),
bracketMatching(),
rectangularSelection(),
- highlightActiveLine(),
+ ];
+}
+
+/**
+ * @returns {({extension: Extension}|readonly Extension[])[]}
+ */
+function getDynamicActiveLineHighlighter() {
+ const highlightingCompartment = new Compartment();
+ const domEvents = {
+ focus(event, view) {
+ view.dispatch({
+ effects: highlightingCompartment.reconfigure([
+ highlightActiveLineGutter(),
+ highlightActiveLine(),
+ ]),
+ });
+ },
+ blur(event, view) {
+ view.dispatch({
+ effects: highlightingCompartment.reconfigure([]),
+ });
+ },
+ };
+
+ return [
+ highlightingCompartment.of([]),
+ EditorView.domEventHandlers(domEvents),
];
}
export function viewerExtensions(parentEl) {
return [
...common(parentEl),
+ getDynamicActiveLineHighlighter(),
keymap.of([
...defaultKeymap,
]),
export function editorExtensions(parentEl) {
return [
...common(parentEl),
+ highlightActiveLineGutter(),
+ highlightActiveLine(),
history(),
keymap.of([
...defaultKeymap,
this.uploadLimitMessage = this.$opts.uploadLimitMessage;
this.zoneText = this.$opts.zoneText;
this.fileAcceptTypes = this.$opts.fileAccept;
+ this.allowMultiple = this.$opts.allowMultiple === 'true';
this.setupListeners();
}
}
manualSelectHandler() {
- const input = elem('input', {type: 'file', style: 'left: -400px; visibility: hidden; position: fixed;', accept: this.fileAcceptTypes});
+ const input = elem('input', {
+ type: 'file',
+ style: 'left: -400px; visibility: hidden; position: fixed;',
+ accept: this.fileAcceptTypes,
+ multiple: this.allowMultiple ? '' : null,
+ });
this.container.append(input);
input.click();
input.addEventListener('change', () => {
const el = document.createElement(tagName);
for (const [key, val] of Object.entries(attrs)) {
- el.setAttribute(key, val);
+ if (val === null) {
+ el.removeAttribute(key);
+ } else {
+ el.setAttribute(key, val);
+ }
}
for (const child of children) {
-<div component="sortable-list" option:sortable-list:handle-selector=".handle">
+<div component="sortable-list"
+ option:sortable-list:handle-selector=".handle, a">
@foreach($attachments as $attachment)
<div component="ajax-delete-row"
option:ajax-delete-row:url="{{ url('/attachments/' . $attachment->id) }}"
option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}"
option:dropzone:zone-text="{{ trans('entities.attachments_dropzone') }}"
option:dropzone:file-accept="*"
+ option:dropzone:allow-multiple="true"
class="px-l files">
<div refs="attachments@list-container dropzone@drop-target" class="relative">
option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}"
option:dropzone:zone-text="{{ trans('components.image_dropzone_drop') }}"
option:dropzone:file-accept="image/*"
+ option:dropzone:allow-multiple="true"
option:image-manager:uploaded-to="{{ $uploaded_to ?? 0 }}"
class="image-manager">