$teatablecolor : var(--main-color, #6967ce);
$teatablepadd : 12px;
#teatable {
font-family: Arial, sans-serif;
color: #3f4142;
padding: $teatablepadd;
border-radius: $teatablepadd;
button {
border: 1px solid $teatablecolor;
color: $teatablecolor;
background-color: #fff;
padding: ( $teatablepadd - 2) $teatablepadd;
border-radius: $teatablepadd;
cursor: pointer;
&:hover {
background-color: $teatablecolor;
color: #fff;
}
&.btn-danger:hover {
background-color: #e34d4d;
border-color: #df6f6f;
}
}
input {
flex: 1;
width: 100%;
max-width: calc(100% - ($teatablepadd * 2));
padding: $teatablepadd;
margin-bottom: 0.5rem;
border-radius: $teatablepadd;
border: 1px solid $teatablecolor;
color: $teatablecolor;
background-color: #fff;
&::placeholder{
color: $teatablecolor;
}
&:focus {
outline: 2px solid $teatablecolor;
}
}
.table-controls, footer.pagination-controls {
display: flex;
align-items: center;
justify-content: space-between;
gap: 5px;
white-space: nowrap;
margin-bottom: 0.5rem;
input {
margin-bottom: 0;
}
}
.btn-group {
display: flex;
justify-content: center;
align-items: center;
button:not(:first-child):not(:last-child) {
border-left: none;
border-right: none;
border-radius: 0;
}
button:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
button:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.btn-action{
padding: $teatablepadd 1.5rem;
margin-right: 5px;
}
.hidden {
display: none !important;
}
&.dark-mode {
$darkcolor : #444;
background-color: #333;
color: #fff;
button {
border: 1px solid $darkcolor;
color: #fff;
background-color: #8d8d8d;
&:hover {
background-color: $darkcolor;
color: #fff;
}
&.btn-danger:hover {
background-color: #e34d4d;
border-color: #df6f6f;
}
}
input {
border: 1px solid $darkcolor;
color: #fff;
background-color: #8d8d8d;
&::placeholder{
color: #fff;
}
&:focus {
outline: 2px solid $darkcolor;
}
&:hover {
background-color: $darkcolor;
}
}
table{
background: $darkcolor;
color: #fff;
//border-radius: $border-radius;
border: 1px solid $darkcolor;
th {
border: none;
}
td {
border: none;
}
tr:nth-child(odd) {
background: #666;
}
tr:nth-child(even) {
background: #555;
}
}
}
}
// Default options for table style
$table-breakpoint: 480px;
$table-background-color: #FFF;
$table-text-color: #024457;
$table-outer-border: 1px solid $teatablecolor;
$table-cell-border: 1px solid #D9E4E6;
// Extra options for table style (parse these arguments when including your mixin)
$table-border-radius: 10px;
$table-highlight-color: #EAF3F3;
$table-header-background-color: $teatablecolor;
$table-header-text-color: #FFF;
$table-header-border: 1px solid #FFF;
// The Responstable mixin
@mixin responstable(
$breakpoint: $table-breakpoint,
$background-color: $table-background-color,
$text-color: $table-text-color,
$outer-border: $table-outer-border,
$cell-border: $table-cell-border,
$border-radius: none,
$highlight-color: none,
$header-background-color: $table-background-color,
$header-text-color: $table-text-color,
$header-border: $table-cell-border) {
#teatable table {
margin: 0.5rem 0;
width: 100%;
background: $background-color;
color: $text-color;
border-radius: $border-radius;
border: $outer-border;
overflow: hidden;
//white-space: nowrap;
thead th {
border-radius: $teatablepadd / 2;
cursor: pointer;
&:hover {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1rem' height='1rem' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='ai ai-ChevronVertical'%3E%3Cpath d='M8 17l4 4 4-4'/%3E%3Cpath d='M8 7l4-4 4 4'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: calc(100% - 0.2rem) center;
}
}
tr {
border-top: $outer-border;
border-bottom: $outer-border;
&:nth-child(odd) {
background-color: $highlight-color;
}
}
th {
display: none; // hide the table headers for mobile
//border: $header-border;
background-color: $header-background-color;
color: $header-text-color;
}
td {
border-radius: $teatablepadd / 2;
display: block; // display the table as a block element for mobile
&:before {
content: attr(data-th)": "; // grab the headers from the data-th and put them before every table cell for mobile
font-weight: bold;
width: 5rem;
display: inline-block;
@media (min-width: $breakpoint) {
display: none; // do not display the data-th for desktop
}
}
@media (min-width: $breakpoint) {
border: $cell-border;
}
}
th, td {
text-align: left;
margin: .5em 1em;
@media (min-width: $breakpoint) {
display: table-cell; // turn the table into a 'normal' table-cell for desktop
padding: 0.5rem;
&:first-child {
text-align: center;
}
}
}
}
}
// Include the mixin (with extra options as overrides)
@include responstable(
$border-radius: $table-border-radius,
$highlight-color: $table-highlight-color,
$header-background-color: $table-header-background-color,
$header-text-color: $table-header-text-color,
$header-border: $table-header-border);
View Compiled
class TeaTable {
constructor(containerId, options = {}) {
this.containerId = containerId;
this.data = options.data || [];
this.currentPage = 1;
this.rowsPerPage = options.rowsPerPage || 10;
this.sortDirection = true;
this.tableId = "dataTable";
this.callbacks = {
onCreate: options.onCreate || null,
onEdit: options.onEdit || null,
onDelete: options.onDelete || null
// Diğer callback'ler eklenebilir
};
//this.language = options.language || 'tr';
this.themeColor = options.themeColor || "#6967ce";
this.txtAdd = options.txtAdd || "Ekle";
this.txtUpdate = options.txtUpdate || "Güncelle";
this.txtDel = options.txtDel || "Sil";
this.txtEdit = options.txtEdit || "Düzenle";
this.txtAct = options.txtAct || "Eylem";
this.txtSearch = options.txtSearch || "Ara...";
this.txtPage = options.txtPage || "Sayfa";
this.txtConfirm = options.txtConfirm || "Bu veriyi silmek istediğinize emin misiniz?";
this.init();
}
init() {
const container = document.getElementById(this.containerId);
container.innerHTML = this.getTableHTML();
// Event listener'ları ekleme
container.querySelector('#addData').addEventListener('click', () => this.createForm());
container.querySelector('#searchBox').addEventListener('input', this.debounce((event) => this.filterTable(event.target.value), 300));
container.querySelector('#fullscreenButton').addEventListener('click', () => this.toggleFullScreen());
container.querySelector('#exportButton').addEventListener('click', () => this.exportTableToCSV('export.csv'));
container.querySelector('#prevPage').addEventListener('click', () => this.changePage(-1));
container.querySelector('#nextPage').addEventListener('click', () => this.changePage(1));
container.querySelector('#toggleDarkMode').addEventListener('click', () => this.toggleDarkMode());
this.loadDataToTable();
}
getTableHTML() {
// Tablonun HTML yapısını döndür
return `
<section id="teatable" style="--main-color:${this.themeColor}">
<!-- Tablo kontrolleri -->
<header class="table-controls">
<button id="addData"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai ai-Plus"><path d="M12 20v-8m0 0V4m0 8h8m-8 0H4"/></svg></button>
<input type="text" id="searchBox" placeholder="${this.txtSearch}">
<div class="btn-group"><button id="fullscreenButton"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai ai-FullScreen"><path d="M2 7V2h5"/><path d="M22 7V2h-5"/><path d="M7 22H2v-5"/><path d="M17 22h5v-5"/></svg></button>
<button id="exportButton"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai ai-Download"><path d="M12 15V3m0 12l-4-4m4 4l4-4"/><path d="M2 17l.621 2.485A2 2 0 0 0 4.561 21H19.439a2 2 0 0 0 1.94-1.515L22 17"/></svg></button>
<button id="toggleDarkMode"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="currentColor" stroke-width="2" class="ai ai-MoonFill"><path d="M20.958 15.325c.204-.486-.379-.9-.868-.684a7.684 7.684 0 0 1-3.101.648c-4.185 0-7.577-3.324-7.577-7.425a7.28 7.28 0 0 1 1.134-3.91c.284-.448-.057-1.068-.577-.936C5.96 4.041 3 7.613 3 11.862 3 16.909 7.175 21 12.326 21c3.9 0 7.24-2.345 8.632-5.675z"/><path d="M15.611 3.103c-.53-.354-1.162.278-.809.808l.63.945a2.332 2.332 0 0 1 0 2.588l-.63.945c-.353.53.28 1.162.81.808l.944-.63a2.332 2.332 0 0 1 2.588 0l.945.63c.53.354 1.162-.278.808-.808l-.63-.945a2.332 2.332 0 0 1 0-2.588l.63-.945c.354-.53-.278-1.162-.809-.808l-.944.63a2.332 2.332 0 0 1-2.588 0l-.945-.63z"/></svg></button></div>
</header>
<!-- Veri formu -->
<section id="dataForm" class="hidden"></section>
<!-- Tablo -->
<table id="dataTable">
<thead></thead>
<tbody></tbody>
</table>
<!-- Sayfalama -->
<footer class="pagination-controls">
<button id="prevPage"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai ai-ChevronLeft"><path d="M15 4l-8 8 8 8"/></svg></button>
<span id="pageInfo"></span>
<button id="nextPage"><svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai ai-ChevronRight"><path d="M8 4l8 8-8 8"/></svg></button>
</footer>
</section>`;
}
// ...
toggleDarkMode() {
const container = document.getElementById(this.containerId);
container.querySelector('#teatable').classList.toggle('dark-mode');
// Dark mode durumunu buton metninde güncelle
const darkModeButton = document.getElementById('toggleDarkMode');
if (container.classList.contains('dark-mode')) {
darkModeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="currentColor" stroke-width="2" class="ai ai-ToggleOffFill"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 7a5 5 0 0 0 0 10h10a5 5 0 0 0 0-10H7zm0 2.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/></svg>';
} else {
darkModeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24" fill="currentColor" stroke-width="2" class="ai ai-ToggleOffFill"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 7a5 5 0 0 0 0 10h10a5 5 0 0 0 0-10H7zm0 2.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/></svg>';
}
}
setDataAttributes() {
const table = document.getElementById(this.tableId);
const headers = table.querySelectorAll('thead th');
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
row.querySelectorAll('td').forEach((cell, index) => {
if (headers[index]) {
cell.setAttribute('data-th', headers[index].textContent);
}
});
});
}
// Örnek metodlar:
loadDataToTable() {
const table = document.getElementById(this.tableId);
const thead = table.querySelector('thead');
const tbody = table.querySelector('tbody');
// Tablo başlıklarını ayarla
thead.innerHTML = '';
let headerRow = thead.insertRow(0);
let keys = this.getAllKeys(this.data);
keys.forEach(key => {
let th = document.createElement('th');
th.textContent = key;
th.addEventListener('click', () => this.sortTableByColumn(key));
headerRow.appendChild(th);
});
// "Action" sütun başlığını ekle
let actionTh = document.createElement('th');
actionTh.textContent = this.txtAct;
headerRow.appendChild(actionTh);
// Veri satırlarını ayarla
tbody.innerHTML = '';
const start = (this.currentPage - 1) * this.rowsPerPage;
const end = start + this.rowsPerPage;
const paginatedItems = this.data.slice(start, end);
paginatedItems.forEach(item => {
let row = tbody.insertRow();
keys.forEach(key => {
let cell = row.insertCell();
cell.textContent = item[key];
});
// Eylem butonlarını ekle
let actionCell = row.insertCell();
let editButton = document.createElement('button');
editButton.textContent = this.txtEdit;
editButton.onclick = () => this.editData(item.id);
let deleteButton = document.createElement('button');
deleteButton.classList.add('btn-danger');
deleteButton.textContent = this.txtDel;
deleteButton.onclick = () => this.deleteData(item.id);
let groupButton = document.createElement('div');
groupButton.classList.add('btn-group');
groupButton.appendChild(editButton)
groupButton.appendChild(deleteButton);
actionCell.appendChild(groupButton);
});
// Veri yoksa boş bir satır ekle
if (this.data.length === 0) {
let row = tbody.insertRow();
let cell = row.insertCell();
cell.colSpan = keys.length + 1; // Tüm sütunları kapsar
cell.textContent = 'Veri Yok';
}
// Verileri yükledikten sonra data-th niteliklerini ayarla
this.setDataAttributes();
this.updatePageInfo();
}
createForm(editData = null) {
const formDiv = document.getElementById('dataForm');
formDiv.innerHTML = '';
formDiv.classList.remove('hidden');
let keys = this.getAllKeys(this.data);
keys.forEach(key => {
if (key !== 'id') { // 'id' alanını formda göstermeyin
let input = document.createElement('input');
input.type = 'text';
input.placeholder = key;
input.id = 'form-' + key;
input.value = editData ? (editData[key] ?? '') : '';
formDiv.appendChild(input);
}
});
let submitButton = document.createElement('button');
submitButton.textContent = editData ? this.txtUpdate : this.txtAdd;
submitButton.classList.add('btn-action');
submitButton.addEventListener('click', () => {
this.submitForm(editData ? editData.id : null);
if(editData) formDiv.classList.add('hidden');
});
formDiv.appendChild(submitButton);
let cancelButton = document.createElement('button');
cancelButton.textContent = 'Cancel';
cancelButton.classList.add('btn-action');
cancelButton.addEventListener('click', () => {
formDiv.classList.add('hidden');
});
formDiv.appendChild(cancelButton);
}
submitForm(id) {
const formData = {};
this.getAllKeys(this.data).forEach(key => {
if (key !== 'id') {
formData[key] = document.getElementById('form-' + key).value;
}
});
if (id === null) {
// Yeni veri ekleme
formData.id = this.data.length > 0 ? this.data[this.data.length - 1].id + 1 : 1;
this.data.push(formData);
} else {
// Mevcut veriyi güncelleme
const index = this.data.findIndex(item => item.id === id);
if (index !== -1) {
this.data[index] = { ...this.data[index], ...formData };
}
}
this.loadDataToTable();
}
getAllKeys(data) {
const allKeys = new Set();
data.forEach(item => {
Object.keys(item).forEach(key => allKeys.add(key));
});
return Array.from(allKeys);
}
editData(id) {
const editItem = this.data.find(item => item.id === id);
if (editItem) {
this.createForm(editItem);
if (typeof this.callbacks.onEdit === 'function') {
this.callbacks.onEdit(editItem);
}
}
}
deleteData(id) {
if (confirm(this.txtConfirm)) {
this.data = this.data.filter(item => item.id !== id);
this.loadDataToTable();
if (typeof this.callbacks.onDelete === 'function') {
this.callbacks.onDelete(id);
}
}
}
filterTable(searchText) {
const rows = document.getElementById(this.tableId).getElementsByTagName('tbody')[0].rows;
let searchTextLower = searchText.toLowerCase();
if (searchTextLower) {
// Arama metni varsa, sayfalama ayarını geçici olarak değiştir
this.tempRowsPerPage = this.rowsPerPage;
this.rowsPerPage = 100; // Tüm satırları tek sayfada göster
this.currentPage = 1; // Sayfayı ilk sayfaya ayarla
this.changePage(0)
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
let allCellText = '';
for (let j = 0; j < row.cells.length - 1; j++) {
allCellText += row.cells[j].textContent.toLowerCase() + ' ';
}
row.style.display = allCellText.includes(searchTextLower) ? '' : 'none';
}
} else {
// Arama metni yoksa, sayfalama ayarlarını eski haline getir
this.rowsPerPage = this.tempRowsPerPage;
this.loadDataToTable();
}
}
debounce(func, wait) {
let timeout;
return function(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
toggleFullScreen() {
const container = document.getElementById(this.containerId);
if (!document.fullscreenElement && !document.mozFullScreenElement &&
!document.webkitFullscreenElement && !document.msFullscreenElement ) {
// Tam ekran moduna geç
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
} else if (container.mozRequestFullScreen) {
container.mozRequestFullScreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
// Tam ekran modundan çık
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
}
changePage(amount) {
const totalPages = Math.ceil(this.data.length / this.rowsPerPage);
this.currentPage += amount;
if (this.currentPage > totalPages) {
this.currentPage = totalPages;
} else if (this.currentPage < 1) {
this.currentPage = 1;
}
this.loadDataToTable();
}
updatePageInfo() {
const pageInfo = document.getElementById('pageInfo');
const totalPages = Math.ceil(this.data.length / this.rowsPerPage);
pageInfo.textContent = `${this.txtPage} ${this.currentPage} / ${totalPages}`;
}
sortTableByColumn(column) {
if (this.sortColumn === column) {
// Aynı sütuna tekrar tıklandığında sıralama yönünü değiştir
this.sortDirection = !this.sortDirection;
} else {
// Farklı bir sütuna tıklandığında varsayılan olarak artan sıralama yap
this.sortDirection = true;
this.sortColumn = column;
}
this.data.sort((a, b) => {
if (a[column] < b[column]) {
return this.sortDirection ? -1 : 1;
}
if (a[column] > b[column]) {
return this.sortDirection ? 1 : -1;
}
return 0;
});
this.loadDataToTable();
}
exportTableToCSV(filename) {
const csv = [];
const rows = document.getElementById(this.tableId).querySelectorAll("tr");
for (let i = 0; i < rows.length; i++) {
const cells = rows[i].querySelectorAll("td, th");
const row = Array.from(cells).map(cell => {
let cellText = cell.textContent.replace(/"/g, '""'); // Çift tırnakları escape et
return `"${cellText}"`; // Her hücreyi çift tırnak içine al
}).join(",");
csv.push(row);
}
this.downloadCSV(csv.join("\n"), filename);
}
downloadCSV(csv, filename) {
const csvFile = new Blob([csv], { type: "text/csv" });
const downloadLink = document.createElement("a");
downloadLink.download = filename;
downloadLink.href = window.URL.createObjectURL(csvFile);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
//export default TeaTable;
// INITIALIZE
const options = {
data: [
{ id: 1, name: "Example Data 1", job: "Ekstra Info 1" },
{ id: 2, name: "Example Data 2" },
{ id: 3, name: "teatable", job: "table"}
],
themeColor : "#6967ce",
rowsPerPage: 5, // Opsiyonel: Sayfa başına satır sayısı (varsayılan: 5)
// Opsiyonel: Callback fonksiyonları
onCreate: (item) => { /* ... */ },
onEdit: (item, index) => { /* ... */ },
onDelete: (item, index) => { /* ... */ },
// language support
txtAdd : "Add",
txtUpdate : "Update",
txtDel : "Delete",
txtEdit : "Edit",
txtAct : "Actions",
txtSearch : "Search...",
txtPage : "Page",
txtConfirm : "Are you sure to delete this data?"
};
const myTable = new TeaTable('tableContainerId', options);