2023-11-15 09:53:07 +01:00
|
|
|
import { ApplicationController } from './application_controller';
|
|
|
|
import { Editor } from '@tiptap/core';
|
|
|
|
import StarterKit from '@tiptap/starter-kit';
|
|
|
|
import Mention from '@tiptap/extension-mention';
|
2023-11-15 15:32:38 +01:00
|
|
|
import tippy, { type Instance } from 'tippy.js';
|
2023-11-15 09:53:07 +01:00
|
|
|
import { httpRequest } from '@utils';
|
|
|
|
|
|
|
|
export class AttestationController extends ApplicationController {
|
|
|
|
static values = {
|
|
|
|
tags: Array,
|
|
|
|
url: String
|
|
|
|
};
|
|
|
|
|
|
|
|
static targets = ['editor', 'bold'];
|
|
|
|
|
|
|
|
declare readonly tagsValue: string[];
|
|
|
|
declare readonly urlValue: string;
|
|
|
|
declare editor: Editor;
|
|
|
|
declare editorTarget: HTMLElement;
|
|
|
|
declare boldTarget: HTMLButtonElement;
|
|
|
|
|
|
|
|
connect() {
|
|
|
|
const conf = {
|
|
|
|
element: this.editorTarget,
|
|
|
|
editorProps: {
|
|
|
|
attributes: {
|
|
|
|
class: 'fr-input'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
extensions: [
|
|
|
|
StarterKit,
|
|
|
|
Mention.configure({
|
|
|
|
HTMLAttributes: {
|
|
|
|
class: 'mention'
|
|
|
|
},
|
|
|
|
suggestion: {
|
|
|
|
items: ({ query }) => {
|
|
|
|
return this.tagsValue
|
|
|
|
.filter((item) =>
|
|
|
|
item.toLowerCase().startsWith(query.toLowerCase())
|
|
|
|
)
|
|
|
|
.slice(0, 5);
|
|
|
|
},
|
|
|
|
|
|
|
|
render: () => {
|
2023-11-15 15:32:38 +01:00
|
|
|
let popup: Instance;
|
2023-11-15 09:53:07 +01:00
|
|
|
let div: HTMLElement;
|
|
|
|
let selectedIndex = 0;
|
|
|
|
let items: string[];
|
|
|
|
let command: (props: object) => void;
|
|
|
|
|
|
|
|
const makeList = () => {
|
|
|
|
return items
|
|
|
|
.map((item, i) => {
|
|
|
|
if (i == selectedIndex) {
|
|
|
|
return `<li class='selected'>${item}</li>`;
|
|
|
|
} else {
|
|
|
|
return `<li>${item}</li>`;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.join('');
|
|
|
|
};
|
|
|
|
|
|
|
|
return {
|
|
|
|
onStart: (props) => {
|
|
|
|
items = props.items;
|
|
|
|
command = props.command;
|
|
|
|
|
|
|
|
div = document.createElement('UL');
|
|
|
|
div.innerHTML = makeList();
|
|
|
|
|
|
|
|
if (!props.clientRect) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-15 15:32:38 +01:00
|
|
|
popup = tippy(document.body, {
|
|
|
|
getReferenceClientRect: () => {
|
|
|
|
const domrect = props.clientRect?.();
|
|
|
|
if (!domrect) {
|
|
|
|
throw new Error('No client rect');
|
|
|
|
}
|
|
|
|
return domrect;
|
|
|
|
},
|
2023-11-15 09:53:07 +01:00
|
|
|
appendTo: () => this.element,
|
|
|
|
content: div,
|
|
|
|
showOnCreate: true,
|
|
|
|
interactive: true,
|
|
|
|
trigger: 'manual',
|
|
|
|
placement: 'bottom-start'
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onUpdate(props) {
|
|
|
|
command = props.command;
|
|
|
|
items = props.items;
|
|
|
|
|
|
|
|
div.innerHTML = makeList();
|
|
|
|
|
|
|
|
if (!props.clientRect) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-15 15:32:38 +01:00
|
|
|
popup.setProps({
|
|
|
|
getReferenceClientRect: () => {
|
|
|
|
const domrect = props.clientRect?.();
|
|
|
|
if (!domrect) {
|
|
|
|
throw new Error('No client rect');
|
|
|
|
}
|
|
|
|
return domrect;
|
|
|
|
}
|
2023-11-15 09:53:07 +01:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onKeyDown(props) {
|
|
|
|
if (props.event.key === 'Escape') {
|
2023-11-15 15:32:38 +01:00
|
|
|
popup.hide();
|
2023-11-15 09:53:07 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (props.event.key === 'ArrowDown') {
|
|
|
|
selectedIndex = (selectedIndex + 1) % items.length;
|
|
|
|
div.innerHTML = makeList();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (props.event.key === 'ArrowUp') {
|
|
|
|
selectedIndex =
|
|
|
|
(selectedIndex + items.length - 1) % items.length;
|
|
|
|
div.innerHTML = makeList();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (props.event.key === 'Enter') {
|
|
|
|
const item = items[selectedIndex];
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
command({ id: item });
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
onExit() {
|
2023-11-15 15:32:38 +01:00
|
|
|
popup.destroy();
|
2023-11-15 09:53:07 +01:00
|
|
|
div.remove();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
],
|
|
|
|
content:
|
|
|
|
'<p>La situation de M. <span data-type="mention" data-id="nom"></span> dont la demande de logement social</p>'
|
|
|
|
};
|
|
|
|
|
|
|
|
this.editor = new Editor(conf);
|
|
|
|
|
|
|
|
this.editor.on('transaction', () => {
|
|
|
|
this.boldTarget.disabled = !this.editor
|
|
|
|
.can()
|
|
|
|
.chain()
|
|
|
|
.focus()
|
|
|
|
.toggleBold()
|
|
|
|
.run();
|
|
|
|
|
|
|
|
if (this.editor.isActive('bold')) {
|
|
|
|
this.boldTarget.classList.add('fr-btn--secondary');
|
|
|
|
} else {
|
|
|
|
this.boldTarget.classList.remove('fr-btn--secondary');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bold() {
|
|
|
|
this.editor.chain().focus().toggleBold().run();
|
|
|
|
}
|
|
|
|
|
|
|
|
send() {
|
|
|
|
const json = this.editor.getJSON();
|
|
|
|
httpRequest(this.urlValue, { method: 'put', json }).json();
|
|
|
|
}
|
|
|
|
}
|