Skip to content

Commit

Permalink
feat: improve note clicks experience
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 25, 2024
1 parent 9011527 commit 87c5648
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 18 deletions.
48 changes: 35 additions & 13 deletions packages/client/internals/NoteDisplay.vue
Expand Up @@ -16,14 +16,18 @@ defineEmits(['click'])
const withClicks = computed(() => props.clicksContext?.current != null && props.noteHtml?.includes('slidev-note-click-mark'))
const noteDisplay = ref<HTMLElement | null>(null)
const CLASS_FADE = 'slidev-note-fade'
const CLASS_MARKER = 'slidev-note-click-mark'
function highlightNote() {
if (!noteDisplay.value || !withClicks.value || props.clicksContext?.current == null)
return
const disabled = +props.clicksContext?.current < 0 || +props.clicksContext?.current >= CLICKS_MAX
const current = +props.clicksContext?.current ?? CLICKS_MAX
const disabled = current < 0 || current >= CLICKS_MAX
if (disabled) {
Array.from(noteDisplay.value.querySelectorAll('*'))
.forEach(el => el.classList.remove('slidev-note-fade'))
.forEach(el => el.classList.remove(CLASS_FADE))
return
}
Expand All @@ -36,10 +40,14 @@ function highlightNote() {
ignoreParent(node.parentElement)
}
const markers = Array.from(noteDisplay.value.querySelectorAll('.slidev-note-click-mark'))
const markers = Array.from(noteDisplay.value.querySelectorAll(`.${CLASS_MARKER}`)) as HTMLElement[]
const markersMap = new Map<number, HTMLElement>()
// Convert all sibling text nodes to spans, so we attach classes to them
for (const marker of markers) {
const parent = marker.parentElement!
const clicks = Number(marker.dataset!.clicks)
markersMap.set(clicks, marker)
// Ignore the parents of the marker, so the class only applies to the children
ignoreParent(parent)
Array.from(parent!.childNodes)
Expand All @@ -56,25 +64,39 @@ function highlightNote() {
let count = 0
const groups = new Map<number, Element[]>()
// Segmenting notes by clicks
const segments = new Map<number, Element[]>()
for (const child of children) {
if (!groups.has(count))
groups.set(count, [])
groups.get(count)!.push(child)
if (child.classList.contains('slidev-note-click-mark'))
if (!segments.has(count))
segments.set(count, [])
segments.get(count)!.push(child)
// Update count when reach marker
if (child.classList.contains(CLASS_MARKER))
count = Number((child as HTMLElement).dataset.clicks) || (count + 1)
}
for (const [count, els] of groups) {
// Apply
for (const [count, els] of segments) {
els.forEach(el => el.classList.toggle(
'slidev-note-fade',
CLASS_FADE,
nodeToIgnores.has(el)
? false
: +count !== +props.clicksContext!.current!,
: count !== current,
))
}
for (const [clicks, marker] of markersMap) {
marker.classList.remove(CLASS_FADE)
marker.classList.toggle(`${CLASS_MARKER}-past`, clicks < current)
marker.classList.toggle(`${CLASS_MARKER}-active`, clicks === current)
marker.classList.toggle(`${CLASS_MARKER}-next`, clicks === current + 1)
marker.classList.toggle(`${CLASS_MARKER}-future`, clicks > current + 1)
marker.addEventListener('dblclick', (e) => {
props.clicksContext!.current = clicks
e.stopPropagation()
e.stopImmediatePropagation()
})
}
}
watch(
Expand Down
1 change: 1 addition & 0 deletions packages/client/pages/notes.vue
Expand Up @@ -51,6 +51,7 @@ function decreaseFontSize() {
:note="currentRoute?.meta?.slide?.note"
:note-html="currentRoute?.meta?.slide?.noteHTML"
:placeholder="`No notes for Slide ${pageNo}.`"
:clicks-context="currentRoute?.meta?.__clicksContext"
/>
</div>
<div class="flex-none border-t border-main">
Expand Down
2 changes: 1 addition & 1 deletion packages/client/pages/overview.vue
Expand Up @@ -181,7 +181,7 @@ onMounted(() => {
class="max-w-250 w-250 text-lg rounded p3"
:auto-height="true"
:editing="edittingNote === idx"
:clicks="getClicksContext(route).current"
:clicks-context="getClicksContext(route)"
@dblclick="edittingNote !== idx ? edittingNote = idx : null"
@update:editing="edittingNote = null"
/>
Expand Down
6 changes: 3 additions & 3 deletions packages/client/pages/presenter.vue
Expand Up @@ -121,7 +121,7 @@ onMounted(() => {
class="h-full w-full"
>
<SlideWrapper
:is="nextFrame[0].component as any"
:is="(nextFrame[0].component as any)"
:key="nextFrame[0].path"
:clicks-context="nextFrameClicksCtx"
:class="getSlideClass(nextFrame[0])"
Expand All @@ -144,7 +144,7 @@ onMounted(() => {
:no="currentSlideId"
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
:editing="notesEditing"
:clicks="clicksContext.current"
:clicks-context="clicksContext"
:style="{ fontSize: `${presenterNotesFontSize}em` }"
/>
<NoteStatic
Expand All @@ -153,7 +153,7 @@ onMounted(() => {
:no="currentSlideId"
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
:style="{ fontSize: `${presenterNotesFontSize}em` }"
:clicks="clicksContext.current"
:clicks-context="clicksContext"
/>
<div class="border-t border-main py-1 px-2 text-sm">
<IconButton title="Increase font size" @click="increasePresenterFontSize">
Expand Down
13 changes: 12 additions & 1 deletion packages/client/styles/index.css
Expand Up @@ -70,9 +70,20 @@ html {
}

.slidev-note-click-mark {
user-select: none;
font-size: 0.7em;
display: inline-flex;
--uno: text-violet bg-violet/10 px1 font-mono rounded items-center;
--uno: text-violet bg-violet/10 px1 font-mono rounded items-center border border-transparent;
}
.slidev-note-click-mark.slidev-note-click-mark-active {
--uno: border border-violet;
}
.slidev-note-click-mark.slidev-note-click-mark-past {
filter: saturate(0);
opacity: 0.5;
}
.slidev-note-click-mark.slidev-note-click-mark-future {
opacity: 0.5;
}

.slidev-note-click-mark::before {
Expand Down

0 comments on commit 87c5648

Please sign in to comment.