Pular para o conteúdo principal

Componentes

O Nerdify fornece um sistema de componentes para construir interfaces dinâmicas. Os componentes são definidos no backend (Ruby) e renderizados automaticamente no frontend (Angular).


Tipos de Componentes

O Nerdify suporta os seguintes tipos de componentes:

TipoDescrição
containerContainer genérico para agrupar outros componentes
fieldsetVisual de fieldset (diferente de fieldset real)
buttonBotão de ação
textTexto estático ou dinâmico
iconÍcone Material Design
imageImagem
inputCampo de entrada (viewonly ou editável)
cardCard com estrutura visual
embedIncorpora outro recurso/página
listLista de itens
nameExibe o nome do recurso

Container

O container é o componente mais versátil, usado para agrupar e organizar outros componentes.

component :container, size: 12, styles: { padding: { left: 2, right: 2 } } do
component :text, name: :title, type: :h4
component :text, name: :description
end

Opções do Container

OpçãoTipoDescrição
sizeIntegerLargura em colunas (1-12)
stylesHashEstilos CSS personalizados
render_ifStringCondição para renderizar (JavaScript)
clickHashAção ao clicar
positionSymbolPosição (:right, :left)

Exemplo com Layout Complexo

component :container, name: "header", size: 12 do
component :container, size: 6 do
component :text, name: :name, type: :h3
end

component :container, size: 6, position: :right do
component :text, name: :total, styles: { color: "primary" }
end
end

Text

Exibe texto estático (label) ou dinâmico (valor do campo).

# Texto dinâmico (valor do campo)
component :text, name: :customer_name

# Texto estático (label traduzido)
component :text, name: :title, static: true

# Com tipo de heading
component :text, name: :name, type: :h4

Opções do Text

OpçãoTipoDescrição
nameSymbolNome do campo ou chave de tradução
typeSymbolTipo: :h1 a :h6, :small, :span, :p
staticBooleanSe true, exibe o label traduzido
stylesHashEstilos personalizados
tooltipBoolean/SymbolExibe tooltip com informação adicional

Estilos Comuns

component :text, name: :value, styles: {
color: "primary", # primary, danger, success, warning, info
font_weight: 5, # 1-9
font_size: 6, # 1-9
nowrap: true, # Não quebra linha
align: { body: "center" } # Alinhamento
}

Icon

Exibe ícones do Material Design.

component :icon, name: :shopping_cart

# Com estilos
component :icon, name: :star, styles: {
color: "warning",
font_size: 4
}

# Ícone outlined
component :icon, name: :info, icon_type: :outlined

Opções do Icon

OpçãoTipoDescrição
nameSymbolNome do ícone Material
icon_typeSymbol:filled (padrão) ou :outlined
staticBooleanSe true, ícone fixo sem valor do campo
stylesHashEstilos personalizados
tooltipSymbolTooltip com tradução do campo
clickHashAção ao clicar

Image

Exibe uma imagem.

component :image, name: :photo_url

# Com tamanho personalizado
component :image, name: :avatar, image_size: 4, styles: {
rounded: { top_left: 5, top_right: 5, bottom_left: 5, bottom_right: 5 }
}

Opções da Image

OpçãoTipoDescrição
nameSymbolCampo que contém a URL da imagem
image_sizeIntegerTamanho da imagem (1-12)
stylesHashEstilos (rounded, padding, etc)

Input

Exibe um campo de input, geralmente em modo viewonly.

# Campo viewonly
component :input, input_type: :money, name: :total, view: :viewonly, label: false

# Campo select viewonly
component :input, input_type: :select, name: :status, view: :viewonly, layout: :inline

Opções do Input

OpçãoTipoDescrição
nameSymbolNome do campo
input_typeSymbolTipo: :text, :money, :select, etc
viewSymbol:viewonly para apenas visualização
labelBooleanExibe ou oculta o label
layoutSymbol:inline para layout horizontal
sizeInteger/NilLargura em colunas

Embed

Incorpora outro recurso ou página dentro do componente atual.

component :embed, name: :pets, path: "/customers/:resource.id/pets", size: 12

# Com detecção de mudanças
component :embed, name: :activities, path: "/activities/:resource.id", detect_changes: true

Opções do Embed

OpçãoTipoDescrição
nameSymbolIdentificador do embed
pathStringCaminho do recurso (suporta interpolação)
sizeIntegerLargura em colunas
detect_changesBooleanRecarrega quando há mudanças

