mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 15:51:16 +08:00
Issue number: Internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> As part of FW-2832, the team would like to swap out usages of the any type for stronger types. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. -->c529bc23f1- `scrollToTop` doesn't return anything, so I added the `void` return typea96971ad28- `animation.effect` is a type of [AnimationEffect](https://developer.mozilla.org/en-US/docs/Web/API/Animation/effect). One of the more common types of effects is a `KeyframeEffect`. However, TypeScript doesn't know which specific type of AnimationEffect we are using, so I cast `animation.effect` as KeyframeEffect where appropriate. - I also added `!` to places where we know the effect and other properties are always defined (since they run after the web animation has been constructed) - Added stronger types to the internal to/from/fromTo functions (the public facing type improvements are in https://github.com/ionic-team/ionic-framework/pull/28334)fdaf550059- `getRootNode` can return multiple types of objects, so I cast it to the specific types that we work with in `isFocused`.46a6efa510- Added the "Animation" type and resolved related errors once we had stronger typesa7cb9a5685- Made heavier use of the `T` generic - Once we know `node` is an Element (`nodeType === 1`) we manually cast the element as `T`6a9d1f095d- The focus visible utility is an internal utility, but it was lacking an interface, so I added one.90b64c2de5- Removed unneeded HTMLElement casting - Added `!` since we can assume the selected elements are defined with the refresher - Added documentation as to why casting `referencEl.style` as `any` is something we need to keep.3a084caf83- Avoided the Event naming collision by using globalThis ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Note: This PR contains only type changes. Changes the required updates to the implementation of Ionic are pulled out into separate PRs and target a minor release branch to minimize risk. --------- Co-authored-by: Amanda Johnston <90629384+amandaejohnston@users.noreply.github.com>
83 lines
2.1 KiB
TypeScript
83 lines
2.1 KiB
TypeScript
const ION_FOCUSED = 'ion-focused';
|
|
const ION_FOCUSABLE = 'ion-focusable';
|
|
const FOCUS_KEYS = [
|
|
'Tab',
|
|
'ArrowDown',
|
|
'Space',
|
|
'Escape',
|
|
' ',
|
|
'Shift',
|
|
'Enter',
|
|
'ArrowLeft',
|
|
'ArrowRight',
|
|
'ArrowUp',
|
|
'Home',
|
|
'End',
|
|
];
|
|
|
|
export interface FocusVisibleUtility {
|
|
destroy: () => void;
|
|
setFocus: (elements: Element[]) => void;
|
|
}
|
|
|
|
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
|
|
let currentFocus: Element[] = [];
|
|
let keyboardMode = true;
|
|
|
|
const ref = rootEl ? rootEl.shadowRoot! : document;
|
|
const root = rootEl ? rootEl : document.body;
|
|
|
|
const setFocus = (elements: Element[]) => {
|
|
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
|
|
elements.forEach((el) => el.classList.add(ION_FOCUSED));
|
|
currentFocus = elements;
|
|
};
|
|
const pointerDown = () => {
|
|
keyboardMode = false;
|
|
setFocus([]);
|
|
};
|
|
|
|
const onKeydown = (ev: Event) => {
|
|
keyboardMode = FOCUS_KEYS.includes((ev as KeyboardEvent).key);
|
|
if (!keyboardMode) {
|
|
setFocus([]);
|
|
}
|
|
};
|
|
const onFocusin = (ev: Event) => {
|
|
if (keyboardMode && ev.composedPath !== undefined) {
|
|
const toFocus = ev.composedPath().filter((el: any) => {
|
|
// TODO(FW-2832): type
|
|
if (el.classList) {
|
|
return el.classList.contains(ION_FOCUSABLE);
|
|
}
|
|
return false;
|
|
}) as Element[];
|
|
setFocus(toFocus);
|
|
}
|
|
};
|
|
const onFocusout = () => {
|
|
if (ref.activeElement === root) {
|
|
setFocus([]);
|
|
}
|
|
};
|
|
|
|
ref.addEventListener('keydown', onKeydown);
|
|
ref.addEventListener('focusin', onFocusin);
|
|
ref.addEventListener('focusout', onFocusout);
|
|
ref.addEventListener('touchstart', pointerDown, { passive: true });
|
|
ref.addEventListener('mousedown', pointerDown);
|
|
|
|
const destroy = () => {
|
|
ref.removeEventListener('keydown', onKeydown);
|
|
ref.removeEventListener('focusin', onFocusin);
|
|
ref.removeEventListener('focusout', onFocusout);
|
|
ref.removeEventListener('touchstart', pointerDown);
|
|
ref.removeEventListener('mousedown', pointerDown);
|
|
};
|
|
|
|
return {
|
|
destroy,
|
|
setFocus,
|
|
};
|
|
};
|