Запуск активации контрола

Прежде чем говорить об активации, необходимо ввести само понятие активности.

Определение понятия "физический родитель контрола"

Физический родитель контрола определятся использованием одного контрола в другом. Другими словами, "Контрол А" является физическим родителем для "Контрол B", если "Контрол B" вставляется в шаблон "Контрол А".

Шаблон "Контрол А".

<!-- WML -->
<div>
    <div>
        Control A
    </div>
    <ControlLibs.ControlB />
</div>

Шаблон "Контрол B".

<!-- WML -->
<div>
    Control B
</div>

Пример 1.

В данном примере рассматривается использование контрола внутри другого, а не объявление.

Шаблон контрола Example.

<!-- WML -->
<Controls.buttons:Button name="Add"/>

Физическим родителем контрола Add будет контрол Example.

Пример 2.

В данном примере рассматривается объявление в одном контроле, а использование в другом.

Шаблон контрола Example.

<!-- WML -->
<Controls.scroll:Container name="scrollContainer">
    <ws:content>
        <Controls.list:View name="listView"/>
    </ws:content>
</Controls.scroll:Container>

Шаблон контрола Controls/scroll:Container

<!-- WML -->
...
    <ws:partial template="{{ _options.content }}"/>
...

В примере Controls.list:View объявляется в контроле Example, но используется в Controls.scroll:Container.

В таком случае:

  • Физическим родителем Controls/list:View будет Controls/scroll:Container (т.к. физический родитель определяется по использованию).
  • Физическим родителем Controls/scroll:Container будет Example.

Определение понятия "опенер контрола" (opener)

Контрол_1 является опенером по отношению к Контролу_2, если для Контрола_2 опция opener равняется Контролу_1.

Опция opener задает логическую связь между открываемым дочерним контролом и родительским контролом, который является причиной открытия дочернего. Без этой связи невозможно доподлинно определить интересующую нас логическую связь между областями.

Таким образом, опенеры корректируют структуру контролов так, что она перестает учитывать только DOM-дерево, но также начинает учитывать логическую связь между контролами.

Обычно логической связью соединяется попап и открывающий его контрол.

Определение понятия "активность" контрола

Контрол активен, если выполняется одно из условий:

  1. Один из элементов в его шаблоне — в фокусе.
  2. Он является физическим родителем контрола, который активен и у которого нет опенера.
  3. Он является опенером контрола, который активен.

Разберемся на примерах:

Пример 1. На схеме фокус установлен в шаблоне "Контрол D".

В таком случае активными будут считаться "Контрол А", "Контрол B1" и "Контрол D", т.к. выполняются 1 и 2 условия активности:

  • "Контрол D" активен т.к. в его шаблон установлен фокус. (условие 1)
  • "Контрол B1" активен т.к. является физическим родителем для "Контрол D". (условие 2)
  • "Контрол А" активен т.к. является физическим родителем для "Контрол B1". (условие 2)

Пример 2. На схеме "Контрол В" является опенером для "Контрол PopupA", фокус установлен в шаблоне "Контрол E".

В таком случае активными будут считаться "Контрол А", "Контрол B", "Контрол Popup" и "Контрол E", т.к. выполняются все условия активности:

  • "Контрол E" активен т.к. в его шаблон установлен фокус. (условие 1)
  • "Контрол Popup" активен т.к. является физическим родителем для "Контрол E". (условие 2)
  • "Контрол B" активен т.к. является опенером для "Контрол Popup". (условие 3)
  • "Контрол А" активен т.к. является физическим родителем для "Контрол B". (условие 2)

Пример 3. На схеме “Контрол В” является опенером для “Контрол PopupA”, фокус установлен в шаблоне “Контрол С”.

В таком случае активными будут считаться "Контрол А", "Контрол B" и "Контрол С", т.к. выполняются 1 и 2 условия активности:

  • "Контрол С" активен т.к. в его шаблон установлен фокус. (условие 1)
  • "Контрол B" активен т.к. является физическим родителем для "Контрол C". (условие 2)
  • "Контрол А" активен т.к. является физическим родителем для "Контрол B". (условие 2)

Метод wasaby-контрола activate для запуска активации

Иногда возникает необходимость перевести фокус программно. Программный перевод фокуса затрудняется поиском элемента, который должен принять на себя фокус. Метод activate предоставляет высокоуровневый способ найти и сфокусировать элемент внутри выбранного контрола. Метод является частью API базового контрола UI/Base:Control.

Метод activate предполагает, что контрол уже был построен. Так что на этапе _beforeMount звать метод нельзя.

API метода activate

control.activate(config), где config — необязательный аргумент. Это объект, который может содержать параметры:

  • enableScreenKeyboard — в случае значения true на мобильных устройствах разрешается фокусировка полей ввода и соответствующее отображение экранной клавиатуры. В случае значения false на мобильных устройствах запрещается фокусировка полей ввода (фокусироваться будет контейнер). По умолчанию значение равно false.
  • enableScrollToElement — в случае значения true, если фокусируемый элемент находится за пределами видимой области, после фокусировки происходит подскролл к элементу, чтобы он стал видимым. В случае значения false подскролл к элементу отключен. По умолчанию значение равно false.

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

Если на элементе указать ws-autofocus="true" и tabindex="-1", то такой элемент не будет участвовать в активации.

В примере после _afterMount RootControl'a фокус будет установлен в ControlС.

<!-- WML -->
<div>
    <ControlA attr:ws-autofocus="true" attr:tabindex="-1">
        <ControlB attr:tabindex="1">
            <input type="text />
        </ControlB>
    </ControlA>
    <ControlС attr:tabindex="1">   
        <input type="text />
    </ControlС>
</div>
// TypeScript
...
_afterMount() {
   this._activate();
}
...

Если внутри контрола с ws-autofocus="true" нет фокусируемых элементов, то фокус устанавливается на контейнер.

В примере после _afterMount RootControl'a фокус будет установлен на контейнер ControlB.

<!-- WML -->
<div>
    <ControlA attr:tabindex="1" >
        <input type="text />
    </ControlA>
    <ControlB attr:ws-autofocus="true" attr:tabindex="2" >
        <ControlC attr:tabindex="-1">
            <input type="text />
        </ControlC>
        <ControlD attr:tabindex="-1">   
            <input type="text />
        </ControlD>
    </ControlB>
</div>
...
_afterMount() {
   this._activate();
}
...

Вместо ws-autofocus="true" можно так же задавать ws-autofocus="{{true}}". Оба способа поддерживаются.

Автофокусировка отобразившейся области

В рамках системы фокусов метод wasaby-контрола activate зовется только для запуска автофокусировки.

Автофокусировка запускается:

  • При загрузке страницы — вызов activate для корневого контрола страницы.
  • При переходе на страницу в SPA-режиме — вызов activate для корневого контрола страницы.
  • При открытии попапа — вызов activate для попапа.

Алгоритм активации

  1. Вызов activate() у контрола.
  2. Рекурсивно ищем директиву ws-autofocus в шаблоне контрола.
  3. Если нашли — вызываем activate() у найденного контрола.
  4. Если не нашли — ищем элемент с учетом контекстной зависимости tabindex.
    • Нашли элемент — фокусируем (в таком случае считаем активацию успешной).
  5. Не нашли — пытаемся фокусировать корневой элемент области, для который вызвали activate().
    • Корневой элемент можно сфокусировать — фокусируем (в таком случае считаем активацию успешной).
    • Корневой элемент нельзя сфокусировать — фокус остается на месте (в таком случае считаем активацию не успешной).

ws-autofocus следует использовать только для корректировки поиска элемента при автофокусировке. ws-autofocus ищется в шаблоне контрола и во всех дочерних контролах тоже. То есть достаточно указать ws-autofocus только контролу, в который должен уходить фокус при автофокусировке.

Шаблон контрола Document:

<!-- WML -->
<html>
   <head>...</head>
   <body>
      <MyControls.Container/>
   </body>
</html>

Шаблон контрола MyControls.Container:

<!-- WML -->
<div>
   <MyControls.Accordeon/>
   <MyControls.Header/>
   <MyControls.Browser/>
   <MyControls.Footer/>
</div>

Шаблон контрола MyControls.Browser:

<!-- WML -->
<div>
   <MyControls.Button on:click="createElement()"/>
   <MyControls.Search attr:ws-autofocus="true"/>
   <MyControls.List/>
</div>

Таким образом MyControls.Search будет фокусироваться при загрузке страницы.