Skip to content

Commit

Permalink
feat(UI): Blur markdown editor when user clicks outside (#1144)
Browse files Browse the repository at this point in the history
And save a trimmed version of the text.

As requested in feedbacks.

---------

Co-authored-by: Auguste Baum <[email protected]>
  • Loading branch information
rouk1 and auguste-probabl authored Jan 20, 2025
1 parent 8870723 commit 8b2f0c7
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 9 deletions.
8 changes: 6 additions & 2 deletions skore-ui/src/stores/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,11 @@ export const useProjectStore = defineStore("project", () => {
*/
let _stopBackendPolling: (() => void) | null = null;
async function startBackendPolling() {
_isCanceledCall = false;
_stopBackendPolling = await poll(fetch, 1500);
// ensure that there is only one polling running
if (_stopBackendPolling === null) {
_isCanceledCall = false;
_stopBackendPolling = await poll(fetch, 1500);
}
}

/**
Expand All @@ -107,6 +110,7 @@ export const useProjectStore = defineStore("project", () => {
_isCanceledCall = true;
if (_stopBackendPolling) {
_stopBackendPolling();
_stopBackendPolling = null;
}
}

Expand Down
46 changes: 39 additions & 7 deletions skore-ui/src/views/project/ItemNote.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { nextTick, ref, useTemplateRef, watch } from "vue";
import { nextTick, onBeforeUnmount, ref, useTemplateRef, watch } from "vue";
import MarkdownWidget from "@/components/MarkdownWidget.vue";
import RichTextEditor from "@/components/RichTextEditor.vue";
Expand All @@ -9,27 +9,54 @@ import { useProjectStore } from "@/stores/project";
const props = defineProps<{ name: string; note: string | null }>();
const projectStore = useProjectStore();
const el = useTemplateRef<HTMLDivElement>("el");
const editor = useTemplateRef<InstanceType<typeof RichTextEditor>>("editor");
const isEditing = ref(false);
const innerNote = ref(`${props.note !== null ? props.note : ""}`);
function onEdit() {
projectStore.stopBackendPolling();
isEditing.value = true;
// start to listen for click outside of this component
document.addEventListener("click", onClickOutside);
// actually wait for the editor to be open to focus it
nextTick(() => {
editor.value?.focus();
});
}
async function onEditionEnd() {
await projectStore.setNoteOnItem(props.name, innerNote.value);
projectStore.startBackendPolling();
isEditing.value = false;
// if there is multiple instances of the component in the page
// this function may be called multiple times
// so guard this call
if (isEditing.value) {
// stop listening to outside click
document.removeEventListener("click", onClickOutside);
await projectStore.setNoteOnItem(props.name, innerNote.value.trimEnd());
isEditing.value = false;
// actually wait for the editor to be closed to resatrt backend polling
nextTick(() => {
projectStore.startBackendPolling();
});
}
}
function onClear() {
async function onClear() {
innerNote.value = "";
onEditionEnd();
await onEditionEnd();
}
async function onClickOutside(e: Event) {
if (isEditing.value) {
const clicked = e.target as Node;
if (el.value && document.body.contains(clicked)) {
// is it a click outside ?
const isOutside = !el.value.contains(clicked);
if (isOutside) {
await onEditionEnd();
}
}
}
}
watch(innerNote, async () => {
Expand All @@ -42,10 +69,15 @@ watch(
innerNote.value = `${newNote !== null ? newNote : ""}`;
}
);
onBeforeUnmount(() => {
// avoid event listener leak in case the component is unmounted in edit mode
document.removeEventListener("click", onClickOutside);
});
</script>

<template>
<div class="item-note">
<div class="item-note" ref="el">
<div class="header">
<div class="info">
<div>Note</div>
Expand Down

0 comments on commit 8b2f0c7

Please sign in to comment.