Bom dia @Leonardo,
Para adicionar um componente personalizado utilizando o HTML element você pode seguir os seguintes passos
Elemento personalizável com HTML Element
- Crie um campo do tipo HTML Element
- Na configuração ‘Templete Html’ adicione o conteúdo do corpo de seu componente em uma DIV
<div class="container-calendar">
<style>
/* Estilizando a página */
.container-calendar {
font-family: Arial, sans-serif;
margin: 20px;
}
.page-head {
display: flex;
justify-content: space-between;
align-items: center;
}
.container-calendar h1 {
font-size: 24px;
}
.calendario {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 0; /* Remover o espaço entre os dias */
margin-top: 20px;
}
.dia {
border: 1px solid #ccc;
padding: 7px;
text-align: center;
position: relative;
height: 150px; /* Altura do dia (números) ajustada para acomodar múltiplos eventos */
}
.dia-semana {
font-weight: bold; /* Negrito para os dias da semana */
padding: 5px; /* Mantém o texto próximo */
height: 30px; /* Altura ajustada apenas para os dias da semana */
display: flex; /* Para centralizar o texto verticalmente */
align-items: center; /* Centraliza verticalmente */
justify-content: center; /* Centraliza horizontalmente */
}
.mes {
text-align: center;
font-weight: bold;
margin-bottom: 10px;
font-size: 25px;
flex-grow: 1; /* Para centralizar corretamente */
}
.dia-inativo {
background-color: #f0f0f0; /* Tom mais claro para dias de outros meses */
color: #bbb; /* Tom mais claro para o texto */
}
.navegacao {
margin: 10px 0;
display: flex;
align-items: center;
}
.botao {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
margin-right: 1px; /* Margem entre os botões */
font-size: 18px; /* Tamanho da fonte dos botões */
}
</style>
<div class="page-head">
<div>
<h1>Calendário</h1>
</div>
</div>
<div class="navegacao">
<button class="botao" id="btnAnterior"><</button>
<button class="botao" id="btnProximo">></button>
<button class="botao" id="btnHoje">Hoje</button>
<div class="mes" id="mesAno"></div>
</div>
<div id="calendario" class="calendario"></div>
<script type="text/javascript">
setTimeout(
function() {
const diasDaSemana = ['Dom.', 'Seg.', 'Ter.', 'Qua.', 'Qui.', 'Sex.', 'Sáb.'];
const meses = ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'];
let mesAtual = new Date().getMonth();
let anoAtual = new Date().getFullYear();
const dataAtual = new Date(); // Armazena a data atual
function criarCalendario(mes, ano) {
const calendario = document.getElementById('calendario');
const mesAno = document.getElementById('mesAno');
calendario.innerHTML = ''; // Limpa o calendário atual
// Adiciona o nome do mês e ano
mesAno.textContent = `${meses[mes]} de ${ano}`;
// Adiciona os dias da semana
diasDaSemana.forEach(dia => {
const diaDaSemana = document.createElement('div');
diaDaSemana.textContent = dia;
diaDaSemana.classList.add('dia', 'dia-semana'); // Classe adicionada para estilização
calendario.appendChild(diaDaSemana);
});
// Ajuste para garantir que o primeiro dia seja domingo
const primeiroDia = new Date(ano, mes, 1).getDay();
const ultimoDia = new Date(ano, mes + 1, 0).getDate();
const ultimoDiaDoMesAnterior = new Date(ano, mes, 0).getDate();
// Preenche os dias em branco do mês anterior
for (let i = 0; i < primeiroDia; i++) {
const diaEmBranco = document.createElement('div');
diaEmBranco.textContent = ultimoDiaDoMesAnterior - primeiroDia + 1 + i;
diaEmBranco.classList.add('dia', 'dia-inativo'); // Marca como dia inativo
calendario.appendChild(diaEmBranco);
}
// Adiciona os dias do mês
for (let dia = 1; dia <= ultimoDia; dia++) {
const diaElemento = document.createElement('div');
diaElemento.textContent = dia;
diaElemento.classList.add('dia');
calendario.appendChild(diaElemento);
}
// Preenche os dias em branco do próximo mês
const proximoMesDias = 42 - calendario.childElementCount; // Máx. de 42 células no calendário (6 semanas)
for (let i = 1; i <= proximoMesDias; i++) {
const diaEmBranco = document.createElement('div');
diaEmBranco.textContent = i;
diaEmBranco.classList.add('dia', 'dia-inativo'); // Marca como dia inativo
calendario.appendChild(diaEmBranco);
}
}
// Função para navegar entre os meses
document.getElementById('btnAnterior').addEventListener('click', () => {
mesAtual = mesAtual === 0 ? 11 : mesAtual - 1;
anoAtual = mesAtual === 11 ? anoAtual - 1 : anoAtual;
criarCalendario(mesAtual, anoAtual);
});
document.getElementById('btnProximo').addEventListener('click', () => {
mesAtual = mesAtual === 11 ? 0 : mesAtual + 1;
anoAtual = mesAtual === 0 ? anoAtual + 1 : anoAtual;
criarCalendario(mesAtual, anoAtual);
});
document.getElementById('btnHoje').addEventListener('click', () => {
mesAtual = dataAtual.getMonth();
anoAtual = dataAtual.getFullYear();
criarCalendario(mesAtual, anoAtual);
});
// Inicializa o calendário com o mês atual
criarCalendario(mesAtual, anoAtual);
}
, 2000
)
</script>
</div>
Obs.: Fiz duas pequenas alterações para funcionar no campo.
1: Adição de todo o conteúdo em uma DIV de container.
2: adição de um Timeout no código JS para aguardar o carregamento dos campos
Temos também a possibilidade de usar um componente de extensão customizado do latromi
Segue o passo a passo para usá-lo
Calendário Latromi
Adicionando Javascript no formulário
- Acesse a aba ‘Arquivos Javascript’ do objeto formulário
- Inclua o código abaixo referente a criação do componente no arquivo
/********************************* Calendar Component **********************************/
/*
Dependências:
*/
function deepMerge(target, ...sources) {
sources.forEach(source => {
if (source) {
Object.keys(source).forEach(key => {
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
if (!target[key]) {
target[key] = {};
}
deepMerge(target[key], source[key]); // Recursivamente mescla objetos
} else {
target[key] = source[key]; // Cópia simples para valores não-objetos
}
});
}
});
return target;
}
var latromiCalendarConfigurator = function (options) {
var settings = deepMerge({
containerCssClass: 'calendar-container',
calendarCssClass: 'lcplace',
drawInterval: 100
}, options)
var lcObjects = []
function drawCalendar(element) {
var latromiCalendarObj = lcObjects.find(function (value) { return value.id === element.attributes['data-guid']?.value })
if (latromiCalendarObj)
latromiCalendarObj.build()
else {
// Localiza o element Script, que contém o JSON com os dados.
var childrens = [...element.children].filter(function (value) { return value.tagName === 'SCRIPT' })
if (childrens.length > 0) {
var script = childrens[0]
if (script.innerHTML && script.innerHTML !== '')
var json = JSON.parse(script.innerHTML)
}
latromiCalendarObj = new latromiCalendar(element, json)
lcObjects.push(latromiCalendarObj)
latromiCalendarObj.build()
}
}
function get(element) {
var obj = lcObjects.find(function (value) { return value.id === element.attributes['data-guid']?.value })
if (obj)
return obj.getValue()
return null
}
function set(element, value) {
lcObjects.find(function (value) { return value.id === element.attributes['data-guid']?.value }).setValue(value)
}
return {
load: function () {
if (document.getElementsByClassName(settings.containerCssClass).length === 0) return
setInterval(function () {
var itens = [...document.getElementsByClassName(settings.calendarCssClass)].filter(function (value) { return !value.attributes['data-state'] })
for (let index = 0; index < itens.length; index++) {
drawCalendar(itens[index])
}
}, settings.drawInterval)
},
getValue: function (element) { return get(element) },
setValue: function (element, value) { set(element, value) }
}
}
var latromiCalendar = function (element, options) {
var guid = _uuidv4()
var settings = deepMerge({
header: {
layout: null
},
legend: {
dayOfWeek: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
buttonToday: null
},
initialValue: null
}, options)
var _ = new Date() // para a inicialização do componente
if (settings.initialValue && settings.initialValue !== '') {
_ = new Date(settings.initialValue)
_.setDate(_.getDate() + 1)
}
_.setHours(0, 0, 0, 0)
var props = {
year: _.getFullYear(),
month: _.getMonth(),
value: _,
element: element,
isRenderer: false
}
function _uuidv4() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
);
}
function build() {
props.element.innerHTML = ''
var style = getDefaultStyle()
var header = getHeader()
var body = getBody()
props.element.appendChild(style)
props.element.appendChild(header)
props.element.appendChild(body)
props.isRenderer = true
props.element.setAttribute('data-state', 'ok')
props.element.setAttribute('data-guid', guid)
var event = document.createEvent('Event')
event.initEvent('change', true, false)
element.dispatchEvent(event)
}
function getDefaultStyle() {
var style = document.createElement('style')
style.innerHTML = `
/* Default container */
.${props.element.parentElement.attributes['class'].value} {
container-name: ctnCalendar;
container-type: inline-size;
display: grid;
}
/* Header */
.lc-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.lc-header-1,
.lc-header-2 {
display: flex;
flex-wrap: nowrap;
}
.lc-header-layout-default {
flex-direction: column;
}
.lc-header-layout-default .lc-header-2 {
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.lc-header-title {
text-align: center;
font-weight: bold;
font-size: 25px;
flex-grow: 1;
align-content: center;
}
.lc-btn {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
margin-right: 1px;
font-size: 18px;
}
.lc-header-layout-default .lc-today {
background: transparent;
color: black;
}
/* Body */
.lc-body {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 0;
margin-top: 20px;
font-size: 16px;
}
.lc-day {
border: 1px solid #ccc;
padding: 7px;
text-align: center;
position: relative;
padding-bottom: calc(100% - 16px);
background-color: white;
}
.lc-day .lc-day-content {
position: absolute;
margin-top: 7px;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.lc-day:hover {
cursor: pointer;
}
.lc-day-select {
background-color: #FF4500;
color: #000;
}
.lc-day-disable {
background-color: #f0f0f0;
color: #bbb;
}
.lc-day-of-week {
font-weight: bold;
padding: 5px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
width: calc(100% - 12px);
}
.lc-day-of-week:hover {
cursor: default !important;
}
@container ctnCalendar (max-width: 170px) {
/* Header */
.lc-header-title {
font-size: 10px;
}
.lc-btn {
padding: 4px 5px;
font-size: 8px;
}
/* Body */
.lc-body {
margin-top: 5px;
font-size: 7px;
}
.lc-day .lc-day-content {
margin-top: 1px !important;
}
.lc-day-of-week {
height: 3px;
}
}
@container ctnCalendar (170px <= width <= 240px) {
/* Header */
.lc-header-title {
font-size: 14px;
}
.lc-btn {
padding: 4px 6.4px;
font-size: 10px;
}
/* Body */
.lc-body {
margin-top: 6.4px;
font-size: 8px;
}
.lc-day .lc-day-content {
margin-top: 2px !important;
}
.lc-day-of-week {
height: 5px;
}
}
@container ctnCalendar (240px <= width <= 400px) {
/* Header */
.lc-header-title {
font-size: 18px;
}
.lc-btn {
padding: 6px 12px;
font-size: 12px;
}
/* Body */
.lc-body {
margin-top: 12px;
font-size: 12px;
}
.lc-day .lc-day-content {
margin-top: 3px !important;
}
.lc-day-of-week {
height: 9px;
}
}
`
return style
}
function getHeader() {
var header = document.createElement('div')
header.classList.add('lc-header');
var headerTop = document.createElement('div')
headerTop.classList.add('lc-header-1')
var headerBottom = document.createElement('div')
headerBottom.classList.add('lc-header-2')
var btnBack = document.createElement('button')
btnBack.classList.add('lc-btn')
btnBack.innerHTML = '<'
btnBack.addEventListener('click', function () {
props.month = props.month === 0 ? 11 : props.month - 1;
props.year = props.month === 11 ? props.year - 1 : props.year;
build()
})
var btnNext = document.createElement('button')
btnNext.classList.add('lc-btn')
btnNext.addEventListener('click', function () {
props.month = props.month === 11 ? 0 : props.month + 1;
props.year = props.month === 0 ? props.year + 1 : props.year;
build()
})
btnNext.innerHTML = '>'
var btnToday = document.createElement('button')
btnToday.classList.add('lc-btn', 'lc-today')
btnToday.addEventListener('click', function () {
var today = new Date()
props.month = today.getMonth()
props.year = today.getFullYear()
build()
})
var dtToday = new Date()
btnToday.textContent = settings.legend.buttonToday ?? `${settings.legend.dayOfWeek[dtToday.getDay()]}, ${settings.legend.month[dtToday.getMonth()]} ${dtToday.getDate()}`
var lblTitle = document.createElement('div')
lblTitle.classList.add('lc-header-title')
lblTitle.textContent = `${settings.legend.month[props.month]} ${props.year}`
switch (settings.header.layout) {
case 'center':
headerBottom.appendChild(btnBack)
headerBottom.appendChild(lblTitle)
headerBottom.appendChild(btnNext)
headerTop.appendChild(btnToday)
header.appendChild(headerTop)
header.appendChild(headerBottom)
header.classList.add('lc-header-layout-default')
break;
case 'left':
btnToday.classList.remove('lc-today')
headerTop.appendChild(btnBack)
headerTop.appendChild(btnNext)
headerTop.appendChild(btnToday)
headerBottom.appendChild(lblTitle)
header.appendChild(headerTop)
header.appendChild(headerBottom)
break;
case 'right':
btnToday.classList.remove('lc-today')
headerBottom.appendChild(btnToday)
headerBottom.appendChild(btnBack)
headerBottom.appendChild(btnNext)
headerTop.appendChild(lblTitle)
header.appendChild(headerTop)
header.appendChild(headerBottom)
break;
default:
headerBottom.appendChild(btnBack)
headerBottom.appendChild(lblTitle)
headerBottom.appendChild(btnNext)
headerTop.appendChild(btnToday)
header.appendChild(headerTop)
header.appendChild(headerBottom)
header.classList.add('lc-header-layout-default')
break;
}
return header
}
function getBody() {
var calendar = document.createElement('div')
calendar.classList.add('lc-body')
settings.legend.dayOfWeek.forEach(function (week) {
var dayOfWeekDiv = document.createElement('div')
dayOfWeekDiv.classList.add('lc-day', 'lc-day-of-week')
var span = document.createElement('span')
span.textContent = week
span.classList.add('lc-day-content')
dayOfWeekDiv.appendChild(span)
calendar.appendChild(dayOfWeekDiv)
})
var currentDayOfWeek = new Date(props.year, props.month, 1).getDay()
var lastDayMonth = new Date(props.year, props.month + 1, 0).getDate()
var lastDayOfLastMonth = new Date(props.year, props.month, 0).getDate()
for (var buffer = 0; buffer < currentDayOfWeek; buffer++) {
var dayDiv = document.createElement('div')
var day = lastDayOfLastMonth - currentDayOfWeek + 1 + buffer
dayDiv.classList.add('lc-day', 'lc-day-disable')
var span = document.createElement('span')
span.textContent = day
span.classList.add('lc-day-content')
dayDiv.appendChild(span)
$(dayDiv).attr('data-value', day)
dayDiv.addEventListener('click', function () {
var newDate = new Date(props.year, props.month - 1, parseInt($(this).attr('data-value')))
props.month = newDate.getMonth()
props.year = newDate.getFullYear()
props.value = newDate
build()
})
calendar.appendChild(dayDiv)
}
for (var dayMonth = 1; dayMonth <= lastDayMonth; dayMonth++) {
var dayDiv = document.createElement('div')
dayDiv.classList.add('lc-day')
var selectedDate = new Date(props.year, props.month, dayMonth)
if (selectedDate.getTime() === props.value.getTime())
dayDiv.classList.add('lc-day-select')
var span = document.createElement('span')
span.textContent = dayMonth
span.classList.add('lc-day-content')
dayDiv.appendChild(span)
$(dayDiv).attr('data-value', dayMonth)
dayDiv.addEventListener('click', function () {
var newDate = new Date(props.year, props.month, parseInt($(this).attr('data-value')))
props.month = newDate.getMonth()
props.year = newDate.getFullYear()
props.value = newDate
build()
})
calendar.appendChild(dayDiv)
}
var dayNextMonthTotal = 49 - calendar.childElementCount; // Leg.: 49 - Max value grid elements
for (var dayNextMonth = 1; dayNextMonth <= dayNextMonthTotal; dayNextMonth++) {
var dayDiv = document.createElement('div')
dayDiv.classList.add('lc-day', 'lc-day-disable')
var span = document.createElement('span')
span.textContent = dayNextMonth
span.classList.add('lc-day-content')
dayDiv.appendChild(span)
$(dayDiv).attr('data-value', dayNextMonth)
dayDiv.addEventListener('click', function () {
var newDate = new Date(props.year, props.month + 1, parseInt($(this).attr('data-value')))
props.month = newDate.getMonth()
props.year = newDate.getFullYear()
props.value = newDate
build()
})
calendar.appendChild(dayDiv)
}
return calendar
}
return {
id: guid,
options: settings,
properties: props,
build: build,
getValue: function() { return props.value },
setValue: function(value) { props.value = value }
}
}
- Inclua o código abaixo para carregar o calendário
const myForm = (function () {
// Adiciona Callbacks no Formulário
latromi.formManager.setOnFormCreatedCallback(onFormCreated);
latromi.formManager.setOnEventFiringCallback(onEventFiring);
latromi.formManager.setOnFieldValueChangedCallback(onFieldValueChanged);
// Ocorre logo após o form ser inicializado
function onFormCreated(ev) {
var calendarObj = new latromiCalendarConfigurator({
containerCssClass: 'calendar-container',
calendarCssClass: 'lcplace',
drawInterval: 100
})
calendarObj.load()
}
// Ocorre quando um evento é disparado no Form
function onEventFiring(ev) { }
// Ocorre quando o valor de um campo é alterado no Form
function onFieldValueChanged(ev) { }
// Todas as funções acima são "privadas", e não podem ser chamadas externamente.
// As únicas funções que podem ser chamadas externamente são as que compões o resultado a seguir:
return {
test: function() { }
}
})();
Adicionando o campo HTML Element
- Adicione o campo no formulário
- Inclua na configuração ‘Templete Html’ o seguinte código
<div class="calendar-container">
<div class="lcplace">
<script type="application/json">{
"header": {
"layout": "center"
},
"legend": {
"dayOfWeek": ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"],
"month": ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
"buttonToday": "Hoje"
}
}</script>
</div>
</div>
Propriedades do componente
As propriedades abaixo podem ser adicionados no templete HTML dentro da tag SCRIPT como um JSON, para personalizar o componente:
Parametros
header
- layout: define o estilo do calendário sendo o estilo padrão o ‘center’ (Valores possíveis: ‘center’, ‘right’ e ‘left’)
legend
- dayOfWeek: Define o texto para os dias da semana
- month: Define o texto para os meses
- buttonToday: Define texto para o botão que volta o calendário para o dia atual
initialValue
Esse campo é do tipo Datetime e define a data inicial para o calendário