Проблемы с фокусом / курсором в интерфейсе Office UI Fabric TextField - PullRequest
0 голосов
/ 11 января 2019

У меня есть CodePen, который иллюстрирует проблему здесь: https://codepen.io/elegault/pen/QzZwLO

Сценарий : компонент DetailsList и поле поиска (компонент TextField). Элементы списка могут быть отфильтрованы по типу пользователя в поле поиска. Любой выбранный проект все равно будет выбран в результатах поиска, ЕСЛИ он есть в результатах поиска. Если его нет в результатах поиска и последующий поиск включает этот выбор, он будет переизбран. (ПРИМЕЧАНИЕ: команда Office UI Fabric, похоже, знает, что это должно быть обработано изначально, но я не уверен в планах добавить эту функциональность в соответствии с этой проблемой GitHub ).

Проблема : после каждого нажатия клавиши фокус теряется, что затрудняет ввод и редактирование критериев поиска, поскольку пользователю приходится каждый раз повторно вставлять курсор.

Что не работает : вызов focus () для TextField, когда он уже сфокусирован (isFocused = true), ничего не делает. Вызов focus () работает только тогда, когда isFocused = false. Но это верно только тогда, когда DetailsList.focusIndex () вызывается после восстановления выбора в отфильтрованном списке.

Псевдо-код:

componentDidUpdate(previousProps: any, previousState: AppProjectListState) {
  //Toggle off the current selection
  this._selection.toggleIndexSelected(currentIdx);
  //Set the new selection
  this._selection.toggleIndexSelected(newIdx);
  //Scroll the selection into view
  this._detailsListRef.current.focusIndex(newIdx, false);
}

Это какая-то ошибка в компоненте TextField или DetailsList? Или как я это делаю в жизненном цикле компонента React? Или есть способ гарантировать, что фокус не будет потерян из TextField, пока пользователь печатает, а элементы списка пересчитываются, а выбранный индекс изменяется?

1 Ответ

0 голосов
/ 17 января 2019

Недавно я наткнулся на аналогичный запрос функции и предложил следующее решение, которое позволяет сохранить выбор в DetailsList во время фильтрации данных.

Сначала вводится отдельный компонент, который реализует логику для сохранения выбора:

export interface IViewSelection {}

export interface IViewSelectionProps
  extends React.HTMLAttributes<HTMLDivElement> {
  componentRef?: IRefObject<IViewSelection>;

  /**
   * The selection object to interact with when updating selection changes.
   */
  selection: ISelection;

  items: any[];
}

export interface IViewSelectionState {}

export class ViewSelection extends BaseComponent<
  IViewSelectionProps,
  IViewSelectionState
> {
  private items: any[];
  private selectedIndices: any[];
  constructor(props: IViewSelectionProps) {
    super(props);
    this.state = {};
    this.items = this.props.items;
    this.selectedIndices = [];
  }

  public render() {
    const { children } = this.props;
    return <div>{children}</div>;
  }

  public componentWillUpdate(
    nextProps: IViewSelectionProps,
    nextState: IViewSelectionState
  ) {
    this.saveSelection();
  }

  public componentDidUpdate(
    prevProps: IViewSelectionProps,
    prevState: IViewSelectionState
  ) {
    this.restoreSelection();
  }

  private toListIndex(index: number) {
    const viewItems = this.props.selection.getItems();
    const viewItem = viewItems[index];
    return this.items.findIndex(listItem => listItem === viewItem);
  }

  private toViewIndex(index: number) {
    const listItem = this.items[index];
    const viewIndex = this.props.selection
      .getItems()
      .findIndex(viewItem => viewItem === listItem);
    return viewIndex;
  }

  private saveSelection(): void {
    const newIndices = this.props.selection
      .getSelectedIndices()
      .map(index => this.toListIndex(index))
      .filter(index => this.selectedIndices.indexOf(index) === -1);

    const unselectedIndices = this.props.selection
      .getItems()
      .map((item, index) => index)
      .filter(index => this.props.selection.isIndexSelected(index) === false)
      .map(index => this.toListIndex(index));

    this.selectedIndices = this.selectedIndices.filter(
      index => unselectedIndices.indexOf(index) === -1
    );
    this.selectedIndices = [...this.selectedIndices, ...newIndices];
  }

  private restoreSelection(): void {
    const indices = this.selectedIndices
      .map(index => this.toViewIndex(index))
      .filter(index => index !== -1);
    for (const index of indices) {
      this.props.selection.setIndexSelected(index, true, false);
    }
  }
}

Теперь компонент DetailsList необходимо обернуть компонентом ViewSelection , чтобы сохранить и восстановить выделение при применении фильтрации :

const items = generateItems(20);

export default class DetailsListBasicExample extends React.Component<
  {},
  {
    viewItems: any[];
  }
> {
  private selection: Selection;
  private detailsList = React.createRef<IDetailsList>();

  constructor(props: {}) {
    super(props);

    this.selection = new Selection({
    });
    this.state = {
      viewItems: items
    };
    this.handleChange = this.handleChange.bind(this);
  }

  public render(): JSX.Element {
    return (
      <div>
        <TextField label="Filter by name:" onChange={this.handleChange} />
        <ViewSelection selection={this.selection} items={this.state.viewItems} >
          <DetailsList
            componentRef={this.detailsList}
            items={this.state.viewItems}
            columns={columns}
            setKey="set"
            layoutMode={DetailsListLayoutMode.fixedColumns}
            selection={this.selection}
            selectionMode={SelectionMode.multiple}
            selectionPreservedOnEmptyClick={true}
          />
        </ViewSelection>
      </div>
    );
  }

  private handleChange = (
    ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
    text: string
  ): void => {
    const viewItems = text
      ? items.filter(item => item.name.toLowerCase().indexOf(text.toLocaleLowerCase()) > -1)
      : items;
    this.setState({ viewItems });
  };
}

Вот демо

...