Interpolação de Path

Use :resource.campo para interpolar valores:

path: "/customers/:resource.id/pets"
path: "/sales/:resource.sale_id/items"

Button

Cria um botão de ação.

component :button, name: :save, click: { submit: "form" }

# Com estilos
component :button, name: :cancel, click: { close: :dialog }, styles: {
background: "transparent",
color: "danger"
}

Opções do Button

OpçãoTipoDescrição
nameSymbolNome/label do botão
clickHashAção ao clicar
stylesHashEstilos personalizados
iconSymbolÍcone do botão

Card

Cria um card com estrutura visual.

component :card, name: :info_card do
component :text, name: :title, type: :h5
component :text, name: :description
end

Estilos Comuns

Cores

styles: { color: "primary" }   # Cor do texto
styles: { background: "light" } # Cor de fundo

Valores: primary, secondary, success, danger, warning, info, light, dark, white, transparent

Espaçamento

styles: {
padding: { left: 2, right: 2, top: 1, bottom: 1 },
margin: { left: 1, right: 1 }
}

Valores: 0-5 (unidades de espaçamento Bootstrap)

Bordas

styles: {
border: { top: 1, right: 1, bottom: 1, left: 1 },
rounded: { top_left: 2, top_right: 2, bottom_left: 2, bottom_right: 2 }
}

Alinhamento

styles: {
align: { body: "center" }, # center, left, right
vertical_align: { top: "start" }, # start, center, end
orientation: { body: "vertical" } # vertical, horizontal
}

Outros

styles: {
font_weight: 5, # 1-9
font_size: 6, # 1-9
opacity: 50, # 0-100
nowrap: true, # Não quebra linha
height: 100 # Altura em porcentagem
}

Renderização Condicional

Use render_if para exibir componentes condicionalmente:

# Condição baseada no recurso
component :text, name: :discount, render_if: "resource.has_discount"

# Condição com comparação
component :icon, name: :star, render_if: "resource.stars.length > 0"

# Negação
component :text, name: :empty, render_if: "!resource.items || resource.items.length == 0"

A expressão é avaliada no frontend (JavaScript).


Ações de Click

click: { redirect_to: "/customers/:resource.id" }
click: { redirect_to: ":resource.phone_link", target: "_blank" }

Abrir Dialog

click: { redirect_to: "edit", open_in: :dialog }

Fechar Dialog

click: { close: :dialog }

Submit de Formulário

click: { submit: "form_name" }

Requisição HTTP

click: {
put: ":resource.id/approve",
success: { toast: :success, update: "resource" },
error: { toast: :error }
}

Exemplo Completo

fieldset :customer_show, size: 12 do
component :container, size: 3, styles: { border: { right: 1 } } do
component :container, styles: { align: { body: "center" }, padding: { top: 3 } } do
component :image, name: :photo_url, image_size: 4, styles: {
rounded: { top_left: 5, top_right: 5, bottom_left: 5, bottom_right: 5 }
}
component :text, type: :h4, name: :name

component :container do
component :icon, name: :star, static: true, styles: { color: "warning" },
render_if: "resource.stars.length > 0"
component :icon, name: :star, static: true, styles: { color: "info", opacity: 50 },
render_if: "resource.stars.length == 0"
end

component :container do
component :icon, name: :phone, static: true, styles: { background: "light" },
render_if: "resource.phone && resource.phone.length > 0",
click: { redirect_to: ":resource.phone_link", target: "_blank" },
tooltip: :phone
end
end

component :container, size: 12, styles: { border: { top: 1 } } do
component :text, type: :small, name: :total_sales, static: true, styles: { font_weight: 5 }
component :input, input_type: :money, name: :ltv, view: :viewonly, label: false,
styles: { color: "primary" }
end
end

fieldset :customer_tabs, size: 9 do
fieldset :about, tab: :about, size: 12 do
field :name, presence: true
field :email
field :phone
end

fieldset :sales, tab: :sales, size: 12 do
component :embed, name: :sales, path: "/customers/:resource.id/sales", size: 12
end
end
end

Boas Práticas

Faça

  • Use container para agrupar componentes relacionados
  • Aproveite render_if para interfaces dinâmicas
  • Use styles consistentes em toda a aplicação
  • Documente componentes complexos

Evite

  • Aninhar muitos níveis de containers
  • Duplicar estilos - extraia para constantes
  • Misturar lógica de negócio nos componentes
  • Criar componentes muito genéricos