yandex-ui/nanoislands

Разумное переопределение в наноостровах

Opened this issue · 12 comments

Пролог

Сейчас в наноостровах каждый контрол является в некотором смысле монолитным, так что пользовать им нужно через АПИ. Со временем, требования к контролам растет — растет АПИ.

Кажется, что в некоторых случаях, этого можно избежать, введя возможность переопределения в заранее размеченных, предусмотренных местах.

Задача

Есть кастомная проектная иконка, которую не хочется или не получается вынести в острова.
В таком случая, можно сделать такую иконку просто прокинув кастомный класс.

Есть кнопка, вдруг в этой кнопке появляется кастомная иконка.

Кажется, в этом месте было бы удобно ввести место для переопределение. Поясню на примере:

Сейчас шаблон для кнопки выглядит так:

match .button nb {
    <button>
        _nb-button-attributes('button')

        _nb-button-static()

        apply . nb-main-attrs

        <span class="_nb-button-content">
             if .icon {
                nb-icon({
                    'icon': .icon
                })
            }

            _nb-button-content()
        </span>
    </button>
}

Может выглядеть так:

match .button nb {
    <button>
        _nb-button-attributes('button')

        _nb-button-static()

        apply . nb-main-attrs

        <span class="_nb-button-content">
             apply .[.icon] nb-button-icon

            _nb-button-content()
        </span>
    </button>
}

match .button nb-button-icon {
    nb-icon({
        'icon': .icon
    })
}

тогда на проекте можно написать:

...
// где-то в шаблоне кнопка
nb-button({
    'icon': 'custom-icon'
})
...

// элегантно подхачиваемся к шаблону блока
match .button[.icon == 'custom-icon'] nb-button-icon {
  // здесь моя кастомная иконка
}

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

В открытое АПИ в таком случае должны быть включены названия мод шаблонов.

Так же, кажется, это может быть удобно, когда в самой библиотеке станет большей уровней переопределение, чем ноль — появится мобильная версия блоков.

Тогда будет:

|--- button
|--- | --- button.desktop.yate
|--- | --- button.mobile.yate

первый содержит шаблоны по умолчанию, а во втором файлике — переопределения с предикатами .isMobile, так что можно переопределить ту же иконку, например.

Таким же образом можно решить следующую задачу с кастомной иконкой.

Сейчас пишем так:

nb-icon({
  'icon': 'custom'
  'class': 'nb-icon_custom nb-icon_size_m'
})

В итоге получаем странный класс nb-s-custom-icon, которого нет нигде. Да и использование class таким образом — явный хак.

Можно было бы написать так:

...
nb-icon({
   'icon': 'custom'
})
...

match .icon[.icon == "custom"] nb-icon-classes {
  'nb-icon_custom nb-icon_size_m'
}

Мне нравится пример с иконкой и не нравится пример с классом. Мне кажется, переопределение нужно использовать в тех случаях, когда нельзя удобно сделать это через json-api. Т.е. мне кажется правильным использовать переопределение через yate-api только для элементов составного блока.

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

Я бы не хотел давать точки для переопределения.
Это потеря контроля над содержимым блоков.
Сейчас разработчики вынуждены делать все через АПИ.

Внимание вопрос, зачем тебе

// элегантно подхачиваемся к шаблону блока
match .button[.icon == 'custom-icon'] nb-button-icon {
  // здесь моя кастомная иконка
}

для чего конкретно, и есть ли то что ты хочешь в гайдах?

Другими словами я готов к таким вещим в сложных составных блоках типа шапки и домика, но никак не в кнопке.

kizu commented

Я в очередной раз напишу: гайды — не цель, а направляющие, в них не должно быть всё-всё-всё, они должны служить базой. Хорошие гайды не появляются на пустом месте, а растут вместе с дизайном и с сервисами, их использующими. Невозможно в гайдах вывести все возможные сценарии использования блоков и элементов. Поэтому и нужны различные точки переопределения.

Я не считаю, что фреймворк должен сковывать оковами и не давать ничего переопределять. Я не понимаю, зачем нужен такой жёсткий контроль.

Любые проблемы, которые у разработчиков могут появиться при обновлении фреймворка будут регламентированы семвером, и это будет проблема разработчиков.

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

@basvasilich есть конретный пример кастомных иконок в кнопках.

Представим, что у меня есть 100500 кнопок с кастомной иконкой. Сейчас я могу либо отказаться от возможность вставлять иконку в кнопку через соответствующее АПИ и писать везде через поле content

nb-button({
   'content': apply . my-custom-icon-with-title
})

match / my-custom-icon-with-title {
   nb-icon({
      'icon': 'my-custom-icon'
   })
   ' Нажать!'
}

или я мог бы по всему проекта спокойно писать:

nb-button({
   'icon': 'my-custom-icon'
   'content': 'Нажать!'
})

и один раз написать свое проектное доопределение:

match .button[.icon == 'my-custom-icon'] nb-button-icon {
   nb-icon({
      'icon': 'my-custom-icon'
   })
}

По-моему, очевидно, что последний путь — верный. Разве, нет?

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

Кажется, все согласились с тем, что переопределение допустимо только в составных блоках. Вопрос теперь только в том, является ли кнопка с иконкой составным блоком?