Feast — Экспериментальный шаблонизатор на основе xhtml-синтаксиса с конвертацией в vdom
npm install feastFeast
-----
Экспериментальный шаблонизатор на основе xhtml-синтаксиса с конвертацией в vdom (citojs).
* cd feast
* npm i --save-dev feast feast-dev — установка необходимых пакетов
* node ./node_modules/feast-dev/index.js --feast-demo — запуск сервера: http://localhost:2015/
* Подробнее о fesat-dev
- WebStorm
---
- ./node_modules/feast/bin/assist create-block --name={name} --path=path/to/feast/blocks/
``js
import {configure, Block} from 'feast';
import UIIcon from '../path/to/Icon/Icon';
@configure({
name: 'btn', // название блока
events: {tap: 'handleTap'}, // обработчики события
isolate: false, // разрешить наследование значений аттрибутов родительского блока
isolateEvents: false, // разрешить всплытие событий
blocks: {icon: UIIcon}, // используемые блоки
defaults: {text: 'Wow!'}, // свойства по умолчанию
template: ,
})
export default class extends Block {
handleTap(evt) {
// ...
}
}
// Читый ES2015+ (без декоратора)
export default configure({
// Опции
})(class extends Block {
// Методы
});
`
`js
import {configure, Block} from 'feast';
import UIIcon from '../path/to/Icon/Icon';
export interface ButtonProps {
text: string;
}
@configure({
name: 'btn', // название блока
events: {tap: 'handleTap'}, // обработчики события
})
export class extends Block
template({text}) {
return (
);
}
handleTap(evt) {
// ...
}
}
`
---
* fn:transition
* fn:if animated
* fn:if
* fn:choose: fn:when и fn:otherwise
* fn:for
* fn:value и короткий синтакиси {...}
* fn:attr
* fn:add-class
* fn:match
* fn:apply-match
---
* id
* ref
* on-event
* remit:event
* use:mixin
---
* События
* Список
* Расширение
---
* bem:mod — элемент и атрибут
* bem:elem — спец. атрибут
---
* feast-css
* feast-css: expose
---
##### button.html — шаблон
`html`
{attrs.text}
##### button.js — описание поведения блока
Полное описание методов feast.Block
`js
import feast from 'feast';
import template from 'feast-tpl!./button.html';
import styleSheet from 'feast-css!./button.css';
export default feast.Block.extend({
name: 'button', // уникальное название блока
template,
styleSheet,
defaults: {
text: null,
disabled: false
}
});
`
---
- test — любое javascript выражение
`html
Поздравляем!
---
if. -
test — любое javascript выражение
- animated или animated="slide"`html
Поздравляем!
Поздравляем!
Поздравляем!
`
#### fn:transition
Анимация вложенного контента:
-
name — название эффекта (optional)
- appear — добавить аниация на изначальный рендер (optional) - Изначальный рендер (appear)
1. Первый frame:
fx[-name]-appear-enter
2. Анимация: fx[-name]-appear-enter-active fx[-name]-appear-enter-to - Добавление элемента
1. Первый frame:
fx[-name]-enter
2. Анимация: fx[-name]-enter-active fx[-name]-enter-to - Удаление элемента
1. Первый frame:
fx[-name]-leave
2. Анимация: fx[-name]-leave-active fx[-name]-leave-to
`html
Wow!
Wow!
Wow!
`---
`html
Привет, {attrs.userName}!
Авторизуйтесь!
`---
#### fn:for
Перебор массива или объекта.
ВАЖНО: У всех рутовых элементов внутри
fn:for должен быть задан уникальный key-атрибут -
data — массив или объект (любое javascript выражение)
- as — название переменной очередного элемент массива (опционально)
- key — индекс или ключ (опционально)
- filter — функция фильтрации списка, на вход получает два элемента: as и key (опционально)
- cached — vdom-кеширование по ключу (опционально)`html
`---
#### fn:value
Вывести любое javascript выражение
-
output — режим вывода
- raw — «как есть»
- text — обычный текст (по умолчанию)`html
Привет attrs.username !
`или короткий синтаксис
`html
Привет {attrs.username}!
`Чтобы вывести смивол
{ и } используйте экранирование \{.
---
#### fn:attr
Заменить или установить атрибут родительского элементу
-
name — название
- value — значение
- test — любое javascript выражение (опционально)`html
...
`---
#### fn:add-class
Добавить css-класс родительскому элементу
-
name — название класса (через пробел)
- test — любое javascript выражение (опционально)`html
...
`---
#### fn:match и fn:apply-match
Определение и использование подшаблона.
#####
fn:match
- name — имя подшаблона
- args — названия аргументов через запятую, которые будут переданны в match от apply-match (опционально)
#####
fn:apply-match -
name — имя вызываемого подшаблона
- args — аргументы через запятую, которые нужно передать в match (опционально)
notify.html
`html
`Использование:
`html
Заголовок
Какое-то содержание
`---
feast.Block по уникальному id
`js
var App = feast.Block.extend({
name: 'app',
template: ' ',
didMount() {
const nav = this.ids.nav;
}
});
`---
HTMLElement по уникальному ref
`js
var Form = feast.Block.extend({
name: 'form',
template: '',
didMount() {
const sendEl = this.refs.send;
}
});
`---
`html
`---
#### remit:event
Преобразование DOM-события в пользовательское
`js
var Btn = feast.Block.extend({
name: 'btn',
template: '',
events: {
'tap': 'handleTap'
},
handleTap(evt) {
const details = evt.details; // если event:details не задан, то details будет ссылкой на this
}
});
`##### Правильный способ организации всплытия
remit-событий
`js
var SubscribeForm = feast.Block.extend({
name: 'subscribe-form',
template: feast.parse('')
});var App = feast.Block.extend({
name: 'app',
blocks: {'subscribe-form': SubscribeForm},
template: feast.parse('
'),
events: {
'subscribe:news': 'handleSubscribe'
},
handleSubscribe(evt) {
const type = evt.type.split(':').pop();
}
});
`
---
$3
Сигнатура
`js
function format(value, format) {
// применяем модицикацию
return newValue;
}
`Пример
`js
import dateFormat from './data-format';feast.Block.extend({
mods: {
trim: (val) => val.trim(),
ucfirst: (val) => val.charAt(0).toUpperCase() + val.substr(1),
'bem-prefix': (val, prefix) => prefix + val,
dateFormat
},
template:
});
`---
$3
Это инструмент, позволяющий на этапе подписки на событие применить к нему разные методы или фильтрацию,
например по названию клавиши или её коду:`html
` - Модификатор события
-
prevent — отменить действие по умолчанию
- stop — остановить всплытие
- Фильтрация по
- Названию клавиши
- enter
- esc
- tab
- left
- right
- up
- down
- spacebar
- shift
- ctrl
- alt
- XXXXX — произвольный keyCode
- Зажатой клавише
- alt-pressed
- ctrl-pressed
- shift-pressed
- meta-pressed
- Кнопке мыши
- left-btn
- right-btn#### Расширение
`js
// on-click.my-modifier="..."
feast.vdom.eventModifiers['my-modifier'] = function myModifier(evt) {
if (expression(evt)) {
// Отменяем исполнение слушателя события
return false;
}
};// Добавляем имя клавиши
feast.vdom.eventModifiers.KEYS.q = 'Q'.charCodeAt(0); // да-да, сначало в нижнем, потом верхнем регистре
`---
`js
feast.tags.$mixins['form-element'] = function formElementMixin(node, attrs) {
attrs.name = '{attrs.name}';
attrs.type = '{attrs.type || "text"}';
attrs.tabindex = '{attrs.index}';
attrs.placeholder = '{attrs.placeholder}';
attrs.required = '{attrs.required}';
// и так далее
};var Inp = feast.Block.extend({
name: 'inp',
template: ''
});
`---
*
name — название модификатора
* value — значение модикатора (опционально)
* test — условие добавления модификатора (любое javascript выражение, опционально)`html
`---
#### bem:elem
Спец. атрибут для BEM-именования элементов (только плоское именование, никаких элемент элемента).
`html
...
...
`---
$3
Для работы используйте feast/src/require-css.js`js
require.config({
// ...
map: {
'*': {
'feast-css': '/node_modules/feast/src/require-css.js',
// ...
},
},
});
`---
`js
import css from "feast-css!./style.css";console.log(css.className); // -jdy73jk
`
---
#### feast-css: expose
Обеспечивает доступ к классом из внешнего css
`css
/ expose {form, container as formContainer} from "../form/form.css" /.button { color: red; }
.form .button { color: green; }
.formContainer .container { margin: 10px; }
`
---
$3
`js
// Описание блока
import feast from 'feast';import template from 'feast-tpl!path/to/block-name/block-name.html';
import styleSheet from 'feast-css!path/to/block-name/block-name.css'; // только если нужна модульность для CSS (уникальные имена CSS-селекторов)
// Используемые блоки
import UIBtn from 'path/to/btn/btn';
export default feast.Block.extend({
name: 'block-name', // Имя блока, оно же используется как CSS-класс
template, // или
''
styleSheet, // опционально, только если нужна модульность, css: {'block-name': '{uniq_hash}'} // Изолировать аттрибуты
// true — по умолчанию
// false — наследовать аттрибуты родителя
isolate: false,
// Изолировать события испускаемые блоком рамками этого блока, по умолчанию true
isolateEvents: false,
// Список используемых блоков
blocks: {
btn: UIBtn
},
// Обработка изменения атрибутов
attrChanged: {
'attr-name': function (newValue, oldValue) {
// Любое действие
}
},
// Обработка событий
events: {
'click': 'handleClick' // название события => название метода
},
handleClick(/* Event /evt) {
// Обработка события click
},
didMount() {
// Блок добавлен в DOM
// Подписываемся на DOM событие (unmount элемента такие события будут сняты автоматически)
this.$on(document, 'click', 'handleOutsideClick');
},
didUnmount() {
// Блок извлечен из DOM
},
handleOutsideClick(/* Event /evt) {
// Проверяем, что клик сделан за пределами блока
if (this.el.contains(evt.target)) {
}
}
});
// Использование
import UIBlockName from 'path/to/block-name/block-name';
const block = new UIBlockName({
'attr-bool': false,
});
// Рендер блока
block.renderTo(document.body);
// Уничтожить блок
block.destroy(); // генерируем событие destroy, на которое можно подписаться через on
``
#### Методы
- get(attrName:string):* — получить значение атрибутаstring
- set(attrName:, value:*):void 0 — изменить значение атрибутаobject
- set(attributes:):void 0 — изменить значения атрибутовstring
- is(attrName:):boolean — проверить значение атрибута на истинностьstring
- invert(attrName:):boolean — инвертировать значение атрибутаstring
- on(name:, fn:function) — подписаться на событие блокаstring
- off(name:, fn:function) — отписаться от события блокаstring
- broadcast(name:[, details:*, [, originalEvent:Event]) — распространить событие вверх по vdom-дереву (т.е. дереву блоков)HTMLElement
- $on(target:, eventName:string, handle:string|function) — подписаться на DOM событиеHTMLElement
- $off(target:[, eventName:string[, handle:string|function]]) — отписаться от DOM события или событийHTMLElement
- one(target:[, eventName:string[, handle:string|function]])string
- setTemplateMatch(name:, match:Function) — установить функцию отвечающую за apply-match
---
0. Официальная документация
1. Editor > Language Injection > \click\ [+] > XML Attributes Injection
- name: feast-keywordsJavaScript
- Language > ID: if|for|when|var|add-class|attr|match|mod|apply-match
- XML Tag > Local name: test|data|as|key|args
- XML Attrbiute > Local name:
2. Editor > Language Injection > \click\ [+] > XML Attributes Injection
- name: feast-interpolation-in-htmlJavaScript
- Language > ID: [a-zA-Z-]+
- XML Attrbiute > Local name: \{(.*?)\}
- Advanced > Value pattern:
3. Editor > Language Injection > \click\ [+] > XML Attributes Injection
- name: feast-interpolation-in-feastJavaScript
- Language > ID: mod|add-class|attr
- XML Tag > Local name: bem|fn
- XML Tag > Local namespace: name|value
- XML Attrbiute > Local name: \{(.*?)\}
- Advanced > Value pattern:
4. Editor > Language Injection > \click\ [+] > XML Tag Injection
- name: feast-valueJavaScript
- Language > ID: value`
- XML Tag > Local name: