nanka iroiro
This commit is contained in:
		
							parent
							
								
									78c2535c3c
								
							
						
					
					
						commit
						2f6b0b142a
					
				
					 5 changed files with 213 additions and 232 deletions
				
			
		|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }"> | ||||
| 	<sequential-entrance class="rrevdjwt" :class="{ left: align === 'left' }" :delay="15" :direction="direction"> | ||||
| <x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap"> | ||||
| 	<sequential-entrance class="rrevdjwt" :class="{ left: align === 'left' }" :delay="15" :direction="direction" ref="items"> | ||||
| 		<template v-for="(item, i) in items.filter(item => item !== undefined)"> | ||||
| 			<div v-if="item === null" class="divider" :key="i"></div> | ||||
| 			<span v-else-if="item.type === 'label'" class="label item" :key="i"> | ||||
|  | @ -36,6 +36,7 @@ | |||
| import Vue from 'vue'; | ||||
| import { faCircle } from '@fortawesome/free-solid-svg-icons'; | ||||
| import XPopup from './popup.vue'; | ||||
| import { focusPrev, focusNext } from '../scripts/focus'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
|  | @ -69,12 +70,31 @@ export default Vue.extend({ | |||
| 			type: String, | ||||
| 			required: false | ||||
| 		}, | ||||
| 		viaKeyboard: { | ||||
| 			type: Boolean, | ||||
| 			required: false | ||||
| 		}, | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			faCircle | ||||
| 		}; | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'up|k|shift+tab': this.focusUp, | ||||
| 				'down|j|tab': this.focusDown, | ||||
| 			}; | ||||
| 		}, | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (this.viaKeyboard) { | ||||
| 			this.$nextTick(() => { | ||||
| 				focusNext(this.$refs.items.$slots.default[0].elm, true); | ||||
| 			}); | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		clicked(fn) { | ||||
| 			fn(); | ||||
|  | @ -82,6 +102,12 @@ export default Vue.extend({ | |||
| 		}, | ||||
| 		close() { | ||||
| 			this.$refs.popup.close(); | ||||
| 		}, | ||||
| 		focusUp() { | ||||
| 			focusPrev(document.activeElement); | ||||
| 		}, | ||||
| 		focusDown() { | ||||
| 			focusNext(document.activeElement); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  | @ -119,6 +145,10 @@ export default Vue.extend({ | |||
| 			background: var(--accentDarken); | ||||
| 		} | ||||
| 
 | ||||
| 		&:not(:active):focus { | ||||
| 			box-shadow: 0 0 0 2px var(--focus) inset; | ||||
| 		} | ||||
| 
 | ||||
| 		&.label { | ||||
| 			pointer-events: none; | ||||
| 			font-size: 0.7em; | ||||
|  |  | |||
|  | @ -1,200 +0,0 @@ | |||
| <template> | ||||
| <x-menu :source="source" :items="items" @closed="closed"/> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { faStar, faLink, faThumbtack, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'; | ||||
| import i18n from '../i18n'; | ||||
| import { url } from '../config'; | ||||
| import copyToClipboard from '../scripts/copy-to-clipboard'; | ||||
| import XMenu from './menu.vue'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n, | ||||
| 	components: { | ||||
| 		XMenu | ||||
| 	}, | ||||
| 	props: ['note', 'source'], | ||||
| 	data() { | ||||
| 		return { | ||||
| 			isFavorited: false, | ||||
| 			isWatching: false | ||||
| 		}; | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		items(): any[] { | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 				return [{ | ||||
| 					icon: faCopy, | ||||
| 					text: this.$t('copyContent'), | ||||
| 					action: this.copyContent | ||||
| 				}, { | ||||
| 					icon: faLink, | ||||
| 					text: this.$t('copyLink'), | ||||
| 					action: this.copyLink | ||||
| 				}, this.note.uri ? { | ||||
| 					icon: faExternalLinkSquareAlt, | ||||
| 					text: this.$t('showOnRemote'), | ||||
| 					action: () => { | ||||
| 						window.open(this.note.uri, '_blank'); | ||||
| 					} | ||||
| 				} : undefined, | ||||
| 				null, | ||||
| 				this.isFavorited ? { | ||||
| 					icon: faStar, | ||||
| 					text: this.$t('unfavorite'), | ||||
| 					action: () => this.toggleFavorite(false) | ||||
| 				} : { | ||||
| 					icon: faStar, | ||||
| 					text: this.$t('favorite'), | ||||
| 					action: () => this.toggleFavorite(true) | ||||
| 				}, | ||||
| 				this.note.userId != this.$store.state.i.id ? this.isWatching ? { | ||||
| 					icon: faEyeSlash, | ||||
| 					text: this.$t('unwatch'), | ||||
| 					action: () => this.toggleWatch(false) | ||||
| 				} : { | ||||
| 					icon: faEye, | ||||
| 					text: this.$t('watch'), | ||||
| 					action: () => this.toggleWatch(true) | ||||
| 				} : undefined, | ||||
| 				this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? { | ||||
| 					icon: faThumbtack, | ||||
| 					text: this.$t('unpin'), | ||||
| 					action: () => this.togglePin(false) | ||||
| 				} : { | ||||
| 					icon: faThumbtack, | ||||
| 					text: this.$t('pin'), | ||||
| 					action: () => this.togglePin(true) | ||||
| 				} : undefined, | ||||
| 				...(this.note.userId == this.$store.state.i.id ? [ | ||||
| 					null, | ||||
| 					{ | ||||
| 						icon: faTrashAlt, | ||||
| 						text: this.$t('delete'), | ||||
| 						action: this.del | ||||
| 					}] | ||||
| 					: [] | ||||
| 				)] | ||||
| 				.filter(x => x !== undefined); | ||||
| 			} else { | ||||
| 				return [{ | ||||
| 					icon: faCopy, | ||||
| 					text: this.$t('copyContent'), | ||||
| 					action: this.copyContent | ||||
| 				}, { | ||||
| 					icon: faLink, | ||||
| 					text: this.$t('copyLink'), | ||||
| 					action: this.copyLink | ||||
| 				}, this.note.uri ? { | ||||
| 					icon: faExternalLinkSquareAlt, | ||||
| 					text: this.$t('showOnRemote'), | ||||
| 					action: () => { | ||||
| 						window.open(this.note.uri, '_blank'); | ||||
| 					} | ||||
| 				} : undefined] | ||||
| 				.filter(x => x !== undefined); | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.$root.api('notes/state', { | ||||
| 			noteId: this.note.id | ||||
| 		}).then(state => { | ||||
| 			this.isFavorited = state.isFavorited; | ||||
| 			this.isWatching = state.isWatching; | ||||
| 		}); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		copyContent() { | ||||
| 			copyToClipboard(this.note.text); | ||||
| 			this.$root.dialog({ | ||||
| 				type: 'success', | ||||
| 				iconOnly: true, autoClose: true | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		copyLink() { | ||||
| 			copyToClipboard(`${url}/notes/${this.note.id}`); | ||||
| 			this.$root.dialog({ | ||||
| 				type: 'success', | ||||
| 				iconOnly: true, autoClose: true | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		togglePin(pin: boolean) { | ||||
| 			this.$root.api(pin ? 'i/pin' : 'i/unpin', { | ||||
| 				noteId: this.note.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 				this.$emit('closed'); | ||||
| 				this.destroyDom(); | ||||
| 			}).catch(e => { | ||||
| 				if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { | ||||
| 					this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: this.$t('pinLimitExceeded') | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		del() { | ||||
| 			this.$root.dialog({ | ||||
| 				type: 'warning', | ||||
| 				text: this.$t('noteDeleteConfirm'), | ||||
| 				showCancelButton: true | ||||
| 			}).then(({ canceled }) => { | ||||
| 				if (canceled) return; | ||||
| 
 | ||||
| 				this.$root.api('notes/delete', { | ||||
| 					noteId: this.note.id | ||||
| 				}).then(() => { | ||||
| 					this.$emit('closed'); | ||||
| 					this.destroyDom(); | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		toggleFavorite(favorite: boolean) { | ||||
| 			this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { | ||||
| 				noteId: this.note.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 				this.$emit('closed'); | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		toggleWatch(watch: boolean) { | ||||
| 			this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', { | ||||
| 				noteId: this.note.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 				this.$emit('closed'); | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		closed() { | ||||
| 			this.$emit('closed'); | ||||
| 			this.$nextTick(() => { | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | @ -83,7 +83,8 @@ | |||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faTrashAlt, faQuoteRight } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'; | ||||
| import { parse } from '../../mfm/parse'; | ||||
| import { sum, unique } from '../../prelude/array'; | ||||
| import i18n from '../i18n'; | ||||
|  | @ -95,20 +96,11 @@ import XMediaList from './media-list.vue'; | |||
| import XCwButton from './cw-button.vue'; | ||||
| import XPoll from './poll.vue'; | ||||
| import XUrlPreview from './url-preview.vue'; | ||||
| import MkNoteMenu from './note-menu.vue'; | ||||
| import MkReactionPicker from './reaction-picker.vue'; | ||||
| import pleaseLogin from '../scripts/please-login'; | ||||
| 
 | ||||
| function focus(el, fn) { | ||||
| 	const target = fn(el); | ||||
| 	if (target) { | ||||
| 		if (target.hasAttribute('tabindex')) { | ||||
| 			target.focus(); | ||||
| 		} else { | ||||
| 			focus(target, fn); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| import { focusPrev, focusNext } from '../scripts/focus'; | ||||
| import { url } from '../config'; | ||||
| import copyToClipboard from '../scripts/copy-to-clipboard'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n, | ||||
|  | @ -148,7 +140,6 @@ export default Vue.extend({ | |||
| 			replies: [], | ||||
| 			showContent: false, | ||||
| 			hideThisNote: false, | ||||
| 			openingMenu: false, | ||||
| 			faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan | ||||
| 		}; | ||||
| 	}, | ||||
|  | @ -375,7 +366,7 @@ export default Vue.extend({ | |||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		renote() { | ||||
| 		renote(viaKeyboard = false) { | ||||
| 			pleaseLogin(this.$root); | ||||
| 			this.blur(); | ||||
| 			this.$root.menu({ | ||||
|  | @ -397,6 +388,7 @@ export default Vue.extend({ | |||
| 					} | ||||
| 				}] | ||||
| 				source: this.$refs.renoteButton, | ||||
| 				viaKeyboard | ||||
| 			}).then(this.focus); | ||||
| 		}, | ||||
| 
 | ||||
|  | @ -465,19 +457,113 @@ export default Vue.extend({ | |||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		menu(viaKeyboard = false) { | ||||
| 			if (this.openingMenu) return; | ||||
| 			this.openingMenu = true; | ||||
| 			const w = this.$root.new(MkNoteMenu, { | ||||
| 				source: this.$refs.menuButton, | ||||
| 				note: this.appearNote, | ||||
| 				animation: !viaKeyboard | ||||
| 			}).$once('closed', () => { | ||||
| 				this.openingMenu = false; | ||||
| 				this.focus(); | ||||
| 		toggleFavorite(favorite: boolean) { | ||||
| 			this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { | ||||
| 				noteId: this.appearNote.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		toggleWatch(watch: boolean) { | ||||
| 			this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', { | ||||
| 				noteId: this.appearNote.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		async menu(viaKeyboard = false) { | ||||
| 			let menu; | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 				const state = await this.$root.api('notes/state', { | ||||
| 					noteId: this.appearNote.id | ||||
| 				}); | ||||
| 				menu = [{ | ||||
| 					icon: faCopy, | ||||
| 					text: this.$t('copyContent'), | ||||
| 					action: this.copyContent | ||||
| 				}, { | ||||
| 					icon: faLink, | ||||
| 					text: this.$t('copyLink'), | ||||
| 					action: this.copyLink | ||||
| 				}, this.appearNote.uri ? { | ||||
| 					icon: faExternalLinkSquareAlt, | ||||
| 					text: this.$t('showOnRemote'), | ||||
| 					action: () => { | ||||
| 						window.open(this.appearNote.uri, '_blank'); | ||||
| 					} | ||||
| 				} : undefined, | ||||
| 				null, | ||||
| 				state.isFavorited ? { | ||||
| 					icon: faStar, | ||||
| 					text: this.$t('unfavorite'), | ||||
| 					action: () => this.toggleFavorite(false) | ||||
| 				} : { | ||||
| 					icon: faStar, | ||||
| 					text: this.$t('favorite'), | ||||
| 					action: () => this.toggleFavorite(true) | ||||
| 				}, | ||||
| 				this.appearNote.userId != this.$store.state.i.id ? state.isWatching ? { | ||||
| 					icon: faEyeSlash, | ||||
| 					text: this.$t('unwatch'), | ||||
| 					action: () => this.toggleWatch(false) | ||||
| 				} : { | ||||
| 					icon: faEye, | ||||
| 					text: this.$t('watch'), | ||||
| 					action: () => this.toggleWatch(true) | ||||
| 				} : undefined, | ||||
| 				this.appearNote.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.appearNote.id) ? { | ||||
| 					icon: faThumbtack, | ||||
| 					text: this.$t('unpin'), | ||||
| 					action: () => this.togglePin(false) | ||||
| 				} : { | ||||
| 					icon: faThumbtack, | ||||
| 					text: this.$t('pin'), | ||||
| 					action: () => this.togglePin(true) | ||||
| 				} : undefined, | ||||
| 				...(this.appearNote.userId == this.$store.state.i.id ? [ | ||||
| 					null, | ||||
| 					{ | ||||
| 						icon: faTrashAlt, | ||||
| 						text: this.$t('delete'), | ||||
| 						action: this.del | ||||
| 					}] | ||||
| 					: [] | ||||
| 				)] | ||||
| 				.filter(x => x !== undefined); | ||||
| 			} else { | ||||
| 				menu = [{ | ||||
| 					icon: faCopy, | ||||
| 					text: this.$t('copyContent'), | ||||
| 					action: this.copyContent | ||||
| 				}, { | ||||
| 					icon: faLink, | ||||
| 					text: this.$t('copyLink'), | ||||
| 					action: this.copyLink | ||||
| 				}, this.appearNote.uri ? { | ||||
| 					icon: faExternalLinkSquareAlt, | ||||
| 					text: this.$t('showOnRemote'), | ||||
| 					action: () => { | ||||
| 						window.open(this.appearNote.uri, '_blank'); | ||||
| 					} | ||||
| 				} : undefined] | ||||
| 				.filter(x => x !== undefined); | ||||
| 			} | ||||
| 
 | ||||
| 			this.$root.menu({ | ||||
| 				items: menu, | ||||
| 				source: this.$refs.menuButton, | ||||
| 				viaKeyboard | ||||
| 			}).then(this.focus); | ||||
| 		}, | ||||
| 
 | ||||
| 		showRenoteMenu(ev) { | ||||
| 			if (!this.$store.getters.isSignedIn || (this.$store.state.i.id !== this.note.userId)) return; | ||||
| 			this.$root.menu({ | ||||
|  | @ -492,6 +578,7 @@ export default Vue.extend({ | |||
| 					} | ||||
| 				}], | ||||
| 				source: ev.currentTarget || ev.target, | ||||
| 				viaKeyboard: viaKeyboard | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
|  | @ -499,6 +586,40 @@ export default Vue.extend({ | |||
| 			this.showContent = !this.showContent; | ||||
| 		}, | ||||
| 
 | ||||
| 		copyContent() { | ||||
| 			copyToClipboard(this.appearNote.text); | ||||
| 			this.$root.dialog({ | ||||
| 				type: 'success', | ||||
| 				iconOnly: true, autoClose: true | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		copyLink() { | ||||
| 			copyToClipboard(`${url}/notes/${this.appearNote.id}`); | ||||
| 			this.$root.dialog({ | ||||
| 				type: 'success', | ||||
| 				iconOnly: true, autoClose: true | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		togglePin(pin: boolean) { | ||||
| 			this.$root.api(pin ? 'i/pin' : 'i/unpin', { | ||||
| 				noteId: this.appearNote.id | ||||
| 			}).then(() => { | ||||
| 				this.$root.dialog({ | ||||
| 					type: 'success', | ||||
| 					iconOnly: true, autoClose: true | ||||
| 				}); | ||||
| 			}).catch(e => { | ||||
| 				if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { | ||||
| 					this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: this.$t('pinLimitExceeded') | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		focus() { | ||||
| 			this.$el.focus(); | ||||
| 		}, | ||||
|  | @ -508,11 +629,11 @@ export default Vue.extend({ | |||
| 		}, | ||||
| 
 | ||||
| 		focusBefore() { | ||||
| 			focus(this.$el, e => e.previousElementSibling); | ||||
| 			focusPrev(this.$el); | ||||
| 		}, | ||||
| 
 | ||||
| 		focusAfter() { | ||||
| 			focus(this.$el, e => e.nextElementSibling); | ||||
| 			focusNext(this.$el); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <div class="mk-popup"> | ||||
| <div class="mk-popup" v-hotkey.global="keymap"> | ||||
| 	<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear> | ||||
| 		<div class="bg" ref="bg" @click="close()" v-if="show"></div> | ||||
| 	</transition> | ||||
|  | @ -35,6 +35,13 @@ export default Vue.extend({ | |||
| 			show: true, | ||||
| 		}; | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'esc': this.close, | ||||
| 			}; | ||||
| 		}, | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.$nextTick(() => { | ||||
| 			const popover = this.$refs.content as any; | ||||
|  | @ -96,8 +103,8 @@ export default Vue.extend({ | |||
| 	methods: { | ||||
| 		close() { | ||||
| 			this.show = false; | ||||
| 			(this.$refs.bg as any).style.pointerEvents = 'none'; | ||||
| 			(this.$refs.content as any).style.pointerEvents = 'none'; | ||||
| 			if (this.$refs.bg) (this.$refs.bg as any).style.pointerEvents = 'none'; | ||||
| 			if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none'; | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										23
									
								
								src/client/scripts/focus.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/client/scripts/focus.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| export function focusPrev(el: Element | null, self = false) { | ||||
| 	if (el == null) return; | ||||
| 	if (!self) el = el.previousElementSibling; | ||||
| 	if (el) { | ||||
| 		if (el.hasAttribute('tabindex')) { | ||||
| 			(el as HTMLElement).focus(); | ||||
| 		} else { | ||||
| 			focusPrev(el.previousElementSibling, true); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export function focusNext(el: Element | null, self = false) { | ||||
| 	if (el == null) return; | ||||
| 	if (!self) el = el.nextElementSibling; | ||||
| 	if (el) { | ||||
| 		if (el.hasAttribute('tabindex')) { | ||||
| 			(el as HTMLElement).focus(); | ||||
| 		} else { | ||||
| 			focusPrev(el.nextElementSibling, true); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue