Skip to content

Commit

Permalink
add key binding to copy code
Browse files Browse the repository at this point in the history
  • Loading branch information
Sneezry committed Jun 25, 2021
1 parent 3b3a86b commit 1a36d3f
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 28 deletions.
11 changes: 10 additions & 1 deletion sass/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ body {
font-size: 16px;
cursor: default;
user-select: none;

:focus {
outline: auto;
}
}

.hideoutline * {
outline: none !important;
}

svg {
Expand Down Expand Up @@ -282,6 +290,8 @@ svg {
}
border-radius: 2px;
position: relative;
display: block;
cursor: pointer;

.issuer {
font-size: 12px;
Expand All @@ -301,7 +311,6 @@ svg {
width: 80%;
user-select: text;
font-family: "Droid Sans Mono";
cursor: pointer;
}

.sector,
Expand Down
10 changes: 9 additions & 1 deletion src/components/Popup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
'theme-normal': theme !== 'accessibility' && theme !== 'dark',
'theme-accessibility': theme === 'accessibility',
'theme-dark': theme === 'dark',
hideoutline,
}"
v-on:mousedown="hideoutline = true"
v-on:keydown="hideoutline = false"
>
<MainHeader />
<MainBody
Expand Down Expand Up @@ -51,7 +54,7 @@
></div>

<!-- CLIPBOARD -->
<input type="text" id="codeClipboard" />
<input type="text" id="codeClipboard" tabindex="-1" />
</div>
</template>
<script lang="ts">
Expand All @@ -78,6 +81,11 @@ for (const module of computedPrototype) {
}
export default Vue.extend({
data: function () {
return {
hideoutline: true,
};
},
computed,
methods: {
hideQr() {
Expand Down
22 changes: 16 additions & 6 deletions src/components/Popup/EntryComponent.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
<template>
<div>
<a
role="button"
data-x-role="entry"
v-bind:tabindex="tabindex"
v-bind:class="{
entry: true,
pinnedEntry: entry.pinned,
'no-copy': noCopy(entry.code),
}"
v-on:click="copyCode(entry)"
v-on:keydown.enter="copyCode(entry)"
>
<div class="deleteAction" v-on:click="removeEntry(entry)">
<IconMinusCircle />
</div>
Expand Down Expand Up @@ -40,10 +51,8 @@
v-bind:class="{
code: true,
hotp: entry.type === OTPType.hotp || entry.type === OTPType.hhex,
'no-copy': noCopy(entry.code),
timeout: entry.period - (second % entry.period) < 5,
}"
v-on:click="copyCode(entry)"
v-html="style.isEditing ? showBulls(entry) : showCode(entry.code)"
></div>
<div class="issuer">{{ entry.account }}</div>
Expand All @@ -58,17 +67,17 @@
<div
class="showqr"
v-if="shouldShowQrIcon(entry)"
v-on:click="showQr(entry)"
v-on:click.stop="showQr(entry)"
>
<IconQr />
</div>
<div class="pin" v-on:click="pin(entry)">
<div class="pin" v-on:click.stop="pin(entry)">
<IconPin />
</div>
<div class="movehandle">
<IconBars />
</div>
</div>
</a>
</template>
<script lang="ts">
import Vue from "vue";
Expand Down Expand Up @@ -104,6 +113,7 @@ export default Vue.extend({
computed,
props: {
entry: OTPEntry,
tabindex: Number,
},
methods: {
noCopy(code: string) {
Expand Down
111 changes: 96 additions & 15 deletions src/components/Popup/MainBody.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@
</div>
</div>
<!-- Entries -->
<div v-dragula drake="entryDrake">
<div
v-dragula
drake="entryDrake"
v-on:keydown.down="focusNextEntry(entries, shouldFilter, filter)"
v-on:keydown.right="focusNextEntry(entries, shouldFilter, filter)"
v-on:keydown.up="focusLastEntry(entries, shouldFilter, filter)"
v-on:keydown.left="focusLastEntry(entries, shouldFilter, filter)"
>
<EntryComponent
class="entry pinnedEntry"
v-for="entry in pinnedEntries"
v-for="entry in entries"
:key="entry.hash"
v-bind:filtered="!entry.pinned && !isMatchedEntry(entry)"
v-bind:notSearched="!isSearchedEntry(entry)"
v-bind:entry="entry"
/>
<EntryComponent
class="entry"
v-for="entry in unpinnedEntries"
:key="entry.hash"
v-bind:filtered="!isMatchedEntry(entry)"
v-bind:notSearched="!isSearchedEntry(entry)"
v-bind:entry="entry"
v-bind:tabindex="getTabindex(entry, entries, shouldFilter, filter)"
/>
</div>
</div>
Expand All @@ -53,10 +53,7 @@ import IconPlus from "../../../svg/plus.svg";
let computed = mapState("accounts", ["entries", "filter", "showSearch"]);
Object.assign(
computed,
mapGetters("accounts", ["shouldFilter", "pinnedEntries", "unpinnedEntries"])
);
Object.assign(computed, mapGetters("accounts", ["shouldFilter", "entries"]));
export default Vue.extend({
data: function () {
Expand All @@ -65,6 +62,9 @@ export default Vue.extend({
};
},
computed,
mounted: function () {
document.querySelector<HTMLLinkElement>(".entry[tabindex='0']")?.focus();
},
methods: {
isMatchedEntry(entry: OTPEntry) {
for (const hash of this.$store.getters["accounts/matchedEntries"]) {
Expand All @@ -90,6 +90,87 @@ export default Vue.extend({
clearFilter() {
this.$store.dispatch("accounts/clearFilter");
},
isEntryVisible(entry: OTPEntry, shouldFilter: boolean, filter: boolean) {
return (
this.isSearchedEntry(entry) &&
(entry.pinned || !shouldFilter || !filter || this.isMatchedEntry(entry))
);
},
getTabindex(
entry: OTPEntry,
entries: OTPEntry[],
shouldFilter: boolean,
filter: boolean
) {
const firstEntry = entries.find((entry) =>
this.isEntryVisible(entry, shouldFilter, filter)
);
return entry === firstEntry ? 0 : -1;
},
findNextEntryIndex(
entries: OTPEntry[],
shouldFilter: boolean,
filter: boolean,
reverse: boolean
) {
if (document.activeElement?.getAttribute("data-x-role") !== "entry") {
return -1;
}
const activeIndex = Array.prototype.indexOf.call(
document.querySelectorAll(".entry"),
document.activeElement
);
if (activeIndex === -1) {
return -1;
}
// reverse modify origin array, and use slice() to make a clone first
const _entries = reverse ? entries.slice().reverse() : entries;
let nextIndex = _entries.findIndex(
(entry, index) =>
index > (reverse ? entries.length - 1 - activeIndex : activeIndex) &&
this.isEntryVisible(entry, shouldFilter, filter)
);
if (nextIndex === -1) {
nextIndex = _entries.findIndex((entry) =>
this.isEntryVisible(entry, shouldFilter, filter)
);
}
return nextIndex;
},
focusNextEntry(
entries: OTPEntry[],
shouldFilter: boolean,
filter: boolean
) {
const nextIndex = this.findNextEntryIndex(
entries,
shouldFilter,
filter,
false
);
document
.querySelector<HTMLLinkElement>(`.entry:nth-child(${nextIndex + 1})`)
?.focus();
},
focusLastEntry(
entries: OTPEntry[],
shouldFilter: boolean,
filter: boolean
) {
const lastIndex =
entries.length -
1 -
this.findNextEntryIndex(entries, shouldFilter, filter, true);
document
.querySelector<HTMLLinkElement>(`.entry:nth-child(${lastIndex + 1})`)
?.focus();
},
},
created() {
// Don't drag if !isEditing
Expand Down
11 changes: 6 additions & 5 deletions src/store/Accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ export class Accounts implements Module {
}
return false;
},
pinnedEntries(state: AccountsState) {
return state.entries.filter((entry) => entry.pinned);
},
unpinnedEntries(state: AccountsState) {
return state.entries.filter((entry) => !entry.pinned);
entries(state: AccountsState) {
const pinnedEntries = state.entries.filter((entry) => entry.pinned);
const unpinnedEntries = state.entries.filter(
(entry) => !entry.pinned
);
return [...pinnedEntries, ...unpinnedEntries];
},
},
mutations: {
Expand Down

0 comments on commit 1a36d3f

Please sign in to comment.