mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-12 23:31:02 +00:00
d06168d94a
After a tap, WebKit and Chrome keep :hover on what was tapped and send a
made-up mouseover, so buttons and rows stayed tinted until the next tap.
While a finger drives the app <html> carries data-touch-input: a PostCSS
step guards every :hover rule with it (no specificity change), and the
made-up mouseover and mouseout stop before any handler. A trackpad or mouse
clears the mark and gets hover back; a browser never sets it.
A control without a press of its own dims the moment a finger lands on it,
after a 60 ms wait so a scroll does not flash rows, and only the innermost
pressed control. A focus: ring no longer stays round a tapped button.
Fields get a fitting return key ("done", "go" in a form, "search" on the
search fields), and addresses, codes and numbers are no longer capitalised
or corrected. Ticking off packing items and tasks, a picker chip, a select
option, a picked date and dragging a plan row now give the haptics a native
list gives.
Confirmations ask through the system where the binary has the new
@capacitor/action-sheet and @capacitor/dialog plugins: a sheet with the
action in red when it destroys something, an alert otherwise, also for
"discard changes" in a sheet. An older app, a browser and a question with a
list under it keep the HTML dialog, as does a system that could not ask.
iOS no longer lifts a page preview on a long press on a link.
50 lines
2.0 KiB
JavaScript
50 lines
2.0 KiB
JavaScript
/**
|
|
* Hover styles only while a pointer drives the UI.
|
|
*
|
|
* A finger has no hover, yet WebKit and Chrome apply :hover to whatever was
|
|
* tapped and keep it there until the next tap lands somewhere else. In the
|
|
* iOS and Android app that left buttons and rows tinted after every tap, a
|
|
* tell of a web page that no native app has. The app marks <html> with
|
|
* data-touch-input while a finger is in use (src/native/touchInput.ts), and
|
|
* this plugin makes every :hover rule, Tailwind's and our own, wait for that
|
|
* mark to be gone. A browser never sets it, so nothing changes there, and an
|
|
* iPad with a trackpad clears it on the first pointer move.
|
|
*
|
|
* The condition sits on the hovered element itself, wrapped in :where(), so a
|
|
* rule keeps its specificity and still wins or loses where it did before. A
|
|
* :hover after a pseudo-element (::-webkit-slider-thumb:hover) stays as it is:
|
|
* nothing may follow it there, and a rule with an invalid selector is dropped
|
|
* whole.
|
|
*/
|
|
|
|
export const TOUCH_INPUT_ATTR = 'data-touch-input';
|
|
const GUARD = `:where(:root:not([${TOUCH_INPUT_ATTR}]) *)`;
|
|
// :hover itself, not the escaped `hover\:` inside a Tailwind class name, and not :hover-something.
|
|
const HOVER = /(?<!\\):hover(?![\w-])/g;
|
|
const COMPOUND_START = /[\s>+~(,]/;
|
|
|
|
function compoundBefore(selector, index) {
|
|
let i = index - 1;
|
|
while (i >= 0 && !COMPOUND_START.test(selector[i])) i--;
|
|
return selector.slice(i + 1, index);
|
|
}
|
|
|
|
export function guardHover(selector) {
|
|
if (selector.includes(GUARD)) return selector;
|
|
return selector.replace(HOVER, (match, offset) =>
|
|
compoundBefore(selector, offset).includes('::') ? match : `${match}${GUARD}`
|
|
);
|
|
}
|
|
|
|
export default function touchHover() {
|
|
return {
|
|
postcssPlugin: 'trek-touch-hover',
|
|
Rule(rule) {
|
|
if (!rule.selector.includes(':hover')) return;
|
|
const next = rule.selectors.map(guardHover);
|
|
if (next.some((s, i) => s !== rule.selectors[i])) rule.selectors = next;
|
|
},
|
|
};
|
|
}
|
|
touchHover.postcss = true;
|