ar-design
Version:
AR Design is a (react | nextjs) ui library.
742 lines (741 loc) • 40.5 kB
JavaScript
"use client";
import "../../../assets/css/components/data-display/table/styles.css";
import { ARIcon } from "../../icons";
import Button from "../../form/button";
import Checkbox from "../../form/checkbox";
import Pagination from "../../navigation/pagination";
import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from "react";
import Input from "../../form/input";
import Utils from "../../../libs/infrastructure/shared/Utils";
import FilterPopup from "./FilterPopup";
import { FilterOperator } from "../../../libs/infrastructure/shared/Enums";
import Select from "../../form/select";
import Grid from "../grid-system";
import THeadCell from "./THeadCell";
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";
import PropertiesPopup from "./PropertiesPopup";
import { ExtractKey } from "./Helpers";
import Header from "./header/Header";
import TBody from "./body/TBody";
import DatePicker from "../../form/date-picker";
import { useTranslation } from "../../../libs/core/application/hooks";
const { Row, Column } = Grid;
const Table = forwardRef(({ children, trackBy, title, description, data, columns, actions, rowBackgroundColor, selections, previousSelections, sortedParams, searchedParams, onEditable, onDnD, pagination, config = { isSearchable: false }, }, ref) => {
// refs
const _innerRef = useRef(null);
const _tableWrapper = useRef(null);
const _tableContent = useRef(null);
const _tBody = useRef(null);
const _dragItem = useRef(null);
const _checkboxItems = useRef([]);
const _filterCheckboxItems = useRef([]);
// refs -> Search
const _searchTextInputs = useRef([]);
const _searchTimeOut = useRef(null);
// refs -> Properties
const _propertiesButton = useRef([]);
// refs -> Filter
const _filterButton = useRef([]);
// refs -> Selection
const _selectionItems = useRef([]);
const lastSentRef = useRef([]);
// variables
const _subrowOpenAutomatically = config.subrow?.openAutomatically ?? false;
const _subrowSelector = config.subrow?.selector ?? "subitems";
const _subrowButton = config.subrow?.button ?? true;
// className
const _tableClassName = ["ar-table", "scroll"];
// states
const [selectAll, setSelectAll] = useState(false);
const [showSubitems, setShowSubitems] = useState({});
// states -> Action Buttons
const [createTrigger, setCreateTrigger] = useState(false);
// states -> Search
const [searchedText, setSearchedText] = useState(null);
const [_searchedParams, setSearchedParams] = useState(null);
const [checkboxSelectedParams, setCheckboxSelectedParams] = useState(null);
// states -> Sort
const [sortConfig, setSortConfig] = useState([]);
const [sortCurrentColumn, setSortCurrentColumn] = useState(null);
// states -> Properties
const [openProperties, setOpenProperties] = useState(false);
const [propertiesButtonCoordinate, setPropertiesButtonCoordinate] = useState({
x: 0,
y: 0,
});
// states -> Filter
const [filterButtonCoordinate, setFilterButtonCoordinate] = useState({ x: 0, y: 0 });
const [filterPopupContent, setFilterPopupContent] = useState(null);
const [filterPopupOption, setFilterPopupOption] = useState(null);
const [filterPopupOptionSearchText, setFilterPopupOptionSearchText] = useState(null);
// states -> Filter Fields Backup
const [openFilter, setOpenFilter] = useState(false);
const [filterCurrentColumn, setFilterCurrentColumn] = useState(null);
const [filterCurrentDataType, setFilterCurrentDataType] = useState(null);
const [filterCurrentIndex, setFilterCurrentIndex] = useState(null);
// states -> Pagination
const [totalRecords, setTotalRecords] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
const [selectedPerPage, setSelectedPerPage] = useState(pagination?.perPage ?? 10);
// states -> Mobil
const [isMobile, setIsMobile] = useState(false);
// hooks
const { t } = useTranslation(String(config.locale ?? "tr"));
// Dışarıdan gelen ref'i _innerRef'e bağla.
useImperativeHandle(ref, () => _innerRef.current);
if (config && Object.keys(config.scroll || {}).length > 0) {
if (_tableContent.current && config.scroll) {
if (config.scroll.maxHeight) {
_tableContent.current.style.maxHeight = `${config?.scroll?.maxHeight}rem`;
}
}
}
// methods
const handleScroll = useCallback(() => {
if (!_tableWrapper.current)
return;
const wrapperRect = _tableWrapper.current.getBoundingClientRect();
const updateStickyPositions = (elements) => {
elements.forEach((element) => {
const leftChildren = Array.from(element.childNodes)
.filter((node) => node.nodeType === Node.ELEMENT_NODE)
.filter((child) => child.dataset.stickyPosition === "left");
const rightChildren = Array.from(element.childNodes)
.filter((node) => node.nodeType === Node.ELEMENT_NODE)
.filter((child) => child.dataset.stickyPosition === "right")
.reverse();
// Calculate positions and minimize `getBoundingClientRect` calls
const leftRects = leftChildren.map((child) => child.getBoundingClientRect());
const rightRects = rightChildren.map((child) => child.getBoundingClientRect());
const leftPrevious = leftRects.map((rect) => Math.abs(rect.right - wrapperRect.left));
const rightPrevious = rightRects.map((rect) => Math.abs(rect.left - wrapperRect.right));
// #region Left
leftChildren.forEach((child, index) => {
const prevLeft = index > 0 ? leftPrevious[index - 1] : 0;
if (index > 0) {
const childLeft = leftRects[index].left - wrapperRect.left;
if (Math.floor(childLeft) === Math.floor(prevLeft)) {
if (!child.classList.contains("active-sticky"))
child.classList.add("active-sticky");
}
else
child.classList.remove("active-sticky");
child.style.left = `${prevLeft}px`;
}
else
child.classList.add("sticky");
if (child.nodeName === "TD")
child.style.zIndex = `5`;
});
// #endregion
// #region Right
rightChildren.forEach((child, index) => {
const prevRight = index > 0 ? rightPrevious[index - 1] : 0;
if (index > 0) {
const childRight = Math.abs(rightRects[index].right - wrapperRect.right);
if (Math.floor(childRight) === Math.floor(prevRight)) {
if (!child.classList.contains("active-sticky"))
child.classList.add("active-sticky");
}
else
child.classList.remove("active-sticky");
child.style.right = `${prevRight}px`;
}
else
child.classList.add("sticky");
});
// #endregion
});
};
const theadElements = _tableWrapper.current.querySelectorAll("table > thead > tr");
const tbodyElements = _tableWrapper.current.querySelectorAll("table > tbody > tr");
requestAnimationFrame(() => {
updateStickyPositions(theadElements);
updateStickyPositions(tbodyElements);
});
}, []);
const handleResize = useMemo(() => {
return (_) => {
setIsMobile(window.innerWidth <= 768);
};
}, []);
const handleSearch = useCallback((name, value, dataType) => {
const operator = filterPopupOption?.key == name
? filterPopupOption.option?.value
: FilterOperator.Contains;
if (config.isServerSide) {
if (_searchTimeOut.current)
clearTimeout(_searchTimeOut.current);
_searchTimeOut.current = setTimeout(() => {
setSearchedParams((prev) => ({
...prev,
[name]: { value: value, operator: operator },
}));
if (pagination)
pagination.onChange?.(1, selectedPerPage);
}, dataType === "date" ? 0 : 750);
}
else {
setSearchedText((prev) => {
const _state = { ...prev };
if (value === "") {
delete _state[name]; // Key'i siliyoruz
}
else {
_state[name] = { value: value, operator: operator }; // Yeni değeri ekliyoruz
}
return _state;
});
}
setCurrentPage(1);
}, [filterPopupOption]);
const handleCheckboxChange = useCallback(async (event) => {
event.stopPropagation();
const { name, value, checked } = event.target;
setCheckboxSelectedParams((prev) => {
const prevFilters = prev?.[name] || [];
const updatedSet = new Set(prevFilters.map((f) => String(f.value)));
checked ? updatedSet.add(value) : updatedSet.delete(value);
const updatedArray = Array.from(updatedSet).map((v) => ({
value: v,
operator: FilterOperator.Equals, // Checkbox’lar genelde “Equals” anlamındadır.
}));
return {
...prev,
...(updatedArray.length > 0 ? { [name]: updatedArray } : { [name]: [] }),
};
});
}, []);
const handleFilterPopupContent = (c, dataType, index) => {
const key = ExtractKey(c.key);
if (!key)
return;
const value = Array.isArray(searchedText?.[key])
? "" // veya ihtiyacına göre birleştirme yap: searchedText[key].map(v => v.value).join(", ").
: searchedText?.[key]?.value;
const handleChange = (value) => {
const input = _searchTextInputs.current[index ?? 0];
if (input) {
const event = new Event("input", { bubbles: true });
input.value = value;
input.dispatchEvent(event);
}
};
setFilterPopupContent(() => {
switch (dataType) {
case "string":
case "number":
return (React.createElement(Row, null,
React.createElement(Column, { size: 12 },
React.createElement(Select, { value: filterOption.find((x) => x.value === filterPopupOption?.option?.value && filterPopupOption.key === c.key) ?? filterOption[0], options: filterOption, onChange: (option) => {
setFilterPopupOption({ key: c.key, option: option });
}, placeholder: t("Table.Filters.Where.Input.Placeholder") })),
React.createElement(Column, { size: 12 },
React.createElement(Input, { value: value ?? "", onChange: (event) => handleChange(event.target.value), placeholder: t("Table.Filters.Search.Input.Placeholder") }))));
case "object":
case "boolean":
return (React.createElement(Row, null,
React.createElement(Column, { size: 12 },
React.createElement(Input, { value: value ?? "", onChange: (event) => setFilterPopupOptionSearchText(event.target.value), placeholder: t("Table.Filters.Search.Input.Placeholder") })),
React.createElement(Column, { size: 12 }, c.filters
?.filter((filter) => filter.text.toLocaleLowerCase().includes(filterPopupOptionSearchText?.toLocaleLowerCase() ?? ""))
?.map((filter, fIndex) => {
const name = typeof c.key !== "object" ? String(c.key) : String(c.key.field);
return (React.createElement("div", null,
React.createElement(Checkbox, { ref: (element) => {
if (!element)
return;
_filterCheckboxItems.current[fIndex] = element;
}, variant: "filled", color: "green", label: filter.text, name: name, value: filter.value, checked: Array.isArray(checkboxSelectedParams?.[name]) &&
checkboxSelectedParams?.[name]?.some((f) => String(f.value) === String(filter.value)), onChange: async (event) => await handleCheckboxChange(event) })));
}))));
default:
return React.createElement(React.Fragment, null);
}
});
};
// Derinlemesine arama yapmak için özyinelemeli bir fonksiyon olarak kullanılmaktadır.
const deepSearch = (item, searchedText) => {
if (!searchedText || Object.keys(searchedText).length === 0)
return true;
const applyOperator = (value, filter) => {
if (Array.isArray(value)) {
// Array içindeki herhangi bir öğe eşleşirse true dön
return value.some((item) => applyOperator(item, filter));
}
if (typeof value === "object" && value !== null) {
// Eğer obje ise, içindeki değerlerden biri eşleşirse true dön
return Object.values(value).some((v) => applyOperator(v, filter));
}
const text = String(value ?? "").toLocaleLowerCase();
const searchText = String(filter.value ?? "").toLocaleLowerCase();
switch (filter.operator) {
case FilterOperator.Contains:
return text.includes(searchText);
case FilterOperator.DoesNotContains:
return !text.includes(searchText);
case FilterOperator.Equals:
return text === searchText;
case FilterOperator.DoesNotEquals:
return text !== searchText;
case FilterOperator.BeginsWith:
return text.startsWith(searchText);
case FilterOperator.EndsWith:
return text.endsWith(searchText);
case FilterOperator.Blank:
return text.trim() === "";
case FilterOperator.NotBlank:
return text.trim() !== "";
default:
return false;
}
};
return Object.entries(searchedText).every(([key, param]) => {
const _itemValue = item[key];
if (Array.isArray(param)) {
if (param.length === 0)
return true;
return param.some((filter) => applyOperator(_itemValue, filter));
}
else {
return applyOperator(_itemValue, param);
}
});
// Eğer değer bir sayı veya string ise, aranan metinle eşleşip eşleşmediğini kontrol ediyoruz.
// return Object.entries(searchedText).every(([key, param]) => {
// const _itemValue = item[key as keyof typeof item];
// if (typeof _itemValue === "number" || typeof _itemValue === "string" || typeof _itemValue === "boolean") {
// if (Array.isArray(param)) {
// if (param.length === 0) return true;
// else return param.some((v) => _itemValue.toString().toLocaleLowerCase().includes(v.toLocaleLowerCase()));
// }
// return _itemValue
// .toString()
// .toLocaleLowerCase()
// .includes(param.toLocaleLowerCase() ?? "");
// }
// if (typeof _itemValue === "object") {
// if (Array.isArray(param)) {
// if (param.length === 0) return true;
// else {
// return param.some((v) => {
// if (Array.isArray(_itemValue)) {
// return Object.values(_itemValue?.[0 as keyof typeof _itemValue] ?? {}).some((objValue) => {
// return String(objValue).toLocaleLowerCase().includes(String(v).toLocaleLowerCase());
// });
// }
// });
// }
// }
// }
// if (Array.isArray(_itemValue)) {
// console.log("Buradasın", _itemValue);
// }
// return false;
// });
};
const openAllSubrowsRecursively = (data, parentKey = "") => {
let result = {};
data.forEach((item) => {
const id = trackBy?.(item);
const key = parentKey ? `${parentKey}.${id}` : `${id}`;
const subitems = item[_subrowSelector];
if (subitems && Array.isArray(subitems) && subitems.length > 0) {
const nested = openAllSubrowsRecursively(subitems, key);
result[key] = true;
result = { ...result, ...nested };
}
});
return result;
};
const getData = useMemo(() => {
let _data = [...data];
if (_subrowOpenAutomatically)
setShowSubitems(openAllSubrowsRecursively(data));
if (searchedText && Object.keys(searchedText).length > 0) {
_data = _data.filter((item) => deepSearch(item, searchedText));
setTotalRecords(_data.length);
}
else {
setTotalRecords(data.length);
}
// Sorting...
if (sortConfig.length > 0 && !config.isServerSide) {
_data.sort((a, b) => {
for (const config of sortConfig) {
const aValue = a[config.key];
const bValue = b[config.key];
if (aValue < bValue)
return config.direction === "asc" ? -1 : 1;
if (aValue > bValue)
return config.direction === "asc" ? 1 : -1;
}
return 0;
});
}
if (pagination && !config.isServerSide) {
const indexOfLastRow = currentPage * selectedPerPage;
const indexOfFirstRow = indexOfLastRow - selectedPerPage;
_data = _data.slice(indexOfFirstRow, indexOfLastRow);
}
handleScroll();
return _data;
}, [data, searchedText, currentPage, selectedPerPage, sortConfig, config.isServerSide]);
// useEffects
useEffect(() => {
if (!previousSelections || previousSelections.length === 0) {
_selectionItems.current = [];
return;
}
const validSelections = data.filter((item) => previousSelections.some((selected) => trackBy?.(selected) === trackBy?.(item)));
// Gereksiz overwrite’i engelle.
if (!Utils.DeepEqual(_selectionItems.current, validSelections)) {
_selectionItems.current = validSelections;
}
}, [previousSelections, data, trackBy]);
useEffect(() => {
if (config?.isServerSide && sortedParams) {
const sortRecord = {};
sortConfig?.forEach((s) => {
if (s.direction)
sortRecord[String(s.key)] = s.direction;
});
const query = new URLSearchParams(sortRecord);
sortedParams(sortConfig ?? [], query.toString());
}
}, [sortConfig]);
useEffect(() => {
if (config?.isServerSide && searchedParams) {
const searchRecord = {};
Object.entries(_searchedParams ?? {}).forEach(([key, value]) => {
if (Array.isArray(value)) {
// Çoklu filtre değerleri varsa virgülle birleştir.
searchRecord[key] = value.map((v) => v.value).join(",");
}
else if (value && typeof value === "object") {
searchRecord[key] = String(value.value);
}
});
const query = new URLSearchParams(searchRecord);
columns.forEach((column) => {
const key = column.key;
const filterValue = _searchedParams?.[key];
const filterArray = Array.isArray(filterValue) ? filterValue : filterValue ? [filterValue] : [];
const getParamsLength = column.filters?.length ?? 0;
const searchedParamLength = filterArray.length;
if (getParamsLength === searchedParamLength) {
query.delete(column.key);
}
});
searchedParams(_searchedParams, query.toString(), filterPopupOption?.option?.value);
}
}, [_searchedParams]);
useEffect(() => {
if (!checkboxSelectedParams)
return;
if (config.isServerSide) {
if (_searchTimeOut.current)
clearTimeout(_searchTimeOut.current);
setSearchedParams((prev) => ({ ...prev, ...checkboxSelectedParams }));
}
else {
setSearchedText((prev) => ({ ...prev, ...checkboxSelectedParams }));
}
setCurrentPage(1);
if (pagination)
pagination.onChange?.(1, selectedPerPage);
}, [checkboxSelectedParams]);
useEffect(() => {
if (typeof selections !== "function")
return;
const payload = _selectionItems.current.map((item) => ({
...item,
trackByValue: trackBy?.(item),
}));
if (!Utils.DeepEqual(payload, lastSentRef.current)) {
lastSentRef.current = payload;
selections(payload);
}
}, [selections, trackBy]);
useEffect(() => {
// Filter Content alanı re-render işlemi.
if (filterCurrentColumn && filterCurrentDataType) {
handleFilterPopupContent(filterCurrentColumn, filterCurrentDataType, filterCurrentIndex);
}
}, [checkboxSelectedParams, filterPopupOption, filterPopupOptionSearchText]);
useLayoutEffect(() => {
// @DND
if (!onDnD || !_tBody.current || data.length === 0)
return;
_tBody.current.childNodes.forEach((item) => {
const _item = item;
// Events
_item.ondragstart = (event) => {
const dragItem = event.currentTarget;
_dragItem.current = dragItem;
dragItem.classList.add("drag-item");
if (event.dataTransfer) {
// 1. Geçici bir kapsayıcı oluştur
const shadowContainer = document.createElement("div");
shadowContainer.style.position = "absolute";
shadowContainer.style.top = "-9999px";
shadowContainer.style.left = "-9999px";
document.body.appendChild(shadowContainer);
if (config.dnd?.renderItem) {
// 2a. React Node varsa: createRoot ile render et
const root = createRoot(shadowContainer);
// flushSync kullanıyoruz çünkü setDragImage çağrılmadan önce
// DOM'un hemen güncellenmesi gerekiyor.
flushSync(() => {
root.render(config.dnd?.renderItem);
});
}
else {
// 2b. React Node yoksa: Varsayılan HTML string
shadowContainer.innerHTML = `
<div class="ar-dnd-shadow" style="background: white; padding: 10px; border: 1px solid #ccc;">
<i class="bi bi-gear-wide-connected"></i>
<span>Dragging...</span>
</div>
`;
}
// 3. Tarayıcıya bu elementi sürükleme görseli yapmasını söyle
// (0, 0) koordinatları mouse'un görselin neresinde duracağını belirler
event.dataTransfer.setDragImage(shadowContainer, 20, 20);
// 4. Temizlik: Görsel hafızaya alındıktan sonra DOM'dan kaldırabiliriz
// Bir sonraki event loop'ta silmek en güvenlisidir
setTimeout(() => {
document.body.removeChild(shadowContainer);
}, 0);
}
};
_item.ondragover = (event) => {
event.preventDefault();
const overItem = event.currentTarget;
const rect = overItem.getBoundingClientRect();
// Otomatik scroll.
if (rect.top < 250)
window.scrollBy(0, -20);
if (rect.bottom > window.innerHeight - 150)
window.scrollBy(0, 20);
// Gerçek taşıma işlemi.
if (_dragItem.current !== overItem) {
if (_tBody.current && _dragItem.current) {
const dragItemIndex = [..._tBody.current.children].indexOf(_dragItem.current);
const dropItemIndex = [..._tBody.current.children].indexOf(overItem);
if (dragItemIndex === -1 || dropItemIndex === -1)
return;
_tBody.current.insertBefore(_dragItem.current, dragItemIndex < dropItemIndex ? overItem.nextSibling : overItem);
const movedItem = data.splice(dragItemIndex, 1)[0];
if (movedItem) {
data.splice(dropItemIndex, 0, movedItem);
onDnD?.(data);
}
}
}
};
_item.ondragend = (event) => {
const item = event.currentTarget;
item.classList.remove("drag-item");
item.classList.add("end-item");
setTimeout(() => {
item.classList.remove("end-item");
if (item.classList.length === 0)
item.removeAttribute("class");
}, 1000);
};
});
_tBody.current.ondragover = (event) => event.preventDefault();
return () => {
if (!_tBody.current)
return;
_tBody.current.childNodes.forEach((item) => {
const _item = item;
_item.ondragstart = null;
_item.ondragover = null;
_item.ondragend = null;
});
_tBody.current.ondragover = null;
};
}, [data]);
useLayoutEffect(() => {
if (!pagination?.currentPage)
return;
setTimeout(() => handleScroll(), 0);
setCurrentPage(pagination?.currentPage ?? 1);
}, [pagination?.currentPage]);
useLayoutEffect(() => {
setCurrentPage(1);
setTimeout(() => handleScroll(), 0);
}, [selectedPerPage]);
useEffect(() => {
if (typeof selections !== "function" && config.validation) {
const updatedData = data.map((item) => {
if (!("trackByValue" in item) && trackBy) {
return { ...item, trackByValue: trackBy(item) };
}
return item;
});
config.validation?.getChangeData?.(updatedData);
}
}, [createTrigger]);
useEffect(() => {
setIsMobile(window.innerWidth <= 768);
window.addEventListener("resize", handleResize);
if (typeof selections !== "function" && config.validation) {
config.validation.getChangeData?.(data.map((d) => ({ ...d, trackByValue: trackBy?.(d) })) ?? []);
}
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
const filterOption = [
{ value: FilterOperator.Contains, text: t("Table.Filters.Where.Input.Item.1.Text") },
{ value: FilterOperator.DoesNotContains, text: t("Table.Filters.Where.Input.Item.2.Text") },
{ value: FilterOperator.Equals, text: t("Table.Filters.Where.Input.Item.3.Text") },
{ value: FilterOperator.DoesNotEquals, text: t("Table.Filters.Where.Input.Item.4.Text") },
{ value: FilterOperator.BeginsWith, text: t("Table.Filters.Where.Input.Item.5.Text") },
{ value: FilterOperator.EndsWith, text: t("Table.Filters.Where.Input.Item.6.Text") },
{ value: FilterOperator.Blank, text: t("Table.Filters.Where.Input.Item.7.Text") },
{ value: FilterOperator.NotBlank, text: t("Table.Filters.Where.Input.Item.8.Text") },
];
return (React.createElement("div", { ref: _tableWrapper, className: _tableClassName.map((c) => c).join(" ") },
(title || description || actions || React.Children.count(children) > 0) && (React.createElement(Header, { states: { createTrigger: { get: createTrigger, set: setCreateTrigger } }, title: title, description: description, actions: actions })),
React.createElement("div", { ref: _tableContent, className: "content", onScroll: handleScroll },
React.createElement("table", { ref: _innerRef },
React.createElement("thead", null,
React.createElement("tr", { key: "selection" },
data.some((item) => _subrowSelector in item) && _subrowButton && (React.createElement("td", { style: { width: 0, minWidth: 0 } })),
selections && (React.createElement("th", { className: "selection-col sticky-left", "data-sticky-position": "left", style: { bottom: 0 } },
React.createElement(Checkbox, { variant: "filled", color: "green", checked: selectAll, onChange: (event) => {
if (_checkboxItems.current.length > 0) {
setSelectAll(event.target.checked);
_checkboxItems.current.forEach((item) => {
if (item) {
if (item.checked !== event.target.checked)
item.click();
}
});
}
} }))),
React.createElement(THeadCell, { refs: {
propertiesButton: _propertiesButton,
}, states: {
open: { get: openProperties, set: setOpenProperties },
sort: { get: sortConfig, set: setSortConfig },
sortCurrentColumn: { set: setSortCurrentColumn },
propertiesButtonCoordinate: { set: setPropertiesButtonCoordinate },
}, methods: { handleScroll }, columns: columns, config: config })),
config?.isSearchable && (React.createElement("tr", { key: "isSearchable" },
selections && (React.createElement("th", { key: `column-selections`, className: "selection-col sticky-left", "data-sticky-position": "left" })),
columns.map((c, cIndex) => {
let _className = [];
const key = typeof c.key !== "object" ? String(c.key) : String(c.key.field);
const csrValue = Array.isArray(searchedText?.[key])
? "" // veya ihtiyacına göre birleştirme yap: searchedText[key].map(v => v.value).join(", ").
: searchedText?.[key]?.value;
const ssrValue = Array.isArray(_searchedParams?.[key])
? "" // veya ihtiyacına göre birleştirme yap: _searchedParams[key].map(v => v.value).join(", ").
: _searchedParams?.[key]?.value;
if (c.config?.sticky)
_className.push(`sticky-${c.config.sticky}`);
if (c.config?.alignContent) {
_className.push(`align-content-${c.config.alignContent}`);
}
return (React.createElement("th", { key: `column-${cIndex}`, ...(_className.length > 0 && {
className: `${_className.map((c) => c).join(" ")}`,
}), ...(c.config?.sticky && {
"data-sticky-position": c.config.sticky,
}) }, c.key && (React.createElement("div", { className: "filter-field" }, c.filterDataType === "date" ? (React.createElement(DatePicker, { value: (config.isServerSide ? ssrValue : csrValue) ?? "", name: key, onClick: () => {
handleScroll();
}, onChange: (value) => handleSearch(key, value, c.filterDataType), style: { height: "2rem" }, config: { isClock: true, isFooterButton: true, locale: config.locale }, disabled: !c.key || !!c.filters })) : (React.createElement(React.Fragment, null,
React.createElement(Input, { ref: (element) => {
if (!element)
return;
_searchTextInputs.current[cIndex] = element;
}, variant: c.key && !c.filters ? "outlined" : "filled", style: { height: "2rem" }, value: (config.isServerSide ? ssrValue : csrValue) ?? "", name: key, onClick: () => {
handleScroll();
}, onInput: (event) => handleSearch(event.currentTarget.name, event.currentTarget.value), disabled: !c.key || !!c.filters }),
React.createElement("span", { ref: (element) => {
if (!element)
return;
_filterButton.current[cIndex] = element;
}, onClick: (event) => {
event.preventDefault();
event.stopPropagation();
// Temizlik...
setFilterPopupOptionSearchText("");
const rect = event.currentTarget.getBoundingClientRect();
const screenCenterX = window.innerWidth / 2;
// const screenCenterY = window.innerHeight / 2;
const coordinateX = rect.x > screenCenterX ? rect.x + rect.width - 225 : rect.x;
const coordinateY = rect.y + rect.height;
// data içindeki alanların tiplerini bulmak için kullanılmaktadır
const getDataFirstItem = { ...data[0] };
const key = typeof c.key !== "object" ? String(c.key) : String(c.key.field);
const getValueByKey = getDataFirstItem[key];
let dataType = typeof getValueByKey;
if (getValueByKey == null)
dataType = "string";
setFilterButtonCoordinate({ x: coordinateX, y: coordinateY });
setFilterCurrentColumn(c);
setFilterCurrentDataType(c.filterDataType ?? dataType);
setFilterCurrentIndex(cIndex);
setOpenFilter(true);
handleFilterPopupContent(c, c.filterDataType ?? dataType, cIndex);
handleScroll();
} },
React.createElement(Button, { variant: "borderless", icon: {
element: React.createElement(ARIcon, { size: 24, icon: "Filter", fill: "var(--dark)", strokeWidth: 0 }),
} }))))))));
})))),
React.createElement("tbody", { ref: _tBody },
React.createElement(TBody, { data: getData, columns: columns, refs: { _checkboxItems: _checkboxItems, _selectionItems: _selectionItems }, states: {
setSelectAll: { get: selectAll, set: setSelectAll },
showSubitems: { get: showSubitems, set: setShowSubitems },
}, methods: {
trackBy: trackBy,
selections: selections,
onDnD: onDnD,
onEditable: onEditable,
rowBackgroundColor: rowBackgroundColor,
}, config: config })))),
React.createElement(FilterPopup, { refs: {
tableContent: _tableContent,
buttons: _filterButton,
}, states: {
open: { get: openFilter, set: setOpenFilter },
}, coordinate: filterButtonCoordinate }, filterPopupContent),
config.isProperties && (React.createElement(PropertiesPopup, { refs: {
tableContent: _tableContent,
buttons: _propertiesButton,
}, states: {
open: { get: openProperties, set: setOpenProperties },
sort: { get: sortConfig, set: setSortConfig, currentColumn: sortCurrentColumn },
}, methods: { handleScroll }, coordinate: propertiesButtonCoordinate, config: config })),
React.createElement("div", { className: "footer" },
React.createElement("span", null, isMobile ? (React.createElement(React.Fragment, null,
React.createElement("strong", null,
(currentPage - 1) * selectedPerPage + 1,
" -",
" ",
Math.min(currentPage * selectedPerPage, pagination?.totalRecords || getData.length),
" of",
" ",
pagination?.totalRecords || getData.length))) : (t("Table.Pagination.Information.Text", (currentPage - 1) * selectedPerPage + 1, Math.min(currentPage * selectedPerPage, pagination?.totalRecords || getData.length), pagination?.totalRecords || getData.length))),
pagination && (React.createElement(Pagination, { totalRecords: config.isServerSide ? pagination.totalRecords : (totalRecords ?? 0), currentPage: currentPage, perPage: selectedPerPage, onChange: (currentPage, perPage) => {
setCurrentPage(currentPage);
setSelectedPerPage(perPage);
pagination.onChange?.(currentPage, perPage);
setTimeout(() => handleScroll(), 0);
} })))));
});
export default memo(Table, (prevProps, nextProps) => {
const data = Utils.DeepEqual(prevProps.data, nextProps.data);
const columns = Utils.DeepEqual(prevProps.columns, nextProps.columns);
const actions = Utils.DeepEqual(prevProps.actions, nextProps.actions);
const previousSelections = Utils.DeepEqual(prevProps.previousSelections, nextProps.previousSelections);
const pagination = Utils.DeepEqual(prevProps.pagination, nextProps.pagination);
return data && columns && actions && previousSelections && pagination;
});