O framework JS mais limpo e leve de todos
- Escreva HTML totalmente em Javascript.
- Atualize o HTML facilmente quando quiser e sem hooks.
- Separe a view da lógica ou misture tudo. Você quem decide.
- Apenas Javascript. Qualquer desenvolvedor JS pode utilizar prontamente.
O HTML é gerado a partir de um objeto modelo, como no exemplo a seguir.
{
tag: 'div', className: 'form-input', childs: [
{tag: 'label', textContent: 'Email'},
{tag: 'input', value: person.email, onchange: (props: any) => person.name = props.elm.value}
]
}As Functions do gerador estão no arquivo html-generator.
Um componente é uma classe que deve conter a Function init e a propriedade mainPanel:
export class MyComponent implements IComponent {
/*
* genel(): gera um elemento HTML a partir de um objeto modelo.
* tag: nome da tag.
* elm: elemento HTML gerado.
*/
mainPanel = genel({tag: 'div'}).elm;
/**
* Crie seu modelo como propriedade da class.
* Assim, as functions podem acessá-lo diretamente.
* Isto facilita a modificação do HTML a partir de eventos.
*/
model: HTMLElementModel = {
tag: 'div',
mainClass: 'my-component', // classe usada exclusivamente para isolar o CSS do componente (opcional)
css: this.getCss(),
ref: 'mainModel', // variável para acessar o elemento HTML gerado
childs: [{tag: 'p', textContent: 'MyComponent works!'}]
}
init() {
/*
* genChild(): gera um elemento HTML filho.
* textContent: uma propriedade qualquer de um elemento HTML.
*/
genChild(this.mainPanel, this.model);
}
getCss() {
return `
p {
color: ${getTheme().fontColor};
font-size: 20px;
}
`;
}
onCssUpdate() {
// Caso seja necessário atualizar apenas o CSS do componente após sua exibição
// O model principal armazena todas as variáveis de elemento HTML em refs, independente da hierarquia
refreshCss(this.model.refs.mainModel, this.getCss());
}
}Não é necessário criar uma classe para gerar um subcomponente. Simplesmente crie uma função que retorne um objeto modelo:
getInput(label: string, value: string, setter: Function) {
// childs: tags filhas de um elemento.
return {tag: 'div', className: 'form-input', childs: [
{tag: 'label', textContent: label},
{
tag: 'input',
value: value,
// Functions recebem o próprio objeto modelo em que estão inseridos e 'elm' é o elemento criado pelo modelo.
onchange: (p: any) => setter(p.elm.value),
// O evento é acessado pela propriedade 'evt' do modelo.
onkeyup: (p: any) => {if (p.evt.keyCode === 13) this.save()}
}
]};
}Páginas devem implementar a interface IPage. É quase idêntica à IComponent, com uma propriedade a mais (pageCss), que é opcional.
export class MyPage implements IPage {
mainPanel = genel({tag: 'div'}).elm;
// Este arquivo CSS só estará ativo quando esta página estiver em exibição.
pageCss = '/styles/my-page.css';
model: HTMLElementModel = {tag: 'div', childs: [
{tag: 'h2', textContent: 'Hello World! Click me.', ref: 'title', onclick: () => this.click()},
// Um componente pode ser adicionado como filho de um objeto modelo.
new MyComponent()
]}
init() {
genChild(this.mainPanel, model);
}
click() {
// Os elementos são acessados pelo objeto 'refs' do modelo
this.model.refs.title.textContent = 'Hello dev!';
}
}getGlobalCss() {
return `
body {
background: ${getTheme().backgroundColor}
}
`;
}
// Registre a função de CSS global
addGlobalCSS(getGlobalCss);
// Gere novamente o CSS (em caso de mudança de tema, por exemplo)
genGlobalCSS();- tag: nome da tag HTML.
- childs: tags filhas.
- elm: o elemento HTML gerado.
- ref: referência do elemento.
- refs: objeto com os elementos referenciados (independente da hierarquia).
- Demais propriedades são atribuídas ao elemento HTML (incluindo o style).
- Functions recebem o objeto modelo no parâmetro.
- Functions com nome iniciando com 'on' são eventListeners, com o evento na propriedade evt do modelo.
- Functions com nome iniciando com 'listen' são eventListeners controlados pelo código. São executados com modelo.events.fire('NomeEvento').
- Demais functions são executadas e o retorno é atribuído à propriedade em questão.
- genel: gera um elemento.
- addChild: adiciona um filho ao elemento.
- addChilds: adiciona uma lista de filhos ao elemento.
- genChild: retira os filhos do elemento e adiciona um novo.
- genChilds: retira os filhos do elemento e adiciona uma lista de filhos.
- removeChilds: retira os filhos do elemento
showDialog = false;
model = {tag: 'div', childs: [
{tag: 'button', textContent: 'Show Dialog', onclick: (p: any) => {
showDialog = true;
// O primeiro parâmetro é o nome do evento.
// O segundo parâmetro é opcional e de qualquer tipo.
p.events.fire('ToggleDialog', 'Hello Dialog!');
}},
// Este elemento escutará eventos com nome 'ToggleDialog'
{tag: 'div', ref: 'dialog', listenToggleDialog: (p: any) => {
p.refs.dialog.hidden = !showDialog;
// O segundo parâmetro de fire() é acessado na propriedade 'evt':
p.refs.dialogTitle.innerText = p.evt;
}, childs: [
{tag: 'h3', innerText: 'Dialog Title', ref: 'dialogTitle'}
]}
]}Confira este exemplo de forma completa no arquivo pages/toggle-dialog-page.ts
names = ['Name 1', 'Mame 2', 'Name 3'];
/**
* Objeto de contexto
*
* Serve para compartilhar o acesso a refs e eventos listen
*/
context = {};
// Parâmetros: contexto, ref do elemento, propriedade do elemento, valor inicial (opcional)
setName = getSetter(this.context, 'name', 'textContent', 'Initial Value');
// Propriedade childs gera o HTML novamente (equivalente a chamar genChilds())
setNameList = getSetter(this.context, 'nameList', 'childs', this.getNameListModel());
// Atribua o objeto de contexto à propriedade super
model = {tag: 'div', super: this.context, childs: [
{tag: 'input', ref: 'name'},
{tag: 'div', ref: 'listName'}
]}
addName(name) {
this.names.push(name);
// Atualize o valor do elemento passando apenas o valor da propriedade
this.setNameList(this.getNameListModel());
}Sim, o tutorial é só isso!
Quer um exemplo completo?
Confira o CRUD na página Persons (arquivo pages/persons-page).
// Arquivo routes.ts
export const routes: IRote[] = [
{path: 'home', label: 'Home', createPage: () => new HomePage()},
{path: 'persons', label: 'Persons', createPage: () => new PersonsPage()},
{path: 'name-sorter', label: 'Sortear Nome', createPage: () => new NameSorterPage()},
{path: 'flex-css', label: 'Flex CSS', createPage: () => new FlexCSSPage()}
];
// Ir para a página:
navToPage(rote.path);Confira um exemplo de uso no arquivo pages/shared/header-comp.ts
Responsividade para todo o CSS, não apenas para a grid.
Esqueça media query.
const sizes1 = [
{ prefix: 's-', minSize: 0 },
{ prefix: 'm-', minSize: 500 },
{ prefix: 'l-', minSize: 1000 }
];
const sizes2 = [
{ prefix: 'xs-', minSize: 0 },
{ prefix: 's-', minSize: 433 },
{ prefix: 'm-', minSize: 733 },
{ prefix: 'l-', minSize: 1000 },
{ prefix: 'xl-', minSize: 1333 }
];.xs-panel {width: 100%}
.m-panel {width: 50%}
.l-panel {width: 33%}
.xs-span {
text-align: left;
color: black;
}
.m-span {
text-align: center;
color: blue;
}// Chame esta função no seu main passando sua lista de tamanhos no parâmetro, ou não, para usar os tamanhos default (sizes2).
processFlexCSS(sizes);
/**
* O FlexCSS executa automaticamente apenas quando detecta uma alteração na largura da tela.
* Caso tenha alguma alteração no HTML que seja necessario executar o FlexCSS, chame esta Function.
* Casos de uso comuns: troca de página e abertura de dialog.
*/
resetFlexCSS();{tag: 'div', className: 'fx-xs-m-xl-classname'}
// É o mesmo que:
{tag: 'div', className: 'classname xs-classname m-classname xl-classname'}Confira um exemplo de responsividade no arquivo pages/flex-css-page.ts
Controle o fluxo entre os componentes diretamente, sem se preocupar com a hierarquia da tela.
// Function adicionada para ser executada pelo nome.
appEvents.add('eventName', (evt: string) => alert(evt));
/**
* Function executada a partir de outro local no código.
* Primeiro parâmetro: nome do evento a ser executado.
* Segundo parâmetro: um dado qualquer (opcional) que é passado como parâmetro à Function a ser executada.
*/
appEvents.exec('eventName', 'Executando o evento');- Para instalar: npm install.
- Para rodar: npm run dev + Go Live no arquivo index.html (Live Server).