start on trying to save focus for timeline

feels like this isn't really working because I can't distinguish between blur events because the timeline is being destroyed and blur events because the user clicked and we lost focus... not sure how important this feature is, and it adds a lot of complexity in the form of extra attributes to track the focused element
save-focus-on-timeline
Nolan Lawson 2018-02-10 12:12:05 -08:00
rodzic e6bf344aec
commit 820d77a78f
4 zmienionych plików z 98 dodań i 15 usunięć

Wyświetl plik

@ -1,7 +1,8 @@
<article class="status-article {{getClasses(originalStatus, timelineType, isStatusInOwnThread)}}"
tabindex="0"
delegate-click-key="{{delegateKey}}"
delegate-keydown-key="{{delegateKey}}"
delegate-click-key="{{elementKey}}"
delegate-keydown-key="{{elementKey}}"
focus-key="{{elementKey}}"
aria-posinset="{{index}}"
aria-setsize="{{length}}"
aria-label="Status by {{originalStatus.account.display_name || originalStatus.account.username}}"
@ -94,15 +95,15 @@
export default {
oncreate() {
let delegateKey = this.get('delegateKey')
let elementKey = this.get('elementKey')
let onClickOrKeydown = this.onClickOrKeydown.bind(this)
registerDelegate('click', delegateKey, onClickOrKeydown)
registerDelegate('keydown', delegateKey, onClickOrKeydown)
registerDelegate('click', elementKey, onClickOrKeydown)
registerDelegate('keydown', elementKey, onClickOrKeydown)
},
ondestroy() {
let delegateKey = this.get('delegateKey')
unregisterDelegate('click', delegateKey)
unregisterDelegate('keydown', delegateKey)
let elementKey = this.get('elementKey')
unregisterDelegate('click', elementKey)
unregisterDelegate('keydown', elementKey)
},
components: {
StatusSidebar,
@ -147,7 +148,7 @@
computed: {
originalStatus: (status) => status.reblog ? status.reblog : status,
statusId: (originalStatus) => originalStatus.id,
delegateKey: (statusId, timelineType, timelineValue) => `status-${timelineType}-${timelineValue}-${statusId}`,
elementKey: (statusId, timelineType, timelineValue) => `status-${timelineType}-${timelineValue}-${statusId}`,
contextualStatusId: ($currentInstance, timelineType, timelineValue, status, notification) => {
return `${$currentInstance}/${timelineType}/${timelineValue}/${notification ? notification.id : ''}/${status.id}`
},

Wyświetl plik

@ -1,4 +1,9 @@
<div class="timeline" role="feed" aria-label="{{label}}">
<div class="timeline"
role="feed"
aria-label="{{label}}"
on:focusWithCapture="saveFocus(event)"
on:blurWithCapture="clearFocus(event)"
>
{{#if !$initialized}}
<LoadingPage />
{{/if}}
@ -55,11 +60,19 @@
import { database } from '../../_database/database'
import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
import LoadingPage from '../LoadingPage.html'
import { focusWithCapture, blurWithCapture } from '../../_utils/events'
export default {
async oncreate() {
oncreate() {
console.log('timeline oncreate()')
setupTimeline()
if (this.store.get('initialized')) {
console.log('initialized!!!!')
this.restoreFocus()
}
},
ondestroy() {
console.log('ondestroy')
},
data: () => ({
StatusVirtualListItem,
@ -109,6 +122,10 @@
PseudoVirtualList,
LoadingPage
},
events: {
focusWithCapture,
blurWithCapture
},
methods: {
initialize() {
if (this.store.get('initialized') || !this.store.get('timelineItemIds')) {
@ -124,7 +141,46 @@
return
}
fetchTimelineItemsOnScrollToBottom()
}
},
saveFocus(e) {
let instanceName = this.store.get('currentInstance')
let timelineName = this.get('timeline')
let lastFocusedElementSelector
let activeElement = e.target
if (activeElement) {
let focusKey = activeElement.getAttribute('focus-key')
if (focusKey) {
lastFocusedElementSelector = `[focus-key=${focusKey}]`
}
}
console.log('saving focus to ', lastFocusedElementSelector)
this.store.setForTimeline(instanceName, timelineName, {
lastFocusedElementSelector
})
},
clearFocus() {
/*console.log('clearing focus')
let instanceName = this.store.get('currentInstance')
let timelineName = this.get('timeline')
this.store.setForTimeline(instanceName, timelineName, {
lastFocusedElementSelector: null
})*/
},
restoreFocus() {
let lastFocusedElementSelector = this.store.get('lastFocusedElementSelector')
console.log('lastFocused', lastFocusedElementSelector)
if (lastFocusedElementSelector) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
let element = document.querySelector(lastFocusedElementSelector)
console.log('el', element)
if (element) {
element.focus()
}
})
})
}
},
}
}
</script>

Wyświetl plik

@ -1,11 +1,19 @@
function computeForTimeline(store, key) {
store.compute(key, ['currentTimelineData'], (currentTimelineData) => currentTimelineData[key])
}
export function timelineComputations (store) {
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
(currentInstance, currentTimeline, timelines) => {
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
})
store.compute('timelineItemIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.timelineItemIds)
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
computeForTimeline(store, 'timelineItemIds')
computeForTimeline(store, 'runningUpdate')
computeForTimeline(store, 'initialized')
computeForTimeline(store, 'lastFocusedElementSelector')
store.compute('lastTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds && timelineItemIds.length && timelineItemIds[timelineItemIds.length - 1])
}

Wyświetl plik

@ -34,3 +34,21 @@ export function mouseover (node, callback) {
}
}
}
export function focusWithCapture (node, callback) {
node.addEventListener('focus', callback, true)
return {
teardown () {
node.removeEventListener('focus', callback, true)
}
}
}
export function blurWithCapture (node, callback) {
node.addEventListener('blur', callback, true)
return {
teardown () {
node.removeEventListener('blur', callback, true)
}
}
}