forked from cybrespace/pinafore
go back to having Timeline.html manage focus
This commit is contained in:
parent
7a9cb22269
commit
cd968245e1
|
@ -7,8 +7,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<article class="notification-article"
|
<article class="notification-article"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-posinset="{{index}}" aria-setsize="{{length}}"
|
aria-posinset="{{index}}" aria-setsize="{{length}}" >
|
||||||
ref:node >
|
|
||||||
<StatusHeader :notification :notificationId :status :statusId :timelineType
|
<StatusHeader :notification :notificationId :status :statusId :timelineType
|
||||||
:account :accountId :uuid isStatusInNotification="true" />
|
:account :accountId :uuid isStatusInNotification="true" />
|
||||||
</article>
|
</article>
|
||||||
|
@ -32,16 +31,8 @@
|
||||||
import Status from './Status.html'
|
import Status from './Status.html'
|
||||||
import StatusHeader from './StatusHeader.html'
|
import StatusHeader from './StatusHeader.html'
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { restoreFocus } from '../../_utils/restoreFocus'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
|
||||||
let focusSelector = this.get('focusSelector')
|
|
||||||
if (this.refs.node && focusSelector &&
|
|
||||||
this.store.getForCurrentTimeline('shouldRestoreFocus')) {
|
|
||||||
restoreFocus(this.refs.node, focusSelector)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
Status,
|
Status,
|
||||||
StatusHeader
|
StatusHeader
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
aria-posinset="{{index}}"
|
aria-posinset="{{index}}"
|
||||||
aria-setsize="{{length}}"
|
aria-setsize="{{length}}"
|
||||||
aria-label="{{ariaLabel}}"
|
aria-label="{{ariaLabel}}"
|
||||||
on:recalculateHeight
|
on:recalculateHeight >
|
||||||
ref:node >
|
|
||||||
{{#if showHeader}}
|
{{#if showHeader}}
|
||||||
<StatusHeader :notification :notificationId :status :statusId :timelineType
|
<StatusHeader :notification :notificationId :status :statusId :timelineType
|
||||||
:account :accountId :uuid :isStatusInNotification />
|
:account :accountId :uuid :isStatusInNotification />
|
||||||
|
@ -103,7 +102,6 @@
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
import { registerClickDelegate, unregisterClickDelegate } from '../../_utils/delegate'
|
import { registerClickDelegate, unregisterClickDelegate } from '../../_utils/delegate'
|
||||||
import { classname } from '../../_utils/classname'
|
import { classname } from '../../_utils/classname'
|
||||||
import { restoreFocus } from '../../_utils/restoreFocus'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
oncreate() {
|
||||||
|
@ -112,11 +110,6 @@
|
||||||
// the whole <article> is clickable in this case
|
// the whole <article> is clickable in this case
|
||||||
registerClickDelegate(delegateKey, (e) => this.onClickOrKeydown(e))
|
registerClickDelegate(delegateKey, (e) => this.onClickOrKeydown(e))
|
||||||
}
|
}
|
||||||
let focusSelector = this.get('focusSelector')
|
|
||||||
if (this.refs.node && focusSelector &&
|
|
||||||
this.store.getForCurrentTimeline('shouldRestoreFocus')) {
|
|
||||||
restoreFocus(this.refs.node, focusSelector)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ondestroy() {
|
ondestroy() {
|
||||||
let delegateKey = this.get('delegateKey')
|
let delegateKey = this.get('delegateKey')
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
console.log('timeline oncreate()')
|
console.log('timeline oncreate()')
|
||||||
this.setupFocus()
|
this.setupFocus()
|
||||||
setupTimeline()
|
setupTimeline()
|
||||||
|
this.restoreFocus()
|
||||||
this.setupStreaming()
|
this.setupStreaming()
|
||||||
},
|
},
|
||||||
ondestroy() {
|
ondestroy() {
|
||||||
|
@ -98,7 +99,7 @@
|
||||||
VirtualListComponent: (timelineType) => {
|
VirtualListComponent: (timelineType) => {
|
||||||
return timelineType === 'notifications' ? NotificationVirtualListItem : StatusVirtualListItem
|
return timelineType === 'notifications' ? NotificationVirtualListItem : StatusVirtualListItem
|
||||||
},
|
},
|
||||||
makeProps: ($currentInstance, timelineType, timelineValue, $lastFocusedElementSelector) => async (itemId) => {
|
makeProps: ($currentInstance, timelineType, timelineValue) => async (itemId) => {
|
||||||
let res = {
|
let res = {
|
||||||
timelineType,
|
timelineType,
|
||||||
timelineValue
|
timelineValue
|
||||||
|
@ -108,12 +109,6 @@
|
||||||
} else {
|
} else {
|
||||||
res.status = await database.getStatus($currentInstance, itemId)
|
res.status = await database.getStatus($currentInstance, itemId)
|
||||||
}
|
}
|
||||||
if ($lastFocusedElementSelector && $lastFocusedElementSelector.includes(itemId)) {
|
|
||||||
// this selector is guaranteed to contain the statusId. false positives
|
|
||||||
// (e.g. notification id "1" matches notification id "11") are okay
|
|
||||||
// because Status.html won't be able to find the selector which is fine.
|
|
||||||
res.focusSelector = $lastFocusedElementSelector
|
|
||||||
}
|
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
label: (timeline, $currentInstance, timelineType, timelineValue) => {
|
label: (timeline, $currentInstance, timelineType, timelineValue) => {
|
||||||
|
@ -177,16 +172,6 @@
|
||||||
},
|
},
|
||||||
onScrollTopChanged(scrollTop) {
|
onScrollTopChanged(scrollTop) {
|
||||||
this.set({scrollTop: scrollTop})
|
this.set({scrollTop: scrollTop})
|
||||||
if (!this.get('observedOnScrollTopChanged')) {
|
|
||||||
// ignore the first scroll top change, e.g.
|
|
||||||
// because we forced a scroll top change
|
|
||||||
this.set({observedOnScrollTopChanged: true})
|
|
||||||
} else {
|
|
||||||
// after that, don't allow statuses/notifications to call focus()
|
|
||||||
// after we've already started scrolling. that causes scrolling to
|
|
||||||
// jump around
|
|
||||||
this.store.setForCurrentTimeline({shouldRestoreFocus: false})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onScrollToBottom() {
|
onScrollToBottom() {
|
||||||
if (!this.store.get('initialized') ||
|
if (!this.store.get('initialized') ||
|
||||||
|
@ -244,8 +229,7 @@
|
||||||
setupFocus() {
|
setupFocus() {
|
||||||
this.onPushState = this.onPushState.bind(this)
|
this.onPushState = this.onPushState.bind(this)
|
||||||
this.store.setForCurrentTimeline({
|
this.store.setForCurrentTimeline({
|
||||||
ignoreBlurEvents: false,
|
ignoreBlurEvents: false
|
||||||
shouldRestoreFocus: true
|
|
||||||
})
|
})
|
||||||
window.addEventListener('pushState', this.onPushState)
|
window.addEventListener('pushState', this.onPushState)
|
||||||
},
|
},
|
||||||
|
@ -289,7 +273,22 @@
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('unable to clear focus', err)
|
console.error('unable to clear focus', err)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
restoreFocus() {
|
||||||
|
let lastFocusedElementSelector = this.store.get('lastFocusedElementSelector')
|
||||||
|
if (!lastFocusedElementSelector) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
console.log('restoreFocus', lastFocusedElementSelector)
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
let element = document.querySelector(lastFocusedElementSelector)
|
||||||
|
if (element) {
|
||||||
|
element.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -18,7 +18,6 @@ export function timelineComputations (store) {
|
||||||
computeForTimeline(store, 'showHeader', false)
|
computeForTimeline(store, 'showHeader', false)
|
||||||
computeForTimeline(store, 'shouldShowHeader', false)
|
computeForTimeline(store, 'shouldShowHeader', false)
|
||||||
computeForTimeline(store, 'timelineItemIdsAreStale', false)
|
computeForTimeline(store, 'timelineItemIdsAreStale', false)
|
||||||
computeForTimeline(store, 'shouldRestoreFocus', false)
|
|
||||||
|
|
||||||
store.compute('firstTimelineItemId', ['timelineItemIds'], (timelineItemIds) => {
|
store.compute('firstTimelineItemId', ['timelineItemIds'], (timelineItemIds) => {
|
||||||
return timelineItemIds && timelineItemIds[0]
|
return timelineItemIds && timelineItemIds[0]
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
export function restoreFocus (element, selector) {
|
|
||||||
// Have to check from the parent because otherwise this element itself wouldn't match.
|
|
||||||
// This is fine for <article class=status> elements because they already have a div wrapper.
|
|
||||||
let elementToFocus = element.parentElement.querySelector(selector)
|
|
||||||
console.log('restoreFocus', selector, elementToFocus)
|
|
||||||
if (elementToFocus) {
|
|
||||||
elementToFocus.focus()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue