Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tab swiping #1656

Merged
merged 7 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ app.

## [Unreleased]

- Made it possible to change tabs in the popup by swiping horizontally
thanks to [@StarScape](https://github.com/StarScape)
([#1656](https://github.com/birchill/10ten-ja-reader/issues/1656)).
- Made copying to the clipboard include rare headwords again when not using
simplified copy mode
([#1665](https://github.com/birchill/10ten-ja-reader/issues/1665)).
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- [Installing](#installing)
- [Features](#features)
- [Usage](#usage)
- [Building from source](#building-from-source)
- [Building from source](#building-a-release-from-source)
- [Contributing](#contributing)
- [Contributors](#contributors)

Expand Down
26 changes: 22 additions & 4 deletions src/content/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2057,19 +2057,19 @@ export class ContentHandler {
}

showDictionary(
dictToShow: 'next' | MajorDataSeries,
dictToShow: 'next' | 'prev' | MajorDataSeries,
options: { fixPopupPosition?: boolean } = {}
) {
if (!this.currentSearchResult) {
return;
}

let dict: MajorDataSeries;
const cycleOrder: Array<MajorDataSeries> = ['words', 'kanji', 'names'];

if (dictToShow == 'next') {
if (dictToShow === 'next') {
dict = this.currentDict;

const cycleOrder: Array<MajorDataSeries> = ['words', 'kanji', 'names'];
let next = (cycleOrder.indexOf(this.currentDict) + 1) % cycleOrder.length;
while (cycleOrder[next] !== this.currentDict) {
const nextDict = cycleOrder[next];
Expand All @@ -2082,6 +2082,24 @@ export class ContentHandler {
}
next = ++next % cycleOrder.length;
}
} else if (dictToShow === 'prev') {
dict = this.currentDict;

let prev = mod(
cycleOrder.indexOf(this.currentDict) - 1,
cycleOrder.length
);
while (cycleOrder[prev] !== this.currentDict) {
const prevDict = cycleOrder[prev];
if (
this.currentSearchResult[prevDict] ||
(prevDict === 'words' && !!this.currentLookupParams?.meta)
) {
dict = prevDict;
break;
}
prev = mod(--prev, cycleOrder.length);
}
} else {
dict = dictToShow;
}
Expand Down Expand Up @@ -2207,7 +2225,7 @@ export class ContentHandler {
// Ignore
});
},
onSwitchDictionary: (dict: MajorDataSeries) => {
onSwitchDictionary: (dict: MajorDataSeries | 'next' | 'prev') => {
this.showDictionary(dict, { fixPopupPosition: true });
},
onTogglePin: () => {
Expand Down
10 changes: 7 additions & 3 deletions src/content/popup/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { renderKanjiEntries } from './kanji';
import { renderMetadata } from './metadata';
import { renderNamesEntries } from './names';
import { renderCopyDetails, renderUpdatingStatus } from './status';
import { onHorizontalSwipe } from './swipe';
import { renderTabBar } from './tabs';
import { renderWordEntries } from './words';

Expand Down Expand Up @@ -69,7 +70,7 @@ export interface PopupOptions {
onClosePopup?: () => void;
onExpandPopup?: () => void;
onShowSettings?: () => void;
onSwitchDictionary?: (newDict: MajorDataSeries) => void;
onSwitchDictionary?: (newDict: MajorDataSeries | 'next' | 'prev') => void;
onTogglePin?: () => void;
pinShortcuts?: ReadonlyArray<string>;
posDisplay: PartOfSpeechDisplay;
Expand All @@ -93,6 +94,7 @@ export function renderPopup(
fontSize: options.fontSize || 'normal',
popupStyle: options.popupStyle,
});
const contentContainer = html('div', { class: 'content' });

const hasResult = result && (result.words || result.kanji || result.names);
const showTabs =
Expand Down Expand Up @@ -123,9 +125,11 @@ export function renderPopup(
);

windowElem.dataset.tabSide = options.tabDisplay || 'top';
}

const contentContainer = html('div', { class: 'content' });
onHorizontalSwipe(contentContainer, (direction) => {
options.onSwitchDictionary?.(direction === 'left' ? 'prev' : 'next');
});
}

const resultToShow = result?.[options.dictToShow];

Expand Down
58 changes: 58 additions & 0 deletions src/content/popup/swipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
type HorizontalSwipeDirection = 'right' | 'left';

export function onHorizontalSwipe(
element: HTMLElement,
handler: (swipeDirection: HorizontalSwipeDirection) => void
) {
// Min x distance traveled to be considered swipe
const xMinThreshold = 50;
// Max y distance that can be traveled before
// it's no longer considered a horizontal swipe
const yMaxThreshold = 100;
// Max time allowed to travel that distance
const allowedTime = 200;

let startTime = 0;
let startX: number;
let startY: number;

element.addEventListener(
'touchstart',
function (e) {
startX = e.changedTouches[0].pageX;
startY = e.changedTouches[0].pageY;
startTime = performance.now();
},
false
);

element.addEventListener(
'touchmove',
function (e) {
// Prevent dragging viewport
e.preventDefault();
},
false
);

element.addEventListener(
'touchend',
function (e) {
const touch = e.changedTouches[0];
const deltaX = touch.pageX - startX;
const deltaY = touch.pageY - startY;
const elapsedTime = performance.now() - startTime;

// Check that elapsed time is within specified, horizontal dist
// traveled >= threshold, and vertical dist traveled <= 100
const isSwipe =
elapsedTime <= allowedTime &&
Math.abs(deltaX) >= xMinThreshold &&
Math.abs(deltaY) <= yMaxThreshold;
if (isSwipe) {
handler(deltaX < 0 ? 'right' : 'left');
}
},
false
);
}