| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import { Directive, ElementRef, HostListener } from '@angular/core';
-
- @Directive({
- selector: 'input[matInput]:not([type="submit"]):not([type="button"]), textarea[matInput]',
- standalone: true
- })
- export class EnterNavigationDirective {
- private isComposing = false;
-
- constructor(private elementRef: ElementRef<HTMLElement>) {}
-
- @HostListener('keydown.enter', ['$event'])
- onEnter(event: KeyboardEvent): void {
- // 跳过IME组合输入状态下的回车(让IME完成输入确认)
- if (event.isComposing || this.isComposing) {
- return;
- }
-
- // 阻止默认的提交行为,保持现有表单提交逻辑
- event.preventDefault();
-
- // 如果是textarea且按着Shift键,允许换行
- if (this.isTextArea() && event.shiftKey) {
- return;
- }
-
- // 查找下一个可聚焦元素
- const nextElement = this.findNextFocusable();
-
- if (nextElement) {
- nextElement.focus();
-
- // 对于select元素,阻止事件进一步传播
- if (nextElement.tagName === 'SELECT') {
- event.stopPropagation();
- }
- } else {
- // 没有下一个可聚焦元素,触发默认行为(提交表单)
- this.triggerFormSubmit(event);
- }
- }
-
- @HostListener('focus')
- onFocus(): void {
- const element = this.elementRef.nativeElement;
-
- // 只处理input元素(排除textarea、select等)
- if (element.tagName !== 'INPUT') return;
-
- const inputElement = element as HTMLInputElement;
-
- // 排除只读字段和禁用字段
- if (inputElement.readOnly || inputElement.disabled) return;
-
- // 使用setTimeout确保在Angular变更检测后执行
- setTimeout(() => inputElement.select(), 0);
- }
-
- @HostListener('compositionstart')
- onCompositionStart(): void {
- this.isComposing = true;
- }
-
- @HostListener('compositionend')
- onCompositionEnd(): void {
- this.isComposing = false;
- }
-
- private isTextArea(): boolean {
- return this.elementRef.nativeElement.tagName === 'TEXTAREA';
- }
-
- private findNextFocusable(): HTMLElement | null {
- const currentElement = this.elementRef.nativeElement;
- const form = this.findParentForm(currentElement);
-
- if (!form) {
- return null;
- }
-
- const focusableElements = this.getFocusableElements(form);
- const currentIndex = focusableElements.indexOf(currentElement);
-
- // 如果当前元素不在列表中或已经是最后一个,返回null
- if (currentIndex === -1 || currentIndex >= focusableElements.length - 1) {
- return null;
- }
-
- return focusableElements[currentIndex + 1];
- }
-
- private findParentForm(element: HTMLElement): HTMLElement | null {
- let parent = element.parentElement;
-
- while (parent && parent.tagName !== 'FORM') {
- parent = parent.parentElement;
- }
-
- return parent;
- }
-
- private getFocusableElements(container: HTMLElement): HTMLElement[] {
- // 定义输入类可聚焦元素的CSS选择器(排除按钮)
- const inputSelectors = [
- 'input[matInput]:not([type="hidden"]):not([disabled]):not([readonly])',
- 'textarea[matInput]:not([disabled]):not([readonly])',
- 'select[matInput]:not([disabled])',
- 'input:not([matInput]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([disabled]):not([readonly])',
- 'textarea:not([matInput]):not([disabled]):not([readonly])',
- 'select:not([matInput]):not([disabled])',
- '[contenteditable="true"]:not([disabled])'
- ].join(', ');
-
- // 获取所有匹配元素
- const allElements = Array.from(container.querySelectorAll(inputSelectors)) as HTMLElement[];
-
- // 过滤掉隐藏的元素和不可交互的元素
- return allElements.filter(element => {
- const style = window.getComputedStyle(element);
- const isVisible = style.display !== 'none' &&
- style.visibility !== 'hidden' &&
- style.opacity !== '0' &&
- !element.hasAttribute('hidden');
-
- // 检查元素是否在视图中(粗略检查)
- const rect = element.getBoundingClientRect();
- const isInViewport = rect.width > 0 && rect.height > 0;
-
- return isVisible && isInViewport;
- });
- }
-
- private triggerFormSubmit(event: KeyboardEvent): void {
- const currentElement = this.elementRef.nativeElement;
- const form = this.findParentForm(currentElement);
-
- if (form) {
- // 查找表单中的提交按钮并点击
- const submitButton = form.querySelector('button[type="submit"], input[type="submit"]') as HTMLElement;
-
- if (submitButton) {
- submitButton.click();
- } else {
- // 如果没有找到提交按钮,尝试触发表单的submit事件
- const submitEvent = new Event('submit', { bubbles: true, cancelable: true });
- form.dispatchEvent(submitEvent);
- }
- }
- }
- }
|